mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-11 08:38:24 +00:00
Merge 812f0054a4 into e1580b7b83
This commit is contained in:
commit
40706c3668
18 changed files with 570 additions and 55 deletions
|
|
@ -24,6 +24,7 @@ class AzureOpenAIGPT5Config(AzureOpenAIConfig, OpenAIGPT5Config):
|
|||
|
||||
GPT5_SERIES_ROUTE = "gpt5_series/"
|
||||
OPT_IN_REASONING_EFFORTS: ClassVar[frozenset[str]] = frozenset(("xhigh", "none"))
|
||||
LOOKS_UP_BASE_MODEL: ClassVar[bool] = True
|
||||
|
||||
@classmethod
|
||||
def _model_map_lookup_name(cls, model: str) -> str:
|
||||
|
|
@ -88,7 +89,7 @@ class AzureOpenAIGPT5Config(AzureOpenAIConfig, OpenAIGPT5Config):
|
|||
drop_params: bool,
|
||||
api_version: str = "",
|
||||
*,
|
||||
declared_reasoning_efforts: Collection[str] = (),
|
||||
declared_reasoning_efforts: Collection[str] | None = None,
|
||||
) -> dict:
|
||||
return OpenAIGPT5Config.map_openai_params(
|
||||
self,
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ class AzureAIStudioConfig(OpenAIConfig):
|
|||
model: str,
|
||||
drop_params: bool,
|
||||
*,
|
||||
declared_reasoning_efforts: Collection[str] = (),
|
||||
declared_reasoning_efforts: Collection[str] | None = None,
|
||||
) -> dict[str, object]: # mutable-ok: OpenAIConfig.map_openai_params signature
|
||||
if not azureAIGPT5Config.is_model_gpt_5_model(model):
|
||||
return super().map_openai_params(
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ from litellm.utils import (
|
|||
)
|
||||
|
||||
from ..common_utils import (
|
||||
DROP_DISABLED_REASONING_EFFORT_WARNING,
|
||||
BedrockError,
|
||||
BedrockModelInfo,
|
||||
bedrock_cache_point_ttl_supported,
|
||||
|
|
@ -1192,11 +1193,7 @@ class AmazonConverseConfig(BaseConfig):
|
|||
),
|
||||
status_code=400,
|
||||
)
|
||||
verbose_logger.debug(
|
||||
"Dropping unsupported `reasoning_effort=%s` for Bedrock model=%s.",
|
||||
value,
|
||||
model,
|
||||
)
|
||||
verbose_logger.warning(DROP_DISABLED_REASONING_EFFORT_WARNING, "reasoning_effort", value, model)
|
||||
elif param == "reasoning_effort" and isinstance(value, str):
|
||||
self._handle_reasoning_effort_parameter(
|
||||
model=model, reasoning_effort=value, optional_params=optional_params
|
||||
|
|
|
|||
|
|
@ -1314,6 +1314,12 @@ def _mantle_api_base_from_env() -> str | None:
|
|||
return next((base[: -len(suffix)] for suffix in _MANTLE_OPENAI_BASE_SUFFIXES if base.endswith(suffix)), base)
|
||||
|
||||
|
||||
DROP_DISABLED_REASONING_EFFORT_WARNING: Final = (
|
||||
"Dropping %s=%r for Bedrock model=%r (drop_params=True) because the model map marks that level unsupported. "
|
||||
"Send a level the model supports."
|
||||
)
|
||||
|
||||
|
||||
def bedrock_reasoning_effort_disabled(model: str, effort: str) -> bool:
|
||||
from litellm.utils import is_explicitly_disabled_factory
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ from litellm.llms.base_llm.responses.codex_compat import drop_unsupported_tools,
|
|||
from litellm.llms.bedrock.base_aws_llm import BaseAWSLLM
|
||||
from litellm.llms.bedrock.common_utils import (
|
||||
BEDROCK_CHAT_COMPLETIONS_ROUTE_PREFIX,
|
||||
DROP_DISABLED_REASONING_EFFORT_WARNING,
|
||||
BedrockError,
|
||||
bedrock_reasoning_effort_disabled,
|
||||
bedrock_supports_openai_responses,
|
||||
|
|
@ -173,7 +174,7 @@ def _without_disabled_reasoning_effort(
|
|||
),
|
||||
status_code=400,
|
||||
)
|
||||
verbose_logger.debug("Dropping unsupported `reasoning.effort=%s` for Bedrock model=%s.", effort, model)
|
||||
verbose_logger.warning(DROP_DISABLED_REASONING_EFFORT_WARNING, "reasoning.effort", effort, model)
|
||||
rest: Final = {key: value for key, value in reasoning.items() if key != "effort"}
|
||||
without_reasoning: Final = {key: value for key, value in params.items() if key != "reasoning"}
|
||||
return {**without_reasoning, "reasoning": rest} if rest else without_reasoning
|
||||
|
|
|
|||
|
|
@ -5,8 +5,10 @@ from collections.abc import Collection
|
|||
from typing import ClassVar, Final
|
||||
|
||||
import litellm
|
||||
from litellm._logging import verbose_logger
|
||||
from litellm.utils import (
|
||||
declared_value_factory,
|
||||
has_catalog_entry,
|
||||
is_explicitly_disabled_factory,
|
||||
supports_factory,
|
||||
)
|
||||
|
|
@ -16,6 +18,25 @@ from .gpt_transformation import OpenAIGPTConfig
|
|||
_GPT_SERIES_VERSION: Final = re.compile(r"^gpt-(\d+)(?:\.(\d+))?(?=[.-]|$)")
|
||||
|
||||
|
||||
def refused_reasoning_effort_warning(*, suggest_declared_levels: bool, suggest_base_model: bool) -> str:
|
||||
return "".join(
|
||||
(
|
||||
"Dropping %s=%r for model=%r (drop_params=True) because the model map does not mark that level "
|
||||
"supported for %r.",
|
||||
(
|
||||
" If the deployment supports it, list every level it supports in model_info.reasoning_effort_levels."
|
||||
if suggest_declared_levels
|
||||
else ""
|
||||
),
|
||||
(
|
||||
" If the deployment name is not its underlying model, set model_info.base_model to that model."
|
||||
if suggest_base_model
|
||||
else ""
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def _catalogue_declares_default_effort() -> bool:
|
||||
"""Whether the loaded cost map carries default_reasoning_effort for ANY entry.
|
||||
|
||||
|
|
@ -93,6 +114,7 @@ class OpenAIGPT5Config(OpenAIGPTConfig):
|
|||
|
||||
OPT_IN_REASONING_EFFORTS: ClassVar[frozenset[str]] = frozenset(("xhigh",))
|
||||
ROUTE_SPECIFIC_REASONING_EFFORTS: ClassVar[frozenset[str]] = frozenset(("max",))
|
||||
LOOKS_UP_BASE_MODEL: ClassVar[bool] = False
|
||||
|
||||
@classmethod
|
||||
def is_model_gpt_5_model(cls, model: str) -> bool:
|
||||
|
|
@ -307,7 +329,7 @@ class OpenAIGPT5Config(OpenAIGPTConfig):
|
|||
model: str,
|
||||
drop_params: bool,
|
||||
*,
|
||||
declared_reasoning_efforts: Collection[str] = (),
|
||||
declared_reasoning_efforts: Collection[str] | None = None,
|
||||
) -> dict:
|
||||
if self.is_model_gpt_5_search_model(model):
|
||||
if "max_tokens" in non_default_params:
|
||||
|
|
@ -338,10 +360,21 @@ class OpenAIGPT5Config(OpenAIGPTConfig):
|
|||
|
||||
if (
|
||||
requested_effort is not None
|
||||
and requested_effort not in declared_reasoning_efforts
|
||||
and requested_effort not in (declared_reasoning_efforts or ())
|
||||
and self.refuses_reasoning_effort_level(model, requested_effort)
|
||||
):
|
||||
if litellm.drop_params or drop_params:
|
||||
verbose_logger.warning(
|
||||
refused_reasoning_effort_warning(
|
||||
suggest_declared_levels=declared_reasoning_efforts is not None,
|
||||
suggest_base_model=self.LOOKS_UP_BASE_MODEL
|
||||
and not has_catalog_entry(self._model_map_lookup_name(model), None),
|
||||
),
|
||||
"reasoning_effort",
|
||||
requested_effort,
|
||||
model,
|
||||
self._model_map_lookup_name(model),
|
||||
)
|
||||
non_default_params.pop("reasoning_effort", None)
|
||||
optional_params.pop("reasoning_effort", None)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ from litellm.litellm_core_utils.url_utils import encode_url_path_segment, join_a
|
|||
from litellm.llms.base_llm.responses.transformation import BaseResponsesAPIConfig
|
||||
from litellm.llms.openai.chat.gpt_reasoning_transformation import (
|
||||
is_gpt_reasoning_series_name,
|
||||
refused_reasoning_effort_warning,
|
||||
unsupported_reasoning_effort_message,
|
||||
)
|
||||
from litellm.responses.litellm_completion_transformation.custom_tools import TOOL_CALL_ITEM_ID_PREFIX_BY_TYPE
|
||||
|
|
@ -50,6 +51,9 @@ from ..common_utils import OpenAIError
|
|||
from ..workload_identity import get_workload_identity_bearer_token, resolve_openai_workload_identity_config
|
||||
|
||||
OPENAI_RESPONSES_API_MIN_MAX_OUTPUT_TOKENS: Final = 16
|
||||
DROP_UNSUPPORTED_REASONING_WARNING: Final = (
|
||||
"Dropping reasoning=%r for model=%r (drop_params=True) because its model cost map entry lacks supports_reasoning."
|
||||
)
|
||||
_RAW_RESPONSE_JSON: Final = TypeAdapter(dict[str, object], config=ConfigDict(strict=True))
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
@ -324,12 +328,19 @@ class OpenAIResponsesAPIConfig(BaseResponsesAPIConfig):
|
|||
return declared
|
||||
return info["key"] in _bundled_openai_reasoning_models()
|
||||
|
||||
@staticmethod
|
||||
def _is_known_non_reasoning_model(model: str) -> bool:
|
||||
from litellm.utils import is_known_non_reasoning_model
|
||||
|
||||
return is_known_non_reasoning_model(model.split("/")[-1], LlmProviders.OPENAI.value)
|
||||
|
||||
@staticmethod
|
||||
def _requests_reasoning_effort(reasoning: object) -> bool:
|
||||
effort: Final = (
|
||||
reasoning.get("effort") if isinstance(reasoning, Mapping) else getattr(reasoning, "effort", None)
|
||||
)
|
||||
return effort is not None
|
||||
return OpenAIResponsesAPIConfig._requested_effort(reasoning) is not None
|
||||
|
||||
@staticmethod
|
||||
def _requested_effort(reasoning: object) -> object:
|
||||
return reasoning.get("effort") if isinstance(reasoning, Mapping) else getattr(reasoning, "effort", None)
|
||||
|
||||
@staticmethod
|
||||
def _enforce_min_max_output_tokens(max_output_tokens: "int | None") -> "int | None":
|
||||
|
|
@ -371,17 +382,20 @@ class OpenAIResponsesAPIConfig(BaseResponsesAPIConfig):
|
|||
) -> dict:
|
||||
return dict(
|
||||
self.map_openai_params_for_deployment(
|
||||
response_api_optional_params, model, drop_params, declared_reasoning_efforts=()
|
||||
response_api_optional_params, model, drop_params, declared_reasoning_efforts=None
|
||||
)
|
||||
)
|
||||
|
||||
def honors_declared_reasoning_efforts(self) -> bool:
|
||||
return type(self).map_openai_params is OpenAIResponsesAPIConfig.map_openai_params
|
||||
|
||||
def map_openai_params_for_deployment(
|
||||
self,
|
||||
response_api_optional_params: ResponsesAPIOptionalRequestParams,
|
||||
model: str,
|
||||
drop_params: bool,
|
||||
*,
|
||||
declared_reasoning_efforts: Collection[str],
|
||||
declared_reasoning_efforts: Collection[str] | None,
|
||||
) -> Mapping[str, object]:
|
||||
"""No mapping applied since inputs are in OpenAI spec already.
|
||||
|
||||
|
|
@ -401,7 +415,9 @@ class OpenAIResponsesAPIConfig(BaseResponsesAPIConfig):
|
|||
and not self._supports_reasoning_param(model=model)
|
||||
):
|
||||
if drop_params or litellm.drop_params:
|
||||
params.pop("reasoning", None)
|
||||
dropped_reasoning: Final = params.pop("reasoning", None)
|
||||
if self._requested_effort(dropped_reasoning) != "none" or not self._is_known_non_reasoning_model(model):
|
||||
verbose_logger.warning(DROP_UNSUPPORTED_REASONING_WARNING, dropped_reasoning, model)
|
||||
else:
|
||||
raise litellm.UnsupportedParamsError(
|
||||
message=(
|
||||
|
|
@ -419,7 +435,7 @@ class OpenAIResponsesAPIConfig(BaseResponsesAPIConfig):
|
|||
if (
|
||||
isinstance(reasoning, dict)
|
||||
and isinstance(requested_effort, str)
|
||||
and requested_effort not in declared_reasoning_efforts
|
||||
and requested_effort not in (declared_reasoning_efforts or ())
|
||||
and self._effort_level_is_disabled(lookup_name, requested_effort)
|
||||
):
|
||||
if not (drop_params or litellm.drop_params):
|
||||
|
|
@ -427,6 +443,15 @@ class OpenAIResponsesAPIConfig(BaseResponsesAPIConfig):
|
|||
message=unsupported_reasoning_effort_message("reasoning.effort", requested_effort, model),
|
||||
status_code=400,
|
||||
)
|
||||
verbose_logger.warning(
|
||||
refused_reasoning_effort_warning(
|
||||
suggest_declared_levels=declared_reasoning_efforts is not None, suggest_base_model=False
|
||||
),
|
||||
"reasoning.effort",
|
||||
requested_effort,
|
||||
model,
|
||||
lookup_name,
|
||||
)
|
||||
remaining: Final = reasoning.copy()
|
||||
remaining.pop("effort", None)
|
||||
if remaining:
|
||||
|
|
|
|||
|
|
@ -131,14 +131,14 @@ def _provider_mapped_params(
|
|||
params: ResponsesAPIOptionalRequestParams,
|
||||
model: str,
|
||||
drop_params: bool,
|
||||
declared_reasoning_efforts: Collection[str],
|
||||
declared_reasoning_efforts: Collection[str] | None,
|
||||
) -> Mapping[str, object]:
|
||||
from litellm.llms.openai.responses.transformation import OpenAIResponsesAPIConfig
|
||||
|
||||
if (
|
||||
declared_reasoning_efforts
|
||||
declared_reasoning_efforts is not None
|
||||
and isinstance(config, OpenAIResponsesAPIConfig)
|
||||
and type(config).map_openai_params is OpenAIResponsesAPIConfig.map_openai_params
|
||||
and config.honors_declared_reasoning_efforts()
|
||||
):
|
||||
return config.map_openai_params_for_deployment(
|
||||
params, model, drop_params, declared_reasoning_efforts=declared_reasoning_efforts
|
||||
|
|
@ -287,7 +287,7 @@ class ResponsesAPIRequestUtils:
|
|||
response_api_optional_params: ResponsesAPIOptionalRequestParams,
|
||||
allowed_openai_params: list[str] | None = None,
|
||||
drop_params: bool | None = None,
|
||||
declared_reasoning_efforts: Collection[str] = (),
|
||||
declared_reasoning_efforts: Collection[str] | None = None,
|
||||
) -> dict:
|
||||
"""
|
||||
Get optional parameters for the responses API.
|
||||
|
|
|
|||
|
|
@ -426,6 +426,27 @@ def _log_remap(
|
|||
logged_deployment_id: Final = _single_line(deployment_id)
|
||||
logged_model_group: Final = _single_line(model_group)
|
||||
logged_deployment_model: Final = _single_line(deployment_model)
|
||||
extra: Final = MappingProxyType(
|
||||
{
|
||||
"reasoning_effort_deployment_id": logged_deployment_id,
|
||||
"reasoning_effort_model_group": logged_model_group,
|
||||
"reasoning_effort_deployment_model": logged_deployment_model,
|
||||
"reasoning_effort_requested": logged_requested_level,
|
||||
"reasoning_effort_sent": logged_sent_level,
|
||||
"reasoning_effort_rounding": rounding,
|
||||
}
|
||||
)
|
||||
if sent_level is None and requested_level != _DEFAULT_EFFORT:
|
||||
verbose_logger.warning(
|
||||
"Dropping reasoning level %s (drop_params=True) for deployment %s, model group %s, model %s because its "
|
||||
"model_info.reasoning_effort_levels does not list that level. List every level the deployment supports.",
|
||||
logged_requested_level,
|
||||
logged_deployment_id,
|
||||
logged_model_group,
|
||||
logged_deployment_model,
|
||||
extra=extra,
|
||||
)
|
||||
return
|
||||
verbose_logger.info(
|
||||
"reasoning_effort %s sent as %s (rounding %s) to deployment %s, model group %s, model %s",
|
||||
logged_requested_level,
|
||||
|
|
@ -434,14 +455,7 @@ def _log_remap(
|
|||
logged_deployment_id,
|
||||
logged_model_group,
|
||||
logged_deployment_model,
|
||||
extra={
|
||||
"reasoning_effort_deployment_id": logged_deployment_id,
|
||||
"reasoning_effort_model_group": logged_model_group,
|
||||
"reasoning_effort_deployment_model": logged_deployment_model,
|
||||
"reasoning_effort_requested": logged_requested_level,
|
||||
"reasoning_effort_sent": logged_sent_level,
|
||||
"reasoning_effort_rounding": rounding,
|
||||
},
|
||||
extra=extra,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ from litellm.litellm_core_utils.fallback_generalizations import (
|
|||
match_capability_generalizations,
|
||||
match_fill_missing_generalizations,
|
||||
)
|
||||
from litellm.litellm_core_utils.get_model_cost_map import AliasedCostEntry
|
||||
from litellm.litellm_core_utils.get_model_cost_map import AliasedCostEntry, GetModelCostMap
|
||||
from litellm.litellm_core_utils.model_param_helper import peek_reasoning_summary_aliases
|
||||
from litellm.litellm_core_utils.offline_mode import is_offline_mode
|
||||
from litellm.litellm_core_utils.sensitive_data_masker import redact_credentials_in_payload
|
||||
|
|
@ -4974,6 +4974,46 @@ def _declared_reasoning_efforts(model_info: Mapping[str, object] | None) -> tupl
|
|||
return deployment_reasoning_effort_levels(model_info) or ()
|
||||
|
||||
|
||||
_DROPPED_REASONING_PARAMS: Final = ("reasoning_effort", "thinking")
|
||||
|
||||
|
||||
def _turns_reasoning_off(value: object) -> bool:
|
||||
match value:
|
||||
case "none" | {"effort": "none"} | {"type": "disabled"}:
|
||||
return True
|
||||
case _:
|
||||
return False
|
||||
|
||||
|
||||
def is_known_non_reasoning_model(model: str, custom_llm_provider: str | None) -> bool:
|
||||
info: Final = lookup_model_info_for_registration(model, custom_llm_provider)
|
||||
if isinstance(info, ModelInfoLookupMiss) or info.get("supports_reasoning") is True:
|
||||
return False
|
||||
return info.get("supports_reasoning") is False or info["key"] in GetModelCostMap.loaded_model_cost_map()
|
||||
|
||||
|
||||
def has_catalog_entry(model: str, custom_llm_provider: str | None) -> bool:
|
||||
info: Final = lookup_model_info_for_registration(model, custom_llm_provider)
|
||||
return not isinstance(info, ModelInfoLookupMiss) and info["key"] in GetModelCostMap.loaded_model_cost_map()
|
||||
|
||||
|
||||
def _dropped_reasoning_warning(model: str, custom_llm_provider: str | None) -> str:
|
||||
return "".join(
|
||||
(
|
||||
"Dropping %s=%r for model=%r (drop_params=True) because %s does not support %s for it.",
|
||||
(
|
||||
""
|
||||
if has_catalog_entry(model, custom_llm_provider)
|
||||
else " If the deployment is a reasoning model, set model_info.base_model to its underlying model."
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def _warns_on_dropped_reasoning(value: object, model: str, custom_llm_provider: str | None) -> bool:
|
||||
return not (_turns_reasoning_off(value) and is_known_non_reasoning_model(model, custom_llm_provider))
|
||||
|
||||
|
||||
def get_optional_params(
|
||||
# use the openai defaults
|
||||
# https://platform.openai.com/docs/api-reference/chat/create
|
||||
|
|
@ -5061,19 +5101,30 @@ def get_optional_params(
|
|||
verbose_logger.info("\nLiteLLM completion() model= %s; provider = %s", model, custom_llm_provider)
|
||||
verbose_logger.debug("\nLiteLLM: Params passed to completion() %s", passed_params)
|
||||
verbose_logger.debug("\nLiteLLM: Non-Default params passed to completion() %s", non_default_params)
|
||||
unsupported_params: Final = {}
|
||||
for k in non_default_params:
|
||||
if k not in supported_params:
|
||||
if k in PROVIDER_UNVALIDATED_PARAMS:
|
||||
continue
|
||||
if k == "n" and n == 1: # langchain sends n=1 as a default value
|
||||
continue # skip this param
|
||||
unsupported_params[k] = non_default_params[k]
|
||||
unsupported_params: Final[MappingProxyType[str, object]] = MappingProxyType(
|
||||
{
|
||||
k: non_default_params[k]
|
||||
for k in non_default_params
|
||||
if k not in supported_params and k not in PROVIDER_UNVALIDATED_PARAMS and not (k == "n" and n == 1)
|
||||
}
|
||||
)
|
||||
|
||||
if unsupported_params:
|
||||
if litellm.drop_params is True or (drop_params is not None and drop_params is True):
|
||||
for k in unsupported_params:
|
||||
non_default_params.pop(k, None)
|
||||
for reasoning_param in _DROPPED_REASONING_PARAMS:
|
||||
if reasoning_param in unsupported_params and _warns_on_dropped_reasoning(
|
||||
unsupported_params[reasoning_param], base_model or model, custom_llm_provider
|
||||
):
|
||||
verbose_logger.warning(
|
||||
_dropped_reasoning_warning(base_model or model, custom_llm_provider),
|
||||
reasoning_param,
|
||||
unsupported_params[reasoning_param],
|
||||
model,
|
||||
custom_llm_provider,
|
||||
reasoning_param,
|
||||
)
|
||||
else:
|
||||
raise UnsupportedParamsError(
|
||||
status_code=500,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import logging
|
||||
from collections.abc import Mapping
|
||||
from typing import Final
|
||||
|
||||
|
|
@ -279,6 +280,29 @@ def test_azure_gpt5_reasoning_effort_none_dropped(config: AzureOpenAIGPT5Config)
|
|||
assert "reasoning_effort" not in params or params.get("reasoning_effort") != "none"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("model", "names_base_model"), [("azure/litellm-qa-sol-alias", True), ("azure/gpt-5", False)])
|
||||
def test_azure_dropped_effort_warning_suggests_base_model_only_for_a_name_the_map_lacks(
|
||||
config: AzureOpenAIGPT5Config, caplog: pytest.LogCaptureFixture, model: str, names_base_model: bool
|
||||
) -> None:
|
||||
with caplog.at_level(logging.WARNING, logger="LiteLLM"):
|
||||
params: Final = config.map_openai_params(
|
||||
non_default_params={"reasoning_effort": "none"},
|
||||
optional_params={},
|
||||
model=model,
|
||||
drop_params=True,
|
||||
api_version="2025-04-01-preview",
|
||||
declared_reasoning_efforts=(),
|
||||
)
|
||||
|
||||
assert "reasoning_effort" not in params
|
||||
drop_warnings: Final = [
|
||||
record.getMessage() for record in caplog.records if "Dropping reasoning_effort='none'" in record.getMessage()
|
||||
]
|
||||
assert len(drop_warnings) == 1
|
||||
assert "model_info.reasoning_effort_levels" in drop_warnings[0]
|
||||
assert ("model_info.base_model" in drop_warnings[0]) is names_base_model
|
||||
|
||||
|
||||
# Logprobs support tests for Azure GPT-5.2
|
||||
def test_azure_gpt5_2_supports_logprobs(config: AzureOpenAIGPT5Config):
|
||||
"""Test that Azure GPT-5.2 models support logprobs parameters.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import base64
|
||||
import copy
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
from collections.abc import Iterator, Mapping
|
||||
|
|
@ -966,7 +967,9 @@ def test_reasoning_effort_maps_to_reasoning_effort_for_openai_gpt5_converse(mode
|
|||
"us.openai.gpt-6-astra",
|
||||
],
|
||||
)
|
||||
def test_openai_gpt5_converse_rejects_effort_level_disabled_in_model_map(model, local_model_cost_map):
|
||||
def test_openai_gpt5_converse_rejects_effort_level_disabled_in_model_map(
|
||||
model: str, local_model_cost_map: None, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
config = AmazonConverseConfig()
|
||||
assert litellm.utils.is_explicitly_disabled_factory(
|
||||
model=model, custom_llm_provider="bedrock_converse", key="supports_minimal_reasoning_effort"
|
||||
|
|
@ -980,15 +983,19 @@ def test_openai_gpt5_converse_rejects_effort_level_disabled_in_model_map(model,
|
|||
drop_params=False,
|
||||
)
|
||||
|
||||
optional_params = config.map_openai_params(
|
||||
non_default_params={"reasoning_effort": "minimal"},
|
||||
optional_params={},
|
||||
model=model,
|
||||
drop_params=True,
|
||||
)
|
||||
with caplog.at_level(logging.WARNING, logger="LiteLLM"):
|
||||
optional_params: Final = config.map_openai_params(
|
||||
non_default_params={"reasoning_effort": "minimal"},
|
||||
optional_params={},
|
||||
model=model,
|
||||
drop_params=True,
|
||||
)
|
||||
_, additional_request_params, _, _ = config._prepare_request_params(optional_params, model)
|
||||
assert "reasoning" not in additional_request_params
|
||||
assert "thinking" not in additional_request_params
|
||||
dropped: Final = [r.getMessage() for r in caplog.records if "Dropping reasoning_effort='minimal'" in r.getMessage()]
|
||||
assert len(dropped) == 1 and model in dropped[0]
|
||||
assert "model_info" not in dropped[0]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
|
|
|||
|
|
@ -7,13 +7,17 @@ falls back to the Chat Completions bridge and rides Converse.
|
|||
import json
|
||||
import logging
|
||||
from importlib.resources import files
|
||||
from typing import Final
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
import litellm
|
||||
from litellm.llms.bedrock.common_utils import bedrock_supports_openai_responses
|
||||
from litellm.llms.bedrock.responses.transformation import BedrockOpenAIResponsesConfig
|
||||
from litellm.llms.bedrock.responses.transformation import (
|
||||
BedrockOpenAIResponsesConfig,
|
||||
_without_disabled_reasoning_effort,
|
||||
)
|
||||
from litellm.responses.file_search.emulated_handler import should_use_emulated_file_search
|
||||
from litellm.types.router import GenericLiteLLMParams
|
||||
from litellm.types.utils import LlmProviders
|
||||
|
|
@ -337,13 +341,37 @@ class TestDisabledReasoningEffort:
|
|||
)
|
||||
|
||||
@pytest.mark.parametrize("model", ["us.openai.gpt-5.6-luna", MODEL])
|
||||
def test_effort_level_disabled_in_model_map_is_dropped_with_drop_params(self, model, local_model_cost_map):
|
||||
params = _cfg().map_openai_params(
|
||||
response_api_optional_params={"reasoning": {"effort": "minimal", "summary": "auto"}, "max_output_tokens": 64},
|
||||
model=model,
|
||||
drop_params=True,
|
||||
)
|
||||
def test_effort_level_disabled_in_model_map_is_dropped_with_a_warning(
|
||||
self, model: str, local_model_cost_map: None, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
with caplog.at_level(logging.WARNING, logger="LiteLLM"):
|
||||
params: Final = _cfg().map_openai_params(
|
||||
response_api_optional_params={
|
||||
"reasoning": {"effort": "minimal", "summary": "auto"},
|
||||
"max_output_tokens": 64,
|
||||
},
|
||||
model=model,
|
||||
drop_params=True,
|
||||
)
|
||||
assert params == {"reasoning": {"summary": "auto"}, "max_output_tokens": 64}
|
||||
dropped: Final = [
|
||||
r.getMessage() for r in caplog.records if "Dropping reasoning.effort='minimal'" in r.getMessage()
|
||||
]
|
||||
assert len(dropped) == 1 and model in dropped[0]
|
||||
assert "model_info" not in dropped[0]
|
||||
|
||||
def test_bedrock_disabled_effort_drop_warns_on_its_own(
|
||||
self, local_model_cost_map: None, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
with caplog.at_level(logging.WARNING, logger="LiteLLM"):
|
||||
params: Final = _without_disabled_reasoning_effort(
|
||||
{"reasoning": {"effort": "minimal"}, "max_output_tokens": 64}, MODEL, True
|
||||
)
|
||||
assert params == {"max_output_tokens": 64}
|
||||
dropped: Final = [
|
||||
r.getMessage() for r in caplog.records if "Dropping reasoning.effort='minimal'" in r.getMessage()
|
||||
]
|
||||
assert len(dropped) == 1 and MODEL in dropped[0]
|
||||
|
||||
def test_effort_only_reasoning_is_removed_when_dropped(self, local_model_cost_map):
|
||||
params = _cfg().map_openai_params(
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ gate, the URL construction for both paths, and the shared Bearer auth.
|
|||
|
||||
import copy
|
||||
import logging
|
||||
from typing import Final
|
||||
|
||||
import pytest
|
||||
from botocore.exceptions import (
|
||||
|
|
@ -572,6 +573,32 @@ class TestBedrockMantleServiceTier:
|
|||
assert "priority" in str(mock_warning.call_args)
|
||||
|
||||
|
||||
class TestBedrockMantleRefusedEffort:
|
||||
def test_dropping_a_refused_effort_does_not_suggest_declaring_levels_mantle_ignores(
|
||||
self, local_cost_map: None, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
refused: Final = next(
|
||||
level
|
||||
for level in ("none", "minimal", "low")
|
||||
if litellm.model_cost["gpt-6.1-sol"].get(f"supports_{level}_reasoning_effort") is False
|
||||
)
|
||||
with caplog.at_level(logging.WARNING, logger="LiteLLM"):
|
||||
params: Final = BedrockMantleResponsesAPIConfig().map_openai_params(
|
||||
response_api_optional_params={"reasoning": {"effort": refused}},
|
||||
model="openai.gpt-6.1-sol",
|
||||
drop_params=True,
|
||||
)
|
||||
|
||||
assert "reasoning" not in params
|
||||
drop_warnings: Final = [
|
||||
record.getMessage()
|
||||
for record in caplog.records
|
||||
if f"Dropping reasoning.effort={refused!r}" in record.getMessage()
|
||||
]
|
||||
assert len(drop_warnings) == 1
|
||||
assert "model_info" not in drop_warnings[0]
|
||||
|
||||
|
||||
class TestBedrockMantleReasoningSummary:
|
||||
@pytest.mark.parametrize("summary", ["concise", "detailed"])
|
||||
def test_unsupported_reasoning_summary_dropped_when_drop_params_true(self, summary):
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import json
|
||||
import logging
|
||||
from collections.abc import Callable, Mapping
|
||||
from types import SimpleNamespace
|
||||
from typing import Final, cast
|
||||
|
|
@ -15,6 +16,7 @@ from litellm.llms.azure.responses.transformation import AzureOpenAIResponsesAPIC
|
|||
from litellm.llms.base_llm.responses.transformation import BaseResponsesAPIConfig
|
||||
from litellm.llms.openai.responses.transformation import OpenAIResponsesAPIConfig
|
||||
from litellm.responses.litellm_completion_transformation.transformation import LiteLLMCompletionResponsesConfig
|
||||
from litellm.responses.utils import ResponsesAPIRequestUtils
|
||||
from litellm.types.llms.openai import (
|
||||
ImageGenerationPartialImageEvent,
|
||||
IncompleteDetails,
|
||||
|
|
@ -2387,6 +2389,105 @@ class TestResponsesSurfaceSharesTheEffortRule:
|
|||
)
|
||||
assert mapped["reasoning"] == {"summary": "auto"}
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("declared_reasoning_efforts", "suggests_declared_levels"),
|
||||
[((), True), (None, False)],
|
||||
ids=["responses route reads model_info levels", "compact route reads none"],
|
||||
)
|
||||
def test_dropping_a_refused_effort_logs_one_warning_naming_only_the_fix_its_route_honors(
|
||||
self,
|
||||
local_model_cost_map: None,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
declared_reasoning_efforts: tuple[str, ...] | None,
|
||||
suggests_declared_levels: bool,
|
||||
) -> None:
|
||||
refused: Final = next(
|
||||
level
|
||||
for level in ("none", "minimal", "low")
|
||||
if litellm.model_cost["gpt-6.1-sol"].get(f"supports_{level}_reasoning_effort") is False
|
||||
)
|
||||
with caplog.at_level(logging.WARNING, logger="LiteLLM"):
|
||||
mapped: Final = ResponsesAPIRequestUtils.get_optional_params_responses_api(
|
||||
model="gpt-6.1-sol",
|
||||
responses_api_provider_config=OpenAIResponsesAPIConfig(),
|
||||
response_api_optional_params={"reasoning": {"effort": refused}},
|
||||
drop_params=True,
|
||||
declared_reasoning_efforts=declared_reasoning_efforts,
|
||||
)
|
||||
|
||||
assert "reasoning" not in mapped
|
||||
drop_warnings: Final = [record.getMessage() for record in caplog.records if "Dropping" in record.getMessage()]
|
||||
assert len(drop_warnings) == 1
|
||||
assert repr(refused) in drop_warnings[0] and "gpt-6.1-sol" in drop_warnings[0]
|
||||
assert ("model_info.reasoning_effort_levels" in drop_warnings[0]) is suggests_declared_levels
|
||||
assert "model_info.base_model" not in drop_warnings[0]
|
||||
|
||||
def test_dropping_reasoning_for_a_model_without_supports_reasoning_logs_one_warning(
|
||||
self, local_model_cost_map: None, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
with caplog.at_level(logging.WARNING, logger="LiteLLM"):
|
||||
mapped: Final = OpenAIResponsesAPIConfig().map_openai_params(
|
||||
response_api_optional_params={"reasoning": {"effort": "medium"}},
|
||||
model="gpt-4o",
|
||||
drop_params=True,
|
||||
)
|
||||
|
||||
assert "reasoning" not in mapped
|
||||
drop_warnings: Final = [record.getMessage() for record in caplog.records if "Dropping" in record.getMessage()]
|
||||
assert len(drop_warnings) == 1
|
||||
assert all(fragment in drop_warnings[0] for fragment in ("'medium'", "gpt-4o", "supports_reasoning"))
|
||||
|
||||
def test_turning_reasoning_off_for_a_model_without_supports_reasoning_drops_it_silently(
|
||||
self, local_model_cost_map: None, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
with caplog.at_level(logging.WARNING, logger="LiteLLM"):
|
||||
mapped: Final = OpenAIResponsesAPIConfig().map_openai_params(
|
||||
response_api_optional_params={"reasoning": {"effort": "none", "summary": "auto"}},
|
||||
model="gpt-4o",
|
||||
drop_params=True,
|
||||
)
|
||||
|
||||
assert "reasoning" not in mapped
|
||||
assert not [record for record in caplog.records if "Dropping" in record.getMessage()]
|
||||
|
||||
def test_turning_reasoning_off_warns_when_only_custom_pricing_describes_the_model(
|
||||
self, local_model_cost_map: None, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
litellm.register_model(
|
||||
{
|
||||
"pricing-only-test-model": {
|
||||
"litellm_provider": "openai",
|
||||
"mode": "responses",
|
||||
"input_cost_per_token": 0.000001,
|
||||
"output_cost_per_token": 0.000002,
|
||||
}
|
||||
}
|
||||
)
|
||||
with caplog.at_level(logging.WARNING, logger="LiteLLM"):
|
||||
mapped: Final = OpenAIResponsesAPIConfig().map_openai_params(
|
||||
response_api_optional_params={"reasoning": {"effort": "none"}},
|
||||
model="pricing-only-test-model",
|
||||
drop_params=True,
|
||||
)
|
||||
|
||||
assert "reasoning" not in mapped
|
||||
drop_warnings: Final = [record.getMessage() for record in caplog.records if "Dropping" in record.getMessage()]
|
||||
assert len(drop_warnings) == 1
|
||||
assert all(fragment in drop_warnings[0] for fragment in ("'none'", "pricing-only-test-model"))
|
||||
|
||||
def test_a_forwarded_effort_logs_no_drop_warning(
|
||||
self, local_model_cost_map: None, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
with caplog.at_level(logging.WARNING, logger="LiteLLM"):
|
||||
mapped: Final = OpenAIResponsesAPIConfig().map_openai_params(
|
||||
response_api_optional_params={"reasoning": {"effort": "medium"}},
|
||||
model="gpt-6.1-sol",
|
||||
drop_params=True,
|
||||
)
|
||||
|
||||
assert mapped["reasoning"] == {"effort": "medium"}
|
||||
assert not [record for record in caplog.records if "Dropping" in record.getMessage()]
|
||||
|
||||
@pytest.mark.parametrize("level", ["none", "minimal", "low", "medium", "high", "xhigh", "max"])
|
||||
def test_gpt5_3_codex_forwards_exactly_the_effort_levels_its_map_row_allows(self, local_model_cost_map, level):
|
||||
"""The gate turns a stale false into a local refusal, so the Responses-only codex row has to
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import logging
|
||||
import re
|
||||
from collections.abc import Mapping
|
||||
from typing import Final
|
||||
|
|
@ -1589,3 +1590,55 @@ def test_gpt_6_astra_takes_the_reasoning_series_request_shape():
|
|||
assert "max_tokens" not in params
|
||||
assert params["reasoning_effort"] == "max"
|
||||
assert params["verbosity"] == "low"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("custom_llm_provider", "level", "advice"),
|
||||
[
|
||||
("azure", "none", frozenset(("model_info.reasoning_effort_levels", "model_info.base_model"))),
|
||||
("azure", "xhigh", frozenset(("model_info.reasoning_effort_levels", "model_info.base_model"))),
|
||||
("openai", "xhigh", frozenset(("model_info.reasoning_effort_levels",))),
|
||||
("azure_ai", "xhigh", frozenset(("model_info.reasoning_effort_levels",))),
|
||||
("github_copilot", "xhigh", frozenset()),
|
||||
],
|
||||
)
|
||||
def test_a_reasoning_effort_the_level_gate_drops_logs_a_warning_naming_only_the_fixes_its_route_honors(
|
||||
caplog: pytest.LogCaptureFixture, custom_llm_provider: str, level: str, advice: frozenset[str]
|
||||
) -> None:
|
||||
with caplog.at_level(logging.WARNING, logger="LiteLLM"):
|
||||
params: Final = litellm.get_optional_params(
|
||||
model="gpt-5.6-terra-ptu",
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
reasoning_effort=level,
|
||||
drop_params=True,
|
||||
)
|
||||
|
||||
assert "reasoning_effort" not in params
|
||||
drop_warnings: Final = [
|
||||
record.getMessage()
|
||||
for record in caplog.records
|
||||
if record.levelno == logging.WARNING and "reasoning_effort" in record.getMessage()
|
||||
]
|
||||
assert len(drop_warnings) == 1
|
||||
assert repr(level) in drop_warnings[0] and "gpt-5.6-terra-ptu" in drop_warnings[0]
|
||||
assert (
|
||||
frozenset(
|
||||
fragment
|
||||
for fragment in ("model_info.reasoning_effort_levels", "model_info.base_model")
|
||||
if fragment in drop_warnings[0]
|
||||
)
|
||||
== advice
|
||||
)
|
||||
|
||||
|
||||
def test_a_reasoning_effort_the_level_gate_forwards_logs_no_drop_warning(caplog: pytest.LogCaptureFixture) -> None:
|
||||
with caplog.at_level(logging.WARNING, logger="LiteLLM"):
|
||||
params: Final = litellm.get_optional_params(
|
||||
model="gpt-5.6-terra-ptu",
|
||||
custom_llm_provider="azure",
|
||||
reasoning_effort="medium",
|
||||
drop_params=True,
|
||||
)
|
||||
|
||||
assert params["reasoning_effort"] == "medium"
|
||||
assert not [record for record in caplog.records if "reasoning_effort" in record.getMessage()]
|
||||
|
|
|
|||
|
|
@ -626,8 +626,9 @@ class TestResolveDeploymentReasoningEffort:
|
|||
_sent("turbo\nforged line", drop_params=True)
|
||||
|
||||
assert [record.getMessage() for record in caplog.records] == [
|
||||
"reasoning_effort unrecognized sent as None (rounding up) to deployment deployment-1, "
|
||||
"model group kimi-k3, model hosted_vllm/my-kimi"
|
||||
"Dropping reasoning level unrecognized (drop_params=True) for deployment deployment-1, model group "
|
||||
"kimi-k3, model hosted_vllm/my-kimi because its model_info.reasoning_effort_levels does not list that "
|
||||
"level. List every level the deployment supports."
|
||||
]
|
||||
|
||||
def test_line_breaks_in_logged_names_cannot_forge_a_log_line(self, caplog: pytest.LogCaptureFixture) -> None:
|
||||
|
|
@ -669,6 +670,27 @@ class TestResolveDeploymentReasoningEffort:
|
|||
"reasoning_effort_rounding": "up",
|
||||
}
|
||||
|
||||
def test_only_a_dropped_level_logs_a_warning(self, caplog: pytest.LogCaptureFixture) -> None:
|
||||
verbose_logger.addHandler(caplog.handler)
|
||||
verbose_logger.propagate = False
|
||||
verbose_logger.setLevel(logging.INFO)
|
||||
declared: Final = {"reasoning_effort_levels": ["low", "high"]}
|
||||
|
||||
_sent("none", declared, drop_params=True)
|
||||
_sent("medium")
|
||||
_sent("default", declared, drop_params=True)
|
||||
_sent("default", declared)
|
||||
|
||||
assert [record.levelno for record in caplog.records] == [
|
||||
logging.WARNING,
|
||||
logging.INFO,
|
||||
logging.INFO,
|
||||
logging.INFO,
|
||||
]
|
||||
dropped: Final = caplog.records[0].getMessage()
|
||||
assert "Dropping reasoning level none" in dropped
|
||||
assert "model_info.reasoning_effort_levels" in dropped
|
||||
|
||||
|
||||
class TestAzureGpt6SolAndLunaAdvertiseTheOpenAiLevels:
|
||||
@pytest.mark.parametrize(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import json
|
||||
import logging
|
||||
from typing import Final
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
|
|
@ -2097,3 +2098,127 @@ def test_supports_system_message_merges_system_prompt_into_user_message() -> Non
|
|||
]
|
||||
|
||||
assert map_system_message_pt(messages=messages) == [{"role": "user", "content": "Listen here! Hello there!"}]
|
||||
|
||||
|
||||
def test_dropping_an_unsupported_reasoning_effort_logs_one_warning(
|
||||
local_model_cost_map: None, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
with caplog.at_level(logging.WARNING, logger="LiteLLM"):
|
||||
optional_params: Final = get_optional_params(
|
||||
model="terra-ptu", custom_llm_provider="azure", reasoning_effort="none", drop_params=True
|
||||
)
|
||||
|
||||
assert "reasoning_effort" not in optional_params
|
||||
drop_warnings: Final = [
|
||||
record.getMessage() for record in caplog.records if "Dropping reasoning_effort" in record.getMessage()
|
||||
]
|
||||
assert len(drop_warnings) == 1
|
||||
assert all(fragment in drop_warnings[0] for fragment in ("'none'", "terra-ptu", "model_info.base_model"))
|
||||
|
||||
|
||||
def test_dropping_an_unsupported_thinking_logs_one_warning(
|
||||
local_model_cost_map: None, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
with caplog.at_level(logging.WARNING, logger="LiteLLM"):
|
||||
optional_params: Final = get_optional_params(
|
||||
model="terra-ptu", custom_llm_provider="azure", thinking={"type": "disabled"}, drop_params=True
|
||||
)
|
||||
|
||||
assert "thinking" not in optional_params
|
||||
drop_warnings: Final = [
|
||||
record.getMessage() for record in caplog.records if "Dropping thinking" in record.getMessage()
|
||||
]
|
||||
assert len(drop_warnings) == 1
|
||||
assert all(fragment in drop_warnings[0] for fragment in ("'disabled'", "terra-ptu", "model_info.base_model"))
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("reasoning_kwargs", "warns"),
|
||||
[
|
||||
({"reasoning_effort": "none"}, False),
|
||||
({"thinking": {"type": "disabled"}}, False),
|
||||
({"reasoning_effort": "high"}, True),
|
||||
({"thinking": {"type": "enabled", "budget_tokens": 1024}}, True),
|
||||
],
|
||||
)
|
||||
def test_reasoning_drop_warns_unless_a_mapped_non_reasoning_model_was_asked_for_none(
|
||||
local_model_cost_map: None,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
reasoning_kwargs: dict[str, object],
|
||||
warns: bool,
|
||||
) -> None:
|
||||
litellm.register_model(
|
||||
{
|
||||
"azure/plain-chat-test-deployment": {
|
||||
"litellm_provider": "azure",
|
||||
"mode": "chat",
|
||||
"max_tokens": 1024,
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"supports_reasoning": False,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
with caplog.at_level(logging.WARNING, logger="LiteLLM"):
|
||||
optional_params: Final = get_optional_params(
|
||||
model="plain-chat-test-deployment", custom_llm_provider="azure", drop_params=True, **reasoning_kwargs
|
||||
)
|
||||
|
||||
assert not set(reasoning_kwargs) & set(optional_params)
|
||||
assert any("Dropping" in record.getMessage() for record in caplog.records) is warns
|
||||
|
||||
|
||||
@pytest.mark.parametrize("reasoning_kwargs", [{"reasoning_effort": "none"}, {"thinking": {"type": "disabled"}}])
|
||||
def test_turning_reasoning_off_warns_when_only_custom_pricing_describes_the_model(
|
||||
local_model_cost_map: None, caplog: pytest.LogCaptureFixture, reasoning_kwargs: dict[str, object]
|
||||
) -> None:
|
||||
litellm.register_model(
|
||||
{
|
||||
"azure/pricing-only-test-deployment": {
|
||||
"litellm_provider": "azure",
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 0.000001,
|
||||
"output_cost_per_token": 0.000002,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
with caplog.at_level(logging.WARNING, logger="LiteLLM"):
|
||||
optional_params: Final = get_optional_params(
|
||||
model="pricing-only-test-deployment", custom_llm_provider="azure", drop_params=True, **reasoning_kwargs
|
||||
)
|
||||
|
||||
assert not set(reasoning_kwargs) & set(optional_params)
|
||||
drop_warnings: Final = [record.getMessage() for record in caplog.records if "Dropping" in record.getMessage()]
|
||||
assert len(drop_warnings) == 1
|
||||
assert all(fragment in drop_warnings[0] for fragment in ("pricing-only-test-deployment", "model_info.base_model"))
|
||||
|
||||
|
||||
def test_dropping_reasoning_for_a_catalog_reasoning_model_does_not_suggest_base_model(
|
||||
local_model_cost_map: None, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
assert litellm.model_cost["azure/gpt-5.6-sol"].get("supports_reasoning") is True
|
||||
with caplog.at_level(logging.WARNING, logger="LiteLLM"):
|
||||
optional_params: Final = get_optional_params(
|
||||
model="gpt-5.6-sol", custom_llm_provider="azure", thinking={"type": "disabled"}, drop_params=True
|
||||
)
|
||||
|
||||
assert "thinking" not in optional_params
|
||||
drop_warnings: Final = [
|
||||
record.getMessage() for record in caplog.records if "Dropping thinking" in record.getMessage()
|
||||
]
|
||||
assert len(drop_warnings) == 1
|
||||
assert "model_info.base_model" not in drop_warnings[0]
|
||||
|
||||
|
||||
def test_turning_reasoning_off_for_a_catalog_non_reasoning_model_drops_it_silently(
|
||||
local_model_cost_map: None, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
with caplog.at_level(logging.WARNING, logger="LiteLLM"):
|
||||
optional_params: Final = get_optional_params(
|
||||
model="gpt-4o", custom_llm_provider="azure", reasoning_effort="none", drop_params=True
|
||||
)
|
||||
|
||||
assert "reasoning_effort" not in optional_params
|
||||
assert not [record for record in caplog.records if "Dropping" in record.getMessage()]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue