From 8aed3497b3f7a30d03dab492a6a78c02361cc267 Mon Sep 17 00:00:00 2001 From: hasnaat Date: Thu, 2 Jul 2026 00:40:52 +0500 Subject: [PATCH 01/12] fix(anthropic-pass-through): drop context_management for haiku on vertex_ai/bedrock when drop_params=True Signed-off-by: Hasnaat Hussain --- .../messages/handler.py | 23 +++++++++++++++++ ...erimental_pass_through_messages_handler.py | 25 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/litellm/llms/anthropic/experimental_pass_through/messages/handler.py b/litellm/llms/anthropic/experimental_pass_through/messages/handler.py index b82903d6f87..33c76556798 100644 --- a/litellm/llms/anthropic/experimental_pass_through/messages/handler.py +++ b/litellm/llms/anthropic/experimental_pass_through/messages/handler.py @@ -640,6 +640,29 @@ def anthropic_messages_handler( custom_llm_provider=custom_llm_provider, ) ) + + should_drop_params = ( + litellm.drop_params is True + or getattr(litellm_params, "drop_params", None) is True + or kwargs.get("drop_params") is True + ) + + if should_drop_params: + from litellm.llms.anthropic.chat.transformation import AnthropicConfig + from litellm.utils import _should_drop_param + + additional_drop_params = kwargs.get("additional_drop_params") or [] + for k in list(anthropic_messages_optional_request_params.keys()): + if _should_drop_param(k, additional_drop_params): + anthropic_messages_optional_request_params.pop(k, None) + + if not AnthropicConfig._model_supports_effort_param(model): + anthropic_messages_optional_request_params.pop("output_config", None) + if not AnthropicConfig._supports_model_capability(model, "supports_reasoning"): + anthropic_messages_optional_request_params.pop("thinking", None) + if "context_management" in anthropic_messages_optional_request_params: + if custom_llm_provider in ["vertex_ai", "bedrock"] and "haiku" in model.lower(): + anthropic_messages_optional_request_params.pop("context_management", None) if is_reasoning_auto_summary_enabled(): thinking_param: Final = anthropic_messages_optional_request_params.get("thinking") if isinstance(thinking_param, dict) and thinking_param.get("type") != "disabled": diff --git a/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py b/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py index e819433c269..d8121d47bcb 100644 --- a/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py +++ b/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py @@ -1390,3 +1390,28 @@ async def test_anthropic_messages_leaves_non_provider_failures_unmapped(): ) assert "Traceback" not in str(excinfo.value) +@pytest.mark.asyncio +async def test_anthropic_pass_through_drop_params(monkeypatch): + from unittest.mock import AsyncMock + from litellm.llms.anthropic.experimental_pass_through.messages.handler import ( + anthropic_messages_handler, + ) + + mock_handler = AsyncMock() + from litellm.llms.anthropic.experimental_pass_through.messages import handler + + monkeypatch.setattr(handler.base_llm_http_handler, "anthropic_messages_handler", mock_handler) + + # Test automatic dropping of context_management for haiku on vertex_ai + await anthropic_messages_handler( + model="vertex_ai/claude-3-haiku-20240307", + messages=[{"role": "user", "content": "hello"}], + max_tokens=64, + drop_params=True, + context_management={"edits": []}, + custom_llm_provider="vertex_ai", + is_async=True, + ) + called_kwargs = mock_handler.call_args.kwargs + optional_params = called_kwargs.get("anthropic_messages_optional_request_params", {}) + assert "context_management" not in optional_params From 41d3072817e38fe50840bbc4c4c4dab63ac35eeb Mon Sep 17 00:00:00 2001 From: hasnaat Date: Thu, 2 Jul 2026 17:23:40 +0500 Subject: [PATCH 02/12] trigger CI after base branch re-targeting Signed-off-by: Hasnaat Hussain From 3cabf7fa13c708681d1bf854898631bfdece4f8d Mon Sep 17 00:00:00 2001 From: hasnaat Date: Thu, 2 Jul 2026 17:34:34 +0500 Subject: [PATCH 03/12] refactor(anthropic-pass-through): extract drop parameter logic to reduce McCabe complexity Signed-off-by: Hasnaat Hussain --- .../messages/handler.py | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/litellm/llms/anthropic/experimental_pass_through/messages/handler.py b/litellm/llms/anthropic/experimental_pass_through/messages/handler.py index 33c76556798..1537674bce3 100644 --- a/litellm/llms/anthropic/experimental_pass_through/messages/handler.py +++ b/litellm/llms/anthropic/experimental_pass_through/messages/handler.py @@ -417,6 +417,30 @@ def validate_anthropic_api_metadata(metadata: dict | None = None) -> dict | None return anthropic_metadata_obj.model_dump(exclude_none=True) +def _drop_unsupported_anthropic_messages_params( + anthropic_messages_optional_request_params: dict, + model: str, + custom_llm_provider: str | None, + additional_drop_params: list[str] | None = None, +) -> dict: + from litellm.llms.anthropic.chat.transformation import AnthropicConfig + from litellm.utils import _should_drop_param + + additional_drop_params = additional_drop_params or [] + for k in list(anthropic_messages_optional_request_params.keys()): + if _should_drop_param(k, additional_drop_params): + anthropic_messages_optional_request_params.pop(k, None) + + if not AnthropicConfig._model_supports_effort_param(model): + anthropic_messages_optional_request_params.pop("output_config", None) + if not AnthropicConfig._supports_model_capability(model, "supports_reasoning"): + anthropic_messages_optional_request_params.pop("thinking", None) + if "context_management" in anthropic_messages_optional_request_params: + if custom_llm_provider in ["vertex_ai", "bedrock"] and "haiku" in model.lower(): + anthropic_messages_optional_request_params.pop("context_management", None) + return anthropic_messages_optional_request_params + + def anthropic_messages_handler( max_tokens: int, messages: list[dict], @@ -648,21 +672,12 @@ def anthropic_messages_handler( ) if should_drop_params: - from litellm.llms.anthropic.chat.transformation import AnthropicConfig - from litellm.utils import _should_drop_param - - additional_drop_params = kwargs.get("additional_drop_params") or [] - for k in list(anthropic_messages_optional_request_params.keys()): - if _should_drop_param(k, additional_drop_params): - anthropic_messages_optional_request_params.pop(k, None) - - if not AnthropicConfig._model_supports_effort_param(model): - anthropic_messages_optional_request_params.pop("output_config", None) - if not AnthropicConfig._supports_model_capability(model, "supports_reasoning"): - anthropic_messages_optional_request_params.pop("thinking", None) - if "context_management" in anthropic_messages_optional_request_params: - if custom_llm_provider in ["vertex_ai", "bedrock"] and "haiku" in model.lower(): - anthropic_messages_optional_request_params.pop("context_management", None) + anthropic_messages_optional_request_params = _drop_unsupported_anthropic_messages_params( + anthropic_messages_optional_request_params=anthropic_messages_optional_request_params, + model=model, + custom_llm_provider=custom_llm_provider, + additional_drop_params=kwargs.get("additional_drop_params"), + ) if is_reasoning_auto_summary_enabled(): thinking_param: Final = anthropic_messages_optional_request_params.get("thinking") if isinstance(thinking_param, dict) and thinking_param.get("type") != "disabled": From b9a71c73a33d7717690782ab27d909a50d781905 Mon Sep 17 00:00:00 2001 From: Hasnaat Hussain Date: Wed, 15 Jul 2026 16:07:05 +0500 Subject: [PATCH 04/12] fix(anthropic): pass custom_llm_provider to model supports checks and clean up test file imports/prints Signed-off-by: Hasnaat Hussain --- .../messages/handler.py | 4 ++-- ...xperimental_pass_through_messages_handler.py | 17 ++++++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/litellm/llms/anthropic/experimental_pass_through/messages/handler.py b/litellm/llms/anthropic/experimental_pass_through/messages/handler.py index 1537674bce3..04c8483cde7 100644 --- a/litellm/llms/anthropic/experimental_pass_through/messages/handler.py +++ b/litellm/llms/anthropic/experimental_pass_through/messages/handler.py @@ -431,9 +431,9 @@ def _drop_unsupported_anthropic_messages_params( if _should_drop_param(k, additional_drop_params): anthropic_messages_optional_request_params.pop(k, None) - if not AnthropicConfig._model_supports_effort_param(model): + if not AnthropicConfig._model_supports_effort_param(model, custom_llm_provider or "anthropic"): anthropic_messages_optional_request_params.pop("output_config", None) - if not AnthropicConfig._supports_model_capability(model, "supports_reasoning"): + if not AnthropicConfig._supports_model_capability(model, "supports_reasoning", custom_llm_provider or "anthropic"): anthropic_messages_optional_request_params.pop("thinking", None) if "context_management" in anthropic_messages_optional_request_params: if custom_llm_provider in ["vertex_ai", "bedrock"] and "haiku" in model.lower(): diff --git a/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py b/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py index d8121d47bcb..e0356a94e3d 100644 --- a/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py +++ b/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py @@ -42,8 +42,8 @@ def test_anthropic_experimental_pass_through_messages_handler(): model="openai/claude-3-5-sonnet-20240620", api_key="test-api-key", ) - except (ValueError, TypeError, AttributeError) as e: - print(f"Error: {e}") + except (ValueError, TypeError, AttributeError): + pass mock_responses.assert_called_once() assert mock_responses.call_args.kwargs["api_key"] == "test-api-key" @@ -129,8 +129,8 @@ def test_anthropic_experimental_pass_through_messages_handler_dynamic_api_key_an api_base="test-api-base", custom_key="custom_value", ) - except (ValueError, TypeError, AttributeError) as e: - print(f"Error: {e}") + except (ValueError, TypeError, AttributeError): + pass mock_completion.assert_called_once() assert mock_completion.call_args.kwargs["api_key"] == "test-api-key" assert mock_completion.call_args.kwargs["api_base"] == "test-api-base" @@ -244,8 +244,8 @@ def test_anthropic_experimental_pass_through_messages_handler_custom_llm_provide custom_llm_provider="my-custom-llm", api_key="test-api-key", ) - except (ValueError, TypeError, AttributeError) as e: - print(f"Error: {e}") + except (ValueError, TypeError, AttributeError): + pass # Assert that litellm.completion was called when using a custom LLM provider mock_completion.assert_called_once() @@ -296,7 +296,6 @@ async def test_bedrock_converse_budget_tokens_preserved(): mock_acompletion.assert_called_once() call_kwargs = mock_acompletion.call_args.kwargs - print("acompletion call kwargs: ", json.dumps(call_kwargs, indent=4, default=str)) # Verify thinking parameter is passed through with budget_tokens preserved thinking_param = call_kwargs.get("thinking") @@ -328,8 +327,8 @@ def test_openai_model_with_thinking_converts_to_reasoning(): api_key="test-api-key", thinking={"type": "enabled", "budget_tokens": 1024}, ) - except (ValueError, TypeError, AttributeError) as e: - print(f"Error: {e}") + except (ValueError, TypeError, AttributeError): + pass mock_responses.assert_called_once() From edf860dd2836e3b96dbf4642fad14b1d50583f5a Mon Sep 17 00:00:00 2001 From: Hasnaat Hussain Date: Sat, 8 Aug 2026 15:10:22 +0500 Subject: [PATCH 05/12] fix(anthropic): satisfy strict lint gate Signed-off-by: Hasnaat Hussain --- .../experimental_pass_through/messages/handler.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/litellm/llms/anthropic/experimental_pass_through/messages/handler.py b/litellm/llms/anthropic/experimental_pass_through/messages/handler.py index 04c8483cde7..88ead5df3dd 100644 --- a/litellm/llms/anthropic/experimental_pass_through/messages/handler.py +++ b/litellm/llms/anthropic/experimental_pass_through/messages/handler.py @@ -435,9 +435,12 @@ def _drop_unsupported_anthropic_messages_params( anthropic_messages_optional_request_params.pop("output_config", None) if not AnthropicConfig._supports_model_capability(model, "supports_reasoning", custom_llm_provider or "anthropic"): anthropic_messages_optional_request_params.pop("thinking", None) - if "context_management" in anthropic_messages_optional_request_params: - if custom_llm_provider in ["vertex_ai", "bedrock"] and "haiku" in model.lower(): - anthropic_messages_optional_request_params.pop("context_management", None) + if ( + "context_management" in anthropic_messages_optional_request_params + and custom_llm_provider in ["vertex_ai", "bedrock"] + and "haiku" in model.lower() + ): + anthropic_messages_optional_request_params.pop("context_management", None) return anthropic_messages_optional_request_params From 3bf1181403260cf67af851104a7bcf14da677bcd Mon Sep 17 00:00:00 2001 From: Hasnaat Hussain Date: Sat, 8 Aug 2026 15:30:53 +0500 Subject: [PATCH 06/12] refactor(anthropic): keep drop params immutable Signed-off-by: Hasnaat Hussain --- .../messages/handler.py | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/litellm/llms/anthropic/experimental_pass_through/messages/handler.py b/litellm/llms/anthropic/experimental_pass_through/messages/handler.py index 88ead5df3dd..da8f798f9d1 100644 --- a/litellm/llms/anthropic/experimental_pass_through/messages/handler.py +++ b/litellm/llms/anthropic/experimental_pass_through/messages/handler.py @@ -7,8 +7,9 @@ import asyncio import contextvars -from collections.abc import AsyncIterator, Coroutine, Iterator +from collections.abc import AsyncIterator, Coroutine, Iterator, Mapping, Sequence from functools import partial +from types import MappingProxyType from typing import Any, Final, cast import litellm @@ -418,30 +419,30 @@ def validate_anthropic_api_metadata(metadata: dict | None = None) -> dict | None def _drop_unsupported_anthropic_messages_params( - anthropic_messages_optional_request_params: dict, + anthropic_messages_optional_request_params: Mapping[str, Any], model: str, custom_llm_provider: str | None, - additional_drop_params: list[str] | None = None, -) -> dict: + additional_drop_params: Sequence[str] | None = None, +) -> Mapping[str, Any]: from litellm.llms.anthropic.chat.transformation import AnthropicConfig from litellm.utils import _should_drop_param - additional_drop_params = additional_drop_params or [] - for k in list(anthropic_messages_optional_request_params.keys()): - if _should_drop_param(k, additional_drop_params): - anthropic_messages_optional_request_params.pop(k, None) - - if not AnthropicConfig._model_supports_effort_param(model, custom_llm_provider or "anthropic"): - anthropic_messages_optional_request_params.pop("output_config", None) - if not AnthropicConfig._supports_model_capability(model, "supports_reasoning", custom_llm_provider or "anthropic"): - anthropic_messages_optional_request_params.pop("thinking", None) - if ( - "context_management" in anthropic_messages_optional_request_params - and custom_llm_provider in ["vertex_ai", "bedrock"] - and "haiku" in model.lower() - ): - anthropic_messages_optional_request_params.pop("context_management", None) - return anthropic_messages_optional_request_params + additional_drop_params_list: Final = additional_drop_params if isinstance(additional_drop_params, list) else None + supports_effort: Final = AnthropicConfig._model_supports_effort_param(model, custom_llm_provider or "anthropic") + supports_reasoning: Final = AnthropicConfig._supports_model_capability( + model, "supports_reasoning", custom_llm_provider or "anthropic" + ) + drop_context_management: Final = custom_llm_provider in ["vertex_ai", "bedrock"] and "haiku" in model.lower() + return MappingProxyType( + { + k: v + for k, v in anthropic_messages_optional_request_params.items() + if not _should_drop_param(k, additional_drop_params_list) + and (k != "output_config" or supports_effort) + and (k != "thinking" or supports_reasoning) + and (k != "context_management" or not drop_context_management) + } + ) def anthropic_messages_handler( @@ -668,7 +669,7 @@ def anthropic_messages_handler( ) ) - should_drop_params = ( + should_drop_params: Final = ( litellm.drop_params is True or getattr(litellm_params, "drop_params", None) is True or kwargs.get("drop_params") is True From 82e12481693295f1715e03fe7ffe5f3df942cd13 Mon Sep 17 00:00:00 2001 From: Hasnaat Hussain Date: Wed, 12 Aug 2026 22:27:35 +0500 Subject: [PATCH 07/12] fix(anthropic): preserve immutable drop params Signed-off-by: Hasnaat Hussain --- .../messages/handler.py | 30 ++++++--- ...erimental_pass_through_messages_handler.py | 67 ++++++++++++------- 2 files changed, 62 insertions(+), 35 deletions(-) diff --git a/litellm/llms/anthropic/experimental_pass_through/messages/handler.py b/litellm/llms/anthropic/experimental_pass_through/messages/handler.py index da8f798f9d1..a1887f529c0 100644 --- a/litellm/llms/anthropic/experimental_pass_through/messages/handler.py +++ b/litellm/llms/anthropic/experimental_pass_through/messages/handler.py @@ -675,26 +675,38 @@ def anthropic_messages_handler( or kwargs.get("drop_params") is True ) - if should_drop_params: - anthropic_messages_optional_request_params = _drop_unsupported_anthropic_messages_params( + filtered_anthropic_messages_params: Final = ( + _drop_unsupported_anthropic_messages_params( anthropic_messages_optional_request_params=anthropic_messages_optional_request_params, model=model, custom_llm_provider=custom_llm_provider, additional_drop_params=kwargs.get("additional_drop_params"), ) - if is_reasoning_auto_summary_enabled(): - thinking_param: Final = anthropic_messages_optional_request_params.get("thinking") - if isinstance(thinking_param, dict) and thinking_param.get("type") != "disabled": - anthropic_messages_optional_request_params["thinking"] = { - **thinking_param, - "display": "summarized", + if should_drop_params + else anthropic_messages_optional_request_params + ) + thinking_param: Final = filtered_anthropic_messages_params.get("thinking") + final_anthropic_messages_params: Final = ( + MappingProxyType( + { + **filtered_anthropic_messages_params, + "thinking": { + **thinking_param, + "display": "summarized", + }, } + ) + if is_reasoning_auto_summary_enabled() + and isinstance(thinking_param, dict) + and thinking_param.get("type") != "disabled" + else filtered_anthropic_messages_params + ) return base_llm_http_handler.anthropic_messages_handler( model=model, messages=messages, anthropic_messages_provider_config=anthropic_messages_provider_config, - anthropic_messages_optional_request_params=dict(anthropic_messages_optional_request_params), + anthropic_messages_optional_request_params=dict(final_anthropic_messages_params), _is_async=is_async, client=client, custom_llm_provider=custom_llm_provider, diff --git a/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py b/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py index e0356a94e3d..212c5d05b55 100644 --- a/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py +++ b/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py @@ -299,11 +299,15 @@ async def test_bedrock_converse_budget_tokens_preserved(): # Verify thinking parameter is passed through with budget_tokens preserved thinking_param = call_kwargs.get("thinking") - assert thinking_param is not None, "thinking parameter should be passed to acompletion" - assert thinking_param.get("type") == "enabled", "thinking.type should be 'enabled'" - assert thinking_param.get("budget_tokens") == 1024, ( - f"thinking.budget_tokens should be 1024, but got {thinking_param.get('budget_tokens')}" - ) + assert ( + thinking_param is not None + ), "thinking parameter should be passed to acompletion" + assert ( + thinking_param.get("type") == "enabled" + ), "thinking.type should be 'enabled'" + assert ( + thinking_param.get("budget_tokens") == 1024 + ), f"thinking.budget_tokens should be 1024, but got {thinking_param.get('budget_tokens')}" def test_openai_model_with_thinking_converts_to_reasoning(): @@ -335,18 +339,23 @@ def test_openai_model_with_thinking_converts_to_reasoning(): call_kwargs = mock_responses.call_args.kwargs # Verify reasoning is set (converted from thinking) - assert "reasoning" in call_kwargs, "reasoning should be passed to litellm.responses" + assert ( + "reasoning" in call_kwargs + ), "reasoning should be passed to litellm.responses" # budget_tokens=1024 -> effort="low" (at the LOW budget threshold) # reasoning_auto_summary is False by default, so no summary key expected_reasoning = {"effort": "low"} assert call_kwargs["reasoning"] == expected_reasoning, ( - f"reasoning should be {expected_reasoning} for budget_tokens=1024, got {call_kwargs.get('reasoning')}" + f"reasoning should be {expected_reasoning} for budget_tokens=1024, " + f"got {call_kwargs.get('reasoning')}" ) assert "summary" not in call_kwargs["reasoning"] # Verify thinking is NOT passed directly to the Responses API - assert "thinking" not in call_kwargs, "thinking should NOT be passed directly to litellm.responses" + assert ( + "thinking" not in call_kwargs + ), "thinking should NOT be passed directly to litellm.responses" class TestThinkingParameterTransformation: @@ -399,7 +408,9 @@ class TestThinkingParameterTransformation: thinking=thinking, model="openai/gpt-5.2", ) - assert result == {"reasoning_effort": {"effort": "high", "summary": "detailed"}} + assert result == { + "reasoning_effort": {"effort": "high", "summary": "detailed"} + } finally: litellm.reasoning_auto_summary = original @@ -597,9 +608,9 @@ class TestThinkingSummaryPreservation: mock_responses.assert_called_once() call_kwargs = mock_responses.call_args.kwargs reasoning = call_kwargs["reasoning"] - assert reasoning["summary"] == "concise", ( - f"Expected summary='concise', got summary='{reasoning.get('summary')}'" - ) + assert ( + reasoning["summary"] == "concise" + ), f"Expected summary='concise', got summary='{reasoning.get('summary')}'" def test_responses_adapter_preserves_summary(self): """translate_thinking_to_reasoning should include summary when user provides it.""" @@ -608,7 +619,9 @@ class TestThinkingSummaryPreservation: ) thinking = {"type": "enabled", "budget_tokens": 5000, "summary": "concise"} - result = LiteLLMAnthropicToResponsesAPIAdapter.translate_thinking_to_reasoning(thinking) + result = LiteLLMAnthropicToResponsesAPIAdapter.translate_thinking_to_reasoning( + thinking + ) assert result == {"effort": "high", "summary": "concise"} def test_responses_adapter_no_summary_by_default(self): @@ -622,7 +635,11 @@ class TestThinkingSummaryPreservation: try: litellm.reasoning_auto_summary = False thinking = {"type": "enabled", "budget_tokens": 5000} - result = LiteLLMAnthropicToResponsesAPIAdapter.translate_thinking_to_reasoning(thinking) + result = ( + LiteLLMAnthropicToResponsesAPIAdapter.translate_thinking_to_reasoning( + thinking + ) + ) assert result == {"effort": "high"} assert result is not None and "summary" not in result finally: @@ -639,7 +656,9 @@ class TestThinkingSummaryPreservation: thinking=thinking, model="openai/gpt-5.2", ) - assert result == {"reasoning_effort": {"effort": "high", "summary": "concise"}} + assert result == { + "reasoning_effort": {"effort": "high", "summary": "concise"} + } def test_translate_thinking_for_model_disabled_stays_plain_string_when_auto_summary_enabled(self): """Disabled thinking must stay a plain string even when reasoning_auto_summary is on.""" @@ -785,7 +804,9 @@ def test_presanitized_flag_not_leaked_to_provider_params(): def fake_base_handler(*args, **kwargs): captured.update(kwargs) - captured["optional"] = kwargs.get("anthropic_messages_optional_request_params", {}) + captured["optional"] = kwargs.get( + "anthropic_messages_optional_request_params", {} + ) return "stub" with patch.object( @@ -1391,18 +1412,12 @@ async def test_anthropic_messages_leaves_non_provider_failures_unmapped(): assert "Traceback" not in str(excinfo.value) @pytest.mark.asyncio async def test_anthropic_pass_through_drop_params(monkeypatch): - from unittest.mock import AsyncMock - from litellm.llms.anthropic.experimental_pass_through.messages.handler import ( - anthropic_messages_handler, - ) - - mock_handler = AsyncMock() from litellm.llms.anthropic.experimental_pass_through.messages import handler + mock_handler = AsyncMock() monkeypatch.setattr(handler.base_llm_http_handler, "anthropic_messages_handler", mock_handler) - # Test automatic dropping of context_management for haiku on vertex_ai - await anthropic_messages_handler( + await handler.anthropic_messages_handler( model="vertex_ai/claude-3-haiku-20240307", messages=[{"role": "user", "content": "hello"}], max_tokens=64, @@ -1411,6 +1426,6 @@ async def test_anthropic_pass_through_drop_params(monkeypatch): custom_llm_provider="vertex_ai", is_async=True, ) - called_kwargs = mock_handler.call_args.kwargs - optional_params = called_kwargs.get("anthropic_messages_optional_request_params", {}) + + optional_params = mock_handler.call_args.kwargs.get("anthropic_messages_optional_request_params", {}) assert "context_management" not in optional_params From 416b0107f3ebca413c12f871fadb2f05344be610 Mon Sep 17 00:00:00 2001 From: Hasnaat Hussain Date: Wed, 12 Aug 2026 22:46:29 +0500 Subject: [PATCH 08/12] test(anthropic): cover drop params branches Signed-off-by: Hasnaat Hussain --- ...erimental_pass_through_messages_handler.py | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py b/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py index 212c5d05b55..05a1d6f3d98 100644 --- a/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py +++ b/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py @@ -1429,3 +1429,72 @@ async def test_anthropic_pass_through_drop_params(monkeypatch): optional_params = mock_handler.call_args.kwargs.get("anthropic_messages_optional_request_params", {}) assert "context_management" not in optional_params + + +def test_drop_params_filters_unsupported_anthropic_params(): + from litellm.llms.anthropic.experimental_pass_through.messages.handler import ( + _drop_unsupported_anthropic_messages_params, + ) + + params = { + "max_tokens": 64, + "metadata": {"request_id": "test"}, + "thinking": {"type": "enabled", "budget_tokens": 1024}, + "output_config": {"effort": "high"}, + "context_management": {"edits": []}, + } + + filtered = _drop_unsupported_anthropic_messages_params( + anthropic_messages_optional_request_params=params, + model="claude-3-haiku-20240307", + custom_llm_provider="vertex_ai", + additional_drop_params=["metadata"], + ) + + assert filtered == {"max_tokens": 64} + assert params["thinking"] == {"type": "enabled", "budget_tokens": 1024} + assert "context_management" in params + + +def test_drop_params_preserves_supported_anthropic_params(): + from litellm.llms.anthropic.experimental_pass_through.messages.handler import ( + _drop_unsupported_anthropic_messages_params, + ) + + params = { + "thinking": {"type": "enabled", "budget_tokens": 1024}, + "output_config": {"effort": "high"}, + "context_management": {"edits": []}, + } + + filtered = _drop_unsupported_anthropic_messages_params( + anthropic_messages_optional_request_params=params, + model="claude-opus-4-5-20251101", + custom_llm_provider="vertex_ai", + ) + + assert filtered == params + assert filtered is not params + + +@pytest.mark.asyncio +async def test_anthropic_pass_through_keeps_supported_params_without_drop(monkeypatch): + import litellm + from litellm.llms.anthropic.experimental_pass_through.messages import handler + + mock_handler = AsyncMock() + monkeypatch.setattr(litellm, "drop_params", False) + monkeypatch.setattr(handler.base_llm_http_handler, "anthropic_messages_handler", mock_handler) + + await handler.anthropic_messages_handler( + model="vertex_ai/claude-opus-4-5-20251101", + messages=[{"role": "user", "content": "hello"}], + max_tokens=64, + thinking={"type": "enabled", "budget_tokens": 1024}, + drop_params=False, + custom_llm_provider="vertex_ai", + is_async=True, + ) + + optional_params = mock_handler.call_args.kwargs["anthropic_messages_optional_request_params"] + assert optional_params["thinking"] == {"type": "enabled", "budget_tokens": 1024} From 06210c6a717200e63cdb5b3ccc5b01d7582dfe48 Mon Sep 17 00:00:00 2001 From: Hasnaat Hussain Date: Thu, 13 Aug 2026 01:57:59 +0500 Subject: [PATCH 09/12] fix(anthropic): satisfy type discipline budget Signed-off-by: Hasnaat Hussain --- .../experimental_pass_through/messages/handler.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/litellm/llms/anthropic/experimental_pass_through/messages/handler.py b/litellm/llms/anthropic/experimental_pass_through/messages/handler.py index a1887f529c0..246e5cff8bf 100644 --- a/litellm/llms/anthropic/experimental_pass_through/messages/handler.py +++ b/litellm/llms/anthropic/experimental_pass_through/messages/handler.py @@ -432,7 +432,7 @@ def _drop_unsupported_anthropic_messages_params( supports_reasoning: Final = AnthropicConfig._supports_model_capability( model, "supports_reasoning", custom_llm_provider or "anthropic" ) - drop_context_management: Final = custom_llm_provider in ["vertex_ai", "bedrock"] and "haiku" in model.lower() + drop_context_management: Final = custom_llm_provider in ("vertex_ai", "bedrock") and "haiku" in model.lower() return MappingProxyType( { k: v @@ -690,7 +690,7 @@ def anthropic_messages_handler( MappingProxyType( { **filtered_anthropic_messages_params, - "thinking": { + "thinking": { # mutable-ok: construct the summarized payload before freezing **thinking_param, "display": "summarized", }, @@ -706,7 +706,9 @@ def anthropic_messages_handler( model=model, messages=messages, anthropic_messages_provider_config=anthropic_messages_provider_config, - anthropic_messages_optional_request_params=dict(final_anthropic_messages_params), + anthropic_messages_optional_request_params=dict( # mutable-ok: downstream handler requires a dict + final_anthropic_messages_params + ), _is_async=is_async, client=client, custom_llm_provider=custom_llm_provider, From 361be85b7d89f5333d630178e1889992022e6681 Mon Sep 17 00:00:00 2001 From: Hasnaat Hussain Date: Sun, 23 Aug 2026 11:00:10 +0500 Subject: [PATCH 10/12] style(anthropic): format pass-through tests Signed-off-by: Hasnaat Hussain --- ...erimental_pass_through_messages_handler.py | 53 ++++++------------- 1 file changed, 16 insertions(+), 37 deletions(-) diff --git a/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py b/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py index 05a1d6f3d98..c71a59ecf78 100644 --- a/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py +++ b/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py @@ -299,15 +299,11 @@ async def test_bedrock_converse_budget_tokens_preserved(): # Verify thinking parameter is passed through with budget_tokens preserved thinking_param = call_kwargs.get("thinking") - assert ( - thinking_param is not None - ), "thinking parameter should be passed to acompletion" - assert ( - thinking_param.get("type") == "enabled" - ), "thinking.type should be 'enabled'" - assert ( - thinking_param.get("budget_tokens") == 1024 - ), f"thinking.budget_tokens should be 1024, but got {thinking_param.get('budget_tokens')}" + assert thinking_param is not None, "thinking parameter should be passed to acompletion" + assert thinking_param.get("type") == "enabled", "thinking.type should be 'enabled'" + assert thinking_param.get("budget_tokens") == 1024, ( + f"thinking.budget_tokens should be 1024, but got {thinking_param.get('budget_tokens')}" + ) def test_openai_model_with_thinking_converts_to_reasoning(): @@ -339,23 +335,18 @@ def test_openai_model_with_thinking_converts_to_reasoning(): call_kwargs = mock_responses.call_args.kwargs # Verify reasoning is set (converted from thinking) - assert ( - "reasoning" in call_kwargs - ), "reasoning should be passed to litellm.responses" + assert "reasoning" in call_kwargs, "reasoning should be passed to litellm.responses" # budget_tokens=1024 -> effort="low" (at the LOW budget threshold) # reasoning_auto_summary is False by default, so no summary key expected_reasoning = {"effort": "low"} assert call_kwargs["reasoning"] == expected_reasoning, ( - f"reasoning should be {expected_reasoning} for budget_tokens=1024, " - f"got {call_kwargs.get('reasoning')}" + f"reasoning should be {expected_reasoning} for budget_tokens=1024, got {call_kwargs.get('reasoning')}" ) assert "summary" not in call_kwargs["reasoning"] # Verify thinking is NOT passed directly to the Responses API - assert ( - "thinking" not in call_kwargs - ), "thinking should NOT be passed directly to litellm.responses" + assert "thinking" not in call_kwargs, "thinking should NOT be passed directly to litellm.responses" class TestThinkingParameterTransformation: @@ -408,9 +399,7 @@ class TestThinkingParameterTransformation: thinking=thinking, model="openai/gpt-5.2", ) - assert result == { - "reasoning_effort": {"effort": "high", "summary": "detailed"} - } + assert result == {"reasoning_effort": {"effort": "high", "summary": "detailed"}} finally: litellm.reasoning_auto_summary = original @@ -608,9 +597,9 @@ class TestThinkingSummaryPreservation: mock_responses.assert_called_once() call_kwargs = mock_responses.call_args.kwargs reasoning = call_kwargs["reasoning"] - assert ( - reasoning["summary"] == "concise" - ), f"Expected summary='concise', got summary='{reasoning.get('summary')}'" + assert reasoning["summary"] == "concise", ( + f"Expected summary='concise', got summary='{reasoning.get('summary')}'" + ) def test_responses_adapter_preserves_summary(self): """translate_thinking_to_reasoning should include summary when user provides it.""" @@ -619,9 +608,7 @@ class TestThinkingSummaryPreservation: ) thinking = {"type": "enabled", "budget_tokens": 5000, "summary": "concise"} - result = LiteLLMAnthropicToResponsesAPIAdapter.translate_thinking_to_reasoning( - thinking - ) + result = LiteLLMAnthropicToResponsesAPIAdapter.translate_thinking_to_reasoning(thinking) assert result == {"effort": "high", "summary": "concise"} def test_responses_adapter_no_summary_by_default(self): @@ -635,11 +622,7 @@ class TestThinkingSummaryPreservation: try: litellm.reasoning_auto_summary = False thinking = {"type": "enabled", "budget_tokens": 5000} - result = ( - LiteLLMAnthropicToResponsesAPIAdapter.translate_thinking_to_reasoning( - thinking - ) - ) + result = LiteLLMAnthropicToResponsesAPIAdapter.translate_thinking_to_reasoning(thinking) assert result == {"effort": "high"} assert result is not None and "summary" not in result finally: @@ -656,9 +639,7 @@ class TestThinkingSummaryPreservation: thinking=thinking, model="openai/gpt-5.2", ) - assert result == { - "reasoning_effort": {"effort": "high", "summary": "concise"} - } + assert result == {"reasoning_effort": {"effort": "high", "summary": "concise"}} def test_translate_thinking_for_model_disabled_stays_plain_string_when_auto_summary_enabled(self): """Disabled thinking must stay a plain string even when reasoning_auto_summary is on.""" @@ -804,9 +785,7 @@ def test_presanitized_flag_not_leaked_to_provider_params(): def fake_base_handler(*args, **kwargs): captured.update(kwargs) - captured["optional"] = kwargs.get( - "anthropic_messages_optional_request_params", {} - ) + captured["optional"] = kwargs.get("anthropic_messages_optional_request_params", {}) return "stub" with patch.object( From fd158442eb3d8ee46a7cf3f71f547ca42508b0bf Mon Sep 17 00:00:00 2001 From: Hasnaat Hussain Date: Sun, 23 Aug 2026 11:54:20 +0500 Subject: [PATCH 11/12] Fix Anthropic pass-through type-budget diagnostics Signed-off-by: Hasnaat Hussain --- .../experimental_pass_through/messages/handler.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/litellm/llms/anthropic/experimental_pass_through/messages/handler.py b/litellm/llms/anthropic/experimental_pass_through/messages/handler.py index 246e5cff8bf..fc2c085b79b 100644 --- a/litellm/llms/anthropic/experimental_pass_through/messages/handler.py +++ b/litellm/llms/anthropic/experimental_pass_through/messages/handler.py @@ -425,11 +425,10 @@ def _drop_unsupported_anthropic_messages_params( additional_drop_params: Sequence[str] | None = None, ) -> Mapping[str, Any]: from litellm.llms.anthropic.chat.transformation import AnthropicConfig - from litellm.utils import _should_drop_param additional_drop_params_list: Final = additional_drop_params if isinstance(additional_drop_params, list) else None - supports_effort: Final = AnthropicConfig._model_supports_effort_param(model, custom_llm_provider or "anthropic") - supports_reasoning: Final = AnthropicConfig._supports_model_capability( + supports_effort: Final = AnthropicConfig._model_supports_effort_param(model, custom_llm_provider or "anthropic") # pyright: ignore[reportPrivateUsage] # shared Anthropic capability gate + supports_reasoning: Final = AnthropicConfig._supports_model_capability( # pyright: ignore[reportPrivateUsage] # shared Anthropic capability gate model, "supports_reasoning", custom_llm_provider or "anthropic" ) drop_context_management: Final = custom_llm_provider in ("vertex_ai", "bedrock") and "haiku" in model.lower() @@ -437,7 +436,7 @@ def _drop_unsupported_anthropic_messages_params( { k: v for k, v in anthropic_messages_optional_request_params.items() - if not _should_drop_param(k, additional_drop_params_list) + if not (additional_drop_params_list is not None and k in additional_drop_params_list) and (k != "output_config" or supports_effort) and (k != "thinking" or supports_reasoning) and (k != "context_management" or not drop_context_management) From a7837717ace6e62ae55e42662a8d537c891c79c7 Mon Sep 17 00:00:00 2001 From: Hasnaat Hussain Date: Sat, 29 Aug 2026 14:32:19 +0500 Subject: [PATCH 12/12] style(anthropic): separate pass-through tests Signed-off-by: Hasnaat Hussain --- ...test_anthropic_experimental_pass_through_messages_handler.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py b/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py index c71a59ecf78..d4655f41a6c 100644 --- a/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py +++ b/tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py @@ -1389,6 +1389,8 @@ async def test_anthropic_messages_leaves_non_provider_failures_unmapped(): ) assert "Traceback" not in str(excinfo.value) + + @pytest.mark.asyncio async def test_anthropic_pass_through_drop_params(monkeypatch): from litellm.llms.anthropic.experimental_pass_through.messages import handler