lmnr 0.4.8__tar.gz → 0.4.9__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.9}/PKG-INFO +20 -18
- {lmnr-0.4.8 → lmnr-0.4.9}/README.md +9 -7
- {lmnr-0.4.8 → lmnr-0.4.9}/pyproject.toml +12 -12
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/sdk/decorators.py +3 -3
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/sdk/laminar.py +12 -58
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/sdk/types.py +1 -1
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/traceloop_sdk/__init__.py +7 -17
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/traceloop_sdk/decorators/base.py +6 -98
- lmnr-0.4.9/src/lmnr/traceloop_sdk/metrics/__init__.py +0 -0
- lmnr-0.4.9/src/lmnr/traceloop_sdk/tests/__init__.py +1 -0
- lmnr-0.4.9/src/lmnr/traceloop_sdk/tests/conftest.py +111 -0
- lmnr-0.4.9/src/lmnr/traceloop_sdk/tests/test_association_properties.py +229 -0
- lmnr-0.4.9/src/lmnr/traceloop_sdk/tests/test_manual.py +48 -0
- lmnr-0.4.9/src/lmnr/traceloop_sdk/tests/test_nested_tasks.py +47 -0
- lmnr-0.4.9/src/lmnr/traceloop_sdk/tests/test_privacy_no_prompts.py +50 -0
- lmnr-0.4.9/src/lmnr/traceloop_sdk/tests/test_sdk_initialization.py +57 -0
- lmnr-0.4.9/src/lmnr/traceloop_sdk/tests/test_tasks.py +32 -0
- lmnr-0.4.9/src/lmnr/traceloop_sdk/tests/test_workflows.py +262 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/traceloop_sdk/tracing/__init__.py +0 -1
- {lmnr-0.4.8 → lmnr-0.4.9}/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.9}/LICENSE +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/__init__.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/sdk/__init__.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/sdk/evaluations.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/sdk/log.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/sdk/utils.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/traceloop_sdk/.flake8 +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/traceloop_sdk/.python-version +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/traceloop_sdk/config/__init__.py +0 -0
- {lmnr-0.4.8/src/lmnr/traceloop_sdk/metrics → lmnr-0.4.9/src/lmnr/traceloop_sdk/decorators}/__init__.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/traceloop_sdk/instruments.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/traceloop_sdk/metrics/metrics.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/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.9}/src/lmnr/traceloop_sdk/tests/cassettes/test_association_properties/test_langchain_association_properties.yaml +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/traceloop_sdk/tests/cassettes/test_manual/test_manual_report.yaml +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/traceloop_sdk/tests/cassettes/test_manual/test_resource_attributes.yaml +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/traceloop_sdk/tests/cassettes/test_privacy_no_prompts/test_simple_workflow.yaml +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/traceloop_sdk/tests/cassettes/test_prompt_management/test_prompt_management.yaml +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/traceloop_sdk/tests/cassettes/test_sdk_initialization/test_resource_attributes.yaml +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/traceloop_sdk/tests/cassettes/test_tasks/test_task_io_serialization_with_langchain.yaml +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/traceloop_sdk/tests/cassettes/test_workflows/test_simple_aworkflow.yaml +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/traceloop_sdk/tests/cassettes/test_workflows/test_simple_workflow.yaml +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/traceloop_sdk/tests/cassettes/test_workflows/test_streaming_workflow.yaml +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/traceloop_sdk/tracing/content_allow_list.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/traceloop_sdk/tracing/context_manager.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/traceloop_sdk/tracing/manual.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/traceloop_sdk/utils/__init__.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/traceloop_sdk/utils/in_memory_span_exporter.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/traceloop_sdk/utils/json_encoder.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/src/lmnr/traceloop_sdk/utils/package_check.py +0 -0
- {lmnr-0.4.8 → lmnr-0.4.9}/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.9
|
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
|
@@ -79,11 +79,13 @@ And the in your main Python file
|
|
79
79
|
```python
|
80
80
|
from lmnr import Laminar as L
|
81
81
|
|
82
|
-
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>")
|
82
|
+
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>", instruments=set())
|
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
|
+
Also if you want to automatically instrument all supported libraries, then pass `instruments=None` or don't pass `instruments` at all.
|
87
89
|
|
88
90
|
We rely on the amazing [OpenLLMetry](https://github.com/traceloop/openllmetry), open-source package
|
89
91
|
by TraceLoop, to achieve that.
|
@@ -106,7 +108,7 @@ from openai import OpenAI
|
|
106
108
|
|
107
109
|
|
108
110
|
from lmnr import observe, Laminar as L
|
109
|
-
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>")
|
111
|
+
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>", instruments=set())
|
110
112
|
|
111
113
|
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
|
112
114
|
|
@@ -135,7 +137,7 @@ just call `L.start_span`, and OpenTelemetry will handle the context management
|
|
135
137
|
|
136
138
|
```python
|
137
139
|
from lmnr import observe, Laminar as L
|
138
|
-
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>")
|
140
|
+
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>", instruments=set())
|
139
141
|
|
140
142
|
def poem_writer(topic="turbulence"):
|
141
143
|
|
@@ -155,7 +157,7 @@ def poem_writer(topic="turbulence"):
|
|
155
157
|
# while within the span, you can attach laminar events to it
|
156
158
|
L.event("event_name", "event_value")
|
157
159
|
|
158
|
-
L.set_span_output(
|
160
|
+
L.set_span_output(poem) # set an output
|
159
161
|
|
160
162
|
# IMPORTANT: don't forget to end all the spans (usually in `finally` blocks)
|
161
163
|
# Otherwise, the trace may not be sent/displayed correctly
|
@@ -204,7 +206,7 @@ Example use:
|
|
204
206
|
```python
|
205
207
|
from lmnr import Laminar as L
|
206
208
|
|
207
|
-
L.initialize('<YOUR_PROJECT_API_KEY>')
|
209
|
+
L.initialize('<YOUR_PROJECT_API_KEY>', instruments=set())
|
208
210
|
|
209
211
|
result = l.run(
|
210
212
|
pipeline = 'my_pipeline_name',
|
@@ -21,11 +21,13 @@ And the in your main Python file
|
|
21
21
|
```python
|
22
22
|
from lmnr import Laminar as L
|
23
23
|
|
24
|
-
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>")
|
24
|
+
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>", instruments=set())
|
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
|
+
Also if you want to automatically instrument all supported libraries, then pass `instruments=None` or don't pass `instruments` at all.
|
29
31
|
|
30
32
|
We rely on the amazing [OpenLLMetry](https://github.com/traceloop/openllmetry), open-source package
|
31
33
|
by TraceLoop, to achieve that.
|
@@ -48,7 +50,7 @@ from openai import OpenAI
|
|
48
50
|
|
49
51
|
|
50
52
|
from lmnr import observe, Laminar as L
|
51
|
-
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>")
|
53
|
+
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>", instruments=set())
|
52
54
|
|
53
55
|
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
|
54
56
|
|
@@ -77,7 +79,7 @@ just call `L.start_span`, and OpenTelemetry will handle the context management
|
|
77
79
|
|
78
80
|
```python
|
79
81
|
from lmnr import observe, Laminar as L
|
80
|
-
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>")
|
82
|
+
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>", instruments=set())
|
81
83
|
|
82
84
|
def poem_writer(topic="turbulence"):
|
83
85
|
|
@@ -97,7 +99,7 @@ def poem_writer(topic="turbulence"):
|
|
97
99
|
# while within the span, you can attach laminar events to it
|
98
100
|
L.event("event_name", "event_value")
|
99
101
|
|
100
|
-
L.set_span_output(
|
102
|
+
L.set_span_output(poem) # set an output
|
101
103
|
|
102
104
|
# IMPORTANT: don't forget to end all the spans (usually in `finally` blocks)
|
103
105
|
# Otherwise, the trace may not be sent/displayed correctly
|
@@ -146,7 +148,7 @@ Example use:
|
|
146
148
|
```python
|
147
149
|
from lmnr import Laminar as L
|
148
150
|
|
149
|
-
L.initialize('<YOUR_PROJECT_API_KEY>')
|
151
|
+
L.initialize('<YOUR_PROJECT_API_KEY>', instruments=set())
|
150
152
|
|
151
153
|
result = l.run(
|
152
154
|
pipeline = 'my_pipeline_name',
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "lmnr"
|
3
|
-
version = "0.4.
|
3
|
+
version = "0.4.9"
|
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.9"
|
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)
|
@@ -7,15 +7,10 @@ import warnings
|
|
7
7
|
|
8
8
|
from opentelemetry import trace
|
9
9
|
from opentelemetry import context as context_api
|
10
|
-
from opentelemetry.semconv_ai import SpanAttributes
|
11
|
-
|
12
|
-
from lmnr.traceloop_sdk.tracing import get_tracer
|
13
|
-
from lmnr.traceloop_sdk.tracing.tracing import
|
14
|
-
TracerWrapper,
|
15
|
-
set_entity_path,
|
16
|
-
get_chained_entity_path,
|
17
|
-
)
|
18
|
-
from lmnr.traceloop_sdk.utils import camel_to_snake
|
10
|
+
from opentelemetry.semconv_ai import SpanAttributes
|
11
|
+
|
12
|
+
from lmnr.traceloop_sdk.tracing import get_tracer
|
13
|
+
from lmnr.traceloop_sdk.tracing.tracing import TracerWrapper
|
19
14
|
from lmnr.traceloop_sdk.utils.json_encoder import JSONEncoder
|
20
15
|
|
21
16
|
|
@@ -40,8 +35,6 @@ def _json_dumps(data: dict) -> str:
|
|
40
35
|
|
41
36
|
def entity_method(
|
42
37
|
name: Optional[str] = None,
|
43
|
-
version: Optional[int] = None,
|
44
|
-
tlp_span_kind: Optional[TraceloopSpanKindValues] = TraceloopSpanKindValues.TASK,
|
45
38
|
):
|
46
39
|
def decorate(fn):
|
47
40
|
@wraps(fn)
|
@@ -49,33 +42,13 @@ def entity_method(
|
|
49
42
|
if not TracerWrapper.verify_initialized():
|
50
43
|
return fn(*args, **kwargs)
|
51
44
|
|
52
|
-
|
53
|
-
if tlp_span_kind in [
|
54
|
-
TraceloopSpanKindValues.WORKFLOW,
|
55
|
-
TraceloopSpanKindValues.AGENT,
|
56
|
-
]:
|
57
|
-
set_workflow_name(entity_name)
|
58
|
-
span_name = f"{entity_name}.{tlp_span_kind.value}"
|
45
|
+
span_name = name or fn.__name__
|
59
46
|
|
60
47
|
with get_tracer() as tracer:
|
61
48
|
span = tracer.start_span(span_name)
|
62
49
|
ctx = trace.set_span_in_context(span)
|
63
50
|
ctx_token = context_api.attach(ctx)
|
64
51
|
|
65
|
-
if tlp_span_kind in [
|
66
|
-
TraceloopSpanKindValues.TASK,
|
67
|
-
TraceloopSpanKindValues.TOOL,
|
68
|
-
]:
|
69
|
-
entity_path = get_chained_entity_path(entity_name)
|
70
|
-
set_entity_path(entity_path)
|
71
|
-
|
72
|
-
span.set_attribute(
|
73
|
-
SpanAttributes.TRACELOOP_SPAN_KIND, tlp_span_kind.value
|
74
|
-
)
|
75
|
-
span.set_attribute(SpanAttributes.TRACELOOP_ENTITY_NAME, entity_name)
|
76
|
-
if version:
|
77
|
-
span.set_attribute(SpanAttributes.TRACELOOP_ENTITY_VERSION, version)
|
78
|
-
|
79
52
|
try:
|
80
53
|
if _should_send_prompts():
|
81
54
|
span.set_attribute(
|
@@ -110,69 +83,25 @@ def entity_method(
|
|
110
83
|
return decorate
|
111
84
|
|
112
85
|
|
113
|
-
def entity_class(
|
114
|
-
name: Optional[str],
|
115
|
-
version: Optional[int],
|
116
|
-
method_name: str,
|
117
|
-
tlp_span_kind: Optional[TraceloopSpanKindValues] = TraceloopSpanKindValues.TASK,
|
118
|
-
):
|
119
|
-
def decorator(cls):
|
120
|
-
task_name = name if name else camel_to_snake(cls.__name__)
|
121
|
-
method = getattr(cls, method_name)
|
122
|
-
setattr(
|
123
|
-
cls,
|
124
|
-
method_name,
|
125
|
-
entity_method(name=task_name, version=version, tlp_span_kind=tlp_span_kind)(
|
126
|
-
method
|
127
|
-
),
|
128
|
-
)
|
129
|
-
return cls
|
130
|
-
|
131
|
-
return decorator
|
132
|
-
|
133
|
-
|
134
86
|
# Async Decorators
|
135
87
|
|
136
88
|
|
137
89
|
def aentity_method(
|
138
90
|
name: Optional[str] = None,
|
139
|
-
version: Optional[int] = None,
|
140
|
-
tlp_span_kind: Optional[TraceloopSpanKindValues] = TraceloopSpanKindValues.TASK,
|
141
91
|
):
|
142
92
|
def decorate(fn):
|
143
93
|
@wraps(fn)
|
144
94
|
async def wrap(*args, **kwargs):
|
145
95
|
if not TracerWrapper.verify_initialized():
|
146
|
-
print("Tracer not initialized")
|
147
96
|
return await fn(*args, **kwargs)
|
148
97
|
|
149
|
-
|
150
|
-
if tlp_span_kind in [
|
151
|
-
TraceloopSpanKindValues.WORKFLOW,
|
152
|
-
TraceloopSpanKindValues.AGENT,
|
153
|
-
]:
|
154
|
-
set_workflow_name(entity_name)
|
155
|
-
span_name = f"{entity_name}.{tlp_span_kind.value}"
|
98
|
+
span_name = name or fn.__name__
|
156
99
|
|
157
100
|
with get_tracer() as tracer:
|
158
101
|
span = tracer.start_span(span_name)
|
159
102
|
ctx = trace.set_span_in_context(span)
|
160
103
|
ctx_token = context_api.attach(ctx)
|
161
104
|
|
162
|
-
if tlp_span_kind in [
|
163
|
-
TraceloopSpanKindValues.TASK,
|
164
|
-
TraceloopSpanKindValues.TOOL,
|
165
|
-
]:
|
166
|
-
entity_path = get_chained_entity_path(entity_name)
|
167
|
-
set_entity_path(entity_path)
|
168
|
-
|
169
|
-
span.set_attribute(
|
170
|
-
SpanAttributes.TRACELOOP_SPAN_KIND, tlp_span_kind.value
|
171
|
-
)
|
172
|
-
span.set_attribute(SpanAttributes.TRACELOOP_ENTITY_NAME, entity_name)
|
173
|
-
if version:
|
174
|
-
span.set_attribute(SpanAttributes.TRACELOOP_ENTITY_VERSION, version)
|
175
|
-
|
176
105
|
try:
|
177
106
|
if _should_send_prompts():
|
178
107
|
span.set_attribute(
|
@@ -206,27 +135,6 @@ def aentity_method(
|
|
206
135
|
return decorate
|
207
136
|
|
208
137
|
|
209
|
-
def aentity_class(
|
210
|
-
name: Optional[str],
|
211
|
-
version: Optional[int],
|
212
|
-
method_name: str,
|
213
|
-
tlp_span_kind: Optional[TraceloopSpanKindValues] = TraceloopSpanKindValues.TASK,
|
214
|
-
):
|
215
|
-
def decorator(cls):
|
216
|
-
task_name = name if name else camel_to_snake(cls.__name__)
|
217
|
-
method = getattr(cls, method_name)
|
218
|
-
setattr(
|
219
|
-
cls,
|
220
|
-
method_name,
|
221
|
-
aentity_method(
|
222
|
-
name=task_name, version=version, tlp_span_kind=tlp_span_kind
|
223
|
-
)(method),
|
224
|
-
)
|
225
|
-
return cls
|
226
|
-
|
227
|
-
return decorator
|
228
|
-
|
229
|
-
|
230
138
|
def _handle_generator(span, res):
|
231
139
|
# for some reason the SPAN_KEY is not being set in the context of the generator, so we re-set it
|
232
140
|
context_api.attach(trace.set_span_in_context(span))
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
# """unit tests."""
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# """Unit tests configuration module."""
|
2
|
+
|
3
|
+
# import os
|
4
|
+
# import pytest
|
5
|
+
# from lmnr.traceloop_sdk import Traceloop
|
6
|
+
# from lmnr.traceloop_sdk.instruments import Instruments
|
7
|
+
# from lmnr.traceloop_sdk.tracing.tracing import TracerWrapper
|
8
|
+
# from opentelemetry.sdk.trace.export import SimpleSpanProcessor
|
9
|
+
# from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter
|
10
|
+
|
11
|
+
# # pytest_plugins = []
|
12
|
+
|
13
|
+
|
14
|
+
# @pytest.fixture(scope="session")
|
15
|
+
# def exporter():
|
16
|
+
# exporter = InMemorySpanExporter()
|
17
|
+
# Traceloop.init(
|
18
|
+
# app_name="test",
|
19
|
+
# resource_attributes={"something": "yes"},
|
20
|
+
# disable_batch=True,
|
21
|
+
# exporter=exporter,
|
22
|
+
# )
|
23
|
+
# return exporter
|
24
|
+
|
25
|
+
|
26
|
+
# @pytest.fixture(autouse=True)
|
27
|
+
# def clear_exporter(exporter):
|
28
|
+
# exporter.clear()
|
29
|
+
|
30
|
+
|
31
|
+
# @pytest.fixture(autouse=True)
|
32
|
+
# def environment():
|
33
|
+
# if "OPENAI_API_KEY" not in os.environ:
|
34
|
+
# os.environ["OPENAI_API_KEY"] = "test_api_key"
|
35
|
+
|
36
|
+
|
37
|
+
# @pytest.fixture(scope="module")
|
38
|
+
# def vcr_config():
|
39
|
+
# return {
|
40
|
+
# "filter_headers": ["authorization"],
|
41
|
+
# "ignore_hosts": ["openaipublic.blob.core.windows.net"],
|
42
|
+
# }
|
43
|
+
|
44
|
+
|
45
|
+
# @pytest.fixture
|
46
|
+
# def exporter_with_custom_span_processor():
|
47
|
+
# # Clear singleton if existed
|
48
|
+
# if hasattr(TracerWrapper, "instance"):
|
49
|
+
# _trace_wrapper_instance = TracerWrapper.instance
|
50
|
+
# del TracerWrapper.instance
|
51
|
+
|
52
|
+
# class CustomSpanProcessor(SimpleSpanProcessor):
|
53
|
+
# def on_start(self, span, parent_context=None):
|
54
|
+
# span.set_attribute("custom_span", "yes")
|
55
|
+
|
56
|
+
# exporter = InMemorySpanExporter()
|
57
|
+
# Traceloop.init(
|
58
|
+
# exporter=exporter,
|
59
|
+
# processor=CustomSpanProcessor(exporter),
|
60
|
+
# )
|
61
|
+
|
62
|
+
# yield exporter
|
63
|
+
|
64
|
+
# # Restore singleton if any
|
65
|
+
# if _trace_wrapper_instance:
|
66
|
+
# TracerWrapper.instance = _trace_wrapper_instance
|
67
|
+
|
68
|
+
|
69
|
+
# @pytest.fixture
|
70
|
+
# def exporter_with_custom_instrumentations():
|
71
|
+
# # Clear singleton if existed
|
72
|
+
# if hasattr(TracerWrapper, "instance"):
|
73
|
+
# _trace_wrapper_instance = TracerWrapper.instance
|
74
|
+
# del TracerWrapper.instance
|
75
|
+
|
76
|
+
# exporter = InMemorySpanExporter()
|
77
|
+
# Traceloop.init(
|
78
|
+
# exporter=exporter,
|
79
|
+
# disable_batch=True,
|
80
|
+
# instruments=[i for i in Instruments],
|
81
|
+
# )
|
82
|
+
|
83
|
+
# yield exporter
|
84
|
+
|
85
|
+
# # Restore singleton if any
|
86
|
+
# if _trace_wrapper_instance:
|
87
|
+
# TracerWrapper.instance = _trace_wrapper_instance
|
88
|
+
|
89
|
+
|
90
|
+
# @pytest.fixture
|
91
|
+
# def exporter_with_no_metrics():
|
92
|
+
# # Clear singleton if existed
|
93
|
+
# if hasattr(TracerWrapper, "instance"):
|
94
|
+
# _trace_wrapper_instance = TracerWrapper.instance
|
95
|
+
# del TracerWrapper.instance
|
96
|
+
|
97
|
+
# os.environ["TRACELOOP_METRICS_ENABLED"] = "false"
|
98
|
+
|
99
|
+
# exporter = InMemorySpanExporter()
|
100
|
+
|
101
|
+
# Traceloop.init(
|
102
|
+
# exporter=exporter,
|
103
|
+
# disable_batch=True,
|
104
|
+
# )
|
105
|
+
|
106
|
+
# yield exporter
|
107
|
+
|
108
|
+
# # Restore singleton if any
|
109
|
+
# if _trace_wrapper_instance:
|
110
|
+
# TracerWrapper.instance = _trace_wrapper_instance
|
111
|
+
# os.environ["TRACELOOP_METRICS_ENABLED"] = "true"
|