raindrop-ai 0.0.32__py3-none-any.whl → 0.0.34__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 +117 -1
- raindrop/interaction.py +44 -0
- raindrop/version.py +1 -1
- {raindrop_ai-0.0.32.dist-info → raindrop_ai-0.0.34.dist-info}/METADATA +2 -2
- raindrop_ai-0.0.34.dist-info/RECORD +10 -0
- raindrop_ai-0.0.32.dist-info/RECORD +0 -10
- {raindrop_ai-0.0.32.dist-info → raindrop_ai-0.0.34.dist-info}/WHEEL +0 -0
raindrop/analytics.py
CHANGED
|
@@ -63,6 +63,8 @@ __all__ = [
|
|
|
63
63
|
"tool",
|
|
64
64
|
"task_span",
|
|
65
65
|
"tool_span",
|
|
66
|
+
"start_span",
|
|
67
|
+
"ManualSpan",
|
|
66
68
|
"set_span_properties",
|
|
67
69
|
"flush",
|
|
68
70
|
"shutdown",
|
|
@@ -492,7 +494,7 @@ def begin(
|
|
|
492
494
|
{k: v for k, v in span_attributes.items() if v is not None}
|
|
493
495
|
)
|
|
494
496
|
|
|
495
|
-
interaction = Interaction(eid)
|
|
497
|
+
interaction = Interaction(eid, user_id=user_id, event=event, convo_id=convo_id)
|
|
496
498
|
INTERACTION_EVENT_ID_REGISTRY[eid] = interaction
|
|
497
499
|
if current_trace_id is not None and current_trace_id != 0:
|
|
498
500
|
INTERACTION_TRACE_ID_REGISTRY[current_trace_id] = interaction
|
|
@@ -635,6 +637,63 @@ class TraceEntitySpan:
|
|
|
635
637
|
Traceloop.set_association_properties(props)
|
|
636
638
|
|
|
637
639
|
|
|
640
|
+
class ManualSpan:
|
|
641
|
+
"""
|
|
642
|
+
A manually-controlled span for async/distributed operations.
|
|
643
|
+
Unlike context-managed spans, this requires explicit .end() calls.
|
|
644
|
+
"""
|
|
645
|
+
|
|
646
|
+
def __init__(self, span, kind: str, name: str, event_id: str | None = None):
|
|
647
|
+
self._span = span
|
|
648
|
+
self._kind = kind
|
|
649
|
+
self._name = name
|
|
650
|
+
self._event_id = event_id
|
|
651
|
+
self._ended = False
|
|
652
|
+
|
|
653
|
+
@property
|
|
654
|
+
def event_id(self) -> str | None:
|
|
655
|
+
return self._event_id
|
|
656
|
+
|
|
657
|
+
def record_input(self, data: Any) -> None:
|
|
658
|
+
if self._span and _should_send_prompts():
|
|
659
|
+
try:
|
|
660
|
+
json_input = json.dumps({"args": [data]}, cls=JSONEncoder)
|
|
661
|
+
truncated = _truncate_json_if_needed(json_input)
|
|
662
|
+
self._span.set_attribute(
|
|
663
|
+
SpanAttributes.TRACELOOP_ENTITY_INPUT, truncated
|
|
664
|
+
)
|
|
665
|
+
except TypeError as e:
|
|
666
|
+
logger.debug(f"[raindrop] Could not serialize input for span: {e}")
|
|
667
|
+
|
|
668
|
+
def record_output(self, data: Any) -> None:
|
|
669
|
+
if self._span and _should_send_prompts():
|
|
670
|
+
try:
|
|
671
|
+
json_output = json.dumps(data, cls=JSONEncoder)
|
|
672
|
+
truncated = _truncate_json_if_needed(json_output)
|
|
673
|
+
self._span.set_attribute(
|
|
674
|
+
SpanAttributes.TRACELOOP_ENTITY_OUTPUT, truncated
|
|
675
|
+
)
|
|
676
|
+
except TypeError as e:
|
|
677
|
+
logger.debug(f"[raindrop] Could not serialize output for span: {e}")
|
|
678
|
+
|
|
679
|
+
def set_properties(self, props: Dict[str, Any]) -> None:
|
|
680
|
+
if self._span and props:
|
|
681
|
+
for key, value in props.items():
|
|
682
|
+
if value is not None:
|
|
683
|
+
self._span.set_attribute(
|
|
684
|
+
f"traceloop.association.properties.{key}", value
|
|
685
|
+
)
|
|
686
|
+
|
|
687
|
+
def end(self, error: Exception | None = None) -> None:
|
|
688
|
+
if self._ended or not self._span:
|
|
689
|
+
return
|
|
690
|
+
self._ended = True
|
|
691
|
+
if error is not None:
|
|
692
|
+
self._span.set_status(Status(StatusCode.ERROR, str(error)))
|
|
693
|
+
self._span.record_exception(error)
|
|
694
|
+
self._span.end()
|
|
695
|
+
|
|
696
|
+
|
|
638
697
|
class _EntitySpanContext:
|
|
639
698
|
def __init__(self, kind: Literal["task", "tool"], name: str, version: int | None):
|
|
640
699
|
self._kind = kind
|
|
@@ -708,6 +767,63 @@ def tool_span(name: str, version: int | None = None) -> _EntitySpanContext:
|
|
|
708
767
|
return _EntitySpanContext("tool", name, version)
|
|
709
768
|
|
|
710
769
|
|
|
770
|
+
def start_span(
|
|
771
|
+
kind: Literal["task", "tool"],
|
|
772
|
+
name: str,
|
|
773
|
+
version: int | None = None,
|
|
774
|
+
event_id: str | None = None,
|
|
775
|
+
user_id: str | None = None,
|
|
776
|
+
event: str | None = None,
|
|
777
|
+
convo_id: str | None = None,
|
|
778
|
+
) -> ManualSpan:
|
|
779
|
+
"""
|
|
780
|
+
Create a manual span that must be explicitly ended with .end().
|
|
781
|
+
|
|
782
|
+
Use this for async/distributed operations where the span lifecycle
|
|
783
|
+
extends beyond a single context manager scope.
|
|
784
|
+
|
|
785
|
+
Args:
|
|
786
|
+
kind: Type of span - "task" or "tool"
|
|
787
|
+
name: Name of the span
|
|
788
|
+
version: Optional version number
|
|
789
|
+
event_id: Optional event_id for tracing association
|
|
790
|
+
user_id: Optional user_id for tracing association
|
|
791
|
+
event: Optional event name for tracing association
|
|
792
|
+
convo_id: Optional conversation ID for tracing association
|
|
793
|
+
|
|
794
|
+
Returns:
|
|
795
|
+
ManualSpan instance (safe to use even if tracing is disabled)
|
|
796
|
+
"""
|
|
797
|
+
if not _tracing_enabled or not TracerWrapper.verify_initialized():
|
|
798
|
+
return ManualSpan(None, kind, name, event_id)
|
|
799
|
+
|
|
800
|
+
tlp_kind = (
|
|
801
|
+
TraceloopSpanKindValues.TASK if kind == "task" else TraceloopSpanKindValues.TOOL
|
|
802
|
+
)
|
|
803
|
+
span_name = f"{name}.{tlp_kind.value}"
|
|
804
|
+
|
|
805
|
+
with get_tracer() as tracer:
|
|
806
|
+
span = tracer.start_span(span_name)
|
|
807
|
+
|
|
808
|
+
span.set_attribute(SpanAttributes.TRACELOOP_SPAN_KIND, tlp_kind.value)
|
|
809
|
+
span.set_attribute(SpanAttributes.TRACELOOP_ENTITY_NAME, name)
|
|
810
|
+
if version is not None:
|
|
811
|
+
span.set_attribute(SpanAttributes.TRACELOOP_ENTITY_VERSION, version)
|
|
812
|
+
|
|
813
|
+
# Set association properties directly on the span (not on current context)
|
|
814
|
+
association_props = {
|
|
815
|
+
"event_id": event_id,
|
|
816
|
+
"user_id": user_id,
|
|
817
|
+
"event": event,
|
|
818
|
+
"convo_id": convo_id,
|
|
819
|
+
}
|
|
820
|
+
for key, value in association_props.items():
|
|
821
|
+
if value is not None:
|
|
822
|
+
span.set_attribute(f"traceloop.association.properties.{key}", value)
|
|
823
|
+
|
|
824
|
+
return ManualSpan(span, kind, name, event_id)
|
|
825
|
+
|
|
826
|
+
|
|
711
827
|
def resume_interaction(event_id: str | None = None) -> Interaction:
|
|
712
828
|
"""Return an Interaction associated with the current trace or given event_id."""
|
|
713
829
|
|
raindrop/interaction.py
CHANGED
|
@@ -3,7 +3,9 @@ from typing import (
|
|
|
3
3
|
Any,
|
|
4
4
|
Dict,
|
|
5
5
|
List,
|
|
6
|
+
Literal,
|
|
6
7
|
Optional,
|
|
8
|
+
TYPE_CHECKING,
|
|
7
9
|
Union,
|
|
8
10
|
Iterator,
|
|
9
11
|
)
|
|
@@ -14,6 +16,9 @@ from .models import Attachment, PartialTrackAIEvent
|
|
|
14
16
|
from . import analytics as _core
|
|
15
17
|
from opentelemetry import context as context_api
|
|
16
18
|
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from .analytics import ManualSpan
|
|
21
|
+
|
|
17
22
|
|
|
18
23
|
class Interaction:
|
|
19
24
|
"""
|
|
@@ -23,6 +28,9 @@ class Interaction:
|
|
|
23
28
|
|
|
24
29
|
__slots__ = (
|
|
25
30
|
"_event_id",
|
|
31
|
+
"_user_id",
|
|
32
|
+
"_event",
|
|
33
|
+
"_convo_id",
|
|
26
34
|
"_analytics",
|
|
27
35
|
"__weakref__",
|
|
28
36
|
)
|
|
@@ -30,8 +38,14 @@ class Interaction:
|
|
|
30
38
|
def __init__(
|
|
31
39
|
self,
|
|
32
40
|
event_id: Optional[str] = None,
|
|
41
|
+
user_id: Optional[str] = None,
|
|
42
|
+
event: Optional[str] = None,
|
|
43
|
+
convo_id: Optional[str] = None,
|
|
33
44
|
):
|
|
34
45
|
self._event_id = event_id or str(uuid4())
|
|
46
|
+
self._user_id = user_id
|
|
47
|
+
self._event = event
|
|
48
|
+
self._convo_id = convo_id
|
|
35
49
|
self._analytics = _core
|
|
36
50
|
|
|
37
51
|
# -- mutators ----------------------------------------------------------- #
|
|
@@ -63,6 +77,36 @@ class Interaction:
|
|
|
63
77
|
)
|
|
64
78
|
self._analytics._track_ai_partial(payload)
|
|
65
79
|
|
|
80
|
+
def start_span(
|
|
81
|
+
self,
|
|
82
|
+
kind: Literal["task", "tool"],
|
|
83
|
+
name: str,
|
|
84
|
+
version: int | None = None,
|
|
85
|
+
) -> "ManualSpan":
|
|
86
|
+
"""
|
|
87
|
+
Create a manual span tied to this interaction.
|
|
88
|
+
|
|
89
|
+
The span automatically inherits association properties from this interaction
|
|
90
|
+
(event_id, user_id, event, convo_id) for proper tracing.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
kind: Type of span - "task" or "tool"
|
|
94
|
+
name: Name of the span
|
|
95
|
+
version: Optional version number
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
ManualSpan instance that must be explicitly ended with .end()
|
|
99
|
+
"""
|
|
100
|
+
return self._analytics.start_span(
|
|
101
|
+
kind,
|
|
102
|
+
name,
|
|
103
|
+
version,
|
|
104
|
+
event_id=self._event_id,
|
|
105
|
+
user_id=self._user_id,
|
|
106
|
+
event=self._event,
|
|
107
|
+
convo_id=self._convo_id,
|
|
108
|
+
)
|
|
109
|
+
|
|
66
110
|
# convenience
|
|
67
111
|
@property
|
|
68
112
|
def id(self) -> str:
|
raindrop/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
VERSION = "0.0.
|
|
1
|
+
VERSION = "0.0.34"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: raindrop-ai
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.34
|
|
4
4
|
Summary: Raindrop AI (Python SDK)
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Raindrop AI
|
|
@@ -13,7 +13,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
14
|
Requires-Dist: pydantic (>=2.09,<3)
|
|
15
15
|
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
|
16
|
-
Requires-Dist: traceloop-sdk (
|
|
16
|
+
Requires-Dist: traceloop-sdk (>=0.46.0)
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
18
|
|
|
19
19
|
# Raindrop Python SDK
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
raindrop/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
raindrop/analytics.py,sha256=uS4D8_r9VEzIVJgPDBpT7Nkr2i1PS9Yj_QCWgqGEBNM,28507
|
|
3
|
+
raindrop/interaction.py,sha256=Kw8HNaDSHVIi1p-2HK5SsYGI4Xg1zQFml4dX2EhIT7w,3123
|
|
4
|
+
raindrop/models.py,sha256=9lOOUQ2FF11RPkntuLZwN3e54pa9HtR8lGvCbzlWOPM,5198
|
|
5
|
+
raindrop/redact.py,sha256=rMNUoI90KxOY3d_zcHAr0TFD2yQ_CDgpDz-1XJLVmHs,7658
|
|
6
|
+
raindrop/version.py,sha256=46z7DjpRJ03wdmbNWoSr-zmeqNLwMTq3Ue5OAhyiKPU,19
|
|
7
|
+
raindrop/well-known-names.json,sha256=9giJF6u6W1R0APW-Pf1dvNUU32OXQEoQ9CBQXSnA3ks,144403
|
|
8
|
+
raindrop_ai-0.0.34.dist-info/METADATA,sha256=24MFAmx6jhHt8W2MybBsEfbWa-wcTrFaU0ATAK6F3Xs,1269
|
|
9
|
+
raindrop_ai-0.0.34.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
10
|
+
raindrop_ai-0.0.34.dist-info/RECORD,,
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
raindrop/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
raindrop/analytics.py,sha256=XHCXHM-S92ZAgsigPrDovUDTSlHiLLfuIHHEq9wnpTY,24354
|
|
3
|
-
raindrop/interaction.py,sha256=1gYFj2-oEobLtG56RZw5de3RjYr0pCXg_NqYDGx32Uc,1896
|
|
4
|
-
raindrop/models.py,sha256=9lOOUQ2FF11RPkntuLZwN3e54pa9HtR8lGvCbzlWOPM,5198
|
|
5
|
-
raindrop/redact.py,sha256=rMNUoI90KxOY3d_zcHAr0TFD2yQ_CDgpDz-1XJLVmHs,7658
|
|
6
|
-
raindrop/version.py,sha256=cNdQQHZ8mflTb7N2BvtJg9nS-15yTl7r-Bo6P1TZQ4s,19
|
|
7
|
-
raindrop/well-known-names.json,sha256=9giJF6u6W1R0APW-Pf1dvNUU32OXQEoQ9CBQXSnA3ks,144403
|
|
8
|
-
raindrop_ai-0.0.32.dist-info/METADATA,sha256=2s5D6vchrSiUXk1VTFLz5ngqvdgE8pj_8YLigrpTeg0,1269
|
|
9
|
-
raindrop_ai-0.0.32.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
10
|
-
raindrop_ai-0.0.32.dist-info/RECORD,,
|
|
File without changes
|