braintrust 3.6.0 → 3.7.1
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/dev/dist/index.js +2692 -1472
- package/dev/dist/index.mjs +2616 -1396
- package/dist/auto-instrumentations/bundler/esbuild.cjs +46 -21
- package/dist/auto-instrumentations/bundler/esbuild.mjs +2 -2
- package/dist/auto-instrumentations/bundler/rollup.cjs +46 -21
- package/dist/auto-instrumentations/bundler/rollup.mjs +2 -2
- package/dist/auto-instrumentations/bundler/vite.cjs +46 -21
- package/dist/auto-instrumentations/bundler/vite.mjs +2 -2
- package/dist/auto-instrumentations/bundler/webpack-loader.cjs +952 -0
- package/dist/auto-instrumentations/bundler/webpack-loader.d.ts +53 -0
- package/dist/auto-instrumentations/bundler/webpack.cjs +46 -21
- package/dist/auto-instrumentations/bundler/webpack.mjs +2 -2
- package/dist/auto-instrumentations/{chunk-WOUC73KB.mjs → chunk-NY4CGTN6.mjs} +1 -1
- package/dist/auto-instrumentations/{chunk-F7WAXFNM.mjs → chunk-YCKND42U.mjs} +46 -21
- package/dist/auto-instrumentations/hook.mjs +77 -26
- package/dist/auto-instrumentations/index.cjs +46 -21
- package/dist/auto-instrumentations/index.mjs +1 -1
- package/dist/browser.d.mts +8 -30
- package/dist/browser.d.ts +8 -30
- package/dist/browser.js +5051 -6344
- package/dist/browser.mjs +5051 -6344
- package/dist/cli.js +2622 -1398
- package/dist/edge-light.js +9456 -10773
- package/dist/edge-light.mjs +9456 -10773
- package/dist/index.d.mts +8 -30
- package/dist/index.d.ts +8 -30
- package/dist/index.js +5078 -6371
- package/dist/index.mjs +4870 -6163
- package/dist/instrumentation/index.js +2491 -1319
- package/dist/instrumentation/index.mjs +2491 -1319
- package/dist/workerd.js +9456 -10773
- package/dist/workerd.mjs +9456 -10773
- package/package.json +6 -2
|
@@ -70,7 +70,7 @@ var DefaultTracingChannel = class {
|
|
|
70
70
|
}
|
|
71
71
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
72
72
|
tracePromise(fn, _message, thisArg, ...args) {
|
|
73
|
-
return
|
|
73
|
+
return fn.apply(thisArg, args);
|
|
74
74
|
}
|
|
75
75
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
76
76
|
traceCallback(fn, _position, _message, thisArg, ...args) {
|
|
@@ -90,6 +90,7 @@ var iso = {
|
|
|
90
90
|
processOn: (_0, _1) => {
|
|
91
91
|
},
|
|
92
92
|
basename: (filepath) => filepath.split(/[\\/]/).pop() || filepath,
|
|
93
|
+
// eslint-disable-next-line no-restricted-properties -- preserving intentional console usage.
|
|
93
94
|
writeln: (text) => console.log(text)
|
|
94
95
|
};
|
|
95
96
|
var isomorph_default = iso;
|
|
@@ -125,7 +126,7 @@ function patchStreamIfNeeded(stream, options) {
|
|
|
125
126
|
if (!completed) {
|
|
126
127
|
completed = true;
|
|
127
128
|
try {
|
|
128
|
-
options.onComplete(chunks);
|
|
129
|
+
await options.onComplete(chunks);
|
|
129
130
|
} catch (error) {
|
|
130
131
|
console.error("Error in stream onComplete handler:", error);
|
|
131
132
|
}
|
|
@@ -137,7 +138,7 @@ function patchStreamIfNeeded(stream, options) {
|
|
|
137
138
|
chunks.push(chunk);
|
|
138
139
|
if (options.onChunk) {
|
|
139
140
|
try {
|
|
140
|
-
options.onChunk(chunk);
|
|
141
|
+
await options.onChunk(chunk);
|
|
141
142
|
} catch (error) {
|
|
142
143
|
console.error("Error in stream onChunk handler:", error);
|
|
143
144
|
}
|
|
@@ -150,7 +151,7 @@ function patchStreamIfNeeded(stream, options) {
|
|
|
150
151
|
completed = true;
|
|
151
152
|
if (options.onError) {
|
|
152
153
|
try {
|
|
153
|
-
options.onError(
|
|
154
|
+
await options.onError(
|
|
154
155
|
error instanceof Error ? error : new Error(String(error)),
|
|
155
156
|
chunks
|
|
156
157
|
);
|
|
@@ -168,7 +169,7 @@ function patchStreamIfNeeded(stream, options) {
|
|
|
168
169
|
if (!completed) {
|
|
169
170
|
completed = true;
|
|
170
171
|
try {
|
|
171
|
-
options.onComplete(chunks);
|
|
172
|
+
await options.onComplete(chunks);
|
|
172
173
|
} catch (error) {
|
|
173
174
|
console.error("Error in stream onComplete handler:", error);
|
|
174
175
|
}
|
|
@@ -185,7 +186,7 @@ function patchStreamIfNeeded(stream, options) {
|
|
|
185
186
|
const error = rawError instanceof Error ? rawError : new Error(String(rawError));
|
|
186
187
|
if (options.onError) {
|
|
187
188
|
try {
|
|
188
|
-
options.onError(error, chunks);
|
|
189
|
+
await options.onError(error, chunks);
|
|
189
190
|
} catch (handlerError) {
|
|
190
191
|
console.error("Error in stream onError handler:", handlerError);
|
|
191
192
|
}
|
|
@@ -1537,6 +1538,15 @@ function addAzureBlobHeaders(headers, url) {
|
|
|
1537
1538
|
headers["x-ms-blob-type"] = "BlockBlob";
|
|
1538
1539
|
}
|
|
1539
1540
|
}
|
|
1541
|
+
function filterFrom(record, keys) {
|
|
1542
|
+
const out = {};
|
|
1543
|
+
for (const k of Object.keys(record)) {
|
|
1544
|
+
if (!keys.includes(k)) {
|
|
1545
|
+
out[k] = record[k];
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
return out;
|
|
1549
|
+
}
|
|
1540
1550
|
|
|
1541
1551
|
// src/generated_types.ts
|
|
1542
1552
|
import { z as z6 } from "zod/v3";
|
|
@@ -8332,51 +8342,28 @@ function traceSyncStreamChannel(channel2, config) {
|
|
|
8332
8342
|
}
|
|
8333
8343
|
const { span, startTime } = spanData;
|
|
8334
8344
|
const endEvent = event;
|
|
8335
|
-
|
|
8336
|
-
|
|
8337
|
-
|
|
8338
|
-
|
|
8339
|
-
|
|
8340
|
-
|
|
8341
|
-
|
|
8342
|
-
|
|
8343
|
-
|
|
8344
|
-
|
|
8345
|
-
|
|
8346
|
-
|
|
8347
|
-
|
|
8348
|
-
return;
|
|
8349
|
-
}
|
|
8350
|
-
let first = true;
|
|
8351
|
-
stream.on("chunk", () => {
|
|
8352
|
-
if (first) {
|
|
8353
|
-
span.log({
|
|
8354
|
-
metrics: {
|
|
8355
|
-
time_to_first_token: getCurrentUnixTimestamp() - startTime
|
|
8356
|
-
}
|
|
8357
|
-
});
|
|
8358
|
-
first = false;
|
|
8359
|
-
}
|
|
8360
|
-
});
|
|
8361
|
-
stream.on("chatCompletion", (completion) => {
|
|
8362
|
-
try {
|
|
8363
|
-
if (hasChoices(completion)) {
|
|
8364
|
-
span.log({
|
|
8365
|
-
output: completion.choices
|
|
8366
|
-
});
|
|
8367
|
-
}
|
|
8368
|
-
} catch (error) {
|
|
8369
|
-
console.error(
|
|
8370
|
-
`Error extracting chatCompletion for ${channelName}:`,
|
|
8371
|
-
error
|
|
8372
|
-
);
|
|
8345
|
+
const handleResolvedResult = (result) => {
|
|
8346
|
+
const resolvedEndEvent = {
|
|
8347
|
+
...endEvent,
|
|
8348
|
+
result
|
|
8349
|
+
};
|
|
8350
|
+
if (config.patchResult?.({
|
|
8351
|
+
channelName,
|
|
8352
|
+
endEvent: resolvedEndEvent,
|
|
8353
|
+
result,
|
|
8354
|
+
span,
|
|
8355
|
+
startTime
|
|
8356
|
+
})) {
|
|
8357
|
+
return;
|
|
8373
8358
|
}
|
|
8374
|
-
|
|
8375
|
-
|
|
8376
|
-
|
|
8359
|
+
const stream = result;
|
|
8360
|
+
if (!isSyncStreamLike(stream)) {
|
|
8361
|
+
span.end();
|
|
8362
|
+
states.delete(event);
|
|
8377
8363
|
return;
|
|
8378
8364
|
}
|
|
8379
|
-
|
|
8365
|
+
let first = true;
|
|
8366
|
+
stream.on("chunk", () => {
|
|
8380
8367
|
if (first) {
|
|
8381
8368
|
span.log({
|
|
8382
8369
|
metrics: {
|
|
@@ -8385,25 +8372,55 @@ function traceSyncStreamChannel(channel2, config) {
|
|
|
8385
8372
|
});
|
|
8386
8373
|
first = false;
|
|
8387
8374
|
}
|
|
8388
|
-
|
|
8389
|
-
|
|
8390
|
-
|
|
8375
|
+
});
|
|
8376
|
+
stream.on("chatCompletion", (completion) => {
|
|
8377
|
+
try {
|
|
8378
|
+
if (hasChoices(completion)) {
|
|
8379
|
+
span.log({
|
|
8380
|
+
output: completion.choices
|
|
8381
|
+
});
|
|
8382
|
+
}
|
|
8383
|
+
} catch (error) {
|
|
8384
|
+
console.error(
|
|
8385
|
+
`Error extracting chatCompletion for ${channelName}:`,
|
|
8386
|
+
error
|
|
8387
|
+
);
|
|
8391
8388
|
}
|
|
8392
|
-
} catch (error) {
|
|
8393
|
-
console.error(`Error extracting event for ${channelName}:`, error);
|
|
8394
|
-
}
|
|
8395
|
-
});
|
|
8396
|
-
stream.on("end", () => {
|
|
8397
|
-
span.end();
|
|
8398
|
-
states.delete(event);
|
|
8399
|
-
});
|
|
8400
|
-
stream.on("error", (error) => {
|
|
8401
|
-
span.log({
|
|
8402
|
-
error: error.message
|
|
8403
8389
|
});
|
|
8404
|
-
|
|
8405
|
-
|
|
8406
|
-
|
|
8390
|
+
stream.on("event", (streamEvent) => {
|
|
8391
|
+
if (!config.extractFromEvent) {
|
|
8392
|
+
return;
|
|
8393
|
+
}
|
|
8394
|
+
try {
|
|
8395
|
+
if (first) {
|
|
8396
|
+
span.log({
|
|
8397
|
+
metrics: {
|
|
8398
|
+
time_to_first_token: getCurrentUnixTimestamp() - startTime
|
|
8399
|
+
}
|
|
8400
|
+
});
|
|
8401
|
+
first = false;
|
|
8402
|
+
}
|
|
8403
|
+
const extracted = config.extractFromEvent(streamEvent);
|
|
8404
|
+
if (extracted && Object.keys(extracted).length > 0) {
|
|
8405
|
+
span.log(extracted);
|
|
8406
|
+
}
|
|
8407
|
+
} catch (error) {
|
|
8408
|
+
console.error(`Error extracting event for ${channelName}:`, error);
|
|
8409
|
+
}
|
|
8410
|
+
});
|
|
8411
|
+
stream.on("end", () => {
|
|
8412
|
+
span.end();
|
|
8413
|
+
states.delete(event);
|
|
8414
|
+
});
|
|
8415
|
+
stream.on("error", (error) => {
|
|
8416
|
+
span.log({
|
|
8417
|
+
error: error.message
|
|
8418
|
+
});
|
|
8419
|
+
span.end();
|
|
8420
|
+
states.delete(event);
|
|
8421
|
+
});
|
|
8422
|
+
};
|
|
8423
|
+
handleResolvedResult(endEvent.result);
|
|
8407
8424
|
},
|
|
8408
8425
|
error: (event) => {
|
|
8409
8426
|
logErrorAndEnd(states, event);
|
|
@@ -9183,7 +9200,7 @@ var AnthropicPlugin = class extends BasePlugin {
|
|
|
9183
9200
|
this.unsubscribers.push(
|
|
9184
9201
|
traceStreamingChannel(anthropicChannels.betaMessagesCreate, {
|
|
9185
9202
|
...anthropicConfig,
|
|
9186
|
-
name: "anthropic.
|
|
9203
|
+
name: "anthropic.messages.create"
|
|
9187
9204
|
})
|
|
9188
9205
|
);
|
|
9189
9206
|
}
|
|
@@ -9206,9 +9223,12 @@ function parseMetricsFromUsage2(usage) {
|
|
|
9206
9223
|
return metrics;
|
|
9207
9224
|
}
|
|
9208
9225
|
function aggregateAnthropicStreamChunks(chunks) {
|
|
9209
|
-
const
|
|
9226
|
+
const fallbackTextDeltas = [];
|
|
9227
|
+
const contentBlocks = {};
|
|
9228
|
+
const contentBlockDeltas = {};
|
|
9210
9229
|
let metrics = {};
|
|
9211
9230
|
let metadata = {};
|
|
9231
|
+
let role;
|
|
9212
9232
|
for (const event of chunks) {
|
|
9213
9233
|
switch (event?.type) {
|
|
9214
9234
|
case "message_start":
|
|
@@ -9216,15 +9236,55 @@ function aggregateAnthropicStreamChunks(chunks) {
|
|
|
9216
9236
|
const initialMetrics = parseMetricsFromUsage2(event.message.usage);
|
|
9217
9237
|
metrics = { ...metrics, ...initialMetrics };
|
|
9218
9238
|
}
|
|
9239
|
+
if (typeof event.message?.role === "string") {
|
|
9240
|
+
role = event.message.role;
|
|
9241
|
+
}
|
|
9242
|
+
break;
|
|
9243
|
+
case "content_block_start":
|
|
9244
|
+
if (event.content_block) {
|
|
9245
|
+
contentBlocks[event.index] = event.content_block;
|
|
9246
|
+
contentBlockDeltas[event.index] = { textDeltas: [], citations: [] };
|
|
9247
|
+
}
|
|
9219
9248
|
break;
|
|
9220
|
-
case "content_block_delta":
|
|
9221
|
-
|
|
9222
|
-
|
|
9249
|
+
case "content_block_delta": {
|
|
9250
|
+
const acc = contentBlockDeltas[event.index];
|
|
9251
|
+
const delta = event.delta;
|
|
9252
|
+
if (!delta) break;
|
|
9253
|
+
if (delta.type === "text_delta" && "text" in delta) {
|
|
9254
|
+
const text = delta.text;
|
|
9223
9255
|
if (text) {
|
|
9224
|
-
|
|
9256
|
+
if (acc !== void 0) {
|
|
9257
|
+
acc.textDeltas.push(text);
|
|
9258
|
+
} else {
|
|
9259
|
+
fallbackTextDeltas.push(text);
|
|
9260
|
+
}
|
|
9261
|
+
}
|
|
9262
|
+
} else if (delta.type === "input_json_delta" && "partial_json" in delta) {
|
|
9263
|
+
const partialJson = delta.partial_json;
|
|
9264
|
+
if (partialJson && acc !== void 0) {
|
|
9265
|
+
acc.textDeltas.push(partialJson);
|
|
9266
|
+
}
|
|
9267
|
+
} else if (delta.type === "thinking_delta" && "thinking" in delta) {
|
|
9268
|
+
const thinking = delta.thinking;
|
|
9269
|
+
if (thinking && acc !== void 0) {
|
|
9270
|
+
acc.textDeltas.push(thinking);
|
|
9271
|
+
}
|
|
9272
|
+
} else if (delta.type === "citations_delta" && "citation" in delta) {
|
|
9273
|
+
const citation = delta.citation;
|
|
9274
|
+
if (citation && acc !== void 0) {
|
|
9275
|
+
acc.citations.push(citation);
|
|
9225
9276
|
}
|
|
9226
9277
|
}
|
|
9227
9278
|
break;
|
|
9279
|
+
}
|
|
9280
|
+
case "content_block_stop":
|
|
9281
|
+
finalizeContentBlock(
|
|
9282
|
+
event.index,
|
|
9283
|
+
contentBlocks,
|
|
9284
|
+
contentBlockDeltas,
|
|
9285
|
+
fallbackTextDeltas
|
|
9286
|
+
);
|
|
9287
|
+
break;
|
|
9228
9288
|
case "message_delta":
|
|
9229
9289
|
if (event.usage) {
|
|
9230
9290
|
const finalMetrics = parseMetricsFromUsage2(event.usage);
|
|
@@ -9236,7 +9296,21 @@ function aggregateAnthropicStreamChunks(chunks) {
|
|
|
9236
9296
|
break;
|
|
9237
9297
|
}
|
|
9238
9298
|
}
|
|
9239
|
-
const
|
|
9299
|
+
const orderedContent = Object.entries(contentBlocks).map(([index, block]) => ({
|
|
9300
|
+
block,
|
|
9301
|
+
index: Number(index)
|
|
9302
|
+
})).filter(({ block }) => block !== void 0).sort((left, right) => left.index - right.index).map(({ block }) => block);
|
|
9303
|
+
let output = fallbackTextDeltas.join("");
|
|
9304
|
+
if (orderedContent.length > 0) {
|
|
9305
|
+
if (orderedContent.every(isTextContentBlock) && orderedContent.every((block) => !block.citations?.length)) {
|
|
9306
|
+
output = orderedContent.map((block) => block.text).join("");
|
|
9307
|
+
} else {
|
|
9308
|
+
output = {
|
|
9309
|
+
...role ? { role } : {},
|
|
9310
|
+
content: orderedContent
|
|
9311
|
+
};
|
|
9312
|
+
}
|
|
9313
|
+
}
|
|
9240
9314
|
const finalized = finalizeAnthropicTokens(metrics);
|
|
9241
9315
|
const filteredMetrics = Object.fromEntries(
|
|
9242
9316
|
Object.entries(finalized).filter(
|
|
@@ -9249,6 +9323,61 @@ function aggregateAnthropicStreamChunks(chunks) {
|
|
|
9249
9323
|
metadata
|
|
9250
9324
|
};
|
|
9251
9325
|
}
|
|
9326
|
+
function finalizeContentBlock(index, contentBlocks, contentBlockDeltas, fallbackTextDeltas) {
|
|
9327
|
+
const contentBlock = contentBlocks[index];
|
|
9328
|
+
if (!contentBlock) {
|
|
9329
|
+
return;
|
|
9330
|
+
}
|
|
9331
|
+
const acc = contentBlockDeltas[index];
|
|
9332
|
+
const text = acc?.textDeltas.join("") ?? "";
|
|
9333
|
+
if (isToolUseContentBlock(contentBlock)) {
|
|
9334
|
+
if (!text) {
|
|
9335
|
+
return;
|
|
9336
|
+
}
|
|
9337
|
+
try {
|
|
9338
|
+
contentBlocks[index] = {
|
|
9339
|
+
...contentBlock,
|
|
9340
|
+
input: JSON.parse(text)
|
|
9341
|
+
};
|
|
9342
|
+
} catch {
|
|
9343
|
+
fallbackTextDeltas.push(text);
|
|
9344
|
+
delete contentBlocks[index];
|
|
9345
|
+
}
|
|
9346
|
+
return;
|
|
9347
|
+
}
|
|
9348
|
+
if (isTextContentBlock(contentBlock)) {
|
|
9349
|
+
if (!text) {
|
|
9350
|
+
delete contentBlocks[index];
|
|
9351
|
+
return;
|
|
9352
|
+
}
|
|
9353
|
+
const updated = { ...contentBlock, text };
|
|
9354
|
+
if (acc?.citations.length) {
|
|
9355
|
+
updated.citations = acc.citations;
|
|
9356
|
+
}
|
|
9357
|
+
contentBlocks[index] = updated;
|
|
9358
|
+
return;
|
|
9359
|
+
}
|
|
9360
|
+
if (isThinkingContentBlock(contentBlock)) {
|
|
9361
|
+
if (!text) {
|
|
9362
|
+
delete contentBlocks[index];
|
|
9363
|
+
return;
|
|
9364
|
+
}
|
|
9365
|
+
contentBlocks[index] = {
|
|
9366
|
+
...contentBlock,
|
|
9367
|
+
thinking: text
|
|
9368
|
+
};
|
|
9369
|
+
return;
|
|
9370
|
+
}
|
|
9371
|
+
}
|
|
9372
|
+
function isTextContentBlock(contentBlock) {
|
|
9373
|
+
return contentBlock.type === "text";
|
|
9374
|
+
}
|
|
9375
|
+
function isToolUseContentBlock(contentBlock) {
|
|
9376
|
+
return contentBlock.type === "tool_use";
|
|
9377
|
+
}
|
|
9378
|
+
function isThinkingContentBlock(contentBlock) {
|
|
9379
|
+
return contentBlock.type === "thinking";
|
|
9380
|
+
}
|
|
9252
9381
|
function isAnthropicBase64ContentBlock(input) {
|
|
9253
9382
|
return (input.type === "image" || input.type === "document") && isObject(input.source) && input.source.type === "base64";
|
|
9254
9383
|
}
|
|
@@ -9303,15 +9432,6 @@ function coalesceInput(messages, system) {
|
|
|
9303
9432
|
}
|
|
9304
9433
|
return input;
|
|
9305
9434
|
}
|
|
9306
|
-
function filterFrom(obj, fieldsToRemove) {
|
|
9307
|
-
const result = {};
|
|
9308
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
9309
|
-
if (!fieldsToRemove.includes(key)) {
|
|
9310
|
-
result[key] = value;
|
|
9311
|
-
}
|
|
9312
|
-
}
|
|
9313
|
-
return result;
|
|
9314
|
-
}
|
|
9315
9435
|
|
|
9316
9436
|
// src/wrappers/ai-sdk/normalize-logged-output.ts
|
|
9317
9437
|
var REMOVE_NORMALIZED_VALUE = Symbol("braintrust.ai-sdk.remove-normalized");
|
|
@@ -9425,10 +9545,6 @@ var aiSDKChannels = defineChannels("ai", {
|
|
|
9425
9545
|
channelName: "streamText",
|
|
9426
9546
|
kind: "async"
|
|
9427
9547
|
}),
|
|
9428
|
-
streamTextSync: channel({
|
|
9429
|
-
channelName: "streamText.sync",
|
|
9430
|
-
kind: "sync-stream"
|
|
9431
|
-
}),
|
|
9432
9548
|
generateObject: channel({
|
|
9433
9549
|
channelName: "generateObject",
|
|
9434
9550
|
kind: "async"
|
|
@@ -9437,10 +9553,6 @@ var aiSDKChannels = defineChannels("ai", {
|
|
|
9437
9553
|
channelName: "streamObject",
|
|
9438
9554
|
kind: "async"
|
|
9439
9555
|
}),
|
|
9440
|
-
streamObjectSync: channel({
|
|
9441
|
-
channelName: "streamObject.sync",
|
|
9442
|
-
kind: "sync-stream"
|
|
9443
|
-
}),
|
|
9444
9556
|
agentGenerate: channel({
|
|
9445
9557
|
channelName: "Agent.generate",
|
|
9446
9558
|
kind: "async"
|
|
@@ -9476,6 +9588,9 @@ var DEFAULT_DENY_OUTPUT_PATHS = [
|
|
|
9476
9588
|
];
|
|
9477
9589
|
var AUTO_PATCHED_MODEL = Symbol.for("braintrust.ai-sdk.auto-patched-model");
|
|
9478
9590
|
var AUTO_PATCHED_TOOL = Symbol.for("braintrust.ai-sdk.auto-patched-tool");
|
|
9591
|
+
var RUNTIME_DENY_OUTPUT_PATHS = Symbol.for(
|
|
9592
|
+
"braintrust.ai-sdk.deny-output-paths"
|
|
9593
|
+
);
|
|
9479
9594
|
var AISDKPlugin = class extends BasePlugin {
|
|
9480
9595
|
config;
|
|
9481
9596
|
constructor(config = {}) {
|
|
@@ -9497,7 +9612,10 @@ var AISDKPlugin = class extends BasePlugin {
|
|
|
9497
9612
|
extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
|
|
9498
9613
|
extractOutput: (result, endEvent) => {
|
|
9499
9614
|
finalizeAISDKChildTracing(endEvent);
|
|
9500
|
-
return processAISDKOutput(
|
|
9615
|
+
return processAISDKOutput(
|
|
9616
|
+
result,
|
|
9617
|
+
resolveDenyOutputPaths(endEvent, denyOutputPaths)
|
|
9618
|
+
);
|
|
9501
9619
|
},
|
|
9502
9620
|
extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
|
|
9503
9621
|
aggregateChunks: aggregateAISDKChunks
|
|
@@ -9508,25 +9626,14 @@ var AISDKPlugin = class extends BasePlugin {
|
|
|
9508
9626
|
name: "streamText",
|
|
9509
9627
|
type: "llm" /* LLM */,
|
|
9510
9628
|
extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
|
|
9511
|
-
extractOutput: (result) => processAISDKOutput(
|
|
9629
|
+
extractOutput: (result, endEvent) => processAISDKOutput(
|
|
9630
|
+
result,
|
|
9631
|
+
resolveDenyOutputPaths(endEvent, denyOutputPaths)
|
|
9632
|
+
),
|
|
9512
9633
|
extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
|
|
9513
9634
|
aggregateChunks: aggregateAISDKChunks,
|
|
9514
9635
|
patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
|
|
9515
|
-
denyOutputPaths,
|
|
9516
|
-
endEvent,
|
|
9517
|
-
result,
|
|
9518
|
-
span,
|
|
9519
|
-
startTime
|
|
9520
|
-
})
|
|
9521
|
-
})
|
|
9522
|
-
);
|
|
9523
|
-
this.unsubscribers.push(
|
|
9524
|
-
traceSyncStreamChannel(aiSDKChannels.streamTextSync, {
|
|
9525
|
-
name: "streamText",
|
|
9526
|
-
type: "llm" /* LLM */,
|
|
9527
|
-
extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
|
|
9528
|
-
patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
|
|
9529
|
-
denyOutputPaths,
|
|
9636
|
+
defaultDenyOutputPaths: denyOutputPaths,
|
|
9530
9637
|
endEvent,
|
|
9531
9638
|
result,
|
|
9532
9639
|
span,
|
|
@@ -9541,7 +9648,10 @@ var AISDKPlugin = class extends BasePlugin {
|
|
|
9541
9648
|
extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
|
|
9542
9649
|
extractOutput: (result, endEvent) => {
|
|
9543
9650
|
finalizeAISDKChildTracing(endEvent);
|
|
9544
|
-
return processAISDKOutput(
|
|
9651
|
+
return processAISDKOutput(
|
|
9652
|
+
result,
|
|
9653
|
+
resolveDenyOutputPaths(endEvent, denyOutputPaths)
|
|
9654
|
+
);
|
|
9545
9655
|
},
|
|
9546
9656
|
extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
|
|
9547
9657
|
aggregateChunks: aggregateAISDKChunks
|
|
@@ -9552,25 +9662,14 @@ var AISDKPlugin = class extends BasePlugin {
|
|
|
9552
9662
|
name: "streamObject",
|
|
9553
9663
|
type: "llm" /* LLM */,
|
|
9554
9664
|
extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
|
|
9555
|
-
extractOutput: (result) => processAISDKOutput(
|
|
9665
|
+
extractOutput: (result, endEvent) => processAISDKOutput(
|
|
9666
|
+
result,
|
|
9667
|
+
resolveDenyOutputPaths(endEvent, denyOutputPaths)
|
|
9668
|
+
),
|
|
9556
9669
|
extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
|
|
9557
9670
|
aggregateChunks: aggregateAISDKChunks,
|
|
9558
9671
|
patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
|
|
9559
|
-
denyOutputPaths,
|
|
9560
|
-
endEvent,
|
|
9561
|
-
result,
|
|
9562
|
-
span,
|
|
9563
|
-
startTime
|
|
9564
|
-
})
|
|
9565
|
-
})
|
|
9566
|
-
);
|
|
9567
|
-
this.unsubscribers.push(
|
|
9568
|
-
traceSyncStreamChannel(aiSDKChannels.streamObjectSync, {
|
|
9569
|
-
name: "streamObject",
|
|
9570
|
-
type: "llm" /* LLM */,
|
|
9571
|
-
extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
|
|
9572
|
-
patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
|
|
9573
|
-
denyOutputPaths,
|
|
9672
|
+
defaultDenyOutputPaths: denyOutputPaths,
|
|
9574
9673
|
endEvent,
|
|
9575
9674
|
result,
|
|
9576
9675
|
span,
|
|
@@ -9585,7 +9684,10 @@ var AISDKPlugin = class extends BasePlugin {
|
|
|
9585
9684
|
extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
|
|
9586
9685
|
extractOutput: (result, endEvent) => {
|
|
9587
9686
|
finalizeAISDKChildTracing(endEvent);
|
|
9588
|
-
return processAISDKOutput(
|
|
9687
|
+
return processAISDKOutput(
|
|
9688
|
+
result,
|
|
9689
|
+
resolveDenyOutputPaths(endEvent, denyOutputPaths)
|
|
9690
|
+
);
|
|
9589
9691
|
},
|
|
9590
9692
|
extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
|
|
9591
9693
|
aggregateChunks: aggregateAISDKChunks
|
|
@@ -9596,11 +9698,14 @@ var AISDKPlugin = class extends BasePlugin {
|
|
|
9596
9698
|
name: "Agent.stream",
|
|
9597
9699
|
type: "llm" /* LLM */,
|
|
9598
9700
|
extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
|
|
9599
|
-
extractOutput: (result) => processAISDKOutput(
|
|
9701
|
+
extractOutput: (result, endEvent) => processAISDKOutput(
|
|
9702
|
+
result,
|
|
9703
|
+
resolveDenyOutputPaths(endEvent, denyOutputPaths)
|
|
9704
|
+
),
|
|
9600
9705
|
extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
|
|
9601
9706
|
aggregateChunks: aggregateAISDKChunks,
|
|
9602
9707
|
patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
|
|
9603
|
-
denyOutputPaths,
|
|
9708
|
+
defaultDenyOutputPaths: denyOutputPaths,
|
|
9604
9709
|
endEvent,
|
|
9605
9710
|
result,
|
|
9606
9711
|
span,
|
|
@@ -9615,7 +9720,10 @@ var AISDKPlugin = class extends BasePlugin {
|
|
|
9615
9720
|
extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
|
|
9616
9721
|
extractOutput: (result, endEvent) => {
|
|
9617
9722
|
finalizeAISDKChildTracing(endEvent);
|
|
9618
|
-
return processAISDKOutput(
|
|
9723
|
+
return processAISDKOutput(
|
|
9724
|
+
result,
|
|
9725
|
+
resolveDenyOutputPaths(endEvent, denyOutputPaths)
|
|
9726
|
+
);
|
|
9619
9727
|
},
|
|
9620
9728
|
extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
|
|
9621
9729
|
aggregateChunks: aggregateAISDKChunks
|
|
@@ -9626,11 +9734,14 @@ var AISDKPlugin = class extends BasePlugin {
|
|
|
9626
9734
|
name: "ToolLoopAgent.stream",
|
|
9627
9735
|
type: "llm" /* LLM */,
|
|
9628
9736
|
extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
|
|
9629
|
-
extractOutput: (result) => processAISDKOutput(
|
|
9737
|
+
extractOutput: (result, endEvent) => processAISDKOutput(
|
|
9738
|
+
result,
|
|
9739
|
+
resolveDenyOutputPaths(endEvent, denyOutputPaths)
|
|
9740
|
+
),
|
|
9630
9741
|
extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
|
|
9631
9742
|
aggregateChunks: aggregateAISDKChunks,
|
|
9632
9743
|
patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
|
|
9633
|
-
denyOutputPaths,
|
|
9744
|
+
defaultDenyOutputPaths: denyOutputPaths,
|
|
9634
9745
|
endEvent,
|
|
9635
9746
|
result,
|
|
9636
9747
|
span,
|
|
@@ -9640,80 +9751,378 @@ var AISDKPlugin = class extends BasePlugin {
|
|
|
9640
9751
|
);
|
|
9641
9752
|
}
|
|
9642
9753
|
};
|
|
9643
|
-
function
|
|
9644
|
-
if (
|
|
9645
|
-
|
|
9646
|
-
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
9647
|
-
return input;
|
|
9754
|
+
function resolveDenyOutputPaths(event, defaultDenyOutputPaths) {
|
|
9755
|
+
if (Array.isArray(event?.denyOutputPaths)) {
|
|
9756
|
+
return event.denyOutputPaths;
|
|
9648
9757
|
}
|
|
9649
|
-
const
|
|
9650
|
-
|
|
9651
|
-
|
|
9652
|
-
function prepareAISDKInput(params, event, span, denyOutputPaths) {
|
|
9653
|
-
const input = processAISDKInput(params);
|
|
9654
|
-
const metadata = extractMetadataFromParams(params, event.self);
|
|
9655
|
-
const childTracing = prepareAISDKChildTracing(
|
|
9656
|
-
params,
|
|
9657
|
-
event.self,
|
|
9658
|
-
span,
|
|
9659
|
-
denyOutputPaths
|
|
9660
|
-
);
|
|
9661
|
-
event.__braintrust_ai_sdk_model_wrapped = childTracing.modelWrapped;
|
|
9662
|
-
if (childTracing.cleanup) {
|
|
9663
|
-
event.__braintrust_ai_sdk_cleanup = childTracing.cleanup;
|
|
9758
|
+
const firstArgument = event?.arguments && event.arguments.length > 0 ? event.arguments[0] : void 0;
|
|
9759
|
+
if (!firstArgument || typeof firstArgument !== "object") {
|
|
9760
|
+
return defaultDenyOutputPaths;
|
|
9664
9761
|
}
|
|
9665
|
-
|
|
9666
|
-
|
|
9667
|
-
|
|
9668
|
-
};
|
|
9669
|
-
}
|
|
9670
|
-
function extractTopLevelAISDKMetrics(result, event, startTime) {
|
|
9671
|
-
const metrics = hasModelChildTracing(event) ? {} : extractTokenMetrics(result);
|
|
9672
|
-
if (startTime) {
|
|
9673
|
-
metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
|
|
9762
|
+
const runtimeDenyOutputPaths = firstArgument[RUNTIME_DENY_OUTPUT_PATHS];
|
|
9763
|
+
if (Array.isArray(runtimeDenyOutputPaths) && runtimeDenyOutputPaths.every((path) => typeof path === "string")) {
|
|
9764
|
+
return runtimeDenyOutputPaths;
|
|
9674
9765
|
}
|
|
9675
|
-
return
|
|
9676
|
-
}
|
|
9677
|
-
function hasModelChildTracing(event) {
|
|
9678
|
-
return event?.__braintrust_ai_sdk_model_wrapped === true;
|
|
9766
|
+
return defaultDenyOutputPaths;
|
|
9679
9767
|
}
|
|
9680
|
-
|
|
9681
|
-
|
|
9682
|
-
|
|
9683
|
-
|
|
9684
|
-
|
|
9768
|
+
var isZodSchema2 = (value) => {
|
|
9769
|
+
return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
|
|
9770
|
+
};
|
|
9771
|
+
var serializeZodSchema2 = (schema) => {
|
|
9772
|
+
try {
|
|
9773
|
+
return zodToJsonSchema(schema);
|
|
9774
|
+
} catch {
|
|
9775
|
+
return {
|
|
9776
|
+
type: "object",
|
|
9777
|
+
description: "Zod schema (conversion failed)"
|
|
9778
|
+
};
|
|
9779
|
+
}
|
|
9780
|
+
};
|
|
9781
|
+
var isOutputObject = (value) => {
|
|
9782
|
+
if (value == null || typeof value !== "object") {
|
|
9783
|
+
return false;
|
|
9784
|
+
}
|
|
9785
|
+
const output = value;
|
|
9786
|
+
if (!("responseFormat" in output)) {
|
|
9787
|
+
return false;
|
|
9788
|
+
}
|
|
9789
|
+
if (output.type === "object" || output.type === "text") {
|
|
9790
|
+
return true;
|
|
9791
|
+
}
|
|
9792
|
+
if (typeof output.responseFormat === "function" || typeof output.responseFormat === "object") {
|
|
9793
|
+
return true;
|
|
9794
|
+
}
|
|
9795
|
+
return false;
|
|
9796
|
+
};
|
|
9797
|
+
var serializeOutputObject = (output, model) => {
|
|
9798
|
+
try {
|
|
9799
|
+
const result = {
|
|
9800
|
+
response_format: null
|
|
9801
|
+
};
|
|
9802
|
+
if (output.type) {
|
|
9803
|
+
result.type = output.type;
|
|
9804
|
+
}
|
|
9805
|
+
let responseFormat;
|
|
9806
|
+
if (typeof output.responseFormat === "function") {
|
|
9807
|
+
const mockModelForSchema = {
|
|
9808
|
+
supportsStructuredOutputs: true,
|
|
9809
|
+
...model && typeof model === "object" ? model : {}
|
|
9810
|
+
};
|
|
9811
|
+
responseFormat = output.responseFormat({ model: mockModelForSchema });
|
|
9812
|
+
} else if (output.responseFormat != null && typeof output.responseFormat === "object") {
|
|
9813
|
+
responseFormat = output.responseFormat;
|
|
9814
|
+
}
|
|
9815
|
+
if (responseFormat) {
|
|
9816
|
+
if (typeof responseFormat.then === "function") {
|
|
9817
|
+
result.response_format = Promise.resolve(responseFormat).then(
|
|
9818
|
+
(resolved) => {
|
|
9819
|
+
if (resolved.schema && isZodSchema2(resolved.schema)) {
|
|
9820
|
+
return {
|
|
9821
|
+
...resolved,
|
|
9822
|
+
schema: serializeZodSchema2(resolved.schema)
|
|
9823
|
+
};
|
|
9824
|
+
}
|
|
9825
|
+
return resolved;
|
|
9826
|
+
}
|
|
9827
|
+
);
|
|
9828
|
+
} else {
|
|
9829
|
+
const syncResponseFormat = responseFormat;
|
|
9830
|
+
if (syncResponseFormat.schema && isZodSchema2(syncResponseFormat.schema)) {
|
|
9831
|
+
responseFormat = {
|
|
9832
|
+
...syncResponseFormat,
|
|
9833
|
+
schema: serializeZodSchema2(syncResponseFormat.schema)
|
|
9834
|
+
};
|
|
9835
|
+
}
|
|
9836
|
+
result.response_format = responseFormat;
|
|
9837
|
+
}
|
|
9685
9838
|
}
|
|
9686
|
-
|
|
9687
|
-
|
|
9688
|
-
|
|
9689
|
-
|
|
9690
|
-
|
|
9691
|
-
if (model) {
|
|
9692
|
-
metadata.model = model;
|
|
9839
|
+
return result;
|
|
9840
|
+
} catch {
|
|
9841
|
+
return {
|
|
9842
|
+
response_format: null
|
|
9843
|
+
};
|
|
9693
9844
|
}
|
|
9694
|
-
|
|
9695
|
-
|
|
9845
|
+
};
|
|
9846
|
+
var processInputAttachmentsSync = (input) => {
|
|
9847
|
+
if (!input) return { input };
|
|
9848
|
+
const processed = { ...input };
|
|
9849
|
+
if (input.messages && Array.isArray(input.messages)) {
|
|
9850
|
+
processed.messages = input.messages.map(processMessage);
|
|
9851
|
+
}
|
|
9852
|
+
if (input.prompt && typeof input.prompt === "object") {
|
|
9853
|
+
if (Array.isArray(input.prompt)) {
|
|
9854
|
+
processed.prompt = input.prompt.map(processMessage);
|
|
9855
|
+
} else {
|
|
9856
|
+
processed.prompt = processPromptContent(input.prompt);
|
|
9857
|
+
}
|
|
9696
9858
|
}
|
|
9697
|
-
|
|
9698
|
-
|
|
9699
|
-
metadata.tools = tools;
|
|
9859
|
+
if (input.schema && isZodSchema2(input.schema)) {
|
|
9860
|
+
processed.schema = serializeZodSchema2(input.schema);
|
|
9700
9861
|
}
|
|
9701
|
-
|
|
9702
|
-
|
|
9703
|
-
|
|
9704
|
-
|
|
9705
|
-
|
|
9706
|
-
|
|
9707
|
-
let
|
|
9708
|
-
|
|
9709
|
-
const
|
|
9710
|
-
if (
|
|
9711
|
-
|
|
9862
|
+
if (input.callOptionsSchema && isZodSchema2(input.callOptionsSchema)) {
|
|
9863
|
+
processed.callOptionsSchema = serializeZodSchema2(input.callOptionsSchema);
|
|
9864
|
+
}
|
|
9865
|
+
if (input.tools) {
|
|
9866
|
+
processed.tools = serializeAISDKToolsForLogging(input.tools);
|
|
9867
|
+
}
|
|
9868
|
+
let outputPromise;
|
|
9869
|
+
if (input.output && isOutputObject(input.output)) {
|
|
9870
|
+
const serialized = serializeOutputObject(input.output, input.model);
|
|
9871
|
+
if (serialized.response_format && typeof serialized.response_format.then === "function") {
|
|
9872
|
+
processed.output = { ...serialized, response_format: {} };
|
|
9873
|
+
outputPromise = serialized.response_format.then(
|
|
9874
|
+
(resolvedFormat) => ({
|
|
9875
|
+
output: { ...serialized, response_format: resolvedFormat }
|
|
9876
|
+
})
|
|
9877
|
+
);
|
|
9878
|
+
} else {
|
|
9879
|
+
processed.output = serialized;
|
|
9712
9880
|
}
|
|
9713
|
-
|
|
9714
|
-
|
|
9715
|
-
|
|
9716
|
-
|
|
9881
|
+
}
|
|
9882
|
+
if ("prepareCall" in processed && typeof processed.prepareCall === "function") {
|
|
9883
|
+
processed.prepareCall = "[Function]";
|
|
9884
|
+
}
|
|
9885
|
+
return { input: processed, outputPromise };
|
|
9886
|
+
};
|
|
9887
|
+
var processMessage = (message) => {
|
|
9888
|
+
if (!message || typeof message !== "object") return message;
|
|
9889
|
+
if (Array.isArray(message.content)) {
|
|
9890
|
+
return {
|
|
9891
|
+
...message,
|
|
9892
|
+
content: message.content.map(processContentPart)
|
|
9893
|
+
};
|
|
9894
|
+
}
|
|
9895
|
+
if (typeof message.content === "object" && message.content !== null) {
|
|
9896
|
+
return {
|
|
9897
|
+
...message,
|
|
9898
|
+
content: processContentPart(message.content)
|
|
9899
|
+
};
|
|
9900
|
+
}
|
|
9901
|
+
return message;
|
|
9902
|
+
};
|
|
9903
|
+
var processPromptContent = (prompt) => {
|
|
9904
|
+
if (Array.isArray(prompt)) {
|
|
9905
|
+
return prompt.map(processContentPart);
|
|
9906
|
+
}
|
|
9907
|
+
if (prompt.content) {
|
|
9908
|
+
if (Array.isArray(prompt.content)) {
|
|
9909
|
+
return {
|
|
9910
|
+
...prompt,
|
|
9911
|
+
content: prompt.content.map(processContentPart)
|
|
9912
|
+
};
|
|
9913
|
+
} else if (typeof prompt.content === "object") {
|
|
9914
|
+
return {
|
|
9915
|
+
...prompt,
|
|
9916
|
+
content: processContentPart(prompt.content)
|
|
9917
|
+
};
|
|
9918
|
+
}
|
|
9919
|
+
}
|
|
9920
|
+
return prompt;
|
|
9921
|
+
};
|
|
9922
|
+
var processContentPart = (part) => {
|
|
9923
|
+
if (!part || typeof part !== "object") return part;
|
|
9924
|
+
try {
|
|
9925
|
+
if (part.type === "image" && part.image) {
|
|
9926
|
+
const imageAttachment = convertImageToAttachment(
|
|
9927
|
+
part.image,
|
|
9928
|
+
part.mimeType || part.mediaType
|
|
9929
|
+
);
|
|
9930
|
+
if (imageAttachment) {
|
|
9931
|
+
return {
|
|
9932
|
+
...part,
|
|
9933
|
+
image: imageAttachment
|
|
9934
|
+
};
|
|
9935
|
+
}
|
|
9936
|
+
}
|
|
9937
|
+
if (part.type === "file" && part.data && (part.mimeType || part.mediaType)) {
|
|
9938
|
+
const fileAttachment = convertDataToAttachment(
|
|
9939
|
+
part.data,
|
|
9940
|
+
part.mimeType || part.mediaType,
|
|
9941
|
+
part.name || part.filename
|
|
9942
|
+
);
|
|
9943
|
+
if (fileAttachment) {
|
|
9944
|
+
return {
|
|
9945
|
+
...part,
|
|
9946
|
+
data: fileAttachment
|
|
9947
|
+
};
|
|
9948
|
+
}
|
|
9949
|
+
}
|
|
9950
|
+
if (part.type === "image_url" && part.image_url) {
|
|
9951
|
+
if (typeof part.image_url === "object" && part.image_url.url) {
|
|
9952
|
+
const imageAttachment = convertImageToAttachment(part.image_url.url);
|
|
9953
|
+
if (imageAttachment) {
|
|
9954
|
+
return {
|
|
9955
|
+
...part,
|
|
9956
|
+
image_url: {
|
|
9957
|
+
...part.image_url,
|
|
9958
|
+
url: imageAttachment
|
|
9959
|
+
}
|
|
9960
|
+
};
|
|
9961
|
+
}
|
|
9962
|
+
}
|
|
9963
|
+
}
|
|
9964
|
+
} catch (error) {
|
|
9965
|
+
console.warn("Error processing content part:", error);
|
|
9966
|
+
}
|
|
9967
|
+
return part;
|
|
9968
|
+
};
|
|
9969
|
+
var convertImageToAttachment = (image, explicitMimeType) => {
|
|
9970
|
+
try {
|
|
9971
|
+
if (typeof image === "string" && image.startsWith("data:")) {
|
|
9972
|
+
const [mimeTypeSection, base64Data] = image.split(",");
|
|
9973
|
+
const mimeType = mimeTypeSection.match(/data:(.*?);/)?.[1];
|
|
9974
|
+
if (mimeType && base64Data) {
|
|
9975
|
+
const blob = convertDataToBlob(base64Data, mimeType);
|
|
9976
|
+
if (blob) {
|
|
9977
|
+
return new Attachment({
|
|
9978
|
+
data: blob,
|
|
9979
|
+
filename: `image.${getExtensionFromMediaType(mimeType)}`,
|
|
9980
|
+
contentType: mimeType
|
|
9981
|
+
});
|
|
9982
|
+
}
|
|
9983
|
+
}
|
|
9984
|
+
}
|
|
9985
|
+
if (explicitMimeType) {
|
|
9986
|
+
if (image instanceof Uint8Array) {
|
|
9987
|
+
return new Attachment({
|
|
9988
|
+
data: new Blob([image], { type: explicitMimeType }),
|
|
9989
|
+
filename: `image.${getExtensionFromMediaType(explicitMimeType)}`,
|
|
9990
|
+
contentType: explicitMimeType
|
|
9991
|
+
});
|
|
9992
|
+
}
|
|
9993
|
+
if (typeof Buffer !== "undefined" && Buffer.isBuffer(image)) {
|
|
9994
|
+
return new Attachment({
|
|
9995
|
+
data: new Blob([image], { type: explicitMimeType }),
|
|
9996
|
+
filename: `image.${getExtensionFromMediaType(explicitMimeType)}`,
|
|
9997
|
+
contentType: explicitMimeType
|
|
9998
|
+
});
|
|
9999
|
+
}
|
|
10000
|
+
}
|
|
10001
|
+
if (image instanceof Blob && image.type) {
|
|
10002
|
+
return new Attachment({
|
|
10003
|
+
data: image,
|
|
10004
|
+
filename: `image.${getExtensionFromMediaType(image.type)}`,
|
|
10005
|
+
contentType: image.type
|
|
10006
|
+
});
|
|
10007
|
+
}
|
|
10008
|
+
if (image instanceof Attachment) {
|
|
10009
|
+
return image;
|
|
10010
|
+
}
|
|
10011
|
+
} catch (error) {
|
|
10012
|
+
console.warn("Error converting image to attachment:", error);
|
|
10013
|
+
}
|
|
10014
|
+
return null;
|
|
10015
|
+
};
|
|
10016
|
+
var convertDataToAttachment = (data, mimeType, filename) => {
|
|
10017
|
+
if (!mimeType) return null;
|
|
10018
|
+
try {
|
|
10019
|
+
let blob = null;
|
|
10020
|
+
if (typeof data === "string" && data.startsWith("data:")) {
|
|
10021
|
+
const [, base64Data] = data.split(",");
|
|
10022
|
+
if (base64Data) {
|
|
10023
|
+
blob = convertDataToBlob(base64Data, mimeType);
|
|
10024
|
+
}
|
|
10025
|
+
} else if (typeof data === "string" && data.length > 0) {
|
|
10026
|
+
blob = convertDataToBlob(data, mimeType);
|
|
10027
|
+
} else if (data instanceof Uint8Array) {
|
|
10028
|
+
blob = new Blob([data], { type: mimeType });
|
|
10029
|
+
} else if (typeof Buffer !== "undefined" && Buffer.isBuffer(data)) {
|
|
10030
|
+
blob = new Blob([data], { type: mimeType });
|
|
10031
|
+
} else if (data instanceof Blob) {
|
|
10032
|
+
blob = data;
|
|
10033
|
+
}
|
|
10034
|
+
if (blob) {
|
|
10035
|
+
return new Attachment({
|
|
10036
|
+
data: blob,
|
|
10037
|
+
filename: filename || `file.${getExtensionFromMediaType(mimeType)}`,
|
|
10038
|
+
contentType: mimeType
|
|
10039
|
+
});
|
|
10040
|
+
}
|
|
10041
|
+
} catch (error) {
|
|
10042
|
+
console.warn("Error converting data to attachment:", error);
|
|
10043
|
+
}
|
|
10044
|
+
return null;
|
|
10045
|
+
};
|
|
10046
|
+
function processAISDKInput(params) {
|
|
10047
|
+
return processInputAttachmentsSync(params);
|
|
10048
|
+
}
|
|
10049
|
+
function prepareAISDKInput(params, event, span, defaultDenyOutputPaths) {
|
|
10050
|
+
const { input, outputPromise } = processAISDKInput(params);
|
|
10051
|
+
if (outputPromise && input && typeof input === "object") {
|
|
10052
|
+
outputPromise.then((resolvedData) => {
|
|
10053
|
+
span.log({
|
|
10054
|
+
input: {
|
|
10055
|
+
...input,
|
|
10056
|
+
...resolvedData
|
|
10057
|
+
}
|
|
10058
|
+
});
|
|
10059
|
+
}).catch(() => {
|
|
10060
|
+
});
|
|
10061
|
+
}
|
|
10062
|
+
const metadata = extractMetadataFromParams(params, event.self);
|
|
10063
|
+
const childTracing = prepareAISDKChildTracing(
|
|
10064
|
+
params,
|
|
10065
|
+
event.self,
|
|
10066
|
+
span,
|
|
10067
|
+
defaultDenyOutputPaths,
|
|
10068
|
+
event.aiSDK
|
|
10069
|
+
);
|
|
10070
|
+
event.modelWrapped = childTracing.modelWrapped;
|
|
10071
|
+
if (childTracing.cleanup) {
|
|
10072
|
+
event.__braintrust_ai_sdk_cleanup = childTracing.cleanup;
|
|
10073
|
+
}
|
|
10074
|
+
return {
|
|
10075
|
+
input,
|
|
10076
|
+
metadata
|
|
10077
|
+
};
|
|
10078
|
+
}
|
|
10079
|
+
function extractTopLevelAISDKMetrics(result, event, startTime) {
|
|
10080
|
+
const metrics = hasModelChildTracing(event) ? {} : extractTokenMetrics(result);
|
|
10081
|
+
if (startTime) {
|
|
10082
|
+
metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
|
|
10083
|
+
}
|
|
10084
|
+
return metrics;
|
|
10085
|
+
}
|
|
10086
|
+
function hasModelChildTracing(event) {
|
|
10087
|
+
return event?.modelWrapped === true || event?.__braintrust_ai_sdk_model_wrapped === true;
|
|
10088
|
+
}
|
|
10089
|
+
function extractMetadataFromParams(params, self) {
|
|
10090
|
+
const metadata = {
|
|
10091
|
+
braintrust: {
|
|
10092
|
+
integration_name: "ai-sdk",
|
|
10093
|
+
sdk_language: "typescript"
|
|
10094
|
+
}
|
|
10095
|
+
};
|
|
10096
|
+
const agentModel = self && typeof self === "object" && "model" in self && self.model ? self.model : self && typeof self === "object" && "settings" in self && self.settings?.model ? self.settings?.model : void 0;
|
|
10097
|
+
const { model, provider } = serializeModelWithProvider(
|
|
10098
|
+
params.model ?? agentModel
|
|
10099
|
+
);
|
|
10100
|
+
if (model) {
|
|
10101
|
+
metadata.model = model;
|
|
10102
|
+
}
|
|
10103
|
+
if (provider) {
|
|
10104
|
+
metadata.provider = provider;
|
|
10105
|
+
}
|
|
10106
|
+
const tools = serializeAISDKToolsForLogging(params.tools);
|
|
10107
|
+
if (tools) {
|
|
10108
|
+
metadata.tools = tools;
|
|
10109
|
+
}
|
|
10110
|
+
return metadata;
|
|
10111
|
+
}
|
|
10112
|
+
function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiSDK) {
|
|
10113
|
+
const cleanup = [];
|
|
10114
|
+
const patchedModels = /* @__PURE__ */ new WeakSet();
|
|
10115
|
+
const patchedTools = /* @__PURE__ */ new WeakSet();
|
|
10116
|
+
let modelWrapped = false;
|
|
10117
|
+
const patchModel = (model) => {
|
|
10118
|
+
const resolvedModel = resolveAISDKModel(model, aiSDK);
|
|
10119
|
+
if (!resolvedModel || typeof resolvedModel !== "object" || typeof resolvedModel.doGenerate !== "function" || patchedModels.has(resolvedModel) || resolvedModel[AUTO_PATCHED_MODEL]) {
|
|
10120
|
+
return resolvedModel;
|
|
10121
|
+
}
|
|
10122
|
+
patchedModels.add(resolvedModel);
|
|
10123
|
+
resolvedModel[AUTO_PATCHED_MODEL] = true;
|
|
10124
|
+
modelWrapped = true;
|
|
10125
|
+
const originalDoGenerate = resolvedModel.doGenerate;
|
|
9717
10126
|
const originalDoStream = resolvedModel.doStream;
|
|
9718
10127
|
const baseMetadata = buildAISDKChildMetadata(resolvedModel);
|
|
9719
10128
|
resolvedModel.doGenerate = async function doGeneratePatched(options) {
|
|
@@ -9737,7 +10146,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
|
|
|
9737
10146
|
type: "llm" /* LLM */
|
|
9738
10147
|
},
|
|
9739
10148
|
event: {
|
|
9740
|
-
input: processAISDKInput(options),
|
|
10149
|
+
input: processAISDKInput(options).input,
|
|
9741
10150
|
metadata: baseMetadata
|
|
9742
10151
|
}
|
|
9743
10152
|
}
|
|
@@ -9751,7 +10160,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
|
|
|
9751
10160
|
type: "llm" /* LLM */
|
|
9752
10161
|
},
|
|
9753
10162
|
event: {
|
|
9754
|
-
input: processAISDKInput(options),
|
|
10163
|
+
input: processAISDKInput(options).input,
|
|
9755
10164
|
metadata: baseMetadata
|
|
9756
10165
|
}
|
|
9757
10166
|
});
|
|
@@ -9759,6 +10168,8 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
|
|
|
9759
10168
|
span,
|
|
9760
10169
|
() => Reflect.apply(originalDoStream, resolvedModel, [options])
|
|
9761
10170
|
);
|
|
10171
|
+
const streamStartTime = getCurrentUnixTimestamp();
|
|
10172
|
+
let firstChunkTime;
|
|
9762
10173
|
const output = {};
|
|
9763
10174
|
let text = "";
|
|
9764
10175
|
let reasoning = "";
|
|
@@ -9766,6 +10177,9 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
|
|
|
9766
10177
|
let object = void 0;
|
|
9767
10178
|
const transformStream = new TransformStream({
|
|
9768
10179
|
transform(chunk, controller) {
|
|
10180
|
+
if (firstChunkTime === void 0) {
|
|
10181
|
+
firstChunkTime = getCurrentUnixTimestamp();
|
|
10182
|
+
}
|
|
9769
10183
|
switch (chunk.type) {
|
|
9770
10184
|
case "text-delta":
|
|
9771
10185
|
text += extractTextDelta(chunk);
|
|
@@ -9806,12 +10220,19 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
|
|
|
9806
10220
|
if (object !== void 0) {
|
|
9807
10221
|
output.object = object;
|
|
9808
10222
|
}
|
|
10223
|
+
const metrics = extractTokenMetrics(output);
|
|
10224
|
+
if (firstChunkTime !== void 0) {
|
|
10225
|
+
metrics.time_to_first_token = Math.max(
|
|
10226
|
+
firstChunkTime - streamStartTime,
|
|
10227
|
+
1e-6
|
|
10228
|
+
);
|
|
10229
|
+
}
|
|
9809
10230
|
span.log({
|
|
9810
10231
|
output: processAISDKOutput(
|
|
9811
10232
|
output,
|
|
9812
10233
|
denyOutputPaths
|
|
9813
10234
|
),
|
|
9814
|
-
metrics
|
|
10235
|
+
metrics,
|
|
9815
10236
|
...buildResolvedMetadataPayload(output)
|
|
9816
10237
|
});
|
|
9817
10238
|
span.end();
|
|
@@ -9833,6 +10254,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
|
|
|
9833
10254
|
}
|
|
9834
10255
|
delete resolvedModel[AUTO_PATCHED_MODEL];
|
|
9835
10256
|
});
|
|
10257
|
+
return resolvedModel;
|
|
9836
10258
|
};
|
|
9837
10259
|
const patchTool = (tool, name) => {
|
|
9838
10260
|
if (tool == null || typeof tool !== "object" || !("execute" in tool) || typeof tool.execute !== "function" || patchedTools.has(tool) || tool[AUTO_PATCHED_TOOL]) {
|
|
@@ -9905,17 +10327,26 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
|
|
|
9905
10327
|
}
|
|
9906
10328
|
};
|
|
9907
10329
|
if (params && typeof params === "object") {
|
|
9908
|
-
patchModel(params.model);
|
|
10330
|
+
const patchedParamModel = patchModel(params.model);
|
|
10331
|
+
if (typeof params.model === "string" && patchedParamModel && typeof patchedParamModel === "object") {
|
|
10332
|
+
params.model = patchedParamModel;
|
|
10333
|
+
}
|
|
9909
10334
|
patchTools(params.tools);
|
|
9910
10335
|
}
|
|
9911
10336
|
if (self && typeof self === "object") {
|
|
9912
10337
|
const selfRecord = self;
|
|
9913
10338
|
if (selfRecord.model !== void 0) {
|
|
9914
|
-
patchModel(selfRecord.model);
|
|
10339
|
+
const patchedSelfModel = patchModel(selfRecord.model);
|
|
10340
|
+
if (typeof selfRecord.model === "string" && patchedSelfModel && typeof patchedSelfModel === "object") {
|
|
10341
|
+
selfRecord.model = patchedSelfModel;
|
|
10342
|
+
}
|
|
9915
10343
|
}
|
|
9916
10344
|
if (selfRecord.settings && typeof selfRecord.settings === "object") {
|
|
9917
10345
|
if (selfRecord.settings.model !== void 0) {
|
|
9918
|
-
patchModel(selfRecord.settings.model);
|
|
10346
|
+
const patchedSettingsModel = patchModel(selfRecord.settings.model);
|
|
10347
|
+
if (typeof selfRecord.settings.model === "string" && patchedSettingsModel && typeof patchedSettingsModel === "object") {
|
|
10348
|
+
selfRecord.settings.model = patchedSettingsModel;
|
|
10349
|
+
}
|
|
9919
10350
|
}
|
|
9920
10351
|
if (selfRecord.settings.tools !== void 0) {
|
|
9921
10352
|
patchTools(selfRecord.settings.tools);
|
|
@@ -9939,63 +10370,173 @@ function finalizeAISDKChildTracing(event) {
|
|
|
9939
10370
|
}
|
|
9940
10371
|
}
|
|
9941
10372
|
function patchAISDKStreamingResult(args) {
|
|
9942
|
-
const {
|
|
10373
|
+
const { defaultDenyOutputPaths, endEvent, result, span, startTime } = args;
|
|
9943
10374
|
if (!result || typeof result !== "object") {
|
|
9944
10375
|
return false;
|
|
9945
10376
|
}
|
|
9946
10377
|
const resultRecord = result;
|
|
9947
|
-
|
|
10378
|
+
attachKnownResultPromiseHandlers(resultRecord);
|
|
10379
|
+
if (isReadableStreamLike(resultRecord.baseStream)) {
|
|
10380
|
+
let firstChunkTime2;
|
|
10381
|
+
const wrappedBaseStream = resultRecord.baseStream.pipeThrough(
|
|
10382
|
+
new TransformStream({
|
|
10383
|
+
transform(chunk, controller) {
|
|
10384
|
+
if (firstChunkTime2 === void 0) {
|
|
10385
|
+
firstChunkTime2 = getCurrentUnixTimestamp();
|
|
10386
|
+
}
|
|
10387
|
+
controller.enqueue(chunk);
|
|
10388
|
+
},
|
|
10389
|
+
async flush() {
|
|
10390
|
+
const metrics = extractTopLevelAISDKMetrics(result, endEvent);
|
|
10391
|
+
if (metrics.time_to_first_token === void 0 && firstChunkTime2 !== void 0) {
|
|
10392
|
+
metrics.time_to_first_token = firstChunkTime2 - startTime;
|
|
10393
|
+
}
|
|
10394
|
+
const output = await processAISDKStreamingOutput(
|
|
10395
|
+
result,
|
|
10396
|
+
resolveDenyOutputPaths(endEvent, defaultDenyOutputPaths)
|
|
10397
|
+
);
|
|
10398
|
+
const metadata = buildResolvedMetadataPayload(result).metadata;
|
|
10399
|
+
span.log({
|
|
10400
|
+
output,
|
|
10401
|
+
...metadata ? { metadata } : {},
|
|
10402
|
+
metrics
|
|
10403
|
+
});
|
|
10404
|
+
finalizeAISDKChildTracing(endEvent);
|
|
10405
|
+
span.end();
|
|
10406
|
+
}
|
|
10407
|
+
})
|
|
10408
|
+
);
|
|
10409
|
+
Object.defineProperty(resultRecord, "baseStream", {
|
|
10410
|
+
configurable: true,
|
|
10411
|
+
enumerable: true,
|
|
10412
|
+
value: wrappedBaseStream,
|
|
10413
|
+
writable: true
|
|
10414
|
+
});
|
|
10415
|
+
return true;
|
|
10416
|
+
}
|
|
10417
|
+
const streamField = findAsyncIterableField(resultRecord, [
|
|
10418
|
+
"partialObjectStream",
|
|
10419
|
+
"textStream",
|
|
10420
|
+
"fullStream",
|
|
10421
|
+
"stream"
|
|
10422
|
+
]);
|
|
10423
|
+
if (!streamField) {
|
|
9948
10424
|
return false;
|
|
9949
10425
|
}
|
|
9950
10426
|
let firstChunkTime;
|
|
9951
|
-
const
|
|
9952
|
-
|
|
9953
|
-
|
|
9954
|
-
|
|
9955
|
-
firstChunkTime = getCurrentUnixTimestamp();
|
|
9956
|
-
}
|
|
9957
|
-
controller.enqueue(chunk);
|
|
9958
|
-
},
|
|
9959
|
-
async flush() {
|
|
9960
|
-
const metrics = extractTopLevelAISDKMetrics(result, endEvent);
|
|
9961
|
-
if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
|
|
9962
|
-
metrics.time_to_first_token = firstChunkTime - startTime;
|
|
9963
|
-
}
|
|
9964
|
-
const output = await processAISDKStreamingOutput(
|
|
9965
|
-
result,
|
|
9966
|
-
denyOutputPaths
|
|
9967
|
-
);
|
|
9968
|
-
const metadata = buildResolvedMetadataPayload(result).metadata;
|
|
9969
|
-
span.log({
|
|
9970
|
-
output,
|
|
9971
|
-
...metadata ? { metadata } : {},
|
|
9972
|
-
metrics
|
|
9973
|
-
});
|
|
9974
|
-
finalizeAISDKChildTracing(endEvent);
|
|
9975
|
-
span.end();
|
|
10427
|
+
const wrappedStream = createPatchedAsyncIterable(streamField.stream, {
|
|
10428
|
+
onChunk: () => {
|
|
10429
|
+
if (firstChunkTime === void 0) {
|
|
10430
|
+
firstChunkTime = getCurrentUnixTimestamp();
|
|
9976
10431
|
}
|
|
9977
|
-
}
|
|
9978
|
-
|
|
9979
|
-
|
|
10432
|
+
},
|
|
10433
|
+
onComplete: async () => {
|
|
10434
|
+
const metrics = extractTopLevelAISDKMetrics(result, endEvent);
|
|
10435
|
+
if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
|
|
10436
|
+
metrics.time_to_first_token = firstChunkTime - startTime;
|
|
10437
|
+
}
|
|
10438
|
+
const output = await processAISDKStreamingOutput(
|
|
10439
|
+
result,
|
|
10440
|
+
resolveDenyOutputPaths(endEvent, defaultDenyOutputPaths)
|
|
10441
|
+
);
|
|
10442
|
+
const metadata = buildResolvedMetadataPayload(result).metadata;
|
|
10443
|
+
span.log({
|
|
10444
|
+
output,
|
|
10445
|
+
...metadata ? { metadata } : {},
|
|
10446
|
+
metrics
|
|
10447
|
+
});
|
|
10448
|
+
finalizeAISDKChildTracing(endEvent);
|
|
10449
|
+
span.end();
|
|
10450
|
+
},
|
|
10451
|
+
onError: (error) => {
|
|
10452
|
+
span.log({
|
|
10453
|
+
error: error.message
|
|
10454
|
+
});
|
|
10455
|
+
finalizeAISDKChildTracing(endEvent);
|
|
10456
|
+
span.end();
|
|
10457
|
+
}
|
|
10458
|
+
});
|
|
10459
|
+
Object.defineProperty(resultRecord, streamField.field, {
|
|
9980
10460
|
configurable: true,
|
|
9981
10461
|
enumerable: true,
|
|
9982
|
-
value:
|
|
10462
|
+
value: wrappedStream,
|
|
9983
10463
|
writable: true
|
|
9984
10464
|
});
|
|
9985
10465
|
return true;
|
|
9986
10466
|
}
|
|
10467
|
+
function attachKnownResultPromiseHandlers(result) {
|
|
10468
|
+
const promiseLikeFields = [
|
|
10469
|
+
"content",
|
|
10470
|
+
"text",
|
|
10471
|
+
"object",
|
|
10472
|
+
"finishReason",
|
|
10473
|
+
"usage",
|
|
10474
|
+
"totalUsage",
|
|
10475
|
+
"steps"
|
|
10476
|
+
];
|
|
10477
|
+
for (const field of promiseLikeFields) {
|
|
10478
|
+
try {
|
|
10479
|
+
if (!(field in result)) {
|
|
10480
|
+
continue;
|
|
10481
|
+
}
|
|
10482
|
+
const value = result[field];
|
|
10483
|
+
if (isPromiseLike(value)) {
|
|
10484
|
+
void Promise.resolve(value).catch(() => {
|
|
10485
|
+
});
|
|
10486
|
+
}
|
|
10487
|
+
} catch {
|
|
10488
|
+
}
|
|
10489
|
+
}
|
|
10490
|
+
}
|
|
9987
10491
|
function isReadableStreamLike(value) {
|
|
9988
10492
|
return value != null && typeof value === "object" && typeof value.pipeThrough === "function";
|
|
9989
10493
|
}
|
|
10494
|
+
function isAsyncIterableLike(value) {
|
|
10495
|
+
return value != null && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function";
|
|
10496
|
+
}
|
|
10497
|
+
function findAsyncIterableField(result, candidateFields) {
|
|
10498
|
+
for (const field of candidateFields) {
|
|
10499
|
+
try {
|
|
10500
|
+
const stream = result[field];
|
|
10501
|
+
if (isAsyncIterableLike(stream)) {
|
|
10502
|
+
return { field, stream };
|
|
10503
|
+
}
|
|
10504
|
+
} catch {
|
|
10505
|
+
}
|
|
10506
|
+
}
|
|
10507
|
+
return null;
|
|
10508
|
+
}
|
|
10509
|
+
function createPatchedAsyncIterable(stream, hooks) {
|
|
10510
|
+
return {
|
|
10511
|
+
async *[Symbol.asyncIterator]() {
|
|
10512
|
+
try {
|
|
10513
|
+
for await (const chunk of stream) {
|
|
10514
|
+
hooks.onChunk(chunk);
|
|
10515
|
+
yield chunk;
|
|
10516
|
+
}
|
|
10517
|
+
await hooks.onComplete();
|
|
10518
|
+
} catch (error) {
|
|
10519
|
+
hooks.onError(
|
|
10520
|
+
error instanceof Error ? error : new Error(String(error))
|
|
10521
|
+
);
|
|
10522
|
+
throw error;
|
|
10523
|
+
}
|
|
10524
|
+
}
|
|
10525
|
+
};
|
|
10526
|
+
}
|
|
9990
10527
|
async function processAISDKStreamingOutput(result, denyOutputPaths) {
|
|
9991
10528
|
const output = processAISDKOutput(result, denyOutputPaths);
|
|
9992
10529
|
if (!output || typeof output !== "object") {
|
|
9993
10530
|
return output;
|
|
9994
10531
|
}
|
|
9995
10532
|
const outputRecord = output;
|
|
10533
|
+
const isObjectStreamingResult = result != null && typeof result === "object" && "partialObjectStream" in result;
|
|
9996
10534
|
try {
|
|
9997
|
-
if ("text" in result
|
|
9998
|
-
|
|
10535
|
+
if (!isObjectStreamingResult && "text" in result) {
|
|
10536
|
+
const resolvedText = await Promise.resolve(result.text);
|
|
10537
|
+
if (typeof resolvedText === "string") {
|
|
10538
|
+
outputRecord.text = resolvedText;
|
|
10539
|
+
}
|
|
9999
10540
|
}
|
|
10000
10541
|
} catch {
|
|
10001
10542
|
}
|
|
@@ -10008,6 +10549,15 @@ async function processAISDKStreamingOutput(result, denyOutputPaths) {
|
|
|
10008
10549
|
}
|
|
10009
10550
|
} catch {
|
|
10010
10551
|
}
|
|
10552
|
+
try {
|
|
10553
|
+
if ("finishReason" in result) {
|
|
10554
|
+
const resolvedFinishReason = await Promise.resolve(result.finishReason);
|
|
10555
|
+
if (resolvedFinishReason !== void 0) {
|
|
10556
|
+
outputRecord.finishReason = resolvedFinishReason;
|
|
10557
|
+
}
|
|
10558
|
+
}
|
|
10559
|
+
} catch {
|
|
10560
|
+
}
|
|
10011
10561
|
return outputRecord;
|
|
10012
10562
|
}
|
|
10013
10563
|
function buildAISDKChildMetadata(model) {
|
|
@@ -10030,16 +10580,25 @@ function buildResolvedMetadataPayload(result) {
|
|
|
10030
10580
|
if (gatewayInfo?.model) {
|
|
10031
10581
|
metadata.model = gatewayInfo.model;
|
|
10032
10582
|
}
|
|
10033
|
-
|
|
10034
|
-
|
|
10583
|
+
let finishReason;
|
|
10584
|
+
try {
|
|
10585
|
+
finishReason = result.finishReason;
|
|
10586
|
+
} catch {
|
|
10587
|
+
finishReason = void 0;
|
|
10588
|
+
}
|
|
10589
|
+
if (isPromiseLike(finishReason)) {
|
|
10590
|
+
void Promise.resolve(finishReason).catch(() => {
|
|
10591
|
+
});
|
|
10592
|
+
} else if (finishReason !== void 0) {
|
|
10593
|
+
metadata.finish_reason = finishReason;
|
|
10035
10594
|
}
|
|
10036
10595
|
return Object.keys(metadata).length > 0 ? { metadata } : {};
|
|
10037
10596
|
}
|
|
10038
|
-
function resolveAISDKModel(model) {
|
|
10597
|
+
function resolveAISDKModel(model, aiSDK) {
|
|
10039
10598
|
if (typeof model !== "string") {
|
|
10040
10599
|
return model;
|
|
10041
10600
|
}
|
|
10042
|
-
const provider = globalThis.AI_SDK_DEFAULT_PROVIDER ?? null;
|
|
10601
|
+
const provider = globalThis.AI_SDK_DEFAULT_PROVIDER ?? aiSDK?.gateway ?? null;
|
|
10043
10602
|
if (provider && typeof provider.languageModel === "function") {
|
|
10044
10603
|
return provider.languageModel(model);
|
|
10045
10604
|
}
|
|
@@ -10062,15 +10621,15 @@ function processAISDKOutput(output, denyOutputPaths) {
|
|
|
10062
10621
|
}
|
|
10063
10622
|
function extractTokenMetrics(result) {
|
|
10064
10623
|
const metrics = {};
|
|
10065
|
-
let usage
|
|
10066
|
-
|
|
10067
|
-
|
|
10068
|
-
|
|
10069
|
-
|
|
10070
|
-
|
|
10071
|
-
|
|
10072
|
-
|
|
10073
|
-
|
|
10624
|
+
let usage;
|
|
10625
|
+
const totalUsageValue = safeResultFieldRead(result, "totalUsage");
|
|
10626
|
+
if (totalUsageValue !== void 0 && !isPromiseLike(totalUsageValue)) {
|
|
10627
|
+
usage = totalUsageValue;
|
|
10628
|
+
}
|
|
10629
|
+
if (!usage) {
|
|
10630
|
+
const usageValue = safeResultFieldRead(result, "usage");
|
|
10631
|
+
if (usageValue !== void 0 && !isPromiseLike(usageValue)) {
|
|
10632
|
+
usage = usageValue;
|
|
10074
10633
|
}
|
|
10075
10634
|
}
|
|
10076
10635
|
if (!usage) {
|
|
@@ -10108,6 +10667,22 @@ function extractTokenMetrics(result) {
|
|
|
10108
10667
|
}
|
|
10109
10668
|
return metrics;
|
|
10110
10669
|
}
|
|
10670
|
+
function safeResultFieldRead(result, field) {
|
|
10671
|
+
return safeSerializableFieldRead(result, field);
|
|
10672
|
+
}
|
|
10673
|
+
function safeSerializableFieldRead(obj, field) {
|
|
10674
|
+
try {
|
|
10675
|
+
const value = obj?.[field];
|
|
10676
|
+
if (isPromiseLike(value)) {
|
|
10677
|
+
void Promise.resolve(value).catch(() => {
|
|
10678
|
+
});
|
|
10679
|
+
return void 0;
|
|
10680
|
+
}
|
|
10681
|
+
return value;
|
|
10682
|
+
} catch {
|
|
10683
|
+
return void 0;
|
|
10684
|
+
}
|
|
10685
|
+
}
|
|
10111
10686
|
function aggregateAISDKChunks(chunks, _result, endEvent) {
|
|
10112
10687
|
const lastChunk = chunks[chunks.length - 1];
|
|
10113
10688
|
const output = {};
|
|
@@ -10116,17 +10691,21 @@ function aggregateAISDKChunks(chunks, _result, endEvent) {
|
|
|
10116
10691
|
if (lastChunk) {
|
|
10117
10692
|
metrics = hasModelChildTracing(endEvent) ? {} : extractTokenMetrics(lastChunk);
|
|
10118
10693
|
metadata = buildResolvedMetadataPayload(lastChunk).metadata;
|
|
10119
|
-
|
|
10120
|
-
|
|
10694
|
+
const text = safeSerializableFieldRead(lastChunk, "text");
|
|
10695
|
+
if (text !== void 0) {
|
|
10696
|
+
output.text = text;
|
|
10121
10697
|
}
|
|
10122
|
-
|
|
10123
|
-
|
|
10698
|
+
const objectValue = safeSerializableFieldRead(lastChunk, "object");
|
|
10699
|
+
if (objectValue !== void 0) {
|
|
10700
|
+
output.object = objectValue;
|
|
10124
10701
|
}
|
|
10125
|
-
|
|
10126
|
-
|
|
10702
|
+
const finishReason = safeSerializableFieldRead(lastChunk, "finishReason");
|
|
10703
|
+
if (finishReason !== void 0) {
|
|
10704
|
+
output.finishReason = finishReason;
|
|
10127
10705
|
}
|
|
10128
|
-
|
|
10129
|
-
|
|
10706
|
+
const toolCalls = safeSerializableFieldRead(lastChunk, "toolCalls");
|
|
10707
|
+
if (toolCalls !== void 0) {
|
|
10708
|
+
output.toolCalls = toolCalls;
|
|
10130
10709
|
}
|
|
10131
10710
|
}
|
|
10132
10711
|
finalizeAISDKChildTracing(endEvent);
|
|
@@ -10135,6 +10714,7 @@ function aggregateAISDKChunks(chunks, _result, endEvent) {
|
|
|
10135
10714
|
function extractGetterValues(obj) {
|
|
10136
10715
|
const getterValues = {};
|
|
10137
10716
|
const getterNames = [
|
|
10717
|
+
"content",
|
|
10138
10718
|
"text",
|
|
10139
10719
|
"object",
|
|
10140
10720
|
"finishReason",
|
|
@@ -10150,8 +10730,17 @@ function extractGetterValues(obj) {
|
|
|
10150
10730
|
];
|
|
10151
10731
|
for (const name of getterNames) {
|
|
10152
10732
|
try {
|
|
10153
|
-
if (obj
|
|
10154
|
-
|
|
10733
|
+
if (!obj || !(name in obj)) {
|
|
10734
|
+
continue;
|
|
10735
|
+
}
|
|
10736
|
+
const value = obj[name];
|
|
10737
|
+
if (isPromiseLike(value)) {
|
|
10738
|
+
void Promise.resolve(value).catch(() => {
|
|
10739
|
+
});
|
|
10740
|
+
continue;
|
|
10741
|
+
}
|
|
10742
|
+
if (isSerializableOutputValue(value)) {
|
|
10743
|
+
getterValues[name] = value;
|
|
10155
10744
|
}
|
|
10156
10745
|
} catch {
|
|
10157
10746
|
}
|
|
@@ -10173,6 +10762,11 @@ function extractSerializableOutputFields(output) {
|
|
|
10173
10762
|
for (const name of directFieldNames) {
|
|
10174
10763
|
try {
|
|
10175
10764
|
const value = output?.[name];
|
|
10765
|
+
if (isPromiseLike(value)) {
|
|
10766
|
+
void Promise.resolve(value).catch(() => {
|
|
10767
|
+
});
|
|
10768
|
+
continue;
|
|
10769
|
+
}
|
|
10176
10770
|
if (isSerializableOutputValue(value)) {
|
|
10177
10771
|
serialized[name] = value;
|
|
10178
10772
|
}
|
|
@@ -10184,6 +10778,9 @@ function extractSerializableOutputFields(output) {
|
|
|
10184
10778
|
...extractGetterValues(output)
|
|
10185
10779
|
};
|
|
10186
10780
|
}
|
|
10781
|
+
function isPromiseLike(value) {
|
|
10782
|
+
return value != null && typeof value === "object" && typeof value.then === "function";
|
|
10783
|
+
}
|
|
10187
10784
|
function isSerializableOutputValue(value) {
|
|
10188
10785
|
if (typeof value === "function") {
|
|
10189
10786
|
return false;
|
|
@@ -10225,8 +10822,9 @@ function parseGatewayModelString(modelString) {
|
|
|
10225
10822
|
return { model: modelString };
|
|
10226
10823
|
}
|
|
10227
10824
|
function extractGatewayRoutingInfo(result) {
|
|
10228
|
-
|
|
10229
|
-
|
|
10825
|
+
const steps = safeSerializableFieldRead(result, "steps");
|
|
10826
|
+
if (Array.isArray(steps) && steps.length > 0) {
|
|
10827
|
+
const routing2 = steps[0]?.providerMetadata?.gateway?.routing;
|
|
10230
10828
|
if (routing2) {
|
|
10231
10829
|
return {
|
|
10232
10830
|
provider: routing2.resolvedProvider || routing2.finalProvider,
|
|
@@ -10234,7 +10832,11 @@ function extractGatewayRoutingInfo(result) {
|
|
|
10234
10832
|
};
|
|
10235
10833
|
}
|
|
10236
10834
|
}
|
|
10237
|
-
const
|
|
10835
|
+
const providerMetadata = safeSerializableFieldRead(
|
|
10836
|
+
result,
|
|
10837
|
+
"providerMetadata"
|
|
10838
|
+
);
|
|
10839
|
+
const routing = providerMetadata?.gateway?.routing;
|
|
10238
10840
|
if (routing) {
|
|
10239
10841
|
return {
|
|
10240
10842
|
provider: routing.resolvedProvider || routing.finalProvider,
|
|
@@ -10244,10 +10846,11 @@ function extractGatewayRoutingInfo(result) {
|
|
|
10244
10846
|
return null;
|
|
10245
10847
|
}
|
|
10246
10848
|
function extractCostFromResult(result) {
|
|
10247
|
-
|
|
10849
|
+
const steps = safeSerializableFieldRead(result, "steps");
|
|
10850
|
+
if (Array.isArray(steps) && steps.length > 0) {
|
|
10248
10851
|
let totalCost = 0;
|
|
10249
10852
|
let foundCost = false;
|
|
10250
|
-
for (const step of
|
|
10853
|
+
for (const step of steps) {
|
|
10251
10854
|
const gateway2 = step?.providerMetadata?.gateway;
|
|
10252
10855
|
const stepCost = parseGatewayCost(gateway2?.cost) || parseGatewayCost(gateway2?.marketCost);
|
|
10253
10856
|
if (stepCost !== void 0 && stepCost > 0) {
|
|
@@ -10259,7 +10862,11 @@ function extractCostFromResult(result) {
|
|
|
10259
10862
|
return totalCost;
|
|
10260
10863
|
}
|
|
10261
10864
|
}
|
|
10262
|
-
const
|
|
10865
|
+
const providerMetadata = safeSerializableFieldRead(
|
|
10866
|
+
result,
|
|
10867
|
+
"providerMetadata"
|
|
10868
|
+
);
|
|
10869
|
+
const gateway = providerMetadata?.gateway;
|
|
10263
10870
|
const directCost = parseGatewayCost(gateway?.cost) || parseGatewayCost(gateway?.marketCost);
|
|
10264
10871
|
if (directCost !== void 0 && directCost > 0) {
|
|
10265
10872
|
return directCost;
|
|
@@ -10371,12 +10978,15 @@ var claudeAgentSDKChannels = defineChannels(
|
|
|
10371
10978
|
{
|
|
10372
10979
|
query: channel({
|
|
10373
10980
|
channelName: "query",
|
|
10374
|
-
kind: "
|
|
10981
|
+
kind: "sync-stream"
|
|
10375
10982
|
})
|
|
10376
10983
|
}
|
|
10377
10984
|
);
|
|
10378
10985
|
|
|
10379
10986
|
// src/instrumentation/plugins/claude-agent-sdk-plugin.ts
|
|
10987
|
+
function isSubAgentToolName(toolName) {
|
|
10988
|
+
return toolName === "Agent" || toolName === "Task";
|
|
10989
|
+
}
|
|
10380
10990
|
function filterSerializableOptions(options) {
|
|
10381
10991
|
const allowedKeys = [
|
|
10382
10992
|
"model",
|
|
@@ -10430,34 +11040,50 @@ function extractUsageFromMessage(message) {
|
|
|
10430
11040
|
const cacheReadTokens = getNumberProperty(usage, "cache_read_input_tokens") || 0;
|
|
10431
11041
|
const cacheCreationTokens = getNumberProperty(usage, "cache_creation_input_tokens") || 0;
|
|
10432
11042
|
if (cacheReadTokens > 0 || cacheCreationTokens > 0) {
|
|
10433
|
-
|
|
10434
|
-
|
|
10435
|
-
cacheCreationTokens
|
|
11043
|
+
Object.assign(
|
|
11044
|
+
metrics,
|
|
11045
|
+
extractAnthropicCacheTokens(cacheReadTokens, cacheCreationTokens)
|
|
10436
11046
|
);
|
|
10437
|
-
Object.assign(metrics, cacheTokens);
|
|
10438
11047
|
}
|
|
10439
11048
|
if (Object.keys(metrics).length > 0) {
|
|
10440
11049
|
Object.assign(metrics, finalizeAnthropicTokens(metrics));
|
|
10441
11050
|
}
|
|
10442
11051
|
return metrics;
|
|
10443
11052
|
}
|
|
10444
|
-
function buildLLMInput(prompt, conversationHistory) {
|
|
10445
|
-
const
|
|
10446
|
-
|
|
10447
|
-
|
|
10448
|
-
|
|
10449
|
-
|
|
11053
|
+
function buildLLMInput(prompt, conversationHistory, capturedPromptMessages) {
|
|
11054
|
+
const promptMessages = [];
|
|
11055
|
+
if (typeof prompt === "string") {
|
|
11056
|
+
promptMessages.push({ content: prompt, role: "user" });
|
|
11057
|
+
} else if (capturedPromptMessages && capturedPromptMessages.length > 0) {
|
|
11058
|
+
for (const msg of capturedPromptMessages) {
|
|
11059
|
+
const role = msg.message?.role;
|
|
11060
|
+
const content = msg.message?.content;
|
|
11061
|
+
if (role && content !== void 0) {
|
|
11062
|
+
promptMessages.push({ content, role });
|
|
11063
|
+
}
|
|
11064
|
+
}
|
|
11065
|
+
}
|
|
11066
|
+
const inputParts = [...promptMessages, ...conversationHistory];
|
|
10450
11067
|
return inputParts.length > 0 ? inputParts : void 0;
|
|
10451
11068
|
}
|
|
10452
|
-
|
|
10453
|
-
|
|
11069
|
+
function formatCapturedMessages(messages) {
|
|
11070
|
+
return messages.length > 0 ? messages : [];
|
|
11071
|
+
}
|
|
11072
|
+
async function createLLMSpanForMessages(messages, prompt, conversationHistory, options, startTime, capturedPromptMessages, parentSpan) {
|
|
11073
|
+
if (messages.length === 0) {
|
|
11074
|
+
return void 0;
|
|
11075
|
+
}
|
|
10454
11076
|
const lastMessage = messages[messages.length - 1];
|
|
10455
11077
|
if (lastMessage.type !== "assistant" || !lastMessage.message?.usage) {
|
|
10456
11078
|
return void 0;
|
|
10457
11079
|
}
|
|
10458
11080
|
const model = lastMessage.message.model || options.model;
|
|
10459
11081
|
const usage = extractUsageFromMessage(lastMessage);
|
|
10460
|
-
const input = buildLLMInput(
|
|
11082
|
+
const input = buildLLMInput(
|
|
11083
|
+
prompt,
|
|
11084
|
+
conversationHistory,
|
|
11085
|
+
capturedPromptMessages
|
|
11086
|
+
);
|
|
10461
11087
|
const outputs = messages.map(
|
|
10462
11088
|
(m) => m.message?.content && m.message?.role ? { content: m.message.content, role: m.message.role } : void 0
|
|
10463
11089
|
).filter(
|
|
@@ -10465,21 +11091,359 @@ async function createLLMSpanForMessages(messages, prompt, conversationHistory, o
|
|
|
10465
11091
|
);
|
|
10466
11092
|
const span = startSpan({
|
|
10467
11093
|
name: "anthropic.messages.create",
|
|
11094
|
+
parent: parentSpan,
|
|
10468
11095
|
spanAttributes: {
|
|
10469
11096
|
type: "llm" /* LLM */
|
|
10470
11097
|
},
|
|
10471
|
-
startTime
|
|
10472
|
-
parent: parentSpan
|
|
11098
|
+
startTime
|
|
10473
11099
|
});
|
|
10474
11100
|
span.log({
|
|
10475
11101
|
input,
|
|
10476
|
-
output: outputs,
|
|
10477
11102
|
metadata: model ? { model } : void 0,
|
|
10478
|
-
metrics: usage
|
|
11103
|
+
metrics: usage,
|
|
11104
|
+
output: outputs
|
|
10479
11105
|
});
|
|
10480
11106
|
await span.end();
|
|
10481
11107
|
return lastMessage.message?.content && lastMessage.message?.role ? { content: lastMessage.message.content, role: lastMessage.message.role } : void 0;
|
|
10482
11108
|
}
|
|
11109
|
+
function getMcpServerMetadata(serverName, mcpServers) {
|
|
11110
|
+
if (!serverName || !mcpServers) {
|
|
11111
|
+
return {};
|
|
11112
|
+
}
|
|
11113
|
+
const serverConfig = mcpServers[serverName];
|
|
11114
|
+
if (!serverConfig) {
|
|
11115
|
+
return {};
|
|
11116
|
+
}
|
|
11117
|
+
const metadata = {};
|
|
11118
|
+
if (serverConfig.type) {
|
|
11119
|
+
metadata["mcp.type"] = serverConfig.type;
|
|
11120
|
+
} else if (typeof serverConfig === "object" && "transport" in serverConfig) {
|
|
11121
|
+
metadata["mcp.type"] = "sdk";
|
|
11122
|
+
}
|
|
11123
|
+
if (serverConfig.url) {
|
|
11124
|
+
metadata["mcp.url"] = serverConfig.url;
|
|
11125
|
+
}
|
|
11126
|
+
if (serverConfig.command) {
|
|
11127
|
+
metadata["mcp.command"] = serverConfig.command;
|
|
11128
|
+
if (serverConfig.args) {
|
|
11129
|
+
metadata["mcp.args"] = serverConfig.args.join(" ");
|
|
11130
|
+
}
|
|
11131
|
+
}
|
|
11132
|
+
return metadata;
|
|
11133
|
+
}
|
|
11134
|
+
function parseToolName(rawToolName) {
|
|
11135
|
+
const mcpMatch = rawToolName.match(/^mcp__([^_]+)__(.+)$/);
|
|
11136
|
+
if (mcpMatch) {
|
|
11137
|
+
const [, mcpServer, toolName] = mcpMatch;
|
|
11138
|
+
return {
|
|
11139
|
+
displayName: `tool: ${mcpServer}/${toolName}`,
|
|
11140
|
+
mcpServer,
|
|
11141
|
+
rawToolName,
|
|
11142
|
+
toolName
|
|
11143
|
+
};
|
|
11144
|
+
}
|
|
11145
|
+
return {
|
|
11146
|
+
displayName: `tool: ${rawToolName}`,
|
|
11147
|
+
rawToolName,
|
|
11148
|
+
toolName: rawToolName
|
|
11149
|
+
};
|
|
11150
|
+
}
|
|
11151
|
+
function createToolTracingHooks(resolveParentSpan, activeToolSpans, mcpServers, subAgentSpans, endedSubAgentSpans) {
|
|
11152
|
+
const preToolUse = async (input, toolUseID) => {
|
|
11153
|
+
if (input.hook_event_name !== "PreToolUse" || !toolUseID) {
|
|
11154
|
+
return {};
|
|
11155
|
+
}
|
|
11156
|
+
if (isSubAgentToolName(input.tool_name)) {
|
|
11157
|
+
return {};
|
|
11158
|
+
}
|
|
11159
|
+
const parsed = parseToolName(input.tool_name);
|
|
11160
|
+
const toolSpan = startSpan({
|
|
11161
|
+
event: {
|
|
11162
|
+
input: input.tool_input,
|
|
11163
|
+
metadata: {
|
|
11164
|
+
"claude_agent_sdk.cwd": input.cwd,
|
|
11165
|
+
"claude_agent_sdk.raw_tool_name": parsed.rawToolName,
|
|
11166
|
+
"claude_agent_sdk.session_id": input.session_id,
|
|
11167
|
+
"gen_ai.tool.call.id": toolUseID,
|
|
11168
|
+
"gen_ai.tool.name": parsed.toolName,
|
|
11169
|
+
...parsed.mcpServer && { "mcp.server": parsed.mcpServer },
|
|
11170
|
+
...getMcpServerMetadata(parsed.mcpServer, mcpServers)
|
|
11171
|
+
}
|
|
11172
|
+
},
|
|
11173
|
+
name: parsed.displayName,
|
|
11174
|
+
parent: await resolveParentSpan(toolUseID),
|
|
11175
|
+
spanAttributes: { type: "tool" /* TOOL */ }
|
|
11176
|
+
});
|
|
11177
|
+
activeToolSpans.set(toolUseID, toolSpan);
|
|
11178
|
+
return {};
|
|
11179
|
+
};
|
|
11180
|
+
const postToolUse = async (input, toolUseID) => {
|
|
11181
|
+
if (input.hook_event_name !== "PostToolUse" || !toolUseID) {
|
|
11182
|
+
return {};
|
|
11183
|
+
}
|
|
11184
|
+
const subAgentSpan = subAgentSpans.get(toolUseID);
|
|
11185
|
+
if (subAgentSpan) {
|
|
11186
|
+
try {
|
|
11187
|
+
const response = input.tool_response;
|
|
11188
|
+
const metadata = {};
|
|
11189
|
+
if (response?.status) {
|
|
11190
|
+
metadata["claude_agent_sdk.status"] = response.status;
|
|
11191
|
+
}
|
|
11192
|
+
if (response?.totalDurationMs) {
|
|
11193
|
+
metadata["claude_agent_sdk.duration_ms"] = response.totalDurationMs;
|
|
11194
|
+
}
|
|
11195
|
+
if (response?.totalToolUseCount !== void 0) {
|
|
11196
|
+
metadata["claude_agent_sdk.tool_use_count"] = response.totalToolUseCount;
|
|
11197
|
+
}
|
|
11198
|
+
subAgentSpan.log({
|
|
11199
|
+
metadata,
|
|
11200
|
+
output: response?.content
|
|
11201
|
+
});
|
|
11202
|
+
} finally {
|
|
11203
|
+
subAgentSpan.end();
|
|
11204
|
+
endedSubAgentSpans.add(toolUseID);
|
|
11205
|
+
}
|
|
11206
|
+
return {};
|
|
11207
|
+
}
|
|
11208
|
+
const toolSpan = activeToolSpans.get(toolUseID);
|
|
11209
|
+
if (!toolSpan) {
|
|
11210
|
+
return {};
|
|
11211
|
+
}
|
|
11212
|
+
try {
|
|
11213
|
+
toolSpan.log({ output: input.tool_response });
|
|
11214
|
+
} finally {
|
|
11215
|
+
toolSpan.end();
|
|
11216
|
+
activeToolSpans.delete(toolUseID);
|
|
11217
|
+
}
|
|
11218
|
+
return {};
|
|
11219
|
+
};
|
|
11220
|
+
const postToolUseFailure = async (input, toolUseID) => {
|
|
11221
|
+
if (input.hook_event_name !== "PostToolUseFailure" || !toolUseID) {
|
|
11222
|
+
return {};
|
|
11223
|
+
}
|
|
11224
|
+
const subAgentSpan = subAgentSpans.get(toolUseID);
|
|
11225
|
+
if (subAgentSpan) {
|
|
11226
|
+
try {
|
|
11227
|
+
subAgentSpan.log({ error: input.error });
|
|
11228
|
+
} finally {
|
|
11229
|
+
subAgentSpan.end();
|
|
11230
|
+
endedSubAgentSpans.add(toolUseID);
|
|
11231
|
+
}
|
|
11232
|
+
return {};
|
|
11233
|
+
}
|
|
11234
|
+
const toolSpan = activeToolSpans.get(toolUseID);
|
|
11235
|
+
if (!toolSpan) {
|
|
11236
|
+
return {};
|
|
11237
|
+
}
|
|
11238
|
+
const parsed = parseToolName(input.tool_name);
|
|
11239
|
+
try {
|
|
11240
|
+
toolSpan.log({
|
|
11241
|
+
error: input.error,
|
|
11242
|
+
metadata: {
|
|
11243
|
+
"claude_agent_sdk.is_interrupt": input.is_interrupt,
|
|
11244
|
+
"claude_agent_sdk.session_id": input.session_id,
|
|
11245
|
+
"gen_ai.tool.call.id": toolUseID,
|
|
11246
|
+
"gen_ai.tool.name": parsed.toolName,
|
|
11247
|
+
...parsed.mcpServer && { "mcp.server": parsed.mcpServer }
|
|
11248
|
+
}
|
|
11249
|
+
});
|
|
11250
|
+
} finally {
|
|
11251
|
+
toolSpan.end();
|
|
11252
|
+
activeToolSpans.delete(toolUseID);
|
|
11253
|
+
}
|
|
11254
|
+
return {};
|
|
11255
|
+
};
|
|
11256
|
+
return { postToolUse, postToolUseFailure, preToolUse };
|
|
11257
|
+
}
|
|
11258
|
+
function injectTracingHooks(options, resolveParentSpan, activeToolSpans, subAgentSpans, endedSubAgentSpans) {
|
|
11259
|
+
const { preToolUse, postToolUse, postToolUseFailure } = createToolTracingHooks(
|
|
11260
|
+
resolveParentSpan,
|
|
11261
|
+
activeToolSpans,
|
|
11262
|
+
options.mcpServers,
|
|
11263
|
+
subAgentSpans,
|
|
11264
|
+
endedSubAgentSpans
|
|
11265
|
+
);
|
|
11266
|
+
const existingHooks = options.hooks ?? {};
|
|
11267
|
+
return {
|
|
11268
|
+
...options,
|
|
11269
|
+
hooks: {
|
|
11270
|
+
...existingHooks,
|
|
11271
|
+
PostToolUse: [
|
|
11272
|
+
...existingHooks.PostToolUse ?? [],
|
|
11273
|
+
{ hooks: [postToolUse] }
|
|
11274
|
+
],
|
|
11275
|
+
PostToolUseFailure: [
|
|
11276
|
+
...existingHooks.PostToolUseFailure ?? [],
|
|
11277
|
+
{
|
|
11278
|
+
hooks: [postToolUseFailure]
|
|
11279
|
+
}
|
|
11280
|
+
],
|
|
11281
|
+
PreToolUse: [
|
|
11282
|
+
...existingHooks.PreToolUse ?? [],
|
|
11283
|
+
{ hooks: [preToolUse] }
|
|
11284
|
+
]
|
|
11285
|
+
}
|
|
11286
|
+
};
|
|
11287
|
+
}
|
|
11288
|
+
async function finalizeCurrentMessageGroup(state) {
|
|
11289
|
+
if (state.currentMessages.length === 0) {
|
|
11290
|
+
return;
|
|
11291
|
+
}
|
|
11292
|
+
const parentToolUseId = state.currentMessages[0]?.parent_tool_use_id ?? null;
|
|
11293
|
+
let parentSpan = await state.span.export();
|
|
11294
|
+
if (parentToolUseId) {
|
|
11295
|
+
const subAgentSpan = state.subAgentSpans.get(parentToolUseId);
|
|
11296
|
+
if (subAgentSpan) {
|
|
11297
|
+
parentSpan = await subAgentSpan.export();
|
|
11298
|
+
}
|
|
11299
|
+
}
|
|
11300
|
+
const finalMessage = await createLLMSpanForMessages(
|
|
11301
|
+
state.currentMessages,
|
|
11302
|
+
state.originalPrompt,
|
|
11303
|
+
state.finalResults,
|
|
11304
|
+
state.options,
|
|
11305
|
+
state.currentMessageStartTime,
|
|
11306
|
+
state.capturedPromptMessages,
|
|
11307
|
+
parentSpan
|
|
11308
|
+
);
|
|
11309
|
+
if (finalMessage) {
|
|
11310
|
+
state.finalResults.push(finalMessage);
|
|
11311
|
+
}
|
|
11312
|
+
const lastMessage = state.currentMessages[state.currentMessages.length - 1];
|
|
11313
|
+
if (lastMessage?.message?.usage) {
|
|
11314
|
+
state.accumulatedOutputTokens += getNumberProperty(lastMessage.message.usage, "output_tokens") || 0;
|
|
11315
|
+
}
|
|
11316
|
+
state.currentMessages.length = 0;
|
|
11317
|
+
}
|
|
11318
|
+
function maybeTrackToolUseContext(state, message) {
|
|
11319
|
+
if (message.type !== "assistant" || !Array.isArray(message.message?.content)) {
|
|
11320
|
+
return;
|
|
11321
|
+
}
|
|
11322
|
+
const parentToolUseId = message.parent_tool_use_id ?? null;
|
|
11323
|
+
for (const block of message.message.content) {
|
|
11324
|
+
if (typeof block !== "object" || block === null || !("type" in block) || block.type !== "tool_use" || !("id" in block) || typeof block.id !== "string") {
|
|
11325
|
+
continue;
|
|
11326
|
+
}
|
|
11327
|
+
state.toolUseToParent.set(block.id, parentToolUseId);
|
|
11328
|
+
if (block.name === "Task" && typeof block.input === "object" && block.input !== null && "subagent_type" in block.input && typeof block.input.subagent_type === "string") {
|
|
11329
|
+
state.pendingSubAgentNames.set(block.id, block.input.subagent_type);
|
|
11330
|
+
}
|
|
11331
|
+
}
|
|
11332
|
+
}
|
|
11333
|
+
async function maybeStartSubAgentSpan(state, message) {
|
|
11334
|
+
if (!("parent_tool_use_id" in message)) {
|
|
11335
|
+
return;
|
|
11336
|
+
}
|
|
11337
|
+
const parentToolUseId = message.parent_tool_use_id;
|
|
11338
|
+
if (!parentToolUseId) {
|
|
11339
|
+
return;
|
|
11340
|
+
}
|
|
11341
|
+
await ensureSubAgentSpan(
|
|
11342
|
+
state.pendingSubAgentNames,
|
|
11343
|
+
state.span,
|
|
11344
|
+
state.subAgentSpans,
|
|
11345
|
+
parentToolUseId
|
|
11346
|
+
);
|
|
11347
|
+
}
|
|
11348
|
+
async function ensureSubAgentSpan(pendingSubAgentNames, rootSpan, subAgentSpans, parentToolUseId) {
|
|
11349
|
+
const existingSpan = subAgentSpans.get(parentToolUseId);
|
|
11350
|
+
if (existingSpan) {
|
|
11351
|
+
return existingSpan;
|
|
11352
|
+
}
|
|
11353
|
+
const agentName = pendingSubAgentNames.get(parentToolUseId);
|
|
11354
|
+
const spanName = agentName ? `Agent: ${agentName}` : "Agent: sub-agent";
|
|
11355
|
+
const subAgentSpan = startSpan({
|
|
11356
|
+
event: {
|
|
11357
|
+
metadata: {
|
|
11358
|
+
...agentName && { "claude_agent_sdk.agent_type": agentName }
|
|
11359
|
+
}
|
|
11360
|
+
},
|
|
11361
|
+
name: spanName,
|
|
11362
|
+
parent: await rootSpan.export(),
|
|
11363
|
+
spanAttributes: { type: "task" /* TASK */ }
|
|
11364
|
+
});
|
|
11365
|
+
subAgentSpans.set(parentToolUseId, subAgentSpan);
|
|
11366
|
+
return subAgentSpan;
|
|
11367
|
+
}
|
|
11368
|
+
async function handleStreamMessage(state, message) {
|
|
11369
|
+
maybeTrackToolUseContext(state, message);
|
|
11370
|
+
await maybeStartSubAgentSpan(state, message);
|
|
11371
|
+
const messageId = message.message?.id;
|
|
11372
|
+
if (messageId && messageId !== state.currentMessageId) {
|
|
11373
|
+
await finalizeCurrentMessageGroup(state);
|
|
11374
|
+
state.currentMessageId = messageId;
|
|
11375
|
+
state.currentMessageStartTime = getCurrentUnixTimestamp();
|
|
11376
|
+
}
|
|
11377
|
+
if (message.type === "assistant" && message.message?.usage) {
|
|
11378
|
+
state.currentMessages.push(message);
|
|
11379
|
+
}
|
|
11380
|
+
if (message.type !== "result" || !message.usage) {
|
|
11381
|
+
return;
|
|
11382
|
+
}
|
|
11383
|
+
const finalUsageMetrics = extractUsageFromMessage(message);
|
|
11384
|
+
if (state.currentMessages.length > 0 && finalUsageMetrics.completion_tokens !== void 0) {
|
|
11385
|
+
const lastMessage = state.currentMessages[state.currentMessages.length - 1];
|
|
11386
|
+
if (lastMessage?.message?.usage) {
|
|
11387
|
+
const adjustedTokens = finalUsageMetrics.completion_tokens - state.accumulatedOutputTokens;
|
|
11388
|
+
if (adjustedTokens >= 0) {
|
|
11389
|
+
lastMessage.message.usage.output_tokens = adjustedTokens;
|
|
11390
|
+
}
|
|
11391
|
+
const resultUsage = message.usage;
|
|
11392
|
+
if (resultUsage && typeof resultUsage === "object") {
|
|
11393
|
+
const cacheReadTokens = getNumberProperty(
|
|
11394
|
+
resultUsage,
|
|
11395
|
+
"cache_read_input_tokens"
|
|
11396
|
+
);
|
|
11397
|
+
if (cacheReadTokens !== void 0) {
|
|
11398
|
+
lastMessage.message.usage.cache_read_input_tokens = cacheReadTokens;
|
|
11399
|
+
}
|
|
11400
|
+
const cacheCreationTokens = getNumberProperty(
|
|
11401
|
+
resultUsage,
|
|
11402
|
+
"cache_creation_input_tokens"
|
|
11403
|
+
);
|
|
11404
|
+
if (cacheCreationTokens !== void 0) {
|
|
11405
|
+
lastMessage.message.usage.cache_creation_input_tokens = cacheCreationTokens;
|
|
11406
|
+
}
|
|
11407
|
+
}
|
|
11408
|
+
}
|
|
11409
|
+
}
|
|
11410
|
+
const metadata = {};
|
|
11411
|
+
if (message.num_turns !== void 0) {
|
|
11412
|
+
metadata.num_turns = message.num_turns;
|
|
11413
|
+
}
|
|
11414
|
+
if (message.session_id !== void 0) {
|
|
11415
|
+
metadata.session_id = message.session_id;
|
|
11416
|
+
}
|
|
11417
|
+
if (Object.keys(metadata).length > 0) {
|
|
11418
|
+
state.span.log({ metadata });
|
|
11419
|
+
}
|
|
11420
|
+
}
|
|
11421
|
+
async function finalizeQuerySpan(state) {
|
|
11422
|
+
try {
|
|
11423
|
+
await finalizeCurrentMessageGroup(state);
|
|
11424
|
+
state.span.log({
|
|
11425
|
+
output: state.finalResults.length > 0 ? state.finalResults[state.finalResults.length - 1] : void 0
|
|
11426
|
+
});
|
|
11427
|
+
if (state.capturedPromptMessages) {
|
|
11428
|
+
if (state.promptStarted()) {
|
|
11429
|
+
await state.promptDone;
|
|
11430
|
+
}
|
|
11431
|
+
if (state.capturedPromptMessages.length > 0) {
|
|
11432
|
+
state.span.log({
|
|
11433
|
+
input: formatCapturedMessages(state.capturedPromptMessages)
|
|
11434
|
+
});
|
|
11435
|
+
}
|
|
11436
|
+
}
|
|
11437
|
+
} finally {
|
|
11438
|
+
for (const [id, subAgentSpan] of state.subAgentSpans) {
|
|
11439
|
+
if (!state.endedSubAgentSpans.has(id)) {
|
|
11440
|
+
subAgentSpan.end();
|
|
11441
|
+
}
|
|
11442
|
+
}
|
|
11443
|
+
state.subAgentSpans.clear();
|
|
11444
|
+
state.span.end();
|
|
11445
|
+
}
|
|
11446
|
+
}
|
|
10483
11447
|
var ClaudeAgentSDKPlugin = class extends BasePlugin {
|
|
10484
11448
|
onEnable() {
|
|
10485
11449
|
this.subscribeToQuery();
|
|
@@ -10490,19 +11454,36 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
|
|
|
10490
11454
|
}
|
|
10491
11455
|
this.unsubscribers = [];
|
|
10492
11456
|
}
|
|
10493
|
-
/**
|
|
10494
|
-
* Subscribe to the query channel for agent interactions.
|
|
10495
|
-
* Handles streaming responses and traces both the top-level agent task
|
|
10496
|
-
* and individual LLM calls.
|
|
10497
|
-
*/
|
|
10498
11457
|
subscribeToQuery() {
|
|
10499
11458
|
const channel2 = claudeAgentSDKChannels.query.tracingChannel();
|
|
10500
11459
|
const spans = /* @__PURE__ */ new WeakMap();
|
|
10501
11460
|
const handlers = {
|
|
10502
11461
|
start: (event) => {
|
|
10503
|
-
const params = event.arguments[0];
|
|
10504
|
-
const
|
|
10505
|
-
const options = params
|
|
11462
|
+
const params = event.arguments[0] ?? {};
|
|
11463
|
+
const originalPrompt = params.prompt;
|
|
11464
|
+
const options = params.options ?? {};
|
|
11465
|
+
const promptIsAsyncIterable = isAsyncIterable(originalPrompt);
|
|
11466
|
+
let promptStarted = false;
|
|
11467
|
+
let capturedPromptMessages;
|
|
11468
|
+
let resolvePromptDone;
|
|
11469
|
+
const promptDone = new Promise((resolve) => {
|
|
11470
|
+
resolvePromptDone = resolve;
|
|
11471
|
+
});
|
|
11472
|
+
if (promptIsAsyncIterable) {
|
|
11473
|
+
capturedPromptMessages = [];
|
|
11474
|
+
const promptStream = originalPrompt;
|
|
11475
|
+
params.prompt = (async function* () {
|
|
11476
|
+
promptStarted = true;
|
|
11477
|
+
try {
|
|
11478
|
+
for await (const message of promptStream) {
|
|
11479
|
+
capturedPromptMessages.push(message);
|
|
11480
|
+
yield message;
|
|
11481
|
+
}
|
|
11482
|
+
} finally {
|
|
11483
|
+
resolvePromptDone?.();
|
|
11484
|
+
}
|
|
11485
|
+
})();
|
|
11486
|
+
}
|
|
10506
11487
|
const span = startSpan({
|
|
10507
11488
|
name: "Claude Agent",
|
|
10508
11489
|
spanAttributes: {
|
|
@@ -10512,163 +11493,111 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
|
|
|
10512
11493
|
const startTime = getCurrentUnixTimestamp();
|
|
10513
11494
|
try {
|
|
10514
11495
|
span.log({
|
|
10515
|
-
input: typeof
|
|
10516
|
-
type: "streaming",
|
|
10517
|
-
description: "AsyncIterable<ClaudeAgentSDKMessage>"
|
|
10518
|
-
},
|
|
11496
|
+
input: typeof originalPrompt === "string" ? originalPrompt : promptIsAsyncIterable ? void 0 : originalPrompt !== void 0 ? String(originalPrompt) : void 0,
|
|
10519
11497
|
metadata: filterSerializableOptions(options)
|
|
10520
11498
|
});
|
|
10521
11499
|
} catch (error) {
|
|
10522
11500
|
console.error("Error extracting input for Claude Agent SDK:", error);
|
|
10523
11501
|
}
|
|
11502
|
+
const activeToolSpans = /* @__PURE__ */ new Map();
|
|
11503
|
+
const subAgentSpans = /* @__PURE__ */ new Map();
|
|
11504
|
+
const endedSubAgentSpans = /* @__PURE__ */ new Set();
|
|
11505
|
+
const toolUseToParent = /* @__PURE__ */ new Map();
|
|
11506
|
+
const pendingSubAgentNames = /* @__PURE__ */ new Map();
|
|
11507
|
+
const optionsWithHooks = injectTracingHooks(
|
|
11508
|
+
options,
|
|
11509
|
+
async (toolUseID) => {
|
|
11510
|
+
const parentToolUseId = toolUseToParent.get(toolUseID);
|
|
11511
|
+
if (parentToolUseId) {
|
|
11512
|
+
const subAgentSpan = await ensureSubAgentSpan(
|
|
11513
|
+
pendingSubAgentNames,
|
|
11514
|
+
span,
|
|
11515
|
+
subAgentSpans,
|
|
11516
|
+
parentToolUseId
|
|
11517
|
+
);
|
|
11518
|
+
return subAgentSpan.export();
|
|
11519
|
+
}
|
|
11520
|
+
return span.export();
|
|
11521
|
+
},
|
|
11522
|
+
activeToolSpans,
|
|
11523
|
+
subAgentSpans,
|
|
11524
|
+
endedSubAgentSpans
|
|
11525
|
+
);
|
|
11526
|
+
params.options = optionsWithHooks;
|
|
11527
|
+
event.arguments[0] = params;
|
|
10524
11528
|
spans.set(event, {
|
|
10525
|
-
|
|
10526
|
-
|
|
10527
|
-
|
|
10528
|
-
currentMessages: [],
|
|
11529
|
+
accumulatedOutputTokens: 0,
|
|
11530
|
+
activeToolSpans,
|
|
11531
|
+
capturedPromptMessages,
|
|
10529
11532
|
currentMessageId: void 0,
|
|
10530
11533
|
currentMessageStartTime: startTime,
|
|
10531
|
-
|
|
11534
|
+
currentMessages: [],
|
|
11535
|
+
endedSubAgentSpans,
|
|
11536
|
+
finalResults: [],
|
|
11537
|
+
options: optionsWithHooks,
|
|
11538
|
+
originalPrompt,
|
|
11539
|
+
pendingSubAgentNames,
|
|
11540
|
+
processing: Promise.resolve(),
|
|
11541
|
+
promptDone,
|
|
11542
|
+
promptStarted: () => promptStarted,
|
|
11543
|
+
span,
|
|
11544
|
+
subAgentSpans,
|
|
11545
|
+
toolUseToParent
|
|
10532
11546
|
});
|
|
10533
11547
|
},
|
|
10534
|
-
|
|
10535
|
-
const
|
|
10536
|
-
if (!
|
|
11548
|
+
end: (event) => {
|
|
11549
|
+
const state = spans.get(event);
|
|
11550
|
+
if (!state) {
|
|
10537
11551
|
return;
|
|
10538
11552
|
}
|
|
10539
11553
|
const eventResult = event.result;
|
|
10540
11554
|
if (eventResult === void 0) {
|
|
10541
|
-
|
|
11555
|
+
state.span.end();
|
|
10542
11556
|
spans.delete(event);
|
|
10543
11557
|
return;
|
|
10544
11558
|
}
|
|
10545
11559
|
if (isAsyncIterable(eventResult)) {
|
|
10546
11560
|
patchStreamIfNeeded(eventResult, {
|
|
10547
|
-
onChunk:
|
|
10548
|
-
|
|
10549
|
-
|
|
10550
|
-
const prompt = params?.prompt;
|
|
10551
|
-
const options = params?.options ?? {};
|
|
10552
|
-
const messageId = message.message?.id;
|
|
10553
|
-
if (messageId && messageId !== spanData.currentMessageId) {
|
|
10554
|
-
if (spanData.currentMessages.length > 0) {
|
|
10555
|
-
const finalMessage = await createLLMSpanForMessages(
|
|
10556
|
-
spanData.currentMessages,
|
|
10557
|
-
prompt,
|
|
10558
|
-
spanData.conversationHistory,
|
|
10559
|
-
options,
|
|
10560
|
-
spanData.currentMessageStartTime,
|
|
10561
|
-
await spanData.span.export()
|
|
10562
|
-
);
|
|
10563
|
-
if (finalMessage) {
|
|
10564
|
-
spanData.conversationHistory.push(finalMessage);
|
|
10565
|
-
}
|
|
10566
|
-
const lastMessage = spanData.currentMessages[spanData.currentMessages.length - 1];
|
|
10567
|
-
if (lastMessage?.message?.usage) {
|
|
10568
|
-
const outputTokens = getNumberProperty(
|
|
10569
|
-
lastMessage.message.usage,
|
|
10570
|
-
"output_tokens"
|
|
10571
|
-
) || 0;
|
|
10572
|
-
spanData.accumulatedOutputTokens += outputTokens;
|
|
10573
|
-
}
|
|
10574
|
-
spanData.currentMessages = [];
|
|
10575
|
-
}
|
|
10576
|
-
spanData.currentMessageId = messageId;
|
|
10577
|
-
spanData.currentMessageStartTime = currentTime;
|
|
10578
|
-
}
|
|
10579
|
-
if (message.type === "assistant" && message.message?.usage) {
|
|
10580
|
-
spanData.currentMessages.push(message);
|
|
10581
|
-
}
|
|
10582
|
-
if (message.type === "result" && message.usage) {
|
|
10583
|
-
const finalUsageMetrics = extractUsageFromMessage(message);
|
|
10584
|
-
if (spanData.currentMessages.length > 0 && finalUsageMetrics.completion_tokens !== void 0) {
|
|
10585
|
-
const lastMessage = spanData.currentMessages[spanData.currentMessages.length - 1];
|
|
10586
|
-
if (lastMessage?.message?.usage) {
|
|
10587
|
-
const adjustedTokens = finalUsageMetrics.completion_tokens - spanData.accumulatedOutputTokens;
|
|
10588
|
-
if (adjustedTokens >= 0) {
|
|
10589
|
-
lastMessage.message.usage.output_tokens = adjustedTokens;
|
|
10590
|
-
}
|
|
10591
|
-
}
|
|
10592
|
-
}
|
|
10593
|
-
const result_metadata = {};
|
|
10594
|
-
if (message.num_turns !== void 0) {
|
|
10595
|
-
result_metadata.num_turns = message.num_turns;
|
|
10596
|
-
}
|
|
10597
|
-
if (message.session_id !== void 0) {
|
|
10598
|
-
result_metadata.session_id = message.session_id;
|
|
10599
|
-
}
|
|
10600
|
-
if (Object.keys(result_metadata).length > 0) {
|
|
10601
|
-
spanData.span.log({
|
|
10602
|
-
metadata: result_metadata
|
|
10603
|
-
});
|
|
10604
|
-
}
|
|
10605
|
-
}
|
|
10606
|
-
},
|
|
10607
|
-
onComplete: async () => {
|
|
10608
|
-
try {
|
|
10609
|
-
const params = event.arguments[0];
|
|
10610
|
-
const prompt = params?.prompt;
|
|
10611
|
-
const options = params?.options ?? {};
|
|
10612
|
-
if (spanData.currentMessages.length > 0) {
|
|
10613
|
-
const finalMessage = await createLLMSpanForMessages(
|
|
10614
|
-
spanData.currentMessages,
|
|
10615
|
-
prompt,
|
|
10616
|
-
spanData.conversationHistory,
|
|
10617
|
-
options,
|
|
10618
|
-
spanData.currentMessageStartTime,
|
|
10619
|
-
await spanData.span.export()
|
|
10620
|
-
);
|
|
10621
|
-
if (finalMessage) {
|
|
10622
|
-
spanData.conversationHistory.push(finalMessage);
|
|
10623
|
-
}
|
|
10624
|
-
}
|
|
10625
|
-
spanData.span.log({
|
|
10626
|
-
output: spanData.conversationHistory.length > 0 ? spanData.conversationHistory[spanData.conversationHistory.length - 1] : void 0
|
|
10627
|
-
});
|
|
10628
|
-
} catch (error) {
|
|
11561
|
+
onChunk: (message) => {
|
|
11562
|
+
maybeTrackToolUseContext(state, message);
|
|
11563
|
+
state.processing = state.processing.then(() => handleStreamMessage(state, message)).catch((error) => {
|
|
10629
11564
|
console.error(
|
|
10630
|
-
"Error
|
|
11565
|
+
"Error processing Claude Agent SDK stream chunk:",
|
|
10631
11566
|
error
|
|
10632
11567
|
);
|
|
10633
|
-
}
|
|
10634
|
-
spanData.span.end();
|
|
10635
|
-
spans.delete(event);
|
|
10636
|
-
}
|
|
11568
|
+
});
|
|
10637
11569
|
},
|
|
10638
|
-
|
|
10639
|
-
|
|
11570
|
+
onComplete: () => state.processing.then(() => finalizeQuerySpan(state)).finally(() => {
|
|
11571
|
+
spans.delete(event);
|
|
11572
|
+
}),
|
|
11573
|
+
onError: (error) => state.processing.then(() => {
|
|
11574
|
+
state.span.log({
|
|
10640
11575
|
error: error.message
|
|
10641
11576
|
});
|
|
10642
|
-
|
|
11577
|
+
}).then(() => finalizeQuerySpan(state)).finally(() => {
|
|
10643
11578
|
spans.delete(event);
|
|
10644
|
-
}
|
|
11579
|
+
})
|
|
10645
11580
|
});
|
|
10646
|
-
|
|
10647
|
-
|
|
10648
|
-
|
|
10649
|
-
|
|
10650
|
-
|
|
10651
|
-
|
|
10652
|
-
|
|
10653
|
-
|
|
10654
|
-
|
|
10655
|
-
);
|
|
10656
|
-
} finally {
|
|
10657
|
-
spanData.span.end();
|
|
10658
|
-
spans.delete(event);
|
|
10659
|
-
}
|
|
11581
|
+
return;
|
|
11582
|
+
}
|
|
11583
|
+
try {
|
|
11584
|
+
state.span.log({ output: eventResult });
|
|
11585
|
+
} catch (error) {
|
|
11586
|
+
console.error("Error extracting output for Claude Agent SDK:", error);
|
|
11587
|
+
} finally {
|
|
11588
|
+
state.span.end();
|
|
11589
|
+
spans.delete(event);
|
|
10660
11590
|
}
|
|
10661
11591
|
},
|
|
10662
11592
|
error: (event) => {
|
|
10663
|
-
const
|
|
10664
|
-
if (!
|
|
11593
|
+
const state = spans.get(event);
|
|
11594
|
+
if (!state || !event.error) {
|
|
10665
11595
|
return;
|
|
10666
11596
|
}
|
|
10667
|
-
|
|
10668
|
-
span.log({
|
|
11597
|
+
state.span.log({
|
|
10669
11598
|
error: event.error.message
|
|
10670
11599
|
});
|
|
10671
|
-
span.end();
|
|
11600
|
+
state.span.end();
|
|
10672
11601
|
spans.delete(event);
|
|
10673
11602
|
}
|
|
10674
11603
|
};
|
|
@@ -10692,6 +11621,18 @@ var googleGenAIChannels = defineChannels("@google/genai", {
|
|
|
10692
11621
|
});
|
|
10693
11622
|
|
|
10694
11623
|
// src/instrumentation/plugins/google-genai-plugin.ts
|
|
11624
|
+
var GOOGLE_GENAI_INTERNAL_CONTEXT = {
|
|
11625
|
+
caller_filename: "<node-internal>",
|
|
11626
|
+
caller_functionname: "<node-internal>",
|
|
11627
|
+
caller_lineno: 0
|
|
11628
|
+
};
|
|
11629
|
+
function createWrapperParityEvent(args) {
|
|
11630
|
+
return {
|
|
11631
|
+
context: GOOGLE_GENAI_INTERNAL_CONTEXT,
|
|
11632
|
+
input: args.input,
|
|
11633
|
+
metadata: args.metadata
|
|
11634
|
+
};
|
|
11635
|
+
}
|
|
10695
11636
|
var GoogleGenAIPlugin = class extends BasePlugin {
|
|
10696
11637
|
onEnable() {
|
|
10697
11638
|
this.subscribeToGoogleGenAIChannels();
|
|
@@ -10700,70 +11641,304 @@ var GoogleGenAIPlugin = class extends BasePlugin {
|
|
|
10700
11641
|
this.unsubscribers = unsubscribeAll(this.unsubscribers);
|
|
10701
11642
|
}
|
|
10702
11643
|
subscribeToGoogleGenAIChannels() {
|
|
10703
|
-
this.
|
|
10704
|
-
|
|
10705
|
-
|
|
10706
|
-
|
|
10707
|
-
|
|
10708
|
-
|
|
10709
|
-
|
|
10710
|
-
|
|
10711
|
-
|
|
10712
|
-
|
|
10713
|
-
|
|
10714
|
-
|
|
10715
|
-
|
|
10716
|
-
|
|
10717
|
-
|
|
10718
|
-
|
|
10719
|
-
|
|
10720
|
-
|
|
10721
|
-
|
|
11644
|
+
this.subscribeToGenerateContentChannel();
|
|
11645
|
+
this.subscribeToGenerateContentStreamChannel();
|
|
11646
|
+
}
|
|
11647
|
+
subscribeToGenerateContentChannel() {
|
|
11648
|
+
const tracingChannel = googleGenAIChannels.generateContent.tracingChannel();
|
|
11649
|
+
const states = /* @__PURE__ */ new WeakMap();
|
|
11650
|
+
const unbindCurrentSpanStore = bindCurrentSpanStoreToStart2(
|
|
11651
|
+
tracingChannel,
|
|
11652
|
+
states,
|
|
11653
|
+
(event) => {
|
|
11654
|
+
const params = event.arguments[0];
|
|
11655
|
+
const input = serializeInput(params);
|
|
11656
|
+
const metadata = extractMetadata(params);
|
|
11657
|
+
const span = startSpan({
|
|
11658
|
+
name: "generate_content",
|
|
11659
|
+
spanAttributes: {
|
|
11660
|
+
type: "llm" /* LLM */
|
|
11661
|
+
},
|
|
11662
|
+
event: createWrapperParityEvent({ input, metadata })
|
|
11663
|
+
});
|
|
11664
|
+
return {
|
|
11665
|
+
span,
|
|
11666
|
+
startTime: getCurrentUnixTimestamp()
|
|
11667
|
+
};
|
|
11668
|
+
}
|
|
10722
11669
|
);
|
|
10723
|
-
|
|
10724
|
-
|
|
10725
|
-
|
|
10726
|
-
|
|
10727
|
-
extractInput: ([params]) => {
|
|
11670
|
+
const handlers = {
|
|
11671
|
+
start: (event) => {
|
|
11672
|
+
ensureSpanState(states, event, () => {
|
|
11673
|
+
const params = event.arguments[0];
|
|
10728
11674
|
const input = serializeInput(params);
|
|
10729
11675
|
const metadata = extractMetadata(params);
|
|
11676
|
+
const span = startSpan({
|
|
11677
|
+
name: "generate_content",
|
|
11678
|
+
spanAttributes: {
|
|
11679
|
+
type: "llm" /* LLM */
|
|
11680
|
+
},
|
|
11681
|
+
event: createWrapperParityEvent({ input, metadata })
|
|
11682
|
+
});
|
|
10730
11683
|
return {
|
|
10731
|
-
|
|
10732
|
-
|
|
11684
|
+
span,
|
|
11685
|
+
startTime: getCurrentUnixTimestamp()
|
|
10733
11686
|
};
|
|
10734
|
-
}
|
|
10735
|
-
|
|
10736
|
-
|
|
10737
|
-
|
|
10738
|
-
|
|
10739
|
-
return
|
|
10740
|
-
},
|
|
10741
|
-
aggregateChunks: (chunks, _result, _endEvent, startTime) => {
|
|
10742
|
-
return aggregateGenerateContentChunks(chunks, startTime);
|
|
11687
|
+
});
|
|
11688
|
+
},
|
|
11689
|
+
asyncEnd: (event) => {
|
|
11690
|
+
const spanState = states.get(event);
|
|
11691
|
+
if (!spanState) {
|
|
11692
|
+
return;
|
|
10743
11693
|
}
|
|
10744
|
-
|
|
10745
|
-
|
|
10746
|
-
|
|
10747
|
-
|
|
10748
|
-
|
|
10749
|
-
|
|
10750
|
-
|
|
10751
|
-
|
|
10752
|
-
|
|
10753
|
-
|
|
10754
|
-
|
|
10755
|
-
|
|
10756
|
-
|
|
10757
|
-
|
|
10758
|
-
|
|
11694
|
+
try {
|
|
11695
|
+
spanState.span.log({
|
|
11696
|
+
metrics: cleanMetrics(
|
|
11697
|
+
extractGenerateContentMetrics(
|
|
11698
|
+
event.result,
|
|
11699
|
+
spanState.startTime
|
|
11700
|
+
)
|
|
11701
|
+
),
|
|
11702
|
+
output: event.result
|
|
11703
|
+
});
|
|
11704
|
+
} finally {
|
|
11705
|
+
spanState.span.end();
|
|
11706
|
+
states.delete(event);
|
|
11707
|
+
}
|
|
11708
|
+
},
|
|
11709
|
+
error: (event) => {
|
|
11710
|
+
logErrorAndEndSpan(states, event);
|
|
10759
11711
|
}
|
|
10760
|
-
|
|
10761
|
-
|
|
11712
|
+
};
|
|
11713
|
+
tracingChannel.subscribe(handlers);
|
|
11714
|
+
this.unsubscribers.push(() => {
|
|
11715
|
+
unbindCurrentSpanStore?.();
|
|
11716
|
+
tracingChannel.unsubscribe(handlers);
|
|
11717
|
+
});
|
|
10762
11718
|
}
|
|
10763
|
-
|
|
10764
|
-
|
|
10765
|
-
|
|
10766
|
-
|
|
11719
|
+
subscribeToGenerateContentStreamChannel() {
|
|
11720
|
+
const tracingChannel = googleGenAIChannels.generateContentStream.tracingChannel();
|
|
11721
|
+
const handlers = {
|
|
11722
|
+
start: (event) => {
|
|
11723
|
+
const streamEvent = event;
|
|
11724
|
+
const params = event.arguments[0];
|
|
11725
|
+
streamEvent.googleGenAIInput = serializeInput(params);
|
|
11726
|
+
streamEvent.googleGenAIMetadata = extractMetadata(params);
|
|
11727
|
+
streamEvent.googleGenAIStartTime = getCurrentUnixTimestamp();
|
|
11728
|
+
},
|
|
11729
|
+
asyncEnd: (event) => {
|
|
11730
|
+
const streamEvent = event;
|
|
11731
|
+
patchGoogleGenAIStreamingResult({
|
|
11732
|
+
input: streamEvent.googleGenAIInput,
|
|
11733
|
+
metadata: streamEvent.googleGenAIMetadata,
|
|
11734
|
+
startTime: streamEvent.googleGenAIStartTime,
|
|
11735
|
+
result: streamEvent.result
|
|
11736
|
+
});
|
|
11737
|
+
},
|
|
11738
|
+
error: () => {
|
|
11739
|
+
}
|
|
11740
|
+
};
|
|
11741
|
+
tracingChannel.subscribe(handlers);
|
|
11742
|
+
this.unsubscribers.push(() => {
|
|
11743
|
+
tracingChannel.unsubscribe(handlers);
|
|
11744
|
+
});
|
|
11745
|
+
}
|
|
11746
|
+
};
|
|
11747
|
+
function ensureSpanState(states, event, create) {
|
|
11748
|
+
const existing = states.get(event);
|
|
11749
|
+
if (existing) {
|
|
11750
|
+
return existing;
|
|
11751
|
+
}
|
|
11752
|
+
const created = create();
|
|
11753
|
+
states.set(event, created);
|
|
11754
|
+
return created;
|
|
11755
|
+
}
|
|
11756
|
+
function bindCurrentSpanStoreToStart2(tracingChannel, states, create) {
|
|
11757
|
+
const state = _internalGetGlobalState();
|
|
11758
|
+
const startChannel = tracingChannel.start;
|
|
11759
|
+
const currentSpanStore = state?.contextManager ? state.contextManager[BRAINTRUST_CURRENT_SPAN_STORE] : void 0;
|
|
11760
|
+
if (!startChannel?.bindStore || !currentSpanStore) {
|
|
11761
|
+
return void 0;
|
|
11762
|
+
}
|
|
11763
|
+
startChannel.bindStore(
|
|
11764
|
+
currentSpanStore,
|
|
11765
|
+
(event) => ensureSpanState(
|
|
11766
|
+
states,
|
|
11767
|
+
event,
|
|
11768
|
+
() => create(event)
|
|
11769
|
+
).span
|
|
11770
|
+
);
|
|
11771
|
+
return () => {
|
|
11772
|
+
startChannel.unbindStore?.(currentSpanStore);
|
|
11773
|
+
};
|
|
11774
|
+
}
|
|
11775
|
+
function logErrorAndEndSpan(states, event) {
|
|
11776
|
+
const spanState = states.get(event);
|
|
11777
|
+
if (!spanState) {
|
|
11778
|
+
return;
|
|
11779
|
+
}
|
|
11780
|
+
spanState.span.log({
|
|
11781
|
+
error: event.error.message
|
|
11782
|
+
});
|
|
11783
|
+
spanState.span.end();
|
|
11784
|
+
states.delete(event);
|
|
11785
|
+
}
|
|
11786
|
+
function patchGoogleGenAIStreamingResult(args) {
|
|
11787
|
+
const { input, metadata, result, startTime } = args;
|
|
11788
|
+
if (!input || !metadata || !result || typeof result !== "object" || typeof result.next !== "function") {
|
|
11789
|
+
return false;
|
|
11790
|
+
}
|
|
11791
|
+
const chunks = [];
|
|
11792
|
+
let firstTokenTime = null;
|
|
11793
|
+
let finalized = false;
|
|
11794
|
+
let span = null;
|
|
11795
|
+
const requestStartTime = startTime ?? getCurrentUnixTimestamp();
|
|
11796
|
+
const ensureSpan = () => {
|
|
11797
|
+
if (!span) {
|
|
11798
|
+
span = startSpan({
|
|
11799
|
+
name: "generate_content_stream",
|
|
11800
|
+
spanAttributes: {
|
|
11801
|
+
type: "llm" /* LLM */
|
|
11802
|
+
},
|
|
11803
|
+
event: {
|
|
11804
|
+
input,
|
|
11805
|
+
metadata
|
|
11806
|
+
}
|
|
11807
|
+
});
|
|
11808
|
+
}
|
|
11809
|
+
return span;
|
|
11810
|
+
};
|
|
11811
|
+
const finalize = (options) => {
|
|
11812
|
+
if (finalized || !span) {
|
|
11813
|
+
return;
|
|
11814
|
+
}
|
|
11815
|
+
finalized = true;
|
|
11816
|
+
if (options.result) {
|
|
11817
|
+
const { end, ...metricsWithoutEnd } = options.result.metrics;
|
|
11818
|
+
span.log({
|
|
11819
|
+
metrics: cleanMetrics(metricsWithoutEnd),
|
|
11820
|
+
output: options.result.aggregated
|
|
11821
|
+
});
|
|
11822
|
+
span.end(typeof end === "number" ? { endTime: end } : void 0);
|
|
11823
|
+
return;
|
|
11824
|
+
}
|
|
11825
|
+
if (options.error !== void 0) {
|
|
11826
|
+
span.log({
|
|
11827
|
+
error: options.error instanceof Error ? options.error.message : String(options.error)
|
|
11828
|
+
});
|
|
11829
|
+
}
|
|
11830
|
+
span.end();
|
|
11831
|
+
};
|
|
11832
|
+
const patchIterator = (iterator) => {
|
|
11833
|
+
if (typeof iterator !== "object" || iterator === null || "__braintrustGoogleGenAIPatched" in iterator) {
|
|
11834
|
+
return iterator;
|
|
11835
|
+
}
|
|
11836
|
+
const iteratorRecord = iterator;
|
|
11837
|
+
const originalNext = typeof iteratorRecord.next === "function" ? iteratorRecord.next.bind(iterator) : void 0;
|
|
11838
|
+
const originalReturn = typeof iteratorRecord.return === "function" ? iteratorRecord.return.bind(iterator) : void 0;
|
|
11839
|
+
const originalThrow = typeof iteratorRecord.throw === "function" ? iteratorRecord.throw.bind(iterator) : void 0;
|
|
11840
|
+
const asyncIteratorMethod = iteratorRecord[Symbol.asyncIterator];
|
|
11841
|
+
const originalAsyncIterator = typeof asyncIteratorMethod === "function" ? asyncIteratorMethod.bind(iterator) : void 0;
|
|
11842
|
+
Object.defineProperty(iteratorRecord, "__braintrustGoogleGenAIPatched", {
|
|
11843
|
+
configurable: true,
|
|
11844
|
+
enumerable: false,
|
|
11845
|
+
value: true,
|
|
11846
|
+
writable: false
|
|
11847
|
+
});
|
|
11848
|
+
if (originalNext) {
|
|
11849
|
+
iteratorRecord.next = async (...nextArgs) => {
|
|
11850
|
+
ensureSpan();
|
|
11851
|
+
try {
|
|
11852
|
+
const nextResult = await originalNext(
|
|
11853
|
+
...nextArgs
|
|
11854
|
+
);
|
|
11855
|
+
if (!nextResult.done && nextResult.value) {
|
|
11856
|
+
if (firstTokenTime === null) {
|
|
11857
|
+
firstTokenTime = getCurrentUnixTimestamp();
|
|
11858
|
+
}
|
|
11859
|
+
chunks.push(nextResult.value);
|
|
11860
|
+
}
|
|
11861
|
+
if (nextResult.done) {
|
|
11862
|
+
finalize({
|
|
11863
|
+
result: aggregateGenerateContentChunks(
|
|
11864
|
+
chunks,
|
|
11865
|
+
requestStartTime,
|
|
11866
|
+
firstTokenTime
|
|
11867
|
+
)
|
|
11868
|
+
});
|
|
11869
|
+
}
|
|
11870
|
+
return nextResult;
|
|
11871
|
+
} catch (error) {
|
|
11872
|
+
finalize({ error });
|
|
11873
|
+
throw error;
|
|
11874
|
+
}
|
|
11875
|
+
};
|
|
11876
|
+
}
|
|
11877
|
+
if (originalReturn) {
|
|
11878
|
+
iteratorRecord.return = async (...returnArgs) => {
|
|
11879
|
+
ensureSpan();
|
|
11880
|
+
try {
|
|
11881
|
+
return await originalReturn(
|
|
11882
|
+
...returnArgs
|
|
11883
|
+
);
|
|
11884
|
+
} finally {
|
|
11885
|
+
if (chunks.length > 0) {
|
|
11886
|
+
finalize({
|
|
11887
|
+
result: aggregateGenerateContentChunks(
|
|
11888
|
+
chunks,
|
|
11889
|
+
requestStartTime,
|
|
11890
|
+
firstTokenTime
|
|
11891
|
+
)
|
|
11892
|
+
});
|
|
11893
|
+
} else {
|
|
11894
|
+
finalize({});
|
|
11895
|
+
}
|
|
11896
|
+
}
|
|
11897
|
+
};
|
|
11898
|
+
}
|
|
11899
|
+
if (originalThrow) {
|
|
11900
|
+
iteratorRecord.throw = async (...throwArgs) => {
|
|
11901
|
+
ensureSpan();
|
|
11902
|
+
try {
|
|
11903
|
+
return await originalThrow(
|
|
11904
|
+
...throwArgs
|
|
11905
|
+
);
|
|
11906
|
+
} catch (error) {
|
|
11907
|
+
finalize({ error });
|
|
11908
|
+
throw error;
|
|
11909
|
+
}
|
|
11910
|
+
};
|
|
11911
|
+
}
|
|
11912
|
+
iteratorRecord[Symbol.asyncIterator] = () => {
|
|
11913
|
+
const asyncIterator = originalAsyncIterator ? originalAsyncIterator() : iterator;
|
|
11914
|
+
return patchIterator(asyncIterator);
|
|
11915
|
+
};
|
|
11916
|
+
return iterator;
|
|
11917
|
+
};
|
|
11918
|
+
patchIterator(result);
|
|
11919
|
+
return true;
|
|
11920
|
+
}
|
|
11921
|
+
function serializeInput(params) {
|
|
11922
|
+
const input = {
|
|
11923
|
+
model: params.model,
|
|
11924
|
+
contents: serializeContents(params.contents)
|
|
11925
|
+
};
|
|
11926
|
+
if (params.config) {
|
|
11927
|
+
const config = tryToDict(params.config);
|
|
11928
|
+
if (config) {
|
|
11929
|
+
const filteredConfig = {};
|
|
11930
|
+
Object.keys(config).forEach((key) => {
|
|
11931
|
+
if (key !== "tools") {
|
|
11932
|
+
filteredConfig[key] = config[key];
|
|
11933
|
+
}
|
|
11934
|
+
});
|
|
11935
|
+
input.config = filteredConfig;
|
|
11936
|
+
}
|
|
11937
|
+
}
|
|
11938
|
+
return input;
|
|
11939
|
+
}
|
|
11940
|
+
function serializeContents(contents) {
|
|
11941
|
+
if (contents === null || contents === void 0) {
|
|
10767
11942
|
return null;
|
|
10768
11943
|
}
|
|
10769
11944
|
if (Array.isArray(contents)) {
|
|
@@ -10844,12 +12019,18 @@ function extractMetadata(params) {
|
|
|
10844
12019
|
});
|
|
10845
12020
|
}
|
|
10846
12021
|
}
|
|
12022
|
+
const tools = serializeTools(params);
|
|
12023
|
+
if (tools) {
|
|
12024
|
+
metadata.tools = tools;
|
|
12025
|
+
}
|
|
10847
12026
|
return metadata;
|
|
10848
12027
|
}
|
|
10849
12028
|
function extractGenerateContentMetrics(response, startTime) {
|
|
10850
12029
|
const metrics = {};
|
|
10851
|
-
if (startTime) {
|
|
12030
|
+
if (startTime !== void 0) {
|
|
10852
12031
|
const end = getCurrentUnixTimestamp();
|
|
12032
|
+
metrics.start = startTime;
|
|
12033
|
+
metrics.end = end;
|
|
10853
12034
|
metrics.duration = end - startTime;
|
|
10854
12035
|
}
|
|
10855
12036
|
if (response?.usageMetadata) {
|
|
@@ -10874,19 +12055,18 @@ function populateUsageMetrics(metrics, usage) {
|
|
|
10874
12055
|
metrics.completion_reasoning_tokens = usage.thoughtsTokenCount;
|
|
10875
12056
|
}
|
|
10876
12057
|
}
|
|
10877
|
-
function aggregateGenerateContentChunks(chunks, startTime) {
|
|
10878
|
-
const
|
|
10879
|
-
|
|
10880
|
-
|
|
10881
|
-
|
|
10882
|
-
|
|
10883
|
-
|
|
10884
|
-
if (
|
|
10885
|
-
firstTokenTime = getCurrentUnixTimestamp();
|
|
12058
|
+
function aggregateGenerateContentChunks(chunks, startTime, firstTokenTime) {
|
|
12059
|
+
const end = getCurrentUnixTimestamp();
|
|
12060
|
+
const metrics = {
|
|
12061
|
+
start: startTime,
|
|
12062
|
+
end,
|
|
12063
|
+
duration: end - startTime
|
|
12064
|
+
};
|
|
12065
|
+
if (firstTokenTime !== null) {
|
|
10886
12066
|
metrics.time_to_first_token = firstTokenTime - startTime;
|
|
10887
12067
|
}
|
|
10888
12068
|
if (chunks.length === 0) {
|
|
10889
|
-
return {
|
|
12069
|
+
return { aggregated: {}, metrics };
|
|
10890
12070
|
}
|
|
10891
12071
|
let text = "";
|
|
10892
12072
|
let thoughtText = "";
|
|
@@ -10922,7 +12102,7 @@ function aggregateGenerateContentChunks(chunks, startTime) {
|
|
|
10922
12102
|
}
|
|
10923
12103
|
}
|
|
10924
12104
|
}
|
|
10925
|
-
const
|
|
12105
|
+
const aggregated = {};
|
|
10926
12106
|
const parts = [];
|
|
10927
12107
|
if (thoughtText) {
|
|
10928
12108
|
parts.push({ text: thoughtText, thought: true });
|
|
@@ -10948,16 +12128,25 @@ function aggregateGenerateContentChunks(chunks, startTime) {
|
|
|
10948
12128
|
}
|
|
10949
12129
|
candidates.push(candidateDict);
|
|
10950
12130
|
}
|
|
10951
|
-
|
|
12131
|
+
aggregated.candidates = candidates;
|
|
10952
12132
|
}
|
|
10953
12133
|
if (usageMetadata) {
|
|
10954
|
-
|
|
12134
|
+
aggregated.usageMetadata = usageMetadata;
|
|
10955
12135
|
populateUsageMetrics(metrics, usageMetadata);
|
|
10956
12136
|
}
|
|
10957
12137
|
if (text) {
|
|
10958
|
-
|
|
12138
|
+
aggregated.text = text;
|
|
12139
|
+
}
|
|
12140
|
+
return { aggregated, metrics };
|
|
12141
|
+
}
|
|
12142
|
+
function cleanMetrics(metrics) {
|
|
12143
|
+
const cleaned = {};
|
|
12144
|
+
for (const [key, value] of Object.entries(metrics)) {
|
|
12145
|
+
if (value !== null && value !== void 0) {
|
|
12146
|
+
cleaned[key] = value;
|
|
12147
|
+
}
|
|
10959
12148
|
}
|
|
10960
|
-
return
|
|
12149
|
+
return cleaned;
|
|
10961
12150
|
}
|
|
10962
12151
|
function tryToDict(obj) {
|
|
10963
12152
|
if (obj === null || obj === void 0) {
|
|
@@ -10991,242 +12180,442 @@ var openRouterChannels = defineChannels("@openrouter/sdk", {
|
|
|
10991
12180
|
channelName: "callModel",
|
|
10992
12181
|
kind: "sync-stream"
|
|
10993
12182
|
}),
|
|
12183
|
+
callModelTurn: channel({
|
|
12184
|
+
channelName: "callModel.turn",
|
|
12185
|
+
kind: "async"
|
|
12186
|
+
}),
|
|
10994
12187
|
toolExecute: channel({
|
|
10995
12188
|
channelName: "tool.execute",
|
|
10996
12189
|
kind: "async"
|
|
10997
12190
|
})
|
|
10998
12191
|
});
|
|
10999
12192
|
|
|
11000
|
-
// src/openrouter-
|
|
11001
|
-
var
|
|
11002
|
-
|
|
11003
|
-
|
|
11004
|
-
completionTokens: "completion_tokens",
|
|
11005
|
-
outputTokens: "completion_tokens",
|
|
11006
|
-
totalTokens: "tokens",
|
|
11007
|
-
prompt_tokens: "prompt_tokens",
|
|
11008
|
-
input_tokens: "prompt_tokens",
|
|
11009
|
-
completion_tokens: "completion_tokens",
|
|
11010
|
-
output_tokens: "completion_tokens",
|
|
11011
|
-
total_tokens: "tokens"
|
|
11012
|
-
};
|
|
11013
|
-
var TOKEN_DETAIL_PREFIX_MAP = {
|
|
11014
|
-
promptTokensDetails: "prompt",
|
|
11015
|
-
inputTokensDetails: "prompt",
|
|
11016
|
-
completionTokensDetails: "completion",
|
|
11017
|
-
outputTokensDetails: "completion",
|
|
11018
|
-
costDetails: "cost",
|
|
11019
|
-
prompt_tokens_details: "prompt",
|
|
11020
|
-
input_tokens_details: "prompt",
|
|
11021
|
-
completion_tokens_details: "completion",
|
|
11022
|
-
output_tokens_details: "completion",
|
|
11023
|
-
cost_details: "cost"
|
|
11024
|
-
};
|
|
11025
|
-
function camelToSnake(value) {
|
|
11026
|
-
return value.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`);
|
|
11027
|
-
}
|
|
11028
|
-
function parseOpenRouterMetricsFromUsage(usage) {
|
|
11029
|
-
if (!isObject(usage)) {
|
|
11030
|
-
return {};
|
|
11031
|
-
}
|
|
11032
|
-
const metrics = {};
|
|
11033
|
-
for (const [name, value] of Object.entries(usage)) {
|
|
11034
|
-
if (typeof value === "number") {
|
|
11035
|
-
metrics[TOKEN_NAME_MAP2[name] || camelToSnake(name)] = value;
|
|
11036
|
-
continue;
|
|
11037
|
-
}
|
|
11038
|
-
if (!isObject(value)) {
|
|
11039
|
-
continue;
|
|
11040
|
-
}
|
|
11041
|
-
const prefix = TOKEN_DETAIL_PREFIX_MAP[name];
|
|
11042
|
-
if (!prefix) {
|
|
11043
|
-
continue;
|
|
11044
|
-
}
|
|
11045
|
-
for (const [nestedName, nestedValue] of Object.entries(value)) {
|
|
11046
|
-
if (typeof nestedValue !== "number") {
|
|
11047
|
-
continue;
|
|
11048
|
-
}
|
|
11049
|
-
metrics[`${prefix}_${camelToSnake(nestedName)}`] = nestedValue;
|
|
11050
|
-
}
|
|
11051
|
-
}
|
|
11052
|
-
return metrics;
|
|
11053
|
-
}
|
|
11054
|
-
function extractOpenRouterUsageMetadata(usage) {
|
|
11055
|
-
if (!isObject(usage)) {
|
|
11056
|
-
return void 0;
|
|
11057
|
-
}
|
|
11058
|
-
const metadata = {};
|
|
11059
|
-
if (typeof usage.isByok === "boolean") {
|
|
11060
|
-
metadata.is_byok = usage.isByok;
|
|
11061
|
-
} else if (typeof usage.is_byok === "boolean") {
|
|
11062
|
-
metadata.is_byok = usage.is_byok;
|
|
11063
|
-
}
|
|
11064
|
-
return Object.keys(metadata).length > 0 ? metadata : void 0;
|
|
11065
|
-
}
|
|
11066
|
-
|
|
11067
|
-
// src/openrouter-logging.ts
|
|
11068
|
-
var OMITTED_OPENROUTER_KEYS = /* @__PURE__ */ new Set([
|
|
11069
|
-
"execute",
|
|
11070
|
-
"render",
|
|
11071
|
-
"nextTurnParams",
|
|
11072
|
-
"requireApproval"
|
|
11073
|
-
]);
|
|
11074
|
-
function parseOpenRouterModelString(model) {
|
|
11075
|
-
if (typeof model !== "string") {
|
|
11076
|
-
return { model };
|
|
11077
|
-
}
|
|
11078
|
-
const slashIndex = model.indexOf("/");
|
|
11079
|
-
if (slashIndex > 0 && slashIndex < model.length - 1) {
|
|
11080
|
-
return {
|
|
11081
|
-
provider: model.substring(0, slashIndex),
|
|
11082
|
-
model: model.substring(slashIndex + 1)
|
|
11083
|
-
};
|
|
11084
|
-
}
|
|
11085
|
-
return { model };
|
|
11086
|
-
}
|
|
11087
|
-
function isZodSchema2(value) {
|
|
11088
|
-
return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
|
|
11089
|
-
}
|
|
11090
|
-
function serializeZodSchema2(schema) {
|
|
11091
|
-
try {
|
|
11092
|
-
return zodToJsonSchema(schema);
|
|
11093
|
-
} catch {
|
|
11094
|
-
return {
|
|
11095
|
-
type: "object",
|
|
11096
|
-
description: "Zod schema (conversion failed)"
|
|
11097
|
-
};
|
|
11098
|
-
}
|
|
11099
|
-
}
|
|
11100
|
-
function serializeOpenRouterTool(tool) {
|
|
11101
|
-
if (!isObject(tool)) {
|
|
11102
|
-
return tool;
|
|
11103
|
-
}
|
|
11104
|
-
const serialized = {};
|
|
11105
|
-
for (const [key, value] of Object.entries(tool)) {
|
|
11106
|
-
if (OMITTED_OPENROUTER_KEYS.has(key)) {
|
|
11107
|
-
continue;
|
|
11108
|
-
}
|
|
11109
|
-
if (key === "function" && isObject(value)) {
|
|
11110
|
-
serialized.function = sanitizeOpenRouterLoggedValue(value);
|
|
11111
|
-
continue;
|
|
11112
|
-
}
|
|
11113
|
-
serialized[key] = sanitizeOpenRouterLoggedValue(value);
|
|
11114
|
-
}
|
|
11115
|
-
return serialized;
|
|
11116
|
-
}
|
|
11117
|
-
function serializeOpenRouterToolsForLogging(tools) {
|
|
11118
|
-
if (!Array.isArray(tools)) {
|
|
11119
|
-
return void 0;
|
|
11120
|
-
}
|
|
11121
|
-
return tools.map((tool) => serializeOpenRouterTool(tool));
|
|
11122
|
-
}
|
|
11123
|
-
function sanitizeOpenRouterLoggedValue(value) {
|
|
11124
|
-
if (isZodSchema2(value)) {
|
|
11125
|
-
return serializeZodSchema2(value);
|
|
12193
|
+
// src/instrumentation/plugins/openrouter-plugin.ts
|
|
12194
|
+
var OpenRouterPlugin = class extends BasePlugin {
|
|
12195
|
+
onEnable() {
|
|
12196
|
+
this.subscribeToOpenRouterChannels();
|
|
11126
12197
|
}
|
|
11127
|
-
|
|
11128
|
-
|
|
12198
|
+
onDisable() {
|
|
12199
|
+
this.unsubscribers = unsubscribeAll(this.unsubscribers);
|
|
11129
12200
|
}
|
|
11130
|
-
|
|
11131
|
-
|
|
12201
|
+
subscribeToOpenRouterChannels() {
|
|
12202
|
+
this.unsubscribers.push(
|
|
12203
|
+
traceStreamingChannel(openRouterChannels.chatSend, {
|
|
12204
|
+
name: "openrouter.chat.send",
|
|
12205
|
+
type: "llm" /* LLM */,
|
|
12206
|
+
extractInput: (args) => {
|
|
12207
|
+
const request = getOpenRouterRequestArg(args);
|
|
12208
|
+
const chatGenerationParams = isObject(request?.chatGenerationParams) ? request.chatGenerationParams : {};
|
|
12209
|
+
const httpReferer = request?.httpReferer;
|
|
12210
|
+
const xTitle = request?.xTitle;
|
|
12211
|
+
const { messages, ...metadata } = chatGenerationParams;
|
|
12212
|
+
return {
|
|
12213
|
+
input: messages,
|
|
12214
|
+
metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
|
|
12215
|
+
};
|
|
12216
|
+
},
|
|
12217
|
+
extractOutput: (result) => {
|
|
12218
|
+
return isObject(result) ? result.choices : void 0;
|
|
12219
|
+
},
|
|
12220
|
+
extractMetrics: (result, startTime) => {
|
|
12221
|
+
const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
|
|
12222
|
+
if (startTime) {
|
|
12223
|
+
metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
|
|
12224
|
+
}
|
|
12225
|
+
return metrics;
|
|
12226
|
+
},
|
|
12227
|
+
aggregateChunks: aggregateOpenRouterChatChunks
|
|
12228
|
+
})
|
|
12229
|
+
);
|
|
12230
|
+
this.unsubscribers.push(
|
|
12231
|
+
traceAsyncChannel(openRouterChannels.embeddingsGenerate, {
|
|
12232
|
+
name: "openrouter.embeddings.generate",
|
|
12233
|
+
type: "llm" /* LLM */,
|
|
12234
|
+
extractInput: (args) => {
|
|
12235
|
+
const request = getOpenRouterRequestArg(args);
|
|
12236
|
+
const requestBody = isObject(request?.requestBody) ? request.requestBody : {};
|
|
12237
|
+
const httpReferer = request?.httpReferer;
|
|
12238
|
+
const xTitle = request?.xTitle;
|
|
12239
|
+
const { input, ...metadata } = requestBody;
|
|
12240
|
+
return {
|
|
12241
|
+
input,
|
|
12242
|
+
metadata: buildOpenRouterEmbeddingMetadata(
|
|
12243
|
+
metadata,
|
|
12244
|
+
httpReferer,
|
|
12245
|
+
xTitle
|
|
12246
|
+
)
|
|
12247
|
+
};
|
|
12248
|
+
},
|
|
12249
|
+
extractOutput: (result) => {
|
|
12250
|
+
if (!isObject(result)) {
|
|
12251
|
+
return void 0;
|
|
12252
|
+
}
|
|
12253
|
+
const embedding = result.data?.[0]?.embedding;
|
|
12254
|
+
return Array.isArray(embedding) ? { embedding_length: embedding.length } : void 0;
|
|
12255
|
+
},
|
|
12256
|
+
extractMetadata: (result) => {
|
|
12257
|
+
if (!isObject(result)) {
|
|
12258
|
+
return void 0;
|
|
12259
|
+
}
|
|
12260
|
+
return extractOpenRouterResponseMetadata(result);
|
|
12261
|
+
},
|
|
12262
|
+
extractMetrics: (result) => {
|
|
12263
|
+
return isObject(result) ? parseOpenRouterMetricsFromUsage(result.usage) : {};
|
|
12264
|
+
}
|
|
12265
|
+
})
|
|
12266
|
+
);
|
|
12267
|
+
this.unsubscribers.push(
|
|
12268
|
+
traceStreamingChannel(openRouterChannels.betaResponsesSend, {
|
|
12269
|
+
name: "openrouter.beta.responses.send",
|
|
12270
|
+
type: "llm" /* LLM */,
|
|
12271
|
+
extractInput: (args) => {
|
|
12272
|
+
const request = getOpenRouterRequestArg(args);
|
|
12273
|
+
const openResponsesRequest = isObject(request?.openResponsesRequest) ? request.openResponsesRequest : {};
|
|
12274
|
+
const httpReferer = request?.httpReferer;
|
|
12275
|
+
const xTitle = request?.xTitle;
|
|
12276
|
+
const { input, ...metadata } = openResponsesRequest;
|
|
12277
|
+
return {
|
|
12278
|
+
input,
|
|
12279
|
+
metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
|
|
12280
|
+
};
|
|
12281
|
+
},
|
|
12282
|
+
extractOutput: (result) => extractOpenRouterResponseOutput(result),
|
|
12283
|
+
extractMetadata: (result) => extractOpenRouterResponseMetadata(result),
|
|
12284
|
+
extractMetrics: (result, startTime) => {
|
|
12285
|
+
const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
|
|
12286
|
+
if (startTime) {
|
|
12287
|
+
metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
|
|
12288
|
+
}
|
|
12289
|
+
return metrics;
|
|
12290
|
+
},
|
|
12291
|
+
aggregateChunks: aggregateOpenRouterResponseStreamEvents
|
|
12292
|
+
})
|
|
12293
|
+
);
|
|
12294
|
+
this.unsubscribers.push(
|
|
12295
|
+
traceSyncStreamChannel(openRouterChannels.callModel, {
|
|
12296
|
+
name: "openrouter.callModel",
|
|
12297
|
+
type: "llm" /* LLM */,
|
|
12298
|
+
extractInput: (args) => {
|
|
12299
|
+
const request = getOpenRouterCallModelRequestArg(args);
|
|
12300
|
+
return {
|
|
12301
|
+
input: request ? extractOpenRouterCallModelInput(request) : void 0,
|
|
12302
|
+
metadata: request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" }
|
|
12303
|
+
};
|
|
12304
|
+
},
|
|
12305
|
+
patchResult: ({ endEvent, result, span }) => {
|
|
12306
|
+
return patchOpenRouterCallModelResult({
|
|
12307
|
+
request: getOpenRouterCallModelRequestArg(endEvent.arguments),
|
|
12308
|
+
result,
|
|
12309
|
+
span
|
|
12310
|
+
});
|
|
12311
|
+
}
|
|
12312
|
+
})
|
|
12313
|
+
);
|
|
12314
|
+
this.unsubscribers.push(
|
|
12315
|
+
traceAsyncChannel(openRouterChannels.callModelTurn, {
|
|
12316
|
+
name: "openrouter.beta.responses.send",
|
|
12317
|
+
type: "llm" /* LLM */,
|
|
12318
|
+
extractInput: (args, event) => {
|
|
12319
|
+
const request = getOpenRouterCallModelRequestArg(args);
|
|
12320
|
+
const metadata = request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" };
|
|
12321
|
+
if (isObject(metadata) && "tools" in metadata) {
|
|
12322
|
+
delete metadata.tools;
|
|
12323
|
+
}
|
|
12324
|
+
return {
|
|
12325
|
+
input: request ? extractOpenRouterCallModelInput(request) : void 0,
|
|
12326
|
+
metadata: {
|
|
12327
|
+
...metadata,
|
|
12328
|
+
step: event.step,
|
|
12329
|
+
step_type: event.stepType
|
|
12330
|
+
}
|
|
12331
|
+
};
|
|
12332
|
+
},
|
|
12333
|
+
extractOutput: (result) => extractOpenRouterResponseOutput(result),
|
|
12334
|
+
extractMetadata: (result, event) => {
|
|
12335
|
+
if (!isObject(result)) {
|
|
12336
|
+
return {
|
|
12337
|
+
step: event?.step,
|
|
12338
|
+
step_type: event?.stepType
|
|
12339
|
+
};
|
|
12340
|
+
}
|
|
12341
|
+
return {
|
|
12342
|
+
...extractOpenRouterResponseMetadata(result) || {},
|
|
12343
|
+
...event?.step !== void 0 ? { step: event.step } : {},
|
|
12344
|
+
...event?.stepType ? { step_type: event.stepType } : {}
|
|
12345
|
+
};
|
|
12346
|
+
},
|
|
12347
|
+
extractMetrics: (result) => isObject(result) ? parseOpenRouterMetricsFromUsage(result.usage) : {}
|
|
12348
|
+
})
|
|
12349
|
+
);
|
|
12350
|
+
this.unsubscribers.push(
|
|
12351
|
+
traceStreamingChannel(openRouterChannels.toolExecute, {
|
|
12352
|
+
name: "openrouter.tool",
|
|
12353
|
+
type: "tool" /* TOOL */,
|
|
12354
|
+
extractInput: (args, event) => ({
|
|
12355
|
+
input: args[0],
|
|
12356
|
+
metadata: {
|
|
12357
|
+
provider: "openrouter",
|
|
12358
|
+
tool_name: event.toolName,
|
|
12359
|
+
...event.toolCallId ? { tool_call_id: event.toolCallId } : {}
|
|
12360
|
+
}
|
|
12361
|
+
}),
|
|
12362
|
+
extractOutput: (result) => result,
|
|
12363
|
+
extractMetrics: () => ({}),
|
|
12364
|
+
aggregateChunks: (chunks) => ({
|
|
12365
|
+
output: chunks.length > 0 ? chunks[chunks.length - 1] : void 0,
|
|
12366
|
+
metrics: {}
|
|
12367
|
+
})
|
|
12368
|
+
})
|
|
12369
|
+
);
|
|
12370
|
+
const callModelChannel = openRouterChannels.callModel.tracingChannel();
|
|
12371
|
+
const callModelHandlers = {
|
|
12372
|
+
start: (event) => {
|
|
12373
|
+
const request = getOpenRouterCallModelRequestArg(event.arguments);
|
|
12374
|
+
if (!request) {
|
|
12375
|
+
return;
|
|
12376
|
+
}
|
|
12377
|
+
patchOpenRouterCallModelRequestTools(request);
|
|
12378
|
+
}
|
|
12379
|
+
};
|
|
12380
|
+
callModelChannel.subscribe(callModelHandlers);
|
|
12381
|
+
this.unsubscribers.push(() => {
|
|
12382
|
+
callModelChannel.unsubscribe(callModelHandlers);
|
|
12383
|
+
});
|
|
11132
12384
|
}
|
|
11133
|
-
|
|
11134
|
-
|
|
12385
|
+
};
|
|
12386
|
+
function normalizeArgs(args) {
|
|
12387
|
+
if (Array.isArray(args)) {
|
|
12388
|
+
return args;
|
|
11135
12389
|
}
|
|
11136
|
-
|
|
11137
|
-
|
|
11138
|
-
if (OMITTED_OPENROUTER_KEYS.has(key)) {
|
|
11139
|
-
continue;
|
|
11140
|
-
}
|
|
11141
|
-
if (key === "tools" && Array.isArray(entry)) {
|
|
11142
|
-
sanitized.tools = serializeOpenRouterToolsForLogging(entry);
|
|
11143
|
-
continue;
|
|
11144
|
-
}
|
|
11145
|
-
sanitized[key] = sanitizeOpenRouterLoggedValue(entry);
|
|
12390
|
+
if (isArrayLike(args)) {
|
|
12391
|
+
return Array.from(args);
|
|
11146
12392
|
}
|
|
11147
|
-
return
|
|
11148
|
-
}
|
|
11149
|
-
function buildOpenRouterMetadata(metadata, httpReferer, xTitle) {
|
|
11150
|
-
const sanitized = sanitizeOpenRouterLoggedValue(metadata);
|
|
11151
|
-
const metadataRecord = isObject(sanitized) ? sanitized : {};
|
|
11152
|
-
const { model, provider: providerRouting, ...rest } = metadataRecord;
|
|
11153
|
-
const normalizedModel = parseOpenRouterModelString(model);
|
|
11154
|
-
return {
|
|
11155
|
-
...rest,
|
|
11156
|
-
...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
|
|
11157
|
-
...providerRouting !== void 0 ? { providerRouting } : {},
|
|
11158
|
-
...httpReferer !== void 0 ? { httpReferer } : {},
|
|
11159
|
-
...xTitle !== void 0 ? { xTitle } : {},
|
|
11160
|
-
provider: normalizedModel.provider || "openrouter"
|
|
11161
|
-
};
|
|
11162
|
-
}
|
|
11163
|
-
function buildOpenRouterEmbeddingMetadata(metadata, httpReferer, xTitle) {
|
|
11164
|
-
const normalized = buildOpenRouterMetadata(metadata, httpReferer, xTitle);
|
|
11165
|
-
return typeof normalized.model === "string" ? {
|
|
11166
|
-
...normalized,
|
|
11167
|
-
embedding_model: normalized.model
|
|
11168
|
-
} : normalized;
|
|
11169
|
-
}
|
|
11170
|
-
function extractOpenRouterCallModelInput(request) {
|
|
11171
|
-
return isObject(request) && "input" in request ? sanitizeOpenRouterLoggedValue(request.input) : void 0;
|
|
12393
|
+
return [args];
|
|
11172
12394
|
}
|
|
11173
|
-
function
|
|
11174
|
-
|
|
11175
|
-
return { provider: "openrouter" };
|
|
11176
|
-
}
|
|
11177
|
-
const { input: _input, ...metadata } = request;
|
|
11178
|
-
return buildOpenRouterMetadata(metadata, void 0, void 0);
|
|
12395
|
+
function isArrayLike(value) {
|
|
12396
|
+
return isObject(value) && "length" in value && typeof value.length === "number" && Number.isInteger(value.length) && value.length >= 0;
|
|
11179
12397
|
}
|
|
11180
|
-
function
|
|
11181
|
-
|
|
11182
|
-
|
|
12398
|
+
function getOpenRouterRequestArg(args) {
|
|
12399
|
+
const normalizedArgs = normalizeArgs(args);
|
|
12400
|
+
const keyedCandidate = normalizedArgs.find(
|
|
12401
|
+
(arg) => isObject(arg) && ("chatGenerationParams" in arg || "requestBody" in arg || "openResponsesRequest" in arg)
|
|
12402
|
+
);
|
|
12403
|
+
if (isObject(keyedCandidate)) {
|
|
12404
|
+
return keyedCandidate;
|
|
11183
12405
|
}
|
|
11184
|
-
const
|
|
11185
|
-
|
|
11186
|
-
const metadataRecord = isObject(sanitized) ? sanitized : {};
|
|
11187
|
-
const { model, provider, ...rest } = metadataRecord;
|
|
11188
|
-
const normalizedModel = parseOpenRouterModelString(model);
|
|
11189
|
-
const normalizedProvider = (typeof provider === "string" ? provider : void 0) || normalizedModel.provider;
|
|
11190
|
-
const usageMetadata = extractOpenRouterUsageMetadata(usage);
|
|
11191
|
-
const combined = {
|
|
11192
|
-
...rest,
|
|
11193
|
-
...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
|
|
11194
|
-
...usageMetadata || {},
|
|
11195
|
-
...normalizedProvider !== void 0 ? { provider: normalizedProvider } : {}
|
|
11196
|
-
};
|
|
11197
|
-
return Object.keys(combined).length > 0 ? combined : void 0;
|
|
12406
|
+
const firstObjectArg = normalizedArgs.find((arg) => isObject(arg));
|
|
12407
|
+
return isObject(firstObjectArg) ? firstObjectArg : void 0;
|
|
11198
12408
|
}
|
|
11199
|
-
function
|
|
11200
|
-
|
|
11201
|
-
|
|
11202
|
-
}
|
|
11203
|
-
if (fallbackOutput !== void 0) {
|
|
11204
|
-
return sanitizeOpenRouterLoggedValue(fallbackOutput);
|
|
11205
|
-
}
|
|
11206
|
-
return void 0;
|
|
12409
|
+
function getOpenRouterCallModelRequestArg(args) {
|
|
12410
|
+
const firstObjectArg = normalizeArgs(args).find((arg) => isObject(arg));
|
|
12411
|
+
return isObject(firstObjectArg) ? firstObjectArg : void 0;
|
|
11207
12412
|
}
|
|
11208
|
-
|
|
11209
|
-
|
|
11210
|
-
|
|
11211
|
-
|
|
11212
|
-
"
|
|
11213
|
-
|
|
11214
|
-
|
|
11215
|
-
"
|
|
11216
|
-
"
|
|
11217
|
-
"
|
|
11218
|
-
"
|
|
11219
|
-
|
|
11220
|
-
|
|
11221
|
-
"
|
|
11222
|
-
|
|
11223
|
-
|
|
11224
|
-
"
|
|
11225
|
-
"
|
|
11226
|
-
"
|
|
11227
|
-
"
|
|
11228
|
-
"
|
|
11229
|
-
|
|
12413
|
+
var TOKEN_NAME_MAP2 = {
|
|
12414
|
+
promptTokens: "prompt_tokens",
|
|
12415
|
+
inputTokens: "prompt_tokens",
|
|
12416
|
+
completionTokens: "completion_tokens",
|
|
12417
|
+
outputTokens: "completion_tokens",
|
|
12418
|
+
totalTokens: "tokens",
|
|
12419
|
+
prompt_tokens: "prompt_tokens",
|
|
12420
|
+
input_tokens: "prompt_tokens",
|
|
12421
|
+
completion_tokens: "completion_tokens",
|
|
12422
|
+
output_tokens: "completion_tokens",
|
|
12423
|
+
total_tokens: "tokens"
|
|
12424
|
+
};
|
|
12425
|
+
var TOKEN_DETAIL_PREFIX_MAP = {
|
|
12426
|
+
promptTokensDetails: "prompt",
|
|
12427
|
+
inputTokensDetails: "prompt",
|
|
12428
|
+
completionTokensDetails: "completion",
|
|
12429
|
+
outputTokensDetails: "completion",
|
|
12430
|
+
costDetails: "cost",
|
|
12431
|
+
prompt_tokens_details: "prompt",
|
|
12432
|
+
input_tokens_details: "prompt",
|
|
12433
|
+
completion_tokens_details: "completion",
|
|
12434
|
+
output_tokens_details: "completion",
|
|
12435
|
+
cost_details: "cost"
|
|
12436
|
+
};
|
|
12437
|
+
function camelToSnake(value) {
|
|
12438
|
+
return value.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`);
|
|
12439
|
+
}
|
|
12440
|
+
function parseOpenRouterMetricsFromUsage(usage) {
|
|
12441
|
+
if (!isObject(usage)) {
|
|
12442
|
+
return {};
|
|
12443
|
+
}
|
|
12444
|
+
const metrics = {};
|
|
12445
|
+
for (const [name, value] of Object.entries(usage)) {
|
|
12446
|
+
if (typeof value === "number") {
|
|
12447
|
+
metrics[TOKEN_NAME_MAP2[name] || camelToSnake(name)] = value;
|
|
12448
|
+
continue;
|
|
12449
|
+
}
|
|
12450
|
+
if (!isObject(value)) {
|
|
12451
|
+
continue;
|
|
12452
|
+
}
|
|
12453
|
+
const prefix = TOKEN_DETAIL_PREFIX_MAP[name];
|
|
12454
|
+
if (!prefix) {
|
|
12455
|
+
continue;
|
|
12456
|
+
}
|
|
12457
|
+
for (const [nestedName, nestedValue] of Object.entries(value)) {
|
|
12458
|
+
if (typeof nestedValue !== "number") {
|
|
12459
|
+
continue;
|
|
12460
|
+
}
|
|
12461
|
+
metrics[`${prefix}_${camelToSnake(nestedName)}`] = nestedValue;
|
|
12462
|
+
}
|
|
12463
|
+
}
|
|
12464
|
+
return metrics;
|
|
12465
|
+
}
|
|
12466
|
+
function extractOpenRouterUsageMetadata(usage) {
|
|
12467
|
+
if (!isObject(usage)) {
|
|
12468
|
+
return void 0;
|
|
12469
|
+
}
|
|
12470
|
+
const metadata = {};
|
|
12471
|
+
if (typeof usage.isByok === "boolean") {
|
|
12472
|
+
metadata.is_byok = usage.isByok;
|
|
12473
|
+
} else if (typeof usage.is_byok === "boolean") {
|
|
12474
|
+
metadata.is_byok = usage.is_byok;
|
|
12475
|
+
}
|
|
12476
|
+
return Object.keys(metadata).length > 0 ? metadata : void 0;
|
|
12477
|
+
}
|
|
12478
|
+
var OMITTED_OPENROUTER_KEYS = /* @__PURE__ */ new Set([
|
|
12479
|
+
"execute",
|
|
12480
|
+
"render",
|
|
12481
|
+
"nextTurnParams",
|
|
12482
|
+
"requireApproval"
|
|
12483
|
+
]);
|
|
12484
|
+
function parseOpenRouterModelString(model) {
|
|
12485
|
+
if (typeof model !== "string") {
|
|
12486
|
+
return { model };
|
|
12487
|
+
}
|
|
12488
|
+
const slashIndex = model.indexOf("/");
|
|
12489
|
+
if (slashIndex > 0 && slashIndex < model.length - 1) {
|
|
12490
|
+
return {
|
|
12491
|
+
provider: model.substring(0, slashIndex),
|
|
12492
|
+
model: model.substring(slashIndex + 1)
|
|
12493
|
+
};
|
|
12494
|
+
}
|
|
12495
|
+
return { model };
|
|
12496
|
+
}
|
|
12497
|
+
function isZodSchema3(value) {
|
|
12498
|
+
return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
|
|
12499
|
+
}
|
|
12500
|
+
function serializeZodSchema3(schema) {
|
|
12501
|
+
try {
|
|
12502
|
+
return zodToJsonSchema(schema);
|
|
12503
|
+
} catch {
|
|
12504
|
+
return {
|
|
12505
|
+
type: "object",
|
|
12506
|
+
description: "Zod schema (conversion failed)"
|
|
12507
|
+
};
|
|
12508
|
+
}
|
|
12509
|
+
}
|
|
12510
|
+
function serializeOpenRouterTool(tool) {
|
|
12511
|
+
if (!isObject(tool)) {
|
|
12512
|
+
return tool;
|
|
12513
|
+
}
|
|
12514
|
+
const serialized = {};
|
|
12515
|
+
for (const [key, value] of Object.entries(tool)) {
|
|
12516
|
+
if (OMITTED_OPENROUTER_KEYS.has(key)) {
|
|
12517
|
+
continue;
|
|
12518
|
+
}
|
|
12519
|
+
if (key === "function" && isObject(value)) {
|
|
12520
|
+
serialized.function = sanitizeOpenRouterLoggedValue(value);
|
|
12521
|
+
continue;
|
|
12522
|
+
}
|
|
12523
|
+
serialized[key] = sanitizeOpenRouterLoggedValue(value);
|
|
12524
|
+
}
|
|
12525
|
+
return serialized;
|
|
12526
|
+
}
|
|
12527
|
+
function serializeOpenRouterToolsForLogging(tools) {
|
|
12528
|
+
if (!Array.isArray(tools)) {
|
|
12529
|
+
return void 0;
|
|
12530
|
+
}
|
|
12531
|
+
return tools.map((tool) => serializeOpenRouterTool(tool));
|
|
12532
|
+
}
|
|
12533
|
+
function sanitizeOpenRouterLoggedValue(value) {
|
|
12534
|
+
if (isZodSchema3(value)) {
|
|
12535
|
+
return serializeZodSchema3(value);
|
|
12536
|
+
}
|
|
12537
|
+
if (typeof value === "function") {
|
|
12538
|
+
return "[Function]";
|
|
12539
|
+
}
|
|
12540
|
+
if (Array.isArray(value)) {
|
|
12541
|
+
return value.map((entry) => sanitizeOpenRouterLoggedValue(entry));
|
|
12542
|
+
}
|
|
12543
|
+
if (!isObject(value)) {
|
|
12544
|
+
return value;
|
|
12545
|
+
}
|
|
12546
|
+
const sanitized = {};
|
|
12547
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
12548
|
+
if (OMITTED_OPENROUTER_KEYS.has(key)) {
|
|
12549
|
+
continue;
|
|
12550
|
+
}
|
|
12551
|
+
if (key === "tools" && Array.isArray(entry)) {
|
|
12552
|
+
sanitized.tools = serializeOpenRouterToolsForLogging(entry);
|
|
12553
|
+
continue;
|
|
12554
|
+
}
|
|
12555
|
+
sanitized[key] = sanitizeOpenRouterLoggedValue(entry);
|
|
12556
|
+
}
|
|
12557
|
+
return sanitized;
|
|
12558
|
+
}
|
|
12559
|
+
function buildOpenRouterMetadata(metadata, httpReferer, xTitle) {
|
|
12560
|
+
const sanitized = sanitizeOpenRouterLoggedValue(metadata);
|
|
12561
|
+
const metadataRecord = isObject(sanitized) ? sanitized : {};
|
|
12562
|
+
const { model, provider: providerRouting, ...rest } = metadataRecord;
|
|
12563
|
+
const normalizedModel = parseOpenRouterModelString(model);
|
|
12564
|
+
return {
|
|
12565
|
+
...rest,
|
|
12566
|
+
...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
|
|
12567
|
+
...providerRouting !== void 0 ? { providerRouting } : {},
|
|
12568
|
+
...httpReferer !== void 0 ? { httpReferer } : {},
|
|
12569
|
+
...xTitle !== void 0 ? { xTitle } : {},
|
|
12570
|
+
provider: normalizedModel.provider || "openrouter"
|
|
12571
|
+
};
|
|
12572
|
+
}
|
|
12573
|
+
function buildOpenRouterEmbeddingMetadata(metadata, httpReferer, xTitle) {
|
|
12574
|
+
const normalized = buildOpenRouterMetadata(metadata, httpReferer, xTitle);
|
|
12575
|
+
return typeof normalized.model === "string" ? {
|
|
12576
|
+
...normalized,
|
|
12577
|
+
embedding_model: normalized.model
|
|
12578
|
+
} : normalized;
|
|
12579
|
+
}
|
|
12580
|
+
function extractOpenRouterCallModelInput(request) {
|
|
12581
|
+
return isObject(request) && "input" in request ? sanitizeOpenRouterLoggedValue(request.input) : void 0;
|
|
12582
|
+
}
|
|
12583
|
+
function extractOpenRouterCallModelMetadata(request) {
|
|
12584
|
+
if (!isObject(request)) {
|
|
12585
|
+
return { provider: "openrouter" };
|
|
12586
|
+
}
|
|
12587
|
+
const { input: _input, ...metadata } = request;
|
|
12588
|
+
return buildOpenRouterMetadata(metadata, void 0, void 0);
|
|
12589
|
+
}
|
|
12590
|
+
function extractOpenRouterResponseMetadata(result) {
|
|
12591
|
+
if (!isObject(result)) {
|
|
12592
|
+
return void 0;
|
|
12593
|
+
}
|
|
12594
|
+
const { output: _output, data: _data, usage, ...metadata } = result;
|
|
12595
|
+
const sanitized = sanitizeOpenRouterLoggedValue(metadata);
|
|
12596
|
+
const metadataRecord = isObject(sanitized) ? sanitized : {};
|
|
12597
|
+
const { model, provider, ...rest } = metadataRecord;
|
|
12598
|
+
const normalizedModel = parseOpenRouterModelString(model);
|
|
12599
|
+
const normalizedProvider = (typeof provider === "string" ? provider : void 0) || normalizedModel.provider;
|
|
12600
|
+
const usageMetadata = extractOpenRouterUsageMetadata(usage);
|
|
12601
|
+
const combined = {
|
|
12602
|
+
...rest,
|
|
12603
|
+
...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
|
|
12604
|
+
...usageMetadata || {},
|
|
12605
|
+
...normalizedProvider !== void 0 ? { provider: normalizedProvider } : {}
|
|
12606
|
+
};
|
|
12607
|
+
return Object.keys(combined).length > 0 ? combined : void 0;
|
|
12608
|
+
}
|
|
12609
|
+
function extractOpenRouterResponseOutput(response, fallbackOutput) {
|
|
12610
|
+
if (isObject(response) && "output" in response && response.output !== void 0) {
|
|
12611
|
+
return sanitizeOpenRouterLoggedValue(response.output);
|
|
12612
|
+
}
|
|
12613
|
+
if (fallbackOutput !== void 0) {
|
|
12614
|
+
return sanitizeOpenRouterLoggedValue(fallbackOutput);
|
|
12615
|
+
}
|
|
12616
|
+
return void 0;
|
|
12617
|
+
}
|
|
12618
|
+
var OPENROUTER_WRAPPED_TOOL = Symbol("braintrust.openrouter.wrappedTool");
|
|
11230
12619
|
function patchOpenRouterCallModelRequestTools(request) {
|
|
11231
12620
|
if (!Array.isArray(request.tools) || request.tools.length === 0) {
|
|
11232
12621
|
return void 0;
|
|
@@ -11244,29 +12633,234 @@ function patchOpenRouterCallModelRequestTools(request) {
|
|
|
11244
12633
|
request.tools = originalTools;
|
|
11245
12634
|
};
|
|
11246
12635
|
}
|
|
11247
|
-
function
|
|
11248
|
-
if (
|
|
11249
|
-
return
|
|
11250
|
-
}
|
|
11251
|
-
const resultLike = result;
|
|
11252
|
-
const hasInstrumentableMethod = typeof resultLike.getResponse === "function" || typeof resultLike.getText === "function" || OPENROUTER_CALL_MODEL_STREAM_METHODS.some(
|
|
11253
|
-
(methodName) => typeof resultLike[methodName] === "function"
|
|
11254
|
-
);
|
|
11255
|
-
if (!hasInstrumentableMethod) {
|
|
11256
|
-
return false;
|
|
12636
|
+
function wrapOpenRouterTool(tool) {
|
|
12637
|
+
if (isWrappedTool(tool) || !tool.function || typeof tool.function !== "object" || typeof tool.function.execute !== "function") {
|
|
12638
|
+
return tool;
|
|
11257
12639
|
}
|
|
11258
|
-
|
|
12640
|
+
const toolName = tool.function.name || "tool";
|
|
12641
|
+
const originalExecute = tool.function.execute;
|
|
12642
|
+
const wrappedTool = {
|
|
12643
|
+
...tool,
|
|
12644
|
+
function: {
|
|
12645
|
+
...tool.function,
|
|
12646
|
+
execute(...args) {
|
|
12647
|
+
return traceToolExecution({
|
|
12648
|
+
args,
|
|
12649
|
+
execute: () => Reflect.apply(originalExecute, this, args),
|
|
12650
|
+
toolCallId: getToolCallId(args[1]),
|
|
12651
|
+
toolName
|
|
12652
|
+
});
|
|
12653
|
+
}
|
|
12654
|
+
}
|
|
12655
|
+
};
|
|
12656
|
+
Object.defineProperty(wrappedTool, OPENROUTER_WRAPPED_TOOL, {
|
|
11259
12657
|
value: true,
|
|
11260
12658
|
enumerable: false,
|
|
11261
12659
|
configurable: false
|
|
11262
12660
|
});
|
|
11263
|
-
|
|
11264
|
-
|
|
11265
|
-
|
|
11266
|
-
|
|
11267
|
-
|
|
11268
|
-
|
|
11269
|
-
|
|
12661
|
+
return wrappedTool;
|
|
12662
|
+
}
|
|
12663
|
+
function isWrappedTool(tool) {
|
|
12664
|
+
return Boolean(tool[OPENROUTER_WRAPPED_TOOL]);
|
|
12665
|
+
}
|
|
12666
|
+
function traceToolExecution(args) {
|
|
12667
|
+
const tracingChannel = openRouterChannels.toolExecute.tracingChannel();
|
|
12668
|
+
const input = args.args.length > 0 ? args.args[0] : void 0;
|
|
12669
|
+
const event = {
|
|
12670
|
+
arguments: [input],
|
|
12671
|
+
span_info: {
|
|
12672
|
+
name: args.toolName
|
|
12673
|
+
},
|
|
12674
|
+
toolCallId: args.toolCallId,
|
|
12675
|
+
toolName: args.toolName
|
|
12676
|
+
};
|
|
12677
|
+
tracingChannel.start.publish(event);
|
|
12678
|
+
try {
|
|
12679
|
+
const result = args.execute();
|
|
12680
|
+
return publishToolResult(tracingChannel, event, result);
|
|
12681
|
+
} catch (error) {
|
|
12682
|
+
event.error = normalizeError(error);
|
|
12683
|
+
tracingChannel.error.publish(event);
|
|
12684
|
+
throw error;
|
|
12685
|
+
}
|
|
12686
|
+
}
|
|
12687
|
+
function publishToolResult(tracingChannel, event, result) {
|
|
12688
|
+
if (isPromiseLike2(result)) {
|
|
12689
|
+
return result.then(
|
|
12690
|
+
(resolved) => {
|
|
12691
|
+
event.result = resolved;
|
|
12692
|
+
tracingChannel.asyncEnd.publish(event);
|
|
12693
|
+
return resolved;
|
|
12694
|
+
},
|
|
12695
|
+
(error) => {
|
|
12696
|
+
event.error = normalizeError(error);
|
|
12697
|
+
tracingChannel.error.publish(event);
|
|
12698
|
+
throw error;
|
|
12699
|
+
}
|
|
12700
|
+
);
|
|
12701
|
+
}
|
|
12702
|
+
event.result = result;
|
|
12703
|
+
tracingChannel.asyncEnd.publish(event);
|
|
12704
|
+
return result;
|
|
12705
|
+
}
|
|
12706
|
+
function getToolCallId(context) {
|
|
12707
|
+
const toolContext = context;
|
|
12708
|
+
return typeof toolContext?.toolCall?.id === "string" ? toolContext.toolCall.id : void 0;
|
|
12709
|
+
}
|
|
12710
|
+
function isPromiseLike2(value) {
|
|
12711
|
+
return !!value && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
|
|
12712
|
+
}
|
|
12713
|
+
function aggregateOpenRouterChatChunks(chunks) {
|
|
12714
|
+
let role;
|
|
12715
|
+
let content = "";
|
|
12716
|
+
let toolCalls;
|
|
12717
|
+
let finishReason;
|
|
12718
|
+
let metrics = {};
|
|
12719
|
+
for (const chunk of chunks) {
|
|
12720
|
+
metrics = {
|
|
12721
|
+
...metrics,
|
|
12722
|
+
...parseOpenRouterMetricsFromUsage(chunk?.usage)
|
|
12723
|
+
};
|
|
12724
|
+
const choice = chunk?.choices?.[0];
|
|
12725
|
+
const delta = choice?.delta;
|
|
12726
|
+
if (!delta) {
|
|
12727
|
+
if (choice?.finish_reason !== void 0) {
|
|
12728
|
+
finishReason = choice.finish_reason;
|
|
12729
|
+
}
|
|
12730
|
+
continue;
|
|
12731
|
+
}
|
|
12732
|
+
if (!role && delta.role) {
|
|
12733
|
+
role = delta.role;
|
|
12734
|
+
}
|
|
12735
|
+
if (typeof delta.content === "string") {
|
|
12736
|
+
content += delta.content;
|
|
12737
|
+
}
|
|
12738
|
+
const choiceFinishReason = choice?.finishReason ?? choice?.finish_reason ?? void 0;
|
|
12739
|
+
const deltaFinishReason = delta.finishReason ?? delta.finish_reason ?? void 0;
|
|
12740
|
+
if (choiceFinishReason !== void 0) {
|
|
12741
|
+
finishReason = choiceFinishReason;
|
|
12742
|
+
} else if (deltaFinishReason !== void 0) {
|
|
12743
|
+
finishReason = deltaFinishReason;
|
|
12744
|
+
}
|
|
12745
|
+
const toolCallDeltas = Array.isArray(delta.toolCalls) ? delta.toolCalls : Array.isArray(delta.tool_calls) ? delta.tool_calls : void 0;
|
|
12746
|
+
if (!toolCallDeltas) {
|
|
12747
|
+
continue;
|
|
12748
|
+
}
|
|
12749
|
+
for (const toolDelta of toolCallDeltas) {
|
|
12750
|
+
if (!toolDelta?.function) {
|
|
12751
|
+
continue;
|
|
12752
|
+
}
|
|
12753
|
+
const toolIndex = toolDelta.index ?? 0;
|
|
12754
|
+
const existingToolCall = toolCalls?.[toolIndex];
|
|
12755
|
+
if (!existingToolCall || toolDelta.id && existingToolCall.id !== void 0 && existingToolCall.id !== toolDelta.id) {
|
|
12756
|
+
const nextToolCalls = [...toolCalls || []];
|
|
12757
|
+
nextToolCalls[toolIndex] = {
|
|
12758
|
+
index: toolIndex,
|
|
12759
|
+
id: toolDelta.id,
|
|
12760
|
+
type: toolDelta.type,
|
|
12761
|
+
function: {
|
|
12762
|
+
name: toolDelta.function.name,
|
|
12763
|
+
arguments: toolDelta.function.arguments || ""
|
|
12764
|
+
}
|
|
12765
|
+
};
|
|
12766
|
+
toolCalls = nextToolCalls;
|
|
12767
|
+
continue;
|
|
12768
|
+
}
|
|
12769
|
+
const current = existingToolCall;
|
|
12770
|
+
if (toolDelta.id && !current.id) {
|
|
12771
|
+
current.id = toolDelta.id;
|
|
12772
|
+
}
|
|
12773
|
+
if (toolDelta.type && !current.type) {
|
|
12774
|
+
current.type = toolDelta.type;
|
|
12775
|
+
}
|
|
12776
|
+
if (toolDelta.function.name && !current.function.name) {
|
|
12777
|
+
current.function.name = toolDelta.function.name;
|
|
12778
|
+
}
|
|
12779
|
+
current.function.arguments += toolDelta.function.arguments || "";
|
|
12780
|
+
}
|
|
12781
|
+
}
|
|
12782
|
+
return {
|
|
12783
|
+
output: [
|
|
12784
|
+
{
|
|
12785
|
+
index: 0,
|
|
12786
|
+
message: {
|
|
12787
|
+
role,
|
|
12788
|
+
content: content || void 0,
|
|
12789
|
+
...toolCalls ? { tool_calls: toolCalls } : {}
|
|
12790
|
+
},
|
|
12791
|
+
logprobs: null,
|
|
12792
|
+
finish_reason: finishReason
|
|
12793
|
+
}
|
|
12794
|
+
],
|
|
12795
|
+
metrics
|
|
12796
|
+
};
|
|
12797
|
+
}
|
|
12798
|
+
function aggregateOpenRouterResponseStreamEvents(chunks) {
|
|
12799
|
+
let finalResponse;
|
|
12800
|
+
for (const chunk of chunks) {
|
|
12801
|
+
const response = chunk?.response;
|
|
12802
|
+
if (!response) {
|
|
12803
|
+
continue;
|
|
12804
|
+
}
|
|
12805
|
+
if (chunk.type === "response.completed" || chunk.type === "response.incomplete" || chunk.type === "response.failed") {
|
|
12806
|
+
finalResponse = response;
|
|
12807
|
+
}
|
|
12808
|
+
}
|
|
12809
|
+
if (!finalResponse) {
|
|
12810
|
+
return {
|
|
12811
|
+
output: void 0,
|
|
12812
|
+
metrics: {}
|
|
12813
|
+
};
|
|
12814
|
+
}
|
|
12815
|
+
return {
|
|
12816
|
+
output: extractOpenRouterResponseOutput(finalResponse),
|
|
12817
|
+
metrics: parseOpenRouterMetricsFromUsage(finalResponse.usage),
|
|
12818
|
+
...extractOpenRouterResponseMetadata(finalResponse) ? { metadata: extractOpenRouterResponseMetadata(finalResponse) } : {}
|
|
12819
|
+
};
|
|
12820
|
+
}
|
|
12821
|
+
var OPENROUTER_WRAPPED_CALL_MODEL_RESULT = Symbol(
|
|
12822
|
+
"braintrust.openrouter.wrappedCallModelResult"
|
|
12823
|
+
);
|
|
12824
|
+
var OPENROUTER_CALL_MODEL_STREAM_METHODS = [
|
|
12825
|
+
"getFullResponsesStream",
|
|
12826
|
+
"getItemsStream",
|
|
12827
|
+
"getNewMessagesStream",
|
|
12828
|
+
"getReasoningStream",
|
|
12829
|
+
"getTextStream",
|
|
12830
|
+
"getToolCallsStream",
|
|
12831
|
+
"getToolStream"
|
|
12832
|
+
];
|
|
12833
|
+
var OPENROUTER_CALL_MODEL_CONTEXT_METHODS = [
|
|
12834
|
+
"cancel",
|
|
12835
|
+
"getPendingToolCalls",
|
|
12836
|
+
"getState",
|
|
12837
|
+
"getToolCalls",
|
|
12838
|
+
"requiresApproval"
|
|
12839
|
+
];
|
|
12840
|
+
function patchOpenRouterCallModelResult(args) {
|
|
12841
|
+
const { request, result, span } = args;
|
|
12842
|
+
if (!isObject(result) || isWrappedCallModelResult(result)) {
|
|
12843
|
+
return false;
|
|
12844
|
+
}
|
|
12845
|
+
const resultLike = result;
|
|
12846
|
+
const hasInstrumentableMethod = typeof resultLike.getResponse === "function" || typeof resultLike.getText === "function" || OPENROUTER_CALL_MODEL_STREAM_METHODS.some(
|
|
12847
|
+
(methodName) => typeof resultLike[methodName] === "function"
|
|
12848
|
+
);
|
|
12849
|
+
if (!hasInstrumentableMethod) {
|
|
12850
|
+
return false;
|
|
12851
|
+
}
|
|
12852
|
+
Object.defineProperty(resultLike, OPENROUTER_WRAPPED_CALL_MODEL_RESULT, {
|
|
12853
|
+
value: true,
|
|
12854
|
+
enumerable: false,
|
|
12855
|
+
configurable: false
|
|
12856
|
+
});
|
|
12857
|
+
const originalGetResponse = typeof resultLike.getResponse === "function" ? resultLike.getResponse.bind(resultLike) : void 0;
|
|
12858
|
+
const originalGetInitialResponse = typeof resultLike.getInitialResponse === "function" ? resultLike.getInitialResponse.bind(resultLike) : void 0;
|
|
12859
|
+
const originalMakeFollowupRequest = typeof resultLike.makeFollowupRequest === "function" ? resultLike.makeFollowupRequest.bind(resultLike) : void 0;
|
|
12860
|
+
let ended = false;
|
|
12861
|
+
let tracedTurnCount = 0;
|
|
12862
|
+
const endSpanWithResult = async (response, fallbackOutput) => {
|
|
12863
|
+
if (ended) {
|
|
11270
12864
|
return;
|
|
11271
12865
|
}
|
|
11272
12866
|
ended = true;
|
|
@@ -11317,10 +12911,10 @@ function patchOpenRouterCallModelResult(span, result, request) {
|
|
|
11317
12911
|
}
|
|
11318
12912
|
};
|
|
11319
12913
|
if (originalGetResponse) {
|
|
11320
|
-
resultLike.getResponse = async (...
|
|
12914
|
+
resultLike.getResponse = async (...args2) => {
|
|
11321
12915
|
return await withCurrent(span, async () => {
|
|
11322
12916
|
try {
|
|
11323
|
-
const response = await originalGetResponse(...
|
|
12917
|
+
const response = await originalGetResponse(...args2);
|
|
11324
12918
|
await endSpanWithResult(response);
|
|
11325
12919
|
return response;
|
|
11326
12920
|
} catch (error) {
|
|
@@ -11332,10 +12926,10 @@ function patchOpenRouterCallModelResult(span, result, request) {
|
|
|
11332
12926
|
}
|
|
11333
12927
|
if (typeof resultLike.getText === "function") {
|
|
11334
12928
|
const originalGetText = resultLike.getText.bind(resultLike);
|
|
11335
|
-
resultLike.getText = async (...
|
|
12929
|
+
resultLike.getText = async (...args2) => {
|
|
11336
12930
|
return await withCurrent(span, async () => {
|
|
11337
12931
|
try {
|
|
11338
|
-
const text = await originalGetText(...
|
|
12932
|
+
const text = await originalGetText(...args2);
|
|
11339
12933
|
await finalizeFromResponse(text);
|
|
11340
12934
|
return text;
|
|
11341
12935
|
} catch (error) {
|
|
@@ -11350,9 +12944,9 @@ function patchOpenRouterCallModelResult(span, result, request) {
|
|
|
11350
12944
|
continue;
|
|
11351
12945
|
}
|
|
11352
12946
|
const originalMethod = resultLike[methodName];
|
|
11353
|
-
resultLike[methodName] = async (...
|
|
12947
|
+
resultLike[methodName] = async (...args2) => {
|
|
11354
12948
|
return await withCurrent(span, async () => {
|
|
11355
|
-
return await originalMethod.apply(resultLike,
|
|
12949
|
+
return await originalMethod.apply(resultLike, args2);
|
|
11356
12950
|
});
|
|
11357
12951
|
};
|
|
11358
12952
|
}
|
|
@@ -11361,10 +12955,10 @@ function patchOpenRouterCallModelResult(span, result, request) {
|
|
|
11361
12955
|
continue;
|
|
11362
12956
|
}
|
|
11363
12957
|
const originalMethod = resultLike[methodName];
|
|
11364
|
-
resultLike[methodName] = (...
|
|
12958
|
+
resultLike[methodName] = (...args2) => {
|
|
11365
12959
|
const stream = withCurrent(
|
|
11366
12960
|
span,
|
|
11367
|
-
() => originalMethod.apply(resultLike,
|
|
12961
|
+
() => originalMethod.apply(resultLike, args2)
|
|
11368
12962
|
);
|
|
11369
12963
|
if (!isAsyncIterable2(stream)) {
|
|
11370
12964
|
return stream;
|
|
@@ -11379,616 +12973,194 @@ function patchOpenRouterCallModelResult(span, result, request) {
|
|
|
11379
12973
|
}
|
|
11380
12974
|
if (originalGetInitialResponse) {
|
|
11381
12975
|
let initialTurnTraced = false;
|
|
11382
|
-
resultLike.getInitialResponse = async (...
|
|
12976
|
+
resultLike.getInitialResponse = async (...args2) => {
|
|
11383
12977
|
if (initialTurnTraced) {
|
|
11384
12978
|
return await withCurrent(span, async () => {
|
|
11385
|
-
return await originalGetInitialResponse(...
|
|
11386
|
-
});
|
|
11387
|
-
}
|
|
11388
|
-
initialTurnTraced = true;
|
|
11389
|
-
const resolvedRequest = getOpenRouterResolvedRequest(resultLike, request);
|
|
11390
|
-
const childSpan = startOpenRouterCallModelTurnSpan({
|
|
11391
|
-
request: resolvedRequest,
|
|
11392
|
-
step: tracedTurnCount + 1,
|
|
11393
|
-
stepType: tracedTurnCount === 0 ? "initial" : "continue"
|
|
11394
|
-
});
|
|
11395
|
-
return await withCurrent(childSpan, async () => {
|
|
11396
|
-
try {
|
|
11397
|
-
const response = await originalGetInitialResponse(...args);
|
|
11398
|
-
tracedTurnCount++;
|
|
11399
|
-
finishOpenRouterCallModelTurnSpan({
|
|
11400
|
-
response,
|
|
11401
|
-
step: tracedTurnCount,
|
|
11402
|
-
stepType: tracedTurnCount === 1 ? "initial" : "continue",
|
|
11403
|
-
span: childSpan
|
|
11404
|
-
});
|
|
11405
|
-
return response;
|
|
11406
|
-
} catch (error) {
|
|
11407
|
-
childSpan.log({
|
|
11408
|
-
error: normalizeError(error).message
|
|
11409
|
-
});
|
|
11410
|
-
childSpan.end();
|
|
11411
|
-
throw error;
|
|
11412
|
-
}
|
|
11413
|
-
});
|
|
11414
|
-
};
|
|
11415
|
-
}
|
|
11416
|
-
if (originalMakeFollowupRequest) {
|
|
11417
|
-
resultLike.makeFollowupRequest = async (...args) => {
|
|
11418
|
-
const currentResponse = args[0];
|
|
11419
|
-
const toolResults = Array.isArray(args[1]) ? args[1] : [];
|
|
11420
|
-
const resolvedRequest = getOpenRouterResolvedRequest(resultLike, request);
|
|
11421
|
-
const followupRequest = buildOpenRouterFollowupRequest(
|
|
11422
|
-
resolvedRequest,
|
|
11423
|
-
currentResponse,
|
|
11424
|
-
toolResults
|
|
11425
|
-
);
|
|
11426
|
-
const childSpan = startOpenRouterCallModelTurnSpan({
|
|
11427
|
-
request: followupRequest,
|
|
11428
|
-
step: tracedTurnCount + 1,
|
|
11429
|
-
stepType: "continue"
|
|
11430
|
-
});
|
|
11431
|
-
return await withCurrent(childSpan, async () => {
|
|
11432
|
-
try {
|
|
11433
|
-
const response = await originalMakeFollowupRequest(...args);
|
|
11434
|
-
tracedTurnCount++;
|
|
11435
|
-
finishOpenRouterCallModelTurnSpan({
|
|
11436
|
-
response,
|
|
11437
|
-
step: tracedTurnCount,
|
|
11438
|
-
stepType: "continue",
|
|
11439
|
-
span: childSpan
|
|
11440
|
-
});
|
|
11441
|
-
return response;
|
|
11442
|
-
} catch (error) {
|
|
11443
|
-
childSpan.log({
|
|
11444
|
-
error: normalizeError(error).message
|
|
11445
|
-
});
|
|
11446
|
-
childSpan.end();
|
|
11447
|
-
throw error;
|
|
11448
|
-
}
|
|
11449
|
-
});
|
|
11450
|
-
};
|
|
11451
|
-
}
|
|
11452
|
-
return true;
|
|
11453
|
-
}
|
|
11454
|
-
function wrapOpenRouterTool(tool) {
|
|
11455
|
-
if (isWrappedTool(tool) || !tool.function || typeof tool.function !== "object" || typeof tool.function.execute !== "function") {
|
|
11456
|
-
return tool;
|
|
11457
|
-
}
|
|
11458
|
-
const toolName = tool.function.name || "tool";
|
|
11459
|
-
const originalExecute = tool.function.execute;
|
|
11460
|
-
const wrappedTool = {
|
|
11461
|
-
...tool,
|
|
11462
|
-
function: {
|
|
11463
|
-
...tool.function,
|
|
11464
|
-
execute(...args) {
|
|
11465
|
-
return traceToolExecution({
|
|
11466
|
-
args,
|
|
11467
|
-
execute: () => Reflect.apply(originalExecute, this, args),
|
|
11468
|
-
toolCallId: getToolCallId(args[1]),
|
|
11469
|
-
toolName
|
|
11470
|
-
});
|
|
11471
|
-
}
|
|
11472
|
-
}
|
|
11473
|
-
};
|
|
11474
|
-
Object.defineProperty(wrappedTool, OPENROUTER_WRAPPED_TOOL, {
|
|
11475
|
-
value: true,
|
|
11476
|
-
enumerable: false,
|
|
11477
|
-
configurable: false
|
|
11478
|
-
});
|
|
11479
|
-
return wrappedTool;
|
|
11480
|
-
}
|
|
11481
|
-
function isWrappedTool(tool) {
|
|
11482
|
-
return Boolean(tool[OPENROUTER_WRAPPED_TOOL]);
|
|
11483
|
-
}
|
|
11484
|
-
function isWrappedCallModelResult(value) {
|
|
11485
|
-
return Boolean(
|
|
11486
|
-
isObject(value) && value[OPENROUTER_WRAPPED_CALL_MODEL_RESULT]
|
|
11487
|
-
);
|
|
11488
|
-
}
|
|
11489
|
-
function traceToolExecution(args) {
|
|
11490
|
-
const tracingChannel = openRouterChannels.toolExecute.tracingChannel();
|
|
11491
|
-
const input = args.args.length > 0 ? args.args[0] : void 0;
|
|
11492
|
-
const event = {
|
|
11493
|
-
arguments: [input],
|
|
11494
|
-
span_info: {
|
|
11495
|
-
name: args.toolName
|
|
11496
|
-
},
|
|
11497
|
-
toolCallId: args.toolCallId,
|
|
11498
|
-
toolName: args.toolName
|
|
11499
|
-
};
|
|
11500
|
-
tracingChannel.start.publish(event);
|
|
11501
|
-
try {
|
|
11502
|
-
const result = args.execute();
|
|
11503
|
-
return publishToolResult(tracingChannel, event, result);
|
|
11504
|
-
} catch (error) {
|
|
11505
|
-
event.error = normalizeError(error);
|
|
11506
|
-
tracingChannel.error.publish(event);
|
|
11507
|
-
throw error;
|
|
11508
|
-
}
|
|
11509
|
-
}
|
|
11510
|
-
function publishToolResult(tracingChannel, event, result) {
|
|
11511
|
-
if (isPromiseLike(result)) {
|
|
11512
|
-
return result.then(
|
|
11513
|
-
(resolved) => {
|
|
11514
|
-
event.result = resolved;
|
|
11515
|
-
tracingChannel.asyncEnd.publish(event);
|
|
11516
|
-
return resolved;
|
|
11517
|
-
},
|
|
11518
|
-
(error) => {
|
|
11519
|
-
event.error = normalizeError(error);
|
|
11520
|
-
tracingChannel.error.publish(event);
|
|
11521
|
-
throw error;
|
|
11522
|
-
}
|
|
11523
|
-
);
|
|
11524
|
-
}
|
|
11525
|
-
event.result = result;
|
|
11526
|
-
tracingChannel.asyncEnd.publish(event);
|
|
11527
|
-
return result;
|
|
11528
|
-
}
|
|
11529
|
-
function getToolCallId(context) {
|
|
11530
|
-
const toolContext = context;
|
|
11531
|
-
return typeof toolContext?.toolCall?.id === "string" ? toolContext.toolCall.id : void 0;
|
|
11532
|
-
}
|
|
11533
|
-
function extractOpenRouterCallModelResultMetadata(response, turnCount) {
|
|
11534
|
-
const combined = {
|
|
11535
|
-
...extractOpenRouterResponseMetadata(response) || {},
|
|
11536
|
-
...turnCount !== void 0 ? { turn_count: turnCount } : {}
|
|
11537
|
-
};
|
|
11538
|
-
return Object.keys(combined).length > 0 ? combined : void 0;
|
|
11539
|
-
}
|
|
11540
|
-
function getFinalOpenRouterCallModelResponse(result, response) {
|
|
11541
|
-
if (isObject(response)) {
|
|
11542
|
-
return response;
|
|
11543
|
-
}
|
|
11544
|
-
return isObject(result.finalResponse) ? result.finalResponse : void 0;
|
|
11545
|
-
}
|
|
11546
|
-
function getOpenRouterCallModelRounds(result) {
|
|
11547
|
-
if (!Array.isArray(result.allToolExecutionRounds)) {
|
|
11548
|
-
return [];
|
|
11549
|
-
}
|
|
11550
|
-
return result.allToolExecutionRounds.filter((round) => isObject(round)).map((round) => ({
|
|
11551
|
-
response: isObject(round.response) ? round.response : void 0,
|
|
11552
|
-
round: typeof round.round === "number" ? round.round : void 0,
|
|
11553
|
-
toolResults: Array.isArray(round.toolResults) ? round.toolResults : []
|
|
11554
|
-
})).filter((round) => round.response !== void 0);
|
|
11555
|
-
}
|
|
11556
|
-
function aggregateOpenRouterCallModelMetrics(rounds, finalResponse) {
|
|
11557
|
-
const metrics = {};
|
|
11558
|
-
const responses = [
|
|
11559
|
-
...rounds.map((round) => round.response).filter(isObject),
|
|
11560
|
-
finalResponse
|
|
11561
|
-
];
|
|
11562
|
-
for (const response of responses) {
|
|
11563
|
-
const responseMetrics = parseOpenRouterMetricsFromUsage(response.usage);
|
|
11564
|
-
for (const [name, value] of Object.entries(responseMetrics)) {
|
|
11565
|
-
metrics[name] = (metrics[name] || 0) + value;
|
|
11566
|
-
}
|
|
11567
|
-
}
|
|
11568
|
-
return metrics;
|
|
11569
|
-
}
|
|
11570
|
-
function buildNextOpenRouterCallModelInput(currentInput, response, toolResults) {
|
|
11571
|
-
const normalizedInput = Array.isArray(currentInput) ? [...currentInput] : currentInput === void 0 ? [] : [currentInput];
|
|
11572
|
-
const responseOutput = Array.isArray(response.output) ? response.output : response.output === void 0 ? [] : [response.output];
|
|
11573
|
-
return [...normalizedInput, ...responseOutput, ...toolResults].map(
|
|
11574
|
-
(entry) => sanitizeOpenRouterLoggedValue(entry)
|
|
11575
|
-
);
|
|
11576
|
-
}
|
|
11577
|
-
function startOpenRouterCallModelTurnSpan(args) {
|
|
11578
|
-
const requestRecord = isObject(args.request) ? args.request : void 0;
|
|
11579
|
-
const metadata = requestRecord ? extractOpenRouterCallModelMetadata(requestRecord) : { provider: "openrouter" };
|
|
11580
|
-
if (isObject(metadata) && "tools" in metadata) {
|
|
11581
|
-
delete metadata.tools;
|
|
11582
|
-
}
|
|
11583
|
-
return startSpan({
|
|
11584
|
-
name: "openrouter.beta.responses.send",
|
|
11585
|
-
spanAttributes: {
|
|
11586
|
-
type: "llm" /* LLM */
|
|
11587
|
-
},
|
|
11588
|
-
event: {
|
|
11589
|
-
input: requestRecord ? extractOpenRouterCallModelInput(requestRecord) : void 0,
|
|
11590
|
-
metadata: {
|
|
11591
|
-
...metadata,
|
|
11592
|
-
step: args.step,
|
|
11593
|
-
step_type: args.stepType
|
|
11594
|
-
}
|
|
11595
|
-
}
|
|
11596
|
-
});
|
|
11597
|
-
}
|
|
11598
|
-
function finishOpenRouterCallModelTurnSpan(args) {
|
|
11599
|
-
if (!isObject(args.response)) {
|
|
11600
|
-
args.span.end();
|
|
11601
|
-
return;
|
|
11602
|
-
}
|
|
11603
|
-
args.span.log({
|
|
11604
|
-
output: extractOpenRouterResponseOutput(args.response),
|
|
11605
|
-
...extractOpenRouterResponseMetadata(args.response) ? {
|
|
11606
|
-
metadata: {
|
|
11607
|
-
...extractOpenRouterResponseMetadata(args.response),
|
|
11608
|
-
...args.step !== void 0 ? { step: args.step } : {},
|
|
11609
|
-
...args.stepType ? { step_type: args.stepType } : {}
|
|
11610
|
-
}
|
|
11611
|
-
} : {},
|
|
11612
|
-
metrics: parseOpenRouterMetricsFromUsage(args.response.usage)
|
|
11613
|
-
});
|
|
11614
|
-
args.span.end();
|
|
11615
|
-
}
|
|
11616
|
-
function getOpenRouterResolvedRequest(result, request) {
|
|
11617
|
-
if (isObject(result.resolvedRequest)) {
|
|
11618
|
-
return result.resolvedRequest;
|
|
11619
|
-
}
|
|
11620
|
-
return request;
|
|
11621
|
-
}
|
|
11622
|
-
function buildOpenRouterFollowupRequest(request, currentResponse, toolResults) {
|
|
11623
|
-
if (!request) {
|
|
11624
|
-
return void 0;
|
|
11625
|
-
}
|
|
11626
|
-
return {
|
|
11627
|
-
...request,
|
|
11628
|
-
input: buildNextOpenRouterCallModelInput(
|
|
11629
|
-
extractOpenRouterCallModelInput(request),
|
|
11630
|
-
isObject(currentResponse) ? currentResponse : {},
|
|
11631
|
-
toolResults
|
|
11632
|
-
),
|
|
11633
|
-
stream: false
|
|
11634
|
-
};
|
|
11635
|
-
}
|
|
11636
|
-
function wrapAsyncIterableWithSpan(args) {
|
|
11637
|
-
return {
|
|
11638
|
-
[Symbol.asyncIterator]() {
|
|
11639
|
-
const iterator = args.iteratorFactory();
|
|
11640
|
-
return {
|
|
11641
|
-
next(value) {
|
|
11642
|
-
return withCurrent(
|
|
11643
|
-
args.span,
|
|
11644
|
-
() => value === void 0 ? iterator.next() : iterator.next(value)
|
|
11645
|
-
).then(
|
|
11646
|
-
async (result) => {
|
|
11647
|
-
if (result.done) {
|
|
11648
|
-
await args.finalize();
|
|
11649
|
-
}
|
|
11650
|
-
return result;
|
|
11651
|
-
},
|
|
11652
|
-
(error) => {
|
|
11653
|
-
args.onError(error);
|
|
11654
|
-
throw error;
|
|
11655
|
-
}
|
|
11656
|
-
);
|
|
11657
|
-
},
|
|
11658
|
-
return(value) {
|
|
11659
|
-
if (typeof iterator.return !== "function") {
|
|
11660
|
-
return args.finalize().then(() => ({
|
|
11661
|
-
done: true,
|
|
11662
|
-
value
|
|
11663
|
-
}));
|
|
11664
|
-
}
|
|
11665
|
-
return withCurrent(args.span, () => iterator.return(value)).then(
|
|
11666
|
-
async (result) => {
|
|
11667
|
-
await args.finalize();
|
|
11668
|
-
return result;
|
|
11669
|
-
},
|
|
11670
|
-
(error) => {
|
|
11671
|
-
args.onError(error);
|
|
11672
|
-
throw error;
|
|
11673
|
-
}
|
|
11674
|
-
);
|
|
11675
|
-
},
|
|
11676
|
-
throw(error) {
|
|
11677
|
-
args.onError(error);
|
|
11678
|
-
if (typeof iterator.throw !== "function") {
|
|
11679
|
-
return Promise.reject(error);
|
|
11680
|
-
}
|
|
11681
|
-
return withCurrent(args.span, () => iterator.throw(error));
|
|
11682
|
-
},
|
|
11683
|
-
[Symbol.asyncIterator]() {
|
|
11684
|
-
return this;
|
|
11685
|
-
}
|
|
11686
|
-
};
|
|
11687
|
-
}
|
|
11688
|
-
};
|
|
11689
|
-
}
|
|
11690
|
-
function isAsyncIterable2(value) {
|
|
11691
|
-
return !!value && (typeof value === "object" || typeof value === "function") && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
|
|
11692
|
-
}
|
|
11693
|
-
function isPromiseLike(value) {
|
|
11694
|
-
return !!value && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
|
|
11695
|
-
}
|
|
11696
|
-
function normalizeError(error) {
|
|
11697
|
-
return error instanceof Error ? error : new Error(String(error));
|
|
11698
|
-
}
|
|
11699
|
-
|
|
11700
|
-
// src/instrumentation/plugins/openrouter-plugin.ts
|
|
11701
|
-
var OpenRouterPlugin = class extends BasePlugin {
|
|
11702
|
-
onEnable() {
|
|
11703
|
-
this.subscribeToOpenRouterChannels();
|
|
11704
|
-
}
|
|
11705
|
-
onDisable() {
|
|
11706
|
-
this.unsubscribers = unsubscribeAll(this.unsubscribers);
|
|
11707
|
-
}
|
|
11708
|
-
subscribeToOpenRouterChannels() {
|
|
11709
|
-
this.unsubscribers.push(
|
|
11710
|
-
traceStreamingChannel(openRouterChannels.chatSend, {
|
|
11711
|
-
name: "openrouter.chat.send",
|
|
11712
|
-
type: "llm" /* LLM */,
|
|
11713
|
-
extractInput: (args) => {
|
|
11714
|
-
const request = getOpenRouterRequestArg(args);
|
|
11715
|
-
const chatGenerationParams = isObject(request?.chatGenerationParams) ? request.chatGenerationParams : {};
|
|
11716
|
-
const httpReferer = request?.httpReferer;
|
|
11717
|
-
const xTitle = request?.xTitle;
|
|
11718
|
-
const { messages, ...metadata } = chatGenerationParams;
|
|
11719
|
-
return {
|
|
11720
|
-
input: messages,
|
|
11721
|
-
metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
|
|
11722
|
-
};
|
|
11723
|
-
},
|
|
11724
|
-
extractOutput: (result) => {
|
|
11725
|
-
return isObject(result) ? result.choices : void 0;
|
|
11726
|
-
},
|
|
11727
|
-
extractMetrics: (result, startTime) => {
|
|
11728
|
-
const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
|
|
11729
|
-
if (startTime) {
|
|
11730
|
-
metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
|
|
11731
|
-
}
|
|
11732
|
-
return metrics;
|
|
11733
|
-
},
|
|
11734
|
-
aggregateChunks: aggregateOpenRouterChatChunks
|
|
11735
|
-
})
|
|
11736
|
-
);
|
|
11737
|
-
this.unsubscribers.push(
|
|
11738
|
-
traceAsyncChannel(openRouterChannels.embeddingsGenerate, {
|
|
11739
|
-
name: "openrouter.embeddings.generate",
|
|
11740
|
-
type: "llm" /* LLM */,
|
|
11741
|
-
extractInput: (args) => {
|
|
11742
|
-
const request = getOpenRouterRequestArg(args);
|
|
11743
|
-
const requestBody = isObject(request?.requestBody) ? request.requestBody : {};
|
|
11744
|
-
const httpReferer = request?.httpReferer;
|
|
11745
|
-
const xTitle = request?.xTitle;
|
|
11746
|
-
const { input, ...metadata } = requestBody;
|
|
11747
|
-
return {
|
|
11748
|
-
input,
|
|
11749
|
-
metadata: buildOpenRouterEmbeddingMetadata(
|
|
11750
|
-
metadata,
|
|
11751
|
-
httpReferer,
|
|
11752
|
-
xTitle
|
|
11753
|
-
)
|
|
11754
|
-
};
|
|
11755
|
-
},
|
|
11756
|
-
extractOutput: (result) => {
|
|
11757
|
-
if (!isObject(result)) {
|
|
11758
|
-
return void 0;
|
|
11759
|
-
}
|
|
11760
|
-
const embedding = result.data?.[0]?.embedding;
|
|
11761
|
-
return Array.isArray(embedding) ? { embedding_length: embedding.length } : void 0;
|
|
11762
|
-
},
|
|
11763
|
-
extractMetadata: (result) => {
|
|
11764
|
-
if (!isObject(result)) {
|
|
11765
|
-
return void 0;
|
|
11766
|
-
}
|
|
11767
|
-
return extractOpenRouterResponseMetadata(result);
|
|
11768
|
-
},
|
|
11769
|
-
extractMetrics: (result) => {
|
|
11770
|
-
return isObject(result) ? parseOpenRouterMetricsFromUsage(result.usage) : {};
|
|
11771
|
-
}
|
|
11772
|
-
})
|
|
11773
|
-
);
|
|
11774
|
-
this.unsubscribers.push(
|
|
11775
|
-
traceStreamingChannel(openRouterChannels.betaResponsesSend, {
|
|
11776
|
-
name: "openrouter.beta.responses.send",
|
|
11777
|
-
type: "llm" /* LLM */,
|
|
11778
|
-
extractInput: (args) => {
|
|
11779
|
-
const request = getOpenRouterRequestArg(args);
|
|
11780
|
-
const openResponsesRequest = isObject(request?.openResponsesRequest) ? request.openResponsesRequest : {};
|
|
11781
|
-
const httpReferer = request?.httpReferer;
|
|
11782
|
-
const xTitle = request?.xTitle;
|
|
11783
|
-
const { input, ...metadata } = openResponsesRequest;
|
|
11784
|
-
return {
|
|
11785
|
-
input,
|
|
11786
|
-
metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
|
|
11787
|
-
};
|
|
11788
|
-
},
|
|
11789
|
-
extractOutput: (result) => extractOpenRouterResponseOutput(result),
|
|
11790
|
-
extractMetadata: (result) => extractOpenRouterResponseMetadata(result),
|
|
11791
|
-
extractMetrics: (result, startTime) => {
|
|
11792
|
-
const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
|
|
11793
|
-
if (startTime) {
|
|
11794
|
-
metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
|
|
11795
|
-
}
|
|
11796
|
-
return metrics;
|
|
11797
|
-
},
|
|
11798
|
-
aggregateChunks: aggregateOpenRouterResponseStreamEvents
|
|
11799
|
-
})
|
|
11800
|
-
);
|
|
11801
|
-
this.unsubscribers.push(
|
|
11802
|
-
traceSyncStreamChannel(openRouterChannels.callModel, {
|
|
11803
|
-
name: "openrouter.callModel",
|
|
11804
|
-
type: "llm" /* LLM */,
|
|
11805
|
-
extractInput: (args) => {
|
|
11806
|
-
const request = getOpenRouterCallModelRequestArg(args);
|
|
11807
|
-
return {
|
|
11808
|
-
input: request ? extractOpenRouterCallModelInput(request) : void 0,
|
|
11809
|
-
metadata: request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" }
|
|
11810
|
-
};
|
|
11811
|
-
},
|
|
11812
|
-
patchResult: ({ endEvent, result, span }) => {
|
|
11813
|
-
return patchOpenRouterCallModelResult(
|
|
11814
|
-
span,
|
|
11815
|
-
result,
|
|
11816
|
-
getOpenRouterCallModelRequestArg(endEvent.arguments)
|
|
11817
|
-
);
|
|
11818
|
-
}
|
|
11819
|
-
})
|
|
11820
|
-
);
|
|
11821
|
-
this.unsubscribers.push(
|
|
11822
|
-
traceStreamingChannel(openRouterChannels.toolExecute, {
|
|
11823
|
-
name: "openrouter.tool",
|
|
11824
|
-
type: "tool" /* TOOL */,
|
|
11825
|
-
extractInput: (args, event) => ({
|
|
11826
|
-
input: args[0],
|
|
11827
|
-
metadata: {
|
|
11828
|
-
provider: "openrouter",
|
|
11829
|
-
tool_name: event.toolName,
|
|
11830
|
-
...event.toolCallId ? { tool_call_id: event.toolCallId } : {}
|
|
11831
|
-
}
|
|
11832
|
-
}),
|
|
11833
|
-
extractOutput: (result) => result,
|
|
11834
|
-
extractMetrics: () => ({}),
|
|
11835
|
-
aggregateChunks: (chunks) => ({
|
|
11836
|
-
output: chunks.length > 0 ? chunks[chunks.length - 1] : void 0,
|
|
11837
|
-
metrics: {}
|
|
11838
|
-
})
|
|
11839
|
-
})
|
|
11840
|
-
);
|
|
11841
|
-
const callModelChannel = openRouterChannels.callModel.tracingChannel();
|
|
11842
|
-
const callModelHandlers = {
|
|
11843
|
-
start: (event) => {
|
|
11844
|
-
const request = getOpenRouterCallModelRequestArg(event.arguments);
|
|
11845
|
-
if (!request) {
|
|
11846
|
-
return;
|
|
11847
|
-
}
|
|
11848
|
-
patchOpenRouterCallModelRequestTools(request);
|
|
12979
|
+
return await originalGetInitialResponse(...args2);
|
|
12980
|
+
});
|
|
11849
12981
|
}
|
|
12982
|
+
initialTurnTraced = true;
|
|
12983
|
+
const step = tracedTurnCount + 1;
|
|
12984
|
+
const stepType = tracedTurnCount === 0 ? "initial" : "continue";
|
|
12985
|
+
const response = await traceOpenRouterCallModelTurn({
|
|
12986
|
+
fn: async () => {
|
|
12987
|
+
const nextResponse = await originalGetInitialResponse(...args2);
|
|
12988
|
+
tracedTurnCount++;
|
|
12989
|
+
return nextResponse;
|
|
12990
|
+
},
|
|
12991
|
+
parentSpan: span,
|
|
12992
|
+
request: getOpenRouterResolvedRequest(resultLike, request),
|
|
12993
|
+
step,
|
|
12994
|
+
stepType
|
|
12995
|
+
});
|
|
12996
|
+
return response;
|
|
11850
12997
|
};
|
|
11851
|
-
callModelChannel.subscribe(callModelHandlers);
|
|
11852
|
-
this.unsubscribers.push(() => {
|
|
11853
|
-
callModelChannel.unsubscribe(callModelHandlers);
|
|
11854
|
-
});
|
|
11855
|
-
}
|
|
11856
|
-
};
|
|
11857
|
-
function normalizeArgs(args) {
|
|
11858
|
-
if (Array.isArray(args)) {
|
|
11859
|
-
return args;
|
|
11860
12998
|
}
|
|
11861
|
-
if (
|
|
11862
|
-
|
|
12999
|
+
if (originalMakeFollowupRequest) {
|
|
13000
|
+
resultLike.makeFollowupRequest = async (...args2) => {
|
|
13001
|
+
const currentResponse = args2[0];
|
|
13002
|
+
const toolResults = Array.isArray(args2[1]) ? args2[1] : [];
|
|
13003
|
+
const step = tracedTurnCount + 1;
|
|
13004
|
+
const response = await traceOpenRouterCallModelTurn({
|
|
13005
|
+
fn: async () => {
|
|
13006
|
+
const nextResponse = await originalMakeFollowupRequest(...args2);
|
|
13007
|
+
tracedTurnCount++;
|
|
13008
|
+
return nextResponse;
|
|
13009
|
+
},
|
|
13010
|
+
parentSpan: span,
|
|
13011
|
+
request: buildOpenRouterFollowupRequest(
|
|
13012
|
+
getOpenRouterResolvedRequest(resultLike, request),
|
|
13013
|
+
currentResponse,
|
|
13014
|
+
toolResults
|
|
13015
|
+
),
|
|
13016
|
+
step,
|
|
13017
|
+
stepType: "continue"
|
|
13018
|
+
});
|
|
13019
|
+
return response;
|
|
13020
|
+
};
|
|
11863
13021
|
}
|
|
11864
|
-
return
|
|
13022
|
+
return true;
|
|
11865
13023
|
}
|
|
11866
|
-
function
|
|
11867
|
-
|
|
13024
|
+
async function traceOpenRouterCallModelTurn(args) {
|
|
13025
|
+
const context = {
|
|
13026
|
+
arguments: [args.request],
|
|
13027
|
+
step: args.step,
|
|
13028
|
+
stepType: args.stepType
|
|
13029
|
+
};
|
|
13030
|
+
return await withCurrent(
|
|
13031
|
+
args.parentSpan,
|
|
13032
|
+
() => openRouterChannels.callModelTurn.tracePromise(args.fn, context)
|
|
13033
|
+
);
|
|
11868
13034
|
}
|
|
11869
|
-
function
|
|
11870
|
-
|
|
11871
|
-
|
|
11872
|
-
(arg) => isObject(arg) && ("chatGenerationParams" in arg || "requestBody" in arg || "openResponsesRequest" in arg)
|
|
13035
|
+
function isWrappedCallModelResult(value) {
|
|
13036
|
+
return Boolean(
|
|
13037
|
+
isObject(value) && value[OPENROUTER_WRAPPED_CALL_MODEL_RESULT]
|
|
11873
13038
|
);
|
|
11874
|
-
|
|
11875
|
-
|
|
13039
|
+
}
|
|
13040
|
+
function extractOpenRouterCallModelResultMetadata(response, turnCount) {
|
|
13041
|
+
const combined = {
|
|
13042
|
+
...extractOpenRouterResponseMetadata(response) || {},
|
|
13043
|
+
...turnCount !== void 0 ? { turn_count: turnCount } : {}
|
|
13044
|
+
};
|
|
13045
|
+
return Object.keys(combined).length > 0 ? combined : void 0;
|
|
13046
|
+
}
|
|
13047
|
+
function getFinalOpenRouterCallModelResponse(result, response) {
|
|
13048
|
+
if (isObject(response)) {
|
|
13049
|
+
return response;
|
|
11876
13050
|
}
|
|
11877
|
-
|
|
11878
|
-
return isObject(firstObjectArg) ? firstObjectArg : void 0;
|
|
13051
|
+
return isObject(result.finalResponse) ? result.finalResponse : void 0;
|
|
11879
13052
|
}
|
|
11880
|
-
function
|
|
11881
|
-
|
|
11882
|
-
|
|
13053
|
+
function getOpenRouterCallModelRounds(result) {
|
|
13054
|
+
if (!Array.isArray(result.allToolExecutionRounds)) {
|
|
13055
|
+
return [];
|
|
13056
|
+
}
|
|
13057
|
+
return result.allToolExecutionRounds.filter((round) => isObject(round)).map((round) => ({
|
|
13058
|
+
response: isObject(round.response) ? round.response : void 0,
|
|
13059
|
+
round: typeof round.round === "number" ? round.round : void 0,
|
|
13060
|
+
toolResults: Array.isArray(round.toolResults) ? round.toolResults : []
|
|
13061
|
+
})).filter((round) => round.response !== void 0);
|
|
11883
13062
|
}
|
|
11884
|
-
function
|
|
11885
|
-
|
|
11886
|
-
|
|
11887
|
-
|
|
11888
|
-
|
|
11889
|
-
|
|
11890
|
-
for (const
|
|
11891
|
-
|
|
11892
|
-
|
|
11893
|
-
|
|
11894
|
-
};
|
|
11895
|
-
const choice = chunk?.choices?.[0];
|
|
11896
|
-
const delta = choice?.delta;
|
|
11897
|
-
if (!delta) {
|
|
11898
|
-
if (choice?.finish_reason !== void 0) {
|
|
11899
|
-
finishReason = choice.finish_reason;
|
|
11900
|
-
}
|
|
11901
|
-
continue;
|
|
11902
|
-
}
|
|
11903
|
-
if (!role && delta.role) {
|
|
11904
|
-
role = delta.role;
|
|
11905
|
-
}
|
|
11906
|
-
if (typeof delta.content === "string") {
|
|
11907
|
-
content += delta.content;
|
|
11908
|
-
}
|
|
11909
|
-
const choiceFinishReason = choice?.finishReason ?? choice?.finish_reason ?? void 0;
|
|
11910
|
-
const deltaFinishReason = delta.finishReason ?? delta.finish_reason ?? void 0;
|
|
11911
|
-
if (choiceFinishReason !== void 0) {
|
|
11912
|
-
finishReason = choiceFinishReason;
|
|
11913
|
-
} else if (deltaFinishReason !== void 0) {
|
|
11914
|
-
finishReason = deltaFinishReason;
|
|
11915
|
-
}
|
|
11916
|
-
const toolCallDeltas = Array.isArray(delta.toolCalls) ? delta.toolCalls : Array.isArray(delta.tool_calls) ? delta.tool_calls : void 0;
|
|
11917
|
-
if (!toolCallDeltas) {
|
|
11918
|
-
continue;
|
|
11919
|
-
}
|
|
11920
|
-
for (const toolDelta of toolCallDeltas) {
|
|
11921
|
-
if (!toolDelta?.function) {
|
|
11922
|
-
continue;
|
|
11923
|
-
}
|
|
11924
|
-
const toolIndex = toolDelta.index ?? 0;
|
|
11925
|
-
const existingToolCall = toolCalls?.[toolIndex];
|
|
11926
|
-
if (!existingToolCall || toolDelta.id && existingToolCall.id !== void 0 && existingToolCall.id !== toolDelta.id) {
|
|
11927
|
-
const nextToolCalls = [...toolCalls || []];
|
|
11928
|
-
nextToolCalls[toolIndex] = {
|
|
11929
|
-
index: toolIndex,
|
|
11930
|
-
id: toolDelta.id,
|
|
11931
|
-
type: toolDelta.type,
|
|
11932
|
-
function: {
|
|
11933
|
-
name: toolDelta.function.name,
|
|
11934
|
-
arguments: toolDelta.function.arguments || ""
|
|
11935
|
-
}
|
|
11936
|
-
};
|
|
11937
|
-
toolCalls = nextToolCalls;
|
|
11938
|
-
continue;
|
|
11939
|
-
}
|
|
11940
|
-
const current = existingToolCall;
|
|
11941
|
-
if (toolDelta.id && !current.id) {
|
|
11942
|
-
current.id = toolDelta.id;
|
|
11943
|
-
}
|
|
11944
|
-
if (toolDelta.type && !current.type) {
|
|
11945
|
-
current.type = toolDelta.type;
|
|
11946
|
-
}
|
|
11947
|
-
if (toolDelta.function.name && !current.function.name) {
|
|
11948
|
-
current.function.name = toolDelta.function.name;
|
|
11949
|
-
}
|
|
11950
|
-
current.function.arguments += toolDelta.function.arguments || "";
|
|
13063
|
+
function aggregateOpenRouterCallModelMetrics(rounds, finalResponse) {
|
|
13064
|
+
const metrics = {};
|
|
13065
|
+
const responses = [
|
|
13066
|
+
...rounds.map((round) => round.response).filter(isObject),
|
|
13067
|
+
finalResponse
|
|
13068
|
+
];
|
|
13069
|
+
for (const response of responses) {
|
|
13070
|
+
const responseMetrics = parseOpenRouterMetricsFromUsage(response.usage);
|
|
13071
|
+
for (const [name, value] of Object.entries(responseMetrics)) {
|
|
13072
|
+
metrics[name] = (metrics[name] || 0) + value;
|
|
11951
13073
|
}
|
|
11952
13074
|
}
|
|
11953
|
-
return
|
|
11954
|
-
output: [
|
|
11955
|
-
{
|
|
11956
|
-
index: 0,
|
|
11957
|
-
message: {
|
|
11958
|
-
role,
|
|
11959
|
-
content: content || void 0,
|
|
11960
|
-
...toolCalls ? { tool_calls: toolCalls } : {}
|
|
11961
|
-
},
|
|
11962
|
-
logprobs: null,
|
|
11963
|
-
finish_reason: finishReason
|
|
11964
|
-
}
|
|
11965
|
-
],
|
|
11966
|
-
metrics
|
|
11967
|
-
};
|
|
13075
|
+
return metrics;
|
|
11968
13076
|
}
|
|
11969
|
-
function
|
|
11970
|
-
|
|
11971
|
-
|
|
11972
|
-
|
|
11973
|
-
|
|
11974
|
-
|
|
11975
|
-
|
|
11976
|
-
|
|
11977
|
-
|
|
11978
|
-
|
|
13077
|
+
function buildNextOpenRouterCallModelInput(currentInput, response, toolResults) {
|
|
13078
|
+
const normalizedInput = Array.isArray(currentInput) ? [...currentInput] : currentInput === void 0 ? [] : [currentInput];
|
|
13079
|
+
const responseOutput = Array.isArray(response.output) ? response.output : response.output === void 0 ? [] : [response.output];
|
|
13080
|
+
return [...normalizedInput, ...responseOutput, ...toolResults].map(
|
|
13081
|
+
(entry) => sanitizeOpenRouterLoggedValue(entry)
|
|
13082
|
+
);
|
|
13083
|
+
}
|
|
13084
|
+
function getOpenRouterResolvedRequest(result, request) {
|
|
13085
|
+
if (isObject(result.resolvedRequest)) {
|
|
13086
|
+
return result.resolvedRequest;
|
|
11979
13087
|
}
|
|
11980
|
-
|
|
11981
|
-
|
|
11982
|
-
|
|
11983
|
-
|
|
11984
|
-
|
|
13088
|
+
return request;
|
|
13089
|
+
}
|
|
13090
|
+
function buildOpenRouterFollowupRequest(request, currentResponse, toolResults) {
|
|
13091
|
+
if (!request) {
|
|
13092
|
+
return void 0;
|
|
11985
13093
|
}
|
|
11986
13094
|
return {
|
|
11987
|
-
|
|
11988
|
-
|
|
11989
|
-
|
|
13095
|
+
...request,
|
|
13096
|
+
input: buildNextOpenRouterCallModelInput(
|
|
13097
|
+
extractOpenRouterCallModelInput(request),
|
|
13098
|
+
isObject(currentResponse) ? currentResponse : {},
|
|
13099
|
+
toolResults
|
|
13100
|
+
),
|
|
13101
|
+
stream: false
|
|
13102
|
+
};
|
|
13103
|
+
}
|
|
13104
|
+
function wrapAsyncIterableWithSpan(args) {
|
|
13105
|
+
return {
|
|
13106
|
+
[Symbol.asyncIterator]() {
|
|
13107
|
+
const iterator = args.iteratorFactory();
|
|
13108
|
+
return {
|
|
13109
|
+
next(value) {
|
|
13110
|
+
return withCurrent(
|
|
13111
|
+
args.span,
|
|
13112
|
+
() => value === void 0 ? iterator.next() : iterator.next(value)
|
|
13113
|
+
).then(
|
|
13114
|
+
async (result) => {
|
|
13115
|
+
if (result.done) {
|
|
13116
|
+
await args.finalize();
|
|
13117
|
+
}
|
|
13118
|
+
return result;
|
|
13119
|
+
},
|
|
13120
|
+
(error) => {
|
|
13121
|
+
args.onError(error);
|
|
13122
|
+
throw error;
|
|
13123
|
+
}
|
|
13124
|
+
);
|
|
13125
|
+
},
|
|
13126
|
+
return(value) {
|
|
13127
|
+
if (typeof iterator.return !== "function") {
|
|
13128
|
+
return args.finalize().then(() => ({
|
|
13129
|
+
done: true,
|
|
13130
|
+
value
|
|
13131
|
+
}));
|
|
13132
|
+
}
|
|
13133
|
+
return withCurrent(args.span, () => iterator.return(value)).then(
|
|
13134
|
+
async (result) => {
|
|
13135
|
+
await args.finalize();
|
|
13136
|
+
return result;
|
|
13137
|
+
},
|
|
13138
|
+
(error) => {
|
|
13139
|
+
args.onError(error);
|
|
13140
|
+
throw error;
|
|
13141
|
+
}
|
|
13142
|
+
);
|
|
13143
|
+
},
|
|
13144
|
+
throw(error) {
|
|
13145
|
+
args.onError(error);
|
|
13146
|
+
if (typeof iterator.throw !== "function") {
|
|
13147
|
+
return Promise.reject(error);
|
|
13148
|
+
}
|
|
13149
|
+
return withCurrent(args.span, () => iterator.throw(error));
|
|
13150
|
+
},
|
|
13151
|
+
[Symbol.asyncIterator]() {
|
|
13152
|
+
return this;
|
|
13153
|
+
}
|
|
13154
|
+
};
|
|
13155
|
+
}
|
|
11990
13156
|
};
|
|
11991
13157
|
}
|
|
13158
|
+
function isAsyncIterable2(value) {
|
|
13159
|
+
return !!value && (typeof value === "object" || typeof value === "function") && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
|
|
13160
|
+
}
|
|
13161
|
+
function normalizeError(error) {
|
|
13162
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
13163
|
+
}
|
|
11992
13164
|
|
|
11993
13165
|
// src/instrumentation/braintrust-plugin.ts
|
|
11994
13166
|
var BraintrustPlugin = class extends BasePlugin {
|