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.
- lmnr/__init__.py +0 -4
- lmnr/opentelemetry_lib/decorators/__init__.py +81 -32
- lmnr/opentelemetry_lib/litellm/__init__.py +5 -2
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/__init__.py +6 -2
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/__init__.py +11 -2
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/__init__.py +3 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/langgraph/__init__.py +16 -16
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/__init__.py +6 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/chat_wrappers.py +141 -9
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/completion_wrappers.py +10 -2
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/embeddings_wrappers.py +6 -2
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/assistant_wrappers.py +8 -2
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/event_handler_wrapper.py +4 -1
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/responses_wrappers.py +20 -4
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/threading/__init__.py +190 -0
- lmnr/opentelemetry_lib/tracing/__init__.py +89 -1
- lmnr/opentelemetry_lib/tracing/context.py +126 -0
- lmnr/opentelemetry_lib/tracing/processor.py +5 -6
- lmnr/opentelemetry_lib/tracing/tracer.py +29 -0
- lmnr/sdk/browser/browser_use_otel.py +5 -5
- lmnr/sdk/browser/patchright_otel.py +14 -0
- lmnr/sdk/browser/playwright_otel.py +32 -6
- lmnr/sdk/browser/pw_utils.py +119 -112
- lmnr/sdk/browser/recorder/record.umd.min.cjs +84 -0
- lmnr/sdk/client/asynchronous/resources/browser_events.py +1 -0
- lmnr/sdk/laminar.py +156 -186
- lmnr/sdk/types.py +17 -11
- lmnr/version.py +1 -1
- {lmnr-0.6.21.dist-info → lmnr-0.7.1.dist-info}/METADATA +3 -2
- {lmnr-0.6.21.dist-info → lmnr-0.7.1.dist-info}/RECORD +32 -31
- {lmnr-0.6.21.dist-info → lmnr-0.7.1.dist-info}/WHEEL +1 -1
- lmnr/opentelemetry_lib/tracing/context_properties.py +0 -65
- lmnr/sdk/browser/rrweb/rrweb.umd.min.cjs +0 -98
- {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
|
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
|
17
|
-
from opentelemetry
|
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
|
-
|
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.
|
211
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
239
|
-
|
240
|
-
|
241
|
-
|
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(
|
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
|
-
|
307
|
-
|
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
|
-
|
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
|
-
|
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
|
491
|
-
ctx = context or
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 =
|
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
|
-
|
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))[
|
79
|
-
|
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))[
|
110
|
-
|
111
|
-
|
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
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: lmnr
|
3
|
-
Version: 0.
|
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'
|