lmnr 0.6.20__py3-none-any.whl → 0.7.0__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 (46) hide show
  1. lmnr/__init__.py +0 -4
  2. lmnr/opentelemetry_lib/decorators/__init__.py +211 -151
  3. lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/__init__.py +678 -0
  4. lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/config.py +13 -0
  5. lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/event_emitter.py +211 -0
  6. lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/event_models.py +41 -0
  7. lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/span_utils.py +256 -0
  8. lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/streaming.py +295 -0
  9. lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/utils.py +179 -0
  10. lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/version.py +1 -0
  11. lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/__init__.py +4 -0
  12. lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/__init__.py +488 -0
  13. lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/config.py +8 -0
  14. lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/event_emitter.py +143 -0
  15. lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/event_models.py +41 -0
  16. lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/span_utils.py +229 -0
  17. lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/utils.py +92 -0
  18. lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/version.py +1 -0
  19. lmnr/opentelemetry_lib/opentelemetry/instrumentation/langgraph/__init__.py +16 -16
  20. lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/chat_wrappers.py +3 -0
  21. lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/completion_wrappers.py +3 -0
  22. lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/utils.py +3 -3
  23. lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/assistant_wrappers.py +3 -0
  24. lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/responses_wrappers.py +7 -0
  25. lmnr/opentelemetry_lib/opentelemetry/instrumentation/threading/__init__.py +190 -0
  26. lmnr/opentelemetry_lib/tracing/__init__.py +90 -2
  27. lmnr/opentelemetry_lib/tracing/_instrument_initializers.py +12 -7
  28. lmnr/opentelemetry_lib/tracing/context.py +109 -0
  29. lmnr/opentelemetry_lib/tracing/processor.py +6 -7
  30. lmnr/opentelemetry_lib/tracing/tracer.py +29 -0
  31. lmnr/opentelemetry_lib/utils/package_check.py +9 -0
  32. lmnr/sdk/browser/browser_use_otel.py +9 -7
  33. lmnr/sdk/browser/patchright_otel.py +14 -26
  34. lmnr/sdk/browser/playwright_otel.py +72 -73
  35. lmnr/sdk/browser/pw_utils.py +436 -119
  36. lmnr/sdk/client/asynchronous/resources/browser_events.py +1 -0
  37. lmnr/sdk/decorators.py +39 -4
  38. lmnr/sdk/evaluations.py +23 -9
  39. lmnr/sdk/laminar.py +181 -209
  40. lmnr/sdk/types.py +0 -6
  41. lmnr/version.py +1 -1
  42. {lmnr-0.6.20.dist-info → lmnr-0.7.0.dist-info}/METADATA +10 -8
  43. {lmnr-0.6.20.dist-info → lmnr-0.7.0.dist-info}/RECORD +45 -29
  44. {lmnr-0.6.20.dist-info → lmnr-0.7.0.dist-info}/WHEEL +1 -1
  45. lmnr/opentelemetry_lib/tracing/context_properties.py +0 -65
  46. {lmnr-0.6.20.dist-info → lmnr-0.7.0.dist-info}/entry_points.txt +0 -0
lmnr/sdk/laminar.py CHANGED
@@ -1,10 +1,10 @@
1
1
  from contextlib import contextmanager
2
2
  from contextvars import Context
3
3
  import warnings
4
- from typing_extensions import deprecated
5
4
  from lmnr.opentelemetry_lib import TracerManager
5
+ from lmnr.opentelemetry_lib.tracing import TracerWrapper, get_current_context
6
6
  from lmnr.opentelemetry_lib.tracing.instruments import Instruments
7
- from lmnr.opentelemetry_lib.tracing.tracer import get_tracer
7
+ from lmnr.opentelemetry_lib.tracing.tracer import get_tracer_with_context
8
8
  from lmnr.opentelemetry_lib.tracing.attributes import (
9
9
  ASSOCIATION_PROPERTIES,
10
10
  USER_ID,
@@ -13,13 +13,13 @@ from lmnr.opentelemetry_lib.tracing.attributes import (
13
13
  )
14
14
  from lmnr.opentelemetry_lib import MAX_MANUAL_SPAN_PAYLOAD_SIZE
15
15
  from lmnr.opentelemetry_lib.decorators import json_dumps
16
- from opentelemetry import context as context_api, trace
16
+ from opentelemetry import trace
17
17
  from opentelemetry.context import attach, detach
18
- from opentelemetry.trace import INVALID_TRACE_ID
18
+ from opentelemetry.trace import INVALID_TRACE_ID, Span, Status, StatusCode, use_span
19
19
  from opentelemetry.sdk.trace.id_generator import RandomIdGenerator
20
20
  from opentelemetry.util.types import AttributeValue
21
21
 
22
- from typing import Any, Literal
22
+ from typing import Any, Iterator, Literal
23
23
 
24
24
  import datetime
25
25
  import logging
@@ -33,12 +33,6 @@ from lmnr.opentelemetry_lib.tracing.attributes import (
33
33
  SPAN_OUTPUT,
34
34
  TRACE_TYPE,
35
35
  )
36
- from lmnr.opentelemetry_lib.tracing.context_properties import (
37
- get_association_properties,
38
- remove_association_properties,
39
- set_association_properties,
40
- update_association_properties,
41
- )
42
36
  from lmnr.sdk.utils import from_env, is_otel_attribute_value_type
43
37
 
44
38
  from .log import VerboseColorfulFormatter
@@ -46,7 +40,6 @@ from .log import VerboseColorfulFormatter
46
40
  from .types import (
47
41
  LaminarSpanContext,
48
42
  TraceType,
49
- TracingLevel,
50
43
  )
51
44
 
52
45
 
@@ -60,10 +53,15 @@ class Laminar:
60
53
  cls,
61
54
  project_api_key: str | None = None,
62
55
  base_url: str | None = None,
56
+ base_http_url: str | None = None,
63
57
  http_port: int | None = None,
64
58
  grpc_port: int | None = None,
65
- instruments: set[Instruments] | None = None,
66
- disabled_instruments: set[Instruments] | None = None,
59
+ instruments: (
60
+ list[Instruments] | set[Instruments] | tuple[Instruments] | None
61
+ ) = None,
62
+ disabled_instruments: (
63
+ list[Instruments] | set[Instruments] | tuple[Instruments] | None
64
+ ) = None,
67
65
  disable_batch: bool = False,
68
66
  max_export_batch_size: int | None = None,
69
67
  export_timeout_seconds: int | None = None,
@@ -76,40 +74,45 @@ class Laminar:
76
74
 
77
75
  Args:
78
76
  project_api_key (str | None, optional): Laminar project api key.\
79
- You can generate one by going to the projects\
80
- settings page on the Laminar dashboard.\
81
- If not specified, it will try to read from the\
82
- LMNR_PROJECT_API_KEY environment variable\
83
- in os.environ or in .env file.
84
- Defaults to None.
77
+ You can generate one by going to the projects settings page on\
78
+ the Laminar dashboard. If not specified, we will try to read\
79
+ from the LMNR_PROJECT_API_KEY environment variable in os.environ\
80
+ or in .env file. Defaults to None.
85
81
  base_url (str | None, optional): Laminar API url. Do NOT include\
86
- the port number, use `http_port` and `grpc_port`.\
87
- If not specified, defaults to https://api.lmnr.ai.
88
- http_port (int | None, optional): Laminar API http port.\
89
- If not specified, defaults to 443.
90
- grpc_port (int | None, optional): Laminar API grpc port.\
91
- If not specified, defaults to 8443.
92
- instruments (set[Instruments] | None, optional): Instruments to\
93
- enable. Defaults to all instruments. You can pass\
94
- an empty set to disable all instruments. Read more:\
95
- https://docs.lmnr.ai/tracing/automatic-instrumentation
96
- disabled_instruments (set[Instruments] | None, optional): Instruments to\
97
- disable. Defaults to None.
82
+ the port number, use `http_port` and `grpc_port`. If not\
83
+ specified, defaults to https://api.lmnr.ai.
84
+ base_http_url (str | None, optional): Laminar API http url. Only\
85
+ set this if your Laminar backend HTTP is proxied through a\
86
+ different host. If not specified, defaults to\
87
+ https://api.lmnr.ai.
88
+ http_port (int | None, optional): Laminar API http port. If not\
89
+ specified, defaults to 443.
90
+ grpc_port (int | None, optional): Laminar API grpc port. If not\
91
+ specified, defaults to 8443.
92
+ instruments (set[Instruments] | list[Instruments] | tuple[Instruments] | None, optional):
93
+ Instruments to enable. Defaults to all instruments. You can pass\
94
+ an empty set to disable all instruments. Read more:\
95
+ https://docs.lmnr.ai/tracing/automatic-instrumentation
96
+ disabled_instruments (set[Instruments] | list[Instruments] | tuple[Instruments] | None, optional):
97
+ Instruments to disable. Defaults to None.
98
98
  disable_batch (bool, optional): If set to True, spans will be sent\
99
- immediately to the backend. Useful for debugging, but\
100
- may cause performance overhead in production.
101
- Defaults to False.
99
+ immediately to the backend. Useful for debugging, but may cause\
100
+ performance overhead in production. Defaults to False.
101
+ max_export_batch_size (int | None, optional): Maximum number of spans\
102
+ to export in a single batch. If not specified, defaults to 64\
103
+ (lower than the OpenTelemetry default of 512). If you see\
104
+ `DEADLINE_EXCEEDED` errors, try reducing this value.
102
105
  export_timeout_seconds (int | None, optional): Timeout for the OTLP\
103
- exporter. Defaults to 30 seconds (unlike the\
104
- OpenTelemetry default of 10 seconds).
105
- Defaults to None.
106
+ exporter. Defaults to 30 seconds (unlike the OpenTelemetry\
107
+ default of 10 seconds). Defaults to None.
106
108
  set_global_tracer_provider (bool, optional): If set to True, the\
107
- Laminar tracer provider will be set as the global\
108
- tracer provider. OpenTelemetry allows only one tracer\
109
- provider per app, so set this to False, if you are using\
110
- another tracing library. Setting this to False may break\
111
- some external instrumentations, e.g. LiteLLM.
112
- Defaults to True.
109
+ Laminar tracer provider will be set as the global tracer provider.\
110
+ OpenTelemetry allows only one tracer provider per app, so set this\
111
+ to False, if you are using another tracing library. Setting this to\
112
+ False may break some external instrumentations, e.g. LiteLLM.\
113
+ Defaults to True.
114
+ otel_logger_level (int, optional): OpenTelemetry logger level. Defaults\
115
+ to logging.ERROR.
113
116
 
114
117
  Raises:
115
118
  ValueError: If project API key is not set
@@ -132,10 +135,16 @@ class Laminar:
132
135
 
133
136
  url = base_url or from_env("LMNR_BASE_URL") or "https://api.lmnr.ai"
134
137
  url = url.rstrip("/")
135
- if not url.startswith("http"):
138
+ if not url.startswith("http:") and not url.startswith("https:"):
136
139
  url = f"https://{url}"
137
140
  if match := re.search(r":(\d{1,5})$", url):
138
141
  url = url[: -len(match.group(0))]
142
+ cls.__logger.info(f"Ignoring port in base URL: {match.group(1)}")
143
+ http_url = base_http_url or url
144
+ if not http_url.startswith("http:") and not http_url.startswith("https:"):
145
+ http_url = f"https://{http_url}"
146
+ if match := re.search(r":(\d{1,5})$", http_url):
147
+ http_url = http_url[: -len(match.group(0))]
139
148
  if http_port is None:
140
149
  cls.__logger.info(f"Using HTTP port from base URL: {match.group(1)}")
141
150
  http_port = int(match.group(1))
@@ -143,7 +152,7 @@ class Laminar:
143
152
  cls.__logger.info(f"Using HTTP port passed as an argument: {http_port}")
144
153
 
145
154
  cls.__initialized = True
146
- cls.__base_http_url = f"{url}:{http_port or 443}"
155
+ cls.__base_http_url = f"{http_url}:{http_port or 443}"
147
156
 
148
157
  if not os.getenv("OTEL_ATTRIBUTE_COUNT_LIMIT"):
149
158
  # each message is at least 2 attributes: role and content,
@@ -155,8 +164,10 @@ class Laminar:
155
164
  http_port=http_port or 443,
156
165
  port=grpc_port or 8443,
157
166
  project_api_key=cls.__project_api_key,
158
- instruments=instruments,
159
- block_instruments=disabled_instruments,
167
+ instruments=set(instruments) if instruments is not None else None,
168
+ block_instruments=(
169
+ set(disabled_instruments) if disabled_instruments is not None else None
170
+ ),
160
171
  disable_batch=disable_batch,
161
172
  max_export_batch_size=max_export_batch_size,
162
173
  timeout_seconds=export_timeout_seconds,
@@ -202,6 +213,9 @@ class Laminar:
202
213
  be epoch nanoseconds. If not specified, relies on the underlying\
203
214
  OpenTelemetry implementation. Defaults to None.
204
215
  """
216
+ if not cls.is_initialized():
217
+ return
218
+
205
219
  if timestamp and isinstance(timestamp, datetime.datetime):
206
220
  timestamp = int(timestamp.timestamp() * 1e9)
207
221
 
@@ -211,7 +225,7 @@ class Laminar:
211
225
  if value is not None:
212
226
  event["lmnr.event.value"] = value
213
227
 
214
- current_span = trace.get_current_span()
228
+ current_span = trace.get_current_span(context=get_current_context())
215
229
  if current_span == trace.INVALID_SPAN:
216
230
  cls.__logger.warning(
217
231
  "`Laminar().event()` called outside of span context. "
@@ -233,7 +247,7 @@ class Laminar:
233
247
  labels: list[str] | None = None,
234
248
  parent_span_context: LaminarSpanContext | None = None,
235
249
  tags: list[str] | None = None,
236
- ):
250
+ ) -> Iterator[Span]:
237
251
  """Start a new span as the current span. Useful for manual
238
252
  instrumentation. If `span_type` is set to `"LLM"`, you should report
239
253
  usage and response attributes manually. See `Laminar.set_span_attributes`
@@ -281,8 +295,10 @@ class Laminar:
281
295
  )
282
296
  return
283
297
 
284
- with get_tracer() as tracer:
285
- ctx = context or context_api.get_current()
298
+ wrapper = TracerWrapper()
299
+
300
+ with get_tracer_with_context() as (tracer, isolated_context):
301
+ ctx = context or isolated_context
286
302
  if parent_span_context is not None:
287
303
  span_context = LaminarSpanContext.try_to_otel_span_context(
288
304
  parent_span_context, cls.__logger
@@ -313,6 +329,7 @@ class Laminar:
313
329
  f"`start_as_current_span` Could not set tags: {tags}. Tags must be a list of strings. "
314
330
  "Tags will be ignored."
315
331
  )
332
+
316
333
  with tracer.start_as_current_span(
317
334
  name,
318
335
  context=ctx,
@@ -322,6 +339,7 @@ class Laminar:
322
339
  **(tag_props),
323
340
  },
324
341
  ) as span:
342
+ wrapper.push_span_context(span)
325
343
  if input is not None:
326
344
  serialized_input = json_dumps(input)
327
345
  if len(serialized_input) > MAX_MANUAL_SPAN_PAYLOAD_SIZE:
@@ -336,60 +354,13 @@ class Laminar:
336
354
  )
337
355
  yield span
338
356
 
357
+ wrapper.pop_span_context()
339
358
  # TODO: Figure out if this is necessary
340
359
  try:
341
360
  detach(ctx_token)
342
361
  except Exception:
343
362
  pass
344
363
 
345
- @classmethod
346
- @contextmanager
347
- @deprecated(
348
- "Use `Laminar.set_span_tags` or the `tags` argument of "
349
- "`Laminar.start_as_current_span` or `Laminar.start_span` instead"
350
- )
351
- def with_labels(cls, labels: list[str], context: Context | None = None):
352
- """Set labels for spans within this `with` context. This is useful for
353
- adding labels to the spans created in the auto-instrumentations.
354
-
355
- Requirements:
356
- - Labels must be created in your project in advance.
357
- - Keys must be strings from your label names.
358
- - Values must be strings matching the label's allowed values.
359
-
360
- Usage example:
361
- ```python
362
- with Laminar.with_labels({"sentiment": "positive"}):
363
- openai_client.chat.completions.create()
364
- ```
365
- """
366
- warnings.warn(
367
- "`Laminar.with_labels` is deprecated. Use `Laminar.set_span_tags` or the `tags` argument of "
368
- "`Laminar.start_as_current_span` or `Laminar.start_span` instead",
369
- DeprecationWarning,
370
- )
371
- if not cls.is_initialized():
372
- yield
373
- return
374
-
375
- with get_tracer():
376
- label_props = labels.copy()
377
- prev_labels = get_association_properties(context).get("labels", [])
378
- update_association_properties(
379
- {"labels": prev_labels + label_props},
380
- set_on_current_span=False,
381
- context=context,
382
- )
383
- yield
384
- try:
385
- set_association_properties({"labels": prev_labels})
386
- except Exception:
387
- cls.__logger.warning(
388
- f"`with_labels` Could not remove labels: {labels}. They will be "
389
- "propagated to the next span."
390
- )
391
- pass
392
-
393
364
  @classmethod
