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