langfuse-haystack 4.0.1__tar.gz → 4.1.0__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 (22) hide show
  1. {langfuse_haystack-4.0.1 → langfuse_haystack-4.1.0}/CHANGELOG.md +7 -0
  2. {langfuse_haystack-4.0.1 → langfuse_haystack-4.1.0}/PKG-INFO +1 -1
  3. {langfuse_haystack-4.0.1 → langfuse_haystack-4.1.0}/example/basic_rag.py +2 -2
  4. {langfuse_haystack-4.0.1 → langfuse_haystack-4.1.0}/example/chat.py +2 -2
  5. langfuse_haystack-4.1.0/pydoc/config_docusaurus.yml +14 -0
  6. {langfuse_haystack-4.0.1 → langfuse_haystack-4.1.0}/pyproject.toml +14 -5
  7. {langfuse_haystack-4.0.1 → langfuse_haystack-4.1.0}/src/haystack_integrations/components/connectors/langfuse/langfuse_connector.py +1 -0
  8. {langfuse_haystack-4.0.1 → langfuse_haystack-4.1.0}/src/haystack_integrations/tracing/langfuse/tracer.py +13 -1
  9. {langfuse_haystack-4.0.1 → langfuse_haystack-4.1.0}/tests/test_tracer.py +16 -3
  10. langfuse_haystack-4.0.1/pydoc/config_docusaurus.yml +0 -29
  11. {langfuse_haystack-4.0.1 → langfuse_haystack-4.1.0}/.gitignore +0 -0
  12. {langfuse_haystack-4.0.1 → langfuse_haystack-4.1.0}/LICENSE.txt +0 -0
  13. {langfuse_haystack-4.0.1 → langfuse_haystack-4.1.0}/README.md +0 -0
  14. {langfuse_haystack-4.0.1 → langfuse_haystack-4.1.0}/example/requirements.txt +0 -0
  15. {langfuse_haystack-4.0.1 → langfuse_haystack-4.1.0}/src/haystack_integrations/components/connectors/__init__.py +0 -0
  16. {langfuse_haystack-4.0.1 → langfuse_haystack-4.1.0}/src/haystack_integrations/components/connectors/langfuse/__init__.py +0 -0
  17. {langfuse_haystack-4.0.1 → langfuse_haystack-4.1.0}/src/haystack_integrations/components/connectors/py.typed +0 -0
  18. {langfuse_haystack-4.0.1 → langfuse_haystack-4.1.0}/src/haystack_integrations/tracing/langfuse/__init__.py +0 -0
  19. {langfuse_haystack-4.0.1 → langfuse_haystack-4.1.0}/src/haystack_integrations/tracing/py.typed +0 -0
  20. {langfuse_haystack-4.0.1 → langfuse_haystack-4.1.0}/tests/__init__.py +0 -0
  21. {langfuse_haystack-4.0.1 → langfuse_haystack-4.1.0}/tests/test_langfuse_connector.py +0 -0
  22. {langfuse_haystack-4.0.1 → langfuse_haystack-4.1.0}/tests/test_tracing.py +0 -0
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [integrations/langfuse-v4.0.1] - 2026-01-27
4
+
5
+ ### 🐛 Bug Fixes
6
+
7
+ - *(langfuse)* Apply trace_id from tracing context to Langfuse traces (#2781)
8
+
9
+
3
10
  ## [integrations/langfuse-v4.0.0] - 2026-01-12
4
11
 
5
12
  ### 🧹 Chores
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langfuse-haystack
3
- Version: 4.0.1
3
+ Version: 4.1.0
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
@@ -62,5 +62,5 @@ if __name__ == "__main__":
62
62
  question = "What does Rhodes Statue look like?"
63
63
  response = pipeline.run({"text_embedder": {"text": question}, "prompt_builder": {"question": question}})
64
64
 
65
- print(response["llm"]["replies"][0]) # noqa: T201
66
- print(response["tracer"]["trace_url"]) # noqa: T201
65
+ print(response["llm"]["replies"][0])
66
+ print(response["tracer"]["trace_url"])
@@ -59,5 +59,5 @@ if __name__ == "__main__":
59
59
  },
