From 011b8baeffaf4874f5b876998644b0156064d9f6 Mon Sep 17 00:00:00 2001 From: Tib <34336452+Tib-Gridello@users.noreply.github.com> Date: Thu, 12 Mar 2026 17:54:58 +0100 Subject: [PATCH] fix(openrouter): remove usage injection that causes TypeError on OpenAI SDK path (#22774) * fix(openrouter): remove usage injection that causes TypeError on OpenAI SDK path The transform_request method injected `usage: {include: true}` into the request dict for cost tracking (added in #9747). This causes a TypeError when the request routes through the OpenAI SDK path, as AsyncCompletions.create() does not accept `usage` as a keyword argument. OpenRouter now returns cost data (token counts, cost, cost_details) by default in all responses, making the usage request parameter unnecessary. Remove the injection and update tests to assert usage is not present. * fix: update stale comment referencing removed usage.include parameter --------- Co-authored-by: tibG --- litellm/llms/openrouter/chat/transformation.py | 7 +------ .../chat/test_openrouter_chat_transformation.py | 14 ++++++-------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/litellm/llms/openrouter/chat/transformation.py b/litellm/llms/openrouter/chat/transformation.py index e3770dbbf49..5320f212ddb 100644 --- a/litellm/llms/openrouter/chat/transformation.py +++ b/litellm/llms/openrouter/chat/transformation.py @@ -167,11 +167,6 @@ class OpenrouterConfig(OpenAIGPTConfig): ) response.update(extra_body) - # ALWAYS add usage parameter to get cost data from OpenRouter - # This ensures cost tracking works for all OpenRouter models - if "usage" not in response: - response["usage"] = {"include": True} - return response def transform_response( @@ -212,7 +207,7 @@ class OpenrouterConfig(OpenAIGPTConfig): ) # Extract cost from OpenRouter response body - # OpenRouter returns cost information in the usage object when usage.include=true + # OpenRouter returns cost information in the usage object by default try: response_json = raw_response.json() if "usage" in response_json and response_json["usage"]: diff --git a/tests/test_litellm/llms/openrouter/chat/test_openrouter_chat_transformation.py b/tests/test_litellm/llms/openrouter/chat/test_openrouter_chat_transformation.py index d5a73b3fd12..9e20b37a2a5 100644 --- a/tests/test_litellm/llms/openrouter/chat/test_openrouter_chat_transformation.py +++ b/tests/test_litellm/llms/openrouter/chat/test_openrouter_chat_transformation.py @@ -373,7 +373,7 @@ def test_openrouter_cost_tracking_non_streaming(): Test OpenRouter cost tracking for non-streaming completions. Verifies: - 1. Request includes usage.include=true to get cost data + 1. Request does NOT inject usage parameter (OpenRouter returns cost data by default) 2. Response extracts cost from usage.cost and stores in _hidden_params """ from unittest.mock import Mock, patch @@ -381,7 +381,7 @@ def test_openrouter_cost_tracking_non_streaming(): config = OpenrouterConfig() - # Test request adds usage parameter + # Verify request does NOT add usage parameter — OpenRouter returns cost by default transformed_request = config.transform_request( model="openrouter/anthropic/claude-sonnet-4.5", messages=[{"role": "user", "content": "Hello"}], @@ -389,8 +389,7 @@ def test_openrouter_cost_tracking_non_streaming(): litellm_params={}, headers={}, ) - assert "usage" in transformed_request - assert transformed_request["usage"] == {"include": True} + assert "usage" not in transformed_request # Test response extracts cost mock_response = Mock(spec=httpx.Response) @@ -434,13 +433,13 @@ def test_openrouter_cost_tracking_streaming(): Test OpenRouter cost tracking for streaming completions. Verifies: - 1. Request includes usage.include=true (same as non-streaming) + 1. Request does NOT inject usage parameter (OpenRouter returns cost data by default) 2. Streaming chunks preserve usage/cost data in the final chunk 3. Cost field is accessible in the usage object """ config = OpenrouterConfig() - # Test request adds usage parameter for streaming + # Verify request does NOT add usage parameter — OpenRouter returns cost by default transformed_request = config.transform_request( model="openrouter/anthropic/claude-sonnet-4.5", messages=[{"role": "user", "content": "Hello"}], @@ -448,8 +447,7 @@ def test_openrouter_cost_tracking_streaming(): litellm_params={}, headers={}, ) - assert "usage" in transformed_request - assert transformed_request["usage"] == {"include": True} + assert "usage" not in transformed_request # Test streaming chunks preserve cost data handler = OpenRouterChatCompletionStreamingHandler(