Repository navigation
test(llms): shared LLMMessage.content contract for OpenAI and Anthropic - #8039
Syed Ahnaf Raza (syahra712) wants to merge 2 commits into
Conversation
Pin what each native provider puts on the wire for string, multimodal, and None content: strings and text parts are sent verbatim, image parts keep their bytes in the provider's block format, None never drops the message or becomes the text "None", and caller messages are not mutated. Provider differences (None vs "", system as message vs system param) are recorded explicitly rather than normalised away. Refs crewAIInc#8006 (milestone 2)
Strix Security ReviewWarning This pull request has 29 commits after the last Strix review ( No security issues found. Review summaryReviewed the single changed file in PR #8039: Updated for Reviewed by Strix |
|
Important Review skippedReview was skipped as selected files did not have any reviewable changes. ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Priority: ⬇️ Low Merge Risk: ⚪ Minimal · up to
|
Add Gemini to the plain-text contract from crewAIInc#8037 and the LLMMessage.content contract from crewAIInc#8039, using fake google-genai clients. Two known Gemini formatting defects are marked xfail(strict=True). Refs crewAIInc#8127 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add Gemini to the plain-text contract from crewAIInc#8037 and the LLMMessage.content contract from crewAIInc#8039, using fake google-genai clients. Two known Gemini formatting defects are marked xfail(strict=True). Refs crewAIInc#8127
Related issue
Part of #8006 (milestone 2:
LLMMessage.contentas text, multimodal parts, andNone). Defects found while writing these tests are filed separately in #8038, not fixed here.Summary
Adds
lib/crewai/tests/llms/test_message_content_contract.py, one shared test module parametrized over the native OpenAI and Anthropic providers. It drives each provider's own formatting and request-building code (_format_messages/_format_messages_for_anthropic→_prepare_completion_params) and asserts on the request payload, so there are no SDK calls and no API keys.Shared contract, asserted for both providers:
image_urlvsimage/source);Nonecontent never drops the message and never becomes the text"None";message_content_textis the one place content collapses to a string (text /None/ multimodal).Legitimate provider differences are recorded explicitly in the
PROVIDERStable instead of being normalised away: OpenAI forwardsNoneand keeps system messages in the list; Anthropic sends""and lifts system text intosystem.No production code changes. This is standalone so it doesn't overlap milestone 1; once a shared fixture from milestone 1 lands I'll fold the
PROVIDERStable into it.Verification
Tests added or updated for the changed behavior
Relevant tests and quality checks pass locally
pytest lib/crewai/tests/llms/test_message_content_contract.py: 19 passed.Mutation check, each seeded bug fails at least one test:
str(None)for user content: caught.ruff check/ruff format: clean.Additional context
The system-message cases (
Noneor list content stringified into the prompt on the OpenAI o1 path and in Anthropic) are left out of this PR on purpose and tracked in #8038, per the project plan (open a bounded issue before submitting a fix).Prepared with AI assistance (Claude Code), per CONTRIBUTING. I can't apply the
llm-generatedlabel myself; could a maintainer add it?Note
Low Risk
Test-only addition with no runtime or API behavior changes; risk is limited to CI coverage expectations.
Overview
Adds
lib/crewai/tests/llms/test_message_content_contract.py, a parametrized contract suite forLLMMessage.content(str | list[dict] | None) on the native OpenAI and Anthropic completion paths.Tests exercise each provider’s own message formatting and
_prepare_completion_paramswiring (no SDK calls or real API keys) and assert the outbound request shape: verbatim strings, unchanged text/assistant parts, image bytes preserved in provider-specific blocks,Nonenever dropped or stringified as"None", no in-place mutation of caller message dicts, and system prompts reaching the request (in-message for OpenAI vs liftedsystemfor Anthropic). A smallPROVIDERStable encodes intentional differences (Nonevs"", system placement).message_content_textis covered separately as the only place content collapses to a plain string.No production code changes—this pins behavior for milestone work on multimodal/
Nonecontent; known gaps (e.g. some system-message edge cases) are tracked elsewhere.Reviewed by Cursor Bugbot for commit bbfbaae. Bugbot is set up for automated code reviews on this repo. Configure here.