lmnr 0.4.8__tar.gz → 0.4.10__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.
- {lmnr-0.4.8 → lmnr-0.4.10}/PKG-INFO +44 -49
- {lmnr-0.4.8 → lmnr-0.4.10}/README.md +33 -38
- {lmnr-0.4.8 → lmnr-0.4.10}/pyproject.toml +12 -12
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/__init__.py +1 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/sdk/decorators.py +3 -3
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/sdk/laminar.py +12 -58
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/sdk/types.py +1 -1
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/__init__.py +7 -17
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/decorators/base.py +6 -98
- lmnr-0.4.10/src/lmnr/traceloop_sdk/metrics/__init__.py +0 -0
- lmnr-0.4.10/src/lmnr/traceloop_sdk/tests/__init__.py +1 -0
- lmnr-0.4.10/src/lmnr/traceloop_sdk/tests/conftest.py +111 -0
- lmnr-0.4.10/src/lmnr/traceloop_sdk/tests/test_association_properties.py +229 -0
- lmnr-0.4.10/src/lmnr/traceloop_sdk/tests/test_manual.py +48 -0
- lmnr-0.4.10/src/lmnr/traceloop_sdk/tests/test_nested_tasks.py +47 -0
- lmnr-0.4.10/src/lmnr/traceloop_sdk/tests/test_privacy_no_prompts.py +50 -0
- lmnr-0.4.10/src/lmnr/traceloop_sdk/tests/test_sdk_initialization.py +57 -0
- lmnr-0.4.10/src/lmnr/traceloop_sdk/tests/test_tasks.py +32 -0
- lmnr-0.4.10/src/lmnr/traceloop_sdk/tests/test_workflows.py +262 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/tracing/__init__.py +0 -1
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/tracing/tracing.py +19 -30
- lmnr-0.4.8/src/lmnr/traceloop_sdk/README.md +0 -16
- lmnr-0.4.8/src/lmnr/traceloop_sdk/decorators/__init__.py +0 -131
- lmnr-0.4.8/src/lmnr/traceloop_sdk/tests/__init__.py +0 -1
- lmnr-0.4.8/src/lmnr/traceloop_sdk/tests/conftest.py +0 -111
- lmnr-0.4.8/src/lmnr/traceloop_sdk/tests/test_association_properties.py +0 -229
- lmnr-0.4.8/src/lmnr/traceloop_sdk/tests/test_manual.py +0 -48
- lmnr-0.4.8/src/lmnr/traceloop_sdk/tests/test_nested_tasks.py +0 -47
- lmnr-0.4.8/src/lmnr/traceloop_sdk/tests/test_privacy_no_prompts.py +0 -50
- lmnr-0.4.8/src/lmnr/traceloop_sdk/tests/test_sdk_initialization.py +0 -57
- lmnr-0.4.8/src/lmnr/traceloop_sdk/tests/test_tasks.py +0 -32
- lmnr-0.4.8/src/lmnr/traceloop_sdk/tests/test_workflows.py +0 -261
- {lmnr-0.4.8 → lmnr-0.4.10}/LICENSE +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/sdk/__init__.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/sdk/evaluations.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/sdk/log.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/sdk/utils.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/.flake8 +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/.python-version +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/config/__init__.py +0 -0
- {lmnr-0.4.8/src/lmnr/traceloop_sdk/metrics → lmnr-0.4.10/src/lmnr/traceloop_sdk/decorators}/__init__.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/instruments.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/metrics/metrics.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/tests/cassettes/test_association_properties/test_langchain_and_external_association_properties.yaml +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/tests/cassettes/test_association_properties/test_langchain_association_properties.yaml +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/tests/cassettes/test_manual/test_manual_report.yaml +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/tests/cassettes/test_manual/test_resource_attributes.yaml +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/tests/cassettes/test_privacy_no_prompts/test_simple_workflow.yaml +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/tests/cassettes/test_prompt_management/test_prompt_management.yaml +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/tests/cassettes/test_sdk_initialization/test_resource_attributes.yaml +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/tests/cassettes/test_tasks/test_task_io_serialization_with_langchain.yaml +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/tests/cassettes/test_workflows/test_simple_aworkflow.yaml +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/tests/cassettes/test_workflows/test_simple_workflow.yaml +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/tests/cassettes/test_workflows/test_streaming_workflow.yaml +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/tracing/content_allow_list.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/tracing/context_manager.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/tracing/manual.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/utils/__init__.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/utils/in_memory_span_exporter.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/utils/json_encoder.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/utils/package_check.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.10}/src/lmnr/traceloop_sdk/version.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: lmnr
|
3
|
-
Version: 0.4.
|
3
|
+
Version: 0.4.10
|
4
4
|
Summary: Python SDK for Laminar AI
|
5
5
|
License: Apache-2.0
|
6
6
|
Author: lmnr.ai
|
@@ -11,11 +11,11 @@ Classifier: Programming Language :: Python :: 3.9
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.10
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
14
|
-
Requires-Dist: asyncio (>=3.
|
15
|
-
Requires-Dist: backoff (>=2.
|
16
|
-
Requires-Dist: colorama (>=0.4
|
17
|
-
Requires-Dist: deprecated (>=1.
|
18
|
-
Requires-Dist: jinja2 (>=3.
|
14
|
+
Requires-Dist: asyncio (>=3.0,<4.0)
|
15
|
+
Requires-Dist: backoff (>=2.0,<3.0)
|
16
|
+
Requires-Dist: colorama (>=0.4,<0.5)
|
17
|
+
Requires-Dist: deprecated (>=1.0,<2.0)
|
18
|
+
Requires-Dist: jinja2 (>=3.0,<4.0)
|
19
19
|
Requires-Dist: opentelemetry-api (>=1.27.0,<2.0.0)
|
20
20
|
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc (>=1.26.0,<2.0.0)
|
21
21
|
Requires-Dist: opentelemetry-exporter-otlp-proto-http (>=1.26.0,<2.0.0)
|
@@ -49,11 +49,11 @@ Requires-Dist: opentelemetry-instrumentation-watsonx (>=0.30.0,<0.31.0)
|
|
49
49
|
Requires-Dist: opentelemetry-instrumentation-weaviate (>=0.30.0,<0.31.0)
|
50
50
|
Requires-Dist: opentelemetry-sdk (>=1.27.0,<2.0.0)
|
51
51
|
Requires-Dist: opentelemetry-semantic-conventions-ai (==0.4.1)
|
52
|
-
Requires-Dist: posthog (
|
53
|
-
Requires-Dist: pydantic (>=2.7
|
54
|
-
Requires-Dist: python-dotenv (>=1.0
|
55
|
-
Requires-Dist: requests (>=2.
|
56
|
-
Requires-Dist: tenacity (>=8.
|
52
|
+
Requires-Dist: posthog (>=3.0,<4.0)
|
53
|
+
Requires-Dist: pydantic (>=2.7,<3.0)
|
54
|
+
Requires-Dist: python-dotenv (>=1.0,<2.0)
|
55
|
+
Requires-Dist: requests (>=2.0,<3.0)
|
56
|
+
Requires-Dist: tenacity (>=8.0,<9.0)
|
57
57
|
Description-Content-Type: text/markdown
|
58
58
|
|
59
59
|
# Laminar Python
|
@@ -77,16 +77,19 @@ pip install lmnr
|
|
77
77
|
And the in your main Python file
|
78
78
|
|
79
79
|
```python
|
80
|
-
from lmnr import Laminar as L
|
80
|
+
from lmnr import Laminar as L, Instruments
|
81
81
|
|
82
|
-
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>")
|
82
|
+
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>", instruments={Instruments.OPENAI, Instruments.ANTHROPIC})
|
83
83
|
```
|
84
84
|
|
85
|
-
|
86
|
-
calls with OpenTelemetry-compatible instrumentation
|
85
|
+
If you want to automatically instrument particular LLM, Vector DB, and related
|
86
|
+
calls with OpenTelemetry-compatible instrumentation, then pass the appropriate instruments to `.initialize()`.
|
87
|
+
|
88
|
+
You can pass an empty set as `instruments=set()` to disable any kind of automatic instrumentation.
|
89
|
+
Also if you want to automatically instrument all supported libraries, then pass `instruments=None` or don't pass `instruments` at all.
|
87
90
|
|
88
|
-
|
89
|
-
by TraceLoop, to
|
91
|
+
Our code is based on the [OpenLLMetry](https://github.com/traceloop/openllmetry), open-source package
|
92
|
+
by TraceLoop. Also, we are grateful to Traceloop for implementing autoinstrumentations for many libraries.
|
90
93
|
|
91
94
|
### Project API key
|
92
95
|
|
@@ -105,8 +108,8 @@ import os
|
|
105
108
|
from openai import OpenAI
|
106
109
|
|
107
110
|
|
108
|
-
from lmnr import observe, Laminar as L
|
109
|
-
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>")
|
111
|
+
from lmnr import observe, Laminar as L, Instruments
|
112
|
+
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>", instruments={Instruments.OPENAI})
|
110
113
|
|
111
114
|
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
|
112
115
|
|
@@ -128,40 +131,32 @@ print(poem_writer(topic="laminar flow"))
|
|
128
131
|
|
129
132
|
### Manual instrumentation
|
130
133
|
|
131
|
-
|
132
|
-
`trace.start_span`. Our wrapper sets the span into the active context.
|
133
|
-
You don't have to explicitly pass the spans around, it is enough to
|
134
|
-
just call `L.start_span`, and OpenTelemetry will handle the context management
|
134
|
+
Also, you can `Laminar.start_as_current_span` if you want to record a chunk of your code.
|
135
135
|
|
136
136
|
```python
|
137
|
-
from lmnr import observe, Laminar as L
|
138
|
-
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>")
|
137
|
+
from lmnr import observe, Laminar as L, Instruments
|
138
|
+
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>", instruments={Instruments.OPENAI})
|
139
139
|
|
140
140
|
def poem_writer(topic="turbulence"):
|
141
|
-
|
142
|
-
span = L.start_span("poem_writer", topic) # start a span
|
143
|
-
|
144
141
|
prompt = f"write a poem about {topic}"
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
return poem
|
142
|
+
messages = [
|
143
|
+
{"role": "system", "content": "You are a helpful assistant."},
|
144
|
+
{"role": "user", "content": prompt},
|
145
|
+
]
|
146
|
+
|
147
|
+
with L.start_as_current_span(name="poem_writer", input=messages):
|
148
|
+
# OpenAI calls are still automatically instrumented with OpenLLMetry
|
149
|
+
response = client.chat.completions.create(
|
150
|
+
model="gpt-4o",
|
151
|
+
messages=messages,
|
152
|
+
)
|
153
|
+
poem = response.choices[0].message.content
|
154
|
+
# while within the span, you can attach laminar events to it
|
155
|
+
L.event("event_name", "event_value")
|
156
|
+
|
157
|
+
L.set_span_output(poem) # set an output
|
158
|
+
|
159
|
+
return poem
|
165
160
|
```
|
166
161
|
|
167
162
|
|
@@ -204,7 +199,7 @@ Example use:
|
|
204
199
|
```python
|
205
200
|
from lmnr import Laminar as L
|
206
201
|
|
207
|
-
L.initialize('<YOUR_PROJECT_API_KEY>')
|
202
|
+
L.initialize('<YOUR_PROJECT_API_KEY>', instruments=set())
|
208
203
|
|
209
204
|
result = l.run(
|
210
205
|
pipeline = 'my_pipeline_name',
|
@@ -19,16 +19,19 @@ pip install lmnr
|
|
19
19
|
And the in your main Python file
|
20
20
|
|
21
21
|
```python
|
22
|
-
from lmnr import Laminar as L
|
22
|
+
from lmnr import Laminar as L, Instruments
|
23
23
|
|
24
|
-
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>")
|
24
|
+
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>", instruments={Instruments.OPENAI, Instruments.ANTHROPIC})
|
25
25
|
```
|
26
26
|
|
27
|
-
|
28
|
-
calls with OpenTelemetry-compatible instrumentation
|
27
|
+
If you want to automatically instrument particular LLM, Vector DB, and related
|
28
|
+
calls with OpenTelemetry-compatible instrumentation, then pass the appropriate instruments to `.initialize()`.
|
29
|
+
|
30
|
+
You can pass an empty set as `instruments=set()` to disable any kind of automatic instrumentation.
|
31
|
+
Also if you want to automatically instrument all supported libraries, then pass `instruments=None` or don't pass `instruments` at all.
|
29
32
|
|
30
|
-
|
31
|
-
by TraceLoop, to
|
33
|
+
Our code is based on the [OpenLLMetry](https://github.com/traceloop/openllmetry), open-source package
|
34
|
+
by TraceLoop. Also, we are grateful to Traceloop for implementing autoinstrumentations for many libraries.
|
32
35
|
|
33
36
|
### Project API key
|
34
37
|
|
@@ -47,8 +50,8 @@ import os
|
|
47
50
|
from openai import OpenAI
|
48
51
|
|
49
52
|
|
50
|
-
from lmnr import observe, Laminar as L
|
51
|
-
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>")
|
53
|
+
from lmnr import observe, Laminar as L, Instruments
|
54
|
+
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>", instruments={Instruments.OPENAI})
|
52
55
|
|
53
56
|
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
|
54
57
|
|
@@ -70,40 +73,32 @@ print(poem_writer(topic="laminar flow"))
|
|
70
73
|
|
71
74
|
### Manual instrumentation
|
72
75
|
|
73
|
-
|
74
|
-
`trace.start_span`. Our wrapper sets the span into the active context.
|
75
|
-
You don't have to explicitly pass the spans around, it is enough to
|
76
|
-
just call `L.start_span`, and OpenTelemetry will handle the context management
|
76
|
+
Also, you can `Laminar.start_as_current_span` if you want to record a chunk of your code.
|
77
77
|
|
78
78
|
```python
|
79
|
-
from lmnr import observe, Laminar as L
|
80
|
-
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>")
|
79
|
+
from lmnr import observe, Laminar as L, Instruments
|
80
|
+
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>", instruments={Instruments.OPENAI})
|
81
81
|
|
82
82
|
def poem_writer(topic="turbulence"):
|
83
|
-
|
84
|
-
span = L.start_span("poem_writer", topic) # start a span
|
85
|
-
|
86
83
|
prompt = f"write a poem about {topic}"
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
return poem
|
84
|
+
messages = [
|
85
|
+
{"role": "system", "content": "You are a helpful assistant."},
|
86
|
+
{"role": "user", "content": prompt},
|
87
|
+
]
|
88
|
+
|
89
|
+
with L.start_as_current_span(name="poem_writer", input=messages):
|
90
|
+
# OpenAI calls are still automatically instrumented with OpenLLMetry
|
91
|
+
response = client.chat.completions.create(
|
92
|
+
model="gpt-4o",
|
93
|
+
messages=messages,
|
94
|
+
)
|
95
|
+
poem = response.choices[0].message.content
|
96
|
+
# while within the span, you can attach laminar events to it
|
97
|
+
L.event("event_name", "event_value")
|
98
|
+
|
99
|
+
L.set_span_output(poem) # set an output
|
100
|
+
|
101
|
+
return poem
|
107
102
|
```
|
108
103
|
|
109
104
|
|
@@ -146,7 +141,7 @@ Example use:
|
|
146
141
|
```python
|
147
142
|
from lmnr import Laminar as L
|
148
143
|
|
149
|
-
L.initialize('<YOUR_PROJECT_API_KEY>')
|
144
|
+
L.initialize('<YOUR_PROJECT_API_KEY>', instruments=set())
|
150
145
|
|
151
146
|
result = l.run(
|
152
147
|
pipeline = 'my_pipeline_name',
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "lmnr"
|
3
|
-
version = "0.4.
|
3
|
+
version = "0.4.10"
|
4
4
|
description = "Python SDK for Laminar AI"
|
5
5
|
authors = [
|
6
6
|
{ name = "lmnr.ai", email = "founders@lmnr.ai" }
|
@@ -11,7 +11,7 @@ license = "Apache-2.0"
|
|
11
11
|
|
12
12
|
[tool.poetry]
|
13
13
|
name = "lmnr"
|
14
|
-
version = "0.4.
|
14
|
+
version = "0.4.10"
|
15
15
|
description = "Python SDK for Laminar AI"
|
16
16
|
authors = ["lmnr.ai"]
|
17
17
|
readme = "README.md"
|
@@ -19,11 +19,11 @@ license = "Apache-2.0"
|
|
19
19
|
|
20
20
|
[tool.poetry.dependencies]
|
21
21
|
python = ">=3.9,<4"
|
22
|
-
pydantic = "
|
23
|
-
requests = "
|
24
|
-
python-dotenv = "
|
25
|
-
backoff = "
|
26
|
-
asyncio = "
|
22
|
+
pydantic = "~=2.7"
|
23
|
+
requests = "~=2.0"
|
24
|
+
python-dotenv = "~=1.0"
|
25
|
+
backoff = "~=2.0"
|
26
|
+
asyncio = "~=3.0"
|
27
27
|
opentelemetry-api = "^1.27.0"
|
28
28
|
opentelemetry-sdk = "^1.27.0"
|
29
29
|
opentelemetry-exporter-otlp-proto-http = "^1.26.0"
|
@@ -33,11 +33,11 @@ opentelemetry-instrumentation-sqlalchemy = "^0.48b0"
|
|
33
33
|
opentelemetry-instrumentation-urllib3 = "^0.48b0"
|
34
34
|
opentelemetry-instrumentation-threading = "^0.48b0"
|
35
35
|
opentelemetry-semantic-conventions-ai = "0.4.1"
|
36
|
-
colorama = "^0.4
|
37
|
-
tenacity = "
|
38
|
-
jinja2 = "
|
39
|
-
deprecated = "
|
40
|
-
posthog = "
|
36
|
+
colorama = "^0.4"
|
37
|
+
tenacity = "~=8.0"
|
38
|
+
jinja2 = "~=3.0"
|
39
|
+
deprecated = "~=1.0"
|
40
|
+
posthog = "~=3.0"
|
41
41
|
opentelemetry-instrumentation-mistralai = "^0.30.0"
|
42
42
|
opentelemetry-instrumentation-openai = "^0.30.0"
|
43
43
|
opentelemetry-instrumentation-ollama = "^0.30.0"
|
@@ -3,11 +3,11 @@ from lmnr.traceloop_sdk.decorators.base import (
|
|
3
3
|
aentity_method,
|
4
4
|
)
|
5
5
|
from opentelemetry.trace import INVALID_SPAN, get_current_span
|
6
|
-
from lmnr.traceloop_sdk import Traceloop
|
7
6
|
|
8
7
|
from typing import Callable, Optional, ParamSpec, TypeVar, cast
|
9
8
|
|
10
|
-
from .
|
9
|
+
from lmnr.traceloop_sdk.tracing.tracing import update_association_properties
|
10
|
+
|
11
11
|
from .utils import is_async
|
12
12
|
|
13
13
|
P = ParamSpec("P")
|
@@ -57,7 +57,7 @@ def observe(
|
|
57
57
|
association_properties["session_id"] = session_id
|
58
58
|
if user_id is not None:
|
59
59
|
association_properties["user_id"] = user_id
|
60
|
-
|
60
|
+
update_association_properties(association_properties)
|
61
61
|
return (
|
62
62
|
aentity_method(name=name)(func)
|
63
63
|
if is_async(func)
|
@@ -1,9 +1,8 @@
|
|
1
|
+
from lmnr.traceloop_sdk.instruments import Instruments
|
1
2
|
from opentelemetry import context
|
2
3
|
from opentelemetry.trace import (
|
3
4
|
INVALID_SPAN,
|
4
5
|
get_current_span,
|
5
|
-
set_span_in_context,
|
6
|
-
Span,
|
7
6
|
SpanKind,
|
8
7
|
)
|
9
8
|
from opentelemetry.semconv_ai import SpanAttributes
|
@@ -16,7 +15,7 @@ from contextlib import contextmanager
|
|
16
15
|
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
|
17
16
|
|
18
17
|
from pydantic.alias_generators import to_snake
|
19
|
-
from typing import Any, Optional, Union
|
18
|
+
from typing import Any, Optional, Set, Union
|
20
19
|
|
21
20
|
import copy
|
22
21
|
import datetime
|
@@ -27,6 +26,8 @@ import os
|
|
27
26
|
import requests
|
28
27
|
import uuid
|
29
28
|
|
29
|
+
from lmnr.traceloop_sdk.tracing.tracing import set_association_properties, update_association_properties
|
30
|
+
|
30
31
|
from .log import VerboseColorfulFormatter
|
31
32
|
|
32
33
|
from .types import (
|
@@ -51,6 +52,7 @@ class Laminar:
|
|
51
52
|
project_api_key: Optional[str] = None,
|
52
53
|
env: dict[str, str] = {},
|
53
54
|
base_url: Optional[str] = None,
|
55
|
+
instruments: Optional[Set[Instruments]] = None,
|
54
56
|
):
|
55
57
|
"""Initialize Laminar context across the application.
|
56
58
|
This method must be called before using any other Laminar methods or
|
@@ -104,6 +106,7 @@ class Laminar:
|
|
104
106
|
endpoint=cls.__base_url,
|
105
107
|
headers={"authorization": f"Bearer {cls.__project_api_key}"},
|
106
108
|
),
|
109
|
+
instruments=instruments,
|
107
110
|
)
|
108
111
|
|
109
112
|
@classmethod
|
@@ -354,50 +357,15 @@ class Laminar:
|
|
354
357
|
yield span
|
355
358
|
|
356
359
|
@classmethod
|
357
|
-
def
|
358
|
-
|
359
|
-
name: str,
|
360
|
-
input: Any = None,
|
361
|
-
) -> Span:
|
362
|
-
"""Start a new span with the given name. Useful for manual
|
363
|
-
instrumentation.
|
364
|
-
|
365
|
-
Args:
|
366
|
-
name (str): name of the span
|
367
|
-
input (Any, optional): input to the span. Will be sent as an
|
368
|
-
attribute, so must be json serializable. Defaults to None.
|
369
|
-
|
370
|
-
Returns:
|
371
|
-
Tuple[Span, object]: Span - the started span, object -
|
372
|
-
context token
|
373
|
-
that must be passed to `end_span` to end the span.
|
374
|
-
|
375
|
-
"""
|
376
|
-
tracer = get_tracer().__enter__()
|
377
|
-
span = tracer.start_span(name)
|
378
|
-
# apparently, detaching from this context is not mandatory.
|
379
|
-
# According to traceloop, and the github issue in opentelemetry,
|
380
|
-
# the context is collected by the garbage collector.
|
381
|
-
# https://github.com/open-telemetry/opentelemetry-python/issues/2606#issuecomment-2106320379
|
382
|
-
context.attach(set_span_in_context(span))
|
383
|
-
|
384
|
-
if input is not None:
|
385
|
-
span.set_attribute(
|
386
|
-
SpanAttributes.TRACELOOP_ENTITY_INPUT, json.dumps({"input": input})
|
387
|
-
)
|
388
|
-
|
389
|
-
return span
|
390
|
-
|
391
|
-
@classmethod
|
392
|
-
def set_span_output(cls, span: Span, output: Any = None):
|
393
|
-
"""Set the output of the span. Useful for manual instrumentation.
|
360
|
+
def set_span_output(cls, output: Any = None):
|
361
|
+
"""Set the output of the current span. Useful for manual instrumentation.
|
394
362
|
|
395
363
|
Args:
|
396
|
-
span (Span): the span to set the output for
|
397
364
|
output (Any, optional): output of the span. Will be sent as an
|
398
365
|
attribute, so must be json serializable. Defaults to None.
|
399
366
|
"""
|
400
|
-
|
367
|
+
span = get_current_span()
|
368
|
+
if output is not None and span != INVALID_SPAN:
|
401
369
|
span.set_attribute(
|
402
370
|
SpanAttributes.TRACELOOP_ENTITY_OUTPUT, json.dumps(output)
|
403
371
|
)
|
@@ -421,26 +389,12 @@ class Laminar:
|
|
421
389
|
Useful for grouping spans or traces by user.
|
422
390
|
Defaults to None.
|
423
391
|
"""
|
424
|
-
current_span = get_current_span()
|
425
|
-
if current_span != INVALID_SPAN:
|
426
|
-
cls.__logger.debug(
|
427
|
-
"Laminar().set_session() called inside a span context. Setting"
|
428
|
-
" it manually in the current span."
|
429
|
-
)
|
430
|
-
if session_id is not None:
|
431
|
-
current_span.set_attribute(
|
432
|
-
"traceloop.association.properties.session_id", session_id
|
433
|
-
)
|
434
|
-
if user_id is not None:
|
435
|
-
current_span.set_attribute(
|
436
|
-
"traceloop.association.properties.user_id", user_id
|
437
|
-
)
|
438
392
|
association_properties = {}
|
439
393
|
if session_id is not None:
|
440
394
|
association_properties["session_id"] = session_id
|
441
395
|
if user_id is not None:
|
442
396
|
association_properties["user_id"] = user_id
|
443
|
-
|
397
|
+
update_association_properties(association_properties)
|
444
398
|
|
445
399
|
@classmethod
|
446
400
|
def clear_session(cls):
|
@@ -448,7 +402,7 @@ class Laminar:
|
|
448
402
|
props: dict = copy.copy(context.get_value("association_properties"))
|
449
403
|
props.pop("session_id", None)
|
450
404
|
props.pop("user_id", None)
|
451
|
-
|
405
|
+
set_association_properties(props)
|
452
406
|
|
453
407
|
@classmethod
|
454
408
|
def create_evaluation(cls, name: str) -> CreateEvaluationResponse:
|
@@ -18,11 +18,7 @@ from lmnr.traceloop_sdk.config import (
|
|
18
18
|
is_tracing_enabled,
|
19
19
|
is_metrics_enabled,
|
20
20
|
)
|
21
|
-
from lmnr.traceloop_sdk.tracing.tracing import
|
22
|
-
TracerWrapper,
|
23
|
-
set_association_properties,
|
24
|
-
set_external_prompt_tracing_context,
|
25
|
-
)
|
21
|
+
from lmnr.traceloop_sdk.tracing.tracing import TracerWrapper
|
26
22
|
from typing import Dict
|
27
23
|
|
28
24
|
|
@@ -38,14 +34,14 @@ class Traceloop:
|
|
38
34
|
def init(
|
39
35
|
app_name: Optional[str] = sys.argv[0],
|
40
36
|
api_endpoint: str = "https://api.lmnr.ai",
|
41
|
-
api_key: str = None,
|
37
|
+
api_key: Optional[str] = None,
|
42
38
|
headers: Dict[str, str] = {},
|
43
39
|
disable_batch=False,
|
44
|
-
exporter: SpanExporter = None,
|
45
|
-
metrics_exporter: MetricExporter = None,
|
46
|
-
metrics_headers: Dict[str, str] = None,
|
47
|
-
processor: SpanProcessor = None,
|
48
|
-
propagator: TextMapPropagator = None,
|
40
|
+
exporter: Optional[SpanExporter] = None,
|
41
|
+
metrics_exporter: Optional[MetricExporter] = None,
|
42
|
+
metrics_headers: Optional[Dict[str, str]] = None,
|
43
|
+
processor: Optional[SpanProcessor] = None,
|
44
|
+
propagator: Optional[TextMapPropagator] = None,
|
49
45
|
should_enrich_metrics: bool = True,
|
50
46
|
resource_attributes: dict = {},
|
51
47
|
instruments: Optional[Set[Instruments]] = None,
|
@@ -130,9 +126,3 @@ class Traceloop:
|
|
130
126
|
)
|
131
127
|
|
132
128
|
Traceloop.__metrics_wrapper = MetricsWrapper(exporter=metrics_exporter)
|
133
|
-
|
134
|
-
def set_association_properties(properties: dict) -> None:
|
135
|
-
set_association_properties(properties)
|
136
|
-
|
137
|
-
def set_prompt(template: str, variables: dict, version: int):
|
138
|
-
set_external_prompt_tracing_context(template, variables, version)
|