lmnr 0.6.21__py3-none-any.whl → 0.7.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 (34) hide show
  1. lmnr/__init__.py +0 -4
  2. lmnr/opentelemetry_lib/decorators/__init__.py +81 -32
  3. lmnr/opentelemetry_lib/litellm/__init__.py +5 -2
  4. lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/__init__.py +6 -2
  5. lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/__init__.py +11 -2
  6. lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/__init__.py +3 -0
  7. lmnr/opentelemetry_lib/opentelemetry/instrumentation/langgraph/__init__.py +16 -16
  8. lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/__init__.py +6 -0
  9. lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/chat_wrappers.py +141 -9
  10. lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/completion_wrappers.py +10 -2
  11. lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/embeddings_wrappers.py +6 -2
  12. lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/assistant_wrappers.py +8 -2
  13. lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/event_handler_wrapper.py +4 -1
  14. lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/responses_wrappers.py +20 -4
  15. lmnr/opentelemetry_lib/opentelemetry/instrumentation/threading/__init__.py +190 -0
  16. lmnr/opentelemetry_lib/tracing/__init__.py +89 -1
  17. lmnr/opentelemetry_lib/tracing/context.py +126 -0
  18. lmnr/opentelemetry_lib/tracing/processor.py +5 -6
  19. lmnr/opentelemetry_lib/tracing/tracer.py +29 -0
  20. lmnr/sdk/browser/browser_use_otel.py +5 -5
  21. lmnr/sdk/browser/patchright_otel.py +14 -0
  22. lmnr/sdk/browser/playwright_otel.py +32 -6
  23. lmnr/sdk/browser/pw_utils.py +119 -112
  24. lmnr/sdk/browser/recorder/record.umd.min.cjs +84 -0
  25. lmnr/sdk/client/asynchronous/resources/browser_events.py +1 -0
  26. lmnr/sdk/laminar.py +156 -186
  27. lmnr/sdk/types.py +17 -11
  28. lmnr/version.py +1 -1
  29. {lmnr-0.6.21.dist-info → lmnr-0.7.1.dist-info}/METADATA +3 -2
  30. {lmnr-0.6.21.dist-info → lmnr-0.7.1.dist-info}/RECORD +32 -31
  31. {lmnr-0.6.21.dist-info → lmnr-0.7.1.dist-info}/WHEEL +1 -1
  32. lmnr/opentelemetry_lib/tracing/context_properties.py +0 -65
  33. lmnr/sdk/browser/rrweb/rrweb.umd.min.cjs +0 -98
  34. {lmnr-0.6.21.dist-info → lmnr-0.7.1.dist-info}/entry_points.txt +0 -0
lmnr/sdk/laminar.py CHANGED
@@ -1,10 +1,16 @@
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
+ from lmnr.opentelemetry_lib.tracing.context import (
7
+ CONTEXT_SESSION_ID_KEY,
8
+ CONTEXT_USER_ID_KEY,
9
+ attach_context,
10
+ get_event_attributes_from_context,
11
+ )
6
12
  from lmnr.opentelemetry_lib.tracing.instruments import Instruments
7
- from lmnr.opentelemetry_lib.tracing.tracer import get_tracer
13
+ from lmnr.opentelemetry_lib.tracing.tracer import get_tracer_with_context
8
14
  from lmnr.opentelemetry_lib.tracing.attributes import (
9
15
  ASSOCIATION_PROPERTIES,
10
16
  USER_ID,
@@ -13,13 +19,13 @@ from lmnr.opentelemetry_lib.tracing.attributes import (
13
19
  )
14
20
  from lmnr.opentelemetry_lib import MAX_MANUAL_SPAN_PAYLOAD_SIZE
15
21
  from lmnr.opentelemetry_lib.decorators import json_dumps
16
- from opentelemetry import context as context_api, trace
17
- from opentelemetry.context import attach, detach
18
- from opentelemetry.trace import INVALID_TRACE_ID
22
+ from opentelemetry import trace
23
+ from opentelemetry import context as context_api
24
+ from opentelemetry.trace import INVALID_TRACE_ID, Span, Status, StatusCode, use_span
19
25
  from opentelemetry.sdk.trace.id_generator import RandomIdGenerator
20
26
  from opentelemetry.util.types import AttributeValue
21
27
 
22
- from typing import Any, Literal
28
+ from typing import Any, Iterator, Literal
23
29
 
24
30
  import datetime
25
31
  import logging
@@ -33,12 +39,6 @@ from lmnr.opentelemetry_lib.tracing.attributes import (
33
39
  SPAN_OUTPUT,
34
40
  TRACE_TYPE,
35
41
  )
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
42
  from lmnr.sdk.utils import from_env, is_otel_attribute_value_type
43
43
 
44
44
  from .log import VerboseColorfulFormatter
@@ -46,7 +46,6 @@ from .log import VerboseColorfulFormatter
46
46
  from .types import (
47
47
  LaminarSpanContext,
48
48
  TraceType,
49
- TracingLevel,
50
49
  )
51
50
 
52
51
 
@@ -147,7 +146,6 @@ class Laminar:
147
146
  if match := re.search(r":(\d{1,5})$", url):
148
147
  url = url[: -len(match.group(0))]
149
148
  cls.__logger.info(f"Ignoring port in base URL: {match.group(1)}")
150
-
151
149
  http_url = base_http_url or url
152
150
  if not http_url.startswith("http:") and not http_url.startswith("https:"):
153
151
  http_url = f"https://{http_url}"
@@ -172,9 +170,9 @@ class Laminar:
172
170
  http_port=http_port or 443,
173
171
  port=grpc_port or 8443,
174
172
  project_api_key=cls.__project_api_key,
175
- instruments=set(instruments) if instruments else None,
173
+ instruments=set(instruments) if instruments is not None else None,
176
174
  block_instruments=(
177
- set(disabled_instruments) if disabled_instruments else None
175
+ set(disabled_instruments) if disabled_instruments is not None else None
178
176
  ),
179
177
  disable_batch=disable_batch,
180
178
  max_export_batch_size=max_export_batch_size,
@@ -204,18 +202,18 @@ class Laminar:
204
202
  def event(
205
203
  cls,
206
204
  name: str,
207
- value: AttributeValue | None = None,
205
+ attributes: dict[str, AttributeValue] | None = None,
208
206
  timestamp: datetime.datetime | int | None = None,
207
+ *,
208
+ user_id: str | None = None,
209
+ session_id: str | None = None,
209
210
  ):
210
- """Associate an event with the current span. If using manual\
211
- instrumentation, use raw OpenTelemetry `span.add_event()` instead.\
212
- `value` will be saved as a `lmnr.event.value` attribute.
211
+ """Associate an event with the current span. This is a wrapper around
212
+ `span.add_event()` that adds the event to the current span.
213
213
 
214
214
  Args:
215
215
  name (str): event name
216
- value (AttributeValue | None, optional): event value. Must be a\
217
- primitive type. Boolean `True` is assumed in the backend if\
218
- `value` is None.
216
+ attributes (dict[str, AttributeValue] | None, optional): event attributes.
219
217
  Defaults to None.
220
218
  timestamp (datetime.datetime | int | None, optional): If int, must\
221
219
  be epoch nanoseconds. If not specified, relies on the underlying\
@@ -227,22 +225,26 @@ class Laminar:
227
225
  if timestamp and isinstance(timestamp, datetime.datetime):
228
226
  timestamp = int(timestamp.timestamp() * 1e9)
229
227
 
230
- event = {
231
- "lmnr.event.type": "default",
232
- }
233
- if value is not None:
234
- event["lmnr.event.value"] = value
228
+ extra_attributes = get_event_attributes_from_context()
235
229
 
236
- current_span = trace.get_current_span()
230
+ # override the user_id and session_id from the context with the ones
231
+ # passed as arguments
232
+ if user_id is not None:
233
+ extra_attributes["lmnr.event.user_id"] = user_id
234
+ if session_id is not None:
235
+ extra_attributes["lmnr.event.session_id"] = session_id
236
+
237
+ current_span = trace.get_current_span(context=get_current_context())
237
238
  if current_span == trace.INVALID_SPAN:
238
- cls.__logger.warning(
239
- "`Laminar().event()` called outside of span context. "
240
- f"Event '{name}' will not be recorded in the trace. "
241
- "Make sure to annotate the function with a decorator"
242
- )
239
+ with cls.start_as_current_span(name) as span:
240
+ span.add_event(
241
+ name, {**(attributes or {}), **extra_attributes}, timestamp
242
+ )
243
243
  return
244
244
 
245
- current_span.add_event(name, event, timestamp)
245
+ current_span.add_event(
246
+ name, {**(attributes or {}), **extra_attributes}, timestamp
247
+ )
246
248
 
247
249
  @classmethod
248
250
  @contextmanager
@@ -255,7 +257,7 @@ class Laminar:
255
257
  labels: list[str] | None = None,
256
258
  parent_span_context: LaminarSpanContext | None = None,
257
259
  tags: list[str] | None = None,
258
- ):
260
+ ) -> Iterator[Span]:
259
261
  """Start a new span as the current span. Useful for manual
260
262
  instrumentation. If `span_type` is set to `"LLM"`, you should report
261
263
  usage and response attributes manually. See `Laminar.set_span_attributes`
@@ -303,8 +305,10 @@ class Laminar:
303
305
  )
304
306
  return
305
307
 
306
- with get_tracer() as tracer:
307
- ctx = context or context_api.get_current()
308
+ wrapper = TracerWrapper()
309
+
310
+ with get_tracer_with_context() as (tracer, isolated_context):
311
+ ctx = context or isolated_context
308
312
  if parent_span_context is not None:
309
313
  span_context = LaminarSpanContext.try_to_otel_span_context(
310
314
  parent_span_context, cls.__logger
@@ -312,7 +316,7 @@ class Laminar:
312
316
  ctx = trace.set_span_in_context(
313
317
  trace.NonRecordingSpan(span_context), ctx
314
318
  )
315
- ctx_token = attach(ctx)
319
+ ctx_token = context_api.attach(ctx)
316
320
  label_props = {}
317
321
  try:
318
322
  if labels:
@@ -335,6 +339,7 @@ class Laminar:
335
339
  f"`start_as_current_span` Could not set tags: {tags}. Tags must be a list of strings. "
336
340
  "Tags will be ignored."
337
341
  )
342
+
338
343
  with tracer.start_as_current_span(
339
344
  name,
340
345
  context=ctx,
@@ -344,6 +349,7 @@ class Laminar:
344
349
  **(tag_props),
345
350
  },
346
351
  ) as span:
352
+ wrapper.push_span_context(span)
347
353
  if input is not None:
348
354
  serialized_input = json_dumps(input)
349
355
  if len(serialized_input) > MAX_MANUAL_SPAN_PAYLOAD_SIZE:
@@ -358,58 +364,10 @@ class Laminar:
358
364
  )
359
365
  yield span
360
366
 
361
- # TODO: Figure out if this is necessary
362
- try:
363
- detach(ctx_token)
364
- except Exception:
365
- pass
366
-
367
- @classmethod
368
- @contextmanager
369
- @deprecated(
370
- "Use `Laminar.set_span_tags` or the `tags` argument of "
371
- "`Laminar.start_as_current_span` or `Laminar.start_span` instead"
372
- )
373
- def with_labels(cls, labels: list[str], context: Context | None = None):
374
- """Set labels for spans within this `with` context. This is useful for
375
- adding labels to the spans created in the auto-instrumentations.
376
-
377
- Requirements:
378
- - Labels must be created in your project in advance.
379
- - Keys must be strings from your label names.
380
- - Values must be strings matching the label's allowed values.
381
-
382
- Usage example:
383
- ```python
384
- with Laminar.with_labels({"sentiment": "positive"}):
385
- openai_client.chat.completions.create()
386
- ```
387
- """
388
- warnings.warn(
389
- "`Laminar.with_labels` is deprecated. Use `Laminar.set_span_tags` or the `tags` argument of "
390
- "`Laminar.start_as_current_span` or `Laminar.start_span` instead",
391
- DeprecationWarning,
392
- )
393
- if not cls.is_initialized():
394
- yield
395
- return
396
-
397
- with get_tracer():
398
- label_props = labels.copy()
399
- prev_labels = get_association_properties(context).get("labels", [])
400
- update_association_properties(
401
- {"labels": prev_labels + label_props},
402
- set_on_current_span=False,
403
- context=context,
404
- )
405
- yield
367
+ wrapper.pop_span_context()
406
368
  try:
407
- set_association_properties({"labels": prev_labels})
369
+ context_api.detach(ctx_token)
408
370
  except Exception:
409
- cls.__logger.warning(
410
- f"`with_labels` Could not remove labels: {labels}. They will be "
411
- "propagated to the next span."
412
- )
413
371
  pass
414
372
 
415
373
  @classmethod
@@ -487,8 +445,8 @@ class Laminar:
487
445
  )
488
446
  )
489
447
 
490
- with get_tracer() as tracer:
491
- ctx = context or context_api.get_current()
448
+ with get_tracer_with_context() as (tracer, isolated_context):
449
+ ctx = context or isolated_context
492
450
  if parent_span_context is not None:
493
451
  span_context = LaminarSpanContext.try_to_otel_span_context(
494
452
  parent_span_context, cls.__logger
@@ -520,6 +478,7 @@ class Laminar:
520
478
  f"`start_span` Could not set tags: {tags}. Tags must be a list of strings. "
521
479
  + "Tags will be ignored."
522
480
  )
481
+
523
482
  span = tracer.start_span(
524
483
  name,
525
484
  context=ctx,
@@ -529,6 +488,7 @@ class Laminar:
529
488
  **(tag_props),
530
489
  },
531
490
  )
491
+
532
492
  if input is not None:
533
493
  serialized_input = json_dumps(input)
534
494
  if len(serialized_input) > MAX_MANUAL_SPAN_PAYLOAD_SIZE:
@@ -543,6 +503,82 @@ class Laminar:
543
503
  )
544
504
  return span
545
505
 
506
+ @classmethod
507
+ @contextmanager
508
+ def use_span(
509
+ cls,
510
+ span: Span,
511
+ end_on_exit: bool = False,
512
+ record_exception: bool = True,
513
+ set_status_on_exception: bool = True,
514
+ ) -> Iterator[Span]:
515
+ """Use a span as the current span. Useful for manual instrumentation.
516
+
517
+ Fully copies the implementation of `use_span` from opentelemetry.trace
518
+ and replaces the context API with Laminar's isolated context.
519
+
520
+ Args:
521
+ span: The span that should be activated in the current context.
522
+ end_on_exit: Whether to end the span automatically when leaving the
523
+ context manager scope.
524
+ record_exception: Whether to record any exceptions raised within the
525
+ context as error event on the span.
526
+ set_status_on_exception: Only relevant if the returned span is used
527
+ in a with/context manager. Defines whether the span status will
528
+ be automatically set to ERROR when an uncaught exception is
529
+ raised in the span with block. The span status won't be set by
530
+ this mechanism if it was previously set manually.
531
+ """
532
+ if not cls.is_initialized():
533
+ yield from use_span(
534
+ span, end_on_exit, record_exception, set_status_on_exception
535
+ )
536
+
537
+ wrapper = TracerWrapper()
538
+
539
+ try:
540
+ context = wrapper.push_span_context(span)
541
+ # Some auto-instrumentations are not under our control, so they
542
+ # don't have access to our isolated context. We attach the context
543
+ # to the OTEL global context, so that spans know their parent
544
+ # span and trace_id.
545
+ context_token = context_api.attach(context)
546
+ try:
547
+ yield span
548
+ finally:
549
+ context_api.detach(context_token)
550
+ wrapper.pop_span_context()
551
+
552
+ # Record only exceptions that inherit Exception class but not BaseException, because
553
+ # classes that directly inherit BaseException are not technically errors, e.g. GeneratorExit.
554
+ # See https://github.com/open-telemetry/opentelemetry-python/issues/4484
555
+ except Exception as exc: # pylint: disable=broad-exception-caught
556
+ if isinstance(span, Span) and span.is_recording():
557
+ # Record the exception as an event
558
+ if record_exception:
559
+ span.record_exception(
560
+ exc, attributes=get_event_attributes_from_context()
561
+ )
562
+
563
+ # Set status in case exception was raised
564
+ if set_status_on_exception:
565
+ span.set_status(
566
+ Status(
567
+ status_code=StatusCode.ERROR,
568
+ description=f"{type(exc).__name__}: {exc}",
569
+ )
570
+ )
571
+
572
+ # This causes parent spans to set their status to ERROR and to record
573
+ # an exception as an event if a child span raises an exception even if
574
+ # such child span was started with both record_exception and
575
+ # set_status_on_exception attributes set to False.
576
+ raise
577
+
578
+ finally:
579
+ if end_on_exit:
580
+ span.end()
581
+
546
582
  @classmethod
547
583
  def set_span_output(cls, output: Any = None):
548
584
  """Set the output of the current span. Useful for manual
@@ -552,7 +588,7 @@ class Laminar:
552
588
  output (Any, optional): output of the span. Will be sent as an\
553
589
  attribute, so must be json serializable. Defaults to None.
554
590
  """
555
- span = trace.get_current_span()
591
+ span = trace.get_current_span(context=get_current_context())
556
592
  if output is not None and span != trace.INVALID_SPAN:
557
593
  serialized_output = json_dumps(output)
558
594
  if len(serialized_output) > MAX_MANUAL_SPAN_PAYLOAD_SIZE:
@@ -563,39 +599,6 @@ class Laminar:
563
599
  else:
564
600
  span.set_attribute(SPAN_OUTPUT, serialized_output)
565
601
 
566
- @classmethod
567
- @contextmanager
568
- def set_tracing_level(self, level: TracingLevel):
569
- """Set the tracing level for the current span and the context
570
- (i.e. any children spans created from the current span in the current
571
- thread).
572
-
573
- Tracing level can be one of:
574
- - `TracingLevel.ALL`: Enable tracing for the current span and all
575
- children spans.
576
- - `TracingLevel.META_ONLY`: Enable tracing for the current span and all
577
- children spans, but only record metadata, e.g. tokens, costs.
578
- - `TracingLevel.OFF`: Disable recording any spans.
579
-
580
- Example:
581
- ```python
582
- from lmnr import Laminar, TracingLevel
583
-
584
- with Laminar.set_tracing_level(TracingLevel.META_ONLY):
585
- openai_client.chat.completions.create()
586
- ```
587
- """
588
- if level == TracingLevel.ALL:
589
- yield
590
- else:
591
- level = "meta_only" if level == TracingLevel.META_ONLY else "off"
592
- update_association_properties({"tracing_level": level})
593
- yield
594
- try:
595
- remove_association_properties({"tracing_level": level})
596
- except Exception:
597
- pass
598
-
599
602
  @classmethod
600
603
  def set_span_attributes(
601
604
  cls,
@@ -623,7 +626,7 @@ class Laminar:
623
626
  Args:
624
627
  attributes (dict[Attributes | str, Any]): attributes to set for the span
625
628
  """