60
60
  }
61
61
  )
62
- print(response["llm"]["replies"][0]) # noqa: T201
63
- print(response["tracer"]["trace_url"]) # noqa: T201
62
+ print(response["llm"]["replies"][0])
63
+ print(response["tracer"]["trace_url"])
@@ -0,0 +1,14 @@
1
+ loaders:
2
+ - modules:
3
+ - haystack_integrations.components.connectors.langfuse.langfuse_connector
4
+ - haystack_integrations.tracing.langfuse.tracer
5
+ search_path: [../src]
6
+ processors:
7
+ - type: filter
8
+ documented_only: true
9
+ skip_empty_modules: true
10
+ renderer:
11
+ description: Langfuse integration for Haystack
12
+ id: integrations-langfuse
13
+ filename: langfuse.md
14
+ title: langfuse
@@ -45,7 +45,7 @@ installer = "uv"
45
45
  dependencies = ["haystack-pydoc-tools", "ruff"]
46
46
 
47
47
  [tool.hatch.envs.default.scripts]
48
- docs = ["pydoc-markdown pydoc/config_docusaurus.yml"]
48
+ docs = ["haystack-pydoc pydoc/config_docusaurus.yml"]
49
49
  fmt = "ruff check --fix {args}; ruff format {args}"
50
50
  fmt-check = "ruff check {args} && ruff format --check {args}"
51
51
 
@@ -76,8 +76,6 @@ non_interactive = true
76
76
  check_untyped_defs = true
77
77
  disallow_incomplete_defs = true
78
78
 
79
- [tool.hatch.metadata]
80
- allow-direct-references = true
81
79
 
82
80
 
83
81
  [tool.ruff]
@@ -86,9 +84,17 @@ line-length = 120
86
84
  [tool.ruff.lint]
