fix(openai)!: send the whole reasoning_effort dict when chat is bridged to Responses (#48807)

* fix(openai): send the whole reasoning_effort dict when chat is bridged to Responses

* fix(openai): drop only a refused effort and honor drop params on the bridged reasoning dict

* fix(openai): drop generate_summary and forward a non-catalog model's reasoning dict whole on the bridge

* fix(responses): drop a mode or context the model does not take under drop_params

Read each OpenAI model's reasoning_modes and reasoning_contexts from the model map
when drop_params filters the bridged reasoning object, send no reasoning object when
validation dropped the effort, and ignore an additional_drop_params value that is not
a list.

* fix(responses): drop only a refused effort and forward an unlisted mode or context on the bridge

---------

Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com>
This commit is contained in:
m6atlas 2026-10-28 12:38:36 -07:00 • committed by GitHub
parent 41fcce62ff
commit 9675c13cb0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 1083 additions and 16 deletions

View file

@ -3,7 +3,6 @@ Handler for transforming /chat/completions api requests to litellm.responses req
"""
import json
import os
from collections.abc import AsyncIterator, Callable, Iterable, Iterator, Mapping, Sequence
from itertools import accumulate, chain
from types import MappingProxyType
@ -43,8 +42,8 @@ from litellm.responses.sse_output_recovery import (
)
from litellm.responses.utils import (
ResponsesAPIRequestUtils,
auto_reasoning_summary,
normalize_responses_api_stream_options,
opt_in_auto_reasoning_summary,
)
from litellm.types.llms.openai import (
ChatCompletionAnnotation,
@ -1745,19 +1744,11 @@ class LiteLLMResponsesTransformationHandler(CompletionTransformationBridge):
**cast(Reasoning, reasoning_effort) # cast-ok: dict is forwarded verbatim to the provider
)
# Check if auto-summary is enabled via flag or environment variable
# Priority: litellm.reasoning_auto_summary flag > LITELLM_REASONING_AUTO_SUMMARY env var
auto_summary_enabled: Final = (
litellm.reasoning_auto_summary or os.getenv("LITELLM_REASONING_AUTO_SUMMARY", "false").lower() == "true"
)
summary: Final = opt_in_auto_reasoning_summary(model, custom_llm_provider)
return (
Reasoning(
effort=reasoning_effort,
summary="detailed" if model is None else auto_reasoning_summary(model, custom_llm_provider),
)
if auto_summary_enabled
else Reasoning(effort=reasoning_effort)
Reasoning(effort=reasoning_effort)
if summary is None
else Reasoning(effort=reasoning_effort, summary=summary)
)
_map_reasoning_effort = map_reasoning_effort

View file

@ -6124,7 +6124,18 @@ def completion(
optional_params, rs_val = strip_reasoning_summary_aliases_from_optional_params(optional_params)
if isinstance(resolved_reasoning_effort, dict) and "summary" in resolved_reasoning_effort:
if custom_llm_provider == "openai" and isinstance(resolved_reasoning_effort, dict):
from litellm.responses.utils import openai_bridge_reasoning
optional_params["reasoning_effort"] = openai_bridge_reasoning(
requested=resolved_reasoning_effort,
effort_kept="reasoning_effort" in optional_params,
summary_alias=rs_val,
model=model,
drop_params=drop_unsupported_reasoning,
additional_drop_params=routing_kwargs.get("additional_drop_params"),
)
elif isinstance(resolved_reasoning_effort, dict) and "summary" in resolved_reasoning_effort:
optional_params["reasoning_effort"] = resolved_reasoning_effort
elif rs_val is not None:
eff: Final = optional_params.get("reasoning_effort", resolved_reasoning_effort)

View file

@ -35598,6 +35598,13 @@
"output_cost_per_token_batches": 5e-06,
"output_cost_per_token_flex": 5e-06,
"output_cost_per_token_priority": 2e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -35675,6 +35682,13 @@
"mode": "chat",
"output_cost_per_token": 1e-05,
"output_cost_per_token_priority": 2e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -35851,6 +35865,13 @@
"mode": "chat",
"output_cost_per_token": 1e-05,
"output_cost_per_token_priority": 2e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -35902,6 +35923,13 @@
"mode": "chat",
"output_cost_per_token": 1.4e-05,
"output_cost_per_token_priority": 2.8e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -36017,6 +36045,13 @@
"mode": "chat",
"output_cost_per_token": 1.4e-05,
"output_cost_per_token_priority": 2.8e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -36066,6 +36101,13 @@
"mode": "responses",
"output_cost_per_token": 0.000168,
"output_cost_per_token_batches": 8.4e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"pro"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -36107,6 +36149,13 @@
"mode": "responses",
"output_cost_per_token": 0.000168,
"output_cost_per_token_batches": 8.4e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"pro"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -36194,6 +36243,15 @@
"output_cost_per_token_flex": 2.5e-05,
"output_cost_per_token_priority": 0.0001,
"output_cost_per_token_ultrafast": 0.0003,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard",
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -36278,6 +36336,15 @@
"output_cost_per_token_above_272k_tokens_batches": 7.5e-06,
"output_cost_per_token_flex": 5e-06,
"output_cost_per_token_priority": 2e-05,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard",
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -36370,6 +36437,15 @@
"output_cost_per_token_above_272k_tokens_batches": 3.75e-07,
"output_cost_per_token_flex": 2.5e-07,
"output_cost_per_token_priority": 1e-06,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard",
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -36453,6 +36529,15 @@
"output_cost_per_token_above_272k_tokens_batches": 1.5e-05,
"output_cost_per_token_flex": 1e-05,
"output_cost_per_token_priority": 4e-05,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard",
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -36534,6 +36619,15 @@
"output_cost_per_token_above_272k_tokens_batches": 1.5e-05,
"output_cost_per_token_flex": 1e-05,
"output_cost_per_token_priority": 4e-05,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard",
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -36617,6 +36711,15 @@
"output_cost_per_token_above_272k_tokens_batches": 9e-06,
"output_cost_per_token_flex": 6e-06,
"output_cost_per_token_priority": 2.4e-05,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard",
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -36699,6 +36802,15 @@
"output_cost_per_token_above_272k_tokens_batches": 9e-07,
"output_cost_per_token_flex": 6e-07,
"output_cost_per_token_priority": 2.4e-06,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard",
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -36936,6 +37048,14 @@
"reasoning_effort_levels": [
"medium"
],
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard"
],
"source": "https://developers.openai.com/api/docs/pricing",
"supported_endpoints": [
"/v1/chat/completions",
@ -36983,6 +37103,14 @@
"output_cost_per_token_batches": 1.5e-05,
"output_cost_per_token_above_272k_tokens_batches": 2.25e-05,
"output_cost_per_token_priority": 7.5e-05,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -37045,6 +37173,14 @@
"output_cost_per_token_batches": 1.5e-05,
"output_cost_per_token_above_272k_tokens_batches": 2.25e-05,
"output_cost_per_token_priority": 7.5e-05,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -37099,6 +37235,14 @@
"output_cost_per_token_flex": 9e-05,
"output_cost_per_token_batches": 9e-05,
"output_cost_per_token_above_272k_tokens_batches": 0.000135,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -37150,6 +37294,14 @@
"output_cost_per_token_flex": 9e-05,
"output_cost_per_token_batches": 9e-05,
"output_cost_per_token_above_272k_tokens_batches": 0.000135,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -37209,6 +37361,14 @@
"output_cost_per_token_batches": 7.5e-06,
"output_cost_per_token_above_272k_tokens_batches": 1.125e-05,
"output_cost_per_token_priority": 3e-05,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"supported_endpoints": [
@ -37266,6 +37426,14 @@
"output_cost_per_token_batches": 7.5e-06,
"output_cost_per_token_above_272k_tokens_batches": 1.125e-05,
"output_cost_per_token_priority": 3e-05,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"supported_endpoints": [
@ -37315,6 +37483,14 @@
"output_cost_per_token_flex": 9e-05,
"output_cost_per_token_batches": 9e-05,
"output_cost_per_token_above_272k_tokens_batches": 0.000135,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -37367,6 +37543,14 @@
"output_cost_per_token_flex": 9e-05,
"output_cost_per_token_batches": 9e-05,
"output_cost_per_token_above_272k_tokens_batches": 0.000135,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -37421,6 +37605,14 @@
"output_cost_per_token_flex": 2.25e-06,
"output_cost_per_token_batches": 2.25e-06,
"output_cost_per_token_priority": 9e-06,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -37475,6 +37667,14 @@
"output_cost_per_token_flex": 2.25e-06,
"output_cost_per_token_batches": 2.25e-06,
"output_cost_per_token_priority": 9e-06,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -37526,6 +37726,14 @@
"output_cost_per_token": 1.25e-06,
"output_cost_per_token_flex": 6.25e-07,
"output_cost_per_token_batches": 6.25e-07,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -37578,6 +37786,14 @@
"output_cost_per_token": 1.25e-06,
"output_cost_per_token_flex": 6.25e-07,
"output_cost_per_token_batches": 6.25e-07,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -37624,6 +37840,13 @@
"mode": "responses",
"output_cost_per_token": 0.00012,
"output_cost_per_token_batches": 6e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"pro"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -37666,6 +37889,13 @@
"mode": "responses",
"output_cost_per_token": 0.00012,
"output_cost_per_token_batches": 6e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"pro"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -37716,6 +37946,13 @@
"output_cost_per_token_batches": 5e-06,
"output_cost_per_token_flex": 5e-06,
"output_cost_per_token_priority": 2e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -37826,6 +38063,13 @@
"mode": "responses",
"output_cost_per_token": 1.4e-05,
"output_cost_per_token_priority": 2.8e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -37908,6 +38152,13 @@
"output_cost_per_token_batches": 1e-06,
"output_cost_per_token_flex": 1e-06,
"output_cost_per_token_priority": 3.6e-06,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -37959,6 +38210,13 @@
"output_cost_per_token_batches": 1e-06,
"output_cost_per_token_flex": 1e-06,
"output_cost_per_token_priority": 3.6e-06,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -38007,6 +38265,13 @@
"output_cost_per_token": 4e-07,
"output_cost_per_token_batches": 2e-07,
"output_cost_per_token_flex": 2e-07,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -38055,6 +38320,13 @@
"output_cost_per_token": 4e-07,
"output_cost_per_token_batches": 2e-07,
"output_cost_per_token_flex": 2e-07,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -43821,6 +44093,13 @@
"output_cost_per_token_batches": 4e-06,
"output_cost_per_token_flex": 4e-06,
"output_cost_per_token_priority": 1.4e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -43929,6 +44208,13 @@
"output_cost_per_token_batches": 4e-06,
"output_cost_per_token_flex": 4e-06,
"output_cost_per_token_priority": 1.4e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -44008,6 +44294,13 @@
"mode": "responses",
"output_cost_per_token": 8e-05,
"output_cost_per_token_batches": 4e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"pro"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -44045,6 +44338,13 @@
"mode": "responses",
"output_cost_per_token": 8e-05,
"output_cost_per_token_batches": 4e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"pro"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -73288,6 +73588,15 @@
"output_cost_per_token_flex": 5e-06,
"output_cost_per_token_priority": 2e-05,
"output_cost_per_token_ultrafast": 6e-05,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard",
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -76787,6 +77096,15 @@
"output_cost_per_token_batches": 2.5e-06,
"output_cost_per_token_flex": 2.5e-06,
"output_cost_per_token_priority": 1e-05,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard",
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {"search_context_size_high":0.01,"search_context_size_low":0.01,"search_context_size_medium":0.01},

View file

@ -1,8 +1,10 @@
import base64
import os
import re
from collections.abc import Callable, Collection, Iterable, Mapping, Sequence
from functools import reduce
from typing import Any, Final, Optional, TypeVar, Union, cast, get_type_hints, overload
from types import MappingProxyType
from typing import Any, Final, Optional, TypeVar, Union, cast, get_args, get_type_hints, overload
from pydantic import BaseModel, TypeAdapter
from typing_extensions import TypedDict as ExtraItemsTypedDict
@ -14,6 +16,7 @@ from litellm.litellm_core_utils.core_helpers import as_str_mapping, attribute_or
from litellm.litellm_core_utils.dot_notation_indexing import delete_nested_value, is_nested_path
from litellm.litellm_core_utils.get_provider_specific_headers import ProviderSpecificHeaderUtils
from litellm.llms.base_llm.responses.transformation import BaseResponsesAPIConfig, ReasoningSummaryLevel
from litellm.llms.openai.chat.gpt_transformation import OpenAIGPTConfig
from litellm.types.llms.openai import (
AllMessageValues,
InputTokensDetails,
@ -29,6 +32,7 @@ from litellm.types.responses.main import DecodedResponseId
from litellm.types.utils import (
CacheCreationTokenDetails,
CompletionTokensDetailsWrapper,
ModelInfo,
PromptTokensDetailsWrapper,
ProviderSpecificHeader,
SpecialEnums,
@ -1382,3 +1386,98 @@ def auto_reasoning_summary(model: str, custom_llm_provider: object) -> Reasoning
provider, model.removeprefix(f"{provider}/")
)
return "detailed" if responses_config is None else responses_config.auto_reasoning_summary()
def opt_in_auto_reasoning_summary(model: str | None, custom_llm_provider: object) -> ReasoningSummaryLevel | None:
if not (litellm.reasoning_auto_summary or os.getenv("LITELLM_REASONING_AUTO_SUMMARY", "false").lower() == "true"):
return None
return "detailed" if model is None else auto_reasoning_summary(model, custom_llm_provider)
def reasoning_with_summary_default(
reasoning: Mapping[str, object], summary_alias: object, model: str, custom_llm_provider: object
) -> Mapping[str, object]:
if "summary" in reasoning:
return {**reasoning}
summary: Final = (
summary_alias if summary_alias is not None else opt_in_auto_reasoning_summary(model, custom_llm_provider)
)
return {**reasoning} if summary is None else {**reasoning, "summary": summary}
_SUMMARY_VALUES: Final = frozenset((*get_args(ReasoningSummaryLevel), None))
def _openai_model_info(model: str) -> ModelInfo | None:
from litellm.utils import ModelInfoLookupMiss, lookup_model_info_for_registration
model_info: Final = lookup_model_info_for_registration(model.removeprefix("responses/"), "openai")
return None if isinstance(model_info, ModelInfoLookupMiss) else model_info
def _declared_values(values: Sequence[str] | None) -> frozenset[str | None] | None:
return None if values is None else frozenset(values)
def _openai_reasoning_field_values(model: str) -> Mapping[str, frozenset[str | None] | None]:
model_info: Final = _openai_model_info(model)
if model_info is None:
return MappingProxyType({"summary": _SUMMARY_VALUES, "mode": None, "context": None})
if not model_info.get("supports_reasoning"):
return MappingProxyType({})
return MappingProxyType(
{
"summary": _SUMMARY_VALUES,
"mode": _declared_values(model_info.get("reasoning_modes")),
"context": _declared_values(model_info.get("reasoning_contexts")),
}
)
def _openai_accepts_reasoning_field(
key: str, value: object, values_by_field: Mapping[str, frozenset[str | None] | None]
) -> bool:
if key == "effort":
return True
if key not in values_by_field:
return False
allowed: Final = values_by_field[key]
return allowed is None or (isinstance(value, str | None) and value in allowed)
def _openai_accepted_reasoning(reasoning: Mapping[str, object], model: str) -> Mapping[str, object]:
values_by_field: Final = _openai_reasoning_field_values(model)
return {
key: value for key, value in reasoning.items() if _openai_accepts_reasoning_field(key, value, values_by_field)
}
def _additional_drop_paths(additional_drop_params: object) -> tuple[str, ...]:
if additional_drop_params.__class__ is not list:
return ()
return tuple(path for path in iter_items(additional_drop_params) if isinstance(path, str))
def openai_bridge_reasoning(
*,
requested: Mapping[str, object],
effort_kept: bool,
summary_alias: object,
model: str,
drop_params: bool,
additional_drop_params: object,
) -> Mapping[str, object] | None:
drop_paths: Final = _additional_drop_paths(additional_drop_params)
if "reasoning_effort" in drop_paths:
return None
kept: Final = requested if effort_kept else {key: value for key, value in requested.items() if key != "effort"}
with_summary: Final = reasoning_with_summary_default(kept, summary_alias, model, "openai")
accepted: Final = (
_openai_accepted_reasoning(with_summary, model)
if drop_params and OpenAIGPTConfig.is_openai_catalog_model(model)
else {**with_summary}
)
remaining: Final = as_str_mapping(
_apply_nested_drop_params({"reasoning_effort": accepted}, list(drop_paths)).get("reasoning_effort")
)
return remaining or None

View file

@ -225,6 +225,8 @@ class ProviderSpecificModelInfo(TypedDict, total=False):
supports_xhigh_reasoning_effort: bool | None
supports_max_reasoning_effort: bool | None
reasoning_effort_levels: ReadOnly[Sequence[str] | None]
reasoning_modes: ReadOnly[Sequence[str] | None]
reasoning_contexts: ReadOnly[Sequence[str] | None]
default_reasoning_effort: ReadOnly[Literal["none", "minimal", "low", "medium", "high", "xhigh"] | None]
supports_output_config: bool | None
supports_image_size: bool | None

View file

@ -7000,6 +7000,8 @@ def get_model_info_helper(
supports_xhigh_reasoning_effort=_model_info.get("supports_xhigh_reasoning_effort", None),
supports_max_reasoning_effort=_model_info.get("supports_max_reasoning_effort", None),
reasoning_effort_levels=_model_info.get("reasoning_effort_levels", None),
reasoning_modes=_model_info.get("reasoning_modes", None),
reasoning_contexts=_model_info.get("reasoning_contexts", None),
default_reasoning_effort=_model_info.get("default_reasoning_effort", None),
bedrock_output_config_effort_ceiling=_model_info.get("bedrock_output_config_effort_ceiling", None),
bedrock_converse_supports_strict_tools=_model_info.get("bedrock_converse_supports_strict_tools", None),

View file

@ -35598,6 +35598,13 @@
"output_cost_per_token_batches": 5e-06,
"output_cost_per_token_flex": 5e-06,
"output_cost_per_token_priority": 2e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -35675,6 +35682,13 @@
"mode": "chat",
"output_cost_per_token": 1e-05,
"output_cost_per_token_priority": 2e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -35851,6 +35865,13 @@
"mode": "chat",
"output_cost_per_token": 1e-05,
"output_cost_per_token_priority": 2e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -35902,6 +35923,13 @@
"mode": "chat",
"output_cost_per_token": 1.4e-05,
"output_cost_per_token_priority": 2.8e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -36017,6 +36045,13 @@
"mode": "chat",
"output_cost_per_token": 1.4e-05,
"output_cost_per_token_priority": 2.8e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -36066,6 +36101,13 @@
"mode": "responses",
"output_cost_per_token": 0.000168,
"output_cost_per_token_batches": 8.4e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"pro"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -36107,6 +36149,13 @@
"mode": "responses",
"output_cost_per_token": 0.000168,
"output_cost_per_token_batches": 8.4e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"pro"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -36194,6 +36243,15 @@
"output_cost_per_token_flex": 2.5e-05,
"output_cost_per_token_priority": 0.0001,
"output_cost_per_token_ultrafast": 0.0003,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard",
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -36278,6 +36336,15 @@
"output_cost_per_token_above_272k_tokens_batches": 7.5e-06,
"output_cost_per_token_flex": 5e-06,
"output_cost_per_token_priority": 2e-05,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard",
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -36370,6 +36437,15 @@
"output_cost_per_token_above_272k_tokens_batches": 3.75e-07,
"output_cost_per_token_flex": 2.5e-07,
"output_cost_per_token_priority": 1e-06,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard",
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -36453,6 +36529,15 @@
"output_cost_per_token_above_272k_tokens_batches": 1.5e-05,
"output_cost_per_token_flex": 1e-05,
"output_cost_per_token_priority": 4e-05,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard",
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -36534,6 +36619,15 @@
"output_cost_per_token_above_272k_tokens_batches": 1.5e-05,
"output_cost_per_token_flex": 1e-05,
"output_cost_per_token_priority": 4e-05,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard",
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -36617,6 +36711,15 @@
"output_cost_per_token_above_272k_tokens_batches": 9e-06,
"output_cost_per_token_flex": 6e-06,
"output_cost_per_token_priority": 2.4e-05,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard",
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -36699,6 +36802,15 @@
"output_cost_per_token_above_272k_tokens_batches": 9e-07,
"output_cost_per_token_flex": 6e-07,
"output_cost_per_token_priority": 2.4e-06,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard",
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -36936,6 +37048,14 @@
"reasoning_effort_levels": [
"medium"
],
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard"
],
"source": "https://developers.openai.com/api/docs/pricing",
"supported_endpoints": [
"/v1/chat/completions",
@ -36983,6 +37103,14 @@
"output_cost_per_token_batches": 1.5e-05,
"output_cost_per_token_above_272k_tokens_batches": 2.25e-05,
"output_cost_per_token_priority": 7.5e-05,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -37045,6 +37173,14 @@
"output_cost_per_token_batches": 1.5e-05,
"output_cost_per_token_above_272k_tokens_batches": 2.25e-05,
"output_cost_per_token_priority": 7.5e-05,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -37099,6 +37235,14 @@
"output_cost_per_token_flex": 9e-05,
"output_cost_per_token_batches": 9e-05,
"output_cost_per_token_above_272k_tokens_batches": 0.000135,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -37150,6 +37294,14 @@
"output_cost_per_token_flex": 9e-05,
"output_cost_per_token_batches": 9e-05,
"output_cost_per_token_above_272k_tokens_batches": 0.000135,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -37209,6 +37361,14 @@
"output_cost_per_token_batches": 7.5e-06,
"output_cost_per_token_above_272k_tokens_batches": 1.125e-05,
"output_cost_per_token_priority": 3e-05,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"supported_endpoints": [
@ -37266,6 +37426,14 @@
"output_cost_per_token_batches": 7.5e-06,
"output_cost_per_token_above_272k_tokens_batches": 1.125e-05,
"output_cost_per_token_priority": 3e-05,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"supported_endpoints": [
@ -37315,6 +37483,14 @@
"output_cost_per_token_flex": 9e-05,
"output_cost_per_token_batches": 9e-05,
"output_cost_per_token_above_272k_tokens_batches": 0.000135,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -37367,6 +37543,14 @@
"output_cost_per_token_flex": 9e-05,
"output_cost_per_token_batches": 9e-05,
"output_cost_per_token_above_272k_tokens_batches": 0.000135,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -37421,6 +37605,14 @@
"output_cost_per_token_flex": 2.25e-06,
"output_cost_per_token_batches": 2.25e-06,
"output_cost_per_token_priority": 9e-06,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -37475,6 +37667,14 @@
"output_cost_per_token_flex": 2.25e-06,
"output_cost_per_token_batches": 2.25e-06,
"output_cost_per_token_priority": 9e-06,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -37526,6 +37726,14 @@
"output_cost_per_token": 1.25e-06,
"output_cost_per_token_flex": 6.25e-07,
"output_cost_per_token_batches": 6.25e-07,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -37578,6 +37786,14 @@
"output_cost_per_token": 1.25e-06,
"output_cost_per_token_flex": 6.25e-07,
"output_cost_per_token_batches": 6.25e-07,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -37624,6 +37840,13 @@
"mode": "responses",
"output_cost_per_token": 0.00012,
"output_cost_per_token_batches": 6e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"pro"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -37666,6 +37889,13 @@
"mode": "responses",
"output_cost_per_token": 0.00012,
"output_cost_per_token_batches": 6e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"pro"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -37716,6 +37946,13 @@
"output_cost_per_token_batches": 5e-06,
"output_cost_per_token_flex": 5e-06,
"output_cost_per_token_priority": 2e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -37826,6 +38063,13 @@
"mode": "responses",
"output_cost_per_token": 1.4e-05,
"output_cost_per_token_priority": 2.8e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -37908,6 +38152,13 @@
"output_cost_per_token_batches": 1e-06,
"output_cost_per_token_flex": 1e-06,
"output_cost_per_token_priority": 3.6e-06,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -37959,6 +38210,13 @@
"output_cost_per_token_batches": 1e-06,
"output_cost_per_token_flex": 1e-06,
"output_cost_per_token_priority": 3.6e-06,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -38007,6 +38265,13 @@
"output_cost_per_token": 4e-07,
"output_cost_per_token_batches": 2e-07,
"output_cost_per_token_flex": 2e-07,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -38055,6 +38320,13 @@
"output_cost_per_token": 4e-07,
"output_cost_per_token_batches": 2e-07,
"output_cost_per_token_flex": 2e-07,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -43821,6 +44093,13 @@
"output_cost_per_token_batches": 4e-06,
"output_cost_per_token_flex": 4e-06,
"output_cost_per_token_priority": 1.4e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -43929,6 +44208,13 @@
"output_cost_per_token_batches": 4e-06,
"output_cost_per_token_flex": 4e-06,
"output_cost_per_token_priority": 1.4e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"standard"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -44008,6 +44294,13 @@
"mode": "responses",
"output_cost_per_token": 8e-05,
"output_cost_per_token_batches": 4e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"pro"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -44045,6 +44338,13 @@
"mode": "responses",
"output_cost_per_token": 8e-05,
"output_cost_per_token_batches": 4e-05,
"reasoning_contexts": [
"auto",
"current_turn"
],
"reasoning_modes": [
"pro"
],
"search_context_cost_per_query": {
"search_context_size_high": 0.01,
"search_context_size_low": 0.01,
@ -73288,6 +73588,15 @@
"output_cost_per_token_flex": 5e-06,
"output_cost_per_token_priority": 2e-05,
"output_cost_per_token_ultrafast": 6e-05,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard",
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {
@ -76787,6 +77096,15 @@
"output_cost_per_token_batches": 2.5e-06,
"output_cost_per_token_flex": 2.5e-06,
"output_cost_per_token_priority": 1e-05,
"reasoning_contexts": [
"auto",
"current_turn",
"all_turns"
],
"reasoning_modes": [
"standard",
"pro"
],
"regional_processing_uplift_multiplier_eu": 1.1,
"regional_processing_uplift_multiplier_us": 1.1,
"search_context_cost_per_query": {"search_context_size_high":0.01,"search_context_size_low":0.01,"search_context_size_medium":0.01},

View file

@ -2127,6 +2127,330 @@ def test_responses_bridge_preserves_reasoning_effort_with_drop_params(
assert request_body["reasoning"] == {"effort": "high"}
@pytest.fixture
def openai_responses_route(
restore_model_registry, respx_mock: respx.MockRouter, monkeypatch: pytest.MonkeyPatch
) -> respx.Route:
monkeypatch.setattr(litellm, "disable_aiohttp_transport", True)
monkeypatch.delenv("LITELLM_REASONING_AUTO_SUMMARY", raising=False)
litellm.register_model(
{
"gpt-6.1-terra": {
"litellm_provider": "openai",
"mode": "chat",
"supports_reasoning": True,
"supports_minimal_reasoning_effort": False,
"reasoning_modes": ["standard", "pro"],
"reasoning_contexts": ["auto", "current_turn", "all_turns"],
"input_cost_per_token": 0.0,
"output_cost_per_token": 0.0,
}
},
persist_across_reloads=False,
)
return respx_mock.post("https://api.openai.com/v1/responses").respond(
json={
"id": "resp_test",
"object": "response",
"created_at": 1734366691,
"status": "completed",
"model": "gpt-6.1-terra",
"output": [
{
"type": "message",
"id": "msg_1",
"status": "completed",
"role": "assistant",
"content": [{"type": "output_text", "text": "Done.", "annotations": []}],
}
],
"usage": {"input_tokens": 1, "output_tokens": 1, "total_tokens": 2},
}
)
@pytest.mark.parametrize(
("reasoning_effort", "summary_alias", "expected_reasoning"),
[
(
{"effort": "low", "mode": "pro", "context": "current_turn"},
{},
{"effort": "low", "mode": "pro", "context": "current_turn"},
),
(
{"effort": "low", "mode": "pro", "context": "current_turn"},
{"reasoning_summary": "auto"},
{"effort": "low", "mode": "pro", "context": "current_turn", "summary": "auto"},
),
(
{"effort": "low", "mode": "pro", "summary": "concise"},
{"reasoningSummary": "auto"},
{"effort": "low", "mode": "pro", "summary": "concise"},
),
(
{"effort": "low", "context": "current_turn", "summary": None},
{"reasoning_summary": "auto"},
{"effort": "low", "context": "current_turn", "summary": None},
),
({"mode": "pro", "context": "all_turns"}, {}, {"mode": "pro", "context": "all_turns"}),
("low", {}, {"effort": "low"}),
],
)
def test_openai_responses_bridge_sends_the_whole_reasoning_effort_dict(
reasoning_effort: str | dict[str, str | None],
summary_alias: dict[str, str],
expected_reasoning: dict[str, str | None],
openai_responses_route: respx.Route,
):
caller_reasoning_effort: Final = copy.deepcopy(reasoning_effort)
litellm.completion(
model="openai/responses/gpt-6.1-terra",
messages=[{"role": "user", "content": "hello"}],
reasoning_effort=reasoning_effort,
api_key="fake-key",
**summary_alias,
)
request_body: Final = json.loads(openai_responses_route.calls[0].request.content)
assert request_body["reasoning"] == expected_reasoning
assert reasoning_effort == caller_reasoning_effort
@pytest.mark.parametrize(
"reasoning_effort, summary_alias, expected_reasoning",
[
(
{"effort": "minimal", "mode": "pro", "context": "current_turn"},
{},
{"mode": "pro", "context": "current_turn"},
),
({"effort": "minimal", "mode": "pro", "summary": "auto"}, {}, {"mode": "pro", "summary": "auto"}),
({"effort": "minimal", "mode": "pro"}, {"reasoning_summary": "auto"}, {"mode": "pro", "summary": "auto"}),
],
)
def test_openai_responses_bridge_drops_only_a_refused_effort_under_drop_params(
reasoning_effort: dict[str, str],
summary_alias: dict[str, str],
expected_reasoning: dict[str, str],
openai_responses_route: respx.Route,
):
litellm.completion(
model="openai/responses/gpt-6.1-terra",
messages=[{"role": "user", "content": "hello"}],
reasoning_effort=reasoning_effort,
drop_params=True,
api_key="fake-key",
**summary_alias,
)
request_body: Final = json.loads(openai_responses_route.calls[0].request.content)
assert request_body["reasoning"] == expected_reasoning
def test_openai_responses_bridge_drops_a_reasoning_effort_dict_for_a_model_without_reasoning(
openai_responses_route: respx.Route,
):
litellm.completion(
model="openai/responses/gpt-4.1",
messages=[{"role": "user", "content": "hello"}],
reasoning_effort={"effort": "low", "mode": "pro", "context": "current_turn"},
drop_params=True,
api_key="fake-key",
)
request_body: Final = json.loads(openai_responses_route.calls[0].request.content)
assert "reasoning" not in request_body
@pytest.mark.parametrize(
"drop_params, expected_reasoning",
[
(True, {"effort": "low", "context": "current_turn"}),
(
False,
{
"effort": "low",
"exclude": False,
"mode": "turbo",
"context": "current_turn",
"generate_summary": "concise",
},
),
],
)
def test_openai_responses_bridge_drops_reasoning_fields_openai_refuses_only_under_drop_params(
drop_params: bool,
expected_reasoning: dict[str, object],
openai_responses_route: respx.Route,
):
litellm.completion(
model="openai/responses/gpt-6.1-terra",
messages=[{"role": "user", "content": "hello"}],
reasoning_effort={
"effort": "low",
"exclude": False,
"mode": "turbo",
"context": "current_turn",
"generate_summary": "concise",
},
drop_params=drop_params,
api_key="fake-key",
)
request_body: Final = json.loads(openai_responses_route.calls[0].request.content)
assert request_body["reasoning"] == expected_reasoning
@pytest.mark.parametrize(
"declared, drop_params, expected_reasoning",
[
(
{"reasoning_modes": ["standard"], "reasoning_contexts": ["auto", "current_turn"]},
True,
{"effort": "low"},
),
(
{"reasoning_modes": ["pro"], "reasoning_contexts": ["all_turns"]},
True,
{"effort": "low", "mode": "pro", "context": "all_turns"},
),
({}, True, {"effort": "low", "mode": "pro", "context": "all_turns"}),
({"reasoning_modes": ["standard"]}, True, {"effort": "low", "context": "all_turns"}),
(
{"reasoning_modes": ["standard"], "reasoning_contexts": ["auto", "current_turn"]},
False,
{"effort": "low", "mode": "pro", "context": "all_turns"},
),
],
)
def test_openai_responses_bridge_drops_a_mode_or_context_the_model_does_not_list_only_under_drop_params(
declared: dict[str, list[str]],
drop_params: bool,
expected_reasoning: dict[str, str],
openai_responses_route: respx.Route,
):
litellm.register_model(
{
"gpt-6.1-reasoning-test": {
"litellm_provider": "openai",
"mode": "chat",
"supports_reasoning": True,
"input_cost_per_token": 0.0,
"output_cost_per_token": 0.0,
**declared,
}
},
persist_across_reloads=False,
)
litellm.completion(
model="openai/responses/gpt-6.1-reasoning-test",
messages=[{"role": "user", "content": "hello"}],
reasoning_effort={"effort": "low", "mode": "pro", "context": "all_turns"},
drop_params=drop_params,
api_key="fake-key",
)
request_body: Final = json.loads(openai_responses_route.calls[0].request.content)
assert request_body["reasoning"] == expected_reasoning
def test_openai_responses_bridge_forwards_mode_and_context_under_drop_params_for_a_catalog_model_with_no_cost_entry(
openai_responses_route: respx.Route,
):
litellm.open_ai_chat_completion_models.add("unpriced-reasoning-model")
litellm.completion(
model="openai/responses/unpriced-reasoning-model",
messages=[{"role": "user", "content": "hello"}],
reasoning_effort={"effort": "low", "mode": "pro", "context": "all_turns", "generate_summary": "auto"},
drop_params=True,
api_key="fake-key",
)
request_body: Final = json.loads(openai_responses_route.calls[0].request.content)
assert request_body["reasoning"] == {"effort": "low", "mode": "pro", "context": "all_turns"}
def test_openai_responses_bridge_forwards_a_reasoning_effort_dict_whole_for_a_model_outside_the_openai_catalog(
openai_responses_route: respx.Route,
):
reasoning_effort: Final = {"effort": "high", "exclude": True, "budget_tokens": 2000}
litellm.completion(
model="openai/responses/my-proxy-alias",
messages=[{"role": "user", "content": "hello"}],
reasoning_effort=reasoning_effort,
drop_params=True,
api_key="fake-key",
)
request_body: Final = json.loads(openai_responses_route.calls[0].request.content)
assert request_body["reasoning"] == reasoning_effort
@pytest.mark.parametrize(
"additional_drop_params, summary_alias, expected_reasoning",
[
(["reasoning_effort.mode"], {}, {"effort": "low", "context": "current_turn"}),
(
["reasoning_effort.summary"],
{"reasoning_summary": "auto"},
{"effort": "low", "mode": "pro", "context": "current_turn"},
),
(["reasoning_effort"], {"reasoning_summary": "auto"}, None),
("reasoning_effort", {}, {"effort": "low", "mode": "pro", "context": "current_turn"}),
],
)
def test_openai_responses_bridge_applies_additional_drop_params_to_a_reasoning_effort_dict(
additional_drop_params: list[str] | str,
summary_alias: dict[str, str],
expected_reasoning: dict[str, str] | None,
openai_responses_route: respx.Route,
):
litellm.completion(
model="openai/responses/gpt-6.1-terra",
messages=[{"role": "user", "content": "hello"}],
reasoning_effort={"effort": "low", "mode": "pro", "context": "current_turn"},
additional_drop_params=additional_drop_params,
api_key="fake-key",
**summary_alias,
)
request_body: Final = json.loads(openai_responses_route.calls[0].request.content)
assert request_body.get("reasoning") == expected_reasoning
def test_openai_responses_bridge_gives_a_reasoning_effort_dict_the_auto_summary_a_string_gets(
openai_responses_route: respx.Route, monkeypatch: pytest.MonkeyPatch
):
monkeypatch.setenv("LITELLM_REASONING_AUTO_SUMMARY", "true")
litellm.completion(
model="openai/responses/gpt-6.1-terra",
messages=[{"role": "user", "content": "hello"}],
reasoning_effort="low",
api_key="fake-key",
)
litellm.completion(
model="openai/responses/gpt-6.1-terra",
messages=[{"role": "user", "content": "hello"}],
reasoning_effort={"effort": "low", "mode": "pro", "context": "current_turn"},
api_key="fake-key",
)
string_reasoning, dict_reasoning = (
json.loads(call.request.content)["reasoning"] for call in openai_responses_route.calls
)
assert string_reasoning["summary"] is not None
assert dict_reasoning == {
"effort": "low",
"mode": "pro",
"context": "current_turn",
"summary": string_reasoning["summary"],
}
_BRIDGED_RESPONSE_ANSWER: Final = json.dumps(
{
"id": "resp_slow",

View file

@ -1195,6 +1195,8 @@ _COST_MAP_ROW_SCHEMA: Final = {
"type": "array",
"items": {"type": "string", "enum": ["none", "minimal", "low", "medium", "high", "xhigh", "max"]},
},
"reasoning_modes": {"type": "array", "items": {"type": "string"}},
"reasoning_contexts": {"type": "array", "items": {"type": "string"}},
"default_reasoning_effort": {
"type": "string",
"enum": ["none", "minimal", "low", "medium", "high", "xhigh"],