Skip to content

test(llms): shared LLMMessage.content contract for OpenAI and Anthropic - #8039

Open
Syed Ahnaf Raza (syahra712) wants to merge 2 commits into
crewAIInc:mainfrom
syahra712:test/llm-message-content-contract
Open

Syed Ahnaf Raza (syahra712) wants to merge 2 commits into
crewAIInc:mainfrom
syahra712:test/llm-message-content-contract

Conversation

@syahra712

@syahra712 Syed Ahnaf Raza (syahra712) commented Oct 10, 2026 •

Copy link
Copy Markdown

Related issue

Part of #8006 (milestone 2: LLMMessage.content as text, multimodal parts, and None). 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:

  • string content is sent verbatim;
  • text-only parts and assistant parts are sent unchanged;
  • an image part keeps its bytes, in the provider's own block format (image_url vs image/source);
  • None content never drops the message and never becomes the text "None";
  • formatting does not mutate the caller's messages (the executor reuses its buffer across calls);
  • message_content_text is the one place content collapses to a string (text / None / multimodal).

Legitimate provider differences are recorded explicitly in the PROVIDERS table instead of being normalised away: OpenAI forwards None and keeps system messages in the list; Anthropic sends "" and lifts system text into system.

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 PROVIDERS table 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:

    • Anthropic sending str(None) for user content: caught.
    • Anthropic skipping image-block conversion: caught.
    • The base formatter mutating the caller's message dicts in place: caught (6 failures).
  • ruff check / ruff format: clean.

Additional context

The system-message cases (None or 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-generated label 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 for LLMMessage.content (str | list[dict] | None) on the native OpenAI and Anthropic completion paths.

Tests exercise each provider’s own message formatting and _prepare_completion_params wiring (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, None never dropped or stringified as "None", no in-place mutation of caller message dicts, and system prompts reaching the request (in-message for OpenAI vs lifted system for Anthropic). A small PROVIDERS table encodes intentional differences (None vs "", system placement). message_content_text is covered separately as the only place content collapses to a plain string.

No production code changes—this pins behavior for milestone work on multimodal/None content; 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.

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

strix-security Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Strix Security Review

Warning

This pull request has 29 commits after the last Strix review (b802216). Strix has not reviewed these changes.
Automatic review on push is off for this repository. To review the latest changes, tag @strix-security in a comment, or turn on re-review on push.

No security issues found.

Review summary

Reviewed the single changed file in PR #8039: lib/crewai/tests/llms/test_message_content_contract.py (a new, test-only module). The file adds provider-parametrized unit tests that exercise the native OpenAI and Anthropic message-formatting paths and assert on the resulting request payload dictionaries. It introduces no production code, no new endpoints, no user-controlled input sinks, and no dangerous operations. The only credentials present (api_key="test-key") are clearly placeholders, not committed secrets. No security vulnerabilities were identified in the changed code.

Updated for b802216.


Reviewed by Strix
Re-run review · Configure security review settings

@coderabbitai

coderabbitai Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 23281f60-260f-4f8b-b766-2a209da5ee30
📥 Commits

Reviewing files that changed from the base of the PR and between b802216 and bbfbaae.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 8500b5b8-e884-4f62-8ba3-cf29f0890eb0
📥 Commits

Reviewing files that changed from the base of the PR and between 6b93fa0 and b802216.

📒 Files selected for processing (1)
  • lib/crewai/tests/llms/test_message_content_contract.py

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

📝 Walkthrough

Adds shared tests for OpenAI and Anthropic message formatting. The tests cover content preservation, provider-specific handling of None and system messages, content immutability, and message_content_text output.

Changes: Message Content Contract

Layer / File(s) Summary
Shared provider contract tests
lib/crewai/tests/llms/test_message_content_contract.py
Adds provider adapters and parameterized assertions for string, text-only, assistant, image, and None content. Tests provider-specific system-message placement, preservation of caller content, and message_content_text conversion.

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to b8022

This change adds tests for message content handling in the OpenAI and Anthropic providers. It does not change production behavior, so there is no meaningful merge risk.

Pre-merge checks | Passed 4 | Failed 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 12 functions across 1 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check Passed The title clearly identifies the addition of a shared LLMMessage.content contract test for OpenAI and Anthropic providers.
Description check Passed The description includes a related issue, a detailed summary, verification results, checked test and quality items, and additional context. It matches the repository template and explains intentional …
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Bahram Mohmand (Bmohmand) added a commit to Bmohmand/crewAI that referenced this pull request Oct 23, 2026
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>
Bahram Mohmand (Bmohmand) added a commit to Bmohmand/crewAI that referenced this pull request Oct 23, 2026
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

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant