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.
- lmnr/__init__.py +0 -4
- lmnr/opentelemetry_lib/decorators/__init__.py +211 -151
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/__init__.py +678 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/config.py +13 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/event_emitter.py +211 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/event_models.py +41 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/span_utils.py +256 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/streaming.py +295 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/utils.py +179 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/version.py +1 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/__init__.py +4 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/__init__.py +488 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/config.py +8 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/event_emitter.py +143 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/event_models.py +41 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/span_utils.py +229 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/utils.py +92 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/version.py +1 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/langgraph/__init__.py +16 -16
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/chat_wrappers.py +3 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/completion_wrappers.py +3 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/utils.py +3 -3
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/assistant_wrappers.py +3 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/responses_wrappers.py +7 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/threading/__init__.py +190 -0
- lmnr/opentelemetry_lib/tracing/__init__.py +90 -2
- lmnr/opentelemetry_lib/tracing/_instrument_initializers.py +12 -7
- lmnr/opentelemetry_lib/tracing/context.py +109 -0
- lmnr/opentelemetry_lib/tracing/processor.py +6 -7
- lmnr/opentelemetry_lib/tracing/tracer.py +29 -0
- lmnr/opentelemetry_lib/utils/package_check.py +9 -0
- lmnr/sdk/browser/browser_use_otel.py +9 -7
- lmnr/sdk/browser/patchright_otel.py +14 -26
- lmnr/sdk/browser/playwright_otel.py +72 -73
- lmnr/sdk/browser/pw_utils.py +436 -119
- lmnr/sdk/client/asynchronous/resources/browser_events.py +1 -0
- lmnr/sdk/decorators.py +39 -4
- lmnr/sdk/evaluations.py +23 -9
- lmnr/sdk/laminar.py +181 -209
- lmnr/sdk/types.py +0 -6
- lmnr/version.py +1 -1
- {lmnr-0.6.20.dist-info → lmnr-0.7.0.dist-info}/METADATA +10 -8
- {lmnr-0.6.20.dist-info → lmnr-0.7.0.dist-info}/RECORD +45 -29
- {lmnr-0.6.20.dist-info → lmnr-0.7.0.dist-info}/WHEEL +1 -1
- lmnr/opentelemetry_lib/tracing/context_properties.py +0 -65
- {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
|
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
|
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:
|
66
|
-
|
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
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
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
|
-
|
100
|
-
|
101
|
-
|
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
|
-
|
104
|
-
|
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
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
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"{
|
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=
|
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
|
-
|
285
|
-
|
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
|
469
|
-
ctx = context or
|
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[
|
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
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 =
|
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
|
-
|
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
|