raindrop-ai 0.0.80 → 0.0.81-otelv2
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/dist/{chunk-B5UZWYFP.mjs → chunk-PGFJVUKL.mjs} +558 -23
- package/dist/index.d.mts +14 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +492 -44
- package/dist/index.mjs +8 -93
- package/dist/tracing/index.d.mts +13 -0
- package/dist/tracing/index.d.ts +13 -0
- package/dist/tracing/index.js +563 -29
- package/dist/tracing/index.mjs +1 -1
- package/package.json +5 -2
|
@@ -1,5 +1,97 @@
|
|
|
1
|
+
// package.json
|
|
2
|
+
var package_default = {
|
|
3
|
+
name: "raindrop-ai",
|
|
4
|
+
version: "0.0.81",
|
|
5
|
+
main: "dist/index.js",
|
|
6
|
+
module: "dist/index.mjs",
|
|
7
|
+
types: "dist/index.d.ts",
|
|
8
|
+
license: "MIT",
|
|
9
|
+
exports: {
|
|
10
|
+
".": {
|
|
11
|
+
types: "./dist/index.d.ts",
|
|
12
|
+
import: "./dist/index.mjs",
|
|
13
|
+
require: "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./tracing": {
|
|
16
|
+
types: "./dist/tracing/index.d.ts",
|
|
17
|
+
node: "./dist/tracing/index.js",
|
|
18
|
+
import: "./dist/tracing/index.mjs",
|
|
19
|
+
require: "./dist/tracing/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./otel": {
|
|
22
|
+
types: "./dist/otel/index.d.ts",
|
|
23
|
+
node: "./dist/otel/index.js",
|
|
24
|
+
import: "./dist/otel/index.mjs",
|
|
25
|
+
require: "./dist/otel/index.js"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
files: [
|
|
29
|
+
"dist/**"
|
|
30
|
+
],
|
|
31
|
+
scripts: {
|
|
32
|
+
build: "tsup",
|
|
33
|
+
dev: "tsup --watch",
|
|
34
|
+
clean: "rm -rf .turbo && rm -rf dist",
|
|
35
|
+
try: "tsx ./src/example/index.ts",
|
|
36
|
+
test: "vitest run",
|
|
37
|
+
smoke: "tsx ./scripts/smoke.ts"
|
|
38
|
+
},
|
|
39
|
+
devDependencies: {
|
|
40
|
+
"@types/node": "^20.11.17",
|
|
41
|
+
msw: "^2.12.8",
|
|
42
|
+
tsup: "^8.4.0",
|
|
43
|
+
tsx: "^4.20.3",
|
|
44
|
+
typescript: "^5.3.3",
|
|
45
|
+
vitest: "^4.0.10"
|
|
46
|
+
},
|
|
47
|
+
dependencies: {
|
|
48
|
+
"@dawn-analytics/redact-pii": "0.1.2",
|
|
49
|
+
"@opentelemetry/api": "^1.9.0",
|
|
50
|
+
"@opentelemetry/resources": "^2.0.1",
|
|
51
|
+
"@traceloop/node-server-sdk": "0.19.0-otel-v1",
|
|
52
|
+
weakref: "^0.2.1",
|
|
53
|
+
zod: "^3.23.8"
|
|
54
|
+
},
|
|
55
|
+
publishConfig: {
|
|
56
|
+
access: "public",
|
|
57
|
+
provenance: false
|
|
58
|
+
},
|
|
59
|
+
tsup: {
|
|
60
|
+
entry: [
|
|
61
|
+
"src/index.ts",
|
|
62
|
+
"src/tracing/index.ts",
|
|
63
|
+
"src/otel/index.ts"
|
|
64
|
+
],
|
|
65
|
+
format: [
|
|
66
|
+
"cjs",
|
|
67
|
+
"esm"
|
|
68
|
+
],
|
|
69
|
+
external: [
|
|
70
|
+
"@traceloop/node-server-sdk",
|
|
71
|
+
"@traceloop/instrumentation-anthropic",
|
|
72
|
+
"@traceloop/instrumentation-openai",
|
|
73
|
+
"@traceloop/instrumentation-cohere",
|
|
74
|
+
"@traceloop/instrumentation-bedrock",
|
|
75
|
+
"@traceloop/instrumentation-vertexai",
|
|
76
|
+
"@traceloop/instrumentation-pinecone",
|
|
77
|
+
"@traceloop/instrumentation-chromadb",
|
|
78
|
+
"@traceloop/instrumentation-qdrant",
|
|
79
|
+
"@traceloop/instrumentation-together",
|
|
80
|
+
"@opentelemetry/instrumentation",
|
|
81
|
+
"@opentelemetry/sdk-trace-base"
|
|
82
|
+
],
|
|
83
|
+
noExternal: [
|
|
84
|
+
"@dawn/schemas"
|
|
85
|
+
],
|
|
86
|
+
dts: {
|
|
87
|
+
resolve: true
|
|
88
|
+
},
|
|
89
|
+
clean: true
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
1
93
|
// src/tracing/tracer-core.ts
|
|
2
|
-
import { context, trace as
|
|
94
|
+
import { context as context2, trace as trace4 } from "@opentelemetry/api";
|
|
3
95
|
import { AnthropicInstrumentation } from "@traceloop/instrumentation-anthropic";
|
|
4
96
|
import { BedrockInstrumentation } from "@traceloop/instrumentation-bedrock";
|
|
5
97
|
import { ChromaDBInstrumentation } from "@traceloop/instrumentation-chromadb";
|
|
@@ -15,18 +107,353 @@ import {
|
|
|
15
107
|
import * as traceloop3 from "@traceloop/node-server-sdk";
|
|
16
108
|
import { WeakValueMap } from "weakref";
|
|
17
109
|
|
|
110
|
+
// src/tracing/direct/http.ts
|
|
111
|
+
function wait(ms) {
|
|
112
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
113
|
+
}
|
|
114
|
+
function formatEndpoint(endpoint) {
|
|
115
|
+
if (!endpoint) return void 0;
|
|
116
|
+
return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
|
|
117
|
+
}
|
|
118
|
+
function parseRetryAfter(headers) {
|
|
119
|
+
var _a;
|
|
120
|
+
const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
|
|
121
|
+
if (!value) return void 0;
|
|
122
|
+
const asNumber = Number(value);
|
|
123
|
+
if (!Number.isNaN(asNumber)) return asNumber * 1e3;
|
|
124
|
+
const asDate = new Date(value).getTime();
|
|
125
|
+
if (!Number.isNaN(asDate)) {
|
|
126
|
+
const delta = asDate - Date.now();
|
|
127
|
+
return delta > 0 ? delta : 0;
|
|
128
|
+
}
|
|
129
|
+
return void 0;
|
|
130
|
+
}
|
|
131
|
+
function getRetryDelayMs(attemptNumber, previousError) {
|
|
132
|
+
if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
|
|
133
|
+
const v = previousError.retryAfterMs;
|
|
134
|
+
if (typeof v === "number") return Math.max(0, v);
|
|
135
|
+
}
|
|
136
|
+
if (attemptNumber <= 1) return 0;
|
|
137
|
+
const base = 500;
|
|
138
|
+
const factor = Math.pow(2, attemptNumber - 2);
|
|
139
|
+
return base * factor;
|
|
140
|
+
}
|
|
141
|
+
async function withRetry(operation, opName, opts) {
|
|
142
|
+
let lastError = void 0;
|
|
143
|
+
for (let attemptNumber = 1; attemptNumber <= opts.maxAttempts; attemptNumber++) {
|
|
144
|
+
if (attemptNumber > 1) {
|
|
145
|
+
const delay = getRetryDelayMs(attemptNumber, lastError);
|
|
146
|
+
if (opts.debug) {
|
|
147
|
+
console.warn(
|
|
148
|
+
`[raindrop] ${opName} retry ${attemptNumber}/${opts.maxAttempts} in ${delay}ms`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
if (delay > 0) await wait(delay);
|
|
152
|
+
} else if (opts.debug) {
|
|
153
|
+
console.log(`[raindrop] ${opName} attempt ${attemptNumber}/${opts.maxAttempts}`);
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
return await operation();
|
|
157
|
+
} catch (err) {
|
|
158
|
+
lastError = err;
|
|
159
|
+
if (opts.debug) {
|
|
160
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
161
|
+
console.warn(
|
|
162
|
+
`[raindrop] ${opName} attempt ${attemptNumber} failed: ${msg}${attemptNumber === opts.maxAttempts ? " (no more retries)" : ""}`
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
if (attemptNumber === opts.maxAttempts) break;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
169
|
+
}
|
|
170
|
+
async function postJson(url, body, headers, opts) {
|
|
171
|
+
const opName = `POST ${url}`;
|
|
172
|
+
await withRetry(
|
|
173
|
+
async () => {
|
|
174
|
+
const resp = await fetch(url, {
|
|
175
|
+
method: "POST",
|
|
176
|
+
headers: {
|
|
177
|
+
"Content-Type": "application/json",
|
|
178
|
+
...headers
|
|
179
|
+
},
|
|
180
|
+
body: JSON.stringify(body)
|
|
181
|
+
});
|
|
182
|
+
if (!resp.ok) {
|
|
183
|
+
const text = await resp.text().catch(() => "");
|
|
184
|
+
const err = new Error(
|
|
185
|
+
`HTTP ${resp.status} ${resp.statusText}${text ? `: ${text}` : ""}`
|
|
186
|
+
);
|
|
187
|
+
const retryAfterMs = parseRetryAfter(resp.headers);
|
|
188
|
+
if (typeof retryAfterMs === "number") err.retryAfterMs = retryAfterMs;
|
|
189
|
+
throw err;
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
opName,
|
|
193
|
+
opts
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// src/tracing/direct/ids.ts
|
|
198
|
+
function getCrypto() {
|
|
199
|
+
const c = globalThis.crypto;
|
|
200
|
+
return c;
|
|
201
|
+
}
|
|
202
|
+
function randomBytes(length) {
|
|
203
|
+
const cryptoObj = getCrypto();
|
|
204
|
+
const out = new Uint8Array(length);
|
|
205
|
+
if (cryptoObj && typeof cryptoObj.getRandomValues === "function") {
|
|
206
|
+
cryptoObj.getRandomValues(out);
|
|
207
|
+
return out;
|
|
208
|
+
}
|
|
209
|
+
for (let i = 0; i < out.length; i++) out[i] = Math.floor(Math.random() * 256);
|
|
210
|
+
return out;
|
|
211
|
+
}
|
|
212
|
+
function base64Encode(bytes) {
|
|
213
|
+
const maybeBuffer = globalThis.Buffer;
|
|
214
|
+
if (maybeBuffer) {
|
|
215
|
+
return maybeBuffer.from(bytes).toString("base64");
|
|
216
|
+
}
|
|
217
|
+
let binary = "";
|
|
218
|
+
for (let i2 = 0; i2 < bytes.length; i2++) {
|
|
219
|
+
binary += String.fromCharCode(bytes[i2]);
|
|
220
|
+
}
|
|
221
|
+
const btoaFn = globalThis.btoa;
|
|
222
|
+
if (typeof btoaFn === "function") return btoaFn(binary);
|
|
223
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
224
|
+
let out = "";
|
|
225
|
+
let i = 0;
|
|
226
|
+
while (i < binary.length) {
|
|
227
|
+
const c1 = binary.charCodeAt(i++) & 255;
|
|
228
|
+
const c2 = i < binary.length ? binary.charCodeAt(i++) & 255 : NaN;
|
|
229
|
+
const c3 = i < binary.length ? binary.charCodeAt(i++) & 255 : NaN;
|
|
230
|
+
const e1 = c1 >> 2;
|
|
231
|
+
const e2 = (c1 & 3) << 4 | (Number.isNaN(c2) ? 0 : c2 >> 4);
|
|
232
|
+
const e3 = Number.isNaN(c2) ? 64 : (c2 & 15) << 2 | (Number.isNaN(c3) ? 0 : c3 >> 6);
|
|
233
|
+
const e4 = Number.isNaN(c3) ? 64 : c3 & 63;
|
|
234
|
+
out += alphabet.charAt(e1);
|
|
235
|
+
out += alphabet.charAt(e2);
|
|
236
|
+
out += e3 === 64 ? "=" : alphabet.charAt(e3);
|
|
237
|
+
out += e4 === 64 ? "=" : alphabet.charAt(e4);
|
|
238
|
+
}
|
|
239
|
+
return out;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// src/tracing/direct/otlp.ts
|
|
243
|
+
var libraryVersion = package_default.version;
|
|
244
|
+
var SERVICE_NAME = "raindrop-ai";
|
|
245
|
+
function attrString(key, value) {
|
|
246
|
+
if (value === void 0) return void 0;
|
|
247
|
+
return { key, value: { stringValue: value } };
|
|
248
|
+
}
|
|
249
|
+
function attrInt(key, value) {
|
|
250
|
+
if (value === void 0) return void 0;
|
|
251
|
+
if (!Number.isFinite(value)) return void 0;
|
|
252
|
+
return { key, value: { intValue: String(Math.trunc(value)) } };
|
|
253
|
+
}
|
|
254
|
+
function buildOtlpSpan(args) {
|
|
255
|
+
const attrs = args.attributes.filter((x) => x !== void 0);
|
|
256
|
+
const span = {
|
|
257
|
+
traceId: args.ids.traceIdB64,
|
|
258
|
+
spanId: args.ids.spanIdB64,
|
|
259
|
+
name: args.name,
|
|
260
|
+
startTimeUnixNano: args.startTimeUnixNano,
|
|
261
|
+
endTimeUnixNano: args.endTimeUnixNano
|
|
262
|
+
};
|
|
263
|
+
if (args.ids.parentSpanIdB64) span.parentSpanId = args.ids.parentSpanIdB64;
|
|
264
|
+
if (attrs.length) span.attributes = attrs;
|
|
265
|
+
if (args.status) span.status = args.status;
|
|
266
|
+
return span;
|
|
267
|
+
}
|
|
268
|
+
function buildExportTraceServiceRequest(spans) {
|
|
269
|
+
return {
|
|
270
|
+
resourceSpans: [
|
|
271
|
+
{
|
|
272
|
+
resource: {
|
|
273
|
+
attributes: [{ key: "service.name", value: { stringValue: SERVICE_NAME } }]
|
|
274
|
+
},
|
|
275
|
+
scopeSpans: [
|
|
276
|
+
{
|
|
277
|
+
scope: { name: SERVICE_NAME, version: libraryVersion },
|
|
278
|
+
spans
|
|
279
|
+
}
|
|
280
|
+
]
|
|
281
|
+
}
|
|
282
|
+
]
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// src/tracing/direct/context.ts
|
|
287
|
+
import { context, trace } from "@opentelemetry/api";
|
|
288
|
+
var INVALID_TRACE_ID = "00000000000000000000000000000000";
|
|
289
|
+
var INVALID_SPAN_ID = "0000000000000000";
|
|
290
|
+
function hexToBase64(hex) {
|
|
291
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
292
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
293
|
+
bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
|
|
294
|
+
}
|
|
295
|
+
return base64Encode(bytes);
|
|
296
|
+
}
|
|
297
|
+
function getActiveTraceContext() {
|
|
298
|
+
const activeSpan = trace.getSpan(context.active());
|
|
299
|
+
if (!activeSpan) return {};
|
|
300
|
+
const spanContext = activeSpan.spanContext();
|
|
301
|
+
const traceIdHex = spanContext.traceId;
|
|
302
|
+
const spanIdHex = spanContext.spanId;
|
|
303
|
+
if (traceIdHex === INVALID_TRACE_ID) return {};
|
|
304
|
+
return {
|
|
305
|
+
traceIdB64: hexToBase64(traceIdHex),
|
|
306
|
+
parentSpanIdB64: spanIdHex !== INVALID_SPAN_ID ? hexToBase64(spanIdHex) : void 0
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// src/tracing/direct/shipper.ts
|
|
311
|
+
var DirectTraceShipper = class {
|
|
312
|
+
constructor(opts) {
|
|
313
|
+
this.queue = [];
|
|
314
|
+
this.inFlight = /* @__PURE__ */ new Set();
|
|
315
|
+
var _a, _b, _c, _d, _e;
|
|
316
|
+
this.writeKey = opts.writeKey;
|
|
317
|
+
this.baseUrl = (_a = formatEndpoint(opts.endpoint)) != null ? _a : "https://api.raindrop.ai/v1/";
|
|
318
|
+
this.enabled = (_b = opts.enabled) != null ? _b : true;
|
|
319
|
+
this.debug = opts.debug;
|
|
320
|
+
this.flushIntervalMs = (_c = opts.flushIntervalMs) != null ? _c : 1e3;
|
|
321
|
+
this.maxBatchSize = (_d = opts.maxBatchSize) != null ? _d : 50;
|
|
322
|
+
this.maxQueueSize = (_e = opts.maxQueueSize) != null ? _e : 5e3;
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Build and enqueue an OTLP span for a trackTool call.
|
|
326
|
+
*
|
|
327
|
+
* Reads the active OTEL context if traceId/parentSpanId are not provided,
|
|
328
|
+
* so spans still appear as children in the trace tree.
|
|
329
|
+
*/
|
|
330
|
+
sendToolSpan(params) {
|
|
331
|
+
if (!this.enabled) return;
|
|
332
|
+
let traceIdB64 = params.traceId;
|
|
333
|
+
let parentSpanIdB64 = params.parentSpanId;
|
|
334
|
+
if (!traceIdB64) {
|
|
335
|
+
const activeCtx = getActiveTraceContext();
|
|
336
|
+
traceIdB64 = activeCtx.traceIdB64;
|
|
337
|
+
parentSpanIdB64 = activeCtx.parentSpanIdB64;
|
|
338
|
+
}
|
|
339
|
+
const spanIdB64 = base64Encode(randomBytes(8));
|
|
340
|
+
if (!traceIdB64) {
|
|
341
|
+
traceIdB64 = base64Encode(randomBytes(16));
|
|
342
|
+
}
|
|
343
|
+
const startTimeUnixNano = String(params.startTimeMs * 1e6);
|
|
344
|
+
const endTimeUnixNano = String(params.endTimeMs * 1e6);
|
|
345
|
+
const attributes = [
|
|
346
|
+
// Required: passes hasAIOperation() filter in backend (parseSpan.ts)
|
|
347
|
+
attrString("traceloop.span.kind", "tool"),
|
|
348
|
+
// Parsed by backend for input/output payloads
|
|
349
|
+
attrString("traceloop.entity.input", params.input),
|
|
350
|
+
attrString("traceloop.entity.output", params.output),
|
|
351
|
+
// Duration tracking
|
|
352
|
+
attrInt("traceloop.entity.duration_ms", params.durationMs)
|
|
353
|
+
];
|
|
354
|
+
for (const [key, value] of Object.entries(params.properties)) {
|
|
355
|
+
attributes.push(attrString(`traceloop.association.properties.${key}`, value));
|
|
356
|
+
}
|
|
357
|
+
let status;
|
|
358
|
+
if (params.error) {
|
|
359
|
+
status = { code: 2, message: params.error };
|
|
360
|
+
} else {
|
|
361
|
+
status = {
|
|
362
|
+
code: 1
|
|
363
|
+
/* STATUS_CODE_OK */
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
const span = buildOtlpSpan({
|
|
367
|
+
ids: {
|
|
368
|
+
traceIdB64,
|
|
369
|
+
spanIdB64,
|
|
370
|
+
parentSpanIdB64
|
|
371
|
+
},
|
|
372
|
+
name: params.name,
|
|
373
|
+
startTimeUnixNano,
|
|
374
|
+
endTimeUnixNano,
|
|
375
|
+
attributes,
|
|
376
|
+
status
|
|
377
|
+
});
|
|
378
|
+
this.enqueue(span);
|
|
379
|
+
if (this.debug) {
|
|
380
|
+
console.log(`[raindrop] direct shipper: enqueued span "${params.name}"`);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
enqueue(span) {
|
|
384
|
+
if (!this.enabled) return;
|
|
385
|
+
if (this.queue.length >= this.maxQueueSize) {
|
|
386
|
+
this.queue.shift();
|
|
387
|
+
}
|
|
388
|
+
this.queue.push(span);
|
|
389
|
+
if (this.queue.length >= this.maxBatchSize) {
|
|
390
|
+
void this.flush().catch(() => {
|
|
391
|
+
});
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (!this.timer) {
|
|
395
|
+
this.timer = setTimeout(() => {
|
|
396
|
+
this.timer = void 0;
|
|
397
|
+
void this.flush().catch(() => {
|
|
398
|
+
});
|
|
399
|
+
}, this.flushIntervalMs);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
async flush() {
|
|
403
|
+
if (!this.enabled) return;
|
|
404
|
+
if (this.timer) {
|
|
405
|
+
clearTimeout(this.timer);
|
|
406
|
+
this.timer = void 0;
|
|
407
|
+
}
|
|
408
|
+
while (this.queue.length > 0) {
|
|
409
|
+
const batch = this.queue.splice(0, this.maxBatchSize);
|
|
410
|
+
const body = buildExportTraceServiceRequest(batch);
|
|
411
|
+
const url = `${this.baseUrl}traces`;
|
|
412
|
+
const p = postJson(
|
|
413
|
+
url,
|
|
414
|
+
body,
|
|
415
|
+
{ Authorization: `Bearer ${this.writeKey}` },
|
|
416
|
+
{ maxAttempts: 3, debug: this.debug }
|
|
417
|
+
);
|
|
418
|
+
this.inFlight.add(p);
|
|
419
|
+
try {
|
|
420
|
+
try {
|
|
421
|
+
await p;
|
|
422
|
+
if (this.debug) console.log(`[raindrop] direct: sent ${batch.length} spans`);
|
|
423
|
+
} catch (err) {
|
|
424
|
+
if (this.debug) {
|
|
425
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
426
|
+
console.warn(`[raindrop] direct: failed to send spans (dropping): ${msg}`);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
} finally {
|
|
430
|
+
this.inFlight.delete(p);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
async shutdown() {
|
|
435
|
+
if (this.timer) {
|
|
436
|
+
clearTimeout(this.timer);
|
|
437
|
+
this.timer = void 0;
|
|
438
|
+
}
|
|
439
|
+
await this.flush();
|
|
440
|
+
await Promise.all([...this.inFlight].map((p) => p.catch(() => {
|
|
441
|
+
})));
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
|
|
18
445
|
// src/tracing/liveInteraction.ts
|
|
19
|
-
import { SpanStatusCode, trace } from "@opentelemetry/api";
|
|
446
|
+
import { SpanStatusCode, trace as trace2 } from "@opentelemetry/api";
|
|
20
447
|
import * as traceloop from "@traceloop/node-server-sdk";
|
|
21
|
-
function getPropertiesFromContext(
|
|
448
|
+
function getPropertiesFromContext(context3) {
|
|
22
449
|
const properties = {
|
|
23
|
-
...
|
|
24
|
-
...
|
|
25
|
-
...
|
|
26
|
-
...
|
|
450
|
+
...context3.userId && { user_id: context3.userId },
|
|
451
|
+
...context3.convoId && { convo_id: context3.convoId },
|
|
452
|
+
...context3.eventId && { event_id: context3.eventId },
|
|
453
|
+
...context3.properties || {}
|
|
27
454
|
};
|
|
28
|
-
if (
|
|
29
|
-
properties.attachments = JSON.stringify(
|
|
455
|
+
if (context3.attachments && context3.attachments.length > 0) {
|
|
456
|
+
properties.attachments = JSON.stringify(context3.attachments);
|
|
30
457
|
}
|
|
31
458
|
return properties;
|
|
32
459
|
}
|
|
@@ -62,11 +489,12 @@ var LiveToolSpan = class {
|
|
|
62
489
|
}
|
|
63
490
|
};
|
|
64
491
|
var LiveInteraction = class {
|
|
65
|
-
constructor(
|
|
66
|
-
this.context =
|
|
492
|
+
constructor(context3, traceId, analytics, directShipper) {
|
|
493
|
+
this.context = context3;
|
|
67
494
|
this.analytics = analytics;
|
|
68
|
-
this.tracer =
|
|
495
|
+
this.tracer = trace2.getTracer("traceloop.tracer");
|
|
69
496
|
this.traceId = traceId;
|
|
497
|
+
this.directShipper = directShipper;
|
|
70
498
|
}
|
|
71
499
|
async withSpan(params, fn, thisArg, ...args) {
|
|
72
500
|
var _a;
|
|
@@ -101,10 +529,54 @@ var LiveInteraction = class {
|
|
|
101
529
|
...getPropertiesFromContext(this.context),
|
|
102
530
|
...params.properties || {}
|
|
103
531
|
};
|
|
104
|
-
const inputParams = params.inputParameters ? [params.inputParameters] : void 0;
|
|
105
532
|
if ((_a = this.analytics) == null ? void 0 : _a.debugLogs) {
|
|
106
533
|
console.log("[raindrop] using withTool in liveInteraction");
|
|
107
534
|
}
|
|
535
|
+
if (this.directShipper) {
|
|
536
|
+
const { traceIdB64, parentSpanIdB64 } = getActiveTraceContext();
|
|
537
|
+
const startTimeMs = Date.now();
|
|
538
|
+
let result;
|
|
539
|
+
let error;
|
|
540
|
+
try {
|
|
541
|
+
result = await fn.apply(thisArg, args);
|
|
542
|
+
} catch (err) {
|
|
543
|
+
error = err instanceof Error ? err.message : String(err);
|
|
544
|
+
const endTimeMs2 = Date.now();
|
|
545
|
+
const durationMs2 = endTimeMs2 - startTimeMs;
|
|
546
|
+
const inputStr2 = params.inputParameters ? JSON.stringify(params.inputParameters) : void 0;
|
|
547
|
+
this.directShipper.sendToolSpan({
|
|
548
|
+
name: toolName,
|
|
549
|
+
input: inputStr2,
|
|
550
|
+
output: void 0,
|
|
551
|
+
durationMs: durationMs2,
|
|
552
|
+
startTimeMs,
|
|
553
|
+
endTimeMs: endTimeMs2,
|
|
554
|
+
error,
|
|
555
|
+
properties,
|
|
556
|
+
traceId: traceIdB64,
|
|
557
|
+
parentSpanId: parentSpanIdB64
|
|
558
|
+
});
|
|
559
|
+
throw err;
|
|
560
|
+
}
|
|
561
|
+
const endTimeMs = Date.now();
|
|
562
|
+
const durationMs = endTimeMs - startTimeMs;
|
|
563
|
+
const inputStr = params.inputParameters ? JSON.stringify(params.inputParameters) : void 0;
|
|
564
|
+
const outputStr = result !== void 0 ? typeof result === "string" ? result : JSON.stringify(result) : void 0;
|
|
565
|
+
this.directShipper.sendToolSpan({
|
|
566
|
+
name: toolName,
|
|
567
|
+
input: inputStr,
|
|
568
|
+
output: outputStr,
|
|
569
|
+
durationMs,
|
|
570
|
+
startTimeMs,
|
|
571
|
+
endTimeMs,
|
|
572
|
+
error: void 0,
|
|
573
|
+
properties,
|
|
574
|
+
traceId: traceIdB64,
|
|
575
|
+
parentSpanId: parentSpanIdB64
|
|
576
|
+
});
|
|
577
|
+
return result;
|
|
578
|
+
}
|
|
579
|
+
const inputParams = params.inputParameters ? [params.inputParameters] : void 0;
|
|
108
580
|
return traceloop.withTool(
|
|
109
581
|
{
|
|
110
582
|
name: toolName,
|
|
@@ -210,10 +682,39 @@ var LiveInteraction = class {
|
|
|
210
682
|
});
|
|
211
683
|
}
|
|
212
684
|
trackTool(params) {
|
|
213
|
-
var _a;
|
|
685
|
+
var _a, _b;
|
|
214
686
|
const { name, input, output, durationMs, startTime, error, properties = {} } = params;
|
|
215
687
|
const startTimeMs = startTime instanceof Date ? startTime.getTime() : typeof startTime === "number" ? startTime : Date.now() - (durationMs != null ? durationMs : 0);
|
|
216
688
|
const endTimeMs = startTimeMs + (durationMs != null ? durationMs : 0);
|
|
689
|
+
if (this.directShipper) {
|
|
690
|
+
const { traceIdB64, parentSpanIdB64 } = getActiveTraceContext();
|
|
691
|
+
const inputStr = input !== void 0 ? typeof input === "string" ? input : JSON.stringify(input) : void 0;
|
|
692
|
+
const outputStr = output !== void 0 ? typeof output === "string" ? output : JSON.stringify(output) : void 0;
|
|
693
|
+
this.directShipper.sendToolSpan({
|
|
694
|
+
name,
|
|
695
|
+
input: inputStr,
|
|
696
|
+
output: outputStr,
|
|
697
|
+
durationMs,
|
|
698
|
+
startTimeMs,
|
|
699
|
+
endTimeMs,
|
|
700
|
+
error: error ? error instanceof Error ? error.message : String(error) : void 0,
|
|
701
|
+
properties: {
|
|
702
|
+
...getPropertiesFromContext(this.context),
|
|
703
|
+
...properties
|
|
704
|
+
},
|
|
705
|
+
traceId: traceIdB64,
|
|
706
|
+
parentSpanId: parentSpanIdB64
|
|
707
|
+
});
|
|
708
|
+
if ((_a = this.analytics) == null ? void 0 : _a.debugLogs) {
|
|
709
|
+
console.log(`[raindrop] trackTool (direct): logged tool span "${name}"`, {
|
|
710
|
+
input,
|
|
711
|
+
output,
|
|
712
|
+
durationMs,
|
|
713
|
+
error: error ? String(error) : void 0
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
217
718
|
const span = this.tracer.startSpan(name, {
|
|
218
719
|
startTime: startTimeMs
|
|
219
720
|
});
|
|
@@ -240,7 +741,7 @@ var LiveInteraction = class {
|
|
|
240
741
|
span.setStatus({ code: SpanStatusCode.OK });
|
|
241
742
|
}
|
|
242
743
|
span.end(endTimeMs);
|
|
243
|
-
if ((
|
|
744
|
+
if ((_b = this.analytics) == null ? void 0 : _b.debugLogs) {
|
|
244
745
|
console.log(`[raindrop] trackTool: logged tool span "${name}"`, {
|
|
245
746
|
input,
|
|
246
747
|
output,
|
|
@@ -252,12 +753,13 @@ var LiveInteraction = class {
|
|
|
252
753
|
};
|
|
253
754
|
|
|
254
755
|
// src/tracing/liveTracer.ts
|
|
255
|
-
import { SpanStatusCode as SpanStatusCode2, trace as
|
|
756
|
+
import { SpanStatusCode as SpanStatusCode2, trace as trace3 } from "@opentelemetry/api";
|
|
256
757
|
import * as traceloop2 from "@traceloop/node-server-sdk";
|
|
257
758
|
var LiveTracer = class {
|
|
258
|
-
constructor(globalProperties) {
|
|
259
|
-
this.tracer =
|
|
759
|
+
constructor(globalProperties, directShipper) {
|
|
760
|
+
this.tracer = trace3.getTracer("traceloop.tracer");
|
|
260
761
|
this.globalProperties = globalProperties || {};
|
|
762
|
+
this.directShipper = directShipper;
|
|
261
763
|
}
|
|
262
764
|
async withSpan(params, fn, thisArg, ...args) {
|
|
263
765
|
const taskName = typeof params === "string" ? params : params.name;
|
|
@@ -285,6 +787,27 @@ var LiveTracer = class {
|
|
|
285
787
|
const { name, input, output, durationMs, startTime, error, properties = {} } = params;
|
|
286
788
|
const startTimeMs = startTime instanceof Date ? startTime.getTime() : typeof startTime === "number" ? startTime : Date.now() - (durationMs != null ? durationMs : 0);
|
|
287
789
|
const endTimeMs = startTimeMs + (durationMs != null ? durationMs : 0);
|
|
790
|
+
if (this.directShipper) {
|
|
791
|
+
const { traceIdB64, parentSpanIdB64 } = getActiveTraceContext();
|
|
792
|
+
const inputStr = input !== void 0 ? typeof input === "string" ? input : JSON.stringify(input) : void 0;
|
|
793
|
+
const outputStr = output !== void 0 ? typeof output === "string" ? output : JSON.stringify(output) : void 0;
|
|
794
|
+
this.directShipper.sendToolSpan({
|
|
795
|
+
name,
|
|
796
|
+
input: inputStr,
|
|
797
|
+
output: outputStr,
|
|
798
|
+
durationMs,
|
|
799
|
+
startTimeMs,
|
|
800
|
+
endTimeMs,
|
|
801
|
+
error: error ? error instanceof Error ? error.message : String(error) : void 0,
|
|
802
|
+
properties: {
|
|
803
|
+
...this.globalProperties,
|
|
804
|
+
...properties
|
|
805
|
+
},
|
|
806
|
+
traceId: traceIdB64,
|
|
807
|
+
parentSpanId: parentSpanIdB64
|
|
808
|
+
});
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
288
811
|
const span = this.tracer.startSpan(name, {
|
|
289
812
|
startTime: startTimeMs
|
|
290
813
|
});
|
|
@@ -390,14 +913,23 @@ var activeInteractions = new WeakValueMap();
|
|
|
390
913
|
var activeInteractionsByEventId = new WeakValueMap();
|
|
391
914
|
function getCurrentTraceId() {
|
|
392
915
|
var _a;
|
|
393
|
-
return (_a =
|
|
916
|
+
return (_a = trace4.getSpan(context2.active())) == null ? void 0 : _a.spanContext().traceId;
|
|
394
917
|
}
|
|
395
918
|
var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
|
|
396
|
-
const { logLevel, useExternalOtel, tracingEnabled, disableBatching, ...otherOptions } = options;
|
|
919
|
+
const { logLevel, useExternalOtel, tracingEnabled, disableBatching, bypassOtelForTools, directEndpoint, ...otherOptions } = options;
|
|
397
920
|
if (isDebug) {
|
|
398
921
|
const mode = useExternalOtel ? "(external OTEL mode)" : tracingEnabled === false ? "(tracing disabled)" : "";
|
|
399
922
|
console.log("[raindrop] Initializing tracing", mode);
|
|
923
|
+
if (bypassOtelForTools) {
|
|
924
|
+
console.log("[raindrop] bypassOtelForTools enabled \u2014 tool spans ship directly via HTTP");
|
|
925
|
+
}
|
|
400
926
|
}
|
|
927
|
+
const directShipper = bypassOtelForTools ? new DirectTraceShipper({
|
|
928
|
+
writeKey,
|
|
929
|
+
endpoint: directEndpoint != null ? directEndpoint : apiUrl,
|
|
930
|
+
debug: isDebug,
|
|
931
|
+
enabled: tracingEnabled !== false
|
|
932
|
+
}) : void 0;
|
|
401
933
|
let traceloopInitialized = false;
|
|
402
934
|
try {
|
|
403
935
|
if (useExternalOtel) {
|
|
@@ -522,7 +1054,8 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
|
|
|
522
1054
|
const interaction = new LiveInteraction(
|
|
523
1055
|
traceContext,
|
|
524
1056
|
traceId,
|
|
525
|
-
analytics
|
|
1057
|
+
analytics,
|
|
1058
|
+
directShipper
|
|
526
1059
|
);
|
|
527
1060
|
if (traceId) {
|
|
528
1061
|
activeInteractions.set(traceId, interaction);
|
|
@@ -542,16 +1075,17 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
|
|
|
542
1075
|
return interactionByTraceId;
|
|
543
1076
|
}
|
|
544
1077
|
}
|
|
545
|
-
const interaction = new LiveInteraction({ eventId }, traceId, analytics);
|
|
1078
|
+
const interaction = new LiveInteraction({ eventId }, traceId, analytics, directShipper);
|
|
546
1079
|
activeInteractionsByEventId.set(eventId, interaction);
|
|
547
1080
|
return interaction;
|
|
548
1081
|
},
|
|
549
1082
|
tracer(globalProperties) {
|
|
550
|
-
return new LiveTracer(globalProperties);
|
|
1083
|
+
return new LiveTracer(globalProperties, directShipper);
|
|
551
1084
|
},
|
|
552
1085
|
async close() {
|
|
553
1086
|
activeInteractions.clear();
|
|
554
1087
|
activeInteractionsByEventId.clear();
|
|
1088
|
+
await (directShipper == null ? void 0 : directShipper.shutdown());
|
|
555
1089
|
if (traceloopInitialized) {
|
|
556
1090
|
try {
|
|
557
1091
|
await traceloop3.forceFlush();
|
|
@@ -563,5 +1097,6 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
|
|
|
563
1097
|
};
|
|
564
1098
|
|
|
565
1099
|
export {
|
|
1100
|
+
package_default,
|
|
566
1101
|
tracing
|
|
567
1102
|
};
|
package/dist/index.d.mts
CHANGED
|
@@ -620,6 +620,20 @@ interface AnalyticsConfig {
|
|
|
620
620
|
* Defaults to true in development (NODE_ENV !== 'production').
|
|
621
621
|
*/
|
|
622
622
|
disableBatching?: boolean;
|
|
623
|
+
/**
|
|
624
|
+
* When true, all `trackTool()` and `withTool()` calls bypass the OTEL
|
|
625
|
+
* exporter pipeline and ship spans directly to the Raindrop API via HTTP POST.
|
|
626
|
+
*
|
|
627
|
+
* Spans still read the active OTEL context to inherit traceId
|
|
628
|
+
* and parentSpanId — they still appear as children in the trace tree.
|
|
629
|
+
* Only the shipping mechanism changes.
|
|
630
|
+
*
|
|
631
|
+
* This is useful when OTEL setup is fragile (e.g., conflicting providers,
|
|
632
|
+
* broken exporters) but you still want tool spans to work reliably.
|
|
633
|
+
*
|
|
634
|
+
* @default false
|
|
635
|
+
*/
|
|
636
|
+
bypassOtelForTools?: boolean;
|
|
623
637
|
/**
|
|
624
638
|
* Explicitly specify modules to instrument. Optional.
|
|
625
639
|
*
|