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
@@ -111,7 +111,7 @@ var DefaultTracingChannel = class {
111
111
  }
112
112
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
113
113
  tracePromise(fn, _message, thisArg, ...args) {
114
- return Promise.resolve(fn.apply(thisArg, args));
114
+ return fn.apply(thisArg, args);
115
115
  }
116
116
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
117
117
  traceCallback(fn, _position, _message, thisArg, ...args) {
@@ -131,6 +131,7 @@ var iso = {
131
131
  processOn: (_0, _1) => {
132
132
  },
133
133
  basename: (filepath) => filepath.split(/[\\/]/).pop() || filepath,
134
+ // eslint-disable-next-line no-restricted-properties -- preserving intentional console usage.
134
135
  writeln: (text) => console.log(text)
135
136
  };
136
137
  var isomorph_default = iso;
@@ -166,7 +167,7 @@ function patchStreamIfNeeded(stream, options) {
166
167
  if (!completed) {
167
168
  completed = true;
168
169
  try {
169
- options.onComplete(chunks);
170
+ await options.onComplete(chunks);
170
171
  } catch (error) {
171
172
  console.error("Error in stream onComplete handler:", error);
172
173
  }
@@ -178,7 +179,7 @@ function patchStreamIfNeeded(stream, options) {
178
179
  chunks.push(chunk);
179
180
  if (options.onChunk) {
180
181
  try {
181
- options.onChunk(chunk);
182
+ await options.onChunk(chunk);
182
183
  } catch (error) {
183
184
  console.error("Error in stream onChunk handler:", error);
184
185
  }
@@ -191,7 +192,7 @@ function patchStreamIfNeeded(stream, options) {
191
192
  completed = true;
192
193
  if (options.onError) {
193
194
  try {
194
- options.onError(
195
+ await options.onError(
195
196
  error instanceof Error ? error : new Error(String(error)),
196
197
  chunks
197
198
  );
@@ -209,7 +210,7 @@ function patchStreamIfNeeded(stream, options) {
209
210
  if (!completed) {
210
211
  completed = true;
211
212
  try {
212
- options.onComplete(chunks);
213
+ await options.onComplete(chunks);
213
214
  } catch (error) {
214
215
  console.error("Error in stream onComplete handler:", error);
215
216
  }
@@ -226,7 +227,7 @@ function patchStreamIfNeeded(stream, options) {
226
227
  const error = rawError instanceof Error ? rawError : new Error(String(rawError));
227
228
  if (options.onError) {
228
229
  try {
229
- options.onError(error, chunks);
230
+ await options.onError(error, chunks);
230
231
  } catch (handlerError) {
231
232
  console.error("Error in stream onError handler:", handlerError);
232
233
  }
@@ -1578,6 +1579,15 @@ function addAzureBlobHeaders(headers, url) {
1578
1579
  headers["x-ms-blob-type"] = "BlockBlob";
1579
1580
  }
1580
1581
  }
1582
+ function filterFrom(record, keys) {
1583
+ const out = {};
1584
+ for (const k of Object.keys(record)) {
1585
+ if (!keys.includes(k)) {
1586
+ out[k] = record[k];
1587
+ }
1588
+ }
1589
+ return out;
1590
+ }
1581
1591
 
1582
1592
  // src/generated_types.ts
1583
1593
  var import_v36 = require("zod/v3");
@@ -8371,51 +8381,28 @@ function traceSyncStreamChannel(channel2, config) {
8371
8381
  }
8372
8382
  const { span, startTime } = spanData;
8373
8383
  const endEvent = event;
8374
- if (config.patchResult?.({
8375
- channelName,
8376
- endEvent,
8377
- result: endEvent.result,
8378
- span,
8379
- startTime
8380
- })) {
8381
- return;
8382
- }
8383
- const stream = endEvent.result;
8384
- if (!isSyncStreamLike(stream)) {
8385
- span.end();
8386
- states.delete(event);
8387
- return;
8388
- }
8389
- let first = true;
8390
- stream.on("chunk", () => {
8391
- if (first) {
8392
- span.log({
8393
- metrics: {
8394
- time_to_first_token: getCurrentUnixTimestamp() - startTime
8395
- }
8396
- });
8397
- first = false;
8398
- }
8399
- });
8400
- stream.on("chatCompletion", (completion) => {
8401
- try {
8402
- if (hasChoices(completion)) {
8403
- span.log({
8404
- output: completion.choices
8405
- });
8406
- }
8407
- } catch (error) {
8408
- console.error(
8409
- `Error extracting chatCompletion for ${channelName}:`,
8410
- error
8411
- );
8384
+ const handleResolvedResult = (result) => {
8385
+ const resolvedEndEvent = {
8386
+ ...endEvent,
8387
+ result
8388
+ };
8389
+ if (config.patchResult?.({
8390
+ channelName,
8391
+ endEvent: resolvedEndEvent,
8392
+ result,
8393
+ span,
8394
+ startTime
8395
+ })) {
8396
+ return;
8412
8397
  }
8413
- });
8414
- stream.on("event", (streamEvent) => {
8415
- if (!config.extractFromEvent) {
8398
+ const stream = result;
8399
+ if (!isSyncStreamLike(stream)) {
8400
+ span.end();
8401
+ states.delete(event);
8416
8402
  return;
8417
8403
  }
8418
- try {
8404
+ let first = true;
8405
+ stream.on("chunk", () => {
8419
8406
  if (first) {
8420
8407
  span.log({
8421
8408
  metrics: {
@@ -8424,25 +8411,55 @@ function traceSyncStreamChannel(channel2, config) {
8424
8411
  });
8425
8412
  first = false;
8426
8413
  }
8427
- const extracted = config.extractFromEvent(streamEvent);
8428
- if (extracted && Object.keys(extracted).length > 0) {
8429
- span.log(extracted);
8414
+ });
8415
+ stream.on("chatCompletion", (completion) => {
8416
+ try {
8417
+ if (hasChoices(completion)) {
8418
+ span.log({
8419
+ output: completion.choices
8420
+ });
8421
+ }
8422
+ } catch (error) {
8423
+ console.error(
8424
+ `Error extracting chatCompletion for ${channelName}:`,
8425
+ error
8426
+ );
8430
8427
  }
8431
- } catch (error) {
8432
- console.error(`Error extracting event for ${channelName}:`, error);
8433
- }
8434
- });
8435
- stream.on("end", () => {
8436
- span.end();
8437
- states.delete(event);
8438
- });
8439
- stream.on("error", (error) => {
8440
- span.log({
8441
- error: error.message
8442
8428
  });
8443
- span.end();
8444
- states.delete(event);
8445
- });
8429
+ stream.on("event", (streamEvent) => {
8430
+ if (!config.extractFromEvent) {
8431
+ return;
8432
+ }
8433
+ try {
8434
+ if (first) {
8435
+ span.log({
8436
+ metrics: {
8437
+ time_to_first_token: getCurrentUnixTimestamp() - startTime
8438
+ }
8439
+ });
8440
+ first = false;
8441
+ }
8442
+ const extracted = config.extractFromEvent(streamEvent);
8443
+ if (extracted && Object.keys(extracted).length > 0) {
8444
+ span.log(extracted);
8445
+ }
8446
+ } catch (error) {
8447
+ console.error(`Error extracting event for ${channelName}:`, error);
8448
+ }
8449
+ });
8450
+ stream.on("end", () => {
8451
+ span.end();
8452
+ states.delete(event);
8453
+ });
8454
+ stream.on("error", (error) => {
8455
+ span.log({
8456
+ error: error.message
8457
+ });
8458
+ span.end();
8459
+ states.delete(event);
8460
+ });
8461
+ };
8462
+ handleResolvedResult(endEvent.result);
8446
8463
  },
8447
8464
  error: (event) => {
8448
8465
  logErrorAndEnd(states, event);
@@ -9265,28 +9282,40 @@ function aggregateAnthropicStreamChunks(chunks) {
9265
9282
  case "content_block_start":
9266
9283
  if (event.content_block) {
9267
9284
  contentBlocks[event.index] = event.content_block;
9268
- contentBlockDeltas[event.index] = [];
9285
+ contentBlockDeltas[event.index] = { textDeltas: [], citations: [] };
9269
9286
  }
9270
9287
  break;
9271
- case "content_block_delta":
9272
- if (event.delta?.type === "text_delta") {
9273
- const text = event.delta.text;
9288
+ case "content_block_delta": {
9289
+ const acc = contentBlockDeltas[event.index];
9290
+ const delta = event.delta;
9291
+ if (!delta) break;
9292
+ if (delta.type === "text_delta" && "text" in delta) {
9293
+ const text = delta.text;
9274
9294
  if (text) {
9275
- if (contentBlocks[event.index] !== void 0 || contentBlockDeltas[event.index] !== void 0) {
9276
- contentBlockDeltas[event.index] ??= [];
9277
- contentBlockDeltas[event.index].push(text);
9295
+ if (acc !== void 0) {
9296
+ acc.textDeltas.push(text);
9278
9297
  } else {
9279
9298
  fallbackTextDeltas.push(text);
9280
9299
  }
9281
9300
  }
9282
- } else if (event.delta?.type === "input_json_delta") {
9283
- const partialJson = event.delta.partial_json;
9284
- if (partialJson) {
9285
- contentBlockDeltas[event.index] ??= [];
9286
- contentBlockDeltas[event.index].push(partialJson);
9301
+ } else if (delta.type === "input_json_delta" && "partial_json" in delta) {
9302
+ const partialJson = delta.partial_json;
9303
+ if (partialJson && acc !== void 0) {
9304
+ acc.textDeltas.push(partialJson);
9305
+ }
9306
+ } else if (delta.type === "thinking_delta" && "thinking" in delta) {
9307
+ const thinking = delta.thinking;
9308
+ if (thinking && acc !== void 0) {
9309
+ acc.textDeltas.push(thinking);
9310
+ }
9311
+ } else if (delta.type === "citations_delta" && "citation" in delta) {
9312
+ const citation = delta.citation;
9313
+ if (citation && acc !== void 0) {
9314
+ acc.citations.push(citation);
9287
9315
  }
9288
9316
  }
9289
9317
  break;
9318
+ }
9290
9319
  case "content_block_stop":
9291
9320
  finalizeContentBlock(
9292
9321
  event.index,
@@ -9312,7 +9341,7 @@ function aggregateAnthropicStreamChunks(chunks) {
9312
9341
  })).filter(({ block }) => block !== void 0).sort((left, right) => left.index - right.index).map(({ block }) => block);
9313
9342
  let output = fallbackTextDeltas.join("");
9314
9343
  if (orderedContent.length > 0) {
9315
- if (orderedContent.every(isTextContentBlock)) {
9344
+ if (orderedContent.every(isTextContentBlock) && orderedContent.every((block) => !block.citations?.length)) {
9316
9345
  output = orderedContent.map((block) => block.text).join("");
9317
9346
  } else {
9318
9347
  output = {
@@ -9338,7 +9367,8 @@ function finalizeContentBlock(index, contentBlocks, contentBlockDeltas, fallback
9338
9367
  if (!contentBlock) {
9339
9368
  return;
9340
9369
  }
9341
- const text = contentBlockDeltas[index]?.join("") ?? "";
9370
+ const acc = contentBlockDeltas[index];
9371
+ const text = acc?.textDeltas.join("") ?? "";
9342
9372
  if (isToolUseContentBlock(contentBlock)) {
9343
9373
  if (!text) {
9344
9374
  return;
@@ -9355,20 +9385,28 @@ function finalizeContentBlock(index, contentBlocks, contentBlockDeltas, fallback
9355
9385
  return;
9356
9386
  }
9357
9387
  if (isTextContentBlock(contentBlock)) {
9388
+ if (!text) {
9389
+ delete contentBlocks[index];
9390
+ return;
9391
+ }
9392
+ const updated = { ...contentBlock, text };
9393
+ if (acc?.citations.length) {
9394
+ updated.citations = acc.citations;
9395
+ }
9396
+ contentBlocks[index] = updated;
9397
+ return;
9398
+ }
9399
+ if (isThinkingContentBlock(contentBlock)) {
9358
9400
  if (!text) {
9359
9401
  delete contentBlocks[index];
9360
9402
  return;
9361
9403
  }
9362
9404
  contentBlocks[index] = {
9363
9405
  ...contentBlock,
9364
- text
9406
+ thinking: text
9365
9407
  };
9366
9408
  return;
9367
9409
  }
9368
- if (text) {
9369
- fallbackTextDeltas.push(text);
9370
- }
9371
- delete contentBlocks[index];
9372
9410
  }
9373
9411
  function isTextContentBlock(contentBlock) {
9374
9412
  return contentBlock.type === "text";
@@ -9376,6 +9414,9 @@ function isTextContentBlock(contentBlock) {
9376
9414
  function isToolUseContentBlock(contentBlock) {
9377
9415
  return contentBlock.type === "tool_use";
9378
9416
  }
9417
+ function isThinkingContentBlock(contentBlock) {
9418
+ return contentBlock.type === "thinking";
9419
+ }
9379
9420
  function isAnthropicBase64ContentBlock(input) {
9380
9421
  return (input.type === "image" || input.type === "document") && isObject(input.source) && input.source.type === "base64";
9381
9422
  }
@@ -9430,15 +9471,6 @@ function coalesceInput(messages, system) {
9430
9471
  }
9431
9472
  return input;
9432
9473
  }
9433
- function filterFrom(obj, fieldsToRemove) {
9434
- const result = {};
9435
- for (const [key, value] of Object.entries(obj)) {
9436
- if (!fieldsToRemove.includes(key)) {
9437
- result[key] = value;
9438
- }
9439
- }
9440
- return result;
9441
- }
9442
9474
 
9443
9475
  // src/wrappers/ai-sdk/normalize-logged-output.ts
9444
9476
  var REMOVE_NORMALIZED_VALUE = Symbol("braintrust.ai-sdk.remove-normalized");
@@ -9552,10 +9584,6 @@ var aiSDKChannels = defineChannels("ai", {
9552
9584
  channelName: "streamText",
9553
9585
  kind: "async"
9554
9586
  }),
9555
- streamTextSync: channel({
9556
- channelName: "streamText.sync",
9557
- kind: "sync-stream"
9558
- }),
9559
9587
  generateObject: channel({
9560
9588
  channelName: "generateObject",
9561
9589
  kind: "async"
@@ -9564,10 +9592,6 @@ var aiSDKChannels = defineChannels("ai", {
9564
9592
  channelName: "streamObject",
9565
9593
  kind: "async"
9566
9594
  }),
9567
- streamObjectSync: channel({
9568
- channelName: "streamObject.sync",
9569
- kind: "sync-stream"
9570
- }),
9571
9595
  agentGenerate: channel({
9572
9596
  channelName: "Agent.generate",
9573
9597
  kind: "async"
@@ -9603,6 +9627,9 @@ var DEFAULT_DENY_OUTPUT_PATHS = [
9603
9627
  ];
9604
9628
  var AUTO_PATCHED_MODEL = Symbol.for("braintrust.ai-sdk.auto-patched-model");
9605
9629
  var AUTO_PATCHED_TOOL = Symbol.for("braintrust.ai-sdk.auto-patched-tool");
9630
+ var RUNTIME_DENY_OUTPUT_PATHS = Symbol.for(
9631
+ "braintrust.ai-sdk.deny-output-paths"
9632
+ );
9606
9633
  var AISDKPlugin = class extends BasePlugin {
9607
9634
  config;
9608
9635
  constructor(config = {}) {
@@ -9624,7 +9651,10 @@ var AISDKPlugin = class extends BasePlugin {
9624
9651
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9625
9652
  extractOutput: (result, endEvent) => {
9626
9653
  finalizeAISDKChildTracing(endEvent);
9627
- return processAISDKOutput(result, denyOutputPaths);
9654
+ return processAISDKOutput(
9655
+ result,
9656
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9657
+ );
9628
9658
  },
9629
9659
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
9630
9660
  aggregateChunks: aggregateAISDKChunks
@@ -9635,25 +9665,14 @@ var AISDKPlugin = class extends BasePlugin {
9635
9665
  name: "streamText",
9636
9666
  type: "llm" /* LLM */,
9637
9667
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9638
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
9668
+ extractOutput: (result, endEvent) => processAISDKOutput(
9669
+ result,
9670
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9671
+ ),
9639
9672
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
9640
9673
  aggregateChunks: aggregateAISDKChunks,
9641
9674
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9642
- denyOutputPaths,
9643
- endEvent,
9644
- result,
9645
- span,
9646
- startTime
9647
- })
9648
- })
9649
- );
9650
- this.unsubscribers.push(
9651
- traceSyncStreamChannel(aiSDKChannels.streamTextSync, {
9652
- name: "streamText",
9653
- type: "llm" /* LLM */,
9654
- extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9655
- patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9656
- denyOutputPaths,
9675
+ defaultDenyOutputPaths: denyOutputPaths,
9657
9676
  endEvent,
9658
9677
  result,
9659
9678
  span,
@@ -9668,7 +9687,10 @@ var AISDKPlugin = class extends BasePlugin {
9668
9687
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9669
9688
  extractOutput: (result, endEvent) => {
9670
9689
  finalizeAISDKChildTracing(endEvent);
9671
- return processAISDKOutput(result, denyOutputPaths);
9690
+ return processAISDKOutput(
9691
+ result,
9692
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9693
+ );
9672
9694
  },
9673
9695
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
9674
9696
  aggregateChunks: aggregateAISDKChunks
@@ -9679,25 +9701,14 @@ var AISDKPlugin = class extends BasePlugin {
9679
9701
  name: "streamObject",
9680
9702
  type: "llm" /* LLM */,
9681
9703
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9682
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
9704
+ extractOutput: (result, endEvent) => processAISDKOutput(
9705
+ result,
9706
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9707
+ ),
9683
9708
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
9684
9709
  aggregateChunks: aggregateAISDKChunks,
9685
9710
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9686
- denyOutputPaths,
9687
- endEvent,
9688
- result,
9689
- span,
9690
- startTime
9691
- })
9692
- })
9693
- );
9694
- this.unsubscribers.push(
9695
- traceSyncStreamChannel(aiSDKChannels.streamObjectSync, {
9696
- name: "streamObject",
9697
- type: "llm" /* LLM */,
9698
- extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9699
- patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9700
- denyOutputPaths,
9711
+ defaultDenyOutputPaths: denyOutputPaths,
9701
9712
  endEvent,
9702
9713
  result,
9703
9714
  span,
@@ -9712,7 +9723,10 @@ var AISDKPlugin = class extends BasePlugin {
9712
9723
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9713
9724
  extractOutput: (result, endEvent) => {
9714
9725
  finalizeAISDKChildTracing(endEvent);
9715
- return processAISDKOutput(result, denyOutputPaths);
9726
+ return processAISDKOutput(
9727
+ result,
9728
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9729
+ );
9716
9730
  },
9717
9731
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
9718
9732
  aggregateChunks: aggregateAISDKChunks
@@ -9723,11 +9737,14 @@ var AISDKPlugin = class extends BasePlugin {
9723
9737
  name: "Agent.stream",
9724
9738
  type: "llm" /* LLM */,
9725
9739
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9726
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
9740
+ extractOutput: (result, endEvent) => processAISDKOutput(
9741
+ result,
9742
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9743
+ ),
9727
9744
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
9728
9745
  aggregateChunks: aggregateAISDKChunks,
9729
9746
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9730
- denyOutputPaths,
9747
+ defaultDenyOutputPaths: denyOutputPaths,
9731
9748
  endEvent,
9732
9749
  result,
9733
9750
  span,
@@ -9742,7 +9759,10 @@ var AISDKPlugin = class extends BasePlugin {
9742
9759
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9743
9760
  extractOutput: (result, endEvent) => {
9744
9761
  finalizeAISDKChildTracing(endEvent);
9745
- return processAISDKOutput(result, denyOutputPaths);
9762
+ return processAISDKOutput(
9763
+ result,
9764
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9765
+ );
9746
9766
  },
9747
9767
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
9748
9768
  aggregateChunks: aggregateAISDKChunks
@@ -9753,11 +9773,14 @@ var AISDKPlugin = class extends BasePlugin {
9753
9773
  name: "ToolLoopAgent.stream",
9754
9774
  type: "llm" /* LLM */,
9755
9775
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9756
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
9776
+ extractOutput: (result, endEvent) => processAISDKOutput(
9777
+ result,
9778
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9779
+ ),
9757
9780
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
9758
9781
  aggregateChunks: aggregateAISDKChunks,
9759
9782
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9760
- denyOutputPaths,
9783
+ defaultDenyOutputPaths: denyOutputPaths,
9761
9784
  endEvent,
9762
9785
  result,
9763
9786
  span,
@@ -9767,132 +9790,435 @@ var AISDKPlugin = class extends BasePlugin {
9767
9790
  );
9768
9791
  }
9769
9792
  };
9770
- function processAISDKInput(params) {
9771
- if (!params) return params;
9772
- const input = processInputAttachments(params);
9773
- if (!input || typeof input !== "object" || Array.isArray(input)) {
9774
- return input;
9793
+ function resolveDenyOutputPaths(event, defaultDenyOutputPaths) {
9794
+ if (Array.isArray(event?.denyOutputPaths)) {
9795
+ return event.denyOutputPaths;
9775
9796
  }
9776
- const { tools: _tools, ...rest } = input;
9777
- return rest;
9778
- }
9779
- function prepareAISDKInput(params, event, span, denyOutputPaths) {
9780
- const input = processAISDKInput(params);
9781
- const metadata = extractMetadataFromParams(params, event.self);
9782
- const childTracing = prepareAISDKChildTracing(
9783
- params,
9784
- event.self,
9785
- span,
9786
- denyOutputPaths
9787
- );
9788
- event.__braintrust_ai_sdk_model_wrapped = childTracing.modelWrapped;
9789
- if (childTracing.cleanup) {
9790
- event.__braintrust_ai_sdk_cleanup = childTracing.cleanup;
9797
+ const firstArgument = event?.arguments && event.arguments.length > 0 ? event.arguments[0] : void 0;
9798
+ if (!firstArgument || typeof firstArgument !== "object") {
9799
+ return defaultDenyOutputPaths;
9791
9800
  }
9792
- return {
9793
- input,
9794
- metadata
9795
- };
9796
- }
9797
- function extractTopLevelAISDKMetrics(result, event, startTime) {
9798
- const metrics = hasModelChildTracing(event) ? {} : extractTokenMetrics(result);
9799
- if (startTime) {
9800
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9801
+ const runtimeDenyOutputPaths = firstArgument[RUNTIME_DENY_OUTPUT_PATHS];
9802
+ if (Array.isArray(runtimeDenyOutputPaths) && runtimeDenyOutputPaths.every((path) => typeof path === "string")) {
9803
+ return runtimeDenyOutputPaths;
9801
9804
  }
9802
- return metrics;
9803
- }
9804
- function hasModelChildTracing(event) {
9805
- return event?.__braintrust_ai_sdk_model_wrapped === true;
9805
+ return defaultDenyOutputPaths;
9806
9806
  }
9807
- function extractMetadataFromParams(params, self) {
9808
- const metadata = {
9809
- braintrust: {
9810
- integration_name: "ai-sdk",
9811
- sdk_language: "typescript"
9812
- }
9813
- };
9814
- 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;
9815
- const { model, provider } = serializeModelWithProvider(
9816
- params.model ?? agentModel
9817
- );
9818
- if (model) {
9819
- metadata.model = model;
9807
+ var isZodSchema2 = (value) => {
9808
+ return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
9809
+ };
9810
+ var serializeZodSchema2 = (schema) => {
9811
+ try {
9812
+ return zodToJsonSchema(schema);
9813
+ } catch {
9814
+ return {
9815
+ type: "object",
9816
+ description: "Zod schema (conversion failed)"
9817
+ };
9820
9818
  }
9821
- if (provider) {
9822
- metadata.provider = provider;
9819
+ };
9820
+ var isOutputObject = (value) => {
9821
+ if (value == null || typeof value !== "object") {
9822
+ return false;
9823
9823
  }
9824
- const tools = serializeAISDKToolsForLogging(params.tools);
9825
- if (tools) {
9826
- metadata.tools = tools;
9824
+ const output = value;
9825
+ if (!("responseFormat" in output)) {
9826
+ return false;
9827
9827
  }
9828
- return metadata;
9829
- }
9830
- function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9831
- const cleanup = [];
9832
- const patchedModels = /* @__PURE__ */ new WeakSet();
9833
- const patchedTools = /* @__PURE__ */ new WeakSet();
9834
- let modelWrapped = false;
9835
- const patchModel = (model) => {
9836
- const resolvedModel = resolveAISDKModel(model);
9837
- if (!resolvedModel || typeof resolvedModel !== "object" || typeof resolvedModel.doGenerate !== "function" || patchedModels.has(resolvedModel) || resolvedModel[AUTO_PATCHED_MODEL]) {
9838
- return;
9839
- }
9840
- patchedModels.add(resolvedModel);
9841
- resolvedModel[AUTO_PATCHED_MODEL] = true;
9842
- modelWrapped = true;
9843
- const originalDoGenerate = resolvedModel.doGenerate;
9844
- const originalDoStream = resolvedModel.doStream;
9845
- const baseMetadata = buildAISDKChildMetadata(resolvedModel);
9846
- resolvedModel.doGenerate = async function doGeneratePatched(options) {
9847
- return parentSpan.traced(
9848
- async (span) => {
9849
- const result = await Reflect.apply(
9850
- originalDoGenerate,
9851
- resolvedModel,
9852
- [options]
9853
- );
9854
- span.log({
9855
- output: processAISDKOutput(result, denyOutputPaths),
9856
- metrics: extractTokenMetrics(result),
9857
- ...buildResolvedMetadataPayload(result)
9858
- });
9859
- return result;
9860
- },
9861
- {
9862
- name: "doGenerate",
9863
- spanAttributes: {
9864
- type: "llm" /* LLM */
9865
- },
9866
- event: {
9867
- input: processAISDKInput(options),
9868
- metadata: baseMetadata
9869
- }
9870
- }
9871
- );
9828
+ if (output.type === "object" || output.type === "text") {
9829
+ return true;
9830
+ }
9831
+ if (typeof output.responseFormat === "function" || typeof output.responseFormat === "object") {
9832
+ return true;
9833
+ }
9834
+ return false;
9835
+ };
9836
+ var serializeOutputObject = (output, model) => {
9837
+ try {
9838
+ const result = {
9839
+ response_format: null
9872
9840
  };
9873
- if (originalDoStream) {
9874
- resolvedModel.doStream = async function doStreamPatched(options) {
9875
- const span = parentSpan.startSpan({
9876
- name: "doStream",
9877
- spanAttributes: {
9878
- type: "llm" /* LLM */
9879
- },
9880
- event: {
9881
- input: processAISDKInput(options),
9882
- metadata: baseMetadata
9841
+ if (output.type) {
9842
+ result.type = output.type;
9843
+ }
9844
+ let responseFormat;
9845
+ if (typeof output.responseFormat === "function") {
9846
+ const mockModelForSchema = {
9847
+ supportsStructuredOutputs: true,
9848
+ ...model && typeof model === "object" ? model : {}
9849
+ };
9850
+ responseFormat = output.responseFormat({ model: mockModelForSchema });
9851
+ } else if (output.responseFormat != null && typeof output.responseFormat === "object") {
9852
+ responseFormat = output.responseFormat;
9853
+ }
9854
+ if (responseFormat) {
9855
+ if (typeof responseFormat.then === "function") {
9856
+ result.response_format = Promise.resolve(responseFormat).then(
9857
+ (resolved) => {
9858
+ if (resolved.schema && isZodSchema2(resolved.schema)) {
9859
+ return {
9860
+ ...resolved,
9861
+ schema: serializeZodSchema2(resolved.schema)
9862
+ };
9863
+ }
9864
+ return resolved;
9883
9865
  }
9884
- });
9885
- const result = await withCurrent(
9886
- span,
9887
- () => Reflect.apply(originalDoStream, resolvedModel, [options])
9888
9866
  );
9889
- const output = {};
9890
- let text = "";
9891
- let reasoning = "";
9892
- const toolCalls = [];
9867
+ } else {
9868
+ const syncResponseFormat = responseFormat;
9869
+ if (syncResponseFormat.schema && isZodSchema2(syncResponseFormat.schema)) {
9870
+ responseFormat = {
9871
+ ...syncResponseFormat,
9872
+ schema: serializeZodSchema2(syncResponseFormat.schema)
9873
+ };
9874
+ }
9875
+ result.response_format = responseFormat;
9876
+ }
9877
+ }
9878
+ return result;
9879
+ } catch {
9880
+ return {
9881
+ response_format: null
9882
+ };
9883
+ }
9884
+ };
9885
+ var processInputAttachmentsSync = (input) => {
9886
+ if (!input) return { input };
9887
+ const processed = { ...input };
9888
+ if (input.messages && Array.isArray(input.messages)) {
9889
+ processed.messages = input.messages.map(processMessage);
9890
+ }
9891
+ if (input.prompt && typeof input.prompt === "object") {
9892
+ if (Array.isArray(input.prompt)) {
9893
+ processed.prompt = input.prompt.map(processMessage);
9894
+ } else {
9895
+ processed.prompt = processPromptContent(input.prompt);
9896
+ }
9897
+ }
9898
+ if (input.schema && isZodSchema2(input.schema)) {
9899
+ processed.schema = serializeZodSchema2(input.schema);
9900
+ }
9901
+ if (input.callOptionsSchema && isZodSchema2(input.callOptionsSchema)) {
9902
+ processed.callOptionsSchema = serializeZodSchema2(input.callOptionsSchema);
9903
+ }
9904
+ if (input.tools) {
9905
+ processed.tools = serializeAISDKToolsForLogging(input.tools);
9906
+ }
9907
+ let outputPromise;
9908
+ if (input.output && isOutputObject(input.output)) {
9909
+ const serialized = serializeOutputObject(input.output, input.model);
9910
+ if (serialized.response_format && typeof serialized.response_format.then === "function") {
9911
+ processed.output = { ...serialized, response_format: {} };
9912
+ outputPromise = serialized.response_format.then(
9913
+ (resolvedFormat) => ({
9914
+ output: { ...serialized, response_format: resolvedFormat }
9915
+ })
9916
+ );
9917
+ } else {
9918
+ processed.output = serialized;
9919
+ }
9920
+ }
9921
+ if ("prepareCall" in processed && typeof processed.prepareCall === "function") {
9922
+ processed.prepareCall = "[Function]";
9923
+ }
9924
+ return { input: processed, outputPromise };
9925
+ };
9926
+ var processMessage = (message) => {
9927
+ if (!message || typeof message !== "object") return message;
9928
+ if (Array.isArray(message.content)) {
9929
+ return {
9930
+ ...message,
9931
+ content: message.content.map(processContentPart)
9932
+ };
9933
+ }
9934
+ if (typeof message.content === "object" && message.content !== null) {
9935
+ return {
9936
+ ...message,
9937
+ content: processContentPart(message.content)
9938
+ };
9939
+ }
9940
+ return message;
9941
+ };
9942
+ var processPromptContent = (prompt) => {
9943
+ if (Array.isArray(prompt)) {
9944
+ return prompt.map(processContentPart);
9945
+ }
9946
+ if (prompt.content) {
9947
+ if (Array.isArray(prompt.content)) {
9948
+ return {
9949
+ ...prompt,
9950
+ content: prompt.content.map(processContentPart)
9951
+ };
9952
+ } else if (typeof prompt.content === "object") {
9953
+ return {
9954
+ ...prompt,
9955
+ content: processContentPart(prompt.content)
9956
+ };
9957
+ }
9958
+ }
9959
+ return prompt;
9960
+ };
9961
+ var processContentPart = (part) => {
9962
+ if (!part || typeof part !== "object") return part;
9963
+ try {
9964
+ if (part.type === "image" && part.image) {
9965
+ const imageAttachment = convertImageToAttachment(
9966
+ part.image,
9967
+ part.mimeType || part.mediaType
9968
+ );
9969
+ if (imageAttachment) {
9970
+ return {
9971
+ ...part,
9972
+ image: imageAttachment
9973
+ };
9974
+ }
9975
+ }
9976
+ if (part.type === "file" && part.data && (part.mimeType || part.mediaType)) {
9977
+ const fileAttachment = convertDataToAttachment(
9978
+ part.data,
9979
+ part.mimeType || part.mediaType,
9980
+ part.name || part.filename
9981
+ );
9982
+ if (fileAttachment) {
9983
+ return {
9984
+ ...part,
9985
+ data: fileAttachment
9986
+ };
9987
+ }
9988
+ }
9989
+ if (part.type === "image_url" && part.image_url) {
9990
+ if (typeof part.image_url === "object" && part.image_url.url) {
9991
+ const imageAttachment = convertImageToAttachment(part.image_url.url);
9992
+ if (imageAttachment) {
9993
+ return {
9994
+ ...part,
9995
+ image_url: {
9996
+ ...part.image_url,
9997
+ url: imageAttachment
9998
+ }
9999
+ };
10000
+ }
10001
+ }
10002
+ }
10003
+ } catch (error) {
10004
+ console.warn("Error processing content part:", error);
10005
+ }
10006
+ return part;
10007
+ };
10008
+ var convertImageToAttachment = (image, explicitMimeType) => {
10009
+ try {
10010
+ if (typeof image === "string" && image.startsWith("data:")) {
10011
+ const [mimeTypeSection, base64Data] = image.split(",");
10012
+ const mimeType = mimeTypeSection.match(/data:(.*?);/)?.[1];
10013
+ if (mimeType && base64Data) {
10014
+ const blob = convertDataToBlob(base64Data, mimeType);
10015
+ if (blob) {
10016
+ return new Attachment({
10017
+ data: blob,
10018
+ filename: `image.${getExtensionFromMediaType(mimeType)}`,
10019
+ contentType: mimeType
10020
+ });
10021
+ }
10022
+ }
10023
+ }
10024
+ if (explicitMimeType) {
10025
+ if (image instanceof Uint8Array) {
10026
+ return new Attachment({
10027
+ data: new Blob([image], { type: explicitMimeType }),
10028
+ filename: `image.${getExtensionFromMediaType(explicitMimeType)}`,
10029
+ contentType: explicitMimeType
10030
+ });
10031
+ }
10032
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer(image)) {
10033
+ return new Attachment({
10034
+ data: new Blob([image], { type: explicitMimeType }),
10035
+ filename: `image.${getExtensionFromMediaType(explicitMimeType)}`,
10036
+ contentType: explicitMimeType
10037
+ });
10038
+ }
10039
+ }
10040
+ if (image instanceof Blob && image.type) {
10041
+ return new Attachment({
10042
+ data: image,
10043
+ filename: `image.${getExtensionFromMediaType(image.type)}`,
10044
+ contentType: image.type
10045
+ });
10046
+ }
10047
+ if (image instanceof Attachment) {
10048
+ return image;
10049
+ }
10050
+ } catch (error) {
10051
+ console.warn("Error converting image to attachment:", error);
10052
+ }
10053
+ return null;
10054
+ };
10055
+ var convertDataToAttachment = (data, mimeType, filename) => {
10056
+ if (!mimeType) return null;
10057
+ try {
10058
+ let blob = null;
10059
+ if (typeof data === "string" && data.startsWith("data:")) {
10060
+ const [, base64Data] = data.split(",");
10061
+ if (base64Data) {
10062
+ blob = convertDataToBlob(base64Data, mimeType);
10063
+ }
10064
+ } else if (typeof data === "string" && data.length > 0) {
10065
+ blob = convertDataToBlob(data, mimeType);
10066
+ } else if (data instanceof Uint8Array) {
10067
+ blob = new Blob([data], { type: mimeType });
10068
+ } else if (typeof Buffer !== "undefined" && Buffer.isBuffer(data)) {
10069
+ blob = new Blob([data], { type: mimeType });
10070
+ } else if (data instanceof Blob) {
10071
+ blob = data;
10072
+ }
10073
+ if (blob) {
10074
+ return new Attachment({
10075
+ data: blob,
10076
+ filename: filename || `file.${getExtensionFromMediaType(mimeType)}`,
10077
+ contentType: mimeType
10078
+ });
10079
+ }
10080
+ } catch (error) {
10081
+ console.warn("Error converting data to attachment:", error);
10082
+ }
10083
+ return null;
10084
+ };
10085
+ function processAISDKInput(params) {
10086
+ return processInputAttachmentsSync(params);
10087
+ }
10088
+ function prepareAISDKInput(params, event, span, defaultDenyOutputPaths) {
10089
+ const { input, outputPromise } = processAISDKInput(params);
10090
+ if (outputPromise && input && typeof input === "object") {
10091
+ outputPromise.then((resolvedData) => {
10092
+ span.log({
10093
+ input: {
10094
+ ...input,
10095
+ ...resolvedData
10096
+ }
10097
+ });
10098
+ }).catch(() => {
10099
+ });
10100
+ }
10101
+ const metadata = extractMetadataFromParams(params, event.self);
10102
+ const childTracing = prepareAISDKChildTracing(
10103
+ params,
10104
+ event.self,
10105
+ span,
10106
+ defaultDenyOutputPaths,
10107
+ event.aiSDK
10108
+ );
10109
+ event.modelWrapped = childTracing.modelWrapped;
10110
+ if (childTracing.cleanup) {
10111
+ event.__braintrust_ai_sdk_cleanup = childTracing.cleanup;
10112
+ }
10113
+ return {
10114
+ input,
10115
+ metadata
10116
+ };
10117
+ }
10118
+ function extractTopLevelAISDKMetrics(result, event, startTime) {
10119
+ const metrics = hasModelChildTracing(event) ? {} : extractTokenMetrics(result);
10120
+ if (startTime) {
10121
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
10122
+ }
10123
+ return metrics;
10124
+ }
10125
+ function hasModelChildTracing(event) {
10126
+ return event?.modelWrapped === true || event?.__braintrust_ai_sdk_model_wrapped === true;
10127
+ }
10128
+ function extractMetadataFromParams(params, self) {
10129
+ const metadata = {
10130
+ braintrust: {
10131
+ integration_name: "ai-sdk",
10132
+ sdk_language: "typescript"
10133
+ }
10134
+ };
10135
+ 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;
10136
+ const { model, provider } = serializeModelWithProvider(
10137
+ params.model ?? agentModel
10138
+ );
10139
+ if (model) {
10140
+ metadata.model = model;
10141
+ }
10142
+ if (provider) {
10143
+ metadata.provider = provider;
10144
+ }
10145
+ const tools = serializeAISDKToolsForLogging(params.tools);
10146
+ if (tools) {
10147
+ metadata.tools = tools;
10148
+ }
10149
+ return metadata;
10150
+ }
10151
+ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiSDK) {
10152
+ const cleanup = [];
10153
+ const patchedModels = /* @__PURE__ */ new WeakSet();
10154
+ const patchedTools = /* @__PURE__ */ new WeakSet();
10155
+ let modelWrapped = false;
10156
+ const patchModel = (model) => {
10157
+ const resolvedModel = resolveAISDKModel(model, aiSDK);
10158
+ if (!resolvedModel || typeof resolvedModel !== "object" || typeof resolvedModel.doGenerate !== "function" || patchedModels.has(resolvedModel) || resolvedModel[AUTO_PATCHED_MODEL]) {
10159
+ return resolvedModel;
10160
+ }
10161
+ patchedModels.add(resolvedModel);
10162
+ resolvedModel[AUTO_PATCHED_MODEL] = true;
10163
+ modelWrapped = true;
10164
+ const originalDoGenerate = resolvedModel.doGenerate;
10165
+ const originalDoStream = resolvedModel.doStream;
10166
+ const baseMetadata = buildAISDKChildMetadata(resolvedModel);
10167
+ resolvedModel.doGenerate = async function doGeneratePatched(options) {
10168
+ return parentSpan.traced(
10169
+ async (span) => {
10170
+ const result = await Reflect.apply(
10171
+ originalDoGenerate,
10172
+ resolvedModel,
10173
+ [options]
10174
+ );
10175
+ span.log({
10176
+ output: processAISDKOutput(result, denyOutputPaths),
10177
+ metrics: extractTokenMetrics(result),
10178
+ ...buildResolvedMetadataPayload(result)
10179
+ });
10180
+ return result;
10181
+ },
10182
+ {
10183
+ name: "doGenerate",
10184
+ spanAttributes: {
10185
+ type: "llm" /* LLM */
10186
+ },
10187
+ event: {
10188
+ input: processAISDKInput(options).input,
10189
+ metadata: baseMetadata
10190
+ }
10191
+ }
10192
+ );
10193
+ };
10194
+ if (originalDoStream) {
10195
+ resolvedModel.doStream = async function doStreamPatched(options) {
10196
+ const span = parentSpan.startSpan({
10197
+ name: "doStream",
10198
+ spanAttributes: {
10199
+ type: "llm" /* LLM */
10200
+ },
10201
+ event: {
10202
+ input: processAISDKInput(options).input,
10203
+ metadata: baseMetadata
10204
+ }
10205
+ });
10206
+ const result = await withCurrent(
10207
+ span,
10208
+ () => Reflect.apply(originalDoStream, resolvedModel, [options])
10209
+ );
10210
+ const streamStartTime = getCurrentUnixTimestamp();
10211
+ let firstChunkTime;
10212
+ const output = {};
10213
+ let text = "";
10214
+ let reasoning = "";
10215
+ const toolCalls = [];
9893
10216
  let object = void 0;
9894
10217
  const transformStream = new TransformStream({
9895
10218
  transform(chunk, controller) {
10219
+ if (firstChunkTime === void 0) {
10220
+ firstChunkTime = getCurrentUnixTimestamp();
10221
+ }
9896
10222
  switch (chunk.type) {
9897
10223
  case "text-delta":
9898
10224
  text += extractTextDelta(chunk);
@@ -9933,12 +10259,19 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9933
10259
  if (object !== void 0) {
9934
10260
  output.object = object;
9935
10261
  }
10262
+ const metrics = extractTokenMetrics(output);
10263
+ if (firstChunkTime !== void 0) {
10264
+ metrics.time_to_first_token = Math.max(
10265
+ firstChunkTime - streamStartTime,
10266
+ 1e-6
10267
+ );
10268
+ }
9936
10269
  span.log({
9937
10270
  output: processAISDKOutput(
9938
10271
  output,
9939
10272
  denyOutputPaths
9940
10273
  ),
9941
- metrics: extractTokenMetrics(output),
10274
+ metrics,
9942
10275
  ...buildResolvedMetadataPayload(output)
9943
10276
  });
9944
10277
  span.end();
@@ -9960,6 +10293,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9960
10293
  }
9961
10294
  delete resolvedModel[AUTO_PATCHED_MODEL];
9962
10295
  });
10296
+ return resolvedModel;
9963
10297
  };
9964
10298
  const patchTool = (tool, name) => {
9965
10299
  if (tool == null || typeof tool !== "object" || !("execute" in tool) || typeof tool.execute !== "function" || patchedTools.has(tool) || tool[AUTO_PATCHED_TOOL]) {
@@ -10032,17 +10366,26 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
10032
10366
  }
10033
10367
  };
10034
10368
  if (params && typeof params === "object") {
10035
- patchModel(params.model);
10369
+ const patchedParamModel = patchModel(params.model);
10370
+ if (typeof params.model === "string" && patchedParamModel && typeof patchedParamModel === "object") {
10371
+ params.model = patchedParamModel;
10372
+ }
10036
10373
  patchTools(params.tools);
10037
10374
  }
10038
10375
  if (self && typeof self === "object") {
10039
10376
  const selfRecord = self;
10040
10377
  if (selfRecord.model !== void 0) {
10041
- patchModel(selfRecord.model);
10378
+ const patchedSelfModel = patchModel(selfRecord.model);
10379
+ if (typeof selfRecord.model === "string" && patchedSelfModel && typeof patchedSelfModel === "object") {
10380
+ selfRecord.model = patchedSelfModel;
10381
+ }
10042
10382
  }
10043
10383
  if (selfRecord.settings && typeof selfRecord.settings === "object") {
10044
10384
  if (selfRecord.settings.model !== void 0) {
10045
- patchModel(selfRecord.settings.model);
10385
+ const patchedSettingsModel = patchModel(selfRecord.settings.model);
10386
+ if (typeof selfRecord.settings.model === "string" && patchedSettingsModel && typeof patchedSettingsModel === "object") {
10387
+ selfRecord.settings.model = patchedSettingsModel;
10388
+ }
10046
10389
  }
10047
10390
  if (selfRecord.settings.tools !== void 0) {
10048
10391
  patchTools(selfRecord.settings.tools);
@@ -10066,63 +10409,173 @@ function finalizeAISDKChildTracing(event) {
10066
10409
  }
10067
10410
  }
10068
10411
  function patchAISDKStreamingResult(args) {
10069
- const { denyOutputPaths, endEvent, result, span, startTime } = args;
10412
+ const { defaultDenyOutputPaths, endEvent, result, span, startTime } = args;
10070
10413
  if (!result || typeof result !== "object") {
10071
10414
  return false;
10072
10415
  }
10073
10416
  const resultRecord = result;
10074
- if (!isReadableStreamLike(resultRecord.baseStream)) {
10417
+ attachKnownResultPromiseHandlers(resultRecord);
10418
+ if (isReadableStreamLike(resultRecord.baseStream)) {
10419
+ let firstChunkTime2;
10420
+ const wrappedBaseStream = resultRecord.baseStream.pipeThrough(
10421
+ new TransformStream({
10422
+ transform(chunk, controller) {
10423
+ if (firstChunkTime2 === void 0) {
10424
+ firstChunkTime2 = getCurrentUnixTimestamp();
10425
+ }
10426
+ controller.enqueue(chunk);
10427
+ },
10428
+ async flush() {
10429
+ const metrics = extractTopLevelAISDKMetrics(result, endEvent);
10430
+ if (metrics.time_to_first_token === void 0 && firstChunkTime2 !== void 0) {
10431
+ metrics.time_to_first_token = firstChunkTime2 - startTime;
10432
+ }
10433
+ const output = await processAISDKStreamingOutput(
10434
+ result,
10435
+ resolveDenyOutputPaths(endEvent, defaultDenyOutputPaths)
10436
+ );
10437
+ const metadata = buildResolvedMetadataPayload(result).metadata;
10438
+ span.log({
10439
+ output,
10440
+ ...metadata ? { metadata } : {},
10441
+ metrics
10442
+ });
10443
+ finalizeAISDKChildTracing(endEvent);
10444
+ span.end();
10445
+ }
10446
+ })
10447
+ );
10448
+ Object.defineProperty(resultRecord, "baseStream", {
10449
+ configurable: true,
10450
+ enumerable: true,
10451
+ value: wrappedBaseStream,
10452
+ writable: true
10453
+ });
10454
+ return true;
10455
+ }
10456
+ const streamField = findAsyncIterableField(resultRecord, [
10457
+ "partialObjectStream",
10458
+ "textStream",
10459
+ "fullStream",
10460
+ "stream"
10461
+ ]);
10462
+ if (!streamField) {
10075
10463
  return false;
10076
10464
  }
10077
10465
  let firstChunkTime;
10078
- const wrappedBaseStream = resultRecord.baseStream.pipeThrough(
10079
- new TransformStream({
10080
- transform(chunk, controller) {
10081
- if (firstChunkTime === void 0) {
10082
- firstChunkTime = getCurrentUnixTimestamp();
10083
- }
10084
- controller.enqueue(chunk);
10085
- },
10086
- async flush() {
10087
- const metrics = extractTopLevelAISDKMetrics(result, endEvent);
10088
- if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
10089
- metrics.time_to_first_token = firstChunkTime - startTime;
10090
- }
10091
- const output = await processAISDKStreamingOutput(
10092
- result,
10093
- denyOutputPaths
10094
- );
10095
- const metadata = buildResolvedMetadataPayload(result).metadata;
10096
- span.log({
10097
- output,
10098
- ...metadata ? { metadata } : {},
10099
- metrics
10100
- });
10101
- finalizeAISDKChildTracing(endEvent);
10102
- span.end();
10466
+ const wrappedStream = createPatchedAsyncIterable(streamField.stream, {
10467
+ onChunk: () => {
10468
+ if (firstChunkTime === void 0) {
10469
+ firstChunkTime = getCurrentUnixTimestamp();
10103
10470
  }
10104
- })
10105
- );
10106
- Object.defineProperty(resultRecord, "baseStream", {
10471
+ },
10472
+ onComplete: async () => {
10473
+ const metrics = extractTopLevelAISDKMetrics(result, endEvent);
10474
+ if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
10475
+ metrics.time_to_first_token = firstChunkTime - startTime;
10476
+ }
10477
+ const output = await processAISDKStreamingOutput(
10478
+ result,
10479
+ resolveDenyOutputPaths(endEvent, defaultDenyOutputPaths)
10480
+ );
10481
+ const metadata = buildResolvedMetadataPayload(result).metadata;
10482
+ span.log({
10483
+ output,
10484
+ ...metadata ? { metadata } : {},
10485
+ metrics
10486
+ });
10487
+ finalizeAISDKChildTracing(endEvent);
10488
+ span.end();
10489
+ },
10490
+ onError: (error) => {
10491
+ span.log({
10492
+ error: error.message
10493
+ });
10494
+ finalizeAISDKChildTracing(endEvent);
10495
+ span.end();
10496
+ }
10497
+ });
10498
+ Object.defineProperty(resultRecord, streamField.field, {
10107
10499
  configurable: true,
10108
10500
  enumerable: true,
10109
- value: wrappedBaseStream,
10501
+ value: wrappedStream,
10110
10502
  writable: true
10111
10503
  });
10112
10504
  return true;
10113
10505
  }
10114
- function isReadableStreamLike(value) {
10115
- return value != null && typeof value === "object" && typeof value.pipeThrough === "function";
10116
- }
10117
- async function processAISDKStreamingOutput(result, denyOutputPaths) {
10118
- const output = processAISDKOutput(result, denyOutputPaths);
10119
- if (!output || typeof output !== "object") {
10506
+ function attachKnownResultPromiseHandlers(result) {
10507
+ const promiseLikeFields = [
10508
+ "content",
10509
+ "text",
10510
+ "object",
10511
+ "finishReason",
10512
+ "usage",
10513
+ "totalUsage",
10514
+ "steps"
10515
+ ];
10516
+ for (const field of promiseLikeFields) {
10517
+ try {
10518
+ if (!(field in result)) {
10519
+ continue;
10520
+ }
10521
+ const value = result[field];
10522
+ if (isPromiseLike(value)) {
10523
+ void Promise.resolve(value).catch(() => {
10524
+ });
10525
+ }
10526
+ } catch {
10527
+ }
10528
+ }
10529
+ }
10530
+ function isReadableStreamLike(value) {
10531
+ return value != null && typeof value === "object" && typeof value.pipeThrough === "function";
10532
+ }
10533
+ function isAsyncIterableLike(value) {
10534
+ return value != null && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function";
10535
+ }
10536
+ function findAsyncIterableField(result, candidateFields) {
10537
+ for (const field of candidateFields) {
10538
+ try {
10539
+ const stream = result[field];
10540
+ if (isAsyncIterableLike(stream)) {
10541
+ return { field, stream };
10542
+ }
10543
+ } catch {
10544
+ }
10545
+ }
10546
+ return null;
10547
+ }
10548
+ function createPatchedAsyncIterable(stream, hooks) {
10549
+ return {
10550
+ async *[Symbol.asyncIterator]() {
10551
+ try {
10552
+ for await (const chunk of stream) {
10553
+ hooks.onChunk(chunk);
10554
+ yield chunk;
10555
+ }
10556
+ await hooks.onComplete();
10557
+ } catch (error) {
10558
+ hooks.onError(
10559
+ error instanceof Error ? error : new Error(String(error))
10560
+ );
10561
+ throw error;
10562
+ }
10563
+ }
10564
+ };
10565
+ }
10566
+ async function processAISDKStreamingOutput(result, denyOutputPaths) {
10567
+ const output = processAISDKOutput(result, denyOutputPaths);
10568
+ if (!output || typeof output !== "object") {
10120
10569
  return output;
10121
10570
  }
10122
10571
  const outputRecord = output;
10572
+ const isObjectStreamingResult = result != null && typeof result === "object" && "partialObjectStream" in result;
10123
10573
  try {
10124
- if ("text" in result && typeof result.text === "string") {
10125
- outputRecord.text = result.text;
10574
+ if (!isObjectStreamingResult && "text" in result) {
10575
+ const resolvedText = await Promise.resolve(result.text);
10576
+ if (typeof resolvedText === "string") {
10577
+ outputRecord.text = resolvedText;
10578
+ }
10126
10579
  }
10127
10580
  } catch {
10128
10581
  }
@@ -10135,6 +10588,15 @@ async function processAISDKStreamingOutput(result, denyOutputPaths) {
10135
10588
  }
10136
10589
  } catch {
10137
10590
  }
10591
+ try {
10592
+ if ("finishReason" in result) {
10593
+ const resolvedFinishReason = await Promise.resolve(result.finishReason);
10594
+ if (resolvedFinishReason !== void 0) {
10595
+ outputRecord.finishReason = resolvedFinishReason;
10596
+ }
10597
+ }
10598
+ } catch {
10599
+ }
10138
10600
  return outputRecord;
10139
10601
  }
10140
10602
  function buildAISDKChildMetadata(model) {
@@ -10157,16 +10619,25 @@ function buildResolvedMetadataPayload(result) {
10157
10619
  if (gatewayInfo?.model) {
10158
10620
  metadata.model = gatewayInfo.model;
10159
10621
  }
10160
- if (result.finishReason !== void 0) {
10161
- metadata.finish_reason = result.finishReason;
10622
+ let finishReason;
10623
+ try {
10624
+ finishReason = result.finishReason;
10625
+ } catch {
10626
+ finishReason = void 0;
10627
+ }
10628
+ if (isPromiseLike(finishReason)) {
10629
+ void Promise.resolve(finishReason).catch(() => {
10630
+ });
10631
+ } else if (finishReason !== void 0) {
10632
+ metadata.finish_reason = finishReason;
10162
10633
  }
10163
10634
  return Object.keys(metadata).length > 0 ? { metadata } : {};
10164
10635
  }
10165
- function resolveAISDKModel(model) {
10636
+ function resolveAISDKModel(model, aiSDK) {
10166
10637
  if (typeof model !== "string") {
10167
10638
  return model;
10168
10639
  }
10169
- const provider = globalThis.AI_SDK_DEFAULT_PROVIDER ?? null;
10640
+ const provider = globalThis.AI_SDK_DEFAULT_PROVIDER ?? aiSDK?.gateway ?? null;
10170
10641
  if (provider && typeof provider.languageModel === "function") {
10171
10642
  return provider.languageModel(model);
10172
10643
  }
@@ -10189,15 +10660,15 @@ function processAISDKOutput(output, denyOutputPaths) {
10189
10660
  }
10190
10661
  function extractTokenMetrics(result) {
10191
10662
  const metrics = {};
10192
- let usage = result?.totalUsage || result?.usage;
10193
- if (!usage && result) {
10194
- try {
10195
- if ("totalUsage" in result && typeof result.totalUsage !== "function") {
10196
- usage = result.totalUsage;
10197
- } else if ("usage" in result && typeof result.usage !== "function") {
10198
- usage = result.usage;
10199
- }
10200
- } catch {
10663
+ let usage;
10664
+ const totalUsageValue = safeResultFieldRead(result, "totalUsage");
10665
+ if (totalUsageValue !== void 0 && !isPromiseLike(totalUsageValue)) {
10666
+ usage = totalUsageValue;
10667
+ }
10668
+ if (!usage) {
10669
+ const usageValue = safeResultFieldRead(result, "usage");
10670
+ if (usageValue !== void 0 && !isPromiseLike(usageValue)) {
10671
+ usage = usageValue;
10201
10672
  }
10202
10673
  }
10203
10674
  if (!usage) {
@@ -10235,6 +10706,22 @@ function extractTokenMetrics(result) {
10235
10706
  }
10236
10707
  return metrics;
10237
10708
  }
10709
+ function safeResultFieldRead(result, field) {
10710
+ return safeSerializableFieldRead(result, field);
10711
+ }
10712
+ function safeSerializableFieldRead(obj, field) {
10713
+ try {
10714
+ const value = obj?.[field];
10715
+ if (isPromiseLike(value)) {
10716
+ void Promise.resolve(value).catch(() => {
10717
+ });
10718
+ return void 0;
10719
+ }
10720
+ return value;
10721
+ } catch {
10722
+ return void 0;
10723
+ }
10724
+ }
10238
10725
  function aggregateAISDKChunks(chunks, _result, endEvent) {
10239
10726
  const lastChunk = chunks[chunks.length - 1];
10240
10727
  const output = {};
@@ -10243,17 +10730,21 @@ function aggregateAISDKChunks(chunks, _result, endEvent) {
10243
10730
  if (lastChunk) {
10244
10731
  metrics = hasModelChildTracing(endEvent) ? {} : extractTokenMetrics(lastChunk);
10245
10732
  metadata = buildResolvedMetadataPayload(lastChunk).metadata;
10246
- if (lastChunk.text !== void 0) {
10247
- output.text = lastChunk.text;
10733
+ const text = safeSerializableFieldRead(lastChunk, "text");
10734
+ if (text !== void 0) {
10735
+ output.text = text;
10248
10736
  }
10249
- if (lastChunk.object !== void 0) {
10250
- output.object = lastChunk.object;
10737
+ const objectValue = safeSerializableFieldRead(lastChunk, "object");
10738
+ if (objectValue !== void 0) {
10739
+ output.object = objectValue;
10251
10740
  }
10252
- if (lastChunk.finishReason !== void 0) {
10253
- output.finishReason = lastChunk.finishReason;
10741
+ const finishReason = safeSerializableFieldRead(lastChunk, "finishReason");
10742
+ if (finishReason !== void 0) {
10743
+ output.finishReason = finishReason;
10254
10744
  }
10255
- if (lastChunk.toolCalls !== void 0) {
10256
- output.toolCalls = lastChunk.toolCalls;
10745
+ const toolCalls = safeSerializableFieldRead(lastChunk, "toolCalls");
10746
+ if (toolCalls !== void 0) {
10747
+ output.toolCalls = toolCalls;
10257
10748
  }
10258
10749
  }
10259
10750
  finalizeAISDKChildTracing(endEvent);
@@ -10262,6 +10753,7 @@ function aggregateAISDKChunks(chunks, _result, endEvent) {
10262
10753
  function extractGetterValues(obj) {
10263
10754
  const getterValues = {};
10264
10755
  const getterNames = [
10756
+ "content",
10265
10757
  "text",
10266
10758
  "object",
10267
10759
  "finishReason",
@@ -10277,8 +10769,17 @@ function extractGetterValues(obj) {
10277
10769
  ];
10278
10770
  for (const name of getterNames) {
10279
10771
  try {
10280
- if (obj && name in obj && isSerializableOutputValue(obj[name])) {
10281
- getterValues[name] = obj[name];
10772
+ if (!obj || !(name in obj)) {
10773
+ continue;
10774
+ }
10775
+ const value = obj[name];
10776
+ if (isPromiseLike(value)) {
10777
+ void Promise.resolve(value).catch(() => {
10778
+ });
10779
+ continue;
10780
+ }
10781
+ if (isSerializableOutputValue(value)) {
10782
+ getterValues[name] = value;
10282
10783
  }
10283
10784
  } catch {
10284
10785
  }
@@ -10300,6 +10801,11 @@ function extractSerializableOutputFields(output) {
10300
10801
  for (const name of directFieldNames) {
10301
10802
  try {
10302
10803
  const value = output?.[name];
10804
+ if (isPromiseLike(value)) {
10805
+ void Promise.resolve(value).catch(() => {
10806
+ });
10807
+ continue;
10808
+ }
10303
10809
  if (isSerializableOutputValue(value)) {
10304
10810
  serialized[name] = value;
10305
10811
  }
@@ -10311,6 +10817,9 @@ function extractSerializableOutputFields(output) {
10311
10817
  ...extractGetterValues(output)
10312
10818
  };
10313
10819
  }
10820
+ function isPromiseLike(value) {
10821
+ return value != null && typeof value === "object" && typeof value.then === "function";
10822
+ }
10314
10823
  function isSerializableOutputValue(value) {
10315
10824
  if (typeof value === "function") {
10316
10825
  return false;
@@ -10352,8 +10861,9 @@ function parseGatewayModelString(modelString) {
10352
10861
  return { model: modelString };
10353
10862
  }
10354
10863
  function extractGatewayRoutingInfo(result) {
10355
- if (result?.steps && Array.isArray(result.steps) && result.steps.length > 0) {
10356
- const routing2 = result.steps[0]?.providerMetadata?.gateway?.routing;
10864
+ const steps = safeSerializableFieldRead(result, "steps");
10865
+ if (Array.isArray(steps) && steps.length > 0) {
10866
+ const routing2 = steps[0]?.providerMetadata?.gateway?.routing;
10357
10867
  if (routing2) {
10358
10868
  return {
10359
10869
  provider: routing2.resolvedProvider || routing2.finalProvider,
@@ -10361,7 +10871,11 @@ function extractGatewayRoutingInfo(result) {
10361
10871
  };
10362
10872
  }
10363
10873
  }
10364
- const routing = result?.providerMetadata?.gateway?.routing;
10874
+ const providerMetadata = safeSerializableFieldRead(
10875
+ result,
10876
+ "providerMetadata"
10877
+ );
10878
+ const routing = providerMetadata?.gateway?.routing;
10365
10879
  if (routing) {
10366
10880
  return {
10367
10881
  provider: routing.resolvedProvider || routing.finalProvider,
@@ -10371,10 +10885,11 @@ function extractGatewayRoutingInfo(result) {
10371
10885
  return null;
10372
10886
  }
10373
10887
  function extractCostFromResult(result) {
10374
- if (result?.steps && Array.isArray(result.steps) && result.steps.length > 0) {
10888
+ const steps = safeSerializableFieldRead(result, "steps");
10889
+ if (Array.isArray(steps) && steps.length > 0) {
10375
10890
  let totalCost = 0;
10376
10891
  let foundCost = false;
10377
- for (const step of result.steps) {
10892
+ for (const step of steps) {
10378
10893
  const gateway2 = step?.providerMetadata?.gateway;
10379
10894
  const stepCost = parseGatewayCost(gateway2?.cost) || parseGatewayCost(gateway2?.marketCost);
10380
10895
  if (stepCost !== void 0 && stepCost > 0) {
@@ -10386,7 +10901,11 @@ function extractCostFromResult(result) {
10386
10901
  return totalCost;
10387
10902
  }
10388
10903
  }
10389
- const gateway = result?.providerMetadata?.gateway;
10904
+ const providerMetadata = safeSerializableFieldRead(
10905
+ result,
10906
+ "providerMetadata"
10907
+ );
10908
+ const gateway = providerMetadata?.gateway;
10390
10909
  const directCost = parseGatewayCost(gateway?.cost) || parseGatewayCost(gateway?.marketCost);
10391
10910
  if (directCost !== void 0 && directCost > 0) {
10392
10911
  return directCost;
@@ -11087,20 +11606,16 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
11087
11606
  );
11088
11607
  });
11089
11608
  },
11090
- onComplete: () => {
11091
- void state.processing.then(() => finalizeQuerySpan(state)).finally(() => {
11092
- spans.delete(event);
11093
- });
11094
- },
11095
- onError: (error) => {
11096
- void state.processing.then(() => {
11097
- state.span.log({
11098
- error: error.message
11099
- });
11100
- }).then(() => finalizeQuerySpan(state)).finally(() => {
11101
- spans.delete(event);
11609
+ onComplete: () => state.processing.then(() => finalizeQuerySpan(state)).finally(() => {
11610
+ spans.delete(event);
11611
+ }),
11612
+ onError: (error) => state.processing.then(() => {
11613
+ state.span.log({
11614
+ error: error.message
11102
11615
  });
11103
- }
11616
+ }).then(() => finalizeQuerySpan(state)).finally(() => {
11617
+ spans.delete(event);
11618
+ })
11104
11619
  });
11105
11620
  return;
11106
11621
  }
@@ -11248,12 +11763,14 @@ var GoogleGenAIPlugin = class extends BasePlugin {
11248
11763
  const params = event.arguments[0];
11249
11764
  streamEvent.googleGenAIInput = serializeInput(params);
11250
11765
  streamEvent.googleGenAIMetadata = extractMetadata(params);
11766
+ streamEvent.googleGenAIStartTime = getCurrentUnixTimestamp();
11251
11767
  },
11252
11768
  asyncEnd: (event) => {
11253
11769
  const streamEvent = event;
11254
11770
  patchGoogleGenAIStreamingResult({
11255
11771
  input: streamEvent.googleGenAIInput,
11256
11772
  metadata: streamEvent.googleGenAIMetadata,
11773
+ startTime: streamEvent.googleGenAIStartTime,
11257
11774
  result: streamEvent.result
11258
11775
  });
11259
11776
  },
@@ -11306,7 +11823,7 @@ function logErrorAndEndSpan(states, event) {
11306
11823
  states.delete(event);
11307
11824
  }
11308
11825
  function patchGoogleGenAIStreamingResult(args) {
11309
- const { input, metadata, result } = args;
11826
+ const { input, metadata, result, startTime } = args;
11310
11827
  if (!input || !metadata || !result || typeof result !== "object" || typeof result.next !== "function") {
11311
11828
  return false;
11312
11829
  }
@@ -11314,7 +11831,7 @@ function patchGoogleGenAIStreamingResult(args) {
11314
11831
  let firstTokenTime = null;
11315
11832
  let finalized = false;
11316
11833
  let span = null;
11317
- let startTime = null;
11834
+ const requestStartTime = startTime ?? getCurrentUnixTimestamp();
11318
11835
  const ensureSpan = () => {
11319
11836
  if (!span) {
11320
11837
  span = startSpan({
@@ -11327,7 +11844,6 @@ function patchGoogleGenAIStreamingResult(args) {
11327
11844
  metadata
11328
11845
  }
11329
11846
  });
11330
- startTime = getCurrentUnixTimestamp();
11331
11847
  }
11332
11848
  return span;
11333
11849
  };
@@ -11381,11 +11897,11 @@ function patchGoogleGenAIStreamingResult(args) {
11381
11897
  }
11382
11898
  chunks.push(nextResult.value);
11383
11899
  }
11384
- if (nextResult.done && startTime !== null) {
11900
+ if (nextResult.done) {
11385
11901
  finalize({
11386
11902
  result: aggregateGenerateContentChunks(
11387
11903
  chunks,
11388
- startTime,
11904
+ requestStartTime,
11389
11905
  firstTokenTime
11390
11906
  )
11391
11907
  });
@@ -11405,13 +11921,13 @@ function patchGoogleGenAIStreamingResult(args) {
11405
11921
  ...returnArgs
11406
11922
  );
11407
11923
  } finally {
11408
- if (startTime !== null) {
11924
+ if (chunks.length > 0) {
11409
11925
  finalize({
11410
- result: chunks.length > 0 ? aggregateGenerateContentChunks(
11926
+ result: aggregateGenerateContentChunks(
11411
11927
  chunks,
11412
- startTime,
11928
+ requestStartTime,
11413
11929
  firstTokenTime
11414
- ) : void 0
11930
+ )
11415
11931
  });
11416
11932
  } else {
11417
11933
  finalize({});
@@ -11703,500 +12219,488 @@ var openRouterChannels = defineChannels("@openrouter/sdk", {
11703
12219
  channelName: "callModel",
11704
12220
  kind: "sync-stream"
11705
12221
  }),
12222
+ callModelTurn: channel({
12223
+ channelName: "callModel.turn",
12224
+ kind: "async"
12225
+ }),
11706
12226
  toolExecute: channel({
11707
12227
  channelName: "tool.execute",
11708
12228
  kind: "async"
11709
12229
  })
11710
12230
  });
11711
12231
 
11712
- // src/openrouter-utils.ts
11713
- var TOKEN_NAME_MAP2 = {
11714
- promptTokens: "prompt_tokens",
11715
- inputTokens: "prompt_tokens",
11716
- completionTokens: "completion_tokens",
11717
- outputTokens: "completion_tokens",
11718
- totalTokens: "tokens",
11719
- prompt_tokens: "prompt_tokens",
11720
- input_tokens: "prompt_tokens",
11721
- completion_tokens: "completion_tokens",
11722
- output_tokens: "completion_tokens",
11723
- total_tokens: "tokens"
11724
- };
11725
- var TOKEN_DETAIL_PREFIX_MAP = {
11726
- promptTokensDetails: "prompt",
11727
- inputTokensDetails: "prompt",
11728
- completionTokensDetails: "completion",
11729
- outputTokensDetails: "completion",
11730
- costDetails: "cost",
11731
- prompt_tokens_details: "prompt",
11732
- input_tokens_details: "prompt",
11733
- completion_tokens_details: "completion",
11734
- output_tokens_details: "completion",
11735
- cost_details: "cost"
11736
- };
11737
- function camelToSnake(value) {
11738
- return value.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`);
11739
- }
11740
- function parseOpenRouterMetricsFromUsage(usage) {
11741
- if (!isObject(usage)) {
11742
- return {};
11743
- }
11744
- const metrics = {};
11745
- for (const [name, value] of Object.entries(usage)) {
11746
- if (typeof value === "number") {
11747
- metrics[TOKEN_NAME_MAP2[name] || camelToSnake(name)] = value;
11748
- continue;
11749
- }
11750
- if (!isObject(value)) {
11751
- continue;
11752
- }
11753
- const prefix = TOKEN_DETAIL_PREFIX_MAP[name];
11754
- if (!prefix) {
11755
- continue;
11756
- }
11757
- for (const [nestedName, nestedValue] of Object.entries(value)) {
11758
- if (typeof nestedValue !== "number") {
11759
- continue;
11760
- }
11761
- metrics[`${prefix}_${camelToSnake(nestedName)}`] = nestedValue;
11762
- }
11763
- }
11764
- return metrics;
11765
- }
11766
- function extractOpenRouterUsageMetadata(usage) {
11767
- if (!isObject(usage)) {
11768
- return void 0;
11769
- }
11770
- const metadata = {};
11771
- if (typeof usage.isByok === "boolean") {
11772
- metadata.is_byok = usage.isByok;
11773
- } else if (typeof usage.is_byok === "boolean") {
11774
- metadata.is_byok = usage.is_byok;
11775
- }
11776
- return Object.keys(metadata).length > 0 ? metadata : void 0;
11777
- }
11778
-
11779
- // src/openrouter-logging.ts
11780
- var OMITTED_OPENROUTER_KEYS = /* @__PURE__ */ new Set([
11781
- "execute",
11782
- "render",
11783
- "nextTurnParams",
11784
- "requireApproval"
11785
- ]);
11786
- function parseOpenRouterModelString(model) {
11787
- if (typeof model !== "string") {
11788
- return { model };
11789
- }
11790
- const slashIndex = model.indexOf("/");
11791
- if (slashIndex > 0 && slashIndex < model.length - 1) {
11792
- return {
11793
- provider: model.substring(0, slashIndex),
11794
- model: model.substring(slashIndex + 1)
11795
- };
11796
- }
11797
- return { model };
11798
- }
11799
- function isZodSchema2(value) {
11800
- return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
11801
- }
11802
- function serializeZodSchema2(schema) {
11803
- try {
11804
- return zodToJsonSchema(schema);
11805
- } catch {
11806
- return {
11807
- type: "object",
11808
- description: "Zod schema (conversion failed)"
11809
- };
11810
- }
11811
- }
11812
- function serializeOpenRouterTool(tool) {
11813
- if (!isObject(tool)) {
11814
- return tool;
11815
- }
11816
- const serialized = {};
11817
- for (const [key, value] of Object.entries(tool)) {
11818
- if (OMITTED_OPENROUTER_KEYS.has(key)) {
11819
- continue;
11820
- }
11821
- if (key === "function" && isObject(value)) {
11822
- serialized.function = sanitizeOpenRouterLoggedValue(value);
11823
- continue;
11824
- }
11825
- serialized[key] = sanitizeOpenRouterLoggedValue(value);
11826
- }
11827
- return serialized;
11828
- }
11829
- function serializeOpenRouterToolsForLogging(tools) {
11830
- if (!Array.isArray(tools)) {
11831
- return void 0;
11832
- }
11833
- return tools.map((tool) => serializeOpenRouterTool(tool));
11834
- }
11835
- function sanitizeOpenRouterLoggedValue(value) {
11836
- if (isZodSchema2(value)) {
11837
- return serializeZodSchema2(value);
11838
- }
11839
- if (typeof value === "function") {
11840
- return "[Function]";
11841
- }
11842
- if (Array.isArray(value)) {
11843
- return value.map((entry) => sanitizeOpenRouterLoggedValue(entry));
11844
- }
11845
- if (!isObject(value)) {
11846
- return value;
12232
+ // src/instrumentation/plugins/openrouter-plugin.ts
12233
+ var OpenRouterPlugin = class extends BasePlugin {
12234
+ onEnable() {
12235
+ this.subscribeToOpenRouterChannels();
11847
12236
  }
11848
- const sanitized = {};
11849
- for (const [key, entry] of Object.entries(value)) {
11850
- if (OMITTED_OPENROUTER_KEYS.has(key)) {
11851
- continue;
11852
- }
11853
- if (key === "tools" && Array.isArray(entry)) {
11854
- sanitized.tools = serializeOpenRouterToolsForLogging(entry);
11855
- continue;
11856
- }
11857
- sanitized[key] = sanitizeOpenRouterLoggedValue(entry);
12237
+ onDisable() {
12238
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
11858
12239
  }
11859
- return sanitized;
11860
- }
11861
- function buildOpenRouterMetadata(metadata, httpReferer, xTitle) {
11862
- const sanitized = sanitizeOpenRouterLoggedValue(metadata);
11863
- const metadataRecord = isObject(sanitized) ? sanitized : {};
11864
- const { model, provider: providerRouting, ...rest } = metadataRecord;
11865
- const normalizedModel = parseOpenRouterModelString(model);
11866
- return {
11867
- ...rest,
11868
- ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
11869
- ...providerRouting !== void 0 ? { providerRouting } : {},
11870
- ...httpReferer !== void 0 ? { httpReferer } : {},
11871
- ...xTitle !== void 0 ? { xTitle } : {},
11872
- provider: normalizedModel.provider || "openrouter"
11873
- };
11874
- }
11875
- function buildOpenRouterEmbeddingMetadata(metadata, httpReferer, xTitle) {
11876
- const normalized = buildOpenRouterMetadata(metadata, httpReferer, xTitle);
11877
- return typeof normalized.model === "string" ? {
11878
- ...normalized,
11879
- embedding_model: normalized.model
11880
- } : normalized;
11881
- }
11882
- function extractOpenRouterCallModelInput(request) {
11883
- return isObject(request) && "input" in request ? sanitizeOpenRouterLoggedValue(request.input) : void 0;
11884
- }
11885
- function extractOpenRouterCallModelMetadata(request) {
11886
- if (!isObject(request)) {
11887
- return { provider: "openrouter" };
11888
- }
11889
- const { input: _input, ...metadata } = request;
11890
- return buildOpenRouterMetadata(metadata, void 0, void 0);
11891
- }
11892
- function extractOpenRouterResponseMetadata(result) {
11893
- if (!isObject(result)) {
11894
- return void 0;
12240
+ subscribeToOpenRouterChannels() {
12241
+ this.unsubscribers.push(
12242
+ traceStreamingChannel(openRouterChannels.chatSend, {
12243
+ name: "openrouter.chat.send",
12244
+ type: "llm" /* LLM */,
12245
+ extractInput: (args) => {
12246
+ const request = getOpenRouterRequestArg(args);
12247
+ const chatGenerationParams = isObject(request?.chatGenerationParams) ? request.chatGenerationParams : {};
12248
+ const httpReferer = request?.httpReferer;
12249
+ const xTitle = request?.xTitle;
12250
+ const { messages, ...metadata } = chatGenerationParams;
12251
+ return {
12252
+ input: messages,
12253
+ metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
12254
+ };
12255
+ },
12256
+ extractOutput: (result) => {
12257
+ return isObject(result) ? result.choices : void 0;
12258
+ },
12259
+ extractMetrics: (result, startTime) => {
12260
+ const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
12261
+ if (startTime) {
12262
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
12263
+ }
12264
+ return metrics;
12265
+ },
12266
+ aggregateChunks: aggregateOpenRouterChatChunks
12267
+ })
12268
+ );
12269
+ this.unsubscribers.push(
12270
+ traceAsyncChannel(openRouterChannels.embeddingsGenerate, {
12271
+ name: "openrouter.embeddings.generate",
12272
+ type: "llm" /* LLM */,
12273
+ extractInput: (args) => {
12274
+ const request = getOpenRouterRequestArg(args);
12275
+ const requestBody = isObject(request?.requestBody) ? request.requestBody : {};
12276
+ const httpReferer = request?.httpReferer;
12277
+ const xTitle = request?.xTitle;
12278
+ const { input, ...metadata } = requestBody;
12279
+ return {
12280
+ input,
12281
+ metadata: buildOpenRouterEmbeddingMetadata(
12282
+ metadata,
12283
+ httpReferer,
12284
+ xTitle
12285
+ )
12286
+ };
12287
+ },
12288
+ extractOutput: (result) => {
12289
+ if (!isObject(result)) {
12290
+ return void 0;
12291
+ }
12292
+ const embedding = result.data?.[0]?.embedding;
12293
+ return Array.isArray(embedding) ? { embedding_length: embedding.length } : void 0;
12294
+ },
12295
+ extractMetadata: (result) => {
12296
+ if (!isObject(result)) {
12297
+ return void 0;
12298
+ }
12299
+ return extractOpenRouterResponseMetadata(result);
12300
+ },
12301
+ extractMetrics: (result) => {
12302
+ return isObject(result) ? parseOpenRouterMetricsFromUsage(result.usage) : {};
12303
+ }
12304
+ })
12305
+ );
12306
+ this.unsubscribers.push(
12307
+ traceStreamingChannel(openRouterChannels.betaResponsesSend, {
12308
+ name: "openrouter.beta.responses.send",
12309
+ type: "llm" /* LLM */,
12310
+ extractInput: (args) => {
12311
+ const request = getOpenRouterRequestArg(args);
12312
+ const openResponsesRequest = isObject(request?.openResponsesRequest) ? request.openResponsesRequest : {};
12313
+ const httpReferer = request?.httpReferer;
12314
+ const xTitle = request?.xTitle;
12315
+ const { input, ...metadata } = openResponsesRequest;
12316
+ return {
12317
+ input,
12318
+ metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
12319
+ };
12320
+ },
12321
+ extractOutput: (result) => extractOpenRouterResponseOutput(result),
12322
+ extractMetadata: (result) => extractOpenRouterResponseMetadata(result),
12323
+ extractMetrics: (result, startTime) => {
12324
+ const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
12325
+ if (startTime) {
12326
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
12327
+ }
12328
+ return metrics;
12329
+ },
12330
+ aggregateChunks: aggregateOpenRouterResponseStreamEvents
12331
+ })
12332
+ );
12333
+ this.unsubscribers.push(
12334
+ traceSyncStreamChannel(openRouterChannels.callModel, {
12335
+ name: "openrouter.callModel",
12336
+ type: "llm" /* LLM */,
12337
+ extractInput: (args) => {
12338
+ const request = getOpenRouterCallModelRequestArg(args);
12339
+ return {
12340
+ input: request ? extractOpenRouterCallModelInput(request) : void 0,
12341
+ metadata: request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" }
12342
+ };
12343
+ },
12344
+ patchResult: ({ endEvent, result, span }) => {
12345
+ return patchOpenRouterCallModelResult({
12346
+ request: getOpenRouterCallModelRequestArg(endEvent.arguments),
12347
+ result,
12348
+ span
12349
+ });
12350
+ }
12351
+ })
12352
+ );
12353
+ this.unsubscribers.push(
12354
+ traceAsyncChannel(openRouterChannels.callModelTurn, {
12355
+ name: "openrouter.beta.responses.send",
12356
+ type: "llm" /* LLM */,
12357
+ extractInput: (args, event) => {
12358
+ const request = getOpenRouterCallModelRequestArg(args);
12359
+ const metadata = request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" };
12360
+ if (isObject(metadata) && "tools" in metadata) {
12361
+ delete metadata.tools;
12362
+ }
12363
+ return {
12364
+ input: request ? extractOpenRouterCallModelInput(request) : void 0,
12365
+ metadata: {
12366
+ ...metadata,
12367
+ step: event.step,
12368
+ step_type: event.stepType
12369
+ }
12370
+ };
12371
+ },
12372
+ extractOutput: (result) => extractOpenRouterResponseOutput(result),
12373
+ extractMetadata: (result, event) => {
12374
+ if (!isObject(result)) {
12375
+ return {
12376
+ step: event?.step,
12377
+ step_type: event?.stepType
12378
+ };
12379
+ }
12380
+ return {
12381
+ ...extractOpenRouterResponseMetadata(result) || {},
12382
+ ...event?.step !== void 0 ? { step: event.step } : {},
12383
+ ...event?.stepType ? { step_type: event.stepType } : {}
12384
+ };
12385
+ },
12386
+ extractMetrics: (result) => isObject(result) ? parseOpenRouterMetricsFromUsage(result.usage) : {}
12387
+ })
12388
+ );
12389
+ this.unsubscribers.push(
12390
+ traceStreamingChannel(openRouterChannels.toolExecute, {
12391
+ name: "openrouter.tool",
12392
+ type: "tool" /* TOOL */,
12393
+ extractInput: (args, event) => ({
12394
+ input: args[0],
12395
+ metadata: {
12396
+ provider: "openrouter",
12397
+ tool_name: event.toolName,
12398
+ ...event.toolCallId ? { tool_call_id: event.toolCallId } : {}
12399
+ }
12400
+ }),
12401
+ extractOutput: (result) => result,
12402
+ extractMetrics: () => ({}),
12403
+ aggregateChunks: (chunks) => ({
12404
+ output: chunks.length > 0 ? chunks[chunks.length - 1] : void 0,
12405
+ metrics: {}
12406
+ })
12407
+ })
12408
+ );
12409
+ const callModelChannel = openRouterChannels.callModel.tracingChannel();
12410
+ const callModelHandlers = {
12411
+ start: (event) => {
12412
+ const request = getOpenRouterCallModelRequestArg(event.arguments);
12413
+ if (!request) {
12414
+ return;
12415
+ }
12416
+ patchOpenRouterCallModelRequestTools(request);
12417
+ }
12418
+ };
12419
+ callModelChannel.subscribe(callModelHandlers);
12420
+ this.unsubscribers.push(() => {
12421
+ callModelChannel.unsubscribe(callModelHandlers);
12422
+ });
11895
12423
  }
11896
- const { output: _output, data: _data, usage, ...metadata } = result;
11897
- const sanitized = sanitizeOpenRouterLoggedValue(metadata);
11898
- const metadataRecord = isObject(sanitized) ? sanitized : {};
11899
- const { model, provider, ...rest } = metadataRecord;
11900
- const normalizedModel = parseOpenRouterModelString(model);
11901
- const normalizedProvider = (typeof provider === "string" ? provider : void 0) || normalizedModel.provider;
11902
- const usageMetadata = extractOpenRouterUsageMetadata(usage);
11903
- const combined = {
11904
- ...rest,
11905
- ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
11906
- ...usageMetadata || {},
11907
- ...normalizedProvider !== void 0 ? { provider: normalizedProvider } : {}
11908
- };
11909
- return Object.keys(combined).length > 0 ? combined : void 0;
11910
- }
11911
- function extractOpenRouterResponseOutput(response, fallbackOutput) {
11912
- if (isObject(response) && "output" in response && response.output !== void 0) {
11913
- return sanitizeOpenRouterLoggedValue(response.output);
12424
+ };
12425
+ function normalizeArgs(args) {
12426
+ if (Array.isArray(args)) {
12427
+ return args;
11914
12428
  }
11915
- if (fallbackOutput !== void 0) {
11916
- return sanitizeOpenRouterLoggedValue(fallbackOutput);
12429
+ if (isArrayLike(args)) {
12430
+ return Array.from(args);
11917
12431
  }
11918
- return void 0;
12432
+ return [args];
11919
12433
  }
11920
-
11921
- // src/openrouter-tool-wrapping.ts
11922
- var OPENROUTER_WRAPPED_TOOL = Symbol("braintrust.openrouter.wrappedTool");
11923
- var OPENROUTER_WRAPPED_CALL_MODEL_RESULT = Symbol(
11924
- "braintrust.openrouter.wrappedCallModelResult"
11925
- );
11926
- var OPENROUTER_CALL_MODEL_STREAM_METHODS = [
11927
- "getFullResponsesStream",
11928
- "getItemsStream",
11929
- "getNewMessagesStream",
11930
- "getReasoningStream",
11931
- "getTextStream",
11932
- "getToolCallsStream",
11933
- "getToolStream"
11934
- ];
11935
- var OPENROUTER_CALL_MODEL_CONTEXT_METHODS = [
11936
- "cancel",
11937
- "getPendingToolCalls",
11938
- "getState",
11939
- "getToolCalls",
11940
- "requiresApproval"
11941
- ];
11942
- function patchOpenRouterCallModelRequestTools(request) {
11943
- if (!Array.isArray(request.tools) || request.tools.length === 0) {
11944
- return void 0;
11945
- }
11946
- const originalTools = request.tools;
11947
- const wrappedTools = originalTools.map((tool) => wrapOpenRouterTool(tool));
11948
- const didPatch = wrappedTools.some(
11949
- (tool, index) => tool !== originalTools[index]
12434
+ function isArrayLike(value) {
12435
+ return isObject(value) && "length" in value && typeof value.length === "number" && Number.isInteger(value.length) && value.length >= 0;
12436
+ }
12437
+ function getOpenRouterRequestArg(args) {
12438
+ const normalizedArgs = normalizeArgs(args);
12439
+ const keyedCandidate = normalizedArgs.find(
12440
+ (arg) => isObject(arg) && ("chatGenerationParams" in arg || "requestBody" in arg || "openResponsesRequest" in arg)
11950
12441
  );
11951
- if (!didPatch) {
11952
- return void 0;
12442
+ if (isObject(keyedCandidate)) {
12443
+ return keyedCandidate;
11953
12444
  }
11954
- request.tools = wrappedTools;
11955
- return () => {
11956
- request.tools = originalTools;
11957
- };
12445
+ const firstObjectArg = normalizedArgs.find((arg) => isObject(arg));
12446
+ return isObject(firstObjectArg) ? firstObjectArg : void 0;
11958
12447
  }
11959
- function patchOpenRouterCallModelResult(span, result, request) {
11960
- if (!isObject(result) || isWrappedCallModelResult(result)) {
11961
- return false;
11962
- }
11963
- const resultLike = result;
11964
- const hasInstrumentableMethod = typeof resultLike.getResponse === "function" || typeof resultLike.getText === "function" || OPENROUTER_CALL_MODEL_STREAM_METHODS.some(
11965
- (methodName) => typeof resultLike[methodName] === "function"
11966
- );
11967
- if (!hasInstrumentableMethod) {
11968
- return false;
12448
+ function getOpenRouterCallModelRequestArg(args) {
12449
+ const firstObjectArg = normalizeArgs(args).find((arg) => isObject(arg));
12450
+ return isObject(firstObjectArg) ? firstObjectArg : void 0;
12451
+ }
12452
+ var TOKEN_NAME_MAP2 = {
12453
+ promptTokens: "prompt_tokens",
12454
+ inputTokens: "prompt_tokens",
12455
+ completionTokens: "completion_tokens",
12456
+ outputTokens: "completion_tokens",
12457
+ totalTokens: "tokens",
12458
+ prompt_tokens: "prompt_tokens",
12459
+ input_tokens: "prompt_tokens",
12460
+ completion_tokens: "completion_tokens",
12461
+ output_tokens: "completion_tokens",
12462
+ total_tokens: "tokens"
12463
+ };
12464
+ var TOKEN_DETAIL_PREFIX_MAP = {
12465
+ promptTokensDetails: "prompt",
12466
+ inputTokensDetails: "prompt",
12467
+ completionTokensDetails: "completion",
12468
+ outputTokensDetails: "completion",
12469
+ costDetails: "cost",
12470
+ prompt_tokens_details: "prompt",
12471
+ input_tokens_details: "prompt",
12472
+ completion_tokens_details: "completion",
12473
+ output_tokens_details: "completion",
12474
+ cost_details: "cost"
12475
+ };
12476
+ function camelToSnake(value) {
12477
+ return value.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`);
12478
+ }
12479
+ function parseOpenRouterMetricsFromUsage(usage) {
12480
+ if (!isObject(usage)) {
12481
+ return {};
11969
12482
  }
11970
- Object.defineProperty(resultLike, OPENROUTER_WRAPPED_CALL_MODEL_RESULT, {
11971
- value: true,
11972
- enumerable: false,
11973
- configurable: false
11974
- });
11975
- const originalGetResponse = typeof resultLike.getResponse === "function" ? resultLike.getResponse.bind(resultLike) : void 0;
11976
- const originalGetInitialResponse = typeof resultLike.getInitialResponse === "function" ? resultLike.getInitialResponse.bind(resultLike) : void 0;
11977
- const originalMakeFollowupRequest = typeof resultLike.makeFollowupRequest === "function" ? resultLike.makeFollowupRequest.bind(resultLike) : void 0;
11978
- let ended = false;
11979
- let tracedTurnCount = 0;
11980
- const endSpanWithResult = async (response, fallbackOutput) => {
11981
- if (ended) {
11982
- return;
11983
- }
11984
- ended = true;
11985
- const finalResponse = getFinalOpenRouterCallModelResponse(
11986
- resultLike,
11987
- response
11988
- );
11989
- if (finalResponse) {
11990
- const rounds = getOpenRouterCallModelRounds(resultLike);
11991
- const metadata = extractOpenRouterCallModelResultMetadata(
11992
- finalResponse,
11993
- rounds.length + 1
11994
- );
11995
- span.log({
11996
- output: extractOpenRouterResponseOutput(finalResponse, fallbackOutput),
11997
- ...metadata ? { metadata } : {},
11998
- metrics: aggregateOpenRouterCallModelMetrics(rounds, finalResponse)
11999
- });
12000
- span.end();
12001
- return;
12002
- }
12003
- if (fallbackOutput !== void 0) {
12004
- span.log({
12005
- output: fallbackOutput
12006
- });
12483
+ const metrics = {};
12484
+ for (const [name, value] of Object.entries(usage)) {
12485
+ if (typeof value === "number") {
12486
+ metrics[TOKEN_NAME_MAP2[name] || camelToSnake(name)] = value;
12487
+ continue;
12007
12488
  }
12008
- span.end();
12009
- };
12010
- const endSpanWithError = (error) => {
12011
- if (ended) {
12012
- return;
12489
+ if (!isObject(value)) {
12490
+ continue;
12013
12491
  }
12014
- ended = true;
12015
- span.log({
12016
- error: normalizeError(error).message
12017
- });
12018
- span.end();
12019
- };
12020
- const finalizeFromResponse = async (fallbackOutput) => {
12021
- if (!originalGetResponse) {
12022
- await endSpanWithResult(void 0, fallbackOutput);
12023
- return;
12492
+ const prefix = TOKEN_DETAIL_PREFIX_MAP[name];
12493
+ if (!prefix) {
12494
+ continue;
12024
12495
  }
12025
- try {
12026
- await endSpanWithResult(await originalGetResponse(), fallbackOutput);
12027
- } catch {
12028
- await endSpanWithResult(void 0, fallbackOutput);
12496
+ for (const [nestedName, nestedValue] of Object.entries(value)) {
12497
+ if (typeof nestedValue !== "number") {
12498
+ continue;
12499
+ }
12500
+ metrics[`${prefix}_${camelToSnake(nestedName)}`] = nestedValue;
12029
12501
  }
12030
- };
12031
- if (originalGetResponse) {
12032
- resultLike.getResponse = async (...args) => {
12033
- return await withCurrent(span, async () => {
12034
- try {
12035
- const response = await originalGetResponse(...args);
12036
- await endSpanWithResult(response);
12037
- return response;
12038
- } catch (error) {
12039
- endSpanWithError(error);
12040
- throw error;
12041
- }
12042
- });
12043
- };
12044
12502
  }
12045
- if (typeof resultLike.getText === "function") {
12046
- const originalGetText = resultLike.getText.bind(resultLike);
12047
- resultLike.getText = async (...args) => {
12048
- return await withCurrent(span, async () => {
12049
- try {
12050
- const text = await originalGetText(...args);
12051
- await finalizeFromResponse(text);
12052
- return text;
12053
- } catch (error) {
12054
- endSpanWithError(error);
12055
- throw error;
12056
- }
12057
- });
12058
- };
12503
+ return metrics;
12504
+ }
12505
+ function extractOpenRouterUsageMetadata(usage) {
12506
+ if (!isObject(usage)) {
12507
+ return void 0;
12059
12508
  }
12060
- for (const methodName of OPENROUTER_CALL_MODEL_CONTEXT_METHODS) {
12061
- if (typeof resultLike[methodName] !== "function") {
12062
- continue;
12063
- }
12064
- const originalMethod = resultLike[methodName];
12065
- resultLike[methodName] = async (...args) => {
12066
- return await withCurrent(span, async () => {
12067
- return await originalMethod.apply(resultLike, args);
12068
- });
12069
- };
12509
+ const metadata = {};
12510
+ if (typeof usage.isByok === "boolean") {
12511
+ metadata.is_byok = usage.isByok;
12512
+ } else if (typeof usage.is_byok === "boolean") {
12513
+ metadata.is_byok = usage.is_byok;
12070
12514
  }
12071
- for (const methodName of OPENROUTER_CALL_MODEL_STREAM_METHODS) {
12072
- if (typeof resultLike[methodName] !== "function") {
12073
- continue;
12074
- }
12075
- const originalMethod = resultLike[methodName];
12076
- resultLike[methodName] = (...args) => {
12077
- const stream = withCurrent(
12078
- span,
12079
- () => originalMethod.apply(resultLike, args)
12080
- );
12081
- if (!isAsyncIterable2(stream)) {
12082
- return stream;
12083
- }
12084
- return wrapAsyncIterableWithSpan({
12085
- finalize: finalizeFromResponse,
12086
- iteratorFactory: () => stream[Symbol.asyncIterator](),
12087
- onError: endSpanWithError,
12088
- span
12089
- });
12090
- };
12515
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
12516
+ }
12517
+ var OMITTED_OPENROUTER_KEYS = /* @__PURE__ */ new Set([
12518
+ "execute",
12519
+ "render",
12520
+ "nextTurnParams",
12521
+ "requireApproval"
12522
+ ]);
12523
+ function parseOpenRouterModelString(model) {
12524
+ if (typeof model !== "string") {
12525
+ return { model };
12091
12526
  }
12092
- if (originalGetInitialResponse) {
12093
- let initialTurnTraced = false;
12094
- resultLike.getInitialResponse = async (...args) => {
12095
- if (initialTurnTraced) {
12096
- return await withCurrent(span, async () => {
12097
- return await originalGetInitialResponse(...args);
12098
- });
12099
- }
12100
- initialTurnTraced = true;
12101
- const resolvedRequest = getOpenRouterResolvedRequest(resultLike, request);
12102
- const childSpan = startOpenRouterCallModelTurnSpan({
12103
- request: resolvedRequest,
12104
- step: tracedTurnCount + 1,
12105
- stepType: tracedTurnCount === 0 ? "initial" : "continue"
12106
- });
12107
- return await withCurrent(childSpan, async () => {
12108
- try {
12109
- const response = await originalGetInitialResponse(...args);
12110
- tracedTurnCount++;
12111
- finishOpenRouterCallModelTurnSpan({
12112
- response,
12113
- step: tracedTurnCount,
12114
- stepType: tracedTurnCount === 1 ? "initial" : "continue",
12115
- span: childSpan
12116
- });
12117
- return response;
12118
- } catch (error) {
12119
- childSpan.log({
12120
- error: normalizeError(error).message
12121
- });
12122
- childSpan.end();
12123
- throw error;
12124
- }
12125
- });
12527
+ const slashIndex = model.indexOf("/");
12528
+ if (slashIndex > 0 && slashIndex < model.length - 1) {
12529
+ return {
12530
+ provider: model.substring(0, slashIndex),
12531
+ model: model.substring(slashIndex + 1)
12126
12532
  };
12127
12533
  }
12128
- if (originalMakeFollowupRequest) {
12129
- resultLike.makeFollowupRequest = async (...args) => {
12130
- const currentResponse = args[0];
12131
- const toolResults = Array.isArray(args[1]) ? args[1] : [];
12132
- const resolvedRequest = getOpenRouterResolvedRequest(resultLike, request);
12133
- const followupRequest = buildOpenRouterFollowupRequest(
12134
- resolvedRequest,
12135
- currentResponse,
12136
- toolResults
12137
- );
12138
- const childSpan = startOpenRouterCallModelTurnSpan({
12139
- request: followupRequest,
12140
- step: tracedTurnCount + 1,
12141
- stepType: "continue"
12142
- });
12143
- return await withCurrent(childSpan, async () => {
12144
- try {
12145
- const response = await originalMakeFollowupRequest(...args);
12146
- tracedTurnCount++;
12147
- finishOpenRouterCallModelTurnSpan({
12148
- response,
12149
- step: tracedTurnCount,
12150
- stepType: "continue",
12151
- span: childSpan
12152
- });
12153
- return response;
12154
- } catch (error) {
12155
- childSpan.log({
12156
- error: normalizeError(error).message
12157
- });
12158
- childSpan.end();
12159
- throw error;
12160
- }
12161
- });
12534
+ return { model };
12535
+ }
12536
+ function isZodSchema3(value) {
12537
+ return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
12538
+ }
12539
+ function serializeZodSchema3(schema) {
12540
+ try {
12541
+ return zodToJsonSchema(schema);
12542
+ } catch {
12543
+ return {
12544
+ type: "object",
12545
+ description: "Zod schema (conversion failed)"
12162
12546
  };
12163
12547
  }
12164
- return true;
12165
12548
  }
12166
- function wrapOpenRouterTool(tool) {
12167
- if (isWrappedTool(tool) || !tool.function || typeof tool.function !== "object" || typeof tool.function.execute !== "function") {
12549
+ function serializeOpenRouterTool(tool) {
12550
+ if (!isObject(tool)) {
12168
12551
  return tool;
12169
12552
  }
12170
- const toolName = tool.function.name || "tool";
12171
- const originalExecute = tool.function.execute;
12172
- const wrappedTool = {
12173
- ...tool,
12174
- function: {
12175
- ...tool.function,
12176
- execute(...args) {
12177
- return traceToolExecution({
12178
- args,
12179
- execute: () => Reflect.apply(originalExecute, this, args),
12180
- toolCallId: getToolCallId(args[1]),
12181
- toolName
12182
- });
12183
- }
12553
+ const serialized = {};
12554
+ for (const [key, value] of Object.entries(tool)) {
12555
+ if (OMITTED_OPENROUTER_KEYS.has(key)) {
12556
+ continue;
12184
12557
  }
12185
- };
12186
- Object.defineProperty(wrappedTool, OPENROUTER_WRAPPED_TOOL, {
12187
- value: true,
12188
- enumerable: false,
12189
- configurable: false
12190
- });
12191
- return wrappedTool;
12192
- }
12193
- function isWrappedTool(tool) {
12194
- return Boolean(tool[OPENROUTER_WRAPPED_TOOL]);
12558
+ if (key === "function" && isObject(value)) {
12559
+ serialized.function = sanitizeOpenRouterLoggedValue(value);
12560
+ continue;
12561
+ }
12562
+ serialized[key] = sanitizeOpenRouterLoggedValue(value);
12563
+ }
12564
+ return serialized;
12195
12565
  }
12196
- function isWrappedCallModelResult(value) {
12197
- return Boolean(
12198
- isObject(value) && value[OPENROUTER_WRAPPED_CALL_MODEL_RESULT]
12566
+ function serializeOpenRouterToolsForLogging(tools) {
12567
+ if (!Array.isArray(tools)) {
12568
+ return void 0;
12569
+ }
12570
+ return tools.map((tool) => serializeOpenRouterTool(tool));
12571
+ }
12572
+ function sanitizeOpenRouterLoggedValue(value) {
12573
+ if (isZodSchema3(value)) {
12574
+ return serializeZodSchema3(value);
12575
+ }
12576
+ if (typeof value === "function") {
12577
+ return "[Function]";
12578
+ }
12579
+ if (Array.isArray(value)) {
12580
+ return value.map((entry) => sanitizeOpenRouterLoggedValue(entry));
12581
+ }
12582
+ if (!isObject(value)) {
12583
+ return value;
12584
+ }
12585
+ const sanitized = {};
12586
+ for (const [key, entry] of Object.entries(value)) {
12587
+ if (OMITTED_OPENROUTER_KEYS.has(key)) {
12588
+ continue;
12589
+ }
12590
+ if (key === "tools" && Array.isArray(entry)) {
12591
+ sanitized.tools = serializeOpenRouterToolsForLogging(entry);
12592
+ continue;
12593
+ }
12594
+ sanitized[key] = sanitizeOpenRouterLoggedValue(entry);
12595
+ }
12596
+ return sanitized;
12597
+ }
12598
+ function buildOpenRouterMetadata(metadata, httpReferer, xTitle) {
12599
+ const sanitized = sanitizeOpenRouterLoggedValue(metadata);
12600
+ const metadataRecord = isObject(sanitized) ? sanitized : {};
12601
+ const { model, provider: providerRouting, ...rest } = metadataRecord;
12602
+ const normalizedModel = parseOpenRouterModelString(model);
12603
+ return {
12604
+ ...rest,
12605
+ ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
12606
+ ...providerRouting !== void 0 ? { providerRouting } : {},
12607
+ ...httpReferer !== void 0 ? { httpReferer } : {},
12608
+ ...xTitle !== void 0 ? { xTitle } : {},
12609
+ provider: normalizedModel.provider || "openrouter"
12610
+ };
12611
+ }
12612
+ function buildOpenRouterEmbeddingMetadata(metadata, httpReferer, xTitle) {
12613
+ const normalized = buildOpenRouterMetadata(metadata, httpReferer, xTitle);
12614
+ return typeof normalized.model === "string" ? {
12615
+ ...normalized,
12616
+ embedding_model: normalized.model
12617
+ } : normalized;
12618
+ }
12619
+ function extractOpenRouterCallModelInput(request) {
12620
+ return isObject(request) && "input" in request ? sanitizeOpenRouterLoggedValue(request.input) : void 0;
12621
+ }
12622
+ function extractOpenRouterCallModelMetadata(request) {
12623
+ if (!isObject(request)) {
12624
+ return { provider: "openrouter" };
12625
+ }
12626
+ const { input: _input, ...metadata } = request;
12627
+ return buildOpenRouterMetadata(metadata, void 0, void 0);
12628
+ }
12629
+ function extractOpenRouterResponseMetadata(result) {
12630
+ if (!isObject(result)) {
12631
+ return void 0;
12632
+ }
12633
+ const { output: _output, data: _data, usage, ...metadata } = result;
12634
+ const sanitized = sanitizeOpenRouterLoggedValue(metadata);
12635
+ const metadataRecord = isObject(sanitized) ? sanitized : {};
12636
+ const { model, provider, ...rest } = metadataRecord;
12637
+ const normalizedModel = parseOpenRouterModelString(model);
12638
+ const normalizedProvider = (typeof provider === "string" ? provider : void 0) || normalizedModel.provider;
12639
+ const usageMetadata = extractOpenRouterUsageMetadata(usage);
12640
+ const combined = {
12641
+ ...rest,
12642
+ ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
12643
+ ...usageMetadata || {},
12644
+ ...normalizedProvider !== void 0 ? { provider: normalizedProvider } : {}
12645
+ };
12646
+ return Object.keys(combined).length > 0 ? combined : void 0;
12647
+ }
12648
+ function extractOpenRouterResponseOutput(response, fallbackOutput) {
12649
+ if (isObject(response) && "output" in response && response.output !== void 0) {
12650
+ return sanitizeOpenRouterLoggedValue(response.output);
12651
+ }
12652
+ if (fallbackOutput !== void 0) {
12653
+ return sanitizeOpenRouterLoggedValue(fallbackOutput);
12654
+ }
12655
+ return void 0;
12656
+ }
12657
+ var OPENROUTER_WRAPPED_TOOL = Symbol("braintrust.openrouter.wrappedTool");
12658
+ function patchOpenRouterCallModelRequestTools(request) {
12659
+ if (!Array.isArray(request.tools) || request.tools.length === 0) {
12660
+ return void 0;
12661
+ }
12662
+ const originalTools = request.tools;
12663
+ const wrappedTools = originalTools.map((tool) => wrapOpenRouterTool(tool));
12664
+ const didPatch = wrappedTools.some(
12665
+ (tool, index) => tool !== originalTools[index]
12199
12666
  );
12667
+ if (!didPatch) {
12668
+ return void 0;
12669
+ }
12670
+ request.tools = wrappedTools;
12671
+ return () => {
12672
+ request.tools = originalTools;
12673
+ };
12674
+ }
12675
+ function wrapOpenRouterTool(tool) {
12676
+ if (isWrappedTool(tool) || !tool.function || typeof tool.function !== "object" || typeof tool.function.execute !== "function") {
12677
+ return tool;
12678
+ }
12679
+ const toolName = tool.function.name || "tool";
12680
+ const originalExecute = tool.function.execute;
12681
+ const wrappedTool = {
12682
+ ...tool,
12683
+ function: {
12684
+ ...tool.function,
12685
+ execute(...args) {
12686
+ return traceToolExecution({
12687
+ args,
12688
+ execute: () => Reflect.apply(originalExecute, this, args),
12689
+ toolCallId: getToolCallId(args[1]),
12690
+ toolName
12691
+ });
12692
+ }
12693
+ }
12694
+ };
12695
+ Object.defineProperty(wrappedTool, OPENROUTER_WRAPPED_TOOL, {
12696
+ value: true,
12697
+ enumerable: false,
12698
+ configurable: false
12699
+ });
12700
+ return wrappedTool;
12701
+ }
12702
+ function isWrappedTool(tool) {
12703
+ return Boolean(tool[OPENROUTER_WRAPPED_TOOL]);
12200
12704
  }
12201
12705
  function traceToolExecution(args) {
12202
12706
  const tracingChannel = openRouterChannels.toolExecute.tracingChannel();
@@ -12220,7 +12724,7 @@ function traceToolExecution(args) {
12220
12724
  }
12221
12725
  }
12222
12726
  function publishToolResult(tracingChannel, event, result) {
12223
- if (isPromiseLike(result)) {
12727
+ if (isPromiseLike2(result)) {
12224
12728
  return result.then(
12225
12729
  (resolved) => {
12226
12730
  event.result = resolved;
@@ -12242,465 +12746,460 @@ function getToolCallId(context) {
12242
12746
  const toolContext = context;
12243
12747
  return typeof toolContext?.toolCall?.id === "string" ? toolContext.toolCall.id : void 0;
12244
12748
  }
12245
- function extractOpenRouterCallModelResultMetadata(response, turnCount) {
12246
- const combined = {
12247
- ...extractOpenRouterResponseMetadata(response) || {},
12248
- ...turnCount !== void 0 ? { turn_count: turnCount } : {}
12249
- };
12250
- return Object.keys(combined).length > 0 ? combined : void 0;
12251
- }
12252
- function getFinalOpenRouterCallModelResponse(result, response) {
12253
- if (isObject(response)) {
12254
- return response;
12255
- }
12256
- return isObject(result.finalResponse) ? result.finalResponse : void 0;
12257
- }
12258
- function getOpenRouterCallModelRounds(result) {
12259
- if (!Array.isArray(result.allToolExecutionRounds)) {
12260
- return [];
12261
- }
12262
- return result.allToolExecutionRounds.filter((round) => isObject(round)).map((round) => ({
12263
- response: isObject(round.response) ? round.response : void 0,
12264
- round: typeof round.round === "number" ? round.round : void 0,
12265
- toolResults: Array.isArray(round.toolResults) ? round.toolResults : []
12266
- })).filter((round) => round.response !== void 0);
12267
- }
12268
- function aggregateOpenRouterCallModelMetrics(rounds, finalResponse) {
12269
- const metrics = {};
12270
- const responses = [
12271
- ...rounds.map((round) => round.response).filter(isObject),
12272
- finalResponse
12273
- ];
12274
- for (const response of responses) {
12275
- const responseMetrics = parseOpenRouterMetricsFromUsage(response.usage);
12276
- for (const [name, value] of Object.entries(responseMetrics)) {
12277
- metrics[name] = (metrics[name] || 0) + value;
12278
- }
12279
- }
12280
- return metrics;
12281
- }
12282
- function buildNextOpenRouterCallModelInput(currentInput, response, toolResults) {
12283
- const normalizedInput = Array.isArray(currentInput) ? [...currentInput] : currentInput === void 0 ? [] : [currentInput];
12284
- const responseOutput = Array.isArray(response.output) ? response.output : response.output === void 0 ? [] : [response.output];
12285
- return [...normalizedInput, ...responseOutput, ...toolResults].map(
12286
- (entry) => sanitizeOpenRouterLoggedValue(entry)
12287
- );
12749
+ function isPromiseLike2(value) {
12750
+ return !!value && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
12288
12751
  }
12289
- function startOpenRouterCallModelTurnSpan(args) {
12290
- const requestRecord = isObject(args.request) ? args.request : void 0;
12291
- const metadata = requestRecord ? extractOpenRouterCallModelMetadata(requestRecord) : { provider: "openrouter" };
12292
- if (isObject(metadata) && "tools" in metadata) {
12293
- delete metadata.tools;
12294
- }
12295
- return startSpan({
12296
- name: "openrouter.beta.responses.send",
12297
- spanAttributes: {
12298
- type: "llm" /* LLM */
12299
- },
12300
- event: {
12301
- input: requestRecord ? extractOpenRouterCallModelInput(requestRecord) : void 0,
12302
- metadata: {
12303
- ...metadata,
12304
- step: args.step,
12305
- step_type: args.stepType
12752
+ function aggregateOpenRouterChatChunks(chunks) {
12753
+ let role;
12754
+ let content = "";
12755
+ let toolCalls;
12756
+ let finishReason;
12757
+ let metrics = {};
12758
+ for (const chunk of chunks) {
12759
+ metrics = {
12760
+ ...metrics,
12761
+ ...parseOpenRouterMetricsFromUsage(chunk?.usage)
12762
+ };
12763
+ const choice = chunk?.choices?.[0];
12764
+ const delta = choice?.delta;
12765
+ if (!delta) {
12766
+ if (choice?.finish_reason !== void 0) {
12767
+ finishReason = choice.finish_reason;
12306
12768
  }
12769
+ continue;
12307
12770
  }
12308
- });
12309
- }
12310
- function finishOpenRouterCallModelTurnSpan(args) {
12311
- if (!isObject(args.response)) {
12312
- args.span.end();
12313
- return;
12314
- }
12315
- args.span.log({
12316
- output: extractOpenRouterResponseOutput(args.response),
12317
- ...extractOpenRouterResponseMetadata(args.response) ? {
12318
- metadata: {
12319
- ...extractOpenRouterResponseMetadata(args.response),
12320
- ...args.step !== void 0 ? { step: args.step } : {},
12321
- ...args.stepType ? { step_type: args.stepType } : {}
12771
+ if (!role && delta.role) {
12772
+ role = delta.role;
12773
+ }
12774
+ if (typeof delta.content === "string") {
12775
+ content += delta.content;
12776
+ }
12777
+ const choiceFinishReason = choice?.finishReason ?? choice?.finish_reason ?? void 0;
12778
+ const deltaFinishReason = delta.finishReason ?? delta.finish_reason ?? void 0;
12779
+ if (choiceFinishReason !== void 0) {
12780
+ finishReason = choiceFinishReason;
12781
+ } else if (deltaFinishReason !== void 0) {
12782
+ finishReason = deltaFinishReason;
12783
+ }
12784
+ const toolCallDeltas = Array.isArray(delta.toolCalls) ? delta.toolCalls : Array.isArray(delta.tool_calls) ? delta.tool_calls : void 0;
12785
+ if (!toolCallDeltas) {
12786
+ continue;
12787
+ }
12788
+ for (const toolDelta of toolCallDeltas) {
12789
+ if (!toolDelta?.function) {
12790
+ continue;
12322
12791
  }
12323
- } : {},
12324
- metrics: parseOpenRouterMetricsFromUsage(args.response.usage)
12325
- });
12326
- args.span.end();
12327
- }
12328
- function getOpenRouterResolvedRequest(result, request) {
12329
- if (isObject(result.resolvedRequest)) {
12330
- return result.resolvedRequest;
12331
- }
12332
- return request;
12333
- }
12334
- function buildOpenRouterFollowupRequest(request, currentResponse, toolResults) {
12335
- if (!request) {
12336
- return void 0;
12337
- }
12338
- return {
12339
- ...request,
12340
- input: buildNextOpenRouterCallModelInput(
12341
- extractOpenRouterCallModelInput(request),
12342
- isObject(currentResponse) ? currentResponse : {},
12343
- toolResults
12344
- ),
12345
- stream: false
12346
- };
12347
- }
12348
- function wrapAsyncIterableWithSpan(args) {
12349
- return {
12350
- [Symbol.asyncIterator]() {
12351
- const iterator = args.iteratorFactory();
12352
- return {
12353
- next(value) {
12354
- return withCurrent(
12355
- args.span,
12356
- () => value === void 0 ? iterator.next() : iterator.next(value)
12357
- ).then(
12358
- async (result) => {
12359
- if (result.done) {
12360
- await args.finalize();
12361
- }
12362
- return result;
12363
- },
12364
- (error) => {
12365
- args.onError(error);
12366
- throw error;
12367
- }
12368
- );
12369
- },
12370
- return(value) {
12371
- if (typeof iterator.return !== "function") {
12372
- return args.finalize().then(() => ({
12373
- done: true,
12374
- value
12375
- }));
12376
- }
12377
- return withCurrent(args.span, () => iterator.return(value)).then(
12378
- async (result) => {
12379
- await args.finalize();
12380
- return result;
12381
- },
12382
- (error) => {
12383
- args.onError(error);
12384
- throw error;
12385
- }
12386
- );
12387
- },
12388
- throw(error) {
12389
- args.onError(error);
12390
- if (typeof iterator.throw !== "function") {
12391
- return Promise.reject(error);
12792
+ const toolIndex = toolDelta.index ?? 0;
12793
+ const existingToolCall = toolCalls?.[toolIndex];
12794
+ if (!existingToolCall || toolDelta.id && existingToolCall.id !== void 0 && existingToolCall.id !== toolDelta.id) {
12795
+ const nextToolCalls = [...toolCalls || []];
12796
+ nextToolCalls[toolIndex] = {
12797
+ index: toolIndex,
12798
+ id: toolDelta.id,
12799
+ type: toolDelta.type,
12800
+ function: {
12801
+ name: toolDelta.function.name,
12802
+ arguments: toolDelta.function.arguments || ""
12392
12803
  }
12393
- return withCurrent(args.span, () => iterator.throw(error));
12394
- },
12395
- [Symbol.asyncIterator]() {
12396
- return this;
12397
- }
12398
- };
12804
+ };
12805
+ toolCalls = nextToolCalls;
12806
+ continue;
12807
+ }
12808
+ const current = existingToolCall;
12809
+ if (toolDelta.id && !current.id) {
12810
+ current.id = toolDelta.id;
12811
+ }
12812
+ if (toolDelta.type && !current.type) {
12813
+ current.type = toolDelta.type;
12814
+ }
12815
+ if (toolDelta.function.name && !current.function.name) {
12816
+ current.function.name = toolDelta.function.name;
12817
+ }
12818
+ current.function.arguments += toolDelta.function.arguments || "";
12399
12819
  }
12820
+ }
12821
+ return {
12822
+ output: [
12823
+ {
12824
+ index: 0,
12825
+ message: {
12826
+ role,
12827
+ content: content || void 0,
12828
+ ...toolCalls ? { tool_calls: toolCalls } : {}
12829
+ },
12830
+ logprobs: null,
12831
+ finish_reason: finishReason
12832
+ }
12833
+ ],
12834
+ metrics
12400
12835
  };
12401
12836
  }
12402
- function isAsyncIterable2(value) {
12403
- return !!value && (typeof value === "object" || typeof value === "function") && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
12404
- }
12405
- function isPromiseLike(value) {
12406
- return !!value && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
12407
- }
12408
- function normalizeError(error) {
12409
- return error instanceof Error ? error : new Error(String(error));
12837
+ function aggregateOpenRouterResponseStreamEvents(chunks) {
12838
+ let finalResponse;
12839
+ for (const chunk of chunks) {
12840
+ const response = chunk?.response;
12841
+ if (!response) {
12842
+ continue;
12843
+ }
12844
+ if (chunk.type === "response.completed" || chunk.type === "response.incomplete" || chunk.type === "response.failed") {
12845
+ finalResponse = response;
12846
+ }
12847
+ }
12848
+ if (!finalResponse) {
12849
+ return {
12850
+ output: void 0,
12851
+ metrics: {}
12852
+ };
12853
+ }
12854
+ return {
12855
+ output: extractOpenRouterResponseOutput(finalResponse),
12856
+ metrics: parseOpenRouterMetricsFromUsage(finalResponse.usage),
12857
+ ...extractOpenRouterResponseMetadata(finalResponse) ? { metadata: extractOpenRouterResponseMetadata(finalResponse) } : {}
12858
+ };
12410
12859
  }
12411
-
12412
- // src/instrumentation/plugins/openrouter-plugin.ts
12413
- var OpenRouterPlugin = class extends BasePlugin {
12414
- onEnable() {
12415
- this.subscribeToOpenRouterChannels();
12860
+ var OPENROUTER_WRAPPED_CALL_MODEL_RESULT = Symbol(
12861
+ "braintrust.openrouter.wrappedCallModelResult"
12862
+ );
12863
+ var OPENROUTER_CALL_MODEL_STREAM_METHODS = [
12864
+ "getFullResponsesStream",
12865
+ "getItemsStream",
12866
+ "getNewMessagesStream",
12867
+ "getReasoningStream",
12868
+ "getTextStream",
12869
+ "getToolCallsStream",
12870
+ "getToolStream"
12871
+ ];
12872
+ var OPENROUTER_CALL_MODEL_CONTEXT_METHODS = [
12873
+ "cancel",
12874
+ "getPendingToolCalls",
12875
+ "getState",
12876
+ "getToolCalls",
12877
+ "requiresApproval"
12878
+ ];
12879
+ function patchOpenRouterCallModelResult(args) {
12880
+ const { request, result, span } = args;
12881
+ if (!isObject(result) || isWrappedCallModelResult(result)) {
12882
+ return false;
12416
12883
  }
12417
- onDisable() {
12418
- this.unsubscribers = unsubscribeAll(this.unsubscribers);
12884
+ const resultLike = result;
12885
+ const hasInstrumentableMethod = typeof resultLike.getResponse === "function" || typeof resultLike.getText === "function" || OPENROUTER_CALL_MODEL_STREAM_METHODS.some(
12886
+ (methodName) => typeof resultLike[methodName] === "function"
12887
+ );
12888
+ if (!hasInstrumentableMethod) {
12889
+ return false;
12419
12890
  }
12420
- subscribeToOpenRouterChannels() {
12421
- this.unsubscribers.push(
12422
- traceStreamingChannel(openRouterChannels.chatSend, {
12423
- name: "openrouter.chat.send",
12424
- type: "llm" /* LLM */,
12425
- extractInput: (args) => {
12426
- const request = getOpenRouterRequestArg(args);
12427
- const chatGenerationParams = isObject(request?.chatGenerationParams) ? request.chatGenerationParams : {};
12428
- const httpReferer = request?.httpReferer;
12429
- const xTitle = request?.xTitle;
12430
- const { messages, ...metadata } = chatGenerationParams;
12431
- return {
12432
- input: messages,
12433
- metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
12434
- };
12435
- },
12436
- extractOutput: (result) => {
12437
- return isObject(result) ? result.choices : void 0;
12438
- },
12439
- extractMetrics: (result, startTime) => {
12440
- const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
12441
- if (startTime) {
12442
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
12443
- }
12444
- return metrics;
12445
- },
12446
- aggregateChunks: aggregateOpenRouterChatChunks
12447
- })
12448
- );
12449
- this.unsubscribers.push(
12450
- traceAsyncChannel(openRouterChannels.embeddingsGenerate, {
12451
- name: "openrouter.embeddings.generate",
12452
- type: "llm" /* LLM */,
12453
- extractInput: (args) => {
12454
- const request = getOpenRouterRequestArg(args);
12455
- const requestBody = isObject(request?.requestBody) ? request.requestBody : {};
12456
- const httpReferer = request?.httpReferer;
12457
- const xTitle = request?.xTitle;
12458
- const { input, ...metadata } = requestBody;
12459
- return {
12460
- input,
12461
- metadata: buildOpenRouterEmbeddingMetadata(
12462
- metadata,
12463
- httpReferer,
12464
- xTitle
12465
- )
12466
- };
12467
- },
12468
- extractOutput: (result) => {
12469
- if (!isObject(result)) {
12470
- return void 0;
12471
- }
12472
- const embedding = result.data?.[0]?.embedding;
12473
- return Array.isArray(embedding) ? { embedding_length: embedding.length } : void 0;
12474
- },
12475
- extractMetadata: (result) => {
12476
- if (!isObject(result)) {
12477
- return void 0;
12478
- }
12479
- return extractOpenRouterResponseMetadata(result);
12480
- },
12481
- extractMetrics: (result) => {
12482
- return isObject(result) ? parseOpenRouterMetricsFromUsage(result.usage) : {};
12483
- }
12484
- })
12485
- );
12486
- this.unsubscribers.push(
12487
- traceStreamingChannel(openRouterChannels.betaResponsesSend, {
12488
- name: "openrouter.beta.responses.send",
12489
- type: "llm" /* LLM */,
12490
- extractInput: (args) => {
12491
- const request = getOpenRouterRequestArg(args);
12492
- const openResponsesRequest = isObject(request?.openResponsesRequest) ? request.openResponsesRequest : {};
12493
- const httpReferer = request?.httpReferer;
12494
- const xTitle = request?.xTitle;
12495
- const { input, ...metadata } = openResponsesRequest;
12496
- return {
12497
- input,
12498
- metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
12499
- };
12500
- },
12501
- extractOutput: (result) => extractOpenRouterResponseOutput(result),
12502
- extractMetadata: (result) => extractOpenRouterResponseMetadata(result),
12503
- extractMetrics: (result, startTime) => {
12504
- const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
12505
- if (startTime) {
12506
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
12507
- }
12508
- return metrics;
12509
- },
12510
- aggregateChunks: aggregateOpenRouterResponseStreamEvents
12511
- })
12891
+ Object.defineProperty(resultLike, OPENROUTER_WRAPPED_CALL_MODEL_RESULT, {
12892
+ value: true,
12893
+ enumerable: false,
12894
+ configurable: false
12895
+ });
12896
+ const originalGetResponse = typeof resultLike.getResponse === "function" ? resultLike.getResponse.bind(resultLike) : void 0;
12897
+ const originalGetInitialResponse = typeof resultLike.getInitialResponse === "function" ? resultLike.getInitialResponse.bind(resultLike) : void 0;
12898
+ const originalMakeFollowupRequest = typeof resultLike.makeFollowupRequest === "function" ? resultLike.makeFollowupRequest.bind(resultLike) : void 0;
12899
+ let ended = false;
12900
+ let tracedTurnCount = 0;
12901
+ const endSpanWithResult = async (response, fallbackOutput) => {
12902
+ if (ended) {
12903
+ return;
12904
+ }
12905
+ ended = true;
12906
+ const finalResponse = getFinalOpenRouterCallModelResponse(
12907
+ resultLike,
12908
+ response
12512
12909
  );
12513
- this.unsubscribers.push(
12514
- traceSyncStreamChannel(openRouterChannels.callModel, {
12515
- name: "openrouter.callModel",
12516
- type: "llm" /* LLM */,
12517
- extractInput: (args) => {
12518
- const request = getOpenRouterCallModelRequestArg(args);
12519
- return {
12520
- input: request ? extractOpenRouterCallModelInput(request) : void 0,
12521
- metadata: request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" }
12522
- };
12523
- },
12524
- patchResult: ({ endEvent, result, span }) => {
12525
- return patchOpenRouterCallModelResult(
12526
- span,
12527
- result,
12528
- getOpenRouterCallModelRequestArg(endEvent.arguments)
12529
- );
12910
+ if (finalResponse) {
12911
+ const rounds = getOpenRouterCallModelRounds(resultLike);
12912
+ const metadata = extractOpenRouterCallModelResultMetadata(
12913
+ finalResponse,
12914
+ rounds.length + 1
12915
+ );
12916
+ span.log({
12917
+ output: extractOpenRouterResponseOutput(finalResponse, fallbackOutput),
12918
+ ...metadata ? { metadata } : {},
12919
+ metrics: aggregateOpenRouterCallModelMetrics(rounds, finalResponse)
12920
+ });
12921
+ span.end();
12922
+ return;
12923
+ }
12924
+ if (fallbackOutput !== void 0) {
12925
+ span.log({
12926
+ output: fallbackOutput
12927
+ });
12928
+ }
12929
+ span.end();
12930
+ };
12931
+ const endSpanWithError = (error) => {
12932
+ if (ended) {
12933
+ return;
12934
+ }
12935
+ ended = true;
12936
+ span.log({
12937
+ error: normalizeError(error).message
12938
+ });
12939
+ span.end();
12940
+ };
12941
+ const finalizeFromResponse = async (fallbackOutput) => {
12942
+ if (!originalGetResponse) {
12943
+ await endSpanWithResult(void 0, fallbackOutput);
12944
+ return;
12945
+ }
12946
+ try {
12947
+ await endSpanWithResult(await originalGetResponse(), fallbackOutput);
12948
+ } catch {
12949
+ await endSpanWithResult(void 0, fallbackOutput);
12950
+ }
12951
+ };
12952
+ if (originalGetResponse) {
12953
+ resultLike.getResponse = async (...args2) => {
12954
+ return await withCurrent(span, async () => {
12955
+ try {
12956
+ const response = await originalGetResponse(...args2);
12957
+ await endSpanWithResult(response);
12958
+ return response;
12959
+ } catch (error) {
12960
+ endSpanWithError(error);
12961
+ throw error;
12530
12962
  }
12531
- })
12532
- );
12533
- this.unsubscribers.push(
12534
- traceStreamingChannel(openRouterChannels.toolExecute, {
12535
- name: "openrouter.tool",
12536
- type: "tool" /* TOOL */,
12537
- extractInput: (args, event) => ({
12538
- input: args[0],
12539
- metadata: {
12540
- provider: "openrouter",
12541
- tool_name: event.toolName,
12542
- ...event.toolCallId ? { tool_call_id: event.toolCallId } : {}
12543
- }
12544
- }),
12545
- extractOutput: (result) => result,
12546
- extractMetrics: () => ({}),
12547
- aggregateChunks: (chunks) => ({
12548
- output: chunks.length > 0 ? chunks[chunks.length - 1] : void 0,
12549
- metrics: {}
12550
- })
12551
- })
12552
- );
12553
- const callModelChannel = openRouterChannels.callModel.tracingChannel();
12554
- const callModelHandlers = {
12555
- start: (event) => {
12556
- const request = getOpenRouterCallModelRequestArg(event.arguments);
12557
- if (!request) {
12558
- return;
12963
+ });
12964
+ };
12965
+ }
12966
+ if (typeof resultLike.getText === "function") {
12967
+ const originalGetText = resultLike.getText.bind(resultLike);
12968
+ resultLike.getText = async (...args2) => {
12969
+ return await withCurrent(span, async () => {
12970
+ try {
12971
+ const text = await originalGetText(...args2);
12972
+ await finalizeFromResponse(text);
12973
+ return text;
12974
+ } catch (error) {
12975
+ endSpanWithError(error);
12976
+ throw error;
12559
12977
  }
12560
- patchOpenRouterCallModelRequestTools(request);
12978
+ });
12979
+ };
12980
+ }
12981
+ for (const methodName of OPENROUTER_CALL_MODEL_CONTEXT_METHODS) {
12982
+ if (typeof resultLike[methodName] !== "function") {
12983
+ continue;
12984
+ }
12985
+ const originalMethod = resultLike[methodName];
12986
+ resultLike[methodName] = async (...args2) => {
12987
+ return await withCurrent(span, async () => {
12988
+ return await originalMethod.apply(resultLike, args2);
12989
+ });
12990
+ };
12991
+ }
12992
+ for (const methodName of OPENROUTER_CALL_MODEL_STREAM_METHODS) {
12993
+ if (typeof resultLike[methodName] !== "function") {
12994
+ continue;
12995
+ }
12996
+ const originalMethod = resultLike[methodName];
12997
+ resultLike[methodName] = (...args2) => {
12998
+ const stream = withCurrent(
12999
+ span,
13000
+ () => originalMethod.apply(resultLike, args2)
13001
+ );
13002
+ if (!isAsyncIterable2(stream)) {
13003
+ return stream;
13004
+ }
13005
+ return wrapAsyncIterableWithSpan({
13006
+ finalize: finalizeFromResponse,
13007
+ iteratorFactory: () => stream[Symbol.asyncIterator](),
13008
+ onError: endSpanWithError,
13009
+ span
13010
+ });
13011
+ };
13012
+ }
13013
+ if (originalGetInitialResponse) {
13014
+ let initialTurnTraced = false;
13015
+ resultLike.getInitialResponse = async (...args2) => {
13016
+ if (initialTurnTraced) {
13017
+ return await withCurrent(span, async () => {
13018
+ return await originalGetInitialResponse(...args2);
13019
+ });
12561
13020
  }
13021
+ initialTurnTraced = true;
13022
+ const step = tracedTurnCount + 1;
13023
+ const stepType = tracedTurnCount === 0 ? "initial" : "continue";
13024
+ const response = await traceOpenRouterCallModelTurn({
13025
+ fn: async () => {
13026
+ const nextResponse = await originalGetInitialResponse(...args2);
13027
+ tracedTurnCount++;
13028
+ return nextResponse;
13029
+ },
13030
+ parentSpan: span,
13031
+ request: getOpenRouterResolvedRequest(resultLike, request),
13032
+ step,
13033
+ stepType
13034
+ });
13035
+ return response;
12562
13036
  };
12563
- callModelChannel.subscribe(callModelHandlers);
12564
- this.unsubscribers.push(() => {
12565
- callModelChannel.unsubscribe(callModelHandlers);
12566
- });
12567
- }
12568
- };
12569
- function normalizeArgs(args) {
12570
- if (Array.isArray(args)) {
12571
- return args;
12572
13037
  }
12573
- if (isArrayLike(args)) {
12574
- return Array.from(args);
13038
+ if (originalMakeFollowupRequest) {
13039
+ resultLike.makeFollowupRequest = async (...args2) => {
13040
+ const currentResponse = args2[0];
13041
+ const toolResults = Array.isArray(args2[1]) ? args2[1] : [];
13042
+ const step = tracedTurnCount + 1;
13043
+ const response = await traceOpenRouterCallModelTurn({
13044
+ fn: async () => {
13045
+ const nextResponse = await originalMakeFollowupRequest(...args2);
13046
+ tracedTurnCount++;
13047
+ return nextResponse;
13048
+ },
13049
+ parentSpan: span,
13050
+ request: buildOpenRouterFollowupRequest(
13051
+ getOpenRouterResolvedRequest(resultLike, request),
13052
+ currentResponse,
13053
+ toolResults
13054
+ ),
13055
+ step,
13056
+ stepType: "continue"
13057
+ });
13058
+ return response;
13059
+ };
12575
13060
  }
12576
- return [args];
13061
+ return true;
12577
13062
  }
12578
- function isArrayLike(value) {
12579
- return isObject(value) && "length" in value && typeof value.length === "number" && Number.isInteger(value.length) && value.length >= 0;
13063
+ async function traceOpenRouterCallModelTurn(args) {
13064
+ const context = {
13065
+ arguments: [args.request],
13066
+ step: args.step,
13067
+ stepType: args.stepType
13068
+ };
13069
+ return await withCurrent(
13070
+ args.parentSpan,
13071
+ () => openRouterChannels.callModelTurn.tracePromise(args.fn, context)
13072
+ );
12580
13073
  }
12581
- function getOpenRouterRequestArg(args) {
12582
- const normalizedArgs = normalizeArgs(args);
12583
- const keyedCandidate = normalizedArgs.find(
12584
- (arg) => isObject(arg) && ("chatGenerationParams" in arg || "requestBody" in arg || "openResponsesRequest" in arg)
13074
+ function isWrappedCallModelResult(value) {
13075
+ return Boolean(
13076
+ isObject(value) && value[OPENROUTER_WRAPPED_CALL_MODEL_RESULT]
12585
13077
  );
12586
- if (isObject(keyedCandidate)) {
12587
- return keyedCandidate;
13078
+ }
13079
+ function extractOpenRouterCallModelResultMetadata(response, turnCount) {
13080
+ const combined = {
13081
+ ...extractOpenRouterResponseMetadata(response) || {},
13082
+ ...turnCount !== void 0 ? { turn_count: turnCount } : {}
13083
+ };
13084
+ return Object.keys(combined).length > 0 ? combined : void 0;
13085
+ }
13086
+ function getFinalOpenRouterCallModelResponse(result, response) {
13087
+ if (isObject(response)) {
13088
+ return response;
12588
13089
  }
12589
- const firstObjectArg = normalizedArgs.find((arg) => isObject(arg));
12590
- return isObject(firstObjectArg) ? firstObjectArg : void 0;
13090
+ return isObject(result.finalResponse) ? result.finalResponse : void 0;
12591
13091
  }
12592
- function getOpenRouterCallModelRequestArg(args) {
12593
- const firstObjectArg = normalizeArgs(args).find((arg) => isObject(arg));
12594
- return isObject(firstObjectArg) ? firstObjectArg : void 0;
13092
+ function getOpenRouterCallModelRounds(result) {
13093
+ if (!Array.isArray(result.allToolExecutionRounds)) {
13094
+ return [];
13095
+ }
13096
+ return result.allToolExecutionRounds.filter((round) => isObject(round)).map((round) => ({
13097
+ response: isObject(round.response) ? round.response : void 0,
13098
+ round: typeof round.round === "number" ? round.round : void 0,
13099
+ toolResults: Array.isArray(round.toolResults) ? round.toolResults : []
13100
+ })).filter((round) => round.response !== void 0);
12595
13101
  }
12596
- function aggregateOpenRouterChatChunks(chunks) {
12597
- let role;
12598
- let content = "";
12599
- let toolCalls;
12600
- let finishReason;
12601
- let metrics = {};
12602
- for (const chunk of chunks) {
12603
- metrics = {
12604
- ...metrics,
12605
- ...parseOpenRouterMetricsFromUsage(chunk?.usage)
12606
- };
12607
- const choice = chunk?.choices?.[0];
12608
- const delta = choice?.delta;
12609
- if (!delta) {
12610
- if (choice?.finish_reason !== void 0) {
12611
- finishReason = choice.finish_reason;
12612
- }
12613
- continue;
12614
- }
12615
- if (!role && delta.role) {
12616
- role = delta.role;
12617
- }
12618
- if (typeof delta.content === "string") {
12619
- content += delta.content;
12620
- }
12621
- const choiceFinishReason = choice?.finishReason ?? choice?.finish_reason ?? void 0;
12622
- const deltaFinishReason = delta.finishReason ?? delta.finish_reason ?? void 0;
12623
- if (choiceFinishReason !== void 0) {
12624
- finishReason = choiceFinishReason;
12625
- } else if (deltaFinishReason !== void 0) {
12626
- finishReason = deltaFinishReason;
12627
- }
12628
- const toolCallDeltas = Array.isArray(delta.toolCalls) ? delta.toolCalls : Array.isArray(delta.tool_calls) ? delta.tool_calls : void 0;
12629
- if (!toolCallDeltas) {
12630
- continue;
12631
- }
12632
- for (const toolDelta of toolCallDeltas) {
12633
- if (!toolDelta?.function) {
12634
- continue;
12635
- }
12636
- const toolIndex = toolDelta.index ?? 0;
12637
- const existingToolCall = toolCalls?.[toolIndex];
12638
- if (!existingToolCall || toolDelta.id && existingToolCall.id !== void 0 && existingToolCall.id !== toolDelta.id) {
12639
- const nextToolCalls = [...toolCalls || []];
12640
- nextToolCalls[toolIndex] = {
12641
- index: toolIndex,
12642
- id: toolDelta.id,
12643
- type: toolDelta.type,
12644
- function: {
12645
- name: toolDelta.function.name,
12646
- arguments: toolDelta.function.arguments || ""
12647
- }
12648
- };
12649
- toolCalls = nextToolCalls;
12650
- continue;
12651
- }
12652
- const current = existingToolCall;
12653
- if (toolDelta.id && !current.id) {
12654
- current.id = toolDelta.id;
12655
- }
12656
- if (toolDelta.type && !current.type) {
12657
- current.type = toolDelta.type;
12658
- }
12659
- if (toolDelta.function.name && !current.function.name) {
12660
- current.function.name = toolDelta.function.name;
12661
- }
12662
- current.function.arguments += toolDelta.function.arguments || "";
13102
+ function aggregateOpenRouterCallModelMetrics(rounds, finalResponse) {
13103
+ const metrics = {};
13104
+ const responses = [
13105
+ ...rounds.map((round) => round.response).filter(isObject),
13106
+ finalResponse
13107
+ ];
13108
+ for (const response of responses) {
13109
+ const responseMetrics = parseOpenRouterMetricsFromUsage(response.usage);
13110
+ for (const [name, value] of Object.entries(responseMetrics)) {
13111
+ metrics[name] = (metrics[name] || 0) + value;
12663
13112
  }
12664
13113
  }
12665
- return {
12666
- output: [
12667
- {
12668
- index: 0,
12669
- message: {
12670
- role,
12671
- content: content || void 0,
12672
- ...toolCalls ? { tool_calls: toolCalls } : {}
12673
- },
12674
- logprobs: null,
12675
- finish_reason: finishReason
12676
- }
12677
- ],
12678
- metrics
12679
- };
13114
+ return metrics;
12680
13115
  }
12681
- function aggregateOpenRouterResponseStreamEvents(chunks) {
12682
- let finalResponse;
12683
- for (const chunk of chunks) {
12684
- const response = chunk?.response;
12685
- if (!response) {
12686
- continue;
12687
- }
12688
- if (chunk.type === "response.completed" || chunk.type === "response.incomplete" || chunk.type === "response.failed") {
12689
- finalResponse = response;
12690
- }
13116
+ function buildNextOpenRouterCallModelInput(currentInput, response, toolResults) {
13117
+ const normalizedInput = Array.isArray(currentInput) ? [...currentInput] : currentInput === void 0 ? [] : [currentInput];
13118
+ const responseOutput = Array.isArray(response.output) ? response.output : response.output === void 0 ? [] : [response.output];
13119
+ return [...normalizedInput, ...responseOutput, ...toolResults].map(
13120
+ (entry) => sanitizeOpenRouterLoggedValue(entry)
13121
+ );
13122
+ }
13123
+ function getOpenRouterResolvedRequest(result, request) {
13124
+ if (isObject(result.resolvedRequest)) {
13125
+ return result.resolvedRequest;
12691
13126
  }
12692
- if (!finalResponse) {
12693
- return {
12694
- output: void 0,
12695
- metrics: {}
12696
- };
13127
+ return request;
13128
+ }
13129
+ function buildOpenRouterFollowupRequest(request, currentResponse, toolResults) {
13130
+ if (!request) {
13131
+ return void 0;
12697
13132
  }
12698
13133
  return {
12699
- output: extractOpenRouterResponseOutput(finalResponse),
12700
- metrics: parseOpenRouterMetricsFromUsage(finalResponse.usage),
12701
- ...extractOpenRouterResponseMetadata(finalResponse) ? { metadata: extractOpenRouterResponseMetadata(finalResponse) } : {}
13134
+ ...request,
13135
+ input: buildNextOpenRouterCallModelInput(
13136
+ extractOpenRouterCallModelInput(request),
13137
+ isObject(currentResponse) ? currentResponse : {},
13138
+ toolResults
13139
+ ),
13140
+ stream: false
13141
+ };
13142
+ }
13143
+ function wrapAsyncIterableWithSpan(args) {
13144
+ return {
13145
+ [Symbol.asyncIterator]() {
13146
+ const iterator = args.iteratorFactory();
13147
+ return {
13148
+ next(value) {
13149
+ return withCurrent(
13150
+ args.span,
13151
+ () => value === void 0 ? iterator.next() : iterator.next(value)
13152
+ ).then(
13153
+ async (result) => {
13154
+ if (result.done) {
13155
+ await args.finalize();
13156
+ }
13157
+ return result;
13158
+ },
13159
+ (error) => {
13160
+ args.onError(error);
13161
+ throw error;
13162
+ }
13163
+ );
13164
+ },
13165
+ return(value) {
13166
+ if (typeof iterator.return !== "function") {
13167
+ return args.finalize().then(() => ({
13168
+ done: true,
13169
+ value
13170
+ }));
13171
+ }
13172
+ return withCurrent(args.span, () => iterator.return(value)).then(
13173
+ async (result) => {
13174
+ await args.finalize();
13175
+ return result;
13176
+ },
13177
+ (error) => {
13178
+ args.onError(error);
13179
+ throw error;
13180
+ }
13181
+ );
13182
+ },
13183
+ throw(error) {
13184
+ args.onError(error);
13185
+ if (typeof iterator.throw !== "function") {
13186
+ return Promise.reject(error);
13187
+ }
13188
+ return withCurrent(args.span, () => iterator.throw(error));
13189
+ },
13190
+ [Symbol.asyncIterator]() {
13191
+ return this;
13192
+ }
13193
+ };
13194
+ }
12702
13195
  };
12703
13196
  }
13197
+ function isAsyncIterable2(value) {
13198
+ return !!value && (typeof value === "object" || typeof value === "function") && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
13199
+ }
13200
+ function normalizeError(error) {
13201
+ return error instanceof Error ? error : new Error(String(error));
13202
+ }
12704
13203
 
12705
13204
  // src/instrumentation/braintrust-plugin.ts
12706
13205
  var BraintrustPlugin = class extends BasePlugin {