lmnr 0.4.4__tar.gz → 0.4.6__tar.gz
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-0.4.4 → lmnr-0.4.6}/PKG-INFO +39 -1
- {lmnr-0.4.4 → lmnr-0.4.6}/README.md +38 -0
- {lmnr-0.4.4 → lmnr-0.4.6}/pyproject.toml +2 -2
- {lmnr-0.4.4 → lmnr-0.4.6}/src/lmnr/sdk/laminar.py +29 -23
- {lmnr-0.4.4 → lmnr-0.4.6}/LICENSE +0 -0
- {lmnr-0.4.4 → lmnr-0.4.6}/src/lmnr/__init__.py +0 -0
- {lmnr-0.4.4 → lmnr-0.4.6}/src/lmnr/sdk/__init__.py +0 -0
- {lmnr-0.4.4 → lmnr-0.4.6}/src/lmnr/sdk/decorators.py +0 -0
- {lmnr-0.4.4 → lmnr-0.4.6}/src/lmnr/sdk/evaluations.py +0 -0
- {lmnr-0.4.4 → lmnr-0.4.6}/src/lmnr/sdk/log.py +0 -0
- {lmnr-0.4.4 → lmnr-0.4.6}/src/lmnr/sdk/types.py +0 -0
- {lmnr-0.4.4 → lmnr-0.4.6}/src/lmnr/sdk/utils.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: lmnr
|
3
|
-
Version: 0.4.
|
3
|
+
Version: 0.4.6
|
4
4
|
Summary: Python SDK for Laminar AI
|
5
5
|
License: Apache-2.0
|
6
6
|
Author: lmnr.ai
|
@@ -89,6 +89,44 @@ def poem_writer(topic="turbulence"):
|
|
89
89
|
print(poem_writer(topic="laminar flow"))
|
90
90
|
```
|
91
91
|
|
92
|
+
### Manual instrumentation
|
93
|
+
|
94
|
+
Our manual instrumentation is a very thin wrapper around OpenTelemetry's
|
95
|
+
`trace.start_span`. Our wrapper sets the span into the active context.
|
96
|
+
You don't have to explicitly pass the spans around, it is enough to
|
97
|
+
just call `L.start_span`, and OpenTelemetry will handle the context management
|
98
|
+
|
99
|
+
```python
|
100
|
+
from lmnr import observe, Laminar as L
|
101
|
+
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>")
|
102
|
+
|
103
|
+
def poem_writer(topic="turbulence"):
|
104
|
+
|
105
|
+
span = L.start_span("poem_writer", topic) # start a span
|
106
|
+
|
107
|
+
prompt = f"write a poem about {topic}"
|
108
|
+
|
109
|
+
# OpenAI calls are still automatically instrumented with OpenLLMetry
|
110
|
+
response = client.chat.completions.create(
|
111
|
+
model="gpt-4o",
|
112
|
+
messages=[
|
113
|
+
{"role": "system", "content": "You are a helpful assistant."},
|
114
|
+
{"role": "user", "content": prompt},
|
115
|
+
],
|
116
|
+
)
|
117
|
+
poem = response.choices[0].message.content
|
118
|
+
# while within the span, you can attach laminar events to it
|
119
|
+
L.event("event_name", "event_value")
|
120
|
+
|
121
|
+
L.set_span_output(span, poem) # set an output
|
122
|
+
|
123
|
+
# IMPORTANT: don't forget to end all the spans (usually in `finally` blocks)
|
124
|
+
# Otherwise, the trace may not be sent/displayed correctly
|
125
|
+
span.end()
|
126
|
+
|
127
|
+
return poem
|
128
|
+
```
|
129
|
+
|
92
130
|
|
93
131
|
## Sending events
|
94
132
|
|
@@ -68,6 +68,44 @@ def poem_writer(topic="turbulence"):
|
|
68
68
|
print(poem_writer(topic="laminar flow"))
|
69
69
|
```
|
70
70
|
|
71
|
+
### Manual instrumentation
|
72
|
+
|
73
|
+
Our manual instrumentation is a very thin wrapper around OpenTelemetry's
|
74
|
+
`trace.start_span`. Our wrapper sets the span into the active context.
|
75
|
+
You don't have to explicitly pass the spans around, it is enough to
|
76
|
+
just call `L.start_span`, and OpenTelemetry will handle the context management
|
77
|
+
|
78
|
+
```python
|
79
|
+
from lmnr import observe, Laminar as L
|
80
|
+
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>")
|
81
|
+
|
82
|
+
def poem_writer(topic="turbulence"):
|
83
|
+
|
84
|
+
span = L.start_span("poem_writer", topic) # start a span
|
85
|
+
|
86
|
+
prompt = f"write a poem about {topic}"
|
87
|
+
|
88
|
+
# OpenAI calls are still automatically instrumented with OpenLLMetry
|
89
|
+
response = client.chat.completions.create(
|
90
|
+
model="gpt-4o",
|
91
|
+
messages=[
|
92
|
+
{"role": "system", "content": "You are a helpful assistant."},
|
93
|
+
{"role": "user", "content": prompt},
|
94
|
+
],
|
95
|
+
)
|
96
|
+
poem = response.choices[0].message.content
|
97
|
+
# while within the span, you can attach laminar events to it
|
98
|
+
L.event("event_name", "event_value")
|
99
|
+
|
100
|
+
L.set_span_output(span, poem) # set an output
|
101
|
+
|
102
|
+
# IMPORTANT: don't forget to end all the spans (usually in `finally` blocks)
|
103
|
+
# Otherwise, the trace may not be sent/displayed correctly
|
104
|
+
span.end()
|
105
|
+
|
106
|
+
return poem
|
107
|
+
```
|
108
|
+
|
71
109
|
|
72
110
|
## Sending events
|
73
111
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "lmnr"
|
3
|
-
version = "0.4.
|
3
|
+
version = "0.4.6"
|
4
4
|
description = "Python SDK for Laminar AI"
|
5
5
|
authors = [
|
6
6
|
{ name = "lmnr.ai", email = "founders@lmnr.ai" }
|
@@ -11,7 +11,7 @@ license = "Apache-2.0"
|
|
11
11
|
|
12
12
|
[tool.poetry]
|
13
13
|
name = "lmnr"
|
14
|
-
version = "0.4.
|
14
|
+
version = "0.4.6"
|
15
15
|
description = "Python SDK for Laminar AI"
|
16
16
|
authors = ["lmnr.ai"]
|
17
17
|
readme = "README.md"
|
@@ -9,9 +9,10 @@ from opentelemetry.semconv_ai import SpanAttributes
|
|
9
9
|
from opentelemetry.util.types import AttributeValue
|
10
10
|
from traceloop.sdk import Traceloop
|
11
11
|
from traceloop.sdk.tracing import get_tracer
|
12
|
+
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
|
12
13
|
|
13
14
|
from pydantic.alias_generators import to_snake
|
14
|
-
from typing import Any, Optional,
|
15
|
+
from typing import Any, Optional, Union
|
15
16
|
|
16
17
|
import copy
|
17
18
|
import datetime
|
@@ -35,7 +36,7 @@ from .types import (
|
|
35
36
|
|
36
37
|
|
37
38
|
class Laminar:
|
38
|
-
__base_url: str = "https://api.lmnr.ai"
|
39
|
+
__base_url: str = "https://api.lmnr.ai:8443"
|
39
40
|
__project_api_key: Optional[str] = None
|
40
41
|
__env: dict[str, str] = {}
|
41
42
|
__initialized: bool = False
|
@@ -67,9 +68,9 @@ class Laminar:
|
|
67
68
|
base_url (Optional[str], optional): Url of Laminar endpoint,
|
68
69
|
or the customopen telemetry ingester.
|
69
70
|
If not specified, defaults to
|
70
|
-
https://api.lmnr.ai.
|
71
|
+
https://api.lmnr.ai:8443.
|
71
72
|
For locally hosted Laminar, default setting
|
72
|
-
must be http://localhost:
|
73
|
+
must be http://localhost:8001
|
73
74
|
Defaults to None.
|
74
75
|
|
75
76
|
Raises:
|
@@ -97,6 +98,10 @@ class Laminar:
|
|
97
98
|
Traceloop.init(
|
98
99
|
api_endpoint=cls.__base_url,
|
99
100
|
api_key=cls.__project_api_key,
|
101
|
+
exporter=OTLPSpanExporter(
|
102
|
+
endpoint=cls.__base_url,
|
103
|
+
headers={"authorization": f"Bearer {cls.__project_api_key}"},
|
104
|
+
),
|
100
105
|
)
|
101
106
|
|
102
107
|
@classmethod
|
@@ -247,7 +252,7 @@ class Laminar:
|
|
247
252
|
name: str,
|
248
253
|
evaluator: str,
|
249
254
|
data: dict[str, AttributeValue],
|
250
|
-
env: Optional[dict[str, str]] =
|
255
|
+
env: Optional[dict[str, str]] = None,
|
251
256
|
timestamp: Optional[Union[datetime.datetime, int]] = None,
|
252
257
|
):
|
253
258
|
"""Send an event for evaluation to the Laminar backend
|
@@ -289,7 +294,7 @@ class Laminar:
|
|
289
294
|
cls,
|
290
295
|
name: str,
|
291
296
|
input: Any = None,
|
292
|
-
) ->
|
297
|
+
) -> Span:
|
293
298
|
"""Start a new span with the given name. Useful for manual
|
294
299
|
instrumentation.
|
295
300
|
|
@@ -304,33 +309,34 @@ class Laminar:
|
|
304
309
|
that must be passed to `end_span` to end the span.
|
305
310
|
|
306
311
|
"""
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
312
|
+
tracer = get_tracer().__enter__()
|
313
|
+
span = tracer.start_span(name)
|
314
|
+
# apparently, detaching from this context is not mandatory.
|
315
|
+
# According to traceloop, and the github issue in opentelemetry,
|
316
|
+
# the context is collected by the garbage collector.
|
317
|
+
# https://github.com/open-telemetry/opentelemetry-python/issues/2606#issuecomment-2106320379
|
318
|
+
context.attach(set_span_in_context(span))
|
319
|
+
|
320
|
+
if input is not None:
|
321
|
+
span.set_attribute(
|
322
|
+
SpanAttributes.TRACELOOP_ENTITY_INPUT, json.dumps({"input": input})
|
323
|
+
)
|
324
|
+
|
325
|
+
return span
|
317
326
|
|
318
327
|
@classmethod
|
319
|
-
def
|
320
|
-
"""
|
328
|
+
def set_span_output(cls, span: Span, output: Any = None):
|
329
|
+
"""Set the output of the span. Useful for manual instrumentation.
|
321
330
|
|
322
331
|
Args:
|
323
|
-
span (Span): span
|
324
|
-
token (object): context token returned by `start_span`
|
332
|
+
span (Span): the span to set the output for
|
325
333
|
output (Any, optional): output of the span. Will be sent as an
|
326
334
|
attribute, so must be json serializable. Defaults to None.
|
327
335
|
"""
|
328
336
|
if output is not None:
|
329
337
|
span.set_attribute(
|
330
|
-
SpanAttributes.TRACELOOP_ENTITY_OUTPUT, json.dumps(
|
338
|
+
SpanAttributes.TRACELOOP_ENTITY_OUTPUT, json.dumps(output)
|
331
339
|
)
|
332
|
-
span.end()
|
333
|
-
context.detach(token)
|
334
340
|
|
335
341
|
@classmethod
|
336
342
|
def set_session(
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|