394
365
  def start_span(
395
366
  cls,
@@ -465,8 +436,8 @@ class Laminar:
465
436
  )
466
437
  )
467
438
 
468
- with get_tracer() as tracer:
469
- ctx = context or context_api.get_current()
439
+ with get_tracer_with_context() as (tracer, isolated_context):
440
+ ctx = context or isolated_context
470
441
  if parent_span_context is not None:
471
442
  span_context = LaminarSpanContext.try_to_otel_span_context(
472
443
  parent_span_context, cls.__logger
@@ -498,6 +469,7 @@ class Laminar:
498
469
  f"`start_span` Could not set tags: {tags}. Tags must be a list of strings. "
499
470
  + "Tags will be ignored."
500
471
  )
472
+
501
473
  span = tracer.start_span(
502
474
  name,
503
475
  context=ctx,
@@ -507,6 +479,7 @@ class Laminar:
507
479
  **(tag_props),
508
480
  },
509
481
  )
482
+
510
483
  if input is not None:
511
484
  serialized_input = json_dumps(input)
512
485
  if len(serialized_input) > MAX_MANUAL_SPAN_PAYLOAD_SIZE:
@@ -521,6 +494,74 @@ class Laminar:
521
494
  )
522
495
  return span
523
496
 
497
+ @classmethod
498
+ @contextmanager
499
+ def use_span(
500
+ cls,
501
+ span: Span,
502
+ end_on_exit: bool = False,
503
+ record_exception: bool = True,
504
+ set_status_on_exception: bool = True,
505
+ ) -> Iterator[Span]:
506
+ """Use a span as the current span. Useful for manual instrumentation.
507
+
508
+ Fully copies the implementation of `use_span` from opentelemetry.trace
509
+ and replaces the context API with Laminar's isolated context.
510
+
511
+ Args:
512
+ span: The span that should be activated in the current context.
513
+ end_on_exit: Whether to end the span automatically when leaving the
514
+ context manager scope.
515
+ record_exception: Whether to record any exceptions raised within the
516
+ context as error event on the span.
517
+ set_status_on_exception: Only relevant if the returned span is used
518
+ in a with/context manager. Defines whether the span status will
519
+ be automatically set to ERROR when an uncaught exception is
520
+ raised in the span with block. The span status won't be set by
521
+ this mechanism if it was previously set manually.
522
+ """
523
+ if not cls.is_initialized():
524
+ yield from use_span(
525
+ span, end_on_exit, record_exception, set_status_on_exception
526
+ )
527
+
528
+ wrapper = TracerWrapper()
529
+
530
+ try:
531
+ wrapper.push_span_context(span)
532
+ try:
533
+ yield span
534
+ finally:
535
+ wrapper.pop_span_context()
536
+
537
+ # Record only exceptions that inherit Exception class but not BaseException, because
538
+ # classes that directly inherit BaseException are not technically errors, e.g. GeneratorExit.
539
+ # See https://github.com/open-telemetry/opentelemetry-python/issues/4484
540
+ except Exception as exc: # pylint: disable=broad-exception-caught
541
+ if isinstance(span, Span) and span.is_recording():
542
+ # Record the exception as an event
543
+ if record_exception:
544
+ span.record_exception(exc)
545
+
546
+ # Set status in case exception was raised
547
+ if set_status_on_exception:
548
+ span.set_status(
549
+ Status(
550
+ status_code=StatusCode.ERROR,
551
+ description=f"{type(exc).__name__}: {exc}",
552
+ )
553
+ )
554
+
555
+ # This causes parent spans to set their status to ERROR and to record
556
+ # an exception as an event if a child span raises an exception even if
557
+ # such child span was started with both record_exception and
558
+ # set_status_on_exception attributes set to False.
559
+ raise
560
+
561
+ finally:
562
+ if end_on_exit:
563
+ span.end()
564
+
524
565
  @classmethod
