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.
Files changed (33) hide show
  1. package/dev/dist/index.js +2692 -1472
  2. package/dev/dist/index.mjs +2616 -1396
  3. package/dist/auto-instrumentations/bundler/esbuild.cjs +46 -21
  4. package/dist/auto-instrumentations/bundler/esbuild.mjs +2 -2
  5. package/dist/auto-instrumentations/bundler/rollup.cjs +46 -21
  6. package/dist/auto-instrumentations/bundler/rollup.mjs +2 -2
  7. package/dist/auto-instrumentations/bundler/vite.cjs +46 -21
  8. package/dist/auto-instrumentations/bundler/vite.mjs +2 -2
  9. package/dist/auto-instrumentations/bundler/webpack-loader.cjs +952 -0
  10. package/dist/auto-instrumentations/bundler/webpack-loader.d.ts +53 -0
  11. package/dist/auto-instrumentations/bundler/webpack.cjs +46 -21
  12. package/dist/auto-instrumentations/bundler/webpack.mjs +2 -2
  13. package/dist/auto-instrumentations/{chunk-WOUC73KB.mjs → chunk-NY4CGTN6.mjs} +1 -1
  14. package/dist/auto-instrumentations/{chunk-F7WAXFNM.mjs → chunk-YCKND42U.mjs} +46 -21
  15. package/dist/auto-instrumentations/hook.mjs +77 -26
  16. package/dist/auto-instrumentations/index.cjs +46 -21
  17. package/dist/auto-instrumentations/index.mjs +1 -1
  18. package/dist/browser.d.mts +8 -30
  19. package/dist/browser.d.ts +8 -30
  20. package/dist/browser.js +5051 -6344
  21. package/dist/browser.mjs +5051 -6344
  22. package/dist/cli.js +2622 -1398
  23. package/dist/edge-light.js +9456 -10773
  24. package/dist/edge-light.mjs +9456 -10773
  25. package/dist/index.d.mts +8 -30
  26. package/dist/index.d.ts +8 -30
  27. package/dist/index.js +5078 -6371
  28. package/dist/index.mjs +4870 -6163
  29. package/dist/instrumentation/index.js +2491 -1319
  30. package/dist/instrumentation/index.mjs +2491 -1319
  31. package/dist/workerd.js +9456 -10773
  32. package/dist/workerd.mjs +9456 -10773
  33. 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 Promise.resolve(fn.apply(thisArg, args));
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
- if (config.patchResult?.({
8336
- channelName,
8337
- endEvent,
8338
- result: endEvent.result,
8339
- span,
8340
- startTime
8341
- })) {
8342
- return;
8343
- }
8344
- const stream = endEvent.result;
8345
- if (!isSyncStreamLike(stream)) {
8346
- span.end();
8347
- states.delete(event);
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
- stream.on("event", (streamEvent) => {
8376
- if (!config.extractFromEvent) {
8359
+ const stream = result;
8360
+ if (!isSyncStreamLike(stream)) {
8361
+ span.end();
8362
+ states.delete(event);
8377
8363
  return;
8378
8364
  }
8379
- try {
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
- const extracted = config.extractFromEvent(streamEvent);
8389
- if (extracted && Object.keys(extracted).length > 0) {
8390
- span.log(extracted);
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
- span.end();
8405
- states.delete(event);
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.beta.messages.create"
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 deltas = [];
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
- if (event.delta?.type === "text_delta") {
9222
- const text = event.delta.text;
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
- deltas.push(text);
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 output = deltas.join("");
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(result, denyOutputPaths);
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(result, denyOutputPaths),
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(result, denyOutputPaths);
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(result, denyOutputPaths),
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(result, denyOutputPaths);
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(result, denyOutputPaths),
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(result, denyOutputPaths);
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(result, denyOutputPaths),
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 processAISDKInput(params) {
9644
- if (!params) return params;
9645
- const input = processInputAttachments(params);
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 { tools: _tools, ...rest } = input;
9650
- return rest;
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
- return {
9666
- input,
9667
- metadata
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 metrics;
9676
- }
9677
- function hasModelChildTracing(event) {
9678
- return event?.__braintrust_ai_sdk_model_wrapped === true;
9766
+ return defaultDenyOutputPaths;
9679
9767
  }
9680
- function extractMetadataFromParams(params, self) {
9681
- const metadata = {
9682
- braintrust: {
9683
- integration_name: "ai-sdk",
9684
- sdk_language: "typescript"
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
- 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;
9688
- const { model, provider } = serializeModelWithProvider(
9689
- params.model ?? agentModel
9690
- );
9691
- if (model) {
9692
- metadata.model = model;
9839
+ return result;
9840
+ } catch {
9841
+ return {
9842
+ response_format: null
9843
+ };
9693
9844
  }
9694
- if (provider) {
9695
- metadata.provider = provider;
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
- const tools = serializeAISDKToolsForLogging(params.tools);
9698
- if (tools) {
9699
- metadata.tools = tools;
9859
+ if (input.schema && isZodSchema2(input.schema)) {
9860
+ processed.schema = serializeZodSchema2(input.schema);
9700
9861
  }
9701
- return metadata;
9702
- }
9703
- function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9704
- const cleanup = [];
9705
- const patchedModels = /* @__PURE__ */ new WeakSet();
9706
- const patchedTools = /* @__PURE__ */ new WeakSet();
9707
- let modelWrapped = false;
9708
- const patchModel = (model) => {
9709
- const resolvedModel = resolveAISDKModel(model);
9710
- if (!resolvedModel || typeof resolvedModel !== "object" || typeof resolvedModel.doGenerate !== "function" || patchedModels.has(resolvedModel) || resolvedModel[AUTO_PATCHED_MODEL]) {
9711
- return;
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
- patchedModels.add(resolvedModel);
9714
- resolvedModel[AUTO_PATCHED_MODEL] = true;
9715
- modelWrapped = true;
9716
- const originalDoGenerate = resolvedModel.doGenerate;
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: extractTokenMetrics(output),
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 { denyOutputPaths, endEvent, result, span, startTime } = args;
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
- if (!isReadableStreamLike(resultRecord.baseStream)) {
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 wrappedBaseStream = resultRecord.baseStream.pipeThrough(
9952
- new TransformStream({
9953
- transform(chunk, controller) {
9954
- if (firstChunkTime === void 0) {
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
- Object.defineProperty(resultRecord, "baseStream", {
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: wrappedBaseStream,
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 && typeof result.text === "string") {
9998
- outputRecord.text = result.text;
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
- if (result.finishReason !== void 0) {
10034
- metadata.finish_reason = result.finishReason;
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 = result?.totalUsage || result?.usage;
10066
- if (!usage && result) {
10067
- try {
10068
- if ("totalUsage" in result && typeof result.totalUsage !== "function") {
10069
- usage = result.totalUsage;
10070
- } else if ("usage" in result && typeof result.usage !== "function") {
10071
- usage = result.usage;
10072
- }
10073
- } catch {
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
- if (lastChunk.text !== void 0) {
10120
- output.text = lastChunk.text;
10694
+ const text = safeSerializableFieldRead(lastChunk, "text");
10695
+ if (text !== void 0) {
10696
+ output.text = text;
10121
10697
  }
10122
- if (lastChunk.object !== void 0) {
10123
- output.object = lastChunk.object;
10698
+ const objectValue = safeSerializableFieldRead(lastChunk, "object");
10699
+ if (objectValue !== void 0) {
10700
+ output.object = objectValue;
10124
10701
  }
10125
- if (lastChunk.finishReason !== void 0) {
10126
- output.finishReason = lastChunk.finishReason;
10702
+ const finishReason = safeSerializableFieldRead(lastChunk, "finishReason");
10703
+ if (finishReason !== void 0) {
10704
+ output.finishReason = finishReason;
10127
10705
  }
10128
- if (lastChunk.toolCalls !== void 0) {
10129
- output.toolCalls = lastChunk.toolCalls;
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 && name in obj && isSerializableOutputValue(obj[name])) {
10154
- getterValues[name] = obj[name];
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
- if (result?.steps && Array.isArray(result.steps) && result.steps.length > 0) {
10229
- const routing2 = result.steps[0]?.providerMetadata?.gateway?.routing;
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 routing = result?.providerMetadata?.gateway?.routing;
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
- if (result?.steps && Array.isArray(result.steps) && result.steps.length > 0) {
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 result.steps) {
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 gateway = result?.providerMetadata?.gateway;
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: "async"
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
- const cacheTokens = extractAnthropicCacheTokens(
10434
- cacheReadTokens,
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 promptMessage = typeof prompt === "string" ? { content: prompt, role: "user" } : void 0;
10446
- const inputParts = [
10447
- ...promptMessage ? [promptMessage] : [],
10448
- ...conversationHistory
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
- async function createLLMSpanForMessages(messages, prompt, conversationHistory, options, startTime, parentSpan) {
10453
- if (messages.length === 0) return void 0;
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(prompt, conversationHistory);
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 prompt = params?.prompt;
10505
- const options = params?.options ?? {};
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 prompt === "string" ? prompt : {
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
- span,
10526
- startTime,
10527
- conversationHistory: [],
10528
- currentMessages: [],
11529
+ accumulatedOutputTokens: 0,
11530
+ activeToolSpans,
11531
+ capturedPromptMessages,
10529
11532
  currentMessageId: void 0,
10530
11533
  currentMessageStartTime: startTime,
10531
- accumulatedOutputTokens: 0
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
- asyncEnd: (event) => {
10535
- const spanData = spans.get(event);
10536
- if (!spanData) {
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
- spanData.span.end();
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: async (message) => {
10548
- const currentTime = getCurrentUnixTimestamp();
10549
- const params = event.arguments[0];
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 extracting output for Claude Agent SDK:",
11565
+ "Error processing Claude Agent SDK stream chunk:",
10631
11566
  error
10632
11567
  );
10633
- } finally {
10634
- spanData.span.end();
10635
- spans.delete(event);
10636
- }
11568
+ });
10637
11569
  },
10638
- onError: (error) => {
10639
- spanData.span.log({
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
- spanData.span.end();
11577
+ }).then(() => finalizeQuerySpan(state)).finally(() => {
10643
11578
  spans.delete(event);
10644
- }
11579
+ })
10645
11580
  });
10646
- } else {
10647
- try {
10648
- spanData.span.log({
10649
- output: eventResult
10650
- });
10651
- } catch (error) {
10652
- console.error(
10653
- "Error extracting output for Claude Agent SDK:",
10654
- error
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 spanData = spans.get(event);
10664
- if (!spanData || !event.error) {
11593
+ const state = spans.get(event);
11594
+ if (!state || !event.error) {
10665
11595
  return;
10666
11596
  }
10667
- const { span } = spanData;
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.unsubscribers.push(
10704
- traceAsyncChannel(googleGenAIChannels.generateContent, {
10705
- name: "google-genai.generateContent",
10706
- type: "llm" /* LLM */,
10707
- extractInput: ([params]) => {
10708
- const input = serializeInput(params);
10709
- const metadata = extractMetadata(params);
10710
- return {
10711
- input,
10712
- metadata: { ...metadata, provider: "google-genai" }
10713
- };
10714
- },
10715
- extractOutput: (result) => {
10716
- return result;
10717
- },
10718
- extractMetrics: (result, startTime) => {
10719
- return extractGenerateContentMetrics(result, startTime);
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
- this.unsubscribers.push(
10724
- traceStreamingChannel(googleGenAIChannels.generateContentStream, {
10725
- name: "google-genai.generateContentStream",
10726
- type: "llm" /* LLM */,
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
- input,
10732
- metadata: { ...metadata, provider: "google-genai" }
11684
+ span,
11685
+ startTime: getCurrentUnixTimestamp()
10733
11686
  };
10734
- },
10735
- extractOutput: (result) => {
10736
- return result;
10737
- },
10738
- extractMetrics: () => {
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
- function serializeInput(params) {
10749
- const input = {
10750
- model: params.model,
10751
- contents: serializeContents(params.contents)
10752
- };
10753
- if (params.config) {
10754
- const config = tryToDict(params.config);
10755
- if (config) {
10756
- const tools = serializeTools(params);
10757
- if (tools) {
10758
- config.tools = tools;
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
- input.config = config;
10761
- }
11712
+ };
11713
+ tracingChannel.subscribe(handlers);
11714
+ this.unsubscribers.push(() => {
11715
+ unbindCurrentSpanStore?.();
11716
+ tracingChannel.unsubscribe(handlers);
11717
+ });
10762
11718
  }
10763
- return input;
10764
- }
10765
- function serializeContents(contents) {
10766
- if (contents === null || contents === void 0) {
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 metrics = {};
10879
- if (startTime !== void 0) {
10880
- const end = getCurrentUnixTimestamp();
10881
- metrics.duration = end - startTime;
10882
- }
10883
- let firstTokenTime = null;
10884
- if (chunks.length > 0 && firstTokenTime === null && startTime !== void 0) {
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 { output: {}, metrics };
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 output = {};
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
- output.candidates = candidates;
12131
+ aggregated.candidates = candidates;
10952
12132
  }
10953
12133
  if (usageMetadata) {
10954
- output.usageMetadata = usageMetadata;
12134
+ aggregated.usageMetadata = usageMetadata;
10955
12135
  populateUsageMetrics(metrics, usageMetadata);
10956
12136
  }
10957
12137
  if (text) {
10958
- output.text = text;
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 { output, metrics };
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-utils.ts
11001
- var TOKEN_NAME_MAP2 = {
11002
- promptTokens: "prompt_tokens",
11003
- inputTokens: "prompt_tokens",
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
- if (typeof value === "function") {
11128
- return "[Function]";
12198
+ onDisable() {
12199
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
11129
12200
  }
11130
- if (Array.isArray(value)) {
11131
- return value.map((entry) => sanitizeOpenRouterLoggedValue(entry));
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
- if (!isObject(value)) {
11134
- return value;
12385
+ };
12386
+ function normalizeArgs(args) {
12387
+ if (Array.isArray(args)) {
12388
+ return args;
11135
12389
  }
11136
- const sanitized = {};
11137
- for (const [key, entry] of Object.entries(value)) {
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 sanitized;
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 extractOpenRouterCallModelMetadata(request) {
11174
- if (!isObject(request)) {
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 extractOpenRouterResponseMetadata(result) {
11181
- if (!isObject(result)) {
11182
- return void 0;
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 { output: _output, data: _data, usage, ...metadata } = result;
11185
- const sanitized = sanitizeOpenRouterLoggedValue(metadata);
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 extractOpenRouterResponseOutput(response, fallbackOutput) {
11200
- if (isObject(response) && "output" in response && response.output !== void 0) {
11201
- return sanitizeOpenRouterLoggedValue(response.output);
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
- // src/openrouter-tool-wrapping.ts
11210
- var OPENROUTER_WRAPPED_TOOL = Symbol("braintrust.openrouter.wrappedTool");
11211
- var OPENROUTER_WRAPPED_CALL_MODEL_RESULT = Symbol(
11212
- "braintrust.openrouter.wrappedCallModelResult"
11213
- );
11214
- var OPENROUTER_CALL_MODEL_STREAM_METHODS = [
11215
- "getFullResponsesStream",
11216
- "getItemsStream",
11217
- "getNewMessagesStream",
11218
- "getReasoningStream",
11219
- "getTextStream",
11220
- "getToolCallsStream",
11221
- "getToolStream"
11222
- ];
11223
- var OPENROUTER_CALL_MODEL_CONTEXT_METHODS = [
11224
- "cancel",
11225
- "getPendingToolCalls",
11226
- "getState",
11227
- "getToolCalls",
11228
- "requiresApproval"
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 patchOpenRouterCallModelResult(span, result, request) {
11248
- if (!isObject(result) || isWrappedCallModelResult(result)) {
11249
- return false;
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
- Object.defineProperty(resultLike, OPENROUTER_WRAPPED_CALL_MODEL_RESULT, {
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
- const originalGetResponse = typeof resultLike.getResponse === "function" ? resultLike.getResponse.bind(resultLike) : void 0;
11264
- const originalGetInitialResponse = typeof resultLike.getInitialResponse === "function" ? resultLike.getInitialResponse.bind(resultLike) : void 0;
11265
- const originalMakeFollowupRequest = typeof resultLike.makeFollowupRequest === "function" ? resultLike.makeFollowupRequest.bind(resultLike) : void 0;
11266
- let ended = false;
11267
- let tracedTurnCount = 0;
11268
- const endSpanWithResult = async (response, fallbackOutput) => {
11269
- if (ended) {
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 (...args) => {
12914
+ resultLike.getResponse = async (...args2) => {
11321
12915
  return await withCurrent(span, async () => {
11322
12916
  try {
11323
- const response = await originalGetResponse(...args);
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 (...args) => {
12929
+ resultLike.getText = async (...args2) => {
11336
12930
  return await withCurrent(span, async () => {
11337
12931
  try {
11338
- const text = await originalGetText(...args);
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 (...args) => {
12947
+ resultLike[methodName] = async (...args2) => {
11354
12948
  return await withCurrent(span, async () => {
11355
- return await originalMethod.apply(resultLike, args);
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] = (...args) => {
12958
+ resultLike[methodName] = (...args2) => {
11365
12959
  const stream = withCurrent(
11366
12960
  span,
11367
- () => originalMethod.apply(resultLike, args)
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 (...args) => {
12976
+ resultLike.getInitialResponse = async (...args2) => {
11383
12977
  if (initialTurnTraced) {
11384
12978
  return await withCurrent(span, async () => {
11385
- return await originalGetInitialResponse(...args);
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 (isArrayLike(args)) {
11862
- return Array.from(args);
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 [args];
13022
+ return true;
11865
13023
  }
11866
- function isArrayLike(value) {
11867
- return isObject(value) && "length" in value && typeof value.length === "number" && Number.isInteger(value.length) && value.length >= 0;
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 getOpenRouterRequestArg(args) {
11870
- const normalizedArgs = normalizeArgs(args);
11871
- const keyedCandidate = normalizedArgs.find(
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
- if (isObject(keyedCandidate)) {
11875
- return keyedCandidate;
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
- const firstObjectArg = normalizedArgs.find((arg) => isObject(arg));
11878
- return isObject(firstObjectArg) ? firstObjectArg : void 0;
13051
+ return isObject(result.finalResponse) ? result.finalResponse : void 0;
11879
13052
  }
11880
- function getOpenRouterCallModelRequestArg(args) {
11881
- const firstObjectArg = normalizeArgs(args).find((arg) => isObject(arg));
11882
- return isObject(firstObjectArg) ? firstObjectArg : void 0;
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 aggregateOpenRouterChatChunks(chunks) {
11885
- let role;
11886
- let content = "";
11887
- let toolCalls;
11888
- let finishReason;
11889
- let metrics = {};
11890
- for (const chunk of chunks) {
11891
- metrics = {
11892
- ...metrics,
11893
- ...parseOpenRouterMetricsFromUsage(chunk?.usage)
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 aggregateOpenRouterResponseStreamEvents(chunks) {
11970
- let finalResponse;
11971
- for (const chunk of chunks) {
11972
- const response = chunk?.response;
11973
- if (!response) {
11974
- continue;
11975
- }
11976
- if (chunk.type === "response.completed" || chunk.type === "response.incomplete" || chunk.type === "response.failed") {
11977
- finalResponse = response;
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
- if (!finalResponse) {
11981
- return {
11982
- output: void 0,
11983
- metrics: {}
11984
- };
13088
+ return request;
13089
+ }
13090
+ function buildOpenRouterFollowupRequest(request, currentResponse, toolResults) {
13091
+ if (!request) {
13092
+ return void 0;
11985
13093
  }
11986
13094
  return {
11987
- output: extractOpenRouterResponseOutput(finalResponse),
11988
- metrics: parseOpenRouterMetricsFromUsage(finalResponse.usage),
11989
- ...extractOpenRouterResponseMetadata(finalResponse) ? { metadata: extractOpenRouterResponseMetadata(finalResponse) } : {}
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 {