Skip to content

fix(bedrock): collapse multimodal parts content in Converse system and tool messages - #7930

Open
Winoren (Winoren) wants to merge 2 commits into
crewAIInc:mainfrom
Winoren:fix/bedrock-converse-multimodal-content
Open

Winoren (Winoren) wants to merge 2 commits into
crewAIInc:mainfrom
Winoren:fix/bedrock-converse-multimodal-content

Conversation

@Winoren

@Winoren Winoren (Winoren) commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

AI-generated fix — should carry the llm-generated label (I don't have triage permission to add it myself).

Problem

BedrockCompletion._format_messages_for_converse stringified system and tool message content with an f-string or str(), but LLMMessage.content is str | list[dict[str, Any]] | None — the list form is the multimodal parts one. On the three affected sites a parts list ended up as the list repr:

  • a system message following another one put the repr into the system text (system_message += f"\n\n{content}"),
  • a lone system message with parts left a list in system[0].text — botocore rejects the request before it is sent,
  • a tool message put the repr into toolResult.content[0].text.

User and assistant messages were not affected (a parts list there is forwarded to Converse unchanged). Same class as #7670, #7674 and #7678, none of which touch the Bedrock provider.

Change

Collapse the content with the shared message_content_text helper (crewai.utilities.agent_utils), which returns "" for None, joins the text parts of a list, and passes strings through — the same treatment llm.py, memory and token estimation already apply. The "\n\n" join between multiple system messages is unchanged, and plain string content behaves exactly as before.

Validation

  • New tests in lib/crewai/tests/llms/bedrock/test_bedrock.py:
    • lone system message with parts → system_message == "be terse and polite" (failed before, passes after)
    • second system message with parts → "A\n\nbe terse and polite" (failed before, passes after)
    • tool message with parts → toolResult.content == [{"text": "result payload"}] (failed before, passes after)
    • string system/tool content regression test (passes before and after)
  • pytest lib/crewai/tests/llms/bedrock/test_bedrock.py → 42 passed, 1 skipped
  • pytest lib/crewai/tests/llms/ → 851 passed, 20 skipped
  • ruff check / ruff format --check clean on both changed files

Fixes #7920


Note

Low Risk
Scoped to Bedrock Converse formatting with a well-used utility; plain-string paths are unchanged and covered by new tests.

Overview
Fixes Bedrock Converse message formatting when system or tool messages use multimodal parts (content as a list of dicts) instead of a plain string.

_format_messages_for_converse now uses the shared message_content_text helper for system text (including \n\n joins) and toolResult.content[0].text, so text parts are extracted instead of Python list reprs or invalid list payloads that botocore rejects.

Adds Bedrock formatter tests for parts-based system/tool messages and a regression check that string content is unchanged.

Reviewed by Cursor Bugbot for commit 547db2d. Bugbot is set up for automated code reviews on this repo. Configure here.

…d tool messages

_format_messages_for_converse stringified system and tool message content
with an f-string or str(). LLMMessage.content is str | list[dict] | None, so
a parts list there put the list repr into the system text block (botocore
rejects the request before it is sent) or into toolResult.content[0].text.

Collapse content with the shared message_content_text helper, matching the
treatment user and assistant messages already get (a parts list there is
forwarded to Converse unchanged).

Fixes crewAIInc#7920
@strix-security

strix-security Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Strix Security Review

Warning

This pull request has 49 commits after the last Strix review (547db2d). 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

PR #7930 is a narrowly-scoped correctness fix for BedrockCompletion._format_messages_for_converse. It replaces str() / f-string list-repr formatting of system and tool message content with the shared message_content_text helper, so multimodal content parts lists collapse to their text instead of a Python list repr. The helper (message_content_text -> _content_parts_text) returns "" for None, passes strings through, and joins only string-typed text blocks while skipping non-text parts. The changed code has no injection sink, no authentication/authorization adjustment, no secret handling, no deserialization or file/network primitive, and no change to the trust boundary around message content (text is assembled into the Bedrock Converse request exactly as before, merely formatted correctly). Static analysis of the modified source returned no findings. No security-relevant vulnerabilities were identified.

Updated for 547db2d.


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

@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 49 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 2fc2afa1-4972-494a-a809-0a897ed64108
📥 Commits

Reviewing files that changed from the base of the PR and between 547db2d and 34435c7.

📒 Files selected for processing: 2
  • lib/crewai/src/crewai/llms/providers/bedrock/completion.py
  • lib/crewai/tests/llms/bedrock/test_bedrock.py

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: 02e6a82f-8cbf-4343-913e-e0df5107e8b7
📥 Commits

Reviewing files that changed from the base of the PR and between 19d4154 and 547db2d.

📒 Files selected for processing (2)
  • lib/crewai/src/crewai/llms/providers/bedrock/completion.py
  • lib/crewai/tests/llms/bedrock/test_bedrock.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

Bedrock Converse formatting now extracts text from system and tool messages with message_content_text. Tests cover text-part lists, joined system messages, and unchanged formatting for string content.

Changes: Bedrock message formatting

Layer / File(s) Summary
Format system and tool content
lib/crewai/src/crewai/llms/providers/bedrock/completion.py, lib/crewai/tests/llms/bedrock/test_bedrock.py
The formatter uses message_content_text for system and tool message content. Tests check text-part extraction, joining multiple system messages, and preserved formatting for string content.

Suggested reviewers: vidit-ostwal

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 547db

System and tool messages with text parts are now formatted as text, while string content and system-message separators remain intact. No actionable merge-blocking risk was identified.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the Bedrock change: collapsing multimodal parts in Converse system and tool messages.
Description check ✅ Passed The description identifies issue #7920, explains the problem and solution, and reports tests and quality checks. It covers the template’s required information, although it uses different section headi…
Linked Issues check ✅ Passed Issue #7920 requires text parts in system and tool messages to be collapsed with message_content_text, including lone and joined system messages. The PR applies the helper to both formatter paths an…
Out of Scope Changes check ✅ Passed The PR changes only Bedrock Converse formatting and its regression tests. These changes directly support issue #7920. No unrelated changes are reported.
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 7 functions across 2 files.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · 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.

@Winoren

Copy link
Copy Markdown
Contributor Author

Hi crewAI team, friendly ping — this PR has been open a while and the CI checks never actually ran because this is a fork PR. The GitHub Actions checks are stuck showing action_required across all CI suites, which means maintainers need to click Approve and run on the workflow files before they start executing.

Could someone take a look when you have a moment? Happy to rebase on the latest main or adjust anything if needed — just let me know. Thanks!

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.

[BUG] Bedrock Converse formatter stringifies multimodal system and tool message content

1 participant