fix(bedrock): support tool search results + chat annotations - #29120
Conversation
…notations Add an optional tool-message search_results path that maps directly to Bedrock toolResult.searchResult blocks, and convert Converse citationsContent into chat completion annotations for user-facing citation metadata. Co-authored-by: Cursor <cursoragent@cursor.com>
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
Greptile SummaryThis PR bridges Bedrock Converse ↔ OpenAI chat for two RAG-oriented features: tool messages can now carry a
Confidence Score: 5/5Safe to merge; changes are additive, the only behavioral delta on existing callers is the pass-through header filter (conservative and beneficial), and the new Bedrock paths are gated behind an optional search_results field. All three code paths have regression tests, the empty-list fallback edge case is explicitly covered, and no existing test assertions were weakened. The annotations being dropped for mixed text+citations responses is a deliberate, documented design choice. The only structural concern is Bedrock-specific field names in a general utility module, which is a maintainability note rather than a correctness issue. litellm/litellm_core_utils/prompt_templates/common_utils.py — contains Bedrock-specific field names outside the llms/ tree; litellm/llms/bedrock/chat/converse_transformation.py — the annotation-stitching heuristic may need revisiting as Nova grounding response shapes evolve.
|
| Filename | Overview |
|---|---|
| litellm/llms/bedrock/chat/converse_transformation.py | Adds _transform_citations_to_annotations to map Bedrock citationsContent blocks to OpenAI-style annotations, with conservative stitching that only attaches annotations when content is empty or punctuation-only. Logic and offset tracking look correct for the tested Nova grounding pattern. |
| litellm/litellm_core_utils/prompt_templates/factory.py | Refactors _convert_to_bedrock_tool_call_result into smaller helpers and adds _build_bedrock_tool_result_content_blocks for the search_results extension. Empty search_results correctly falls through to the normal content path. |
| litellm/litellm_core_utils/prompt_templates/common_utils.py | Adds extract_search_results_text with Bedrock-specific field names (source, title, content[].text, citations) in a general utility module, and calls it from convert_content_list_to_str for token estimation. |
| litellm/types/llms/bedrock.py | Adds SearchResultBlock, CitationSearchResultLocationBlock, CitationGeneratedContentBlock TypedDicts and extends existing types with new optional fields for search result and citation support. |
| litellm/types/llms/openai.py | Adds annotations field to ChatCompletionResponseMessage TypedDict; ChatCompletionAnnotation was already defined in the file. |
| litellm/proxy/pass_through_endpoints/pass_through_endpoints.py | Filters custom_headers through the existing excluded_headers set before merging, preventing framework-injected defaults like content-length: 0 from overriding real passthrough response headers. |
| enterprise/litellm_enterprise/proxy/hooks/managed_files.py | Changes c["type"] to c.get("type") to avoid KeyError on Bedrock toolResult content blocks that omit the OpenAI-style type field. |
| litellm/litellm_core_utils/token_counter.py | Adds search_results branch in _count_messages to count tokens from the new search_results payload using extract_search_results_text. |
| tests/test_litellm/llms/bedrock/chat/test_converse_transformation.py | Adds comprehensive unit tests for search result mapping, citations-to-annotations conversion, offset tracking, and edge cases (empty search_results, null source/title, non-stitched annotations). All tests use mocked responses. |
Reviews (6): Last reviewed commit: "fix(bedrock): complete citation TypedDic..." | Re-trigger Greptile
Reformat the updated bedrock prompt template conversion file so CI black --check passes. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
Bugbot Autofix is ON, but it could not run because the spend limit has been reached. To enable Bugbot Autofix, have a team admin raise the spend limit in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 65f8e21. Configure here.
PR overviewAll previously flagged issues have been addressed. No open security concerns remain on this pull request. Security reviewNo open security issues remain on this pull request. Fixed/addressed: 2 · PR risk: 0/10 |
…nting Resolve mypy issues in citation parsing, only attach url_citation annotations when citation text is stitched into content, fall back to tool content when search_results is empty, and count search_results text in token/TPM preflight paths. Co-authored-by: Cursor <cursoragent@cursor.com>
Refactor _convert_to_bedrock_tool_call_result into smaller helpers so lint passes without changing Bedrock tool result behavior. Co-authored-by: Cursor <cursoragent@cursor.com>
…mates Include source, title, content text, and citations when estimating tokens so large metadata cannot bypass TPM preflight checks. Reformat factory.py with black. Co-authored-by: Cursor <cursoragent@cursor.com>
…e_ids_from_messages
…itellm_fix_citation_bedrock
1d9095f
into
litellm_internal_staging
…cess tests/test_litellm is documented as mock-only (tests/test_litellm/readme.md), but the Bedrock passthrough regression test bound a real loopback socket and ran a live uvicorn server in a background thread. Drive the same finalizer output through an in-process ASGI server (httpx.ASGITransport) instead, asserting the recomputed Content-Length the client receives against the served body. The test still fails against the pre-#29120 leak, where the placeholder Response's content-length: 0 overrides the real upstream length, and passes once get_response_headers sanitizes custom headers, without opening a real socket.
…#29120) * Fix overiding of fastapi_response headers * fix(bedrock): support tool search results and surface citations as annotations Add an optional tool-message search_results path that maps directly to Bedrock toolResult.searchResult blocks, and convert Converse citationsContent into chat completion annotations for user-facing citation metadata. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(format): align bedrock prompt factory with black Reformat the updated bedrock prompt template conversion file so CI black --check passes. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(bedrock): harden citations, search_results mapping, and token counting Resolve mypy issues in citation parsing, only attach url_citation annotations when citation text is stitched into content, fall back to tool content when search_results is empty, and count search_results text in token/TPM preflight paths. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(bedrock): extract tool result helpers to satisfy PLR0915 Refactor _convert_to_bedrock_tool_call_result into smaller helpers so lint passes without changing Bedrock tool result behavior. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(bedrock): count all forwarded search_results fields in token estimates Include source, title, content text, and citations when estimating tokens so large metadata cannot bypass TPM preflight checks. Reformat factory.py with black. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(managed-files): skip content blocks without a type key in get_file_ids_from_messages * fix(bedrock): stitch citations for any punctuation-only text block * fix(bedrock): map null citation source/title to empty annotation strings * fix(bedrock): advance citation offset for text-only citationsContent blocks * fix(bedrock): complete citation TypedDicts for grounding annotations --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com>

