lmnr 0.4.22__py3-none-any.whl → 0.4.23__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
@@ -3,3 +3,4 @@ from .sdk.laminar import Laminar
3
3
  from .sdk.types import ChatMessage, PipelineRunError, PipelineRunResponse, NodeInput
4
4
  from .sdk.decorators import observe
5
5
  from .traceloop_sdk import Instruments
6
+ from .traceloop_sdk.tracing.attributes import Attributes
lmnr/sdk/laminar.py CHANGED
@@ -9,11 +9,13 @@ from opentelemetry.util.types import AttributeValue
9
9
  from opentelemetry.context import set_value, attach, detach
10
10
  from lmnr.traceloop_sdk import Traceloop
11
11
  from lmnr.traceloop_sdk.tracing import get_tracer
12
+ from lmnr.traceloop_sdk.tracing.attributes import Attributes, SPAN_TYPE
13
+ from lmnr.traceloop_sdk.decorators.base import json_dumps
12
14
  from contextlib import contextmanager
13
15
  from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
14
16
 
15
17
  from pydantic.alias_generators import to_snake
16
- from typing import Any, Optional, Set, Union
18
+ from typing import Any, Literal, Optional, Set, Union
17
19
 
18
20
  import copy
19
21
  import datetime
@@ -279,6 +281,7 @@ class Laminar:
279
281
  cls,
280
282
  name: str,
281
283
  input: Any = None,
284
+ span_type: Union[Literal["DEFAULT"], Literal["LLM"]] = "DEFAULT",
282
285
  ):
283
286
  """Start a new span as the current span. Useful for manual
284
287
  instrumentation.
@@ -287,6 +290,7 @@ class Laminar:
287
290
  ```python
288
291
  with Laminar.start_as_current_span("my_span", input="my_input") as span:
289
292
  await my_async_function()
293
+ Laminar.set_span_output("my_output")`
290
294
  ```
291
295
 
292
296
  Args:
@@ -308,6 +312,7 @@ class Laminar:
308
312
  SPAN_INPUT,
309
313
  json.dumps(input),
310
314
  )
315
+ span.set_attribute(SPAN_TYPE, span_type)
311
316
  yield span
312
317
 
313
318
  # TODO: Figure out if this is necessary
@@ -327,7 +332,36 @@ class Laminar:
327
332
  """
328
333
  span = get_current_span()
329
334
  if output is not None and span != INVALID_SPAN:
330
- span.set_attribute(SPAN_OUTPUT, json.dumps(output))
335
+ span.set_attribute(SPAN_OUTPUT, json_dumps(output))
336
+
337
+ @classmethod
338
+ def set_span_attributes(
339
+ cls,
340
+ attributes: dict[Attributes, Any],
341
+ ):
342
+ """Set attributes for the current span. Useful for manual
343
+ instrumentation.
344
+
345
+ Args:
346
+ attributes (dict[ATTRIBUTES, Any]): attributes to set for the span
347
+ """
348
+ span = get_current_span()
349
+ if span == INVALID_SPAN:
350
+ return
351
+
352
+ for key, value in attributes.items():
353
+ # Python 3.12+ should do: if key not in Attributes:
354
+ try:
355
+ Attributes(key.value)
356
+ except (TypeError, AttributeError):
357
+ cls.__logger.warning(
358
+ f"Attribute {key} is not a valid Laminar attribute."
359
+ )
360
+ continue
361
+ if not isinstance(value, (str, int, float, bool)):
362
+ span.set_attribute(key.value, json_dumps(value))
363
+ else:
364
+ span.set_attribute(key.value, value)
331
365
 
332
366
  @classmethod
