langfuse-haystack 0.0.2__tar.gz → 0.0.4__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.
- langfuse_haystack-0.0.4/PKG-INFO +114 -0
- langfuse_haystack-0.0.4/README.md +91 -0
- {langfuse_haystack-0.0.2 → langfuse_haystack-0.0.4}/example/basic_rag.py +6 -8
- langfuse_haystack-0.0.4/example/chat.py +29 -0
- langfuse_haystack-0.0.4/pydoc/config.yml +30 -0
- {langfuse_haystack-0.0.2 → langfuse_haystack-0.0.4}/pyproject.toml +4 -5
- langfuse_haystack-0.0.4/src/haystack_integrations/components/connectors/langfuse/__init__.py +6 -0
- langfuse_haystack-0.0.4/src/haystack_integrations/components/connectors/langfuse/langfuse_connector.py +82 -0
- {langfuse_haystack-0.0.2 → langfuse_haystack-0.0.4}/src/haystack_integrations/tracing/langfuse/tracer.py +45 -10
- langfuse_haystack-0.0.4/tests/test_tracing.py +57 -0
- langfuse_haystack-0.0.2/PKG-INFO +0 -45
- langfuse_haystack-0.0.2/README.md +0 -22
- langfuse_haystack-0.0.2/src/haystack_integrations/components/others/langfuse/__about__.py +0 -1
- langfuse_haystack-0.0.2/src/haystack_integrations/components/others/langfuse/__init__.py +0 -3
- langfuse_haystack-0.0.2/src/haystack_integrations/components/others/langfuse/langfuse.py +0 -18
- {langfuse_haystack-0.0.2 → langfuse_haystack-0.0.4}/.gitignore +0 -0
- {langfuse_haystack-0.0.2 → langfuse_haystack-0.0.4}/LICENSE.txt +0 -0
- {langfuse_haystack-0.0.2 → langfuse_haystack-0.0.4}/example/requirements.txt +0 -0
- {langfuse_haystack-0.0.2 → langfuse_haystack-0.0.4}/src/haystack_integrations/tracing/langfuse/__init__.py +0 -0
- {langfuse_haystack-0.0.2 → langfuse_haystack-0.0.4}/tests/__init__.py +0 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: langfuse-haystack
|
|
3
|
+
Version: 0.0.4
|
|
4
|
+
Project-URL: Documentation, https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/langfuse#readme
|
|
5
|
+
Project-URL: Issues, https://github.com/deepset-ai/haystack-core-integrations/issues
|
|
6
|
+
Project-URL: Source, https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/langfuse
|
|
7
|
+
Author-email: deepset GmbH <info@deepset.ai>
|
|
8
|
+
License-Expression: Apache-2.0
|
|
9
|
+
License-File: LICENSE.txt
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Programming Language :: Python
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
18
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
19
|
+
Requires-Python: >=3.8
|
|
20
|
+
Requires-Dist: haystack-ai
|
|
21
|
+
Requires-Dist: langfuse
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# langfuse-haystack
|
|
25
|
+
|
|
26
|
+
[](https://pypi.org/project/langfuse-haystack)
|
|
27
|
+
[](https://pypi.org/project/langfuse-haystack)
|
|
28
|
+
|
|
29
|
+
langfuse-haystack integrates tracing capabilities into [Haystack](https://github.com/deepset-ai/haystack) (2.x) pipelines using [Langfuse](https://langfuse.com/). This package enhances the visibility of pipeline runs by capturing comprehensive details of the execution traces, including API calls, context data, prompts, and more. Whether you're monitoring model performance, pinpointing areas for improvement, or creating datasets for fine-tuning and testing from your pipeline executions, langfuse-haystack is the right tool for you.
|
|
30
|
+
## Features
|
|
31
|
+
|
|
32
|
+
- Easy integration with Haystack pipelines
|
|
33
|
+
- Capture the full context of the execution
|
|
34
|
+
- Track model usage and cost
|
|
35
|
+
- Collect user feedback
|
|
36
|
+
- Identify low-quality outputs
|
|
37
|
+
- Build fine-tuning and testing datasets
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
To install langfuse-haystack, run the following command:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
pip install langfuse-haystack
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
To enable tracing in your Haystack pipeline, add the `LangfuseConnector` to your pipeline.
|
|
50
|
+
You also need to set the `LANGFUSE_SECRET_KEY` and `LANGFUSE_PUBLIC_KEY` environment variables in order to connect to Langfuse account.
|
|
51
|
+
You can get these keys by signing up for an account on the Langfuse website.
|
|
52
|
+
|
|
53
|
+
Here's an example:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import os
|
|
57
|
+
|
|
58
|
+
os.environ["LANGFUSE_HOST"] = "https://cloud.langfuse.com"
|
|
59
|
+
os.environ["TOKENIZERS_PARALLELISM"] = "false"
|
|
60
|
+
os.environ["HAYSTACK_CONTENT_TRACING_ENABLED"] = "true"
|
|
61
|
+
|
|
62
|
+
from haystack.components.builders import DynamicChatPromptBuilder
|
|
63
|
+
from haystack.components.generators.chat import OpenAIChatGenerator
|
|
64
|
+
from haystack.dataclasses import ChatMessage
|
|
65
|
+
from haystack import Pipeline
|
|
66
|
+
|
|
67
|
+
from haystack_integrations.components.connectors.langfuse import LangfuseConnector
|
|
68
|
+
|
|
69
|
+
if __name__ == "__main__":
|
|
70
|
+
pipe = Pipeline()
|
|
71
|
+
pipe.add_component("tracer", LangfuseConnector("Chat example"))
|
|
72
|
+
pipe.add_component("prompt_builder", DynamicChatPromptBuilder())
|
|
73
|
+
pipe.add_component("llm", OpenAIChatGenerator(model="gpt-3.5-turbo"))
|
|
74
|
+
|
|
75
|
+
pipe.connect("prompt_builder.prompt", "llm.messages")
|
|
76
|
+
|
|
77
|
+
messages = [
|
|
78
|
+
ChatMessage.from_system("Always respond in German even if some input data is in other languages."),
|
|
79
|
+
ChatMessage.from_user("Tell me about {{location}}"),
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
response = pipe.run(
|
|
83
|
+
data={"prompt_builder": {"template_variables": {"location": "Berlin"}, "prompt_source": messages}}
|
|
84
|
+
)
|
|
85
|
+
print(response["llm"]["replies"][0])
|
|
86
|
+
print(response["tracer"]["trace_url"])
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
In this example, we add the `LangfuseConnector` to the pipeline with the name "tracer". Each run of the pipeline produces one trace viewable on the Langfuse website with a specific URL. The trace captures the entire execution context, including the prompts, completions, and metadata.
|
|
90
|
+
|
|
91
|
+
## Trace Visualization
|
|
92
|
+
|
|
93
|
+
Langfuse provides a user-friendly interface to visualize and analyze the traces generated by your Haystack pipeline. Login into your Langfuse account and navigate to the trace URL to view the trace details.
|
|
94
|
+
|
|
95
|
+
## Contributing
|
|
96
|
+
|
|
97
|
+
`hatch` is the best way to interact with this project. To install it, run:
|
|
98
|
+
```sh
|
|
99
|
+
pip install hatch
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
With `hatch` installed, run all the tests:
|
|
103
|
+
```
|
|
104
|
+
hatch run test
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Run the linters `ruff` and `mypy`:
|
|
108
|
+
```
|
|
109
|
+
hatch run lint:all
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## License
|
|
113
|
+
|
|
114
|
+
`langfuse-haystack` is distributed under the terms of the [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) license.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# langfuse-haystack
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/langfuse-haystack)
|
|
4
|
+
[](https://pypi.org/project/langfuse-haystack)
|
|
5
|
+
|
|
6
|
+
langfuse-haystack integrates tracing capabilities into [Haystack](https://github.com/deepset-ai/haystack) (2.x) pipelines using [Langfuse](https://langfuse.com/). This package enhances the visibility of pipeline runs by capturing comprehensive details of the execution traces, including API calls, context data, prompts, and more. Whether you're monitoring model performance, pinpointing areas for improvement, or creating datasets for fine-tuning and testing from your pipeline executions, langfuse-haystack is the right tool for you.
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Easy integration with Haystack pipelines
|
|
10
|
+
- Capture the full context of the execution
|
|
11
|
+
- Track model usage and cost
|
|
12
|
+
- Collect user feedback
|
|
13
|
+
- Identify low-quality outputs
|
|
14
|
+
- Build fine-tuning and testing datasets
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
To install langfuse-haystack, run the following command:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
pip install langfuse-haystack
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
To enable tracing in your Haystack pipeline, add the `LangfuseConnector` to your pipeline.
|
|
27
|
+
You also need to set the `LANGFUSE_SECRET_KEY` and `LANGFUSE_PUBLIC_KEY` environment variables in order to connect to Langfuse account.
|
|
28
|
+
You can get these keys by signing up for an account on the Langfuse website.
|
|
29
|
+
|
|
30
|
+
Here's an example:
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
import os
|
|
34
|
+
|
|
35
|
+
os.environ["LANGFUSE_HOST"] = "https://cloud.langfuse.com"
|
|
36
|
+
os.environ["TOKENIZERS_PARALLELISM"] = "false"
|
|
37
|
+
os.environ["HAYSTACK_CONTENT_TRACING_ENABLED"] = "true"
|
|
38
|
+
|
|
39
|
+
from haystack.components.builders import DynamicChatPromptBuilder
|
|
40
|
+
from haystack.components.generators.chat import OpenAIChatGenerator
|
|
41
|
+
from haystack.dataclasses import ChatMessage
|
|
42
|
+
from haystack import Pipeline
|
|
43
|
+
|
|
44
|
+
from haystack_integrations.components.connectors.langfuse import LangfuseConnector
|
|
45
|
+
|
|
46
|
+
if __name__ == "__main__":
|
|
47
|
+
pipe = Pipeline()
|
|
48
|
+
pipe.add_component("tracer", LangfuseConnector("Chat example"))
|
|
49
|
+
pipe.add_component("prompt_builder", DynamicChatPromptBuilder())
|
|
50
|
+
pipe.add_component("llm", OpenAIChatGenerator(model="gpt-3.5-turbo"))
|
|
51
|
+
|
|
52
|
+
pipe.connect("prompt_builder.prompt", "llm.messages")
|
|
53
|
+
|
|
54
|
+
messages = [
|
|
55
|
+
ChatMessage.from_system("Always respond in German even if some input data is in other languages."),
|
|
56
|
+
ChatMessage.from_user("Tell me about {{location}}"),
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
response = pipe.run(
|
|
60
|
+
data={"prompt_builder": {"template_variables": {"location": "Berlin"}, "prompt_source": messages}}
|
|
61
|
+
)
|
|
62
|
+
print(response["llm"]["replies"][0])
|
|
63
|
+
print(response["tracer"]["trace_url"])
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
In this example, we add the `LangfuseConnector` to the pipeline with the name "tracer". Each run of the pipeline produces one trace viewable on the Langfuse website with a specific URL. The trace captures the entire execution context, including the prompts, completions, and metadata.
|
|
67
|
+
|
|
68
|
+
## Trace Visualization
|
|
69
|
+
|
|
70
|
+
Langfuse provides a user-friendly interface to visualize and analyze the traces generated by your Haystack pipeline. Login into your Langfuse account and navigate to the trace URL to view the trace details.
|
|
71
|
+
|
|
72
|
+
## Contributing
|
|
73
|
+
|
|
74
|
+
`hatch` is the best way to interact with this project. To install it, run:
|
|
75
|
+
```sh
|
|
76
|
+
pip install hatch
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
With `hatch` installed, run all the tests:
|
|
80
|
+
```
|
|
81
|
+
hatch run test
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Run the linters `ruff` and `mypy`:
|
|
85
|
+
```
|
|
86
|
+
hatch run lint:all
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## License
|
|
90
|
+
|
|
91
|
+
`langfuse-haystack` is distributed under the terms of the [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) license.
|
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
import os
|
|
2
2
|
|
|
3
|
-
os.environ["LANGFUSE_HOST"] = "https://cloud.langfuse.com"
|
|
4
3
|
os.environ["TOKENIZERS_PARALLELISM"] = "false"
|
|
5
4
|
os.environ["HAYSTACK_CONTENT_TRACING_ENABLED"] = "true"
|
|
6
5
|
|
|
7
6
|
from datasets import load_dataset
|
|
8
|
-
|
|
9
|
-
from haystack.document_stores.in_memory import InMemoryDocumentStore
|
|
10
7
|
from haystack import Document, Pipeline
|
|
8
|
+
from haystack.components.builders import PromptBuilder
|
|
11
9
|
from haystack.components.embedders import SentenceTransformersDocumentEmbedder, SentenceTransformersTextEmbedder
|
|
12
|
-
from haystack.components.retrievers import InMemoryEmbeddingRetriever
|
|
13
10
|
from haystack.components.generators import OpenAIGenerator
|
|
14
|
-
from haystack.components.
|
|
15
|
-
|
|
16
|
-
from haystack_integrations.components.
|
|
11
|
+
from haystack.components.retrievers import InMemoryEmbeddingRetriever
|
|
12
|
+
from haystack.document_stores.in_memory import InMemoryDocumentStore
|
|
13
|
+
from haystack_integrations.components.connectors.langfuse import LangfuseConnector
|
|
17
14
|
|
|
18
15
|
|
|
19
16
|
def get_pipeline(document_store: InMemoryDocumentStore):
|
|
@@ -35,7 +32,7 @@ def get_pipeline(document_store: InMemoryDocumentStore):
|
|
|
35
32
|
|
|
36
33
|
basic_rag_pipeline = Pipeline()
|
|
37
34
|
# Add components to your pipeline
|
|
38
|
-
basic_rag_pipeline.add_component("tracer",
|
|
35
|
+
basic_rag_pipeline.add_component("tracer", LangfuseConnector("Basic RAG Pipeline"))
|
|
39
36
|
basic_rag_pipeline.add_component(
|
|
40
37
|
"text_embedder", SentenceTransformersTextEmbedder(model="sentence-transformers/all-MiniLM-L6-v2")
|
|
41
38
|
)
|
|
@@ -65,3 +62,4 @@ if __name__ == "__main__":
|
|
|
65
62
|
response = pipeline.run({"text_embedder": {"text": question}, "prompt_builder": {"question": question}})
|
|
66
63
|
|
|
67
64
|
print(response["llm"]["replies"][0])
|
|
65
|
+
print(response["tracer"]["trace_url"])
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
os.environ["HAYSTACK_CONTENT_TRACING_ENABLED"] = "true"
|
|
4
|
+
|
|
5
|
+
from haystack import Pipeline
|
|
6
|
+
from haystack.components.builders import DynamicChatPromptBuilder
|
|
7
|
+
from haystack.components.generators.chat import OpenAIChatGenerator
|
|
8
|
+
from haystack.dataclasses import ChatMessage
|
|
9
|
+
from haystack_integrations.components.connectors.langfuse import LangfuseConnector
|
|
10
|
+
|
|
11
|
+
if __name__ == "__main__":
|
|
12
|
+
|
|
13
|
+
pipe = Pipeline()
|
|
14
|
+
pipe.add_component("tracer", LangfuseConnector("Chat example"))
|
|
15
|
+
pipe.add_component("prompt_builder", DynamicChatPromptBuilder())
|
|
16
|
+
pipe.add_component("llm", OpenAIChatGenerator(model="gpt-3.5-turbo"))
|
|
17
|
+
|
|
18
|
+
pipe.connect("prompt_builder.prompt", "llm.messages")
|
|
19
|
+
|
|
20
|
+
messages = [
|
|
21
|
+
ChatMessage.from_system("Always respond in German even if some input data is in other languages."),
|
|
22
|
+
ChatMessage.from_user("Tell me about {{location}}"),
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
response = pipe.run(
|
|
26
|
+
data={"prompt_builder": {"template_variables": {"location": "Berlin"}, "prompt_source": messages}}
|
|
27
|
+
)
|
|
28
|
+
print(response["llm"]["replies"][0])
|
|
29
|
+
print(response["tracer"]["trace_url"])
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
loaders:
|
|
2
|
+
- type: haystack_pydoc_tools.loaders.CustomPythonLoader
|
|
3
|
+
search_path: [../src]
|
|
4
|
+
modules: [
|
|
5
|
+
"haystack_integrations.components.connectors.langfuse.langfuse_connector",
|
|
6
|
+
"haystack_integrations.tracing.langfuse.tracer",
|
|
7
|
+
]
|
|
8
|
+
ignore_when_discovered: ["__init__"]
|
|
9
|
+
processors:
|
|
10
|
+
- type: filter
|
|
11
|
+
expression:
|
|
12
|
+
documented_only: true
|
|
13
|
+
do_not_filter_modules: false
|
|
14
|
+
skip_empty_modules: true
|
|
15
|
+
- type: smart
|
|
16
|
+
- type: crossref
|
|
17
|
+
renderer:
|
|
18
|
+
type: haystack_pydoc_tools.renderers.ReadmePreviewRenderer
|
|
19
|
+
excerpt: Langfuse integration for Haystack
|
|
20
|
+
category_slug: integrations-api
|
|
21
|
+
title: langfuse
|
|
22
|
+
slug: integrations-langfuse
|
|
23
|
+
order: 135
|
|
24
|
+
markdown:
|
|
25
|
+
descriptive_class_title: false
|
|
26
|
+
classdef_code_block: false
|
|
27
|
+
descriptive_module_title: true
|
|
28
|
+
add_method_class_prefix: true
|
|
29
|
+
add_member_class_prefix: false
|
|
30
|
+
filename: _readme_langfuse.md
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["hatchling"]
|
|
2
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
3
3
|
build-backend = "hatchling.build"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
@@ -38,10 +38,8 @@ Source = "https://github.com/deepset-ai/haystack-core-integrations/tree/main/int
|
|
|
38
38
|
packages = ["src/haystack_integrations"]
|
|
39
39
|
|
|
40
40
|
[tool.hatch.version]
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
# source = "vcs"
|
|
44
|
-
# tag-pattern = 'integrations\/langfuse-v(?P<version>.*)'
|
|
41
|
+
source = "vcs"
|
|
42
|
+
tag-pattern = 'integrations\/langfuse-v(?P<version>.*)'
|
|
45
43
|
|
|
46
44
|
|
|
47
45
|
[tool.hatch.version.raw-options]
|
|
@@ -52,6 +50,7 @@ git_describe_command = 'git describe --tags --match="integrations/langfuse-v[0-9
|
|
|
52
50
|
dependencies = [
|
|
53
51
|
"coverage[toml]>=6.5",
|
|
54
52
|
"pytest",
|
|
53
|
+
"haystack-pydoc-tools",
|
|
55
54
|
]
|
|
56
55
|
[tool.hatch.envs.default.scripts]
|
|
57
56
|
test = "pytest {args:tests}"
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from haystack import component, tracing
|
|
2
|
+
from haystack_integrations.tracing.langfuse import LangfuseTracer
|
|
3
|
+
|
|
4
|
+
from langfuse import Langfuse
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@component
|
|
8
|
+
class LangfuseConnector:
|
|
9
|
+
"""
|
|
10
|
+
LangfuseConnector connects Haystack LLM framework with Langfuse in order to enable the tracing of operations
|
|
11
|
+
and data flow within various components of a pipeline.
|
|
12
|
+
|
|
13
|
+
Simply add this component to your pipeline, but *do not* connect it to any other component. The LangfuseConnector
|
|
14
|
+
will automatically trace the operations and data flow within the pipeline.
|
|
15
|
+
|
|
16
|
+
Note that you need to set the `LANGFUSE_SECRET_KEY` and `LANGFUSE_PUBLIC_KEY` environment variables in order
|
|
17
|
+
to use this component. The `LANGFUSE_SECRET_KEY` and `LANGFUSE_PUBLIC_KEY` are the secret and public keys provided
|
|
18
|
+
by Langfuse. You can get these keys by signing up for an account on the Langfuse website.
|
|
19
|
+
|
|
20
|
+
In addition, you need to set the `HAYSTACK_CONTENT_TRACING_ENABLED` environment variable to `true` in order to
|
|
21
|
+
enable Haystack tracing in your pipeline.
|
|
22
|
+
|
|
23
|
+
Here is an example of how to use it:
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
import os
|
|
27
|
+
|
|
28
|
+
os.environ["HAYSTACK_CONTENT_TRACING_ENABLED"] = "true"
|
|
29
|
+
|
|
30
|
+
from haystack import Pipeline
|
|
31
|
+
from haystack.components.builders import DynamicChatPromptBuilder
|
|
32
|
+
from haystack.components.generators.chat import OpenAIChatGenerator
|
|
33
|
+
from haystack.dataclasses import ChatMessage
|
|
34
|
+
from haystack_integrations.components.connectors.langfuse import LangfuseConnector
|
|
35
|
+
|
|
36
|
+
if __name__ == "__main__":
|
|
37
|
+
|
|
38
|
+
pipe = Pipeline()
|
|
39
|
+
pipe.add_component("tracer", LangfuseConnector("Chat example"))
|
|
40
|
+
pipe.add_component("prompt_builder", DynamicChatPromptBuilder())
|
|
41
|
+
pipe.add_component("llm", OpenAIChatGenerator(model="gpt-3.5-turbo"))
|
|
42
|
+
|
|
43
|
+
pipe.connect("prompt_builder.prompt", "llm.messages")
|
|
44
|
+
|
|
45
|
+
messages = [
|
|
46
|
+
ChatMessage.from_system("Always respond in German even if some input data is in other languages."),
|
|
47
|
+
ChatMessage.from_user("Tell me about {{location}}"),
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
response = pipe.run(
|
|
51
|
+
data={"prompt_builder": {"template_variables": {"location": "Berlin"}, "prompt_source": messages}}
|
|
52
|
+
)
|
|
53
|
+
print(response["llm"]["replies"][0])
|
|
54
|
+
print(response["tracer"]["trace_url"])
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__(self, name: str, public: bool = False):
|
|
60
|
+
"""
|
|
61
|
+
Initialize the LangfuseConnector component.
|
|
62
|
+
|
|
63
|
+
:param name: The name of the pipeline or component. This name will be used to identify the tracing run on the
|
|
64
|
+
Langfuse dashboard.
|
|
65
|
+
:param public: Whether the tracing data should be public or private. If set to `True`, the tracing data will be
|
|
66
|
+
publicly accessible to anyone with the tracing URL. If set to `False`, the tracing data will be private and
|
|
67
|
+
only accessible to the Langfuse account owner. The default is `False`.
|
|
68
|
+
"""
|
|
69
|
+
self.name = name
|
|
70
|
+
self.tracer = LangfuseTracer(tracer=Langfuse(), name=name, public=public)
|
|
71
|
+
tracing.enable_tracing(self.tracer)
|
|
72
|
+
|
|
73
|
+
@component.output_types(name=str, trace_url=str)
|
|
74
|
+
def run(self):
|
|
75
|
+
"""
|
|
76
|
+
Runs the LangfuseConnector component.
|
|
77
|
+
|
|
78
|
+
:returns: A dictionary with the following keys:
|
|
79
|
+
- `name`: The name of the tracing component.
|
|
80
|
+
- `trace_url`: The URL to the tracing data.
|
|
81
|
+
"""
|
|
82
|
+
return {"name": self.name, "trace_url": self.tracer.get_trace_url()}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import contextlib
|
|
2
2
|
from typing import Any, Dict, Iterator, Optional, Union
|
|
3
3
|
|
|
4
|
+
from haystack.dataclasses import ChatMessage
|
|
4
5
|
from haystack.tracing import Span, Tracer, tracer
|
|
5
6
|
from haystack.tracing import utils as tracing_utils
|
|
6
7
|
|
|
@@ -11,7 +12,7 @@ class LangfuseSpan(Span):
|
|
|
11
12
|
def __init__(self, span: "Union[langfuse.client.StatefulSpanClient, langfuse.client.StatefulTraceClient]") -> None:
|
|
12
13
|
self._span = span
|
|
13
14
|
# locally cache tags
|
|
14
|
-
self._data = {}
|
|
15
|
+
self._data: Dict[str, Any] = {}
|
|
15
16
|
|
|
16
17
|
def set_tag(self, key: str, value: Any) -> None:
|
|
17
18
|
coerced_value = tracing_utils.coerce_tag_value(value)
|
|
@@ -21,11 +22,21 @@ class LangfuseSpan(Span):
|
|
|
21
22
|
def set_content_tag(self, key: str, value: Any) -> None:
|
|
22
23
|
if not tracer.is_content_tracing_enabled:
|
|
23
24
|
return
|
|
24
|
-
|
|
25
25
|
if key.endswith(".input"):
|
|
26
|
-
|
|
26
|
+
if "messages" in value:
|
|
27
|
+
messages = [self.to_openai_format(m) for m in value["messages"]]
|
|
28
|
+
self._span.update(input=messages)
|
|
29
|
+
else:
|
|
30
|
+
self._span.update(input=value)
|
|
27
31
|
elif key.endswith(".output"):
|
|
28
|
-
|
|
32
|
+
if "replies" in value:
|
|
33
|
+
if all(isinstance(r, ChatMessage) for r in value["replies"]):
|
|
34
|
+
replies = [self.to_openai_format(m) for m in value["replies"]]
|
|
35
|
+
else:
|
|
36
|
+
replies = value["replies"]
|
|
37
|
+
self._span.update(output=replies)
|
|
38
|
+
else:
|
|
39
|
+
self._span.update(output=value)
|
|
29
40
|
|
|
30
41
|
self._data[key] = value
|
|
31
42
|
|
|
@@ -35,22 +46,31 @@ class LangfuseSpan(Span):
|
|
|
35
46
|
def get_correlation_data_for_logs(self) -> Dict[str, Any]:
|
|
36
47
|
return {}
|
|
37
48
|
|
|
49
|
+
def to_openai_format(self, m: ChatMessage) -> Dict[str, Any]:
|
|
50
|
+
"""
|
|
51
|
+
Remove after haystack 2.0.1 has been released and use the `to_openai_format` method from the ChatMessage class
|
|
52
|
+
"""
|
|
53
|
+
msg = {"role": m.role.value, "content": m.content}
|
|
54
|
+
if m.name:
|
|
55
|
+
msg["name"] = m.name
|
|
56
|
+
|
|
57
|
+
return msg
|
|
58
|
+
|
|
38
59
|
|
|
39
60
|
class LangfuseTracer(Tracer):
|
|
40
|
-
def __init__(self, tracer: "langfuse.Langfuse", name: str = "Haystack") -> None:
|
|
61
|
+
def __init__(self, tracer: "langfuse.Langfuse", name: str = "Haystack", public: bool = False) -> None:
|
|
41
62
|
self._tracer = tracer
|
|
42
63
|
self._context: list[LangfuseSpan] = []
|
|
43
64
|
self._name = name
|
|
65
|
+
self._public = public
|
|
44
66
|
|
|
45
67
|
@contextlib.contextmanager
|
|
46
68
|
def trace(self, operation_name: str, tags: Optional[Dict[str, Any]] = None) -> Iterator[Span]:
|
|
47
69
|
tags = tags or {}
|
|
48
70
|
span_name = tags.get("haystack.component.name", operation_name)
|
|
49
|
-
is_generation = False
|
|
50
71
|
|
|
51
|
-
if tags.get("haystack.component.type") in ["OpenAIGenerator"]:
|
|
72
|
+
if tags.get("haystack.component.type") in ["OpenAIGenerator", "OpenAIChatGenerator"]:
|
|
52
73
|
span = LangfuseSpan(self.current_span().raw_span().generation(name=span_name))
|
|
53
|
-
is_generation = True
|
|
54
74
|
else:
|
|
55
75
|
span = LangfuseSpan(self.current_span().raw_span().span(name=span_name))
|
|
56
76
|
|
|
@@ -59,13 +79,25 @@ class LangfuseTracer(Tracer):
|
|
|
59
79
|
|
|
60
80
|
yield span
|
|
61
81
|
|
|
62
|
-
if
|
|
82
|
+
if tags.get("haystack.component.type") == "OpenAIGenerator":
|
|
63
83
|
meta = span._data.get("haystack.component.output", {}).get("meta")
|
|
64
84
|
if meta:
|
|
65
85
|
# Haystack returns one meta dict for each message, but the 'usage' value
|
|
66
86
|
# is always the same, let's just pick the first item
|
|
67
87
|
m = meta[0]
|
|
68
88
|
span._span.update(usage=m.get("usage"), model=m.get("model"))
|
|
89
|
+
elif tags.get("haystack.component.type") == "OpenAIChatGenerator":
|
|
90
|
+
replies = span._data.get("haystack.component.output", {}).get("replies")
|
|
91
|
+
if replies:
|
|
92
|
+
meta = replies[0].meta
|
|
93
|
+
span._span.update(usage=meta.get("usage"), model=meta.get("model"))
|
|
94
|
+
|
|
95
|
+
pipeline_input = tags.get("haystack.pipeline.input_data", None)
|
|
96
|
+
if pipeline_input:
|
|
97
|
+
span._span.update(input=tags["haystack.pipeline.input_data"])
|
|
98
|
+
pipeline_output = tags.get("haystack.pipeline.output_data", None)
|
|
99
|
+
if pipeline_output:
|
|
100
|
+
span._span.update(output=tags["haystack.pipeline.output_data"])
|
|
69
101
|
|
|
70
102
|
span.raw_span().end()
|
|
71
103
|
self._context.pop()
|
|
@@ -74,5 +106,8 @@ class LangfuseTracer(Tracer):
|
|
|
74
106
|
def current_span(self) -> Span:
|
|
75
107
|
if not self._context:
|
|
76
108
|
# The root span has to be a trace
|
|
77
|
-
self._context.append(LangfuseSpan(self._tracer.trace(name=self._name)))
|
|
109
|
+
self._context.append(LangfuseSpan(self._tracer.trace(name=self._name, public=self._public)))
|
|
78
110
|
return self._context[-1]
|
|
111
|
+
|
|
112
|
+
def get_trace_url(self) -> str:
|
|
113
|
+
return self._tracer.get_trace_url()
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
# don't remove (or move) this env var setting from here, it's needed to turn tracing on
|
|
4
|
+
os.environ["HAYSTACK_CONTENT_TRACING_ENABLED"] = "true"
|
|
5
|
+
|
|
6
|
+
from urllib.parse import urlparse
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
import requests
|
|
10
|
+
|
|
11
|
+
from haystack import Pipeline
|
|
12
|
+
from haystack.components.builders import DynamicChatPromptBuilder
|
|
13
|
+
from haystack.components.generators.chat import OpenAIChatGenerator
|
|
14
|
+
from haystack.dataclasses import ChatMessage
|
|
15
|
+
from requests.auth import HTTPBasicAuth
|
|
16
|
+
|
|
17
|
+
from haystack_integrations.components.connectors.langfuse import LangfuseConnector
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@pytest.mark.integration
|
|
21
|
+
@pytest.mark.skipif(
|
|
22
|
+
not os.environ.get("LANGFUSE_SECRET_KEY", None) and not os.environ.get("LANGFUSE_PUBLIC_KEY", None),
|
|
23
|
+
reason="Export an env var called LANGFUSE_SECRET_KEY and LANGFUSE_PUBLIC_KEY containing Langfuse credentials.",
|
|
24
|
+
)
|
|
25
|
+
def test_tracing_integration():
|
|
26
|
+
|
|
27
|
+
pipe = Pipeline()
|
|
28
|
+
pipe.add_component("tracer", LangfuseConnector(name="Chat example", public=True)) # public so anyone can verify run
|
|
29
|
+
pipe.add_component("prompt_builder", DynamicChatPromptBuilder())
|
|
30
|
+
pipe.add_component("llm", OpenAIChatGenerator(model="gpt-3.5-turbo"))
|
|
31
|
+
|
|
32
|
+
pipe.connect("prompt_builder.prompt", "llm.messages")
|
|
33
|
+
|
|
34
|
+
messages = [
|
|
35
|
+
ChatMessage.from_system("Always respond in German even if some input data is in other languages."),
|
|
36
|
+
ChatMessage.from_user("Tell me about {{location}}"),
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
response = pipe.run(
|
|
40
|
+
data={"prompt_builder": {"template_variables": {"location": "Berlin"}, "prompt_source": messages}}
|
|
41
|
+
)
|
|
42
|
+
assert "Berlin" in response["llm"]["replies"][0].content
|
|
43
|
+
assert response["tracer"]["trace_url"]
|
|
44
|
+
url = "https://cloud.langfuse.com/api/public/traces/"
|
|
45
|
+
trace_url = response["tracer"]["trace_url"]
|
|
46
|
+
parsed_url = urlparse(trace_url)
|
|
47
|
+
# trace id is the last part of the path (after the last '/')
|
|
48
|
+
uuid = os.path.basename(parsed_url.path)
|
|
49
|
+
try:
|
|
50
|
+
# GET request with Basic Authentication on the Langfuse API
|
|
51
|
+
response = requests.get(
|
|
52
|
+
url + uuid, auth=HTTPBasicAuth(os.environ.get("LANGFUSE_PUBLIC_KEY"), os.environ.get("LANGFUSE_SECRET_KEY"))
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
assert response.status_code == 200, f"Failed to retrieve data from Langfuse API: {response.status_code}"
|
|
56
|
+
except requests.exceptions.RequestException as e:
|
|
57
|
+
assert False, f"Failed to retrieve data from Langfuse API: {e}"
|
langfuse_haystack-0.0.2/PKG-INFO
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.3
|
|
2
|
-
Name: langfuse-haystack
|
|
3
|
-
Version: 0.0.2
|
|
4
|
-
Project-URL: Documentation, https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/langfuse#readme
|
|
5
|
-
Project-URL: Issues, https://github.com/deepset-ai/haystack-core-integrations/issues
|
|
6
|
-
Project-URL: Source, https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/langfuse
|
|
7
|
-
Author-email: deepset GmbH <info@deepset.ai>
|
|
8
|
-
License-Expression: Apache-2.0
|
|
9
|
-
License-File: LICENSE.txt
|
|
10
|
-
Classifier: Development Status :: 4 - Beta
|
|
11
|
-
Classifier: Programming Language :: Python
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
-
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
18
|
-
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
19
|
-
Requires-Python: >=3.8
|
|
20
|
-
Requires-Dist: haystack-ai
|
|
21
|
-
Requires-Dist: langfuse
|
|
22
|
-
Description-Content-Type: text/markdown
|
|
23
|
-
|
|
24
|
-
# langfuse
|
|
25
|
-
|
|
26
|
-
[](https://pypi.org/project/langfuse-haystack)
|
|
27
|
-
[](https://pypi.org/project/langfuse-haystack)
|
|
28
|
-
|
|
29
|
-
-----
|
|
30
|
-
|
|
31
|
-
**Table of Contents**
|
|
32
|
-
|
|
33
|
-
- [langfuse](#langfuse)
|
|
34
|
-
- [Installation](#installation)
|
|
35
|
-
- [License](#license)
|
|
36
|
-
|
|
37
|
-
## Installation
|
|
38
|
-
|
|
39
|
-
```console
|
|
40
|
-
pip install langfuse-haystack
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## License
|
|
44
|
-
|
|
45
|
-
`langfuse` is distributed under the terms of the [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) license.
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# langfuse
|
|
2
|
-
|
|
3
|
-
[](https://pypi.org/project/langfuse-haystack)
|
|
4
|
-
[](https://pypi.org/project/langfuse-haystack)
|
|
5
|
-
|
|
6
|
-
-----
|
|
7
|
-
|
|
8
|
-
**Table of Contents**
|
|
9
|
-
|
|
10
|
-
- [langfuse](#langfuse)
|
|
11
|
-
- [Installation](#installation)
|
|
12
|
-
- [License](#license)
|
|
13
|
-
|
|
14
|
-
## Installation
|
|
15
|
-
|
|
16
|
-
```console
|
|
17
|
-
pip install langfuse-haystack
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## License
|
|
21
|
-
|
|
22
|
-
`langfuse` is distributed under the terms of the [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) license.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.0.2"
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
from haystack import tracing, component
|
|
2
|
-
|
|
3
|
-
from haystack_integrations.tracing.langfuse import LangfuseTracer
|
|
4
|
-
|
|
5
|
-
from langfuse import Langfuse
|
|
6
|
-
|
|
7
|
-
# from langfuse.openai import openai # noqa
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@component
|
|
11
|
-
class LangfuseComponent:
|
|
12
|
-
def __init__(self, name: str):
|
|
13
|
-
self.name = name
|
|
14
|
-
tracing.enable_tracing(LangfuseTracer(Langfuse(), name))
|
|
15
|
-
|
|
16
|
-
@component.output_types(name=str)
|
|
17
|
-
def run(self):
|
|
18
|
-
return {"name": self.name}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|