langfuse-haystack 2.0.0__tar.gz → 2.0.1__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.0.1}/CHANGELOG.md +6 -0
  2. {langfuse_haystack-2.0.0 → langfuse_haystack-2.0.1}/PKG-INFO +1 -1
  3. {langfuse_haystack-2.0.0 → langfuse_haystack-2.0.1}/src/haystack_integrations/tracing/langfuse/tracer.py +59 -2
  4. {langfuse_haystack-2.0.0 → langfuse_haystack-2.0.1}/.gitignore +0 -0
  5. {langfuse_haystack-2.0.0 → langfuse_haystack-2.0.1}/LICENSE.txt +0 -0
  6. {langfuse_haystack-2.0.0 → langfuse_haystack-2.0.1}/README.md +0 -0
  7. {langfuse_haystack-2.0.0 → langfuse_haystack-2.0.1}/example/basic_rag.py +0 -0
  8. {langfuse_haystack-2.0.0 → langfuse_haystack-2.0.1}/example/chat.py +0 -0
  9. {langfuse_haystack-2.0.0 → langfuse_haystack-2.0.1}/example/requirements.txt +0 -0
  10. {langfuse_haystack-2.0.0 → langfuse_haystack-2.0.1}/pydoc/config.yml +0 -0
  11. {langfuse_haystack-2.0.0 → langfuse_haystack-2.0.1}/pyproject.toml +0 -0
  12. {langfuse_haystack-2.0.0 → langfuse_haystack-2.0.1}/src/haystack_integrations/components/connectors/__init__.py +0 -0
  13. {langfuse_haystack-2.0.0 → langfuse_haystack-2.0.1}/src/haystack_integrations/components/connectors/langfuse/__init__.py +0 -0
  14. {langfuse_haystack-2.0.0 → langfuse_haystack-2.0.1}/src/haystack_integrations/components/connectors/langfuse/langfuse_connector.py +0 -0
  15. {langfuse_haystack-2.0.0 → langfuse_haystack-2.0.1}/src/haystack_integrations/tracing/langfuse/__init__.py +0 -0
  16. {langfuse_haystack-2.0.0 → langfuse_haystack-2.0.1}/tests/__init__.py +0 -0
  17. {langfuse_haystack-2.0.0 → langfuse_haystack-2.0.1}/tests/test_langfuse_connector.py +0 -0
  18. {langfuse_haystack-2.0.0 → langfuse_haystack-2.0.1}/tests/test_tracer.py +0 -0
  19. {langfuse_haystack-2.0.0 → langfuse_haystack-2.0.1}/tests/test_tracing.py +0 -0
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [integrations/langfuse-v2.0.0] - 2025-05-27
4
+
5
+ ### 🌀 Miscellaneous
6
+
7
+ - Add Langfuse pins (#1837)
8
+
3
9
  ## [integrations/langfuse-v1.1.2] - 2025-05-16
4
10
 
5
11
  ### 🌀 Miscellaneous
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langfuse-haystack
3
- Version: 2.0.0
3
+ Version: 2.0.1
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
@@ -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)