87
85
  select = [
88
86
  "A",
87
+ "ANN",
89
88
  "ARG",
90
89
  "B",
91
90
  "C",
91
+ "D102", # Missing docstring in public method
92
+ "D103", # Missing docstring in public function
93
+ "D205", # 1 blank line required between summary line and description
94
+ "D209", # Closing triple quotes go to new line
95
+ "D213", # summary lines must be positioned on the second physical line of the docstring
96
+ "D417", # Missing argument descriptions in the docstring
97
+ "D419", # Docstring is empty
92
98
  "DTZ",
93
99
  "E",
94
100
  "EM",
@@ -125,6 +131,8 @@ ignore = [
125
131
  "PLR0915",
126
132
  # Asserts
127
133
  "S101",
134
+ # Allow `Any` - used legitimately for dynamic types and SDK boundaries
135
+ "ANN401",
128
136
  ]
129
137
 
130
138
  [tool.ruff.lint.isort]
@@ -135,9 +143,10 @@ ban-relative-imports = "parents"
135
143
 
136
144
  [tool.ruff.lint.per-file-ignores]
137
145
  # Tests can use magic values, assertions, and relative imports
138
- "tests/**/*" = ["PLR2004", "S101", "TID252"]
146
+ "tests/**/*" = ["D", "PLR2004", "S101", "TID252", "ANN"]
139
147
  # Examples can print their output
140
- "examples/**" = ["T201"]
148
+ "examples/**" = ["T201", "ANN", "D"]
149
+ "example/**" = ["T201", "ANN", "D"]
141
150
  "tests/**" = ["T201"]
142
151
 
143
152
  [tool.coverage.run]
@@ -19,6 +19,7 @@ logger = logging.getLogger(__name__)
19
19
  class LangfuseConnector:
20
20
  """
21
21
  LangfuseConnector connects Haystack LLM framework with [Langfuse](https://langfuse.com) in order to enable the
22
+
22
23
  tracing of operations and data flow within various components of a pipeline.
23
24
 
24
25
  To use LangfuseConnector, add it to your pipeline without connecting it to any other components.
@@ -91,7 +91,10 @@ class LangfuseSpan(Span):
91
91
  if key.endswith(".input"):
92
92
  if "messages" in value:
93
93
  messages = [m.to_openai_dict_format(require_tool_call_ids=False) for m in value["messages"]]
94
- self._span.update(input=messages)
94
+ if isinstance(gen_kwargs := value.get("generation_kwargs"), dict):
95
+ self._span.update(input={"messages": messages, "generation_kwargs": gen_kwargs})
96
+ else:
97
+ self._span.update(input=messages)
95
98
  else:
96
99
  coerced_value = tracing_utils.coerce_tag_value(value)
97
100
  self._span.update(input=coerced_value)
@@ -125,6 +128,7 @@ class LangfuseSpan(Span):
125
128
  return self._data
126
129
 
127
130
  def get_correlation_data_for_logs(self) -> dict[str, Any]:
131
+ """Return correlation data for log enrichment."""
128
132
  return {}
129
133
 
130
134
 
@@ -234,9 +238,11 @@ class SpanHandler(ABC):
234
238
 
235
239
  @classmethod
236
240
  def from_dict(cls, data: dict[str, Any]) -> "SpanHandler":
241
+ """Deserialize a SpanHandler from a dictionary."""
237
242
  return default_from_dict(cls, data)
238
243
 
239
244
  def to_dict(self) -> dict[str, Any]:
245
+ """Serialize this SpanHandler to a dictionary."""
240
246
  return default_to_dict(self)
241
247
 
242
248
 
@@ -273,6 +279,7 @@ class DefaultSpanHandler(SpanHandler):
273
279
  """DefaultSpanHandler provides the default Langfuse tracing behavior for Haystack."""
274
280
 
275
281
  def create_span(self, context: SpanContext) -> LangfuseSpan:
282
+ """Create a Langfuse span based on the given context."""
276
283
  if self.tracer is None:
277
284
  message = (
278
285
  "Tracer is not initialized. "
@@ -343,6 +350,7 @@ class DefaultSpanHandler(SpanHandler):
343
350
  return LangfuseSpan(self.tracer.start_as_current_span(name=context.name))
344
351
 
345
352
  def handle(self, span: LangfuseSpan, component_type: str | None) -> None:
353
+ """Process and enrich a span after component execution."""
346
354
  # If the span is at the pipeline level, we add input and output keys to the span
347
355
  at_pipeline_level = span.get_data().get(_PIPELINE_INPUT_KEY) is not None
348
356
  if at_pipeline_level:
@@ -456,6 +464,7 @@ class LangfuseTracer(Tracer):
456
464
  def trace(
457
465
  self, operation_name: str, tags: dict[str, Any] | None = None, parent_span: Span | None = None
458
466
  ) -> Iterator[Span]:
467
+ """Create and manage a tracing span as a context manager."""
459
468
  tags = tags or {}
460
469
  span_name = tags.get(_COMPONENT_NAME_KEY, operation_name)
461
470
  component_type = tags.get(_COMPONENT_TYPE_KEY)
@@ -543,6 +552,7 @@ class LangfuseTracer(Tracer):
543
552
  self.flush()
544
553
 
545
554
  def flush(self) -> None:
555
+ """Flush all pending spans to Langfuse."""
546
556
  self._tracer.flush()
547
557
 
548
558
  def current_span(self) -> Span | None:
@@ -558,6 +568,7 @@ class LangfuseTracer(Tracer):
558
568
  def get_trace_url(self) -> str:
559
569
  """
560
570
  Return the URL to the tracing data.
571
+
561
572
  :return: The URL to the tracing data.
562
573
  """
563
574
  return self._tracer.get_trace_url() or ""
@@ -565,6 +576,7 @@ class LangfuseTracer(Tracer):
565
576
  def get_trace_id(self) -> str:
566
577
  """
567
578
  Return the trace ID.
579
+
568
580
  :return: The trace ID.
569
581
  """
570
582
  return self._tracer.get_current_trace_id() or ""
@@ -149,7 +149,6 @@ class TestLangfuseSpan:
149
149
  def test_set_content_tag_updates_input_and_output_with_messages(self):
150
150
  mock_context_manager = MockContextManager()
151
151
 
152
- # test message input
153
152
  span = LangfuseSpan(mock_context_manager)
154
153
  span.set_content_tag("key.input", {"messages": [ChatMessage.from_user("message")]})
155
154
  assert mock_context_manager._span.update.call_count == 1
@@ -157,6 +156,20 @@ class TestLangfuseSpan:
157
156
  assert mock_context_manager._span.update.call_args_list[0][1] == {
158
157
  "input": [{"role": "user", "content": "message"}]
159
158
  }
159
+
160
+ # test generation_kwargs are captured
161
+ mock_context_manager._span.update.reset_mock()
162
+ span.set_content_tag(
163
+ "key.input",
164
+ {"messages": [ChatMessage.from_user("message")], "generation_kwargs": {"n": 2, "temperature": 0.7}},
165
+ )
166
+ assert mock_context_manager._span.update.call_count == 1
167
+ assert mock_context_manager._span.update.call_args_list[0][1] == {
168
+ "input": {
169
+ "messages": [{"role": "user", "content": "message"}],
170
+ "generation_kwargs": {"n": 2, "temperature": 0.7},
171
+ }
172
+ }
160
173
  # test replies ChatMessage list
161
174
  mock_context_manager._span.update.reset_mock()
162
175
  span.set_content_tag("key.output", {"replies": [ChatMessage.from_system("reply")]})
@@ -568,7 +581,7 @@ class TestLangfuseTracer:
568
581
  assert tracer._public
569
582
 
570
583
  def test_create_new_span(self):
571
- mock_raw_span = MagicMock()
584
+ mock_raw_span = Mock()
572
585
  mock_raw_span.operation_name = "operation_name"
573
586
  mock_raw_span.metadata = {"tag1": "value1", "tag2": "value2"}
574
587
 
@@ -586,7 +599,7 @@ class TestLangfuseTracer:
586
599
  mock_span_instance.get_data.return_value = {}
587
600
  mock_span_instance._context_manager = mock_context_manager
588
601
 
589
- mock_tracer = MagicMock()
602
+ mock_tracer = Mock()
590
603
  mock_tracer.start_as_current_span.return_value = mock_context_manager
591
604
  mock_tracer.start_as_current_observation.return_value = mock_context_manager
592
605
 
@@ -1,29 +0,0 @@
1
- loaders:
2
- - ignore_when_discovered:
3
- - __init__
4
- modules:
5
- - haystack_integrations.components.connectors.langfuse.langfuse_connector
6
- - haystack_integrations.tracing.langfuse.tracer
7
- search_path:
8
- - ../src
9
- type: haystack_pydoc_tools.loaders.CustomPythonLoader
10
- processors:
11
- - do_not_filter_modules: false
12
- documented_only: true
13
- expression: null
14
- skip_empty_modules: true
15
- type: filter
16
- - type: smart
17
- - type: crossref
18
- renderer:
19
- description: Langfuse integration for Haystack
20
- id: integrations-langfuse
21
- markdown:
22
- add_member_class_prefix: false
23
- add_method_class_prefix: true
24
- classdef_code_block: false
25
- descriptive_class_title: false
26
- descriptive_module_title: true
27
- filename: langfuse.md
28
- title: langfuse
29
- type: haystack_pydoc_tools.renderers.DocusaurusRenderer