diff --git a/litellm/completion_extras/litellm_responses_transformation/transformation.py b/litellm/completion_extras/litellm_responses_transformation/transformation.py index 17815976b4a..d7f6e6ab6d1 100644 --- a/litellm/completion_extras/litellm_responses_transformation/transformation.py +++ b/litellm/completion_extras/litellm_responses_transformation/transformation.py @@ -922,7 +922,13 @@ class LiteLLMResponsesTransformationHandler(CompletionTransformationBridge): if role == "user" or role == "system" or role == "tool": return {"type": "input_text", "text": content} else: - return {"type": "output_text", "text": content} + return {"type": "output_text", "text": content, "annotations": []} + + @staticmethod + def _ensure_output_text_annotations(item: dict[str, object]) -> dict[str, object]: + if item.get("type") == "output_text": + return {"annotations": [], **item} + return item def _convert_content_to_responses_format_image( self, content: "ChatCompletionImageObject", role: str @@ -1027,7 +1033,7 @@ class LiteLLMResponsesTransformationHandler(CompletionTransformationBridge): "summary_text", ]: # Already in responses API format - result.append(item) + result.append(self._ensure_output_text_annotations(item)) verbose_logger.debug("Chat provider: passthrough -> %s", item) else: # Default to input_text for unknown types diff --git a/tests/test_litellm/completion_extras/litellm_responses_transformation/test_completion_extras_litellm_responses_transformation_transformation.py b/tests/test_litellm/completion_extras/litellm_responses_transformation/test_completion_extras_litellm_responses_transformation_transformation.py index 6ca48ce63b8..e5ac10584ca 100644 --- a/tests/test_litellm/completion_extras/litellm_responses_transformation/test_completion_extras_litellm_responses_transformation_transformation.py +++ b/tests/test_litellm/completion_extras/litellm_responses_transformation/test_completion_extras_litellm_responses_transformation_transformation.py @@ -21,6 +21,59 @@ if TYPE_CHECKING: from litellm.types.utils import ModelResponse +@pytest.mark.parametrize( + ("content", "expected_content"), + [ + ( + "ok", + [{"type": "output_text", "text": "ok", "annotations": []}], + ), + ( + [{"type": "text", "text": "ok"}], + [{"type": "output_text", "text": "ok", "annotations": []}], + ), + ( + [{"type": "input_text", "text": "ok"}], + [{"type": "input_text", "text": "ok"}], + ), + ( + [{"type": "output_text", "text": "ok"}], + [{"type": "output_text", "text": "ok", "annotations": []}], + ), + ( + [ + { + "type": "output_text", + "text": "ok", + "annotations": [{"type": "url_citation", "url": "https://example.com"}], + } + ], + [ + { + "type": "output_text", + "text": "ok", + "annotations": [{"type": "url_citation", "url": "https://example.com"}], + } + ], + ), + ], +) +def test_assistant_output_text_includes_required_annotations(content, expected_content): + handler = LiteLLMResponsesTransformationHandler() + + response, _ = handler.convert_chat_completion_messages_to_responses_api( + [{"role": "assistant", "content": content}] + ) + + assert response == [ + { + "type": "message", + "role": "assistant", + "content": expected_content, + } + ] + + def test_convert_chat_completion_messages_to_responses_api_image_input(): from litellm.completion_extras.litellm_responses_transformation.transformation import ( LiteLLMResponsesTransformationHandler,