From 806b57c926c7898ab1608a6d6fbdd52a1e134cb7 Mon Sep 17 00:00:00 2001 From: ege-arhan Date: Thu, 22 Oct 2026 23:43:21 +0000 Subject: [PATCH 01/11] fix(passthrough): keep adaptive thinking/effort on native /v1/messages path The base ArmodMessages transform resolves an unknown model as "anthropic" and strips modern request fields it thinks are unsupported. Stash thinking/output_config/temperature around the super() call and restore them, so an OpenAI-like host that speaks Messages natively gets them verbatim (#40890). Test file moved to tests/unit/llms/openai_like/messages/ after #43199. --- .../openai_like/messages/transformation.py | 16 ++++++- ..._like_anthropic_messages_transformation.py | 44 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/litellm/llms/openai_like/messages/transformation.py b/litellm/llms/openai_like/messages/transformation.py index 12c8b1da166..9aca53271df 100644 --- a/litellm/llms/openai_like/messages/transformation.py +++ b/litellm/llms/openai_like/messages/transformation.py @@ -87,7 +87,18 @@ 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. """ + stashed: dict = {} + for _key in ("thinking", "output_config", "temperature"): + if _key in anthropic_messages_optional_request_params: + stashed[_key] = anthropic_messages_optional_request_params.pop(_key) request: Final = super().transform_anthropic_messages_request( model=model, messages=messages, @@ -96,8 +107,11 @@ class OpenAILikeAnthropicMessagesConfig(AnthropicMessagesConfig): headers=headers, ) if self.supports_cache_control_ttl(): + request.update(stashed) return request - return normalize_cache_control_in_anthropic_payload(request) + normalized = normalize_cache_control_in_anthropic_payload(request) + normalized.update(stashed) + return normalized def get_complete_url( self, 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 87823fbada42d3c82e507dc572468840ae781f6e Mon Sep 17 00:00:00 2001 From: ege-arhan Date: Fri, 23 Oct 2026 01:34:22 +0000 Subject: [PATCH 02/11] fix(passthrough): declare stashed/normalized Final and build stash functionally type-discipline gate: stashed was a mutable dict assigned without Final (LIT001/LIT010) and normalized lacked Final (LIT010). Build the stash with a dict comprehension and mark both Final. --- litellm/llms/openai_like/messages/transformation.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/litellm/llms/openai_like/messages/transformation.py b/litellm/llms/openai_like/messages/transformation.py index 9aca53271df..316e48a0947 100644 --- a/litellm/llms/openai_like/messages/transformation.py +++ b/litellm/llms/openai_like/messages/transformation.py @@ -95,10 +95,11 @@ class OpenAILikeAnthropicMessagesConfig(AnthropicMessagesConfig): model the cost map does not know (see #40890). Pop them before the base transform so no drop warning fires, restore afterwards. """ - stashed: dict = {} - for _key in ("thinking", "output_config", "temperature"): - if _key in anthropic_messages_optional_request_params: - stashed[_key] = anthropic_messages_optional_request_params.pop(_key) + stashed: Final = { + _key: anthropic_messages_optional_request_params.pop(_key) + for _key in ("thinking", "output_config", "temperature") + if _key in anthropic_messages_optional_request_params + } request: Final = super().transform_anthropic_messages_request( model=model, messages=messages, @@ -109,7 +110,7 @@ class OpenAILikeAnthropicMessagesConfig(AnthropicMessagesConfig): if self.supports_cache_control_ttl(): request.update(stashed) return request - normalized = normalize_cache_control_in_anthropic_payload(request) + normalized: Final = normalize_cache_control_in_anthropic_payload(request) normalized.update(stashed) return normalized From fcb2a96dda8909cd01bea92fb03d67ecc65ea89f Mon Sep 17 00:00:00 2001 From: ege-arhan Date: Fri, 23 Oct 2026 03:38:13 +0000 Subject: [PATCH 03/11] fix(passthrough): type stash/request dicts to stay inside basedpyright ceiling --- .../openai_like/messages/transformation.py | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/litellm/llms/openai_like/messages/transformation.py b/litellm/llms/openai_like/messages/transformation.py index 316e48a0947..351d7837627 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 Any, Final, cast import litellm from litellm.litellm_core_utils.url_utils import join_api_base @@ -95,17 +95,22 @@ class OpenAILikeAnthropicMessagesConfig(AnthropicMessagesConfig): model the cost map does not know (see #40890). Pop them before the base transform so no drop warning fires, restore afterwards. """ - stashed: Final = { - _key: anthropic_messages_optional_request_params.pop(_key) - for _key in ("thinking", "output_config", "temperature") - if _key in anthropic_messages_optional_request_params - } - request: Final = super().transform_anthropic_messages_request( - model=model, - messages=messages, - anthropic_messages_optional_request_params=anthropic_messages_optional_request_params, - litellm_params=litellm_params, - headers=headers, + params: Final[dict[str, object]] = cast( + dict[str, object], anthropic_messages_optional_request_params + ) + stashed: Final[dict[str, object]] = {} + for _key in ("thinking", "output_config", "temperature"): + if _key in params: + stashed[_key] = params.pop(_key) + request: Final[dict[str, object]] = cast( + dict[str, object], + super().transform_anthropic_messages_request( # pyright: ignore[reportUnknownMemberType] # base returns unparameterized dict + model=model, + messages=messages, + anthropic_messages_optional_request_params=params, + litellm_params=litellm_params, + headers=headers, + ), ) if self.supports_cache_control_ttl(): request.update(stashed) From 14e62ba32c772e1f464c14f00b9cdb48b785e231 Mon Sep 17 00:00:00 2001 From: ege-arhan Date: Fri, 23 Oct 2026 03:42:52 +0000 Subject: [PATCH 04/11] style(passthrough): ruff format the cast line --- litellm/llms/openai_like/messages/transformation.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/litellm/llms/openai_like/messages/transformation.py b/litellm/llms/openai_like/messages/transformation.py index 351d7837627..520a244cbc2 100644 --- a/litellm/llms/openai_like/messages/transformation.py +++ b/litellm/llms/openai_like/messages/transformation.py @@ -95,9 +95,7 @@ class OpenAILikeAnthropicMessagesConfig(AnthropicMessagesConfig): model the cost map does not know (see #40890). Pop them before the base transform so no drop warning fires, restore afterwards. """ - params: Final[dict[str, object]] = cast( - dict[str, object], anthropic_messages_optional_request_params - ) + params: Final[dict[str, object]] = cast(dict[str, object], anthropic_messages_optional_request_params) stashed: Final[dict[str, object]] = {} for _key in ("thinking", "output_config", "temperature"): if _key in params: From 32a8ffe132c6c0f61f66b77a6b3f7ac1a37b4b99 Mon Sep 17 00:00:00 2001 From: ege-arhan Date: Fri, 23 Oct 2026 03:48:59 +0000 Subject: [PATCH 05/11] fix(passthrough): avoid banned cast, annotate stash/request at the boundary --- .../openai_like/messages/transformation.py | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/litellm/llms/openai_like/messages/transformation.py b/litellm/llms/openai_like/messages/transformation.py index 520a244cbc2..356cb1624e1 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, cast +from typing import Any, Final import litellm from litellm.litellm_core_utils.url_utils import join_api_base @@ -95,20 +95,18 @@ class OpenAILikeAnthropicMessagesConfig(AnthropicMessagesConfig): model the cost map does not know (see #40890). Pop them before the base transform so no drop warning fires, restore afterwards. """ - params: Final[dict[str, object]] = cast(dict[str, object], anthropic_messages_optional_request_params) + params: Final[dict[str, object]] = anthropic_messages_optional_request_params stashed: Final[dict[str, object]] = {} for _key in ("thinking", "output_config", "temperature"): if _key in params: stashed[_key] = params.pop(_key) - request: Final[dict[str, object]] = cast( - dict[str, object], - super().transform_anthropic_messages_request( # pyright: ignore[reportUnknownMemberType] # base returns unparameterized dict - model=model, - messages=messages, - anthropic_messages_optional_request_params=params, - litellm_params=litellm_params, - headers=headers, - ), + # base returns an unparameterized dict; annotate at the boundary + request: Final[dict[str, object]] = super().transform_anthropic_messages_request( # pyright: ignore[reportUnknownMemberType] # base signature is unparameterized + model=model, + messages=messages, + anthropic_messages_optional_request_params=params, + litellm_params=litellm_params, + headers=headers, ) if self.supports_cache_control_ttl(): request.update(stashed) From eaaa81e8d4ba96ba746b0b840cb9d3377cec7e40 Mon Sep 17 00:00:00 2001 From: ege-arhan Date: Fri, 23 Oct 2026 03:57:22 +0000 Subject: [PATCH 06/11] chore(passthrough): annotate the three new dicts with mutable-ok reasons --- 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 356cb1624e1..078c71a1b3d 100644 --- a/litellm/llms/openai_like/messages/transformation.py +++ b/litellm/llms/openai_like/messages/transformation.py @@ -95,13 +95,13 @@ class OpenAILikeAnthropicMessagesConfig(AnthropicMessagesConfig): model the cost map does not know (see #40890). Pop them before the base transform so no drop warning fires, restore afterwards. """ - params: Final[dict[str, object]] = anthropic_messages_optional_request_params - stashed: Final[dict[str, object]] = {} + params: Final[dict[str, object]] = anthropic_messages_optional_request_params # mutable-ok: popped below to detach the stashed keys + 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) # base returns an unparameterized dict; annotate at the boundary - request: Final[dict[str, object]] = super().transform_anthropic_messages_request( # pyright: ignore[reportUnknownMemberType] # base signature is unparameterized + request: Final[dict[str, object]] = super().transform_anthropic_messages_request( # mutable-ok: base returns a dict the caller updates in place # pyright: ignore[reportUnknownMemberType] # base signature is unparameterized model=model, messages=messages, anthropic_messages_optional_request_params=params, From 2ed32416862efc87972101a7bfec5441d22b8366 Mon Sep 17 00:00:00 2001 From: ege-arhan Date: Fri, 23 Oct 2026 03:57:53 +0000 Subject: [PATCH 07/11] style(passthrough): keep mutable-ok comments inside ruff's line budget --- .../openai_like/messages/transformation.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/litellm/llms/openai_like/messages/transformation.py b/litellm/llms/openai_like/messages/transformation.py index 078c71a1b3d..756248c780c 100644 --- a/litellm/llms/openai_like/messages/transformation.py +++ b/litellm/llms/openai_like/messages/transformation.py @@ -95,18 +95,22 @@ class OpenAILikeAnthropicMessagesConfig(AnthropicMessagesConfig): model the cost map does not know (see #40890). Pop them before the base transform so no drop warning fires, restore afterwards. """ - params: Final[dict[str, object]] = anthropic_messages_optional_request_params # mutable-ok: popped below to detach the stashed keys + params: Final[dict[str, object]] = ( + anthropic_messages_optional_request_params # mutable-ok: popped below to detach the stashed keys + ) 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) # base returns an unparameterized dict; annotate at the boundary - request: Final[dict[str, object]] = super().transform_anthropic_messages_request( # mutable-ok: base returns a dict the caller updates in place # pyright: ignore[reportUnknownMemberType] # base signature is unparameterized - model=model, - messages=messages, - anthropic_messages_optional_request_params=params, - litellm_params=litellm_params, - headers=headers, + request: Final[dict[str, object]] = ( + super().transform_anthropic_messages_request( # mutable-ok: base returns a dict the caller updates in place # pyright: ignore[reportUnknownMemberType] # base signature is unparameterized + model=model, + messages=messages, + anthropic_messages_optional_request_params=params, + litellm_params=litellm_params, + headers=headers, + ) ) if self.supports_cache_control_ttl(): request.update(stashed) From d7f83950129c04eba3fba4bef15d10d98a5b2d81 Mon Sep 17 00:00:00 2001 From: ege-arhan Date: Fri, 23 Oct 2026 04:04:07 +0000 Subject: [PATCH 08/11] refactor(passthrough): bind base request before the annotated dict --- .../openai_like/messages/transformation.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/litellm/llms/openai_like/messages/transformation.py b/litellm/llms/openai_like/messages/transformation.py index 756248c780c..7b5567fba67 100644 --- a/litellm/llms/openai_like/messages/transformation.py +++ b/litellm/llms/openai_like/messages/transformation.py @@ -95,23 +95,20 @@ class OpenAILikeAnthropicMessagesConfig(AnthropicMessagesConfig): model the cost map does not know (see #40890). Pop them before the base transform so no drop warning fires, restore afterwards. """ - params: Final[dict[str, object]] = ( - anthropic_messages_optional_request_params # mutable-ok: popped below to detach the stashed keys - ) + 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) # base returns an unparameterized dict; annotate at the boundary - request: Final[dict[str, object]] = ( - super().transform_anthropic_messages_request( # mutable-ok: base returns a dict the caller updates in place # pyright: ignore[reportUnknownMemberType] # base signature is unparameterized - model=model, - messages=messages, - anthropic_messages_optional_request_params=params, - litellm_params=litellm_params, - headers=headers, - ) + base_request: Final = super().transform_anthropic_messages_request( # pyright: ignore[reportUnknownMemberType] # base signature is unparameterized + model=model, + messages=messages, + anthropic_messages_optional_request_params=params, + litellm_params=litellm_params, + headers=headers, ) + request: Final[dict[str, object]] = base_request # mutable-ok: stash restored into it before returning if self.supports_cache_control_ttl(): request.update(stashed) return request From 45b873975e1a1cedf6ef6facc08c8713de8d24a4 Mon Sep 17 00:00:00 2001 From: Ege Arhan Date: Fri, 23 Oct 2026 08:07:29 +0000 Subject: [PATCH 09/11] fix(passthrough): cast base request to clear reportUnknownVariableType --- .../openai_like/messages/transformation.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/litellm/llms/openai_like/messages/transformation.py b/litellm/llms/openai_like/messages/transformation.py index 7b5567fba67..8d1140b7235 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 Any, Final, cast import litellm from litellm.litellm_core_utils.url_utils import join_api_base @@ -100,13 +100,16 @@ class OpenAILikeAnthropicMessagesConfig(AnthropicMessagesConfig): for _key in ("thinking", "output_config", "temperature"): if _key in params: stashed[_key] = params.pop(_key) - # base returns an unparameterized dict; annotate at the boundary - base_request: Final = super().transform_anthropic_messages_request( # pyright: ignore[reportUnknownMemberType] # base signature is unparameterized - model=model, - messages=messages, - anthropic_messages_optional_request_params=params, - litellm_params=litellm_params, - headers=headers, + # base signature is unparameterized; cast at the boundary + base_request: Final[dict[str, object]] = cast( + dict[str, object], + super().transform_anthropic_messages_request( + model=model, + messages=messages, + anthropic_messages_optional_request_params=params, + litellm_params=litellm_params, + headers=headers, + ), ) request: Final[dict[str, object]] = base_request # mutable-ok: stash restored into it before returning if self.supports_cache_control_ttl(): From 2c82d7758a0220cbcbe4ab58868cf189fd51b247 Mon Sep 17 00:00:00 2001 From: Ege Arhan Date: Fri, 23 Oct 2026 08:11:50 +0000 Subject: [PATCH 10/11] fix(passthrough): use bracket pyright ignore at base-call boundary --- .../openai_like/messages/transformation.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/litellm/llms/openai_like/messages/transformation.py b/litellm/llms/openai_like/messages/transformation.py index 8d1140b7235..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 Any, Final, cast +from typing import Any, Final import litellm from litellm.litellm_core_utils.url_utils import join_api_base @@ -100,18 +100,13 @@ class OpenAILikeAnthropicMessagesConfig(AnthropicMessagesConfig): for _key in ("thinking", "output_config", "temperature"): if _key in params: stashed[_key] = params.pop(_key) - # base signature is unparameterized; cast at the boundary - base_request: Final[dict[str, object]] = cast( - dict[str, object], - super().transform_anthropic_messages_request( - model=model, - messages=messages, - anthropic_messages_optional_request_params=params, - litellm_params=litellm_params, - headers=headers, - ), + 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=params, + litellm_params=litellm_params, + headers=headers, ) - request: Final[dict[str, object]] = base_request # mutable-ok: stash restored into it before returning if self.supports_cache_control_ttl(): request.update(stashed) return request From adbd0a257da0efe289a1dbbb41a57be170bea979 Mon Sep 17 00:00:00 2001 From: ege-arhan Date: Fri, 23 Oct 2026 11:29:47 +0000 Subject: [PATCH 11/11] test(interactions): drop queued from expected status enum (live Google spec) --- tests/unit/interactions/test_openapi_compliance.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/interactions/test_openapi_compliance.py b/tests/unit/interactions/test_openapi_compliance.py index 247d02298aa..92dbc5b60ab 100644 --- a/tests/unit/interactions/test_openapi_compliance.py +++ b/tests/unit/interactions/test_openapi_compliance.py @@ -266,7 +266,6 @@ class TestResponseCompliance: "cancelled", "incomplete", "budget_exceeded", - "queued", ] assert status_prop["enum"] == expected_statuses print(f"✓ Status enum values: {expected_statuses}")