langfuse-haystack 2.0.0__tar.gz → 2.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 (19) hide show
  1. {langfuse_haystack-2.0.0 → langfuse_haystack-2.1.0}/CHANGELOG.md +16 -0
  2. {langfuse_haystack-2.0.0 → langfuse_haystack-2.1.0}/PKG-INFO +11 -5
  3. {langfuse_haystack-2.0.0 → langfuse_haystack-2.1.0}/README.md +9 -3
  4. {langfuse_haystack-2.0.0 → langfuse_haystack-2.1.0}/pyproject.toml +29 -18
  5. {langfuse_haystack-2.0.0 → langfuse_haystack-2.1.0}/src/haystack_integrations/tracing/langfuse/tracer.py +59 -2
  6. {langfuse_haystack-2.0.0 → langfuse_haystack-2.1.0}/.gitignore +0 -0
  7. {langfuse_haystack-2.0.0 → langfuse_haystack-2.1.0}/LICENSE.txt +0 -0
  8. {langfuse_haystack-2.0.0 → langfuse_haystack-2.1.0}/example/basic_rag.py +0 -0
  9. {langfuse_haystack-2.0.0 → langfuse_haystack-2.1.0}/example/chat.py +0 -0
  10. {langfuse_haystack-2.0.0 → langfuse_haystack-2.1.0}/example/requirements.txt +0 -0
  11. {langfuse_haystack-2.0.0 → langfuse_haystack-2.1.0}/pydoc/config.yml +0 -0
  12. {langfuse_haystack-2.0.0 → langfuse_haystack-2.1.0}/src/haystack_integrations/components/connectors/__init__.py +0 -0
  13. {langfuse_haystack-2.0.0 → langfuse_haystack-2.1.0}/src/haystack_integrations/components/connectors/langfuse/__init__.py +0 -0
  14. {langfuse_haystack-2.0.0 → langfuse_haystack-2.1.0}/src/haystack_integrations/components/connectors/langfuse/langfuse_connector.py +0 -0
  15. {langfuse_haystack-2.0.0 → langfuse_haystack-2.1.0}/src/haystack_integrations/tracing/langfuse/__init__.py +0 -0
  16. {langfuse_haystack-2.0.0 → langfuse_haystack-2.1.0}/tests/__init__.py +0 -0
  17. {langfuse_haystack-2.0.0 → langfuse_haystack-2.1.0}/tests/test_langfuse_connector.py +0 -0
  18. {langfuse_haystack-2.0.0 → langfuse_haystack-2.1.0}/tests/test_tracer.py +0 -0
  19. {langfuse_haystack-2.0.0 → langfuse_haystack-2.1.0}/tests/test_tracing.py +0 -0
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## [integrations/langfuse-v2.0.1] - 2025-06-02
4
+
5
+ ### 🚀 Features
6
+
7
+ - Use Langfuse local _to_openai_dict_format function to serialize messages (#1885)
8
+
9
+ ### 🌀 Miscellaneous
10
+
11
+ - Feat: Add detailed tracing for GoogleGenAIChatGenerator (#1887)
12
+
13
+ ## [integrations/langfuse-v2.0.0] - 2025-05-27
14
+
15
+ ### 🌀 Miscellaneous
16
+
17
+ - Add Langfuse pins (#1837)
18
+
3
19
  ## [integrations/langfuse-v1.1.2] - 2025-05-16
4
20
 
5
21
  ### 🌀 Miscellaneous
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langfuse-haystack
3
- Version: 2.0.0
3
+ Version: 2.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
@@ -19,7 +19,7 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
19
19
  Classifier: Programming Language :: Python :: Implementation :: PyPy
20
20
  Requires-Python: >=3.9
21
21
  Requires-Dist: haystack-ai>=2.13.0
22
- Requires-Dist: langfuse>=2.9.0
22
+ Requires-Dist: langfuse<3.0.0,>=2.9.0
23
23
  Description-Content-Type: text/markdown
24
24
 
25
25
  # langfuse-haystack
@@ -135,12 +135,18 @@ pip install hatch
135
135
 
136
136
  With `hatch` installed, run all the tests:
137
137
  ```
138
- hatch run test
138
+ hatch run test:all
139
139
  ```
140
140
 
141
- Run the linters `ruff` and `mypy`:
141
+ To format your code and perform linting using Ruff (with automatic fixes), run:
142
142
  ```
143
- hatch run lint:all
143
+ hatch run fmt
144
+ ```
145
+
146
+ To check for static type errors, run:
147
+
148
+ ```console
149
+ $ hatch run test:types
144
150
  ```
145
151
 
146
152
  ## License
@@ -111,12 +111,18 @@ pip install hatch
111
111
 
112
112
  With `hatch` installed, run all the tests:
113
113
  ```
114
- hatch run test
114
+ hatch run test:all
115
115
  ```
116
116
 
117
- Run the linters `ruff` and `mypy`:
117
+ To format your code and perform linting using Ruff (with automatic fixes), run:
118
118
  ```
119
- hatch run lint:all
119
+ hatch run fmt
120
+ ```
121
+
122
+ To check for static type errors, run:
123
+
124
+ ```console
125
+ $ hatch run test:types
120
126
  ```
121
127
 
122
128
  ## License
@@ -22,7 +22,7 @@ classifiers = [
22
22
  "Programming Language :: Python :: Implementation :: CPython",
23
23
  "Programming Language :: Python :: Implementation :: PyPy",
24
24
  ]
25
- dependencies = ["haystack-ai>=2.13.0", "langfuse>=2.9.0"]
25
+ dependencies = ["haystack-ai>=2.13.0", "langfuse>=2.9.0, <3.0.0"]
26
26
 
27
27
  [project.urls]
28
28
  Documentation = "https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/langfuse#readme"
@@ -43,23 +43,37 @@ git_describe_command = 'git describe --tags --match="integrations/langfuse-v[0-9
43
43
 
44
44
  [tool.hatch.envs.default]
45
45
  installer = "uv"
46
- dependencies = [
47
- "coverage[toml]>=6.5",
48
- "pytest",
49
- "pytest-rerunfailures",
50
- "haystack-pydoc-tools",
51
- "anthropic-haystack",
52
- "cohere-haystack"
53
- ]
46
+ dependencies = ["haystack-pydoc-tools", "ruff"]
47
+
54
48
  [tool.hatch.envs.default.scripts]
55
- test = "pytest {args:tests}"
56
- test-cov = "coverage run -m pytest {args:tests}"
57
- test-cov-retry = "test-cov --reruns 3 --reruns-delay 30 -x"
58
- cov-report = ["- coverage combine", "coverage report"]
59
- cov = ["test-cov", "cov-report"]
60
- cov-retry = ["test-cov-retry", "cov-report"]
61
49
  docs = ["pydoc-markdown pydoc/config.yml"]
50
+ fmt = "ruff check --fix {args} && ruff format {args}"
51
+ fmt-check = "ruff check {args} && ruff format --check {args}"
52
+
53
+ [tool.hatch.envs.test]
54
+ dependencies = [
55
+ "pytest",
56
+ "pytest-asyncio",
57
+ "pytest-cov",
58
+ "pytest-rerunfailures",
59
+ "mypy",
60
+ "pip",
61
+ "anthropic-haystack",
62
+ "cohere-haystack",
63
+
64
+ ]
65
+
66
+ [tool.hatch.envs.test.scripts]
67
+ unit = 'pytest -m "not integration" {args:tests}'
68
+ integration = 'pytest -m "integration" {args:tests}'
69
+ all = 'pytest {args:tests}'
70
+ cov-retry = 'all --cov=haystack_integrations --reruns 3 --reruns-delay 30 -x'
71
+
72
+ types = "mypy --install-types --non-interactive --explicit-package-bases {args:src/ tests}"
62
73
 
74
+ # TODO: remove lint environment once this integration is properly typed
75
+ # test environment should be used instead
76
+ # https://github.com/deepset-ai/haystack-core-integrations/issues/1771
63
77
  [tool.hatch.envs.lint]
64
78
  installer = "uv"
65
79
  detached = true
@@ -72,9 +86,6 @@ dependencies = [
72
86
 
73
87
  [tool.hatch.envs.lint.scripts]
74
88
  typing = "mypy --install-types --non-interactive --explicit-package-bases {args:src/ tests}"
75
- style = ["ruff check {args:}", "black --check --diff {args:.}"]
76
- fmt = ["black {args:.}", "ruff check --fix {args:}", "style"]
77
- all = ["style", "typing"]
78
89
 
79
90
  [tool.hatch.metadata]
80
91
  allow-direct-references = true
@@ -3,6 +3,7 @@
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
5
  import contextlib
6
+ import json
6
7
  import os
7
8
  from abc import ABC, abstractmethod
8
9
  from collections import Counter
@@ -45,6 +46,7 @@ _SUPPORTED_CHAT_GENERATORS = [
45
46
  "HuggingFaceLocalChatGenerator",
46
47
  "CohereChatGenerator",
47
48
  "OllamaChatGenerator",
49
+ "GoogleGenAIChatGenerator",
48
50
  ]
49
51
  _ALL_SUPPORTED_GENERATORS = _SUPPORTED_GENERATORS + _SUPPORTED_CHAT_GENERATORS
50
52
 
@@ -64,6 +66,61 @@ _COMPONENT_INPUT_KEY = "haystack.component.input"
64
66
  tracing_context_var: ContextVar[Dict[Any, Any]] = ContextVar("tracing_context")
65
67
 
66
68
 
69
+ def _to_openai_dict_format(chat_message: ChatMessage) -> Dict[str, Any]:
70
+ """
71
+ Convert a ChatMessage to the dictionary format expected by OpenAI's chat completion API.
72
+
73
+ Note: We already have such a method in Haystack's ChatMessage class.
74
+ However, the original method doesn't tolerate None values for ids of ToolCall and ToolCallResult.
75
+ Some generators, like GoogleGenAIChatGenerator, return None values for ids of ToolCall and ToolCallResult.
76
+ To seamlessly support these generators, we use this, Langfuse local, version of the method.
77
+
78
+ :param chat_message: The ChatMessage instance to convert.
79
+ :return: Dictionary in OpenAI Chat API format.
80
+ """
81
+ text_contents = chat_message.texts
82
+ tool_calls = chat_message.tool_calls
83
+ tool_call_results = chat_message.tool_call_results
84
+
85
+ if not text_contents and not tool_calls and not tool_call_results:
86
+ message = "A `ChatMessage` must contain at least one `TextContent`, `ToolCall`, or `ToolCallResult`."
87
+ logger.error(message)
88
+ raise ValueError(message)
89
+ if len(text_contents) + len(tool_call_results) > 1:
90
+ message = "A `ChatMessage` can only contain one `TextContent` or one `ToolCallResult`."
91
+ logger.error(message)
92
+ raise ValueError(message)
93
+
94
+ openai_msg: Dict[str, Any] = {"role": chat_message._role.value}
95
+
96
+ # Add name field if present
97
+ if chat_message._name is not None:
98
+ openai_msg["name"] = chat_message._name
99
+
100
+ if tool_call_results:
101
+ result = tool_call_results[0]
102
+ openai_msg["content"] = result.result
103
+ openai_msg["tool_call_id"] = result.origin.id
104
+ # OpenAI does not provide a way to communicate errors in tool invocations, so we ignore the error field
105
+ return openai_msg
106
+
107
+ if text_contents:
108
+ openai_msg["content"] = text_contents[0]
109
+ if tool_calls:
110
+ openai_tool_calls = []
111
+ for tc in tool_calls:
112
+ openai_tool_calls.append(
113
+ {
114
+ "id": tc.id,
115
+ "type": "function",
116
+ # We disable ensure_ascii so special chars like emojis are not converted
117
+ "function": {"name": tc.tool_name, "arguments": json.dumps(tc.arguments, ensure_ascii=False)},
118
+ }
119
+ )
120
+ openai_msg["tool_calls"] = openai_tool_calls
121
+ return openai_msg
122
+
123
+
67
124
  class LangfuseSpan(Span):
68
125
  """
69
126
  Internal class representing a bridge between the Haystack span tracing API and Langfuse.
@@ -101,7 +158,7 @@ class LangfuseSpan(Span):
101
158
  return
102
159
  if key.endswith(".input"):
103
160
  if "messages" in value:
104
- messages = [m.to_openai_dict_format() for m in value["messages"]]
161
+ messages = [_to_openai_dict_format(m) for m in value["messages"]]
105
162
  self._span.update(input=messages)
106
163
  else:
107
164
  coerced_value = tracing_utils.coerce_tag_value(value)
@@ -109,7 +166,7 @@ class LangfuseSpan(Span):
109
166
  elif key.endswith(".output"):
110
167
  if "replies" in value:
111
168
  if all(isinstance(r, ChatMessage) for r in value["replies"]):
112
- replies = [m.to_openai_dict_format() for m in value["replies"]]
169
+ replies = [_to_openai_dict_format(m) for m in value["replies"]]
113
170
  else:
114
171
  replies = value["replies"]
115
172
  self._span.update(output=replies)