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 164ba14b200..1e5bab7ea3c 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 @@ -277,7 +277,7 @@ class StandardBuiltInToolCostTracking: custom_llm_provider=custom_llm_provider or "openai", quality=quality if isinstance(quality, str) and quality != "auto" else None, n=1, - size=size if isinstance(size, str) else None, + size=size if isinstance(size, str) and size != "auto" else None, ) except Exception as e: # noqa: BLE001 # pricing helpers raise bare Exception for unmapped models; bill 0.0 verbose_logger.debug("Could not price Responses API image_generation_call item: %s", e) @@ -294,19 +294,20 @@ class StandardBuiltInToolCostTracking: model_info: Final = litellm.get_model_info( model=tool_model, custom_llm_provider=custom_llm_provider or "openai" ) - except Exception as e: # noqa: BLE001 # get_model_info raises bare Exception for unmapped models; bill 0.0 + except Exception as e: # noqa: BLE001 # get_model_info raises bare Exception for unmapped models; fall back verbose_logger.debug("Could not resolve pricing for image tool model %s: %s", tool_model, e) - return 0.0 + return None image_gen: Final = tool_usage.image_gen input_details: Final = image_gen.input_tokens_details output_details: Final = image_gen.output_tokens_details - return ( + token_cost: Final = ( (input_details.text_tokens if input_details else 0) * (model_info.get("input_cost_per_token") or 0) + (input_details.image_tokens if input_details else 0) * (model_info.get("input_cost_per_image_token") or 0) + (output_details.image_tokens if output_details else 0) * (model_info.get("output_cost_per_image_token") or 0) + (output_details.text_tokens if output_details else 0) * (model_info.get("output_cost_per_token") or 0) ) + return token_cost if token_cost > 0 else None @staticmethod def _handle_image_generation_cost(response_object: object, custom_llm_provider: str | None) -> float: 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 570b701450d..6d525bca654 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 @@ -990,3 +990,89 @@ def test_responses_zero_image_gen_tokens_fall_back_to_per_image_pricing(local_mo ) ) assert cost > 0 + + +def test_responses_auto_size_image_generation_call_billed_at_default_size(local_model_cost_map): + """An image_generation_call with size "auto" bills at the default size instead of erroring to $0.""" + from litellm.cost_calculator import default_image_cost_calculator + + response = _openai_responses_response( + "gpt-5", + [ + { + "type": "image_generation_call", + "id": "ig_1", + "status": "completed", + "quality": "high", + "size": "auto", + "result": "AAAA", + } + ], + ) + + cost = StandardBuiltInToolCostTracking.get_cost_for_built_in_tools( + model="gpt-5", + response_object=response, + usage=None, + custom_llm_provider="openai", + standard_built_in_tools_params=None, + ) + + assert cost == pytest.approx( + default_image_cost_calculator( + model="gpt-image-1", + custom_llm_provider="openai", + quality="high", + n=1, + size=None, + ) + ) + assert cost > 0 + + +def test_responses_image_gen_total_without_token_details_falls_back_to_per_image(local_model_cost_map): + """A positive image_gen total with no token details falls back to per-image pricing, not $0.""" + tool_model = "gpt-image-1" + quality = "low" + size = "1024x1024" + response = _openai_responses_response( + "gpt-5", + [ + { + "type": "image_generation_call", + "id": "ig_1", + "status": "completed", + "quality": quality, + "size": size, + "result": "AAAA", + } + ], + usage={ + "input_tokens": 10, + "output_tokens": 5, + "total_tokens": 15, + "tool_usage": {"image_gen": {"input_tokens": 5, "output_tokens": 5, "total_tokens": 10}}, + }, + tools=[{"type": "image_generation", "model": tool_model, "quality": quality, "size": size}], + ) + + cost = StandardBuiltInToolCostTracking.get_cost_for_built_in_tools( + model="gpt-5", + response_object=response, + usage=None, + custom_llm_provider="openai", + standard_built_in_tools_params=None, + ) + + from litellm.cost_calculator import default_image_cost_calculator + + assert cost == pytest.approx( + default_image_cost_calculator( + model=tool_model, + custom_llm_provider="openai", + quality=quality, + n=1, + size=size, + ) + ) + assert cost > 0