lmnr 0.6.20__tar.gz → 0.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lmnr-0.6.20 → lmnr-0.7.0}/PKG-INFO +10 -8
- {lmnr-0.6.20 → lmnr-0.7.0}/pyproject.toml +17 -7
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/__init__.py +0 -4
- lmnr-0.7.0/src/lmnr/opentelemetry_lib/decorators/__init__.py +291 -0
- lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/__init__.py +678 -0
- lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/config.py +13 -0
- lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/event_emitter.py +211 -0
- lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/span_utils.py +256 -0
- lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/streaming.py +295 -0
- lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/utils.py +179 -0
- lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/version.py +1 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/__init__.py +4 -0
- lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/__init__.py +488 -0
- lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/config.py +8 -0
- lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/event_emitter.py +143 -0
- lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/event_models.py +41 -0
- lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/span_utils.py +229 -0
- lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/utils.py +92 -0
- lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/version.py +1 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/langgraph/__init__.py +16 -16
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/chat_wrappers.py +3 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/completion_wrappers.py +3 -0
- lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/event_models.py +41 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/utils.py +3 -3
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/assistant_wrappers.py +3 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/responses_wrappers.py +7 -0
- lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/threading/__init__.py +190 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/tracing/__init__.py +90 -2
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/tracing/_instrument_initializers.py +12 -7
- lmnr-0.7.0/src/lmnr/opentelemetry_lib/tracing/context.py +109 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/tracing/processor.py +6 -7
- lmnr-0.7.0/src/lmnr/opentelemetry_lib/tracing/tracer.py +47 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/utils/package_check.py +9 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/browser/browser_use_otel.py +9 -7
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/browser/patchright_otel.py +14 -26
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/browser/playwright_otel.py +72 -73
- lmnr-0.7.0/src/lmnr/sdk/browser/pw_utils.py +655 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/asynchronous/resources/browser_events.py +1 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/decorators.py +39 -4
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/evaluations.py +23 -9
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/laminar.py +181 -209
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/types.py +0 -6
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/version.py +1 -1
- lmnr-0.6.20/src/lmnr/opentelemetry_lib/decorators/__init__.py +0 -231
- lmnr-0.6.20/src/lmnr/opentelemetry_lib/tracing/context_properties.py +0 -65
- lmnr-0.6.20/src/lmnr/opentelemetry_lib/tracing/tracer.py +0 -18
- lmnr-0.6.20/src/lmnr/sdk/browser/pw_utils.py +0 -338
- {lmnr-0.6.20 → lmnr-0.7.0}/README.md +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/cli.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/.flake8 +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/__init__.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/litellm/__init__.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/litellm/utils.py +0 -0
- {lmnr-0.6.20/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared → lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic}/event_models.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/config.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/schema_utils.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/utils.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/langgraph/utils.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/__init__.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/__init__.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/config.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/embeddings_wrappers.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/event_emitter.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/image_gen_wrappers.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v0/__init__.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/__init__.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/event_handler_wrapper.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/version.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/opentelemetry/__init__.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/skyvern/__init__.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/tracing/attributes.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/tracing/exporter.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/tracing/instruments.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/utils/__init__.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/utils/json_encoder.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/py.typed +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/__init__.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/browser/__init__.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/browser/rrweb/rrweb.umd.min.cjs +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/browser/utils.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/asynchronous/async_client.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/asynchronous/resources/__init__.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/asynchronous/resources/agent.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/asynchronous/resources/base.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/asynchronous/resources/evals.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/asynchronous/resources/evaluators.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/asynchronous/resources/tags.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/synchronous/resources/__init__.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/synchronous/resources/agent.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/synchronous/resources/base.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/synchronous/resources/browser_events.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/synchronous/resources/evals.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/synchronous/resources/evaluators.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/synchronous/resources/tags.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/synchronous/sync_client.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/datasets.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/eval_control.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/log.py +0 -0
- {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/utils.py +0 -0
@@ -1,32 +1,38 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: lmnr
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.7.0
|
4
4
|
Summary: Python SDK for Laminar
|
5
5
|
Author: lmnr.ai
|
6
6
|
Author-email: lmnr.ai <founders@lmnr.ai>
|
7
7
|
License-Expression: Apache-2.0
|
8
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
8
14
|
Requires-Dist: pydantic>=2.0.3,<3.0.0
|
9
15
|
Requires-Dist: python-dotenv>=1.0
|
10
16
|
Requires-Dist: opentelemetry-api>=1.33.0
|
11
17
|
Requires-Dist: opentelemetry-sdk>=1.33.0
|
12
18
|
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.33.0
|
13
19
|
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.33.0
|
20
|
+
Requires-Dist: opentelemetry-instrumentation>=0.54b0
|
14
21
|
Requires-Dist: opentelemetry-semantic-conventions>=0.54b0
|
15
22
|
Requires-Dist: opentelemetry-semantic-conventions-ai>=0.4.9
|
16
23
|
Requires-Dist: tqdm>=4.0
|
17
24
|
Requires-Dist: tenacity>=8.0
|
18
25
|
Requires-Dist: grpcio>=1
|
19
26
|
Requires-Dist: httpx>=0.25.0
|
20
|
-
Requires-Dist:
|
27
|
+
Requires-Dist: orjson>=3.10.18
|
28
|
+
Requires-Dist: packaging>=22.0
|
21
29
|
Requires-Dist: opentelemetry-instrumentation-alephalpha>=0.40.12 ; extra == 'alephalpha'
|
22
30
|
Requires-Dist: opentelemetry-instrumentation-alephalpha>=0.40.12 ; extra == 'all'
|
23
|
-
Requires-Dist: opentelemetry-instrumentation-anthropic>=0.40.12 ; extra == 'all'
|
24
31
|
Requires-Dist: opentelemetry-instrumentation-bedrock>=0.40.12 ; extra == 'all'
|
25
32
|
Requires-Dist: opentelemetry-instrumentation-chromadb>=0.40.12 ; extra == 'all'
|
26
33
|
Requires-Dist: opentelemetry-instrumentation-cohere>=0.40.12 ; extra == 'all'
|
27
34
|
Requires-Dist: opentelemetry-instrumentation-crewai>=0.40.12 ; extra == 'all'
|
28
35
|
Requires-Dist: opentelemetry-instrumentation-google-generativeai<0.40.10 ; extra == 'all'
|
29
|
-
Requires-Dist: opentelemetry-instrumentation-groq>=0.40.12 ; extra == 'all'
|
30
36
|
Requires-Dist: opentelemetry-instrumentation-haystack>=0.40.12 ; extra == 'all'
|
31
37
|
Requires-Dist: opentelemetry-instrumentation-lancedb>=0.40.12 ; extra == 'all'
|
32
38
|
Requires-Dist: opentelemetry-instrumentation-langchain>=0.40.12 ; extra == 'all'
|
@@ -45,13 +51,11 @@ Requires-Dist: opentelemetry-instrumentation-transformers>=0.40.12 ; extra == 'a
|
|
45
51
|
Requires-Dist: opentelemetry-instrumentation-vertexai>=0.40.12 ; extra == 'all'
|
46
52
|
Requires-Dist: opentelemetry-instrumentation-watsonx>=0.40.12 ; extra == 'all'
|
47
53
|
Requires-Dist: opentelemetry-instrumentation-weaviate>=0.40.12 ; extra == 'all'
|
48
|
-
Requires-Dist: opentelemetry-instrumentation-anthropic>=0.40.12 ; extra == 'anthropic'
|
49
54
|
Requires-Dist: opentelemetry-instrumentation-bedrock>=0.40.12 ; extra == 'bedrock'
|
50
55
|
Requires-Dist: opentelemetry-instrumentation-chromadb>=0.40.12 ; extra == 'chromadb'
|
51
56
|
Requires-Dist: opentelemetry-instrumentation-cohere>=0.40.12 ; extra == 'cohere'
|
52
57
|
Requires-Dist: opentelemetry-instrumentation-crewai>=0.40.12 ; extra == 'crewai'
|
53
58
|
Requires-Dist: opentelemetry-instrumentation-google-generativeai<0.40.10 ; extra == 'google-generativeai'
|
54
|
-
Requires-Dist: opentelemetry-instrumentation-groq>=0.40.12 ; extra == 'groq'
|
55
59
|
Requires-Dist: opentelemetry-instrumentation-haystack>=0.40.12 ; extra == 'haystack'
|
56
60
|
Requires-Dist: opentelemetry-instrumentation-lancedb>=0.40.12 ; extra == 'lancedb'
|
57
61
|
Requires-Dist: opentelemetry-instrumentation-langchain>=0.40.12 ; extra == 'langchain'
|
@@ -73,13 +77,11 @@ Requires-Dist: opentelemetry-instrumentation-weaviate>=0.40.12 ; extra == 'weavi
|
|
73
77
|
Requires-Python: >=3.10, <4
|
74
78
|
Provides-Extra: alephalpha
|
75
79
|
Provides-Extra: all
|
76
|
-
Provides-Extra: anthropic
|
77
80
|
Provides-Extra: bedrock
|
78
81
|
Provides-Extra: chromadb
|
79
82
|
Provides-Extra: cohere
|
80
83
|
Provides-Extra: crewai
|
81
84
|
Provides-Extra: google-generativeai
|
82
|
-
Provides-Extra: groq
|
83
85
|
Provides-Extra: haystack
|
84
86
|
Provides-Extra: lancedb
|
85
87
|
Provides-Extra: langchain
|
@@ -6,7 +6,7 @@
|
|
6
6
|
|
7
7
|
[project]
|
8
8
|
name = "lmnr"
|
9
|
-
version = "0.
|
9
|
+
version = "0.7.0"
|
10
10
|
description = "Python SDK for Laminar"
|
11
11
|
authors = [
|
12
12
|
{ name = "lmnr.ai", email = "founders@lmnr.ai" }
|
@@ -21,6 +21,7 @@ dependencies = [
|
|
21
21
|
"opentelemetry-sdk (>=1.33.0)",
|
22
22
|
"opentelemetry-exporter-otlp-proto-http (>=1.33.0)",
|
23
23
|
"opentelemetry-exporter-otlp-proto-grpc (>=1.33.0)",
|
24
|
+
"opentelemetry-instrumentation (>=0.54b0)",
|
24
25
|
"opentelemetry-semantic-conventions (>=0.54b0)",
|
25
26
|
"opentelemetry-semantic-conventions-ai (>=0.4.9)",
|
26
27
|
"tqdm (>=4.0)",
|
@@ -36,7 +37,18 @@ dependencies = [
|
|
36
37
|
# https://github.com/grpc/grpc/issues/38490
|
37
38
|
"grpcio>=1",
|
38
39
|
"httpx>=0.25.0",
|
39
|
-
"
|
40
|
+
"orjson>=3.10.18",
|
41
|
+
"packaging>=22.0",
|
42
|
+
]
|
43
|
+
# poetry would auto-generate these based on requires-python, but other build
|
44
|
+
# systems don't, so we need to specify them manually.
|
45
|
+
classifiers = [
|
46
|
+
"License :: OSI Approved :: Apache Software License",
|
47
|
+
"Programming Language :: Python :: 3",
|
48
|
+
"Programming Language :: Python :: 3.10",
|
49
|
+
"Programming Language :: Python :: 3.11",
|
50
|
+
"Programming Language :: Python :: 3.12",
|
51
|
+
"Programming Language :: Python :: 3.13",
|
40
52
|
]
|
41
53
|
|
42
54
|
[project.scripts]
|
@@ -51,7 +63,6 @@ lmnr = "lmnr.cli:cli"
|
|
51
63
|
# `poetry add 'lmnr[anthropic,groq]'`
|
52
64
|
|
53
65
|
alephalpha=["opentelemetry-instrumentation-alephalpha>=0.40.12"]
|
54
|
-
anthropic=["opentelemetry-instrumentation-anthropic>=0.40.12"]
|
55
66
|
bedrock=["opentelemetry-instrumentation-bedrock>=0.40.12"]
|
56
67
|
chromadb=["opentelemetry-instrumentation-chromadb>=0.40.12"]
|
57
68
|
cohere=["opentelemetry-instrumentation-cohere>=0.40.12"]
|
@@ -63,7 +74,6 @@ crewai=["opentelemetry-instrumentation-crewai>=0.40.12"]
|
|
63
74
|
# wrapping google-genai.
|
64
75
|
# https://github.com/traceloop/openllmetry/pull/3014
|
65
76
|
google-generativeai=["opentelemetry-instrumentation-google-generativeai<0.40.10"]
|
66
|
-
groq=["opentelemetry-instrumentation-groq>=0.40.12"]
|
67
77
|
haystack=["opentelemetry-instrumentation-haystack>=0.40.12"]
|
68
78
|
lancedb=["opentelemetry-instrumentation-lancedb>=0.40.12"]
|
69
79
|
langchain=["opentelemetry-instrumentation-langchain>=0.40.12"]
|
@@ -88,14 +98,12 @@ weaviate=["opentelemetry-instrumentation-weaviate>=0.40.12"]
|
|
88
98
|
# like `uv add lmnr --all-extras`
|
89
99
|
all = [
|
90
100
|
"opentelemetry-instrumentation-alephalpha>=0.40.12",
|
91
|
-
"opentelemetry-instrumentation-anthropic>=0.40.12",
|
92
101
|
"opentelemetry-instrumentation-bedrock>=0.40.12",
|
93
102
|
"opentelemetry-instrumentation-chromadb>=0.40.12",
|
94
103
|
"opentelemetry-instrumentation-cohere>=0.40.12",
|
95
104
|
"opentelemetry-instrumentation-crewai>=0.40.12",
|
96
105
|
# See comment above on the google-generativeai extra.
|
97
106
|
"opentelemetry-instrumentation-google-generativeai<0.40.10",
|
98
|
-
"opentelemetry-instrumentation-groq>=0.40.12",
|
99
107
|
"opentelemetry-instrumentation-haystack>=0.40.12",
|
100
108
|
"opentelemetry-instrumentation-lancedb>=0.40.12",
|
101
109
|
"opentelemetry-instrumentation-langchain>=0.40.12",
|
@@ -133,10 +141,12 @@ dev = [
|
|
133
141
|
"langchain-core>=0.3.64",
|
134
142
|
"langchain>=0.3.25",
|
135
143
|
"litellm>=1.72.6",
|
144
|
+
"groq>=0.30.0",
|
145
|
+
"anthropic>=0.57.1",
|
136
146
|
]
|
137
147
|
|
138
148
|
[build-system]
|
139
|
-
requires = ["uv_build>=0.7.
|
149
|
+
requires = ["uv_build>=0.7.21,<0.8"]
|
140
150
|
build-backend = "uv_build"
|
141
151
|
|
142
152
|
[tool.uv.workspace]
|
@@ -9,7 +9,6 @@ from .sdk.types import (
|
|
9
9
|
HumanEvaluator,
|
10
10
|
RunAgentResponseChunk,
|
11
11
|
StepChunkContent,
|
12
|
-
TracingLevel,
|
13
12
|
)
|
14
13
|
from .sdk.decorators import observe
|
15
14
|
from .sdk.types import LaminarSpanContext
|
@@ -18,7 +17,6 @@ from .opentelemetry_lib.tracing.attributes import Attributes
|
|
18
17
|
from .opentelemetry_lib.tracing.instruments import Instruments
|
19
18
|
from .opentelemetry_lib.tracing.processor import LaminarSpanProcessor
|
20
19
|
from .opentelemetry_lib.tracing.tracer import get_laminar_tracer_provider, get_tracer
|
21
|
-
from opentelemetry.trace import use_span
|
22
20
|
|
23
21
|
__all__ = [
|
24
22
|
"AgentOutput",
|
@@ -36,10 +34,8 @@ __all__ = [
|
|
36
34
|
"LaminarSpanProcessor",
|
37
35
|
"RunAgentResponseChunk",
|
38
36
|
"StepChunkContent",
|
39
|
-
"TracingLevel",
|
40
37
|
"get_laminar_tracer_provider",
|
41
38
|
"get_tracer",
|
42
39
|
"evaluate",
|
43
40
|
"observe",
|
44
|
-
"use_span",
|
45
41
|
]
|
@@ -0,0 +1,291 @@
|
|
1
|
+
from functools import wraps
|
2
|
+
import logging
|
3
|
+
import pydantic
|
4
|
+
import orjson
|
5
|
+
import types
|
6
|
+
from typing import Any, AsyncGenerator, Callable, Generator, Literal
|
7
|
+
|
8
|
+
from opentelemetry import context as context_api
|
9
|
+
from opentelemetry.trace import Span
|
10
|
+
|
11
|
+
from lmnr.sdk.utils import get_input_from_func_args, is_method
|
12
|
+
from lmnr.opentelemetry_lib import MAX_MANUAL_SPAN_PAYLOAD_SIZE
|
13
|
+
from lmnr.opentelemetry_lib.tracing.tracer import get_tracer_with_context
|
14
|
+
from lmnr.opentelemetry_lib.tracing.attributes import (
|
15
|
+
ASSOCIATION_PROPERTIES,
|
16
|
+
SPAN_INPUT,
|
17
|
+
SPAN_OUTPUT,
|
18
|
+
SPAN_TYPE,
|
19
|
+
)
|
20
|
+
from lmnr.opentelemetry_lib.tracing import TracerWrapper
|
21
|
+
from lmnr.sdk.log import get_default_logger
|
22
|
+
|
23
|
+
logger = get_default_logger(__name__)
|
24
|
+
|
25
|
+
DEFAULT_PLACEHOLDER = {}
|
26
|
+
|
27
|
+
|
28
|
+
def default_json(o):
|
29
|
+
if isinstance(o, pydantic.BaseModel):
|
30
|
+
return o.model_dump()
|
31
|
+
|
32
|
+
# Handle various sequence types, but not strings or bytes
|
33
|
+
if isinstance(o, (list, tuple, set, frozenset)):
|
34
|
+
return list(o)
|
35
|
+
|
36
|
+
try:
|
37
|
+
return str(o)
|
38
|
+
except Exception:
|
39
|
+
logger.debug("Failed to serialize data to JSON, inner type: %s", type(o))
|
40
|
+
pass
|
41
|
+
return DEFAULT_PLACEHOLDER
|
42
|
+
|
43
|
+
|
44
|
+
def json_dumps(data: dict) -> str:
|
45
|
+
try:
|
46
|
+
return orjson.dumps(
|
47
|
+
data,
|
48
|
+
default=default_json,
|
49
|
+
option=orjson.OPT_SERIALIZE_DATACLASS
|
50
|
+
| orjson.OPT_SERIALIZE_UUID
|
51
|
+
| orjson.OPT_UTC_Z
|
52
|
+
| orjson.OPT_NON_STR_KEYS,
|
53
|
+
).decode("utf-8")
|
54
|
+
except Exception:
|
55
|
+
# Log the exception and return a placeholder if serialization completely fails
|
56
|
+
logging.warning("Failed to serialize data to JSON, type: %s", type(data))
|
57
|
+
return "{}" # Return an empty JSON object as a fallback
|
58
|
+
|
59
|
+
|
60
|
+
def _setup_span(
|
61
|
+
span_name: str, span_type: str, association_properties: dict[str, Any] | None
|
62
|
+
):
|
63
|
+
"""Set up a span with the given name, type, and association properties."""
|
64
|
+
with get_tracer_with_context() as (tracer, isolated_context):
|
65
|
+
# Create span in isolated context
|
66
|
+
span = tracer.start_span(
|
67
|
+
span_name,
|
68
|
+
context=isolated_context,
|
69
|
+
attributes={SPAN_TYPE: span_type},
|
70
|
+
)
|
71
|
+
|
72
|
+
if association_properties is not None:
|
73
|
+
for key, value in association_properties.items():
|
74
|
+
span.set_attribute(f"{ASSOCIATION_PROPERTIES}.{key}", value)
|
75
|
+
|
76
|
+
return span
|
77
|
+
|
78
|
+
|
79
|
+
def _process_input(
|
80
|
+
span: Span,
|
81
|
+
fn: Callable,
|
82
|
+
args: tuple,
|
83
|
+
kwargs: dict,
|
84
|
+
ignore_input: bool,
|
85
|
+
ignore_inputs: list[str] | None,
|
86
|
+
input_formatter: Callable[..., str] | None,
|
87
|
+
):
|
88
|
+
"""Process and set input attributes on the span."""
|
89
|
+
if ignore_input:
|
90
|
+
return
|
91
|
+
|
92
|
+
try:
|
93
|
+
if input_formatter is not None:
|
94
|
+
inp = input_formatter(*args, **kwargs)
|
95
|
+
if not isinstance(inp, str):
|
96
|
+
inp = json_dumps(inp)
|
97
|
+
else:
|
98
|
+
inp = json_dumps(
|
99
|
+
get_input_from_func_args(
|
100
|
+
fn,
|
101
|
+
is_method=is_method(fn),
|
102
|
+
func_args=args,
|
103
|
+
func_kwargs=kwargs,
|
104
|
+
ignore_inputs=ignore_inputs,
|
105
|
+
)
|
106
|
+
)
|
107
|
+
|
108
|
+
if len(inp) > MAX_MANUAL_SPAN_PAYLOAD_SIZE:
|
109
|
+
span.set_attribute(SPAN_INPUT, "Laminar: input too large to record")
|
110
|
+
else:
|
111
|
+
span.set_attribute(SPAN_INPUT, inp)
|
112
|
+
except Exception:
|
113
|
+
msg = "Failed to process input, ignoring"
|
114
|
+
if input_formatter is not None:
|
115
|
+
# Only warn the user if they provided an input formatter
|
116
|
+
# because it's their responsibility to make sure it works.
|
117
|
+
logger.warning(msg, exc_info=True)
|
118
|
+
else:
|
119
|
+
logger.debug(msg, exc_info=True)
|
120
|
+
pass
|
121
|
+
|
122
|
+
|
123
|
+
def _process_output(
|
124
|
+
span: Span,
|
125
|
+
result: Any,
|
126
|
+
ignore_output: bool,
|
127
|
+
output_formatter: Callable[..., str] | None,
|
128
|
+
):
|
129
|
+
"""Process and set output attributes on the span."""
|
130
|
+
if ignore_output:
|
131
|
+
return
|
132
|
+
|
133
|
+
try:
|
134
|
+
if output_formatter is not None:
|
135
|
+
output = output_formatter(result)
|
136
|
+
if not isinstance(output, str):
|
137
|
+
output = json_dumps(output)
|
138
|
+
else:
|
139
|
+
output = json_dumps(result)
|
140
|
+
|
141
|
+
if len(output) > MAX_MANUAL_SPAN_PAYLOAD_SIZE:
|
142
|
+
span.set_attribute(SPAN_OUTPUT, "Laminar: output too large to record")
|
143
|
+
else:
|
144
|
+
span.set_attribute(SPAN_OUTPUT, output)
|
145
|
+
except Exception:
|
146
|
+
msg = "Failed to process output, ignoring"
|
147
|
+
if output_formatter is not None:
|
148
|
+
# Only warn the user if they provided an output formatter
|
149
|
+
# because it's their responsibility to make sure it works.
|
150
|
+
logger.warning(msg, exc_info=True)
|
151
|
+
else:
|
152
|
+
logger.debug(msg, exc_info=True)
|
153
|
+
pass
|
154
|
+
|
155
|
+
|
156
|
+
def _cleanup_span(span: Span, wrapper: TracerWrapper):
|
157
|
+
"""Clean up span and context."""
|
158
|
+
span.end()
|
159
|
+
wrapper.pop_span_context()
|
160
|
+
|
161
|
+
|
162
|
+
def observe_base(
|
163
|
+
name: str | None = None,
|
164
|
+
ignore_input: bool = False,
|
165
|
+
ignore_inputs: list[str] | None = None,
|
166
|
+
ignore_output: bool = False,
|
167
|
+
span_type: Literal["DEFAULT", "LLM", "TOOL"] = "DEFAULT",
|
168
|
+
association_properties: dict[str, Any] | None = None,
|
169
|
+
input_formatter: Callable[..., str] | None = None,
|
170
|
+
output_formatter: Callable[..., str] | None = None,
|
171
|
+
):
|
172
|
+
def decorate(fn):
|
173
|
+
@wraps(fn)
|
174
|
+
def wrap(*args, **kwargs):
|
175
|
+
if not TracerWrapper.verify_initialized():
|
176
|
+
return fn(*args, **kwargs)
|
177
|
+
|
178
|
+
span_name = name or fn.__name__
|
179
|
+
wrapper = TracerWrapper()
|
180
|
+
|
181
|
+
span = _setup_span(span_name, span_type, association_properties)
|
182
|
+
new_context = wrapper.push_span_context(span)
|
183
|
+
ctx_token = context_api.attach(new_context)
|
184
|
+
|
185
|
+
_process_input(
|
186
|
+
span, fn, args, kwargs, ignore_input, ignore_inputs, input_formatter
|
187
|
+
)
|
188
|
+
|
189
|
+
try:
|
190
|
+
res = fn(*args, **kwargs)
|
191
|
+
except Exception as e:
|
192
|
+
_process_exception(span, e)
|
193
|
+
_cleanup_span(span, wrapper)
|
194
|
+
raise e
|
195
|
+
finally:
|
196
|
+
# Always restore global context
|
197
|
+
context_api.detach(ctx_token)
|
198
|
+
|
199
|
+
# span will be ended in the generator
|
200
|
+
if isinstance(res, types.GeneratorType):
|
201
|
+
return _handle_generator(span, ctx_token, res)
|
202
|
+
if isinstance(res, types.AsyncGeneratorType):
|
203
|
+
# async def foo() -> AsyncGenerator[int, None]:
|
204
|
+
# is not considered async in a classical sense in Python,
|
205
|
+
# so we handle this inside the sync wrapper.
|
206
|
+
# In particular, CO_COROUTINE is different from CO_ASYNC_GENERATOR.
|
207
|
+
# Flags are listed from LSB here:
|
208
|
+
# https://docs.python.org/3/library/inspect.html#inspect-module-co-flags
|
209
|
+
# See also: https://groups.google.com/g/python-tulip/c/6rWweGXLutU?pli=1
|
210
|
+
return _ahandle_generator(span, ctx_token, res)
|
211
|
+
|
212
|
+
_process_output(span, res, ignore_output, output_formatter)
|
213
|
+
_cleanup_span(span, wrapper)
|
214
|
+
return res
|
215
|
+
|
216
|
+
return wrap
|
217
|
+
|
218
|
+
return decorate
|
219
|
+
|
220
|
+
|
221
|
+
# Async Decorators
|
222
|
+
def async_observe_base(
|
223
|
+
name: str | None = None,
|
224
|
+
ignore_input: bool = False,
|
225
|
+
ignore_inputs: list[str] | None = None,
|
226
|
+
ignore_output: bool = False,
|
227
|
+
span_type: Literal["DEFAULT", "LLM", "TOOL"] = "DEFAULT",
|
228
|
+
association_properties: dict[str, Any] | None = None,
|
229
|
+
input_formatter: Callable[..., str] | None = None,
|
230
|
+
output_formatter: Callable[..., str] | None = None,
|
231
|
+
):
|
232
|
+
def decorate(fn):
|
233
|
+
@wraps(fn)
|
234
|
+
async def wrap(*args, **kwargs):
|
235
|
+
if not TracerWrapper.verify_initialized():
|
236
|
+
return await fn(*args, **kwargs)
|
237
|
+
|
238
|
+
span_name = name or fn.__name__
|
239
|
+
wrapper = TracerWrapper()
|
240
|
+
|
241
|
+
span = _setup_span(span_name, span_type, association_properties)
|
242
|
+
new_context = wrapper.push_span_context(span)
|
243
|
+
ctx_token = context_api.attach(new_context)
|
244
|
+
|
245
|
+
_process_input(
|
246
|
+
span, fn, args, kwargs, ignore_input, ignore_inputs, input_formatter
|
247
|
+
)
|
248
|
+
|
249
|
+
try:
|
250
|
+
res = await fn(*args, **kwargs)
|
251
|
+
except Exception as e:
|
252
|
+
_process_exception(span, e)
|
253
|
+
_cleanup_span(span, wrapper)
|
254
|
+
raise e
|
255
|
+
finally:
|
256
|
+
# Always restore global context
|
257
|
+
context_api.detach(ctx_token)
|
258
|
+
|
259
|
+
# span will be ended in the generator
|
260
|
+
if isinstance(res, types.AsyncGeneratorType):
|
261
|
+
# probably unreachable, read the comment in the similar
|
262
|
+
# part of the sync wrapper.
|
263
|
+
return await _ahandle_generator(span, ctx_token, res)
|
264
|
+
|
265
|
+
_process_output(span, res, ignore_output, output_formatter)
|
266
|
+
_cleanup_span(span, wrapper)
|
267
|
+
return res
|
268
|
+
|
269
|
+
return wrap
|
270
|
+
|
271
|
+
return decorate
|
272
|
+
|
273
|
+
|
274
|
+
def _handle_generator(span: Span, wrapper: TracerWrapper, res: Generator):
|
275
|
+
try:
|
276
|
+
yield from res
|
277
|
+
finally:
|
278
|
+
_cleanup_span(span, wrapper)
|
279
|
+
|
280
|
+
|
281
|
+
async def _ahandle_generator(span: Span, wrapper: TracerWrapper, res: AsyncGenerator):
|
282
|
+
try:
|
283
|
+
async for part in res:
|
284
|
+
yield part
|
285
|
+
finally:
|
286
|
+
_cleanup_span(span, wrapper)
|
287
|
+
|
288
|
+
|
289
|
+
def _process_exception(span: Span, e: Exception):
|
290
|
+
# Note that this `escaped` is sent as a StringValue("True"), not a boolean.
|
291
|
+
span.record_exception(e, escaped=True)
|