braintrust 3.7.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 (32) hide show
  1. package/dev/dist/index.js +1859 -1338
  2. package/dev/dist/index.mjs +1774 -1253
  3. package/dist/auto-instrumentations/bundler/esbuild.cjs +12 -15
  4. package/dist/auto-instrumentations/bundler/esbuild.mjs +2 -2
  5. package/dist/auto-instrumentations/bundler/rollup.cjs +12 -15
  6. package/dist/auto-instrumentations/bundler/rollup.mjs +2 -2
  7. package/dist/auto-instrumentations/bundler/vite.cjs +12 -15
  8. package/dist/auto-instrumentations/bundler/vite.mjs +2 -2
  9. package/dist/auto-instrumentations/bundler/webpack-loader.cjs +12 -15
  10. package/dist/auto-instrumentations/bundler/webpack.cjs +12 -15
  11. package/dist/auto-instrumentations/bundler/webpack.mjs +2 -2
  12. package/dist/auto-instrumentations/{chunk-ZK2IYER2.mjs → chunk-NY4CGTN6.mjs} +1 -1
  13. package/dist/auto-instrumentations/{chunk-AKEXR4AL.mjs → chunk-YCKND42U.mjs} +12 -15
  14. package/dist/auto-instrumentations/hook.mjs +12 -15
  15. package/dist/auto-instrumentations/index.cjs +12 -15
  16. package/dist/auto-instrumentations/index.mjs +1 -1
  17. package/dist/browser.d.mts +8 -30
  18. package/dist/browser.d.ts +8 -30
  19. package/dist/browser.js +4836 -6828
  20. package/dist/browser.mjs +4836 -6828
  21. package/dist/cli.js +1507 -986
  22. package/dist/edge-light.js +9173 -11163
  23. package/dist/edge-light.mjs +9173 -11163
  24. package/dist/index.d.mts +8 -30
  25. package/dist/index.d.ts +8 -30
  26. package/dist/index.js +4747 -6739
  27. package/dist/index.mjs +4748 -6740
  28. package/dist/instrumentation/index.js +1735 -1236
  29. package/dist/instrumentation/index.mjs +1735 -1236
  30. package/dist/workerd.js +9173 -11163
  31. package/dist/workerd.mjs +9173 -11163
  32. package/package.json +2 -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);
@@ -9226,28 +9243,40 @@ function aggregateAnthropicStreamChunks(chunks) {
9226
9243
  case "content_block_start":
9227
9244
  if (event.content_block) {
9228
9245
  contentBlocks[event.index] = event.content_block;
9229
- contentBlockDeltas[event.index] = [];
9246
+ contentBlockDeltas[event.index] = { textDeltas: [], citations: [] };
9230
9247
  }
9231
9248
  break;
9232
- case "content_block_delta":
9233
- if (event.delta?.type === "text_delta") {
9234
- 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;
9235
9255
  if (text) {
9236
- if (contentBlocks[event.index] !== void 0 || contentBlockDeltas[event.index] !== void 0) {
9237
- contentBlockDeltas[event.index] ??= [];
9238
- contentBlockDeltas[event.index].push(text);
9256
+ if (acc !== void 0) {
9257
+ acc.textDeltas.push(text);
9239
9258
  } else {
9240
9259
  fallbackTextDeltas.push(text);
9241
9260
  }
9242
9261
  }
9243
- } else if (event.delta?.type === "input_json_delta") {
9244
- const partialJson = event.delta.partial_json;
9245
- if (partialJson) {
9246
- contentBlockDeltas[event.index] ??= [];
9247
- contentBlockDeltas[event.index].push(partialJson);
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);
9248
9276
  }
9249
9277
  }
9250
9278
  break;
9279
+ }
9251
9280
  case "content_block_stop":
9252
9281
  finalizeContentBlock(
9253
9282
  event.index,
@@ -9273,7 +9302,7 @@ function aggregateAnthropicStreamChunks(chunks) {
9273
9302
  })).filter(({ block }) => block !== void 0).sort((left, right) => left.index - right.index).map(({ block }) => block);
9274
9303
  let output = fallbackTextDeltas.join("");
