raindrop-ai 0.1.1 → 0.1.2

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.
@@ -50,7 +50,9 @@ var import_instrumentation_vertexai = require("@traceloop/instrumentation-vertex
50
50
  var traceloop3 = __toESM(require("@traceloop/node-server-sdk"));
51
51
  var import_weakref = require("weakref");
52
52
 
53
- // ../core/dist/chunk-OLYXZCOK.js
53
+ // ../core/dist/chunk-U5HUTMR5.js
54
+ var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
55
+ var MAX_RETRY_DELAY_MS = 3e4;
54
56
  function wait(ms) {
55
57
  return new Promise((resolve) => setTimeout(resolve, ms));
56
58
  }
@@ -58,6 +60,46 @@ function formatEndpoint(endpoint) {
58
60
  if (!endpoint) return void 0;
59
61
  return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
60
62
  }
63
+ function redactUrlForLog(url) {
64
+ try {
65
+ const parsed = new URL(url);
66
+ parsed.username = "";
67
+ parsed.password = "";
68
+ parsed.search = "";
69
+ parsed.hash = "";
70
+ return parsed.toString();
71
+ } catch (e) {
72
+ return "<unparseable-url>";
73
+ }
74
+ }
75
+ var RATE_LIMITED_LOG_INTERVAL_MS = 3e4;
76
+ var rateLimitedLogLast = /* @__PURE__ */ new Map();
77
+ function rateLimitedLog(key, log) {
78
+ const now = Date.now();
79
+ const last = rateLimitedLogLast.get(key);
80
+ if (last !== void 0 && now - last < RATE_LIMITED_LOG_INTERVAL_MS) {
81
+ return false;
82
+ }
83
+ rateLimitedLogLast.set(key, now);
84
+ log();
85
+ return true;
86
+ }
87
+ async function raceWithTimeout(promise, timeoutMs) {
88
+ let timer;
89
+ const settledInTime = await Promise.race([
90
+ promise.then(
91
+ () => true,
92
+ () => true
93
+ ),
94
+ new Promise((resolve) => {
95
+ var _a;
96
+ timer = setTimeout(() => resolve(false), Math.max(0, timeoutMs));
97
+ (_a = timer.unref) == null ? void 0 : _a.call(timer);
98
+ })
99
+ ]);
100
+ if (timer) clearTimeout(timer);
101
+ return settledInTime;
102
+ }
61
103
  function parseRetryAfter(headers) {
62
104
  var _a;
63
105
  const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
@@ -74,12 +116,12 @@ function parseRetryAfter(headers) {
74
116
  function getRetryDelayMs(attemptNumber, previousError) {
75
117
  if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
76
118
  const v = previousError.retryAfterMs;
77
- if (typeof v === "number") return Math.max(0, v);
119
+ if (typeof v === "number") return Math.min(Math.max(0, v), MAX_RETRY_DELAY_MS);
78
120
  }
79
121
  if (attemptNumber <= 1) return 0;
80
122
  const base = 500;
81
123
  const factor = Math.pow(2, attemptNumber - 2);
82
- return base * factor;
124
+ return Math.min(base * factor, MAX_RETRY_DELAY_MS);
83
125
  }
84
126
  async function withRetry(operation, opName, opts) {
85
127
  const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
@@ -114,7 +156,9 @@ async function withRetry(operation, opName, opts) {
114
156
  throw lastError instanceof Error ? lastError : new Error(String(lastError));
115
157
  }
116
158
  async function postJson(url, body, headers, opts) {
117
- const opName = `POST ${url}`;
159
+ var _a;
160
+ const opName = `POST ${redactUrlForLog(url)}`;
161
+ const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
118
162
  await withRetry(
119
163
  async () => {
120
164
  const resp = await fetch(url, {
@@ -123,7 +167,8 @@ async function postJson(url, body, headers, opts) {
123
167
  "Content-Type": "application/json",
124
168
  ...headers
125
169
  },
126
- body: JSON.stringify(body)
170
+ body: JSON.stringify(body),
171
+ signal: AbortSignal.timeout(timeoutMs)
127
172
  });
128
173
  if (!resp.ok) {
129
174
  const text = await resp.text().catch(() => "");
@@ -140,6 +185,97 @@ async function postJson(url, body, headers, opts) {
140
185
  opts
141
186
  );
142
187
  }
188
+ var DEFAULT_MAX_TEXT_FIELD_CHARS = 1e6;
189
+ var TRUNCATION_MARKER = "...[truncated by raindrop]";
190
+ var BOUNDED_CLONE_MAX_DEPTH = 12;
191
+ var BOUNDED_CLONE_SLACK = 256;
192
+ var currentDefaultMaxTextFieldChars = DEFAULT_MAX_TEXT_FIELD_CHARS;
193
+ function truncateToLimit(text, limit) {
194
+ if (limit > TRUNCATION_MARKER.length) {
195
+ return text.slice(0, limit - TRUNCATION_MARKER.length) + TRUNCATION_MARKER;
196
+ }
197
+ return text.slice(0, Math.max(0, limit));
198
+ }
199
+ function capText(value, limit) {
200
+ if (typeof value !== "string") return value;
201
+ const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
202
+ if (value.length <= max) return value;
203
+ return truncateToLimit(value, max);
204
+ }
205
+ function boundedClone(obj, charBudget) {
206
+ let budget = charBudget;
207
+ const seen = /* @__PURE__ */ new WeakSet();
208
+ const walk = (node, depth) => {
209
+ if (budget <= 0) return TRUNCATION_MARKER;
210
+ if (typeof node === "string") {
211
+ if (node.length > budget) {
212
+ const taken = node.slice(0, Math.max(0, budget)) + TRUNCATION_MARKER;
213
+ budget = 0;
214
+ return taken;
215
+ }
216
+ budget -= Math.max(node.length, 1);
217
+ return node;
218
+ }
219
+ if (node === null || typeof node === "number" || typeof node === "boolean") {
220
+ budget -= 8;
221
+ return node;
222
+ }
223
+ if (typeof node !== "object") {
224
+ budget -= 8;
225
+ return node;
226
+ }
227
+ if (seen.has(node)) return "[CIRCULAR]";
228
+ if (depth >= BOUNDED_CLONE_MAX_DEPTH) {
229
+ budget -= 16;
230
+ return `<max depth>`;
231
+ }
232
+ seen.add(node);
233
+ if (Array.isArray(node)) {
234
+ const out2 = [];
235
+ for (const v of node) {
236
+ if (budget <= 0) {
237
+ out2.push(TRUNCATION_MARKER);
238
+ break;
239
+ }
240
+ out2.push(walk(v, depth + 1));
241
+ }
242
+ return out2;
243
+ }
244
+ if (typeof node.toJSON === "function") {
245
+ budget -= 16;
246
+ return node;
247
+ }
248
+ const out = {};
249
+ for (const k in node) {
250
+ if (!Object.prototype.hasOwnProperty.call(node, k)) continue;
251
+ if (budget <= 0) {
252
+ out["..."] = TRUNCATION_MARKER;
253
+ break;
254
+ }
255
+ const key = walk(k, depth + 1);
256
+ out[key] = walk(node[k], depth + 1);
257
+ }
258
+ return out;
259
+ };
260
+ return walk(obj, 0);
261
+ }
262
+ function stringifyBounded(value, limit) {
263
+ const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
264
+ if (typeof value === "string") {
265
+ return capText(value, max);
266
+ }
267
+ const pruned = boundedClone(value, max + TRUNCATION_MARKER.length + BOUNDED_CLONE_SLACK);
268
+ let json;
269
+ try {
270
+ json = JSON.stringify(pruned);
271
+ } catch (e) {
272
+ json = void 0;
273
+ }
274
+ if (json === void 0) {
275
+ return capText(String(value), max);
276
+ }
277
+ return json.length <= max ? json : truncateToLimit(json, max);
278
+ }
143
279
  var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
144
280
  var WORKSHOP_ENV_VAR = "RAINDROP_WORKSHOP";
145
281
  var DEFAULT_LOCAL_WORKSHOP_URL = "http://localhost:5899/v1/";
@@ -236,6 +372,13 @@ function sendLocalDebuggerLiveEvent(event, options = {}) {
236
372
  ).catch(() => {
237
373
  });
238
374
  }
375
+ var SHUTDOWN_DEADLINE_MS = 1e4;
376
+ var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
377
+ var DEFAULT_REDACT_ATTRIBUTE_KEYS = [
378
+ "ai.request.providerOptions",
379
+ "ai.response.providerMetadata"
380
+ ];
381
+ var REDACT_JSON_ATTRIBUTE_KEYS = new Set(DEFAULT_REDACT_ATTRIBUTE_KEYS);
239
382
 
240
383
  // ../core/dist/index.node.js
241
384
  var import_async_hooks = require("async_hooks");
@@ -265,12 +408,12 @@ function parseRetryAfter2(headers) {
265
408
  function getRetryDelayMs2(attemptNumber, previousError) {
266
409
  if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
267
410
  const v = previousError.retryAfterMs;
268
- if (typeof v === "number") return Math.max(0, v);
411
+ if (typeof v === "number") return Math.min(Math.max(0, v), MAX_RETRY_DELAY_MS);
269
412
  }
270
413
  if (attemptNumber <= 1) return 0;
271
414
  const base = 500;
272
415
  const factor = Math.pow(2, attemptNumber - 2);
273
- return base * factor;
416
+ return Math.min(base * factor, MAX_RETRY_DELAY_MS);
274
417
  }
275
418
  async function withRetry2(operation, opName, opts) {
276
419
  let lastError = void 0;
@@ -302,7 +445,9 @@ async function withRetry2(operation, opName, opts) {
302
445
  throw lastError instanceof Error ? lastError : new Error(String(lastError));
303
446
  }
304
447
  async function postJson2(url, body, headers, opts) {
305
- const opName = `POST ${url}`;
448
+ var _a;
449
+ const opName = `POST ${redactUrlForLog(url)}`;
450
+ const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
306
451
  await withRetry2(
307
452
  async () => {
308
453
  const resp = await fetch(url, {
@@ -311,7 +456,8 @@ async function postJson2(url, body, headers, opts) {
311
456
  "Content-Type": "application/json",
312
457
  ...headers
313
458
  },
314
- body: JSON.stringify(body)
459
+ body: JSON.stringify(body),
460
+ signal: AbortSignal.timeout(timeoutMs)
315
461
  });
316
462
  if (!resp.ok) {
317
463
  const text = await resp.text().catch(() => "");
@@ -392,7 +538,7 @@ function base64ToHex(base64) {
392
538
  // package.json
393
539
  var package_default = {
394
540
  name: "raindrop-ai",
395
- version: "0.1.1",
541
+ version: "0.1.2",
396
542
  main: "dist/index.js",
397
543
  module: "dist/index.mjs",
398
544
  types: "dist/index.d.ts",
@@ -578,16 +724,19 @@ function getActiveTraceContext() {
578
724
 
579
725
  // src/tracing/direct/shipper.ts
580
726
  function serializeAssociationValue(value) {
581
- if (typeof value === "string") {
582
- return value;
583
- }
584
- const jsonValue = JSON.stringify(value);
585
- return jsonValue === void 0 ? String(value) : jsonValue;
727
+ return stringifyBounded(value);
586
728
  }
587
729
  var DirectTraceShipper = class {
588
730
  constructor(opts) {
589
731
  this.queue = [];
590
732
  this.inFlight = /* @__PURE__ */ new Set();
733
+ /**
734
+ * Set once `shutdown()` begins and never cleared. Sends issued after the
735
+ * drain window (stragglers, or flush work the deadline abandoned
736
+ * mid-drain) run as a single short attempt instead of regaining the full
737
+ * retry schedule.
738
+ */
739
+ this.hasShutdown = false;
591
740
  var _a, _b, _c, _d, _e, _f;
592
741
  this.writeKey = opts.writeKey;
593
742
  this.baseUrl = (_a = formatEndpoint2(opts.endpoint)) != null ? _a : "https://api.raindrop.ai/v1/";
@@ -712,14 +861,19 @@ var DirectTraceShipper = class {
712
861
  if (this.debug) console.log(`[raindrop] direct: dropped ${batch.length} cloud spans (local-only)`);
713
862
  continue;
714
863
  }
864
+ const opts = this.requestOpts();
865
+ if (!opts) {
866
+ rateLimitedLog(
867
+ "js-sdk.direct.shutdown_deadline",
868
+ () => console.warn(
869
+ `[raindrop] direct: shutdown flush deadline exceeded; dropping ${batch.length} spans`
870
+ )
871
+ );
872
+ continue;
873
+ }
715
874
  const body = buildExportTraceServiceRequest2(batch);
716
875
  const url = `${this.baseUrl}traces`;
717
- const p = postJson2(
718
- url,
719
- body,
720
- { Authorization: `Bearer ${this.writeKey}` },
721
- { maxAttempts: 3, debug: this.debug }
722
- );
876
+ const p = postJson2(url, body, { Authorization: `Bearer ${this.writeKey}` }, opts);
723
877
  this.inFlight.add(p);
724
878
  try {
725
879
  try {
@@ -736,14 +890,48 @@ var DirectTraceShipper = class {
736
890
  }
737
891
  }
738
892
  }
893
+ /**
894
+ * Retry/timeout options for one batch POST, honoring the shutdown budget:
895
+ * outside shutdown, the normal 3-attempt schedule; during shutdown, a
896
+ * single attempt clamped to the remaining window; `null` once the window
897
+ * is exhausted (caller drops the batch). After shutdown() returns,
898
+ * stragglers run as a single short attempt.
899
+ */
900
+ requestOpts() {
901
+ if (this.shutdownDeadlineAt !== void 0) {
902
+ const remainingMs = this.shutdownDeadlineAt - Date.now();
903
+ if (remainingMs <= 0) return null;
904
+ return {
905
+ maxAttempts: 1,
906
+ debug: this.debug,
907
+ timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
908
+ };
909
+ }
910
+ if (this.hasShutdown) {
911
+ return { maxAttempts: 1, debug: this.debug, timeoutMs: POST_SHUTDOWN_TIMEOUT_MS };
912
+ }
913
+ return { maxAttempts: 3, debug: this.debug };
914
+ }
739
915
  async shutdown() {
740
- if (this.timer) {
741
- clearTimeout(this.timer);
742
- this.timer = void 0;
916
+ this.hasShutdown = true;
917
+ this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
918
+ try {
919
+ if (this.timer) {
920
+ clearTimeout(this.timer);
921
+ this.timer = void 0;
922
+ }
923
+ const drain = async () => {
924
+ await this.flush();
925
+ await Promise.all([...this.inFlight].map((p) => p.catch(() => {
926
+ })));
927
+ };
928
+ const settled = await raceWithTimeout(drain(), SHUTDOWN_DEADLINE_MS);
929
+ if (!settled && this.debug) {
930
+ console.warn("[raindrop] direct: shutdown deadline exceeded; abandoning in-flight spans");
931
+ }
932
+ } finally {
933
+ this.shutdownDeadlineAt = void 0;
743
934
  }
744
- await this.flush();
745
- await Promise.all([...this.inFlight].map((p) => p.catch(() => {
746
- })));
747
935
  }
748
936
  };
749
937
 
@@ -759,7 +947,7 @@ function getPropertiesFromContext(context5) {
759
947
  ...context5.properties || {}
760
948
  };
761
949
  if (context5.attachments && context5.attachments.length > 0) {
762
- properties.attachments = JSON.stringify(context5.attachments);
950
+ properties.attachments = stringifyBounded(context5.attachments);
763
951
  }
764
952
  return properties;
765
953
  }
@@ -777,11 +965,7 @@ function serializeAssociationProperties(properties) {
777
965
  );
778
966
  }
779
967
  function serializeSpanValue(value) {
780
- if (typeof value === "string") {
781
- return value;
782
- }
783
- const jsonValue = JSON.stringify(value);
784
- return jsonValue === void 0 ? String(value) : jsonValue;
968
+ return stringifyBounded(value);
785
969
  }
786
970
  function getLocalDebuggerMetadata(context5) {
787
971
  return {
@@ -1060,7 +1244,7 @@ var LiveInteraction = class {
1060
1244
  error = err instanceof Error ? err.message : String(err);
1061
1245
  const endTimeMs2 = Date.now();
1062
1246
  const durationMs2 = endTimeMs2 - startTimeMs;
1063
- const inputStr2 = params.inputParameters ? JSON.stringify(params.inputParameters) : void 0;
1247
+ const inputStr2 = params.inputParameters ? stringifyBounded(params.inputParameters) : void 0;
1064
1248
  this.directShipper.sendToolSpan({
1065
1249
  name: toolName,
1066
1250
  spanId: spanIdB64,
@@ -1089,8 +1273,8 @@ var LiveInteraction = class {
1089
1273
  }
1090
1274
  const endTimeMs = Date.now();
1091
1275
  const durationMs = endTimeMs - startTimeMs;
1092
- const inputStr = params.inputParameters ? JSON.stringify(params.inputParameters) : void 0;
1093
- const outputStr = result !== void 0 ? typeof result === "string" ? result : JSON.stringify(result) : void 0;
1276
+ const inputStr = params.inputParameters ? stringifyBounded(params.inputParameters) : void 0;
1277
+ const outputStr = result !== void 0 ? stringifyBounded(result) : void 0;
1094
1278
  this.directShipper.sendToolSpan({
1095
1279
  name: toolName,
1096
1280
  spanId: spanIdB64,
@@ -1327,8 +1511,8 @@ var LiveInteraction = class {
1327
1511
  const traceIdB64 = this.ensureTraceId(activeContext.traceIdB64);
1328
1512
  const { parentSpanIdB64 } = activeContext;
1329
1513
  const spanIdB64 = base64Encode2(randomBytes2(8));
1330
- const inputStr = input !== void 0 ? typeof input === "string" ? input : JSON.stringify(input) : void 0;
1331
- const outputStr = output !== void 0 ? typeof output === "string" ? output : JSON.stringify(output) : void 0;
1514
+ const inputStr = input !== void 0 ? stringifyBounded(input) : void 0;
1515
+ const outputStr = output !== void 0 ? stringifyBounded(output) : void 0;
1332
1516
  this.directShipper.sendToolSpan({
1333
1517
  name,
1334
1518
  spanId: spanIdB64,
@@ -1375,12 +1559,10 @@ var LiveInteraction = class {
1375
1559
  span.setAttribute("traceloop.span.kind", "tool");
1376
1560
  setAssociationProperties(span, properties);
1377
1561
  if (input !== void 0) {
1378
- const inputStr = typeof input === "string" ? input : JSON.stringify(input);
1379
- span.setAttribute("traceloop.entity.input", inputStr);
1562
+ span.setAttribute("traceloop.entity.input", stringifyBounded(input));
1380
1563
  }
1381
1564
  if (output !== void 0) {
1382
- const outputStr = typeof output === "string" ? output : JSON.stringify(output);
1383
- span.setAttribute("traceloop.entity.output", outputStr);
1565
+ span.setAttribute("traceloop.entity.output", stringifyBounded(output));
1384
1566
  }
1385
1567
  if (durationMs !== void 0) {
1386
1568
  span.setAttribute("traceloop.entity.duration_ms", durationMs);
@@ -1468,8 +1650,8 @@ var LiveTracer = class {
1468
1650
  if (this.directShipper) {
1469
1651
  const { traceIdB64, parentSpanIdB64 } = getActiveTraceContext();
1470
1652
  const spanIdB64 = base64Encode2(randomBytes2(8));
1471
- const inputStr = input !== void 0 ? typeof input === "string" ? input : JSON.stringify(input) : void 0;
1472
- const outputStr = output !== void 0 ? typeof output === "string" ? output : JSON.stringify(output) : void 0;
1653
+ const inputStr = input !== void 0 ? stringifyBounded(input) : void 0;
1654
+ const outputStr = output !== void 0 ? stringifyBounded(output) : void 0;
1473
1655
  this.directShipper.sendToolSpan({
1474
1656
  name,
1475
1657
  spanId: spanIdB64,
@@ -1513,12 +1695,10 @@ var LiveTracer = class {
1513
1695
  });
1514
1696
  span.setAttribute("traceloop.span.kind", "tool");
1515
1697
  if (input !== void 0) {
1516
- const inputStr = typeof input === "string" ? input : JSON.stringify(input);
1517
- span.setAttribute("traceloop.entity.input", inputStr);
1698
+ span.setAttribute("traceloop.entity.input", stringifyBounded(input));
1518
1699
  }
1519
1700
  if (output !== void 0) {
1520
- const outputStr = typeof output === "string" ? output : JSON.stringify(output);
1521
- span.setAttribute("traceloop.entity.output", outputStr);
1701
+ span.setAttribute("traceloop.entity.output", stringifyBounded(output));
1522
1702
  }
1523
1703
  if (durationMs !== void 0) {
1524
1704
  span.setAttribute("traceloop.entity.duration_ms", durationMs);
@@ -1950,6 +2130,7 @@ function getCurrentTraceId() {
1950
2130
  return (_a = import_api4.trace.getSpan(import_api4.context.active())) == null ? void 0 : _a.spanContext().traceId;
1951
2131
  }
1952
2132
  var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
2133
+ process.env.TRACELOOP_TELEMETRY = "false";
1953
2134
  const {
1954
2135
  logLevel,
1955
2136
  useExternalOtel,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  tracing
3
- } from "../chunk-DP5ED5DR.mjs";
3
+ } from "../chunk-UICWZEYM.mjs";
4
4
  import "../chunk-UJCSKKID.mjs";
5
5
 
6
6
  // src/tracing/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "raindrop-ai",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.ts",
@@ -46,8 +46,8 @@
46
46
  "typescript": "^5.3.3",
47
47
  "vite": "^7.3.2",
48
48
  "vitest": "^4.0.10",
49
+ "@raindrop-ai/core": "0.0.3",
49
50
  "@raindrop-ai/redact-pii": "0.1.2",
50
- "@raindrop-ai/core": "0.0.1",
51
51
  "@raindrop-ai/schemas": "0.0.1"
52
52
  },
53
53
  "dependencies": {