mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-21 00:21:49 +00:00
fix(responses): carry dict-valued reasoning_effort and keep the frame type on websocket defaults
A deployment whose reasoning_effort is an object is copied through as reasoning the way the HTTP mapper does it instead of being dropped, and the relay re-asserts the response.create frame type after merging extra_body so a type key inside it can never replace it. The lazy OpenAPI snapshot goes back to main: the earlier regeneration came from a Python 3.14 interpreter dedenting docstrings, which CI on 3.12 rejects
This commit is contained in:
parent
4a951847bb
commit
417a88daed
4 changed files with 51 additions and 11 deletions
|
|
@ -19394,7 +19394,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"description": "\nUnified rate-limit error.\n\nEvery rate-limit condition surfaced by litellm \u2014 whether it originated from\nan upstream LLM provider, a vendor batch endpoint, or one of litellm's own\nproxy-side limiters (parallel-requests, dynamic-rate, batch-rate, budget,\nmax-iterations, etc.) \u2014 is raised as an instance of this class.\n\nThe :attr:`category` attribute lets callers distinguish the source. See\n:class:`RateLimitErrorCategory` for the available values.\n"
|
||||
"description": "\n Unified rate-limit error.\n\n Every rate-limit condition surfaced by litellm \u2014 whether it originated from\n an upstream LLM provider, a vendor batch endpoint, or one of litellm's own\n proxy-side limiters (parallel-requests, dynamic-rate, batch-rate, budget,\n max-iterations, etc.) \u2014 is raised as an instance of this class.\n\n The :attr:`category` attribute lets callers distinguish the source. See\n :class:`RateLimitErrorCategory` for the available values.\n "
|
||||
},
|
||||
"500": {
|
||||
"content": {
|
||||
|
|
|
|||
|
|
@ -2262,24 +2262,28 @@ def _build_litellm_metadata_for_ws(kwargs: dict) -> dict:
|
|||
return metadata
|
||||
|
||||
|
||||
_EXTRA_BODY_ADAPTER: Final = TypeAdapter(dict[str, object] | None)
|
||||
_JSON_OBJECT_ADAPTER: Final = TypeAdapter(dict[str, object] | None)
|
||||
|
||||
|
||||
def _deployment_reasoning_default(kwargs: Mapping[str, object]) -> Reasoning | dict[str, object] | None:
|
||||
if kwargs.get("reasoning") is not None:
|
||||
return None
|
||||
reasoning_effort: Final = kwargs.get("reasoning_effort")
|
||||
if isinstance(reasoning_effort, str):
|
||||
return LiteLLMResponsesTransformationHandler()._map_reasoning_effort(reasoning_effort)
|
||||
return _JSON_OBJECT_ADAPTER.validate_python(reasoning_effort) if isinstance(reasoning_effort, Mapping) else None
|
||||
|
||||
|
||||
def _build_responses_websocket_request_defaults(kwargs: Mapping[str, object]) -> ResponsesWebSocketRequestDefaults:
|
||||
reasoning_effort: Final = kwargs.get("reasoning_effort")
|
||||
mapped_reasoning: Final = (
|
||||
LiteLLMResponsesTransformationHandler()._map_reasoning_effort(reasoning_effort)
|
||||
if kwargs.get("reasoning") is None and isinstance(reasoning_effort, str)
|
||||
else None
|
||||
)
|
||||
default_reasoning: Final = _deployment_reasoning_default(kwargs)
|
||||
candidate_params: Final[dict[str, object]] = {
|
||||
**kwargs,
|
||||
**({"reasoning": mapped_reasoning} if mapped_reasoning is not None else {}),
|
||||
**({"reasoning": default_reasoning} if default_reasoning is not None else {}),
|
||||
}
|
||||
fill_missing: Final = ResponsesAPIRequestUtils.get_requested_response_api_optional_param(candidate_params)
|
||||
return ResponsesWebSocketRequestDefaults(
|
||||
fill_missing=MappingProxyType(dict(fill_missing)),
|
||||
overrides=MappingProxyType(_EXTRA_BODY_ADAPTER.validate_python(kwargs.get("extra_body")) or {}),
|
||||
overrides=MappingProxyType(_JSON_OBJECT_ADAPTER.validate_python(kwargs.get("extra_body")) or {}),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1883,7 +1883,7 @@ class ResponsesWebSocketStreaming:
|
|||
nested: Final = msg_obj.get("response")
|
||||
if _is_json_object(nested):
|
||||
return {**msg_obj, "response": self.request_defaults.merged_into(nested)}
|
||||
return self.request_defaults.merged_into(msg_obj)
|
||||
return {**self.request_defaults.merged_into(msg_obj), "type": msg_obj["type"]}
|
||||
|
||||
async def _mask_response_create(self, message: str) -> str:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1252,6 +1252,42 @@ class TestNativeWebSocketDeploymentDefaults:
|
|||
assert dict(defaults.fill_missing) == {"reasoning": {"effort": "low"}}
|
||||
assert dict(defaults.overrides) == {}
|
||||
|
||||
def test_builder_copies_dict_valued_reasoning_effort_like_the_http_path(self):
|
||||
from litellm.responses.main import _build_responses_websocket_request_defaults
|
||||
|
||||
defaults = _build_responses_websocket_request_defaults(
|
||||
{"model": "gpt-5-pro", "reasoning_effort": {"effort": "xhigh", "summary": "auto"}}
|
||||
)
|
||||
|
||||
assert dict(defaults.fill_missing) == {"reasoning": {"effort": "xhigh", "summary": "auto"}}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_extra_body_type_key_never_replaces_the_frame_type(self):
|
||||
from types import MappingProxyType
|
||||
|
||||
from litellm.types.responses.streaming_websocket import ResponsesWebSocketRequestDefaults
|
||||
|
||||
handler = _make_streaming(
|
||||
authorized_model="gpt-5-pro",
|
||||
request_defaults=ResponsesWebSocketRequestDefaults(
|
||||
fill_missing=MappingProxyType({}),
|
||||
overrides=MappingProxyType({"type": "session.update", "provider_default": "configured"}),
|
||||
),
|
||||
)
|
||||
|
||||
forwarded = json.loads(
|
||||
await handler._mask_response_create(
|
||||
json.dumps({"type": "response.create", "model": "gpt-5-pro", "input": "hi"})
|
||||
)
|
||||
)
|
||||
|
||||
assert forwarded == {
|
||||
"type": "response.create",
|
||||
"model": "gpt-5-pro",
|
||||
"input": "hi",
|
||||
"provider_default": "configured",
|
||||
}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_flat_frame_gets_defaults_client_keys_win_extra_body_overrides(self):
|
||||
handler = _make_streaming(authorized_model="gpt-5-pro", request_defaults=_deployment_defaults())
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue