@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.js
CHANGED
|
@@ -28,7 +28,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
28
28
|
// src/callback-handler.ts
|
|
29
29
|
var import_base = require("@langchain/core/callbacks/base");
|
|
30
30
|
|
|
31
|
-
// ../core/dist/chunk-
|
|
31
|
+
// ../core/dist/chunk-U5HUTMR5.js
|
|
32
32
|
function getCrypto() {
|
|
33
33
|
const c = globalThis.crypto;
|
|
34
34
|
return c;
|
|
@@ -83,6 +83,8 @@ function base64Encode(bytes) {
|
|
|
83
83
|
}
|
|
84
84
|
return out;
|
|
85
85
|
}
|
|
86
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
87
|
+
var MAX_RETRY_DELAY_MS = 3e4;
|
|
86
88
|
function wait(ms) {
|
|
87
89
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
88
90
|
}
|
|
@@ -90,6 +92,46 @@ function formatEndpoint(endpoint) {
|
|
|
90
92
|
if (!endpoint) return void 0;
|
|
91
93
|
return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
|
|
92
94
|
}
|
|
95
|
+
function redactUrlForLog(url) {
|
|
96
|
+
try {
|
|
97
|
+
const parsed = new URL(url);
|
|
98
|
+
parsed.username = "";
|
|
99
|
+
parsed.password = "";
|
|
100
|
+
parsed.search = "";
|
|
101
|
+
parsed.hash = "";
|
|
102
|
+
return parsed.toString();
|
|
103
|
+
} catch (e) {
|
|
104
|
+
return "<unparseable-url>";
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
var RATE_LIMITED_LOG_INTERVAL_MS = 3e4;
|
|
108
|
+
var rateLimitedLogLast = /* @__PURE__ */ new Map();
|
|
109
|
+
function rateLimitedLog(key, log) {
|
|
110
|
+
const now = Date.now();
|
|
111
|
+
const last = rateLimitedLogLast.get(key);
|
|
112
|
+
if (last !== void 0 && now - last < RATE_LIMITED_LOG_INTERVAL_MS) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
rateLimitedLogLast.set(key, now);
|
|
116
|
+
log();
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
async function raceWithTimeout(promise, timeoutMs) {
|
|
120
|
+
let timer;
|
|
121
|
+
const settledInTime = await Promise.race([
|
|
122
|
+
promise.then(
|
|
123
|
+
() => true,
|
|
124
|
+
() => true
|
|
125
|
+
),
|
|
126
|
+
new Promise((resolve) => {
|
|
127
|
+
var _a;
|
|
128
|
+
timer = setTimeout(() => resolve(false), Math.max(0, timeoutMs));
|
|
129
|
+
(_a = timer.unref) == null ? void 0 : _a.call(timer);
|
|
130
|
+
})
|
|
131
|
+
]);
|
|
132
|
+
if (timer) clearTimeout(timer);
|
|
133
|
+
return settledInTime;
|
|
134
|
+
}
|
|
93
135
|
function parseRetryAfter(headers) {
|
|
94
136
|
var _a;
|
|
95
137
|
const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
|
|
@@ -106,12 +148,12 @@ function parseRetryAfter(headers) {
|
|
|
106
148
|
function getRetryDelayMs(attemptNumber, previousError) {
|
|
107
149
|
if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
|
|
108
150
|
const v = previousError.retryAfterMs;
|
|
109
|
-
if (typeof v === "number") return Math.max(0, v);
|
|
151
|
+
if (typeof v === "number") return Math.min(Math.max(0, v), MAX_RETRY_DELAY_MS);
|
|
110
152
|
}
|
|
111
153
|
if (attemptNumber <= 1) return 0;
|
|
112
154
|
const base = 500;
|
|
113
155
|
const factor = Math.pow(2, attemptNumber - 2);
|
|
114
|
-
return base * factor;
|
|
156
|
+
return Math.min(base * factor, MAX_RETRY_DELAY_MS);
|
|
115
157
|
}
|
|
116
158
|
async function withRetry(operation, opName, opts) {
|
|
117
159
|
const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
|
|
@@ -146,7 +188,9 @@ async function withRetry(operation, opName, opts) {
|
|
|
146
188
|
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
147
189
|
}
|
|
148
190
|
async function postJson(url, body, headers, opts) {
|
|
149
|
-
|
|
191
|
+
var _a;
|
|
192
|
+
const opName = `POST ${redactUrlForLog(url)}`;
|
|
193
|
+
const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
|
|
150
194
|
await withRetry(
|
|
151
195
|
async () => {
|
|
152
196
|
const resp = await fetch(url, {
|
|
@@ -155,7 +199,8 @@ async function postJson(url, body, headers, opts) {
|
|
|
155
199
|
"Content-Type": "application/json",
|
|
156
200
|
...headers
|
|
157
201
|
},
|
|
158
|
-
body: JSON.stringify(body)
|
|
202
|
+
body: JSON.stringify(body),
|
|
203
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
159
204
|
});
|
|
160
205
|
if (!resp.ok) {
|
|
161
206
|
const text = await resp.text().catch(() => "");
|
|
@@ -172,6 +217,27 @@ async function postJson(url, body, headers, opts) {
|
|
|
172
217
|
opts
|
|
173
218
|
);
|
|
174
219
|
}
|
|
220
|
+
var DEFAULT_MAX_TEXT_FIELD_CHARS = 1e6;
|
|
221
|
+
var TRUNCATION_MARKER = "...[truncated by raindrop]";
|
|
222
|
+
var currentDefaultMaxTextFieldChars = DEFAULT_MAX_TEXT_FIELD_CHARS;
|
|
223
|
+
function resolveMaxTextFieldChars(value) {
|
|
224
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
225
|
+
return Math.floor(value);
|
|
226
|
+
}
|
|
227
|
+
return currentDefaultMaxTextFieldChars;
|
|
228
|
+
}
|
|
229
|
+
function truncateToLimit(text, limit) {
|
|
230
|
+
if (limit > TRUNCATION_MARKER.length) {
|
|
231
|
+
return text.slice(0, limit - TRUNCATION_MARKER.length) + TRUNCATION_MARKER;
|
|
232
|
+
}
|
|
233
|
+
return text.slice(0, Math.max(0, limit));
|
|
234
|
+
}
|
|
235
|
+
function capText(value, limit) {
|
|
236
|
+
if (typeof value !== "string") return value;
|
|
237
|
+
const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
|
|
238
|
+
if (value.length <= max) return value;
|
|
239
|
+
return truncateToLimit(value, max);
|
|
240
|
+
}
|
|
175
241
|
var SpanStatusCode = {
|
|
176
242
|
UNSET: 0,
|
|
177
243
|
OK: 1,
|
|
@@ -229,6 +295,93 @@ function buildExportTraceServiceRequest(spans, serviceName = "raindrop.core", se
|
|
|
229
295
|
]
|
|
230
296
|
};
|
|
231
297
|
}
|
|
298
|
+
var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
|
|
299
|
+
var WORKSHOP_ENV_VAR = "RAINDROP_WORKSHOP";
|
|
300
|
+
var DEFAULT_LOCAL_WORKSHOP_URL = "http://localhost:5899/v1/";
|
|
301
|
+
function readEnvVar(name) {
|
|
302
|
+
var _a;
|
|
303
|
+
try {
|
|
304
|
+
const env = (_a = globalThis == null ? void 0 : globalThis.process) == null ? void 0 : _a.env;
|
|
305
|
+
if (env && typeof env[name] === "string" && env[name].length > 0) {
|
|
306
|
+
return env[name];
|
|
307
|
+
}
|
|
308
|
+
} catch (e) {
|
|
309
|
+
}
|
|
310
|
+
return void 0;
|
|
311
|
+
}
|
|
312
|
+
function readWorkshopEnv() {
|
|
313
|
+
const raw = readEnvVar(WORKSHOP_ENV_VAR);
|
|
314
|
+
if (raw === void 0) return void 0;
|
|
315
|
+
const trimmed = raw.trim();
|
|
316
|
+
if (trimmed.length === 0) return void 0;
|
|
317
|
+
if (/^https?:\/\//i.test(trimmed)) return { url: trimmed };
|
|
318
|
+
if (/^(1|true|yes|on)$/i.test(trimmed)) return "enable";
|
|
319
|
+
if (/^(0|false|no|off)$/i.test(trimmed)) return "disable";
|
|
320
|
+
return void 0;
|
|
321
|
+
}
|
|
322
|
+
function isLocalDevHost(hostname) {
|
|
323
|
+
if (!hostname) return false;
|
|
324
|
+
if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "0.0.0.0" || hostname === "::1") {
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
if (hostname.endsWith(".localhost")) return true;
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
function readRuntimeHostname() {
|
|
331
|
+
try {
|
|
332
|
+
const loc = globalThis == null ? void 0 : globalThis.location;
|
|
333
|
+
if (loc && typeof loc.hostname === "string" && loc.hostname.length > 0) {
|
|
334
|
+
return loc.hostname;
|
|
335
|
+
}
|
|
336
|
+
} catch (e) {
|
|
337
|
+
}
|
|
338
|
+
return void 0;
|
|
339
|
+
}
|
|
340
|
+
function shouldAutoEnableLocalWorkshop() {
|
|
341
|
+
if (isLocalDevHost(readRuntimeHostname())) return true;
|
|
342
|
+
if (readEnvVar("NODE_ENV") === "development") return true;
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
function resolveLocalDebuggerBaseUrl(baseUrl) {
|
|
346
|
+
var _a, _b, _c;
|
|
347
|
+
if (baseUrl === null) return null;
|
|
348
|
+
if (typeof baseUrl === "string" && baseUrl.length > 0) {
|
|
349
|
+
return (_a = formatEndpoint(baseUrl)) != null ? _a : null;
|
|
350
|
+
}
|
|
351
|
+
const explicitUrlEnv = readEnvVar(LOCAL_DEBUGGER_ENV_VAR);
|
|
352
|
+
if (explicitUrlEnv) return (_b = formatEndpoint(explicitUrlEnv)) != null ? _b : null;
|
|
353
|
+
const workshopEnv = readWorkshopEnv();
|
|
354
|
+
if (workshopEnv === "disable") return null;
|
|
355
|
+
if (workshopEnv === "enable") return DEFAULT_LOCAL_WORKSHOP_URL;
|
|
356
|
+
if (workshopEnv && "url" in workshopEnv) return (_c = formatEndpoint(workshopEnv.url)) != null ? _c : null;
|
|
357
|
+
if (shouldAutoEnableLocalWorkshop()) return DEFAULT_LOCAL_WORKSHOP_URL;
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
function mirrorTraceExportToLocalDebugger(body, options = {}) {
|
|
361
|
+
var _a;
|
|
362
|
+
const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
|
|
363
|
+
if (!baseUrl) return;
|
|
364
|
+
void postJson(`${baseUrl}traces`, body, {}, {
|
|
365
|
+
maxAttempts: 1,
|
|
366
|
+
debug: (_a = options.debug) != null ? _a : false,
|
|
367
|
+
sdkName: options.sdkName
|
|
368
|
+
}).catch(() => {
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
function mirrorPartialEventToLocalDebugger(event, options = {}) {
|
|
372
|
+
var _a;
|
|
373
|
+
const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
|
|
374
|
+
if (!baseUrl) return;
|
|
375
|
+
const headers = options.writeKey ? { Authorization: `Bearer ${options.writeKey}` } : {};
|
|
376
|
+
void postJson(`${baseUrl}events/track_partial`, event, headers, {
|
|
377
|
+
maxAttempts: 1,
|
|
378
|
+
debug: (_a = options.debug) != null ? _a : false,
|
|
379
|
+
sdkName: options.sdkName
|
|
380
|
+
}).catch(() => {
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
var SHUTDOWN_DEADLINE_MS = 1e4;
|
|
384
|
+
var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
232
385
|
function mergePatches(target, source) {
|
|
233
386
|
var _a, _b, _c, _d;
|
|
234
387
|
const out = { ...target, ...source };
|
|
@@ -246,7 +399,8 @@ var EventShipper = class {
|
|
|
246
399
|
this.sticky = /* @__PURE__ */ new Map();
|
|
247
400
|
this.timers = /* @__PURE__ */ new Map();
|
|
248
401
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
249
|
-
|
|
402
|
+
this.hasShutdown = false;
|
|
403
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
250
404
|
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
251
405
|
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
252
406
|
this.enabled = opts.enabled !== false;
|
|
@@ -255,11 +409,16 @@ var EventShipper = class {
|
|
|
255
409
|
this.sdkName = (_d = opts.sdkName) != null ? _d : "core";
|
|
256
410
|
this.prefix = `[raindrop-ai/${this.sdkName}]`;
|
|
257
411
|
this.defaultEventName = (_e = opts.defaultEventName) != null ? _e : "ai_generation";
|
|
412
|
+
this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
|
|
413
|
+
this.localDebuggerUrl = (_f = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _f : void 0;
|
|
414
|
+
if (this.debug && this.localDebuggerUrl) {
|
|
415
|
+
console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
|
|
416
|
+
}
|
|
258
417
|
const isNode = typeof process !== "undefined" && typeof process.version === "string";
|
|
259
418
|
this.context = {
|
|
260
419
|
library: {
|
|
261
|
-
name: (
|
|
262
|
-
version: (
|
|
420
|
+
name: (_g = opts.libraryName) != null ? _g : "@raindrop-ai/core",
|
|
421
|
+
version: (_h = opts.libraryVersion) != null ? _h : "0.0.0"
|
|
263
422
|
},
|
|
264
423
|
metadata: {
|
|
265
424
|
jsRuntime: isNode ? "node" : "web",
|
|
@@ -273,10 +432,52 @@ var EventShipper = class {
|
|
|
273
432
|
authHeaders() {
|
|
274
433
|
return this.writeKey ? { Authorization: `Bearer ${this.writeKey}` } : {};
|
|
275
434
|
}
|
|
435
|
+
/**
|
|
436
|
+
* Build the retry/timeout options for one POST, honoring the shutdown
|
|
437
|
+
* deadline. Returns `null` when the shutdown drain window is exhausted —
|
|
438
|
+
* the caller must drop the payload (with a rate-limited warning) instead
|
|
439
|
+
* of issuing a request that could outlive process exit.
|
|
440
|
+
*
|
|
441
|
+
* Checked fresh on EVERY send, so a shutdown that begins while the flush
|
|
442
|
+
* path is mid-drain takes effect immediately: no further retries, and the
|
|
443
|
+
* per-attempt timeout is clamped to the remaining window. After
|
|
444
|
+
* `shutdown()` returns (deadline cleared, `hasShutdown` still set),
|
|
445
|
+
* sends — late callers, or flush work the deadline abandoned mid-drain —
|
|
446
|
+
* run as a single short attempt rather than regaining the full retry
|
|
447
|
+
* schedule.
|
|
448
|
+
*/
|
|
449
|
+
requestOpts() {
|
|
450
|
+
if (this.shutdownDeadlineAt !== void 0) {
|
|
451
|
+
const remainingMs = this.shutdownDeadlineAt - Date.now();
|
|
452
|
+
if (remainingMs <= 0) return null;
|
|
453
|
+
return {
|
|
454
|
+
maxAttempts: 1,
|
|
455
|
+
debug: this.debug,
|
|
456
|
+
sdkName: this.sdkName,
|
|
457
|
+
timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
if (this.hasShutdown) {
|
|
461
|
+
return {
|
|
462
|
+
maxAttempts: 1,
|
|
463
|
+
debug: this.debug,
|
|
464
|
+
sdkName: this.sdkName,
|
|
465
|
+
timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
|
|
469
|
+
}
|
|
276
470
|
async patch(eventId, patch) {
|
|
277
471
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
278
472
|
if (!this.enabled) return;
|
|
279
473
|
if (!eventId || !eventId.trim()) return;
|
|
474
|
+
const maxChars = resolveMaxTextFieldChars(this.maxTextFieldCharsOpt);
|
|
475
|
+
if (typeof patch.input === "string" && patch.input.length > maxChars) {
|
|
476
|
+
patch = { ...patch, input: capText(patch.input, maxChars) };
|
|
477
|
+
}
|
|
478
|
+
if (typeof patch.output === "string" && patch.output.length > maxChars) {
|
|
479
|
+
patch = { ...patch, output: capText(patch.output, maxChars) };
|
|
480
|
+
}
|
|
280
481
|
if (this.debug) {
|
|
281
482
|
console.log(`${this.prefix} queue patch`, {
|
|
282
483
|
eventId,
|
|
@@ -323,9 +524,16 @@ var EventShipper = class {
|
|
|
323
524
|
})));
|
|
324
525
|
}
|
|
325
526
|
async shutdown() {
|
|
326
|
-
|
|
327
|
-
this.
|
|
328
|
-
|
|
527
|
+
this.hasShutdown = true;
|
|
528
|
+
this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
|
|
529
|
+
try {
|
|
530
|
+
for (const t of this.timers.values()) clearTimeout(t);
|
|
531
|
+
this.timers.clear();
|
|
532
|
+
const settled = await raceWithTimeout(this.flush(), SHUTDOWN_DEADLINE_MS);
|
|
533
|
+
if (!settled) this.warnShutdownDrop("in-flight request(s) at shutdown");
|
|
534
|
+
} finally {
|
|
535
|
+
this.shutdownDeadlineAt = void 0;
|
|
536
|
+
}
|
|
329
537
|
}
|
|
330
538
|
async trackSignal(signal) {
|
|
331
539
|
var _a, _b;
|
|
@@ -345,16 +553,21 @@ var EventShipper = class {
|
|
|
345
553
|
}
|
|
346
554
|
}
|
|
347
555
|
];
|
|
556
|
+
if (!this.writeKey) return;
|
|
348
557
|
const url = `${this.baseUrl}signals/track`;
|
|
558
|
+
const opts = this.requestOpts();
|
|
559
|
+
if (!opts) {
|
|
560
|
+
this.warnShutdownDrop("signal");
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
349
563
|
try {
|
|
350
|
-
await postJson(url, body, this.authHeaders(),
|
|
351
|
-
maxAttempts: 3,
|
|
352
|
-
debug: this.debug,
|
|
353
|
-
sdkName: this.sdkName
|
|
354
|
-
});
|
|
564
|
+
await postJson(url, body, this.authHeaders(), opts);
|
|
355
565
|
} catch (err) {
|
|
356
566
|
const msg = err instanceof Error ? err.message : String(err);
|
|
357
|
-
|
|
567
|
+
rateLimitedLog(
|
|
568
|
+
`${this.prefix}.send_signal_failed`,
|
|
569
|
+
() => console.warn(`${this.prefix} failed to send signal (dropping): ${msg}`)
|
|
570
|
+
);
|
|
358
571
|
}
|
|
359
572
|
}
|
|
360
573
|
async identify(users) {
|
|
@@ -375,19 +588,30 @@ var EventShipper = class {
|
|
|
375
588
|
traits: (_a = user.traits) != null ? _a : {}
|
|
376
589
|
};
|
|
377
590
|
});
|
|
591
|
+
if (!this.writeKey) return;
|
|
378
592
|
if (body.length === 0) return;
|
|
379
593
|
const url = `${this.baseUrl}users/identify`;
|
|
594
|
+
const opts = this.requestOpts();
|
|
595
|
+
if (!opts) {
|
|
596
|
+
this.warnShutdownDrop("identify");
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
380
599
|
try {
|
|
381
|
-
await postJson(url, body, this.authHeaders(),
|
|
382
|
-
maxAttempts: 3,
|
|
383
|
-
debug: this.debug,
|
|
384
|
-
sdkName: this.sdkName
|
|
385
|
-
});
|
|
600
|
+
await postJson(url, body, this.authHeaders(), opts);
|
|
386
601
|
} catch (err) {
|
|
387
602
|
const msg = err instanceof Error ? err.message : String(err);
|
|
388
|
-
|
|
603
|
+
rateLimitedLog(
|
|
604
|
+
`${this.prefix}.send_identify_failed`,
|
|
605
|
+
() => console.warn(`${this.prefix} failed to send identify (dropping): ${msg}`)
|
|
606
|
+
);
|
|
389
607
|
}
|
|
390
608
|
}
|
|
609
|
+
warnShutdownDrop(what) {
|
|
610
|
+
rateLimitedLog(
|
|
611
|
+
`${this.prefix}.shutdown_deadline`,
|
|
612
|
+
() => console.warn(`${this.prefix} shutdown flush deadline exceeded; dropping ${what}`)
|
|
613
|
+
);
|
|
614
|
+
}
|
|
391
615
|
async flushOne(eventId) {
|
|
392
616
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
393
617
|
if (!this.enabled) return;
|
|
@@ -451,11 +675,25 @@ var EventShipper = class {
|
|
|
451
675
|
endpoint: url
|
|
452
676
|
});
|
|
453
677
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
678
|
+
if (this.localDebuggerUrl) {
|
|
679
|
+
mirrorPartialEventToLocalDebugger(payload, {
|
|
680
|
+
baseUrl: this.localDebuggerUrl,
|
|
681
|
+
writeKey: this.writeKey,
|
|
682
|
+
debug: this.debug,
|
|
683
|
+
sdkName: this.sdkName
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
if (!this.writeKey) {
|
|
687
|
+
if (!isPending) this.sticky.delete(eventId);
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
const opts = this.requestOpts();
|
|
691
|
+
if (!opts) {
|
|
692
|
+
this.warnShutdownDrop(`track_partial ${eventId}`);
|
|
693
|
+
if (!isPending) this.sticky.delete(eventId);
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
const p = postJson(url, payload, this.authHeaders(), opts);
|
|
459
697
|
this.inFlight.add(p);
|
|
460
698
|
try {
|
|
461
699
|
try {
|
|
@@ -465,7 +703,10 @@ var EventShipper = class {
|
|
|
465
703
|
}
|
|
466
704
|
} catch (err) {
|
|
467
705
|
const msg = err instanceof Error ? err.message : String(err);
|
|
468
|
-
|
|
706
|
+
rateLimitedLog(
|
|
707
|
+
`${this.prefix}.send_track_partial_failed`,
|
|
708
|
+
() => console.warn(`${this.prefix} failed to send track_partial (dropping): ${msg}`)
|
|
709
|
+
);
|
|
469
710
|
}
|
|
470
711
|
} finally {
|
|
471
712
|
this.inFlight.delete(p);
|
|
@@ -475,11 +716,114 @@ var EventShipper = class {
|
|
|
475
716
|
}
|
|
476
717
|
}
|
|
477
718
|
};
|
|
719
|
+
var DEFAULT_SECRET_KEY_NAMES = [
|
|
720
|
+
"apikey",
|
|
721
|
+
"apisecret",
|
|
722
|
+
"apitoken",
|
|
723
|
+
"secretaccesskey",
|
|
724
|
+
"sessiontoken",
|
|
725
|
+
"privatekey",
|
|
726
|
+
"privatekeyid",
|
|
727
|
+
"clientsecret",
|
|
728
|
+
"accesstoken",
|
|
729
|
+
"refreshtoken",
|
|
730
|
+
"oauthtoken",
|
|
731
|
+
"bearertoken",
|
|
732
|
+
"authorization",
|
|
733
|
+
"password",
|
|
734
|
+
"passphrase"
|
|
735
|
+
];
|
|
736
|
+
var REDACTED_PLACEHOLDER = "[REDACTED]";
|
|
737
|
+
function normalizeKeyName(name) {
|
|
738
|
+
return name.toLowerCase().replace(/[-_.]/g, "");
|
|
739
|
+
}
|
|
740
|
+
function redactSecretsInObject(value, options) {
|
|
741
|
+
var _a, _b;
|
|
742
|
+
const normalizedSecretSet = buildSecretSet((_a = options == null ? void 0 : options.secretKeyNames) != null ? _a : DEFAULT_SECRET_KEY_NAMES);
|
|
743
|
+
const placeholder = (_b = options == null ? void 0 : options.placeholder) != null ? _b : REDACTED_PLACEHOLDER;
|
|
744
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
745
|
+
const walk = (node) => {
|
|
746
|
+
if (node === null || typeof node !== "object") return node;
|
|
747
|
+
if (seen.has(node)) return "[CIRCULAR]";
|
|
748
|
+
seen.add(node);
|
|
749
|
+
if (Array.isArray(node)) {
|
|
750
|
+
return node.map((item) => walk(item));
|
|
751
|
+
}
|
|
752
|
+
const out = {};
|
|
753
|
+
for (const [k, v] of Object.entries(node)) {
|
|
754
|
+
if (normalizedSecretSet.has(normalizeKeyName(k))) {
|
|
755
|
+
out[k] = placeholder;
|
|
756
|
+
} else {
|
|
757
|
+
out[k] = walk(v);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
return out;
|
|
761
|
+
};
|
|
762
|
+
return walk(value);
|
|
763
|
+
}
|
|
764
|
+
function buildSecretSet(names) {
|
|
765
|
+
const set = /* @__PURE__ */ new Set();
|
|
766
|
+
for (const name of names) set.add(normalizeKeyName(name));
|
|
767
|
+
return set;
|
|
768
|
+
}
|
|
769
|
+
var DEFAULT_REDACT_ATTRIBUTE_KEYS = [
|
|
770
|
+
"ai.request.providerOptions",
|
|
771
|
+
"ai.response.providerMetadata"
|
|
772
|
+
];
|
|
773
|
+
function defaultTransformSpan(span) {
|
|
774
|
+
const attrs = span.attributes;
|
|
775
|
+
if (!attrs || attrs.length === 0) return span;
|
|
776
|
+
let nextAttrs;
|
|
777
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
778
|
+
const attr = attrs[i];
|
|
779
|
+
const redacted = redactJsonAttributeValue(attr.key, attr.value);
|
|
780
|
+
if (redacted === void 0) continue;
|
|
781
|
+
if (!nextAttrs) nextAttrs = attrs.slice();
|
|
782
|
+
nextAttrs[i] = { key: attr.key, value: redacted };
|
|
783
|
+
}
|
|
784
|
+
if (!nextAttrs) return span;
|
|
785
|
+
return { ...span, attributes: nextAttrs };
|
|
786
|
+
}
|
|
787
|
+
var REDACT_JSON_ATTRIBUTE_KEYS = new Set(DEFAULT_REDACT_ATTRIBUTE_KEYS);
|
|
788
|
+
function redactJsonAttributeValue(key, value) {
|
|
789
|
+
if (!REDACT_JSON_ATTRIBUTE_KEYS.has(key)) return void 0;
|
|
790
|
+
const json = value.stringValue;
|
|
791
|
+
if (typeof json !== "string" || json.length === 0) return void 0;
|
|
792
|
+
let parsed;
|
|
793
|
+
try {
|
|
794
|
+
parsed = JSON.parse(json);
|
|
795
|
+
} catch (e) {
|
|
796
|
+
return void 0;
|
|
797
|
+
}
|
|
798
|
+
const scrubbed = redactSecretsInObject(parsed);
|
|
799
|
+
let scrubbedJson;
|
|
800
|
+
try {
|
|
801
|
+
scrubbedJson = JSON.stringify(scrubbed);
|
|
802
|
+
} catch (e) {
|
|
803
|
+
return void 0;
|
|
804
|
+
}
|
|
805
|
+
if (scrubbedJson === json) return void 0;
|
|
806
|
+
return { stringValue: scrubbedJson };
|
|
807
|
+
}
|
|
808
|
+
function applyOtelSpanAttributeLimit(limit) {
|
|
809
|
+
var _a, _b;
|
|
810
|
+
try {
|
|
811
|
+
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;
|
|
812
|
+
if (!raw) return limit;
|
|
813
|
+
const parsed = Number.parseInt(raw, 10);
|
|
814
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
815
|
+
return Math.min(limit, parsed);
|
|
816
|
+
}
|
|
817
|
+
} catch (e) {
|
|
818
|
+
}
|
|
819
|
+
return limit;
|
|
820
|
+
}
|
|
478
821
|
var TraceShipper = class {
|
|
479
822
|
constructor(opts) {
|
|
480
823
|
this.queue = [];
|
|
481
824
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
482
|
-
|
|
825
|
+
this.hasShutdown = false;
|
|
826
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
483
827
|
this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
|
|
484
828
|
this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
|
|
485
829
|
this.enabled = opts.enabled !== false;
|
|
@@ -492,6 +836,79 @@ var TraceShipper = class {
|
|
|
492
836
|
this.prefix = `[raindrop-ai/${this.sdkName}]`;
|
|
493
837
|
this.serviceName = (_g = opts.serviceName) != null ? _g : "raindrop.core";
|
|
494
838
|
this.serviceVersion = (_h = opts.serviceVersion) != null ? _h : "0.0.0";
|
|
839
|
+
this.localDebuggerUrl = (_i = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _i : void 0;
|
|
840
|
+
if (this.debug && this.localDebuggerUrl) {
|
|
841
|
+
console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
|
|
842
|
+
}
|
|
843
|
+
this.transformSpanHook = opts.transformSpan;
|
|
844
|
+
this.disableDefaultRedaction = opts.disableDefaultRedaction === true;
|
|
845
|
+
this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Cap every string attribute value on the span. O(#attributes) length
|
|
849
|
+
* checks; only oversized values pay a slice. Runs AFTER the redaction
|
|
850
|
+
* pipeline so the default secret-scrub still sees parseable JSON in
|
|
851
|
+
* `ai.request.providerOptions` / `ai.response.providerMetadata` (capping
|
|
852
|
+
* first could cut a JSON blob mid-way, fail the parse, and ship secrets
|
|
853
|
+
* in the surviving prefix).
|
|
854
|
+
*
|
|
855
|
+
* A stricter `OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT` env var is honored
|
|
856
|
+
* for span content, matching the Python SDK and the OTel SDK convention.
|
|
857
|
+
*/
|
|
858
|
+
capSpanAttributes(span) {
|
|
859
|
+
var _a;
|
|
860
|
+
const maxChars = applyOtelSpanAttributeLimit(
|
|
861
|
+
resolveMaxTextFieldChars(this.maxTextFieldCharsOpt)
|
|
862
|
+
);
|
|
863
|
+
const attrs = span.attributes;
|
|
864
|
+
if (!attrs || attrs.length === 0) return span;
|
|
865
|
+
let nextAttrs;
|
|
866
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
867
|
+
const attr = attrs[i];
|
|
868
|
+
const value = (_a = attr.value) == null ? void 0 : _a.stringValue;
|
|
869
|
+
if (typeof value !== "string" || value.length <= maxChars) continue;
|
|
870
|
+
if (!nextAttrs) nextAttrs = attrs.slice();
|
|
871
|
+
nextAttrs[i] = {
|
|
872
|
+
key: attr.key,
|
|
873
|
+
value: { ...attr.value, stringValue: capText(value, maxChars) }
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
if (!nextAttrs) return span;
|
|
877
|
+
return { ...span, attributes: nextAttrs };
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* Apply the user `transformSpan` hook (if any) followed by the default
|
|
881
|
+
* redactor (unless disabled). Returns either the (possibly new) span to
|
|
882
|
+
* ship, or `null` to drop the span entirely.
|
|
883
|
+
*
|
|
884
|
+
* Ordering: user hook runs first so callers can rewrite the span freely
|
|
885
|
+
* (rename attrs, add new ones, scrub things the default doesn't know
|
|
886
|
+
* about). The default redactor then runs on whatever the user produced,
|
|
887
|
+
* acting as the always-on floor for documented BYOK secrets. If the user
|
|
888
|
+
* sets `disableDefaultRedaction: true`, the floor is skipped.
|
|
889
|
+
*
|
|
890
|
+
* Fail-closed: if the user hook throws, the span is dropped — a buggy
|
|
891
|
+
* hook can never accidentally ship raw, un-redacted spans.
|
|
892
|
+
*/
|
|
893
|
+
redactSpan(span) {
|
|
894
|
+
let current = span;
|
|
895
|
+
if (this.transformSpanHook) {
|
|
896
|
+
try {
|
|
897
|
+
const result = this.transformSpanHook(current);
|
|
898
|
+
if (result === null) return null;
|
|
899
|
+
if (result !== void 0) current = result;
|
|
900
|
+
} catch (err) {
|
|
901
|
+
if (this.debug) {
|
|
902
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
903
|
+
console.warn(`${this.prefix} transformSpan hook threw: ${msg}`);
|
|
904
|
+
}
|
|
905
|
+
return null;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
if (!this.disableDefaultRedaction) {
|
|
909
|
+
current = defaultTransformSpan(current);
|
|
910
|
+
}
|
|
911
|
+
return this.capSpanAttributes(current);
|
|
495
912
|
}
|
|
496
913
|
isDebugEnabled() {
|
|
497
914
|
return this.debug;
|
|
@@ -508,7 +925,30 @@ var TraceShipper = class {
|
|
|
508
925
|
attrString("ai.operationId", args.operationId)
|
|
509
926
|
];
|
|
510
927
|
if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
|
|
511
|
-
|
|
928
|
+
const span = { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
|
|
929
|
+
this.mirrorToLocalDebugger(
|
|
930
|
+
buildOtlpSpan({
|
|
931
|
+
ids: span.ids,
|
|
932
|
+
name: span.name,
|
|
933
|
+
startTimeUnixNano: span.startTimeUnixNano,
|
|
934
|
+
endTimeUnixNano: span.startTimeUnixNano,
|
|
935
|
+
// placeholder — will be updated on endSpan
|
|
936
|
+
attributes: span.attributes,
|
|
937
|
+
status: { code: SpanStatusCode.UNSET }
|
|
938
|
+
})
|
|
939
|
+
);
|
|
940
|
+
return span;
|
|
941
|
+
}
|
|
942
|
+
mirrorToLocalDebugger(span) {
|
|
943
|
+
if (!this.localDebuggerUrl) return;
|
|
944
|
+
const redacted = this.redactSpan(span);
|
|
945
|
+
if (redacted === null) return;
|
|
946
|
+
const body = buildExportTraceServiceRequest([redacted], this.serviceName, this.serviceVersion);
|
|
947
|
+
mirrorTraceExportToLocalDebugger(body, {
|
|
948
|
+
baseUrl: this.localDebuggerUrl,
|
|
949
|
+
debug: false,
|
|
950
|
+
sdkName: this.sdkName
|
|
951
|
+
});
|
|
512
952
|
}
|
|
513
953
|
endSpan(span, extra) {
|
|
514
954
|
var _a, _b;
|
|
@@ -531,6 +971,7 @@ var TraceShipper = class {
|
|
|
531
971
|
status
|
|
532
972
|
});
|
|
533
973
|
this.enqueue(otlp);
|
|
974
|
+
this.mirrorToLocalDebugger(otlp);
|
|
534
975
|
}
|
|
535
976
|
createSpan(args) {
|
|
536
977
|
var _a;
|
|
@@ -548,6 +989,7 @@ var TraceShipper = class {
|
|
|
548
989
|
status: args.status
|
|
549
990
|
});
|
|
550
991
|
this.enqueue(otlp);
|
|
992
|
+
this.mirrorToLocalDebugger(otlp);
|
|
551
993
|
}
|
|
552
994
|
enqueue(span) {
|
|
553
995
|
if (!this.enabled) return;
|
|
@@ -559,10 +1001,12 @@ var TraceShipper = class {
|
|
|
559
1001
|
)}`
|
|
560
1002
|
);
|
|
561
1003
|
}
|
|
1004
|
+
const redacted = this.redactSpan(span);
|
|
1005
|
+
if (redacted === null) return;
|
|
562
1006
|
if (this.queue.length >= this.maxQueueSize) {
|
|
563
1007
|
this.queue.shift();
|
|
564
1008
|
}
|
|
565
|
-
this.queue.push(
|
|
1009
|
+
this.queue.push(redacted);
|
|
566
1010
|
if (this.queue.length >= this.maxBatchSize) {
|
|
567
1011
|
void this.flush().catch(() => {
|
|
568
1012
|
});
|
|
@@ -584,6 +1028,17 @@ var TraceShipper = class {
|
|
|
584
1028
|
}
|
|
585
1029
|
while (this.queue.length > 0) {
|
|
586
1030
|
const batch = this.queue.splice(0, this.maxBatchSize);
|
|
1031
|
+
if (!this.writeKey) continue;
|
|
1032
|
+
const opts = this.requestOpts();
|
|
1033
|
+
if (!opts) {
|
|
1034
|
+
rateLimitedLog(
|
|
1035
|
+
`${this.prefix}.shutdown_deadline`,
|
|
1036
|
+
() => console.warn(
|
|
1037
|
+
`${this.prefix} shutdown flush deadline exceeded; dropping ${batch.length} spans`
|
|
1038
|
+
)
|
|
1039
|
+
);
|
|
1040
|
+
continue;
|
|
1041
|
+
}
|
|
587
1042
|
const body = buildExportTraceServiceRequest(batch, this.serviceName, this.serviceVersion);
|
|
588
1043
|
const url = `${this.baseUrl}traces`;
|
|
589
1044
|
if (this.debug) {
|
|
@@ -592,11 +1047,7 @@ var TraceShipper = class {
|
|
|
592
1047
|
endpoint: url
|
|
593
1048
|
});
|
|
594
1049
|
}
|
|
595
|
-
const p = postJson(url, body, this.authHeaders(),
|
|
596
|
-
maxAttempts: 3,
|
|
597
|
-
debug: this.debug,
|
|
598
|
-
sdkName: this.sdkName
|
|
599
|
-
});
|
|
1050
|
+
const p = postJson(url, body, this.authHeaders(), opts);
|
|
600
1051
|
this.inFlight.add(p);
|
|
601
1052
|
try {
|
|
602
1053
|
try {
|
|
@@ -604,24 +1055,195 @@ var TraceShipper = class {
|
|
|
604
1055
|
if (this.debug) console.log(`${this.prefix} sent ${batch.length} spans`);
|
|
605
1056
|
} catch (err) {
|
|
606
1057
|
const msg = err instanceof Error ? err.message : String(err);
|
|
607
|
-
|
|
1058
|
+
rateLimitedLog(
|
|
1059
|
+
`${this.prefix}.send_spans_failed`,
|
|
1060
|
+
() => console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`)
|
|
1061
|
+
);
|
|
608
1062
|
}
|
|
609
1063
|
} finally {
|
|
610
1064
|
this.inFlight.delete(p);
|
|
611
1065
|
}
|
|
612
1066
|
}
|
|
613
1067
|
}
|
|
1068
|
+
/** See EventShipper.requestOpts — same shutdown-budget semantics. */
|
|
1069
|
+
requestOpts() {
|
|
1070
|
+
if (this.shutdownDeadlineAt !== void 0) {
|
|
1071
|
+
const remainingMs = this.shutdownDeadlineAt - Date.now();
|
|
1072
|
+
if (remainingMs <= 0) return null;
|
|
1073
|
+
return {
|
|
1074
|
+
maxAttempts: 1,
|
|
1075
|
+
debug: this.debug,
|
|
1076
|
+
sdkName: this.sdkName,
|
|
1077
|
+
timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
if (this.hasShutdown) {
|
|
1081
|
+
return {
|
|
1082
|
+
maxAttempts: 1,
|
|
1083
|
+
debug: this.debug,
|
|
1084
|
+
sdkName: this.sdkName,
|
|
1085
|
+
timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
|
|
1086
|
+
};
|
|
1087
|
+
}
|
|
1088
|
+
return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
|
|
1089
|
+
}
|
|
614
1090
|
async shutdown() {
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
1091
|
+
this.hasShutdown = true;
|
|
1092
|
+
this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
|
|
1093
|
+
try {
|
|
1094
|
+
if (this.timer) {
|
|
1095
|
+
clearTimeout(this.timer);
|
|
1096
|
+
this.timer = void 0;
|
|
1097
|
+
}
|
|
1098
|
+
const drain = async () => {
|
|
1099
|
+
await this.flush();
|
|
1100
|
+
await Promise.all([...this.inFlight].map((p) => p.catch(() => {
|
|
1101
|
+
})));
|
|
1102
|
+
};
|
|
1103
|
+
const settled = await raceWithTimeout(drain(), SHUTDOWN_DEADLINE_MS);
|
|
1104
|
+
if (!settled) {
|
|
1105
|
+
rateLimitedLog(
|
|
1106
|
+
`${this.prefix}.shutdown_deadline`,
|
|
1107
|
+
() => console.warn(`${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`)
|
|
1108
|
+
);
|
|
1109
|
+
}
|
|
1110
|
+
} finally {
|
|
1111
|
+
this.shutdownDeadlineAt = void 0;
|
|
618
1112
|
}
|
|
619
|
-
await this.flush();
|
|
620
|
-
await Promise.all([...this.inFlight].map((p) => p.catch(() => {
|
|
621
|
-
})));
|
|
622
1113
|
}
|
|
623
1114
|
};
|
|
624
1115
|
|
|
1116
|
+
// ../core/dist/index.node.js
|
|
1117
|
+
var import_async_hooks = require("async_hooks");
|
|
1118
|
+
globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = import_async_hooks.AsyncLocalStorage;
|
|
1119
|
+
|
|
1120
|
+
// src/truncation.ts
|
|
1121
|
+
var TRUNCATION_MARKER2 = "...[truncated by raindrop]";
|
|
1122
|
+
var DEFAULT_MAX_TEXT_FIELD_CHARS2 = 1e6;
|
|
1123
|
+
var MAX_DEPTH = 12;
|
|
1124
|
+
var configuredMaxTextFieldChars;
|
|
1125
|
+
function setMaxTextFieldChars(limit) {
|
|
1126
|
+
if (limit === void 0) return;
|
|
1127
|
+
if (typeof limit === "number" && Number.isFinite(limit) && limit > 0) {
|
|
1128
|
+
configuredMaxTextFieldChars = Math.floor(limit);
|
|
1129
|
+
} else {
|
|
1130
|
+
console.warn(`[raindrop-ai] maxTextFieldChars=${String(limit)} ignored; must be > 0`);
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
function otelEnvLimit() {
|
|
1134
|
+
var _a;
|
|
1135
|
+
if (typeof process === "undefined") return void 0;
|
|
1136
|
+
const raw = (_a = process.env) == null ? void 0 : _a.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT;
|
|
1137
|
+
if (!raw) return void 0;
|
|
1138
|
+
const parsed = Number.parseInt(raw, 10);
|
|
1139
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
|
|
1140
|
+
}
|
|
1141
|
+
function effectiveMaxTextFieldChars() {
|
|
1142
|
+
const base = configuredMaxTextFieldChars != null ? configuredMaxTextFieldChars : DEFAULT_MAX_TEXT_FIELD_CHARS2;
|
|
1143
|
+
const env = otelEnvLimit();
|
|
1144
|
+
return env !== void 0 && env < base ? env : base;
|
|
1145
|
+
}
|
|
1146
|
+
function truncateToLimit2(text, limit) {
|
|
1147
|
+
if (limit > TRUNCATION_MARKER2.length) {
|
|
1148
|
+
return text.slice(0, limit - TRUNCATION_MARKER2.length) + TRUNCATION_MARKER2;
|
|
1149
|
+
}
|
|
1150
|
+
return text.slice(0, limit);
|
|
1151
|
+
}
|
|
1152
|
+
function capText2(value, limit) {
|
|
1153
|
+
if (typeof value !== "string") return value;
|
|
1154
|
+
const max = limit != null ? limit : effectiveMaxTextFieldChars();
|
|
1155
|
+
if (value.length <= max) return value;
|
|
1156
|
+
return truncateToLimit2(value, max);
|
|
1157
|
+
}
|
|
1158
|
+
function boundedClone2(value, budget, depth) {
|
|
1159
|
+
var _a, _b;
|
|
1160
|
+
if (budget.remaining <= 0) return TRUNCATION_MARKER2;
|
|
1161
|
+
if (typeof value === "string") {
|
|
1162
|
+
if (value.length > budget.remaining) {
|
|
1163
|
+
const taken = value.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
|
|
1164
|
+
budget.remaining = 0;
|
|
1165
|
+
return taken;
|
|
1166
|
+
}
|
|
1167
|
+
budget.remaining -= Math.max(value.length, 1);
|
|
1168
|
+
return value;
|
|
1169
|
+
}
|
|
1170
|
+
if (value === null || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
1171
|
+
budget.remaining -= 8;
|
|
1172
|
+
return value;
|
|
1173
|
+
}
|
|
1174
|
+
if (typeof value !== "object") {
|
|
1175
|
+
budget.remaining -= 1;
|
|
1176
|
+
return value;
|
|
1177
|
+
}
|
|
1178
|
+
if (value instanceof Uint8Array) {
|
|
1179
|
+
const maxBytes = Math.max(0, Math.ceil(budget.remaining * 3 / 4));
|
|
1180
|
+
if (value.byteLength > maxBytes) {
|
|
1181
|
+
const taken = base64Encode(value.subarray(0, maxBytes)) + TRUNCATION_MARKER2;
|
|
1182
|
+
budget.remaining = 0;
|
|
1183
|
+
return taken;
|
|
1184
|
+
}
|
|
1185
|
+
const encoded = base64Encode(value);
|
|
1186
|
+
budget.remaining -= Math.max(encoded.length, 1);
|
|
1187
|
+
return encoded;
|
|
1188
|
+
}
|
|
1189
|
+
if (depth >= MAX_DEPTH) {
|
|
1190
|
+
budget.remaining -= 16;
|
|
1191
|
+
const name = (_b = (_a = value.constructor) == null ? void 0 : _a.name) != null ? _b : "object";
|
|
1192
|
+
return `<max depth: ${name}>`;
|
|
1193
|
+
}
|
|
1194
|
+
const toJSON = value.toJSON;
|
|
1195
|
+
if (typeof toJSON === "function") {
|
|
1196
|
+
budget.remaining -= 2;
|
|
1197
|
+
return boundedClone2(toJSON.call(value), budget, depth + 1);
|
|
1198
|
+
}
|
|
1199
|
+
if (Array.isArray(value)) {
|
|
1200
|
+
budget.remaining -= 2;
|
|
1201
|
+
const out2 = [];
|
|
1202
|
+
for (const item of value) {
|
|
1203
|
+
if (budget.remaining <= 0) {
|
|
1204
|
+
out2.push(TRUNCATION_MARKER2);
|
|
1205
|
+
break;
|
|
1206
|
+
}
|
|
1207
|
+
out2.push(boundedClone2(item, budget, depth + 1));
|
|
1208
|
+
}
|
|
1209
|
+
return out2;
|
|
1210
|
+
}
|
|
1211
|
+
budget.remaining -= 2;
|
|
1212
|
+
const out = {};
|
|
1213
|
+
for (const key in value) {
|
|
1214
|
+
if (!Object.prototype.hasOwnProperty.call(value, key)) continue;
|
|
1215
|
+
if (budget.remaining <= 0) {
|
|
1216
|
+
out["..."] = TRUNCATION_MARKER2;
|
|
1217
|
+
break;
|
|
1218
|
+
}
|
|
1219
|
+
let cappedKey = key;
|
|
1220
|
+
if (key.length > budget.remaining) {
|
|
1221
|
+
cappedKey = key.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
|
|
1222
|
+
budget.remaining = 0;
|
|
1223
|
+
out[cappedKey] = TRUNCATION_MARKER2;
|
|
1224
|
+
break;
|
|
1225
|
+
}
|
|
1226
|
+
budget.remaining -= Math.max(key.length, 1);
|
|
1227
|
+
out[cappedKey] = boundedClone2(value[key], budget, depth + 1);
|
|
1228
|
+
}
|
|
1229
|
+
return out;
|
|
1230
|
+
}
|
|
1231
|
+
function boundedStringify(value, limit) {
|
|
1232
|
+
const max = limit != null ? limit : effectiveMaxTextFieldChars();
|
|
1233
|
+
try {
|
|
1234
|
+
if (typeof value === "string") {
|
|
1235
|
+
const text2 = JSON.stringify(capText2(value, max));
|
|
1236
|
+
return text2.length <= max ? text2 : truncateToLimit2(text2, max);
|
|
1237
|
+
}
|
|
1238
|
+
const pruned = boundedClone2(value, { remaining: max + TRUNCATION_MARKER2.length + 256 }, 0);
|
|
1239
|
+
const text = JSON.stringify(pruned);
|
|
1240
|
+
if (text === void 0) return void 0;
|
|
1241
|
+
return text.length <= max ? text : truncateToLimit2(text, max);
|
|
1242
|
+
} catch (e) {
|
|
1243
|
+
return void 0;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
|
|
625
1247
|
// src/callback-handler.ts
|
|
626
1248
|
var LANGGRAPH_INTERNAL_NAMES = /* @__PURE__ */ new Set([
|
|
627
1249
|
"LangGraph",
|
|
@@ -711,7 +1333,7 @@ var RaindropCallbackHandler = class extends import_base.BaseCallbackHandler {
|
|
|
711
1333
|
await this.eventShipper.patch(eventId, {
|
|
712
1334
|
userId: this.userId,
|
|
713
1335
|
convoId: this.convoId,
|
|
714
|
-
input: prompts.join("\n"),
|
|
1336
|
+
input: capText2(prompts.join("\n")),
|
|
715
1337
|
properties: buildProperties(tags, metadata),
|
|
716
1338
|
isPending: true
|
|
717
1339
|
});
|
|
@@ -741,7 +1363,7 @@ var RaindropCallbackHandler = class extends import_base.BaseCallbackHandler {
|
|
|
741
1363
|
return role === "human";
|
|
742
1364
|
});
|
|
743
1365
|
const lastMsg = userMessages.length > 0 ? userMessages[userMessages.length - 1] : allMessages[allMessages.length - 1];
|
|
744
|
-
const inputText = lastMsg ? typeof lastMsg.content === "string" ? lastMsg.content : safeStringify(lastMsg.content) : void 0;
|
|
1366
|
+
const inputText = lastMsg ? typeof lastMsg.content === "string" ? capText2(lastMsg.content) : safeStringify(lastMsg.content) : void 0;
|
|
745
1367
|
await this.eventShipper.patch(eventId, {
|
|
746
1368
|
userId: this.userId,
|
|
747
1369
|
convoId: this.convoId,
|
|
@@ -771,7 +1393,7 @@ var RaindropCallbackHandler = class extends import_base.BaseCallbackHandler {
|
|
|
771
1393
|
if (promptTokens != null) properties["ai.usage.prompt_tokens"] = promptTokens;
|
|
772
1394
|
if (completionTokens != null) properties["ai.usage.completion_tokens"] = completionTokens;
|
|
773
1395
|
await this.finalizeEventIfRoot(runId, {
|
|
774
|
-
output: outputText,
|
|
1396
|
+
output: capText2(outputText),
|
|
775
1397
|
model,
|
|
776
1398
|
properties: Object.keys(properties).length > 0 ? properties : void 0
|
|
777
1399
|
});
|
|
@@ -864,7 +1486,7 @@ var RaindropCallbackHandler = class extends import_base.BaseCallbackHandler {
|
|
|
864
1486
|
try {
|
|
865
1487
|
const span = this.spans.get(runId);
|
|
866
1488
|
if (span) {
|
|
867
|
-
const outputStr = typeof output === "string" ? output : safeStringify(output);
|
|
1489
|
+
const outputStr = typeof output === "string" ? output : safeStringify(output, 4096);
|
|
868
1490
|
this.traceShipper.endSpan(span, {
|
|
869
1491
|
attributes: [attrString("tool.output", truncate(outputStr, 4096))]
|
|
870
1492
|
});
|
|
@@ -949,7 +1571,7 @@ var RaindropCallbackHandler = class extends import_base.BaseCallbackHandler {
|
|
|
949
1571
|
attributes: [
|
|
950
1572
|
attrString("agent.tool", action.tool),
|
|
951
1573
|
attrString("agent.tool_input", truncate(
|
|
952
|
-
typeof action.toolInput === "string" ? action.toolInput : safeStringify(action.toolInput),
|
|
1574
|
+
typeof action.toolInput === "string" ? action.toolInput : safeStringify(action.toolInput, 4096),
|
|
953
1575
|
4096
|
|
954
1576
|
))
|
|
955
1577
|
]
|
|
@@ -991,13 +1613,10 @@ function buildProperties(tags, metadata) {
|
|
|
991
1613
|
if (metadata) Object.assign(props, metadata);
|
|
992
1614
|
return props;
|
|
993
1615
|
}
|
|
994
|
-
function safeStringify(value) {
|
|
1616
|
+
function safeStringify(value, limit) {
|
|
995
1617
|
var _a;
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
} catch (e) {
|
|
999
|
-
return String(value);
|
|
1000
|
-
}
|
|
1618
|
+
if (value === void 0) return "";
|
|
1619
|
+
return (_a = boundedStringify(value, limit)) != null ? _a : String(value);
|
|
1001
1620
|
}
|
|
1002
1621
|
function truncate(str, maxLen) {
|
|
1003
1622
|
if (str.length <= maxLen) return str;
|
|
@@ -1016,18 +1635,28 @@ function extractLLMMetadata(output) {
|
|
|
1016
1635
|
const usage = (_g = usageMeta != null ? usageMeta : tokenUsage) != null ? _g : estimatedTokens;
|
|
1017
1636
|
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;
|
|
1018
1637
|
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;
|
|
1019
|
-
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) ?
|
|
1638
|
+
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;
|
|
1020
1639
|
return { model, promptTokens, completionTokens, outputText };
|
|
1021
1640
|
}
|
|
1022
1641
|
|
|
1023
1642
|
// package.json
|
|
1024
1643
|
var package_default = {
|
|
1025
1644
|
name: "@raindrop-ai/langchain",
|
|
1026
|
-
version: "0.0.
|
|
1645
|
+
version: "0.0.4",
|
|
1027
1646
|
description: "Raindrop integration for LangChain",
|
|
1028
1647
|
main: "dist/index.js",
|
|
1029
1648
|
module: "dist/index.mjs",
|
|
1030
1649
|
types: "dist/index.d.ts",
|
|
1650
|
+
license: "MIT",
|
|
1651
|
+
repository: {
|
|
1652
|
+
type: "git",
|
|
1653
|
+
url: "git+https://github.com/raindrop-ai/raindrop-js.git",
|
|
1654
|
+
directory: "packages/langchain"
|
|
1655
|
+
},
|
|
1656
|
+
homepage: "https://github.com/raindrop-ai/raindrop-js/tree/main/packages/langchain#readme",
|
|
1657
|
+
bugs: {
|
|
1658
|
+
url: "https://github.com/raindrop-ai/raindrop-js/issues"
|
|
1659
|
+
},
|
|
1031
1660
|
exports: {
|
|
1032
1661
|
".": {
|
|
1033
1662
|
types: "./dist/index.d.ts",
|
|
@@ -1036,7 +1665,9 @@ var package_default = {
|
|
|
1036
1665
|
}
|
|
1037
1666
|
},
|
|
1038
1667
|
sideEffects: false,
|
|
1039
|
-
files: [
|
|
1668
|
+
files: [
|
|
1669
|
+
"dist/**"
|
|
1670
|
+
],
|
|
1040
1671
|
scripts: {
|
|
1041
1672
|
build: "tsup",
|
|
1042
1673
|
dev: "tsup --watch",
|
|
@@ -1056,11 +1687,20 @@ var package_default = {
|
|
|
1056
1687
|
vitest: "^2.1.9"
|
|
1057
1688
|
},
|
|
1058
1689
|
tsup: {
|
|
1059
|
-
entry: [
|
|
1060
|
-
|
|
1061
|
-
|
|
1690
|
+
entry: [
|
|
1691
|
+
"src/index.ts"
|
|
1692
|
+
],
|
|
1693
|
+
format: [
|
|
1694
|
+
"cjs",
|
|
1695
|
+
"esm"
|
|
1696
|
+
],
|
|
1697
|
+
dts: {
|
|
1698
|
+
resolve: true
|
|
1699
|
+
},
|
|
1062
1700
|
clean: true,
|
|
1063
|
-
noExternal: [
|
|
1701
|
+
noExternal: [
|
|
1702
|
+
"@raindrop-ai/core"
|
|
1703
|
+
]
|
|
1064
1704
|
},
|
|
1065
1705
|
publishConfig: {
|
|
1066
1706
|
access: "public"
|
|
@@ -1076,6 +1716,7 @@ function createRaindropLangChain(opts) {
|
|
|
1076
1716
|
var _a, _b;
|
|
1077
1717
|
const writeKey = opts.writeKey;
|
|
1078
1718
|
const enabled = !!writeKey;
|
|
1719
|
+
setMaxTextFieldChars(opts.maxTextFieldChars);
|
|
1079
1720
|
if (!writeKey) {
|
|
1080
1721
|
console.warn("[raindrop-ai/langchain] writeKey not provided; telemetry shipping is disabled");
|
|
1081
1722
|
}
|