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
@@ -165,7 +165,7 @@ var DefaultTracingChannel = class {
165
165
  }
166
166
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
167
167
  tracePromise(fn, _message, thisArg, ...args) {
168
- return Promise.resolve(fn.apply(thisArg, args));
168
+ return fn.apply(thisArg, args);
169
169
  }
170
170
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
171
171
  traceCallback(fn, _position, _message, thisArg, ...args) {
@@ -185,6 +185,7 @@ var iso = {
185
185
  processOn: (_0, _1) => {
186
186
  },
187
187
  basename: (filepath) => filepath.split(/[\\/]/).pop() || filepath,
188
+ // eslint-disable-next-line no-restricted-properties -- preserving intentional console usage.
188
189
  writeln: (text) => console.log(text)
189
190
  };
190
191
  var isomorph_default = iso;
@@ -1881,6 +1882,15 @@ var InternalAbortError = class extends Error {
1881
1882
  this.name = "InternalAbortError";
1882
1883
  }
1883
1884
  };
1885
+ function filterFrom(record, keys) {
1886
+ const out = {};
1887
+ for (const k of Object.keys(record)) {
1888
+ if (!keys.includes(k)) {
1889
+ out[k] = record[k];
1890
+ }
1891
+ }
1892
+ return out;
1893
+ }
1884
1894
 
1885
1895
  // src/generated_types.ts
1886
1896
  import { z as z6 } from "zod/v3";
