@raindrop-ai/ai-sdk 0.0.32 → 0.0.34
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.
- package/README.md +66 -3
- package/dist/{chunk-YDMJ6ABM.mjs → chunk-OLOFN54C.mjs} +556 -109
- package/dist/{index-CwK0GdEe.d.mts → index-HYjRP6nV.d.mts} +197 -4
- package/dist/{index-CwK0GdEe.d.ts → index-HYjRP6nV.d.ts} +197 -4
- package/dist/index.browser.d.mts +197 -4
- package/dist/index.browser.d.ts +197 -4
- package/dist/index.browser.js +560 -108
- package/dist/index.browser.mjs +556 -109
- package/dist/index.node.d.mts +1 -1
- package/dist/index.node.d.ts +1 -1
- package/dist/index.node.js +560 -108
- package/dist/index.node.mjs +1 -1
- package/dist/index.workers.d.mts +1 -1
- package/dist/index.workers.d.ts +1 -1
- package/dist/index.workers.js +560 -108
- package/dist/index.workers.mjs +1 -1
- package/package.json +3 -3
package/dist/index.node.js
CHANGED
|
@@ -4,7 +4,7 @@ var async_hooks = require('async_hooks');
|
|
|
4
4
|
|
|
5
5
|
// src/index.node.ts
|
|
6
6
|
|
|
7
|
-
// ../core/dist/chunk-
|
|
7
|
+
// ../core/dist/chunk-U5HUTMR5.js
|
|
8
8
|
function getCrypto() {
|
|
9
9
|
const c = globalThis.crypto;
|
|
10
10
|
return c;
|
|
@@ -59,6 +59,8 @@ function base64Encode(bytes) {
|
|
|
59
59
|
}
|
|
60
60
|
return out;
|
|
61
61
|
}
|
|
62
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
63
|
+
var MAX_RETRY_DELAY_MS = 3e4;
|
|
62
64
|
function wait(ms) {
|
|
63
65
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
64
66
|
}
|
|
@@ -66,6 +68,46 @@ function formatEndpoint(endpoint) {
|
|
|
66
68
|
if (!endpoint) return void 0;
|
|
67
69
|
return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
|
|
68
70
|
}
|
|
71
|
+
function redactUrlForLog(url) {
|
|
72
|
+
try {
|
|
73
|
+
const parsed = new URL(url);
|
|
74
|
+
parsed.username = "";
|
|
75
|
+
parsed.password = "";
|
|
76
|
+
parsed.search = "";
|
|
77
|
+
parsed.hash = "";
|
|
78
|
+
return parsed.toString();
|
|
79
|
+
} catch (e) {
|
|
80
|
+
return "<unparseable-url>";
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
var RATE_LIMITED_LOG_INTERVAL_MS = 3e4;
|
|
84
|
+
var rateLimitedLogLast = /* @__PURE__ */ new Map();
|
|
85
|
+
function rateLimitedLog(key, log) {
|
|
86
|
+
const now = Date.now();
|
|
87
|
+
const last = rateLimitedLogLast.get(key);
|
|
88
|
+
if (last !== void 0 && now - last < RATE_LIMITED_LOG_INTERVAL_MS) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
rateLimitedLogLast.set(key, now);
|
|
92
|
+
log();
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
async function raceWithTimeout(promise, timeoutMs) {
|
|
96
|
+
let timer;
|
|
97
|
+
const settledInTime = await Promise.race([
|
|
98
|
+
promise.then(
|
|
99
|
+
() => true,
|
|
100
|
+
() => true
|
|
101
|
+
),
|
|
102
|
+
new Promise((resolve) => {
|
|
103
|
+
var _a;
|
|
104
|
+
timer = setTimeout(() => resolve(false), Math.max(0, timeoutMs));
|
|
105
|
+
(_a = timer.unref) == null ? void 0 : _a.call(timer);
|
|
106
|
+
})
|
|
107
|
+
]);
|
|
108
|
+
if (timer) clearTimeout(timer);
|
|
109
|
+
return settledInTime;
|
|
110
|
+
}
|
|
69
111
|
function parseRetryAfter(headers) {
|
|
70
112
|
var _a;
|
|
71
113
|
const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
|
|
@@ -82,12 +124,12 @@ function parseRetryAfter(headers) {
|
|
|
82
124
|
function getRetryDelayMs(attemptNumber, previousError) {
|
|
83
125
|
if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
|
|
84
126
|
const v = previousError.retryAfterMs;
|
|
85
|
-
if (typeof v === "number") return Math.max(0, v);
|
|
127
|
+
if (typeof v === "number") return Math.min(Math.max(0, v), MAX_RETRY_DELAY_MS);
|
|
86
128
|
}
|
|
87
129
|
if (attemptNumber <= 1) return 0;
|
|
88
130
|
const base = 500;
|
|
89
131
|
const factor = Math.pow(2, attemptNumber - 2);
|
|
90
|
-
return base * factor;
|
|
132
|
+
return Math.min(base * factor, MAX_RETRY_DELAY_MS);
|
|
91
133
|
}
|
|
92
134
|
async function withRetry(operation, opName2, opts) {
|
|
93
135
|
const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
|
|
@@ -122,7 +164,9 @@ async function withRetry(operation, opName2, opts) {
|
|
|
122
164
|
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
123
165
|
}
|
|
124
166
|
async function postJson(url, body, headers, opts) {
|
|
125
|
-
|
|
167
|
+
var _a;
|
|
168
|
+
const opName2 = `POST ${redactUrlForLog(url)}`;
|
|
169
|
+
const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
|
|
126
170
|
await withRetry(
|
|
127
171
|
async () => {
|
|
128
172
|
const resp = await fetch(url, {
|
|
@@ -131,7 +175,8 @@ async function postJson(url, body, headers, opts) {
|
|
|
131
175
|
"Content-Type": "application/json",
|
|
132
176
|
...headers
|
|
133
177
|
},
|
|
134
|
-
body: JSON.stringify(body)
|
|
178
|
+
body: JSON.stringify(body),
|
|
179
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
135
180
|
});
|
|
136
181
|
if (!resp.ok) {
|
|
137
182
|
const text = await resp.text().catch(() => "");
|
|
@@ -148,6 +193,27 @@ async function postJson(url, body, headers, opts) {
|
|
|
148
193
|
opts
|
|
149
194
|
);
|
|
150
195
|
}
|
|
196
|
+
var DEFAULT_MAX_TEXT_FIELD_CHARS = 1e6;
|
|
197
|
+
var TRUNCATION_MARKER = "...[truncated by raindrop]";
|
|
198
|
+
var currentDefaultMaxTextFieldChars = DEFAULT_MAX_TEXT_FIELD_CHARS;
|
|
199
|
+
function resolveMaxTextFieldChars(value) {
|
|
200
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
201
|
+
return Math.floor(value);
|
|
202
|
+
}
|
|
203
|
+
return currentDefaultMaxTextFieldChars;
|
|
204
|
+
}
|
|
205
|
+
function truncateToLimit(text, limit) {
|
|
206
|
+
if (limit > TRUNCATION_MARKER.length) {
|
|
207
|
+
return text.slice(0, limit - TRUNCATION_MARKER.length) + TRUNCATION_MARKER;
|
|
208
|
+
}
|
|
209
|
+
return text.slice(0, Math.max(0, limit));
|
|
210
|
+
}
|
|
211
|
+
function capText(value, limit) {
|
|
212
|
+
if (typeof value !== "string") return value;
|
|
213
|
+
const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
|
|
214
|
+
if (value.length <= max) return value;
|
|
215
|
+
return truncateToLimit(value, max);
|
|
216
|
+
}
|
|
151
217
|
var SpanStatusCode = {
|
|
152
218
|
UNSET: 0,
|
|
153
219
|
ERROR: 2
|
|
@@ -335,6 +401,8 @@ function sendLocalDebuggerLiveEvent(event, options = {}) {
|
|
|
335
401
|
).catch(() => {
|
|
336
402
|
});
|
|
337
403
|
}
|
|
404
|
+
var SHUTDOWN_DEADLINE_MS = 1e4;
|
|
405
|
+
var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
338
406
|
function mergePatches(target, source) {
|
|
339
407
|
var _a, _b, _c, _d;
|
|
340
408
|
const out = { ...target, ...source };
|
|
@@ -352,6 +420,7 @@ var EventShipper = class {
|
|
|
352
420
|
this.sticky = /* @__PURE__ */ new Map();
|
|
353
421
|
this.timers = /* @__PURE__ */ new Map();
|
|
354
422
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
423
|
+
this.hasShutdown = false;
|
|
355
424
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
356
425
|
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
357
426
|
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
@@ -361,6 +430,7 @@ var EventShipper = class {
|
|
|
361
430
|
this.sdkName = (_d = opts.sdkName) != null ? _d : "core";
|
|
362
431
|
this.prefix = `[raindrop-ai/${this.sdkName}]`;
|
|
363
432
|
this.defaultEventName = (_e = opts.defaultEventName) != null ? _e : "ai_generation";
|
|
433
|
+
this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
|
|
364
434
|
this.localDebuggerUrl = (_f = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _f : void 0;
|
|
365
435
|
if (this.debug && this.localDebuggerUrl) {
|
|
366
436
|
console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
|
|
@@ -383,10 +453,52 @@ var EventShipper = class {
|
|
|
383
453
|
authHeaders() {
|
|
384
454
|
return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
|
|
385
455
|
}
|
|
456
|
+
/**
|
|
457
|
+
* Build the retry/timeout options for one POST, honoring the shutdown
|
|
458
|
+
* deadline. Returns `null` when the shutdown drain window is exhausted —
|
|
459
|
+
* the caller must drop the payload (with a rate-limited warning) instead
|
|
460
|
+
* of issuing a request that could outlive process exit.
|
|
461
|
+
*
|
|
462
|
+
* Checked fresh on EVERY send, so a shutdown that begins while the flush
|
|
463
|
+
* path is mid-drain takes effect immediately: no further retries, and the
|
|
464
|
+
* per-attempt timeout is clamped to the remaining window. After
|
|
465
|
+
* `shutdown()` returns (deadline cleared, `hasShutdown` still set),
|
|
466
|
+
* sends — late callers, or flush work the deadline abandoned mid-drain —
|
|
467
|
+
* run as a single short attempt rather than regaining the full retry
|
|
468
|
+
* schedule.
|
|
469
|
+
*/
|
|
470
|
+
requestOpts() {
|
|
471
|
+
if (this.shutdownDeadlineAt !== void 0) {
|
|
472
|
+
const remainingMs = this.shutdownDeadlineAt - Date.now();
|
|
473
|
+
if (remainingMs <= 0) return null;
|
|
474
|
+
return {
|
|
475
|
+
maxAttempts: 1,
|
|
476
|
+
debug: this.debug,
|
|
477
|
+
sdkName: this.sdkName,
|
|
478
|
+
timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
if (this.hasShutdown) {
|
|
482
|
+
return {
|
|
483
|
+
maxAttempts: 1,
|
|
484
|
+
debug: this.debug,
|
|
485
|
+
sdkName: this.sdkName,
|
|
486
|
+
timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
|
|
490
|
+
}
|
|
386
491
|
async patch(eventId, patch) {
|
|
387
492
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
388
493
|
if (!this.enabled) return;
|
|
389
494
|
if (!eventId || !eventId.trim()) return;
|
|
495
|
+
const maxChars = resolveMaxTextFieldChars(this.maxTextFieldCharsOpt);
|
|
496
|
+
if (typeof patch.input === "string" && patch.input.length > maxChars) {
|
|
497
|
+
patch = { ...patch, input: capText(patch.input, maxChars) };
|
|
498
|
+
}
|
|
499
|
+
if (typeof patch.output === "string" && patch.output.length > maxChars) {
|
|
500
|
+
patch = { ...patch, output: capText(patch.output, maxChars) };
|
|
501
|
+
}
|
|
390
502
|
if (this.debug) {
|
|
391
503
|
console.log(`${this.prefix} queue patch`, {
|
|
392
504
|
eventId,
|
|
@@ -433,9 +545,16 @@ var EventShipper = class {
|
|
|
433
545
|
})));
|
|
434
546
|
}
|
|
435
547
|
async shutdown() {
|
|
436
|
-
|
|
437
|
-
this.
|
|
438
|
-
|
|
548
|
+
this.hasShutdown = true;
|
|
549
|
+
this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
|
|
550
|
+
try {
|
|
551
|
+
for (const t of this.timers.values()) clearTimeout(t);
|
|
552
|
+
this.timers.clear();
|
|
553
|
+
const settled = await raceWithTimeout(this.flush(), SHUTDOWN_DEADLINE_MS);
|
|
554
|
+
if (!settled) this.warnShutdownDrop("in-flight request(s) at shutdown");
|
|
555
|
+
} finally {
|
|
556
|
+
this.shutdownDeadlineAt = void 0;
|
|
557
|
+
}
|
|
439
558
|
}
|
|
440
559
|
async trackSignal(signal) {
|
|
441
560
|
var _a, _b;
|
|
@@ -457,15 +576,19 @@ var EventShipper = class {
|
|
|
457
576
|
];
|
|
458
577
|
if (!this.writeKey) return;
|
|
459
578
|
const url = `${this.baseUrl}signals/track`;
|
|
579
|
+
const opts = this.requestOpts();
|
|
580
|
+
if (!opts) {
|
|
581
|
+
this.warnShutdownDrop("signal");
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
460
584
|
try {
|
|
461
|
-
await postJson(url, body, this.authHeaders(),
|
|
462
|
-
maxAttempts: 3,
|
|
463
|
-
debug: this.debug,
|
|
464
|
-
sdkName: this.sdkName
|
|
465
|
-
});
|
|
585
|
+
await postJson(url, body, this.authHeaders(), opts);
|
|
466
586
|
} catch (err) {
|
|
467
587
|
const msg = err instanceof Error ? err.message : String(err);
|
|
468
|
-
|
|
588
|
+
rateLimitedLog(
|
|
589
|
+
`${this.prefix}.send_signal_failed`,
|
|
590
|
+
() => console.warn(`${this.prefix} failed to send signal (dropping): ${msg}`)
|
|
591
|
+
);
|
|
469
592
|
}
|
|
470
593
|
}
|
|
471
594
|
async identify(users) {
|
|
@@ -489,17 +612,27 @@ var EventShipper = class {
|
|
|
489
612
|
if (!this.writeKey) return;
|
|
490
613
|
if (body.length === 0) return;
|
|
491
614
|
const url = `${this.baseUrl}users/identify`;
|
|
615
|
+
const opts = this.requestOpts();
|
|
616
|
+
if (!opts) {
|
|
617
|
+
this.warnShutdownDrop("identify");
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
492
620
|
try {
|
|
493
|
-
await postJson(url, body, this.authHeaders(),
|
|
494
|
-
maxAttempts: 3,
|
|
495
|
-
debug: this.debug,
|
|
496
|
-
sdkName: this.sdkName
|
|
497
|
-
});
|
|
621
|
+
await postJson(url, body, this.authHeaders(), opts);
|
|
498
622
|
} catch (err) {
|
|
499
623
|
const msg = err instanceof Error ? err.message : String(err);
|
|
500
|
-
|
|
624
|
+
rateLimitedLog(
|
|
625
|
+
`${this.prefix}.send_identify_failed`,
|
|
626
|
+
() => console.warn(`${this.prefix} failed to send identify (dropping): ${msg}`)
|
|
627
|
+
);
|
|
501
628
|
}
|
|
502
629
|
}
|
|
630
|
+
warnShutdownDrop(what) {
|
|
631
|
+
rateLimitedLog(
|
|
632
|
+
`${this.prefix}.shutdown_deadline`,
|
|
633
|
+
() => console.warn(`${this.prefix} shutdown flush deadline exceeded; dropping ${what}`)
|
|
634
|
+
);
|
|
635
|
+
}
|
|
503
636
|
async flushOne(eventId) {
|
|
504
637
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
505
638
|
if (!this.enabled) return;
|
|
@@ -575,11 +708,13 @@ var EventShipper = class {
|
|
|
575
708
|
if (!isPending) this.sticky.delete(eventId);
|
|
576
709
|
return;
|
|
577
710
|
}
|
|
578
|
-
const
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
711
|
+
const opts = this.requestOpts();
|
|
712
|
+
if (!opts) {
|
|
713
|
+
this.warnShutdownDrop(`track_partial ${eventId}`);
|
|
714
|
+
if (!isPending) this.sticky.delete(eventId);
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
const p = postJson(url, payload, this.authHeaders(), opts);
|
|
583
718
|
this.inFlight.add(p);
|
|
584
719
|
try {
|
|
585
720
|
try {
|
|
@@ -589,7 +724,10 @@ var EventShipper = class {
|
|
|
589
724
|
}
|
|
590
725
|
} catch (err) {
|
|
591
726
|
const msg = err instanceof Error ? err.message : String(err);
|
|
592
|
-
|
|
727
|
+
rateLimitedLog(
|
|
728
|
+
`${this.prefix}.send_track_partial_failed`,
|
|
729
|
+
() => console.warn(`${this.prefix} failed to send track_partial (dropping): ${msg}`)
|
|
730
|
+
);
|
|
593
731
|
}
|
|
594
732
|
} finally {
|
|
595
733
|
this.inFlight.delete(p);
|
|
@@ -688,10 +826,24 @@ function redactJsonAttributeValue(key, value) {
|
|
|
688
826
|
if (scrubbedJson === json) return void 0;
|
|
689
827
|
return { stringValue: scrubbedJson };
|
|
690
828
|
}
|
|
829
|
+
function applyOtelSpanAttributeLimit(limit) {
|
|
830
|
+
var _a, _b;
|
|
831
|
+
try {
|
|
832
|
+
const raw = (_b = (_a = globalThis == null ? void 0 : globalThis.process) == null ? void 0 : _a.env) == null ? void 0 : _b.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT;
|
|
833
|
+
if (!raw) return limit;
|
|
834
|
+
const parsed = Number.parseInt(raw, 10);
|
|
835
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
836
|
+
return Math.min(limit, parsed);
|
|
837
|
+
}
|
|
838
|
+
} catch (e) {
|
|
839
|
+
}
|
|
840
|
+
return limit;
|
|
841
|
+
}
|
|
691
842
|
var TraceShipper = class {
|
|
692
843
|
constructor(opts) {
|
|
693
844
|
this.queue = [];
|
|
694
845
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
846
|
+
this.hasShutdown = false;
|
|
695
847
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
696
848
|
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
697
849
|
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
@@ -711,6 +863,39 @@ var TraceShipper = class {
|
|
|
711
863
|
}
|
|
712
864
|
this.transformSpanHook = opts.transformSpan;
|
|
713
865
|
this.disableDefaultRedaction = opts.disableDefaultRedaction === true;
|
|
866
|
+
this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Cap every string attribute value on the span. O(#attributes) length
|
|
870
|
+
* checks; only oversized values pay a slice. Runs AFTER the redaction
|
|
871
|
+
* pipeline so the default secret-scrub still sees parseable JSON in
|
|
872
|
+
* `ai.request.providerOptions` / `ai.response.providerMetadata` (capping
|
|
873
|
+
* first could cut a JSON blob mid-way, fail the parse, and ship secrets
|
|
874
|
+
* in the surviving prefix).
|
|
875
|
+
*
|
|
876
|
+
* A stricter `OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT` env var is honored
|
|
877
|
+
* for span content, matching the Python SDK and the OTel SDK convention.
|
|
878
|
+
*/
|
|
879
|
+
capSpanAttributes(span) {
|
|
880
|
+
var _a;
|
|
881
|
+
const maxChars = applyOtelSpanAttributeLimit(
|
|
882
|
+
resolveMaxTextFieldChars(this.maxTextFieldCharsOpt)
|
|
883
|
+
);
|
|
884
|
+
const attrs = span.attributes;
|
|
885
|
+
if (!attrs || attrs.length === 0) return span;
|
|
886
|
+
let nextAttrs;
|
|
887
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
888
|
+
const attr = attrs[i];
|
|
889
|
+
const value = (_a = attr.value) == null ? void 0 : _a.stringValue;
|
|
890
|
+
if (typeof value !== "string" || value.length <= maxChars) continue;
|
|
891
|
+
if (!nextAttrs) nextAttrs = attrs.slice();
|
|
892
|
+
nextAttrs[i] = {
|
|
893
|
+
key: attr.key,
|
|
894
|
+
value: { ...attr.value, stringValue: capText(value, maxChars) }
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
if (!nextAttrs) return span;
|
|
898
|
+
return { ...span, attributes: nextAttrs };
|
|
714
899
|
}
|
|
715
900
|
/**
|
|
716
901
|
* Apply the user `transformSpan` hook (if any) followed by the default
|
|
@@ -744,7 +929,7 @@ var TraceShipper = class {
|
|
|
744
929
|
if (!this.disableDefaultRedaction) {
|
|
745
930
|
current = defaultTransformSpan(current);
|
|
746
931
|
}
|
|
747
|
-
return current;
|
|
932
|
+
return this.capSpanAttributes(current);
|
|
748
933
|
}
|
|
749
934
|
isDebugEnabled() {
|
|
750
935
|
return this.debug;
|
|
@@ -865,6 +1050,16 @@ var TraceShipper = class {
|
|
|
865
1050
|
while (this.queue.length > 0) {
|
|
866
1051
|
const batch = this.queue.splice(0, this.maxBatchSize);
|
|
867
1052
|
if (!this.writeKey) continue;
|
|
1053
|
+
const opts = this.requestOpts();
|
|
1054
|
+
if (!opts) {
|
|
1055
|
+
rateLimitedLog(
|
|
1056
|
+
`${this.prefix}.shutdown_deadline`,
|
|
1057
|
+
() => console.warn(
|
|
1058
|
+
`${this.prefix} shutdown flush deadline exceeded; dropping ${batch.length} spans`
|
|
1059
|
+
)
|
|
1060
|
+
);
|
|
1061
|
+
continue;
|
|
1062
|
+
}
|
|
868
1063
|
const body = buildExportTraceServiceRequest(batch, this.serviceName, this.serviceVersion);
|
|
869
1064
|
const url = `${this.baseUrl}traces`;
|
|
870
1065
|
if (this.debug) {
|
|
@@ -873,11 +1068,7 @@ var TraceShipper = class {
|
|
|
873
1068
|
endpoint: url
|
|
874
1069
|
});
|
|
875
1070
|
}
|
|
876
|
-
const p = postJson(url, body, this.authHeaders(),
|
|
877
|
-
maxAttempts: 3,
|
|
878
|
-
debug: this.debug,
|
|
879
|
-
sdkName: this.sdkName
|
|
880
|
-
});
|
|
1071
|
+
const p = postJson(url, body, this.authHeaders(), opts);
|
|
881
1072
|
this.inFlight.add(p);
|
|
882
1073
|
try {
|
|
883
1074
|
try {
|
|
@@ -885,21 +1076,61 @@ var TraceShipper = class {
|
|
|
885
1076
|
if (this.debug) console.log(`${this.prefix} sent ${batch.length} spans`);
|
|
886
1077
|
} catch (err) {
|
|
887
1078
|
const msg = err instanceof Error ? err.message : String(err);
|
|
888
|
-
|
|
1079
|
+
rateLimitedLog(
|
|
1080
|
+
`${this.prefix}.send_spans_failed`,
|
|
1081
|
+
() => console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`)
|
|
1082
|
+
);
|
|
889
1083
|
}
|
|
890
1084
|
} finally {
|
|
891
1085
|
this.inFlight.delete(p);
|
|
892
1086
|
}
|
|
893
1087
|
}
|
|
894
1088
|
}
|
|
1089
|
+
/** See EventShipper.requestOpts — same shutdown-budget semantics. */
|
|
1090
|
+
requestOpts() {
|
|
1091
|
+
if (this.shutdownDeadlineAt !== void 0) {
|
|
1092
|
+
const remainingMs = this.shutdownDeadlineAt - Date.now();
|
|
1093
|
+
if (remainingMs <= 0) return null;
|
|
1094
|
+
return {
|
|
1095
|
+
maxAttempts: 1,
|
|
1096
|
+
debug: this.debug,
|
|
1097
|
+
sdkName: this.sdkName,
|
|
1098
|
+
timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1101
|
+
if (this.hasShutdown) {
|
|
1102
|
+
return {
|
|
1103
|
+
maxAttempts: 1,
|
|
1104
|
+
debug: this.debug,
|
|
1105
|
+
sdkName: this.sdkName,
|
|
1106
|
+
timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
|
|
1110
|
+
}
|
|
895
1111
|
async shutdown() {
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
1112
|
+
this.hasShutdown = true;
|
|
1113
|
+
this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
|
|
1114
|
+
try {
|
|
1115
|
+
if (this.timer) {
|
|
1116
|
+
clearTimeout(this.timer);
|
|
1117
|
+
this.timer = void 0;
|
|
1118
|
+
}
|
|
1119
|
+
const drain = async () => {
|
|
1120
|
+
await this.flush();
|
|
1121
|
+
await Promise.all([...this.inFlight].map((p) => p.catch(() => {
|
|
1122
|
+
})));
|
|
1123
|
+
};
|
|
1124
|
+
const settled = await raceWithTimeout(drain(), SHUTDOWN_DEADLINE_MS);
|
|
1125
|
+
if (!settled) {
|
|
1126
|
+
rateLimitedLog(
|
|
1127
|
+
`${this.prefix}.shutdown_deadline`,
|
|
1128
|
+
() => console.warn(`${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`)
|
|
1129
|
+
);
|
|
1130
|
+
}
|
|
1131
|
+
} finally {
|
|
1132
|
+
this.shutdownDeadlineAt = void 0;
|
|
899
1133
|
}
|
|
900
|
-
await this.flush();
|
|
901
|
-
await Promise.all([...this.inFlight].map((p) => p.catch(() => {
|
|
902
|
-
})));
|
|
903
1134
|
}
|
|
904
1135
|
};
|
|
905
1136
|
var NOOP_SPAN = {
|
|
@@ -1024,7 +1255,7 @@ globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = async_hooks.AsyncLocalStorage;
|
|
|
1024
1255
|
// package.json
|
|
1025
1256
|
var package_default = {
|
|
1026
1257
|
name: "@raindrop-ai/ai-sdk",
|
|
1027
|
-
version: "0.0.
|
|
1258
|
+
version: "0.0.34"};
|
|
1028
1259
|
|
|
1029
1260
|
// src/internal/version.ts
|
|
1030
1261
|
var libraryName = package_default.name;
|
|
@@ -1043,6 +1274,133 @@ var EventShipper2 = class extends EventShipper {
|
|
|
1043
1274
|
}
|
|
1044
1275
|
};
|
|
1045
1276
|
|
|
1277
|
+
// src/internal/truncation.ts
|
|
1278
|
+
var TRUNCATION_MARKER2 = "...[truncated by raindrop]";
|
|
1279
|
+
var DEFAULT_MAX_TEXT_FIELD_CHARS2 = 1e6;
|
|
1280
|
+
var MAX_DEPTH = 12;
|
|
1281
|
+
var configuredMaxTextFieldChars;
|
|
1282
|
+
function setMaxTextFieldChars(limit) {
|
|
1283
|
+
if (limit === void 0) return;
|
|
1284
|
+
if (typeof limit === "number" && Number.isFinite(limit) && limit > 0) {
|
|
1285
|
+
configuredMaxTextFieldChars = Math.floor(limit);
|
|
1286
|
+
} else {
|
|
1287
|
+
console.warn(`[raindrop-ai] maxTextFieldChars=${String(limit)} ignored; must be > 0`);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
function otelEnvLimit() {
|
|
1291
|
+
var _a;
|
|
1292
|
+
if (typeof process === "undefined") return void 0;
|
|
1293
|
+
const raw = (_a = process.env) == null ? void 0 : _a.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT;
|
|
1294
|
+
if (!raw) return void 0;
|
|
1295
|
+
const parsed = Number.parseInt(raw, 10);
|
|
1296
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
|
|
1297
|
+
}
|
|
1298
|
+
function effectiveMaxTextFieldChars() {
|
|
1299
|
+
const base = configuredMaxTextFieldChars != null ? configuredMaxTextFieldChars : DEFAULT_MAX_TEXT_FIELD_CHARS2;
|
|
1300
|
+
const env = otelEnvLimit();
|
|
1301
|
+
return env !== void 0 && env < base ? env : base;
|
|
1302
|
+
}
|
|
1303
|
+
function truncateToLimit2(text, limit) {
|
|
1304
|
+
if (limit > TRUNCATION_MARKER2.length) {
|
|
1305
|
+
return text.slice(0, limit - TRUNCATION_MARKER2.length) + TRUNCATION_MARKER2;
|
|
1306
|
+
}
|
|
1307
|
+
return text.slice(0, limit);
|
|
1308
|
+
}
|
|
1309
|
+
function capText2(value, limit) {
|
|
1310
|
+
if (typeof value !== "string") return value;
|
|
1311
|
+
const max = limit != null ? limit : effectiveMaxTextFieldChars();
|
|
1312
|
+
if (value.length <= max) return value;
|
|
1313
|
+
return truncateToLimit2(value, max);
|
|
1314
|
+
}
|
|
1315
|
+
function boundedClone2(value, budget, depth) {
|
|
1316
|
+
var _a, _b;
|
|
1317
|
+
if (budget.remaining <= 0) return TRUNCATION_MARKER2;
|
|
1318
|
+
if (typeof value === "string") {
|
|
1319
|
+
if (value.length > budget.remaining) {
|
|
1320
|
+
const taken = value.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
|
|
1321
|
+
budget.remaining = 0;
|
|
1322
|
+
return taken;
|
|
1323
|
+
}
|
|
1324
|
+
budget.remaining -= Math.max(value.length, 1);
|
|
1325
|
+
return value;
|
|
1326
|
+
}
|
|
1327
|
+
if (value === null || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
1328
|
+
budget.remaining -= 8;
|
|
1329
|
+
return value;
|
|
1330
|
+
}
|
|
1331
|
+
if (typeof value !== "object") {
|
|
1332
|
+
budget.remaining -= 1;
|
|
1333
|
+
return value;
|
|
1334
|
+
}
|
|
1335
|
+
if (value instanceof Uint8Array) {
|
|
1336
|
+
const maxBytes = Math.max(0, Math.ceil(budget.remaining * 3 / 4));
|
|
1337
|
+
if (value.byteLength > maxBytes) {
|
|
1338
|
+
const taken = base64Encode(value.subarray(0, maxBytes)) + TRUNCATION_MARKER2;
|
|
1339
|
+
budget.remaining = 0;
|
|
1340
|
+
return taken;
|
|
1341
|
+
}
|
|
1342
|
+
const encoded = base64Encode(value);
|
|
1343
|
+
budget.remaining -= Math.max(encoded.length, 1);
|
|
1344
|
+
return encoded;
|
|
1345
|
+
}
|
|
1346
|
+
if (depth >= MAX_DEPTH) {
|
|
1347
|
+
budget.remaining -= 16;
|
|
1348
|
+
const name = (_b = (_a = value.constructor) == null ? void 0 : _a.name) != null ? _b : "object";
|
|
1349
|
+
return `<max depth: ${name}>`;
|
|
1350
|
+
}
|
|
1351
|
+
const toJSON = value.toJSON;
|
|
1352
|
+
if (typeof toJSON === "function") {
|
|
1353
|
+
budget.remaining -= 2;
|
|
1354
|
+
return boundedClone2(toJSON.call(value), budget, depth + 1);
|
|
1355
|
+
}
|
|
1356
|
+
if (Array.isArray(value)) {
|
|
1357
|
+
budget.remaining -= 2;
|
|
1358
|
+
const out2 = [];
|
|
1359
|
+
for (const item of value) {
|
|
1360
|
+
if (budget.remaining <= 0) {
|
|
1361
|
+
out2.push(TRUNCATION_MARKER2);
|
|
1362
|
+
break;
|
|
1363
|
+
}
|
|
1364
|
+
out2.push(boundedClone2(item, budget, depth + 1));
|
|
1365
|
+
}
|
|
1366
|
+
return out2;
|
|
1367
|
+
}
|
|
1368
|
+
budget.remaining -= 2;
|
|
1369
|
+
const out = {};
|
|
1370
|
+
for (const key in value) {
|
|
1371
|
+
if (!Object.prototype.hasOwnProperty.call(value, key)) continue;
|
|
1372
|
+
if (budget.remaining <= 0) {
|
|
1373
|
+
out["..."] = TRUNCATION_MARKER2;
|
|
1374
|
+
break;
|
|
1375
|
+
}
|
|
1376
|
+
let cappedKey = key;
|
|
1377
|
+
if (key.length > budget.remaining) {
|
|
1378
|
+
cappedKey = key.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
|
|
1379
|
+
budget.remaining = 0;
|
|
1380
|
+
out[cappedKey] = TRUNCATION_MARKER2;
|
|
1381
|
+
break;
|
|
1382
|
+
}
|
|
1383
|
+
budget.remaining -= Math.max(key.length, 1);
|
|
1384
|
+
out[cappedKey] = boundedClone2(value[key], budget, depth + 1);
|
|
1385
|
+
}
|
|
1386
|
+
return out;
|
|
1387
|
+
}
|
|
1388
|
+
function boundedStringify(value, limit) {
|
|
1389
|
+
const max = limit != null ? limit : effectiveMaxTextFieldChars();
|
|
1390
|
+
try {
|
|
1391
|
+
if (typeof value === "string") {
|
|
1392
|
+
const text2 = JSON.stringify(capText2(value, max));
|
|
1393
|
+
return text2.length <= max ? text2 : truncateToLimit2(text2, max);
|
|
1394
|
+
}
|
|
1395
|
+
const pruned = boundedClone2(value, { remaining: max + TRUNCATION_MARKER2.length + 256 }, 0);
|
|
1396
|
+
const text = JSON.stringify(pruned);
|
|
1397
|
+
if (text === void 0) return void 0;
|
|
1398
|
+
return text.length <= max ? text : truncateToLimit2(text, max);
|
|
1399
|
+
} catch (e) {
|
|
1400
|
+
return void 0;
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1046
1404
|
// src/internal/wrap/helpers.ts
|
|
1047
1405
|
function isRecord(value) {
|
|
1048
1406
|
return typeof value === "object" && value !== null;
|
|
@@ -1067,21 +1425,10 @@ function isModuleNamespace(obj) {
|
|
|
1067
1425
|
}
|
|
1068
1426
|
}
|
|
1069
1427
|
function safeJson(value) {
|
|
1070
|
-
|
|
1071
|
-
return JSON.stringify(value);
|
|
1072
|
-
} catch (e) {
|
|
1073
|
-
return void 0;
|
|
1074
|
-
}
|
|
1428
|
+
return boundedStringify(value);
|
|
1075
1429
|
}
|
|
1076
1430
|
function safeJsonWithUint8(value) {
|
|
1077
|
-
|
|
1078
|
-
return JSON.stringify(value, (_key, v) => {
|
|
1079
|
-
if (v instanceof Uint8Array) return base64Encode(v);
|
|
1080
|
-
return v;
|
|
1081
|
-
});
|
|
1082
|
-
} catch (e) {
|
|
1083
|
-
return void 0;
|
|
1084
|
-
}
|
|
1431
|
+
return boundedStringify(value);
|
|
1085
1432
|
}
|
|
1086
1433
|
function extractModelInfo(model) {
|
|
1087
1434
|
if (typeof model === "string") {
|
|
@@ -1807,6 +2154,25 @@ var RaindropTelemetryIntegration = class {
|
|
|
1807
2154
|
* (the `event.callId` can be the same for parallel sibling tools).
|
|
1808
2155
|
*/
|
|
1809
2156
|
this.priorParentContexts = /* @__PURE__ */ new Map();
|
|
2157
|
+
// ── lifecycle ─────────────────────────────────────────────────────────────
|
|
2158
|
+
//
|
|
2159
|
+
// The shippers buffer spans/events and flush on a timer, so a short-lived
|
|
2160
|
+
// script (e.g. a single `generateText` then `process.exit`) can exit before
|
|
2161
|
+
// anything ships. Expose `flush`/`shutdown` on the integration itself so the
|
|
2162
|
+
// value returned by `raindrop()` is self-sufficient — callers can keep the
|
|
2163
|
+
// reference they pass to `registerTelemetry` and `await rd.flush()` before
|
|
2164
|
+
// exiting, without also constructing a separate client.
|
|
2165
|
+
/** Flush any buffered events and trace spans to their destinations. */
|
|
2166
|
+
this.flush = async () => {
|
|
2167
|
+
await Promise.all([this.eventShipper.flush(), this.traceShipper.flush()]);
|
|
2168
|
+
};
|
|
2169
|
+
/** Flush and stop the background flush timers. */
|
|
2170
|
+
this.shutdown = async () => {
|
|
2171
|
+
await Promise.all([
|
|
2172
|
+
this.eventShipper.shutdown(),
|
|
2173
|
+
this.traceShipper.shutdown()
|
|
2174
|
+
]);
|
|
2175
|
+
};
|
|
1810
2176
|
// ── onStart ─────────────────────────────────────────────────────────────
|
|
1811
2177
|
this.onStart = (event) => {
|
|
1812
2178
|
var _a, _b, _c, _d, _e;
|
|
@@ -1958,7 +2324,10 @@ var RaindropTelemetryIntegration = class {
|
|
|
1958
2324
|
inputAttrs.push(
|
|
1959
2325
|
attrStringArray(
|
|
1960
2326
|
"ai.prompt.tools",
|
|
1961
|
-
event.stepTools.map((t) =>
|
|
2327
|
+
event.stepTools.map((t) => {
|
|
2328
|
+
var _a;
|
|
2329
|
+
return (_a = safeJsonWithUint8(t)) != null ? _a : "";
|
|
2330
|
+
})
|
|
1962
2331
|
)
|
|
1963
2332
|
);
|
|
1964
2333
|
}
|
|
@@ -1966,7 +2335,7 @@ var RaindropTelemetryIntegration = class {
|
|
|
1966
2335
|
inputAttrs.push(
|
|
1967
2336
|
attrString(
|
|
1968
2337
|
"ai.prompt.toolChoice",
|
|
1969
|
-
|
|
2338
|
+
safeJsonWithUint8(event.stepToolChoice)
|
|
1970
2339
|
)
|
|
1971
2340
|
);
|
|
1972
2341
|
}
|
|
@@ -2022,7 +2391,9 @@ var RaindropTelemetryIntegration = class {
|
|
|
2022
2391
|
if (chunk.type === "text-delta") {
|
|
2023
2392
|
const delta = (_c = chunk.textDelta) != null ? _c : chunk.delta;
|
|
2024
2393
|
if (typeof delta === "string") {
|
|
2025
|
-
state.accumulatedText
|
|
2394
|
+
if (state.accumulatedText.length <= effectiveMaxTextFieldChars()) {
|
|
2395
|
+
state.accumulatedText += delta;
|
|
2396
|
+
}
|
|
2026
2397
|
this.emitLive(state, "text_delta", delta);
|
|
2027
2398
|
}
|
|
2028
2399
|
} else if (chunk.type === "reasoning" || chunk.type === "reasoning-delta") {
|
|
@@ -2041,7 +2412,7 @@ var RaindropTelemetryIntegration = class {
|
|
|
2041
2412
|
if (state.recordOutputs) {
|
|
2042
2413
|
outputAttrs.push(
|
|
2043
2414
|
attrString("ai.response.finishReason", event.finishReason),
|
|
2044
|
-
attrString("ai.response.text", (_a = event.text) != null ? _a : void 0),
|
|
2415
|
+
attrString("ai.response.text", capText2((_a = event.text) != null ? _a : void 0)),
|
|
2045
2416
|
attrString("ai.response.id", (_b = event.response) == null ? void 0 : _b.id),
|
|
2046
2417
|
attrString("ai.response.model", (_c = event.response) == null ? void 0 : _c.modelId),
|
|
2047
2418
|
attrString(
|
|
@@ -2057,7 +2428,7 @@ var RaindropTelemetryIntegration = class {
|
|
|
2057
2428
|
outputAttrs.push(
|
|
2058
2429
|
attrString(
|
|
2059
2430
|
"ai.response.toolCalls",
|
|
2060
|
-
|
|
2431
|
+
safeJsonWithUint8(
|
|
2061
2432
|
event.toolCalls.map((tc) => ({
|
|
2062
2433
|
toolCallId: tc.toolCallId,
|
|
2063
2434
|
toolName: tc.toolName,
|
|
@@ -2070,7 +2441,7 @@ var RaindropTelemetryIntegration = class {
|
|
|
2070
2441
|
if (((_g = event.reasoning) == null ? void 0 : _g.length) > 0) {
|
|
2071
2442
|
const reasoningText = event.reasoning.filter((part) => "text" in part).map((part) => part.text).join("\n");
|
|
2072
2443
|
if (reasoningText) {
|
|
2073
|
-
outputAttrs.push(attrString("ai.response.reasoning", reasoningText));
|
|
2444
|
+
outputAttrs.push(attrString("ai.response.reasoning", capText2(reasoningText)));
|
|
2074
2445
|
}
|
|
2075
2446
|
}
|
|
2076
2447
|
}
|
|
@@ -2200,8 +2571,56 @@ var RaindropTelemetryIntegration = class {
|
|
|
2200
2571
|
if (state.subagentToolCallSpan) {
|
|
2201
2572
|
this.traceShipper.endSpan(state.subagentToolCallSpan, { error: actualError });
|
|
2202
2573
|
}
|
|
2574
|
+
const isEmbed = state.operationId === "ai.embed" || state.operationId === "ai.embedMany";
|
|
2575
|
+
if (!isEmbed) {
|
|
2576
|
+
this.finalizeGenerateEvent(state, state.accumulatedText || void 0, void 0);
|
|
2577
|
+
}
|
|
2203
2578
|
this.cleanup(event.callId);
|
|
2204
2579
|
};
|
|
2580
|
+
// ── onAbort ─────────────────────────────────────────────────────────────
|
|
2581
|
+
//
|
|
2582
|
+
// Dispatched by the v7 canary line (post-beta.116) when a `streamText` call
|
|
2583
|
+
// is cancelled via its `AbortSignal`. On abort the SDK fires neither `onEnd`
|
|
2584
|
+
// nor `onError`, so without this every open span would leak and the event
|
|
2585
|
+
// would hang forever in its `isPending` state. We close every open span with
|
|
2586
|
+
// an `ai.response.aborted` marker (no error status — an abort is not a model
|
|
2587
|
+
// failure) and flush the partial event.
|
|
2588
|
+
this.onAbort = (event) => {
|
|
2589
|
+
const callId = event == null ? void 0 : event.callId;
|
|
2590
|
+
if (!callId) return;
|
|
2591
|
+
const state = this.getState(callId);
|
|
2592
|
+
if (!state) return;
|
|
2593
|
+
const abortedAttr = attrString("ai.response.aborted", "true");
|
|
2594
|
+
if (state.stepSpan) {
|
|
2595
|
+
this.traceShipper.endSpan(state.stepSpan, { attributes: [abortedAttr] });
|
|
2596
|
+
state.stepSpan = void 0;
|
|
2597
|
+
state.stepParent = void 0;
|
|
2598
|
+
}
|
|
2599
|
+
for (const embedSpan of state.embedSpans.values()) {
|
|
2600
|
+
this.traceShipper.endSpan(embedSpan, { attributes: [abortedAttr] });
|
|
2601
|
+
}
|
|
2602
|
+
state.embedSpans.clear();
|
|
2603
|
+
for (const toolCallId of state.parentContextToolCallIds) {
|
|
2604
|
+
this.priorParentContexts.delete(toolCallId);
|
|
2605
|
+
}
|
|
2606
|
+
state.parentContextToolCallIds.clear();
|
|
2607
|
+
for (const toolSpan of state.toolSpans.values()) {
|
|
2608
|
+
this.traceShipper.endSpan(toolSpan, { attributes: [abortedAttr] });
|
|
2609
|
+
}
|
|
2610
|
+
state.toolSpans.clear();
|
|
2611
|
+
if (state.rootSpan) {
|
|
2612
|
+
this.traceShipper.endSpan(state.rootSpan, {
|
|
2613
|
+
attributes: [abortedAttr, attrInt("ai.toolCall.count", state.toolCallCount)]
|
|
2614
|
+
});
|
|
2615
|
+
}
|
|
2616
|
+
if (state.subagentToolCallSpan) {
|
|
2617
|
+
this.traceShipper.endSpan(state.subagentToolCallSpan, {
|
|
2618
|
+
attributes: [abortedAttr]
|
|
2619
|
+
});
|
|
2620
|
+
}
|
|
2621
|
+
this.finalizeGenerateEvent(state, state.accumulatedText || void 0, void 0);
|
|
2622
|
+
this.cleanup(callId);
|
|
2623
|
+
};
|
|
2205
2624
|
// ── executeTool ─────────────────────────────────────────────────────────
|
|
2206
2625
|
this.executeTool = async ({
|
|
2207
2626
|
callId,
|
|
@@ -2359,11 +2778,12 @@ var RaindropTelemetryIntegration = class {
|
|
|
2359
2778
|
const toolSpan = state.toolSpans.get(event.toolCall.toolCallId);
|
|
2360
2779
|
if (!toolSpan) return;
|
|
2361
2780
|
state.toolCallCount += 1;
|
|
2362
|
-
|
|
2363
|
-
|
|
2781
|
+
const outcome = "toolOutput" in event ? event.toolOutput.type === "tool-result" ? { success: true, output: event.toolOutput.output } : { success: false, error: event.toolOutput.error } : event.success ? { success: true, output: event.output } : { success: false, error: event.error };
|
|
2782
|
+
if (outcome.success) {
|
|
2783
|
+
const outputAttrs = state.recordOutputs ? [attrString("ai.toolCall.result", safeJsonWithUint8(outcome.output))] : [];
|
|
2364
2784
|
this.traceShipper.endSpan(toolSpan, { attributes: outputAttrs });
|
|
2365
2785
|
} else {
|
|
2366
|
-
this.traceShipper.endSpan(toolSpan, { error:
|
|
2786
|
+
this.traceShipper.endSpan(toolSpan, { error: outcome.error });
|
|
2367
2787
|
}
|
|
2368
2788
|
this.emitLive(state, "tool_result", event.toolCall.toolName);
|
|
2369
2789
|
state.toolSpans.delete(event.toolCall.toolCallId);
|
|
@@ -2437,13 +2857,13 @@ var RaindropTelemetryIntegration = class {
|
|
|
2437
2857
|
}
|
|
2438
2858
|
}
|
|
2439
2859
|
finishGenerate(event, state) {
|
|
2440
|
-
var _a, _b, _c, _d, _e, _f
|
|
2860
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2441
2861
|
if (state.rootSpan) {
|
|
2442
2862
|
const outputAttrs = [];
|
|
2443
2863
|
if (state.recordOutputs) {
|
|
2444
2864
|
outputAttrs.push(
|
|
2445
2865
|
attrString("ai.response.finishReason", event.finishReason),
|
|
2446
|
-
attrString("ai.response.text", (_a = event.text) != null ? _a : void 0),
|
|
2866
|
+
attrString("ai.response.text", capText2((_a = event.text) != null ? _a : void 0)),
|
|
2447
2867
|
attrString(
|
|
2448
2868
|
"ai.response.providerMetadata",
|
|
2449
2869
|
event.providerMetadata ? safeJsonWithUint8(event.providerMetadata) : void 0
|
|
@@ -2453,7 +2873,7 @@ var RaindropTelemetryIntegration = class {
|
|
|
2453
2873
|
outputAttrs.push(
|
|
2454
2874
|
attrString(
|
|
2455
2875
|
"ai.response.toolCalls",
|
|
2456
|
-
|
|
2876
|
+
safeJsonWithUint8(
|
|
2457
2877
|
event.toolCalls.map((tc) => ({
|
|
2458
2878
|
toolCallId: tc.toolCallId,
|
|
2459
2879
|
toolName: tc.toolName,
|
|
@@ -2466,7 +2886,7 @@ var RaindropTelemetryIntegration = class {
|
|
|
2466
2886
|
if (((_c = event.reasoning) == null ? void 0 : _c.length) > 0) {
|
|
2467
2887
|
const reasoningText = event.reasoning.filter((part) => "text" in part).map((part) => part.text).join("\n");
|
|
2468
2888
|
if (reasoningText) {
|
|
2469
|
-
outputAttrs.push(attrString("ai.response.reasoning", reasoningText));
|
|
2889
|
+
outputAttrs.push(attrString("ai.response.reasoning", capText2(reasoningText)));
|
|
2470
2890
|
}
|
|
2471
2891
|
}
|
|
2472
2892
|
}
|
|
@@ -2485,38 +2905,48 @@ var RaindropTelemetryIntegration = class {
|
|
|
2485
2905
|
);
|
|
2486
2906
|
this.traceShipper.endSpan(state.rootSpan, { attributes: outputAttrs });
|
|
2487
2907
|
}
|
|
2908
|
+
this.finalizeGenerateEvent(
|
|
2909
|
+
state,
|
|
2910
|
+
(_e = event.text) != null ? _e : state.accumulatedText || void 0,
|
|
2911
|
+
(_f = event.response) == null ? void 0 : _f.modelId
|
|
2912
|
+
);
|
|
2913
|
+
}
|
|
2914
|
+
/**
|
|
2915
|
+
* Patch the Raindrop event for a completed, aborted, or errored text
|
|
2916
|
+
* generation so it leaves the `isPending` state. Shared by `onFinish`/`onEnd`,
|
|
2917
|
+
* `onAbort`, and `onError`.
|
|
2918
|
+
*/
|
|
2919
|
+
finalizeGenerateEvent(state, output, model) {
|
|
2920
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
2488
2921
|
const suppressSubagentEvent = state.subagentName !== void 0 && state.subagentToolCallSpan !== void 0;
|
|
2489
|
-
if (this.sendEvents
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
);
|
|
2516
|
-
}
|
|
2517
|
-
});
|
|
2922
|
+
if (!this.sendEvents || suppressSubagentEvent) return;
|
|
2923
|
+
const callMeta = this.extractRaindropMetadata(state.metadata);
|
|
2924
|
+
const userId = (_b = callMeta.userId) != null ? _b : (_a = this.defaultContext) == null ? void 0 : _a.userId;
|
|
2925
|
+
if (!userId) return;
|
|
2926
|
+
const eventName = (_e = (_d = callMeta.eventName) != null ? _d : (_c = this.defaultContext) == null ? void 0 : _c.eventName) != null ? _e : state.operationId;
|
|
2927
|
+
const cappedOutput = capText2(output);
|
|
2928
|
+
const input = capText2(state.inputText);
|
|
2929
|
+
const properties = {
|
|
2930
|
+
...(_f = this.defaultContext) == null ? void 0 : _f.properties,
|
|
2931
|
+
...callMeta.properties
|
|
2932
|
+
};
|
|
2933
|
+
const convoId = (_h = callMeta.convoId) != null ? _h : (_g = this.defaultContext) == null ? void 0 : _g.convoId;
|
|
2934
|
+
void this.eventShipper.patch(state.eventId, {
|
|
2935
|
+
eventName,
|
|
2936
|
+
userId,
|
|
2937
|
+
convoId,
|
|
2938
|
+
input,
|
|
2939
|
+
output: cappedOutput,
|
|
2940
|
+
model,
|
|
2941
|
+
properties: Object.keys(properties).length > 0 ? properties : void 0,
|
|
2942
|
+
isPending: false
|
|
2943
|
+
}).catch((err) => {
|
|
2944
|
+
if (this.debug) {
|
|
2945
|
+
console.warn(
|
|
2946
|
+
`[raindrop-ai/ai-sdk] event patch failed: ${err instanceof Error ? err.message : err}`
|
|
2947
|
+
);
|
|
2518
2948
|
}
|
|
2519
|
-
}
|
|
2949
|
+
});
|
|
2520
2950
|
}
|
|
2521
2951
|
finishEmbed(event, state) {
|
|
2522
2952
|
var _a;
|
|
@@ -2979,13 +3409,14 @@ function shouldKeepEventPending(params) {
|
|
|
2979
3409
|
return params.finishReason === "tool-calls" || params.finishReason === "tool_calls";
|
|
2980
3410
|
}
|
|
2981
3411
|
function normalizePromptAttr(arg) {
|
|
3412
|
+
var _a, _b;
|
|
2982
3413
|
if (!isRecord(arg)) return arg;
|
|
2983
3414
|
const system = arg["system"];
|
|
2984
3415
|
const prompt = arg["prompt"];
|
|
2985
3416
|
const messages = arg["messages"];
|
|
2986
3417
|
if (Array.isArray(messages)) {
|
|
2987
3418
|
if (system) {
|
|
2988
|
-
const sysContent = typeof system === "string" ? system :
|
|
3419
|
+
const sysContent = typeof system === "string" ? system : (_a = safeJsonWithUint8(system)) != null ? _a : String(system);
|
|
2989
3420
|
return [{ role: "system", content: sysContent }, ...messages];
|
|
2990
3421
|
}
|
|
2991
3422
|
return messages;
|
|
@@ -2993,7 +3424,10 @@ function normalizePromptAttr(arg) {
|
|
|
2993
3424
|
if (typeof prompt === "string") {
|
|
2994
3425
|
const msgs = [];
|
|
2995
3426
|
if (system) {
|
|
2996
|
-
msgs.push({
|
|
3427
|
+
msgs.push({
|
|
3428
|
+
role: "system",
|
|
3429
|
+
content: typeof system === "string" ? system : (_b = safeJsonWithUint8(system)) != null ? _b : String(system)
|
|
3430
|
+
});
|
|
2997
3431
|
}
|
|
2998
3432
|
msgs.push({ role: "user", content: prompt });
|
|
2999
3433
|
return msgs;
|
|
@@ -3162,7 +3596,8 @@ function createFinalize(params) {
|
|
|
3162
3596
|
};
|
|
3163
3597
|
const built = await maybeBuildEvent(options.buildEvent, allMessages);
|
|
3164
3598
|
const patch = mergeBuildEventPatch(defaultPatch, built);
|
|
3165
|
-
const output = patch.output;
|
|
3599
|
+
const output = capText2(patch.output);
|
|
3600
|
+
const input = capText2(patch.input);
|
|
3166
3601
|
const finalModel = (_c = patch.model) != null ? _c : model;
|
|
3167
3602
|
if (setup.rootSpan) {
|
|
3168
3603
|
const spanEndTimeUnixNano = nowUnixNanoString();
|
|
@@ -3206,7 +3641,7 @@ function createFinalize(params) {
|
|
|
3206
3641
|
eventName: patch.eventName,
|
|
3207
3642
|
userId: setup.ctx.userId,
|
|
3208
3643
|
convoId: setup.ctx.convoId,
|
|
3209
|
-
input
|
|
3644
|
+
input,
|
|
3210
3645
|
output,
|
|
3211
3646
|
model: finalModel,
|
|
3212
3647
|
properties: patch.properties,
|
|
@@ -3721,7 +4156,8 @@ function wrapAgentGenerate(generate, instance, agentSettings, className, aiSDK,
|
|
|
3721
4156
|
};
|
|
3722
4157
|
const built = await maybeBuildEvent(deps.options.buildEvent, allMessages);
|
|
3723
4158
|
const patch = mergeBuildEventPatch(defaultPatch, built);
|
|
3724
|
-
const output = patch.output;
|
|
4159
|
+
const output = capText2(patch.output);
|
|
4160
|
+
const input = capText2(patch.input);
|
|
3725
4161
|
const finalModel = (_c2 = patch.model) != null ? _c2 : model;
|
|
3726
4162
|
if (rootSpan) {
|
|
3727
4163
|
const spanEndTimeUnixNano = nowUnixNanoString();
|
|
@@ -3779,7 +4215,7 @@ function wrapAgentGenerate(generate, instance, agentSettings, className, aiSDK,
|
|
|
3779
4215
|
eventName: patch.eventName,
|
|
3780
4216
|
userId: ctx.userId,
|
|
3781
4217
|
convoId: ctx.convoId,
|
|
3782
|
-
input
|
|
4218
|
+
input,
|
|
3783
4219
|
output,
|
|
3784
4220
|
model: finalModel,
|
|
3785
4221
|
properties: patch.properties,
|
|
@@ -3928,7 +4364,8 @@ function wrapAgentStream(stream, instance, agentSettings, className, aiSDK, deps
|
|
|
3928
4364
|
};
|
|
3929
4365
|
const built = await maybeBuildEvent(deps.options.buildEvent, allMessages);
|
|
3930
4366
|
const patch = mergeBuildEventPatch(defaultPatch, built);
|
|
3931
|
-
const output = patch.output;
|
|
4367
|
+
const output = capText2(patch.output);
|
|
4368
|
+
const input = capText2(patch.input);
|
|
3932
4369
|
const finalModel = (_c2 = patch.model) != null ? _c2 : model;
|
|
3933
4370
|
if (rootSpan) {
|
|
3934
4371
|
const spanEndTimeUnixNano = nowUnixNanoString();
|
|
@@ -3986,7 +4423,7 @@ function wrapAgentStream(stream, instance, agentSettings, className, aiSDK, deps
|
|
|
3986
4423
|
eventName: patch.eventName,
|
|
3987
4424
|
userId: ctx.userId,
|
|
3988
4425
|
convoId: ctx.convoId,
|
|
3989
|
-
input
|
|
4426
|
+
input,
|
|
3990
4427
|
output,
|
|
3991
4428
|
model: finalModel,
|
|
3992
4429
|
properties: patch.properties,
|
|
@@ -4326,7 +4763,7 @@ function wrapModel(args, aiSDK, outerOperationId, ctx) {
|
|
|
4326
4763
|
attributes: [
|
|
4327
4764
|
...((_a = ctx.telemetry) == null ? void 0 : _a.recordOutputs) === false ? [] : [
|
|
4328
4765
|
attrString("ai.response.finishReason", finishReason),
|
|
4329
|
-
attrString("ai.response.text", activeText.length ? activeText : void 0),
|
|
4766
|
+
attrString("ai.response.text", activeText.length ? capText2(activeText) : void 0),
|
|
4330
4767
|
attrString(
|
|
4331
4768
|
"ai.response.toolCalls",
|
|
4332
4769
|
safeJsonWithUint8(toolCallsLocal.length ? toolCallsLocal : void 0)
|
|
@@ -4379,7 +4816,9 @@ function wrapModel(args, aiSDK, outerOperationId, ctx) {
|
|
|
4379
4816
|
} else if (typeof value["textDelta"] === "string") {
|
|
4380
4817
|
textDelta = value["textDelta"];
|
|
4381
4818
|
}
|
|
4382
|
-
if (typeof textDelta === "string"
|
|
4819
|
+
if (typeof textDelta === "string" && activeText.length <= effectiveMaxTextFieldChars()) {
|
|
4820
|
+
activeText += textDelta;
|
|
4821
|
+
}
|
|
4383
4822
|
}
|
|
4384
4823
|
if (type === "finish") finishReason = extractFinishReason(value);
|
|
4385
4824
|
if (type === "tool-call") toolCallsLocal.push(value);
|
|
@@ -4652,8 +5091,8 @@ function eventMetadataFromChatRequest(options) {
|
|
|
4652
5091
|
});
|
|
4653
5092
|
}
|
|
4654
5093
|
function stringify(v) {
|
|
4655
|
-
if (typeof v === "string") return v;
|
|
4656
|
-
return
|
|
5094
|
+
if (typeof v === "string") return capText2(v);
|
|
5095
|
+
return boundedStringify(v);
|
|
4657
5096
|
}
|
|
4658
5097
|
function userAttrsToOtlp(attrs) {
|
|
4659
5098
|
if (!attrs) return [];
|
|
@@ -4671,6 +5110,7 @@ function createRaindropAISDK(opts) {
|
|
|
4671
5110
|
const eventsRequested = ((_a = opts.events) == null ? void 0 : _a.enabled) !== false;
|
|
4672
5111
|
const tracesRequested = ((_b = opts.traces) == null ? void 0 : _b.enabled) !== false;
|
|
4673
5112
|
const envDebug = envDebugEnabled();
|
|
5113
|
+
setMaxTextFieldChars(opts.maxTextFieldChars);
|
|
4674
5114
|
const localWorkshopInput = opts.localWorkshopUrl === false ? null : opts.localWorkshopUrl;
|
|
4675
5115
|
const resolvedLocalDebuggerUrl = resolveLocalDebuggerBaseUrl(localWorkshopInput);
|
|
4676
5116
|
const localDebuggerUrl = localWorkshopInput === null ? null : resolvedLocalDebuggerUrl != null ? resolvedLocalDebuggerUrl : void 0;
|
|
@@ -4849,17 +5289,28 @@ function createRaindropAISDK(opts) {
|
|
|
4849
5289
|
}
|
|
4850
5290
|
};
|
|
4851
5291
|
}
|
|
5292
|
+
function raindrop(options = {}) {
|
|
5293
|
+
var _a;
|
|
5294
|
+
const { context, subagentWrapping, writeKey, ...rest } = options;
|
|
5295
|
+
const resolvedWriteKey = writeKey != null ? writeKey : typeof process !== "undefined" ? (_a = process.env) == null ? void 0 : _a.RAINDROP_WRITE_KEY : void 0;
|
|
5296
|
+
const client = createRaindropAISDK({ ...rest, writeKey: resolvedWriteKey });
|
|
5297
|
+
return client.createTelemetryIntegration({ context, subagentWrapping });
|
|
5298
|
+
}
|
|
4852
5299
|
|
|
4853
5300
|
// src/index.node.ts
|
|
4854
5301
|
globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = async_hooks.AsyncLocalStorage;
|
|
4855
5302
|
|
|
5303
|
+
exports.DEFAULT_MAX_TEXT_FIELD_CHARS = DEFAULT_MAX_TEXT_FIELD_CHARS2;
|
|
4856
5304
|
exports.DEFAULT_REDACT_ATTRIBUTE_KEYS = DEFAULT_REDACT_ATTRIBUTE_KEYS;
|
|
4857
5305
|
exports.DEFAULT_SECRET_KEY_NAMES = DEFAULT_SECRET_KEY_NAMES;
|
|
4858
5306
|
exports.REDACTED_PLACEHOLDER = REDACTED_PLACEHOLDER;
|
|
4859
5307
|
exports.RaindropTelemetryIntegration = RaindropTelemetryIntegration;
|
|
5308
|
+
exports.TRUNCATION_MARKER = TRUNCATION_MARKER2;
|
|
4860
5309
|
exports._resetParentToolContextStorage = _resetParentToolContextStorage;
|
|
4861
5310
|
exports._resetRaindropCallMetadataStorage = _resetRaindropCallMetadataStorage;
|
|
4862
5311
|
exports._resetWarnedMissingUserId = _resetWarnedMissingUserId;
|
|
5312
|
+
exports.boundedStringify = boundedStringify;
|
|
5313
|
+
exports.capText = capText2;
|
|
4863
5314
|
exports.clearParentToolContext = clearParentToolContext;
|
|
4864
5315
|
exports.createRaindropAISDK = createRaindropAISDK;
|
|
4865
5316
|
exports.currentSpan = currentSpan;
|
|
@@ -4870,6 +5321,7 @@ exports.eventMetadataFromChatRequest = eventMetadataFromChatRequest;
|
|
|
4870
5321
|
exports.getContextManager = getContextManager;
|
|
4871
5322
|
exports.getCurrentParentToolContext = getCurrentParentToolContext;
|
|
4872
5323
|
exports.getCurrentRaindropCallMetadata = getCurrentRaindropCallMetadata;
|
|
5324
|
+
exports.raindrop = raindrop;
|
|
4873
5325
|
exports.readRaindropCallMetadataFromArgs = readRaindropCallMetadataFromArgs;
|
|
4874
5326
|
exports.redactJsonAttributeValue = redactJsonAttributeValue;
|
|
4875
5327
|
exports.redactSecretsInObject = redactSecretsInObject;
|