mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-17 23:51:30 +00:00
style(responses): drop inline comments from #34754 fix
This commit is contained in:
parent
eaa0abbf07
commit
79e559d296
2 changed files with 13 additions and 32 deletions
|
|
@ -1,17 +1,12 @@
|
|||
"""
|
||||
Best-effort construction of Responses API objects from raw provider payloads.
|
||||
|
||||
Providers deviate from the OpenAI Responses spec often enough that strict
|
||||
pydantic validation fails (missing ``output``, a ``usage`` block with unexpected
|
||||
fields, ...). Callers used to fall back to ``model_construct``, which skips
|
||||
validation *and* nested model coercion, so ``response.usage`` stayed a plain
|
||||
dict and ``response.output`` could be missing entirely. Everything downstream
|
||||
reads those attributes as declared (``ResponsesAPIResponse.usage`` is a
|
||||
``ResponseAPIUsage``, ``.output`` is a list), so the fallback produced objects
|
||||
that violate their own type and blew up with ``AttributeError`` mid-stream.
|
||||
|
||||
These helpers keep the declared shape intact even when validation fails, so the
|
||||
fallback stays a degraded-data path instead of a crash path.
|
||||
When a provider payload fails strict validation, callers fall back to
|
||||
``model_construct``, which skips nested model coercion and leaves ``usage`` as a
|
||||
plain dict and ``output`` missing. Downstream code reads those attributes as
|
||||
declared, so such objects raise ``AttributeError`` mid-stream. These helpers keep
|
||||
the declared shape intact so the fallback stays a degraded-data path rather than
|
||||
a crash path
|
||||
"""
|
||||
|
||||
from collections.abc import Callable, Mapping
|
||||
|
|
@ -26,11 +21,7 @@ _FIELD_MAPPING_ADAPTER = TypeAdapter(dict[str, object])
|
|||
|
||||
|
||||
def construct_responses_api_response(payload: Mapping[str, object]) -> ResponsesAPIResponse:
|
||||
"""Build a ``ResponsesAPIResponse``, validating when possible.
|
||||
|
||||
On validation failure, fields required by the model are defaulted and
|
||||
``usage`` is coerced, so attribute access on the result behaves as declared.
|
||||
"""
|
||||
"""Validate a raw response payload, coercing ``output`` and ``usage`` when validation fails"""
|
||||
try:
|
||||
return ResponsesAPIResponse.model_validate(dict(payload))
|
||||
except ValidationError:
|
||||
|
|
@ -54,12 +45,7 @@ def construct_responses_api_stream_event(
|
|||
event_model: type[BaseLiteLLMOpenAIResponseObject],
|
||||
payload: Mapping[str, object],
|
||||
) -> BaseLiteLLMOpenAIResponseObject:
|
||||
"""Build a Responses API streaming event without validation.
|
||||
|
||||
Terminal events (``response.completed`` / ``.failed`` / ``.incomplete``)
|
||||
declare a ``ResponsesAPIResponse``; ``model_construct`` alone would leave it
|
||||
as the raw dict, so construct that nested object explicitly.
|
||||
"""
|
||||
"""Build a streaming event without validation, keeping its nested ``response`` typed"""
|
||||
construct: Callable[..., BaseLiteLLMOpenAIResponseObject] = event_model.model_construct
|
||||
response_payload = _as_field_mapping(payload.get("response"))
|
||||
if response_payload is None:
|
||||
|
|
@ -68,7 +54,6 @@ def construct_responses_api_stream_event(
|
|||
|
||||
|
||||
def _construct_usage(usage: object) -> ResponseAPIUsage | None:
|
||||
"""Coerce a raw ``usage`` payload into ``ResponseAPIUsage``, or drop it."""
|
||||
if usage is None or isinstance(usage, ResponseAPIUsage):
|
||||
return usage
|
||||
usage_fields = _as_field_mapping(usage)
|
||||
|
|
@ -92,7 +77,6 @@ def _construct_usage(usage: object) -> ResponseAPIUsage | None:
|
|||
|
||||
|
||||
def _as_field_mapping(value: object) -> Mapping[str, object] | None:
|
||||
"""Validate an untyped payload into a string-keyed field mapping."""
|
||||
try:
|
||||
return _FIELD_MAPPING_ADAPTER.validate_python(value)
|
||||
except ValidationError:
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
"""
|
||||
Regression tests for GitHub issue #34754.
|
||||
|
||||
Providers whose Responses API payloads fail strict validation used to be built
|
||||
with bare ``model_construct``, leaving ``event.response`` as a raw dict and
|
||||
``response.usage`` unparsed. Consumers read those as declared, so streaming
|
||||
requests died with ``AttributeError: 'dict' object has no attribute 'usage'``
|
||||
(HTTP 500 mid-stream) or silently dropped the SpendLogs entry.
|
||||
Providers whose Responses API payloads fail strict validation used to be built with bare
|
||||
``model_construct``, leaving ``event.response`` as a raw dict and ``response.usage`` unparsed. Consumers
|
||||
read those as declared, so streaming requests died with ``AttributeError: 'dict' object has no attribute
|
||||
'usage'`` (HTTP 500 mid-stream) or silently dropped the SpendLogs entry
|
||||
"""
|
||||
|
||||
import datetime
|
||||
|
|
@ -22,8 +21,6 @@ from litellm.types.llms.openai import (
|
|||
ResponsesAPIResponse,
|
||||
)
|
||||
|
||||
# `response.completed` payload that fails validation: `output` is required by
|
||||
# ResponsesAPIResponse but plenty of providers omit it.
|
||||
DICT_FORMAT_COMPLETED_CHUNK = {
|
||||
"type": "response.completed",
|
||||
"response": {
|
||||
|
|
@ -87,7 +84,7 @@ def test_valid_payload_is_validated_not_constructed():
|
|||
)
|
||||
|
||||
assert isinstance(response.usage, ResponseAPIUsage)
|
||||
assert response.model_fields_set # validated instance, not a bare model_construct
|
||||
assert response.model_fields_set
|
||||
|
||||
|
||||
def test_streaming_event_response_is_not_a_dict():
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue