raindrop-ai 0.0.29__py3-none-any.whl → 0.0.31__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.
raindrop/analytics.py CHANGED
@@ -29,8 +29,18 @@ import weakref
29
29
  import urllib.parse
30
30
 
31
31
  from traceloop.sdk import Traceloop
32
- from traceloop.sdk.tracing.tracing import TracerWrapper
32
+ from traceloop.sdk.tracing.tracing import (
33
+ TracerWrapper,
34
+ get_chained_entity_path,
35
+ set_entity_path,
36
+ )
33
37
  from opentelemetry.trace import get_current_span
38
+ from opentelemetry import trace
39
+ from opentelemetry import context as context_api
40
+ from opentelemetry.semconv_ai import SpanAttributes
41
+ from opentelemetry.trace.status import Status, StatusCode
42
+ from traceloop.sdk.utils.json_encoder import JSONEncoder
43
+ from traceloop.sdk.tracing.context_manager import get_tracer
34
44
  from traceloop.sdk.decorators import (
35
45
  task as tlp_task,
36
46
  workflow as tlp_workflow,
@@ -38,6 +48,26 @@ from traceloop.sdk.decorators import (
38
48
  F,
39
49
  )
40
50
 
51
+ __all__ = [
52
+ # Configuration functions
53
+ "set_debug_logs",
54
+ "set_redact_pii",
55
+ "init",
56
+ "identify",
57
+ "track_ai",
58
+ "track_signal",
59
+ "begin",
60
+ "resume_interaction",
61
+ "interaction",
62
+ "task",
63
+ "tool",
64
+ "task_span",
65
+ "tool_span",
66
+ "set_span_properties",
67
+ "flush",
68
+ "shutdown",
69
+ ]
70
+
41
71
 
42
72
  # Configure logging
43
73
  logging.basicConfig(
@@ -293,6 +323,28 @@ def _get_size(event: dict[str, any]) -> int:
293
323
  return 0
294
324
 
295
325
 
326
+ def _truncate_json_if_needed(json_str: str) -> str:
327
+ """
328
+ Truncate JSON string if it exceeds OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT;
329
+ truncation may yield an invalid JSON string, which is expected for logging purposes.
330
+ """
331
+ limit_str = os.getenv("OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT")
332
+ if limit_str:
333
+ try:
334
+ limit = int(limit_str)
335
+ if limit > 0 and len(json_str) > limit:
336
+ return json_str[:limit]
337
+ except ValueError:
338
+ pass
339
+ return json_str
340
+
341
+
342
+ def _should_send_prompts():
343
+ return (
344
+ os.getenv("TRACELOOP_TRACE_CONTENT") or "true"
345
+ ).lower() == "true" or context_api.get_value("override_enable_content_tracing")
346
+
347
+
296
348
  # Signal types - This is now defined in models.py
297
349
  # SignalType = Literal["default", "feedback", "edit"]
298
350
 
@@ -552,6 +604,110 @@ def set_span_properties(properties: Dict[str, Any]) -> None:
552
604
  Traceloop.set_association_properties(properties)
553
605
 
554
606
 
607
+ class TraceEntitySpan:
608
+ def __init__(self, span):
609
+ self._span = span
610
+
611
+ def record_input(self, data: Any) -> None:
612
+ if self._span and _should_send_prompts():
613
+ try:
614
+ json_input = json.dumps({"args": [data]}, cls=JSONEncoder)
615
+ truncated = _truncate_json_if_needed(json_input)
616
+ self._span.set_attribute(
617
+ SpanAttributes.TRACELOOP_ENTITY_INPUT, truncated
618
+ )
619
+ except TypeError as e:
620
+ logger.debug(f"[raindrop] Could not serialize input for span: {e}")
621
+
622
+ def record_output(self, data: Any) -> None:
623
+ if self._span and _should_send_prompts():
624
+ try:
625
+ json_output = json.dumps(data, cls=JSONEncoder)
626
+ truncated = _truncate_json_if_needed(json_output)
627
+ self._span.set_attribute(
628
+ SpanAttributes.TRACELOOP_ENTITY_OUTPUT, truncated
629
+ )
630
+ except TypeError as e:
631
+ logger.debug(f"[raindrop] Could not serialize output for span: {e}")
632
+
633
+ def set_properties(self, props: Dict[str, Any]) -> None:
634
+ if _tracing_enabled and props:
635
+ Traceloop.set_association_properties(props)
636
+
637
+
638
+ class _EntitySpanContext:
639
+ def __init__(self, kind: Literal["task", "tool"], name: str, version: int | None):
640
+ self._kind = kind
641
+ self._name = name
642
+ self._version = version
643
+ self._span = None
644
+ self._ctx_token = None
645
+ self._span_cm = None
646
+ self._helper = TraceEntitySpan(None)
647
+
648
+ # internal start/finish
649
+ def _start(self) -> None:
650
+ if not _tracing_enabled or not TracerWrapper.verify_initialized():
651
+ return
652
+ tlp_kind = (
653
+ TraceloopSpanKindValues.TASK
654
+ if self._kind == "task"
655
+ else TraceloopSpanKindValues.TOOL
656
+ )
657
+ span_name = f"{self._name}.{tlp_kind.value}"
658
+ with get_tracer() as tracer:
659
+ self._span_cm = tracer.start_as_current_span(span_name)
660
+ span = self._span_cm.__enter__()
661
+
662
+ if tlp_kind in [TraceloopSpanKindValues.TASK, TraceloopSpanKindValues.TOOL]:
663
+ entity_path = get_chained_entity_path(self._name)
664
+ set_entity_path(entity_path)
665
+
666
+ span.set_attribute(SpanAttributes.TRACELOOP_SPAN_KIND, tlp_kind.value)
667
+ span.set_attribute(SpanAttributes.TRACELOOP_ENTITY_NAME, self._name)
668
+ if self._version is not None:
669
+ span.set_attribute(SpanAttributes.TRACELOOP_ENTITY_VERSION, self._version)
670
+
671
+ self._span = span
672
+ self._helper = TraceEntitySpan(span)
673
+
674
+ def _end(self, exc_type, exc, tb) -> bool:
675
+ if not self._span:
676
+ return False
677
+ try:
678
+ if exc is not None:
679
+ self._span.set_status(Status(StatusCode.ERROR, str(exc)))
680
+ self._span.record_exception(exc)
681
+ return False
682
+ finally:
683
+ if self._span_cm is not None:
684
+ self._span_cm.__exit__(exc_type, exc, tb)
685
+
686
+ # sync
687
+ def __enter__(self) -> TraceEntitySpan:
688
+ self._start()
689
+ return self._helper
690
+
691
+ def __exit__(self, exc_type, exc, tb) -> bool:
692
+ return self._end(exc_type, exc, tb)
693
+
694
+ # async
695
+ async def __aenter__(self) -> TraceEntitySpan:
696
+ self._start()
697
+ return self._helper
698
+
699
+ async def __aexit__(self, exc_type, exc, tb) -> bool:
700
+ return self._end(exc_type, exc, tb)
701
+
702
+
703
+ def task_span(name: str, version: int | None = None) -> _EntitySpanContext:
704
+ return _EntitySpanContext("task", name, version)
705
+
706
+
707
+ def tool_span(name: str, version: int | None = None) -> _EntitySpanContext:
708
+ return _EntitySpanContext("tool", name, version)
709
+
710
+
555
711
  def resume_interaction(event_id: str | None = None) -> Interaction:
556
712
  """Return an Interaction associated with the current trace or given event_id."""
557
713
 
raindrop/version.py CHANGED
@@ -1 +1 @@
1
- VERSION = "0.0.29"
1
+ VERSION = "0.0.31"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: raindrop-ai
3
- Version: 0.0.29
3
+ Version: 0.0.31
4
4
  Summary: Raindrop AI (Python SDK)
5
5
  License: MIT
6
6
  Author: Raindrop AI
@@ -1,10 +1,10 @@
1
1
  raindrop/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- raindrop/analytics.py,sha256=L6CrwfngxGJOw-05FPPytC3sANaHccmlw82j8ajrf6c,19240
2
+ raindrop/analytics.py,sha256=XHCXHM-S92ZAgsigPrDovUDTSlHiLLfuIHHEq9wnpTY,24354
3
3
  raindrop/interaction.py,sha256=1gYFj2-oEobLtG56RZw5de3RjYr0pCXg_NqYDGx32Uc,1896
4
4
  raindrop/models.py,sha256=dEhwTtBEgwOOW_qaPEYugCLeOqvDXuBpEfi7vUANVK0,5361
5
5
  raindrop/redact.py,sha256=rMNUoI90KxOY3d_zcHAr0TFD2yQ_CDgpDz-1XJLVmHs,7658
6
- raindrop/version.py,sha256=ZIBhRcpTwwJfP6TpTO4noMPlluF8zyhxOYd3p_p0zNI,19
6
+ raindrop/version.py,sha256=g9PK7-EZQg9Xj85paKedV-EguQ1hpWMe5uKllKCG_QI,19
7
7
  raindrop/well-known-names.json,sha256=9giJF6u6W1R0APW-Pf1dvNUU32OXQEoQ9CBQXSnA3ks,144403
8
- raindrop_ai-0.0.29.dist-info/METADATA,sha256=YHcFiEkQETKv7xi5imYLYF5MmLi5lFCXsQQisxfswao,1269
9
- raindrop_ai-0.0.29.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
10
- raindrop_ai-0.0.29.dist-info/RECORD,,
8
+ raindrop_ai-0.0.31.dist-info/METADATA,sha256=1ChjDAUZRU0R2kjK9RpwveWVefChGoRwm-bhdn1cctg,1269
9
+ raindrop_ai-0.0.31.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
10
+ raindrop_ai-0.0.31.dist-info/RECORD,,