From 3e165b5a97c0719b9da7d4e0c1594a7a1f67e7b4 Mon Sep 17 00:00:00 2001 From: chjnett Date: Mon, 17 Aug 2026 18:58:09 +0900 Subject: [PATCH 1/2] fix(langfuse_otel): set observation output for rerank responses --- litellm/integrations/langfuse/langfuse_otel.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/litellm/integrations/langfuse/langfuse_otel.py b/litellm/integrations/langfuse/langfuse_otel.py index a93c45ef840..9766716eee0 100644 --- a/litellm/integrations/langfuse/langfuse_otel.py +++ b/litellm/integrations/langfuse/langfuse_otel.py @@ -213,6 +213,14 @@ class LangfuseOtelLogger(OpenTelemetry): safe_dumps(output_items_data), ) + rerank_results: Final = response_obj.get("results") + if rerank_results: + safe_set_attribute( + span, + LangfuseSpanAttributes.OBSERVATION_OUTPUT.value, + safe_dumps(rerank_results), + ) + @staticmethod def _set_langfuse_specific_attributes(span: Span, kwargs, response_obj): """ From 1b7e4abed62904e440840f3a5dc37d610516f26f Mon Sep 17 00:00:00 2001 From: chjnett Date: Mon, 17 Aug 2026 19:16:24 +0900 Subject: [PATCH 2/2] test(langfuse_otel): cover rerank observation output --- .../test_langfuse_otel_rerank_output.py | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 tests/test_litellm/integrations/langfuse/test_langfuse_otel_rerank_output.py diff --git a/tests/test_litellm/integrations/langfuse/test_langfuse_otel_rerank_output.py b/tests/test_litellm/integrations/langfuse/test_langfuse_otel_rerank_output.py new file mode 100644 index 00000000000..82831f8f7ec --- /dev/null +++ b/tests/test_litellm/integrations/langfuse/test_langfuse_otel_rerank_output.py @@ -0,0 +1,62 @@ +""" +Unit tests for LangfuseOtelLogger._set_observation_output. + +Regression for #36537: with ``success_callback: ["langfuse_otel"]`` the +``/v1/rerank`` span reached Langfuse with Input set but Output always empty, +while the legacy ``langfuse`` callback populated it. The OTEL helper handled +``choices`` (chat completions) and ``output`` (Responses API items) but never +``RerankResponse.results``. +""" +from unittest.mock import MagicMock + +from litellm.integrations.langfuse.langfuse_otel import LangfuseOtelLogger +from litellm.types.integrations.langfuse_otel import LangfuseSpanAttributes +from litellm.types.utils import RerankResponse + + +def _make_span(): + span = MagicMock() + span.set_attribute = MagicMock() + return span + + +def _extract_output(span) -> str: + """Pull the value written to OBSERVATION_OUTPUT by set_attribute.""" + for args, _ in span.set_attribute.call_args_list: + if args[0] == LangfuseSpanAttributes.OBSERVATION_OUTPUT.value: + return args[1] + return None + + +def test_rerank_response_sets_observation_output(): + span = _make_span() + response = RerankResponse( + results=[ + {"index": 2, "relevance_score": 0.98}, + {"index": 0, "relevance_score": 0.41}, + ] + ) + + LangfuseOtelLogger._set_observation_output(span=span, response_obj=response) + + output = _extract_output(span) + assert output is not None + assert "0.98" in output + assert "0.41" in output + + +def test_rerank_without_results_sets_no_output(): + span = _make_span() + response = RerankResponse(results=None) + + LangfuseOtelLogger._set_observation_output(span=span, response_obj=response) + + assert _extract_output(span) is None + + +def test_none_response_is_noop(): + span = _make_span() + + LangfuseOtelLogger._set_observation_output(span=span, response_obj=None) + + span.set_attribute.assert_not_called()