From 0fecb4a222e9d36af9fb1efeabfb7b3632d00091 Mon Sep 17 00:00:00 2001 From: Mrun25 <165297479+Mrun25@users.noreply.github.com> Date: Sat, 17 Oct 2026 01:00:54 +0530 Subject: [PATCH] fix(proxy): keep server_tool_use in streamed usage cost injection (issue #46365) Added server tool usage tracking to usage report. --- litellm/proxy/common_request_processing.py | 4 ++++ .../proxy/test_common_request_processing.py | 23 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/litellm/proxy/common_request_processing.py b/litellm/proxy/common_request_processing.py index 5f798fce6cf..f7c44c7a922 100644 --- a/litellm/proxy/common_request_processing.py +++ b/litellm/proxy/common_request_processing.py @@ -4330,6 +4330,10 @@ class ProxyBaseLLMRequestProcessing: total_tokens=total_tokens, completion_tokens_details=usage.get("completion_tokens_details"), prompt_tokens_details=usage.get("prompt_tokens_details"), + # Built-in tool usage (e.g. Claude web search count) carries + # per-call fees priced by StandardBuiltInToolCostTracking; + # dropping it under-reports usage.cost (issue #46365). + server_tool_use=usage.get("server_tool_use"), ) @staticmethod diff --git a/tests/unit/proxy/test_common_request_processing.py b/tests/unit/proxy/test_common_request_processing.py index 1d197602543..6f3c9574206 100644 --- a/tests/unit/proxy/test_common_request_processing.py +++ b/tests/unit/proxy/test_common_request_processing.py @@ -8960,6 +8960,29 @@ class TestInjectCostIntoUsageDict: assert result["usage"]["cost"] == pytest.approx(expected_flex_cost) assert result["usage"]["cost"] < self._expected_cost("gpt-5-mini", 1000, 100) + def test_openai_chunk_usage_cost_includes_web_search_fee(self) -> None: + """Streamed usage.cost must include the Claude web search fee: server_tool_use survives the usage rebuild (issue #46365).""" + event: Final = { + "id": "chatcmpl-1", + "object": "chat.completion.chunk", + "choices": [], + "usage": { + "prompt_tokens": 10760, + "completion_tokens": 399, + "total_tokens": 11159, + "server_tool_use": {"web_search_requests": 1}, + }, + } + model: Final = "vertex_ai/claude-opus-5-5" + + result: Final = ProxyBaseLLMRequestProcessing._inject_cost_into_usage_dict(event, model) + + assert result is not None + token_cost: Final = self._expected_cost(model, 10760, 399) + search_fee: Final = litellm.model_cost[model]["search_context_cost_per_query"]["search_context_size_medium"] + assert result["usage"]["cost"] == pytest.approx(token_cost + search_fee) + assert result["usage"]["server_tool_use"] == {"web_search_requests": 1} + def test_openai_chunk_with_null_usage_is_not_modified(self): event = { "id": "chatcmpl-1",