lmnr 0.4.33__tar.gz → 0.4.35__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.
Files changed (52) hide show
  1. {lmnr-0.4.33 → lmnr-0.4.35}/PKG-INFO +1 -1
  2. {lmnr-0.4.33 → lmnr-0.4.35}/pyproject.toml +2 -2
  3. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/sdk/laminar.py +80 -5
  4. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tracing/tracing.py +32 -19
  5. lmnr-0.4.35/src/lmnr/traceloop_sdk/utils/package_check.py +6 -0
  6. lmnr-0.4.33/src/lmnr/traceloop_sdk/utils/package_check.py +0 -8
  7. {lmnr-0.4.33 → lmnr-0.4.35}/LICENSE +0 -0
  8. {lmnr-0.4.33 → lmnr-0.4.35}/README.md +0 -0
  9. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/__init__.py +0 -0
  10. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/cli.py +0 -0
  11. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/sdk/__init__.py +0 -0
  12. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/sdk/datasets.py +0 -0
  13. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/sdk/decorators.py +0 -0
  14. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/sdk/evaluations.py +0 -0
  15. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/sdk/log.py +0 -0
  16. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/sdk/types.py +0 -0
  17. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/sdk/utils.py +0 -0
  18. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/.flake8 +0 -0
  19. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/.python-version +0 -0
  20. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/__init__.py +0 -0
  21. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/config/__init__.py +0 -0
  22. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/decorators/__init__.py +0 -0
  23. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/decorators/base.py +0 -0
  24. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/instruments.py +0 -0
  25. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tests/__init__.py +0 -0
  26. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tests/cassettes/test_association_properties/test_langchain_and_external_association_properties.yaml +0 -0
  27. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tests/cassettes/test_association_properties/test_langchain_association_properties.yaml +0 -0
  28. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tests/cassettes/test_manual/test_manual_report.yaml +0 -0
  29. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tests/cassettes/test_manual/test_resource_attributes.yaml +0 -0
  30. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tests/cassettes/test_privacy_no_prompts/test_simple_workflow.yaml +0 -0
  31. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tests/cassettes/test_prompt_management/test_prompt_management.yaml +0 -0
  32. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tests/cassettes/test_sdk_initialization/test_resource_attributes.yaml +0 -0
  33. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tests/cassettes/test_tasks/test_task_io_serialization_with_langchain.yaml +0 -0
  34. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tests/cassettes/test_workflows/test_simple_aworkflow.yaml +0 -0
  35. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tests/cassettes/test_workflows/test_simple_workflow.yaml +0 -0
  36. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tests/cassettes/test_workflows/test_streaming_workflow.yaml +0 -0
  37. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tests/conftest.py +0 -0
  38. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tests/test_association_properties.py +0 -0
  39. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tests/test_manual.py +0 -0
  40. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tests/test_nested_tasks.py +0 -0
  41. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tests/test_privacy_no_prompts.py +0 -0
  42. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tests/test_sdk_initialization.py +0 -0
  43. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tests/test_tasks.py +0 -0
  44. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tests/test_workflows.py +0 -0
  45. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tracing/__init__.py +0 -0
  46. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tracing/attributes.py +0 -0
  47. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tracing/content_allow_list.py +0 -0
  48. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/tracing/context_manager.py +0 -0
  49. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/utils/__init__.py +0 -0
  50. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/utils/in_memory_span_exporter.py +0 -0
  51. {lmnr-0.4.33 → lmnr-0.4.35}/src/lmnr/traceloop_sdk/utils/json_encoder.py +0 -0
  52. {lmnr-0.4.33 → lmnr-0.4.35}/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.33
3
+ Version: 0.4.35
4
4
  Summary: Python SDK for Laminar AI
5
5
  License: Apache-2.0
6
6
  Author: lmnr.ai
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "lmnr"
3
- version = "0.4.33"
3
+ version = "0.4.35"
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.33"
14
+ version = "0.4.35"
15
15
  description = "Python SDK for Laminar AI"
16
16
  authors = ["lmnr.ai"]
17
17
  readme = "README.md"
@@ -1,18 +1,19 @@
1
1
  from contextlib import contextmanager
2
2
  from contextvars import Context
3
- from opentelemetry import context, trace
4
- from opentelemetry.util.types import AttributeValue
5
- from opentelemetry.context import set_value, attach, detach
6
3
  from lmnr.traceloop_sdk import Traceloop
7
4
  from lmnr.traceloop_sdk.instruments import Instruments
8
5
  from lmnr.traceloop_sdk.tracing import get_tracer
9
6
  from lmnr.traceloop_sdk.tracing.attributes import (
7
+ ASSOCIATION_PROPERTIES,
10
8
  Attributes,
11
9
  SPAN_TYPE,
12
10
  OVERRIDE_PARENT_SPAN,
13
11
  )
14
12
  from lmnr.traceloop_sdk.decorators.base import json_dumps
13
+ from opentelemetry import context, trace
14
+ from opentelemetry.context import attach, detach, set_value
15
15
  from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
16
+ from opentelemetry.util.types import AttributeValue
16
17
 
17
18
  from pydantic.alias_generators import to_snake
18
19
  from typing import Any, Literal, Optional, Set, Union
@@ -39,6 +40,7 @@ from lmnr.traceloop_sdk.tracing.attributes import (
39
40
  )
40
41
  from lmnr.traceloop_sdk.tracing.tracing import (
41
42
  get_span_path,
43
+ remove_association_properties,
42
44
  set_association_properties,
43
45
  update_association_properties,
44
46
  )
@@ -298,6 +300,7 @@ class Laminar:
298
300
  span_type: Union[Literal["DEFAULT"], Literal["LLM"]] = "DEFAULT",
299
301
  context: Optional[Context] = None,
300
302
  trace_id: Optional[uuid.UUID] = None,
303
+ labels: Optional[dict[str, str]] = None,
301
304
  ):
302
305
  """Start a new span as the current span. Useful for manual
303
306
  instrumentation. If `span_type` is set to `"LLM"`, you should report
@@ -323,6 +326,8 @@ class Laminar:
323
326
  trace_id (Optional[uuid.UUID], optional): [EXPERIMENTAL] override\
324
327
  the trace id for the span. If not provided, use the current\
325
328
  trace id. Defaults to None.
329
+ labels (Optional[dict[str, str]], optional): labels to set for the\
330
+ span. Defaults to None.
326
331
  """
327
332
 
328
333
  with get_tracer() as tracer:
@@ -345,10 +350,26 @@ class Laminar:
345
350
  " is not a valid UUID"
346
351
  )
347
352
  ctx_token = attach(ctx)
353
+ label_props = {}
354
+ try:
355
+ if labels:
356
+ label_props = dict(
357
+ (f"{ASSOCIATION_PROPERTIES}.label.{k}", json_dumps(v))
358
+ for k, v in labels.items() # noqa: F821
359
+ )
360
+ except Exception:
361
+ cls.__logger.warning(
362
+ f"`start_as_current_span` Could not set labels: {labels}. "
363
+ "They will be propagated to the next span."
364
+ )
348
365
  with tracer.start_as_current_span(
349
366
  name,
350
367
  context=ctx,
351
- attributes={SPAN_PATH: span_path, SPAN_TYPE: span_type},
368
+ attributes={
369
+ SPAN_PATH: span_path,
370
+ SPAN_TYPE: span_type,
371
+ **(label_props),
372
+ },
352
373
  ) as span:
353
374
  if trace_id is not None and isinstance(trace_id, uuid.UUID):
354
375
  span.set_attribute(OVERRIDE_PARENT_SPAN, True)
@@ -365,6 +386,41 @@ class Laminar:
365
386
  except Exception:
366
387
  pass
367
388
 
389
+ @classmethod
390
+ @contextmanager
391
+ def with_labels(cls, labels: dict[str, str], context: Optional[Context] = None):
392
+ """Set labels for spans within this `with` context. This is useful for
393
+ adding labels to the spans created in the auto-instrumentations.
394
+
395
+ Requirements:
396
+ - Labels must be created in your project in advance.
397
+ - Keys must be strings from your label names.
398
+ - Values must be strings matching the label's allowed values.
399
+
400
+ Usage example:
401
+ ```python
402
+ with Laminar.with_labels({"sentiment": "positive"}):
403
+ openai_client.chat.completions.create()
404
+ ```
405
+ """
406
+ with get_tracer():
407
+ label_props = labels.copy()
408
+ label_props = dict(
409
+ (f"label.{k}", json_dumps(v)) for k, v in label_props.items()
410
+ )
411
+ update_association_properties(
412
+ label_props, set_on_current_span=False, context=context
413
+ )
414
+ yield
415
+ try:
416
+ remove_association_properties(label_props)
417
+ except Exception:
418
+ cls.__logger.warning(
419
+ f"`with_labels` Could not remove labels: {labels}. They will be "
420
+ "propagated to the next span."
421
+ )
422
+ pass
423
+
368
424
  @classmethod
369
425
  def start_span(
370
426
  cls,
@@ -373,6 +429,7 @@ class Laminar:
373
429
  span_type: Union[Literal["DEFAULT"], Literal["LLM"]] = "DEFAULT",
374
430
  context: Optional[Context] = None,
375
431
  trace_id: Optional[uuid.UUID] = None,
432
+ labels: Optional[dict[str, str]] = None,
376
433
  ):
377
434
  """Start a new span. Useful for manual instrumentation.
378
435
  If `span_type` is set to `"LLM"`, you should report usage and response
@@ -417,6 +474,8 @@ class Laminar:
417
474
  trace_id (Optional[uuid.UUID], optional): [EXPERIMENTAL] override\
418
475
  the trace id for the span. If not provided, use the current\
419
476
  trace id. Defaults to None.
477
+ labels (Optional[dict[str, str]], optional): labels to set for the\
478
+ span. Defaults to None.
420
479
  """
421
480
  with get_tracer() as tracer:
422
481
  span_path = get_span_path(name)
@@ -437,10 +496,26 @@ class Laminar:
437
496
  "trace_id provided to `Laminar.start_span`"
438
497
  " is not a valid UUID"
439
498
  )
499
+ label_props = {}
500
+ try:
501
+ if labels:
502
+ label_props = dict(
503
+ (f"{ASSOCIATION_PROPERTIES}.label.{k}", json_dumps(v))
504
+ for k, v in labels.items() # noqa: F821
505
+ )
506
+ except Exception:
507
+ cls.__logger.warning(
508
+ f"`start_span` Could not set labels: {labels}. They will be "
509
+ "propagated to the next span."
510
+ )
440
511
  span = tracer.start_span(
441
512
  name,
442
513
  context=ctx,
443
- attributes={SPAN_PATH: span_path, SPAN_TYPE: span_type},
514
+ attributes={
515
+ SPAN_PATH: span_path,
516
+ SPAN_TYPE: span_type,
517
+ **(label_props),
518
+ },
444
519
  )
445
520
  if trace_id is not None and isinstance(trace_id, uuid.UUID):
446
521
  span.set_attribute(OVERRIDE_PARENT_SPAN, True)
@@ -1,8 +1,18 @@
1
1
  import atexit
2
+ import copy
2
3
  import logging
3
- import os
4
4
 
5
5
 
6
+ from contextvars import Context
7
+ from lmnr.traceloop_sdk.instruments import Instruments
8
+ from lmnr.traceloop_sdk.tracing.attributes import (
9
+ ASSOCIATION_PROPERTIES,
10
+ SPAN_INSTRUMENTATION_SOURCE,
11
+ SPAN_PATH,
12
+ )
13
+ from lmnr.traceloop_sdk.tracing.content_allow_list import ContentAllowList
14
+ from lmnr.traceloop_sdk.utils import is_notebook
15
+ from lmnr.traceloop_sdk.utils.package_check import is_package_installed
6
16
  from opentelemetry import trace
7
17
  from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
8
18
  OTLPSpanExporter as HTTPExporter,
@@ -10,28 +20,19 @@ from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
10
20
  from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
11
21
  OTLPSpanExporter as GRPCExporter,
12
22
  )
23
+ from opentelemetry.instrumentation.threading import ThreadingInstrumentor
24
+ from opentelemetry.context import get_value, attach, set_value
25
+ from opentelemetry.propagate import set_global_textmap
26
+ from opentelemetry.propagators.textmap import TextMapPropagator
13
27
  from opentelemetry.sdk.resources import Resource
14
28
  from opentelemetry.sdk.trace import TracerProvider, SpanProcessor
15
- from opentelemetry.propagators.textmap import TextMapPropagator
16
- from opentelemetry.propagate import set_global_textmap
17
29
  from opentelemetry.sdk.trace.export import (
18
30
  SpanExporter,
19
31
  SimpleSpanProcessor,
20
32
  BatchSpanProcessor,
21
33
  )
22
34
  from opentelemetry.trace import get_tracer_provider, ProxyTracerProvider
23
- from opentelemetry.context import get_value, attach, set_value
24
- from opentelemetry.instrumentation.threading import ThreadingInstrumentor
25
35
 
26
- from lmnr.traceloop_sdk.instruments import Instruments
27
- from lmnr.traceloop_sdk.tracing.attributes import (
28
- ASSOCIATION_PROPERTIES,
29
- SPAN_INSTRUMENTATION_SOURCE,
30
- SPAN_PATH,
31
- )
32
- from lmnr.traceloop_sdk.tracing.content_allow_list import ContentAllowList
33
- from lmnr.traceloop_sdk.utils import is_notebook
34
- from lmnr.traceloop_sdk.utils.package_check import is_package_installed
35
36
  from typing import Dict, Optional, Set
36
37
 
37
38
 
@@ -188,15 +189,27 @@ def set_association_properties(properties: dict) -> None:
188
189
  _set_association_properties_attributes(span, properties)
189
190
 
190
191
 
191
- def update_association_properties(properties: dict) -> None:
192
+ def update_association_properties(
193
+ properties: dict,
194
+ set_on_current_span: bool = True,
195
+ context: Optional[Context] = None,
196
+ ) -> None:
192
197
  """Only adds or updates properties that are not already present"""
193
- association_properties = get_value("association_properties") or {}
198
+ association_properties = get_value("association_properties", context) or {}
194
199
  association_properties.update(properties)
195
200
 
196
- attach(set_value("association_properties", association_properties))
201
+ attach(set_value("association_properties", association_properties, context))
197
202
 
198
- span = trace.get_current_span()
199
- _set_association_properties_attributes(span, properties)
203
+ if set_on_current_span:
204
+ span = trace.get_current_span()
205
+ _set_association_properties_attributes(span, properties)
206
+
207
+
208
+ def remove_association_properties(properties: dict) -> None:
209
+ props: dict = copy.copy(get_value("association_properties") or {})
210
+ for k in properties.keys():
211
+ props.pop(k, None)
212
+ set_association_properties(props)
200
213
 
201
214
 
202
215
  def _set_association_properties_attributes(span, properties: dict) -> None:
@@ -0,0 +1,6 @@
1
+ from importlib.metadata import distributions
2
+
3
+ installed_packages = {dist.metadata["Name"].lower() for dist in distributions()}
4
+
5
+ def is_package_installed(package_name: str) -> bool:
6
+ return package_name.lower() in installed_packages
@@ -1,8 +0,0 @@
1
- import pkg_resources
2
-
3
- installed_packages = {p.key for p in pkg_resources.working_set}
4
-
5
-
6
- def is_package_installed(package_name: str) -> bool:
7
- # return importlib.util.find_spec(package_name) is not None
8
- return package_name in installed_packages
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes