fix(responses): preserve typed parts when logprobs is omitted (#48686)

This commit is contained in:
tin-berri 2026-10-27 21:33:39 -04:00 • committed by GitHub
parent 4bdeb1378b
commit cd01c73dd9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 39 additions and 1 deletions

View file

@ -1814,7 +1814,7 @@ class ContentPartDonePartOutputText(BaseLiteLLMOpenAIResponseObject):
type: Literal["output_text"]
text: str
annotations: list[BaseLiteLLMOpenAIResponseObject]
logprobs: list[OpenAIChatCompletionLogprobsContent] | None
logprobs: list[OpenAIChatCompletionLogprobsContent] | None = None
class ContentPartDonePartRefusal(BaseLiteLLMOpenAIResponseObject):

View file

@ -13,6 +13,7 @@ from pydantic import BaseModel, TypeAdapter
import litellm
from litellm.integrations.custom_logger import CustomLogger
from litellm.types.llms.openai import (
ContentPartDonePartOutputText,
IncompleteDetails,
ResponseAPIUsage,
ResponseCompletedEvent,
@ -225,10 +226,12 @@ def _assert_valid_stream(events: tuple[object, ...], item_id: str) -> None:
}
assert getattr(events[4], "delta", None) == _OUTPUT_TEXT
assert getattr(events[5], "text", None) == _OUTPUT_TEXT
assert isinstance(getattr(events[6], "part", None), ContentPartDonePartOutputText)
assert _json_object(getattr(events[6], "part", None)) == {
"type": "output_text",
"text": _OUTPUT_TEXT,
"annotations": [],
"logprobs": None,
}
assert getattr(getattr(events[7], "item", None), "type", None) == "message"
assert tuple(item.id for item in completed.response.output) == (item_id,)

View file

@ -8,7 +8,10 @@ import json
import litellm
from litellm.types.llms.openai import (
ContentPartDoneEvent,
ContentPartDonePartOutputText,
HttpxBinaryResponseContent,
OpenAIChatCompletionLogprobsContent,
OpenAIModerationResponse,
OpenAIVideoObject,
ResponseCompletedEvent,
@ -76,6 +79,38 @@ def test_output_item_added_event():
assert event.item is None
@pytest.mark.parametrize(
("include_logprobs", "logprobs"),
(
(False, None),
(True, None),
(True, []),
(True, [{"token": "hello", "logprob": -0.5, "top_logprobs": []}]),
),
ids=("missing", "null", "empty", "values"),
)
def test_content_part_done_preserves_typed_text_with_optional_logprobs(
include_logprobs: bool, logprobs: list[OpenAIChatCompletionLogprobsContent] | None
) -> None:
event: Final = ContentPartDoneEvent.model_validate(
{
"type": "response.content_part.done",
"item_id": "msg_test",
"output_index": 0,
"content_index": 0,
"part": {
"type": "output_text",
"text": "hello",
"annotations": [],
**({"logprobs": logprobs} if include_logprobs else {}),
},
}
)
assert isinstance(event.part, ContentPartDonePartOutputText)
assert event.part.text == "hello"
assert event.part.logprobs == logprobs
class TestResponsesAPIResponseOutputText:
"""Tests for the output_text property on ResponsesAPIResponse"""