mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-21 00:21:49 +00:00
fix: fail-closed thinking default (Veria review finding)
_threaded thinking was fail-open for a malformed config: with thinking: {}
(missing type) the request side (translate
anthropic_thinking_to_reasoning_effort) defaulted to disabled, but the
response side still converted provider reasoning_content into a thinking
block, leaking internal reasoning. _is_thinking_disabled now suppresses
unless the client explicitly opted in (type in enabled/adaptive).
Truth-table tests extended to cover {}, {budget_tokens: 500} and an
unknown type (all -> disabled). Bot review comments answered.
This commit is contained in:
parent
ddde64e210
commit
d1ce467d04
2 changed files with 43 additions and 14 deletions
|
|
@ -320,8 +320,17 @@ ANTHROPIC_ADAPTER: Final = AnthropicAdapter()
|
|||
class LiteLLMMessagesToCompletionTransformationHandler:
|
||||
@staticmethod
|
||||
def _is_thinking_disabled(thinking: Mapping | None) -> bool:
|
||||
"""Return True when the client's thinking param is absent or explicitly disabled."""
|
||||
return thinking is None or (isinstance(thinking, dict) and thinking.get("type") == "disabled")
|
||||
"""Return True (suppressed) unless the client explicitly opted in.
|
||||
|
||||
Only ``{"type": "enabled"|"adaptive"}`` enables the reasoning
|
||||
translation. Absent, disabled, or malformed objects (missing
|
||||
``type``) fail closed: the request side
|
||||
(``translate_anthropic_thinking_to_reasoning_effort``) already
|
||||
defaults a missing ``type`` to ``disabled``, and a malformed
|
||||
object must not surface provider ``reasoning_content`` through
|
||||
a thinking block (review finding).
|
||||
"""
|
||||
return not (isinstance(thinking, dict) and thinking.get("type") in ("enabled", "adaptive"))
|
||||
|
||||
@staticmethod
|
||||
def _route_openai_thinking_to_responses_api_if_needed(
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
"""Handler-level tests for ``thinking_disabled`` computation and threading.
|
||||
|
||||
Covers the boolean logic that decides whether thinking is disabled
|
||||
(``thinking is None or thinking.type == "disabled"``) and verifies it is
|
||||
threaded correctly to ``ANTHROPIC_ADAPTER`` output-translation calls for
|
||||
both the async and sync handler entry points, in streaming and non-streaming
|
||||
modes.
|
||||
(fail-closed: only an explicit ``{\"type\": \"enabled"|\"adaptive\"}``
|
||||
enables it; absent, disabled, or malformed objects disable it) and
|
||||
verifies it is threaded correctly to ``ANTHROPIC_ADAPTER`` output-
|
||||
translation calls for both the async and sync handler entry points, in
|
||||
streaming and non-streaming modes.
|
||||
|
||||
Mocks ``litellm.acompletion`` / ``litellm.completion`` and
|
||||
``ANTHROPIC_ADAPTER`` directly, alongside the preparation helpers that run
|
||||
|
|
@ -22,7 +23,10 @@ from litellm.llms.anthropic.experimental_pass_through.adapters.handler import (
|
|||
|
||||
THINKING_PARAMS = [
|
||||
(None, True),
|
||||
({}, True),
|
||||
({"type": "disabled"}, True),
|
||||
({"budget_tokens": 1024}, True),
|
||||
({"type": "weird"}, True),
|
||||
({"type": "enabled", "budget_tokens": 1024}, False),
|
||||
({"type": "adaptive"}, False),
|
||||
]
|
||||
|
|
@ -52,8 +56,12 @@ async def test_async_handler_streaming_threads_thinking_disabled(thinking_param,
|
|||
"_prepare_completion_kwargs",
|
||||
return_value=({}, {}),
|
||||
),
|
||||
patch("litellm.acompletion", return_value=MagicMock()), # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
|
||||
patch("litellm.llms.anthropic.experimental_pass_through.adapters.handler.ANTHROPIC_ADAPTER") as mock_adapter, # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
|
||||
patch( # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
|
||||
"litellm.acompletion", return_value=MagicMock()
|
||||
), # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
|
||||
patch( # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
|
||||
"litellm.llms.anthropic.experimental_pass_through.adapters.handler.ANTHROPIC_ADAPTER"
|
||||
) as mock_adapter, # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
|
||||
):
|
||||
mock_adapter.translate_completion_output_params_streaming.return_value = iter([])
|
||||
await LiteLLMMessagesToCompletionTransformationHandler.async_anthropic_messages_handler(
|
||||
|
|
@ -91,8 +99,12 @@ async def test_async_handler_non_streaming_threads_thinking_disabled(thinking_pa
|
|||
"_prepare_completion_kwargs",
|
||||
return_value=({}, {}),
|
||||
),
|
||||
patch("litellm.acompletion", return_value=MagicMock()), # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
|
||||
patch("litellm.llms.anthropic.experimental_pass_through.adapters.handler.ANTHROPIC_ADAPTER") as mock_adapter, # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
|
||||
patch( # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
|
||||
"litellm.acompletion", return_value=MagicMock()
|
||||
), # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
|
||||
patch( # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
|
||||
"litellm.llms.anthropic.experimental_pass_through.adapters.handler.ANTHROPIC_ADAPTER"
|
||||
) as mock_adapter, # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
|
||||
):
|
||||
mock_adapter.translate_completion_output_params.return_value = MagicMock()
|
||||
await LiteLLMMessagesToCompletionTransformationHandler.async_anthropic_messages_handler(
|
||||
|
|
@ -130,8 +142,12 @@ def test_sync_handler_streaming_threads_thinking_disabled(thinking_param, expect
|
|||
"_prepare_completion_kwargs",
|
||||
return_value=({}, {}),
|
||||
),
|
||||
patch("litellm.completion", return_value=MagicMock()), # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
|
||||
patch("litellm.llms.anthropic.experimental_pass_through.adapters.handler.ANTHROPIC_ADAPTER") as mock_adapter, # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
|
||||
patch( # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
|
||||
"litellm.completion", return_value=MagicMock()
|
||||
), # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
|
||||
patch( # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
|
||||
"litellm.llms.anthropic.experimental_pass_through.adapters.handler.ANTHROPIC_ADAPTER"
|
||||
) as mock_adapter, # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
|
||||
):
|
||||
mock_adapter.translate_completion_output_params_streaming.return_value = iter([])
|
||||
LiteLLMMessagesToCompletionTransformationHandler.anthropic_messages_handler(
|
||||
|
|
@ -169,8 +185,12 @@ def test_sync_handler_non_streaming_threads_thinking_disabled(thinking_param, ex
|
|||
"_prepare_completion_kwargs",
|
||||
return_value=({}, {}),
|
||||
),
|
||||
patch("litellm.completion", return_value=MagicMock()), # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
|
||||
patch("litellm.llms.anthropic.experimental_pass_through.adapters.handler.ANTHROPIC_ADAPTER") as mock_adapter, # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
|
||||
patch( # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
|
||||
"litellm.completion", return_value=MagicMock()
|
||||
), # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
|
||||
patch( # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
|
||||
"litellm.llms.anthropic.experimental_pass_through.adapters.handler.ANTHROPIC_ADAPTER"
|
||||
) as mock_adapter, # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
|
||||
):
|
||||
mock_adapter.translate_completion_output_params.return_value = MagicMock()
|
||||
LiteLLMMessagesToCompletionTransformationHandler.anthropic_messages_handler(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue