lmnr 0.5.3__py3-none-any.whl → 0.6.1__py3-none-any.whl

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 (33) hide show
  1. lmnr/__init__.py +6 -1
  2. lmnr/opentelemetry_lib/__init__.py +23 -36
  3. lmnr/opentelemetry_lib/decorators/__init__.py +219 -0
  4. lmnr/opentelemetry_lib/tracing/__init__.py +158 -1
  5. lmnr/opentelemetry_lib/tracing/_instrument_initializers.py +398 -0
  6. lmnr/opentelemetry_lib/tracing/attributes.py +14 -7
  7. lmnr/opentelemetry_lib/tracing/context_properties.py +53 -0
  8. lmnr/opentelemetry_lib/tracing/exporter.py +60 -0
  9. lmnr/opentelemetry_lib/tracing/instruments.py +121 -0
  10. lmnr/opentelemetry_lib/tracing/processor.py +96 -0
  11. lmnr/opentelemetry_lib/tracing/{context_manager.py → tracer.py} +6 -1
  12. lmnr/opentelemetry_lib/utils/package_check.py +3 -1
  13. lmnr/sdk/browser/browser_use_otel.py +1 -1
  14. lmnr/sdk/browser/playwright_otel.py +22 -7
  15. lmnr/sdk/browser/pw_utils.py +25 -9
  16. lmnr/sdk/client/asynchronous/resources/agent.py +3 -1
  17. lmnr/sdk/client/synchronous/resources/agent.py +3 -1
  18. lmnr/sdk/decorators.py +4 -2
  19. lmnr/sdk/evaluations.py +3 -3
  20. lmnr/sdk/laminar.py +28 -31
  21. lmnr/sdk/utils.py +2 -3
  22. lmnr/version.py +1 -1
  23. {lmnr-0.5.3.dist-info → lmnr-0.6.1.dist-info}/METADATA +65 -62
  24. {lmnr-0.5.3.dist-info → lmnr-0.6.1.dist-info}/RECORD +27 -28
  25. lmnr/opentelemetry_lib/config/__init__.py +0 -12
  26. lmnr/opentelemetry_lib/decorators/base.py +0 -210
  27. lmnr/opentelemetry_lib/instruments.py +0 -42
  28. lmnr/opentelemetry_lib/tracing/content_allow_list.py +0 -24
  29. lmnr/opentelemetry_lib/tracing/tracing.py +0 -1016
  30. lmnr/opentelemetry_lib/utils/in_memory_span_exporter.py +0 -61
  31. {lmnr-0.5.3.dist-info → lmnr-0.6.1.dist-info}/LICENSE +0 -0
  32. {lmnr-0.5.3.dist-info → lmnr-0.6.1.dist-info}/WHEEL +0 -0
  33. {lmnr-0.5.3.dist-info → lmnr-0.6.1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,398 @@
1
+ import abc
2
+
3
+ from typing import Optional
4
+ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
5
+
6
+ from lmnr.opentelemetry_lib.utils.package_check import is_package_installed
7
+
8
+
9
+ class InstrumentorInitializer(abc.ABC):
10
+ @abc.abstractmethod
11
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
12
+ pass
13
+
14
+
15
+ class AlephAlphaInstrumentorInitializer(InstrumentorInitializer):
16
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
17
+ if not is_package_installed("aleph_alpha_client"):
18
+ return None
19
+ if not is_package_installed("opentelemetry-instrumentation-alephalpha"):
20
+ return None
21
+
22
+ from opentelemetry.instrumentation.alephalpha import AlephAlphaInstrumentor
23
+
24
+ return AlephAlphaInstrumentor()
25
+
26
+
27
+ class AnthropicInstrumentorInitializer(InstrumentorInitializer):
28
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
29
+ if not is_package_installed("anthropic"):
30
+ return None
31
+ if not is_package_installed("opentelemetry-instrumentation-anthropic"):
32
+ return None
33
+
34
+ from opentelemetry.instrumentation.anthropic import AnthropicInstrumentor
35
+
36
+ return AnthropicInstrumentor(
37
+ upload_base64_image=None,
38
+ )
39
+
40
+
41
+ class BedrockInstrumentorInitializer(InstrumentorInitializer):
42
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
43
+ if not is_package_installed("boto3"):
44
+ return None
45
+ if not is_package_installed("opentelemetry-instrumentation-bedrock"):
46
+ return None
47
+
48
+ from opentelemetry.instrumentation.bedrock import BedrockInstrumentor
49
+
50
+ return BedrockInstrumentor()
51
+
52
+
53
+ class BrowserUseInstrumentorInitializer(InstrumentorInitializer):
54
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
55
+ if not is_package_installed("browser-use"):
56
+ return None
57
+
58
+ from lmnr.sdk.browser.browser_use_otel import BrowserUseInstrumentor
59
+
60
+ return BrowserUseInstrumentor()
61
+
62
+
63
+ class ChromaInstrumentorInitializer(InstrumentorInitializer):
64
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
65
+ if not is_package_installed("chromadb"):
66
+ return None
67
+ if not is_package_installed("opentelemetry-instrumentation-chromadb"):
68
+ return None
69
+
70
+ from opentelemetry.instrumentation.chromadb import ChromaInstrumentor
71
+
72
+ return ChromaInstrumentor()
73
+
74
+
75
+ class CohereInstrumentorInitializer(InstrumentorInitializer):
76
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
77
+ if not is_package_installed("cohere"):
78
+ return None
79
+ if not is_package_installed("opentelemetry-instrumentation-cohere"):
80
+ return None
81
+
82
+ from opentelemetry.instrumentation.cohere import CohereInstrumentor
83
+
84
+ return CohereInstrumentor()
85
+
86
+
87
+ class CrewAIInstrumentorInitializer(InstrumentorInitializer):
88
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
89
+ if not is_package_installed("crewai"):
90
+ return None
91
+ if not is_package_installed("opentelemetry-instrumentation-crewai"):
92
+ return None
93
+
94
+ from opentelemetry.instrumentation.crewai import CrewAiInstrumentor
95
+
96
+ return CrewAiInstrumentor()
97
+
98
+
99
+ class GoogleGenerativeAIInstrumentorInitializer(InstrumentorInitializer):
100
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
101
+ if not is_package_installed("google-generativeai"):
102
+ return None
103
+ if not is_package_installed(
104
+ "opentelemetry-instrumentation-google-generativeai"
105
+ ):
106
+ return None
107
+
108
+ from opentelemetry.instrumentation.google_generativeai import (
109
+ GoogleGenerativeAiInstrumentor,
110
+ )
111
+
112
+ return GoogleGenerativeAiInstrumentor()
113
+
114
+
115
+ class GoogleGenAIInstrumentorInitializer(InstrumentorInitializer):
116
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
117
+ if not is_package_installed("google-genai"):
118
+ return None
119
+
120
+ from ..opentelemetry.instrumentation.google_genai import (
121
+ GoogleGenAiSdkInstrumentor,
122
+ )
123
+
124
+ return GoogleGenAiSdkInstrumentor()
125
+
126
+
127
+ class GroqInstrumentorInitializer(InstrumentorInitializer):
128
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
129
+ if not is_package_installed("groq"):
130
+ return None
131
+ if not is_package_installed("opentelemetry-instrumentation-groq"):
132
+ return None
133
+
134
+ from opentelemetry.instrumentation.groq import GroqInstrumentor
135
+
136
+ return GroqInstrumentor()
137
+
138
+
139
+ class HaystackInstrumentorInitializer(InstrumentorInitializer):
140
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
141
+ if not is_package_installed("haystack"):
142
+ return None
143
+ if not is_package_installed("opentelemetry-instrumentation-haystack"):
144
+ return None
145
+
146
+ from opentelemetry.instrumentation.haystack import HaystackInstrumentor
147
+
148
+ return HaystackInstrumentor()
149
+
150
+
151
+ class LanceDBInstrumentorInitializer(InstrumentorInitializer):
152
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
153
+ if not is_package_installed("lancedb"):
154
+ return None
155
+ if not is_package_installed("opentelemetry-instrumentation-lancedb"):
156
+ return None
157
+
158
+ from opentelemetry.instrumentation.lancedb import LanceInstrumentor
159
+
160
+ return LanceInstrumentor()
161
+
162
+
163
+ class LangchainInstrumentorInitializer(InstrumentorInitializer):
164
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
165
+ if not is_package_installed("langchain"):
166
+ return None
167
+ if not is_package_installed("opentelemetry-instrumentation-langchain"):
168
+ return None
169
+
170
+ from opentelemetry.instrumentation.langchain import LangchainInstrumentor
171
+
172
+ return LangchainInstrumentor()
173
+
174
+
175
+ class LlamaIndexInstrumentorInitializer(InstrumentorInitializer):
176
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
177
+ if not (
178
+ is_package_installed("llama-index") or is_package_installed("llama_index")
179
+ ):
180
+ return None
181
+ if not is_package_installed("opentelemetry-instrumentation-llamaindex"):
182
+ return None
183
+
184
+ from opentelemetry.instrumentation.llamaindex import LlamaIndexInstrumentor
185
+
186
+ return LlamaIndexInstrumentor()
187
+
188
+
189
+ class MarqoInstrumentorInitializer(InstrumentorInitializer):
190
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
191
+ if not is_package_installed("marqo"):
192
+ return None
193
+ if not is_package_installed("opentelemetry-instrumentation-marqo"):
194
+ return None
195
+
196
+ from opentelemetry.instrumentation.marqo import MarqoInstrumentor
197
+
198
+ return MarqoInstrumentor()
199
+
200
+
201
+ class MCPInstrumentorInitializer(InstrumentorInitializer):
202
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
203
+ if not is_package_installed("mcp"):
204
+ return None
205
+ if not is_package_installed("opentelemetry-instrumentation-mcp"):
206
+ return None
207
+
208
+ from opentelemetry.instrumentation.mcp import MCPInstrumentor
209
+
210
+ return MCPInstrumentor()
211
+
212
+
213
+ class MilvusInstrumentorInitializer(InstrumentorInitializer):
214
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
215
+ if not is_package_installed("pymilvus"):
216
+ return None
217
+ if not is_package_installed("opentelemetry-instrumentation-milvus"):
218
+ return None
219
+
220
+ from opentelemetry.instrumentation.milvus import MilvusInstrumentor
221
+
222
+ return MilvusInstrumentor()
223
+
224
+
225
+ class MistralInstrumentorInitializer(InstrumentorInitializer):
226
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
227
+ if not is_package_installed("mistralai"):
228
+ return None
229
+ if not is_package_installed("opentelemetry-instrumentation-mistralai"):
230
+ return None
231
+
232
+ from opentelemetry.instrumentation.mistralai import MistralAiInstrumentor
233
+
234
+ return MistralAiInstrumentor()
235
+
236
+
237
+ class OllamaInstrumentorInitializer(InstrumentorInitializer):
238
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
239
+ if not is_package_installed("ollama"):
240
+ return None
241
+ if not is_package_installed("opentelemetry-instrumentation-ollama"):
242
+ return None
243
+
244
+ from opentelemetry.instrumentation.ollama import OllamaInstrumentor
245
+
246
+ return OllamaInstrumentor()
247
+
248
+
249
+ class OpenAIInstrumentorInitializer(InstrumentorInitializer):
250
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
251
+ if not is_package_installed("openai"):
252
+ return None
253
+ if not is_package_installed("opentelemetry-instrumentation-openai"):
254
+ return None
255
+
256
+ from opentelemetry.instrumentation.openai import OpenAIInstrumentor
257
+
258
+ return OpenAIInstrumentor(
259
+ # Default in the package provided is an empty function, which
260
+ # results in dropping the image data if we don't explicitly
261
+ # set it to None.
262
+ upload_base64_image=None,
263
+ )
264
+
265
+
266
+ class PatchrightInstrumentorInitializer(InstrumentorInitializer):
267
+ def init_instrumentor(
268
+ self, client, async_client, *args, **kwargs
269
+ ) -> Optional[BaseInstrumentor]:
270
+ if not is_package_installed("patchright"):
271
+ return None
272
+
273
+ from lmnr.sdk.browser.patchright_otel import PatchrightInstrumentor
274
+
275
+ return PatchrightInstrumentor(client, async_client)
276
+
277
+
278
+ class PineconeInstrumentorInitializer(InstrumentorInitializer):
279
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
280
+ if not is_package_installed("pinecone"):
281
+ return None
282
+ if not is_package_installed("opentelemetry-instrumentation-pinecone"):
283
+ return None
284
+
285
+ from opentelemetry.instrumentation.pinecone import PineconeInstrumentor
286
+
287
+ return PineconeInstrumentor()
288
+
289
+
290
+ class PlaywrightInstrumentorInitializer(InstrumentorInitializer):
291
+ def init_instrumentor(
292
+ self, client, async_client, *args, **kwargs
293
+ ) -> Optional[BaseInstrumentor]:
294
+ if not is_package_installed("playwright"):
295
+ return None
296
+
297
+ from lmnr.sdk.browser.playwright_otel import PlaywrightInstrumentor
298
+
299
+ return PlaywrightInstrumentor(client, async_client)
300
+
301
+
302
+ class QdrantInstrumentorInitializer(InstrumentorInitializer):
303
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
304
+ if not is_package_installed("qdrant_client"):
305
+ return None
306
+ if not is_package_installed("opentelemetry-instrumentation-qdrant"):
307
+ return None
308
+
309
+ from opentelemetry.instrumentation.qdrant import QdrantInstrumentor
310
+
311
+ return QdrantInstrumentor()
312
+
313
+
314
+ class ReplicateInstrumentorInitializer(InstrumentorInitializer):
315
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
316
+ if not is_package_installed("replicate"):
317
+ return None
318
+ if not is_package_installed("opentelemetry-instrumentation-replicate"):
319
+ return None
320
+
321
+ from opentelemetry.instrumentation.replicate import ReplicateInstrumentor
322
+
323
+ return ReplicateInstrumentor()
324
+
325
+
326
+ class SageMakerInstrumentorInitializer(InstrumentorInitializer):
327
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
328
+ if not is_package_installed("boto3"):
329
+ return None
330
+ if not is_package_installed("opentelemetry-instrumentation-sagemaker"):
331
+ return None
332
+
333
+ from opentelemetry.instrumentation.sagemaker import SageMakerInstrumentor
334
+
335
+ return SageMakerInstrumentor()
336
+
337
+
338
+ class TogetherInstrumentorInitializer(InstrumentorInitializer):
339
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
340
+ if not is_package_installed("together"):
341
+ return None
342
+ if not is_package_installed("opentelemetry-instrumentation-together"):
343
+ return None
344
+
345
+ from opentelemetry.instrumentation.together import TogetherAiInstrumentor
346
+
347
+ return TogetherAiInstrumentor()
348
+
349
+
350
+ class TransformersInstrumentorInitializer(InstrumentorInitializer):
351
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
352
+ if not is_package_installed("transformers"):
353
+ return None
354
+ if not is_package_installed("opentelemetry-instrumentation-transformers"):
355
+ return None
356
+
357
+ from opentelemetry.instrumentation.transformers import TransformersInstrumentor
358
+
359
+ return TransformersInstrumentor()
360
+
361
+
362
+ class VertexAIInstrumentorInitializer(InstrumentorInitializer):
363
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
364
+ if not is_package_installed("vertexai"):
365
+ return None
366
+ if not is_package_installed("opentelemetry-instrumentation-vertexai"):
367
+ return None
368
+
369
+ from opentelemetry.instrumentation.vertexai import VertexAIInstrumentor
370
+
371
+ return VertexAIInstrumentor()
372
+
373
+
374
+ class WatsonxInstrumentorInitializer(InstrumentorInitializer):
375
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
376
+ if not (
377
+ is_package_installed("ibm-watsonx-ai")
378
+ or is_package_installed("ibm-watson-machine-learning")
379
+ ):
380
+ return None
381
+ if not is_package_installed("opentelemetry-instrumentation-watsonx"):
382
+ return None
383
+
384
+ from opentelemetry.instrumentation.watsonx import WatsonxInstrumentor
385
+
386
+ return WatsonxInstrumentor()
387
+
388
+
389
+ class WeaviateInstrumentorInitializer(InstrumentorInitializer):
390
+ def init_instrumentor(self, *args, **kwargs) -> Optional[BaseInstrumentor]:
391
+ if not is_package_installed("weaviate"):
392
+ return None
393
+ if not is_package_installed("opentelemetry-instrumentation-weaviate"):
394
+ return None
395
+
396
+ from opentelemetry.instrumentation.weaviate import WeaviateInstrumentor
397
+
398
+ return WeaviateInstrumentor()
@@ -1,4 +1,12 @@
1
1
  from enum import Enum
2
+ from opentelemetry.semconv._incubating.attributes.gen_ai_attributes import (
3
+ GEN_AI_SYSTEM,
4
+ GEN_AI_REQUEST_MODEL,
5
+ GEN_AI_RESPONSE_MODEL,
6
+ GEN_AI_USAGE_INPUT_TOKENS,
7
+ GEN_AI_USAGE_OUTPUT_TOKENS,
8
+ GEN_AI_RESPONSE_ID,
9
+ )
2
10
  from opentelemetry.semconv_ai import SpanAttributes
3
11
 
4
12
  SPAN_INPUT = "lmnr.span.input"
@@ -21,14 +29,12 @@ TRACING_LEVEL = "tracing_level"
21
29
  class Attributes(Enum):
22
30
  # == This is the minimum set of attributes for a proper LLM span ==
23
31
  #
24
- # not SpanAttributes.LLM_USAGE_PROMPT_TOKENS,
25
- INPUT_TOKEN_COUNT = "gen_ai.usage.input_tokens"
26
- # not SpanAttributes.LLM_USAGE_COMPLETION_TOKENS,
27
- OUTPUT_TOKEN_COUNT = "gen_ai.usage.output_tokens"
32
+ INPUT_TOKEN_COUNT = GEN_AI_USAGE_INPUT_TOKENS
33
+ OUTPUT_TOKEN_COUNT = GEN_AI_USAGE_OUTPUT_TOKENS
28
34
  TOTAL_TOKEN_COUNT = SpanAttributes.LLM_USAGE_TOTAL_TOKENS
29
- PROVIDER = SpanAttributes.LLM_SYSTEM
30
- REQUEST_MODEL = SpanAttributes.LLM_REQUEST_MODEL
31
- RESPONSE_MODEL = SpanAttributes.LLM_RESPONSE_MODEL
35
+ PROVIDER = GEN_AI_SYSTEM
36
+ REQUEST_MODEL = GEN_AI_REQUEST_MODEL
37
+ RESPONSE_MODEL = GEN_AI_RESPONSE_MODEL
32
38
  #
33
39
  ## == End of minimum set ==
34
40
  # == Additional attributes ==
@@ -36,5 +42,6 @@ class Attributes(Enum):
36
42
  INPUT_COST = "gen_ai.usage.input_cost"
37
43
  OUTPUT_COST = "gen_ai.usage.output_cost"
38
44
  TOTAL_COST = "gen_ai.usage.cost"
45
+ RESPONSE_ID = GEN_AI_RESPONSE_ID
39
46
  #
40
47
  # == End of additional attributes ==
@@ -0,0 +1,53 @@
1
+ import copy
2
+ from typing import Optional
3
+
4
+ from lmnr.opentelemetry_lib.tracing.attributes import (
5
+ ASSOCIATION_PROPERTIES,
6
+ TRACING_LEVEL,
7
+ )
8
+
9
+ from opentelemetry.context import Context, attach, set_value, get_value
10
+ from opentelemetry.trace import Span
11
+ from opentelemetry import trace
12
+
13
+
14
+ def set_association_properties(properties: dict) -> None:
15
+ attach(set_value("association_properties", properties))
16
+
17
+ span = trace.get_current_span()
18
+ _set_association_properties_attributes(span, properties)
19
+
20
+
21
+ def get_association_properties(context: Optional[Context] = None) -> dict:
22
+ return get_value("association_properties", context) or {}
23
+
24
+
25
+ def update_association_properties(
26
+ properties: dict,
27
+ set_on_current_span: bool = True,
28
+ context: Optional[Context] = None,
29
+ ) -> None:
30
+ """Only adds or updates properties that are not already present"""
31
+ association_properties = get_value("association_properties", context) or {}
32
+ association_properties.update(properties)
33
+
34
+ attach(set_value("association_properties", association_properties, context))
35
+
36
+ if set_on_current_span:
37
+ span = trace.get_current_span()
38
+ _set_association_properties_attributes(span, properties)
39
+
40
+
41
+ def remove_association_properties(properties: dict) -> None:
42
+ props: dict = copy.copy(get_value("association_properties") or {})
43
+ for k in properties.keys():
44
+ props.pop(k, None)
45
+ set_association_properties(props)
46
+
47
+
48
+ def _set_association_properties_attributes(span: Span, properties: dict) -> None:
49
+ for key, value in properties.items():
50
+ if key == TRACING_LEVEL:
51
+ span.set_attribute(f"lmnr.internal.{TRACING_LEVEL}", value)
52
+ continue
53
+ span.set_attribute(f"{ASSOCIATION_PROPERTIES}.{key}", value)
@@ -0,0 +1,60 @@
1
+ import grpc
2
+ import re
3
+ from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
4
+ from opentelemetry.sdk.trace import ReadableSpan
5
+ from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
6
+ OTLPSpanExporter,
7
+ )
8
+ from opentelemetry.exporter.otlp.proto.http import Compression
9
+ from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
10
+ OTLPSpanExporter as HTTPOTLPSpanExporter,
11
+ )
12
+ from typing import Optional, Union
13
+
14
+ from lmnr.sdk.utils import from_env
15
+
16
+
17
+ class LaminarSpanExporter(SpanExporter):
18
+ instance: Union[OTLPSpanExporter, HTTPOTLPSpanExporter]
19
+
20
+ def __init__(
21
+ self,
22
+ base_url: Optional[str] = None,
23
+ port: Optional[int] = None,
24
+ api_key: Optional[str] = None,
25
+ timeout_seconds: int = 30,
26
+ force_http: bool = False,
27
+ ):
28
+ url = base_url or from_env("LMNR_BASE_URL") or "https://api.lmnr.ai"
29
+ url = url.rstrip("/")
30
+ if match := re.search(r":(\d{1,5})$", url):
31
+ url = url[: -len(match.group(0))]
32
+ if port is None:
33
+ port = int(match.group(1))
34
+ if port is None:
35
+ port = 443 if force_http else 8443
36
+ final_url = f"{url}:{port or 443}"
37
+ api_key = api_key or from_env("LMNR_PROJECT_API_KEY")
38
+ if force_http:
39
+ self.instance = HTTPOTLPSpanExporter(
40
+ endpoint=f"{final_url}/v1/traces",
41
+ headers={"Authorization": f"Bearer {api_key}"},
42
+ compression=Compression.Gzip,
43
+ timeout=timeout_seconds,
44
+ )
45
+ else:
46
+ self.instance = OTLPSpanExporter(
47
+ endpoint=final_url,
48
+ headers={"authorization": f"Bearer {api_key}"},
49
+ compression=grpc.Compression.Gzip,
50
+ timeout=timeout_seconds,
51
+ )
52
+
53
+ def export(self, spans: list[ReadableSpan]) -> SpanExportResult:
54
+ return self.instance.export(spans)
55
+
56
+ def shutdown(self) -> None:
57
+ return self.instance.shutdown()
58
+
59
+ def force_flush(self, timeout_millis: int = 30000) -> bool:
60
+ return self.instance.force_flush(timeout_millis)
@@ -0,0 +1,121 @@
1
+ import logging
2
+
3
+ from enum import Enum
4
+ from typing import Optional, Set, Dict
5
+
6
+ from opentelemetry.trace import TracerProvider
7
+ import lmnr.opentelemetry_lib.tracing._instrument_initializers as initializers
8
+ from lmnr.sdk.client.synchronous.sync_client import LaminarClient
9
+ from lmnr.sdk.client.asynchronous.async_client import AsyncLaminarClient
10
+
11
+ module_logger = logging.getLogger(__name__)
12
+
13
+
14
+ class Instruments(Enum):
15
+ # The list of libraries which will be autoinstrumented
16
+ # if no specific instruments are provided to initialize()
17
+ ALEPHALPHA = "alephalpha"
18
+ ANTHROPIC = "anthropic"
19
+ BEDROCK = "bedrock"
20
+ BROWSER_USE = "browser_use"
21
+ CHROMA = "chroma"
22
+ COHERE = "cohere"
23
+ CREWAI = "crewai"
24
+ GOOGLE_GENERATIVEAI = "google_generativeai"
25
+ GOOGLE_GENAI = "google_genai"
26
+ GROQ = "groq"
27
+ HAYSTACK = "haystack"
28
+ LANCEDB = "lancedb"
29
+ LANGCHAIN = "langchain"
30
+ LLAMA_INDEX = "llama_index"
31
+ MARQO = "marqo"
32
+ MCP = "mcp"
33
+ MILVUS = "milvus"
34
+ MISTRAL = "mistral"
35
+ OLLAMA = "ollama"
36
+ OPENAI = "openai"
37
+ PATCHRIGHT = "patchright"
38
+ PINECONE = "pinecone"
39
+ PLAYWRIGHT = "playwright"
40
+ QDRANT = "qdrant"
41
+ REPLICATE = "replicate"
42
+ SAGEMAKER = "sagemaker"
43
+ TOGETHER = "together"
44
+ TRANSFORMERS = "transformers"
45
+ VERTEXAI = "vertexai"
46
+ WATSONX = "watsonx"
47
+ WEAVIATE = "weaviate"
48
+
49
+
50
+ INSTRUMENTATION_INITIALIZERS: Dict[
51
+ Instruments, initializers.InstrumentorInitializer
52
+ ] = {
53
+ Instruments.ALEPHALPHA: initializers.AlephAlphaInstrumentorInitializer(),
54
+ Instruments.ANTHROPIC: initializers.AnthropicInstrumentorInitializer(),
55
+ Instruments.BEDROCK: initializers.BedrockInstrumentorInitializer(),
56
+ Instruments.BROWSER_USE: initializers.BrowserUseInstrumentorInitializer(),
57
+ Instruments.CHROMA: initializers.ChromaInstrumentorInitializer(),
58
+ Instruments.COHERE: initializers.CohereInstrumentorInitializer(),
59
+ Instruments.CREWAI: initializers.CrewAIInstrumentorInitializer(),
60
+ Instruments.GOOGLE_GENERATIVEAI: initializers.GoogleGenerativeAIInstrumentorInitializer(),
61
+ Instruments.GOOGLE_GENAI: initializers.GoogleGenAIInstrumentorInitializer(),
62
+ Instruments.GROQ: initializers.GroqInstrumentorInitializer(),
63
+ Instruments.HAYSTACK: initializers.HaystackInstrumentorInitializer(),
64
+ Instruments.LANCEDB: initializers.LanceDBInstrumentorInitializer(),
65
+ Instruments.LANGCHAIN: initializers.LangchainInstrumentorInitializer(),
66
+ Instruments.LLAMA_INDEX: initializers.LlamaIndexInstrumentorInitializer(),
67
+ Instruments.MARQO: initializers.MarqoInstrumentorInitializer(),
68
+ Instruments.MCP: initializers.MCPInstrumentorInitializer(),
69
+ Instruments.MILVUS: initializers.MilvusInstrumentorInitializer(),
70
+ Instruments.MISTRAL: initializers.MistralInstrumentorInitializer(),
71
+ Instruments.OLLAMA: initializers.OllamaInstrumentorInitializer(),
72
+ Instruments.OPENAI: initializers.OpenAIInstrumentorInitializer(),
73
+ Instruments.PATCHRIGHT: initializers.PatchrightInstrumentorInitializer(),
74
+ Instruments.PINECONE: initializers.PineconeInstrumentorInitializer(),
75
+ Instruments.PLAYWRIGHT: initializers.PlaywrightInstrumentorInitializer(),
76
+ Instruments.QDRANT: initializers.QdrantInstrumentorInitializer(),
77
+ Instruments.REPLICATE: initializers.ReplicateInstrumentorInitializer(),
78
+ Instruments.SAGEMAKER: initializers.SageMakerInstrumentorInitializer(),
79
+ Instruments.TOGETHER: initializers.TogetherInstrumentorInitializer(),
80
+ Instruments.TRANSFORMERS: initializers.TransformersInstrumentorInitializer(),
81
+ Instruments.VERTEXAI: initializers.VertexAIInstrumentorInitializer(),
82
+ Instruments.WATSONX: initializers.WatsonxInstrumentorInitializer(),
83
+ Instruments.WEAVIATE: initializers.WeaviateInstrumentorInitializer(),
84
+ }
85
+
86
+
87
+ def init_instrumentations(
88
+ tracer_provider: TracerProvider,
89
+ instruments: Optional[Set[Instruments]] = None,
90
+ block_instruments: Optional[Set[Instruments]] = None,
91
+ client: Optional[LaminarClient] = None,
92
+ async_client: Optional[AsyncLaminarClient] = None,
93
+ ):
94
+ block_instruments = block_instruments or set()
95
+ if instruments is None:
96
+ instruments = set(Instruments)
97
+ if not isinstance(instruments, set):
98
+ instruments = set(instruments)
99
+
100
+ # Remove any instruments that were explicitly blocked
101
+ instruments = instruments - block_instruments
102
+
103
+ for instrument in instruments:
104
+ initializer = INSTRUMENTATION_INITIALIZERS.get(instrument)
105
+ if initializer is None:
106
+ module_logger.error(f"Invalid instrument: {instrument}")
107
+ continue
108
+
109
+ try:
110
+ instrumentor = initializer.init_instrumentor(client, async_client)
111
+ if instrumentor is None:
112
+ continue
113
+ if not instrumentor.is_instrumented_by_opentelemetry:
114
+ instrumentor.instrument(tracer_provider=tracer_provider)
115
+ except Exception as e:
116
+ if "No module named 'langchain_community'" in str(e):
117
+ # LangChain instrumentor does not require langchain_community,
118
+ # but throws this error if it's not installed.
119
+ continue
120
+ module_logger.error(f"Error initializing instrumentor: {e}")
121
+ continue