lmnr 0.6.7__py3-none-any.whl → 0.6.9__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/cli.py +50 -24
- lmnr/opentelemetry_lib/decorators/__init__.py +12 -15
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/__init__.py +90 -15
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/utils.py +81 -58
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/langgraph/__init__.py +119 -0
- lmnr/opentelemetry_lib/opentelemetry/instrumentation/langgraph/utils.py +60 -0
- lmnr/opentelemetry_lib/tracing/_instrument_initializers.py +12 -0
- lmnr/opentelemetry_lib/tracing/context_properties.py +14 -1
- lmnr/opentelemetry_lib/tracing/instruments.py +2 -0
- lmnr/opentelemetry_lib/tracing/processor.py +1 -1
- lmnr/sdk/decorators.py +12 -0
- lmnr/sdk/evaluations.py +4 -3
- lmnr/sdk/laminar.py +145 -28
- lmnr/sdk/types.py +2 -2
- lmnr/sdk/utils.py +15 -0
- lmnr/version.py +1 -1
- {lmnr-0.6.7.dist-info → lmnr-0.6.9.dist-info}/METADATA +55 -55
- {lmnr-0.6.7.dist-info → lmnr-0.6.9.dist-info}/RECORD +21 -19
- {lmnr-0.6.7.dist-info → lmnr-0.6.9.dist-info}/LICENSE +0 -0
- {lmnr-0.6.7.dist-info → lmnr-0.6.9.dist-info}/WHEEL +0 -0
- {lmnr-0.6.7.dist-info → lmnr-0.6.9.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,119 @@
|
|
1
|
+
"""OpenTelemetry Langgraph instrumentation"""
|
2
|
+
|
3
|
+
import json
|
4
|
+
import logging
|
5
|
+
from typing import Collection
|
6
|
+
|
7
|
+
from .utils import (
|
8
|
+
with_tracer_wrapper,
|
9
|
+
)
|
10
|
+
|
11
|
+
from langchain_core.runnables.graph import Graph
|
12
|
+
from opentelemetry.trace import Tracer
|
13
|
+
from wrapt import wrap_function_wrapper
|
14
|
+
from opentelemetry.trace import get_tracer, get_current_span
|
15
|
+
|
16
|
+
from lmnr.opentelemetry_lib.tracing.context_properties import (
|
17
|
+
update_association_properties,
|
18
|
+
)
|
19
|
+
|
20
|
+
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
21
|
+
from opentelemetry.instrumentation.utils import unwrap
|
22
|
+
|
23
|
+
|
24
|
+
logger = logging.getLogger(__name__)
|
25
|
+
|
26
|
+
_instruments = ("langgraph >= 0.1.0",)
|
27
|
+
|
28
|
+
|
29
|
+
@with_tracer_wrapper
|
30
|
+
def wrap_pregel_stream(tracer: Tracer, to_wrap, wrapped, instance, args, kwargs):
|
31
|
+
graph: Graph = instance.get_graph()
|
32
|
+
nodes = [
|
33
|
+
{
|
34
|
+
"id": node.id,
|
35
|
+
"name": node.name,
|
36
|
+
"metadata": node.metadata,
|
37
|
+
}
|
38
|
+
for node in graph.nodes.values()
|
39
|
+
]
|
40
|
+
edges = [
|
41
|
+
{
|
42
|
+
"source": edge.source,
|
43
|
+
"target": edge.target,
|
44
|
+
"conditional": edge.conditional,
|
45
|
+
}
|
46
|
+
for edge in graph.edges
|
47
|
+
]
|
48
|
+
update_association_properties(
|
49
|
+
{
|
50
|
+
"langgraph.edges": json.dumps(edges),
|
51
|
+
"langgraph.nodes": json.dumps(nodes),
|
52
|
+
},
|
53
|
+
)
|
54
|
+
return wrapped(*args, **kwargs)
|
55
|
+
|
56
|
+
|
57
|
+
@with_tracer_wrapper
|
58
|
+
async def async_wrap_pregel_stream(
|
59
|
+
tracer: Tracer, to_wrap, wrapped, instance, args, kwargs
|
60
|
+
):
|
61
|
+
graph: Graph = await instance.aget_graph()
|
62
|
+
nodes = [
|
63
|
+
{
|
64
|
+
"id": node.id,
|
65
|
+
"name": node.name,
|
66
|
+
"metadata": node.metadata,
|
67
|
+
}
|
68
|
+
for node in graph.nodes.values()
|
69
|
+
]
|
70
|
+
edges = [
|
71
|
+
{
|
72
|
+
"source": edge.source,
|
73
|
+
"target": edge.target,
|
74
|
+
"conditional": edge.conditional,
|
75
|
+
}
|
76
|
+
for edge in graph.edges
|
77
|
+
]
|
78
|
+
update_association_properties(
|
79
|
+
{
|
80
|
+
"langgraph.edges": json.dumps(edges),
|
81
|
+
"langgraph.nodes": json.dumps(nodes),
|
82
|
+
},
|
83
|
+
)
|
84
|
+
return await wrapped(*args, **kwargs)
|
85
|
+
|
86
|
+
|
87
|
+
class LanggraphInstrumentor(BaseInstrumentor):
|
88
|
+
"""An instrumentor for Langgraph."""
|
89
|
+
|
90
|
+
def __init__(self):
|
91
|
+
super().__init__()
|
92
|
+
|
93
|
+
def instrumentation_dependencies(self) -> Collection[str]:
|
94
|
+
return _instruments
|
95
|
+
|
96
|
+
def _instrument(self, **kwargs):
|
97
|
+
tracer_provider = kwargs.get("tracer_provider")
|
98
|
+
tracer = get_tracer(__name__, "0.0.1a0", tracer_provider)
|
99
|
+
|
100
|
+
wrap_function_wrapper(
|
101
|
+
module="langgraph.pregel",
|
102
|
+
name="Pregel.stream",
|
103
|
+
wrapper=wrap_pregel_stream(tracer, "Pregel.stream"),
|
104
|
+
)
|
105
|
+
wrap_function_wrapper(
|
106
|
+
module="langgraph.pregel",
|
107
|
+
name="Pregel.astream",
|
108
|
+
wrapper=async_wrap_pregel_stream(tracer, "Pregel.astream"),
|
109
|
+
)
|
110
|
+
|
111
|
+
def _uninstrument(self, **kwargs):
|
112
|
+
unwrap(
|
113
|
+
module="langgraph.pregel",
|
114
|
+
name="Pregel.stream",
|
115
|
+
)
|
116
|
+
unwrap(
|
117
|
+
module="langgraph.pregel",
|
118
|
+
name="Pregel.astream",
|
119
|
+
)
|
@@ -0,0 +1,60 @@
|
|
1
|
+
import logging
|
2
|
+
import traceback
|
3
|
+
|
4
|
+
import pydantic
|
5
|
+
from opentelemetry.trace import Span
|
6
|
+
from typing import Any
|
7
|
+
|
8
|
+
|
9
|
+
def set_span_attribute(span: Span, name: str, value: str):
|
10
|
+
if value is not None:
|
11
|
+
if value != "":
|
12
|
+
span.set_attribute(name, value)
|
13
|
+
return
|
14
|
+
|
15
|
+
|
16
|
+
def dont_throw(func):
|
17
|
+
"""
|
18
|
+
A decorator that wraps the passed in function and logs exceptions instead of throwing them.
|
19
|
+
|
20
|
+
@param func: The function to wrap
|
21
|
+
@return: The wrapper function
|
22
|
+
"""
|
23
|
+
# Obtain a logger specific to the function's module
|
24
|
+
logger = logging.getLogger(func.__module__)
|
25
|
+
|
26
|
+
def wrapper(*args, **kwargs):
|
27
|
+
try:
|
28
|
+
return func(*args, **kwargs)
|
29
|
+
except Exception:
|
30
|
+
logger.debug(
|
31
|
+
"Laminar failed to trace in %s, error: %s",
|
32
|
+
func.__name__,
|
33
|
+
traceback.format_exc(),
|
34
|
+
)
|
35
|
+
|
36
|
+
return wrapper
|
37
|
+
|
38
|
+
|
39
|
+
def to_dict(obj: pydantic.BaseModel | dict) -> dict[str, Any]:
|
40
|
+
try:
|
41
|
+
if isinstance(obj, pydantic.BaseModel):
|
42
|
+
return obj.model_dump()
|
43
|
+
elif isinstance(obj, dict):
|
44
|
+
return obj
|
45
|
+
else:
|
46
|
+
return dict(obj)
|
47
|
+
except Exception:
|
48
|
+
return dict(obj)
|
49
|
+
|
50
|
+
|
51
|
+
def with_tracer_wrapper(func):
|
52
|
+
"""Helper for providing tracer for wrapper functions."""
|
53
|
+
|
54
|
+
def _with_tracer(tracer, to_wrap):
|
55
|
+
def wrapper(wrapped, instance, args, kwargs):
|
56
|
+
return func(tracer, to_wrap, wrapped, instance, args, kwargs)
|
57
|
+
|
58
|
+
return wrapper
|
59
|
+
|
60
|
+
return _with_tracer
|
@@ -171,6 +171,18 @@ class LangchainInstrumentorInitializer(InstrumentorInitializer):
|
|
171
171
|
return LangchainInstrumentor()
|
172
172
|
|
173
173
|
|
174
|
+
class LanggraphInstrumentorInitializer(InstrumentorInitializer):
|
175
|
+
def init_instrumentor(self, *args, **kwargs) -> BaseInstrumentor | None:
|
176
|
+
if not is_package_installed("langgraph"):
|
177
|
+
return None
|
178
|
+
if not is_package_installed("langchain-core"):
|
179
|
+
return None
|
180
|
+
|
181
|
+
from ..opentelemetry.instrumentation.langgraph import LanggraphInstrumentor
|
182
|
+
|
183
|
+
return LanggraphInstrumentor()
|
184
|
+
|
185
|
+
|
174
186
|
class LlamaIndexInstrumentorInitializer(InstrumentorInitializer):
|
175
187
|
def init_instrumentor(self, *args, **kwargs) -> BaseInstrumentor | None:
|
176
188
|
if not (
|
@@ -6,10 +6,12 @@ from lmnr.opentelemetry_lib.tracing.attributes import (
|
|
6
6
|
)
|
7
7
|
|
8
8
|
from opentelemetry.context import Context, attach, set_value, get_value
|
9
|
-
from opentelemetry.trace import Span
|
9
|
+
from opentelemetry.sdk.trace import Span
|
10
10
|
from opentelemetry import trace
|
11
11
|
|
12
12
|
|
13
|
+
# TODO: delete this once deprecated Laminar.with_labels is removed. The logic
|
14
|
+
# should be moved into Laminar.set_tracing_level
|
13
15
|
def set_association_properties(properties: dict) -> None:
|
14
16
|
attach(set_value("association_properties", properties))
|
15
17
|
|
@@ -17,10 +19,13 @@ def set_association_properties(properties: dict) -> None:
|
|
17
19
|
_set_association_properties_attributes(span, properties)
|
18
20
|
|
19
21
|
|
22
|
+
# TODO: delete this once deprecated Laminar.with_labels is removed
|
20
23
|
def get_association_properties(context: Context | None = None) -> dict:
|
21
24
|
return get_value("association_properties", context) or {}
|
22
25
|
|
23
26
|
|
27
|
+
# TODO: delete this once deprecated Laminar.with_labels is removed. The logic
|
28
|
+
# should be moved into Laminar.set_tracing_level
|
24
29
|
def update_association_properties(
|
25
30
|
properties: dict,
|
26
31
|
set_on_current_span: bool = True,
|
@@ -37,6 +42,7 @@ def update_association_properties(
|
|
37
42
|
_set_association_properties_attributes(span, properties)
|
38
43
|
|
39
44
|
|
45
|
+
# TODO: this logic should be moved into Laminar.set_tracing_level
|
40
46
|
def remove_association_properties(properties: dict) -> None:
|
41
47
|
props: dict = copy.copy(get_value("association_properties") or {})
|
42
48
|
for k in properties.keys():
|
@@ -45,8 +51,15 @@ def remove_association_properties(properties: dict) -> None:
|
|
45
51
|
|
46
52
|
|
47
53
|
def _set_association_properties_attributes(span: Span, properties: dict) -> None:
|
54
|
+
if not span.is_recording():
|
55
|
+
return
|
48
56
|
for key, value in properties.items():
|
49
57
|
if key == TRACING_LEVEL:
|
50
58
|
span.set_attribute(f"lmnr.internal.{TRACING_LEVEL}", value)
|
51
59
|
continue
|
60
|
+
if (
|
61
|
+
key in ["langgraph.edges", "langgraph.nodes"]
|
62
|
+
and span.name != "LangGraph.workflow"
|
63
|
+
):
|
64
|
+
continue
|
52
65
|
span.set_attribute(f"{ASSOCIATION_PROPERTIES}.{key}", value)
|
@@ -26,6 +26,7 @@ class Instruments(Enum):
|
|
26
26
|
HAYSTACK = "haystack"
|
27
27
|
LANCEDB = "lancedb"
|
28
28
|
LANGCHAIN = "langchain"
|
29
|
+
LANGGRAPH = "langgraph"
|
29
30
|
LLAMA_INDEX = "llama_index"
|
30
31
|
MARQO = "marqo"
|
31
32
|
MCP = "mcp"
|
@@ -62,6 +63,7 @@ INSTRUMENTATION_INITIALIZERS: dict[
|
|
62
63
|
Instruments.HAYSTACK: initializers.HaystackInstrumentorInitializer(),
|
63
64
|
Instruments.LANCEDB: initializers.LanceDBInstrumentorInitializer(),
|
64
65
|
Instruments.LANGCHAIN: initializers.LangchainInstrumentorInitializer(),
|
66
|
+
Instruments.LANGGRAPH: initializers.LanggraphInstrumentorInitializer(),
|
65
67
|
Instruments.LLAMA_INDEX: initializers.LlamaIndexInstrumentorInitializer(),
|
66
68
|
Instruments.MARQO: initializers.MarqoInstrumentorInitializer(),
|
67
69
|
Instruments.MCP: initializers.MCPInstrumentorInitializer(),
|
@@ -6,7 +6,7 @@ from opentelemetry.sdk.trace.export import (
|
|
6
6
|
BatchSpanProcessor,
|
7
7
|
SimpleSpanProcessor,
|
8
8
|
)
|
9
|
-
from opentelemetry.trace import Span
|
9
|
+
from opentelemetry.sdk.trace import Span
|
10
10
|
from opentelemetry.context import Context, get_value, get_current, set_value
|
11
11
|
|
12
12
|
from lmnr.opentelemetry_lib.tracing.attributes import (
|
lmnr/sdk/decorators.py
CHANGED
@@ -9,9 +9,11 @@ from typing import Any, Callable, Literal, TypeVar, cast
|
|
9
9
|
from typing_extensions import ParamSpec
|
10
10
|
|
11
11
|
from lmnr.opentelemetry_lib.tracing.attributes import SESSION_ID
|
12
|
+
from lmnr.sdk.log import get_default_logger
|
12
13
|
|
13
14
|
from .utils import is_async
|
14
15
|
|
16
|
+
logger = get_default_logger(__name__)
|
15
17
|
|
16
18
|
P = ParamSpec("P")
|
17
19
|
R = TypeVar("R")
|
@@ -27,6 +29,7 @@ def observe(
|
|
27
29
|
span_type: Literal["DEFAULT", "LLM", "TOOL"] = "DEFAULT",
|
28
30
|
ignore_inputs: list[str] | None = None,
|
29
31
|
metadata: dict[str, Any] | None = None,
|
32
|
+
tags: list[str] | None = None,
|
30
33
|
) -> Callable[[Callable[P, R]], Callable[P, R]]:
|
31
34
|
"""The main decorator entrypoint for Laminar. This is used to wrap
|
32
35
|
functions and methods to create spans.
|
@@ -52,6 +55,8 @@ def observe(
|
|
52
55
|
this argument. Defaults to None.
|
53
56
|
metadata (dict[str, Any] | None, optional): Metadata to associate with\
|
54
57
|
the trace. Must be JSON serializable. Defaults to None.
|
58
|
+
tags (list[str] | None, optional): Tags to associate with the trace.
|
59
|
+
Defaults to None.
|
55
60
|
Raises:
|
56
61
|
Exception: re-raises the exception if the wrapped function raises an\
|
57
62
|
exception
|
@@ -79,6 +84,13 @@ def observe(
|
|
79
84
|
for k, v in metadata.items()
|
80
85
|
}
|
81
86
|
)
|
87
|
+
if tags is not None:
|
88
|
+
if not isinstance(tags, list) or not all(
|
89
|
+
isinstance(tag, str) for tag in tags
|
90
|
+
):
|
91
|
+
logger.warning("Tags must be a list of strings. Tags will be ignored.")
|
92
|
+
else:
|
93
|
+
association_properties["tags"] = tags
|
82
94
|
result = (
|
83
95
|
aentity_method(
|
84
96
|
name=name,
|
lmnr/sdk/evaluations.py
CHANGED
@@ -233,10 +233,10 @@ class Evaluation:
|
|
233
233
|
export_timeout_seconds=trace_export_timeout_seconds,
|
234
234
|
)
|
235
235
|
|
236
|
-
async def run(self) -> Awaitable[
|
236
|
+
async def run(self) -> Awaitable[dict[str, int | float]]:
|
237
237
|
return await self._run()
|
238
238
|
|
239
|
-
async def _run(self) ->
|
239
|
+
async def _run(self) -> dict[str, int | float]:
|
240
240
|
if isinstance(self.data, LaminarDataset):
|
241
241
|
self.data.set_client(
|
242
242
|
LaminarClient(
|
@@ -261,11 +261,12 @@ class Evaluation:
|
|
261
261
|
except Exception as e:
|
262
262
|
self.reporter.stopWithError(e)
|
263
263
|
await self._shutdown()
|
264
|
-
return
|
264
|
+
return {}
|
265
265
|
|
266
266
|
average_scores = get_average_scores(result_datapoints)
|
267
267
|
self.reporter.stop(average_scores, evaluation.projectId, evaluation.id)
|
268
268
|
await self._shutdown()
|
269
|
+
return average_scores
|
269
270
|
|
270
271
|
async def _shutdown(self):
|
271
272
|
# We use flush() instead of shutdown() because multiple evaluations
|
lmnr/sdk/laminar.py
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
from contextlib import contextmanager
|
2
2
|
from contextvars import Context
|
3
|
+
import warnings
|
4
|
+
from typing_extensions import deprecated
|
3
5
|
from lmnr.opentelemetry_lib import TracerManager
|
4
6
|
from lmnr.opentelemetry_lib.tracing.instruments import Instruments
|
5
7
|
from lmnr.opentelemetry_lib.tracing.tracer import get_tracer
|
6
8
|
from lmnr.opentelemetry_lib.tracing.attributes import (
|
7
9
|
ASSOCIATION_PROPERTIES,
|
10
|
+
USER_ID,
|
8
11
|
Attributes,
|
9
12
|
SPAN_TYPE,
|
10
13
|
)
|
@@ -18,7 +21,6 @@ from opentelemetry.util.types import AttributeValue
|
|
18
21
|
|
19
22
|
from typing import Any, Literal
|
20
23
|
|
21
|
-
import copy
|
22
24
|
import datetime
|
23
25
|
import logging
|
24
26
|
import os
|
@@ -37,7 +39,7 @@ from lmnr.opentelemetry_lib.tracing.context_properties import (
|
|
37
39
|
set_association_properties,
|
38
40
|
update_association_properties,
|
39
41
|
)
|
40
|
-
from lmnr.sdk.utils import from_env
|
42
|
+
from lmnr.sdk.utils import from_env, is_otel_attribute_value_type
|
41
43
|
|
42
44
|
from .log import VerboseColorfulFormatter
|
43
45
|
|
@@ -224,6 +226,7 @@ class Laminar:
|
|
224
226
|
context: Context | None = None,
|
225
227
|
labels: list[str] | None = None,
|
226
228
|
parent_span_context: LaminarSpanContext | None = None,
|
229
|
+
tags: list[str] | None = None,
|
227
230
|
):
|
228
231
|
"""Start a new span as the current span. Useful for manual
|
229
232
|
instrumentation. If `span_type` is set to `"LLM"`, you should report
|
@@ -256,8 +259,10 @@ class Laminar:
|
|
256
259
|
`Laminar.get_span_context`, `Laminar.get_span_context_dict` and\
|
257
260
|
`Laminar.get_span_context_str` for more information.
|
258
261
|
Defaults to None.
|
259
|
-
labels (list[str] | None, optional):
|
260
|
-
span. Defaults to None.
|
262
|
+
labels (list[str] | None, optional): [DEPRECATED] Use tags\
|
263
|
+
instead. Labels to set for the span. Defaults to None.
|
264
|
+
tags (list[str] | None, optional): tags to set for the span.
|
265
|
+
Defaults to None.
|
261
266
|
"""
|
262
267
|
|
263
268
|
if not cls.is_initialized():
|
@@ -283,18 +288,32 @@ class Laminar:
|
|
283
288
|
label_props = {}
|
284
289
|
try:
|
285
290
|
if labels:
|
291
|
+
warnings.warn(
|
292
|
+
"`Laminar.start_as_current_span` `labels` is deprecated. Use `tags` instead.",
|
293
|
+
DeprecationWarning,
|
294
|
+
)
|
286
295
|
label_props = {f"{ASSOCIATION_PROPERTIES}.labels": labels}
|
287
296
|
except Exception:
|
288
297
|
cls.__logger.warning(
|
289
298
|
f"`start_as_current_span` Could not set labels: {labels}. "
|
290
299
|
"They will be propagated to the next span."
|
291
300
|
)
|
301
|
+
tag_props = {}
|
302
|
+
if tags:
|
303
|
+
if isinstance(tags, list) and all(isinstance(tag, str) for tag in tags):
|
304
|
+
tag_props = {f"{ASSOCIATION_PROPERTIES}.tags": tags}
|
305
|
+
else:
|
306
|
+
cls.__logger.warning(
|
307
|
+
f"`start_as_current_span` Could not set tags: {tags}. Tags must be a list of strings. "
|
308
|
+
"Tags will be ignored."
|
309
|
+
)
|
292
310
|
with tracer.start_as_current_span(
|
293
311
|
name,
|
294
312
|
context=ctx,
|
295
313
|
attributes={
|
296
314
|
SPAN_TYPE: span_type,
|
297
315
|
**(label_props),
|
316
|
+
**(tag_props),
|
298
317
|
},
|
299
318
|
) as span:
|
300
319
|
if input is not None:
|
@@ -319,6 +338,10 @@ class Laminar:
|
|
319
338
|
|
320
339
|
@classmethod
|
321
340
|
@contextmanager
|
341
|
+
@deprecated(
|
342
|
+
"Use `Laminar.set_span_tags` or the `tags` argument of "
|
343
|
+
"`Laminar.start_as_current_span` or `Laminar.start_span` instead"
|
344
|
+
)
|
322
345
|
def with_labels(cls, labels: list[str], context: Context | None = None):
|
323
346
|
"""Set labels for spans within this `with` context. This is useful for
|
324
347
|
adding labels to the spans created in the auto-instrumentations.
|
@@ -334,6 +357,11 @@ class Laminar:
|
|
334
357
|
openai_client.chat.completions.create()
|
335
358
|
```
|
336
359
|
"""
|
360
|
+
warnings.warn(
|
361
|
+
"`Laminar.with_labels` is deprecated. Use `Laminar.set_span_tags` or the `tags` argument of "
|
362
|
+
"`Laminar.start_as_current_span` or `Laminar.start_span` instead",
|
363
|
+
DeprecationWarning,
|
364
|
+
)
|
337
365
|
if not cls.is_initialized():
|
338
366
|
yield
|
339
367
|
return
|
@@ -365,6 +393,7 @@ class Laminar:
|
|
365
393
|
context: Context | None = None,
|
366
394
|
parent_span_context: LaminarSpanContext | None = None,
|
367
395
|
labels: dict[str, str] | None = None,
|
396
|
+
tags: list[str] | None = None,
|
368
397
|
):
|
369
398
|
"""Start a new span. Useful for manual instrumentation.
|
370
399
|
If `span_type` is set to `"LLM"`, you should report usage and response
|
@@ -416,8 +445,10 @@ class Laminar:
|
|
416
445
|
`Laminar.get_span_context`, `Laminar.get_span_context_dict` and\
|
417
446
|
`Laminar.get_span_context_str` for more information.
|
418
447
|
Defaults to None.
|
419
|
-
|
420
|
-
|
448
|
+
tags (list[str] | None, optional): tags to set for the span.
|
449
|
+
Defaults to None.
|
450
|
+
labels (dict[str, str] | None, optional): [DEPRECATED] Use tags\
|
451
|
+
instead. Labels to set for the span. Defaults to None.
|
421
452
|
"""
|
422
453
|
if not cls.is_initialized():
|
423
454
|
return trace.NonRecordingSpan(
|
@@ -440,6 +471,10 @@ class Laminar:
|
|
440
471
|
label_props = {}
|
441
472
|
try:
|
442
473
|
if labels:
|
474
|
+
warnings.warn(
|
475
|
+
"`Laminar.start_span` `labels` is deprecated. Use `tags` instead.",
|
476
|
+
DeprecationWarning,
|
477
|
+
)
|
443
478
|
label_props = {
|
444
479
|
f"{ASSOCIATION_PROPERTIES}.labels": json_dumps(labels)
|
445
480
|
}
|
@@ -448,12 +483,22 @@ class Laminar:
|
|
448
483
|
f"`start_span` Could not set labels: {labels}. They will be "
|
449
484
|
"propagated to the next span."
|
450
485
|
)
|
486
|
+
tag_props = {}
|
487
|
+
if tags:
|
488
|
+
if isinstance(tags, list) and all(isinstance(tag, str) for tag in tags):
|
489
|
+
tag_props = {f"{ASSOCIATION_PROPERTIES}.tags": tags}
|
490
|
+
else:
|
491
|
+
cls.__logger.warning(
|
492
|
+
f"`start_span` Could not set tags: {tags}. Tags must be a list of strings. "
|
493
|
+
+ "Tags will be ignored."
|
494
|
+
)
|
451
495
|
span = tracer.start_span(
|
452
496
|
name,
|
453
497
|
context=ctx,
|
454
498
|
attributes={
|
455
499
|
SPAN_TYPE: span_type,
|
456
500
|
**(label_props),
|
501
|
+
**(tag_props),
|
457
502
|
},
|
458
503
|
)
|
459
504
|
if input is not None:
|
@@ -562,7 +607,6 @@ class Laminar:
|
|
562
607
|
cls.__logger.warning(
|
563
608
|
f"Attribute {key} is not a valid Laminar attribute."
|
564
609
|
)
|
565
|
-
continue
|
566
610
|
if not isinstance(value, (str, int, float, bool)):
|
567
611
|
span.set_attribute(key.value, json_dumps(value))
|
568
612
|
else:
|
@@ -649,11 +693,30 @@ class Laminar:
|
|
649
693
|
TracerManager.shutdown()
|
650
694
|
|
651
695
|
@classmethod
|
696
|
+
def set_span_tags(cls, tags: list[str]):
|
697
|
+
"""Set the tags for the current span.
|
698
|
+
|
699
|
+
Args:
|
700
|
+
tags (list[str]): Tags to set for the span.
|
701
|
+
"""
|
702
|
+
span = trace.get_current_span()
|
703
|
+
if span == trace.INVALID_SPAN:
|
704
|
+
cls.__logger.warning("No active span to set tags on")
|
705
|
+
return
|
706
|
+
if not isinstance(tags, list) or not all(isinstance(tag, str) for tag in tags):
|
707
|
+
cls.__logger.warning(
|
708
|
+
"Tags must be a list of strings. Tags will be ignored."
|
709
|
+
)
|
710
|
+
return
|
711
|
+
span.set_attribute(f"{ASSOCIATION_PROPERTIES}.tags", tags)
|
712
|
+
|
713
|
+
@classmethod
|
714
|
+
@deprecated("Use `Laminar.set_trace_session_id` instead")
|
652
715
|
def set_session(
|
653
716
|
cls,
|
654
717
|
session_id: str | None = None,
|
655
718
|
):
|
656
|
-
"""Set the session
|
719
|
+
"""Set the session id for the current span and the context
|
657
720
|
(i.e. any children spans created from the current span in the current
|
658
721
|
thread).
|
659
722
|
|
@@ -663,12 +726,53 @@ class Laminar:
|
|
663
726
|
sessions/conversations.
|
664
727
|
Defaults to None.
|
665
728
|
"""
|
729
|
+
warnings.warn(
|
730
|
+
"`Laminar.set_session` is deprecated. Use `Laminar.set_trace_session_id` instead",
|
731
|
+
DeprecationWarning,
|
732
|
+
)
|
666
733
|
association_properties = {}
|
667
734
|
if session_id is not None:
|
668
735
|
association_properties[SESSION_ID] = session_id
|
669
|
-
update_association_properties(association_properties)
|
736
|
+
# update_association_properties(association_properties)
|
737
|
+
span = trace.get_current_span()
|
738
|
+
if span == trace.INVALID_SPAN:
|
739
|
+
cls.__logger.warning("No active span to set session id on")
|
740
|
+
return
|
741
|
+
if session_id is not None:
|
742
|
+
span.set_attribute(f"{ASSOCIATION_PROPERTIES}.{SESSION_ID}", session_id)
|
743
|
+
|
744
|
+
@classmethod
|
745
|
+
def set_trace_session_id(cls, session_id: str | None = None):
|
746
|
+
"""Set the session id for the current trace.
|
747
|
+
Overrides any existing session id.
|
748
|
+
|
749
|
+
Args:
|
750
|
+
session_id (str | None, optional): Custom session id. Defaults to None.
|
751
|
+
"""
|
752
|
+
span = trace.get_current_span()
|
753
|
+
if span == trace.INVALID_SPAN:
|
754
|
+
cls.__logger.warning("No active span to set session id on")
|
755
|
+
return
|
756
|
+
if session_id is not None:
|
757
|
+
span.set_attribute(f"{ASSOCIATION_PROPERTIES}.{SESSION_ID}", session_id)
|
670
758
|
|
671
759
|
@classmethod
|
760
|
+
def set_trace_user_id(cls, user_id: str | None = None):
|
761
|
+
"""Set the user id for the current trace.
|
762
|
+
Overrides any existing user id.
|
763
|
+
|
764
|
+
Args:
|
765
|
+
user_id (str | None, optional): Custom user id. Defaults to None.
|
766
|
+
"""
|
767
|
+
span = trace.get_current_span()
|
768
|
+
if span == trace.INVALID_SPAN:
|
769
|
+
cls.__logger.warning("No active span to set user id on")
|
770
|
+
return
|
771
|
+
if user_id is not None:
|
772
|
+
span.set_attribute(f"{ASSOCIATION_PROPERTIES}.{USER_ID}", user_id)
|
773
|
+
|
774
|
+
@classmethod
|
775
|
+
@deprecated("Use `Laminar.set_trace_metadata` instead")
|
672
776
|
def set_metadata(cls, metadata: dict[str, str]):
|
673
777
|
"""Set the metadata for the current trace.
|
674
778
|
|
@@ -676,25 +780,37 @@ class Laminar:
|
|
676
780
|
metadata (dict[str, str]): Metadata to set for the trace. Will be\
|
677
781
|
sent as attributes, so must be json serializable.
|
678
782
|
"""
|
783
|
+
warnings.warn(
|
784
|
+
"`Laminar.set_metadata` is deprecated. Use `Laminar.set_trace_metadata` instead",
|
785
|
+
DeprecationWarning,
|
786
|
+
)
|
679
787
|
props = {f"metadata.{k}": json_dumps(v) for k, v in metadata.items()}
|
680
|
-
update_association_properties(props)
|
788
|
+
# update_association_properties(props)
|
789
|
+
span = trace.get_current_span()
|
790
|
+
if span == trace.INVALID_SPAN:
|
791
|
+
cls.__logger.warning("No active span to set metadata on")
|
792
|
+
return
|
793
|
+
for key, value in props.items():
|
794
|
+
span.set_attribute(key, value)
|
681
795
|
|
682
796
|
@classmethod
|
683
|
-
def
|
684
|
-
"""
|
685
|
-
props: dict = copy.copy(context_api.get_value("association_properties"))
|
686
|
-
metadata_keys = [k for k in props.keys() if k.startswith("metadata.")]
|
687
|
-
for k in metadata_keys:
|
688
|
-
props.pop(k)
|
689
|
-
set_association_properties(props)
|
797
|
+
def set_trace_metadata(cls, metadata: dict[str, AttributeValue]):
|
798
|
+
"""Set the metadata for the current trace.
|
690
799
|
|
691
|
-
|
692
|
-
|
693
|
-
"""
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
800
|
+
Args:
|
801
|
+
metadata (dict[str, AttributeValue]): Metadata to set for the trace.
|
802
|
+
"""
|
803
|
+
span = trace.get_current_span()
|
804
|
+
if span == trace.INVALID_SPAN:
|
805
|
+
cls.__logger.warning("No active span to set metadata on")
|
806
|
+
return
|
807
|
+
for key, value in metadata.items():
|
808
|
+
if is_otel_attribute_value_type(value):
|
809
|
+
span.set_attribute(f"{ASSOCIATION_PROPERTIES}.metadata.{key}", value)
|
810
|
+
else:
|
811
|
+
span.set_attribute(
|
812
|
+
f"{ASSOCIATION_PROPERTIES}.metadata.{key}", json_dumps(value)
|
813
|
+
)
|
698
814
|
|
699
815
|
@classmethod
|
700
816
|
def get_base_http_url(cls):
|
@@ -735,7 +851,8 @@ class Laminar:
|
|
735
851
|
Args:
|
736
852
|
trace_type (TraceType): Type of the trace
|
737
853
|
"""
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
854
|
+
span = trace.get_current_span()
|
855
|
+
if span == trace.INVALID_SPAN:
|
856
|
+
cls.__logger.warning("No active span to set trace type on")
|
857
|
+
return
|
858
|
+
span.set_attribute(f"{ASSOCIATION_PROPERTIES}.{TRACE_TYPE}", trace_type.value)
|
lmnr/sdk/types.py
CHANGED
@@ -81,7 +81,7 @@ class PartialEvaluationDatapoint(pydantic.BaseModel):
|
|
81
81
|
"traceId": str(self.trace_id),
|
82
82
|
"executorSpanId": str(self.executor_span_id),
|
83
83
|
"metadata": (
|
84
|
-
serialize(self.metadata) if self.metadata is not None else
|
84
|
+
serialize(self.metadata) if self.metadata is not None else {}
|
85
85
|
),
|
86
86
|
}
|
87
87
|
except Exception as e:
|
@@ -123,7 +123,7 @@ class EvaluationResultDatapoint(pydantic.BaseModel):
|
|
123
123
|
"executorSpanId": str(self.executor_span_id),
|
124
124
|
"index": self.index,
|
125
125
|
"metadata": (
|
126
|
-
serialize(self.metadata) if self.metadata is not None else
|
126
|
+
serialize(self.metadata) if self.metadata is not None else {}
|
127
127
|
),
|
128
128
|
}
|
129
129
|
except Exception as e:
|