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.
Files changed (99) hide show
  1. {lmnr-0.6.20 → lmnr-0.7.0}/PKG-INFO +10 -8
  2. {lmnr-0.6.20 → lmnr-0.7.0}/pyproject.toml +17 -7
  3. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/__init__.py +0 -4
  4. lmnr-0.7.0/src/lmnr/opentelemetry_lib/decorators/__init__.py +291 -0
  5. lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/__init__.py +678 -0
  6. lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/config.py +13 -0
  7. lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/event_emitter.py +211 -0
  8. lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/span_utils.py +256 -0
  9. lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/streaming.py +295 -0
  10. lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/utils.py +179 -0
  11. lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/version.py +1 -0
  12. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/__init__.py +4 -0
  13. lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/__init__.py +488 -0
  14. lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/config.py +8 -0
  15. lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/event_emitter.py +143 -0
  16. lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/event_models.py +41 -0
  17. lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/span_utils.py +229 -0
  18. lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/utils.py +92 -0
  19. lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/version.py +1 -0
  20. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/langgraph/__init__.py +16 -16
  21. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/chat_wrappers.py +3 -0
  22. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/completion_wrappers.py +3 -0
  23. lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/event_models.py +41 -0
  24. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/utils.py +3 -3
  25. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/assistant_wrappers.py +3 -0
  26. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/responses_wrappers.py +7 -0
  27. lmnr-0.7.0/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/threading/__init__.py +190 -0
  28. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/tracing/__init__.py +90 -2
  29. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/tracing/_instrument_initializers.py +12 -7
  30. lmnr-0.7.0/src/lmnr/opentelemetry_lib/tracing/context.py +109 -0
  31. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/tracing/processor.py +6 -7
  32. lmnr-0.7.0/src/lmnr/opentelemetry_lib/tracing/tracer.py +47 -0
  33. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/utils/package_check.py +9 -0
  34. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/browser/browser_use_otel.py +9 -7
  35. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/browser/patchright_otel.py +14 -26
  36. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/browser/playwright_otel.py +72 -73
  37. lmnr-0.7.0/src/lmnr/sdk/browser/pw_utils.py +655 -0
  38. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/asynchronous/resources/browser_events.py +1 -0
  39. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/decorators.py +39 -4
  40. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/evaluations.py +23 -9
  41. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/laminar.py +181 -209
  42. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/types.py +0 -6
  43. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/version.py +1 -1
  44. lmnr-0.6.20/src/lmnr/opentelemetry_lib/decorators/__init__.py +0 -231
  45. lmnr-0.6.20/src/lmnr/opentelemetry_lib/tracing/context_properties.py +0 -65
  46. lmnr-0.6.20/src/lmnr/opentelemetry_lib/tracing/tracer.py +0 -18
  47. lmnr-0.6.20/src/lmnr/sdk/browser/pw_utils.py +0 -338
  48. {lmnr-0.6.20 → lmnr-0.7.0}/README.md +0 -0
  49. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/cli.py +0 -0
  50. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/.flake8 +0 -0
  51. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/__init__.py +0 -0
  52. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/litellm/__init__.py +0 -0
  53. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/litellm/utils.py +0 -0
  54. {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
  55. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/config.py +0 -0
  56. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/schema_utils.py +0 -0
  57. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/utils.py +0 -0
  58. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/langgraph/utils.py +0 -0
  59. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/__init__.py +0 -0
  60. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/__init__.py +0 -0
  61. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/config.py +0 -0
  62. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/embeddings_wrappers.py +0 -0
  63. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/event_emitter.py +0 -0
  64. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/image_gen_wrappers.py +0 -0
  65. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v0/__init__.py +0 -0
  66. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/__init__.py +0 -0
  67. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/event_handler_wrapper.py +0 -0
  68. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/version.py +0 -0
  69. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/opentelemetry/__init__.py +0 -0
  70. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/skyvern/__init__.py +0 -0
  71. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/tracing/attributes.py +0 -0
  72. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/tracing/exporter.py +0 -0
  73. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/tracing/instruments.py +0 -0
  74. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/utils/__init__.py +0 -0
  75. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/opentelemetry_lib/utils/json_encoder.py +0 -0
  76. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/py.typed +0 -0
  77. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/__init__.py +0 -0
  78. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/browser/__init__.py +0 -0
  79. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/browser/rrweb/rrweb.umd.min.cjs +0 -0
  80. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/browser/utils.py +0 -0
  81. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/asynchronous/async_client.py +0 -0
  82. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/asynchronous/resources/__init__.py +0 -0
  83. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/asynchronous/resources/agent.py +0 -0
  84. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/asynchronous/resources/base.py +0 -0
  85. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/asynchronous/resources/evals.py +0 -0
  86. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/asynchronous/resources/evaluators.py +0 -0
  87. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/asynchronous/resources/tags.py +0 -0
  88. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/synchronous/resources/__init__.py +0 -0
  89. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/synchronous/resources/agent.py +0 -0
  90. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/synchronous/resources/base.py +0 -0
  91. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/synchronous/resources/browser_events.py +0 -0
  92. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/synchronous/resources/evals.py +0 -0
  93. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/synchronous/resources/evaluators.py +0 -0
  94. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/synchronous/resources/tags.py +0 -0
  95. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/client/synchronous/sync_client.py +0 -0
  96. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/datasets.py +0 -0
  97. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/eval_control.py +0 -0
  98. {lmnr-0.6.20 → lmnr-0.7.0}/src/lmnr/sdk/log.py +0 -0
  99. {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.6.20
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: opentelemetry-instrumentation-threading>=0.54b0
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.6.20"
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
- "opentelemetry-instrumentation-threading>=0.54b0",
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.19,<0.8"]
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)