From 144cadea70f38e79aaf53aef05abcc5eddd0a456 Mon Sep 17 00:00:00 2001 From: ege-arhan Date: Sat, 24 Oct 2026 22:19:19 +0000 Subject: [PATCH 1/2] fix(passthrough): keep adaptive thinking/effort on native /v1/messages path (#40890) --- .../openai_like/messages/transformation.py | 27 +++++++++--- ..._like_anthropic_messages_transformation.py | 44 +++++++++++++++++++ 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/litellm/llms/openai_like/messages/transformation.py b/litellm/llms/openai_like/messages/transformation.py index 0ab55d653e0..878f89de965 100644 --- a/litellm/llms/openai_like/messages/transformation.py +++ b/litellm/llms/openai_like/messages/transformation.py @@ -1,4 +1,4 @@ -from typing import Final +from typing import Any, Final import litellm from litellm.litellm_core_utils.url_utils import join_api_base @@ -41,7 +41,7 @@ class OpenAILikeAnthropicMessagesConfig(AnthropicMessagesConfig): self, headers: dict[str, str], model: str, - messages: list[object], + messages: list[Any], optional_params: dict, litellm_params: dict, api_key: str | None = None, @@ -87,17 +87,32 @@ class OpenAILikeAnthropicMessagesConfig(AnthropicMessagesConfig): on Anthropic-only ``cache_control`` extensions (``cache_control.ttl: 1h is not supported``), so unless the provider declares ttl support the hints are reduced to their portable ``{"type": ...}`` core. + + The deployment speaks the Messages API natively, so caller-supplied + ``thinking``/``output_config``/``temperature`` are forwarded verbatim: + the base capability checks resolve the provider as "anthropic" and + would otherwise strip modern fields (e.g. adaptive thinking) from a + model the cost map does not know (see #40890). Pop them before the + base transform so no drop warning fires, restore afterwards. """ - request: Final = super().transform_anthropic_messages_request( + params: Final[dict[str, object]] = anthropic_messages_optional_request_params # mutable-ok: popped below + stashed: Final[dict[str, object]] = {} # mutable-ok: filled key-by-key, then restored onto the request + for _key in ("thinking", "output_config", "temperature"): + if _key in params: + stashed[_key] = params.pop(_key) + request: Final[dict[str, object]] = super().transform_anthropic_messages_request( # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType] # the shared base returns a bare dict # mutable-ok: stash restored into it before returning model=model, messages=messages, - anthropic_messages_optional_request_params=anthropic_messages_optional_request_params, + anthropic_messages_optional_request_params=params, litellm_params=litellm_params, headers=headers, ) if self.supports_cache_control_ttl(): + request.update(stashed) return request - return normalize_cache_control_in_anthropic_payload(request) + normalized: Final = normalize_cache_control_in_anthropic_payload(request) + normalized.update(stashed) + return normalized def get_complete_url( self, @@ -151,7 +166,7 @@ class JSONProviderAnthropicMessagesConfig(OpenAILikeAnthropicMessagesConfig): self, headers: dict[str, str], model: str, - messages: list[object], + messages: list[Any], optional_params: dict, litellm_params: dict, api_key: str | None = None, diff --git a/tests/unit/llms/openai_like/messages/test_openai_like_anthropic_messages_transformation.py b/tests/unit/llms/openai_like/messages/test_openai_like_anthropic_messages_transformation.py index 1d5ef50ca3a..892bd01193d 100644 --- a/tests/unit/llms/openai_like/messages/test_openai_like_anthropic_messages_transformation.py +++ b/tests/unit/llms/openai_like/messages/test_openai_like_anthropic_messages_transformation.py @@ -539,3 +539,47 @@ def test_safeguards_reach_an_openai_like_host_with_only_the_client_betas( api_base="https://host/v1", ) assert headers.get("anthropic-beta") == expected_beta, headers + + +def test_passthrough_keeps_adaptive_thinking_and_effort( + config: OpenAILikeAnthropicMessagesConfig, caplog: pytest.LogCaptureFixture +) -> None: + """Regression #40890: native /v1/messages passthrough must forward + thinking/output_config/temperature verbatim, not strip them via cost-map + capability checks for a model the cost map does not know.""" + import logging + + with caplog.at_level(logging.WARNING): + payload = config.transform_anthropic_messages_request( + model="my-model", + messages=[{"role": "user", "content": "hi"}], + anthropic_messages_optional_request_params={ + "max_tokens": 4096, + "thinking": {"type": "adaptive"}, + "output_config": {"effort": "high"}, + "temperature": 0.5, + }, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + assert payload["thinking"] == {"type": "adaptive"} + assert payload["output_config"] == {"effort": "high"} + assert payload["temperature"] == 0.5 + assert "Dropping adaptive" not in caplog.text + + +def test_passthrough_keeps_legacy_thinking_shape( + config: OpenAILikeAnthropicMessagesConfig, +) -> None: + payload = config.transform_anthropic_messages_request( + model="my-model", + messages=[{"role": "user", "content": "hi"}], + anthropic_messages_optional_request_params={ + "max_tokens": 4096, + "thinking": {"type": "enabled", "budget_tokens": 1024}, + }, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + assert payload["thinking"] == {"type": "enabled", "budget_tokens": 1024} + assert payload["max_tokens"] == 4096 From 304f060af58edb92cbf140bd24ac6425b1e2356f Mon Sep 17 00:00:00 2001 From: ege-arhan Date: Sun, 25 Oct 2026 06:01:26 +0000 Subject: [PATCH 2/2] fix(lint): replace Any with object to satisfy TID251 strict rule --- litellm/llms/openai_like/messages/transformation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/litellm/llms/openai_like/messages/transformation.py b/litellm/llms/openai_like/messages/transformation.py index 878f89de965..2bd6b13cddc 100644 --- a/litellm/llms/openai_like/messages/transformation.py +++ b/litellm/llms/openai_like/messages/transformation.py @@ -1,4 +1,4 @@ -from typing import Any, Final +from typing import Final import litellm from litellm.litellm_core_utils.url_utils import join_api_base @@ -41,7 +41,7 @@ class OpenAILikeAnthropicMessagesConfig(AnthropicMessagesConfig): self, headers: dict[str, str], model: str, - messages: list[Any], + messages: list[object], optional_params: dict, litellm_params: dict, api_key: str | None = None, @@ -166,7 +166,7 @@ class JSONProviderAnthropicMessagesConfig(OpenAILikeAnthropicMessagesConfig): self, headers: dict[str, str], model: str, - messages: list[Any], + messages: list[object], optional_params: dict, litellm_params: dict, api_key: str | None = None,