525
566
  def set_span_output(cls, output: Any = None):
526
567
  """Set the output of the current span. Useful for manual
@@ -530,7 +571,7 @@ class Laminar:
530
571
  output (Any, optional): output of the span. Will be sent as an\
531
572
  attribute, so must be json serializable. Defaults to None.
532
573
  """
533
- span = trace.get_current_span()
574
+ span = trace.get_current_span(context=get_current_context())
534
575
  if output is not None and span != trace.INVALID_SPAN:
535
576
  serialized_output = json_dumps(output)
536
577
  if len(serialized_output) > MAX_MANUAL_SPAN_PAYLOAD_SIZE:
@@ -541,43 +582,10 @@ class Laminar:
541
582
  else:
542
583
  span.set_attribute(SPAN_OUTPUT, serialized_output)
543
584
 
544
- @classmethod
545
- @contextmanager
546
- def set_tracing_level(self, level: TracingLevel):
547
- """Set the tracing level for the current span and the context
548
- (i.e. any children spans created from the current span in the current
549
- thread).
550
-
551
- Tracing level can be one of:
552
- - `TracingLevel.ALL`: Enable tracing for the current span and all
553
- children spans.
554
- - `TracingLevel.META_ONLY`: Enable tracing for the current span and all
555
- children spans, but only record metadata, e.g. tokens, costs.
556
- - `TracingLevel.OFF`: Disable recording any spans.
557
-
558
- Example:
559
- ```python
560
- from lmnr import Laminar, TracingLevel
561
-
562
- with Laminar.set_tracing_level(TracingLevel.META_ONLY):
563
- openai_client.chat.completions.create()
564
- ```
565
- """
566
- if level == TracingLevel.ALL:
567
- yield
568
- else:
569
- level = "meta_only" if level == TracingLevel.META_ONLY else "off"
570
- update_association_properties({"tracing_level": level})
571
- yield
572
- try:
573
- remove_association_properties({"tracing_level": level})
574
- except Exception:
575
- pass
576
-
577
585
  @classmethod
578
586
  def set_span_attributes(
579
587
  cls,
580
- attributes: dict[Attributes, Any],
588
+ attributes: dict[Attributes | str, Any],
581
589
  ):
582
590
  """Set attributes for the current span. Useful for manual
583
591
  instrumentation.
@@ -599,24 +607,19 @@ class Laminar:
599
607
  ```
600
608
 
601
609
  Args:
602
- attributes (dict[ATTRIBUTES, Any]): attributes to set for the span
610
+ attributes (dict[Attributes | str, Any]): attributes to set for the span
603
611
  """
