diff --git a/litellm/types/llms/openai.py b/litellm/types/llms/openai.py index 3ecf58c0b86..4912f9f1375 100644 --- a/litellm/types/llms/openai.py +++ b/litellm/types/llms/openai.py @@ -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): diff --git a/tests/unit/llms/openai/responses/test_openai_responses_http.py b/tests/unit/llms/openai/responses/test_openai_responses_http.py index 97067881faf..73a45eacf3a 100644 --- a/tests/unit/llms/openai/responses/test_openai_responses_http.py +++ b/tests/unit/llms/openai/responses/test_openai_responses_http.py @@ -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,) diff --git a/tests/unit/types/llms/test_types_llms_openai.py b/tests/unit/types/llms/test_types_llms_openai.py index e90d48b64af..6d5f8fabaed 100644 --- a/tests/unit/types/llms/test_types_llms_openai.py +++ b/tests/unit/types/llms/test_types_llms_openai.py @@ -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"""