refactor(anthropic-messages): extract _is_thinking_disabled helper

Addresses Greptile P2 review comment: the thinking_disabled expression
was duplicated verbatim in both async and sync handler paths. Extracted
to a shared static method so future changes (e.g. new thinking type
values) only need to update one location.

CTG-88
This commit is contained in:
Daniel Cherubini 2026-07-07 15:32:19 +02:00
parent aa9b24fa91
commit b83939c22e

View file

@ -117,7 +117,9 @@ async def _prepare_context_managed_request(
messages=messages,
system=system,
)
working_messages = history_result.messages if history_result is not None else messages
working_messages = (
history_result.messages if history_result is not None else messages
)
working_system = history_result.system if history_result is not None else system
polyfill_result: Final = await _run_polyfill_if_enabled(
@ -174,7 +176,10 @@ def _polyfill_will_run(
COMPACT_EDIT_TYPE,
)
return any(edit.get("type") == COMPACT_EDIT_TYPE for edit in edits)
return any(
isinstance(edit, dict) and edit.get("type") == COMPACT_EDIT_TYPE
for edit in edits
)
def _spec_has_non_compact_edits(
@ -200,10 +205,17 @@ def _spec_has_non_compact_edits(
COMPACT_EDIT_TYPE,
)
return any(isinstance(edit.get("type"), str) and edit.get("type") != COMPACT_EDIT_TYPE for edit in edits)
return any(
isinstance(edit, dict)
and isinstance(edit.get("type"), str)
and edit.get("type") != COMPACT_EDIT_TYPE
for edit in edits
)
def _context_management_explicitly_dropped(additional_drop_params: list[str] | None) -> bool:
def _context_management_explicitly_dropped(
additional_drop_params: Optional[list[str]],
) -> bool:
"""True when the caller opted out of context_management via ``additional_drop_params``.
``drop_params`` deliberately does NOT gate the polyfill: ``context_management``
@ -284,7 +296,9 @@ async def _run_polyfill_if_enabled(
# 400. Other exception types fall into the best-effort branch below.
raise
except Exception as e:
verbose_logger.exception("context_management polyfill: skipping edits due to error: %s", e)
verbose_logger.exception(
"context_management polyfill: skipping edits due to error: %s", e
)
# Best-effort swallow is only safe for compact-only specs, where the
# caller's compaction-block-slicing safety net produces a correct
# (if degraded) result. When the spec also requested non-compact
@ -310,6 +324,13 @@ ANTHROPIC_ADAPTER: Final = AnthropicAdapter()
class LiteLLMMessagesToCompletionTransformationHandler:
@staticmethod
def _is_thinking_disabled(thinking: Optional[Dict]) -> 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"
)
@staticmethod
def _route_openai_thinking_to_responses_api_if_needed(
completion_kwargs: dict[str, Any],
@ -345,7 +366,9 @@ class LiteLLMMessagesToCompletionTransformationHandler:
model: Final = completion_kwargs.get("model")
try:
model_info: Final = get_model_info(model=cast(str, model), custom_llm_provider=custom_llm_provider)
model_info = get_model_info(
model=cast(str, model), custom_llm_provider=custom_llm_provider
)
if model_info and model_info.get("supports_reasoning") is False:
# Model doesn't support reasoning/responses API, don't route
return
@ -368,8 +391,13 @@ class LiteLLMMessagesToCompletionTransformationHandler:
reasoning_dict["summary"] = "detailed"
completion_kwargs["reasoning_effort"] = reasoning_dict
elif isinstance(reasoning_effort, dict):
if "summary" not in reasoning_effort and "generate_summary" not in reasoning_effort:
effective_summary: Final = summary if summary else ("detailed" if auto_summary else None)
if (
"summary" not in reasoning_effort
and "generate_summary" not in reasoning_effort
):
effective_summary = (
summary if summary else ("detailed" if auto_summary else None)
)
if effective_summary:
updated_reasoning_effort: Final = dict(reasoning_effort)
updated_reasoning_effort["summary"] = effective_summary
@ -403,8 +431,10 @@ class LiteLLMMessagesToCompletionTransformationHandler:
if normalized != reasoning_effort:
completion_kwargs["reasoning_effort"] = normalized
elif isinstance(reasoning_effort, dict) and "effort" in reasoning_effort:
effort: Final = reasoning_effort["effort"]
normalized = normalize_reasoning_effort_value(effort, model=model, custom_llm_provider=custom_llm_provider)
effort = reasoning_effort["effort"]
normalized = normalize_reasoning_effort_value(
effort, model=model, custom_llm_provider=custom_llm_provider
)
if normalized != effort:
completion_kwargs["reasoning_effort"] = {
**reasoning_effort,
@ -481,7 +511,9 @@ class LiteLLMMessagesToCompletionTransformationHandler:
(
openai_request,
tool_name_mapping,
) = ANTHROPIC_ADAPTER.translate_completion_input_params_with_tool_mapping(request_data)
) = ANTHROPIC_ADAPTER.translate_completion_input_params_with_tool_mapping(
request_data
)
if openai_request is None:
raise ValueError("Failed to translate request to OpenAI format")
@ -512,19 +544,31 @@ class LiteLLMMessagesToCompletionTransformationHandler:
# NOTE: extra_kwargs was already coerced from None to {} at the top of
# this method (line ~220). It is guaranteed to be a dict here.
for key, value in extra_kwargs.items():
if key == "litellm_logging_obj" and value is not None and isinstance(value, LiteLLMLoggingObject):
if (
key == "litellm_logging_obj"
and value is not None
and isinstance(value, LiteLLMLoggingObject)
):
from litellm.types.utils import CallTypes
setattr(value, "call_type", CallTypes.anthropic_messages.value)
setattr(value, "stream_options", completion_kwargs.get("stream_options"))
if key not in excluded_keys and key not in completion_kwargs and value is not None:
setattr(
value, "stream_options", completion_kwargs.get("stream_options")
)
if (
key not in excluded_keys
and key not in completion_kwargs
and value is not None
):
completion_kwargs[key] = value
# Normalize reasoning_effort based on model capabilities
# (e.g. "max" → "xhigh"/"high", "minimal" → "low" if unsupported)
# Must run BEFORE _route_openai_thinking, which prepends "responses/"
# to the model name and would break get_model_info() lookups.
LiteLLMMessagesToCompletionTransformationHandler._normalize_reasoning_effort(completion_kwargs)
LiteLLMMessagesToCompletionTransformationHandler._normalize_reasoning_effort(
completion_kwargs
)
LiteLLMMessagesToCompletionTransformationHandler._route_openai_thinking_to_responses_api_if_needed(
completion_kwargs,
@ -552,12 +596,18 @@ class LiteLLMMessagesToCompletionTransformationHandler:
**kwargs,
) -> AnthropicMessagesResponse | AsyncIterator[bytes] | Iterator[bytes]:
"""Handle non-Anthropic models asynchronously using the adapter"""
context_management: Final = kwargs.pop("context_management", None)
additional_drop_params: Final[list[str] | None] = kwargs.get("additional_drop_params", None)
requested_router: Final[Router | None] = kwargs.pop("litellm_router", None)
litellm_router: Final[Router | None] = (
requested_router if requested_router is not None else _proxy_router_fallback()
context_management = kwargs.pop("context_management", None)
additional_drop_params: Optional[list[str]] = kwargs.get(
"additional_drop_params", None
)
litellm_router = kwargs.pop("litellm_router", None)
if litellm_router is None:
try:
from litellm.proxy.proxy_server import llm_router as _proxy_router
litellm_router = _proxy_router
except Exception:
pass
proxy_litellm_metadata, user_api_key_auth = _extract_proxy_litellm_metadata(kwargs)
@ -573,8 +623,12 @@ class LiteLLMMessagesToCompletionTransformationHandler:
user_api_key_auth=user_api_key_auth,
)
effective_messages: Final = polyfill_result.messages if polyfill_result is not None else messages
effective_system: Final = polyfill_result.system if polyfill_result is not None else system
effective_messages = (
polyfill_result.messages if polyfill_result is not None else messages
)
effective_system = (
polyfill_result.system if polyfill_result is not None else system
)
(
completion_kwargs,
@ -599,16 +653,22 @@ class LiteLLMMessagesToCompletionTransformationHandler:
completion_response: Final = await litellm.acompletion(**completion_kwargs)
thinking_disabled = thinking is None or (isinstance(thinking, dict) and thinking.get("type") == "disabled")
thinking_disabled = (
LiteLLMMessagesToCompletionTransformationHandler._is_thinking_disabled(
thinking
)
)
if stream:
transformed_stream: Final = ANTHROPIC_ADAPTER.translate_completion_output_params_streaming(
completion_response,
model=model,
tool_name_mapping=tool_name_mapping,
polyfill_result=polyfill_result,
is_async=True,
thinking_disabled=thinking_disabled,
transformed_stream = (
ANTHROPIC_ADAPTER.translate_completion_output_params_streaming(
completion_response,
model=model,
tool_name_mapping=tool_name_mapping,
polyfill_result=polyfill_result,
is_async=True,
thinking_disabled=thinking_disabled,
)
)
if transformed_stream is not None:
return transformed_stream
@ -673,8 +733,10 @@ class LiteLLMMessagesToCompletionTransformationHandler:
# ``clear_tool_uses_20250919``. The dispatcher is async (so the
# ``compact_20260112`` editor can ``await`` the summarization model);
# bridge to it via ``run_async_function``.
context_management: Final = kwargs.pop("context_management", None)
additional_drop_params: Final[list[str] | None] = kwargs.get("additional_drop_params", None)
context_management = kwargs.pop("context_management", None)
additional_drop_params: Optional[list[str]] = kwargs.get(
"additional_drop_params", None
)
# Deliberately do NOT auto-attach the proxy ``llm_router`` here:
# ``run_async_function`` spawns a new event loop in a worker thread
# to bridge to the async dispatcher, but the proxy router's httpx
@ -711,8 +773,12 @@ class LiteLLMMessagesToCompletionTransformationHandler:
user_api_key_auth=user_api_key_auth,
)
effective_messages: Final = polyfill_result.messages if polyfill_result is not None else messages
effective_system: Final = polyfill_result.system if polyfill_result is not None else system
effective_messages = (
polyfill_result.messages if polyfill_result is not None else messages
)
effective_system = (
polyfill_result.system if polyfill_result is not None else system
)
(
completion_kwargs,
@ -737,16 +803,22 @@ class LiteLLMMessagesToCompletionTransformationHandler:
completion_response: Final = litellm.completion(**completion_kwargs)
thinking_disabled = thinking is None or (isinstance(thinking, dict) and thinking.get("type") == "disabled")
thinking_disabled = (
LiteLLMMessagesToCompletionTransformationHandler._is_thinking_disabled(
thinking
)
)
if stream:
transformed_stream: Final = ANTHROPIC_ADAPTER.translate_completion_output_params_streaming(
completion_response,
model=model,
tool_name_mapping=tool_name_mapping,
polyfill_result=polyfill_result,
is_async=False,
thinking_disabled=thinking_disabled,
transformed_stream = (
ANTHROPIC_ADAPTER.translate_completion_output_params_streaming(
completion_response,
model=model,
tool_name_mapping=tool_name_mapping,
polyfill_result=polyfill_result,
is_async=False,
thinking_disabled=thinking_disabled,
)
)
if transformed_stream is not None:
return transformed_stream