lmnr 0.4.10__tar.gz → 0.4.12b1__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.10 → lmnr-0.4.12b1}/PKG-INFO +85 -46
- {lmnr-0.4.10 → lmnr-0.4.12b1}/README.md +82 -45
- {lmnr-0.4.10 → lmnr-0.4.12b1}/pyproject.toml +5 -6
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/__init__.py +1 -1
- lmnr-0.4.12b1/src/lmnr/cli.py +39 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/sdk/decorators.py +4 -7
- lmnr-0.4.12b1/src/lmnr/sdk/evaluations.py +290 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/sdk/laminar.py +49 -34
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/sdk/types.py +23 -19
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/__init__.py +0 -13
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/instruments.py +9 -4
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tracing/tracing.py +6 -63
- lmnr-0.4.10/src/lmnr/sdk/evaluations.py +0 -178
- {lmnr-0.4.10 → lmnr-0.4.12b1}/LICENSE +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/sdk/__init__.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/sdk/log.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/sdk/utils.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/.flake8 +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/.python-version +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/config/__init__.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/decorators/__init__.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/decorators/base.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/metrics/__init__.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/metrics/metrics.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tests/__init__.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tests/cassettes/test_association_properties/test_langchain_and_external_association_properties.yaml +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tests/cassettes/test_association_properties/test_langchain_association_properties.yaml +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tests/cassettes/test_manual/test_manual_report.yaml +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tests/cassettes/test_manual/test_resource_attributes.yaml +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tests/cassettes/test_privacy_no_prompts/test_simple_workflow.yaml +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tests/cassettes/test_prompt_management/test_prompt_management.yaml +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tests/cassettes/test_sdk_initialization/test_resource_attributes.yaml +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tests/cassettes/test_tasks/test_task_io_serialization_with_langchain.yaml +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tests/cassettes/test_workflows/test_simple_aworkflow.yaml +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tests/cassettes/test_workflows/test_simple_workflow.yaml +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tests/cassettes/test_workflows/test_streaming_workflow.yaml +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tests/conftest.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tests/test_association_properties.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tests/test_manual.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tests/test_nested_tasks.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tests/test_privacy_no_prompts.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tests/test_sdk_initialization.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tests/test_tasks.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tests/test_workflows.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tracing/__init__.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tracing/content_allow_list.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tracing/context_manager.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/tracing/manual.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/utils/__init__.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/utils/in_memory_span_exporter.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/utils/json_encoder.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/utils/package_check.py +0 -0
- {lmnr-0.4.10 → lmnr-0.4.12b1}/src/lmnr/traceloop_sdk/version.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.12b1
|
4
4
|
Summary: Python SDK for Laminar AI
|
5
5
|
License: Apache-2.0
|
6
6
|
Author: lmnr.ai
|
@@ -11,6 +11,7 @@ Classifier: Programming Language :: Python :: 3.9
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.10
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
14
|
+
Requires-Dist: argparse (>=1.0,<2.0)
|
14
15
|
Requires-Dist: asyncio (>=3.0,<4.0)
|
15
16
|
Requires-Dist: backoff (>=2.0,<3.0)
|
16
17
|
Requires-Dist: colorama (>=0.4,<0.5)
|
@@ -54,6 +55,7 @@ Requires-Dist: pydantic (>=2.7,<3.0)
|
|
54
55
|
Requires-Dist: python-dotenv (>=1.0,<2.0)
|
55
56
|
Requires-Dist: requests (>=2.0,<3.0)
|
56
57
|
Requires-Dist: tenacity (>=8.0,<9.0)
|
58
|
+
Requires-Dist: tqdm (>=4.0,<5.0)
|
57
59
|
Description-Content-Type: text/markdown
|
58
60
|
|
59
61
|
# Laminar Python
|
@@ -67,6 +69,9 @@ OpenTelemetry log sender for [Laminar](https://github.com/lmnr-ai/lmnr) for Pyth
|
|
67
69
|
|
68
70
|
|
69
71
|
## Quickstart
|
72
|
+
|
73
|
+
First, install the package:
|
74
|
+
|
70
75
|
```sh
|
71
76
|
python3 -m venv .myenv
|
72
77
|
source .myenv/bin/activate # or use your favorite env management tool
|
@@ -74,22 +79,39 @@ source .myenv/bin/activate # or use your favorite env management tool
|
|
74
79
|
pip install lmnr
|
75
80
|
```
|
76
81
|
|
77
|
-
|
82
|
+
Then, you can initialize Laminar in your main file and instrument your code.
|
78
83
|
|
79
84
|
```python
|
80
|
-
|
85
|
+
import os
|
86
|
+
from openai import OpenAI
|
87
|
+
from lmnr import Laminar as L
|
81
88
|
|
82
|
-
L.initialize(
|
83
|
-
|
89
|
+
L.initialize(
|
90
|
+
project_api_key=os.environ["LMNR_PROJECT_API_KEY"],
|
91
|
+
)
|
84
92
|
|
85
|
-
|
86
|
-
|
93
|
+
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
|
94
|
+
|
95
|
+
def poem_writer(topic: str):
|
96
|
+
prompt = f"write a poem about {topic}"
|
97
|
+
|
98
|
+
# OpenAI calls are automatically instrumented
|
99
|
+
response = client.chat.completions.create(
|
100
|
+
model="gpt-4o",
|
101
|
+
messages=[
|
102
|
+
{"role": "system", "content": "You are a helpful assistant."},
|
103
|
+
{"role": "user", "content": prompt},
|
104
|
+
],
|
105
|
+
)
|
106
|
+
poem = response.choices[0].message.content
|
107
|
+
return poem
|
87
108
|
|
88
|
-
|
89
|
-
|
109
|
+
if __name__ == "__main__":
|
110
|
+
print(poem_writer("laminar flow"))
|
111
|
+
|
112
|
+
```
|
90
113
|
|
91
|
-
|
92
|
-
by TraceLoop. Also, we are grateful to Traceloop for implementing autoinstrumentations for many libraries.
|
114
|
+
Note that you need to only initialize Laminar once in your application.
|
93
115
|
|
94
116
|
### Project API key
|
95
117
|
|
@@ -98,67 +120,84 @@ You can either pass it to `.initialize()` or set it to `.env` at the root of you
|
|
98
120
|
|
99
121
|
## Instrumentation
|
100
122
|
|
101
|
-
|
102
|
-
or to trace other functions.
|
123
|
+
### Manual instrumentation
|
103
124
|
|
104
|
-
|
125
|
+
To instrument any function in your code, we provide a simple `@observe()` decorator.
|
126
|
+
This can be useful if you want to trace a request handler or a function which combines multiple LLM calls.
|
105
127
|
|
106
128
|
```python
|
107
129
|
import os
|
108
130
|
from openai import OpenAI
|
131
|
+
from lmnr import Laminar as L, Instruments
|
109
132
|
|
110
|
-
|
111
|
-
from lmnr import observe, Laminar as L, Instruments
|
112
|
-
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>", instruments={Instruments.OPENAI})
|
133
|
+
L.initialize(project_api_key=os.environ["LMNR_PROJECT_API_KEY"])
|
113
134
|
|
114
135
|
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
|
115
136
|
|
116
|
-
|
117
|
-
def poem_writer(topic="turbulence"):
|
137
|
+
def poem_writer(topic: str):
|
118
138
|
prompt = f"write a poem about {topic}"
|
139
|
+
messages = [
|
140
|
+
{"role": "system", "content": "You are a helpful assistant."},
|
141
|
+
{"role": "user", "content": prompt},
|
142
|
+
]
|
143
|
+
|
144
|
+
# OpenAI calls are still automatically instrumented
|
119
145
|
response = client.chat.completions.create(
|
120
146
|
model="gpt-4o",
|
121
|
-
messages=
|
122
|
-
{"role": "system", "content": "You are a helpful assistant."},
|
123
|
-
{"role": "user", "content": prompt},
|
124
|
-
],
|
147
|
+
messages=messages,
|
125
148
|
)
|
126
149
|
poem = response.choices[0].message.content
|
150
|
+
|
127
151
|
return poem
|
128
152
|
|
129
|
-
|
153
|
+
@observe()
|
154
|
+
def generate_poems():
|
155
|
+
poem1 = poem_writer(topic="laminar flow")
|
156
|
+
L.event("is_poem_generated", True)
|
157
|
+
poem2 = poem_writer(topic="turbulence")
|
158
|
+
L.event("is_poem_generated", True)
|
159
|
+
poems = f"{poem1}\n\n---\n\n{poem2}"
|
160
|
+
return poems
|
130
161
|
```
|
131
162
|
|
132
|
-
|
133
|
-
|
134
|
-
Also, you can `Laminar.start_as_current_span` if you want to record a chunk of your code.
|
163
|
+
Also, you can use `Laminar.start_as_current_span` if you want to record a chunk of your code using `with` statement.
|
135
164
|
|
136
165
|
```python
|
137
|
-
|
138
|
-
L.
|
166
|
+
def handle_user_request(topic: str):
|
167
|
+
with L.start_as_current_span(name="poem_writer", input=topic):
|
168
|
+
...
|
169
|
+
|
170
|
+
poem = poem_writer(topic=topic)
|
171
|
+
|
172
|
+
...
|
173
|
+
|
174
|
+
# while within the span, you can attach laminar events to it
|
175
|
+
L.event("is_poem_generated", True)
|
139
176
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
{"role": "system", "content": "You are a helpful assistant."},
|
144
|
-
{"role": "user", "content": prompt},
|
145
|
-
]
|
177
|
+
# Use set_span_output to record the output of the span
|
178
|
+
L.set_span_output(poem)
|
179
|
+
```
|
146
180
|
|
147
|
-
|
148
|
-
# OpenAI calls are still automatically instrumented with OpenLLMetry
|
149
|
-
response = client.chat.completions.create(
|
150
|
-
model="gpt-4o",
|
151
|
-
messages=messages,
|
152
|
-
)
|
153
|
-
poem = response.choices[0].message.content
|
154
|
-
# while within the span, you can attach laminar events to it
|
155
|
-
L.event("event_name", "event_value")
|
181
|
+
### Automatic instrumentation
|
156
182
|
|
157
|
-
|
183
|
+
Laminar allows you to automatically instrument majority of the most popular LLM, Vector DB, database, requests, and other libraries.
|
158
184
|
|
159
|
-
|
185
|
+
If you want to automatically instrument a default set of libraries, then simply do NOT pass `instruments` argument to `.initialize()`.
|
186
|
+
See the full list of available instrumentations in the [enum](/src/lmnr/traceloop_sdk/instruments.py).
|
187
|
+
|
188
|
+
If you want to automatically instrument only specific LLM, Vector DB, or other
|
189
|
+
calls with OpenTelemetry-compatible instrumentation, then pass the appropriate instruments to `.initialize()`.
|
190
|
+
For example, if you want to only instrument OpenAI and Anthropic, then do the following:
|
191
|
+
|
192
|
+
```python
|
193
|
+
from lmnr import Laminar as L, Instruments
|
194
|
+
|
195
|
+
L.initialize(project_api_key=os.environ["LMNR_PROJECT_API_KEY"], instruments={Instruments.OPENAI, Instruments.ANTHROPIC})
|
160
196
|
```
|
161
197
|
|
198
|
+
If you want to fully disable any kind of autoinstrumentation, pass an empty set as `instruments=set()` to `.initialize()`.
|
199
|
+
|
200
|
+
Majority of the autoinstrumentations are provided by Traceloop's [OpenLLMetry](https://github.com/traceloop/openllmetry).
|
162
201
|
|
163
202
|
## Sending events
|
164
203
|
|
@@ -9,6 +9,9 @@ OpenTelemetry log sender for [Laminar](https://github.com/lmnr-ai/lmnr) for Pyth
|
|
9
9
|
|
10
10
|
|
11
11
|
## Quickstart
|
12
|
+
|
13
|
+
First, install the package:
|
14
|
+
|
12
15
|
```sh
|
13
16
|
python3 -m venv .myenv
|
14
17
|
source .myenv/bin/activate # or use your favorite env management tool
|
@@ -16,22 +19,39 @@ source .myenv/bin/activate # or use your favorite env management tool
|
|
16
19
|
pip install lmnr
|
17
20
|
```
|
18
21
|
|
19
|
-
|
22
|
+
Then, you can initialize Laminar in your main file and instrument your code.
|
20
23
|
|
21
24
|
```python
|
22
|
-
|
25
|
+
import os
|
26
|
+
from openai import OpenAI
|
27
|
+
from lmnr import Laminar as L
|
23
28
|
|
24
|
-
L.initialize(
|
25
|
-
|
29
|
+
L.initialize(
|
30
|
+
project_api_key=os.environ["LMNR_PROJECT_API_KEY"],
|
31
|
+
)
|
26
32
|
|
27
|
-
|
28
|
-
|
33
|
+
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
|
34
|
+
|
35
|
+
def poem_writer(topic: str):
|
36
|
+
prompt = f"write a poem about {topic}"
|
37
|
+
|
38
|
+
# OpenAI calls are automatically instrumented
|
39
|
+
response = client.chat.completions.create(
|
40
|
+
model="gpt-4o",
|
41
|
+
messages=[
|
42
|
+
{"role": "system", "content": "You are a helpful assistant."},
|
43
|
+
{"role": "user", "content": prompt},
|
44
|
+
],
|
45
|
+
)
|
46
|
+
poem = response.choices[0].message.content
|
47
|
+
return poem
|
29
48
|
|
30
|
-
|
31
|
-
|
49
|
+
if __name__ == "__main__":
|
50
|
+
print(poem_writer("laminar flow"))
|
51
|
+
|
52
|
+
```
|
32
53
|
|
33
|
-
|
34
|
-
by TraceLoop. Also, we are grateful to Traceloop for implementing autoinstrumentations for many libraries.
|
54
|
+
Note that you need to only initialize Laminar once in your application.
|
35
55
|
|
36
56
|
### Project API key
|
37
57
|
|
@@ -40,67 +60,84 @@ You can either pass it to `.initialize()` or set it to `.env` at the root of you
|
|
40
60
|
|
41
61
|
## Instrumentation
|
42
62
|
|
43
|
-
|
44
|
-
or to trace other functions.
|
63
|
+
### Manual instrumentation
|
45
64
|
|
46
|
-
|
65
|
+
To instrument any function in your code, we provide a simple `@observe()` decorator.
|
66
|
+
This can be useful if you want to trace a request handler or a function which combines multiple LLM calls.
|
47
67
|
|
48
68
|
```python
|
49
69
|
import os
|
50
70
|
from openai import OpenAI
|
71
|
+
from lmnr import Laminar as L, Instruments
|
51
72
|
|
52
|
-
|
53
|
-
from lmnr import observe, Laminar as L, Instruments
|
54
|
-
L.initialize(project_api_key="<LMNR_PROJECT_API_KEY>", instruments={Instruments.OPENAI})
|
73
|
+
L.initialize(project_api_key=os.environ["LMNR_PROJECT_API_KEY"])
|
55
74
|
|
56
75
|
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
|
57
76
|
|
58
|
-
|
59
|
-
def poem_writer(topic="turbulence"):
|
77
|
+
def poem_writer(topic: str):
|
60
78
|
prompt = f"write a poem about {topic}"
|
79
|
+
messages = [
|
80
|
+
{"role": "system", "content": "You are a helpful assistant."},
|
81
|
+
{"role": "user", "content": prompt},
|
82
|
+
]
|
83
|
+
|
84
|
+
# OpenAI calls are still automatically instrumented
|
61
85
|
response = client.chat.completions.create(
|
62
86
|
model="gpt-4o",
|
63
|
-
messages=
|
64
|
-
{"role": "system", "content": "You are a helpful assistant."},
|
65
|
-
{"role": "user", "content": prompt},
|
66
|
-
],
|
87
|
+
messages=messages,
|
67
88
|
)
|
68
89
|
poem = response.choices[0].message.content
|
90
|
+
|
69
91
|
return poem
|
70
92
|
|
71
|
-
|
93
|
+
@observe()
|
94
|
+
def generate_poems():
|
95
|
+
poem1 = poem_writer(topic="laminar flow")
|
96
|
+
L.event("is_poem_generated", True)
|
97
|
+
poem2 = poem_writer(topic="turbulence")
|
98
|
+
L.event("is_poem_generated", True)
|
99
|
+
poems = f"{poem1}\n\n---\n\n{poem2}"
|
100
|
+
return poems
|
72
101
|
```
|
73
102
|
|
74
|
-
|
75
|
-
|
76
|
-
Also, you can `Laminar.start_as_current_span` if you want to record a chunk of your code.
|
103
|
+
Also, you can use `Laminar.start_as_current_span` if you want to record a chunk of your code using `with` statement.
|
77
104
|
|
78
105
|
```python
|
79
|
-
|
80
|
-
L.
|
106
|
+
def handle_user_request(topic: str):
|
107
|
+
with L.start_as_current_span(name="poem_writer", input=topic):
|
108
|
+
...
|
109
|
+
|
110
|
+
poem = poem_writer(topic=topic)
|
111
|
+
|
112
|
+
...
|
113
|
+
|
114
|
+
# while within the span, you can attach laminar events to it
|
115
|
+
L.event("is_poem_generated", True)
|
81
116
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
{"role": "system", "content": "You are a helpful assistant."},
|
86
|
-
{"role": "user", "content": prompt},
|
87
|
-
]
|
117
|
+
# Use set_span_output to record the output of the span
|
118
|
+
L.set_span_output(poem)
|
119
|
+
```
|
88
120
|
|
89
|
-
|
90
|
-
# OpenAI calls are still automatically instrumented with OpenLLMetry
|
91
|
-
response = client.chat.completions.create(
|
92
|
-
model="gpt-4o",
|
93
|
-
messages=messages,
|
94
|
-
)
|
95
|
-
poem = response.choices[0].message.content
|
96
|
-
# while within the span, you can attach laminar events to it
|
97
|
-
L.event("event_name", "event_value")
|
121
|
+
### Automatic instrumentation
|
98
122
|
|
99
|
-
|
123
|
+
Laminar allows you to automatically instrument majority of the most popular LLM, Vector DB, database, requests, and other libraries.
|
100
124
|
|
101
|
-
|
125
|
+
If you want to automatically instrument a default set of libraries, then simply do NOT pass `instruments` argument to `.initialize()`.
|
126
|
+
See the full list of available instrumentations in the [enum](/src/lmnr/traceloop_sdk/instruments.py).
|
127
|
+
|
128
|
+
If you want to automatically instrument only specific LLM, Vector DB, or other
|
129
|
+
calls with OpenTelemetry-compatible instrumentation, then pass the appropriate instruments to `.initialize()`.
|
130
|
+
For example, if you want to only instrument OpenAI and Anthropic, then do the following:
|
131
|
+
|
132
|
+
```python
|
133
|
+
from lmnr import Laminar as L, Instruments
|
134
|
+
|
135
|
+
L.initialize(project_api_key=os.environ["LMNR_PROJECT_API_KEY"], instruments={Instruments.OPENAI, Instruments.ANTHROPIC})
|
102
136
|
```
|
103
137
|
|
138
|
+
If you want to fully disable any kind of autoinstrumentation, pass an empty set as `instruments=set()` to `.initialize()`.
|
139
|
+
|
140
|
+
Majority of the autoinstrumentations are provided by Traceloop's [OpenLLMetry](https://github.com/traceloop/openllmetry).
|
104
141
|
|
105
142
|
## Sending events
|
106
143
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "lmnr"
|
3
|
-
version = "0.4.
|
3
|
+
version = "0.4.12b1"
|
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.12b1"
|
15
15
|
description = "Python SDK for Laminar AI"
|
16
16
|
authors = ["lmnr.ai"]
|
17
17
|
readme = "README.md"
|
@@ -62,6 +62,8 @@ opentelemetry-instrumentation-weaviate = "^0.30.0"
|
|
62
62
|
opentelemetry-instrumentation-alephalpha = "^0.30.0"
|
63
63
|
opentelemetry-instrumentation-marqo = "^0.30.0"
|
64
64
|
opentelemetry-instrumentation-groq = "^0.30.0"
|
65
|
+
tqdm = "~=4.0"
|
66
|
+
argparse = "~=1.0"
|
65
67
|
|
66
68
|
[tool.poetry.group.dev.dependencies]
|
67
69
|
autopep8 = "^2.2.0"
|
@@ -83,11 +85,8 @@ langchain-openai = "^0.1.15"
|
|
83
85
|
requires = ["poetry-core"]
|
84
86
|
build-backend = "poetry.core.masonry.api"
|
85
87
|
|
86
|
-
[project.entry-points.console_scripts]
|
87
|
-
lmnr = "lmnr.cli.cli:cli"
|
88
|
-
|
89
88
|
[tool.poetry.scripts]
|
90
|
-
lmnr = "lmnr.cli
|
89
|
+
lmnr = "lmnr.cli:cli"
|
91
90
|
|
92
91
|
[project.optional-dependencies]
|
93
92
|
test = ["pytest"]
|
@@ -0,0 +1,39 @@
|
|
1
|
+
from argparse import ArgumentParser
|
2
|
+
import asyncio
|
3
|
+
import importlib
|
4
|
+
import os
|
5
|
+
import sys
|
6
|
+
|
7
|
+
from lmnr.sdk.evaluations import set_global_evaluation
|
8
|
+
|
9
|
+
|
10
|
+
# TODO: Refactor this code
|
11
|
+
async def run_evaluation(args):
|
12
|
+
sys.path.insert(0, os.getcwd())
|
13
|
+
|
14
|
+
with set_global_evaluation(True):
|
15
|
+
file = os.path.abspath(args.file)
|
16
|
+
|
17
|
+
spec = importlib.util.spec_from_file_location("run_eval", file)
|
18
|
+
mod = importlib.util.module_from_spec(spec)
|
19
|
+
spec.loader.exec_module(mod)
|
20
|
+
|
21
|
+
from lmnr.sdk.evaluations import _evaluation
|
22
|
+
evaluation = _evaluation
|
23
|
+
await evaluation.run()
|
24
|
+
|
25
|
+
|
26
|
+
def cli():
|
27
|
+
parser = ArgumentParser(
|
28
|
+
prog="lmnr",
|
29
|
+
description="CLI for Laminar",
|
30
|
+
)
|
31
|
+
|
32
|
+
subparsers = parser.add_subparsers(title="subcommands", dest="subcommand")
|
33
|
+
|
34
|
+
parser_eval = subparsers.add_parser("eval", description="Run an evaluation")
|
35
|
+
parser_eval.add_argument("file", help="A file containing the evaluation to run")
|
36
|
+
parser_eval.set_defaults(func=run_evaluation)
|
37
|
+
|
38
|
+
parsed = parser.parse_args()
|
39
|
+
asyncio.run(parsed.func(parsed))
|
@@ -4,22 +4,19 @@ from lmnr.traceloop_sdk.decorators.base import (
|
|
4
4
|
)
|
5
5
|
from opentelemetry.trace import INVALID_SPAN, get_current_span
|
6
6
|
|
7
|
-
from typing import Callable, Optional,
|
7
|
+
from typing import Callable, Optional, cast
|
8
8
|
|
9
9
|
from lmnr.traceloop_sdk.tracing.tracing import update_association_properties
|
10
10
|
|
11
11
|
from .utils import is_async
|
12
12
|
|
13
|
-
P = ParamSpec("P")
|
14
|
-
R = TypeVar("R")
|
15
|
-
|
16
13
|
|
17
14
|
def observe(
|
18
15
|
*,
|
19
16
|
name: Optional[str] = None,
|
20
17
|
user_id: Optional[str] = None,
|
21
18
|
session_id: Optional[str] = None,
|
22
|
-
) -> Callable[[Callable
|
19
|
+
) -> Callable[[Callable], Callable]:
|
23
20
|
"""The main decorator entrypoint for Laminar. This is used to wrap
|
24
21
|
functions and methods to create spans.
|
25
22
|
|
@@ -41,7 +38,7 @@ def observe(
|
|
41
38
|
R: Returns the result of the wrapped function
|
42
39
|
"""
|
43
40
|
|
44
|
-
def decorator(func: Callable
|
41
|
+
def decorator(func: Callable) -> Callable:
|
45
42
|
current_span = get_current_span()
|
46
43
|
if current_span != INVALID_SPAN:
|
47
44
|
if session_id is not None:
|
@@ -64,4 +61,4 @@ def observe(
|
|
64
61
|
else entity_method(name=name)(func)
|
65
62
|
)
|
66
63
|
|
67
|
-
return cast(Callable
|
64
|
+
return cast(Callable, decorator)
|