fix(anthropic): json response_format + user tools non-streaming - #26222
Conversation
…on-streaming Non-streaming path required len(tool_calls)==1 to unwrap json_tool_call, so mixed user tools leaked the internal tool. Align with Bedrock converse handling: strip internal tools, merge structured JSON into content. Made-with: Cursor
Greptile SummaryThis PR fixes a bug where non-streaming Anthropic calls using Confidence Score: 4/5Safe to merge after addressing the silent content-drop edge case in the mixed-mode branch. The core fix is correct and well-tested. One P2 finding (silent JSON drop when litellm/llms/anthropic/chat/transformation.py — mixed-mode branch in
|
| Filename | Overview |
|---|---|
| litellm/llms/anthropic/chat/transformation.py | Adds _resolve_json_mode_non_streaming to correctly strip internal json_tool_call entries in both pure and mixed-tool responses; replaces the old len==1 guard. Minor: silent content drop when arguments is None in the mixed-mode branch, and _transform_response_for_json_mode becomes dead code. |
| tests/test_litellm/llms/anthropic/chat/test_anthropic_chat_transformation.py | Adds a focused unit test for the mixed internal+user tool case; uses only mocks, no real network calls, consistent with the test folder's rules. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[transform_parsed_response] --> B[extract_response_content\ntool_calls, text_content]
B --> C[_resolve_json_mode_non_streaming]
C --> D{json_mode=True\n& tool_calls?}
D -- No --> E[return None, tool_calls, None\nno-op pass-through]
D -- Yes --> F{any RESPONSE_FORMAT_TOOL_NAME\nin tool_calls?}
F -- No --> E
F -- Yes --> G{all tool_calls\nare internal json tools?}
G -- Yes --> H[_convert_tool_response_to_message\nreturn json_message, empty, None]
G -- No: mixed --> I[_convert_tool_response_to_message\nfor first json tool only]
I --> J[return None, filtered_user_tools, extra_content]
H --> K[json_mode_message != None\nset stop_reason=stop\n_message = json_mode_message]
J --> L[merged_text = text_content + extra_content\n_message with filtered tool_calls]
E --> M[litellm.Message with original\ntool_calls + text_content]
Reviews (3): Last reviewed commit: "Fix black formatting" | Re-trigger Greptile
|
| GitGuardian id | GitGuardian status | Secret | Commit | Filename | |
|---|---|---|---|---|---|
| 31539530 | Triggered | Generic Password | 9d58e6e | .github/workflows/_test-unit-services-base.yml | View secret |
| 29203053 | Triggered | Generic Password | 9d58e6e | .circleci/config.yml | View secret |
🛠 Guidelines to remediate hardcoded secrets
- Understand the implications of revoking this secret by investigating where it is used in your code.
- Replace and store your secrets safely. Learn here the best practices.
- Revoke and rotate these secrets.
- If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.
To avoid such incidents in the future consider
- following these best practices for managing and storing secrets including API keys and other credentials
- install secret detection on pre-commit to catch secret before it leaves your machine and ease remediation.
🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.
Low: Response transformation refactor for Anthropic JSON modeThis PR refactors how non-streaming Anthropic responses handle the internal Status: 0 open Posted by Veria AI · 2026-04-24T03:39:40.104Z |
Low: No security issuesThis PR refactors Anthropic response transformation logic to handle JSON mode with mixed internal and user tool calls in non-streaming scenarios. The changes are confined to the SDK translation layer and involve only response data mapping between Anthropic and OpenAI formats. No auth, input validation, database, or file system code is affected. Status: 0 open Posted by Veria AI · 2026-04-24T03:42:58.114Z |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
@Sameerlite Can you see if the Greptile comment is applicable? |
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit f503c06. Configure here.
The first comment is wrong -- it's still called in tests, but that test doesn't need to use it since it's just wrapper. I'll commit the cleanup I think the second comment is a silly minor human readability thing. It's whatever |
The wrapper had no production callers after transform_parsed_response was refactored to call _resolve_json_mode_non_streaming directly. Updated the parametrized test to call the underlying method.
…mode-nonstreaming-mixed-tools fix(anthropic): json response_format + user tools non-streaming
Summary
Direct Anthropic non-streaming chat with
response_formatinjects an internaljson_tool_calltool. The previous path only unwrapped it whenlen(tool_calls) == 1, so responses that also included real user tools surfacedjson_tool_callto the client.Change
_resolve_json_mode_non_streaming(same cases as Bedrock_filter_json_mode_tools): internal-only, mixed, or none.transform_parsed_responsemerges structured JSON intocontentand uses filteredtool_callswhen mixed.Note
Medium Risk
Touches Anthropic response parsing for
json_modeandtool_calls, which can subtly change client-visible output formatting, but scope is limited and covered by a targeted regression test.Overview
Fixes non-streaming Anthropic
response_formathandling when the provider injects an internaljson_tool_callalongside real user tool calls.Adds
_resolve_json_mode_non_streamingto strip internalRESPONSE_FORMAT_TOOL_NAMEtool calls, returning either a full replacement message (when only internal JSON tools are present) or merged JSON payload appended intocontentwhile preserving only user tool calls.transform_parsed_responsenow uses this to avoid leaking the internal tool call to clients and to keep structured JSON available inmessage.content.Includes a regression test covering the mixed internal+user tool case.
Reviewed by Cursor Bugbot for commit f503c06. Bugbot is set up for automated code reviews on this repo. Configure here.