333
367
  def set_session(
@@ -1,6 +1,7 @@
1
1
  import json
2
2
  from functools import wraps
3
3
  import os
4
+ import pydantic
4
5
  import types
5
6
  from typing import Any, Optional
6
7
  import warnings
@@ -17,13 +18,15 @@ from lmnr.traceloop_sdk.utils.json_encoder import JSONEncoder
17
18
 
18
19
  class CustomJSONEncoder(JSONEncoder):
19
20
  def default(self, o: Any) -> Any:
21
+ if isinstance(o, pydantic.BaseModel):
22
+ return o.model_dump_json()
20
23
  try:
21
24
  return super().default(o)
22
25
  except TypeError:
23
26
  return str(o) # Fallback to string representation for unsupported types
24
27
 
25
28
 
26
- def _json_dumps(data: dict) -> str:
29
+ def json_dumps(data: dict) -> str:
27
30
  try:
28
31
  with warnings.catch_warnings():
29
32
  warnings.simplefilter("ignore", RuntimeWarning)
@@ -59,7 +62,7 @@ def entity_method(
59
62
  if _should_send_prompts():
60
63
  span.set_attribute(
61
64
  SPAN_INPUT,
62
- _json_dumps(
65
+ json_dumps(
63
66
  get_input_from_func_args(
64
67
  fn, is_method(fn), args, kwargs
65
68
  )
@@ -78,7 +81,7 @@ def entity_method(
78
81
  if _should_send_prompts():
79
82
  span.set_attribute(
80
83
  SPAN_OUTPUT,
81
- _json_dumps(res),
84
+ json_dumps(res),
82
85
  )
83
86
  except TypeError:
84
87
  pass
@@ -121,7 +124,7 @@ def aentity_method(
121
124
  if _should_send_prompts():
122
125
  span.set_attribute(
123
126
  SPAN_INPUT,
124
- _json_dumps(
127
+ json_dumps(
125
128
  get_input_from_func_args(
126
129
  fn, is_method(fn), args, kwargs
127
130
  )
@@ -1,3 +1,6 @@
1
+ from enum import Enum
2
+ from opentelemetry.semconv_ai import SpanAttributes
3
+
1
4
  SPAN_INPUT = "lmnr.span.input"
2
5
  SPAN_OUTPUT = "lmnr.span.output"
3
6
  SPAN_TYPE = "lmnr.span.type"
@@ -7,3 +10,14 @@ ASSOCIATION_PROPERTIES = "lmnr.association.properties"
7
10
  SESSION_ID = "session_id"
8
11
  USER_ID = "user_id"
9
12
  TRACE_TYPE = "trace_type"
13
+
14
+
15
+ # exposed to the user, configurable
16
+ class Attributes(Enum):
17
+ # not SpanAttributes.LLM_USAGE_PROMPT_TOKENS,
18
+ INPUT_TOKEN_COUNT = "gen_ai.usage.input_tokens"
19
+ # not SpanAttributes.LLM_USAGE_COMPLETION_TOKENS,
20
+ OUTPUT_TOKEN_COUNT = "gen_ai.usage.output_tokens"
21
+ PROVIDER = SpanAttributes.LLM_SYSTEM
22
+ REQUEST_MODEL = SpanAttributes.LLM_REQUEST_MODEL
23
+ RESPONSE_MODEL = SpanAttributes.LLM_RESPONSE_MODEL
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lmnr
3
- Version: 0.4.22
3
+ Version: 0.4.23
4
4
  Summary: Python SDK for Laminar AI
5
5
  License: Apache-2.0
6
6
  Author: lmnr.ai
@@ -1,9 +1,9 @@
1
- lmnr/__init__.py,sha256=5Ks8UIicCzCBgwSz0MOX3I7jVruPMUO3SmxIwUoODzQ,231
1
+ lmnr/__init__.py,sha256=-MdXpUkDNhqJaQ5AVdBohIDQgGmeov_xwwtwErwXnQg,288
2
2
  lmnr/cli.py,sha256=Ptvm5dsNLKUY5lwnN8XkT5GtCYjzpRNi2WvefknB3OQ,1079
3
3
  lmnr/sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  lmnr/sdk/decorators.py,sha256=ZSDaEZyjo-RUzRCltsNbe6x0t9SKl2xRQ2q4uaKvXtk,2250
5
5
  lmnr/sdk/evaluations.py,sha256=C2lhrevH3PDvnTX-qfop1k-NTqTFX4MWaSkc9oG5kxc,15199
6
- lmnr/sdk/laminar.py,sha256=lPSNZ_sz6oeMLZcJ1u3hAr3wb1Ha7mj5zkNtMY7pYUY,15013
6
+ lmnr/sdk/laminar.py,sha256=IoQ_4DJGysKn7tEFZk3ILdRWGxmHvDPzxzXgNZifyws,16293
7
7
  lmnr/sdk/log.py,sha256=EgAMY77Zn1bv1imCqrmflD3imoAJ2yveOkIcrIP3e98,1170
8
8
  lmnr/sdk/types.py,sha256=HvaZEqVRduCZbkF7Cp8rgS5oBbc1qPvOD3PP9tFrRu4,4826
9
9
  lmnr/sdk/utils.py,sha256=s81p6uJehgJSaLWy3sR5fTpEDH7vzn3i_UujUHChl6M,3346
@@ -12,7 +12,7 @@ lmnr/traceloop_sdk/.python-version,sha256=9OLQBQVbD4zE4cJsPePhnAfV_snrPSoqEQw-PX
12
12
  lmnr/traceloop_sdk/__init__.py,sha256=hp3q1OsFaGgaQCEanJrL38BJN32hWqCNVCSjYpndEsY,2957
13
13
  lmnr/traceloop_sdk/config/__init__.py,sha256=DliMGp2NjYAqRFLKpWQPUKjGMHRO8QsVfazBA1qENQ8,248
14
14
  lmnr/traceloop_sdk/decorators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- lmnr/traceloop_sdk/decorators/base.py,sha256=-b8Q738m3StdLTgHARx8zw78m9htynKkZFFTYURQnOA,5524
15
+ lmnr/traceloop_sdk/decorators/base.py,sha256=ZkvzVJfO4fOxRHwPobPL2ryW32nZjgO7cX7CXouyUnY,5621
16
16
  lmnr/traceloop_sdk/instruments.py,sha256=oMvIASueW3GeChpjIdH-DD9aFBVB8OtHZ0HawppTrlI,942
17
17
  lmnr/traceloop_sdk/tests/__init__.py,sha256=RYnG0-8zbXL0-2Ste1mEBf5sN4d_rQjGTCgPBuaZC74,20
18
18
  lmnr/traceloop_sdk/tests/cassettes/test_association_properties/test_langchain_and_external_association_properties.yaml,sha256=26g0wRA0juicHg_XrhcE8H4vhs1lawDs0o0aLFn-I7w,3103
@@ -35,7 +35,7 @@ lmnr/traceloop_sdk/tests/test_sdk_initialization.py,sha256=fRaf6lrxFzJIN94P1Tav_
35
35
  lmnr/traceloop_sdk/tests/test_tasks.py,sha256=xlEx8BKp4yG83SCjK5WkPGfyC33JSrx4h8VyjVwGbgw,906
36
36
  lmnr/traceloop_sdk/tests/test_workflows.py,sha256=RVcfY3WAFIDZC15-aSua21aoQyYeWE7KypDyUsm-2EM,9372
37
37
  lmnr/traceloop_sdk/tracing/__init__.py,sha256=Ckq7zCM26VdJVB5tIZv0GTPyMZKyfso_KWD5yPHaqdo,66
38
- lmnr/traceloop_sdk/tracing/attributes.py,sha256=PXwS1GCZKdjQSypl__BSkQNZhh21RyzwTPnDOh61bnQ,250
38
+ lmnr/traceloop_sdk/tracing/attributes.py,sha256=YBxDZ-9I9QlnehcCIUQcpeHTtv4gPU3vDtct7SqQ06Q,746
39
39
  lmnr/traceloop_sdk/tracing/content_allow_list.py,sha256=3feztm6PBWNelc8pAZUcQyEGyeSpNiVKjOaDk65l2ps,846
40
40
  lmnr/traceloop_sdk/tracing/context_manager.py,sha256=csVlB6kDmbgSPsROHwnddvGGblx55v6lJMRj0wsSMQM,304
41
41
  lmnr/traceloop_sdk/tracing/tracing.py,sha256=_HDLuyy4XgobC1ig4qz5jYbB4tWAZSfD6gbgUqwmYJU,35522
@@ -44,8 +44,8 @@ lmnr/traceloop_sdk/utils/in_memory_span_exporter.py,sha256=H_4TRaThMO1H6vUQ0OpQv
44
44
  lmnr/traceloop_sdk/utils/json_encoder.py,sha256=dK6b_axr70IYL7Vv-bu4wntvDDuyntoqsHaddqX7P58,463
45
45
  lmnr/traceloop_sdk/utils/package_check.py,sha256=TZSngzJOpFhfUZLXIs38cpMxQiZSmp0D-sCrIyhz7BA,251
46
46
  lmnr/traceloop_sdk/version.py,sha256=OlatFEFA4ttqSSIiV8jdE-sq3KG5zu2hnC4B4mzWF3s,23
47
- lmnr-0.4.22.dist-info/LICENSE,sha256=67b_wJHVV1CBaWkrKFWU1wyqTPSdzH77Ls-59631COg,10411
48
- lmnr-0.4.22.dist-info/METADATA,sha256=Jl8q-VclTacQSBZwpynNzfeaOkB_wuPUVzvgrv7S8xw,10493
49
- lmnr-0.4.22.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
50
- lmnr-0.4.22.dist-info/entry_points.txt,sha256=K1jE20ww4jzHNZLnsfWBvU3YKDGBgbOiYG5Y7ivQcq4,37
51
- lmnr-0.4.22.dist-info/RECORD,,
47
+ lmnr-0.4.23.dist-info/LICENSE,sha256=67b_wJHVV1CBaWkrKFWU1wyqTPSdzH77Ls-59631COg,10411
48
+ lmnr-0.4.23.dist-info/METADATA,sha256=BOAKiIyn49ReowUuPFpmqRYhgiQlK6CUSEEipTreYbo,10493
49
+ lmnr-0.4.23.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
50
+ lmnr-0.4.23.dist-info/entry_points.txt,sha256=K1jE20ww4jzHNZLnsfWBvU3YKDGBgbOiYG5Y7ivQcq4,37
51
+ lmnr-0.4.23.dist-info/RECORD,,
File without changes
File without changes