From 94a2d8ab749bc5dce40ff58525fac164a1616510 Mon Sep 17 00:00:00 2001 From: Sebastian Husch Lee Date: Mon, 18 May 2026 13:02:48 +0200 Subject: [PATCH 1/2] fix case where messages returns a None value instead of a list --- .../haystack_integrations/tracing/langfuse/tracer.py | 2 +- integrations/langfuse/tests/test_tracer.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/integrations/langfuse/src/haystack_integrations/tracing/langfuse/tracer.py b/integrations/langfuse/src/haystack_integrations/tracing/langfuse/tracer.py index 6304607793..968d65da55 100644 --- a/integrations/langfuse/src/haystack_integrations/tracing/langfuse/tracer.py +++ b/integrations/langfuse/src/haystack_integrations/tracing/langfuse/tracer.py @@ -90,7 +90,7 @@ def set_content_tag(self, key: str, value: Any) -> None: return if key.endswith(".input"): if "messages" in value: - messages = [m.to_openai_dict_format(require_tool_call_ids=False) for m in value["messages"]] + messages = [m.to_openai_dict_format(require_tool_call_ids=False) for m in (value.get("messages") or [])] if isinstance(gen_kwargs := value.get("generation_kwargs"), dict): self._span.update(input={"messages": messages, "generation_kwargs": gen_kwargs}) else: diff --git a/integrations/langfuse/tests/test_tracer.py b/integrations/langfuse/tests/test_tracer.py index 5ec1d391cb..492d661612 100644 --- a/integrations/langfuse/tests/test_tracer.py +++ b/integrations/langfuse/tests/test_tracer.py @@ -180,6 +180,16 @@ def test_set_content_tag_updates_input_and_output_with_messages(self): # check we handle properly string list replies assert mock_context_manager._span.update.call_args_list[0][1] == {"output": ["reply1", "reply2"]} + def test_set_content_tag_messages_none_does_not_raise(self): + # Regression test: value["messages"] key present but value is None must not raise TypeError + mock_context_manager = MockContextManager() + span = LangfuseSpan(mock_context_manager) + + with patch("haystack_integrations.tracing.langfuse.tracer.proxy_tracer.is_content_tracing_enabled", True): + span.set_content_tag("key.input", {"messages": None}) + assert mock_context_manager._span.update.call_count == 1 + assert mock_context_manager._span.update.call_args_list[0][1] == {"input": []} + class TestSpanContext: def test_post_init(self): From 4026e826a60a2369f30ad15d5e9bb37daea815a5 Mon Sep 17 00:00:00 2001 From: Sebastian Husch Lee Date: Mon, 18 May 2026 13:04:32 +0200 Subject: [PATCH 2/2] also catch same potential issue in replies being None (not likely to happen) --- .../haystack_integrations/tracing/langfuse/tracer.py | 7 ++++--- integrations/langfuse/tests/test_tracer.py | 10 +++++++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/integrations/langfuse/src/haystack_integrations/tracing/langfuse/tracer.py b/integrations/langfuse/src/haystack_integrations/tracing/langfuse/tracer.py index 968d65da55..f95569a9c7 100644 --- a/integrations/langfuse/src/haystack_integrations/tracing/langfuse/tracer.py +++ b/integrations/langfuse/src/haystack_integrations/tracing/langfuse/tracer.py @@ -100,10 +100,11 @@ def set_content_tag(self, key: str, value: Any) -> None: self._span.update(input=coerced_value) elif key.endswith(".output"): if "replies" in value: - if all(isinstance(r, ChatMessage) for r in value["replies"]): - replies = [m.to_openai_dict_format(require_tool_call_ids=False) for m in value["replies"]] + replies_list = value.get("replies") or [] + if all(isinstance(r, ChatMessage) for r in replies_list): + replies = [m.to_openai_dict_format(require_tool_call_ids=False) for m in replies_list] else: - replies = value["replies"] + replies = replies_list self._span.update(output=replies) else: coerced_value = tracing_utils.coerce_tag_value(value) diff --git a/integrations/langfuse/tests/test_tracer.py b/integrations/langfuse/tests/test_tracer.py index 492d661612..27d0e0d4b5 100644 --- a/integrations/langfuse/tests/test_tracer.py +++ b/integrations/langfuse/tests/test_tracer.py @@ -181,7 +181,6 @@ def test_set_content_tag_updates_input_and_output_with_messages(self): assert mock_context_manager._span.update.call_args_list[0][1] == {"output": ["reply1", "reply2"]} def test_set_content_tag_messages_none_does_not_raise(self): - # Regression test: value["messages"] key present but value is None must not raise TypeError mock_context_manager = MockContextManager() span = LangfuseSpan(mock_context_manager) @@ -190,6 +189,15 @@ def test_set_content_tag_messages_none_does_not_raise(self): assert mock_context_manager._span.update.call_count == 1 assert mock_context_manager._span.update.call_args_list[0][1] == {"input": []} + def test_set_content_tag_replies_none_does_not_raise(self): + mock_context_manager = MockContextManager() + span = LangfuseSpan(mock_context_manager) + + with patch("haystack_integrations.tracing.langfuse.tracer.proxy_tracer.is_content_tracing_enabled", True): + span.set_content_tag("key.output", {"replies": None}) + assert mock_context_manager._span.update.call_count == 1 + assert mock_context_manager._span.update.call_args_list[0][1] == {"output": []} + class TestSpanContext: def test_post_init(self):