From 1619f7be9224ad0e1a94fa19ad60e9e1b968467f Mon Sep 17 00:00:00 2001 From: eviltik <4477890+eviltik@users.noreply.github.com> Date: Thu, 15 Oct 2026 14:43:50 +0200 Subject: [PATCH] fix(otel): show the raw response of pass-through routes with an unknown response shape Pass-through endpoints that return a body LiteLLM does not recognize (no choices, content or other known output) produced spans with an empty output, so the Phoenix and Langfuse output panes stayed blank. Fall back to the raw response text, or the JSON-encoded body when it is already a mapping. --- litellm/integrations/otel/model/payloads.py | 8 ++++++++ .../unit/integrations/otel/test_otel_v2_vendor_mappers.py | 2 ++ 2 files changed, 10 insertions(+) diff --git a/litellm/integrations/otel/model/payloads.py b/litellm/integrations/otel/model/payloads.py index 101e6285946..e066293a8d7 100644 --- a/litellm/integrations/otel/model/payloads.py +++ b/litellm/integrations/otel/model/payloads.py @@ -810,6 +810,7 @@ def _output_choices(response: Mapping[str, object]) -> tuple[Mapping[str, object or _search_choices(response) or _image_choices(response) or _binary_choices(response) + or _passthrough_choices(response) ) @@ -919,6 +920,13 @@ def _image_location(item: Mapping[str, object]) -> str | None: return f"b64_json image ({len(encoded) * 3 // 4 - encoded[-2:].count('=')} bytes)" +def _passthrough_choices(response: Mapping[str, object]) -> tuple[_Choice, ...]: + body: Final = response.get("response") + mapping: Final = as_str_mapping(body) + text: Final = json.dumps(dict(mapping)) if mapping is not None else as_str(body) + return (_text_choice(text),) if text else () + + def _binary_choices(response: Mapping[str, object]) -> tuple[_Choice, ...]: size: Final = as_int(response.get("num_bytes")) if size is None: diff --git a/tests/unit/integrations/otel/test_otel_v2_vendor_mappers.py b/tests/unit/integrations/otel/test_otel_v2_vendor_mappers.py index ca7a9b424ad..a8938678858 100644 --- a/tests/unit/integrations/otel/test_otel_v2_vendor_mappers.py +++ b/tests/unit/integrations/otel/test_otel_v2_vendor_mappers.py @@ -702,6 +702,8 @@ def test_langfuse_mapper_renders_an_ocr_call_with_the_page_markdown_as_output(): ("amoderation", {"results": [{"flagged": True, "categories": {"violence": True}}]}, "flagged: violence"), ("aimage_generation", {"data": [{"b64_json": "QUJDRA=="}]}, "b64_json image (4 bytes)"), ("aspeech", {"object": "binary", "content_type": "audio/mpeg", "num_bytes": 9}, "audio/mpeg (9 bytes)"), + ("pass_through_endpoint", {"response": '{"answers": {"q": 0.9}}'}, '{"answers": {"q": 0.9}}'), + ("pass_through_endpoint", {"response": {"answers": {"q": 0.9}}}, '{"answers": {"q": 0.9}}'), ], ) def test_langfuse_mapper_renders_every_non_chat_route_output_as_an_assistant_message(