diff --git a/litellm/litellm_core_utils/get_litellm_params.py b/litellm/litellm_core_utils/get_litellm_params.py index ad9538ac171..5c1c7ea8c8c 100644 --- a/litellm/litellm_core_utils/get_litellm_params.py +++ b/litellm/litellm_core_utils/get_litellm_params.py @@ -90,6 +90,7 @@ def get_litellm_params( async_call: Optional[bool] = None, ssl_verify: Optional[bool] = None, merge_reasoning_content_in_choices: Optional[bool] = None, + force_reasoning_effort: Optional[str] = None, use_litellm_proxy: Optional[bool] = None, api_version: Optional[str] = None, max_retries: Optional[int] = None, @@ -149,6 +150,7 @@ def get_litellm_params( "async_call": async_call, "ssl_verify": ssl_verify, "merge_reasoning_content_in_choices": merge_reasoning_content_in_choices, + "force_reasoning_effort": force_reasoning_effort, "api_version": api_version, "max_retries": max_retries, "use_litellm_proxy": use_litellm_proxy, diff --git a/litellm/llms/anthropic/experimental_pass_through/messages/handler.py b/litellm/llms/anthropic/experimental_pass_through/messages/handler.py index c400d82b7cf..d772b17d3cc 100644 --- a/litellm/llms/anthropic/experimental_pass_through/messages/handler.py +++ b/litellm/llms/anthropic/experimental_pass_through/messages/handler.py @@ -441,6 +441,18 @@ def anthropic_messages_handler( params=local_vars ) ) + force_reasoning_effort = litellm_params.force_reasoning_effort + if force_reasoning_effort: + output_config = anthropic_messages_optional_request_params.get("output_config") or {} + anthropic_messages_optional_request_params["output_config"] = { + **output_config, + "effort": force_reasoning_effort, + } + # Effort alone does not trigger reasoning — force-enable thinking too + thinking = anthropic_messages_optional_request_params.get("thinking") + if not isinstance(thinking, dict) or thinking.get("type") == "disabled": + anthropic_messages_optional_request_params["thinking"] = {"type": "adaptive"} + return base_llm_http_handler.anthropic_messages_handler( model=model, messages=messages, diff --git a/litellm/llms/anthropic/experimental_pass_through/responses_adapters/handler.py b/litellm/llms/anthropic/experimental_pass_through/responses_adapters/handler.py index 198ebe1ff8c..3fabc9c51ea 100644 --- a/litellm/llms/anthropic/experimental_pass_through/responses_adapters/handler.py +++ b/litellm/llms/anthropic/experimental_pass_through/responses_adapters/handler.py @@ -15,6 +15,7 @@ from litellm.types.llms.openai import ResponsesAPIResponse from .streaming_iterator import AnthropicResponsesStreamWrapper from .transformation import LiteLLMAnthropicToResponsesAPIAdapter +from ..utils import is_reasoning_auto_summary_enabled _ADAPTER = LiteLLMAnthropicToResponsesAPIAdapter() @@ -76,7 +77,8 @@ def _build_responses_kwargs( responses_kwargs["stream"] = True # Forward litellm-specific kwargs (api_key, api_base, logging obj, etc.) - excluded = {"anthropic_messages"} + excluded = {"anthropic_messages", "force_reasoning_effort"} + force_reasoning_effort = (extra_kwargs or {}).get("force_reasoning_effort") for key, value in (extra_kwargs or {}).items(): if key == "litellm_logging_obj" and value is not None: from litellm.litellm_core_utils.litellm_logging import ( @@ -93,6 +95,17 @@ def _build_responses_kwargs( elif key not in excluded and key not in responses_kwargs and value is not None: responses_kwargs[key] = value + # Override reasoning effort if force_reasoning_effort is configured + if force_reasoning_effort: + reasoning = responses_kwargs.get("reasoning") + if isinstance(reasoning, dict): + responses_kwargs["reasoning"] = {**reasoning, "effort": force_reasoning_effort} + else: + responses_kwargs["reasoning"] = { + **({"summary": "detailed"} if is_reasoning_auto_summary_enabled() else {}), + "effort": force_reasoning_effort, + } + return responses_kwargs diff --git a/litellm/main.py b/litellm/main.py index 73db4a11cb7..df7fc040351 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -1577,6 +1577,7 @@ def completion( # type: ignore # noqa: PLR0915 merge_reasoning_content_in_choices=kwargs.get( "merge_reasoning_content_in_choices", None ), + force_reasoning_effort=kwargs.get("force_reasoning_effort"), use_litellm_proxy=kwargs.get("use_litellm_proxy", False), api_version=api_version, azure_ad_token=kwargs.get("azure_ad_token"), @@ -1592,6 +1593,11 @@ def completion( # type: ignore # noqa: PLR0915 tpm=kwargs.get("tpm"), rpm=kwargs.get("rpm"), ) + # Override reasoning_effort if force_reasoning_effort is configured + force_reasoning_effort = litellm_params.get("force_reasoning_effort") + if force_reasoning_effort: + optional_params["reasoning_effort"] = force_reasoning_effort + cast(LiteLLMLoggingObj, logging).update_environment_variables( model=model, user=user, diff --git a/litellm/responses/main.py b/litellm/responses/main.py index 0c79f99c9fd..5cf5d295cb0 100644 --- a/litellm/responses/main.py +++ b/litellm/responses/main.py @@ -862,6 +862,14 @@ def responses( if _mapped is not None: reasoning = _mapped local_vars["reasoning"] = _mapped + # Override reasoning effort if force_reasoning_effort is configured + force_reasoning_effort = local_vars.pop("force_reasoning_effort", None) + if force_reasoning_effort: + if isinstance(reasoning, dict): + reasoning = {**reasoning, "effort": force_reasoning_effort} + else: + reasoning = {"effort": force_reasoning_effort} + local_vars["reasoning"] = reasoning # Get ResponsesAPIOptionalRequestParams with only valid parameters response_api_optional_params: ResponsesAPIOptionalRequestParams = ( ResponsesAPIRequestUtils.get_requested_response_api_optional_param( diff --git a/litellm/types/router.py b/litellm/types/router.py index 6bd64915d79..514e39f8e4a 100644 --- a/litellm/types/router.py +++ b/litellm/types/router.py @@ -203,6 +203,7 @@ class GenericLiteLLMParams(CredentialLiteLLMParams, CustomPricingLiteLLMParams): use_litellm_proxy: Optional[bool] = False model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True) merge_reasoning_content_in_choices: Optional[bool] = False + force_reasoning_effort: Optional[str] = None model_info: Optional[Dict] = None mock_response: Optional[Union[str, ModelResponse, Exception, Any]] = None