Summary
search_resultssupport on chat-completions tool messages and map it to BedrocktoolResult.content[].searchResultblocksmessage.annotationswhile keeping provider-specific citation payloadsFixes LIT-3401
Test plan
pytest -q tests/test_litellm/llms/bedrock/chat/test_converse_transformation.py -k \"tool_message_search_results_maps_to_bedrock_search_result_block\"pytest -q tests/test_litellm/llms/bedrock/chat/test_converse_transformation.py -k \"search_results_maps_to_bedrock_search_result_block or citations_content_maps_to_annotations\"python -m black --check litellm/llms/bedrock/chat/converse_transformation.py litellm/types/llms/openai.py litellm/types/llms/bedrock.py litellm/litellm_core_utils/prompt_templates/factory.py tests/test_litellm/llms/bedrock/chat/test_converse_transformation.py tests/enterprise/litellm_enterprise/proxy/hooks/test_managed_files.py enterprise/litellm_enterprise/proxy/hooks/managed_files.pyNote
Medium Risk
Touches request/response translation on the Bedrock Converse path and proxy passthrough headers; behavior changes for tool messages with
search_resultsand citation-heavy assistant output, with regression tests but some risk of edge-case mismatches with provider payloads.Overview
Adds Bedrock Converse ↔ OpenAI chat bridging for RAG-style tool output and grounded replies.
Tool messages: Optional
search_resultson chat-completions tool messages maps to BedrocktoolResult.content[].searchResult(and setsstatus: success), preferring that path over normalcontentso mixed text/search blocks are avoided. Types gainSearchResultBlock/searchResulton tool result content.Assistant responses: Nova/Bedrock
citationsContentis converted to OpenAI-stylemessage.annotations(url_citationwith span indices, title, URL), with citation text merged intocontentwhen the model only returns punctuation in text blocks.ChatCompletionResponseMessageis typed forannotations.Proxy passthrough: Custom response headers are filtered with the same exclusion list as upstream headers so defaults like
content-length: 0cannot override real passthrough bodies.Regression tests cover search-result mapping, citations→annotations, passthrough headers, and managed-file ID scanning when user content includes Bedrock
toolResultblocks without OpenAItypefields (large formatting-only churn in enterprise managed-files tests).Reviewed by Cursor Bugbot for commit 65f8e21. Bugbot is set up for automated code reviews on this repo. Configure here.