lmnr 0.4.2__py3-none-any.whl → 0.4.4__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/sdk/evaluations.py +56 -41
- lmnr/sdk/laminar.py +12 -5
- {lmnr-0.4.2.dist-info → lmnr-0.4.4.dist-info}/METADATA +1 -1
- {lmnr-0.4.2.dist-info → lmnr-0.4.4.dist-info}/RECORD +7 -7
- {lmnr-0.4.2.dist-info → lmnr-0.4.4.dist-info}/LICENSE +0 -0
- {lmnr-0.4.2.dist-info → lmnr-0.4.4.dist-info}/WHEEL +0 -0
- {lmnr-0.4.2.dist-info → lmnr-0.4.4.dist-info}/entry_points.txt +0 -0
lmnr/sdk/evaluations.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
from typing import Union
|
2
2
|
|
3
|
+
from .types import EvaluatorFunction, ExecutorFunction, EvaluationDatapoint
|
3
4
|
from .utils import is_async
|
4
|
-
from .types import EvaluatorFunction, ExecutorFunction, EvaluationDatapoint, Numeric
|
5
5
|
from .laminar import Laminar as L
|
6
6
|
import asyncio
|
7
7
|
|
@@ -95,7 +95,7 @@ class Evaluation:
|
|
95
95
|
self.batch_size = batch_size
|
96
96
|
L.initialize(project_api_key=project_api_key, base_url=base_url)
|
97
97
|
|
98
|
-
|
98
|
+
def run(self):
|
99
99
|
"""Runs the evaluation.
|
100
100
|
|
101
101
|
Creates a new evaluation if no evaluation with such name exists, or
|
@@ -103,61 +103,76 @@ class Evaluation:
|
|
103
103
|
batches of `self.batch_size`. The executor
|
104
104
|
function is called on each data point to get the output,
|
105
105
|
and then evaluate it by each evaluator function.
|
106
|
+
|
107
|
+
Usage:
|
108
|
+
```python
|
109
|
+
# in a synchronous context:
|
110
|
+
e.run()
|
111
|
+
# in an asynchronous context:
|
112
|
+
await e.run()
|
113
|
+
```
|
114
|
+
|
106
115
|
"""
|
116
|
+
loop = asyncio.get_event_loop()
|
117
|
+
if loop.is_running():
|
118
|
+
return loop.create_task(self._run())
|
119
|
+
else:
|
120
|
+
return loop.run_until_complete(self._run())
|
121
|
+
|
122
|
+
async def _run(self):
|
107
123
|
response = L.create_evaluation(self.name)
|
108
|
-
batch_promises = []
|
109
124
|
|
125
|
+
# Process batches sequentially
|
110
126
|
for i in range(0, len(self.data), self.batch_size):
|
111
127
|
batch = (
|
112
128
|
self.data[i : i + self.batch_size]
|
113
129
|
if isinstance(self.data, list)
|
114
130
|
else self.data.slice(i, i + self.batch_size)
|
115
131
|
)
|
116
|
-
|
132
|
+
try:
|
133
|
+
await self._evaluate_batch(batch)
|
134
|
+
except Exception as e:
|
135
|
+
print(f"Error evaluating batch: {e}")
|
117
136
|
|
118
137
|
try:
|
119
|
-
await asyncio.gather(*batch_promises)
|
120
138
|
L.update_evaluation_status(response.name, "Finished")
|
121
139
|
print(f"Evaluation {response.id} complete")
|
122
140
|
except Exception as e:
|
123
|
-
print(f"Error
|
141
|
+
print(f"Error updating evaluation status: {e}")
|
124
142
|
|
125
143
|
async def _evaluate_batch(self, batch: list[EvaluationDatapoint]):
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
if is_async(self.executor)
|
131
|
-
else self.executor(datapoint.data)
|
132
|
-
)
|
133
|
-
target = datapoint.target
|
134
|
-
|
135
|
-
# iterate in order of evaluators
|
136
|
-
scores = {}
|
137
|
-
for evaluator_name in self.evaluator_names:
|
138
|
-
evaluator = self.evaluators[evaluator_name]
|
139
|
-
value = (
|
140
|
-
await evaluator(output, target)
|
141
|
-
if is_async(evaluator)
|
142
|
-
else evaluator(output, target)
|
143
|
-
)
|
144
|
+
batch_promises = [self._evaluate_datapoint(datapoint) for datapoint in batch]
|
145
|
+
results = await asyncio.gather(*batch_promises)
|
146
|
+
|
147
|
+
return L.post_evaluation_results(self.name, results)
|
144
148
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
149
|
+
async def _evaluate_datapoint(self, datapoint):
|
150
|
+
output = (
|
151
|
+
await self.executor(datapoint.data)
|
152
|
+
if is_async(self.executor)
|
153
|
+
else self.executor(datapoint.data)
|
154
|
+
)
|
155
|
+
target = datapoint.target
|
156
|
+
|
157
|
+
# Iterate over evaluators
|
158
|
+
scores = {}
|
159
|
+
for evaluator_name in self.evaluator_names:
|
160
|
+
evaluator = self.evaluators[evaluator_name]
|
161
|
+
value = (
|
162
|
+
await evaluator(output, target)
|
163
|
+
if is_async(evaluator)
|
164
|
+
else evaluator(output, target)
|
161
165
|
)
|
162
166
|
|
163
|
-
|
167
|
+
# If evaluator returns a single number, use evaluator name as key
|
168
|
+
if isinstance(value, (int, float)):
|
169
|
+
scores[evaluator_name] = value
|
170
|
+
else:
|
171
|
+
scores.update(value)
|
172
|
+
|
173
|
+
return {
|
174
|
+
"executorOutput": output,
|
175
|
+
"data": datapoint.data,
|
176
|
+
"target": target,
|
177
|
+
"scores": scores,
|
178
|
+
}
|
lmnr/sdk/laminar.py
CHANGED
@@ -201,15 +201,21 @@ class Laminar:
|
|
201
201
|
def event(
|
202
202
|
cls,
|
203
203
|
name: str,
|
204
|
-
value: AttributeValue,
|
204
|
+
value: Optional[AttributeValue] = None,
|
205
205
|
timestamp: Optional[Union[datetime.datetime, int]] = None,
|
206
206
|
):
|
207
|
-
"""Associate an event with the current span
|
207
|
+
"""Associate an event with the current span. If event with such name never
|
208
|
+
existed, Laminar will create a new event and infer its type from the value.
|
209
|
+
If the event already exists, Laminar will append the value to the event
|
210
|
+
if and only if the value is of a matching type. Otherwise, the event won't
|
211
|
+
be recorded Supported types are string, numeric, and boolean. If the value
|
212
|
+
is `None`, event is considered a boolean tag with the value of `True`.
|
208
213
|
|
209
214
|
Args:
|
210
215
|
name (str): event name
|
211
|
-
value (AttributeValue): event value. Must be a primitive type
|
212
|
-
|
216
|
+
value (Optional[AttributeValue]): event value. Must be a primitive type.
|
217
|
+
Boolean true is assumed in the backend if value is None.
|
218
|
+
Defaults to None.
|
213
219
|
timestamp (Optional[Union[datetime.datetime, int]], optional):
|
214
220
|
If int, must be epoch nanoseconds. If not
|
215
221
|
specified, relies on the underlying OpenTelemetry
|
@@ -220,8 +226,9 @@ class Laminar:
|
|
220
226
|
|
221
227
|
event = {
|
222
228
|
"lmnr.event.type": "default",
|
223
|
-
"lmnr.event.value": value,
|
224
229
|
}
|
230
|
+
if value is not None:
|
231
|
+
event["lmnr.event.value"] = value
|
225
232
|
|
226
233
|
current_span = get_current_span()
|
227
234
|
if current_span == INVALID_SPAN:
|
@@ -1,13 +1,13 @@
|
|
1
1
|
lmnr/__init__.py,sha256=wQwnHl662Xcz7GdSofFsEjmAK0nxioYA2Yq6Q78m4ps,194
|
2
2
|
lmnr/sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
lmnr/sdk/decorators.py,sha256=Xs6n0TGX9LZ9i1hE_UZz4LEyd_ZAfpVGfNQh_rKwOuA,2493
|
4
|
-
lmnr/sdk/evaluations.py,sha256=
|
5
|
-
lmnr/sdk/laminar.py,sha256=
|
4
|
+
lmnr/sdk/evaluations.py,sha256=Z0j2HyXgrwlGyiT_Ql7W3e_ZWjOlNlIj9RWAKjEgkkE,6366
|
5
|
+
lmnr/sdk/laminar.py,sha256=DQ6EvuYK0elERNDcGyKyMmHrkEJZT66-vgBmCJ8fv4Q,17077
|
6
6
|
lmnr/sdk/log.py,sha256=EgAMY77Zn1bv1imCqrmflD3imoAJ2yveOkIcrIP3e98,1170
|
7
7
|
lmnr/sdk/types.py,sha256=gDwRSWR9A1__FGtQhVaFc6PUYQuIhubo5tpfYAajTQQ,4055
|
8
8
|
lmnr/sdk/utils.py,sha256=ZsGJ86tq8lIbvOhSb1gJWH5K3GylO_lgX68FN6rG2nM,3358
|
9
|
-
lmnr-0.4.
|
10
|
-
lmnr-0.4.
|
11
|
-
lmnr-0.4.
|
12
|
-
lmnr-0.4.
|
13
|
-
lmnr-0.4.
|
9
|
+
lmnr-0.4.4.dist-info/LICENSE,sha256=67b_wJHVV1CBaWkrKFWU1wyqTPSdzH77Ls-59631COg,10411
|
10
|
+
lmnr-0.4.4.dist-info/METADATA,sha256=gBmo93OeKl55jRIEJ4Fz8bvb1CTZzWVfCp3HQ8BAKHw,7025
|
11
|
+
lmnr-0.4.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
12
|
+
lmnr-0.4.4.dist-info/entry_points.txt,sha256=Qg7ZRax4k-rcQsZ26XRYQ8YFSBiyY2PNxYfq4a6PYXI,41
|
13
|
+
lmnr-0.4.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|