raindrop-ai 0.0.36__py3-none-any.whl → 0.0.37__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/interaction.py CHANGED
@@ -1,4 +1,6 @@
1
1
  from __future__ import annotations
2
+ import json
3
+ import time
2
4
  from typing import (
3
5
  Any,
4
6
  Dict,
@@ -9,6 +11,7 @@ from typing import (
9
11
  Union,
10
12
  Iterator,
11
13
  )
14
+ from datetime import datetime, timezone
12
15
  from uuid import uuid4
13
16
  from dataclasses import dataclass
14
17
 
@@ -107,6 +110,146 @@ class Interaction:
107
110
  convo_id=self._convo_id,
108
111
  )
109
112
 
113
+ def track_tool(
114
+ self,
115
+ *,
116
+ name: str,
117
+ input: Any | None = None,
118
+ output: Any | None = None,
119
+ duration_ms: float | int | None = None,
120
+ start_time: datetime | int | float | None = None,
121
+ error: BaseException | str | None = None,
122
+ properties: Dict[str, Any] | None = None,
123
+ version: int | None = None,
124
+ ) -> None:
125
+ """
126
+ Retroactively log a tool span tied to this interaction.
127
+ """
128
+ if not _core._tracing_enabled or not _core.TracerWrapper.verify_initialized():
129
+ return
130
+
131
+ # Duration normalization
132
+ dur_ms = float(duration_ms) if duration_ms is not None else 0.0
133
+ if dur_ms < 0:
134
+ dur_ms = 0.0
135
+ duration_ns = int(round(dur_ms * 1_000_000))
136
+
137
+ # start_time normalization (epoch nanoseconds)
138
+ start_ns: int | None = None
139
+ if isinstance(start_time, datetime):
140
+ dt = start_time
141
+ if dt.tzinfo is None:
142
+ dt = dt.replace(tzinfo=timezone.utc)
143
+ else:
144
+ dt = dt.astimezone(timezone.utc)
145
+
146
+ epoch = datetime(1970, 1, 1, tzinfo=timezone.utc)
147
+ delta = dt - epoch
148
+ total_us = (
149
+ delta.days * 86400 + delta.seconds
150
+ ) * 1_000_000 + delta.microseconds
151
+ start_ns = total_us * 1_000
152
+ elif isinstance(start_time, (int, float)):
153
+ v = float(start_time)
154
+ # Heuristic: values smaller than ~1973 in ms are likely epoch-seconds
155
+ start_ns = (
156
+ int(round(v * 1_000_000_000))
157
+ if abs(v) < 1e11
158
+ else int(round(v * 1_000_000))
159
+ )
160
+
161
+ if start_ns is None:
162
+ start_ns = time.time_ns() - duration_ns
163
+
164
+ end_ns = start_ns + duration_ns
165
+
166
+ tlp_kind = _core.TraceloopSpanKindValues.TOOL
167
+ span_name = f"{name}.{tlp_kind.value}"
168
+
169
+ tracer = _core.trace.get_tracer("traceloop.tracer")
170
+ span = tracer.start_span(span_name, start_time=start_ns)
171
+
172
+ try:
173
+ span.set_attribute(_core.SpanAttributes.TRACELOOP_SPAN_KIND, tlp_kind.value)
174
+ span.set_attribute(_core.SpanAttributes.TRACELOOP_ENTITY_NAME, name)
175
+ if version is not None:
176
+ span.set_attribute(
177
+ _core.SpanAttributes.TRACELOOP_ENTITY_VERSION, version
178
+ )
179
+
180
+ association_props = {
181
+ "event_id": self._event_id,
182
+ "user_id": self._user_id,
183
+ "event": self._event,
184
+ "convo_id": self._convo_id,
185
+ }
186
+ for key, value in association_props.items():
187
+ if value is not None:
188
+ span.set_attribute(f"traceloop.association.properties.{key}", value)
189
+
190
+ if properties:
191
+ for key, value in properties.items():
192
+ if key in association_props:
193
+ continue
194
+ if value is None:
195
+ continue
196
+ if isinstance(value, (str, bool, int, float)):
197
+ attr_val: Any = value
198
+ else:
199
+ try:
200
+ attr_val = json.dumps(value, cls=_core.JSONEncoder)
201
+ except Exception:
202
+ attr_val = str(value)
203
+ span.set_attribute(
204
+ f"traceloop.association.properties.{key}", attr_val
205
+ )
206
+
207
+ if duration_ms is not None:
208
+ span.set_attribute("traceloop.entity.duration_ms", dur_ms)
209
+
210
+ if _core._should_send_prompts():
211
+ if input is not None:
212
+ try:
213
+ json_input = json.dumps(
214
+ {"args": [input]}, cls=_core.JSONEncoder
215
+ )
216
+ span.set_attribute(
217
+ _core.SpanAttributes.TRACELOOP_ENTITY_INPUT,
218
+ _core._truncate_json_if_needed(json_input),
219
+ )
220
+ except Exception as e:
221
+ _core.logger.debug(
222
+ f"[raindrop] Could not serialize input for span: {e}"
223
+ )
224
+
225
+ if output is not None:
226
+ try:
227
+ json_output = json.dumps(output, cls=_core.JSONEncoder)
228
+ span.set_attribute(
229
+ _core.SpanAttributes.TRACELOOP_ENTITY_OUTPUT,
230
+ _core._truncate_json_if_needed(json_output),
231
+ )
232
+ except Exception as e:
233
+ _core.logger.debug(
234
+ f"[raindrop] Could not serialize output for span: {e}"
235
+ )
236
+
237
+ if error is not None:
238
+ exc = (
239
+ error if isinstance(error, BaseException) else Exception(str(error))
240
+ )
241
+ span.set_status(_core.Status(_core.StatusCode.ERROR, str(exc)))
242
+ span.record_exception(exc)
243
+ else:
244
+ span.set_status(_core.Status(_core.StatusCode.OK))
245
+ finally:
246
+ span.end(end_time=end_ns)
247
+
248
+ if _core.debug_logs:
249
+ _core.logger.debug(
250
+ f'[raindrop] track_tool: logged tool span "{name}" (duration_ms={duration_ms})'
251
+ )
252
+
110
253
  # convenience
