diff --git a/litellm/litellm_core_utils/llm_cost_calc/tool_call_cost_tracking.py b/litellm/litellm_core_utils/llm_cost_calc/tool_call_cost_tracking.py index 4454fca3b00..8da66d4600d 100644 --- a/litellm/litellm_core_utils/llm_cost_calc/tool_call_cost_tracking.py +++ b/litellm/litellm_core_utils/llm_cost_calc/tool_call_cost_tracking.py @@ -322,9 +322,8 @@ class StandardBuiltInToolCostTracking: ) if has_url_citations: return True - # Fallback: Check usage object for providers that use usage instead of annotations - # (e.g., Vertex AI Gemini uses usage.prompt_tokens_details.web_search_requests) if usage is not None: + # Vertex AI Gemini uses usage.prompt_tokens_details.web_search_requests if ( hasattr(usage, "prompt_tokens_details") and usage.prompt_tokens_details is not None @@ -335,6 +334,15 @@ class StandardBuiltInToolCostTracking: and usage.prompt_tokens_details.web_search_requests is not None ): return True + # 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 return False elif isinstance(response_object, ResponsesAPIResponse): # response api explicitly includes web_search_call in the output diff --git a/litellm/llms/__init__.py b/litellm/llms/__init__.py index c73f0b22b4b..710342bbc78 100644 --- a/litellm/llms/__init__.py +++ b/litellm/llms/__init__.py @@ -34,6 +34,18 @@ def get_cost_for_web_search_request( return get_cost_for_anthropic_web_search(model_info=model_info, usage=usage) elif custom_llm_provider.startswith("vertex_ai"): + # Anthropic Claude models on Vertex AI populate server_tool_use.web_search_requests + # (same as the direct Anthropic API), not prompt_tokens_details.web_search_requests + # (which is the Gemini field). Route claude-* models to the Anthropic calculator. + 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) + from .vertex_ai.gemini.cost_calculator import ( cost_per_web_search_request as cost_per_web_search_request_vertex_ai, )