Litellm dev 04 02 2026 p1 - #25052
Conversation
The ModelResponse branch in response_object_includes_web_search_call() only checked url_citation annotations and prompt_tokens_details, missing Anthropic's server_tool_use.web_search_requests field. This caused _handle_web_search_cost() to never fire for Anthropic Claude models. Also routes vertex_ai/claude-* models to the Anthropic cost calculator instead of the Gemini one, since Claude on Vertex uses the same server_tool_use billing structure as the direct Anthropic API. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
|
Greptile SummaryThis PR fixes web search cost tracking for Anthropic Claude models — both direct API and via Vertex AI — by ensuring the Key changes:
Issues found:
Confidence Score: 4/5Safe to merge after adding unit tests for the new ModelResponse/Vertex AI Claude paths; no security or data-integrity risks. The core logic change is small and targeted. The main blocker is the absence of tests for the two new branches (ModelResponse + server_tool_use, and vertex_ai/claude routing), which the CLAUDE.md rules treat as a hard requirement. The
|
| Filename | Overview |
|---|---|
| litellm/litellm_core_utils/llm_cost_calc/tool_call_cost_tracking.py | Adds server_tool_use.web_search_requests detection inside the ModelResponse branch; logic is correct but guard should be > 0 to avoid false positives when web_search_requests == 0 |
| litellm/llms/init.py | Routes Vertex AI Claude models to the Anthropic web-search cost calculator via model_info["key"]; logic is sound but uses an inline import and has no dedicated unit test for the new routing branch |
| tests/llm_translation/test_azure_openai.py | Replaces real Azure endpoint hostnames with generic fake-azure-endpoint.invalid placeholder — good privacy improvement, no logic changes |
| docs/my-website/docs/providers/azure/azure_responses.md | Sanitizes real Azure endpoint URL in documentation examples — purely cosmetic improvement |
| docs/my-website/docs/proxy/team_model_add.md | Sanitizes real Azure endpoint URL in documentation examples — purely cosmetic improvement |
| docs/my-website/docs/realtime.md | Sanitizes real Azure endpoint URL in commented-out documentation example — purely cosmetic improvement |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[response_object_includes_web_search_call] --> B{ModelResponse?}
B -- Yes --> C{has url_citation annotations?}
C -- Yes --> D[return True]
C -- No --> E{usage != None?}
E -- Yes --> F{prompt_tokens_details.web_search_requests != None? Vertex AI Gemini}
F -- Yes --> D
F -- No --> G{server_tool_use.web_search_requests != None? NEW: Anthropic / Vertex AI Claude}
G -- Yes --> D
G -- No --> H[return False]
E -- No --> H
B -- No: ResponsesAPIResponse --> I[check output_type == web_search_call]
B -- No: other --> J{usage != None?}
J -- Yes --> K{server_tool_use or prompt_tokens_details?}
K -- Yes --> D
K -- No --> H
J -- No --> H
D --> L[get_cost_for_web_search_request]
L --> M{custom_llm_provider?}
M -- anthropic --> N[get_cost_for_anthropic_web_search]
M -- vertex_ai + claude in key --> N
M -- vertex_ai + not claude --> O[cost_per_web_search_request_vertex_ai Gemini calculator]
M -- gemini --> P[cost_per_web_search_request Gemini calculator]
Comments Outside Diff (1)
-
tests/test_litellm/litellm_core_utils/llm_cost_calc/test_tool_call_cost_tracking.py, line 122-139 (link)New
ModelResponsebranch for Anthropic/Vertex AI Claude has no test coverageThis PR adds two new code paths to
response_object_includes_web_search_call:isinstance(response_object, ModelResponse)→ newserver_tool_usecheck (tool_call_cost_tracking.pylines 337–345)custom_llm_provider.startswith("vertex_ai")+"claude" in model_key→ route to Anthropic calculator (llms/__init__.pylines 40–47)
The existing
test_get_cost_for_anthropic_web_searchpassesresponse_object=None, so it exercises theelif usage is not Nonefallback path that already existed — not the newisinstance(response_object, ModelResponse)branch. The Vertex AI / Claude routing path also has no corresponding test.Per CLAUDE.md: "Always add tests when adding new entity types or features — if the existing test file covers other entity types, add corresponding tests for the new one."
A minimal test to cover both gaps would look like:
def test_response_object_includes_web_search_call_anthropic_model_response(): from litellm.types.utils import Choices, Message, ServerToolUse, Usage response = ModelResponse( id="test-id", choices=[ Choices(finish_reason="stop", index=0, message=Message(content="Hi", role="assistant")) ], created=1234567890, model="claude-3-5-sonnet-20241022", object="chat.completion", ) usage = Usage(server_tool_use=ServerToolUse(web_search_requests=1)) assert StandardBuiltInToolCostTracking.response_object_includes_web_search_call( response, usage ) is True def test_get_cost_for_vertex_ai_claude_web_search(): from litellm.types.utils import ServerToolUse, Usage usage = Usage(server_tool_use=ServerToolUse(web_search_requests=1)) cost = StandardBuiltInToolCostTracking.get_cost_for_built_in_tools( model="claude-3-5-sonnet-v2@20241022", usage=usage, response_object=None, standard_built_in_tools_params=None, custom_llm_provider="vertex_ai", ) assert cost > 0.0
Reviews (1): Last reviewed commit: "fix: Anthropic web search cost not track..." | Re-trigger Greptile
| # Anthropic Claude (direct API and Vertex AI) uses server_tool_use.web_search_requests. | ||
| # Without this check, Claude ModelResponse always falls through to return False | ||
| # and _handle_web_search_cost() is never called. | ||
| if ( | ||
| hasattr(usage, "server_tool_use") | ||
| and usage.server_tool_use is not None | ||
| and usage.server_tool_use.web_search_requests is not None | ||
| ): | ||
| return True |
There was a problem hiding this comment.
web_search_requests == 0 triggers web-search cost path unnecessarily
The guard usage.server_tool_use.web_search_requests is not None returns True even when web_search_requests == 0 (zero actual searches). This causes response_object_includes_web_search_call to report True for a response that contained no web searches at all.
While downstream get_cost_for_anthropic_web_search would compute 0 * rate = 0.0, returning True here is semantically incorrect and mirrors a pre-existing gap also present in the Gemini branch. The parallel Gemini check (prompt_tokens_details.web_search_requests is not None) has the same issue, so at minimum be consistent — but consider tightening both to > 0:
| # Anthropic Claude (direct API and Vertex AI) uses server_tool_use.web_search_requests. | |
| # Without this check, Claude ModelResponse always falls through to return False | |
| # and _handle_web_search_cost() is never called. | |
| if ( | |
| hasattr(usage, "server_tool_use") | |
| and usage.server_tool_use is not None | |
| and usage.server_tool_use.web_search_requests is not None | |
| ): | |
| return True | |
| if ( | |
| hasattr(usage, "server_tool_use") | |
| and usage.server_tool_use is not None | |
| and usage.server_tool_use.web_search_requests is not None | |
| and usage.server_tool_use.web_search_requests > 0 | |
| ): | |
| return True |
Evidence: test_anthropic_chat_transformation.py line 1208 explicitly asserts usage.server_tool_use.web_search_requests == 0 as a valid server-returned value.
| model_key: str = model_info.get("key", "") if model_info else "" | ||
| if "claude" in model_key.lower(): | ||
| from .anthropic.cost_calculation import get_cost_for_anthropic_web_search | ||
|
|
||
| verbose_logger.debug( | ||
| "vertex_ai/claude model detected — routing web search cost to Anthropic calculator" | ||
| ) | ||
| return get_cost_for_anthropic_web_search(model_info=model_info, usage=usage) |
There was a problem hiding this comment.
Inline import inside function body
The new import at line 42 (from .anthropic.cost_calculation import get_cost_for_anthropic_web_search) is placed inside the function body. CLAUDE.md requires module-level imports unless inline placement is strictly required to avoid a circular import.
All other branches in this file already follow the same inline-import pattern, so this is consistent — but if refactoring ever brings the imports to module-level, this new one should move too. Consider a top-of-file import with a conditional guard, or document the circular-import reason in a comment, so future contributors know the inline pattern is intentional.
Context Used: CLAUDE.md (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
0caf4c4
into
litellm_oss_staging_04_02_2026_p1
* fix: replace hardcoded url * fix: Anthropic web search cost not tracked for Chat Completions The ModelResponse branch in response_object_includes_web_search_call() only checked url_citation annotations and prompt_tokens_details, missing Anthropic's server_tool_use.web_search_requests field. This caused _handle_web_search_cost() to never fire for Anthropic Claude models. Also routes vertex_ai/claude-* models to the Anthropic cost calculator instead of the Gemini one, since Claude on Vertex uses the same server_tool_use billing structure as the direct Anthropic API. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix(vertex_ai): support pluggable (executable) credential_source for WIF auth (#24700) The WIF credential dispatch in load_auth() only handled identity_pool and aws credential types. When credential_source.executable was present (used for Azure Managed Identity via Workload Identity Federation), it fell through to identity_pool.Credentials which rejected it with MalformedError. Add dispatch to google.auth.pluggable.Credentials for executable-type credential sources, following the same pattern as the existing identity_pool and aws helpers. Fixes authentication for Azure Container Apps → GCP Vertex AI via WIF with executable credential sources. * feat(logging): add component and logger fields to JSON logs for 3rd p… (#24447) * feat(logging): add component and logger fields to JSON logs for 3rd party filtering * Let user-supplied extra fields win over auto-generated component/logger, tighten test assertions * Feat - Add organization into the metrics metadata for org_id & org_alias (#24440) * Add org_id and org_alias label names to Prometheus metric definitions * Add user_api_key_org_alias to StandardLoggingUserAPIKeyMetadata * Populate user_api_key_org_alias in pre-call metadata * Pass org_id and org_alias into per-request Prometheus metric labels * Add test for org labels on per-request Prometheus metrics * chore: resolve test mockdata * Address review: populate org_alias from DB view, add feature flag, use .get() for org metadata * Add org labels to failure path and verify flag behavior in test * Fix test: build flag-off enum_values without org fields * Gate org labels behind feature flag in get_labels() instead of static metric lists * Scope org label injection to metrics that carry team context, remove orphaned budget label defs, add test teardown * Use explicit metric allowlist for org label injection instead of team heuristic * Fix duplicate org label guard, move _org_label_metrics to class constant * Reset custom_prometheus_metadata_labels after duplicate label assertion * fix: emit org labels by default, remove flag, fix missing org_alias in all metadata paths * fix: emit org labels by default, no opt-in flag required * fix: write org_alias to metadata unconditionally in proxy_server.py * fix: 429s from batch creation being converted to 500 (#24703) * add us gov models (#24660) * add us gov models * added max tokens * Litellm dev 04 02 2026 p1 (#25052) * fix: replace hardcoded url * fix: Anthropic web search cost not tracked for Chat Completions The ModelResponse branch in response_object_includes_web_search_call() only checked url_citation annotations and prompt_tokens_details, missing Anthropic's server_tool_use.web_search_requests field. This caused _handle_web_search_cost() to never fire for Anthropic Claude models. Also routes vertex_ai/claude-* models to the Anthropic cost calculator instead of the Gemini one, since Claude on Vertex uses the same server_tool_use billing structure as the direct Anthropic API. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix(anthropic): pass logging_obj to client.post for litellm_overhead_time_ms (#24071) When LITELLM_DETAILED_TIMING=true, litellm_overhead_time_ms was null for Anthropic because the handler did not pass logging_obj to client.post(), so track_llm_api_timing could not set llm_api_duration_ms. Pass logging_obj=logging_obj at all four post() call sites (make_call, make_sync_call, acompletion, completion). Add test to ensure make_call passes logging_obj to client.post. Made-with: Cursor * sap - add additional parameters for grounding - additional parameter for grounding added for the sap provider * sap - fix models * (sap) add filtering, masking, translation SAP GEN AI Hub modules * (sap) add tests and docs for new SAP modules * (sap) add support of multiple modules config * (sap) code refactoring * (sap) rename file * test(): add safeguard tests * (sap) update tests * (sap) update docs, solve merge conflict in transformation.py * (sap) linter fix * (sap) Align embedding request transformation with current API * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) mock commit * (sap) run black formater * (sap) add literals to models, add negative tests, fix test for tool transformation * (sap) fix formating * (sap) fix models * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) commit for rerun bot review * (sap) minor improve * (sap) fix after bot review * (sap) lint fix * docs(sap): update documentation * fix(sap): change creds priority * fix(sap): change creds priority * fix(sap): fix sap creds unit test * fix(sap): linter fix * fix(sap): linter fix * linter fix * (sap) update logic of fetching creds, add additional tests * (sap) clean up code * (sap) fix after review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) add a possibility to put the service key by both variants * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) update test * (sap) update service key resolve function * (sap) run black formater * (sap) fix validate credentials, add negative tests for credential fetching * (sap) fix validate credentials, add negative tests for credential fetching * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) lint fix * (sap) lint fix * feat: support service_tier in gemini * chore: add a service_tier field mapping from openai to gemini * fix: use x-gemini-service-tier header in response * docs: add service_tier to gemini docs * chore: add defaut/standard mapping, and some tests * chore: tidying up some case insensitivity * chore: remove unnecessary guard * fix: remove redundant test file * fix: handle 'auto' case-insensitively * fix: return service_tier on final steamed chunk * chore: black * feat: enable supports_service_tier to gemini models * Fix get_standard_logging_metadata tests * Fix test_get_model_info_bedrock_models * Fix test_get_model_info_bedrock_models * Fix remaining tests * Fix mypy issues * Fix tests * Fix merge conflicts * Fix code qa * Fix code qa * Fix code qa * Fix greptile review --------- Co-authored-by: michelligabriele <gabriele.michelli@icloud.com> Co-authored-by: Josh <36064836+J-Byron@users.noreply.github.com> Co-authored-by: mubashir1osmani <mubashir.osmani777@gmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: milan-berri <milan@berri.ai> Co-authored-by: Alperen Kömürcü <alperen.koemuercue@sap.com> Co-authored-by: Vasilisa Parshikova <vasilisa.parshikova@sap.com> Co-authored-by: Lin Xu <lin.xu03@sap.com> Co-authored-by: Mark McDonald <macd@google.com> Co-authored-by: Sameer Kankute <sameer@berri.ai>
* fix(vertex_ai): support pluggable (executable) credential_source for WIF auth (BerriAI#24700) The WIF credential dispatch in load_auth() only handled identity_pool and aws credential types. When credential_source.executable was present (used for Azure Managed Identity via Workload Identity Federation), it fell through to identity_pool.Credentials which rejected it with MalformedError. Add dispatch to google.auth.pluggable.Credentials for executable-type credential sources, following the same pattern as the existing identity_pool and aws helpers. Fixes authentication for Azure Container Apps → GCP Vertex AI via WIF with executable credential sources. * feat(logging): add component and logger fields to JSON logs for 3rd p… (BerriAI#24447) * feat(logging): add component and logger fields to JSON logs for 3rd party filtering * Let user-supplied extra fields win over auto-generated component/logger, tighten test assertions * Feat - Add organization into the metrics metadata for org_id & org_alias (BerriAI#24440) * Add org_id and org_alias label names to Prometheus metric definitions * Add user_api_key_org_alias to StandardLoggingUserAPIKeyMetadata * Populate user_api_key_org_alias in pre-call metadata * Pass org_id and org_alias into per-request Prometheus metric labels * Add test for org labels on per-request Prometheus metrics * chore: resolve test mockdata * Address review: populate org_alias from DB view, add feature flag, use .get() for org metadata * Add org labels to failure path and verify flag behavior in test * Fix test: build flag-off enum_values without org fields * Gate org labels behind feature flag in get_labels() instead of static metric lists * Scope org label injection to metrics that carry team context, remove orphaned budget label defs, add test teardown * Use explicit metric allowlist for org label injection instead of team heuristic * Fix duplicate org label guard, move _org_label_metrics to class constant * Reset custom_prometheus_metadata_labels after duplicate label assertion * fix: emit org labels by default, remove flag, fix missing org_alias in all metadata paths * fix: emit org labels by default, no opt-in flag required * fix: write org_alias to metadata unconditionally in proxy_server.py * fix: 429s from batch creation being converted to 500 (BerriAI#24703) * add us gov models (BerriAI#24660) * add us gov models * added max tokens * Litellm dev 04 02 2026 p1 (BerriAI#25052) * fix: replace hardcoded url * fix: Anthropic web search cost not tracked for Chat Completions The ModelResponse branch in response_object_includes_web_search_call() only checked url_citation annotations and prompt_tokens_details, missing Anthropic's server_tool_use.web_search_requests field. This caused _handle_web_search_cost() to never fire for Anthropic Claude models. Also routes vertex_ai/claude-* models to the Anthropic cost calculator instead of the Gemini one, since Claude on Vertex uses the same server_tool_use billing structure as the direct Anthropic API. --------- * fix(anthropic): pass logging_obj to client.post for litellm_overhead_time_ms (BerriAI#24071) When LITELLM_DETAILED_TIMING=true, litellm_overhead_time_ms was null for Anthropic because the handler did not pass logging_obj to client.post(), so track_llm_api_timing could not set llm_api_duration_ms. Pass logging_obj=logging_obj at all four post() call sites (make_call, make_sync_call, acompletion, completion). Add test to ensure make_call passes logging_obj to client.post. Made-with: Cursor * sap - add additional parameters for grounding - additional parameter for grounding added for the sap provider * sap - fix models * (sap) add filtering, masking, translation SAP GEN AI Hub modules * (sap) add tests and docs for new SAP modules * (sap) add support of multiple modules config * (sap) code refactoring * (sap) rename file * test(): add safeguard tests * (sap) update tests * (sap) update docs, solve merge conflict in transformation.py * (sap) linter fix * (sap) Align embedding request transformation with current API * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) mock commit * (sap) run black formater * (sap) add literals to models, add negative tests, fix test for tool transformation * (sap) fix formating * (sap) fix models * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) commit for rerun bot review * (sap) minor improve * (sap) fix after bot review * (sap) lint fix * docs(sap): update documentation * fix(sap): change creds priority * fix(sap): change creds priority * fix(sap): fix sap creds unit test * fix(sap): linter fix * fix(sap): linter fix * linter fix * (sap) update logic of fetching creds, add additional tests * (sap) clean up code * (sap) fix after review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) add a possibility to put the service key by both variants * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) update test * (sap) update service key resolve function * (sap) run black formater * (sap) fix validate credentials, add negative tests for credential fetching * (sap) fix validate credentials, add negative tests for credential fetching * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) fix after bot review * (sap) lint fix * (sap) lint fix * feat: support service_tier in gemini * chore: add a service_tier field mapping from openai to gemini * fix: use x-gemini-service-tier header in response * docs: add service_tier to gemini docs * chore: add defaut/standard mapping, and some tests * chore: tidying up some case insensitivity * chore: remove unnecessary guard * fix: remove redundant test file * fix: handle 'auto' case-insensitively * fix: return service_tier on final steamed chunk * chore: black * feat: enable supports_service_tier to gemini models * Fix get_standard_logging_metadata tests * Fix test_get_model_info_bedrock_models * Fix test_get_model_info_bedrock_models * Fix remaining tests * Fix mypy issues * Fix tests * Fix merge conflicts * Fix code qa * Fix code qa * Fix code qa * Fix greptile review --------- Co-authored-by: michelligabriele <gabriele.michelli@icloud.com> Co-authored-by: Josh <36064836+J-Byron@users.noreply.github.com> Co-authored-by: mubashir1osmani <mubashir.osmani777@gmail.com> Co-authored-by: milan-berri <milan@berri.ai> Co-authored-by: Alperen Kömürcü <alperen.koemuercue@sap.com> Co-authored-by: Vasilisa Parshikova <vasilisa.parshikova@sap.com> Co-authored-by: Lin Xu <lin.xu03@sap.com> Co-authored-by: Mark McDonald <macd@google.com> Co-authored-by: Sameer Kankute <sameer@berri.ai>
Relevant issues
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
tests/test_litellm/directory, Adding at least 1 test is a hard requirement - see detailsmake test-unit@greptileaiand received a Confidence Score of at least 4/5 before requesting a maintainer reviewDelays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
CI (LiteLLM team)
Branch creation CI run
Link:
CI run for the last commit
Link:
Merge / cherry-pick CI run
Links:
Type
🆕 New Feature
🐛 Bug Fix
🧹 Refactoring
📖 Documentation
🚄 Infrastructure
✅ Test
Changes