diff --git a/litellm/llms/anthropic/experimental_pass_through/adapters/transformation.py b/litellm/llms/anthropic/experimental_pass_through/adapters/transformation.py index 69f451973b2..6fd439395d0 100644 --- a/litellm/llms/anthropic/experimental_pass_through/adapters/transformation.py +++ b/litellm/llms/anthropic/experimental_pass_through/adapters/transformation.py @@ -72,6 +72,9 @@ from litellm.llms.anthropic.common_utils import normalize_anthropic_tool_use_id from litellm.llms.anthropic.experimental_pass_through.context_management import ( PolyfillResult, ) +from litellm.llms.anthropic.experimental_pass_through.messages.transformation import ( + AnthropicMessagesConfig as AnthropicMessagesConfig, +) from litellm.types.llms.anthropic import ( ANTHROPIC_HOSTED_TOOLS, AllAnthropicPassThroughMessageValues, @@ -877,6 +880,8 @@ class LiteLLMAnthropicMessagesAdapter: system_content: Final = anthropic_message_request["system"] if not system_content: return + # Filter billing header from system when translate_anthropic_to_openai + system_content = AnthropicMessagesConfig._filter_billing_headers_from_system(system_content) # Handle system as string or array of content blocks if isinstance(system_content, str): new_messages.insert( diff --git a/tests/test_litellm/llms/anthropic/experimental_pass_through/adapters/test_anthropic_experimental_pass_through_adapters_transformation.py b/tests/test_litellm/llms/anthropic/experimental_pass_through/adapters/test_anthropic_experimental_pass_through_adapters_transformation.py index 9145829ecb2..150348d2aa0 100644 --- a/tests/test_litellm/llms/anthropic/experimental_pass_through/adapters/test_anthropic_experimental_pass_through_adapters_transformation.py +++ b/tests/test_litellm/llms/anthropic/experimental_pass_through/adapters/test_anthropic_experimental_pass_through_adapters_transformation.py @@ -3020,7 +3020,44 @@ def test_translate_anthropic_to_openai_with_mixed_tools(): # tool_name_mapping should be empty for short tool names assert tool_name_mapping == {} +BILLING_HEADER_BLOCK = { + "type": "text", + "text": "x-anthropic-billing-header: cc_version=1.0.abc; cc_entrypoint=cli; cch=00000;", +} + +def test_translate_anthropic_to_openai_fillter_billing_header(): + """ + Test that filter billing header + """ + from litellm.types.llms.anthropic import AnthropicMessagesRequest + anthropic_request = AnthropicMessagesRequest( + model="gemini-2.5-flash-lite", + max_tokens=4096, + messages=[ + { + "role": "user", + "content": "Get weather and search the web", + } + ], + system=[ + BILLING_HEADER_BLOCK, + {"type": "text", "text": "You are Claude Code, Anthropic's official CLI for Claude"}, + ] + ) + + adapter = LiteLLMAnthropicMessagesAdapter() + openai_request, tool_name_mapping = adapter.translate_anthropic_to_openai( + anthropic_message_request=anthropic_request + ) + + #openai_request should be {'model': 'gemini-2.5-flash-lite', 'messages': [{'role': 'system', 'content': [{'type': 'text', 'text': "You are Claude Code, Anthropic's official CLI for Claude"}]}, {'role': 'user', 'content': 'Get weather and search the web'}], 'max_tokens': 4096} + assert len(openai_request["messages"]) == 2 + system_texts = [block["text"] for block in openai_request["messages"][0]["content"]] + assert all(not t.startswith("x-anthropic-billing-header:") for t in system_texts) + assert "You are Claude Code, Anthropic's official CLI for Claude" in system_texts + assert "Get weather and search the web" == openai_request["messages"][1]["content"] + class TestTranslateAnthropicOutputFormatToOpenAI: """Tests for translate_anthropic_output_format_to_openai adding additionalProperties: false."""