lmnr 0.4.27__py3-none-any.whl → 0.4.29b0__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 CHANGED
@@ -1,7 +1,13 @@
1
1
  from .sdk.datasets import EvaluationDataset, LaminarDataset
2
2
  from .sdk.evaluations import evaluate
3
3
  from .sdk.laminar import Laminar
4
- from .sdk.types import ChatMessage, PipelineRunError, PipelineRunResponse, NodeInput
4
+ from .sdk.types import (
5
+ ChatMessage,
6
+ HumanEvaluator,
7
+ NodeInput,
8
+ PipelineRunError,
9
+ PipelineRunResponse,
10
+ )
5
11
  from .sdk.decorators import observe
6
12
  from .traceloop_sdk import Instruments
7
13
  from .traceloop_sdk.tracing.attributes import Attributes
lmnr/sdk/datasets.py CHANGED
@@ -34,7 +34,7 @@ class LaminarDataset(EvaluationDataset):
34
34
  self._fetched_items = []
35
35
  self._offset = 0
36
36
  self._fetch_size = fetch_size
37
- self._logger = get_default_logger(self.__class__.__name__, level=logging.DEBUG)
37
+ self._logger = get_default_logger(self.__class__.__name__)
38
38
 
39
39
  def _fetch_batch(self):
40
40
  self._logger.debug(
lmnr/sdk/evaluations.py CHANGED
@@ -18,6 +18,7 @@ from .types import (
18
18
  EvaluationResultDatapoint,
19
19
  EvaluatorFunction,
20
20
  ExecutorFunction,
21
+ HumanEvaluator,
21
22
  Numeric,
22
23
  NumericTypes,
23
24
  SpanType,
@@ -99,6 +100,7 @@ class Evaluation:
99
100
  data: Union[EvaluationDataset, list[Union[Datapoint, dict]]],
100
101
  executor: Any,
101
102
  evaluators: dict[str, EvaluatorFunction],
103
+ human_evaluators: dict[str, HumanEvaluator] = {},
102
104
  name: Optional[str] = None,
103
105
  group_id: Optional[str] = None,
104
106
  batch_size: int = DEFAULT_BATCH_SIZE,
@@ -126,6 +128,11 @@ class Evaluation:
126
128
  If the score is a single number, it will be named after the\
127
129
  evaluator function. Evaluator function names must contain only\
128
130
  letters, digits, hyphens, underscores, or spaces.
131
+ human_evaluators (dict[str, HumanEvaluator], optional):\
132
+ [Beta] Dictionary from human evaluator names to instances of\
133
+ HumanEvaluator. For now, human evaluator only holds the queue\
134
+ name.
135
+ Defaults to an empty dictionary.
129
136
  name (Optional[str], optional): Optional name of the evaluation.\
130
137
  Used to identify the evaluation in the group.\
131
138
  If not provided, a random name will be generated.
@@ -159,14 +166,27 @@ class Evaluation:
159
166
  if not evaluators:
160
167
  raise ValueError("No evaluators provided")
161
168
 
162
- # TODO: Compile regex once and then reuse it
169
+ evaluator_name_regex = re.compile(r"^[\w\s-]+$")
163
170
  for evaluator_name in evaluators:
164
- if not re.match(r"^[\w\s-]+$", evaluator_name):
171
+ if not evaluator_name_regex.match(evaluator_name):
165
172
  raise ValueError(
166
173
  f'Invalid evaluator key: "{evaluator_name}". '
167
174
  "Keys must only contain letters, digits, hyphens,"
168
175
  "underscores, or spaces."
169
176
  )
177
+ for evaluator_name in human_evaluators or {}:
178
+ if not evaluator_name_regex.match(evaluator_name):
179
+ raise ValueError(
180
+ f'Invalid human evaluator key: "{evaluator_name}". '
181
+ "Keys must only contain letters, digits, hyphens,"
182
+ "underscores, or spaces."
183
+ )
184
+
185
+ if intersection := set(evaluators.keys()) & set(human_evaluators.keys()):
186
+ raise ValueError(
187
+ "Evaluator and human evaluator names must not overlap. "
188
+ f"Repeated keys: {intersection}"
189
+ )
170
190
 
171
191
  self.is_finished = False
172
192
  self.reporter = EvaluationReporter()
@@ -183,6 +203,7 @@ class Evaluation:
183
203
  self.name = name
184
204
  self.batch_size = batch_size
185
205
  self._logger = get_default_logger(self.__class__.__name__)
206
+ self.human_evaluators = human_evaluators
186
207
  L.initialize(
187
208
  project_api_key=project_api_key,
188
209
  base_url=base_url,
@@ -202,9 +223,7 @@ class Evaluation:
202
223
  return loop.run_until_complete(self._run())
203
224
 
204
225
  async def _run(self) -> None:
205
- self.reporter.start(
206
- len(self.data),
207
- )
226
+ self.reporter.start(len(self.data))
208
227
 
209
228
  try:
210
229
  result_datapoints = await self._evaluate_in_batches()
@@ -212,13 +231,19 @@ class Evaluation:
212
231
  self.reporter.stopWithError(e)
213
232
  self.is_finished = True
214
233
  return
215
- else:
216
- evaluation = L.create_evaluation(
217
- data=result_datapoints, group_id=self.group_id, name=self.name
218
- )
219
- average_scores = get_average_scores(result_datapoints)
220
- self.reporter.stop(average_scores, evaluation.projectId, evaluation.id)
221
- self.is_finished = True
234
+
235
+ # For now add all human evaluators to all result datapoints
236
+ # In the future, we will add ways to specify which human evaluators
237
+ # to add to which result datapoints, e.g. sample some randomly
238
+ for result_datapoint in result_datapoints:
239
+ result_datapoint.human_evaluators = self.human_evaluators or {}
240
+
241
+ evaluation = L.create_evaluation(
242
+ data=result_datapoints, group_id=self.group_id, name=self.name
243
+ )
244
+ average_scores = get_average_scores(result_datapoints)
245
+ self.reporter.stop(average_scores, evaluation.projectId, evaluation.id)
246
+ self.is_finished = True
222
247
 
223
248
  async def _evaluate_in_batches(self) -> list[EvaluationResultDatapoint]:
224
249
  result_datapoints = []
@@ -292,6 +317,7 @@ def evaluate(
292
317
  data: Union[EvaluationDataset, list[Union[Datapoint, dict]]],
293
318
  executor: ExecutorFunction,
294
319
  evaluators: dict[str, EvaluatorFunction],
320
+ human_evaluators: dict[str, HumanEvaluator] = {},
295
321
  name: Optional[str] = None,
296
322
  group_id: Optional[str] = None,
297
323
  batch_size: int = DEFAULT_BATCH_SIZE,
@@ -326,6 +352,10 @@ def evaluate(
326
352
  If the score is a single number, it will be named after the\
327
353
  evaluator function. Evaluator function names must contain only\
328
354
  letters, digits, hyphens, underscores, or spaces.
355
+ human_evaluators (dict[str, HumanEvaluator], optional):\
356
+ [Beta] Dictionary from human evaluator names to instances of\
357
+ HumanEvaluator. For now, human evaluator only holds the queue name.
358
+ Defaults to an empty dictionary.
329
359
  name (Optional[str], optional): Optional name of the evaluation.\
330
360
  Used to identify the evaluation in the group.\
331
361
  If not provided, a random name will be generated.
@@ -359,6 +389,7 @@ def evaluate(
359
389
  executor=executor,
360
390
  evaluators=evaluators,
361
391
  group_id=group_id,
392
+ human_evaluators=human_evaluators,
362
393
  name=name,
363
394
  batch_size=batch_size,
364
395
  project_api_key=project_api_key,
lmnr/sdk/types.py CHANGED
@@ -110,6 +110,13 @@ EvaluatorFunction = Callable[
110
110
  ]
111
111
 
112
112
 
113
+ class HumanEvaluator(pydantic.BaseModel):
114
+ queueName: str
115
+
116
+ def __init__(self, queue_name: str):
117
+ super().__init__(queueName=queue_name)
118
+
119
+
113
120
  class CreateEvaluationResponse(pydantic.BaseModel):
114
121
  id: uuid.UUID
115
122
  createdAt: datetime.datetime
@@ -123,6 +130,7 @@ class EvaluationResultDatapoint(pydantic.BaseModel):
123
130
  target: EvaluationDatapointTarget
124
131
  executor_output: ExecutorFunctionReturnType
125
132
  scores: dict[str, Numeric]
133
+ human_evaluators: dict[str, HumanEvaluator] = pydantic.Field(default_factory=dict)
126
134
  trace_id: uuid.UUID
127
135
 
128
136
  # uuid is not serializable by default, so we need to convert it to a string
@@ -139,6 +147,10 @@ class EvaluationResultDatapoint(pydantic.BaseModel):
139
147
  "executorOutput": serialize(self.executor_output),
140
148
  "scores": self.scores,
141
149
  "traceId": str(self.trace_id),
150
+ "humanEvaluators": {
151
+ k: v.model_dump() if isinstance(v, pydantic.BaseModel) else serialize(v)
152
+ for k, v in self.human_evaluators.items()
153
+ },
142
154
  }
143
155
 
144
156
 
@@ -5,6 +5,7 @@ SPAN_INPUT = "lmnr.span.input"
5
5
  SPAN_OUTPUT = "lmnr.span.output"
6
6
  SPAN_TYPE = "lmnr.span.type"
7
7
  SPAN_PATH = "lmnr.span.path"
8
+ SPAN_INSTRUMENTATION_SOURCE = "lmnr.span.instrumentation_source"
8
9
 
9
10
  ASSOCIATION_PROPERTIES = "lmnr.association.properties"
10
11
  SESSION_ID = "session_id"
@@ -25,7 +25,11 @@ from opentelemetry.instrumentation.threading import ThreadingInstrumentor
25
25
 
26
26
  # from lmnr.traceloop_sdk import Telemetry
27
27
  from lmnr.traceloop_sdk.instruments import Instruments
28
- from lmnr.traceloop_sdk.tracing.attributes import ASSOCIATION_PROPERTIES, SPAN_PATH
28
+ from lmnr.traceloop_sdk.tracing.attributes import (
29
+ ASSOCIATION_PROPERTIES,
30
+ SPAN_INSTRUMENTATION_SOURCE,
31
+ SPAN_PATH,
32
+ )
29
33
  from lmnr.traceloop_sdk.tracing.content_allow_list import ContentAllowList
30
34
  from lmnr.traceloop_sdk.utils import is_notebook
31
35
  from lmnr.traceloop_sdk.utils.package_check import is_package_installed
@@ -235,6 +239,8 @@ class TracerWrapper(object):
235
239
  # the final part of the name to the span on the backend.
236
240
  span.set_attribute(SPAN_PATH, span_path)
237
241
 
242
+ span.set_attribute(SPAN_INSTRUMENTATION_SOURCE, "python")
243
+
238
244
  association_properties = get_value("association_properties")
239
245
  if association_properties is not None:
240
246
  _set_association_properties_attributes(span, association_properties)
@@ -266,10 +272,7 @@ class TracerWrapper(object):
266
272
  if hasattr(cls, "instance"):
267
273
  return True
268
274
 
269
- if (os.getenv("TRACELOOP_SUPPRESS_WARNINGS") or "false").lower() == "true":
270
- return False
271
-
272
- print("Warning: Laminar not initialized, make sure to initialize")
275
+ logging.warning("Warning: Laminar not initialized, make sure to initialize")
273
276
  return False
274
277
 
275
278
  def flush(self):
@@ -557,7 +560,9 @@ def init_langchain_instrumentor():
557
560
  instrumentor.instrument()
558
561
  return True
559
562
  except Exception as e:
560
- logging.error(f"Error initializing LangChain instrumentor: {e}")
563
+ # FIXME: silence this error temporarily, it appears to not be critical
564
+ if str(e) != "No module named 'langchain_community'":
565
+ logging.error(f"Error initializing LangChain instrumentor: {e}")
561
566
  # Telemetry().log_exception(e)
562
567
  return False
563
568
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lmnr
3
- Version: 0.4.27
3
+ Version: 0.4.29b0
4
4
  Summary: Python SDK for Laminar AI
5
5
  License: Apache-2.0
6
6
  Author: lmnr.ai
@@ -1,12 +1,12 @@
1
- lmnr/__init__.py,sha256=87ZHKNA2xmZvqtXrWjhXUNR7YUrdP8-77fYqe-PEtrE,348
1
+ lmnr/__init__.py,sha256=qwI8S02jRm7QvXsyljuEurp-kUt8HOCAN_m9RKVQVtU,389
2
2
  lmnr/cli.py,sha256=Ptvm5dsNLKUY5lwnN8XkT5GtCYjzpRNi2WvefknB3OQ,1079
3
3
  lmnr/sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- lmnr/sdk/datasets.py,sha256=V6q0Zcv7znx5cB1YZd0pBgTEcAKXbb-pEwguScEhBw0,1612
4
+ lmnr/sdk/datasets.py,sha256=w8U9E6fvetAo65Cb2CbYzlfhY8CfXAR-VysrakG6-4I,1591
5
5
  lmnr/sdk/decorators.py,sha256=ZSDaEZyjo-RUzRCltsNbe6x0t9SKl2xRQ2q4uaKvXtk,2250
6
- lmnr/sdk/evaluations.py,sha256=vG788rSDppAGEvIpyglKvm8Ac_D5cw07a6btMeMr8AI,15124
6
+ lmnr/sdk/evaluations.py,sha256=BUdsxuh3Rjk-8oj-481geW7fqTmgLcq0CuMOYbgMFx8,16807
7
7
  lmnr/sdk/laminar.py,sha256=H87fXSWb9shcPW4AeoYwvTXJ-jSTjzm2sI1A1U1Vkg8,18780
8
8
  lmnr/sdk/log.py,sha256=cZBeUoSK39LMEV-X4-eEhTWOciULRfHaKfRK8YqIM8I,1532
9
- lmnr/sdk/types.py,sha256=6_C2LhcbI9PwlntnSuREE0FRsBUxc3WS_yC_Y_trPBI,5052
9
+ lmnr/sdk/types.py,sha256=kj-xIe3uK2WPu47RjP6xIMWnasv_SQybptkx1OVovq8,5486
10
10
  lmnr/sdk/utils.py,sha256=s81p6uJehgJSaLWy3sR5fTpEDH7vzn3i_UujUHChl6M,3346
11
11
  lmnr/traceloop_sdk/.flake8,sha256=bCxuDlGx3YQ55QHKPiGJkncHanh9qGjQJUujcFa3lAU,150
12
12
  lmnr/traceloop_sdk/.python-version,sha256=9OLQBQVbD4zE4cJsPePhnAfV_snrPSoqEQw-PXgPMOs,6
@@ -36,17 +36,17 @@ lmnr/traceloop_sdk/tests/test_sdk_initialization.py,sha256=fRaf6lrxFzJIN94P1Tav_
36
36
  lmnr/traceloop_sdk/tests/test_tasks.py,sha256=xlEx8BKp4yG83SCjK5WkPGfyC33JSrx4h8VyjVwGbgw,906
37
37
  lmnr/traceloop_sdk/tests/test_workflows.py,sha256=RVcfY3WAFIDZC15-aSua21aoQyYeWE7KypDyUsm-2EM,9372
38
38
  lmnr/traceloop_sdk/tracing/__init__.py,sha256=Ckq7zCM26VdJVB5tIZv0GTPyMZKyfso_KWD5yPHaqdo,66
39
- lmnr/traceloop_sdk/tracing/attributes.py,sha256=QzfFVhbuYnT-ym4lHocQF_e5zm5Aw8r36FTxV5Qrjlk,1136
39
+ lmnr/traceloop_sdk/tracing/attributes.py,sha256=QeqItpCCwUipkwgXG7J7swJCD0yk9uuI28aepPhemtE,1201
40
40
  lmnr/traceloop_sdk/tracing/content_allow_list.py,sha256=3feztm6PBWNelc8pAZUcQyEGyeSpNiVKjOaDk65l2ps,846
41
41
  lmnr/traceloop_sdk/tracing/context_manager.py,sha256=csVlB6kDmbgSPsROHwnddvGGblx55v6lJMRj0wsSMQM,304
42
- lmnr/traceloop_sdk/tracing/tracing.py,sha256=_HDLuyy4XgobC1ig4qz5jYbB4tWAZSfD6gbgUqwmYJU,35522
42
+ lmnr/traceloop_sdk/tracing/tracing.py,sha256=2Vyc0hUdTqXYlBzoW7YO6DlLaUKWouLv7eEvrVMZYOo,35680
43
43
  lmnr/traceloop_sdk/utils/__init__.py,sha256=pNhf0G3vTd5ccoc03i1MXDbricSaiqCbi1DLWhSekK8,604
44
44
  lmnr/traceloop_sdk/utils/in_memory_span_exporter.py,sha256=H_4TRaThMO1H6vUQ0OpQvzJk_fZH0OOsRAM1iZQXsR8,2112
45
45
  lmnr/traceloop_sdk/utils/json_encoder.py,sha256=dK6b_axr70IYL7Vv-bu4wntvDDuyntoqsHaddqX7P58,463
46
46
  lmnr/traceloop_sdk/utils/package_check.py,sha256=TZSngzJOpFhfUZLXIs38cpMxQiZSmp0D-sCrIyhz7BA,251
47
47
  lmnr/traceloop_sdk/version.py,sha256=OlatFEFA4ttqSSIiV8jdE-sq3KG5zu2hnC4B4mzWF3s,23
48
- lmnr-0.4.27.dist-info/LICENSE,sha256=67b_wJHVV1CBaWkrKFWU1wyqTPSdzH77Ls-59631COg,10411
49
- lmnr-0.4.27.dist-info/METADATA,sha256=QI26rjCwIxbUPQYfq6HquwkjmEwq0YPP8d5VNfRHJ_M,10622
50
- lmnr-0.4.27.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
51
- lmnr-0.4.27.dist-info/entry_points.txt,sha256=K1jE20ww4jzHNZLnsfWBvU3YKDGBgbOiYG5Y7ivQcq4,37
52
- lmnr-0.4.27.dist-info/RECORD,,
48
+ lmnr-0.4.29b0.dist-info/LICENSE,sha256=67b_wJHVV1CBaWkrKFWU1wyqTPSdzH77Ls-59631COg,10411
49
+ lmnr-0.4.29b0.dist-info/METADATA,sha256=ATL1IcRQIpALFB7apCO0TX0N2ve93ZxwytS61_vt_jA,10624
50
+ lmnr-0.4.29b0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
51
+ lmnr-0.4.29b0.dist-info/entry_points.txt,sha256=K1jE20ww4jzHNZLnsfWBvU3YKDGBgbOiYG5Y7ivQcq4,37
52
+ lmnr-0.4.29b0.dist-info/RECORD,,
File without changes