626
- span = trace.get_current_span()
629
+ span = trace.get_current_span(context=get_current_context())
627
630
  if span == trace.INVALID_SPAN:
628
631
  return
629
632
 
@@ -642,7 +645,10 @@ class Laminar:
642
645
  """Get the laminar span context for a given span.
643
646
  If no span is provided, the current active span will be used.
644
647
  """
645
- span = span or trace.get_current_span()
648
+ if not cls.is_initialized():
649
+ return None
650
+
651
+ span = span or trace.get_current_span(context=get_current_context())
646
652
  if span == trace.INVALID_SPAN:
647
653
  return None
648
654
  return LaminarSpanContext(
@@ -725,7 +731,8 @@ class Laminar:
725
731
  """
726
732
  if not cls.is_initialized():
727
733
  return
728
- span = trace.get_current_span()
734
+
735
+ span = trace.get_current_span(context=get_current_context())
729
736
  if span == trace.INVALID_SPAN:
730
737
  cls.__logger.warning("No active span to set tags on")
731
738
  return
@@ -736,37 +743,6 @@ class Laminar:
736
743
  return
737
744
  span.set_attribute(f"{ASSOCIATION_PROPERTIES}.tags", tags)
738
745
 
739
- @classmethod
740
- @deprecated("Use `Laminar.set_trace_session_id` instead")
741
- def set_session(
742
- cls,
743
- session_id: str | None = None,
744
- ):
745
- """Set the session id for the current span and the context
746
- (i.e. any children spans created from the current span in the current
747
- thread).
748
-
749
- Args:
750
- session_id (str | None, optional): Custom session id.\
751
- Useful to debug and group long-running\
752
- sessions/conversations.
753
- Defaults to None.
754
- """
755
- warnings.warn(
756
- "`Laminar.set_session` is deprecated. Use `Laminar.set_trace_session_id` instead",
757
- DeprecationWarning,
758
- )
759
- association_properties = {}
760
- if session_id is not None:
761
- association_properties[SESSION_ID] = session_id
762
- # update_association_properties(association_properties)
763
- span = trace.get_current_span()
764
- if span == trace.INVALID_SPAN:
765
- cls.__logger.warning("No active span to set session id on")
766
- return
767
- if session_id is not None:
768
- span.set_attribute(f"{ASSOCIATION_PROPERTIES}.{SESSION_ID}", session_id)
769
-
770
746
  @classmethod
771
747
  def set_trace_session_id(cls, session_id: str | None = None):
772
748
  """Set the session id for the current trace.
@@ -777,7 +753,12 @@ class Laminar:
777
753
  """
778
754
  if not cls.is_initialized():
779
755
  return
780
- span = trace.get_current_span()
756
+
757
+ context = get_current_context()
758
+ context = context_api.set_value(CONTEXT_SESSION_ID_KEY, session_id, context)
759
+ attach_context(context)
760
+
761
+ span = trace.get_current_span(context=context)
781
762
  if span == trace.INVALID_SPAN:
782
763
  cls.__logger.warning("No active span to set session id on")
783
764
  return
@@ -794,35 +775,18 @@ class Laminar:
794
775
  """
795
776
  if not cls.is_initialized():
796
777
  return
797
- span = trace.get_current_span()
778
+
779
+ context = get_current_context()
780
+ context = context_api.set_value(CONTEXT_USER_ID_KEY, user_id, context)
781
+ attach_context(context)
782
+
783
+ span = trace.get_current_span(context=context)
798
784
  if span == trace.INVALID_SPAN:
799
785
  cls.__logger.warning("No active span to set user id on")
800
786
  return
801
787
  if user_id is not None:
802
788
  span.set_attribute(f"{ASSOCIATION_PROPERTIES}.{USER_ID}", user_id)
803
789
 
804
- @classmethod
805
- @deprecated("Use `Laminar.set_trace_metadata` instead")
806
- def set_metadata(cls, metadata: dict[str, str]):
807
- """Set the metadata for the current trace.
808
-
809
- Args:
810
- metadata (dict[str, str]): Metadata to set for the trace. Will be\
811
- sent as attributes, so must be json serializable.
812
- """
813
- warnings.warn(
814
- "`Laminar.set_metadata` is deprecated. Use `Laminar.set_trace_metadata` instead",
815
- DeprecationWarning,
816
- )
817
- props = {f"metadata.{k}": json_dumps(v) for k, v in metadata.items()}
818
- # update_association_properties(props)
819
- span = trace.get_current_span()
820
- if span == trace.INVALID_SPAN:
821
- cls.__logger.warning("No active span to set metadata on")
822
- return
823
- for key, value in props.items():
824
- span.set_attribute(key, value)
825
-
826
790
  @classmethod
827
791
  def set_trace_metadata(cls, metadata: dict[str, AttributeValue]):
828
792
  """Set the metadata for the current trace.
@@ -832,7 +796,8 @@ class Laminar:
832
796
  """
833
797
  if not cls.is_initialized():
834
798
  return
835
- span = trace.get_current_span()
799
+
800
+ span = trace.get_current_span(context=get_current_context())
836
801
  if span == trace.INVALID_SPAN:
837
802
  cls.__logger.warning("No active span to set metadata on")
838
803
  return
@@ -861,7 +826,11 @@ class Laminar:
861
826
  uuid.UUID | None: The trace id for the current span, or None if\
862
827
  there is no active span.
863
828
  """
864
- trace_id = trace.get_current_span().get_span_context().trace_id
829
+ trace_id = (
830
+ trace.get_current_span(context=get_current_context())
831
+ .get_span_context()
832
+ .trace_id
833
+ )
865
834
  if trace_id == INVALID_TRACE_ID:
866
835
  return None
867
836
  return uuid.UUID(int=trace_id)
@@ -885,7 +854,8 @@ class Laminar:
885
854
  """
886
855
  if not cls.is_initialized():
887
856
  return
888
- span = trace.get_current_span()
857
+
858
+ span = trace.get_current_span(context=get_current_context())
889
859
  if span == trace.INVALID_SPAN:
890
860
  cls.__logger.warning("No active span to set trace type on")
891
861
  return
lmnr/sdk/types.py CHANGED
@@ -13,6 +13,8 @@ from typing import Any, Awaitable, Callable, Literal, Optional
13
13
 
14
14
  from .utils import serialize
15
15
 
16
+ EVALUATION_DATAPOINT_MAX_DATA_LENGTH = 8_000_000 # 8MB
17
+
16
18
 
17
19
  Numeric = int | float
18
20
  NumericTypes = (int, float) # for use with isinstance
@@ -75,8 +77,12 @@ class PartialEvaluationDatapoint(pydantic.BaseModel):
75
77
  try:
76
78
  return {
77
79
  "id": str(self.id),
78
- "data": str(serialize(self.data))[:100],
79
- "target": str(serialize(self.target))[:100],
80
+ "data": str(serialize(self.data))[
81
+ :EVALUATION_DATAPOINT_MAX_DATA_LENGTH
82
+ ],
83
+ "target": str(serialize(self.target))[
84
+ :EVALUATION_DATAPOINT_MAX_DATA_LENGTH
85
+ ],
80
86
  "index": self.index,
81
87
  "traceId": str(self.trace_id),
82
88
  "executorSpanId": str(self.executor_span_id),
@@ -106,9 +112,15 @@ class EvaluationResultDatapoint(pydantic.BaseModel):
106
112
  # preserve only preview of the data, target and executor output
107
113
  # (full data is in trace)
108
114
  "id": str(self.id),
109
- "data": str(serialize(self.data))[:100],
110
- "target": str(serialize(self.target))[:100],
111
- "executorOutput": str(serialize(self.executor_output))[:100],
115
+ "data": str(serialize(self.data))[
116
+ :EVALUATION_DATAPOINT_MAX_DATA_LENGTH
117
+ ],
118
+ "target": str(serialize(self.target))[
119
+ :EVALUATION_DATAPOINT_MAX_DATA_LENGTH
120
+ ],
121
+ "executorOutput": str(serialize(self.executor_output))[
122
+ :EVALUATION_DATAPOINT_MAX_DATA_LENGTH
123
+ ],
112
124
  "scores": self.scores,
113
125
  "traceId": str(self.trace_id),
114
126
  "executorSpanId": str(self.executor_span_id),
@@ -141,12 +153,6 @@ class GetDatapointsResponse(pydantic.BaseModel):
141
153
  totalCount: int
142
154
 
143
155
 
144
- class TracingLevel(Enum):
145
- OFF = 0
146
- META_ONLY = 1
147
- ALL = 2
148
-
149
-
150
156
  class LaminarSpanContext(pydantic.BaseModel):
151
157
  """
152
158
  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.21"
6
+ __version__ = "0.7.1"
7
7
  PYTHON_VERSION = f"{sys.version_info.major}.{sys.version_info.minor}"
8
8
 
9
9
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lmnr
3
- Version: 0.6.21
3
+ Version: 0.7.1
4
4
  Summary: Python SDK for Laminar
5
5
  Author: lmnr.ai
6
6
  Author-email: lmnr.ai <founders@lmnr.ai>
@@ -17,14 +17,15 @@ Requires-Dist: opentelemetry-api>=1.33.0
17
17
  Requires-Dist: opentelemetry-sdk>=1.33.0
18
18
  Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.33.0
19
19
  Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.33.0
20
+ Requires-Dist: opentelemetry-instrumentation>=0.54b0
20
21
  Requires-Dist: opentelemetry-semantic-conventions>=0.54b0
21
22
  Requires-Dist: opentelemetry-semantic-conventions-ai>=0.4.9
22
23
  Requires-Dist: tqdm>=4.0
23
24
  Requires-Dist: tenacity>=8.0
24
25
  Requires-Dist: grpcio>=1
25
26
  Requires-Dist: httpx>=0.25.0
26
- Requires-Dist: opentelemetry-instrumentation-threading>=0.54b0
27
27
  Requires-Dist: orjson>=3.10.18
28
+ Requires-Dist: packaging>=22.0
28
29
  Requires-Dist: opentelemetry-instrumentation-alephalpha>=0.40.12 ; extra == 'alephalpha'
29
30
  Requires-Dist: opentelemetry-instrumentation-alephalpha>=0.40.12 ; extra == 'all'
30
31
  Requires-Dist: opentelemetry-instrumentation-bedrock>=0.40.12 ; extra == 'all'