@raindrop-ai/langchain 0.0.2 → 0.0.4
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/LICENSE +21 -0
- package/README.md +2 -1
- package/dist/index.d.mts +166 -1
- package/dist/index.d.ts +166 -1
- package/dist/index.js +703 -62
- package/dist/index.mjs +703 -62
- package/package.json +36 -15
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/callback-handler.ts
|
|
2
2
|
import { BaseCallbackHandler } from "@langchain/core/callbacks/base";
|
|
3
3
|
|
|
4
|
-
// ../core/dist/chunk-
|
|
4
|
+
// ../core/dist/chunk-U5HUTMR5.js
|
|
5
5
|
function getCrypto() {
|
|
6
6
|
const c = globalThis.crypto;
|
|
7
7
|
return c;
|
|
@@ -56,6 +56,8 @@ function base64Encode(bytes) {
|
|
|
56
56
|
}
|
|
57
57
|
return out;
|
|
58
58
|
}
|
|
59
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
60
|
+
var MAX_RETRY_DELAY_MS = 3e4;
|
|
59
61
|
function wait(ms) {
|
|
60
62
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
61
63
|
}
|
|
@@ -63,6 +65,46 @@ function formatEndpoint(endpoint) {
|
|
|
63
65
|
if (!endpoint) return void 0;
|
|
64
66
|
return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
|
|
65
67
|
}
|
|
68
|
+
function redactUrlForLog(url) {
|
|
69
|
+
try {
|
|
70
|
+
const parsed = new URL(url);
|
|
71
|
+
parsed.username = "";
|
|
72
|
+
parsed.password = "";
|
|
73
|
+
parsed.search = "";
|
|
74
|
+
parsed.hash = "";
|
|
75
|
+
return parsed.toString();
|
|
76
|
+
} catch (e) {
|
|
77
|
+
return "<unparseable-url>";
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
var RATE_LIMITED_LOG_INTERVAL_MS = 3e4;
|
|
81
|
+
var rateLimitedLogLast = /* @__PURE__ */ new Map();
|
|
82
|
+
function rateLimitedLog(key, log) {
|
|
83
|
+
const now = Date.now();
|
|
84
|
+
const last = rateLimitedLogLast.get(key);
|
|
85
|
+
if (last !== void 0 && now - last < RATE_LIMITED_LOG_INTERVAL_MS) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
rateLimitedLogLast.set(key, now);
|
|
89
|
+
log();
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
async function raceWithTimeout(promise, timeoutMs) {
|
|
93
|
+
let timer;
|
|
94
|
+
const settledInTime = await Promise.race([
|
|
95
|
+
promise.then(
|
|
96
|
+
() => true,
|
|
97
|
+
() => true
|
|
98
|
+
),
|
|
99
|
+
new Promise((resolve) => {
|
|
100
|
+
var _a;
|
|
101
|
+
timer = setTimeout(() => resolve(false), Math.max(0, timeoutMs));
|
|
102
|
+
(_a = timer.unref) == null ? void 0 : _a.call(timer);
|
|
103
|
+
})
|
|
104
|
+
]);
|
|
105
|
+
if (timer) clearTimeout(timer);
|
|
106
|
+
return settledInTime;
|
|
107
|
+
}
|
|
66
108
|
function parseRetryAfter(headers) {
|
|
67
109
|
var _a;
|
|
68
110
|
const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
|
|
@@ -79,12 +121,12 @@ function parseRetryAfter(headers) {
|
|
|
79
121
|
function getRetryDelayMs(attemptNumber, previousError) {
|
|
80
122
|
if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
|
|
81
123
|
const v = previousError.retryAfterMs;
|
|
82
|
-
if (typeof v === "number") return Math.max(0, v);
|
|
124
|
+
if (typeof v === "number") return Math.min(Math.max(0, v), MAX_RETRY_DELAY_MS);
|
|
83
125
|
}
|
|
84
126
|
if (attemptNumber <= 1) return 0;
|
|
85
127
|
const base = 500;
|
|
86
128
|
const factor = Math.pow(2, attemptNumber - 2);
|
|
87
|
-
return base * factor;
|
|
129
|
+
return Math.min(base * factor, MAX_RETRY_DELAY_MS);
|
|
88
130
|
}
|
|
89
131
|
async function withRetry(operation, opName, opts) {
|
|
90
132
|
const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
|
|
@@ -119,7 +161,9 @@ async function withRetry(operation, opName, opts) {
|
|
|
119
161
|
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
120
162
|
}
|
|
121
163
|
async function postJson(url, body, headers, opts) {
|
|
122
|
-
|
|
164
|
+
var _a;
|
|
165
|
+
const opName = `POST ${redactUrlForLog(url)}`;
|
|
166
|
+
const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
|
|
123
167
|
await withRetry(
|
|
124
168
|
async () => {
|
|
125
169
|
const resp = await fetch(url, {
|
|
@@ -128,7 +172,8 @@ async function postJson(url, body, headers, opts) {
|
|
|
128
172
|
"Content-Type": "application/json",
|
|
129
173
|
...headers
|
|
130
174
|
},
|
|
131
|
-
body: JSON.stringify(body)
|
|
175
|
+
body: JSON.stringify(body),
|
|
176
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
132
177
|
});
|
|
133
178
|
if (!resp.ok) {
|
|
134
179
|
const text = await resp.text().catch(() => "");
|
|
@@ -145,6 +190,27 @@ async function postJson(url, body, headers, opts) {
|
|
|
145
190
|
opts
|
|
146
191
|
);
|
|
147
192
|
}
|
|
193
|
+
var DEFAULT_MAX_TEXT_FIELD_CHARS = 1e6;
|
|
194
|
+
var TRUNCATION_MARKER = "...[truncated by raindrop]";
|
|
195
|
+
var currentDefaultMaxTextFieldChars = DEFAULT_MAX_TEXT_FIELD_CHARS;
|
|
196
|
+
function resolveMaxTextFieldChars(value) {
|
|
197
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
198
|
+
return Math.floor(value);
|
|
199
|
+
}
|
|
200
|
+
return currentDefaultMaxTextFieldChars;
|
|
201
|
+
}
|
|
202
|
+
function truncateToLimit(text, limit) {
|
|
203
|
+
if (limit > TRUNCATION_MARKER.length) {
|
|
204
|
+
return text.slice(0, limit - TRUNCATION_MARKER.length) + TRUNCATION_MARKER;
|
|
205
|
+
}
|
|
206
|
+
return text.slice(0, Math.max(0, limit));
|
|
207
|
+
}
|
|
208
|
+
function capText(value, limit) {
|
|
209
|
+
if (typeof value !== "string") return value;
|
|
210
|
+
const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
|
|
211
|
+
if (value.length <= max) return value;
|
|
212
|
+
return truncateToLimit(value, max);
|
|
213
|
+
}
|
|
148
214
|
var SpanStatusCode = {
|
|
149
215
|
UNSET: 0,
|
|
150
216
|
OK: 1,
|
|
@@ -202,6 +268,93 @@ function buildExportTraceServiceRequest(spans, serviceName = "raindrop.core", se
|
|
|
202
268
|
]
|
|
203
269
|
};
|
|
204
270
|
}
|
|
271
|
+
var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
|
|
272
|
+
var WORKSHOP_ENV_VAR = "RAINDROP_WORKSHOP";
|
|
273
|
+
var DEFAULT_LOCAL_WORKSHOP_URL = "http://localhost:5899/v1/";
|
|
274
|
+
function readEnvVar(name) {
|
|
275
|
+
var _a;
|
|
276
|
+
try {
|
|
277
|
+
const env = (_a = globalThis == null ? void 0 : globalThis.process) == null ? void 0 : _a.env;
|
|
278
|
+
if (env && typeof env[name] === "string" && env[name].length > 0) {
|
|
279
|
+
return env[name];
|
|
280
|
+
}
|
|
281
|
+
} catch (e) {
|
|
282
|
+
}
|
|
283
|
+
return void 0;
|
|
284
|
+
}
|
|
285
|
+
function readWorkshopEnv() {
|
|
286
|
+
const raw = readEnvVar(WORKSHOP_ENV_VAR);
|
|
287
|
+
if (raw === void 0) return void 0;
|
|
288
|
+
const trimmed = raw.trim();
|
|
289
|
+
if (trimmed.length === 0) return void 0;
|
|
290
|
+
if (/^https?:\/\//i.test(trimmed)) return { url: trimmed };
|
|
291
|
+
if (/^(1|true|yes|on)$/i.test(trimmed)) return "enable";
|
|
292
|
+
if (/^(0|false|no|off)$/i.test(trimmed)) return "disable";
|
|
293
|
+
return void 0;
|
|
294
|
+
}
|
|
295
|
+
function isLocalDevHost(hostname) {
|
|
296
|
+
if (!hostname) return false;
|
|
297
|
+
if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "0.0.0.0" || hostname === "::1") {
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
if (hostname.endsWith(".localhost")) return true;
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
function readRuntimeHostname() {
|
|
304
|
+
try {
|
|
305
|
+
const loc = globalThis == null ? void 0 : globalThis.location;
|
|
306
|
+
if (loc && typeof loc.hostname === "string" && loc.hostname.length > 0) {
|
|
307
|
+
return loc.hostname;
|
|
308
|
+
}
|
|
309
|
+
} catch (e) {
|
|
310
|
+
}
|
|
311
|
+
return void 0;
|
|
312
|
+
}
|
|
313
|
+
function shouldAutoEnableLocalWorkshop() {
|
|
314
|
+
if (isLocalDevHost(readRuntimeHostname())) return true;
|
|
315
|
+
if (readEnvVar("NODE_ENV") === "development") return true;
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
function resolveLocalDebuggerBaseUrl(baseUrl) {
|
|
319
|
+
var _a, _b, _c;
|
|
320
|
+
if (baseUrl === null) return null;
|
|
321
|
+
if (typeof baseUrl === "string" && baseUrl.length > 0) {
|
|
322
|
+
return (_a = formatEndpoint(baseUrl)) != null ? _a : null;
|
|
323
|
+
}
|
|
324
|
+
const explicitUrlEnv = readEnvVar(LOCAL_DEBUGGER_ENV_VAR);
|
|
325
|
+
if (explicitUrlEnv) return (_b = formatEndpoint(explicitUrlEnv)) != null ? _b : null;
|
|
326
|
+
const workshopEnv = readWorkshopEnv();
|
|
327
|
+
if (workshopEnv === "disable") return null;
|
|
328
|
+
if (workshopEnv === "enable") return DEFAULT_LOCAL_WORKSHOP_URL;
|
|
329
|
+
if (workshopEnv && "url" in workshopEnv) return (_c = formatEndpoint(workshopEnv.url)) != null ? _c : null;
|
|
330
|
+
if (shouldAutoEnableLocalWorkshop()) return DEFAULT_LOCAL_WORKSHOP_URL;
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
function mirrorTraceExportToLocalDebugger(body, options = {}) {
|
|
334
|
+
var _a;
|
|
335
|
+
const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
|
|
336
|
+
if (!baseUrl) return;
|
|
337
|
+
void postJson(`${baseUrl}traces`, body, {}, {
|
|
338
|
+
maxAttempts: 1,
|
|
339
|
+
debug: (_a = options.debug) != null ? _a : false,
|
|
340
|
+
sdkName: options.sdkName
|
|
341
|
+
}).catch(() => {
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
function mirrorPartialEventToLocalDebugger(event, options = {}) {
|
|
345
|
+
var _a;
|
|
346
|
+
const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
|
|
347
|
+
if (!baseUrl) return;
|
|
348
|
+
const headers = options.writeKey ? { Authorization: `Bearer ${options.writeKey}` } : {};
|
|
349
|
+
void postJson(`${baseUrl}events/track_partial`, event, headers, {
|
|
350
|
+
maxAttempts: 1,
|
|
351
|
+
debug: (_a = options.debug) != null ? _a : false,
|
|
352
|
+
sdkName: options.sdkName
|
|
353
|
+
}).catch(() => {
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
var SHUTDOWN_DEADLINE_MS = 1e4;
|
|
357
|
+
var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
205
358
|
function mergePatches(target, source) {
|
|
206
359
|
var _a, _b, _c, _d;
|
|
207
360
|
const out = { ...target, ...source };
|
|
@@ -219,7 +372,8 @@ var EventShipper = class {
|
|
|
219
372
|
this.sticky = /* @__PURE__ */ new Map();
|
|
220
373
|
this.timers = /* @__PURE__ */ new Map();
|
|
221
374
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
222
|
-
|
|
375
|
+
this.hasShutdown = false;
|
|
376
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
223
377
|
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
224
378
|
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
225
379
|
this.enabled = opts.enabled !== false;
|
|
@@ -228,11 +382,16 @@ var EventShipper = class {
|
|
|
228
382
|
this.sdkName = (_d = opts.sdkName) != null ? _d : "core";
|
|
229
383
|
this.prefix = `[raindrop-ai/${this.sdkName}]`;
|
|
230
384
|
this.defaultEventName = (_e = opts.defaultEventName) != null ? _e : "ai_generation";
|
|
385
|
+
this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
|
|
386
|
+
this.localDebuggerUrl = (_f = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _f : void 0;
|
|
387
|
+
if (this.debug && this.localDebuggerUrl) {
|
|
388
|
+
console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
|
|
389
|
+
}
|
|
231
390
|
const isNode = typeof process !== "undefined" && typeof process.version === "string";
|
|
232
391
|
this.context = {
|
|
233
392
|
library: {
|
|
234
|
-
name: (
|
|
235
|
-
version: (
|
|
393
|
+
name: (_g = opts.libraryName) != null ? _g : "@raindrop-ai/core",
|
|
394
|
+
version: (_h = opts.libraryVersion) != null ? _h : "0.0.0"
|
|
236
395
|
},
|
|
237
396
|
metadata: {
|
|
238
397
|
jsRuntime: isNode ? "node" : "web",
|
|
@@ -246,10 +405,52 @@ var EventShipper = class {
|
|
|
246
405
|
authHeaders() {
|
|
247
406
|
return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
|
|
248
407
|
}
|
|
408
|
+
/**
|
|
409
|
+
* Build the retry/timeout options for one POST, honoring the shutdown
|
|
410
|
+
* deadline. Returns `null` when the shutdown drain window is exhausted —
|
|
411
|
+
* the caller must drop the payload (with a rate-limited warning) instead
|
|
412
|
+
* of issuing a request that could outlive process exit.
|
|
413
|
+
*
|
|
414
|
+
* Checked fresh on EVERY send, so a shutdown that begins while the flush
|
|
415
|
+
* path is mid-drain takes effect immediately: no further retries, and the
|
|
416
|
+
* per-attempt timeout is clamped to the remaining window. After
|
|
417
|
+
* `shutdown()` returns (deadline cleared, `hasShutdown` still set),
|
|
418
|
+
* sends — late callers, or flush work the deadline abandoned mid-drain —
|
|
419
|
+
* run as a single short attempt rather than regaining the full retry
|
|
420
|
+
* schedule.
|
|
421
|
+
*/
|
|
422
|
+
requestOpts() {
|
|
423
|
+
if (this.shutdownDeadlineAt !== void 0) {
|
|
424
|
+
const remainingMs = this.shutdownDeadlineAt - Date.now();
|
|
425
|
+
if (remainingMs <= 0) return null;
|
|
426
|
+
return {
|
|
427
|
+
maxAttempts: 1,
|
|
428
|
+
debug: this.debug,
|
|
429
|
+
sdkName: this.sdkName,
|
|
430
|
+
timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
if (this.hasShutdown) {
|
|
434
|
+
return {
|
|
435
|
+
maxAttempts: 1,
|
|
436
|
+
debug: this.debug,
|
|
437
|
+
sdkName: this.sdkName,
|
|
438
|
+
timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
|
|
442
|
+
}
|
|
249
443
|
async patch(eventId, patch) {
|
|
250
444
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
251
445
|
if (!this.enabled) return;
|
|
252
446
|
if (!eventId || !eventId.trim()) return;
|
|
447
|
+
const maxChars = resolveMaxTextFieldChars(this.maxTextFieldCharsOpt);
|
|
448
|
+
if (typeof patch.input === "string" && patch.input.length > maxChars) {
|
|
449
|
+
patch = { ...patch, input: capText(patch.input, maxChars) };
|
|
450
|
+
}
|
|
451
|
+
if (typeof patch.output === "string" && patch.output.length > maxChars) {
|
|
452
|
+
patch = { ...patch, output: capText(patch.output, maxChars) };
|
|
453
|
+
}
|
|
253
454
|
if (this.debug) {
|
|
254
455
|
console.log(`${this.prefix} queue patch`, {
|
|
255
456
|
eventId,
|
|
@@ -296,9 +497,16 @@ var EventShipper = class {
|
|
|
296
497
|
})));
|
|
297
498
|
}
|
|
298
499
|
async shutdown() {
|
|
299
|
-
|
|
300
|
-
this.
|
|
301
|
-
|
|
500
|
+
this.hasShutdown = true;
|
|
501
|
+
this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
|
|
502
|
+
try {
|
|
503
|
+
for (const t of this.timers.values()) clearTimeout(t);
|
|
504
|
+
this.timers.clear();
|
|
505
|
+
const settled = await raceWithTimeout(this.flush(), SHUTDOWN_DEADLINE_MS);
|
|
506
|
+
if (!settled) this.warnShutdownDrop("in-flight request(s) at shutdown");
|
|
507
|
+
} finally {
|
|
508
|
+
this.shutdownDeadlineAt = void 0;
|
|
509
|
+
}
|
|
302
510
|
}
|
|
303
511
|
async trackSignal(signal) {
|
|
304
512
|
var _a, _b;
|
|
@@ -318,16 +526,21 @@ var EventShipper = class {
|
|
|
318
526
|
}
|
|
319
527
|
}
|
|
320
528
|
];
|
|
529
|
+
if (!this.writeKey) return;
|
|
321
530
|
const url = `${this.baseUrl}signals/track`;
|
|
531
|
+
const opts = this.requestOpts();
|
|
532
|
+
if (!opts) {
|
|
533
|
+
this.warnShutdownDrop("signal");
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
322
536
|
try {
|
|
323
|
-
await postJson(url, body, this.authHeaders(),
|
|
324
|
-
maxAttempts: 3,
|
|
325
|
-
debug: this.debug,
|
|
326
|
-
sdkName: this.sdkName
|
|
327
|
-
});
|
|
537
|
+
await postJson(url, body, this.authHeaders(), opts);
|
|
328
538
|
} catch (err) {
|
|
329
539
|
const msg = err instanceof Error ? err.message : String(err);
|
|
330
|
-
|
|
540
|
+
rateLimitedLog(
|
|
541
|
+
`${this.prefix}.send_signal_failed`,
|
|
542
|
+
() => console.warn(`${this.prefix} failed to send signal (dropping): ${msg}`)
|
|
543
|
+
);
|
|
331
544
|
}
|
|
332
545
|
}
|
|
333
546
|
async identify(users) {
|
|
@@ -348,19 +561,30 @@ var EventShipper = class {
|
|
|
348
561
|
traits: (_a = user.traits) != null ? _a : {}
|
|
349
562
|
};
|
|
350
563
|
});
|
|
564
|
+
if (!this.writeKey) return;
|
|
351
565
|
if (body.length === 0) return;
|
|
352
566
|
const url = `${this.baseUrl}users/identify`;
|
|
567
|
+
const opts = this.requestOpts();
|
|
568
|
+
if (!opts) {
|
|
569
|
+
this.warnShutdownDrop("identify");
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
353
572
|
try {
|
|
354
|
-
await postJson(url, body, this.authHeaders(),
|
|
355
|
-
maxAttempts: 3,
|
|
356
|
-
debug: this.debug,
|
|
357
|
-
sdkName: this.sdkName
|
|
358
|
-
});
|
|
573
|
+
await postJson(url, body, this.authHeaders(), opts);
|
|
359
574
|
} catch (err) {
|
|
360
575
|
const msg = err instanceof Error ? err.message : String(err);
|
|
361
|
-
|
|
576
|
+
rateLimitedLog(
|
|
577
|
+
`${this.prefix}.send_identify_failed`,
|
|
578
|
+
() => console.warn(`${this.prefix} failed to send identify (dropping): ${msg}`)
|
|
579
|
+
);
|
|
362
580
|
}
|
|
363
581
|
}
|
|
582
|
+
warnShutdownDrop(what) {
|
|
583
|
+
rateLimitedLog(
|
|
584
|
+
`${this.prefix}.shutdown_deadline`,
|
|
585
|
+
() => console.warn(`${this.prefix} shutdown flush deadline exceeded; dropping ${what}`)
|
|
586
|
+
);
|
|
587
|
+
}
|
|
364
588
|
async flushOne(eventId) {
|
|
365
589
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
366
590
|
if (!this.enabled) return;
|
|
@@ -424,11 +648,25 @@ var EventShipper = class {
|
|
|
424
648
|
endpoint: url
|
|
425
649
|
});
|
|
426
650
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
651
|
+
if (this.localDebuggerUrl) {
|
|
652
|
+
mirrorPartialEventToLocalDebugger(payload, {
|
|
653
|
+
baseUrl: this.localDebuggerUrl,
|
|
654
|
+
writeKey: this.writeKey,
|
|
655
|
+
debug: this.debug,
|
|
656
|
+
sdkName: this.sdkName
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
if (!this.writeKey) {
|
|
660
|
+
if (!isPending) this.sticky.delete(eventId);
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
const opts = this.requestOpts();
|
|
664
|
+
if (!opts) {
|
|
665
|
+
this.warnShutdownDrop(`track_partial ${eventId}`);
|
|
666
|
+
if (!isPending) this.sticky.delete(eventId);
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
const p = postJson(url, payload, this.authHeaders(), opts);
|
|
432
670
|
this.inFlight.add(p);
|
|
433
671
|
try {
|
|
434
672
|
try {
|
|
@@ -438,7 +676,10 @@ var EventShipper = class {
|
|
|
438
676
|
}
|
|
439
677
|
} catch (err) {
|
|
440
678
|
const msg = err instanceof Error ? err.message : String(err);
|
|
441
|
-
|
|
679
|
+
rateLimitedLog(
|
|
680
|
+
`${this.prefix}.send_track_partial_failed`,
|
|
681
|
+
() => console.warn(`${this.prefix} failed to send track_partial (dropping): ${msg}`)
|
|
682
|
+
);
|
|
442
683
|
}
|
|
443
684
|
} finally {
|
|
444
685
|
this.inFlight.delete(p);
|
|
@@ -448,11 +689,114 @@ var EventShipper = class {
|
|
|
448
689
|
}
|
|
449
690
|
}
|
|
450
691
|
};
|
|
692
|
+
var DEFAULT_SECRET_KEY_NAMES = [
|
|
693
|
+
"apikey",
|
|
694
|
+
"apisecret",
|
|
695
|
+
"apitoken",
|
|
696
|
+
"secretaccesskey",
|
|
697
|
+
"sessiontoken",
|
|
698
|
+
"privatekey",
|
|
699
|
+
"privatekeyid",
|
|
700
|
+
"clientsecret",
|
|
701
|
+
"accesstoken",
|
|
702
|
+
"refreshtoken",
|
|
703
|
+
"oauthtoken",
|
|
704
|
+
"bearertoken",
|
|
705
|
+
"authorization",
|
|
706
|
+
"password",
|
|
707
|
+
"passphrase"
|
|
708
|
+
];
|
|
709
|
+
var REDACTED_PLACEHOLDER = "[REDACTED]";
|
|
710
|
+
function normalizeKeyName(name) {
|
|
711
|
+
return name.toLowerCase().replace(/[-_.]/g, "");
|
|
712
|
+
}
|
|
713
|
+
function redactSecretsInObject(value, options) {
|
|
714
|
+
var _a, _b;
|
|
715
|
+
const normalizedSecretSet = buildSecretSet((_a = options == null ? void 0 : options.secretKeyNames) != null ? _a : DEFAULT_SECRET_KEY_NAMES);
|
|
716
|
+
const placeholder = (_b = options == null ? void 0 : options.placeholder) != null ? _b : REDACTED_PLACEHOLDER;
|
|
717
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
718
|
+
const walk = (node) => {
|
|
719
|
+
if (node === null || typeof node !== "object") return node;
|
|
720
|
+
if (seen.has(node)) return "[CIRCULAR]";
|
|
721
|
+
seen.add(node);
|
|
722
|
+
if (Array.isArray(node)) {
|
|
723
|
+
return node.map((item) => walk(item));
|
|
724
|
+
}
|
|
725
|
+
const out = {};
|
|
726
|
+
for (const [k, v] of Object.entries(node)) {
|
|
727
|
+
if (normalizedSecretSet.has(normalizeKeyName(k))) {
|
|
728
|
+
out[k] = placeholder;
|
|
729
|
+
} else {
|
|
730
|
+
out[k] = walk(v);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
return out;
|
|
734
|
+
};
|
|
735
|
+
return walk(value);
|
|
736
|
+
}
|
|
737
|
+
function buildSecretSet(names) {
|
|
738
|
+
const set = /* @__PURE__ */ new Set();
|
|
739
|
+
for (const name of names) set.add(normalizeKeyName(name));
|
|
740
|
+
return set;
|
|
741
|
+
}
|
|
742
|
+
var DEFAULT_REDACT_ATTRIBUTE_KEYS = [
|
|
743
|
+
"ai.request.providerOptions",
|
|
744
|
+
"ai.response.providerMetadata"
|
|
745
|
+
];
|
|
746
|
+
function defaultTransformSpan(span) {
|
|
747
|
+
const attrs = span.attributes;
|
|
748
|
+
if (!attrs || attrs.length === 0) return span;
|
|
749
|
+
let nextAttrs;
|
|
750
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
751
|
+
const attr = attrs[i];
|
|
752
|
+
const redacted = redactJsonAttributeValue(attr.key, attr.value);
|
|
753
|
+
if (redacted === void 0) continue;
|
|
754
|
+
if (!nextAttrs) nextAttrs = attrs.slice();
|
|
755
|
+
nextAttrs[i] = { key: attr.key, value: redacted };
|
|
756
|
+
}
|
|
757
|
+
if (!nextAttrs) return span;
|
|
758
|
+
return { ...span, attributes: nextAttrs };
|
|
759
|
+
}
|
|
760
|
+
var REDACT_JSON_ATTRIBUTE_KEYS = new Set(DEFAULT_REDACT_ATTRIBUTE_KEYS);
|
|
761
|
+
function redactJsonAttributeValue(key, value) {
|
|
762
|
+
if (!REDACT_JSON_ATTRIBUTE_KEYS.has(key)) return void 0;
|
|
763
|
+
const json = value.stringValue;
|
|
764
|
+
if (typeof json !== "string" || json.length === 0) return void 0;
|
|
765
|
+
let parsed;
|
|
766
|
+
try {
|
|
767
|
+
parsed = JSON.parse(json);
|
|
768
|
+
} catch (e) {
|
|
769
|
+
return void 0;
|
|
770
|
+
}
|
|
771
|
+
const scrubbed = redactSecretsInObject(parsed);
|
|
772
|
+
let scrubbedJson;
|
|
773
|
+
try {
|
|
774
|
+
scrubbedJson = JSON.stringify(scrubbed);
|
|
775
|
+
} catch (e) {
|
|
776
|
+
return void 0;
|
|
777
|
+
}
|
|
778
|
+
if (scrubbedJson === json) return void 0;
|
|
779
|
+
return { stringValue: scrubbedJson };
|
|
780
|
+
}
|
|
781
|
+
function applyOtelSpanAttributeLimit(limit) {
|
|
782
|
+
var _a, _b;
|
|
783
|
+
try {
|
|
784
|
+
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;
|
|
785
|
+
if (!raw) return limit;
|
|
786
|
+
const parsed = Number.parseInt(raw, 10);
|
|
787
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
788
|
+
return Math.min(limit, parsed);
|
|
789
|
+
}
|
|
790
|
+
} catch (e) {
|
|
791
|
+
}
|
|
792
|
+
return limit;
|
|
793
|
+
}
|
|
451
794
|
var TraceShipper = class {
|
|
452
795
|
constructor(opts) {
|
|
453
796
|
this.queue = [];
|
|
454
797
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
455
|
-
|
|
798
|
+
this.hasShutdown = false;
|
|
799
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
456
800
|
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
457
801
|
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
458
802
|
this.enabled = opts.enabled !== false;
|
|
@@ -465,6 +809,79 @@ var TraceShipper = class {
|
|
|
465
809
|
this.prefix = `[raindrop-ai/${this.sdkName}]`;
|
|
466
810
|
this.serviceName = (_g = opts.serviceName) != null ? _g : "raindrop.core";
|
|
467
811
|
this.serviceVersion = (_h = opts.serviceVersion) != null ? _h : "0.0.0";
|
|
812
|
+
this.localDebuggerUrl = (_i = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _i : void 0;
|
|
813
|
+
if (this.debug && this.localDebuggerUrl) {
|
|
814
|
+
console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
|
|
815
|
+
}
|
|
816
|
+
this.transformSpanHook = opts.transformSpan;
|
|
817
|
+
this.disableDefaultRedaction = opts.disableDefaultRedaction === true;
|
|
818
|
+
this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Cap every string attribute value on the span. O(#attributes) length
|
|
822
|
+
* checks; only oversized values pay a slice. Runs AFTER the redaction
|
|
823
|
+
* pipeline so the default secret-scrub still sees parseable JSON in
|
|
824
|
+
* `ai.request.providerOptions` / `ai.response.providerMetadata` (capping
|
|
825
|
+
* first could cut a JSON blob mid-way, fail the parse, and ship secrets
|
|
826
|
+
* in the surviving prefix).
|
|
827
|
+
*
|
|
828
|
+
* A stricter `OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT` env var is honored
|
|
829
|
+
* for span content, matching the Python SDK and the OTel SDK convention.
|
|
830
|
+
*/
|
|
831
|
+
capSpanAttributes(span) {
|
|
832
|
+
var _a;
|
|
833
|
+
const maxChars = applyOtelSpanAttributeLimit(
|
|
834
|
+
resolveMaxTextFieldChars(this.maxTextFieldCharsOpt)
|
|
835
|
+
);
|
|
836
|
+
const attrs = span.attributes;
|
|
837
|
+
if (!attrs || attrs.length === 0) return span;
|
|
838
|
+
let nextAttrs;
|
|
839
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
840
|
+
const attr = attrs[i];
|
|
841
|
+
const value = (_a = attr.value) == null ? void 0 : _a.stringValue;
|
|
842
|
+
if (typeof value !== "string" || value.length <= maxChars) continue;
|
|
843
|
+
if (!nextAttrs) nextAttrs = attrs.slice();
|
|
844
|
+
nextAttrs[i] = {
|
|
845
|
+
key: attr.key,
|
|
846
|
+
value: { ...attr.value, stringValue: capText(value, maxChars) }
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
if (!nextAttrs) return span;
|
|
850
|
+
return { ...span, attributes: nextAttrs };
|
|
851
|
+
}
|
|
852
|
+
/**
|
|
853
|
+
* Apply the user `transformSpan` hook (if any) followed by the default
|
|
854
|
+
* redactor (unless disabled). Returns either the (possibly new) span to
|
|
855
|
+
* ship, or `null` to drop the span entirely.
|
|
856
|
+
*
|
|
857
|
+
* Ordering: user hook runs first so callers can rewrite the span freely
|
|
858
|
+
* (rename attrs, add new ones, scrub things the default doesn't know
|
|
859
|
+
* about). The default redactor then runs on whatever the user produced,
|
|
860
|
+
* acting as the always-on floor for documented BYOK secrets. If the user
|
|
861
|
+
* sets `disableDefaultRedaction: true`, the floor is skipped.
|
|
862
|
+
*
|
|
863
|
+
* Fail-closed: if the user hook throws, the span is dropped — a buggy
|
|
864
|
+
* hook can never accidentally ship raw, un-redacted spans.
|
|
865
|
+
*/
|
|
866
|
+
redactSpan(span) {
|
|
867
|
+
let current = span;
|
|
868
|
+
if (this.transformSpanHook) {
|
|
869
|
+
try {
|
|
870
|
+
const result = this.transformSpanHook(current);
|
|
871
|
+
if (result === null) return null;
|
|
872
|
+
if (result !== void 0) current = result;
|
|
873
|
+
} catch (err) {
|
|
874
|
+
if (this.debug) {
|
|
875
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
876
|
+
console.warn(`${this.prefix} transformSpan hook threw: ${msg}`);
|
|
877
|
+
}
|
|
878
|
+
return null;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
if (!this.disableDefaultRedaction) {
|
|
882
|
+
current = defaultTransformSpan(current);
|
|
883
|
+
}
|
|
884
|
+
return this.capSpanAttributes(current);
|
|
468
885
|
}
|
|
469
886
|
isDebugEnabled() {
|
|
470
887
|
return this.debug;
|
|
@@ -481,7 +898,30 @@ var TraceShipper = class {
|
|
|
481
898
|
attrString("ai.operationId", args.operationId)
|
|
482
899
|
];
|
|
483
900
|
if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
|
|
484
|
-
|
|
901
|
+
const span = { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
|
|
902
|
+
this.mirrorToLocalDebugger(
|
|
903
|
+
buildOtlpSpan({
|
|
904
|
+
ids: span.ids,
|
|
905
|
+
name: span.name,
|
|
906
|
+
startTimeUnixNano: span.startTimeUnixNano,
|
|
907
|
+
endTimeUnixNano: span.startTimeUnixNano,
|
|
908
|
+
// placeholder — will be updated on endSpan
|
|
909
|
+
attributes: span.attributes,
|
|
910
|
+
status: { code: SpanStatusCode.UNSET }
|
|
911
|
+
})
|
|
912
|
+
);
|
|
913
|
+
return span;
|
|
914
|
+
}
|
|
915
|
+
mirrorToLocalDebugger(span) {
|
|
916
|
+
if (!this.localDebuggerUrl) return;
|
|
917
|
+
const redacted = this.redactSpan(span);
|
|
918
|
+
if (redacted === null) return;
|
|
919
|
+
const body = buildExportTraceServiceRequest([redacted], this.serviceName, this.serviceVersion);
|
|
920
|
+
mirrorTraceExportToLocalDebugger(body, {
|
|
921
|
+
baseUrl: this.localDebuggerUrl,
|
|
922
|
+
debug: false,
|
|
923
|
+
sdkName: this.sdkName
|
|
924
|
+
});
|
|
485
925
|
}
|
|
486
926
|
endSpan(span, extra) {
|
|
487
927
|
var _a, _b;
|
|
@@ -504,6 +944,7 @@ var TraceShipper = class {
|
|
|
504
944
|
status
|
|
505
945
|
});
|
|
506
946
|
this.enqueue(otlp);
|
|
947
|
+
this.mirrorToLocalDebugger(otlp);
|
|
507
948
|
}
|
|
508
949
|
createSpan(args) {
|
|
509
950
|
var _a;
|
|
@@ -521,6 +962,7 @@ var TraceShipper = class {
|
|
|
521
962
|
status: args.status
|
|
522
963
|
});
|
|
523
964
|
this.enqueue(otlp);
|
|
965
|
+
this.mirrorToLocalDebugger(otlp);
|
|
524
966
|
}
|
|
525
967
|
enqueue(span) {
|
|
526
968
|
if (!this.enabled) return;
|
|
@@ -532,10 +974,12 @@ var TraceShipper = class {
|
|
|
532
974
|
)}`
|
|
533
975
|
);
|
|
534
976
|
}
|
|
977
|
+
const redacted = this.redactSpan(span);
|
|
978
|
+
if (redacted === null) return;
|
|
535
979
|
if (this.queue.length >= this.maxQueueSize) {
|
|
536
980
|
this.queue.shift();
|
|
537
981
|
}
|
|
538
|
-
this.queue.push(
|
|
982
|
+
this.queue.push(redacted);
|
|
539
983
|
if (this.queue.length >= this.maxBatchSize) {
|
|
540
984
|
void this.flush().catch(() => {
|
|
541
985
|
});
|
|
@@ -557,6 +1001,17 @@ var TraceShipper = class {
|
|
|
557
1001
|
}
|
|
558
1002
|
while (this.queue.length > 0) {
|
|
559
1003
|
const batch = this.queue.splice(0, this.maxBatchSize);
|
|
1004
|
+
if (!this.writeKey) continue;
|
|
1005
|
+
const opts = this.requestOpts();
|
|
1006
|
+
if (!opts) {
|
|
1007
|
+
rateLimitedLog(
|
|
1008
|
+
`${this.prefix}.shutdown_deadline`,
|
|
1009
|
+
() => console.warn(
|
|
1010
|
+
`${this.prefix} shutdown flush deadline exceeded; dropping ${batch.length} spans`
|
|
1011
|
+
)
|
|
1012
|
+
);
|
|
1013
|
+
continue;
|
|
1014
|
+
}
|
|
560
1015
|
const body = buildExportTraceServiceRequest(batch, this.serviceName, this.serviceVersion);
|
|
561
1016
|
const url = `${this.baseUrl}traces`;
|
|
562
1017
|
if (this.debug) {
|
|
@@ -565,11 +1020,7 @@ var TraceShipper = class {
|
|
|
565
1020
|
endpoint: url
|
|
566
1021
|
});
|
|
567
1022
|
}
|
|
568
|
-
const p = postJson(url, body, this.authHeaders(),
|
|
569
|
-
maxAttempts: 3,
|
|
570
|
-
debug: this.debug,
|
|
571
|
-
sdkName: this.sdkName
|
|
572
|
-
});
|
|
1023
|
+
const p = postJson(url, body, this.authHeaders(), opts);
|
|
573
1024
|
this.inFlight.add(p);
|
|
574
1025
|
try {
|
|
575
1026
|
try {
|
|
@@ -577,24 +1028,195 @@ var TraceShipper = class {
|
|
|
577
1028
|
if (this.debug) console.log(`${this.prefix} sent ${batch.length} spans`);
|
|
578
1029
|
} catch (err) {
|
|
579
1030
|
const msg = err instanceof Error ? err.message : String(err);
|
|
580
|
-
|
|
1031
|
+
rateLimitedLog(
|
|
1032
|
+
`${this.prefix}.send_spans_failed`,
|
|
1033
|
+
() => console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`)
|
|
1034
|
+
);
|
|
581
1035
|
}
|
|
582
1036
|
} finally {
|
|
583
1037
|
this.inFlight.delete(p);
|
|
584
1038
|
}
|
|
585
1039
|
}
|
|
586
1040
|
}
|
|
1041
|
+
/** See EventShipper.requestOpts — same shutdown-budget semantics. */
|
|
1042
|
+
requestOpts() {
|
|
1043
|
+
if (this.shutdownDeadlineAt !== void 0) {
|
|
1044
|
+
const remainingMs = this.shutdownDeadlineAt - Date.now();
|
|
1045
|
+
if (remainingMs <= 0) return null;
|
|
1046
|
+
return {
|
|
1047
|
+
maxAttempts: 1,
|
|
1048
|
+
debug: this.debug,
|
|
1049
|
+
sdkName: this.sdkName,
|
|
1050
|
+
timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
if (this.hasShutdown) {
|
|
1054
|
+
return {
|
|
1055
|
+
maxAttempts: 1,
|
|
1056
|
+
debug: this.debug,
|
|
1057
|
+
sdkName: this.sdkName,
|
|
1058
|
+
timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
|
|
1062
|
+
}
|
|
587
1063
|
async shutdown() {
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
1064
|
+
this.hasShutdown = true;
|
|
1065
|
+
this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
|
|
1066
|
+
try {
|
|
1067
|
+
if (this.timer) {
|
|
1068
|
+
clearTimeout(this.timer);
|
|
1069
|
+
this.timer = void 0;
|
|
1070
|
+
}
|
|
1071
|
+
const drain = async () => {
|
|
1072
|
+
await this.flush();
|
|
1073
|
+
await Promise.all([...this.inFlight].map((p) => p.catch(() => {
|
|
1074
|
+
})));
|
|
1075
|
+
};
|
|
1076
|
+
const settled = await raceWithTimeout(drain(), SHUTDOWN_DEADLINE_MS);
|
|
1077
|
+
if (!settled) {
|
|
1078
|
+
rateLimitedLog(
|
|
1079
|
+
`${this.prefix}.shutdown_deadline`,
|
|
1080
|
+
() => console.warn(`${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`)
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1083
|
+
} finally {
|
|
1084
|
+
this.shutdownDeadlineAt = void 0;
|
|
591
1085
|
}
|
|
592
|
-
await this.flush();
|
|
593
|
-
await Promise.all([...this.inFlight].map((p) => p.catch(() => {
|
|
594
|
-
})));
|
|
595
1086
|
}
|
|
596
1087
|
};
|
|
597
1088
|
|
|
1089
|
+
// ../core/dist/index.node.js
|
|
1090
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
1091
|
+
globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
|
|
1092
|
+
|
|
1093
|
+
// src/truncation.ts
|
|
1094
|
+
var TRUNCATION_MARKER2 = "...[truncated by raindrop]";
|
|
1095
|
+
var DEFAULT_MAX_TEXT_FIELD_CHARS2 = 1e6;
|
|
1096
|
+
var MAX_DEPTH = 12;
|
|
1097
|
+
var configuredMaxTextFieldChars;
|
|
1098
|
+
function setMaxTextFieldChars(limit) {
|
|
1099
|
+
if (limit === void 0) return;
|
|
1100
|
+
if (typeof limit === "number" && Number.isFinite(limit) && limit > 0) {
|
|
1101
|
+
configuredMaxTextFieldChars = Math.floor(limit);
|
|
1102
|
+
} else {
|
|
1103
|
+
console.warn(`[raindrop-ai] maxTextFieldChars=${String(limit)} ignored; must be > 0`);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
function otelEnvLimit() {
|
|
1107
|
+
var _a;
|
|
1108
|
+
if (typeof process === "undefined") return void 0;
|
|
1109
|
+
const raw = (_a = process.env) == null ? void 0 : _a.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT;
|
|
1110
|
+
if (!raw) return void 0;
|
|
1111
|
+
const parsed = Number.parseInt(raw, 10);
|
|
1112
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
|
|
1113
|
+
}
|
|
1114
|
+
function effectiveMaxTextFieldChars() {
|
|
1115
|
+
const base = configuredMaxTextFieldChars != null ? configuredMaxTextFieldChars : DEFAULT_MAX_TEXT_FIELD_CHARS2;
|
|
1116
|
+
const env = otelEnvLimit();
|
|
1117
|
+
return env !== void 0 && env < base ? env : base;
|
|
1118
|
+
}
|
|
1119
|
+
function truncateToLimit2(text, limit) {
|
|
1120
|
+
if (limit > TRUNCATION_MARKER2.length) {
|
|
1121
|
+
return text.slice(0, limit - TRUNCATION_MARKER2.length) + TRUNCATION_MARKER2;
|
|
1122
|
+
}
|
|
1123
|
+
return text.slice(0, limit);
|
|
1124
|
+
}
|
|
1125
|
+
function capText2(value, limit) {
|
|
1126
|
+
if (typeof value !== "string") return value;
|
|
1127
|
+
const max = limit != null ? limit : effectiveMaxTextFieldChars();
|
|
1128
|
+
if (value.length <= max) return value;
|
|
1129
|
+
return truncateToLimit2(value, max);
|
|
1130
|
+
}
|
|
1131
|
+
function boundedClone2(value, budget, depth) {
|
|
1132
|
+
var _a, _b;
|
|
1133
|
+
if (budget.remaining <= 0) return TRUNCATION_MARKER2;
|
|
1134
|
+
if (typeof value === "string") {
|
|
1135
|
+
if (value.length > budget.remaining) {
|
|
1136
|
+
const taken = value.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
|
|
1137
|
+
budget.remaining = 0;
|
|
1138
|
+
return taken;
|
|
1139
|
+
}
|
|
1140
|
+
budget.remaining -= Math.max(value.length, 1);
|
|
1141
|
+
return value;
|
|
1142
|
+
}
|
|
1143
|
+
if (value === null || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
1144
|
+
budget.remaining -= 8;
|
|
1145
|
+
return value;
|
|
1146
|
+
}
|
|
1147
|
+
if (typeof value !== "object") {
|
|
1148
|
+
budget.remaining -= 1;
|
|
1149
|
+
return value;
|
|
1150
|
+
}
|
|
1151
|
+
if (value instanceof Uint8Array) {
|
|
1152
|
+
const maxBytes = Math.max(0, Math.ceil(budget.remaining * 3 / 4));
|
|
1153
|
+
if (value.byteLength > maxBytes) {
|
|
1154
|
+
const taken = base64Encode(value.subarray(0, maxBytes)) + TRUNCATION_MARKER2;
|
|
1155
|
+
budget.remaining = 0;
|
|
1156
|
+
return taken;
|
|
1157
|
+
}
|
|
1158
|
+
const encoded = base64Encode(value);
|
|
1159
|
+
budget.remaining -= Math.max(encoded.length, 1);
|
|
1160
|
+
return encoded;
|
|
1161
|
+
}
|
|
1162
|
+
if (depth >= MAX_DEPTH) {
|
|
1163
|
+
budget.remaining -= 16;
|
|
1164
|
+
const name = (_b = (_a = value.constructor) == null ? void 0 : _a.name) != null ? _b : "object";
|
|
1165
|
+
return `<max depth: ${name}>`;
|
|
1166
|
+
}
|
|
1167
|
+
const toJSON = value.toJSON;
|
|
1168
|
+
if (typeof toJSON === "function") {
|
|
1169
|
+
budget.remaining -= 2;
|
|
1170
|
+
return boundedClone2(toJSON.call(value), budget, depth + 1);
|
|
1171
|
+
}
|
|
1172
|
+
if (Array.isArray(value)) {
|
|
1173
|
+
budget.remaining -= 2;
|
|
1174
|
+
const out2 = [];
|
|
1175
|
+
for (const item of value) {
|
|
1176
|
+
if (budget.remaining <= 0) {
|
|
1177
|
+
out2.push(TRUNCATION_MARKER2);
|
|
1178
|
+
break;
|
|
1179
|
+
}
|
|
1180
|
+
out2.push(boundedClone2(item, budget, depth + 1));
|
|
1181
|
+
}
|
|
1182
|
+
return out2;
|
|
1183
|
+
}
|
|
1184
|
+
budget.remaining -= 2;
|
|
1185
|
+
const out = {};
|
|
1186
|
+
for (const key in value) {
|
|
1187
|
+
if (!Object.prototype.hasOwnProperty.call(value, key)) continue;
|
|
1188
|
+
if (budget.remaining <= 0) {
|
|
1189
|
+
out["..."] = TRUNCATION_MARKER2;
|
|
1190
|
+
break;
|
|
1191
|
+
}
|
|
1192
|
+
let cappedKey = key;
|
|
1193
|
+
if (key.length > budget.remaining) {
|
|
1194
|
+
cappedKey = key.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
|
|
1195
|
+
budget.remaining = 0;
|
|
1196
|
+
out[cappedKey] = TRUNCATION_MARKER2;
|
|
1197
|
+
break;
|
|
1198
|
+
}
|
|
1199
|
+
budget.remaining -= Math.max(key.length, 1);
|
|
1200
|
+
out[cappedKey] = boundedClone2(value[key], budget, depth + 1);
|
|
1201
|
+
}
|
|
1202
|
+
return out;
|
|
1203
|
+
}
|
|
1204
|
+
function boundedStringify(value, limit) {
|
|
1205
|
+
const max = limit != null ? limit : effectiveMaxTextFieldChars();
|
|
1206
|
+
try {
|
|
1207
|
+
if (typeof value === "string") {
|
|
1208
|
+
const text2 = JSON.stringify(capText2(value, max));
|
|
1209
|
+
return text2.length <= max ? text2 : truncateToLimit2(text2, max);
|
|
1210
|
+
}
|
|
1211
|
+
const pruned = boundedClone2(value, { remaining: max + TRUNCATION_MARKER2.length + 256 }, 0);
|
|
1212
|
+
const text = JSON.stringify(pruned);
|
|
1213
|
+
if (text === void 0) return void 0;
|
|
1214
|
+
return text.length <= max ? text : truncateToLimit2(text, max);
|
|
1215
|
+
} catch (e) {
|
|
1216
|
+
return void 0;
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
|
|
598
1220
|
// src/callback-handler.ts
|
|
599
1221
|
var LANGGRAPH_INTERNAL_NAMES = /* @__PURE__ */ new Set([
|
|
600
1222
|
"LangGraph",
|
|
@@ -684,7 +1306,7 @@ var RaindropCallbackHandler = class extends BaseCallbackHandler {
|
|
|
684
1306
|
await this.eventShipper.patch(eventId, {
|
|
685
1307
|
userId: this.userId,
|
|
686
1308
|
convoId: this.convoId,
|
|
687
|
-
input: prompts.join("\n"),
|
|
1309
|
+
input: capText2(prompts.join("\n")),
|
|
688
1310
|
properties: buildProperties(tags, metadata),
|
|
689
1311
|
isPending: true
|
|
690
1312
|
});
|
|
@@ -714,7 +1336,7 @@ var RaindropCallbackHandler = class extends BaseCallbackHandler {
|
|
|
714
1336
|
return role === "human";
|
|
715
1337
|
});
|
|
716
1338
|
const lastMsg = userMessages.length > 0 ? userMessages[userMessages.length - 1] : allMessages[allMessages.length - 1];
|
|
717
|
-
const inputText = lastMsg ? typeof lastMsg.content === "string" ? lastMsg.content : safeStringify(lastMsg.content) : void 0;
|
|
1339
|
+
const inputText = lastMsg ? typeof lastMsg.content === "string" ? capText2(lastMsg.content) : safeStringify(lastMsg.content) : void 0;
|
|
718
1340
|
await this.eventShipper.patch(eventId, {
|
|
719
1341
|
userId: this.userId,
|
|
720
1342
|
convoId: this.convoId,
|
|
@@ -744,7 +1366,7 @@ var RaindropCallbackHandler = class extends BaseCallbackHandler {
|
|
|
744
1366
|
if (promptTokens != null) properties["ai.usage.prompt_tokens"] = promptTokens;
|
|
745
1367
|
if (completionTokens != null) properties["ai.usage.completion_tokens"] = completionTokens;
|
|
746
1368
|
await this.finalizeEventIfRoot(runId, {
|
|
747
|
-
output: outputText,
|
|
1369
|
+
output: capText2(outputText),
|
|
748
1370
|
model,
|
|
749
1371
|
properties: Object.keys(properties).length > 0 ? properties : void 0
|
|
750
1372
|
});
|
|
@@ -837,7 +1459,7 @@ var RaindropCallbackHandler = class extends BaseCallbackHandler {
|
|
|
837
1459
|
try {
|
|
838
1460
|
const span = this.spans.get(runId);
|
|
839
1461
|
if (span) {
|
|
840
|
-
const outputStr = typeof output === "string" ? output : safeStringify(output);
|
|
1462
|
+
const outputStr = typeof output === "string" ? output : safeStringify(output, 4096);
|
|
841
1463
|
this.traceShipper.endSpan(span, {
|
|
842
1464
|
attributes: [attrString("tool.output", truncate(outputStr, 4096))]
|
|
843
1465
|
});
|
|
@@ -922,7 +1544,7 @@ var RaindropCallbackHandler = class extends BaseCallbackHandler {
|
|
|
922
1544
|
attributes: [
|
|
923
1545
|
attrString("agent.tool", action.tool),
|
|
924
1546
|
attrString("agent.tool_input", truncate(
|
|
925
|
-
typeof action.toolInput === "string" ? action.toolInput : safeStringify(action.toolInput),
|
|
1547
|
+
typeof action.toolInput === "string" ? action.toolInput : safeStringify(action.toolInput, 4096),
|
|
926
1548
|
4096
|
|
927
1549
|
))
|
|
928
1550
|
]
|
|
@@ -964,13 +1586,10 @@ function buildProperties(tags, metadata) {
|
|
|
964
1586
|
if (metadata) Object.assign(props, metadata);
|
|
965
1587
|
return props;
|
|
966
1588
|
}
|
|
967
|
-
function safeStringify(value) {
|
|
1589
|
+
function safeStringify(value, limit) {
|
|
968
1590
|
var _a;
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
} catch (e) {
|
|
972
|
-
return String(value);
|
|
973
|
-
}
|
|
1591
|
+
if (value === void 0) return "";
|
|
1592
|
+
return (_a = boundedStringify(value, limit)) != null ? _a : String(value);
|
|
974
1593
|
}
|
|
975
1594
|
function truncate(str, maxLen) {
|
|
976
1595
|
if (str.length <= maxLen) return str;
|
|
@@ -989,18 +1608,28 @@ function extractLLMMetadata(output) {
|
|
|
989
1608
|
const usage = (_g = usageMeta != null ? usageMeta : tokenUsage) != null ? _g : estimatedTokens;
|
|
990
1609
|
const promptTokens = (_i = (_h = usage == null ? void 0 : usage.input_tokens) != null ? _h : usage == null ? void 0 : usage.prompt_tokens) != null ? _i : usage == null ? void 0 : usage.promptTokens;
|
|
991
1610
|
const completionTokens = (_k = (_j = usage == null ? void 0 : usage.output_tokens) != null ? _j : usage == null ? void 0 : usage.completion_tokens) != null ? _k : usage == null ? void 0 : usage.completionTokens;
|
|
992
|
-
const outputText = (_m = (_l = gen == null ? void 0 : gen.text) != null ? _l : typeof (message == null ? void 0 : message.content) === "string" ? message.content : void 0) != null ? _m : (message == null ? void 0 : message.content) ?
|
|
1611
|
+
const outputText = (_m = (_l = gen == null ? void 0 : gen.text) != null ? _l : typeof (message == null ? void 0 : message.content) === "string" ? message.content : void 0) != null ? _m : (message == null ? void 0 : message.content) ? boundedStringify(message.content) : void 0;
|
|
993
1612
|
return { model, promptTokens, completionTokens, outputText };
|
|
994
1613
|
}
|
|
995
1614
|
|
|
996
1615
|
// package.json
|
|
997
1616
|
var package_default = {
|
|
998
1617
|
name: "@raindrop-ai/langchain",
|
|
999
|
-
version: "0.0.
|
|
1618
|
+
version: "0.0.4",
|
|
1000
1619
|
description: "Raindrop integration for LangChain",
|
|
1001
1620
|
main: "dist/index.js",
|
|
1002
1621
|
module: "dist/index.mjs",
|
|
1003
1622
|
types: "dist/index.d.ts",
|
|
1623
|
+
license: "MIT",
|
|
1624
|
+
repository: {
|
|
1625
|
+
type: "git",
|
|
1626
|
+
url: "git+https://github.com/raindrop-ai/raindrop-js.git",
|
|
1627
|
+
directory: "packages/langchain"
|
|
1628
|
+
},
|
|
1629
|
+
homepage: "https://github.com/raindrop-ai/raindrop-js/tree/main/packages/langchain#readme",
|
|
1630
|
+
bugs: {
|
|
1631
|
+
url: "https://github.com/raindrop-ai/raindrop-js/issues"
|
|
1632
|
+
},
|
|
1004
1633
|
exports: {
|
|
1005
1634
|
".": {
|
|
1006
1635
|
types: "./dist/index.d.ts",
|
|
@@ -1009,7 +1638,9 @@ var package_default = {
|
|
|
1009
1638
|
}
|
|
1010
1639
|
},
|
|
1011
1640
|
sideEffects: false,
|
|
1012
|
-
files: [
|
|
1641
|
+
files: [
|
|
1642
|
+
"dist/**"
|
|
1643
|
+
],
|
|
1013
1644
|
scripts: {
|
|
1014
1645
|
build: "tsup",
|
|
1015
1646
|
dev: "tsup --watch",
|
|
@@ -1029,11 +1660,20 @@ var package_default = {
|
|
|
1029
1660
|
vitest: "^2.1.9"
|
|
1030
1661
|
},
|
|
1031
1662
|
tsup: {
|
|
1032
|
-
entry: [
|
|
1033
|
-
|
|
1034
|
-
|
|
1663
|
+
entry: [
|
|
1664
|
+
"src/index.ts"
|
|
1665
|
+
],
|
|
1666
|
+
format: [
|
|
1667
|
+
"cjs",
|
|
1668
|
+
"esm"
|
|
1669
|
+
],
|
|
1670
|
+
dts: {
|
|
1671
|
+
resolve: true
|
|
1672
|
+
},
|
|
1035
1673
|
clean: true,
|
|
1036
|
-
noExternal: [
|
|
1674
|
+
noExternal: [
|
|
1675
|
+
"@raindrop-ai/core"
|
|
1676
|
+
]
|
|
1037
1677
|
},
|
|
1038
1678
|
publishConfig: {
|
|
1039
1679
|
access: "public"
|
|
@@ -1049,6 +1689,7 @@ function createRaindropLangChain(opts) {
|
|
|
1049
1689
|
var _a, _b;
|
|
1050
1690
|
const writeKey = opts.writeKey;
|
|
1051
1691
|
const enabled = !!writeKey;
|
|
1692
|
+
setMaxTextFieldChars(opts.maxTextFieldChars);
|
|
1052
1693
|
if (!writeKey) {
|
|
1053
1694
|
console.warn("[raindrop-ai/langchain] writeKey not provided; telemetry shipping is disabled");
|
|
1054
1695
|
}
|