fix(agents): keep null in the task output schema embedded in the prompt - #6775
fix(agents): keep null in the task output schema embedded in the prompt#6775monkscode wants to merge 10 commits into
Conversation
build_task_prompt_with_schema embeds the task output schema into the prompt via generate_model_description, whose strip_null_types defaults to True. Combined with ensure_all_properties_required, an Optional[str] = None field reaches the model as a required, non-nullable string, contradicting the provider-side response schema generated from the same model. That sanitizer targets OpenAI strict function-calling schemas. This call site produces prompt prose, where those constraints do not apply. Pass strip_null_types=False, matching the existing call for tool schemas in utilities/agent_utils.py. Fixes crewAIInc#6774
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThe task prompt schema now preserves null types for JSON and Pydantic outputs. Tests verify that optional Pydantic fields produce schemas accepting both strings and null values. ChangesNullable task output schemas
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
lib/crewai/tests/agents/test_agent_utils.py (2)
25-27: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winCover the
output_jsonbranch.This test constructs
Taskwithoutput_pydanticonly. It never executes thetask.output_jsonbranch changed inbuild_task_prompt_with_schema. Add a matching JSON-output case or parameterize the test over both output attributes.Based on PR objectives: nullable prompt schemas must be preserved for both JSON and Pydantic outputs.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/crewai/tests/agents/test_agent_utils.py` around lines 25 - 27, Extend the test around build_task_prompt_with_schema to cover Task configured with output_json in addition to output_pydantic, either by adding a matching JSON-output case or parameterizing both configurations. Ensure the assertions verify nullable prompt schemas are preserved for both output attributes.
33-36: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMake the
anyOfassertion order-independent.The exact list comparison makes member order part of the test contract. JSON Schema alternatives are order-independent, so a valid generator change can fail this test.
Proposed assertion
- assert schema["properties"]["note"]["anyOf"] == [ - {"type": "string"}, - {"type": "null"}, - ] + assert { + entry["type"] for entry in schema["properties"]["note"]["anyOf"] + } == {"string", "null"}Based on coding guidelines: tests should focus on behavior rather than implementation details.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/crewai/tests/agents/test_agent_utils.py` around lines 33 - 36, Update the `anyOf` assertion in the agent schema test to compare alternatives without relying on list order, while still requiring exactly the string and null schema members. Preserve the existing validation of the `note` property’s schema.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@lib/crewai/tests/agents/test_agent_utils.py`:
- Around line 25-27: Extend the test around build_task_prompt_with_schema to
cover Task configured with output_json in addition to output_pydantic, either by
adding a matching JSON-output case or parameterizing both configurations. Ensure
the assertions verify nullable prompt schemas are preserved for both output
attributes.
- Around line 33-36: Update the `anyOf` assertion in the agent schema test to
compare alternatives without relying on list order, while still requiring
exactly the string and null schema members. Preserve the existing validation of
the `note` property’s schema.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: fe8f54e9-1bab-4281-b4e9-a21ba24e21be
📒 Files selected for processing (2)
lib/crewai/src/crewai/agent/utils.pylib/crewai/tests/agents/test_agent_utils.py
|
@lorenzejay @greysonlalonde — flagging this for triage since it touches One concrete blocker before the change itself: CI has never run here. All seven workflows on the head commit sit at The change is two lines, and there is precedent in-tree: On why it is worth the look: the bug is silent, not cosmetic. The provider-side response schema still permits nulls, so every task validates and returns normally — the damage is in what the model writes to avoid emitting a null it has been told is illegal. On our planner task we measured unrelated parameters concatenated into one required string field, a step silently dropped, and some runs extending a single JSON string value until it hit the output-token ceiling. Our end-to-end pass rate went 96.7% → 86.7% on 1.15.10, which is why we are still pinned to 1.8.1. Happy to move or split the test file, or rebase — just say where. |
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
|
Ran #6774's repro on 1. This fixes the nullability half of #6774; the
2. Eight call sites feed this schema into the same prompt slice. This fixes two.
Useful precedent already in the tree: 3.
Two details that make this hard to catch later:
4. The new test passes either way on that shape. None of this argues against merging — the diff is correct for what it does. It's about whether the remaining call sites and |
|
@percymcn — I loaded 1.
Making the in-prompt 2. Other call sites — scoped deliberately, happy to widen. Confirmed on
The I scoped this to the site #6774 reproduces on to keep a first contribution reviewable. Extending to the nine above is mechanical; I'll push it here if a maintainer prefers one PR, otherwise a follow-up. 3. An object with Why I'd keep it out of this PR isn't scope hygiene. 4. Test coverage for that shape — lands with (3). Adding Blocker unchanged: the seven workflows on the head commit are still |
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
|
@lucasgomide — flagging this to you specifically rather than broadly: #4579 is where @theCyberTech — a smaller, separate ask for whoever reaches it first: the seven workflows on the head commit are all still What has changed since 4 Aug The defect is still on The surface is wider than the PR title suggests:
Measured on 1.15.16, offline, for Scope is yours to set. As it stands this is two lines plus a regression test in |
build_task_prompt_with_schemaembeds the task output schema into the prompt usinggenerate_model_description, whosestrip_null_typesdefaults toTrue. Combined withensure_all_properties_required, anOptional[str] = Nonefield is presented to themodel as a required, non-nullable string — leaving it no way to express "not
applicable", and contradicting the provider-side response schema generated from the
same model.
That sanitizer targets OpenAI strict function-calling schemas. This call site produces
prompt prose, where those constraints do not apply.
This passes
strip_null_types=False, matching the existing call for tool schemas atutilities/agent_utils.py:268.requiredstill lists every property, which is thestandard strict-mode idiom once the type is nullable.
Before / after
For
class Step(BaseModel): name: str; note: Optional[str] = None:requiredis['name', 'note']in both cases — unchanged by this PR.Tests
Adds
lib/crewai/tests/agents/test_agent_utils.py. No existing test referencedbuild_task_prompt_with_schema, so the file is new; maintainers may prefer itelsewhere. It fails on
mainwithKeyError: 'anyOf'and passes with this change.Existing cassette-based tests are unaffected:
vcr_configmatches on["method", "scheme", "host", "port", "path"], not on the request body.Fixes #6774