From 48595175f4f1f34dee0301647ba5c928c78254a2 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 08:23:04 +0000 Subject: [PATCH] fix(cost): treat null responses output as empty in built-in tool cost tracking --- .../llm_cost_calc/tool_call_cost_tracking.py | 2 +- .../test_tool_call_cost_tracking.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) 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 221b1ae6eab..84bb494d574 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 @@ -428,7 +428,7 @@ class StandardBuiltInToolCostTracking: Returns: True if the ResponsesAPIResponse includes one of the specified output types, False otherwise. """ - output = response_object.output + output = response_object.output or [] for output_item in output: _output_type: Optional[str] = getattr(output_item, "type", None) if _output_type == output_type: diff --git a/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_tool_call_cost_tracking.py b/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_tool_call_cost_tracking.py index 24fd3c94ee3..4e0fb2b86ea 100644 --- a/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_tool_call_cost_tracking.py +++ b/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_tool_call_cost_tracking.py @@ -142,6 +142,22 @@ def test_get_cost_for_anthropic_web_search(): assert cost > 0.0 +def test_response_includes_output_type_with_null_output(): + """ + Regression test for https://github.com/BerriAI/litellm/issues/34754 + + Providers may send `response.output: null`, which used to make cost tracking raise + `TypeError: 'NoneType' object is not iterable` and drop the response cost. + """ + from litellm.types.llms.openai import ResponsesAPIResponse + + response = ResponsesAPIResponse.model_construct(id="resp_1", output=None) + + assert not StandardBuiltInToolCostTracking.response_includes_output_type( + response_object=response, output_type="web_search_call" + ) + + def test_get_cost_for_anthropic_web_search_with_server_tool_use_dict(): """ Anthropic-compatible passthrough responses can construct Usage from a raw