From 8a051733cbd46133343a5bf40869c0044fcf4420 Mon Sep 17 00:00:00 2001 From: Naineel Soyantar Date: Mon, 7 Sep 2026 10:04:37 +0530 Subject: [PATCH] fix(openrouter): track Responses API stream cost --- .../openrouter/responses/transformation.py | 31 ++++++++++++++++++- ...est_openrouter_responses_transformation.py | 30 ++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/litellm/llms/openrouter/responses/transformation.py b/litellm/llms/openrouter/responses/transformation.py index 30303ac0e4c..5c3b0a2ab3e 100644 --- a/litellm/llms/openrouter/responses/transformation.py +++ b/litellm/llms/openrouter/responses/transformation.py @@ -15,7 +15,12 @@ import httpx import litellm from litellm.llms.openai.responses.transformation import OpenAIResponsesAPIConfig from litellm.secret_managers.main import get_secret_str -from litellm.types.llms.openai import ResponseInputParam, ResponsesAPIResponse +from litellm.types.llms.openai import ( + ResponseCompletedEvent, + ResponseInputParam, + ResponsesAPIResponse, + ResponsesAPIStreamingResponse, +) from litellm.types.router import GenericLiteLLMParams from litellm.types.utils import LlmProviders @@ -143,3 +148,27 @@ class OpenRouterResponsesAPIConfig(OpenAIResponsesAPIConfig): pass return response + + def transform_streaming_response( + self, + model: str, + parsed_chunk: dict, + logging_obj: Any, + ) -> ResponsesAPIStreamingResponse: + response_event: Final = super().transform_streaming_response( + model=model, + parsed_chunk=parsed_chunk, + logging_obj=logging_obj, + ) + if not isinstance(response_event, ResponseCompletedEvent): + return response_event + + usage: Final = response_event.response.usage + if usage is None or usage.cost is None: + return response_event + + response_event.response._hidden_params["additional_headers"] = { + **response_event.response._hidden_params.get("additional_headers", {}), + "llm_provider-x-litellm-response-cost": float(usage.cost), + } + return response_event diff --git a/tests/test_litellm/llms/openrouter/responses/test_openrouter_responses_transformation.py b/tests/test_litellm/llms/openrouter/responses/test_openrouter_responses_transformation.py index cb577686a09..43424e9e5f8 100644 --- a/tests/test_litellm/llms/openrouter/responses/test_openrouter_responses_transformation.py +++ b/tests/test_litellm/llms/openrouter/responses/test_openrouter_responses_transformation.py @@ -186,6 +186,36 @@ class TestOpenRouterResponsesAPICostTracking: "additional_headers", {} ) + def test_transform_streaming_response_extracts_completed_response_cost(self): + config = OpenRouterResponsesAPIConfig() + + result = config.transform_streaming_response( + model="openai/gpt-5-mini", + parsed_chunk={ + "type": "response.completed", + "response": { + "id": "resp_abc123", + "object": "response", + "created_at": 1700000000, + "status": "completed", + "model": "openai/gpt-5-mini", + "output": [], + "usage": { + "input_tokens": 100, + "output_tokens": 50, + "total_tokens": 150, + "cost": 0.01234, + }, + }, + }, + logging_obj=Mock(), + ) + + assert ( + result.response._hidden_params["additional_headers"]["llm_provider-x-litellm-response-cost"] + == 0.01234 + ) + class TestOpenRouterResponsesAPIRegistration: """Test that OpenRouter is properly registered as a native Responses API provider."""