9275
9304
  if (orderedContent.length > 0) {
9276
- if (orderedContent.every(isTextContentBlock)) {
9305
+ if (orderedContent.every(isTextContentBlock) && orderedContent.every((block) => !block.citations?.length)) {
9277
9306
  output = orderedContent.map((block) => block.text).join("");
9278
9307
  } else {
9279
9308
  output = {
@@ -9299,7 +9328,8 @@ function finalizeContentBlock(index, contentBlocks, contentBlockDeltas, fallback
9299
9328
  if (!contentBlock) {
9300
9329
  return;
9301
9330
  }
9302
- const text = contentBlockDeltas[index]?.join("") ?? "";
9331
+ const acc = contentBlockDeltas[index];
9332
+ const text = acc?.textDeltas.join("") ?? "";
9303
9333
  if (isToolUseContentBlock(contentBlock)) {
9304
9334
  if (!text) {
9305
9335
  return;
@@ -9316,20 +9346,28 @@ function finalizeContentBlock(index, contentBlocks, contentBlockDeltas, fallback
9316
9346
  return;
9317
9347
  }
9318
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)) {
9319
9361
  if (!text) {
9320
9362
  delete contentBlocks[index];
9321
9363
  return;
9322
9364
  }
9323
9365
  contentBlocks[index] = {
9324
9366
  ...contentBlock,
9325
- text
9367
+ thinking: text
9326
9368
  };
9327
9369
  return;
9328
9370
  }
9329
- if (text) {
9330
- fallbackTextDeltas.push(text);
9331
- }
9332
- delete contentBlocks[index];
9333
9371
  }
9334
9372
  function isTextContentBlock(contentBlock) {
9335
9373
  return contentBlock.type === "text";
@@ -9337,6 +9375,9 @@ function isTextContentBlock(contentBlock) {
9337
9375
  function isToolUseContentBlock(contentBlock) {
9338
9376
  return contentBlock.type === "tool_use";
9339
9377
  }
9378
+ function isThinkingContentBlock(contentBlock) {
9379
+ return contentBlock.type === "thinking";
9380
+ }
9340
9381
  function isAnthropicBase64ContentBlock(input) {
9341
9382
  return (input.type === "image" || input.type === "document") && isObject(input.source) && input.source.type === "base64";
9342
9383
  }
@@ -9391,15 +9432,6 @@ function coalesceInput(messages, system) {
9391
9432
  }
9392
9433
  return input;
9393
9434
  }
9394
- function filterFrom(obj, fieldsToRemove) {
9395
- const result = {};
9396
- for (const [key, value] of Object.entries(obj)) {
9397
- if (!fieldsToRemove.includes(key)) {
9398
- result[key] = value;
9399
- }
9400
- }
9401
- return result;
9402
- }
9403
9435
 
9404
9436
  // src/wrappers/ai-sdk/normalize-logged-output.ts
9405
9437
  var REMOVE_NORMALIZED_VALUE = Symbol("braintrust.ai-sdk.remove-normalized");
@@ -9513,10 +9545,6 @@ var aiSDKChannels = defineChannels("ai", {
9513
9545
  channelName: "streamText",
9514
9546
  kind: "async"
9515
9547
  }),
9516
- streamTextSync: channel({
9517
- channelName: "streamText.sync",
9518
- kind: "sync-stream"
9519
- }),
9520
9548
  generateObject: channel({
9521
9549
  channelName: "generateObject",
9522
9550
  kind: "async"
@@ -9525,10 +9553,6 @@ var aiSDKChannels = defineChannels("ai", {
9525
9553
  channelName: "streamObject",
9526
9554
  kind: "async"
9527
9555
  }),
9528
- streamObjectSync: channel({
9529
- channelName: "streamObject.sync",
9530
- kind: "sync-stream"
9531
- }),
9532
9556
  agentGenerate: channel({
9533
9557
  channelName: "Agent.generate",
9534
9558
  kind: "async"
@@ -9564,6 +9588,9 @@ var DEFAULT_DENY_OUTPUT_PATHS = [
9564
9588
  ];
9565
9589
  var AUTO_PATCHED_MODEL = Symbol.for("braintrust.ai-sdk.auto-patched-model");
9566
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
+ );
9567
9594
  var AISDKPlugin = class extends BasePlugin {
9568
9595
  config;
9569
9596
  constructor(config = {}) {
@@ -9585,7 +9612,10 @@ var AISDKPlugin = class extends BasePlugin {
9585
9612
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9586
9613
  extractOutput: (result, endEvent) => {
9587
9614
  finalizeAISDKChildTracing(endEvent);
9588
- return processAISDKOutput(result, denyOutputPaths);
9615
+ return processAISDKOutput(
9616
+ result,
9617
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9618
+ );
9589
9619
  },
9590
9620
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
9591
9621
  aggregateChunks: aggregateAISDKChunks
@@ -9596,25 +9626,14 @@ var AISDKPlugin = class extends BasePlugin {
9596
9626
  name: "streamText",
9597
9627
  type: "llm" /* LLM */,
9598
9628
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9599
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
9629
+ extractOutput: (result, endEvent) => processAISDKOutput(
9630
+ result,
9631
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9632
+ ),
9600
9633
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
9601
9634
  aggregateChunks: aggregateAISDKChunks,
9602
9635
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9603
- denyOutputPaths,
9604
- endEvent,
9605
- result,
9606
- span,
9607
- startTime
9608
- })
9609
- })
9610
- );
9611
- this.unsubscribers.push(
9612
- traceSyncStreamChannel(aiSDKChannels.streamTextSync, {
9613
- name: "streamText",
9614
- type: "llm" /* LLM */,
9615
- extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9616
- patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9617
- denyOutputPaths,
9636
+ defaultDenyOutputPaths: denyOutputPaths,
9618
9637
  endEvent,
9619
9638
  result,
9620
9639
  span,
@@ -9629,7 +9648,10 @@ var AISDKPlugin = class extends BasePlugin {
9629
9648
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9630
9649
  extractOutput: (result, endEvent) => {
9631
9650
  finalizeAISDKChildTracing(endEvent);
9632
- return processAISDKOutput(result, denyOutputPaths);
9651
+ return processAISDKOutput(
9652
+ result,
9653
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9654
+ );
9633
9655
  },
9634
9656
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
9635
9657
  aggregateChunks: aggregateAISDKChunks
@@ -9640,25 +9662,14 @@ var AISDKPlugin = class extends BasePlugin {
9640
9662
  name: "streamObject",
9641
9663
  type: "llm" /* LLM */,
9642
9664
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9643
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
9665
+ extractOutput: (result, endEvent) => processAISDKOutput(
9666
+ result,
9667
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9668
+ ),
9644
9669
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
9645
9670
  aggregateChunks: aggregateAISDKChunks,
9646
9671
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9647
- denyOutputPaths,
9648
- endEvent,
9649
- result,
9650
- span,
9651
- startTime
9652
- })
9653
- })
9654
- );
9655
- this.unsubscribers.push(
9656
- traceSyncStreamChannel(aiSDKChannels.streamObjectSync, {
9657
- name: "streamObject",
9658
- type: "llm" /* LLM */,
9659
- extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9660
- patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9661
- denyOutputPaths,
9672
+ defaultDenyOutputPaths: denyOutputPaths,
9662
9673
  endEvent,
9663
9674
  result,
9664
9675
  span,
@@ -9673,7 +9684,10 @@ var AISDKPlugin = class extends BasePlugin {
9673
9684
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9674
9685
  extractOutput: (result, endEvent) => {
9675
9686
  finalizeAISDKChildTracing(endEvent);
9676
- return processAISDKOutput(result, denyOutputPaths);
9687
+ return processAISDKOutput(
9688
+ result,
9689
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9690
+ );
9677
9691
  },
9678
9692
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
9679
9693
  aggregateChunks: aggregateAISDKChunks
@@ -9684,11 +9698,14 @@ var AISDKPlugin = class extends BasePlugin {
9684
9698
  name: "Agent.stream",
9685
9699
  type: "llm" /* LLM */,
9686
9700
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9687
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
9701
+ extractOutput: (result, endEvent) => processAISDKOutput(
9702
+ result,
9703
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9704
+ ),
9688
9705
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
9689
9706
  aggregateChunks: aggregateAISDKChunks,
9690
9707
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9691
- denyOutputPaths,
9708
+ defaultDenyOutputPaths: denyOutputPaths,
9692
9709
  endEvent,
9693
9710
  result,
9694
9711
  span,
@@ -9703,7 +9720,10 @@ var AISDKPlugin = class extends BasePlugin {
9703
9720
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9704
9721
  extractOutput: (result, endEvent) => {
9705
9722
  finalizeAISDKChildTracing(endEvent);
9706
- return processAISDKOutput(result, denyOutputPaths);
9723
+ return processAISDKOutput(
9724
+ result,
9725
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9726
+ );
9707
9727
  },
9708
9728
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
9709
9729
  aggregateChunks: aggregateAISDKChunks
@@ -9714,11 +9734,14 @@ var AISDKPlugin = class extends BasePlugin {
9714
9734
  name: "ToolLoopAgent.stream",
9715
9735
  type: "llm" /* LLM */,
9716
9736
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9717
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
9737
+ extractOutput: (result, endEvent) => processAISDKOutput(
9738
+ result,
9739
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9740
+ ),
9718
9741
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
9719
9742
  aggregateChunks: aggregateAISDKChunks,
9720
9743
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9721
- denyOutputPaths,
9744
+ defaultDenyOutputPaths: denyOutputPaths,
9722
9745
  endEvent,
9723
9746
  result,
9724
9747
  span,
@@ -9728,132 +9751,435 @@ var AISDKPlugin = class extends BasePlugin {
9728
9751
  );
9729
9752
  }
9730
9753
  };
9731
- function processAISDKInput(params) {
9732
- if (!params) return params;
9733
- const input = processInputAttachments(params);
9734
- if (!input || typeof input !== "object" || Array.isArray(input)) {
9735
- return input;
9754
+ function resolveDenyOutputPaths(event, defaultDenyOutputPaths) {
9755
+ if (Array.isArray(event?.denyOutputPaths)) {
9756
+ return event.denyOutputPaths;
9736
9757
  }
9737
- const { tools: _tools, ...rest } = input;
9738
- return rest;
9739
- }
9740
- function prepareAISDKInput(params, event, span, denyOutputPaths) {
9741
- const input = processAISDKInput(params);
9742
- const metadata = extractMetadataFromParams(params, event.self);
9743
- const childTracing = prepareAISDKChildTracing(
9744
- params,
9745
- event.self,
9746
- span,
9747
- denyOutputPaths
9748
- );
9749
- event.__braintrust_ai_sdk_model_wrapped = childTracing.modelWrapped;
9750
- if (childTracing.cleanup) {
9751
- 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;
9752
9761
  }
9753
- return {
9754
- input,
9755
- metadata
9756
- };
9757
- }
9758
- function extractTopLevelAISDKMetrics(result, event, startTime) {
9759
- const metrics = hasModelChildTracing(event) ? {} : extractTokenMetrics(result);
9760
- if (startTime) {
9761
- 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;
9762
9765
  }
9763
- return metrics;
9764
- }
9765
- function hasModelChildTracing(event) {
9766
- return event?.__braintrust_ai_sdk_model_wrapped === true;
9766
+ return defaultDenyOutputPaths;
9767
9767
  }
9768
- function extractMetadataFromParams(params, self) {
9769
- const metadata = {
9770
- braintrust: {
9771
- integration_name: "ai-sdk",
9772
- sdk_language: "typescript"
9773
- }
9774
- };
9775
- 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;
9776
- const { model, provider } = serializeModelWithProvider(
9777
- params.model ?? agentModel
9778
- );
9779
- if (model) {
9780
- metadata.model = model;
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
+ };
9781
9779
  }
9782
- if (provider) {
9783
- metadata.provider = provider;
9780
+ };
9781
+ var isOutputObject = (value) => {
9782
+ if (value == null || typeof value !== "object") {
9783
+ return false;
9784
9784
  }
9785
- const tools = serializeAISDKToolsForLogging(params.tools);
9786
- if (tools) {
9787
- metadata.tools = tools;
9785
+ const output = value;
9786
+ if (!("responseFormat" in output)) {
9787
+ return false;
9788
9788
  }
9789
- return metadata;
9790
- }
9791
- function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9792
- const cleanup = [];
9793
- const patchedModels = /* @__PURE__ */ new WeakSet();
9794
- const patchedTools = /* @__PURE__ */ new WeakSet();
9795
- let modelWrapped = false;
9796
- const patchModel = (model) => {
9797
- const resolvedModel = resolveAISDKModel(model);
9798
- if (!resolvedModel || typeof resolvedModel !== "object" || typeof resolvedModel.doGenerate !== "function" || patchedModels.has(resolvedModel) || resolvedModel[AUTO_PATCHED_MODEL]) {
9799
- return;
9800
- }
9801
- patchedModels.add(resolvedModel);
9802
- resolvedModel[AUTO_PATCHED_MODEL] = true;
9803
- modelWrapped = true;
9804
- const originalDoGenerate = resolvedModel.doGenerate;
9805
- const originalDoStream = resolvedModel.doStream;
9806
- const baseMetadata = buildAISDKChildMetadata(resolvedModel);
9807
- resolvedModel.doGenerate = async function doGeneratePatched(options) {
9808
- return parentSpan.traced(
9809
- async (span) => {
9810
- const result = await Reflect.apply(
9811
- originalDoGenerate,
9812
- resolvedModel,
9813
- [options]
9814
- );
9815
- span.log({
9816
- output: processAISDKOutput(result, denyOutputPaths),
9817
- metrics: extractTokenMetrics(result),
9818
- ...buildResolvedMetadataPayload(result)
9819
- });
9820
- return result;
9821
- },
9822
- {
9823
- name: "doGenerate",
9824
- spanAttributes: {
9825
- type: "llm" /* LLM */
9826
- },
9827
- event: {
9828
- input: processAISDKInput(options),
9829
- metadata: baseMetadata
9830
- }
9831
- }
9832
- );
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
9833
9801
  };
9834
- if (originalDoStream) {
9835
- resolvedModel.doStream = async function doStreamPatched(options) {
9836
- const span = parentSpan.startSpan({
9837
- name: "doStream",
9838
- spanAttributes: {
9839
- type: "llm" /* LLM */
9840
- },
9841
- event: {
9842
- input: processAISDKInput(options),
9843
- metadata: baseMetadata
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;
9844
9826
  }
9845
- });
9846
- const result = await withCurrent(
9847
- span,
9848
- () => Reflect.apply(originalDoStream, resolvedModel, [options])
9849
9827
  );
9850
- const output = {};
9851
- let text = "";
9852
- let reasoning = "";
9853
- const toolCalls = [];
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
+ }
9838
+ }
9839
+ return result;
9840
+ } catch {
9841
+ return {
9842
+ response_format: null
9843
+ };
9844
+ }
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
+ }
9858
+ }
9859
+ if (input.schema && isZodSchema2(input.schema)) {
9860
+ processed.schema = serializeZodSchema2(input.schema);
9861
+ }
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;
9880
+ }
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;
10126
+ const originalDoStream = resolvedModel.doStream;
10127
+ const baseMetadata = buildAISDKChildMetadata(resolvedModel);
10128
+ resolvedModel.doGenerate = async function doGeneratePatched(options) {
10129
+ return parentSpan.traced(
10130
+ async (span) => {
10131
+ const result = await Reflect.apply(
10132
+ originalDoGenerate,
10133
+ resolvedModel,
10134
+ [options]
10135
+ );
10136
+ span.log({
10137
+ output: processAISDKOutput(result, denyOutputPaths),
10138
+ metrics: extractTokenMetrics(result),
10139
+ ...buildResolvedMetadataPayload(result)
10140
+ });
10141
+ return result;
10142
+ },
10143
+ {
10144
+ name: "doGenerate",
10145
+ spanAttributes: {
10146
+ type: "llm" /* LLM */
10147
+ },
10148
+ event: {
10149
+ input: processAISDKInput(options).input,
10150
+ metadata: baseMetadata
10151
+ }
10152
+ }
10153
+ );
10154
+ };
10155
+ if (originalDoStream) {
10156
+ resolvedModel.doStream = async function doStreamPatched(options) {
10157
+ const span = parentSpan.startSpan({
10158
+ name: "doStream",
10159
+ spanAttributes: {
10160
+ type: "llm" /* LLM */
10161
+ },
10162
+ event: {
10163
+ input: processAISDKInput(options).input,
10164
+ metadata: baseMetadata
10165
+ }
10166
+ });
10167
+ const result = await withCurrent(
10168
+ span,
10169
+ () => Reflect.apply(originalDoStream, resolvedModel, [options])
10170
+ );
10171
+ const streamStartTime = getCurrentUnixTimestamp();
10172
+ let firstChunkTime;
10173
+ const output = {};
10174
+ let text = "";
10175
+ let reasoning = "";
10176
+ const toolCalls = [];
9854
10177
  let object = void 0;
9855
10178
  const transformStream = new TransformStream({
9856
10179
  transform(chunk, controller) {
10180
+ if (firstChunkTime === void 0) {
10181
+ firstChunkTime = getCurrentUnixTimestamp();
10182
+ }
9857
10183
  switch (chunk.type) {
9858
10184
  case "text-delta":
9859
10185
  text += extractTextDelta(chunk);
@@ -9894,12 +10220,19 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9894
10220
  if (object !== void 0) {
9895
10221
  output.object = object;
9896
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
+ }
9897
10230
  span.log({
9898
10231
  output: processAISDKOutput(
9899
10232
  output,
9900
10233
  denyOutputPaths
9901
10234
  ),
9902
- metrics: extractTokenMetrics(output),
10235
+ metrics,
9903
10236
  ...buildResolvedMetadataPayload(output)
9904
10237
  });
9905
10238
  span.end();
@@ -9921,6 +10254,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9921
10254
  }
9922
10255
  delete resolvedModel[AUTO_PATCHED_MODEL];
9923
10256
  });
10257
+ return resolvedModel;
9924
10258
  };
9925
10259
  const patchTool = (tool, name) => {
9926
10260
  if (tool == null || typeof tool !== "object" || !("execute" in tool) || typeof tool.execute !== "function" || patchedTools.has(tool) || tool[AUTO_PATCHED_TOOL]) {
@@ -9993,17 +10327,26 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9993
10327
  }
9994
10328
  };
9995
10329
  if (params && typeof params === "object") {
9996
- patchModel(params.model);
10330
+ const patchedParamModel = patchModel(params.model);
10331
+ if (typeof params.model === "string" && patchedParamModel && typeof patchedParamModel === "object") {
10332
+ params.model = patchedParamModel;
10333
+ }
9997
10334
  patchTools(params.tools);
9998
10335
  }
9999
10336
  if (self && typeof self === "object") {
10000
10337
  const selfRecord = self;
10001
10338
  if (selfRecord.model !== void 0) {
10002
- patchModel(selfRecord.model);
10339
+ const patchedSelfModel = patchModel(selfRecord.model);
10340
+ if (typeof selfRecord.model === "string" && patchedSelfModel && typeof patchedSelfModel === "object") {
10341
+ selfRecord.model = patchedSelfModel;
10342
+ }
10003
10343
  }
10004
10344
  if (selfRecord.settings && typeof selfRecord.settings === "object") {
10005
10345
  if (selfRecord.settings.model !== void 0) {
10006
- 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
+ }
10007
10350
  }
10008
10351
  if (selfRecord.settings.tools !== void 0) {
10009
10352
  patchTools(selfRecord.settings.tools);
@@ -10027,63 +10370,173 @@ function finalizeAISDKChildTracing(event) {
10027
10370
  }
10028
10371
  }
10029
10372
  function patchAISDKStreamingResult(args) {
10030
- const { denyOutputPaths, endEvent, result, span, startTime } = args;
10373
+ const { defaultDenyOutputPaths, endEvent, result, span, startTime } = args;
10031
10374
  if (!result || typeof result !== "object") {
10032
10375
  return false;
10033
10376
  }
10034
10377
  const resultRecord = result;
10035
- 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) {
10036
10424
  return false;
10037
10425
  }
10038
10426
  let firstChunkTime;
10039
- const wrappedBaseStream = resultRecord.baseStream.pipeThrough(
10040
- new TransformStream({
10041
- transform(chunk, controller) {
10042
- if (firstChunkTime === void 0) {
10043
- firstChunkTime = getCurrentUnixTimestamp();
10044
- }
10045
- controller.enqueue(chunk);
10046
- },
10047
- async flush() {
10048
- const metrics = extractTopLevelAISDKMetrics(result, endEvent);
10049
- if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
10050
- metrics.time_to_first_token = firstChunkTime - startTime;
10051
- }
10052
- const output = await processAISDKStreamingOutput(
10053
- result,
10054
- denyOutputPaths
10055
- );
10056
- const metadata = buildResolvedMetadataPayload(result).metadata;
10057
- span.log({
10058
- output,
10059
- ...metadata ? { metadata } : {},
10060
- metrics
10061
- });
10062
- finalizeAISDKChildTracing(endEvent);
10063
- span.end();
10427
+ const wrappedStream = createPatchedAsyncIterable(streamField.stream, {
10428
+ onChunk: () => {
10429
+ if (firstChunkTime === void 0) {
10430
+ firstChunkTime = getCurrentUnixTimestamp();
10064
10431
  }
10065
- })
10066
- );
10067
- 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, {
10068
10460
  configurable: true,
10069
10461
  enumerable: true,
10070
- value: wrappedBaseStream,
10462
+ value: wrappedStream,
10071
10463
  writable: true
10072
10464
  });
10073
10465
  return true;
10074
10466
  }
10075
- function isReadableStreamLike(value) {
10076
- return value != null && typeof value === "object" && typeof value.pipeThrough === "function";
10077
- }
10078
- async function processAISDKStreamingOutput(result, denyOutputPaths) {
10079
- const output = processAISDKOutput(result, denyOutputPaths);
10080
- if (!output || typeof output !== "object") {
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
+ }
10491
+ function isReadableStreamLike(value) {
10492
+ return value != null && typeof value === "object" && typeof value.pipeThrough === "function";
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
+ }
10527
+ async function processAISDKStreamingOutput(result, denyOutputPaths) {
10528
+ const output = processAISDKOutput(result, denyOutputPaths);
10529
+ if (!output || typeof output !== "object") {
10081
10530
  return output;
10082
10531
  }
10083
10532
  const outputRecord = output;
10533
+ const isObjectStreamingResult = result != null && typeof result === "object" && "partialObjectStream" in result;
10084
10534
  try {
10085
- if ("text" in result && typeof result.text === "string") {
10086
- 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
+ }
10087
10540
  }
10088
10541
  } catch {
10089
10542
  }
@@ -10096,6 +10549,15 @@ async function processAISDKStreamingOutput(result, denyOutputPaths) {
10096
10549
  }
10097
10550
  } catch {
10098
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
+ }
10099
10561
  return outputRecord;
10100
10562
  }
10101
10563
  function buildAISDKChildMetadata(model) {
@@ -10118,16 +10580,25 @@ function buildResolvedMetadataPayload(result) {
10118
10580
  if (gatewayInfo?.model) {
10119
10581
  metadata.model = gatewayInfo.model;
10120
10582
  }
10121
- if (result.finishReason !== void 0) {
10122
- 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;
10123
10594
  }
10124
10595
  return Object.keys(metadata).length > 0 ? { metadata } : {};
10125
10596
  }
10126
- function resolveAISDKModel(model) {
10597
+ function resolveAISDKModel(model, aiSDK) {
10127
10598
  if (typeof model !== "string") {
10128
10599
  return model;
10129
10600
  }
10130
- const provider = globalThis.AI_SDK_DEFAULT_PROVIDER ?? null;
10601
+ const provider = globalThis.AI_SDK_DEFAULT_PROVIDER ?? aiSDK?.gateway ?? null;
10131
10602
  if (provider && typeof provider.languageModel === "function") {
10132
10603
  return provider.languageModel(model);
10133
10604
  }
@@ -10150,15 +10621,15 @@ function processAISDKOutput(output, denyOutputPaths) {
10150
10621
  }
10151
10622
  function extractTokenMetrics(result) {
10152
10623
  const metrics = {};
10153
- let usage = result?.totalUsage || result?.usage;
10154
- if (!usage && result) {
10155
- try {
10156
- if ("totalUsage" in result && typeof result.totalUsage !== "function") {
10157
- usage = result.totalUsage;
10158
- } else if ("usage" in result && typeof result.usage !== "function") {
10159
- usage = result.usage;
10160
- }
10161
- } 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;
10162
10633
  }
10163
10634
  }
10164
10635
  if (!usage) {
@@ -10196,6 +10667,22 @@ function extractTokenMetrics(result) {
10196
10667
  }
10197
10668
  return metrics;
10198
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
+ }
10199
10686
  function aggregateAISDKChunks(chunks, _result, endEvent) {
10200
10687
  const lastChunk = chunks[chunks.length - 1];
10201
10688
  const output = {};
@@ -10204,17 +10691,21 @@ function aggregateAISDKChunks(chunks, _result, endEvent) {
10204
10691
  if (lastChunk) {
10205
10692
  metrics = hasModelChildTracing(endEvent) ? {} : extractTokenMetrics(lastChunk);
10206
10693
  metadata = buildResolvedMetadataPayload(lastChunk).metadata;
10207
- if (lastChunk.text !== void 0) {
10208
- output.text = lastChunk.text;
10694
+ const text = safeSerializableFieldRead(lastChunk, "text");
10695
+ if (text !== void 0) {
10696
+ output.text = text;
10209
10697
  }
10210
- if (lastChunk.object !== void 0) {
10211
- output.object = lastChunk.object;
10698
+ const objectValue = safeSerializableFieldRead(lastChunk, "object");
10699
+ if (objectValue !== void 0) {
10700
+ output.object = objectValue;
10212
10701
  }
10213
- if (lastChunk.finishReason !== void 0) {
10214
- output.finishReason = lastChunk.finishReason;
10702
+ const finishReason = safeSerializableFieldRead(lastChunk, "finishReason");
10703
+ if (finishReason !== void 0) {
10704
+ output.finishReason = finishReason;
10215
10705
  }
10216
- if (lastChunk.toolCalls !== void 0) {
10217
- output.toolCalls = lastChunk.toolCalls;
10706
+ const toolCalls = safeSerializableFieldRead(lastChunk, "toolCalls");
10707
+ if (toolCalls !== void 0) {
10708
+ output.toolCalls = toolCalls;
10218
10709
  }
10219
10710
  }
10220
10711
  finalizeAISDKChildTracing(endEvent);
@@ -10223,6 +10714,7 @@ function aggregateAISDKChunks(chunks, _result, endEvent) {
10223
10714
  function extractGetterValues(obj) {
10224
10715
  const getterValues = {};
10225
10716
  const getterNames = [
10717
+ "content",
10226
10718
  "text",
10227
10719
  "object",
10228
10720
  "finishReason",
@@ -10238,8 +10730,17 @@ function extractGetterValues(obj) {
10238
10730
  ];
10239
10731
  for (const name of getterNames) {
10240
10732
  try {
10241
- if (obj && name in obj && isSerializableOutputValue(obj[name])) {
10242
- 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;
10243
10744
  }
10244
10745
  } catch {
10245
10746
  }
@@ -10261,6 +10762,11 @@ function extractSerializableOutputFields(output) {
10261
10762
  for (const name of directFieldNames) {
10262
10763
  try {
10263
10764
  const value = output?.[name];
10765
+ if (isPromiseLike(value)) {
10766
+ void Promise.resolve(value).catch(() => {
10767
+ });
10768
+ continue;
10769
+ }
10264
10770
  if (isSerializableOutputValue(value)) {
10265
10771
  serialized[name] = value;
10266
10772
  }
@@ -10272,6 +10778,9 @@ function extractSerializableOutputFields(output) {
10272
10778
  ...extractGetterValues(output)
10273
10779
  };
10274
10780
  }
10781
+ function isPromiseLike(value) {
10782
+ return value != null && typeof value === "object" && typeof value.then === "function";
10783
+ }
10275
10784
  function isSerializableOutputValue(value) {
10276
10785
  if (typeof value === "function") {
10277
10786
  return false;
@@ -10313,8 +10822,9 @@ function parseGatewayModelString(modelString) {
10313
10822
  return { model: modelString };
10314
10823
  }
10315
10824
  function extractGatewayRoutingInfo(result) {
10316
- if (result?.steps && Array.isArray(result.steps) && result.steps.length > 0) {
10317
- 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;
10318
10828
  if (routing2) {
10319
10829
  return {
10320
10830
  provider: routing2.resolvedProvider || routing2.finalProvider,
@@ -10322,7 +10832,11 @@ function extractGatewayRoutingInfo(result) {
10322
10832
  };
10323
10833
  }
10324
10834
  }
10325
- const routing = result?.providerMetadata?.gateway?.routing;
10835
+ const providerMetadata = safeSerializableFieldRead(
10836
+ result,
10837
+ "providerMetadata"
10838
+ );
10839
+ const routing = providerMetadata?.gateway?.routing;
10326
10840
  if (routing) {
10327
10841
  return {
10328
10842
  provider: routing.resolvedProvider || routing.finalProvider,
@@ -10332,10 +10846,11 @@ function extractGatewayRoutingInfo(result) {
10332
10846
  return null;
10333
10847
  }
10334
10848
  function extractCostFromResult(result) {
10335
- 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) {
10336
10851
  let totalCost = 0;
10337
10852
  let foundCost = false;
10338
- for (const step of result.steps) {
10853
+ for (const step of steps) {
10339
10854
  const gateway2 = step?.providerMetadata?.gateway;
10340
10855
  const stepCost = parseGatewayCost(gateway2?.cost) || parseGatewayCost(gateway2?.marketCost);
10341
10856
  if (stepCost !== void 0 && stepCost > 0) {
@@ -10347,7 +10862,11 @@ function extractCostFromResult(result) {
10347
10862
  return totalCost;
10348
10863
  }
10349
10864
  }
10350
- const gateway = result?.providerMetadata?.gateway;
10865
+ const providerMetadata = safeSerializableFieldRead(
10866
+ result,
10867
+ "providerMetadata"
10868
+ );
10869
+ const gateway = providerMetadata?.gateway;
10351
10870
  const directCost = parseGatewayCost(gateway?.cost) || parseGatewayCost(gateway?.marketCost);
10352
10871
  if (directCost !== void 0 && directCost > 0) {
10353
10872
  return directCost;
@@ -11048,20 +11567,16 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
11048
11567
  );
11049
11568
  });
11050
11569
  },
11051
- onComplete: () => {
11052
- void state.processing.then(() => finalizeQuerySpan(state)).finally(() => {
11053
- spans.delete(event);
11054
- });
11055
- },
11056
- onError: (error) => {
11057
- void state.processing.then(() => {
11058
- state.span.log({
11059
- error: error.message
11060
- });
11061
- }).then(() => finalizeQuerySpan(state)).finally(() => {
11062
- spans.delete(event);
11570
+ onComplete: () => state.processing.then(() => finalizeQuerySpan(state)).finally(() => {
11571
+ spans.delete(event);
11572
+ }),
11573
+ onError: (error) => state.processing.then(() => {
11574
+ state.span.log({
11575
+ error: error.message
11063
11576
  });
11064
- }
11577
+ }).then(() => finalizeQuerySpan(state)).finally(() => {
11578
+ spans.delete(event);
11579
+ })
11065
11580
  });
11066
11581
  return;
11067
11582
  }
@@ -11209,12 +11724,14 @@ var GoogleGenAIPlugin = class extends BasePlugin {
11209
11724
  const params = event.arguments[0];
11210
11725
  streamEvent.googleGenAIInput = serializeInput(params);
11211
11726
  streamEvent.googleGenAIMetadata = extractMetadata(params);
11727
+ streamEvent.googleGenAIStartTime = getCurrentUnixTimestamp();
11212
11728
  },
11213
11729
  asyncEnd: (event) => {
11214
11730
  const streamEvent = event;
11215
11731
  patchGoogleGenAIStreamingResult({
11216
11732
  input: streamEvent.googleGenAIInput,
11217
11733
  metadata: streamEvent.googleGenAIMetadata,
11734
+ startTime: streamEvent.googleGenAIStartTime,
11218
11735
  result: streamEvent.result
11219
11736
  });
11220
11737
  },
@@ -11267,7 +11784,7 @@ function logErrorAndEndSpan(states, event) {
11267
11784
  states.delete(event);
11268
11785
  }
11269
11786
  function patchGoogleGenAIStreamingResult(args) {
11270
- const { input, metadata, result } = args;
11787
+ const { input, metadata, result, startTime } = args;
11271
11788
  if (!input || !metadata || !result || typeof result !== "object" || typeof result.next !== "function") {
11272
11789
  return false;
11273
11790
  }
@@ -11275,7 +11792,7 @@ function patchGoogleGenAIStreamingResult(args) {
11275
11792
  let firstTokenTime = null;
11276
11793
  let finalized = false;
11277
11794
  let span = null;
11278
- let startTime = null;
11795
+ const requestStartTime = startTime ?? getCurrentUnixTimestamp();
11279
11796
  const ensureSpan = () => {
11280
11797
  if (!span) {
11281
11798
  span = startSpan({
@@ -11288,7 +11805,6 @@ function patchGoogleGenAIStreamingResult(args) {
11288
11805
  metadata
11289
11806
  }
11290
11807
  });
11291
- startTime = getCurrentUnixTimestamp();
11292
11808
  }
11293
11809
  return span;
11294
11810
  };
@@ -11342,11 +11858,11 @@ function patchGoogleGenAIStreamingResult(args) {
11342
11858
  }
11343
11859
  chunks.push(nextResult.value);
11344
11860
  }
11345
- if (nextResult.done && startTime !== null) {
11861
+ if (nextResult.done) {
11346
11862
  finalize({
11347
11863
  result: aggregateGenerateContentChunks(
11348
11864
  chunks,
11349
- startTime,
11865
+ requestStartTime,
11350
11866
  firstTokenTime
11351
11867
  )
11352
11868
  });
@@ -11366,13 +11882,13 @@ function patchGoogleGenAIStreamingResult(args) {
11366
11882
  ...returnArgs
11367
11883
  );
11368
11884
  } finally {
11369
- if (startTime !== null) {
11885
+ if (chunks.length > 0) {
11370
11886
  finalize({
11371
- result: chunks.length > 0 ? aggregateGenerateContentChunks(
11887
+ result: aggregateGenerateContentChunks(
11372
11888
  chunks,
11373
- startTime,
11889
+ requestStartTime,
11374
11890
  firstTokenTime
11375
- ) : void 0
11891
+ )
11376
11892
  });
11377
11893
  } else {
11378
11894
  finalize({});
@@ -11664,500 +12180,488 @@ var openRouterChannels = defineChannels("@openrouter/sdk", {
11664
12180
  channelName: "callModel",
11665
12181
  kind: "sync-stream"
11666
12182
  }),
12183
+ callModelTurn: channel({
12184
+ channelName: "callModel.turn",
12185
+ kind: "async"
12186
+ }),
11667
12187
  toolExecute: channel({
11668
12188
  channelName: "tool.execute",
11669
12189
  kind: "async"
11670
12190
  })
11671
12191
  });
11672
12192
 
11673
- // src/openrouter-utils.ts
11674
- var TOKEN_NAME_MAP2 = {
11675
- promptTokens: "prompt_tokens",
11676
- inputTokens: "prompt_tokens",
11677
- completionTokens: "completion_tokens",
11678
- outputTokens: "completion_tokens",
11679
- totalTokens: "tokens",
11680
- prompt_tokens: "prompt_tokens",
11681
- input_tokens: "prompt_tokens",
11682
- completion_tokens: "completion_tokens",
11683
- output_tokens: "completion_tokens",
11684
- total_tokens: "tokens"
11685
- };
11686
- var TOKEN_DETAIL_PREFIX_MAP = {
11687
- promptTokensDetails: "prompt",
11688
- inputTokensDetails: "prompt",
11689
- completionTokensDetails: "completion",
11690
- outputTokensDetails: "completion",
11691
- costDetails: "cost",
11692
- prompt_tokens_details: "prompt",
11693
- input_tokens_details: "prompt",
11694
- completion_tokens_details: "completion",
11695
- output_tokens_details: "completion",
11696
- cost_details: "cost"
11697
- };
11698
- function camelToSnake(value) {
11699
- return value.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`);
11700
- }
11701
- function parseOpenRouterMetricsFromUsage(usage) {
11702
- if (!isObject(usage)) {
11703
- return {};
11704
- }
11705
- const metrics = {};
11706
- for (const [name, value] of Object.entries(usage)) {
11707
- if (typeof value === "number") {
11708
- metrics[TOKEN_NAME_MAP2[name] || camelToSnake(name)] = value;
11709
- continue;
11710
- }
11711
- if (!isObject(value)) {
11712
- continue;
11713
- }
11714
- const prefix = TOKEN_DETAIL_PREFIX_MAP[name];
11715
- if (!prefix) {
11716
- continue;
11717
- }
11718
- for (const [nestedName, nestedValue] of Object.entries(value)) {
11719
- if (typeof nestedValue !== "number") {
11720
- continue;
11721
- }
11722
- metrics[`${prefix}_${camelToSnake(nestedName)}`] = nestedValue;
11723
- }
11724
- }
11725
- return metrics;
11726
- }
11727
- function extractOpenRouterUsageMetadata(usage) {
11728
- if (!isObject(usage)) {
11729
- return void 0;
11730
- }
11731
- const metadata = {};
11732
- if (typeof usage.isByok === "boolean") {
11733
- metadata.is_byok = usage.isByok;
11734
- } else if (typeof usage.is_byok === "boolean") {
11735
- metadata.is_byok = usage.is_byok;
11736
- }
11737
- return Object.keys(metadata).length > 0 ? metadata : void 0;
11738
- }
11739
-
11740
- // src/openrouter-logging.ts
11741
- var OMITTED_OPENROUTER_KEYS = /* @__PURE__ */ new Set([
11742
- "execute",
11743
- "render",
11744
- "nextTurnParams",
11745
- "requireApproval"
11746
- ]);
11747
- function parseOpenRouterModelString(model) {
11748
- if (typeof model !== "string") {
11749
- return { model };
11750
- }
11751
- const slashIndex = model.indexOf("/");
11752
- if (slashIndex > 0 && slashIndex < model.length - 1) {
11753
- return {
11754
- provider: model.substring(0, slashIndex),
11755
- model: model.substring(slashIndex + 1)
11756
- };
11757
- }
11758
- return { model };
11759
- }
11760
- function isZodSchema2(value) {
11761
- return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
11762
- }
11763
- function serializeZodSchema2(schema) {
11764
- try {
11765
- return zodToJsonSchema(schema);
11766
- } catch {
11767
- return {
11768
- type: "object",
11769
- description: "Zod schema (conversion failed)"
11770
- };
11771
- }
11772
- }
11773
- function serializeOpenRouterTool(tool) {
11774
- if (!isObject(tool)) {
11775
- return tool;
11776
- }
11777
- const serialized = {};
11778
- for (const [key, value] of Object.entries(tool)) {
11779
- if (OMITTED_OPENROUTER_KEYS.has(key)) {
11780
- continue;
11781
- }
11782
- if (key === "function" && isObject(value)) {
11783
- serialized.function = sanitizeOpenRouterLoggedValue(value);
11784
- continue;
11785
- }
11786
- serialized[key] = sanitizeOpenRouterLoggedValue(value);
11787
- }
11788
- return serialized;
11789
- }
11790
- function serializeOpenRouterToolsForLogging(tools) {
11791
- if (!Array.isArray(tools)) {
11792
- return void 0;
11793
- }
11794
- return tools.map((tool) => serializeOpenRouterTool(tool));
11795
- }
11796
- function sanitizeOpenRouterLoggedValue(value) {
11797
- if (isZodSchema2(value)) {
11798
- return serializeZodSchema2(value);
11799
- }
11800
- if (typeof value === "function") {
11801
- return "[Function]";
11802
- }
11803
- if (Array.isArray(value)) {
11804
- return value.map((entry) => sanitizeOpenRouterLoggedValue(entry));
11805
- }
11806
- if (!isObject(value)) {
11807
- return value;
12193
+ // src/instrumentation/plugins/openrouter-plugin.ts
12194
+ var OpenRouterPlugin = class extends BasePlugin {
12195
+ onEnable() {
12196
+ this.subscribeToOpenRouterChannels();
11808
12197
  }
11809
- const sanitized = {};
11810
- for (const [key, entry] of Object.entries(value)) {
11811
- if (OMITTED_OPENROUTER_KEYS.has(key)) {
11812
- continue;
11813
- }
11814
- if (key === "tools" && Array.isArray(entry)) {
11815
- sanitized.tools = serializeOpenRouterToolsForLogging(entry);
11816
- continue;
11817
- }
11818
- sanitized[key] = sanitizeOpenRouterLoggedValue(entry);
12198
+ onDisable() {
12199
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
11819
12200
  }
11820
- return sanitized;
11821
- }
11822
- function buildOpenRouterMetadata(metadata, httpReferer, xTitle) {
11823
- const sanitized = sanitizeOpenRouterLoggedValue(metadata);
11824
- const metadataRecord = isObject(sanitized) ? sanitized : {};
11825
- const { model, provider: providerRouting, ...rest } = metadataRecord;
11826
- const normalizedModel = parseOpenRouterModelString(model);
11827
- return {
11828
- ...rest,
11829
- ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
11830
- ...providerRouting !== void 0 ? { providerRouting } : {},
11831
- ...httpReferer !== void 0 ? { httpReferer } : {},
11832
- ...xTitle !== void 0 ? { xTitle } : {},
11833
- provider: normalizedModel.provider || "openrouter"
11834
- };
11835
- }
11836
- function buildOpenRouterEmbeddingMetadata(metadata, httpReferer, xTitle) {
11837
- const normalized = buildOpenRouterMetadata(metadata, httpReferer, xTitle);
11838
- return typeof normalized.model === "string" ? {
11839
- ...normalized,
11840
- embedding_model: normalized.model
11841
- } : normalized;
11842
- }
11843
- function extractOpenRouterCallModelInput(request) {
11844
- return isObject(request) && "input" in request ? sanitizeOpenRouterLoggedValue(request.input) : void 0;
11845
- }
11846
- function extractOpenRouterCallModelMetadata(request) {
11847
- if (!isObject(request)) {
11848
- return { provider: "openrouter" };
11849
- }
11850
- const { input: _input, ...metadata } = request;
11851
- return buildOpenRouterMetadata(metadata, void 0, void 0);
11852
- }
11853
- function extractOpenRouterResponseMetadata(result) {
11854
- if (!isObject(result)) {
11855
- return void 0;
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
+ });
11856
12384
  }
11857
- const { output: _output, data: _data, usage, ...metadata } = result;
11858
- const sanitized = sanitizeOpenRouterLoggedValue(metadata);
11859
- const metadataRecord = isObject(sanitized) ? sanitized : {};
11860
- const { model, provider, ...rest } = metadataRecord;
11861
- const normalizedModel = parseOpenRouterModelString(model);
11862
- const normalizedProvider = (typeof provider === "string" ? provider : void 0) || normalizedModel.provider;
11863
- const usageMetadata = extractOpenRouterUsageMetadata(usage);
11864
- const combined = {
11865
- ...rest,
11866
- ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
11867
- ...usageMetadata || {},
11868
- ...normalizedProvider !== void 0 ? { provider: normalizedProvider } : {}
11869
- };
11870
- return Object.keys(combined).length > 0 ? combined : void 0;
11871
- }
11872
- function extractOpenRouterResponseOutput(response, fallbackOutput) {
11873
- if (isObject(response) && "output" in response && response.output !== void 0) {
11874
- return sanitizeOpenRouterLoggedValue(response.output);
12385
+ };
12386
+ function normalizeArgs(args) {
12387
+ if (Array.isArray(args)) {
12388
+ return args;
11875
12389
  }
11876
- if (fallbackOutput !== void 0) {
11877
- return sanitizeOpenRouterLoggedValue(fallbackOutput);
12390
+ if (isArrayLike(args)) {
12391
+ return Array.from(args);
11878
12392
  }
11879
- return void 0;
12393
+ return [args];
11880
12394
  }
11881
-
11882
- // src/openrouter-tool-wrapping.ts
11883
- var OPENROUTER_WRAPPED_TOOL = Symbol("braintrust.openrouter.wrappedTool");
11884
- var OPENROUTER_WRAPPED_CALL_MODEL_RESULT = Symbol(
11885
- "braintrust.openrouter.wrappedCallModelResult"
11886
- );
11887
- var OPENROUTER_CALL_MODEL_STREAM_METHODS = [
11888
- "getFullResponsesStream",
11889
- "getItemsStream",
11890
- "getNewMessagesStream",
11891
- "getReasoningStream",
11892
- "getTextStream",
11893
- "getToolCallsStream",
11894
- "getToolStream"
11895
- ];
11896
- var OPENROUTER_CALL_MODEL_CONTEXT_METHODS = [
11897
- "cancel",
11898
- "getPendingToolCalls",
11899
- "getState",
11900
- "getToolCalls",
11901
- "requiresApproval"
11902
- ];
11903
- function patchOpenRouterCallModelRequestTools(request) {
11904
- if (!Array.isArray(request.tools) || request.tools.length === 0) {
11905
- return void 0;
11906
- }
11907
- const originalTools = request.tools;
11908
- const wrappedTools = originalTools.map((tool) => wrapOpenRouterTool(tool));
11909
- const didPatch = wrappedTools.some(
11910
- (tool, index) => tool !== originalTools[index]
12395
+ function isArrayLike(value) {
12396
+ return isObject(value) && "length" in value && typeof value.length === "number" && Number.isInteger(value.length) && value.length >= 0;
12397
+ }
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)
11911
12402
  );
11912
- if (!didPatch) {
11913
- return void 0;
12403
+ if (isObject(keyedCandidate)) {
12404
+ return keyedCandidate;
11914
12405
  }
11915
- request.tools = wrappedTools;
11916
- return () => {
11917
- request.tools = originalTools;
11918
- };
12406
+ const firstObjectArg = normalizedArgs.find((arg) => isObject(arg));
12407
+ return isObject(firstObjectArg) ? firstObjectArg : void 0;
11919
12408
  }
11920
- function patchOpenRouterCallModelResult(span, result, request) {
11921
- if (!isObject(result) || isWrappedCallModelResult(result)) {
11922
- return false;
11923
- }
11924
- const resultLike = result;
11925
- const hasInstrumentableMethod = typeof resultLike.getResponse === "function" || typeof resultLike.getText === "function" || OPENROUTER_CALL_MODEL_STREAM_METHODS.some(
11926
- (methodName) => typeof resultLike[methodName] === "function"
11927
- );
11928
- if (!hasInstrumentableMethod) {
11929
- return false;
12409
+ function getOpenRouterCallModelRequestArg(args) {
12410
+ const firstObjectArg = normalizeArgs(args).find((arg) => isObject(arg));
12411
+ return isObject(firstObjectArg) ? firstObjectArg : void 0;
12412
+ }
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 {};
11930
12443
  }
11931
- Object.defineProperty(resultLike, OPENROUTER_WRAPPED_CALL_MODEL_RESULT, {
11932
- value: true,
11933
- enumerable: false,
11934
- configurable: false
11935
- });
11936
- const originalGetResponse = typeof resultLike.getResponse === "function" ? resultLike.getResponse.bind(resultLike) : void 0;
11937
- const originalGetInitialResponse = typeof resultLike.getInitialResponse === "function" ? resultLike.getInitialResponse.bind(resultLike) : void 0;
11938
- const originalMakeFollowupRequest = typeof resultLike.makeFollowupRequest === "function" ? resultLike.makeFollowupRequest.bind(resultLike) : void 0;
11939
- let ended = false;
11940
- let tracedTurnCount = 0;
11941
- const endSpanWithResult = async (response, fallbackOutput) => {
11942
- if (ended) {
11943
- return;
11944
- }
11945
- ended = true;
11946
- const finalResponse = getFinalOpenRouterCallModelResponse(
11947
- resultLike,
11948
- response
11949
- );
11950
- if (finalResponse) {
11951
- const rounds = getOpenRouterCallModelRounds(resultLike);
11952
- const metadata = extractOpenRouterCallModelResultMetadata(
11953
- finalResponse,
11954
- rounds.length + 1
11955
- );
11956
- span.log({
11957
- output: extractOpenRouterResponseOutput(finalResponse, fallbackOutput),
11958
- ...metadata ? { metadata } : {},
11959
- metrics: aggregateOpenRouterCallModelMetrics(rounds, finalResponse)
11960
- });
11961
- span.end();
11962
- return;
11963
- }
11964
- if (fallbackOutput !== void 0) {
11965
- span.log({
11966
- output: fallbackOutput
11967
- });
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;
11968
12449
  }
11969
- span.end();
11970
- };
11971
- const endSpanWithError = (error) => {
11972
- if (ended) {
11973
- return;
12450
+ if (!isObject(value)) {
12451
+ continue;
11974
12452
  }
11975
- ended = true;
11976
- span.log({
11977
- error: normalizeError(error).message
11978
- });
11979
- span.end();
11980
- };
11981
- const finalizeFromResponse = async (fallbackOutput) => {
11982
- if (!originalGetResponse) {
11983
- await endSpanWithResult(void 0, fallbackOutput);
11984
- return;
12453
+ const prefix = TOKEN_DETAIL_PREFIX_MAP[name];
12454
+ if (!prefix) {
12455
+ continue;
11985
12456
  }
11986
- try {
11987
- await endSpanWithResult(await originalGetResponse(), fallbackOutput);
11988
- } catch {
11989
- await endSpanWithResult(void 0, fallbackOutput);
12457
+ for (const [nestedName, nestedValue] of Object.entries(value)) {
12458
+ if (typeof nestedValue !== "number") {
12459
+ continue;
12460
+ }
12461
+ metrics[`${prefix}_${camelToSnake(nestedName)}`] = nestedValue;
11990
12462
  }
11991
- };
11992
- if (originalGetResponse) {
11993
- resultLike.getResponse = async (...args) => {
11994
- return await withCurrent(span, async () => {
11995
- try {
11996
- const response = await originalGetResponse(...args);
11997
- await endSpanWithResult(response);
11998
- return response;
11999
- } catch (error) {
12000
- endSpanWithError(error);
12001
- throw error;
12002
- }
12003
- });
12004
- };
12005
12463
  }
12006
- if (typeof resultLike.getText === "function") {
12007
- const originalGetText = resultLike.getText.bind(resultLike);
12008
- resultLike.getText = async (...args) => {
12009
- return await withCurrent(span, async () => {
12010
- try {
12011
- const text = await originalGetText(...args);
12012
- await finalizeFromResponse(text);
12013
- return text;
12014
- } catch (error) {
12015
- endSpanWithError(error);
12016
- throw error;
12017
- }
12018
- });
12019
- };
12464
+ return metrics;
12465
+ }
12466
+ function extractOpenRouterUsageMetadata(usage) {
12467
+ if (!isObject(usage)) {
12468
+ return void 0;
12020
12469
  }
12021
- for (const methodName of OPENROUTER_CALL_MODEL_CONTEXT_METHODS) {
12022
- if (typeof resultLike[methodName] !== "function") {
12023
- continue;
12024
- }
12025
- const originalMethod = resultLike[methodName];
12026
- resultLike[methodName] = async (...args) => {
12027
- return await withCurrent(span, async () => {
12028
- return await originalMethod.apply(resultLike, args);
12029
- });
12030
- };
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;
12031
12475
  }
12032
- for (const methodName of OPENROUTER_CALL_MODEL_STREAM_METHODS) {
12033
- if (typeof resultLike[methodName] !== "function") {
12034
- continue;
12035
- }
12036
- const originalMethod = resultLike[methodName];
12037
- resultLike[methodName] = (...args) => {
12038
- const stream = withCurrent(
12039
- span,
12040
- () => originalMethod.apply(resultLike, args)
12041
- );
12042
- if (!isAsyncIterable2(stream)) {
12043
- return stream;
12044
- }
12045
- return wrapAsyncIterableWithSpan({
12046
- finalize: finalizeFromResponse,
12047
- iteratorFactory: () => stream[Symbol.asyncIterator](),
12048
- onError: endSpanWithError,
12049
- span
12050
- });
12051
- };
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 };
12052
12487
  }
12053
- if (originalGetInitialResponse) {
12054
- let initialTurnTraced = false;
12055
- resultLike.getInitialResponse = async (...args) => {
12056
- if (initialTurnTraced) {
12057
- return await withCurrent(span, async () => {
12058
- return await originalGetInitialResponse(...args);
12059
- });
12060
- }
12061
- initialTurnTraced = true;
12062
- const resolvedRequest = getOpenRouterResolvedRequest(resultLike, request);
12063
- const childSpan = startOpenRouterCallModelTurnSpan({
12064
- request: resolvedRequest,
12065
- step: tracedTurnCount + 1,
12066
- stepType: tracedTurnCount === 0 ? "initial" : "continue"
12067
- });
12068
- return await withCurrent(childSpan, async () => {
12069
- try {
12070
- const response = await originalGetInitialResponse(...args);
12071
- tracedTurnCount++;
12072
- finishOpenRouterCallModelTurnSpan({
12073
- response,
12074
- step: tracedTurnCount,
12075
- stepType: tracedTurnCount === 1 ? "initial" : "continue",
12076
- span: childSpan
12077
- });
12078
- return response;
12079
- } catch (error) {
12080
- childSpan.log({
12081
- error: normalizeError(error).message
12082
- });
12083
- childSpan.end();
12084
- throw error;
12085
- }
12086
- });
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)
12087
12493
  };
12088
12494
  }
12089
- if (originalMakeFollowupRequest) {
12090
- resultLike.makeFollowupRequest = async (...args) => {
12091
- const currentResponse = args[0];
12092
- const toolResults = Array.isArray(args[1]) ? args[1] : [];
12093
- const resolvedRequest = getOpenRouterResolvedRequest(resultLike, request);
12094
- const followupRequest = buildOpenRouterFollowupRequest(
12095
- resolvedRequest,
12096
- currentResponse,
12097
- toolResults
12098
- );
12099
- const childSpan = startOpenRouterCallModelTurnSpan({
12100
- request: followupRequest,
12101
- step: tracedTurnCount + 1,
12102
- stepType: "continue"
12103
- });
12104
- return await withCurrent(childSpan, async () => {
12105
- try {
12106
- const response = await originalMakeFollowupRequest(...args);
12107
- tracedTurnCount++;
12108
- finishOpenRouterCallModelTurnSpan({
12109
- response,
12110
- step: tracedTurnCount,
12111
- stepType: "continue",
12112
- span: childSpan
12113
- });
12114
- return response;
12115
- } catch (error) {
12116
- childSpan.log({
12117
- error: normalizeError(error).message
12118
- });
12119
- childSpan.end();
12120
- throw error;
12121
- }
12122
- });
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)"
12123
12507
  };
12124
12508
  }
12125
- return true;
12126
12509
  }
12127
- function wrapOpenRouterTool(tool) {
12128
- if (isWrappedTool(tool) || !tool.function || typeof tool.function !== "object" || typeof tool.function.execute !== "function") {
12510
+ function serializeOpenRouterTool(tool) {
12511
+ if (!isObject(tool)) {
12129
12512
  return tool;
12130
12513
  }
12131
- const toolName = tool.function.name || "tool";
12132
- const originalExecute = tool.function.execute;
12133
- const wrappedTool = {
12134
- ...tool,
12135
- function: {
12136
- ...tool.function,
12137
- execute(...args) {
12138
- return traceToolExecution({
12139
- args,
12140
- execute: () => Reflect.apply(originalExecute, this, args),
12141
- toolCallId: getToolCallId(args[1]),
12142
- toolName
12143
- });
12144
- }
12514
+ const serialized = {};
12515
+ for (const [key, value] of Object.entries(tool)) {
12516
+ if (OMITTED_OPENROUTER_KEYS.has(key)) {
12517
+ continue;
12145
12518
  }
12146
- };
12147
- Object.defineProperty(wrappedTool, OPENROUTER_WRAPPED_TOOL, {
12148
- value: true,
12149
- enumerable: false,
12150
- configurable: false
12151
- });
12152
- return wrappedTool;
12153
- }
12154
- function isWrappedTool(tool) {
12155
- return Boolean(tool[OPENROUTER_WRAPPED_TOOL]);
12519
+ if (key === "function" && isObject(value)) {
12520
+ serialized.function = sanitizeOpenRouterLoggedValue(value);
12521
+ continue;
12522
+ }
12523
+ serialized[key] = sanitizeOpenRouterLoggedValue(value);
12524
+ }
12525
+ return serialized;
12156
12526
  }
12157
- function isWrappedCallModelResult(value) {
12158
- return Boolean(
12159
- isObject(value) && value[OPENROUTER_WRAPPED_CALL_MODEL_RESULT]
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");
12619
+ function patchOpenRouterCallModelRequestTools(request) {
12620
+ if (!Array.isArray(request.tools) || request.tools.length === 0) {
12621
+ return void 0;
12622
+ }
12623
+ const originalTools = request.tools;
12624
+ const wrappedTools = originalTools.map((tool) => wrapOpenRouterTool(tool));
12625
+ const didPatch = wrappedTools.some(
12626
+ (tool, index) => tool !== originalTools[index]
12160
12627
  );
12628
+ if (!didPatch) {
12629
+ return void 0;
12630
+ }
12631
+ request.tools = wrappedTools;
12632
+ return () => {
12633
+ request.tools = originalTools;
12634
+ };
12635
+ }
12636
+ function wrapOpenRouterTool(tool) {
12637
+ if (isWrappedTool(tool) || !tool.function || typeof tool.function !== "object" || typeof tool.function.execute !== "function") {
12638
+ return tool;
12639
+ }
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, {
12657
+ value: true,
12658
+ enumerable: false,
12659
+ configurable: false
12660
+ });
12661
+ return wrappedTool;
12662
+ }
12663
+ function isWrappedTool(tool) {
12664
+ return Boolean(tool[OPENROUTER_WRAPPED_TOOL]);
12161
12665
  }
12162
12666
  function traceToolExecution(args) {
12163
12667
  const tracingChannel = openRouterChannels.toolExecute.tracingChannel();
@@ -12181,7 +12685,7 @@ function traceToolExecution(args) {
12181
12685
  }
12182
12686
  }
12183
12687
  function publishToolResult(tracingChannel, event, result) {
12184
- if (isPromiseLike(result)) {
12688
+ if (isPromiseLike2(result)) {
12185
12689
  return result.then(
12186
12690
  (resolved) => {
12187
12691
  event.result = resolved;
@@ -12203,465 +12707,460 @@ function getToolCallId(context) {
12203
12707
  const toolContext = context;
12204
12708
  return typeof toolContext?.toolCall?.id === "string" ? toolContext.toolCall.id : void 0;
12205
12709
  }
12206
- function extractOpenRouterCallModelResultMetadata(response, turnCount) {
12207
- const combined = {
12208
- ...extractOpenRouterResponseMetadata(response) || {},
12209
- ...turnCount !== void 0 ? { turn_count: turnCount } : {}
12210
- };
12211
- return Object.keys(combined).length > 0 ? combined : void 0;
12212
- }
12213
- function getFinalOpenRouterCallModelResponse(result, response) {
12214
- if (isObject(response)) {
12215
- return response;
12216
- }
12217
- return isObject(result.finalResponse) ? result.finalResponse : void 0;
12218
- }
12219
- function getOpenRouterCallModelRounds(result) {
12220
- if (!Array.isArray(result.allToolExecutionRounds)) {
12221
- return [];
12222
- }
12223
- return result.allToolExecutionRounds.filter((round) => isObject(round)).map((round) => ({
12224
- response: isObject(round.response) ? round.response : void 0,
12225
- round: typeof round.round === "number" ? round.round : void 0,
12226
- toolResults: Array.isArray(round.toolResults) ? round.toolResults : []
12227
- })).filter((round) => round.response !== void 0);
12228
- }
12229
- function aggregateOpenRouterCallModelMetrics(rounds, finalResponse) {
12230
- const metrics = {};
12231
- const responses = [
12232
- ...rounds.map((round) => round.response).filter(isObject),
12233
- finalResponse
12234
- ];
12235
- for (const response of responses) {
12236
- const responseMetrics = parseOpenRouterMetricsFromUsage(response.usage);
12237
- for (const [name, value] of Object.entries(responseMetrics)) {
12238
- metrics[name] = (metrics[name] || 0) + value;
12239
- }
12240
- }
12241
- return metrics;
12242
- }
12243
- function buildNextOpenRouterCallModelInput(currentInput, response, toolResults) {
12244
- const normalizedInput = Array.isArray(currentInput) ? [...currentInput] : currentInput === void 0 ? [] : [currentInput];
12245
- const responseOutput = Array.isArray(response.output) ? response.output : response.output === void 0 ? [] : [response.output];
12246
- return [...normalizedInput, ...responseOutput, ...toolResults].map(
12247
- (entry) => sanitizeOpenRouterLoggedValue(entry)
12248
- );
12710
+ function isPromiseLike2(value) {
12711
+ return !!value && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
12249
12712
  }
12250
- function startOpenRouterCallModelTurnSpan(args) {
12251
- const requestRecord = isObject(args.request) ? args.request : void 0;
12252
- const metadata = requestRecord ? extractOpenRouterCallModelMetadata(requestRecord) : { provider: "openrouter" };
12253
- if (isObject(metadata) && "tools" in metadata) {
12254
- delete metadata.tools;
12255
- }
12256
- return startSpan({
12257
- name: "openrouter.beta.responses.send",
12258
- spanAttributes: {
12259
- type: "llm" /* LLM */
12260
- },
12261
- event: {
12262
- input: requestRecord ? extractOpenRouterCallModelInput(requestRecord) : void 0,
12263
- metadata: {
12264
- ...metadata,
12265
- step: args.step,
12266
- step_type: args.stepType
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;
12267
12729
  }
12730
+ continue;
12268
12731
  }
12269
- });
12270
- }
12271
- function finishOpenRouterCallModelTurnSpan(args) {
12272
- if (!isObject(args.response)) {
12273
- args.span.end();
12274
- return;
12275
- }
12276
- args.span.log({
12277
- output: extractOpenRouterResponseOutput(args.response),
12278
- ...extractOpenRouterResponseMetadata(args.response) ? {
12279
- metadata: {
12280
- ...extractOpenRouterResponseMetadata(args.response),
12281
- ...args.step !== void 0 ? { step: args.step } : {},
12282
- ...args.stepType ? { step_type: args.stepType } : {}
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;
12283
12752
  }
12284
- } : {},
12285
- metrics: parseOpenRouterMetricsFromUsage(args.response.usage)
12286
- });
12287
- args.span.end();
12288
- }
12289
- function getOpenRouterResolvedRequest(result, request) {
12290
- if (isObject(result.resolvedRequest)) {
12291
- return result.resolvedRequest;
12292
- }
12293
- return request;
12294
- }
12295
- function buildOpenRouterFollowupRequest(request, currentResponse, toolResults) {
12296
- if (!request) {
12297
- return void 0;
12298
- }
12299
- return {
12300
- ...request,
12301
- input: buildNextOpenRouterCallModelInput(
12302
- extractOpenRouterCallModelInput(request),
12303
- isObject(currentResponse) ? currentResponse : {},
12304
- toolResults
12305
- ),
12306
- stream: false
12307
- };
12308
- }
12309
- function wrapAsyncIterableWithSpan(args) {
12310
- return {
12311
- [Symbol.asyncIterator]() {
12312
- const iterator = args.iteratorFactory();
12313
- return {
12314
- next(value) {
12315
- return withCurrent(
12316
- args.span,
12317
- () => value === void 0 ? iterator.next() : iterator.next(value)
12318
- ).then(
12319
- async (result) => {
12320
- if (result.done) {
12321
- await args.finalize();
12322
- }
12323
- return result;
12324
- },
12325
- (error) => {
12326
- args.onError(error);
12327
- throw error;
12328
- }
12329
- );
12330
- },
12331
- return(value) {
12332
- if (typeof iterator.return !== "function") {
12333
- return args.finalize().then(() => ({
12334
- done: true,
12335
- value
12336
- }));
12337
- }
12338
- return withCurrent(args.span, () => iterator.return(value)).then(
12339
- async (result) => {
12340
- await args.finalize();
12341
- return result;
12342
- },
12343
- (error) => {
12344
- args.onError(error);
12345
- throw error;
12346
- }
12347
- );
12348
- },
12349
- throw(error) {
12350
- args.onError(error);
12351
- if (typeof iterator.throw !== "function") {
12352
- return Promise.reject(error);
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 || ""
12353
12764
  }
12354
- return withCurrent(args.span, () => iterator.throw(error));
12355
- },
12356
- [Symbol.asyncIterator]() {
12357
- return this;
12358
- }
12359
- };
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 || "";
12360
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
12361
12796
  };
12362
12797
  }
12363
- function isAsyncIterable2(value) {
12364
- return !!value && (typeof value === "object" || typeof value === "function") && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
12365
- }
12366
- function isPromiseLike(value) {
12367
- return !!value && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
12368
- }
12369
- function normalizeError(error) {
12370
- return error instanceof Error ? error : new Error(String(error));
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
+ };
12371
12820
  }
12372
-
12373
- // src/instrumentation/plugins/openrouter-plugin.ts
12374
- var OpenRouterPlugin = class extends BasePlugin {
12375
- onEnable() {
12376
- this.subscribeToOpenRouterChannels();
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;
12377
12844
  }
12378
- onDisable() {
12379
- this.unsubscribers = unsubscribeAll(this.unsubscribers);
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;
12380
12851
  }
12381
- subscribeToOpenRouterChannels() {
12382
- this.unsubscribers.push(
12383
- traceStreamingChannel(openRouterChannels.chatSend, {
12384
- name: "openrouter.chat.send",
12385
- type: "llm" /* LLM */,
12386
- extractInput: (args) => {
12387
- const request = getOpenRouterRequestArg(args);
12388
- const chatGenerationParams = isObject(request?.chatGenerationParams) ? request.chatGenerationParams : {};
12389
- const httpReferer = request?.httpReferer;
12390
- const xTitle = request?.xTitle;
12391
- const { messages, ...metadata } = chatGenerationParams;
12392
- return {
12393
- input: messages,
12394
- metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
12395
- };
12396
- },
12397
- extractOutput: (result) => {
12398
- return isObject(result) ? result.choices : void 0;
12399
- },
12400
- extractMetrics: (result, startTime) => {
12401
- const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
12402
- if (startTime) {
12403
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
12404
- }
12405
- return metrics;
12406
- },
12407
- aggregateChunks: aggregateOpenRouterChatChunks
12408
- })
12409
- );
12410
- this.unsubscribers.push(
12411
- traceAsyncChannel(openRouterChannels.embeddingsGenerate, {
12412
- name: "openrouter.embeddings.generate",
12413
- type: "llm" /* LLM */,
12414
- extractInput: (args) => {
12415
- const request = getOpenRouterRequestArg(args);
12416
- const requestBody = isObject(request?.requestBody) ? request.requestBody : {};
12417
- const httpReferer = request?.httpReferer;
12418
- const xTitle = request?.xTitle;
12419
- const { input, ...metadata } = requestBody;
12420
- return {
12421
- input,
12422
- metadata: buildOpenRouterEmbeddingMetadata(
12423
- metadata,
12424
- httpReferer,
12425
- xTitle
12426
- )
12427
- };
12428
- },
12429
- extractOutput: (result) => {
12430
- if (!isObject(result)) {
12431
- return void 0;
12432
- }
12433
- const embedding = result.data?.[0]?.embedding;
12434
- return Array.isArray(embedding) ? { embedding_length: embedding.length } : void 0;
12435
- },
12436
- extractMetadata: (result) => {
12437
- if (!isObject(result)) {
12438
- return void 0;
12439
- }
12440
- return extractOpenRouterResponseMetadata(result);
12441
- },
12442
- extractMetrics: (result) => {
12443
- return isObject(result) ? parseOpenRouterMetricsFromUsage(result.usage) : {};
12444
- }
12445
- })
12446
- );
12447
- this.unsubscribers.push(
12448
- traceStreamingChannel(openRouterChannels.betaResponsesSend, {
12449
- name: "openrouter.beta.responses.send",
12450
- type: "llm" /* LLM */,
12451
- extractInput: (args) => {
12452
- const request = getOpenRouterRequestArg(args);
12453
- const openResponsesRequest = isObject(request?.openResponsesRequest) ? request.openResponsesRequest : {};
12454
- const httpReferer = request?.httpReferer;
12455
- const xTitle = request?.xTitle;
12456
- const { input, ...metadata } = openResponsesRequest;
12457
- return {
12458
- input,
12459
- metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
12460
- };
12461
- },
12462
- extractOutput: (result) => extractOpenRouterResponseOutput(result),
12463
- extractMetadata: (result) => extractOpenRouterResponseMetadata(result),
12464
- extractMetrics: (result, startTime) => {
12465
- const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
12466
- if (startTime) {
12467
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
12468
- }
12469
- return metrics;
12470
- },
12471
- aggregateChunks: aggregateOpenRouterResponseStreamEvents
12472
- })
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) {
12864
+ return;
12865
+ }
12866
+ ended = true;
12867
+ const finalResponse = getFinalOpenRouterCallModelResponse(
12868
+ resultLike,
12869
+ response
12473
12870
  );
12474
- this.unsubscribers.push(
12475
- traceSyncStreamChannel(openRouterChannels.callModel, {
12476
- name: "openrouter.callModel",
12477
- type: "llm" /* LLM */,
12478
- extractInput: (args) => {
12479
- const request = getOpenRouterCallModelRequestArg(args);
12480
- return {
12481
- input: request ? extractOpenRouterCallModelInput(request) : void 0,
12482
- metadata: request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" }
12483
- };
12484
- },
12485
- patchResult: ({ endEvent, result, span }) => {
12486
- return patchOpenRouterCallModelResult(
12487
- span,
12488
- result,
12489
- getOpenRouterCallModelRequestArg(endEvent.arguments)
12490
- );
12871
+ if (finalResponse) {
12872
+ const rounds = getOpenRouterCallModelRounds(resultLike);
12873
+ const metadata = extractOpenRouterCallModelResultMetadata(
12874
+ finalResponse,
12875
+ rounds.length + 1
12876
+ );
12877
+ span.log({
12878
+ output: extractOpenRouterResponseOutput(finalResponse, fallbackOutput),
12879
+ ...metadata ? { metadata } : {},
12880
+ metrics: aggregateOpenRouterCallModelMetrics(rounds, finalResponse)
12881
+ });
12882
+ span.end();
12883
+ return;
12884
+ }
12885
+ if (fallbackOutput !== void 0) {
12886
+ span.log({
12887
+ output: fallbackOutput
12888
+ });
12889
+ }
12890
+ span.end();
12891
+ };
12892
+ const endSpanWithError = (error) => {
12893
+ if (ended) {
12894
+ return;
12895
+ }
12896
+ ended = true;
12897
+ span.log({
12898
+ error: normalizeError(error).message
12899
+ });
12900
+ span.end();
12901
+ };
12902
+ const finalizeFromResponse = async (fallbackOutput) => {
12903
+ if (!originalGetResponse) {
12904
+ await endSpanWithResult(void 0, fallbackOutput);
12905
+ return;
12906
+ }
12907
+ try {
12908
+ await endSpanWithResult(await originalGetResponse(), fallbackOutput);
12909
+ } catch {
12910
+ await endSpanWithResult(void 0, fallbackOutput);
12911
+ }
12912
+ };
12913
+ if (originalGetResponse) {
12914
+ resultLike.getResponse = async (...args2) => {
12915
+ return await withCurrent(span, async () => {
12916
+ try {
12917
+ const response = await originalGetResponse(...args2);
12918
+ await endSpanWithResult(response);
12919
+ return response;
12920
+ } catch (error) {
12921
+ endSpanWithError(error);
12922
+ throw error;
12491
12923
  }
12492
- })
12493
- );
12494
- this.unsubscribers.push(
12495
- traceStreamingChannel(openRouterChannels.toolExecute, {
12496
- name: "openrouter.tool",
12497
- type: "tool" /* TOOL */,
12498
- extractInput: (args, event) => ({
12499
- input: args[0],
12500
- metadata: {
12501
- provider: "openrouter",
12502
- tool_name: event.toolName,
12503
- ...event.toolCallId ? { tool_call_id: event.toolCallId } : {}
12504
- }
12505
- }),
12506
- extractOutput: (result) => result,
12507
- extractMetrics: () => ({}),
12508
- aggregateChunks: (chunks) => ({
12509
- output: chunks.length > 0 ? chunks[chunks.length - 1] : void 0,
12510
- metrics: {}
12511
- })
12512
- })
12513
- );
12514
- const callModelChannel = openRouterChannels.callModel.tracingChannel();
12515
- const callModelHandlers = {
12516
- start: (event) => {
12517
- const request = getOpenRouterCallModelRequestArg(event.arguments);
12518
- if (!request) {
12519
- return;
12924
+ });
12925
+ };
12926
+ }
12927
+ if (typeof resultLike.getText === "function") {
12928
+ const originalGetText = resultLike.getText.bind(resultLike);
12929
+ resultLike.getText = async (...args2) => {
12930
+ return await withCurrent(span, async () => {
12931
+ try {
12932
+ const text = await originalGetText(...args2);
12933
+ await finalizeFromResponse(text);
12934
+ return text;
12935
+ } catch (error) {
12936
+ endSpanWithError(error);
12937
+ throw error;
12520
12938
  }
12521
- patchOpenRouterCallModelRequestTools(request);
12939
+ });
12940
+ };
12941
+ }
12942
+ for (const methodName of OPENROUTER_CALL_MODEL_CONTEXT_METHODS) {
12943
+ if (typeof resultLike[methodName] !== "function") {
12944
+ continue;
12945
+ }
12946
+ const originalMethod = resultLike[methodName];
12947
+ resultLike[methodName] = async (...args2) => {
12948
+ return await withCurrent(span, async () => {
12949
+ return await originalMethod.apply(resultLike, args2);
12950
+ });
12951
+ };
12952
+ }
12953
+ for (const methodName of OPENROUTER_CALL_MODEL_STREAM_METHODS) {
12954
+ if (typeof resultLike[methodName] !== "function") {
12955
+ continue;
12956
+ }
12957
+ const originalMethod = resultLike[methodName];
12958
+ resultLike[methodName] = (...args2) => {
12959
+ const stream = withCurrent(
12960
+ span,
12961
+ () => originalMethod.apply(resultLike, args2)
12962
+ );
12963
+ if (!isAsyncIterable2(stream)) {
12964
+ return stream;
12965
+ }
12966
+ return wrapAsyncIterableWithSpan({
12967
+ finalize: finalizeFromResponse,
12968
+ iteratorFactory: () => stream[Symbol.asyncIterator](),
12969
+ onError: endSpanWithError,
12970
+ span
12971
+ });
12972
+ };
12973
+ }
12974
+ if (originalGetInitialResponse) {
12975
+ let initialTurnTraced = false;
12976
+ resultLike.getInitialResponse = async (...args2) => {
12977
+ if (initialTurnTraced) {
12978
+ return await withCurrent(span, async () => {
12979
+ return await originalGetInitialResponse(...args2);
12980
+ });
12522
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;
12523
12997
  };
12524
- callModelChannel.subscribe(callModelHandlers);
12525
- this.unsubscribers.push(() => {
12526
- callModelChannel.unsubscribe(callModelHandlers);
12527
- });
12528
- }
12529
- };
12530
- function normalizeArgs(args) {
12531
- if (Array.isArray(args)) {
12532
- return args;
12533
12998
  }
12534
- if (isArrayLike(args)) {
12535
- 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
+ };
12536
13021
  }
12537
- return [args];
13022
+ return true;
12538
13023
  }
12539
- function isArrayLike(value) {
12540
- 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
+ );
12541
13034
  }
12542
- function getOpenRouterRequestArg(args) {
12543
- const normalizedArgs = normalizeArgs(args);
12544
- const keyedCandidate = normalizedArgs.find(
12545
- (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]
12546
13038
  );
12547
- if (isObject(keyedCandidate)) {
12548
- 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;
12549
13050
  }
12550
- const firstObjectArg = normalizedArgs.find((arg) => isObject(arg));
12551
- return isObject(firstObjectArg) ? firstObjectArg : void 0;
13051
+ return isObject(result.finalResponse) ? result.finalResponse : void 0;
12552
13052
  }
12553
- function getOpenRouterCallModelRequestArg(args) {
12554
- const firstObjectArg = normalizeArgs(args).find((arg) => isObject(arg));
12555
- 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);
12556
13062
  }
12557
- function aggregateOpenRouterChatChunks(chunks) {
12558
- let role;
12559
- let content = "";
12560
- let toolCalls;
12561
- let finishReason;
12562
- let metrics = {};
12563
- for (const chunk of chunks) {
12564
- metrics = {
12565
- ...metrics,
12566
- ...parseOpenRouterMetricsFromUsage(chunk?.usage)
12567
- };
12568
- const choice = chunk?.choices?.[0];
12569
- const delta = choice?.delta;
12570
- if (!delta) {
12571
- if (choice?.finish_reason !== void 0) {
12572
- finishReason = choice.finish_reason;
12573
- }
12574
- continue;
12575
- }
12576
- if (!role && delta.role) {
12577
- role = delta.role;
12578
- }
12579
- if (typeof delta.content === "string") {
12580
- content += delta.content;
12581
- }
12582
- const choiceFinishReason = choice?.finishReason ?? choice?.finish_reason ?? void 0;
12583
- const deltaFinishReason = delta.finishReason ?? delta.finish_reason ?? void 0;
12584
- if (choiceFinishReason !== void 0) {
12585
- finishReason = choiceFinishReason;
12586
- } else if (deltaFinishReason !== void 0) {
12587
- finishReason = deltaFinishReason;
12588
- }
12589
- const toolCallDeltas = Array.isArray(delta.toolCalls) ? delta.toolCalls : Array.isArray(delta.tool_calls) ? delta.tool_calls : void 0;
12590
- if (!toolCallDeltas) {
12591
- continue;
12592
- }
12593
- for (const toolDelta of toolCallDeltas) {
12594
- if (!toolDelta?.function) {
12595
- continue;
12596
- }
12597
- const toolIndex = toolDelta.index ?? 0;
12598
- const existingToolCall = toolCalls?.[toolIndex];
12599
- if (!existingToolCall || toolDelta.id && existingToolCall.id !== void 0 && existingToolCall.id !== toolDelta.id) {
12600
- const nextToolCalls = [...toolCalls || []];
12601
- nextToolCalls[toolIndex] = {
12602
- index: toolIndex,
12603
- id: toolDelta.id,
12604
- type: toolDelta.type,
12605
- function: {
12606
- name: toolDelta.function.name,
12607
- arguments: toolDelta.function.arguments || ""
12608
- }
12609
- };
12610
- toolCalls = nextToolCalls;
12611
- continue;
12612
- }
12613
- const current = existingToolCall;
12614
- if (toolDelta.id && !current.id) {
12615
- current.id = toolDelta.id;
12616
- }
12617
- if (toolDelta.type && !current.type) {
12618
- current.type = toolDelta.type;
12619
- }
12620
- if (toolDelta.function.name && !current.function.name) {
12621
- current.function.name = toolDelta.function.name;
12622
- }
12623
- 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;
12624
13073
  }
12625
13074
  }
12626
- return {
12627
- output: [
12628
- {
12629
- index: 0,
12630
- message: {
12631
- role,
12632
- content: content || void 0,
12633
- ...toolCalls ? { tool_calls: toolCalls } : {}
12634
- },
12635
- logprobs: null,
12636
- finish_reason: finishReason
12637
- }
12638
- ],
12639
- metrics
12640
- };
13075
+ return metrics;
12641
13076
  }
12642
- function aggregateOpenRouterResponseStreamEvents(chunks) {
12643
- let finalResponse;
12644
- for (const chunk of chunks) {
12645
- const response = chunk?.response;
12646
- if (!response) {
12647
- continue;
12648
- }
12649
- if (chunk.type === "response.completed" || chunk.type === "response.incomplete" || chunk.type === "response.failed") {
12650
- finalResponse = response;
12651
- }
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;
12652
13087
  }
12653
- if (!finalResponse) {
12654
- return {
12655
- output: void 0,
12656
- metrics: {}
12657
- };
13088
+ return request;
13089
+ }
13090
+ function buildOpenRouterFollowupRequest(request, currentResponse, toolResults) {
13091
+ if (!request) {
13092
+ return void 0;
12658
13093
  }
12659
13094
  return {
12660
- output: extractOpenRouterResponseOutput(finalResponse),
12661
- metrics: parseOpenRouterMetricsFromUsage(finalResponse.usage),
12662
- ...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
+ }
12663
13156
  };
12664
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
+ }
12665
13164
 
12666
13165
  // src/instrumentation/braintrust-plugin.ts
12667
13166
  var BraintrustPlugin = class extends BasePlugin {