@@ -8874,7 +8884,7 @@ function patchStreamIfNeeded(stream, options) {
8874
8884
  if (!completed) {
8875
8885
  completed = true;
8876
8886
  try {
8877
- options.onComplete(chunks);
8887
+ await options.onComplete(chunks);
8878
8888
  } catch (error) {
8879
8889
  console.error("Error in stream onComplete handler:", error);
8880
8890
  }
@@ -8886,7 +8896,7 @@ function patchStreamIfNeeded(stream, options) {
8886
8896
  chunks.push(chunk);
8887
8897
  if (options.onChunk) {
8888
8898
  try {
8889
- options.onChunk(chunk);
8899
+ await options.onChunk(chunk);
8890
8900
  } catch (error) {
8891
8901
  console.error("Error in stream onChunk handler:", error);
8892
8902
  }
@@ -8899,7 +8909,7 @@ function patchStreamIfNeeded(stream, options) {
8899
8909
  completed = true;
8900
8910
  if (options.onError) {
8901
8911
  try {
8902
- options.onError(
8912
+ await options.onError(
8903
8913
  error instanceof Error ? error : new Error(String(error)),
8904
8914
  chunks
8905
8915
  );
@@ -8917,7 +8927,7 @@ function patchStreamIfNeeded(stream, options) {
8917
8927
  if (!completed) {
8918
8928
  completed = true;
8919
8929
  try {
8920
- options.onComplete(chunks);
8930
+ await options.onComplete(chunks);
8921
8931
  } catch (error) {
8922
8932
  console.error("Error in stream onComplete handler:", error);
8923
8933
  }
@@ -8934,7 +8944,7 @@ function patchStreamIfNeeded(stream, options) {
8934
8944
  const error = rawError instanceof Error ? rawError : new Error(String(rawError));
8935
8945
  if (options.onError) {
8936
8946
  try {
8937
- options.onError(error, chunks);
8947
+ await options.onError(error, chunks);
8938
8948
  } catch (handlerError) {
8939
8949
  console.error("Error in stream onError handler:", handlerError);
8940
8950
  }
@@ -9643,51 +9653,28 @@ function traceSyncStreamChannel(channel2, config) {
9643
9653
  }
9644
9654
  const { span, startTime } = spanData;
9645
9655
  const endEvent = event;
9646
- if (config.patchResult?.({
9647
- channelName,
9648
- endEvent,
9649
- result: endEvent.result,
9650
- span,
9651
- startTime
9652
- })) {
9653
- return;
9654
- }
9655
- const stream = endEvent.result;
9656
- if (!isSyncStreamLike(stream)) {
9657
- span.end();
9658
- states.delete(event);
9659
- return;
9660
- }
9661
- let first = true;
9662
- stream.on("chunk", () => {
9663
- if (first) {
9664
- span.log({
9665
- metrics: {
9666
- time_to_first_token: getCurrentUnixTimestamp() - startTime
9667
- }
9668
- });
9669
- first = false;
9670
- }
9671
- });
9672
- stream.on("chatCompletion", (completion) => {
9673
- try {
9674
- if (hasChoices(completion)) {
9675
- span.log({
9676
- output: completion.choices
9677
- });
9678
- }
9679
- } catch (error) {
9680
- console.error(
9681
- `Error extracting chatCompletion for ${channelName}:`,
9682
- error
9683
- );
9656
+ const handleResolvedResult = (result) => {
9657
+ const resolvedEndEvent = {
9658
+ ...endEvent,
9659
+ result
9660
+ };
9661
+ if (config.patchResult?.({
9662
+ channelName,
9663
+ endEvent: resolvedEndEvent,
9664
+ result,
9665
+ span,
9666
+ startTime
9667
+ })) {
9668
+ return;
9684
9669
  }
9685
- });
9686
- stream.on("event", (streamEvent) => {
9687
- if (!config.extractFromEvent) {
9670
+ const stream = result;
9671
+ if (!isSyncStreamLike(stream)) {
9672
+ span.end();
9673
+ states.delete(event);
9688
9674
  return;
9689
9675
  }
9690
- try {
9676
+ let first = true;
9677
+ stream.on("chunk", () => {
9691
9678
  if (first) {
9692
9679
  span.log({
9693
9680
  metrics: {
@@ -9696,25 +9683,55 @@ function traceSyncStreamChannel(channel2, config) {
9696
9683
  });
9697
9684
  first = false;
9698
9685
  }
9699
- const extracted = config.extractFromEvent(streamEvent);
9700
- if (extracted && Object.keys(extracted).length > 0) {
9701
- span.log(extracted);
9686
+ });
9687
+ stream.on("chatCompletion", (completion) => {
9688
+ try {
9689
+ if (hasChoices(completion)) {
9690
+ span.log({
9691
+ output: completion.choices
9692
+ });
9693
+ }
9694
+ } catch (error) {
9695
+ console.error(
9696
+ `Error extracting chatCompletion for ${channelName}:`,
9697
+ error
9698
+ );
9702
9699
  }
9703
- } catch (error) {
9704
- console.error(`Error extracting event for ${channelName}:`, error);
9705
- }
9706
- });
9707
- stream.on("end", () => {
9708
- span.end();
9709
- states.delete(event);
9710
- });
9711
- stream.on("error", (error) => {
9712
- span.log({
9713
- error: error.message
9714
9700
  });
9715
- span.end();
9716
- states.delete(event);
9717
- });
9701
+ stream.on("event", (streamEvent) => {
9702
+ if (!config.extractFromEvent) {
9703
+ return;
9704
+ }
9705
+ try {
9706
+ if (first) {
9707
+ span.log({
9708
+ metrics: {
9709
+ time_to_first_token: getCurrentUnixTimestamp() - startTime
9710
+ }
9711
+ });
9712
+ first = false;
9713
+ }
9714
+ const extracted = config.extractFromEvent(streamEvent);
9715
+ if (extracted && Object.keys(extracted).length > 0) {
9716
+ span.log(extracted);
9717
+ }
9718
+ } catch (error) {
9719
+ console.error(`Error extracting event for ${channelName}:`, error);
9720
+ }
9721
+ });
9722
+ stream.on("end", () => {
9723
+ span.end();
9724
+ states.delete(event);
9725
+ });
9726
+ stream.on("error", (error) => {
9727
+ span.log({
9728
+ error: error.message
9729
+ });
9730
+ span.end();
9731
+ states.delete(event);
9732
+ });
9733
+ };
9734
+ handleResolvedResult(endEvent.result);
9718
9735
  },
9719
9736
  error: (event) => {
9720
9737
  logErrorAndEnd(states, event);
@@ -10537,28 +10554,40 @@ function aggregateAnthropicStreamChunks(chunks) {
10537
10554
  case "content_block_start":
10538
10555
  if (event.content_block) {
10539
10556
  contentBlocks[event.index] = event.content_block;
10540
- contentBlockDeltas[event.index] = [];
10557
+ contentBlockDeltas[event.index] = { textDeltas: [], citations: [] };
10541
10558
  }
10542
10559
  break;
10543
- case "content_block_delta":
10544
- if (event.delta?.type === "text_delta") {
10545
- const text = event.delta.text;
10560
+ case "content_block_delta": {
10561
+ const acc = contentBlockDeltas[event.index];
10562
+ const delta = event.delta;
10563
+ if (!delta) break;
10564
+ if (delta.type === "text_delta" && "text" in delta) {
10565
+ const text = delta.text;
10546
10566
  if (text) {
10547
- if (contentBlocks[event.index] !== void 0 || contentBlockDeltas[event.index] !== void 0) {
10548
- contentBlockDeltas[event.index] ??= [];
10549
- contentBlockDeltas[event.index].push(text);
10567
+ if (acc !== void 0) {
10568
+ acc.textDeltas.push(text);
10550
10569
  } else {
10551
10570
  fallbackTextDeltas.push(text);
10552
10571
  }
10553
10572
  }
10554
- } else if (event.delta?.type === "input_json_delta") {
10555
- const partialJson = event.delta.partial_json;
10556
- if (partialJson) {
10557
- contentBlockDeltas[event.index] ??= [];
10558
- contentBlockDeltas[event.index].push(partialJson);
10573
+ } else if (delta.type === "input_json_delta" && "partial_json" in delta) {
10574
+ const partialJson = delta.partial_json;
10575
+ if (partialJson && acc !== void 0) {
10576
+ acc.textDeltas.push(partialJson);
10577
+ }
10578
+ } else if (delta.type === "thinking_delta" && "thinking" in delta) {
10579
+ const thinking = delta.thinking;
10580
+ if (thinking && acc !== void 0) {
10581
+ acc.textDeltas.push(thinking);
10582
+ }
10583
+ } else if (delta.type === "citations_delta" && "citation" in delta) {
10584
+ const citation = delta.citation;
10585
+ if (citation && acc !== void 0) {
10586
+ acc.citations.push(citation);
10559
10587
  }
10560
10588
  }
10561
10589
  break;
10590
+ }
10562
10591
  case "content_block_stop":
10563
10592
  finalizeContentBlock(
10564
10593
  event.index,
@@ -10584,7 +10613,7 @@ function aggregateAnthropicStreamChunks(chunks) {
10584
10613
  })).filter(({ block }) => block !== void 0).sort((left, right) => left.index - right.index).map(({ block }) => block);
10585
10614
  let output = fallbackTextDeltas.join("");
10586
10615
  if (orderedContent.length > 0) {
10587
- if (orderedContent.every(isTextContentBlock)) {
10616
+ if (orderedContent.every(isTextContentBlock) && orderedContent.every((block) => !block.citations?.length)) {
10588
10617
  output = orderedContent.map((block) => block.text).join("");
10589
10618
  } else {
10590
10619
  output = {
@@ -10610,7 +10639,8 @@ function finalizeContentBlock(index, contentBlocks, contentBlockDeltas, fallback
10610
10639
  if (!contentBlock) {
10611
10640
  return;
10612
10641
  }
10613
- const text = contentBlockDeltas[index]?.join("") ?? "";
10642
+ const acc = contentBlockDeltas[index];
10643
+ const text = acc?.textDeltas.join("") ?? "";
10614
10644
  if (isToolUseContentBlock(contentBlock)) {
10615
10645
  if (!text) {
10616
10646
  return;
@@ -10627,20 +10657,28 @@ function finalizeContentBlock(index, contentBlocks, contentBlockDeltas, fallback
10627
10657
  return;
10628
10658
  }
10629
10659
  if (isTextContentBlock(contentBlock)) {
10660
+ if (!text) {
10661
+ delete contentBlocks[index];
10662
+ return;
10663
+ }
10664
+ const updated = { ...contentBlock, text };
10665
+ if (acc?.citations.length) {
10666
+ updated.citations = acc.citations;
10667
+ }
10668
+ contentBlocks[index] = updated;
10669
+ return;
10670
+ }
10671
+ if (isThinkingContentBlock(contentBlock)) {
10630
10672
  if (!text) {
10631
10673
  delete contentBlocks[index];
10632
10674
  return;
10633
10675
  }
10634
10676
  contentBlocks[index] = {
10635
10677
  ...contentBlock,
10636
- text
10678
+ thinking: text
10637
10679
  };
10638
10680
  return;
10639
10681
  }
10640
- if (text) {
10641
- fallbackTextDeltas.push(text);
10642
- }
10643
- delete contentBlocks[index];
10644
10682
  }
10645
10683
  function isTextContentBlock(contentBlock) {
10646
10684
  return contentBlock.type === "text";
@@ -10648,6 +10686,9 @@ function isTextContentBlock(contentBlock) {
10648
10686
  function isToolUseContentBlock(contentBlock) {
10649
10687
  return contentBlock.type === "tool_use";
10650
10688
  }
10689
+ function isThinkingContentBlock(contentBlock) {
10690
+ return contentBlock.type === "thinking";
10691
+ }
10651
10692
  function isAnthropicBase64ContentBlock(input) {
10652
10693
  return (input.type === "image" || input.type === "document") && isObject(input.source) && input.source.type === "base64";
10653
10694
  }
@@ -10702,15 +10743,6 @@ function coalesceInput(messages, system) {
10702
10743
  }
10703
10744
  return input;
10704
10745
  }
10705
- function filterFrom(obj, fieldsToRemove) {
10706
- const result = {};
10707
- for (const [key, value] of Object.entries(obj)) {
10708
- if (!fieldsToRemove.includes(key)) {
10709
- result[key] = value;
10710
- }
10711
- }
10712
- return result;
10713
- }
10714
10746
 
10715
10747
  // src/wrappers/ai-sdk/normalize-logged-output.ts
10716
10748
  var REMOVE_NORMALIZED_VALUE = Symbol("braintrust.ai-sdk.remove-normalized");
@@ -10824,10 +10856,6 @@ var aiSDKChannels = defineChannels("ai", {
10824
10856
  channelName: "streamText",
10825
10857
  kind: "async"
10826
10858
  }),
10827
- streamTextSync: channel({
10828
- channelName: "streamText.sync",
10829
- kind: "sync-stream"
10830
- }),
10831
10859
  generateObject: channel({
10832
10860
  channelName: "generateObject",
10833
10861
  kind: "async"
@@ -10836,10 +10864,6 @@ var aiSDKChannels = defineChannels("ai", {
10836
10864
  channelName: "streamObject",
10837
10865
  kind: "async"
10838
10866
  }),
10839
- streamObjectSync: channel({
10840
- channelName: "streamObject.sync",
10841
- kind: "sync-stream"
10842
- }),
10843
10867
  agentGenerate: channel({
10844
10868
  channelName: "Agent.generate",
10845
10869
  kind: "async"
@@ -10875,6 +10899,9 @@ var DEFAULT_DENY_OUTPUT_PATHS = [
10875
10899
  ];
10876
10900
  var AUTO_PATCHED_MODEL = Symbol.for("braintrust.ai-sdk.auto-patched-model");
10877
10901
  var AUTO_PATCHED_TOOL = Symbol.for("braintrust.ai-sdk.auto-patched-tool");
10902
+ var RUNTIME_DENY_OUTPUT_PATHS = Symbol.for(
10903
+ "braintrust.ai-sdk.deny-output-paths"
10904
+ );
10878
10905
  var AISDKPlugin = class extends BasePlugin {
10879
10906
  config;
10880
10907
  constructor(config = {}) {
@@ -10896,7 +10923,10 @@ var AISDKPlugin = class extends BasePlugin {
10896
10923
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10897
10924
  extractOutput: (result, endEvent) => {
10898
10925
  finalizeAISDKChildTracing(endEvent);
10899
- return processAISDKOutput(result, denyOutputPaths);
10926
+ return processAISDKOutput(
10927
+ result,
10928
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
10929
+ );
10900
10930
  },
10901
10931
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
10902
10932
  aggregateChunks: aggregateAISDKChunks
@@ -10907,25 +10937,14 @@ var AISDKPlugin = class extends BasePlugin {
10907
10937
  name: "streamText",
10908
10938
  type: "llm" /* LLM */,
10909
10939
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10910
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
10940
+ extractOutput: (result, endEvent) => processAISDKOutput(
10941
+ result,
10942
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
10943
+ ),
10911
10944
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
10912
10945
  aggregateChunks: aggregateAISDKChunks,
10913
10946
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
10914
- denyOutputPaths,
10915
- endEvent,
10916
- result,
10917
- span,
10918
- startTime
10919
- })
10920
- })
10921
- );
10922
- this.unsubscribers.push(
10923
- traceSyncStreamChannel(aiSDKChannels.streamTextSync, {
10924
- name: "streamText",
10925
- type: "llm" /* LLM */,
10926
- extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10927
- patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
10928
- denyOutputPaths,
10947
+ defaultDenyOutputPaths: denyOutputPaths,
10929
10948
  endEvent,
10930
10949
  result,
10931
10950
  span,
@@ -10940,7 +10959,10 @@ var AISDKPlugin = class extends BasePlugin {
10940
10959
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10941
10960
  extractOutput: (result, endEvent) => {
10942
10961
  finalizeAISDKChildTracing(endEvent);
10943
- return processAISDKOutput(result, denyOutputPaths);
10962
+ return processAISDKOutput(
10963
+ result,
10964
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
10965
+ );
10944
10966
  },
10945
10967
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
10946
10968
  aggregateChunks: aggregateAISDKChunks
@@ -10951,25 +10973,14 @@ var AISDKPlugin = class extends BasePlugin {
10951
10973
  name: "streamObject",
10952
10974
  type: "llm" /* LLM */,
10953
10975
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10954
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
10976
+ extractOutput: (result, endEvent) => processAISDKOutput(
10977
+ result,
10978
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
10979
+ ),
10955
10980
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
10956
10981
  aggregateChunks: aggregateAISDKChunks,
10957
10982
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
10958
- denyOutputPaths,
10959
- endEvent,
10960
- result,
10961
- span,
10962
- startTime
10963
- })
10964
- })
10965
- );
10966
- this.unsubscribers.push(
10967
- traceSyncStreamChannel(aiSDKChannels.streamObjectSync, {
10968
- name: "streamObject",
10969
- type: "llm" /* LLM */,
10970
- extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10971
- patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
10972
- denyOutputPaths,
10983
+ defaultDenyOutputPaths: denyOutputPaths,
10973
10984
  endEvent,
10974
10985
  result,
10975
10986
  span,
@@ -10984,7 +10995,10 @@ var AISDKPlugin = class extends BasePlugin {
10984
10995
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10985
10996
  extractOutput: (result, endEvent) => {
10986
10997
  finalizeAISDKChildTracing(endEvent);
10987
- return processAISDKOutput(result, denyOutputPaths);
10998
+ return processAISDKOutput(
10999
+ result,
11000
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
11001
+ );
10988
11002
  },
10989
11003
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
10990
11004
  aggregateChunks: aggregateAISDKChunks
@@ -10995,11 +11009,14 @@ var AISDKPlugin = class extends BasePlugin {
10995
11009
  name: "Agent.stream",
10996
11010
  type: "llm" /* LLM */,
10997
11011
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10998
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
11012
+ extractOutput: (result, endEvent) => processAISDKOutput(
11013
+ result,
11014
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
11015
+ ),
10999
11016
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
11000
11017
  aggregateChunks: aggregateAISDKChunks,
11001
11018
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
11002
- denyOutputPaths,
11019
+ defaultDenyOutputPaths: denyOutputPaths,
11003
11020
  endEvent,
11004
11021
  result,
11005
11022
  span,
@@ -11014,7 +11031,10 @@ var AISDKPlugin = class extends BasePlugin {
11014
11031
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
11015
11032
  extractOutput: (result, endEvent) => {
11016
11033
  finalizeAISDKChildTracing(endEvent);
11017
- return processAISDKOutput(result, denyOutputPaths);
11034
+ return processAISDKOutput(
11035
+ result,
11036
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
11037
+ );
11018
11038
  },
11019
11039
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
11020
11040
  aggregateChunks: aggregateAISDKChunks
@@ -11025,11 +11045,14 @@ var AISDKPlugin = class extends BasePlugin {
11025
11045
  name: "ToolLoopAgent.stream",
11026
11046
  type: "llm" /* LLM */,
11027
11047
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
11028
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
11048
+ extractOutput: (result, endEvent) => processAISDKOutput(
11049
+ result,
11050
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
11051
+ ),
11029
11052
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
11030
11053
  aggregateChunks: aggregateAISDKChunks,
11031
11054
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
11032
- denyOutputPaths,
11055
+ defaultDenyOutputPaths: denyOutputPaths,
11033
11056
  endEvent,
11034
11057
  result,
11035
11058
  span,
@@ -11039,118 +11062,416 @@ var AISDKPlugin = class extends BasePlugin {
11039
11062
  );
11040
11063
  }
11041
11064
  };
11042
- function processAISDKInput(params) {
11043
- if (!params) return params;
11044
- const input = processInputAttachments(params);
11045
- if (!input || typeof input !== "object" || Array.isArray(input)) {
11046
- return input;
11065
+ function resolveDenyOutputPaths(event, defaultDenyOutputPaths) {
11066
+ if (Array.isArray(event?.denyOutputPaths)) {
11067
+ return event.denyOutputPaths;
11047
11068
  }
11048
- const { tools: _tools, ...rest } = input;
11049
- return rest;
11050
- }
11051
- function prepareAISDKInput(params, event, span, denyOutputPaths) {
11052
- const input = processAISDKInput(params);
11053
- const metadata = extractMetadataFromParams(params, event.self);
11054
- const childTracing = prepareAISDKChildTracing(
11055
- params,
11056
- event.self,
11057
- span,
11058
- denyOutputPaths
11059
- );
11060
- event.__braintrust_ai_sdk_model_wrapped = childTracing.modelWrapped;
11061
- if (childTracing.cleanup) {
11062
- event.__braintrust_ai_sdk_cleanup = childTracing.cleanup;
11069
+ const firstArgument = event?.arguments && event.arguments.length > 0 ? event.arguments[0] : void 0;
11070
+ if (!firstArgument || typeof firstArgument !== "object") {
11071
+ return defaultDenyOutputPaths;
11063
11072
  }
11064
- return {
11065
- input,
11066
- metadata
11067
- };
11068
- }
11069
- function extractTopLevelAISDKMetrics(result, event, startTime) {
11070
- const metrics = hasModelChildTracing(event) ? {} : extractTokenMetrics(result);
11071
- if (startTime) {
11072
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
11073
+ const runtimeDenyOutputPaths = firstArgument[RUNTIME_DENY_OUTPUT_PATHS];
11074
+ if (Array.isArray(runtimeDenyOutputPaths) && runtimeDenyOutputPaths.every((path2) => typeof path2 === "string")) {
11075
+ return runtimeDenyOutputPaths;
11073
11076
  }
11074
- return metrics;
11077
+ return defaultDenyOutputPaths;
11075
11078
  }
11076
- function hasModelChildTracing(event) {
11077
- return event?.__braintrust_ai_sdk_model_wrapped === true;
11078
- }
11079
- function extractMetadataFromParams(params, self) {
11080
- const metadata = {
11081
- braintrust: {
11082
- integration_name: "ai-sdk",
11083
- sdk_language: "typescript"
11084
- }
11085
- };
11086
- 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;
11087
- const { model, provider } = serializeModelWithProvider(
11088
- params.model ?? agentModel
11089
- );
11090
- if (model) {
11091
- metadata.model = model;
11079
+ var isZodSchema2 = (value) => {
11080
+ return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
11081
+ };
11082
+ var serializeZodSchema2 = (schema) => {
11083
+ try {
11084
+ return zodToJsonSchema(schema);
11085
+ } catch {
11086
+ return {
11087
+ type: "object",
11088
+ description: "Zod schema (conversion failed)"
11089
+ };
11092
11090
  }
11093
- if (provider) {
11094
- metadata.provider = provider;
11091
+ };
11092
+ var isOutputObject = (value) => {
11093
+ if (value == null || typeof value !== "object") {
11094
+ return false;
11095
11095
  }
11096
- const tools = serializeAISDKToolsForLogging(params.tools);
11097
- if (tools) {
11098
- metadata.tools = tools;
11096
+ const output = value;
11097
+ if (!("responseFormat" in output)) {
11098
+ return false;
11099
11099
  }
11100
- return metadata;
11101
- }
11102
- function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
11103
- const cleanup = [];
11104
- const patchedModels = /* @__PURE__ */ new WeakSet();
11105
- const patchedTools = /* @__PURE__ */ new WeakSet();
11106
- let modelWrapped = false;
11107
- const patchModel = (model) => {
11108
- const resolvedModel = resolveAISDKModel(model);
11109
- if (!resolvedModel || typeof resolvedModel !== "object" || typeof resolvedModel.doGenerate !== "function" || patchedModels.has(resolvedModel) || resolvedModel[AUTO_PATCHED_MODEL]) {
11110
- return;
11111
- }
11112
- patchedModels.add(resolvedModel);
11113
- resolvedModel[AUTO_PATCHED_MODEL] = true;
11114
- modelWrapped = true;
11115
- const originalDoGenerate = resolvedModel.doGenerate;
11116
- const originalDoStream = resolvedModel.doStream;
11117
- const baseMetadata = buildAISDKChildMetadata(resolvedModel);
11118
- resolvedModel.doGenerate = async function doGeneratePatched(options) {
11119
- return parentSpan.traced(
11120
- async (span) => {
11121
- const result = await Reflect.apply(
11122
- originalDoGenerate,
11123
- resolvedModel,
11124
- [options]
11125
- );
11126
- span.log({
11127
- output: processAISDKOutput(result, denyOutputPaths),
11128
- metrics: extractTokenMetrics(result),
11129
- ...buildResolvedMetadataPayload(result)
11130
- });
11131
- return result;
11132
- },
11133
- {
11134
- name: "doGenerate",
11135
- spanAttributes: {
11136
- type: "llm" /* LLM */
11137
- },
11138
- event: {
11139
- input: processAISDKInput(options),
11140
- metadata: baseMetadata
11100
+ if (output.type === "object" || output.type === "text") {
11101
+ return true;
11102
+ }
11103
+ if (typeof output.responseFormat === "function" || typeof output.responseFormat === "object") {
11104
+ return true;
11105
+ }
11106
+ return false;
11107
+ };
11108
+ var serializeOutputObject = (output, model) => {
11109
+ try {
11110
+ const result = {
11111
+ response_format: null
11112
+ };
11113
+ if (output.type) {
11114
+ result.type = output.type;
11115
+ }
11116
+ let responseFormat;
11117
+ if (typeof output.responseFormat === "function") {
11118
+ const mockModelForSchema = {
11119
+ supportsStructuredOutputs: true,
11120
+ ...model && typeof model === "object" ? model : {}
11121
+ };
11122
+ responseFormat = output.responseFormat({ model: mockModelForSchema });
11123
+ } else if (output.responseFormat != null && typeof output.responseFormat === "object") {
11124
+ responseFormat = output.responseFormat;
11125
+ }
11126
+ if (responseFormat) {
11127
+ if (typeof responseFormat.then === "function") {
11128
+ result.response_format = Promise.resolve(responseFormat).then(
11129
+ (resolved) => {
11130
+ if (resolved.schema && isZodSchema2(resolved.schema)) {
11131
+ return {
11132
+ ...resolved,
11133
+ schema: serializeZodSchema2(resolved.schema)
11134
+ };
11135
+ }
11136
+ return resolved;
11141
11137
  }
11138
+ );
11139
+ } else {
11140
+ const syncResponseFormat = responseFormat;
11141
+ if (syncResponseFormat.schema && isZodSchema2(syncResponseFormat.schema)) {
11142
+ responseFormat = {
11143
+ ...syncResponseFormat,
11144
+ schema: serializeZodSchema2(syncResponseFormat.schema)
11145
+ };
11142
11146
  }
11143
- );
11147
+ result.response_format = responseFormat;
11148
+ }
11149
+ }
11150
+ return result;
11151
+ } catch {
11152
+ return {
11153
+ response_format: null
11144
11154
  };
11145
- if (originalDoStream) {
11146
- resolvedModel.doStream = async function doStreamPatched(options) {
11147
- const span = parentSpan.startSpan({
11148
- name: "doStream",
11155
+ }
11156
+ };
11157
+ var processInputAttachmentsSync = (input) => {
11158
+ if (!input) return { input };
11159
+ const processed = { ...input };
11160
+ if (input.messages && Array.isArray(input.messages)) {
11161
+ processed.messages = input.messages.map(processMessage);
11162
+ }
11163
+ if (input.prompt && typeof input.prompt === "object") {
11164
+ if (Array.isArray(input.prompt)) {
11165
+ processed.prompt = input.prompt.map(processMessage);
11166
+ } else {
11167
+ processed.prompt = processPromptContent(input.prompt);
11168
+ }
11169
+ }
11170
+ if (input.schema && isZodSchema2(input.schema)) {
11171
+ processed.schema = serializeZodSchema2(input.schema);
11172
+ }
11173
+ if (input.callOptionsSchema && isZodSchema2(input.callOptionsSchema)) {
11174
+ processed.callOptionsSchema = serializeZodSchema2(input.callOptionsSchema);
11175
+ }
11176
+ if (input.tools) {
11177
+ processed.tools = serializeAISDKToolsForLogging(input.tools);
11178
+ }
11179
+ let outputPromise;
11180
+ if (input.output && isOutputObject(input.output)) {
11181
+ const serialized = serializeOutputObject(input.output, input.model);
11182
+ if (serialized.response_format && typeof serialized.response_format.then === "function") {
11183
+ processed.output = { ...serialized, response_format: {} };
11184
+ outputPromise = serialized.response_format.then(
11185
+ (resolvedFormat) => ({
11186
+ output: { ...serialized, response_format: resolvedFormat }
11187
+ })
11188
+ );
11189
+ } else {
11190
+ processed.output = serialized;
11191
+ }
11192
+ }
11193
+ if ("prepareCall" in processed && typeof processed.prepareCall === "function") {
11194
+ processed.prepareCall = "[Function]";
11195
+ }
11196
+ return { input: processed, outputPromise };
11197
+ };
11198
+ var processMessage = (message) => {
11199
+ if (!message || typeof message !== "object") return message;
11200
+ if (Array.isArray(message.content)) {
11201
+ return {
11202
+ ...message,
11203
+ content: message.content.map(processContentPart)
11204
+ };
11205
+ }
11206
+ if (typeof message.content === "object" && message.content !== null) {
11207
+ return {
11208
+ ...message,
11209
+ content: processContentPart(message.content)
11210
+ };
11211
+ }
11212
+ return message;
11213
+ };
11214
+ var processPromptContent = (prompt) => {
11215
+ if (Array.isArray(prompt)) {
11216
+ return prompt.map(processContentPart);
11217
+ }
11218
+ if (prompt.content) {
11219
+ if (Array.isArray(prompt.content)) {
11220
+ return {
11221
+ ...prompt,
11222
+ content: prompt.content.map(processContentPart)
11223
+ };
11224
+ } else if (typeof prompt.content === "object") {
11225
+ return {
11226
+ ...prompt,
11227
+ content: processContentPart(prompt.content)
11228
+ };
11229
+ }
11230
+ }
11231
+ return prompt;
11232
+ };
11233
+ var processContentPart = (part) => {
11234
+ if (!part || typeof part !== "object") return part;
11235
+ try {
11236
+ if (part.type === "image" && part.image) {
11237
+ const imageAttachment = convertImageToAttachment(
11238
+ part.image,
11239
+ part.mimeType || part.mediaType
11240
+ );
11241
+ if (imageAttachment) {
11242
+ return {
11243
+ ...part,
11244
+ image: imageAttachment
11245
+ };
11246
+ }
11247
+ }
11248
+ if (part.type === "file" && part.data && (part.mimeType || part.mediaType)) {
11249
+ const fileAttachment = convertDataToAttachment(
11250
+ part.data,
11251
+ part.mimeType || part.mediaType,
11252
+ part.name || part.filename
11253
+ );
11254
+ if (fileAttachment) {
11255
+ return {
11256
+ ...part,
11257
+ data: fileAttachment
11258
+ };
11259
+ }
11260
+ }
11261
+ if (part.type === "image_url" && part.image_url) {
11262
+ if (typeof part.image_url === "object" && part.image_url.url) {
11263
+ const imageAttachment = convertImageToAttachment(part.image_url.url);
11264
+ if (imageAttachment) {
11265
+ return {
11266
+ ...part,
11267
+ image_url: {
11268
+ ...part.image_url,
11269
+ url: imageAttachment
11270
+ }
11271
+ };
11272
+ }
11273
+ }
11274
+ }
11275
+ } catch (error) {
11276
+ console.warn("Error processing content part:", error);
11277
+ }
11278
+ return part;
11279
+ };
11280
+ var convertImageToAttachment = (image, explicitMimeType) => {
11281
+ try {
11282
+ if (typeof image === "string" && image.startsWith("data:")) {
11283
+ const [mimeTypeSection, base64Data] = image.split(",");
11284
+ const mimeType = mimeTypeSection.match(/data:(.*?);/)?.[1];
11285
+ if (mimeType && base64Data) {
11286
+ const blob = convertDataToBlob(base64Data, mimeType);
11287
+ if (blob) {
11288
+ return new Attachment({
11289
+ data: blob,
11290
+ filename: `image.${getExtensionFromMediaType(mimeType)}`,
11291
+ contentType: mimeType
11292
+ });
11293
+ }
11294
+ }
11295
+ }
11296
+ if (explicitMimeType) {
11297
+ if (image instanceof Uint8Array) {
11298
+ return new Attachment({
11299
+ data: new Blob([image], { type: explicitMimeType }),
11300
+ filename: `image.${getExtensionFromMediaType(explicitMimeType)}`,
11301
+ contentType: explicitMimeType
11302
+ });
11303
+ }
11304
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer(image)) {
11305
+ return new Attachment({
11306
+ data: new Blob([image], { type: explicitMimeType }),
11307
+ filename: `image.${getExtensionFromMediaType(explicitMimeType)}`,
11308
+ contentType: explicitMimeType
11309
+ });
11310
+ }
11311
+ }
11312
+ if (image instanceof Blob && image.type) {
11313
+ return new Attachment({
11314
+ data: image,
11315
+ filename: `image.${getExtensionFromMediaType(image.type)}`,
11316
+ contentType: image.type
11317
+ });
11318
+ }
11319
+ if (image instanceof Attachment) {
11320
+ return image;
11321
+ }
11322
+ } catch (error) {
11323
+ console.warn("Error converting image to attachment:", error);
11324
+ }
11325
+ return null;
11326
+ };
11327
+ var convertDataToAttachment = (data, mimeType, filename) => {
11328
+ if (!mimeType) return null;
11329
+ try {
11330
+ let blob = null;
11331
+ if (typeof data === "string" && data.startsWith("data:")) {
11332
+ const [, base64Data] = data.split(",");
11333
+ if (base64Data) {
11334
+ blob = convertDataToBlob(base64Data, mimeType);
11335
+ }
11336
+ } else if (typeof data === "string" && data.length > 0) {
11337
+ blob = convertDataToBlob(data, mimeType);
11338
+ } else if (data instanceof Uint8Array) {
11339
+ blob = new Blob([data], { type: mimeType });
11340
+ } else if (typeof Buffer !== "undefined" && Buffer.isBuffer(data)) {
11341
+ blob = new Blob([data], { type: mimeType });
11342
+ } else if (data instanceof Blob) {
11343
+ blob = data;
11344
+ }
11345
+ if (blob) {
11346
+ return new Attachment({
11347
+ data: blob,
11348
+ filename: filename || `file.${getExtensionFromMediaType(mimeType)}`,
11349
+ contentType: mimeType
11350
+ });
11351
+ }
11352
+ } catch (error) {
11353
+ console.warn("Error converting data to attachment:", error);
11354
+ }
11355
+ return null;
11356
+ };
11357
+ function processAISDKInput(params) {
11358
+ return processInputAttachmentsSync(params);
11359
+ }
11360
+ function prepareAISDKInput(params, event, span, defaultDenyOutputPaths) {
11361
+ const { input, outputPromise } = processAISDKInput(params);
11362
+ if (outputPromise && input && typeof input === "object") {
11363
+ outputPromise.then((resolvedData) => {
11364
+ span.log({
11365
+ input: {
11366
+ ...input,
11367
+ ...resolvedData
11368
+ }
11369
+ });
11370
+ }).catch(() => {
11371
+ });
11372
+ }
11373
+ const metadata = extractMetadataFromParams(params, event.self);
11374
+ const childTracing = prepareAISDKChildTracing(
11375
+ params,
11376
+ event.self,
11377
+ span,
11378
+ defaultDenyOutputPaths,
11379
+ event.aiSDK
11380
+ );
11381
+ event.modelWrapped = childTracing.modelWrapped;
11382
+ if (childTracing.cleanup) {
11383
+ event.__braintrust_ai_sdk_cleanup = childTracing.cleanup;
11384
+ }
11385
+ return {
11386
+ input,
11387
+ metadata
11388
+ };
11389
+ }
11390
+ function extractTopLevelAISDKMetrics(result, event, startTime) {
11391
+ const metrics = hasModelChildTracing(event) ? {} : extractTokenMetrics(result);
11392
+ if (startTime) {
11393
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
11394
+ }
11395
+ return metrics;
11396
+ }
11397
+ function hasModelChildTracing(event) {
11398
+ return event?.modelWrapped === true || event?.__braintrust_ai_sdk_model_wrapped === true;
11399
+ }
11400
+ function extractMetadataFromParams(params, self) {
11401
+ const metadata = {
11402
+ braintrust: {
11403
+ integration_name: "ai-sdk",
11404
+ sdk_language: "typescript"
11405
+ }
11406
+ };
11407
+ 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;
11408
+ const { model, provider } = serializeModelWithProvider(
11409
+ params.model ?? agentModel
11410
+ );
11411
+ if (model) {
11412
+ metadata.model = model;
11413
+ }
11414
+ if (provider) {
11415
+ metadata.provider = provider;
11416
+ }
11417
+ const tools = serializeAISDKToolsForLogging(params.tools);
11418
+ if (tools) {
11419
+ metadata.tools = tools;
11420
+ }
11421
+ return metadata;
11422
+ }
11423
+ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiSDK) {
11424
+ const cleanup = [];
11425
+ const patchedModels = /* @__PURE__ */ new WeakSet();
11426
+ const patchedTools = /* @__PURE__ */ new WeakSet();
11427
+ let modelWrapped = false;
11428
+ const patchModel = (model) => {
11429
+ const resolvedModel = resolveAISDKModel(model, aiSDK);
11430
+ if (!resolvedModel || typeof resolvedModel !== "object" || typeof resolvedModel.doGenerate !== "function" || patchedModels.has(resolvedModel) || resolvedModel[AUTO_PATCHED_MODEL]) {
11431
+ return resolvedModel;
11432
+ }
11433
+ patchedModels.add(resolvedModel);
11434
+ resolvedModel[AUTO_PATCHED_MODEL] = true;
11435
+ modelWrapped = true;
11436
+ const originalDoGenerate = resolvedModel.doGenerate;
11437
+ const originalDoStream = resolvedModel.doStream;
11438
+ const baseMetadata = buildAISDKChildMetadata(resolvedModel);
11439
+ resolvedModel.doGenerate = async function doGeneratePatched(options) {
11440
+ return parentSpan.traced(
11441
+ async (span) => {
11442
+ const result = await Reflect.apply(
11443
+ originalDoGenerate,
11444
+ resolvedModel,
11445
+ [options]
11446
+ );
11447
+ span.log({
11448
+ output: processAISDKOutput(result, denyOutputPaths),
11449
+ metrics: extractTokenMetrics(result),
11450
+ ...buildResolvedMetadataPayload(result)
11451
+ });
11452
+ return result;
11453
+ },
11454
+ {
11455
+ name: "doGenerate",
11149
11456
  spanAttributes: {
11150
11457
  type: "llm" /* LLM */
11151
11458
  },
11152
11459
  event: {
11153
- input: processAISDKInput(options),
11460
+ input: processAISDKInput(options).input,
11461
+ metadata: baseMetadata
11462
+ }
11463
+ }
11464
+ );
11465
+ };
11466
+ if (originalDoStream) {
11467
+ resolvedModel.doStream = async function doStreamPatched(options) {
11468
+ const span = parentSpan.startSpan({
11469
+ name: "doStream",
11470
+ spanAttributes: {
11471
+ type: "llm" /* LLM */
11472
+ },
11473
+ event: {
11474
+ input: processAISDKInput(options).input,
11154
11475
  metadata: baseMetadata
11155
11476
  }
11156
11477
  });
@@ -11158,6 +11479,8 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
11158
11479
  span,
11159
11480
  () => Reflect.apply(originalDoStream, resolvedModel, [options])
11160
11481
  );
11482
+ const streamStartTime = getCurrentUnixTimestamp();
11483
+ let firstChunkTime;
11161
11484
  const output = {};
11162
11485
  let text = "";
11163
11486
  let reasoning = "";
@@ -11165,6 +11488,9 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
11165
11488
  let object = void 0;
11166
11489
  const transformStream = new TransformStream({
11167
11490
  transform(chunk, controller) {
11491
+ if (firstChunkTime === void 0) {
11492
+ firstChunkTime = getCurrentUnixTimestamp();
11493
+ }
11168
11494
  switch (chunk.type) {
11169
11495
  case "text-delta":
11170
11496
  text += extractTextDelta(chunk);
@@ -11205,12 +11531,19 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
11205
11531
  if (object !== void 0) {
11206
11532
  output.object = object;
11207
11533
  }
11534
+ const metrics = extractTokenMetrics(output);
11535
+ if (firstChunkTime !== void 0) {
11536
+ metrics.time_to_first_token = Math.max(
11537
+ firstChunkTime - streamStartTime,
11538
+ 1e-6
11539
+ );
11540
+ }
11208
11541
  span.log({
11209
11542
  output: processAISDKOutput(
11210
11543
  output,
11211
11544
  denyOutputPaths
11212
11545
  ),
11213
- metrics: extractTokenMetrics(output),
11546
+ metrics,
11214
11547
  ...buildResolvedMetadataPayload(output)
11215
11548
  });
11216
11549
  span.end();
@@ -11232,6 +11565,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
11232
11565
  }
11233
11566
  delete resolvedModel[AUTO_PATCHED_MODEL];
11234
11567
  });
11568
+ return resolvedModel;
11235
11569
  };
11236
11570
  const patchTool = (tool, name) => {
11237
11571
  if (tool == null || typeof tool !== "object" || !("execute" in tool) || typeof tool.execute !== "function" || patchedTools.has(tool) || tool[AUTO_PATCHED_TOOL]) {
@@ -11304,17 +11638,26 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
11304
11638
  }
11305
11639
  };
11306
11640
  if (params && typeof params === "object") {
11307
- patchModel(params.model);
11641
+ const patchedParamModel = patchModel(params.model);
11642
+ if (typeof params.model === "string" && patchedParamModel && typeof patchedParamModel === "object") {
11643
+ params.model = patchedParamModel;
11644
+ }
11308
11645
  patchTools(params.tools);
11309
11646
  }
11310
11647
  if (self && typeof self === "object") {
11311
11648
  const selfRecord = self;
11312
11649
  if (selfRecord.model !== void 0) {
11313
- patchModel(selfRecord.model);
11650
+ const patchedSelfModel = patchModel(selfRecord.model);
11651
+ if (typeof selfRecord.model === "string" && patchedSelfModel && typeof patchedSelfModel === "object") {
11652
+ selfRecord.model = patchedSelfModel;
11653
+ }
11314
11654
  }
11315
11655
  if (selfRecord.settings && typeof selfRecord.settings === "object") {
11316
11656
  if (selfRecord.settings.model !== void 0) {
11317
- patchModel(selfRecord.settings.model);
11657
+ const patchedSettingsModel = patchModel(selfRecord.settings.model);
11658
+ if (typeof selfRecord.settings.model === "string" && patchedSettingsModel && typeof patchedSettingsModel === "object") {
11659
+ selfRecord.settings.model = patchedSettingsModel;
11660
+ }
11318
11661
  }
11319
11662
  if (selfRecord.settings.tools !== void 0) {
11320
11663
  patchTools(selfRecord.settings.tools);
@@ -11338,75 +11681,194 @@ function finalizeAISDKChildTracing(event) {
11338
11681
  }
11339
11682
  }
11340
11683
  function patchAISDKStreamingResult(args) {
11341
- const { denyOutputPaths, endEvent, result, span, startTime } = args;
11684
+ const { defaultDenyOutputPaths, endEvent, result, span, startTime } = args;
11342
11685
  if (!result || typeof result !== "object") {
11343
11686
  return false;
11344
11687
  }
11345
11688
  const resultRecord = result;
11346
- if (!isReadableStreamLike(resultRecord.baseStream)) {
11689
+ attachKnownResultPromiseHandlers(resultRecord);
11690
+ if (isReadableStreamLike(resultRecord.baseStream)) {
11691
+ let firstChunkTime2;
11692
+ const wrappedBaseStream = resultRecord.baseStream.pipeThrough(
11693
+ new TransformStream({
11694
+ transform(chunk, controller) {
11695
+ if (firstChunkTime2 === void 0) {
11696
+ firstChunkTime2 = getCurrentUnixTimestamp();
11697
+ }
11698
+ controller.enqueue(chunk);
11699
+ },
11700
+ async flush() {
11701
+ const metrics = extractTopLevelAISDKMetrics(result, endEvent);
11702
+ if (metrics.time_to_first_token === void 0 && firstChunkTime2 !== void 0) {
11703
+ metrics.time_to_first_token = firstChunkTime2 - startTime;
11704
+ }
11705
+ const output = await processAISDKStreamingOutput(
11706
+ result,
11707
+ resolveDenyOutputPaths(endEvent, defaultDenyOutputPaths)
11708
+ );
11709
+ const metadata = buildResolvedMetadataPayload(result).metadata;
11710
+ span.log({
11711
+ output,
11712
+ ...metadata ? { metadata } : {},
11713
+ metrics
11714
+ });
11715
+ finalizeAISDKChildTracing(endEvent);
11716
+ span.end();
11717
+ }
11718
+ })
11719
+ );
11720
+ Object.defineProperty(resultRecord, "baseStream", {
11721
+ configurable: true,
11722
+ enumerable: true,
11723
+ value: wrappedBaseStream,
11724
+ writable: true
11725
+ });
11726
+ return true;
11727
+ }
11728
+ const streamField = findAsyncIterableField(resultRecord, [
11729
+ "partialObjectStream",
11730
+ "textStream",
11731
+ "fullStream",
11732
+ "stream"
11733
+ ]);
11734
+ if (!streamField) {
11347
11735
  return false;
11348
11736
  }
11349
11737
  let firstChunkTime;
11350
- const wrappedBaseStream = resultRecord.baseStream.pipeThrough(
11351
- new TransformStream({
11352
- transform(chunk, controller) {
11353
- if (firstChunkTime === void 0) {
11354
- firstChunkTime = getCurrentUnixTimestamp();
11355
- }
11356
- controller.enqueue(chunk);
11357
- },
11358
- async flush() {
11359
- const metrics = extractTopLevelAISDKMetrics(result, endEvent);
11360
- if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
11361
- metrics.time_to_first_token = firstChunkTime - startTime;
11362
- }
11363
- const output = await processAISDKStreamingOutput(
11364
- result,
11365
- denyOutputPaths
11366
- );
11367
- const metadata = buildResolvedMetadataPayload(result).metadata;
11368
- span.log({
11369
- output,
11370
- ...metadata ? { metadata } : {},
11371
- metrics
11372
- });
11373
- finalizeAISDKChildTracing(endEvent);
11374
- span.end();
11738
+ const wrappedStream = createPatchedAsyncIterable(streamField.stream, {
11739
+ onChunk: () => {
11740
+ if (firstChunkTime === void 0) {
11741
+ firstChunkTime = getCurrentUnixTimestamp();
11375
11742
  }
11376
- })
11377
- );
11378
- Object.defineProperty(resultRecord, "baseStream", {
11743
+ },
11744
+ onComplete: async () => {
11745
+ const metrics = extractTopLevelAISDKMetrics(result, endEvent);
11746
+ if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
11747
+ metrics.time_to_first_token = firstChunkTime - startTime;
11748
+ }
11749
+ const output = await processAISDKStreamingOutput(
11750
+ result,
11751
+ resolveDenyOutputPaths(endEvent, defaultDenyOutputPaths)
11752
+ );
11753
+ const metadata = buildResolvedMetadataPayload(result).metadata;
11754
+ span.log({
11755
+ output,
11756
+ ...metadata ? { metadata } : {},
11757
+ metrics
11758
+ });
11759
+ finalizeAISDKChildTracing(endEvent);
11760
+ span.end();
11761
+ },
11762
+ onError: (error) => {
11763
+ span.log({
11764
+ error: error.message
11765
+ });
11766
+ finalizeAISDKChildTracing(endEvent);
11767
+ span.end();
11768
+ }
11769
+ });
11770
+ Object.defineProperty(resultRecord, streamField.field, {
11379
11771
  configurable: true,
11380
11772
  enumerable: true,
11381
- value: wrappedBaseStream,
11773
+ value: wrappedStream,
11382
11774
  writable: true
11383
11775
  });
11384
11776
  return true;
11385
11777
  }
11778
+ function attachKnownResultPromiseHandlers(result) {
11779
+ const promiseLikeFields = [
11780
+ "content",
11781
+ "text",
11782
+ "object",
11783
+ "finishReason",
11784
+ "usage",
11785
+ "totalUsage",
11786
+ "steps"
11787
+ ];
11788
+ for (const field of promiseLikeFields) {
11789
+ try {
11790
+ if (!(field in result)) {
11791
+ continue;
11792
+ }
11793
+ const value = result[field];
11794
+ if (isPromiseLike(value)) {
11795
+ void Promise.resolve(value).catch(() => {
11796
+ });
11797
+ }
11798
+ } catch {
11799
+ }
11800
+ }
11801
+ }
11386
11802
  function isReadableStreamLike(value) {
11387
11803
  return value != null && typeof value === "object" && typeof value.pipeThrough === "function";
11388
11804
  }
11389
- async function processAISDKStreamingOutput(result, denyOutputPaths) {
11390
- const output = processAISDKOutput(result, denyOutputPaths);
11391
- if (!output || typeof output !== "object") {
11392
- return output;
11393
- }
11394
- const outputRecord = output;
11395
- try {
11396
- if ("text" in result && typeof result.text === "string") {
11397
- outputRecord.text = result.text;
11805
+ function isAsyncIterableLike(value) {
11806
+ return value != null && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function";
11807
+ }
11808
+ function findAsyncIterableField(result, candidateFields) {
11809
+ for (const field of candidateFields) {
11810
+ try {
11811
+ const stream = result[field];
11812
+ if (isAsyncIterableLike(stream)) {
11813
+ return { field, stream };
11814
+ }
11815
+ } catch {
11398
11816
  }
11399
- } catch {
11400
11817
  }
11401
- try {
11402
- if ("object" in result) {
11403
- const resolvedObject = await Promise.resolve(result.object);
11818
+ return null;
11819
+ }
11820
+ function createPatchedAsyncIterable(stream, hooks) {
11821
+ return {
11822
+ async *[Symbol.asyncIterator]() {
11823
+ try {
11824
+ for await (const chunk of stream) {
11825
+ hooks.onChunk(chunk);
11826
+ yield chunk;
11827
+ }
11828
+ await hooks.onComplete();
11829
+ } catch (error) {
11830
+ hooks.onError(
11831
+ error instanceof Error ? error : new Error(String(error))
11832
+ );
11833
+ throw error;
11834
+ }
11835
+ }
11836
+ };
11837
+ }
11838
+ async function processAISDKStreamingOutput(result, denyOutputPaths) {
11839
+ const output = processAISDKOutput(result, denyOutputPaths);
11840
+ if (!output || typeof output !== "object") {
11841
+ return output;
11842
+ }
11843
+ const outputRecord = output;
11844
+ const isObjectStreamingResult = result != null && typeof result === "object" && "partialObjectStream" in result;
11845
+ try {
11846
+ if (!isObjectStreamingResult && "text" in result) {
11847
+ const resolvedText = await Promise.resolve(result.text);
11848
+ if (typeof resolvedText === "string") {
11849
+ outputRecord.text = resolvedText;
11850
+ }
11851
+ }
11852
+ } catch {
11853
+ }
11854
+ try {
11855
+ if ("object" in result) {
11856
+ const resolvedObject = await Promise.resolve(result.object);
11404
11857
  if (resolvedObject !== void 0) {
11405
11858
  outputRecord.object = resolvedObject;
11406
11859
  }
11407
11860
  }
11408
11861
  } catch {
11409
11862
  }
11863
+ try {
11864
+ if ("finishReason" in result) {
11865
+ const resolvedFinishReason = await Promise.resolve(result.finishReason);
11866
+ if (resolvedFinishReason !== void 0) {
11867
+ outputRecord.finishReason = resolvedFinishReason;
11868
+ }
11869
+ }
11870
+ } catch {
11871
+ }
11410
11872
  return outputRecord;
11411
11873
  }
11412
11874
  function buildAISDKChildMetadata(model) {
@@ -11429,16 +11891,25 @@ function buildResolvedMetadataPayload(result) {
11429
11891
  if (gatewayInfo?.model) {
11430
11892
  metadata.model = gatewayInfo.model;
11431
11893
  }
11432
- if (result.finishReason !== void 0) {
11433
- metadata.finish_reason = result.finishReason;
11894
+ let finishReason;
11895
+ try {
11896
+ finishReason = result.finishReason;
11897
+ } catch {
11898
+ finishReason = void 0;
11899
+ }
11900
+ if (isPromiseLike(finishReason)) {
11901
+ void Promise.resolve(finishReason).catch(() => {
11902
+ });
11903
+ } else if (finishReason !== void 0) {
11904
+ metadata.finish_reason = finishReason;
11434
11905
  }
11435
11906
  return Object.keys(metadata).length > 0 ? { metadata } : {};
11436
11907
  }
11437
- function resolveAISDKModel(model) {
11908
+ function resolveAISDKModel(model, aiSDK) {
11438
11909
  if (typeof model !== "string") {
11439
11910
  return model;
11440
11911
  }
11441
- const provider = globalThis.AI_SDK_DEFAULT_PROVIDER ?? null;
11912
+ const provider = globalThis.AI_SDK_DEFAULT_PROVIDER ?? aiSDK?.gateway ?? null;
11442
11913
  if (provider && typeof provider.languageModel === "function") {
11443
11914
  return provider.languageModel(model);
11444
11915
  }
@@ -11461,15 +11932,15 @@ function processAISDKOutput(output, denyOutputPaths) {
11461
11932
  }
11462
11933
  function extractTokenMetrics(result) {
11463
11934
  const metrics = {};
11464
- let usage = result?.totalUsage || result?.usage;
11465
- if (!usage && result) {
11466
- try {
11467
- if ("totalUsage" in result && typeof result.totalUsage !== "function") {
11468
- usage = result.totalUsage;
11469
- } else if ("usage" in result && typeof result.usage !== "function") {
11470
- usage = result.usage;
11471
- }
11472
- } catch {
11935
+ let usage;
11936
+ const totalUsageValue = safeResultFieldRead(result, "totalUsage");
11937
+ if (totalUsageValue !== void 0 && !isPromiseLike(totalUsageValue)) {
11938
+ usage = totalUsageValue;
11939
+ }
11940
+ if (!usage) {
11941
+ const usageValue = safeResultFieldRead(result, "usage");
11942
+ if (usageValue !== void 0 && !isPromiseLike(usageValue)) {
11943
+ usage = usageValue;
11473
11944
  }
11474
11945
  }
11475
11946
  if (!usage) {
@@ -11507,6 +11978,22 @@ function extractTokenMetrics(result) {
11507
11978
  }
11508
11979
  return metrics;
11509
11980
  }
11981
+ function safeResultFieldRead(result, field) {
11982
+ return safeSerializableFieldRead(result, field);
11983
+ }
11984
+ function safeSerializableFieldRead(obj, field) {
11985
+ try {
11986
+ const value = obj?.[field];
11987
+ if (isPromiseLike(value)) {
11988
+ void Promise.resolve(value).catch(() => {
11989
+ });
11990
+ return void 0;
11991
+ }
11992
+ return value;
11993
+ } catch {
11994
+ return void 0;
11995
+ }
11996
+ }
11510
11997
  function aggregateAISDKChunks(chunks, _result, endEvent) {
11511
11998
  const lastChunk = chunks[chunks.length - 1];
11512
11999
  const output = {};
@@ -11515,17 +12002,21 @@ function aggregateAISDKChunks(chunks, _result, endEvent) {
11515
12002
  if (lastChunk) {
11516
12003
  metrics = hasModelChildTracing(endEvent) ? {} : extractTokenMetrics(lastChunk);
11517
12004
  metadata = buildResolvedMetadataPayload(lastChunk).metadata;
11518
- if (lastChunk.text !== void 0) {
11519
- output.text = lastChunk.text;
12005
+ const text = safeSerializableFieldRead(lastChunk, "text");
12006
+ if (text !== void 0) {
12007
+ output.text = text;
11520
12008
  }
11521
- if (lastChunk.object !== void 0) {
11522
- output.object = lastChunk.object;
12009
+ const objectValue = safeSerializableFieldRead(lastChunk, "object");
12010
+ if (objectValue !== void 0) {
12011
+ output.object = objectValue;
11523
12012
  }
11524
- if (lastChunk.finishReason !== void 0) {
11525
- output.finishReason = lastChunk.finishReason;
12013
+ const finishReason = safeSerializableFieldRead(lastChunk, "finishReason");
12014
+ if (finishReason !== void 0) {
12015
+ output.finishReason = finishReason;
11526
12016
  }
11527
- if (lastChunk.toolCalls !== void 0) {
11528
- output.toolCalls = lastChunk.toolCalls;
12017
+ const toolCalls = safeSerializableFieldRead(lastChunk, "toolCalls");
12018
+ if (toolCalls !== void 0) {
12019
+ output.toolCalls = toolCalls;
11529
12020
  }
11530
12021
  }
11531
12022
  finalizeAISDKChildTracing(endEvent);
@@ -11534,6 +12025,7 @@ function aggregateAISDKChunks(chunks, _result, endEvent) {
11534
12025
  function extractGetterValues(obj) {
11535
12026
  const getterValues = {};
11536
12027
  const getterNames = [
12028
+ "content",
11537
12029
  "text",
11538
12030
  "object",
11539
12031
  "finishReason",
@@ -11549,8 +12041,17 @@ function extractGetterValues(obj) {
11549
12041
  ];
11550
12042
  for (const name of getterNames) {
11551
12043
  try {
11552
- if (obj && name in obj && isSerializableOutputValue(obj[name])) {
11553
- getterValues[name] = obj[name];
12044
+ if (!obj || !(name in obj)) {
12045
+ continue;
12046
+ }
12047
+ const value = obj[name];
12048
+ if (isPromiseLike(value)) {
12049
+ void Promise.resolve(value).catch(() => {
12050
+ });
12051
+ continue;
12052
+ }
12053
+ if (isSerializableOutputValue(value)) {
12054
+ getterValues[name] = value;
11554
12055
  }
11555
12056
  } catch {
11556
12057
  }
@@ -11572,6 +12073,11 @@ function extractSerializableOutputFields(output) {
11572
12073
  for (const name of directFieldNames) {
11573
12074
  try {
11574
12075
  const value = output?.[name];
12076
+ if (isPromiseLike(value)) {
12077
+ void Promise.resolve(value).catch(() => {
12078
+ });
12079
+ continue;
12080
+ }
11575
12081
  if (isSerializableOutputValue(value)) {
11576
12082
  serialized[name] = value;
11577
12083
  }
@@ -11583,6 +12089,9 @@ function extractSerializableOutputFields(output) {
11583
12089
  ...extractGetterValues(output)
11584
12090
  };
11585
12091
  }
12092
+ function isPromiseLike(value) {
12093
+ return value != null && typeof value === "object" && typeof value.then === "function";
12094
+ }
11586
12095
  function isSerializableOutputValue(value) {
11587
12096
  if (typeof value === "function") {
11588
12097
  return false;
@@ -11624,8 +12133,9 @@ function parseGatewayModelString(modelString) {
11624
12133
  return { model: modelString };
11625
12134
  }
11626
12135
  function extractGatewayRoutingInfo(result) {
11627
- if (result?.steps && Array.isArray(result.steps) && result.steps.length > 0) {
11628
- const routing2 = result.steps[0]?.providerMetadata?.gateway?.routing;
12136
+ const steps = safeSerializableFieldRead(result, "steps");
12137
+ if (Array.isArray(steps) && steps.length > 0) {
12138
+ const routing2 = steps[0]?.providerMetadata?.gateway?.routing;
11629
12139
  if (routing2) {
11630
12140
  return {
11631
12141
  provider: routing2.resolvedProvider || routing2.finalProvider,
@@ -11633,7 +12143,11 @@ function extractGatewayRoutingInfo(result) {
11633
12143
  };
11634
12144
  }
11635
12145
  }
11636
- const routing = result?.providerMetadata?.gateway?.routing;
12146
+ const providerMetadata = safeSerializableFieldRead(
12147
+ result,
12148
+ "providerMetadata"
12149
+ );
12150
+ const routing = providerMetadata?.gateway?.routing;
11637
12151
  if (routing) {
11638
12152
  return {
11639
12153
  provider: routing.resolvedProvider || routing.finalProvider,
@@ -11643,10 +12157,11 @@ function extractGatewayRoutingInfo(result) {
11643
12157
  return null;
11644
12158
  }
11645
12159
  function extractCostFromResult(result) {
11646
- if (result?.steps && Array.isArray(result.steps) && result.steps.length > 0) {
12160
+ const steps = safeSerializableFieldRead(result, "steps");
12161
+ if (Array.isArray(steps) && steps.length > 0) {
11647
12162
  let totalCost = 0;
11648
12163
  let foundCost = false;
11649
- for (const step of result.steps) {
12164
+ for (const step of steps) {
11650
12165
  const gateway2 = step?.providerMetadata?.gateway;
11651
12166
  const stepCost = parseGatewayCost(gateway2?.cost) || parseGatewayCost(gateway2?.marketCost);
11652
12167
  if (stepCost !== void 0 && stepCost > 0) {
@@ -11658,7 +12173,11 @@ function extractCostFromResult(result) {
11658
12173
  return totalCost;
11659
12174
  }
11660
12175
  }
11661
- const gateway = result?.providerMetadata?.gateway;
12176
+ const providerMetadata = safeSerializableFieldRead(
12177
+ result,
12178
+ "providerMetadata"
12179
+ );
12180
+ const gateway = providerMetadata?.gateway;
11662
12181
  const directCost = parseGatewayCost(gateway?.cost) || parseGatewayCost(gateway?.marketCost);
11663
12182
  if (directCost !== void 0 && directCost > 0) {
11664
12183
  return directCost;
@@ -12359,20 +12878,16 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
12359
12878
  );
12360
12879
  });
12361
12880
  },
12362
- onComplete: () => {
12363
- void state.processing.then(() => finalizeQuerySpan(state)).finally(() => {
12364
- spans.delete(event);
12365
- });
12366
- },
12367
- onError: (error) => {
12368
- void state.processing.then(() => {
12369
- state.span.log({
12370
- error: error.message
12371
- });
12372
- }).then(() => finalizeQuerySpan(state)).finally(() => {
12373
- spans.delete(event);
12881
+ onComplete: () => state.processing.then(() => finalizeQuerySpan(state)).finally(() => {
12882
+ spans.delete(event);
12883
+ }),
12884
+ onError: (error) => state.processing.then(() => {
12885
+ state.span.log({
12886
+ error: error.message
12374
12887
  });
12375
- }
12888
+ }).then(() => finalizeQuerySpan(state)).finally(() => {
12889
+ spans.delete(event);
12890
+ })
12376
12891
  });
12377
12892
  return;
12378
12893
  }
@@ -12520,12 +13035,14 @@ var GoogleGenAIPlugin = class extends BasePlugin {
12520
13035
  const params = event.arguments[0];
12521
13036
  streamEvent.googleGenAIInput = serializeInput(params);
12522
13037
  streamEvent.googleGenAIMetadata = extractMetadata(params);
13038
+ streamEvent.googleGenAIStartTime = getCurrentUnixTimestamp();
12523
13039
  },
12524
13040
  asyncEnd: (event) => {
12525
13041
  const streamEvent = event;
12526
13042
  patchGoogleGenAIStreamingResult({
12527
13043
  input: streamEvent.googleGenAIInput,
12528
13044
  metadata: streamEvent.googleGenAIMetadata,
13045
+ startTime: streamEvent.googleGenAIStartTime,
12529
13046
  result: streamEvent.result
12530
13047
  });
12531
13048
  },
@@ -12578,7 +13095,7 @@ function logErrorAndEndSpan(states, event) {
12578
13095
  states.delete(event);
12579
13096
  }
12580
13097
  function patchGoogleGenAIStreamingResult(args) {
12581
- const { input, metadata, result } = args;
13098
+ const { input, metadata, result, startTime } = args;
12582
13099
  if (!input || !metadata || !result || typeof result !== "object" || typeof result.next !== "function") {
12583
13100
  return false;
12584
13101
  }
@@ -12586,7 +13103,7 @@ function patchGoogleGenAIStreamingResult(args) {
12586
13103
  let firstTokenTime = null;
12587
13104
  let finalized = false;
12588
13105
  let span = null;
12589
- let startTime = null;
13106
+ const requestStartTime = startTime ?? getCurrentUnixTimestamp();
12590
13107
  const ensureSpan = () => {
12591
13108
  if (!span) {
12592
13109
  span = startSpan({
@@ -12599,7 +13116,6 @@ function patchGoogleGenAIStreamingResult(args) {
12599
13116
  metadata
12600
13117
  }
12601
13118
  });
12602
- startTime = getCurrentUnixTimestamp();
12603
13119
  }
12604
13120
  return span;
12605
13121
  };
@@ -12653,11 +13169,11 @@ function patchGoogleGenAIStreamingResult(args) {
12653
13169
  }
12654
13170
  chunks.push(nextResult.value);
12655
13171
  }
12656
- if (nextResult.done && startTime !== null) {
13172
+ if (nextResult.done) {
12657
13173
  finalize({
12658
13174
  result: aggregateGenerateContentChunks(
12659
13175
  chunks,
12660
- startTime,
13176
+ requestStartTime,
12661
13177
  firstTokenTime
12662
13178
  )
12663
13179
  });
@@ -12677,13 +13193,13 @@ function patchGoogleGenAIStreamingResult(args) {
12677
13193
  ...returnArgs
12678
13194
  );
12679
13195
  } finally {
12680
- if (startTime !== null) {
13196
+ if (chunks.length > 0) {
12681
13197
  finalize({
12682
- result: chunks.length > 0 ? aggregateGenerateContentChunks(
13198
+ result: aggregateGenerateContentChunks(
12683
13199
  chunks,
12684
- startTime,
13200
+ requestStartTime,
12685
13201
  firstTokenTime
12686
- ) : void 0
13202
+ )
12687
13203
  });
12688
13204
  } else {
12689
13205
  finalize({});
@@ -12975,895 +13491,535 @@ var openRouterChannels = defineChannels("@openrouter/sdk", {
12975
13491
  channelName: "callModel",
12976
13492
  kind: "sync-stream"
12977
13493
  }),
13494
+ callModelTurn: channel({
13495
+ channelName: "callModel.turn",
13496
+ kind: "async"
13497
+ }),
12978
13498
  toolExecute: channel({
12979
13499
  channelName: "tool.execute",
12980
13500
  kind: "async"
12981
13501
  })
12982
13502
  });
12983
13503
 
12984
- // src/openrouter-utils.ts
12985
- var TOKEN_NAME_MAP2 = {
12986
- promptTokens: "prompt_tokens",
12987
- inputTokens: "prompt_tokens",
12988
- completionTokens: "completion_tokens",
12989
- outputTokens: "completion_tokens",
12990
- totalTokens: "tokens",
12991
- prompt_tokens: "prompt_tokens",
12992
- input_tokens: "prompt_tokens",
12993
- completion_tokens: "completion_tokens",
12994
- output_tokens: "completion_tokens",
12995
- total_tokens: "tokens"
12996
- };
12997
- var TOKEN_DETAIL_PREFIX_MAP = {
12998
- promptTokensDetails: "prompt",
12999
- inputTokensDetails: "prompt",
13000
- completionTokensDetails: "completion",
13001
- outputTokensDetails: "completion",
13002
- costDetails: "cost",
13003
- prompt_tokens_details: "prompt",
13004
- input_tokens_details: "prompt",
13005
- completion_tokens_details: "completion",
13006
- output_tokens_details: "completion",
13007
- cost_details: "cost"
13008
- };
13009
- function camelToSnake(value) {
13010
- return value.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`);
13011
- }
13012
- function parseOpenRouterMetricsFromUsage(usage) {
13013
- if (!isObject(usage)) {
13014
- return {};
13015
- }
13016
- const metrics = {};
13017
- for (const [name, value] of Object.entries(usage)) {
13018
- if (typeof value === "number") {
13019
- metrics[TOKEN_NAME_MAP2[name] || camelToSnake(name)] = value;
13020
- continue;
13021
- }
13022
- if (!isObject(value)) {
13023
- continue;
13024
- }
13025
- const prefix = TOKEN_DETAIL_PREFIX_MAP[name];
13026
- if (!prefix) {
13027
- continue;
13028
- }
13029
- for (const [nestedName, nestedValue] of Object.entries(value)) {
13030
- if (typeof nestedValue !== "number") {
13031
- continue;
13032
- }
13033
- metrics[`${prefix}_${camelToSnake(nestedName)}`] = nestedValue;
13034
- }
13035
- }
13036
- return metrics;
13037
- }
13038
- function extractOpenRouterUsageMetadata(usage) {
13039
- if (!isObject(usage)) {
13040
- return void 0;
13041
- }
13042
- const metadata = {};
13043
- if (typeof usage.isByok === "boolean") {
13044
- metadata.is_byok = usage.isByok;
13045
- } else if (typeof usage.is_byok === "boolean") {
13046
- metadata.is_byok = usage.is_byok;
13047
- }
13048
- return Object.keys(metadata).length > 0 ? metadata : void 0;
13049
- }
13050
-
13051
- // src/openrouter-logging.ts
13052
- var OMITTED_OPENROUTER_KEYS = /* @__PURE__ */ new Set([
13053
- "execute",
13054
- "render",
13055
- "nextTurnParams",
13056
- "requireApproval"
13057
- ]);
13058
- function parseOpenRouterModelString(model) {
13059
- if (typeof model !== "string") {
13060
- return { model };
13061
- }
13062
- const slashIndex = model.indexOf("/");
13063
- if (slashIndex > 0 && slashIndex < model.length - 1) {
13064
- return {
13065
- provider: model.substring(0, slashIndex),
13066
- model: model.substring(slashIndex + 1)
13067
- };
13068
- }
13069
- return { model };
13070
- }
13071
- function isZodSchema2(value) {
13072
- return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
13073
- }
13074
- function serializeZodSchema2(schema) {
13075
- try {
13076
- return zodToJsonSchema(schema);
13077
- } catch {
13078
- return {
13079
- type: "object",
13080
- description: "Zod schema (conversion failed)"
13081
- };
13082
- }
13083
- }
13084
- function serializeOpenRouterTool(tool) {
13085
- if (!isObject(tool)) {
13086
- return tool;
13087
- }
13088
- const serialized = {};
13089
- for (const [key, value] of Object.entries(tool)) {
13090
- if (OMITTED_OPENROUTER_KEYS.has(key)) {
13091
- continue;
13092
- }
13093
- if (key === "function" && isObject(value)) {
13094
- serialized.function = sanitizeOpenRouterLoggedValue(value);
13095
- continue;
13096
- }
13097
- serialized[key] = sanitizeOpenRouterLoggedValue(value);
13098
- }
13099
- return serialized;
13100
- }
13101
- function serializeOpenRouterToolsForLogging(tools) {
13102
- if (!Array.isArray(tools)) {
13103
- return void 0;
13104
- }
13105
- return tools.map((tool) => serializeOpenRouterTool(tool));
13106
- }
13107
- function sanitizeOpenRouterLoggedValue(value) {
13108
- if (isZodSchema2(value)) {
13109
- return serializeZodSchema2(value);
13110
- }
13111
- if (typeof value === "function") {
13112
- return "[Function]";
13113
- }
13114
- if (Array.isArray(value)) {
13115
- return value.map((entry) => sanitizeOpenRouterLoggedValue(entry));
13116
- }
13117
- if (!isObject(value)) {
13118
- return value;
13119
- }
13120
- const sanitized = {};
13121
- for (const [key, entry] of Object.entries(value)) {
13122
- if (OMITTED_OPENROUTER_KEYS.has(key)) {
13123
- continue;
13124
- }
13125
- if (key === "tools" && Array.isArray(entry)) {
13126
- sanitized.tools = serializeOpenRouterToolsForLogging(entry);
13127
- continue;
13128
- }
13129
- sanitized[key] = sanitizeOpenRouterLoggedValue(entry);
13504
+ // src/instrumentation/plugins/openrouter-plugin.ts
13505
+ var OpenRouterPlugin = class extends BasePlugin {
13506
+ onEnable() {
13507
+ this.subscribeToOpenRouterChannels();
13130
13508
  }
13131
- return sanitized;
13132
- }
13133
- function buildOpenRouterMetadata(metadata, httpReferer, xTitle) {
13134
- const sanitized = sanitizeOpenRouterLoggedValue(metadata);
13135
- const metadataRecord = isObject(sanitized) ? sanitized : {};
13136
- const { model, provider: providerRouting, ...rest } = metadataRecord;
13137
- const normalizedModel = parseOpenRouterModelString(model);
13138
- return {
13139
- ...rest,
13140
- ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
13141
- ...providerRouting !== void 0 ? { providerRouting } : {},
13142
- ...httpReferer !== void 0 ? { httpReferer } : {},
13143
- ...xTitle !== void 0 ? { xTitle } : {},
13144
- provider: normalizedModel.provider || "openrouter"
13145
- };
13146
- }
13147
- function buildOpenRouterEmbeddingMetadata(metadata, httpReferer, xTitle) {
13148
- const normalized = buildOpenRouterMetadata(metadata, httpReferer, xTitle);
13149
- return typeof normalized.model === "string" ? {
13150
- ...normalized,
13151
- embedding_model: normalized.model
13152
- } : normalized;
13153
- }
13154
- function extractOpenRouterCallModelInput(request) {
13155
- return isObject(request) && "input" in request ? sanitizeOpenRouterLoggedValue(request.input) : void 0;
13156
- }
13157
- function extractOpenRouterCallModelMetadata(request) {
13158
- if (!isObject(request)) {
13159
- return { provider: "openrouter" };
13509
+ onDisable() {
13510
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
13160
13511
  }
13161
- const { input: _input, ...metadata } = request;
13162
- return buildOpenRouterMetadata(metadata, void 0, void 0);
13163
- }
13164
- function extractOpenRouterResponseMetadata(result) {
13165
- if (!isObject(result)) {
13166
- return void 0;
13167
- }
13168
- const { output: _output, data: _data, usage, ...metadata } = result;
13169
- const sanitized = sanitizeOpenRouterLoggedValue(metadata);
13170
- const metadataRecord = isObject(sanitized) ? sanitized : {};
13171
- const { model, provider, ...rest } = metadataRecord;
13172
- const normalizedModel = parseOpenRouterModelString(model);
13173
- const normalizedProvider = (typeof provider === "string" ? provider : void 0) || normalizedModel.provider;
13174
- const usageMetadata = extractOpenRouterUsageMetadata(usage);
13175
- const combined = {
13176
- ...rest,
13177
- ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
13178
- ...usageMetadata || {},
13179
- ...normalizedProvider !== void 0 ? { provider: normalizedProvider } : {}
13180
- };
13181
- return Object.keys(combined).length > 0 ? combined : void 0;
13182
- }
13183
- function extractOpenRouterResponseOutput(response, fallbackOutput) {
13184
- if (isObject(response) && "output" in response && response.output !== void 0) {
13185
- return sanitizeOpenRouterLoggedValue(response.output);
13186
- }
13187
- if (fallbackOutput !== void 0) {
13188
- return sanitizeOpenRouterLoggedValue(fallbackOutput);
13189
- }
13190
- return void 0;
13191
- }
13192
-
13193
- // src/openrouter-tool-wrapping.ts
13194
- var OPENROUTER_WRAPPED_TOOL = Symbol("braintrust.openrouter.wrappedTool");
13195
- var OPENROUTER_WRAPPED_CALL_MODEL_RESULT = Symbol(
13196
- "braintrust.openrouter.wrappedCallModelResult"
13197
- );
13198
- var OPENROUTER_CALL_MODEL_STREAM_METHODS = [
13199
- "getFullResponsesStream",
13200
- "getItemsStream",
13201
- "getNewMessagesStream",
13202
- "getReasoningStream",
13203
- "getTextStream",
13204
- "getToolCallsStream",
13205
- "getToolStream"
13206
- ];
13207
- var OPENROUTER_CALL_MODEL_CONTEXT_METHODS = [
13208
- "cancel",
13209
- "getPendingToolCalls",
13210
- "getState",
13211
- "getToolCalls",
13212
- "requiresApproval"
13213
- ];
13214
- function patchOpenRouterCallModelRequestTools(request) {
13215
- if (!Array.isArray(request.tools) || request.tools.length === 0) {
13216
- return void 0;
13217
- }
13218
- const originalTools = request.tools;
13219
- const wrappedTools = originalTools.map((tool) => wrapOpenRouterTool(tool));
13220
- const didPatch = wrappedTools.some(
13221
- (tool, index) => tool !== originalTools[index]
13222
- );
13223
- if (!didPatch) {
13224
- return void 0;
13225
- }
13226
- request.tools = wrappedTools;
13227
- return () => {
13228
- request.tools = originalTools;
13229
- };
13230
- }
13231
- function patchOpenRouterCallModelResult(span, result, request) {
13232
- if (!isObject(result) || isWrappedCallModelResult(result)) {
13233
- return false;
13234
- }
13235
- const resultLike = result;
13236
- const hasInstrumentableMethod = typeof resultLike.getResponse === "function" || typeof resultLike.getText === "function" || OPENROUTER_CALL_MODEL_STREAM_METHODS.some(
13237
- (methodName) => typeof resultLike[methodName] === "function"
13238
- );
13239
- if (!hasInstrumentableMethod) {
13240
- return false;
13241
- }
13242
- Object.defineProperty(resultLike, OPENROUTER_WRAPPED_CALL_MODEL_RESULT, {
13243
- value: true,
13244
- enumerable: false,
13245
- configurable: false
13246
- });
13247
- const originalGetResponse = typeof resultLike.getResponse === "function" ? resultLike.getResponse.bind(resultLike) : void 0;
13248
- const originalGetInitialResponse = typeof resultLike.getInitialResponse === "function" ? resultLike.getInitialResponse.bind(resultLike) : void 0;
13249
- const originalMakeFollowupRequest = typeof resultLike.makeFollowupRequest === "function" ? resultLike.makeFollowupRequest.bind(resultLike) : void 0;
13250
- let ended = false;
13251
- let tracedTurnCount = 0;
13252
- const endSpanWithResult = async (response, fallbackOutput) => {
13253
- if (ended) {
13254
- return;
13255
- }
13256
- ended = true;
13257
- const finalResponse = getFinalOpenRouterCallModelResponse(
13258
- resultLike,
13259
- response
13512
+ subscribeToOpenRouterChannels() {
13513
+ this.unsubscribers.push(
13514
+ traceStreamingChannel(openRouterChannels.chatSend, {
13515
+ name: "openrouter.chat.send",
13516
+ type: "llm" /* LLM */,
13517
+ extractInput: (args) => {
13518
+ const request = getOpenRouterRequestArg(args);
13519
+ const chatGenerationParams = isObject(request?.chatGenerationParams) ? request.chatGenerationParams : {};
13520
+ const httpReferer = request?.httpReferer;
13521
+ const xTitle = request?.xTitle;
13522
+ const { messages, ...metadata } = chatGenerationParams;
13523
+ return {
13524
+ input: messages,
13525
+ metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
13526
+ };
13527
+ },
13528
+ extractOutput: (result) => {
13529
+ return isObject(result) ? result.choices : void 0;
13530
+ },
13531
+ extractMetrics: (result, startTime) => {
13532
+ const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
13533
+ if (startTime) {
13534
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
13535
+ }
13536
+ return metrics;
13537
+ },
13538
+ aggregateChunks: aggregateOpenRouterChatChunks
13539
+ })
13260
13540
  );
13261
- if (finalResponse) {
13262
- const rounds = getOpenRouterCallModelRounds(resultLike);
13263
- const metadata = extractOpenRouterCallModelResultMetadata(
13264
- finalResponse,
13265
- rounds.length + 1
13266
- );
13267
- span.log({
13268
- output: extractOpenRouterResponseOutput(finalResponse, fallbackOutput),
13269
- ...metadata ? { metadata } : {},
13270
- metrics: aggregateOpenRouterCallModelMetrics(rounds, finalResponse)
13271
- });
13272
- span.end();
13273
- return;
13274
- }
13275
- if (fallbackOutput !== void 0) {
13276
- span.log({
13277
- output: fallbackOutput
13278
- });
13279
- }
13280
- span.end();
13281
- };
13282
- const endSpanWithError = (error) => {
13283
- if (ended) {
13284
- return;
13285
- }
13286
- ended = true;
13287
- span.log({
13288
- error: normalizeError(error).message
13289
- });
13290
- span.end();
13291
- };
13292
- const finalizeFromResponse = async (fallbackOutput) => {
13293
- if (!originalGetResponse) {
13294
- await endSpanWithResult(void 0, fallbackOutput);
13295
- return;
13296
- }
13297
- try {
13298
- await endSpanWithResult(await originalGetResponse(), fallbackOutput);
13299
- } catch {
13300
- await endSpanWithResult(void 0, fallbackOutput);
13301
- }
13302
- };
13303
- if (originalGetResponse) {
13304
- resultLike.getResponse = async (...args) => {
13305
- return await withCurrent(span, async () => {
13306
- try {
13307
- const response = await originalGetResponse(...args);
13308
- await endSpanWithResult(response);
13309
- return response;
13310
- } catch (error) {
13311
- endSpanWithError(error);
13312
- throw error;
13313
- }
13314
- });
13315
- };
13316
- }
13317
- if (typeof resultLike.getText === "function") {
13318
- const originalGetText = resultLike.getText.bind(resultLike);
13319
- resultLike.getText = async (...args) => {
13320
- return await withCurrent(span, async () => {
13321
- try {
13322
- const text = await originalGetText(...args);
13323
- await finalizeFromResponse(text);
13324
- return text;
13325
- } catch (error) {
13326
- endSpanWithError(error);
13327
- throw error;
13541
+ this.unsubscribers.push(
13542
+ traceAsyncChannel(openRouterChannels.embeddingsGenerate, {
13543
+ name: "openrouter.embeddings.generate",
13544
+ type: "llm" /* LLM */,
13545
+ extractInput: (args) => {
13546
+ const request = getOpenRouterRequestArg(args);
13547
+ const requestBody = isObject(request?.requestBody) ? request.requestBody : {};
13548
+ const httpReferer = request?.httpReferer;
13549
+ const xTitle = request?.xTitle;
13550
+ const { input, ...metadata } = requestBody;
13551
+ return {
13552
+ input,
13553
+ metadata: buildOpenRouterEmbeddingMetadata(
13554
+ metadata,
13555
+ httpReferer,
13556
+ xTitle
13557
+ )
13558
+ };
13559
+ },
13560
+ extractOutput: (result) => {
13561
+ if (!isObject(result)) {
13562
+ return void 0;
13563
+ }
13564
+ const embedding = result.data?.[0]?.embedding;
13565
+ return Array.isArray(embedding) ? { embedding_length: embedding.length } : void 0;
13566
+ },
13567
+ extractMetadata: (result) => {
13568
+ if (!isObject(result)) {
13569
+ return void 0;
13570
+ }
13571
+ return extractOpenRouterResponseMetadata(result);
13572
+ },
13573
+ extractMetrics: (result) => {
13574
+ return isObject(result) ? parseOpenRouterMetricsFromUsage(result.usage) : {};
13328
13575
  }
13329
- });
13330
- };
13331
- }
13332
- for (const methodName of OPENROUTER_CALL_MODEL_CONTEXT_METHODS) {
13333
- if (typeof resultLike[methodName] !== "function") {
13334
- continue;
13335
- }
13336
- const originalMethod = resultLike[methodName];
13337
- resultLike[methodName] = async (...args) => {
13338
- return await withCurrent(span, async () => {
13339
- return await originalMethod.apply(resultLike, args);
13340
- });
13341
- };
13342
- }
13343
- for (const methodName of OPENROUTER_CALL_MODEL_STREAM_METHODS) {
13344
- if (typeof resultLike[methodName] !== "function") {
13345
- continue;
13346
- }
13347
- const originalMethod = resultLike[methodName];
13348
- resultLike[methodName] = (...args) => {
13349
- const stream = withCurrent(
13350
- span,
13351
- () => originalMethod.apply(resultLike, args)
13352
- );
13353
- if (!isAsyncIterable2(stream)) {
13354
- return stream;
13355
- }
13356
- return wrapAsyncIterableWithSpan({
13357
- finalize: finalizeFromResponse,
13358
- iteratorFactory: () => stream[Symbol.asyncIterator](),
13359
- onError: endSpanWithError,
13360
- span
13361
- });
13362
- };
13363
- }
13364
- if (originalGetInitialResponse) {
13365
- let initialTurnTraced = false;
13366
- resultLike.getInitialResponse = async (...args) => {
13367
- if (initialTurnTraced) {
13368
- return await withCurrent(span, async () => {
13369
- return await originalGetInitialResponse(...args);
13370
- });
13371
- }
13372
- initialTurnTraced = true;
13373
- const resolvedRequest = getOpenRouterResolvedRequest(resultLike, request);
13374
- const childSpan = startOpenRouterCallModelTurnSpan({
13375
- request: resolvedRequest,
13376
- step: tracedTurnCount + 1,
13377
- stepType: tracedTurnCount === 0 ? "initial" : "continue"
13378
- });
13379
- return await withCurrent(childSpan, async () => {
13380
- try {
13381
- const response = await originalGetInitialResponse(...args);
13382
- tracedTurnCount++;
13383
- finishOpenRouterCallModelTurnSpan({
13384
- response,
13385
- step: tracedTurnCount,
13386
- stepType: tracedTurnCount === 1 ? "initial" : "continue",
13387
- span: childSpan
13388
- });
13389
- return response;
13390
- } catch (error) {
13391
- childSpan.log({
13392
- error: normalizeError(error).message
13576
+ })
13577
+ );
13578
+ this.unsubscribers.push(
13579
+ traceStreamingChannel(openRouterChannels.betaResponsesSend, {
13580
+ name: "openrouter.beta.responses.send",
13581
+ type: "llm" /* LLM */,
13582
+ extractInput: (args) => {
13583
+ const request = getOpenRouterRequestArg(args);
13584
+ const openResponsesRequest = isObject(request?.openResponsesRequest) ? request.openResponsesRequest : {};
13585
+ const httpReferer = request?.httpReferer;
13586
+ const xTitle = request?.xTitle;
13587
+ const { input, ...metadata } = openResponsesRequest;
13588
+ return {
13589
+ input,
13590
+ metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
13591
+ };
13592
+ },
13593
+ extractOutput: (result) => extractOpenRouterResponseOutput(result),
13594
+ extractMetadata: (result) => extractOpenRouterResponseMetadata(result),
13595
+ extractMetrics: (result, startTime) => {
13596
+ const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
13597
+ if (startTime) {
13598
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
13599
+ }
13600
+ return metrics;
13601
+ },
13602
+ aggregateChunks: aggregateOpenRouterResponseStreamEvents
13603
+ })
13604
+ );
13605
+ this.unsubscribers.push(
13606
+ traceSyncStreamChannel(openRouterChannels.callModel, {
13607
+ name: "openrouter.callModel",
13608
+ type: "llm" /* LLM */,
13609
+ extractInput: (args) => {
13610
+ const request = getOpenRouterCallModelRequestArg(args);
13611
+ return {
13612
+ input: request ? extractOpenRouterCallModelInput(request) : void 0,
13613
+ metadata: request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" }
13614
+ };
13615
+ },
13616
+ patchResult: ({ endEvent, result, span }) => {
13617
+ return patchOpenRouterCallModelResult({
13618
+ request: getOpenRouterCallModelRequestArg(endEvent.arguments),
13619
+ result,
13620
+ span
13393
13621
  });
13394
- childSpan.end();
13395
- throw error;
13396
13622
  }
13397
- });
13398
- };
13399
- }
13400
- if (originalMakeFollowupRequest) {
13401
- resultLike.makeFollowupRequest = async (...args) => {
13402
- const currentResponse = args[0];
13403
- const toolResults = Array.isArray(args[1]) ? args[1] : [];
13404
- const resolvedRequest = getOpenRouterResolvedRequest(resultLike, request);
13405
- const followupRequest = buildOpenRouterFollowupRequest(
13406
- resolvedRequest,
13407
- currentResponse,
13408
- toolResults
13409
- );
13410
- const childSpan = startOpenRouterCallModelTurnSpan({
13411
- request: followupRequest,
13412
- step: tracedTurnCount + 1,
13413
- stepType: "continue"
13414
- });
13415
- return await withCurrent(childSpan, async () => {
13416
- try {
13417
- const response = await originalMakeFollowupRequest(...args);
13418
- tracedTurnCount++;
13419
- finishOpenRouterCallModelTurnSpan({
13420
- response,
13421
- step: tracedTurnCount,
13422
- stepType: "continue",
13423
- span: childSpan
13424
- });
13425
- return response;
13426
- } catch (error) {
13427
- childSpan.log({
13428
- error: normalizeError(error).message
13429
- });
13430
- childSpan.end();
13431
- throw error;
13623
+ })
13624
+ );
13625
+ this.unsubscribers.push(
13626
+ traceAsyncChannel(openRouterChannels.callModelTurn, {
13627
+ name: "openrouter.beta.responses.send",
13628
+ type: "llm" /* LLM */,
13629
+ extractInput: (args, event) => {
13630
+ const request = getOpenRouterCallModelRequestArg(args);
13631
+ const metadata = request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" };
13632
+ if (isObject(metadata) && "tools" in metadata) {
13633
+ delete metadata.tools;
13634
+ }
13635
+ return {
13636
+ input: request ? extractOpenRouterCallModelInput(request) : void 0,
13637
+ metadata: {
13638
+ ...metadata,
13639
+ step: event.step,
13640
+ step_type: event.stepType
13641
+ }
13642
+ };
13643
+ },
13644
+ extractOutput: (result) => extractOpenRouterResponseOutput(result),
13645
+ extractMetadata: (result, event) => {
13646
+ if (!isObject(result)) {
13647
+ return {
13648
+ step: event?.step,
13649
+ step_type: event?.stepType
13650
+ };
13651
+ }
13652
+ return {
13653
+ ...extractOpenRouterResponseMetadata(result) || {},
13654
+ ...event?.step !== void 0 ? { step: event.step } : {},
13655
+ ...event?.stepType ? { step_type: event.stepType } : {}
13656
+ };
13657
+ },
13658
+ extractMetrics: (result) => isObject(result) ? parseOpenRouterMetricsFromUsage(result.usage) : {}
13659
+ })
13660
+ );
13661
+ this.unsubscribers.push(
13662
+ traceStreamingChannel(openRouterChannels.toolExecute, {
13663
+ name: "openrouter.tool",
13664
+ type: "tool" /* TOOL */,
13665
+ extractInput: (args, event) => ({
13666
+ input: args[0],
13667
+ metadata: {
13668
+ provider: "openrouter",
13669
+ tool_name: event.toolName,
13670
+ ...event.toolCallId ? { tool_call_id: event.toolCallId } : {}
13671
+ }
13672
+ }),
13673
+ extractOutput: (result) => result,
13674
+ extractMetrics: () => ({}),
13675
+ aggregateChunks: (chunks) => ({
13676
+ output: chunks.length > 0 ? chunks[chunks.length - 1] : void 0,
13677
+ metrics: {}
13678
+ })
13679
+ })
13680
+ );
13681
+ const callModelChannel = openRouterChannels.callModel.tracingChannel();
13682
+ const callModelHandlers = {
13683
+ start: (event) => {
13684
+ const request = getOpenRouterCallModelRequestArg(event.arguments);
13685
+ if (!request) {
13686
+ return;
13432
13687
  }
13433
- });
13688
+ patchOpenRouterCallModelRequestTools(request);
13689
+ }
13434
13690
  };
13691
+ callModelChannel.subscribe(callModelHandlers);
13692
+ this.unsubscribers.push(() => {
13693
+ callModelChannel.unsubscribe(callModelHandlers);
13694
+ });
13435
13695
  }
13436
- return true;
13437
- }
13438
- function wrapOpenRouterTool(tool) {
13439
- if (isWrappedTool(tool) || !tool.function || typeof tool.function !== "object" || typeof tool.function.execute !== "function") {
13440
- return tool;
13441
- }
13442
- const toolName = tool.function.name || "tool";
13443
- const originalExecute = tool.function.execute;
13444
- const wrappedTool = {
13445
- ...tool,
13446
- function: {
13447
- ...tool.function,
13448
- execute(...args) {
13449
- return traceToolExecution({
13450
- args,
13451
- execute: () => Reflect.apply(originalExecute, this, args),
13452
- toolCallId: getToolCallId(args[1]),
13453
- toolName
13454
- });
13455
- }
13456
- }
13457
- };
13458
- Object.defineProperty(wrappedTool, OPENROUTER_WRAPPED_TOOL, {
13459
- value: true,
13460
- enumerable: false,
13461
- configurable: false
13462
- });
13463
- return wrappedTool;
13464
- }
13465
- function isWrappedTool(tool) {
13466
- return Boolean(tool[OPENROUTER_WRAPPED_TOOL]);
13467
- }
13468
- function isWrappedCallModelResult(value) {
13469
- return Boolean(
13470
- isObject(value) && value[OPENROUTER_WRAPPED_CALL_MODEL_RESULT]
13471
- );
13472
- }
13473
- function traceToolExecution(args) {
13474
- const tracingChannel2 = openRouterChannels.toolExecute.tracingChannel();
13475
- const input = args.args.length > 0 ? args.args[0] : void 0;
13476
- const event = {
13477
- arguments: [input],
13478
- span_info: {
13479
- name: args.toolName
13480
- },
13481
- toolCallId: args.toolCallId,
13482
- toolName: args.toolName
13483
- };
13484
- tracingChannel2.start.publish(event);
13485
- try {
13486
- const result = args.execute();
13487
- return publishToolResult(tracingChannel2, event, result);
13488
- } catch (error) {
13489
- event.error = normalizeError(error);
13490
- tracingChannel2.error.publish(event);
13491
- throw error;
13696
+ };
13697
+ function normalizeArgs(args) {
13698
+ if (Array.isArray(args)) {
13699
+ return args;
13492
13700
  }
13493
- }
13494
- function publishToolResult(tracingChannel2, event, result) {
13495
- if (isPromiseLike(result)) {
13496
- return result.then(
13497
- (resolved) => {
13498
- event.result = resolved;
13499
- tracingChannel2.asyncEnd.publish(event);
13500
- return resolved;
13501
- },
13502
- (error) => {
13503
- event.error = normalizeError(error);
13504
- tracingChannel2.error.publish(event);
13505
- throw error;
13506
- }
13507
- );
13701
+ if (isArrayLike(args)) {
13702
+ return Array.from(args);
13508
13703
  }
13509
- event.result = result;
13510
- tracingChannel2.asyncEnd.publish(event);
13511
- return result;
13512
- }
13513
- function getToolCallId(context) {
13514
- const toolContext = context;
13515
- return typeof toolContext?.toolCall?.id === "string" ? toolContext.toolCall.id : void 0;
13704
+ return [args];
13516
13705
  }
13517
- function extractOpenRouterCallModelResultMetadata(response, turnCount) {
13518
- const combined = {
13519
- ...extractOpenRouterResponseMetadata(response) || {},
13520
- ...turnCount !== void 0 ? { turn_count: turnCount } : {}
13521
- };
13522
- return Object.keys(combined).length > 0 ? combined : void 0;
13706
+ function isArrayLike(value) {
13707
+ return isObject(value) && "length" in value && typeof value.length === "number" && Number.isInteger(value.length) && value.length >= 0;
13523
13708
  }
13524
- function getFinalOpenRouterCallModelResponse(result, response) {
13525
- if (isObject(response)) {
13526
- return response;
13709
+ function getOpenRouterRequestArg(args) {
13710
+ const normalizedArgs = normalizeArgs(args);
13711
+ const keyedCandidate = normalizedArgs.find(
13712
+ (arg) => isObject(arg) && ("chatGenerationParams" in arg || "requestBody" in arg || "openResponsesRequest" in arg)
13713
+ );
13714
+ if (isObject(keyedCandidate)) {
13715
+ return keyedCandidate;
13527
13716
  }
13528
- return isObject(result.finalResponse) ? result.finalResponse : void 0;
13717
+ const firstObjectArg = normalizedArgs.find((arg) => isObject(arg));
13718
+ return isObject(firstObjectArg) ? firstObjectArg : void 0;
13529
13719
  }
13530
- function getOpenRouterCallModelRounds(result) {
13531
- if (!Array.isArray(result.allToolExecutionRounds)) {
13532
- return [];
13533
- }
13534
- return result.allToolExecutionRounds.filter((round) => isObject(round)).map((round) => ({
13535
- response: isObject(round.response) ? round.response : void 0,
13536
- round: typeof round.round === "number" ? round.round : void 0,
13537
- toolResults: Array.isArray(round.toolResults) ? round.toolResults : []
13538
- })).filter((round) => round.response !== void 0);
13720
+ function getOpenRouterCallModelRequestArg(args) {
13721
+ const firstObjectArg = normalizeArgs(args).find((arg) => isObject(arg));
13722
+ return isObject(firstObjectArg) ? firstObjectArg : void 0;
13539
13723
  }
13540
- function aggregateOpenRouterCallModelMetrics(rounds, finalResponse) {
13724
+ var TOKEN_NAME_MAP2 = {
13725
+ promptTokens: "prompt_tokens",
13726
+ inputTokens: "prompt_tokens",
13727
+ completionTokens: "completion_tokens",
13728
+ outputTokens: "completion_tokens",
13729
+ totalTokens: "tokens",
13730
+ prompt_tokens: "prompt_tokens",
13731
+ input_tokens: "prompt_tokens",
13732
+ completion_tokens: "completion_tokens",
13733
+ output_tokens: "completion_tokens",
13734
+ total_tokens: "tokens"
13735
+ };
13736
+ var TOKEN_DETAIL_PREFIX_MAP = {
13737
+ promptTokensDetails: "prompt",
13738
+ inputTokensDetails: "prompt",
13739
+ completionTokensDetails: "completion",
13740
+ outputTokensDetails: "completion",
13741
+ costDetails: "cost",
13742
+ prompt_tokens_details: "prompt",
13743
+ input_tokens_details: "prompt",
13744
+ completion_tokens_details: "completion",
13745
+ output_tokens_details: "completion",
13746
+ cost_details: "cost"
13747
+ };
13748
+ function camelToSnake(value) {
13749
+ return value.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`);
13750
+ }
13751
+ function parseOpenRouterMetricsFromUsage(usage) {
13752
+ if (!isObject(usage)) {
13753
+ return {};
13754
+ }
13541
13755
  const metrics = {};
13542
- const responses = [
13543
- ...rounds.map((round) => round.response).filter(isObject),
13544
- finalResponse
13545
- ];
13546
- for (const response of responses) {
13547
- const responseMetrics = parseOpenRouterMetricsFromUsage(response.usage);
13548
- for (const [name, value] of Object.entries(responseMetrics)) {
13549
- metrics[name] = (metrics[name] || 0) + value;
13756
+ for (const [name, value] of Object.entries(usage)) {
13757
+ if (typeof value === "number") {
13758
+ metrics[TOKEN_NAME_MAP2[name] || camelToSnake(name)] = value;
13759
+ continue;
13760
+ }
13761
+ if (!isObject(value)) {
13762
+ continue;
13763
+ }
13764
+ const prefix = TOKEN_DETAIL_PREFIX_MAP[name];
13765
+ if (!prefix) {
13766
+ continue;
13767
+ }
13768
+ for (const [nestedName, nestedValue] of Object.entries(value)) {
13769
+ if (typeof nestedValue !== "number") {
13770
+ continue;
13771
+ }
13772
+ metrics[`${prefix}_${camelToSnake(nestedName)}`] = nestedValue;
13550
13773
  }
13551
13774
  }
13552
13775
  return metrics;
13553
13776
  }
13554
- function buildNextOpenRouterCallModelInput(currentInput, response, toolResults) {
13555
- const normalizedInput = Array.isArray(currentInput) ? [...currentInput] : currentInput === void 0 ? [] : [currentInput];
13556
- const responseOutput = Array.isArray(response.output) ? response.output : response.output === void 0 ? [] : [response.output];
13557
- return [...normalizedInput, ...responseOutput, ...toolResults].map(
13558
- (entry) => sanitizeOpenRouterLoggedValue(entry)
13559
- );
13777
+ function extractOpenRouterUsageMetadata(usage) {
13778
+ if (!isObject(usage)) {
13779
+ return void 0;
13780
+ }
13781
+ const metadata = {};
13782
+ if (typeof usage.isByok === "boolean") {
13783
+ metadata.is_byok = usage.isByok;
13784
+ } else if (typeof usage.is_byok === "boolean") {
13785
+ metadata.is_byok = usage.is_byok;
13786
+ }
13787
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
13560
13788
  }
13561
- function startOpenRouterCallModelTurnSpan(args) {
13562
- const requestRecord = isObject(args.request) ? args.request : void 0;
13563
- const metadata = requestRecord ? extractOpenRouterCallModelMetadata(requestRecord) : { provider: "openrouter" };
13564
- if (isObject(metadata) && "tools" in metadata) {
13565
- delete metadata.tools;
13789
+ var OMITTED_OPENROUTER_KEYS = /* @__PURE__ */ new Set([
13790
+ "execute",
13791
+ "render",
13792
+ "nextTurnParams",
13793
+ "requireApproval"
13794
+ ]);
13795
+ function parseOpenRouterModelString(model) {
13796
+ if (typeof model !== "string") {
13797
+ return { model };
13566
13798
  }
13567
- return startSpan({
13568
- name: "openrouter.beta.responses.send",
13569
- spanAttributes: {
13570
- type: "llm" /* LLM */
13571
- },
13572
- event: {
13573
- input: requestRecord ? extractOpenRouterCallModelInput(requestRecord) : void 0,
13574
- metadata: {
13575
- ...metadata,
13576
- step: args.step,
13577
- step_type: args.stepType
13578
- }
13579
- }
13580
- });
13799
+ const slashIndex = model.indexOf("/");
13800
+ if (slashIndex > 0 && slashIndex < model.length - 1) {
13801
+ return {
13802
+ provider: model.substring(0, slashIndex),
13803
+ model: model.substring(slashIndex + 1)
13804
+ };
13805
+ }
13806
+ return { model };
13581
13807
  }
13582
- function finishOpenRouterCallModelTurnSpan(args) {
13583
- if (!isObject(args.response)) {
13584
- args.span.end();
13585
- return;
13808
+ function isZodSchema3(value) {
13809
+ return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
13810
+ }
13811
+ function serializeZodSchema3(schema) {
13812
+ try {
13813
+ return zodToJsonSchema(schema);
13814
+ } catch {
13815
+ return {
13816
+ type: "object",
13817
+ description: "Zod schema (conversion failed)"
13818
+ };
13586
13819
  }
13587
- args.span.log({
13588
- output: extractOpenRouterResponseOutput(args.response),
13589
- ...extractOpenRouterResponseMetadata(args.response) ? {
13590
- metadata: {
13591
- ...extractOpenRouterResponseMetadata(args.response),
13592
- ...args.step !== void 0 ? { step: args.step } : {},
13593
- ...args.stepType ? { step_type: args.stepType } : {}
13594
- }
13595
- } : {},
13596
- metrics: parseOpenRouterMetricsFromUsage(args.response.usage)
13597
- });
13598
- args.span.end();
13599
13820
  }
13600
- function getOpenRouterResolvedRequest(result, request) {
13601
- if (isObject(result.resolvedRequest)) {
13602
- return result.resolvedRequest;
13821
+ function serializeOpenRouterTool(tool) {
13822
+ if (!isObject(tool)) {
13823
+ return tool;
13603
13824
  }
13604
- return request;
13825
+ const serialized = {};
13826
+ for (const [key, value] of Object.entries(tool)) {
13827
+ if (OMITTED_OPENROUTER_KEYS.has(key)) {
13828
+ continue;
13829
+ }
13830
+ if (key === "function" && isObject(value)) {
13831
+ serialized.function = sanitizeOpenRouterLoggedValue(value);
13832
+ continue;
13833
+ }
13834
+ serialized[key] = sanitizeOpenRouterLoggedValue(value);
13835
+ }
13836
+ return serialized;
13605
13837
  }
13606
- function buildOpenRouterFollowupRequest(request, currentResponse, toolResults) {
13607
- if (!request) {
13838
+ function serializeOpenRouterToolsForLogging(tools) {
13839
+ if (!Array.isArray(tools)) {
13608
13840
  return void 0;
13609
13841
  }
13610
- return {
13611
- ...request,
13612
- input: buildNextOpenRouterCallModelInput(
13613
- extractOpenRouterCallModelInput(request),
13614
- isObject(currentResponse) ? currentResponse : {},
13615
- toolResults
13616
- ),
13617
- stream: false
13618
- };
13842
+ return tools.map((tool) => serializeOpenRouterTool(tool));
13619
13843
  }
13620
- function wrapAsyncIterableWithSpan(args) {
13621
- return {
13622
- [Symbol.asyncIterator]() {
13623
- const iterator = args.iteratorFactory();
13624
- return {
13625
- next(value) {
13626
- return withCurrent(
13627
- args.span,
13628
- () => value === void 0 ? iterator.next() : iterator.next(value)
13629
- ).then(
13630
- async (result) => {
13631
- if (result.done) {
13632
- await args.finalize();
13633
- }
13634
- return result;
13635
- },
13636
- (error) => {
13637
- args.onError(error);
13638
- throw error;
13639
- }
13640
- );
13641
- },
13642
- return(value) {
13643
- if (typeof iterator.return !== "function") {
13644
- return args.finalize().then(() => ({
13645
- done: true,
13646
- value
13647
- }));
13648
- }
13649
- return withCurrent(args.span, () => iterator.return(value)).then(
13650
- async (result) => {
13651
- await args.finalize();
13652
- return result;
13653
- },
13654
- (error) => {
13655
- args.onError(error);
13656
- throw error;
13657
- }
13658
- );
13659
- },
13660
- throw(error) {
13661
- args.onError(error);
13662
- if (typeof iterator.throw !== "function") {
13663
- return Promise.reject(error);
13664
- }
13665
- return withCurrent(args.span, () => iterator.throw(error));
13666
- },
13667
- [Symbol.asyncIterator]() {
13668
- return this;
13669
- }
13670
- };
13844
+ function sanitizeOpenRouterLoggedValue(value) {
13845
+ if (isZodSchema3(value)) {
13846
+ return serializeZodSchema3(value);
13847
+ }
13848
+ if (typeof value === "function") {
13849
+ return "[Function]";
13850
+ }
13851
+ if (Array.isArray(value)) {
13852
+ return value.map((entry) => sanitizeOpenRouterLoggedValue(entry));
13853
+ }
13854
+ if (!isObject(value)) {
13855
+ return value;
13856
+ }
13857
+ const sanitized = {};
13858
+ for (const [key, entry] of Object.entries(value)) {
13859
+ if (OMITTED_OPENROUTER_KEYS.has(key)) {
13860
+ continue;
13861
+ }
13862
+ if (key === "tools" && Array.isArray(entry)) {
13863
+ sanitized.tools = serializeOpenRouterToolsForLogging(entry);
13864
+ continue;
13671
13865
  }
13866
+ sanitized[key] = sanitizeOpenRouterLoggedValue(entry);
13867
+ }
13868
+ return sanitized;
13869
+ }
13870
+ function buildOpenRouterMetadata(metadata, httpReferer, xTitle) {
13871
+ const sanitized = sanitizeOpenRouterLoggedValue(metadata);
13872
+ const metadataRecord = isObject(sanitized) ? sanitized : {};
13873
+ const { model, provider: providerRouting, ...rest } = metadataRecord;
13874
+ const normalizedModel = parseOpenRouterModelString(model);
13875
+ return {
13876
+ ...rest,
13877
+ ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
13878
+ ...providerRouting !== void 0 ? { providerRouting } : {},
13879
+ ...httpReferer !== void 0 ? { httpReferer } : {},
13880
+ ...xTitle !== void 0 ? { xTitle } : {},
13881
+ provider: normalizedModel.provider || "openrouter"
13672
13882
  };
13673
13883
  }
13674
- function isAsyncIterable2(value) {
13675
- return !!value && (typeof value === "object" || typeof value === "function") && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
13884
+ function buildOpenRouterEmbeddingMetadata(metadata, httpReferer, xTitle) {
13885
+ const normalized = buildOpenRouterMetadata(metadata, httpReferer, xTitle);
13886
+ return typeof normalized.model === "string" ? {
13887
+ ...normalized,
13888
+ embedding_model: normalized.model
13889
+ } : normalized;
13676
13890
  }
13677
- function isPromiseLike(value) {
13678
- return !!value && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
13891
+ function extractOpenRouterCallModelInput(request) {
13892
+ return isObject(request) && "input" in request ? sanitizeOpenRouterLoggedValue(request.input) : void 0;
13679
13893
  }
13680
- function normalizeError(error) {
13681
- return error instanceof Error ? error : new Error(String(error));
13894
+ function extractOpenRouterCallModelMetadata(request) {
13895
+ if (!isObject(request)) {
13896
+ return { provider: "openrouter" };
13897
+ }
13898
+ const { input: _input, ...metadata } = request;
13899
+ return buildOpenRouterMetadata(metadata, void 0, void 0);
13682
13900
  }
13683
-
13684
- // src/instrumentation/plugins/openrouter-plugin.ts
13685
- var OpenRouterPlugin = class extends BasePlugin {
13686
- onEnable() {
13687
- this.subscribeToOpenRouterChannels();
13901
+ function extractOpenRouterResponseMetadata(result) {
13902
+ if (!isObject(result)) {
13903
+ return void 0;
13688
13904
  }
13689
- onDisable() {
13690
- this.unsubscribers = unsubscribeAll(this.unsubscribers);
13905
+ const { output: _output, data: _data, usage, ...metadata } = result;
13906
+ const sanitized = sanitizeOpenRouterLoggedValue(metadata);
13907
+ const metadataRecord = isObject(sanitized) ? sanitized : {};
13908
+ const { model, provider, ...rest } = metadataRecord;
13909
+ const normalizedModel = parseOpenRouterModelString(model);
13910
+ const normalizedProvider = (typeof provider === "string" ? provider : void 0) || normalizedModel.provider;
13911
+ const usageMetadata = extractOpenRouterUsageMetadata(usage);
13912
+ const combined = {
13913
+ ...rest,
13914
+ ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
13915
+ ...usageMetadata || {},
13916
+ ...normalizedProvider !== void 0 ? { provider: normalizedProvider } : {}
13917
+ };
13918
+ return Object.keys(combined).length > 0 ? combined : void 0;
13919
+ }
13920
+ function extractOpenRouterResponseOutput(response, fallbackOutput) {
13921
+ if (isObject(response) && "output" in response && response.output !== void 0) {
13922
+ return sanitizeOpenRouterLoggedValue(response.output);
13691
13923
  }
13692
- subscribeToOpenRouterChannels() {
13693
- this.unsubscribers.push(
13694
- traceStreamingChannel(openRouterChannels.chatSend, {
13695
- name: "openrouter.chat.send",
13696
- type: "llm" /* LLM */,
13697
- extractInput: (args) => {
13698
- const request = getOpenRouterRequestArg(args);
13699
- const chatGenerationParams = isObject(request?.chatGenerationParams) ? request.chatGenerationParams : {};
13700
- const httpReferer = request?.httpReferer;
13701
- const xTitle = request?.xTitle;
13702
- const { messages, ...metadata } = chatGenerationParams;
13703
- return {
13704
- input: messages,
13705
- metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
13706
- };
13707
- },
13708
- extractOutput: (result) => {
13709
- return isObject(result) ? result.choices : void 0;
13710
- },
13711
- extractMetrics: (result, startTime) => {
13712
- const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
13713
- if (startTime) {
13714
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
13715
- }
13716
- return metrics;
13717
- },
13718
- aggregateChunks: aggregateOpenRouterChatChunks
13719
- })
13720
- );
13721
- this.unsubscribers.push(
13722
- traceAsyncChannel(openRouterChannels.embeddingsGenerate, {
13723
- name: "openrouter.embeddings.generate",
13724
- type: "llm" /* LLM */,
13725
- extractInput: (args) => {
13726
- const request = getOpenRouterRequestArg(args);
13727
- const requestBody = isObject(request?.requestBody) ? request.requestBody : {};
13728
- const httpReferer = request?.httpReferer;
13729
- const xTitle = request?.xTitle;
13730
- const { input, ...metadata } = requestBody;
13731
- return {
13732
- input,
13733
- metadata: buildOpenRouterEmbeddingMetadata(
13734
- metadata,
13735
- httpReferer,
13736
- xTitle
13737
- )
13738
- };
13739
- },
13740
- extractOutput: (result) => {
13741
- if (!isObject(result)) {
13742
- return void 0;
13743
- }
13744
- const embedding = result.data?.[0]?.embedding;
13745
- return Array.isArray(embedding) ? { embedding_length: embedding.length } : void 0;
13746
- },
13747
- extractMetadata: (result) => {
13748
- if (!isObject(result)) {
13749
- return void 0;
13750
- }
13751
- return extractOpenRouterResponseMetadata(result);
13752
- },
13753
- extractMetrics: (result) => {
13754
- return isObject(result) ? parseOpenRouterMetricsFromUsage(result.usage) : {};
13755
- }
13756
- })
13757
- );
13758
- this.unsubscribers.push(
13759
- traceStreamingChannel(openRouterChannels.betaResponsesSend, {
13760
- name: "openrouter.beta.responses.send",
13761
- type: "llm" /* LLM */,
13762
- extractInput: (args) => {
13763
- const request = getOpenRouterRequestArg(args);
13764
- const openResponsesRequest = isObject(request?.openResponsesRequest) ? request.openResponsesRequest : {};
13765
- const httpReferer = request?.httpReferer;
13766
- const xTitle = request?.xTitle;
13767
- const { input, ...metadata } = openResponsesRequest;
13768
- return {
13769
- input,
13770
- metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
13771
- };
13772
- },
13773
- extractOutput: (result) => extractOpenRouterResponseOutput(result),
13774
- extractMetadata: (result) => extractOpenRouterResponseMetadata(result),
13775
- extractMetrics: (result, startTime) => {
13776
- const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
13777
- if (startTime) {
13778
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
13779
- }
13780
- return metrics;
13781
- },
13782
- aggregateChunks: aggregateOpenRouterResponseStreamEvents
13783
- })
13784
- );
13785
- this.unsubscribers.push(
13786
- traceSyncStreamChannel(openRouterChannels.callModel, {
13787
- name: "openrouter.callModel",
13788
- type: "llm" /* LLM */,
13789
- extractInput: (args) => {
13790
- const request = getOpenRouterCallModelRequestArg(args);
13791
- return {
13792
- input: request ? extractOpenRouterCallModelInput(request) : void 0,
13793
- metadata: request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" }
13794
- };
13795
- },
13796
- patchResult: ({ endEvent, result, span }) => {
13797
- return patchOpenRouterCallModelResult(
13798
- span,
13799
- result,
13800
- getOpenRouterCallModelRequestArg(endEvent.arguments)
13801
- );
13802
- }
13803
- })
13804
- );
13805
- this.unsubscribers.push(
13806
- traceStreamingChannel(openRouterChannels.toolExecute, {
13807
- name: "openrouter.tool",
13808
- type: "tool" /* TOOL */,
13809
- extractInput: (args, event) => ({
13810
- input: args[0],
13811
- metadata: {
13812
- provider: "openrouter",
13813
- tool_name: event.toolName,
13814
- ...event.toolCallId ? { tool_call_id: event.toolCallId } : {}
13815
- }
13816
- }),
13817
- extractOutput: (result) => result,
13818
- extractMetrics: () => ({}),
13819
- aggregateChunks: (chunks) => ({
13820
- output: chunks.length > 0 ? chunks[chunks.length - 1] : void 0,
13821
- metrics: {}
13822
- })
13823
- })
13824
- );
13825
- const callModelChannel = openRouterChannels.callModel.tracingChannel();
13826
- const callModelHandlers = {
13827
- start: (event) => {
13828
- const request = getOpenRouterCallModelRequestArg(event.arguments);
13829
- if (!request) {
13830
- return;
13831
- }
13832
- patchOpenRouterCallModelRequestTools(request);
13833
- }
13834
- };
13835
- callModelChannel.subscribe(callModelHandlers);
13836
- this.unsubscribers.push(() => {
13837
- callModelChannel.unsubscribe(callModelHandlers);
13838
- });
13924
+ if (fallbackOutput !== void 0) {
13925
+ return sanitizeOpenRouterLoggedValue(fallbackOutput);
13839
13926
  }
13840
- };
13841
- function normalizeArgs(args) {
13842
- if (Array.isArray(args)) {
13843
- return args;
13927
+ return void 0;
13928
+ }
13929
+ var OPENROUTER_WRAPPED_TOOL = Symbol("braintrust.openrouter.wrappedTool");
13930
+ function patchOpenRouterCallModelRequestTools(request) {
13931
+ if (!Array.isArray(request.tools) || request.tools.length === 0) {
13932
+ return void 0;
13844
13933
  }
13845
- if (isArrayLike(args)) {
13846
- return Array.from(args);
13934
+ const originalTools = request.tools;
13935
+ const wrappedTools = originalTools.map((tool) => wrapOpenRouterTool(tool));
13936
+ const didPatch = wrappedTools.some(
13937
+ (tool, index) => tool !== originalTools[index]
13938
+ );
13939
+ if (!didPatch) {
13940
+ return void 0;
13941
+ }
13942
+ request.tools = wrappedTools;
13943
+ return () => {
13944
+ request.tools = originalTools;
13945
+ };
13946
+ }
13947
+ function wrapOpenRouterTool(tool) {
13948
+ if (isWrappedTool(tool) || !tool.function || typeof tool.function !== "object" || typeof tool.function.execute !== "function") {
13949
+ return tool;
13950
+ }
13951
+ const toolName = tool.function.name || "tool";
13952
+ const originalExecute = tool.function.execute;
13953
+ const wrappedTool = {
13954
+ ...tool,
13955
+ function: {
13956
+ ...tool.function,
13957
+ execute(...args) {
13958
+ return traceToolExecution({
13959
+ args,
13960
+ execute: () => Reflect.apply(originalExecute, this, args),
13961
+ toolCallId: getToolCallId(args[1]),
13962
+ toolName
13963
+ });
13964
+ }
13965
+ }
13966
+ };
13967
+ Object.defineProperty(wrappedTool, OPENROUTER_WRAPPED_TOOL, {
13968
+ value: true,
13969
+ enumerable: false,
13970
+ configurable: false
13971
+ });
13972
+ return wrappedTool;
13973
+ }
13974
+ function isWrappedTool(tool) {
13975
+ return Boolean(tool[OPENROUTER_WRAPPED_TOOL]);
13976
+ }
13977
+ function traceToolExecution(args) {
13978
+ const tracingChannel2 = openRouterChannels.toolExecute.tracingChannel();
13979
+ const input = args.args.length > 0 ? args.args[0] : void 0;
13980
+ const event = {
13981
+ arguments: [input],
13982
+ span_info: {
13983
+ name: args.toolName
13984
+ },
13985
+ toolCallId: args.toolCallId,
13986
+ toolName: args.toolName
13987
+ };
13988
+ tracingChannel2.start.publish(event);
13989
+ try {
13990
+ const result = args.execute();
13991
+ return publishToolResult(tracingChannel2, event, result);
13992
+ } catch (error) {
13993
+ event.error = normalizeError(error);
13994
+ tracingChannel2.error.publish(event);
13995
+ throw error;
13847
13996
  }
13848
- return [args];
13849
- }
13850
- function isArrayLike(value) {
13851
- return isObject(value) && "length" in value && typeof value.length === "number" && Number.isInteger(value.length) && value.length >= 0;
13852
13997
  }
13853
- function getOpenRouterRequestArg(args) {
13854
- const normalizedArgs = normalizeArgs(args);
13855
- const keyedCandidate = normalizedArgs.find(
13856
- (arg) => isObject(arg) && ("chatGenerationParams" in arg || "requestBody" in arg || "openResponsesRequest" in arg)
13857
- );
13858
- if (isObject(keyedCandidate)) {
13859
- return keyedCandidate;
13998
+ function publishToolResult(tracingChannel2, event, result) {
13999
+ if (isPromiseLike2(result)) {
14000
+ return result.then(
14001
+ (resolved) => {
14002
+ event.result = resolved;
14003
+ tracingChannel2.asyncEnd.publish(event);
14004
+ return resolved;
14005
+ },
14006
+ (error) => {
14007
+ event.error = normalizeError(error);
14008
+ tracingChannel2.error.publish(event);
14009
+ throw error;
14010
+ }
14011
+ );
13860
14012
  }
13861
- const firstObjectArg = normalizedArgs.find((arg) => isObject(arg));
13862
- return isObject(firstObjectArg) ? firstObjectArg : void 0;
14013
+ event.result = result;
14014
+ tracingChannel2.asyncEnd.publish(event);
14015
+ return result;
13863
14016
  }
13864
- function getOpenRouterCallModelRequestArg(args) {
13865
- const firstObjectArg = normalizeArgs(args).find((arg) => isObject(arg));
13866
- return isObject(firstObjectArg) ? firstObjectArg : void 0;
14017
+ function getToolCallId(context) {
14018
+ const toolContext = context;
14019
+ return typeof toolContext?.toolCall?.id === "string" ? toolContext.toolCall.id : void 0;
14020
+ }
14021
+ function isPromiseLike2(value) {
14022
+ return !!value && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
13867
14023
  }
13868
14024
  function aggregateOpenRouterChatChunks(chunks) {
13869
14025
  let role;
@@ -13884,95 +14040,438 @@ function aggregateOpenRouterChatChunks(chunks) {
13884
14040
  }
13885
14041
  continue;
13886
14042
  }
13887
- if (!role && delta.role) {
13888
- role = delta.role;
13889
- }
13890
- if (typeof delta.content === "string") {
13891
- content += delta.content;
13892
- }
13893
- const choiceFinishReason = choice?.finishReason ?? choice?.finish_reason ?? void 0;
13894
- const deltaFinishReason = delta.finishReason ?? delta.finish_reason ?? void 0;
13895
- if (choiceFinishReason !== void 0) {
13896
- finishReason = choiceFinishReason;
13897
- } else if (deltaFinishReason !== void 0) {
13898
- finishReason = deltaFinishReason;
13899
- }
13900
- const toolCallDeltas = Array.isArray(delta.toolCalls) ? delta.toolCalls : Array.isArray(delta.tool_calls) ? delta.tool_calls : void 0;
13901
- if (!toolCallDeltas) {
14043
+ if (!role && delta.role) {
14044
+ role = delta.role;
14045
+ }
14046
+ if (typeof delta.content === "string") {
14047
+ content += delta.content;
14048
+ }
14049
+ const choiceFinishReason = choice?.finishReason ?? choice?.finish_reason ?? void 0;
14050
+ const deltaFinishReason = delta.finishReason ?? delta.finish_reason ?? void 0;
14051
+ if (choiceFinishReason !== void 0) {
14052
+ finishReason = choiceFinishReason;
14053
+ } else if (deltaFinishReason !== void 0) {
14054
+ finishReason = deltaFinishReason;
14055
+ }
14056
+ const toolCallDeltas = Array.isArray(delta.toolCalls) ? delta.toolCalls : Array.isArray(delta.tool_calls) ? delta.tool_calls : void 0;
14057
+ if (!toolCallDeltas) {
14058
+ continue;
14059
+ }
14060
+ for (const toolDelta of toolCallDeltas) {
14061
+ if (!toolDelta?.function) {
14062
+ continue;
14063
+ }
14064
+ const toolIndex = toolDelta.index ?? 0;
14065
+ const existingToolCall = toolCalls?.[toolIndex];
14066
+ if (!existingToolCall || toolDelta.id && existingToolCall.id !== void 0 && existingToolCall.id !== toolDelta.id) {
14067
+ const nextToolCalls = [...toolCalls || []];
14068
+ nextToolCalls[toolIndex] = {
14069
+ index: toolIndex,
14070
+ id: toolDelta.id,
14071
+ type: toolDelta.type,
14072
+ function: {
14073
+ name: toolDelta.function.name,
14074
+ arguments: toolDelta.function.arguments || ""
14075
+ }
14076
+ };
14077
+ toolCalls = nextToolCalls;
14078
+ continue;
14079
+ }
14080
+ const current = existingToolCall;
14081
+ if (toolDelta.id && !current.id) {
14082
+ current.id = toolDelta.id;
14083
+ }
14084
+ if (toolDelta.type && !current.type) {
14085
+ current.type = toolDelta.type;
14086
+ }
14087
+ if (toolDelta.function.name && !current.function.name) {
14088
+ current.function.name = toolDelta.function.name;
14089
+ }
14090
+ current.function.arguments += toolDelta.function.arguments || "";
14091
+ }
14092
+ }
14093
+ return {
14094
+ output: [
14095
+ {
14096
+ index: 0,
14097
+ message: {
14098
+ role,
14099
+ content: content || void 0,
14100
+ ...toolCalls ? { tool_calls: toolCalls } : {}
14101
+ },
14102
+ logprobs: null,
14103
+ finish_reason: finishReason
14104
+ }
14105
+ ],
14106
+ metrics
14107
+ };
14108
+ }
14109
+ function aggregateOpenRouterResponseStreamEvents(chunks) {
14110
+ let finalResponse;
14111
+ for (const chunk of chunks) {
14112
+ const response = chunk?.response;
14113
+ if (!response) {
14114
+ continue;
14115
+ }
14116
+ if (chunk.type === "response.completed" || chunk.type === "response.incomplete" || chunk.type === "response.failed") {
14117
+ finalResponse = response;
14118
+ }
14119
+ }
14120
+ if (!finalResponse) {
14121
+ return {
14122
+ output: void 0,
14123
+ metrics: {}
14124
+ };
14125
+ }
14126
+ return {
14127
+ output: extractOpenRouterResponseOutput(finalResponse),
14128
+ metrics: parseOpenRouterMetricsFromUsage(finalResponse.usage),
14129
+ ...extractOpenRouterResponseMetadata(finalResponse) ? { metadata: extractOpenRouterResponseMetadata(finalResponse) } : {}
14130
+ };
14131
+ }
14132
+ var OPENROUTER_WRAPPED_CALL_MODEL_RESULT = Symbol(
14133
+ "braintrust.openrouter.wrappedCallModelResult"
14134
+ );
14135
+ var OPENROUTER_CALL_MODEL_STREAM_METHODS = [
14136
+ "getFullResponsesStream",
14137
+ "getItemsStream",
14138
+ "getNewMessagesStream",
14139
+ "getReasoningStream",
14140
+ "getTextStream",
14141
+ "getToolCallsStream",
14142
+ "getToolStream"
14143
+ ];
14144
+ var OPENROUTER_CALL_MODEL_CONTEXT_METHODS = [
14145
+ "cancel",
14146
+ "getPendingToolCalls",
14147
+ "getState",
14148
+ "getToolCalls",
14149
+ "requiresApproval"
14150
+ ];
14151
+ function patchOpenRouterCallModelResult(args) {
14152
+ const { request, result, span } = args;
14153
+ if (!isObject(result) || isWrappedCallModelResult(result)) {
14154
+ return false;
14155
+ }
14156
+ const resultLike = result;
14157
+ const hasInstrumentableMethod = typeof resultLike.getResponse === "function" || typeof resultLike.getText === "function" || OPENROUTER_CALL_MODEL_STREAM_METHODS.some(
14158
+ (methodName) => typeof resultLike[methodName] === "function"
14159
+ );
14160
+ if (!hasInstrumentableMethod) {
14161
+ return false;
14162
+ }
14163
+ Object.defineProperty(resultLike, OPENROUTER_WRAPPED_CALL_MODEL_RESULT, {
14164
+ value: true,
14165
+ enumerable: false,
14166
+ configurable: false
14167
+ });
14168
+ const originalGetResponse = typeof resultLike.getResponse === "function" ? resultLike.getResponse.bind(resultLike) : void 0;
14169
+ const originalGetInitialResponse = typeof resultLike.getInitialResponse === "function" ? resultLike.getInitialResponse.bind(resultLike) : void 0;
14170
+ const originalMakeFollowupRequest = typeof resultLike.makeFollowupRequest === "function" ? resultLike.makeFollowupRequest.bind(resultLike) : void 0;
14171
+ let ended = false;
14172
+ let tracedTurnCount = 0;
14173
+ const endSpanWithResult = async (response, fallbackOutput) => {
14174
+ if (ended) {
14175
+ return;
14176
+ }
14177
+ ended = true;
14178
+ const finalResponse = getFinalOpenRouterCallModelResponse(
14179
+ resultLike,
14180
+ response
14181
+ );
14182
+ if (finalResponse) {
14183
+ const rounds = getOpenRouterCallModelRounds(resultLike);
14184
+ const metadata = extractOpenRouterCallModelResultMetadata(
14185
+ finalResponse,
14186
+ rounds.length + 1
14187
+ );
14188
+ span.log({
14189
+ output: extractOpenRouterResponseOutput(finalResponse, fallbackOutput),
14190
+ ...metadata ? { metadata } : {},
14191
+ metrics: aggregateOpenRouterCallModelMetrics(rounds, finalResponse)
14192
+ });
14193
+ span.end();
14194
+ return;
14195
+ }
14196
+ if (fallbackOutput !== void 0) {
14197
+ span.log({
14198
+ output: fallbackOutput
14199
+ });
14200
+ }
14201
+ span.end();
14202
+ };
14203
+ const endSpanWithError = (error) => {
14204
+ if (ended) {
14205
+ return;
14206
+ }
14207
+ ended = true;
14208
+ span.log({
14209
+ error: normalizeError(error).message
14210
+ });
14211
+ span.end();
14212
+ };
14213
+ const finalizeFromResponse = async (fallbackOutput) => {
14214
+ if (!originalGetResponse) {
14215
+ await endSpanWithResult(void 0, fallbackOutput);
14216
+ return;
14217
+ }
14218
+ try {
14219
+ await endSpanWithResult(await originalGetResponse(), fallbackOutput);
14220
+ } catch {
14221
+ await endSpanWithResult(void 0, fallbackOutput);
14222
+ }
14223
+ };
14224
+ if (originalGetResponse) {
14225
+ resultLike.getResponse = async (...args2) => {
14226
+ return await withCurrent(span, async () => {
14227
+ try {
14228
+ const response = await originalGetResponse(...args2);
14229
+ await endSpanWithResult(response);
14230
+ return response;
14231
+ } catch (error) {
14232
+ endSpanWithError(error);
14233
+ throw error;
14234
+ }
14235
+ });
14236
+ };
14237
+ }
14238
+ if (typeof resultLike.getText === "function") {
14239
+ const originalGetText = resultLike.getText.bind(resultLike);
14240
+ resultLike.getText = async (...args2) => {
14241
+ return await withCurrent(span, async () => {
14242
+ try {
14243
+ const text = await originalGetText(...args2);
14244
+ await finalizeFromResponse(text);
14245
+ return text;
14246
+ } catch (error) {
14247
+ endSpanWithError(error);
14248
+ throw error;
14249
+ }
14250
+ });
14251
+ };
14252
+ }
14253
+ for (const methodName of OPENROUTER_CALL_MODEL_CONTEXT_METHODS) {
14254
+ if (typeof resultLike[methodName] !== "function") {
14255
+ continue;
14256
+ }
14257
+ const originalMethod = resultLike[methodName];
14258
+ resultLike[methodName] = async (...args2) => {
14259
+ return await withCurrent(span, async () => {
14260
+ return await originalMethod.apply(resultLike, args2);
14261
+ });
14262
+ };
14263
+ }
14264
+ for (const methodName of OPENROUTER_CALL_MODEL_STREAM_METHODS) {
14265
+ if (typeof resultLike[methodName] !== "function") {
13902
14266
  continue;
13903
14267
  }
13904
- for (const toolDelta of toolCallDeltas) {
13905
- if (!toolDelta?.function) {
13906
- continue;
13907
- }
13908
- const toolIndex = toolDelta.index ?? 0;
13909
- const existingToolCall = toolCalls?.[toolIndex];
13910
- if (!existingToolCall || toolDelta.id && existingToolCall.id !== void 0 && existingToolCall.id !== toolDelta.id) {
13911
- const nextToolCalls = [...toolCalls || []];
13912
- nextToolCalls[toolIndex] = {
13913
- index: toolIndex,
13914
- id: toolDelta.id,
13915
- type: toolDelta.type,
13916
- function: {
13917
- name: toolDelta.function.name,
13918
- arguments: toolDelta.function.arguments || ""
13919
- }
13920
- };
13921
- toolCalls = nextToolCalls;
13922
- continue;
13923
- }
13924
- const current = existingToolCall;
13925
- if (toolDelta.id && !current.id) {
13926
- current.id = toolDelta.id;
13927
- }
13928
- if (toolDelta.type && !current.type) {
13929
- current.type = toolDelta.type;
14268
+ const originalMethod = resultLike[methodName];
14269
+ resultLike[methodName] = (...args2) => {
14270
+ const stream = withCurrent(
14271
+ span,
14272
+ () => originalMethod.apply(resultLike, args2)
14273
+ );
14274
+ if (!isAsyncIterable2(stream)) {
14275
+ return stream;
13930
14276
  }
13931
- if (toolDelta.function.name && !current.function.name) {
13932
- current.function.name = toolDelta.function.name;
14277
+ return wrapAsyncIterableWithSpan({
14278
+ finalize: finalizeFromResponse,
14279
+ iteratorFactory: () => stream[Symbol.asyncIterator](),
14280
+ onError: endSpanWithError,
14281
+ span
14282
+ });
14283
+ };
14284
+ }
14285
+ if (originalGetInitialResponse) {
14286
+ let initialTurnTraced = false;
14287
+ resultLike.getInitialResponse = async (...args2) => {
14288
+ if (initialTurnTraced) {
14289
+ return await withCurrent(span, async () => {
14290
+ return await originalGetInitialResponse(...args2);
14291
+ });
13933
14292
  }
13934
- current.function.arguments += toolDelta.function.arguments || "";
13935
- }
14293
+ initialTurnTraced = true;
14294
+ const step = tracedTurnCount + 1;
14295
+ const stepType = tracedTurnCount === 0 ? "initial" : "continue";
14296
+ const response = await traceOpenRouterCallModelTurn({
14297
+ fn: async () => {
14298
+ const nextResponse = await originalGetInitialResponse(...args2);
14299
+ tracedTurnCount++;
14300
+ return nextResponse;
14301
+ },
14302
+ parentSpan: span,
14303
+ request: getOpenRouterResolvedRequest(resultLike, request),
14304
+ step,
14305
+ stepType
14306
+ });
14307
+ return response;
14308
+ };
13936
14309
  }
13937
- return {
13938
- output: [
13939
- {
13940
- index: 0,
13941
- message: {
13942
- role,
13943
- content: content || void 0,
13944
- ...toolCalls ? { tool_calls: toolCalls } : {}
14310
+ if (originalMakeFollowupRequest) {
14311
+ resultLike.makeFollowupRequest = async (...args2) => {
14312
+ const currentResponse = args2[0];
14313
+ const toolResults = Array.isArray(args2[1]) ? args2[1] : [];
14314
+ const step = tracedTurnCount + 1;
14315
+ const response = await traceOpenRouterCallModelTurn({
14316
+ fn: async () => {
14317
+ const nextResponse = await originalMakeFollowupRequest(...args2);
14318
+ tracedTurnCount++;
14319
+ return nextResponse;
13945
14320
  },
13946
- logprobs: null,
13947
- finish_reason: finishReason
13948
- }
13949
- ],
13950
- metrics
14321
+ parentSpan: span,
14322
+ request: buildOpenRouterFollowupRequest(
14323
+ getOpenRouterResolvedRequest(resultLike, request),
14324
+ currentResponse,
14325
+ toolResults
14326
+ ),
14327
+ step,
14328
+ stepType: "continue"
14329
+ });
14330
+ return response;
14331
+ };
14332
+ }
14333
+ return true;
14334
+ }
14335
+ async function traceOpenRouterCallModelTurn(args) {
14336
+ const context = {
14337
+ arguments: [args.request],
14338
+ step: args.step,
14339
+ stepType: args.stepType
13951
14340
  };
14341
+ return await withCurrent(
14342
+ args.parentSpan,
14343
+ () => openRouterChannels.callModelTurn.tracePromise(args.fn, context)
14344
+ );
13952
14345
  }
13953
- function aggregateOpenRouterResponseStreamEvents(chunks) {
13954
- let finalResponse;
13955
- for (const chunk of chunks) {
13956
- const response = chunk?.response;
13957
- if (!response) {
13958
- continue;
13959
- }
13960
- if (chunk.type === "response.completed" || chunk.type === "response.incomplete" || chunk.type === "response.failed") {
13961
- finalResponse = response;
14346
+ function isWrappedCallModelResult(value) {
14347
+ return Boolean(
14348
+ isObject(value) && value[OPENROUTER_WRAPPED_CALL_MODEL_RESULT]
14349
+ );
14350
+ }
14351
+ function extractOpenRouterCallModelResultMetadata(response, turnCount) {
14352
+ const combined = {
14353
+ ...extractOpenRouterResponseMetadata(response) || {},
14354
+ ...turnCount !== void 0 ? { turn_count: turnCount } : {}
14355
+ };
14356
+ return Object.keys(combined).length > 0 ? combined : void 0;
14357
+ }
14358
+ function getFinalOpenRouterCallModelResponse(result, response) {
14359
+ if (isObject(response)) {
14360
+ return response;
14361
+ }
14362
+ return isObject(result.finalResponse) ? result.finalResponse : void 0;
14363
+ }
14364
+ function getOpenRouterCallModelRounds(result) {
14365
+ if (!Array.isArray(result.allToolExecutionRounds)) {
14366
+ return [];
14367
+ }
14368
+ return result.allToolExecutionRounds.filter((round) => isObject(round)).map((round) => ({
14369
+ response: isObject(round.response) ? round.response : void 0,
14370
+ round: typeof round.round === "number" ? round.round : void 0,
14371
+ toolResults: Array.isArray(round.toolResults) ? round.toolResults : []
14372
+ })).filter((round) => round.response !== void 0);
14373
+ }
14374
+ function aggregateOpenRouterCallModelMetrics(rounds, finalResponse) {
14375
+ const metrics = {};
14376
+ const responses = [
14377
+ ...rounds.map((round) => round.response).filter(isObject),
14378
+ finalResponse
14379
+ ];
14380
+ for (const response of responses) {
14381
+ const responseMetrics = parseOpenRouterMetricsFromUsage(response.usage);
14382
+ for (const [name, value] of Object.entries(responseMetrics)) {
14383
+ metrics[name] = (metrics[name] || 0) + value;
13962
14384
  }
13963
14385
  }
13964
- if (!finalResponse) {
13965
- return {
13966
- output: void 0,
13967
- metrics: {}
13968
- };
14386
+ return metrics;
14387
+ }
14388
+ function buildNextOpenRouterCallModelInput(currentInput, response, toolResults) {
14389
+ const normalizedInput = Array.isArray(currentInput) ? [...currentInput] : currentInput === void 0 ? [] : [currentInput];
14390
+ const responseOutput = Array.isArray(response.output) ? response.output : response.output === void 0 ? [] : [response.output];
14391
+ return [...normalizedInput, ...responseOutput, ...toolResults].map(
14392
+ (entry) => sanitizeOpenRouterLoggedValue(entry)
14393
+ );
14394
+ }
14395
+ function getOpenRouterResolvedRequest(result, request) {
14396
+ if (isObject(result.resolvedRequest)) {
14397
+ return result.resolvedRequest;
14398
+ }
14399
+ return request;
14400
+ }
14401
+ function buildOpenRouterFollowupRequest(request, currentResponse, toolResults) {
14402
+ if (!request) {
14403
+ return void 0;
13969
14404
  }
13970
14405
  return {
13971
- output: extractOpenRouterResponseOutput(finalResponse),
13972
- metrics: parseOpenRouterMetricsFromUsage(finalResponse.usage),
13973
- ...extractOpenRouterResponseMetadata(finalResponse) ? { metadata: extractOpenRouterResponseMetadata(finalResponse) } : {}
14406
+ ...request,
14407
+ input: buildNextOpenRouterCallModelInput(
14408
+ extractOpenRouterCallModelInput(request),
14409
+ isObject(currentResponse) ? currentResponse : {},
14410
+ toolResults
14411
+ ),
14412
+ stream: false
14413
+ };
14414
+ }
14415
+ function wrapAsyncIterableWithSpan(args) {
14416
+ return {
14417
+ [Symbol.asyncIterator]() {
14418
+ const iterator = args.iteratorFactory();
14419
+ return {
14420
+ next(value) {
14421
+ return withCurrent(
14422
+ args.span,
14423
+ () => value === void 0 ? iterator.next() : iterator.next(value)
14424
+ ).then(
14425
+ async (result) => {
14426
+ if (result.done) {
14427
+ await args.finalize();
14428
+ }
14429
+ return result;
14430
+ },
14431
+ (error) => {
14432
+ args.onError(error);
14433
+ throw error;
14434
+ }
14435
+ );
14436
+ },
14437
+ return(value) {
14438
+ if (typeof iterator.return !== "function") {
14439
+ return args.finalize().then(() => ({
14440
+ done: true,
14441
+ value
14442
+ }));
14443
+ }
14444
+ return withCurrent(args.span, () => iterator.return(value)).then(
14445
+ async (result) => {
14446
+ await args.finalize();
14447
+ return result;
14448
+ },
14449
+ (error) => {
14450
+ args.onError(error);
14451
+ throw error;
14452
+ }
14453
+ );
14454
+ },
14455
+ throw(error) {
14456
+ args.onError(error);
14457
+ if (typeof iterator.throw !== "function") {
14458
+ return Promise.reject(error);
14459
+ }
14460
+ return withCurrent(args.span, () => iterator.throw(error));
14461
+ },
14462
+ [Symbol.asyncIterator]() {
14463
+ return this;
14464
+ }
14465
+ };
14466
+ }
13974
14467
  };
13975
14468
  }
14469
+ function isAsyncIterable2(value) {
14470
+ return !!value && (typeof value === "object" || typeof value === "function") && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
14471
+ }
14472
+ function normalizeError(error) {
14473
+ return error instanceof Error ? error : new Error(String(error));
14474
+ }
13976
14475
 
13977
14476
  // src/instrumentation/braintrust-plugin.ts
13978
14477
  var BraintrustPlugin = class extends BasePlugin {
@@ -15695,7 +16194,28 @@ function validateParametersWithJsonSchema(parameters, schema) {
15695
16194
  }).join(", ");
15696
16195
  throw Error(`Invalid parameters: ${errorMessages}`);
15697
16196
  }
15698
- return parameters;
16197
+ return rehydrateRemoteParameters(parameters, schema);
16198
+ }
16199
+ function rehydrateRemoteParameters(parameters, schema) {
16200
+ const schemaProperties = schema.properties;
16201
+ if (typeof schemaProperties !== "object" || schemaProperties === null) {
16202
+ return parameters;
16203
+ }
16204
+ return Object.fromEntries(
16205
+ Object.entries(parameters).map(([name, value]) => {
16206
+ const propertySchema = Reflect.get(schemaProperties, name);
16207
+ if (typeof propertySchema !== "object" || propertySchema === null) {
16208
+ return [name, value];
16209
+ }
16210
+ if (Reflect.get(propertySchema, "x-bt-type") === "prompt") {
16211
+ return [
16212
+ name,
16213
+ Prompt2.fromPromptData(name, PromptData.parse(value))
16214
+ ];
16215
+ }
16216
+ return [name, value];
16217
+ })
16218
+ );
15699
16219
  }
15700
16220
 
15701
16221
  // src/framework.ts
@@ -16930,7 +17450,8 @@ var ScorerBuilder = class {
16930
17450
  type: "llm_classifier",
16931
17451
  use_cot: opts.useCot,
16932
17452
  choice_scores: opts.choiceScores
16933
- }
17453
+ },
17454
+ ...opts.templateFormat ? { template_format: opts.templateFormat } : {}
16934
17455
  };
16935
17456
  const codePrompt = new CodePrompt(
16936
17457
  this.project,