604
- span = trace.get_current_span()
612
+ span = trace.get_current_span(context=get_current_context())
605
613
  if span == trace.INVALID_SPAN:
606
614
  return
607
615
 
608
616
  for key, value in attributes.items():
609
- # Python 3.12+ should do: if key not in Attributes:
610
- try:
611
- Attributes(key.value)
612
- except (TypeError, AttributeError):
613
- cls.__logger.warning(
614
- f"Attribute {key} is not a valid Laminar attribute."
615
- )
616
- if not isinstance(value, (str, int, float, bool)):
617
- span.set_attribute(key.value, json_dumps(value))
617
+ if isinstance(key, Attributes):
618
+ key = key.value
619
+ if not is_otel_attribute_value_type(value):
620
+ span.set_attribute(key, json_dumps(value))
618
621
  else:
619
- span.set_attribute(key.value, value)
622
+ span.set_attribute(key, value)
620
623
 
621
624
  @classmethod
622
625
  def get_laminar_span_context(
@@ -625,7 +628,10 @@ class Laminar:
625
628
  """Get the laminar span context for a given span.
626
629
  If no span is provided, the current active span will be used.
627
630
  """
628
- span = span or trace.get_current_span()
631
+ if not cls.is_initialized():
632
+ return None
633
+
634
+ span = span or trace.get_current_span(context=get_current_context())
629
635
  if span == trace.INVALID_SPAN:
630
636
  return None
631
637
  return LaminarSpanContext(
@@ -706,7 +712,10 @@ class Laminar:
706
712
  Args:
707
713
  tags (list[str]): Tags to set for the span.
708
714
  """
709
- span = trace.get_current_span()
715
+ if not cls.is_initialized():
716
+ return
717
+
718
+ span = trace.get_current_span(context=get_current_context())
710
719
  if span == trace.INVALID_SPAN:
711
720
  cls.__logger.warning("No active span to set tags on")
712
721
  return
@@ -717,37 +726,6 @@ class Laminar:
717
726
  return
718
727
  span.set_attribute(f"{ASSOCIATION_PROPERTIES}.tags", tags)
719
728
 
720
- @classmethod
721
- @deprecated("Use `Laminar.set_trace_session_id` instead")
722
- def set_session(
723
- cls,
724
- session_id: str | None = None,
725
- ):
726
- """Set the session id for the current span and the context
727
- (i.e. any children spans created from the current span in the current
728
- thread).
729
-
730
- Args:
731
- session_id (str | None, optional): Custom session id.\
732
- Useful to debug and group long-running\
733
- sessions/conversations.
734
- Defaults to None.
735
- """
736
- warnings.warn(
737
- "`Laminar.set_session` is deprecated. Use `Laminar.set_trace_session_id` instead",
738
- DeprecationWarning,
739
- )
740
- association_properties = {}
741
- if session_id is not None:
742
- association_properties[SESSION_ID] = session_id
743
- # update_association_properties(association_properties)
744
- span = trace.get_current_span()
745
- if span == trace.INVALID_SPAN:
746
- cls.__logger.warning("No active span to set session id on")
747
- return
748
- if session_id is not None:
749
- span.set_attribute(f"{ASSOCIATION_PROPERTIES}.{SESSION_ID}", session_id)
750
-
751
729
  @classmethod
752
730
  def set_trace_session_id(cls, session_id: str | None = None):
753
731
  """Set the session id for the current trace.
@@ -756,7 +734,10 @@ class Laminar:
756
734
  Args:
757
735
  session_id (str | None, optional): Custom session id. Defaults to None.
758
736
  """
759
- span = trace.get_current_span()
737
+ if not cls.is_initialized():
738
+ return
739
+
740
+ span = trace.get_current_span(context=get_current_context())
760
741
  if span == trace.INVALID_SPAN:
761
742
  cls.__logger.warning("No active span to set session id on")
762
743
  return
@@ -771,35 +752,16 @@ class Laminar:
771
752
  Args:
772
753
  user_id (str | None, optional): Custom user id. Defaults to None.
773
754
  """
774
- span = trace.get_current_span()
755
+ if not cls.is_initialized():
756
+ return
757
+
758
+ span = trace.get_current_span(context=get_current_context())
775
759
  if span == trace.INVALID_SPAN:
776
760
  cls.__logger.warning("No active span to set user id on")
777
761
  return
778
762
  if user_id is not None:
779
763
  span.set_attribute(f"{ASSOCIATION_PROPERTIES}.{USER_ID}", user_id)
780
764
 
781
- @classmethod
782
- @deprecated("Use `Laminar.set_trace_metadata` instead")
783
- def set_metadata(cls, metadata: dict[str, str]):
784
- """Set the metadata for the current trace.
785
-
786
- Args:
787
- metadata (dict[str, str]): Metadata to set for the trace. Will be\
788
- sent as attributes, so must be json serializable.
789
- """
790
- warnings.warn(
791
- "`Laminar.set_metadata` is deprecated. Use `Laminar.set_trace_metadata` instead",
792
- DeprecationWarning,
793
- )
794
- props = {f"metadata.{k}": json_dumps(v) for k, v in metadata.items()}
795
- # update_association_properties(props)
796
- span = trace.get_current_span()
797
- if span == trace.INVALID_SPAN:
798
- cls.__logger.warning("No active span to set metadata on")
799
- return
800
- for key, value in props.items():
801
- span.set_attribute(key, value)
802
-
803
765
  @classmethod
804
766
  def set_trace_metadata(cls, metadata: dict[str, AttributeValue]):
805
767
  """Set the metadata for the current trace.
@@ -807,7 +769,10 @@ class Laminar:
807
769
  Args:
808
770
  metadata (dict[str, AttributeValue]): Metadata to set for the trace.
809
771
  """
810
- span = trace.get_current_span()
772
+ if not cls.is_initialized():
773
+ return
774
+
775
+ span = trace.get_current_span(context=get_current_context())
811
776
  if span == trace.INVALID_SPAN:
812
777
  cls.__logger.warning("No active span to set metadata on")
813
778
  return
@@ -836,7 +801,11 @@ class Laminar:
836
801
  uuid.UUID | None: The trace id for the current span, or None if\
837
802
  there is no active span.
838
803
  """
839
- trace_id = trace.get_current_span().get_span_context().trace_id
804
+ trace_id = (
805
+ trace.get_current_span(context=get_current_context())
806
+ .get_span_context()
807
+ .trace_id
808
+ )
840
809
  if trace_id == INVALID_TRACE_ID:
841
810
  return None
842
811
  return uuid.UUID(int=trace_id)
@@ -858,7 +827,10 @@ class Laminar:
858
827
  Args:
859
828
  trace_type (TraceType): Type of the trace
860
829
  """
861
- span = trace.get_current_span()
830
+ if not cls.is_initialized():
831
+ return
832
+
833
+ span = trace.get_current_span(context=get_current_context())
862
834
  if span == trace.INVALID_SPAN:
863
835
  cls.__logger.warning("No active span to set trace type on")
864
836
  return
lmnr/sdk/types.py CHANGED
@@ -141,12 +141,6 @@ class GetDatapointsResponse(pydantic.BaseModel):
141
141
  totalCount: int
142
142
 
143
143
 
144
- class TracingLevel(Enum):
145
- OFF = 0
146
- META_ONLY = 1
147
- ALL = 2
148
-
149
-
150
144
  class LaminarSpanContext(pydantic.BaseModel):
151
145
  """
152
146
  A span context that can be used to continue a trace across services. This
lmnr/version.py CHANGED
@@ -3,7 +3,7 @@ import httpx
3
3
  from packaging import version
4
4
 
5
5
 
6
- __version__ = "0.6.20"
6
+ __version__ = "0.7.0"
7
7
  PYTHON_VERSION = f"{sys.version_info.major}.{sys.version_info.minor}"
8
8
 
9
9