111
254
  @property
112
255
  def id(self) -> str:
raindrop/version.py CHANGED
@@ -1 +1 @@
1
- VERSION = "0.0.36"
1
+ VERSION = "0.0.37"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: raindrop-ai
3
- Version: 0.0.36
3
+ Version: 0.0.37
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
2
  raindrop/analytics.py,sha256=dL64bp5Sk1gq5PYcKAdQBNlmrn-5aW-LXTa0C3CApKA,30335
3
- raindrop/interaction.py,sha256=Kw8HNaDSHVIi1p-2HK5SsYGI4Xg1zQFml4dX2EhIT7w,3123
3
+ raindrop/interaction.py,sha256=qMTGN-nMDeYmujLhBqIUPq4pKDQx6HX0fqVwB2aq0eA,8639
4
4
  raindrop/models.py,sha256=9lOOUQ2FF11RPkntuLZwN3e54pa9HtR8lGvCbzlWOPM,5198
5
5
  raindrop/redact.py,sha256=rMNUoI90KxOY3d_zcHAr0TFD2yQ_CDgpDz-1XJLVmHs,7658
6
- raindrop/version.py,sha256=l8qKbO0eI3Ja_0j_Y0EQzBqlq-Sw-KTFzu8GAbKB2VU,19
6
+ raindrop/version.py,sha256=bPu3tNWPJ9tg3HaQ8cbDkapv1vcKPLFyWLaaRG7eW2Q,19
7
7
  raindrop/well-known-names.json,sha256=9giJF6u6W1R0APW-Pf1dvNUU32OXQEoQ9CBQXSnA3ks,144403
8
- raindrop_ai-0.0.36.dist-info/METADATA,sha256=DK-3tpiRg0EeXqXOOzr9TRext7DhnJ3J5kgnvZGQFag,1346
9
- raindrop_ai-0.0.36.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
10
- raindrop_ai-0.0.36.dist-info/RECORD,,
8
+ raindrop_ai-0.0.37.dist-info/METADATA,sha256=BO2HCUOURX8Rlhyu4L_XCon7XwS9ln1EP3qEfk6topY,1346
9
+ raindrop_ai-0.0.37.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
10
+ raindrop_ai-0.0.37.dist-info/RECORD,,