langfuse-haystack 5.0.0__tar.gz → 5.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (21) hide show
  1. {langfuse_haystack-5.0.0 → langfuse_haystack-5.0.2}/CHANGELOG.md +18 -0
  2. {langfuse_haystack-5.0.0 → langfuse_haystack-5.0.2}/PKG-INFO +1 -1
  3. {langfuse_haystack-5.0.0 → langfuse_haystack-5.0.2}/src/haystack_integrations/components/connectors/langfuse/langfuse_connector.py +2 -3
  4. {langfuse_haystack-5.0.0 → langfuse_haystack-5.0.2}/src/haystack_integrations/tracing/langfuse/tracer.py +5 -4
  5. {langfuse_haystack-5.0.0 → langfuse_haystack-5.0.2}/tests/test_langfuse_connector.py +31 -0
  6. {langfuse_haystack-5.0.0 → langfuse_haystack-5.0.2}/tests/test_tracer.py +18 -0
  7. {langfuse_haystack-5.0.0 → langfuse_haystack-5.0.2}/.gitignore +0 -0
  8. {langfuse_haystack-5.0.0 → langfuse_haystack-5.0.2}/LICENSE.txt +0 -0
  9. {langfuse_haystack-5.0.0 → langfuse_haystack-5.0.2}/README.md +0 -0
  10. {langfuse_haystack-5.0.0 → langfuse_haystack-5.0.2}/example/basic_rag.py +0 -0
  11. {langfuse_haystack-5.0.0 → langfuse_haystack-5.0.2}/example/chat.py +0 -0
  12. {langfuse_haystack-5.0.0 → langfuse_haystack-5.0.2}/example/requirements.txt +0 -0
  13. {langfuse_haystack-5.0.0 → langfuse_haystack-5.0.2}/pydoc/config_docusaurus.yml +0 -0
  14. {langfuse_haystack-5.0.0 → langfuse_haystack-5.0.2}/pyproject.toml +0 -0
  15. {langfuse_haystack-5.0.0 → langfuse_haystack-5.0.2}/src/haystack_integrations/components/connectors/__init__.py +0 -0
  16. {langfuse_haystack-5.0.0 → langfuse_haystack-5.0.2}/src/haystack_integrations/components/connectors/langfuse/__init__.py +0 -0
  17. {langfuse_haystack-5.0.0 → langfuse_haystack-5.0.2}/src/haystack_integrations/components/connectors/py.typed +0 -0
  18. {langfuse_haystack-5.0.0 → langfuse_haystack-5.0.2}/src/haystack_integrations/tracing/langfuse/__init__.py +0 -0
  19. {langfuse_haystack-5.0.0 → langfuse_haystack-5.0.2}/src/haystack_integrations/tracing/py.typed +0 -0
  20. {langfuse_haystack-5.0.0 → langfuse_haystack-5.0.2}/tests/__init__.py +0 -0
  21. {langfuse_haystack-5.0.0 → langfuse_haystack-5.0.2}/tests/test_tracing.py +0 -0
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## [integrations/langfuse-v5.0.1] - 2026-05-18
4
+
5
+ ### 🐛 Bug Fixes
6
+
7
+ - Fix langfuse tracer when input messages is `None` (#3324)
8
+
9
+
10
+ ## [integrations/langfuse-v5.0.0] - 2026-04-17
11
+
12
+ ### 🚀 Features
13
+
14
+ - [**breaking**] Migrate to Langfuse Python SDK v4 (#3126)
15
+
16
+ ### 🧪 Testing
17
+
18
+ - Track test coverage for all integrations (#3065)
19
+
20
+
3
21
  ## [integrations/langfuse-v4.1.0] - 2026-03-26
4
22
 
5
23
  ### 🚀 Features
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langfuse-haystack
3
- Version: 5.0.0
3
+ Version: 5.0.2
4
4
  Summary: Langfuse integration for Haystack
5
5
  Project-URL: Documentation, https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/langfuse#readme
6
6
  Project-URL: Issues, https://github.com/deepset-ai/haystack-core-integrations/issues
@@ -228,7 +228,6 @@ class LangfuseConnector:
228
228
  """
229
229
  init_params = data["init_parameters"]
230
230
  deserialize_secrets_inplace(init_params, keys=["secret_key", "public_key"])
231
- init_params["span_handler"] = (
232
- deserialize_class_instance(init_params["span_handler"]) if init_params["span_handler"] else None
233
- )
231
+ if init_params.get("span_handler") is not None:
232
+ init_params["span_handler"] = deserialize_class_instance(init_params["span_handler"])
234
233
  return default_from_dict(cls, data)
@@ -90,7 +90,7 @@ class LangfuseSpan(Span):
90
90
  return
91
91
  if key.endswith(".input"):
92
92
  if "messages" in value:
93
- messages = [m.to_openai_dict_format(require_tool_call_ids=False) for m in value["messages"]]
93
+ messages = [m.to_openai_dict_format(require_tool_call_ids=False) for m in (value.get("messages") or [])]
94
94
  if isinstance(gen_kwargs := value.get("generation_kwargs"), dict):
95
95
  self._span.update(input={"messages": messages, "generation_kwargs": gen_kwargs})
96
96
  else:
@@ -100,10 +100,11 @@ class LangfuseSpan(Span):
100
100
  self._span.update(input=coerced_value)
101
101
  elif key.endswith(".output"):
102
102
  if "replies" in value:
103
- if all(isinstance(r, ChatMessage) for r in value["replies"]):
104
- replies = [m.to_openai_dict_format(require_tool_call_ids=False) for m in value["replies"]]
103
+ replies_list = value.get("replies") or []
104
+ if all(isinstance(r, ChatMessage) for r in replies_list):
105
+ replies = [m.to_openai_dict_format(require_tool_call_ids=False) for m in replies_list]
105
106
  else:
106
- replies = value["replies"]
107
+ replies = replies_list
107
108
  self._span.update(output=replies)
108
109
  else:
109
110
  coerced_value = tracing_utils.coerce_tag_value(value)
@@ -147,6 +147,37 @@ class TestLangfuseConnector:
147
147
  assert langfuse_connector.host is None
148
148
  assert langfuse_connector.langfuse_client_kwargs is None
149
149
 
150
+ def test_from_dict_without_span_handler(self, monkeypatch):
151
+ monkeypatch.setenv("LANGFUSE_SECRET_KEY", "secret")
152
+ monkeypatch.setenv("LANGFUSE_PUBLIC_KEY", "public")
153
+
154
+ # All keys that would point to None (span_handler, host, langfuse_client_kwargs) are intentionally absent
155
+ data = {
156
+ "type": "haystack_integrations.components.connectors.langfuse.langfuse_connector.LangfuseConnector",
157
+ "init_parameters": {
158
+ "name": "Chat example - OpenAI",
159
+ "public": False,
160
+ "secret_key": {
161
+ "type": "env_var",
162
+ "env_vars": ["LANGFUSE_SECRET_KEY"],
163
+ "strict": True,
164
+ },
165
+ "public_key": {
166
+ "type": "env_var",
167
+ "env_vars": ["LANGFUSE_PUBLIC_KEY"],
168
+ "strict": True,
169
+ },
170
+ },
171
+ }
172
+ langfuse_connector = LangfuseConnector.from_dict(data)
173
+ assert langfuse_connector.name == "Chat example - OpenAI"
174
+ assert langfuse_connector.public is False
175
+ assert langfuse_connector.secret_key == Secret.from_env_var("LANGFUSE_SECRET_KEY")
176
+ assert langfuse_connector.public_key == Secret.from_env_var("LANGFUSE_PUBLIC_KEY")
177
+ assert langfuse_connector.span_handler is None
178
+ assert langfuse_connector.host is None
179
+ assert langfuse_connector.langfuse_client_kwargs is None
180
+
150
181
  def test_from_dict_with_params(self, monkeypatch):
151
182
  monkeypatch.setenv("LANGFUSE_SECRET_KEY", "secret")
152
183
  monkeypatch.setenv("LANGFUSE_PUBLIC_KEY", "public")
@@ -180,6 +180,24 @@ class TestLangfuseSpan:
180
180
  # check we handle properly string list replies
181
181
  assert mock_context_manager._span.update.call_args_list[0][1] == {"output": ["reply1", "reply2"]}
182
182
 
183
+ def test_set_content_tag_messages_none_does_not_raise(self):
184
+ mock_context_manager = MockContextManager()
185
+ span = LangfuseSpan(mock_context_manager)
186
+
187
+ with patch("haystack_integrations.tracing.langfuse.tracer.proxy_tracer.is_content_tracing_enabled", True):
188
+ span.set_content_tag("key.input", {"messages": None})
189
+ assert mock_context_manager._span.update.call_count == 1
190
+ assert mock_context_manager._span.update.call_args_list[0][1] == {"input": []}
191
+
192
+ def test_set_content_tag_replies_none_does_not_raise(self):
193
+ mock_context_manager = MockContextManager()
194
+ span = LangfuseSpan(mock_context_manager)
195
+
196
+ with patch("haystack_integrations.tracing.langfuse.tracer.proxy_tracer.is_content_tracing_enabled", True):
197
+ span.set_content_tag("key.output", {"replies": None})
198
+ assert mock_context_manager._span.update.call_count == 1
199
+ assert mock_context_manager._span.update.call_args_list[0][1] == {"output": []}
200
+
183
201
 
184
202
  class TestSpanContext:
185
203
  def test_post_init(self):