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
package/dist/cli.js CHANGED
@@ -1232,7 +1232,7 @@ var require_package = __commonJS({
1232
1232
  "package.json"(exports2, module2) {
1233
1233
  module2.exports = {
1234
1234
  name: "braintrust",
1235
- version: "3.7.0",
1235
+ version: "3.7.1",
1236
1236
  description: "SDK for integrating Braintrust",
1237
1237
  repository: {
1238
1238
  type: "git",
@@ -1425,7 +1425,7 @@ var require_package = __commonJS({
1425
1425
  typescript: "5.4.4",
1426
1426
  vite: "^6.4.1",
1427
1427
  "vite-tsconfig-paths": "^4.3.2",
1428
- vitest: "^4.1.0",
1428
+ vitest: "^4.1.2",
1429
1429
  webpack: "^5.97.1",
1430
1430
  zod: "^3.25.34"
1431
1431
  },
@@ -1561,7 +1561,7 @@ var DefaultTracingChannel = class {
1561
1561
  }
1562
1562
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1563
1563
  tracePromise(fn, _message, thisArg, ...args) {
1564
- return Promise.resolve(fn.apply(thisArg, args));
1564
+ return fn.apply(thisArg, args);
1565
1565
  }
1566
1566
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1567
1567
  traceCallback(fn, _position, _message, thisArg, ...args) {
@@ -1581,6 +1581,7 @@ var iso = {
1581
1581
  processOn: (_0, _1) => {
1582
1582
  },
1583
1583
  basename: (filepath) => filepath.split(/[\\/]/).pop() || filepath,
1584
+ // eslint-disable-next-line no-restricted-properties -- preserving intentional console usage.
1584
1585
  writeln: (text) => console.log(text)
1585
1586
  };
1586
1587
  var isomorph_default = iso;
@@ -3072,6 +3073,15 @@ var InternalAbortError = class extends Error {
3072
3073
  this.name = "InternalAbortError";
3073
3074
  }
3074
3075
  };
3076
+ function filterFrom(record, keys) {
3077
+ const out = {};
3078
+ for (const k of Object.keys(record)) {
3079
+ if (!keys.includes(k)) {
3080
+ out[k] = record[k];
3081
+ }
3082
+ }
3083
+ return out;
3084
+ }
3075
3085
 
3076
3086
  // src/generated_types.ts
3077
3087
  var import_v36 = require("zod/v3");
@@ -11599,7 +11609,28 @@ function validateParametersWithJsonSchema(parameters, schema) {
11599
11609
  }).join(", ");
11600
11610
  throw Error(`Invalid parameters: ${errorMessages}`);
11601
11611
  }
11602
- return parameters;
11612
+ return rehydrateRemoteParameters(parameters, schema);
11613
+ }
11614
+ function rehydrateRemoteParameters(parameters, schema) {
11615
+ const schemaProperties = schema.properties;
11616
+ if (typeof schemaProperties !== "object" || schemaProperties === null) {
11617
+ return parameters;
11618
+ }
11619
+ return Object.fromEntries(
11620
+ Object.entries(parameters).map(([name, value]) => {
11621
+ const propertySchema = Reflect.get(schemaProperties, name);
11622
+ if (typeof propertySchema !== "object" || propertySchema === null) {
11623
+ return [name, value];
11624
+ }
11625
+ if (Reflect.get(propertySchema, "x-bt-type") === "prompt") {
11626
+ return [
11627
+ name,
11628
+ Prompt2.fromPromptData(name, PromptData.parse(value))
11629
+ ];
11630
+ }
11631
+ return [name, value];
11632
+ })
11633
+ );
11603
11634
  }
11604
11635
 
11605
11636
  // src/framework.ts
@@ -12947,7 +12978,7 @@ function patchStreamIfNeeded(stream, options) {
12947
12978
  if (!completed) {
12948
12979
  completed = true;
12949
12980
  try {
12950
- options.onComplete(chunks);
12981
+ await options.onComplete(chunks);
12951
12982
  } catch (error2) {
12952
12983
  console.error("Error in stream onComplete handler:", error2);
12953
12984
  }
@@ -12959,7 +12990,7 @@ function patchStreamIfNeeded(stream, options) {
12959
12990
  chunks.push(chunk);
12960
12991
  if (options.onChunk) {
12961
12992
  try {
12962
- options.onChunk(chunk);
12993
+ await options.onChunk(chunk);
12963
12994
  } catch (error2) {
12964
12995
  console.error("Error in stream onChunk handler:", error2);
12965
12996
  }
@@ -12972,7 +13003,7 @@ function patchStreamIfNeeded(stream, options) {
12972
13003
  completed = true;
12973
13004
  if (options.onError) {
12974
13005
  try {
12975
- options.onError(
13006
+ await options.onError(
12976
13007
  error2 instanceof Error ? error2 : new Error(String(error2)),
12977
13008
  chunks
12978
13009
  );
@@ -12990,7 +13021,7 @@ function patchStreamIfNeeded(stream, options) {
12990
13021
  if (!completed) {
12991
13022
  completed = true;
12992
13023
  try {
12993
- options.onComplete(chunks);
13024
+ await options.onComplete(chunks);
12994
13025
  } catch (error2) {
12995
13026
  console.error("Error in stream onComplete handler:", error2);
12996
13027
  }
@@ -13007,7 +13038,7 @@ function patchStreamIfNeeded(stream, options) {
13007
13038
  const error2 = rawError instanceof Error ? rawError : new Error(String(rawError));
13008
13039
  if (options.onError) {
13009
13040
  try {
13010
- options.onError(error2, chunks);
13041
+ await options.onError(error2, chunks);
13011
13042
  } catch (handlerError) {
13012
13043
  console.error("Error in stream onError handler:", handlerError);
13013
13044
  }
@@ -13716,51 +13747,28 @@ function traceSyncStreamChannel(channel2, config3) {
13716
13747
  }
13717
13748
  const { span, startTime } = spanData;
13718
13749
  const endEvent = event;
13719
- if (config3.patchResult?.({
13720
- channelName,
13721
- endEvent,
13722
- result: endEvent.result,
13723
- span,
13724
- startTime
13725
- })) {
13726
- return;
13727
- }
13728
- const stream = endEvent.result;
13729
- if (!isSyncStreamLike(stream)) {
13730
- span.end();
13731
- states.delete(event);
13732
- return;
13733
- }
13734
- let first = true;
13735
- stream.on("chunk", () => {
13736
- if (first) {
13737
- span.log({
13738
- metrics: {
13739
- time_to_first_token: getCurrentUnixTimestamp() - startTime
13740
- }
13741
- });
13742
- first = false;
13743
- }
13744
- });
13745
- stream.on("chatCompletion", (completion) => {
13746
- try {
13747
- if (hasChoices(completion)) {
13748
- span.log({
13749
- output: completion.choices
13750
- });
13751
- }
13752
- } catch (error2) {
13753
- console.error(
13754
- `Error extracting chatCompletion for ${channelName}:`,
13755
- error2
13756
- );
13750
+ const handleResolvedResult = (result) => {
13751
+ const resolvedEndEvent = {
13752
+ ...endEvent,
13753
+ result
13754
+ };
13755
+ if (config3.patchResult?.({
13756
+ channelName,
13757
+ endEvent: resolvedEndEvent,
13758
+ result,
13759
+ span,
13760
+ startTime
13761
+ })) {
13762
+ return;
13757
13763
  }
13758
- });
13759
- stream.on("event", (streamEvent) => {
13760
- if (!config3.extractFromEvent) {
13764
+ const stream = result;
13765
+ if (!isSyncStreamLike(stream)) {
13766
+ span.end();
13767
+ states.delete(event);
13761
13768
  return;
13762
13769
  }
13763
- try {
13770
+ let first = true;
13771
+ stream.on("chunk", () => {
13764
13772
  if (first) {
13765
13773
  span.log({
13766
13774
  metrics: {
@@ -13769,25 +13777,55 @@ function traceSyncStreamChannel(channel2, config3) {
13769
13777
  });
13770
13778
  first = false;
13771
13779
  }
13772
- const extracted = config3.extractFromEvent(streamEvent);
13773
- if (extracted && Object.keys(extracted).length > 0) {
13774
- span.log(extracted);
13780
+ });
13781
+ stream.on("chatCompletion", (completion) => {
13782
+ try {
13783
+ if (hasChoices(completion)) {
13784
+ span.log({
13785
+ output: completion.choices
13786
+ });
13787
+ }
13788
+ } catch (error2) {
13789
+ console.error(
13790
+ `Error extracting chatCompletion for ${channelName}:`,
13791
+ error2
13792
+ );
13775
13793
  }
13776
- } catch (error2) {
13777
- console.error(`Error extracting event for ${channelName}:`, error2);
13778
- }
13779
- });
13780
- stream.on("end", () => {
13781
- span.end();
13782
- states.delete(event);
13783
- });
13784
- stream.on("error", (error2) => {
13785
- span.log({
13786
- error: error2.message
13787
13794
  });
13788
- span.end();
13789
- states.delete(event);
13790
- });
13795
+ stream.on("event", (streamEvent) => {
13796
+ if (!config3.extractFromEvent) {
13797
+ return;
13798
+ }
13799
+ try {
13800
+ if (first) {
13801
+ span.log({
13802
+ metrics: {
13803
+ time_to_first_token: getCurrentUnixTimestamp() - startTime
13804
+ }
13805
+ });
13806
+ first = false;
13807
+ }
13808
+ const extracted = config3.extractFromEvent(streamEvent);
13809
+ if (extracted && Object.keys(extracted).length > 0) {
13810
+ span.log(extracted);
13811
+ }
13812
+ } catch (error2) {
13813
+ console.error(`Error extracting event for ${channelName}:`, error2);
13814
+ }
13815
+ });
13816
+ stream.on("end", () => {
13817
+ span.end();
13818
+ states.delete(event);
13819
+ });
13820
+ stream.on("error", (error2) => {
13821
+ span.log({
13822
+ error: error2.message
13823
+ });
13824
+ span.end();
13825
+ states.delete(event);
13826
+ });
13827
+ };
13828
+ handleResolvedResult(endEvent.result);
13791
13829
  },
13792
13830
  error: (event) => {
13793
13831
  logErrorAndEnd(states, event);
@@ -14610,28 +14648,40 @@ function aggregateAnthropicStreamChunks(chunks) {
14610
14648
  case "content_block_start":
14611
14649
  if (event.content_block) {
14612
14650
  contentBlocks[event.index] = event.content_block;
14613
- contentBlockDeltas[event.index] = [];
14651
+ contentBlockDeltas[event.index] = { textDeltas: [], citations: [] };
14614
14652
  }
14615
14653
  break;
14616
- case "content_block_delta":
14617
- if (event.delta?.type === "text_delta") {
14618
- const text = event.delta.text;
14654
+ case "content_block_delta": {
14655
+ const acc = contentBlockDeltas[event.index];
14656
+ const delta = event.delta;
14657
+ if (!delta) break;
14658
+ if (delta.type === "text_delta" && "text" in delta) {
14659
+ const text = delta.text;
14619
14660
  if (text) {
14620
- if (contentBlocks[event.index] !== void 0 || contentBlockDeltas[event.index] !== void 0) {
14621
- contentBlockDeltas[event.index] ??= [];
14622
- contentBlockDeltas[event.index].push(text);
14661
+ if (acc !== void 0) {
14662
+ acc.textDeltas.push(text);
14623
14663
  } else {
14624
14664
  fallbackTextDeltas.push(text);
14625
14665
  }
14626
14666
  }
14627
- } else if (event.delta?.type === "input_json_delta") {
14628
- const partialJson = event.delta.partial_json;
14629
- if (partialJson) {
14630
- contentBlockDeltas[event.index] ??= [];
14631
- contentBlockDeltas[event.index].push(partialJson);
14667
+ } else if (delta.type === "input_json_delta" && "partial_json" in delta) {
14668
+ const partialJson = delta.partial_json;
14669
+ if (partialJson && acc !== void 0) {
14670
+ acc.textDeltas.push(partialJson);
14671
+ }
14672
+ } else if (delta.type === "thinking_delta" && "thinking" in delta) {
14673
+ const thinking = delta.thinking;
14674
+ if (thinking && acc !== void 0) {
14675
+ acc.textDeltas.push(thinking);
14676
+ }
14677
+ } else if (delta.type === "citations_delta" && "citation" in delta) {
14678
+ const citation = delta.citation;
14679
+ if (citation && acc !== void 0) {
14680
+ acc.citations.push(citation);
14632
14681
  }
14633
14682
  }
14634
14683
  break;
14684
+ }
14635
14685
  case "content_block_stop":
14636
14686
  finalizeContentBlock(
14637
14687
  event.index,
@@ -14657,7 +14707,7 @@ function aggregateAnthropicStreamChunks(chunks) {
14657
14707
  })).filter(({ block }) => block !== void 0).sort((left, right) => left.index - right.index).map(({ block }) => block);
14658
14708
  let output = fallbackTextDeltas.join("");
14659
14709
  if (orderedContent.length > 0) {
14660
- if (orderedContent.every(isTextContentBlock)) {
14710
+ if (orderedContent.every(isTextContentBlock) && orderedContent.every((block) => !block.citations?.length)) {
14661
14711
  output = orderedContent.map((block) => block.text).join("");
14662
14712
  } else {
14663
14713
  output = {
@@ -14683,7 +14733,8 @@ function finalizeContentBlock(index, contentBlocks, contentBlockDeltas, fallback
14683
14733
  if (!contentBlock) {
14684
14734
  return;
14685
14735
  }
14686
- const text = contentBlockDeltas[index]?.join("") ?? "";
14736
+ const acc = contentBlockDeltas[index];
14737
+ const text = acc?.textDeltas.join("") ?? "";
14687
14738
  if (isToolUseContentBlock(contentBlock)) {
14688
14739
  if (!text) {
14689
14740
  return;
@@ -14700,20 +14751,28 @@ function finalizeContentBlock(index, contentBlocks, contentBlockDeltas, fallback
14700
14751
  return;
14701
14752
  }
14702
14753
  if (isTextContentBlock(contentBlock)) {
14754
+ if (!text) {
14755
+ delete contentBlocks[index];
14756
+ return;
14757
+ }
14758
+ const updated = { ...contentBlock, text };
14759
+ if (acc?.citations.length) {
14760
+ updated.citations = acc.citations;
14761
+ }
14762
+ contentBlocks[index] = updated;
14763
+ return;
14764
+ }
14765
+ if (isThinkingContentBlock(contentBlock)) {
14703
14766
  if (!text) {
14704
14767
  delete contentBlocks[index];
14705
14768
  return;
14706
14769
  }
14707
14770
  contentBlocks[index] = {
14708
14771
  ...contentBlock,
14709
- text
14772
+ thinking: text
14710
14773
  };
14711
14774
  return;
14712
14775
  }
14713
- if (text) {
14714
- fallbackTextDeltas.push(text);
14715
- }
14716
- delete contentBlocks[index];
14717
14776
  }
14718
14777
  function isTextContentBlock(contentBlock) {
14719
14778
  return contentBlock.type === "text";
@@ -14721,6 +14780,9 @@ function isTextContentBlock(contentBlock) {
14721
14780
  function isToolUseContentBlock(contentBlock) {
14722
14781
  return contentBlock.type === "tool_use";
14723
14782
  }
14783
+ function isThinkingContentBlock(contentBlock) {
14784
+ return contentBlock.type === "thinking";
14785
+ }
14724
14786
  function isAnthropicBase64ContentBlock(input) {
14725
14787
  return (input.type === "image" || input.type === "document") && isObject(input.source) && input.source.type === "base64";
14726
14788
  }
@@ -14775,15 +14837,6 @@ function coalesceInput(messages, system) {
14775
14837
  }
14776
14838
  return input;
14777
14839
  }
14778
- function filterFrom(obj, fieldsToRemove) {
14779
- const result = {};
14780
- for (const [key, value] of Object.entries(obj)) {
14781
- if (!fieldsToRemove.includes(key)) {
14782
- result[key] = value;
14783
- }
14784
- }
14785
- return result;
14786
- }
14787
14840
 
14788
14841
  // src/wrappers/ai-sdk/normalize-logged-output.ts
14789
14842
  var REMOVE_NORMALIZED_VALUE = Symbol("braintrust.ai-sdk.remove-normalized");
@@ -14897,10 +14950,6 @@ var aiSDKChannels = defineChannels("ai", {
14897
14950
  channelName: "streamText",
14898
14951
  kind: "async"
14899
14952
  }),
14900
- streamTextSync: channel({
14901
- channelName: "streamText.sync",
14902
- kind: "sync-stream"
14903
- }),
14904
14953
  generateObject: channel({
14905
14954
  channelName: "generateObject",
14906
14955
  kind: "async"
@@ -14909,10 +14958,6 @@ var aiSDKChannels = defineChannels("ai", {
14909
14958
  channelName: "streamObject",
14910
14959
  kind: "async"
14911
14960
  }),
14912
- streamObjectSync: channel({
14913
- channelName: "streamObject.sync",
14914
- kind: "sync-stream"
14915
- }),
14916
14961
  agentGenerate: channel({
14917
14962
  channelName: "Agent.generate",
14918
14963
  kind: "async"
@@ -14948,6 +14993,9 @@ var DEFAULT_DENY_OUTPUT_PATHS = [
14948
14993
  ];
14949
14994
  var AUTO_PATCHED_MODEL = Symbol.for("braintrust.ai-sdk.auto-patched-model");
14950
14995
  var AUTO_PATCHED_TOOL = Symbol.for("braintrust.ai-sdk.auto-patched-tool");
14996
+ var RUNTIME_DENY_OUTPUT_PATHS = Symbol.for(
14997
+ "braintrust.ai-sdk.deny-output-paths"
14998
+ );
14951
14999
  var AISDKPlugin = class extends BasePlugin {
14952
15000
  config;
14953
15001
  constructor(config3 = {}) {
@@ -14969,7 +15017,10 @@ var AISDKPlugin = class extends BasePlugin {
14969
15017
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
14970
15018
  extractOutput: (result, endEvent) => {
14971
15019
  finalizeAISDKChildTracing(endEvent);
14972
- return processAISDKOutput(result, denyOutputPaths);
15020
+ return processAISDKOutput(
15021
+ result,
15022
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
15023
+ );
14973
15024
  },
14974
15025
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
14975
15026
  aggregateChunks: aggregateAISDKChunks
@@ -14980,25 +15031,14 @@ var AISDKPlugin = class extends BasePlugin {
14980
15031
  name: "streamText",
14981
15032
  type: "llm" /* LLM */,
14982
15033
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
14983
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
15034
+ extractOutput: (result, endEvent) => processAISDKOutput(
15035
+ result,
15036
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
15037
+ ),
14984
15038
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
14985
15039
  aggregateChunks: aggregateAISDKChunks,
14986
15040
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
14987
- denyOutputPaths,
14988
- endEvent,
14989
- result,
14990
- span,
14991
- startTime
14992
- })
14993
- })
14994
- );
14995
- this.unsubscribers.push(
14996
- traceSyncStreamChannel(aiSDKChannels.streamTextSync, {
14997
- name: "streamText",
14998
- type: "llm" /* LLM */,
14999
- extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
15000
- patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
15001
- denyOutputPaths,
15041
+ defaultDenyOutputPaths: denyOutputPaths,
15002
15042
  endEvent,
15003
15043
  result,
15004
15044
  span,
@@ -15013,7 +15053,10 @@ var AISDKPlugin = class extends BasePlugin {
15013
15053
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
15014
15054
  extractOutput: (result, endEvent) => {
15015
15055
  finalizeAISDKChildTracing(endEvent);
15016
- return processAISDKOutput(result, denyOutputPaths);
15056
+ return processAISDKOutput(
15057
+ result,
15058
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
15059
+ );
15017
15060
  },
15018
15061
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
15019
15062
  aggregateChunks: aggregateAISDKChunks
@@ -15024,25 +15067,14 @@ var AISDKPlugin = class extends BasePlugin {
15024
15067
  name: "streamObject",
15025
15068
  type: "llm" /* LLM */,
15026
15069
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
15027
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
15070
+ extractOutput: (result, endEvent) => processAISDKOutput(
15071
+ result,
15072
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
15073
+ ),
15028
15074
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
15029
15075
  aggregateChunks: aggregateAISDKChunks,
15030
15076
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
15031
- denyOutputPaths,
15032
- endEvent,
15033
- result,
15034
- span,
15035
- startTime
15036
- })
15037
- })
15038
- );
15039
- this.unsubscribers.push(
15040
- traceSyncStreamChannel(aiSDKChannels.streamObjectSync, {
15041
- name: "streamObject",
15042
- type: "llm" /* LLM */,
15043
- extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
15044
- patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
15045
- denyOutputPaths,
15077
+ defaultDenyOutputPaths: denyOutputPaths,
15046
15078
  endEvent,
15047
15079
  result,
15048
15080
  span,
@@ -15057,7 +15089,10 @@ var AISDKPlugin = class extends BasePlugin {
15057
15089
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
15058
15090
  extractOutput: (result, endEvent) => {
15059
15091
  finalizeAISDKChildTracing(endEvent);
15060
- return processAISDKOutput(result, denyOutputPaths);
15092
+ return processAISDKOutput(
15093
+ result,
15094
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
15095
+ );
15061
15096
  },
15062
15097
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
15063
15098
  aggregateChunks: aggregateAISDKChunks
@@ -15068,11 +15103,14 @@ var AISDKPlugin = class extends BasePlugin {
15068
15103
  name: "Agent.stream",
15069
15104
  type: "llm" /* LLM */,
15070
15105
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
15071
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
15106
+ extractOutput: (result, endEvent) => processAISDKOutput(
15107
+ result,
15108
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
15109
+ ),
15072
15110
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
15073
15111
  aggregateChunks: aggregateAISDKChunks,
15074
15112
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
15075
- denyOutputPaths,
15113
+ defaultDenyOutputPaths: denyOutputPaths,
15076
15114
  endEvent,
15077
15115
  result,
15078
15116
  span,
@@ -15087,7 +15125,10 @@ var AISDKPlugin = class extends BasePlugin {
15087
15125
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
15088
15126
  extractOutput: (result, endEvent) => {
15089
15127
  finalizeAISDKChildTracing(endEvent);
15090
- return processAISDKOutput(result, denyOutputPaths);
15128
+ return processAISDKOutput(
15129
+ result,
15130
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
15131
+ );
15091
15132
  },
15092
15133
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
15093
15134
  aggregateChunks: aggregateAISDKChunks
@@ -15098,11 +15139,14 @@ var AISDKPlugin = class extends BasePlugin {
15098
15139
  name: "ToolLoopAgent.stream",
15099
15140
  type: "llm" /* LLM */,
15100
15141
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
15101
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
15142
+ extractOutput: (result, endEvent) => processAISDKOutput(
15143
+ result,
15144
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
15145
+ ),
15102
15146
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
15103
15147
  aggregateChunks: aggregateAISDKChunks,
15104
15148
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
15105
- denyOutputPaths,
15149
+ defaultDenyOutputPaths: denyOutputPaths,
15106
15150
  endEvent,
15107
15151
  result,
15108
15152
  span,
@@ -15112,85 +15156,383 @@ var AISDKPlugin = class extends BasePlugin {
15112
15156
  );
15113
15157
  }
15114
15158
  };
15115
- function processAISDKInput(params) {
15116
- if (!params) return params;
15117
- const input = processInputAttachments(params);
15118
- if (!input || typeof input !== "object" || Array.isArray(input)) {
15119
- return input;
15159
+ function resolveDenyOutputPaths(event, defaultDenyOutputPaths) {
15160
+ if (Array.isArray(event?.denyOutputPaths)) {
15161
+ return event.denyOutputPaths;
15120
15162
  }
15121
- const { tools: _tools, ...rest } = input;
15122
- return rest;
15123
- }
15124
- function prepareAISDKInput(params, event, span, denyOutputPaths) {
15125
- const input = processAISDKInput(params);
15126
- const metadata = extractMetadataFromParams(params, event.self);
15127
- const childTracing = prepareAISDKChildTracing(
15128
- params,
15129
- event.self,
15130
- span,
15131
- denyOutputPaths
15132
- );
15133
- event.__braintrust_ai_sdk_model_wrapped = childTracing.modelWrapped;
15134
- if (childTracing.cleanup) {
15135
- event.__braintrust_ai_sdk_cleanup = childTracing.cleanup;
15163
+ const firstArgument = event?.arguments && event.arguments.length > 0 ? event.arguments[0] : void 0;
15164
+ if (!firstArgument || typeof firstArgument !== "object") {
15165
+ return defaultDenyOutputPaths;
15136
15166
  }
15137
- return {
15138
- input,
15139
- metadata
15140
- };
15141
- }
15142
- function extractTopLevelAISDKMetrics(result, event, startTime) {
15143
- const metrics = hasModelChildTracing(event) ? {} : extractTokenMetrics(result);
15144
- if (startTime) {
15145
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
15167
+ const runtimeDenyOutputPaths = firstArgument[RUNTIME_DENY_OUTPUT_PATHS];
15168
+ if (Array.isArray(runtimeDenyOutputPaths) && runtimeDenyOutputPaths.every((path8) => typeof path8 === "string")) {
15169
+ return runtimeDenyOutputPaths;
15146
15170
  }
15147
- return metrics;
15171
+ return defaultDenyOutputPaths;
15148
15172
  }
15149
- function hasModelChildTracing(event) {
15150
- return event?.__braintrust_ai_sdk_model_wrapped === true;
15151
- }
15152
- function extractMetadataFromParams(params, self) {
15153
- const metadata = {
15154
- braintrust: {
15155
- integration_name: "ai-sdk",
15156
- sdk_language: "typescript"
15173
+ var isZodSchema2 = (value) => {
15174
+ return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
15175
+ };
15176
+ var serializeZodSchema2 = (schema) => {
15177
+ try {
15178
+ return zodToJsonSchema(schema);
15179
+ } catch {
15180
+ return {
15181
+ type: "object",
15182
+ description: "Zod schema (conversion failed)"
15183
+ };
15184
+ }
15185
+ };
15186
+ var isOutputObject = (value) => {
15187
+ if (value == null || typeof value !== "object") {
15188
+ return false;
15189
+ }
15190
+ const output = value;
15191
+ if (!("responseFormat" in output)) {
15192
+ return false;
15193
+ }
15194
+ if (output.type === "object" || output.type === "text") {
15195
+ return true;
15196
+ }
15197
+ if (typeof output.responseFormat === "function" || typeof output.responseFormat === "object") {
15198
+ return true;
15199
+ }
15200
+ return false;
15201
+ };
15202
+ var serializeOutputObject = (output, model) => {
15203
+ try {
15204
+ const result = {
15205
+ response_format: null
15206
+ };
15207
+ if (output.type) {
15208
+ result.type = output.type;
15209
+ }
15210
+ let responseFormat;
15211
+ if (typeof output.responseFormat === "function") {
15212
+ const mockModelForSchema = {
15213
+ supportsStructuredOutputs: true,
15214
+ ...model && typeof model === "object" ? model : {}
15215
+ };
15216
+ responseFormat = output.responseFormat({ model: mockModelForSchema });
15217
+ } else if (output.responseFormat != null && typeof output.responseFormat === "object") {
15218
+ responseFormat = output.responseFormat;
15219
+ }
15220
+ if (responseFormat) {
15221
+ if (typeof responseFormat.then === "function") {
15222
+ result.response_format = Promise.resolve(responseFormat).then(
15223
+ (resolved) => {
15224
+ if (resolved.schema && isZodSchema2(resolved.schema)) {
15225
+ return {
15226
+ ...resolved,
15227
+ schema: serializeZodSchema2(resolved.schema)
15228
+ };
15229
+ }
15230
+ return resolved;
15231
+ }
15232
+ );
15233
+ } else {
15234
+ const syncResponseFormat = responseFormat;
15235
+ if (syncResponseFormat.schema && isZodSchema2(syncResponseFormat.schema)) {
15236
+ responseFormat = {
15237
+ ...syncResponseFormat,
15238
+ schema: serializeZodSchema2(syncResponseFormat.schema)
15239
+ };
15240
+ }
15241
+ result.response_format = responseFormat;
15242
+ }
15157
15243
  }
15158
- };
15159
- 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;
15160
- const { model, provider } = serializeModelWithProvider(
15161
- params.model ?? agentModel
15162
- );
15163
- if (model) {
15164
- metadata.model = model;
15244
+ return result;
15245
+ } catch {
15246
+ return {
15247
+ response_format: null
15248
+ };
15165
15249
  }
15166
- if (provider) {
15167
- metadata.provider = provider;
15250
+ };
15251
+ var processInputAttachmentsSync = (input) => {
15252
+ if (!input) return { input };
15253
+ const processed = { ...input };
15254
+ if (input.messages && Array.isArray(input.messages)) {
15255
+ processed.messages = input.messages.map(processMessage);
15256
+ }
15257
+ if (input.prompt && typeof input.prompt === "object") {
15258
+ if (Array.isArray(input.prompt)) {
15259
+ processed.prompt = input.prompt.map(processMessage);
15260
+ } else {
15261
+ processed.prompt = processPromptContent(input.prompt);
15262
+ }
15168
15263
  }
15169
- const tools = serializeAISDKToolsForLogging(params.tools);
15170
- if (tools) {
15171
- metadata.tools = tools;
15264
+ if (input.schema && isZodSchema2(input.schema)) {
15265
+ processed.schema = serializeZodSchema2(input.schema);
15172
15266
  }
15173
- return metadata;
15174
- }
15175
- function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
15176
- const cleanup = [];
15177
- const patchedModels = /* @__PURE__ */ new WeakSet();
15178
- const patchedTools = /* @__PURE__ */ new WeakSet();
15179
- let modelWrapped = false;
15180
- const patchModel = (model) => {
15181
- const resolvedModel = resolveAISDKModel(model);
15182
- if (!resolvedModel || typeof resolvedModel !== "object" || typeof resolvedModel.doGenerate !== "function" || patchedModels.has(resolvedModel) || resolvedModel[AUTO_PATCHED_MODEL]) {
15183
- return;
15267
+ if (input.callOptionsSchema && isZodSchema2(input.callOptionsSchema)) {
15268
+ processed.callOptionsSchema = serializeZodSchema2(input.callOptionsSchema);
15269
+ }
15270
+ if (input.tools) {
15271
+ processed.tools = serializeAISDKToolsForLogging(input.tools);
15272
+ }
15273
+ let outputPromise;
15274
+ if (input.output && isOutputObject(input.output)) {
15275
+ const serialized = serializeOutputObject(input.output, input.model);
15276
+ if (serialized.response_format && typeof serialized.response_format.then === "function") {
15277
+ processed.output = { ...serialized, response_format: {} };
15278
+ outputPromise = serialized.response_format.then(
15279
+ (resolvedFormat) => ({
15280
+ output: { ...serialized, response_format: resolvedFormat }
15281
+ })
15282
+ );
15283
+ } else {
15284
+ processed.output = serialized;
15184
15285
  }
15185
- patchedModels.add(resolvedModel);
15186
- resolvedModel[AUTO_PATCHED_MODEL] = true;
15187
- modelWrapped = true;
15188
- const originalDoGenerate = resolvedModel.doGenerate;
15189
- const originalDoStream = resolvedModel.doStream;
15190
- const baseMetadata = buildAISDKChildMetadata(resolvedModel);
15191
- resolvedModel.doGenerate = async function doGeneratePatched(options) {
15192
- return parentSpan.traced(
15193
- async (span) => {
15286
+ }
15287
+ if ("prepareCall" in processed && typeof processed.prepareCall === "function") {
15288
+ processed.prepareCall = "[Function]";
15289
+ }
15290
+ return { input: processed, outputPromise };
15291
+ };
15292
+ var processMessage = (message) => {
15293
+ if (!message || typeof message !== "object") return message;
15294
+ if (Array.isArray(message.content)) {
15295
+ return {
15296
+ ...message,
15297
+ content: message.content.map(processContentPart)
15298
+ };
15299
+ }
15300
+ if (typeof message.content === "object" && message.content !== null) {
15301
+ return {
15302
+ ...message,
15303
+ content: processContentPart(message.content)
15304
+ };
15305
+ }
15306
+ return message;
15307
+ };
15308
+ var processPromptContent = (prompt) => {
15309
+ if (Array.isArray(prompt)) {
15310
+ return prompt.map(processContentPart);
15311
+ }
15312
+ if (prompt.content) {
15313
+ if (Array.isArray(prompt.content)) {
15314
+ return {
15315
+ ...prompt,
15316
+ content: prompt.content.map(processContentPart)
15317
+ };
15318
+ } else if (typeof prompt.content === "object") {
15319
+ return {
15320
+ ...prompt,
15321
+ content: processContentPart(prompt.content)
15322
+ };
15323
+ }
15324
+ }
15325
+ return prompt;
15326
+ };
15327
+ var processContentPart = (part) => {
15328
+ if (!part || typeof part !== "object") return part;
15329
+ try {
15330
+ if (part.type === "image" && part.image) {
15331
+ const imageAttachment = convertImageToAttachment(
15332
+ part.image,
15333
+ part.mimeType || part.mediaType
15334
+ );
15335
+ if (imageAttachment) {
15336
+ return {
15337
+ ...part,
15338
+ image: imageAttachment
15339
+ };
15340
+ }
15341
+ }
15342
+ if (part.type === "file" && part.data && (part.mimeType || part.mediaType)) {
15343
+ const fileAttachment = convertDataToAttachment(
15344
+ part.data,
15345
+ part.mimeType || part.mediaType,
15346
+ part.name || part.filename
15347
+ );
15348
+ if (fileAttachment) {
15349
+ return {
15350
+ ...part,
15351
+ data: fileAttachment
15352
+ };
15353
+ }
15354
+ }
15355
+ if (part.type === "image_url" && part.image_url) {
15356
+ if (typeof part.image_url === "object" && part.image_url.url) {
15357
+ const imageAttachment = convertImageToAttachment(part.image_url.url);
15358
+ if (imageAttachment) {
15359
+ return {
15360
+ ...part,
15361
+ image_url: {
15362
+ ...part.image_url,
15363
+ url: imageAttachment
15364
+ }
15365
+ };
15366
+ }
15367
+ }
15368
+ }
15369
+ } catch (error2) {
15370
+ console.warn("Error processing content part:", error2);
15371
+ }
15372
+ return part;
15373
+ };
15374
+ var convertImageToAttachment = (image, explicitMimeType) => {
15375
+ try {
15376
+ if (typeof image === "string" && image.startsWith("data:")) {
15377
+ const [mimeTypeSection, base64Data] = image.split(",");
15378
+ const mimeType = mimeTypeSection.match(/data:(.*?);/)?.[1];
15379
+ if (mimeType && base64Data) {
15380
+ const blob = convertDataToBlob(base64Data, mimeType);
15381
+ if (blob) {
15382
+ return new Attachment({
15383
+ data: blob,
15384
+ filename: `image.${getExtensionFromMediaType(mimeType)}`,
15385
+ contentType: mimeType
15386
+ });
15387
+ }
15388
+ }
15389
+ }
15390
+ if (explicitMimeType) {
15391
+ if (image instanceof Uint8Array) {
15392
+ return new Attachment({
15393
+ data: new Blob([image], { type: explicitMimeType }),
15394
+ filename: `image.${getExtensionFromMediaType(explicitMimeType)}`,
15395
+ contentType: explicitMimeType
15396
+ });
15397
+ }
15398
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer(image)) {
15399
+ return new Attachment({
15400
+ data: new Blob([image], { type: explicitMimeType }),
15401
+ filename: `image.${getExtensionFromMediaType(explicitMimeType)}`,
15402
+ contentType: explicitMimeType
15403
+ });
15404
+ }
15405
+ }
15406
+ if (image instanceof Blob && image.type) {
15407
+ return new Attachment({
15408
+ data: image,
15409
+ filename: `image.${getExtensionFromMediaType(image.type)}`,
15410
+ contentType: image.type
15411
+ });
15412
+ }
15413
+ if (image instanceof Attachment) {
15414
+ return image;
15415
+ }
15416
+ } catch (error2) {
15417
+ console.warn("Error converting image to attachment:", error2);
15418
+ }
15419
+ return null;
15420
+ };
15421
+ var convertDataToAttachment = (data, mimeType, filename) => {
15422
+ if (!mimeType) return null;
15423
+ try {
15424
+ let blob = null;
15425
+ if (typeof data === "string" && data.startsWith("data:")) {
15426
+ const [, base64Data] = data.split(",");
15427
+ if (base64Data) {
15428
+ blob = convertDataToBlob(base64Data, mimeType);
15429
+ }
15430
+ } else if (typeof data === "string" && data.length > 0) {
15431
+ blob = convertDataToBlob(data, mimeType);
15432
+ } else if (data instanceof Uint8Array) {
15433
+ blob = new Blob([data], { type: mimeType });
15434
+ } else if (typeof Buffer !== "undefined" && Buffer.isBuffer(data)) {
15435
+ blob = new Blob([data], { type: mimeType });
15436
+ } else if (data instanceof Blob) {
15437
+ blob = data;
15438
+ }
15439
+ if (blob) {
15440
+ return new Attachment({
15441
+ data: blob,
15442
+ filename: filename || `file.${getExtensionFromMediaType(mimeType)}`,
15443
+ contentType: mimeType
15444
+ });
15445
+ }
15446
+ } catch (error2) {
15447
+ console.warn("Error converting data to attachment:", error2);
15448
+ }
15449
+ return null;
15450
+ };
15451
+ function processAISDKInput(params) {
15452
+ return processInputAttachmentsSync(params);
15453
+ }
15454
+ function prepareAISDKInput(params, event, span, defaultDenyOutputPaths) {
15455
+ const { input, outputPromise } = processAISDKInput(params);
15456
+ if (outputPromise && input && typeof input === "object") {
15457
+ outputPromise.then((resolvedData) => {
15458
+ span.log({
15459
+ input: {
15460
+ ...input,
15461
+ ...resolvedData
15462
+ }
15463
+ });
15464
+ }).catch(() => {
15465
+ });
15466
+ }
15467
+ const metadata = extractMetadataFromParams(params, event.self);
15468
+ const childTracing = prepareAISDKChildTracing(
15469
+ params,
15470
+ event.self,
15471
+ span,
15472
+ defaultDenyOutputPaths,
15473
+ event.aiSDK
15474
+ );
15475
+ event.modelWrapped = childTracing.modelWrapped;
15476
+ if (childTracing.cleanup) {
15477
+ event.__braintrust_ai_sdk_cleanup = childTracing.cleanup;
15478
+ }
15479
+ return {
15480
+ input,
15481
+ metadata
15482
+ };
15483
+ }
15484
+ function extractTopLevelAISDKMetrics(result, event, startTime) {
15485
+ const metrics = hasModelChildTracing(event) ? {} : extractTokenMetrics(result);
15486
+ if (startTime) {
15487
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
15488
+ }
15489
+ return metrics;
15490
+ }
15491
+ function hasModelChildTracing(event) {
15492
+ return event?.modelWrapped === true || event?.__braintrust_ai_sdk_model_wrapped === true;
15493
+ }
15494
+ function extractMetadataFromParams(params, self) {
15495
+ const metadata = {
15496
+ braintrust: {
15497
+ integration_name: "ai-sdk",
15498
+ sdk_language: "typescript"
15499
+ }
15500
+ };
15501
+ 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;
15502
+ const { model, provider } = serializeModelWithProvider(
15503
+ params.model ?? agentModel
15504
+ );
15505
+ if (model) {
15506
+ metadata.model = model;
15507
+ }
15508
+ if (provider) {
15509
+ metadata.provider = provider;
15510
+ }
15511
+ const tools = serializeAISDKToolsForLogging(params.tools);
15512
+ if (tools) {
15513
+ metadata.tools = tools;
15514
+ }
15515
+ return metadata;
15516
+ }
15517
+ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiSDK) {
15518
+ const cleanup = [];
15519
+ const patchedModels = /* @__PURE__ */ new WeakSet();
15520
+ const patchedTools = /* @__PURE__ */ new WeakSet();
15521
+ let modelWrapped = false;
15522
+ const patchModel = (model) => {
15523
+ const resolvedModel = resolveAISDKModel(model, aiSDK);
15524
+ if (!resolvedModel || typeof resolvedModel !== "object" || typeof resolvedModel.doGenerate !== "function" || patchedModels.has(resolvedModel) || resolvedModel[AUTO_PATCHED_MODEL]) {
15525
+ return resolvedModel;
15526
+ }
15527
+ patchedModels.add(resolvedModel);
15528
+ resolvedModel[AUTO_PATCHED_MODEL] = true;
15529
+ modelWrapped = true;
15530
+ const originalDoGenerate = resolvedModel.doGenerate;
15531
+ const originalDoStream = resolvedModel.doStream;
15532
+ const baseMetadata = buildAISDKChildMetadata(resolvedModel);
15533
+ resolvedModel.doGenerate = async function doGeneratePatched(options) {
15534
+ return parentSpan.traced(
15535
+ async (span) => {
15194
15536
  const result = await Reflect.apply(
15195
15537
  originalDoGenerate,
15196
15538
  resolvedModel,
@@ -15209,7 +15551,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
15209
15551
  type: "llm" /* LLM */
15210
15552
  },
15211
15553
  event: {
15212
- input: processAISDKInput(options),
15554
+ input: processAISDKInput(options).input,
15213
15555
  metadata: baseMetadata
15214
15556
  }
15215
15557
  }
@@ -15223,7 +15565,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
15223
15565
  type: "llm" /* LLM */
15224
15566
  },
15225
15567
  event: {
15226
- input: processAISDKInput(options),
15568
+ input: processAISDKInput(options).input,
15227
15569
  metadata: baseMetadata
15228
15570
  }
15229
15571
  });
@@ -15231,6 +15573,8 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
15231
15573
  span,
15232
15574
  () => Reflect.apply(originalDoStream, resolvedModel, [options])
15233
15575
  );
15576
+ const streamStartTime = getCurrentUnixTimestamp();
15577
+ let firstChunkTime;
15234
15578
  const output = {};
15235
15579
  let text = "";
15236
15580
  let reasoning = "";
@@ -15238,6 +15582,9 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
15238
15582
  let object = void 0;
15239
15583
  const transformStream = new TransformStream({
15240
15584
  transform(chunk, controller) {
15585
+ if (firstChunkTime === void 0) {
15586
+ firstChunkTime = getCurrentUnixTimestamp();
15587
+ }
15241
15588
  switch (chunk.type) {
15242
15589
  case "text-delta":
15243
15590
  text += extractTextDelta(chunk);
@@ -15278,12 +15625,19 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
15278
15625
  if (object !== void 0) {
15279
15626
  output.object = object;
15280
15627
  }
15628
+ const metrics = extractTokenMetrics(output);
15629
+ if (firstChunkTime !== void 0) {
15630
+ metrics.time_to_first_token = Math.max(
15631
+ firstChunkTime - streamStartTime,
15632
+ 1e-6
15633
+ );
15634
+ }
15281
15635
  span.log({
15282
15636
  output: processAISDKOutput(
15283
15637
  output,
15284
15638
  denyOutputPaths
15285
15639
  ),
15286
- metrics: extractTokenMetrics(output),
15640
+ metrics,
15287
15641
  ...buildResolvedMetadataPayload(output)
15288
15642
  });
15289
15643
  span.end();
@@ -15305,6 +15659,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
15305
15659
  }
15306
15660
  delete resolvedModel[AUTO_PATCHED_MODEL];
15307
15661
  });
15662
+ return resolvedModel;
15308
15663
  };
15309
15664
  const patchTool = (tool, name) => {
15310
15665
  if (tool == null || typeof tool !== "object" || !("execute" in tool) || typeof tool.execute !== "function" || patchedTools.has(tool) || tool[AUTO_PATCHED_TOOL]) {
@@ -15377,17 +15732,26 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
15377
15732
  }
15378
15733
  };
15379
15734
  if (params && typeof params === "object") {
15380
- patchModel(params.model);
15735
+ const patchedParamModel = patchModel(params.model);
15736
+ if (typeof params.model === "string" && patchedParamModel && typeof patchedParamModel === "object") {
15737
+ params.model = patchedParamModel;
15738
+ }
15381
15739
  patchTools(params.tools);
15382
15740
  }
15383
15741
  if (self && typeof self === "object") {
15384
15742
  const selfRecord = self;
15385
15743
  if (selfRecord.model !== void 0) {
15386
- patchModel(selfRecord.model);
15744
+ const patchedSelfModel = patchModel(selfRecord.model);
15745
+ if (typeof selfRecord.model === "string" && patchedSelfModel && typeof patchedSelfModel === "object") {
15746
+ selfRecord.model = patchedSelfModel;
15747
+ }
15387
15748
  }
15388
15749
  if (selfRecord.settings && typeof selfRecord.settings === "object") {
15389
15750
  if (selfRecord.settings.model !== void 0) {
15390
- patchModel(selfRecord.settings.model);
15751
+ const patchedSettingsModel = patchModel(selfRecord.settings.model);
15752
+ if (typeof selfRecord.settings.model === "string" && patchedSettingsModel && typeof patchedSettingsModel === "object") {
15753
+ selfRecord.settings.model = patchedSettingsModel;
15754
+ }
15391
15755
  }
15392
15756
  if (selfRecord.settings.tools !== void 0) {
15393
15757
  patchTools(selfRecord.settings.tools);
@@ -15411,63 +15775,173 @@ function finalizeAISDKChildTracing(event) {
15411
15775
  }
15412
15776
  }
15413
15777
  function patchAISDKStreamingResult(args) {
15414
- const { denyOutputPaths, endEvent, result, span, startTime } = args;
15778
+ const { defaultDenyOutputPaths, endEvent, result, span, startTime } = args;
15415
15779
  if (!result || typeof result !== "object") {
15416
15780
  return false;
15417
15781
  }
15418
15782
  const resultRecord = result;
15419
- if (!isReadableStreamLike(resultRecord.baseStream)) {
15783
+ attachKnownResultPromiseHandlers(resultRecord);
15784
+ if (isReadableStreamLike(resultRecord.baseStream)) {
15785
+ let firstChunkTime2;
15786
+ const wrappedBaseStream = resultRecord.baseStream.pipeThrough(
15787
+ new TransformStream({
15788
+ transform(chunk, controller) {
15789
+ if (firstChunkTime2 === void 0) {
15790
+ firstChunkTime2 = getCurrentUnixTimestamp();
15791
+ }
15792
+ controller.enqueue(chunk);
15793
+ },
15794
+ async flush() {
15795
+ const metrics = extractTopLevelAISDKMetrics(result, endEvent);
15796
+ if (metrics.time_to_first_token === void 0 && firstChunkTime2 !== void 0) {
15797
+ metrics.time_to_first_token = firstChunkTime2 - startTime;
15798
+ }
15799
+ const output = await processAISDKStreamingOutput(
15800
+ result,
15801
+ resolveDenyOutputPaths(endEvent, defaultDenyOutputPaths)
15802
+ );
15803
+ const metadata = buildResolvedMetadataPayload(result).metadata;
15804
+ span.log({
15805
+ output,
15806
+ ...metadata ? { metadata } : {},
15807
+ metrics
15808
+ });
15809
+ finalizeAISDKChildTracing(endEvent);
15810
+ span.end();
15811
+ }
15812
+ })
15813
+ );
15814
+ Object.defineProperty(resultRecord, "baseStream", {
15815
+ configurable: true,
15816
+ enumerable: true,
15817
+ value: wrappedBaseStream,
15818
+ writable: true
15819
+ });
15820
+ return true;
15821
+ }
15822
+ const streamField = findAsyncIterableField(resultRecord, [
15823
+ "partialObjectStream",
15824
+ "textStream",
15825
+ "fullStream",
15826
+ "stream"
15827
+ ]);
15828
+ if (!streamField) {
15420
15829
  return false;
15421
15830
  }
15422
15831
  let firstChunkTime;
15423
- const wrappedBaseStream = resultRecord.baseStream.pipeThrough(
15424
- new TransformStream({
15425
- transform(chunk, controller) {
15426
- if (firstChunkTime === void 0) {
15427
- firstChunkTime = getCurrentUnixTimestamp();
15428
- }
15429
- controller.enqueue(chunk);
15430
- },
15431
- async flush() {
15432
- const metrics = extractTopLevelAISDKMetrics(result, endEvent);
15433
- if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
15434
- metrics.time_to_first_token = firstChunkTime - startTime;
15435
- }
15436
- const output = await processAISDKStreamingOutput(
15437
- result,
15438
- denyOutputPaths
15439
- );
15440
- const metadata = buildResolvedMetadataPayload(result).metadata;
15441
- span.log({
15442
- output,
15443
- ...metadata ? { metadata } : {},
15444
- metrics
15445
- });
15446
- finalizeAISDKChildTracing(endEvent);
15447
- span.end();
15832
+ const wrappedStream = createPatchedAsyncIterable(streamField.stream, {
15833
+ onChunk: () => {
15834
+ if (firstChunkTime === void 0) {
15835
+ firstChunkTime = getCurrentUnixTimestamp();
15448
15836
  }
15449
- })
15450
- );
15451
- Object.defineProperty(resultRecord, "baseStream", {
15837
+ },
15838
+ onComplete: async () => {
15839
+ const metrics = extractTopLevelAISDKMetrics(result, endEvent);
15840
+ if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
15841
+ metrics.time_to_first_token = firstChunkTime - startTime;
15842
+ }
15843
+ const output = await processAISDKStreamingOutput(
15844
+ result,
15845
+ resolveDenyOutputPaths(endEvent, defaultDenyOutputPaths)
15846
+ );
15847
+ const metadata = buildResolvedMetadataPayload(result).metadata;
15848
+ span.log({
15849
+ output,
15850
+ ...metadata ? { metadata } : {},
15851
+ metrics
15852
+ });
15853
+ finalizeAISDKChildTracing(endEvent);
15854
+ span.end();
15855
+ },
15856
+ onError: (error2) => {
15857
+ span.log({
15858
+ error: error2.message
15859
+ });
15860
+ finalizeAISDKChildTracing(endEvent);
15861
+ span.end();
15862
+ }
15863
+ });
15864
+ Object.defineProperty(resultRecord, streamField.field, {
15452
15865
  configurable: true,
15453
15866
  enumerable: true,
15454
- value: wrappedBaseStream,
15867
+ value: wrappedStream,
15455
15868
  writable: true
15456
15869
  });
15457
15870
  return true;
15458
15871
  }
15872
+ function attachKnownResultPromiseHandlers(result) {
15873
+ const promiseLikeFields = [
15874
+ "content",
15875
+ "text",
15876
+ "object",
15877
+ "finishReason",
15878
+ "usage",
15879
+ "totalUsage",
15880
+ "steps"
15881
+ ];
15882
+ for (const field of promiseLikeFields) {
15883
+ try {
15884
+ if (!(field in result)) {
15885
+ continue;
15886
+ }
15887
+ const value = result[field];
15888
+ if (isPromiseLike(value)) {
15889
+ void Promise.resolve(value).catch(() => {
15890
+ });
15891
+ }
15892
+ } catch {
15893
+ }
15894
+ }
15895
+ }
15459
15896
  function isReadableStreamLike(value) {
15460
15897
  return value != null && typeof value === "object" && typeof value.pipeThrough === "function";
15461
15898
  }
15899
+ function isAsyncIterableLike(value) {
15900
+ return value != null && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function";
15901
+ }
15902
+ function findAsyncIterableField(result, candidateFields) {
15903
+ for (const field of candidateFields) {
15904
+ try {
15905
+ const stream = result[field];
15906
+ if (isAsyncIterableLike(stream)) {
15907
+ return { field, stream };
15908
+ }
15909
+ } catch {
15910
+ }
15911
+ }
15912
+ return null;
15913
+ }
15914
+ function createPatchedAsyncIterable(stream, hooks) {
15915
+ return {
15916
+ async *[Symbol.asyncIterator]() {
15917
+ try {
15918
+ for await (const chunk of stream) {
15919
+ hooks.onChunk(chunk);
15920
+ yield chunk;
15921
+ }
15922
+ await hooks.onComplete();
15923
+ } catch (error2) {
15924
+ hooks.onError(
15925
+ error2 instanceof Error ? error2 : new Error(String(error2))
15926
+ );
15927
+ throw error2;
15928
+ }
15929
+ }
15930
+ };
15931
+ }
15462
15932
  async function processAISDKStreamingOutput(result, denyOutputPaths) {
15463
15933
  const output = processAISDKOutput(result, denyOutputPaths);
15464
15934
  if (!output || typeof output !== "object") {
15465
15935
  return output;
15466
15936
  }
15467
15937
  const outputRecord = output;
15938
+ const isObjectStreamingResult = result != null && typeof result === "object" && "partialObjectStream" in result;
15468
15939
  try {
15469
- if ("text" in result && typeof result.text === "string") {
15470
- outputRecord.text = result.text;
15940
+ if (!isObjectStreamingResult && "text" in result) {
15941
+ const resolvedText = await Promise.resolve(result.text);
15942
+ if (typeof resolvedText === "string") {
15943
+ outputRecord.text = resolvedText;
15944
+ }
15471
15945
  }
15472
15946
  } catch {
15473
15947
  }
@@ -15480,6 +15954,15 @@ async function processAISDKStreamingOutput(result, denyOutputPaths) {
15480
15954
  }
15481
15955
  } catch {
15482
15956
  }
15957
+ try {
15958
+ if ("finishReason" in result) {
15959
+ const resolvedFinishReason = await Promise.resolve(result.finishReason);
15960
+ if (resolvedFinishReason !== void 0) {
15961
+ outputRecord.finishReason = resolvedFinishReason;
15962
+ }
15963
+ }
15964
+ } catch {
15965
+ }
15483
15966
  return outputRecord;
15484
15967
  }
15485
15968
  function buildAISDKChildMetadata(model) {
@@ -15502,16 +15985,25 @@ function buildResolvedMetadataPayload(result) {
15502
15985
  if (gatewayInfo?.model) {
15503
15986
  metadata.model = gatewayInfo.model;
15504
15987
  }
15505
- if (result.finishReason !== void 0) {
15506
- metadata.finish_reason = result.finishReason;
15988
+ let finishReason;
15989
+ try {
15990
+ finishReason = result.finishReason;
15991
+ } catch {
15992
+ finishReason = void 0;
15993
+ }
15994
+ if (isPromiseLike(finishReason)) {
15995
+ void Promise.resolve(finishReason).catch(() => {
15996
+ });
15997
+ } else if (finishReason !== void 0) {
15998
+ metadata.finish_reason = finishReason;
15507
15999
  }
15508
16000
  return Object.keys(metadata).length > 0 ? { metadata } : {};
15509
16001
  }
15510
- function resolveAISDKModel(model) {
16002
+ function resolveAISDKModel(model, aiSDK) {
15511
16003
  if (typeof model !== "string") {
15512
16004
  return model;
15513
16005
  }
15514
- const provider = globalThis.AI_SDK_DEFAULT_PROVIDER ?? null;
16006
+ const provider = globalThis.AI_SDK_DEFAULT_PROVIDER ?? aiSDK?.gateway ?? null;
15515
16007
  if (provider && typeof provider.languageModel === "function") {
15516
16008
  return provider.languageModel(model);
15517
16009
  }
@@ -15534,15 +16026,15 @@ function processAISDKOutput(output, denyOutputPaths) {
15534
16026
  }
15535
16027
  function extractTokenMetrics(result) {
15536
16028
  const metrics = {};
15537
- let usage = result?.totalUsage || result?.usage;
15538
- if (!usage && result) {
15539
- try {
15540
- if ("totalUsage" in result && typeof result.totalUsage !== "function") {
15541
- usage = result.totalUsage;
15542
- } else if ("usage" in result && typeof result.usage !== "function") {
15543
- usage = result.usage;
15544
- }
15545
- } catch {
16029
+ let usage;
16030
+ const totalUsageValue = safeResultFieldRead(result, "totalUsage");
16031
+ if (totalUsageValue !== void 0 && !isPromiseLike(totalUsageValue)) {
16032
+ usage = totalUsageValue;
16033
+ }
16034
+ if (!usage) {
16035
+ const usageValue = safeResultFieldRead(result, "usage");
16036
+ if (usageValue !== void 0 && !isPromiseLike(usageValue)) {
16037
+ usage = usageValue;
15546
16038
  }
15547
16039
  }
15548
16040
  if (!usage) {
@@ -15580,6 +16072,22 @@ function extractTokenMetrics(result) {
15580
16072
  }
15581
16073
  return metrics;
15582
16074
  }
16075
+ function safeResultFieldRead(result, field) {
16076
+ return safeSerializableFieldRead(result, field);
16077
+ }
16078
+ function safeSerializableFieldRead(obj, field) {
16079
+ try {
16080
+ const value = obj?.[field];
16081
+ if (isPromiseLike(value)) {
16082
+ void Promise.resolve(value).catch(() => {
16083
+ });
16084
+ return void 0;
16085
+ }
16086
+ return value;
16087
+ } catch {
16088
+ return void 0;
16089
+ }
16090
+ }
15583
16091
  function aggregateAISDKChunks(chunks, _result, endEvent) {
15584
16092
  const lastChunk = chunks[chunks.length - 1];
15585
16093
  const output = {};
@@ -15588,17 +16096,21 @@ function aggregateAISDKChunks(chunks, _result, endEvent) {
15588
16096
  if (lastChunk) {
15589
16097
  metrics = hasModelChildTracing(endEvent) ? {} : extractTokenMetrics(lastChunk);
15590
16098
  metadata = buildResolvedMetadataPayload(lastChunk).metadata;
15591
- if (lastChunk.text !== void 0) {
15592
- output.text = lastChunk.text;
16099
+ const text = safeSerializableFieldRead(lastChunk, "text");
16100
+ if (text !== void 0) {
16101
+ output.text = text;
15593
16102
  }
15594
- if (lastChunk.object !== void 0) {
15595
- output.object = lastChunk.object;
16103
+ const objectValue = safeSerializableFieldRead(lastChunk, "object");
16104
+ if (objectValue !== void 0) {
16105
+ output.object = objectValue;
15596
16106
  }
15597
- if (lastChunk.finishReason !== void 0) {
15598
- output.finishReason = lastChunk.finishReason;
16107
+ const finishReason = safeSerializableFieldRead(lastChunk, "finishReason");
16108
+ if (finishReason !== void 0) {
16109
+ output.finishReason = finishReason;
15599
16110
  }
15600
- if (lastChunk.toolCalls !== void 0) {
15601
- output.toolCalls = lastChunk.toolCalls;
16111
+ const toolCalls = safeSerializableFieldRead(lastChunk, "toolCalls");
16112
+ if (toolCalls !== void 0) {
16113
+ output.toolCalls = toolCalls;
15602
16114
  }
15603
16115
  }
15604
16116
  finalizeAISDKChildTracing(endEvent);
@@ -15607,6 +16119,7 @@ function aggregateAISDKChunks(chunks, _result, endEvent) {
15607
16119
  function extractGetterValues(obj) {
15608
16120
  const getterValues = {};
15609
16121
  const getterNames = [
16122
+ "content",
15610
16123
  "text",
15611
16124
  "object",
15612
16125
  "finishReason",
@@ -15622,8 +16135,17 @@ function extractGetterValues(obj) {
15622
16135
  ];
15623
16136
  for (const name of getterNames) {
15624
16137
  try {
15625
- if (obj && name in obj && isSerializableOutputValue(obj[name])) {
15626
- getterValues[name] = obj[name];
16138
+ if (!obj || !(name in obj)) {
16139
+ continue;
16140
+ }
16141
+ const value = obj[name];
16142
+ if (isPromiseLike(value)) {
16143
+ void Promise.resolve(value).catch(() => {
16144
+ });
16145
+ continue;
16146
+ }
16147
+ if (isSerializableOutputValue(value)) {
16148
+ getterValues[name] = value;
15627
16149
  }
15628
16150
  } catch {
15629
16151
  }
@@ -15645,6 +16167,11 @@ function extractSerializableOutputFields(output) {
15645
16167
  for (const name of directFieldNames) {
15646
16168
  try {
15647
16169
  const value = output?.[name];
16170
+ if (isPromiseLike(value)) {
16171
+ void Promise.resolve(value).catch(() => {
16172
+ });
16173
+ continue;
16174
+ }
15648
16175
  if (isSerializableOutputValue(value)) {
15649
16176
  serialized[name] = value;
15650
16177
  }
@@ -15656,6 +16183,9 @@ function extractSerializableOutputFields(output) {
15656
16183
  ...extractGetterValues(output)
15657
16184
  };
15658
16185
  }
16186
+ function isPromiseLike(value) {
16187
+ return value != null && typeof value === "object" && typeof value.then === "function";
16188
+ }
15659
16189
  function isSerializableOutputValue(value) {
15660
16190
  if (typeof value === "function") {
15661
16191
  return false;
@@ -15697,8 +16227,9 @@ function parseGatewayModelString(modelString) {
15697
16227
  return { model: modelString };
15698
16228
  }
15699
16229
  function extractGatewayRoutingInfo(result) {
15700
- if (result?.steps && Array.isArray(result.steps) && result.steps.length > 0) {
15701
- const routing2 = result.steps[0]?.providerMetadata?.gateway?.routing;
16230
+ const steps = safeSerializableFieldRead(result, "steps");
16231
+ if (Array.isArray(steps) && steps.length > 0) {
16232
+ const routing2 = steps[0]?.providerMetadata?.gateway?.routing;
15702
16233
  if (routing2) {
15703
16234
  return {
15704
16235
  provider: routing2.resolvedProvider || routing2.finalProvider,
@@ -15706,7 +16237,11 @@ function extractGatewayRoutingInfo(result) {
15706
16237
  };
15707
16238
  }
15708
16239
  }
15709
- const routing = result?.providerMetadata?.gateway?.routing;
16240
+ const providerMetadata = safeSerializableFieldRead(
16241
+ result,
16242
+ "providerMetadata"
16243
+ );
16244
+ const routing = providerMetadata?.gateway?.routing;
15710
16245
  if (routing) {
15711
16246
  return {
15712
16247
  provider: routing.resolvedProvider || routing.finalProvider,
@@ -15716,10 +16251,11 @@ function extractGatewayRoutingInfo(result) {
15716
16251
  return null;
15717
16252
  }
15718
16253
  function extractCostFromResult(result) {
15719
- if (result?.steps && Array.isArray(result.steps) && result.steps.length > 0) {
16254
+ const steps = safeSerializableFieldRead(result, "steps");
16255
+ if (Array.isArray(steps) && steps.length > 0) {
15720
16256
  let totalCost = 0;
15721
16257
  let foundCost = false;
15722
- for (const step of result.steps) {
16258
+ for (const step of steps) {
15723
16259
  const gateway2 = step?.providerMetadata?.gateway;
15724
16260
  const stepCost = parseGatewayCost(gateway2?.cost) || parseGatewayCost(gateway2?.marketCost);
15725
16261
  if (stepCost !== void 0 && stepCost > 0) {
@@ -15731,7 +16267,11 @@ function extractCostFromResult(result) {
15731
16267
  return totalCost;
15732
16268
  }
15733
16269
  }
15734
- const gateway = result?.providerMetadata?.gateway;
16270
+ const providerMetadata = safeSerializableFieldRead(
16271
+ result,
16272
+ "providerMetadata"
16273
+ );
16274
+ const gateway = providerMetadata?.gateway;
15735
16275
  const directCost = parseGatewayCost(gateway?.cost) || parseGatewayCost(gateway?.marketCost);
15736
16276
  if (directCost !== void 0 && directCost > 0) {
15737
16277
  return directCost;
@@ -16432,20 +16972,16 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
16432
16972
  );
16433
16973
  });
16434
16974
  },
16435
- onComplete: () => {
16436
- void state.processing.then(() => finalizeQuerySpan(state)).finally(() => {
16437
- spans.delete(event);
16438
- });
16439
- },
16440
- onError: (error2) => {
16441
- void state.processing.then(() => {
16442
- state.span.log({
16443
- error: error2.message
16444
- });
16445
- }).then(() => finalizeQuerySpan(state)).finally(() => {
16446
- spans.delete(event);
16975
+ onComplete: () => state.processing.then(() => finalizeQuerySpan(state)).finally(() => {
16976
+ spans.delete(event);
16977
+ }),
16978
+ onError: (error2) => state.processing.then(() => {
16979
+ state.span.log({
16980
+ error: error2.message
16447
16981
  });
16448
- }
16982
+ }).then(() => finalizeQuerySpan(state)).finally(() => {
16983
+ spans.delete(event);
16984
+ })
16449
16985
  });
16450
16986
  return;
16451
16987
  }
@@ -16593,12 +17129,14 @@ var GoogleGenAIPlugin = class extends BasePlugin {
16593
17129
  const params = event.arguments[0];
16594
17130
  streamEvent.googleGenAIInput = serializeInput(params);
16595
17131
  streamEvent.googleGenAIMetadata = extractMetadata(params);
17132
+ streamEvent.googleGenAIStartTime = getCurrentUnixTimestamp();
16596
17133
  },
16597
17134
  asyncEnd: (event) => {
16598
17135
  const streamEvent = event;
16599
17136
  patchGoogleGenAIStreamingResult({
16600
17137
  input: streamEvent.googleGenAIInput,
16601
17138
  metadata: streamEvent.googleGenAIMetadata,
17139
+ startTime: streamEvent.googleGenAIStartTime,
16602
17140
  result: streamEvent.result
16603
17141
  });
16604
17142
  },
@@ -16651,7 +17189,7 @@ function logErrorAndEndSpan(states, event) {
16651
17189
  states.delete(event);
16652
17190
  }
16653
17191
  function patchGoogleGenAIStreamingResult(args) {
16654
- const { input, metadata, result } = args;
17192
+ const { input, metadata, result, startTime } = args;
16655
17193
  if (!input || !metadata || !result || typeof result !== "object" || typeof result.next !== "function") {
16656
17194
  return false;
16657
17195
  }
@@ -16659,7 +17197,7 @@ function patchGoogleGenAIStreamingResult(args) {
16659
17197
  let firstTokenTime = null;
16660
17198
  let finalized = false;
16661
17199
  let span = null;
16662
- let startTime = null;
17200
+ const requestStartTime = startTime ?? getCurrentUnixTimestamp();
16663
17201
  const ensureSpan = () => {
16664
17202
  if (!span) {
16665
17203
  span = startSpan({
@@ -16672,7 +17210,6 @@ function patchGoogleGenAIStreamingResult(args) {
16672
17210
  metadata
16673
17211
  }
16674
17212
  });
16675
- startTime = getCurrentUnixTimestamp();
16676
17213
  }
16677
17214
  return span;
16678
17215
  };
@@ -16726,11 +17263,11 @@ function patchGoogleGenAIStreamingResult(args) {
16726
17263
  }
16727
17264
  chunks.push(nextResult.value);
16728
17265
  }
16729
- if (nextResult.done && startTime !== null) {
17266
+ if (nextResult.done) {
16730
17267
  finalize({
16731
17268
  result: aggregateGenerateContentChunks(
16732
17269
  chunks,
16733
- startTime,
17270
+ requestStartTime,
16734
17271
  firstTokenTime
16735
17272
  )
16736
17273
  });
@@ -16750,13 +17287,13 @@ function patchGoogleGenAIStreamingResult(args) {
16750
17287
  ...returnArgs
16751
17288
  );
16752
17289
  } finally {
16753
- if (startTime !== null) {
17290
+ if (chunks.length > 0) {
16754
17291
  finalize({
16755
- result: chunks.length > 0 ? aggregateGenerateContentChunks(
17292
+ result: aggregateGenerateContentChunks(
16756
17293
  chunks,
16757
- startTime,
17294
+ requestStartTime,
16758
17295
  firstTokenTime
16759
- ) : void 0
17296
+ )
16760
17297
  });
16761
17298
  } else {
16762
17299
  finalize({});
@@ -17048,13 +17585,236 @@ var openRouterChannels = defineChannels("@openrouter/sdk", {
17048
17585
  channelName: "callModel",
17049
17586
  kind: "sync-stream"
17050
17587
  }),
17588
+ callModelTurn: channel({
17589
+ channelName: "callModel.turn",
17590
+ kind: "async"
17591
+ }),
17051
17592
  toolExecute: channel({
17052
17593
  channelName: "tool.execute",
17053
17594
  kind: "async"
17054
17595
  })
17055
17596
  });
17056
17597
 
17057
- // src/openrouter-utils.ts
17598
+ // src/instrumentation/plugins/openrouter-plugin.ts
17599
+ var OpenRouterPlugin = class extends BasePlugin {
17600
+ onEnable() {
17601
+ this.subscribeToOpenRouterChannels();
17602
+ }
17603
+ onDisable() {
17604
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
17605
+ }
17606
+ subscribeToOpenRouterChannels() {
17607
+ this.unsubscribers.push(
17608
+ traceStreamingChannel(openRouterChannels.chatSend, {
17609
+ name: "openrouter.chat.send",
17610
+ type: "llm" /* LLM */,
17611
+ extractInput: (args) => {
17612
+ const request = getOpenRouterRequestArg(args);
17613
+ const chatGenerationParams = isObject(request?.chatGenerationParams) ? request.chatGenerationParams : {};
17614
+ const httpReferer = request?.httpReferer;
17615
+ const xTitle = request?.xTitle;
17616
+ const { messages, ...metadata } = chatGenerationParams;
17617
+ return {
17618
+ input: messages,
17619
+ metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
17620
+ };
17621
+ },
17622
+ extractOutput: (result) => {
17623
+ return isObject(result) ? result.choices : void 0;
17624
+ },
17625
+ extractMetrics: (result, startTime) => {
17626
+ const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
17627
+ if (startTime) {
17628
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
17629
+ }
17630
+ return metrics;
17631
+ },
17632
+ aggregateChunks: aggregateOpenRouterChatChunks
17633
+ })
17634
+ );
17635
+ this.unsubscribers.push(
17636
+ traceAsyncChannel(openRouterChannels.embeddingsGenerate, {
17637
+ name: "openrouter.embeddings.generate",
17638
+ type: "llm" /* LLM */,
17639
+ extractInput: (args) => {
17640
+ const request = getOpenRouterRequestArg(args);
17641
+ const requestBody = isObject(request?.requestBody) ? request.requestBody : {};
17642
+ const httpReferer = request?.httpReferer;
17643
+ const xTitle = request?.xTitle;
17644
+ const { input, ...metadata } = requestBody;
17645
+ return {
17646
+ input,
17647
+ metadata: buildOpenRouterEmbeddingMetadata(
17648
+ metadata,
17649
+ httpReferer,
17650
+ xTitle
17651
+ )
17652
+ };
17653
+ },
17654
+ extractOutput: (result) => {
17655
+ if (!isObject(result)) {
17656
+ return void 0;
17657
+ }
17658
+ const embedding = result.data?.[0]?.embedding;
17659
+ return Array.isArray(embedding) ? { embedding_length: embedding.length } : void 0;
17660
+ },
17661
+ extractMetadata: (result) => {
17662
+ if (!isObject(result)) {
17663
+ return void 0;
17664
+ }
17665
+ return extractOpenRouterResponseMetadata(result);
17666
+ },
17667
+ extractMetrics: (result) => {
17668
+ return isObject(result) ? parseOpenRouterMetricsFromUsage(result.usage) : {};
17669
+ }
17670
+ })
17671
+ );
17672
+ this.unsubscribers.push(
17673
+ traceStreamingChannel(openRouterChannels.betaResponsesSend, {
17674
+ name: "openrouter.beta.responses.send",
17675
+ type: "llm" /* LLM */,
17676
+ extractInput: (args) => {
17677
+ const request = getOpenRouterRequestArg(args);
17678
+ const openResponsesRequest = isObject(request?.openResponsesRequest) ? request.openResponsesRequest : {};
17679
+ const httpReferer = request?.httpReferer;
17680
+ const xTitle = request?.xTitle;
17681
+ const { input, ...metadata } = openResponsesRequest;
17682
+ return {
17683
+ input,
17684
+ metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
17685
+ };
17686
+ },
17687
+ extractOutput: (result) => extractOpenRouterResponseOutput(result),
17688
+ extractMetadata: (result) => extractOpenRouterResponseMetadata(result),
17689
+ extractMetrics: (result, startTime) => {
17690
+ const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
17691
+ if (startTime) {
17692
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
17693
+ }
17694
+ return metrics;
17695
+ },
17696
+ aggregateChunks: aggregateOpenRouterResponseStreamEvents
17697
+ })
17698
+ );
17699
+ this.unsubscribers.push(
17700
+ traceSyncStreamChannel(openRouterChannels.callModel, {
17701
+ name: "openrouter.callModel",
17702
+ type: "llm" /* LLM */,
17703
+ extractInput: (args) => {
17704
+ const request = getOpenRouterCallModelRequestArg(args);
17705
+ return {
17706
+ input: request ? extractOpenRouterCallModelInput(request) : void 0,
17707
+ metadata: request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" }
17708
+ };
17709
+ },
17710
+ patchResult: ({ endEvent, result, span }) => {
17711
+ return patchOpenRouterCallModelResult({
17712
+ request: getOpenRouterCallModelRequestArg(endEvent.arguments),
17713
+ result,
17714
+ span
17715
+ });
17716
+ }
17717
+ })
17718
+ );
17719
+ this.unsubscribers.push(
17720
+ traceAsyncChannel(openRouterChannels.callModelTurn, {
17721
+ name: "openrouter.beta.responses.send",
17722
+ type: "llm" /* LLM */,
17723
+ extractInput: (args, event) => {
17724
+ const request = getOpenRouterCallModelRequestArg(args);
17725
+ const metadata = request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" };
17726
+ if (isObject(metadata) && "tools" in metadata) {
17727
+ delete metadata.tools;
17728
+ }
17729
+ return {
17730
+ input: request ? extractOpenRouterCallModelInput(request) : void 0,
17731
+ metadata: {
17732
+ ...metadata,
17733
+ step: event.step,
17734
+ step_type: event.stepType
17735
+ }
17736
+ };
17737
+ },
17738
+ extractOutput: (result) => extractOpenRouterResponseOutput(result),
17739
+ extractMetadata: (result, event) => {
17740
+ if (!isObject(result)) {
17741
+ return {
17742
+ step: event?.step,
17743
+ step_type: event?.stepType
17744
+ };
17745
+ }
17746
+ return {
17747
+ ...extractOpenRouterResponseMetadata(result) || {},
17748
+ ...event?.step !== void 0 ? { step: event.step } : {},
17749
+ ...event?.stepType ? { step_type: event.stepType } : {}
17750
+ };
17751
+ },
17752
+ extractMetrics: (result) => isObject(result) ? parseOpenRouterMetricsFromUsage(result.usage) : {}
17753
+ })
17754
+ );
17755
+ this.unsubscribers.push(
17756
+ traceStreamingChannel(openRouterChannels.toolExecute, {
17757
+ name: "openrouter.tool",
17758
+ type: "tool" /* TOOL */,
17759
+ extractInput: (args, event) => ({
17760
+ input: args[0],
17761
+ metadata: {
17762
+ provider: "openrouter",
17763
+ tool_name: event.toolName,
17764
+ ...event.toolCallId ? { tool_call_id: event.toolCallId } : {}
17765
+ }
17766
+ }),
17767
+ extractOutput: (result) => result,
17768
+ extractMetrics: () => ({}),
17769
+ aggregateChunks: (chunks) => ({
17770
+ output: chunks.length > 0 ? chunks[chunks.length - 1] : void 0,
17771
+ metrics: {}
17772
+ })
17773
+ })
17774
+ );
17775
+ const callModelChannel = openRouterChannels.callModel.tracingChannel();
17776
+ const callModelHandlers = {
17777
+ start: (event) => {
17778
+ const request = getOpenRouterCallModelRequestArg(event.arguments);
17779
+ if (!request) {
17780
+ return;
17781
+ }
17782
+ patchOpenRouterCallModelRequestTools(request);
17783
+ }
17784
+ };
17785
+ callModelChannel.subscribe(callModelHandlers);
17786
+ this.unsubscribers.push(() => {
17787
+ callModelChannel.unsubscribe(callModelHandlers);
17788
+ });
17789
+ }
17790
+ };
17791
+ function normalizeArgs(args) {
17792
+ if (Array.isArray(args)) {
17793
+ return args;
17794
+ }
17795
+ if (isArrayLike2(args)) {
17796
+ return Array.from(args);
17797
+ }
17798
+ return [args];
17799
+ }
17800
+ function isArrayLike2(value) {
17801
+ return isObject(value) && "length" in value && typeof value.length === "number" && Number.isInteger(value.length) && value.length >= 0;
17802
+ }
17803
+ function getOpenRouterRequestArg(args) {
17804
+ const normalizedArgs = normalizeArgs(args);
17805
+ const keyedCandidate = normalizedArgs.find(
17806
+ (arg) => isObject(arg) && ("chatGenerationParams" in arg || "requestBody" in arg || "openResponsesRequest" in arg)
17807
+ );
17808
+ if (isObject(keyedCandidate)) {
17809
+ return keyedCandidate;
17810
+ }
17811
+ const firstObjectArg = normalizedArgs.find((arg) => isObject(arg));
17812
+ return isObject(firstObjectArg) ? firstObjectArg : void 0;
17813
+ }
17814
+ function getOpenRouterCallModelRequestArg(args) {
17815
+ const firstObjectArg = normalizeArgs(args).find((arg) => isObject(arg));
17816
+ return isObject(firstObjectArg) ? firstObjectArg : void 0;
17817
+ }
17058
17818
  var TOKEN_NAME_MAP2 = {
17059
17819
  promptTokens: "prompt_tokens",
17060
17820
  inputTokens: "prompt_tokens",
@@ -17120,8 +17880,6 @@ function extractOpenRouterUsageMetadata(usage) {
17120
17880
  }
17121
17881
  return Object.keys(metadata).length > 0 ? metadata : void 0;
17122
17882
  }
17123
-
17124
- // src/openrouter-logging.ts
17125
17883
  var OMITTED_OPENROUTER_KEYS = /* @__PURE__ */ new Set([
17126
17884
  "execute",
17127
17885
  "render",
@@ -17141,10 +17899,10 @@ function parseOpenRouterModelString(model) {
17141
17899
  }
17142
17900
  return { model };
17143
17901
  }
17144
- function isZodSchema2(value) {
17902
+ function isZodSchema3(value) {
17145
17903
  return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
17146
17904
  }
17147
- function serializeZodSchema2(schema) {
17905
+ function serializeZodSchema3(schema) {
17148
17906
  try {
17149
17907
  return zodToJsonSchema(schema);
17150
17908
  } catch {
@@ -17178,8 +17936,8 @@ function serializeOpenRouterToolsForLogging(tools) {
17178
17936
  return tools.map((tool) => serializeOpenRouterTool(tool));
17179
17937
  }
17180
17938
  function sanitizeOpenRouterLoggedValue(value) {
17181
- if (isZodSchema2(value)) {
17182
- return serializeZodSchema2(value);
17939
+ if (isZodSchema3(value)) {
17940
+ return serializeZodSchema3(value);
17183
17941
  }
17184
17942
  if (typeof value === "function") {
17185
17943
  return "[Function]";
@@ -17262,28 +18020,7 @@ function extractOpenRouterResponseOutput(response, fallbackOutput) {
17262
18020
  }
17263
18021
  return void 0;
17264
18022
  }
17265
-
17266
- // src/openrouter-tool-wrapping.ts
17267
18023
  var OPENROUTER_WRAPPED_TOOL = Symbol("braintrust.openrouter.wrappedTool");
17268
- var OPENROUTER_WRAPPED_CALL_MODEL_RESULT = Symbol(
17269
- "braintrust.openrouter.wrappedCallModelResult"
17270
- );
17271
- var OPENROUTER_CALL_MODEL_STREAM_METHODS = [
17272
- "getFullResponsesStream",
17273
- "getItemsStream",
17274
- "getNewMessagesStream",
17275
- "getReasoningStream",
17276
- "getTextStream",
17277
- "getToolCallsStream",
17278
- "getToolStream"
17279
- ];
17280
- var OPENROUTER_CALL_MODEL_CONTEXT_METHODS = [
17281
- "cancel",
17282
- "getPendingToolCalls",
17283
- "getState",
17284
- "getToolCalls",
17285
- "requiresApproval"
17286
- ];
17287
18024
  function patchOpenRouterCallModelRequestTools(request) {
17288
18025
  if (!Array.isArray(request.tools) || request.tools.length === 0) {
17289
18026
  return void 0;
@@ -17301,21 +18038,226 @@ function patchOpenRouterCallModelRequestTools(request) {
17301
18038
  request.tools = originalTools;
17302
18039
  };
17303
18040
  }
17304
- function patchOpenRouterCallModelResult(span, result, request) {
17305
- if (!isObject(result) || isWrappedCallModelResult(result)) {
17306
- return false;
17307
- }
17308
- const resultLike = result;
17309
- const hasInstrumentableMethod = typeof resultLike.getResponse === "function" || typeof resultLike.getText === "function" || OPENROUTER_CALL_MODEL_STREAM_METHODS.some(
17310
- (methodName) => typeof resultLike[methodName] === "function"
17311
- );
17312
- if (!hasInstrumentableMethod) {
17313
- return false;
18041
+ function wrapOpenRouterTool(tool) {
18042
+ if (isWrappedTool(tool) || !tool.function || typeof tool.function !== "object" || typeof tool.function.execute !== "function") {
18043
+ return tool;
17314
18044
  }
17315
- Object.defineProperty(resultLike, OPENROUTER_WRAPPED_CALL_MODEL_RESULT, {
17316
- value: true,
17317
- enumerable: false,
17318
- configurable: false
18045
+ const toolName = tool.function.name || "tool";
18046
+ const originalExecute = tool.function.execute;
18047
+ const wrappedTool = {
18048
+ ...tool,
18049
+ function: {
18050
+ ...tool.function,
18051
+ execute(...args) {
18052
+ return traceToolExecution({
18053
+ args,
18054
+ execute: () => Reflect.apply(originalExecute, this, args),
18055
+ toolCallId: getToolCallId(args[1]),
18056
+ toolName
18057
+ });
18058
+ }
18059
+ }
18060
+ };
18061
+ Object.defineProperty(wrappedTool, OPENROUTER_WRAPPED_TOOL, {
18062
+ value: true,
18063
+ enumerable: false,
18064
+ configurable: false
18065
+ });
18066
+ return wrappedTool;
18067
+ }
18068
+ function isWrappedTool(tool) {
18069
+ return Boolean(tool[OPENROUTER_WRAPPED_TOOL]);
18070
+ }
18071
+ function traceToolExecution(args) {
18072
+ const tracingChannel2 = openRouterChannels.toolExecute.tracingChannel();
18073
+ const input = args.args.length > 0 ? args.args[0] : void 0;
18074
+ const event = {
18075
+ arguments: [input],
18076
+ span_info: {
18077
+ name: args.toolName
18078
+ },
18079
+ toolCallId: args.toolCallId,
18080
+ toolName: args.toolName
18081
+ };
18082
+ tracingChannel2.start.publish(event);
18083
+ try {
18084
+ const result = args.execute();
18085
+ return publishToolResult(tracingChannel2, event, result);
18086
+ } catch (error2) {
18087
+ event.error = normalizeError(error2);
18088
+ tracingChannel2.error.publish(event);
18089
+ throw error2;
18090
+ }
18091
+ }
18092
+ function publishToolResult(tracingChannel2, event, result) {
18093
+ if (isPromiseLike2(result)) {
18094
+ return result.then(
18095
+ (resolved) => {
18096
+ event.result = resolved;
18097
+ tracingChannel2.asyncEnd.publish(event);
18098
+ return resolved;
18099
+ },
18100
+ (error2) => {
18101
+ event.error = normalizeError(error2);
18102
+ tracingChannel2.error.publish(event);
18103
+ throw error2;
18104
+ }
18105
+ );
18106
+ }
18107
+ event.result = result;
18108
+ tracingChannel2.asyncEnd.publish(event);
18109
+ return result;
18110
+ }
18111
+ function getToolCallId(context2) {
18112
+ const toolContext = context2;
18113
+ return typeof toolContext?.toolCall?.id === "string" ? toolContext.toolCall.id : void 0;
18114
+ }
18115
+ function isPromiseLike2(value) {
18116
+ return !!value && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
18117
+ }
18118
+ function aggregateOpenRouterChatChunks(chunks) {
18119
+ let role;
18120
+ let content = "";
18121
+ let toolCalls;
18122
+ let finishReason;
18123
+ let metrics = {};
18124
+ for (const chunk of chunks) {
18125
+ metrics = {
18126
+ ...metrics,
18127
+ ...parseOpenRouterMetricsFromUsage(chunk?.usage)
18128
+ };
18129
+ const choice = chunk?.choices?.[0];
18130
+ const delta = choice?.delta;
18131
+ if (!delta) {
18132
+ if (choice?.finish_reason !== void 0) {
18133
+ finishReason = choice.finish_reason;
18134
+ }
18135
+ continue;
18136
+ }
18137
+ if (!role && delta.role) {
18138
+ role = delta.role;
18139
+ }
18140
+ if (typeof delta.content === "string") {
18141
+ content += delta.content;
18142
+ }
18143
+ const choiceFinishReason = choice?.finishReason ?? choice?.finish_reason ?? void 0;
18144
+ const deltaFinishReason = delta.finishReason ?? delta.finish_reason ?? void 0;
18145
+ if (choiceFinishReason !== void 0) {
18146
+ finishReason = choiceFinishReason;
18147
+ } else if (deltaFinishReason !== void 0) {
18148
+ finishReason = deltaFinishReason;
18149
+ }
18150
+ const toolCallDeltas = Array.isArray(delta.toolCalls) ? delta.toolCalls : Array.isArray(delta.tool_calls) ? delta.tool_calls : void 0;
18151
+ if (!toolCallDeltas) {
18152
+ continue;
18153
+ }
18154
+ for (const toolDelta of toolCallDeltas) {
18155
+ if (!toolDelta?.function) {
18156
+ continue;
18157
+ }
18158
+ const toolIndex = toolDelta.index ?? 0;
18159
+ const existingToolCall = toolCalls?.[toolIndex];
18160
+ if (!existingToolCall || toolDelta.id && existingToolCall.id !== void 0 && existingToolCall.id !== toolDelta.id) {
18161
+ const nextToolCalls = [...toolCalls || []];
18162
+ nextToolCalls[toolIndex] = {
18163
+ index: toolIndex,
18164
+ id: toolDelta.id,
18165
+ type: toolDelta.type,
18166
+ function: {
18167
+ name: toolDelta.function.name,
18168
+ arguments: toolDelta.function.arguments || ""
18169
+ }
18170
+ };
18171
+ toolCalls = nextToolCalls;
18172
+ continue;
18173
+ }
18174
+ const current = existingToolCall;
18175
+ if (toolDelta.id && !current.id) {
18176
+ current.id = toolDelta.id;
18177
+ }
18178
+ if (toolDelta.type && !current.type) {
18179
+ current.type = toolDelta.type;
18180
+ }
18181
+ if (toolDelta.function.name && !current.function.name) {
18182
+ current.function.name = toolDelta.function.name;
18183
+ }
18184
+ current.function.arguments += toolDelta.function.arguments || "";
18185
+ }
18186
+ }
18187
+ return {
18188
+ output: [
18189
+ {
18190
+ index: 0,
18191
+ message: {
18192
+ role,
18193
+ content: content || void 0,
18194
+ ...toolCalls ? { tool_calls: toolCalls } : {}
18195
+ },
18196
+ logprobs: null,
18197
+ finish_reason: finishReason
18198
+ }
18199
+ ],
18200
+ metrics
18201
+ };
18202
+ }
18203
+ function aggregateOpenRouterResponseStreamEvents(chunks) {
18204
+ let finalResponse;
18205
+ for (const chunk of chunks) {
18206
+ const response = chunk?.response;
18207
+ if (!response) {
18208
+ continue;
18209
+ }
18210
+ if (chunk.type === "response.completed" || chunk.type === "response.incomplete" || chunk.type === "response.failed") {
18211
+ finalResponse = response;
18212
+ }
18213
+ }
18214
+ if (!finalResponse) {
18215
+ return {
18216
+ output: void 0,
18217
+ metrics: {}
18218
+ };
18219
+ }
18220
+ return {
18221
+ output: extractOpenRouterResponseOutput(finalResponse),
18222
+ metrics: parseOpenRouterMetricsFromUsage(finalResponse.usage),
18223
+ ...extractOpenRouterResponseMetadata(finalResponse) ? { metadata: extractOpenRouterResponseMetadata(finalResponse) } : {}
18224
+ };
18225
+ }
18226
+ var OPENROUTER_WRAPPED_CALL_MODEL_RESULT = Symbol(
18227
+ "braintrust.openrouter.wrappedCallModelResult"
18228
+ );
18229
+ var OPENROUTER_CALL_MODEL_STREAM_METHODS = [
18230
+ "getFullResponsesStream",
18231
+ "getItemsStream",
18232
+ "getNewMessagesStream",
18233
+ "getReasoningStream",
18234
+ "getTextStream",
18235
+ "getToolCallsStream",
18236
+ "getToolStream"
18237
+ ];
18238
+ var OPENROUTER_CALL_MODEL_CONTEXT_METHODS = [
18239
+ "cancel",
18240
+ "getPendingToolCalls",
18241
+ "getState",
18242
+ "getToolCalls",
18243
+ "requiresApproval"
18244
+ ];
18245
+ function patchOpenRouterCallModelResult(args) {
18246
+ const { request, result, span } = args;
18247
+ if (!isObject(result) || isWrappedCallModelResult(result)) {
18248
+ return false;
18249
+ }
18250
+ const resultLike = result;
18251
+ const hasInstrumentableMethod = typeof resultLike.getResponse === "function" || typeof resultLike.getText === "function" || OPENROUTER_CALL_MODEL_STREAM_METHODS.some(
18252
+ (methodName) => typeof resultLike[methodName] === "function"
18253
+ );
18254
+ if (!hasInstrumentableMethod) {
18255
+ return false;
18256
+ }
18257
+ Object.defineProperty(resultLike, OPENROUTER_WRAPPED_CALL_MODEL_RESULT, {
18258
+ value: true,
18259
+ enumerable: false,
18260
+ configurable: false
17319
18261
  });
17320
18262
  const originalGetResponse = typeof resultLike.getResponse === "function" ? resultLike.getResponse.bind(resultLike) : void 0;
17321
18263
  const originalGetInitialResponse = typeof resultLike.getInitialResponse === "function" ? resultLike.getInitialResponse.bind(resultLike) : void 0;
@@ -17374,10 +18316,10 @@ function patchOpenRouterCallModelResult(span, result, request) {
17374
18316
  }
17375
18317
  };
17376
18318
  if (originalGetResponse) {
17377
- resultLike.getResponse = async (...args) => {
18319
+ resultLike.getResponse = async (...args2) => {
17378
18320
  return await withCurrent(span, async () => {
17379
18321
  try {
17380
- const response = await originalGetResponse(...args);
18322
+ const response = await originalGetResponse(...args2);
17381
18323
  await endSpanWithResult(response);
17382
18324
  return response;
17383
18325
  } catch (error2) {
@@ -17389,663 +18331,241 @@ function patchOpenRouterCallModelResult(span, result, request) {
17389
18331
  }
17390
18332
  if (typeof resultLike.getText === "function") {
17391
18333
  const originalGetText = resultLike.getText.bind(resultLike);
17392
- resultLike.getText = async (...args) => {
18334
+ resultLike.getText = async (...args2) => {
17393
18335
  return await withCurrent(span, async () => {
17394
18336
  try {
17395
- const text = await originalGetText(...args);
18337
+ const text = await originalGetText(...args2);
17396
18338
  await finalizeFromResponse(text);
17397
- return text;
17398
- } catch (error2) {
17399
- endSpanWithError(error2);
17400
- throw error2;
17401
- }
17402
- });
17403
- };
17404
- }
17405
- for (const methodName of OPENROUTER_CALL_MODEL_CONTEXT_METHODS) {
17406
- if (typeof resultLike[methodName] !== "function") {
17407
- continue;
17408
- }
17409
- const originalMethod = resultLike[methodName];
17410
- resultLike[methodName] = async (...args) => {
17411
- return await withCurrent(span, async () => {
17412
- return await originalMethod.apply(resultLike, args);
17413
- });
17414
- };
17415
- }
17416
- for (const methodName of OPENROUTER_CALL_MODEL_STREAM_METHODS) {
17417
- if (typeof resultLike[methodName] !== "function") {
17418
- continue;
17419
- }
17420
- const originalMethod = resultLike[methodName];
17421
- resultLike[methodName] = (...args) => {
17422
- const stream = withCurrent(
17423
- span,
17424
- () => originalMethod.apply(resultLike, args)
17425
- );
17426
- if (!isAsyncIterable4(stream)) {
17427
- return stream;
17428
- }
17429
- return wrapAsyncIterableWithSpan({
17430
- finalize: finalizeFromResponse,
17431
- iteratorFactory: () => stream[Symbol.asyncIterator](),
17432
- onError: endSpanWithError,
17433
- span
17434
- });
17435
- };
17436
- }
17437
- if (originalGetInitialResponse) {
17438
- let initialTurnTraced = false;
17439
- resultLike.getInitialResponse = async (...args) => {
17440
- if (initialTurnTraced) {
17441
- return await withCurrent(span, async () => {
17442
- return await originalGetInitialResponse(...args);
17443
- });
17444
- }
17445
- initialTurnTraced = true;
17446
- const resolvedRequest = getOpenRouterResolvedRequest(resultLike, request);
17447
- const childSpan = startOpenRouterCallModelTurnSpan({
17448
- request: resolvedRequest,
17449
- step: tracedTurnCount + 1,
17450
- stepType: tracedTurnCount === 0 ? "initial" : "continue"
17451
- });
17452
- return await withCurrent(childSpan, async () => {
17453
- try {
17454
- const response = await originalGetInitialResponse(...args);
17455
- tracedTurnCount++;
17456
- finishOpenRouterCallModelTurnSpan({
17457
- response,
17458
- step: tracedTurnCount,
17459
- stepType: tracedTurnCount === 1 ? "initial" : "continue",
17460
- span: childSpan
17461
- });
17462
- return response;
17463
- } catch (error2) {
17464
- childSpan.log({
17465
- error: normalizeError(error2).message
17466
- });
17467
- childSpan.end();
17468
- throw error2;
17469
- }
17470
- });
17471
- };
17472
- }
17473
- if (originalMakeFollowupRequest) {
17474
- resultLike.makeFollowupRequest = async (...args) => {
17475
- const currentResponse = args[0];
17476
- const toolResults = Array.isArray(args[1]) ? args[1] : [];
17477
- const resolvedRequest = getOpenRouterResolvedRequest(resultLike, request);
17478
- const followupRequest = buildOpenRouterFollowupRequest(
17479
- resolvedRequest,
17480
- currentResponse,
17481
- toolResults
17482
- );
17483
- const childSpan = startOpenRouterCallModelTurnSpan({
17484
- request: followupRequest,
17485
- step: tracedTurnCount + 1,
17486
- stepType: "continue"
17487
- });
17488
- return await withCurrent(childSpan, async () => {
17489
- try {
17490
- const response = await originalMakeFollowupRequest(...args);
17491
- tracedTurnCount++;
17492
- finishOpenRouterCallModelTurnSpan({
17493
- response,
17494
- step: tracedTurnCount,
17495
- stepType: "continue",
17496
- span: childSpan
17497
- });
17498
- return response;
17499
- } catch (error2) {
17500
- childSpan.log({
17501
- error: normalizeError(error2).message
17502
- });
17503
- childSpan.end();
17504
- throw error2;
17505
- }
17506
- });
17507
- };
17508
- }
17509
- return true;
17510
- }
17511
- function wrapOpenRouterTool(tool) {
17512
- if (isWrappedTool(tool) || !tool.function || typeof tool.function !== "object" || typeof tool.function.execute !== "function") {
17513
- return tool;
17514
- }
17515
- const toolName = tool.function.name || "tool";
17516
- const originalExecute = tool.function.execute;
17517
- const wrappedTool = {
17518
- ...tool,
17519
- function: {
17520
- ...tool.function,
17521
- execute(...args) {
17522
- return traceToolExecution({
17523
- args,
17524
- execute: () => Reflect.apply(originalExecute, this, args),
17525
- toolCallId: getToolCallId(args[1]),
17526
- toolName
17527
- });
17528
- }
17529
- }
17530
- };
17531
- Object.defineProperty(wrappedTool, OPENROUTER_WRAPPED_TOOL, {
17532
- value: true,
17533
- enumerable: false,
17534
- configurable: false
17535
- });
17536
- return wrappedTool;
17537
- }
17538
- function isWrappedTool(tool) {
17539
- return Boolean(tool[OPENROUTER_WRAPPED_TOOL]);
17540
- }
17541
- function isWrappedCallModelResult(value) {
17542
- return Boolean(
17543
- isObject(value) && value[OPENROUTER_WRAPPED_CALL_MODEL_RESULT]
17544
- );
17545
- }
17546
- function traceToolExecution(args) {
17547
- const tracingChannel2 = openRouterChannels.toolExecute.tracingChannel();
17548
- const input = args.args.length > 0 ? args.args[0] : void 0;
17549
- const event = {
17550
- arguments: [input],
17551
- span_info: {
17552
- name: args.toolName
17553
- },
17554
- toolCallId: args.toolCallId,
17555
- toolName: args.toolName
17556
- };
17557
- tracingChannel2.start.publish(event);
17558
- try {
17559
- const result = args.execute();
17560
- return publishToolResult(tracingChannel2, event, result);
17561
- } catch (error2) {
17562
- event.error = normalizeError(error2);
17563
- tracingChannel2.error.publish(event);
17564
- throw error2;
17565
- }
17566
- }
17567
- function publishToolResult(tracingChannel2, event, result) {
17568
- if (isPromiseLike(result)) {
17569
- return result.then(
17570
- (resolved) => {
17571
- event.result = resolved;
17572
- tracingChannel2.asyncEnd.publish(event);
17573
- return resolved;
17574
- },
17575
- (error2) => {
17576
- event.error = normalizeError(error2);
17577
- tracingChannel2.error.publish(event);
17578
- throw error2;
17579
- }
17580
- );
17581
- }
17582
- event.result = result;
17583
- tracingChannel2.asyncEnd.publish(event);
17584
- return result;
17585
- }
17586
- function getToolCallId(context2) {
17587
- const toolContext = context2;
17588
- return typeof toolContext?.toolCall?.id === "string" ? toolContext.toolCall.id : void 0;
17589
- }
17590
- function extractOpenRouterCallModelResultMetadata(response, turnCount) {
17591
- const combined = {
17592
- ...extractOpenRouterResponseMetadata(response) || {},
17593
- ...turnCount !== void 0 ? { turn_count: turnCount } : {}
17594
- };
17595
- return Object.keys(combined).length > 0 ? combined : void 0;
17596
- }
17597
- function getFinalOpenRouterCallModelResponse(result, response) {
17598
- if (isObject(response)) {
17599
- return response;
17600
- }
17601
- return isObject(result.finalResponse) ? result.finalResponse : void 0;
17602
- }
17603
- function getOpenRouterCallModelRounds(result) {
17604
- if (!Array.isArray(result.allToolExecutionRounds)) {
17605
- return [];
17606
- }
17607
- return result.allToolExecutionRounds.filter((round) => isObject(round)).map((round) => ({
17608
- response: isObject(round.response) ? round.response : void 0,
17609
- round: typeof round.round === "number" ? round.round : void 0,
17610
- toolResults: Array.isArray(round.toolResults) ? round.toolResults : []
17611
- })).filter((round) => round.response !== void 0);
17612
- }
17613
- function aggregateOpenRouterCallModelMetrics(rounds, finalResponse) {
17614
- const metrics = {};
17615
- const responses = [
17616
- ...rounds.map((round) => round.response).filter(isObject),
17617
- finalResponse
17618
- ];
17619
- for (const response of responses) {
17620
- const responseMetrics = parseOpenRouterMetricsFromUsage(response.usage);
17621
- for (const [name, value] of Object.entries(responseMetrics)) {
17622
- metrics[name] = (metrics[name] || 0) + value;
17623
- }
17624
- }
17625
- return metrics;
17626
- }
17627
- function buildNextOpenRouterCallModelInput(currentInput, response, toolResults) {
17628
- const normalizedInput = Array.isArray(currentInput) ? [...currentInput] : currentInput === void 0 ? [] : [currentInput];
17629
- const responseOutput = Array.isArray(response.output) ? response.output : response.output === void 0 ? [] : [response.output];
17630
- return [...normalizedInput, ...responseOutput, ...toolResults].map(
17631
- (entry) => sanitizeOpenRouterLoggedValue(entry)
17632
- );
17633
- }
17634
- function startOpenRouterCallModelTurnSpan(args) {
17635
- const requestRecord = isObject(args.request) ? args.request : void 0;
17636
- const metadata = requestRecord ? extractOpenRouterCallModelMetadata(requestRecord) : { provider: "openrouter" };
17637
- if (isObject(metadata) && "tools" in metadata) {
17638
- delete metadata.tools;
17639
- }
17640
- return startSpan({
17641
- name: "openrouter.beta.responses.send",
17642
- spanAttributes: {
17643
- type: "llm" /* LLM */
17644
- },
17645
- event: {
17646
- input: requestRecord ? extractOpenRouterCallModelInput(requestRecord) : void 0,
17647
- metadata: {
17648
- ...metadata,
17649
- step: args.step,
17650
- step_type: args.stepType
17651
- }
17652
- }
17653
- });
17654
- }
17655
- function finishOpenRouterCallModelTurnSpan(args) {
17656
- if (!isObject(args.response)) {
17657
- args.span.end();
17658
- return;
17659
- }
17660
- args.span.log({
17661
- output: extractOpenRouterResponseOutput(args.response),
17662
- ...extractOpenRouterResponseMetadata(args.response) ? {
17663
- metadata: {
17664
- ...extractOpenRouterResponseMetadata(args.response),
17665
- ...args.step !== void 0 ? { step: args.step } : {},
17666
- ...args.stepType ? { step_type: args.stepType } : {}
17667
- }
17668
- } : {},
17669
- metrics: parseOpenRouterMetricsFromUsage(args.response.usage)
17670
- });
17671
- args.span.end();
17672
- }
17673
- function getOpenRouterResolvedRequest(result, request) {
17674
- if (isObject(result.resolvedRequest)) {
17675
- return result.resolvedRequest;
17676
- }
17677
- return request;
17678
- }
17679
- function buildOpenRouterFollowupRequest(request, currentResponse, toolResults) {
17680
- if (!request) {
17681
- return void 0;
17682
- }
17683
- return {
17684
- ...request,
17685
- input: buildNextOpenRouterCallModelInput(
17686
- extractOpenRouterCallModelInput(request),
17687
- isObject(currentResponse) ? currentResponse : {},
17688
- toolResults
17689
- ),
17690
- stream: false
17691
- };
17692
- }
17693
- function wrapAsyncIterableWithSpan(args) {
17694
- return {
17695
- [Symbol.asyncIterator]() {
17696
- const iterator = args.iteratorFactory();
17697
- return {
17698
- next(value) {
17699
- return withCurrent(
17700
- args.span,
17701
- () => value === void 0 ? iterator.next() : iterator.next(value)
17702
- ).then(
17703
- async (result) => {
17704
- if (result.done) {
17705
- await args.finalize();
17706
- }
17707
- return result;
17708
- },
17709
- (error2) => {
17710
- args.onError(error2);
17711
- throw error2;
17712
- }
17713
- );
17714
- },
17715
- return(value) {
17716
- if (typeof iterator.return !== "function") {
17717
- return args.finalize().then(() => ({
17718
- done: true,
17719
- value
17720
- }));
17721
- }
17722
- return withCurrent(args.span, () => iterator.return(value)).then(
17723
- async (result) => {
17724
- await args.finalize();
17725
- return result;
17726
- },
17727
- (error2) => {
17728
- args.onError(error2);
17729
- throw error2;
17730
- }
17731
- );
17732
- },
17733
- throw(error2) {
17734
- args.onError(error2);
17735
- if (typeof iterator.throw !== "function") {
17736
- return Promise.reject(error2);
17737
- }
17738
- return withCurrent(args.span, () => iterator.throw(error2));
17739
- },
17740
- [Symbol.asyncIterator]() {
17741
- return this;
17742
- }
17743
- };
17744
- }
17745
- };
17746
- }
17747
- function isAsyncIterable4(value) {
17748
- return !!value && (typeof value === "object" || typeof value === "function") && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
17749
- }
17750
- function isPromiseLike(value) {
17751
- return !!value && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
17752
- }
17753
- function normalizeError(error2) {
17754
- return error2 instanceof Error ? error2 : new Error(String(error2));
17755
- }
17756
-
17757
- // src/instrumentation/plugins/openrouter-plugin.ts
17758
- var OpenRouterPlugin = class extends BasePlugin {
17759
- onEnable() {
17760
- this.subscribeToOpenRouterChannels();
17761
- }
17762
- onDisable() {
17763
- this.unsubscribers = unsubscribeAll(this.unsubscribers);
17764
- }
17765
- subscribeToOpenRouterChannels() {
17766
- this.unsubscribers.push(
17767
- traceStreamingChannel(openRouterChannels.chatSend, {
17768
- name: "openrouter.chat.send",
17769
- type: "llm" /* LLM */,
17770
- extractInput: (args) => {
17771
- const request = getOpenRouterRequestArg(args);
17772
- const chatGenerationParams = isObject(request?.chatGenerationParams) ? request.chatGenerationParams : {};
17773
- const httpReferer = request?.httpReferer;
17774
- const xTitle = request?.xTitle;
17775
- const { messages, ...metadata } = chatGenerationParams;
17776
- return {
17777
- input: messages,
17778
- metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
17779
- };
17780
- },
17781
- extractOutput: (result) => {
17782
- return isObject(result) ? result.choices : void 0;
17783
- },
17784
- extractMetrics: (result, startTime) => {
17785
- const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
17786
- if (startTime) {
17787
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
17788
- }
17789
- return metrics;
17790
- },
17791
- aggregateChunks: aggregateOpenRouterChatChunks
17792
- })
17793
- );
17794
- this.unsubscribers.push(
17795
- traceAsyncChannel(openRouterChannels.embeddingsGenerate, {
17796
- name: "openrouter.embeddings.generate",
17797
- type: "llm" /* LLM */,
17798
- extractInput: (args) => {
17799
- const request = getOpenRouterRequestArg(args);
17800
- const requestBody = isObject(request?.requestBody) ? request.requestBody : {};
17801
- const httpReferer = request?.httpReferer;
17802
- const xTitle = request?.xTitle;
17803
- const { input, ...metadata } = requestBody;
17804
- return {
17805
- input,
17806
- metadata: buildOpenRouterEmbeddingMetadata(
17807
- metadata,
17808
- httpReferer,
17809
- xTitle
17810
- )
17811
- };
17812
- },
17813
- extractOutput: (result) => {
17814
- if (!isObject(result)) {
17815
- return void 0;
17816
- }
17817
- const embedding = result.data?.[0]?.embedding;
17818
- return Array.isArray(embedding) ? { embedding_length: embedding.length } : void 0;
17819
- },
17820
- extractMetadata: (result) => {
17821
- if (!isObject(result)) {
17822
- return void 0;
17823
- }
17824
- return extractOpenRouterResponseMetadata(result);
17825
- },
17826
- extractMetrics: (result) => {
17827
- return isObject(result) ? parseOpenRouterMetricsFromUsage(result.usage) : {};
17828
- }
17829
- })
17830
- );
17831
- this.unsubscribers.push(
17832
- traceStreamingChannel(openRouterChannels.betaResponsesSend, {
17833
- name: "openrouter.beta.responses.send",
17834
- type: "llm" /* LLM */,
17835
- extractInput: (args) => {
17836
- const request = getOpenRouterRequestArg(args);
17837
- const openResponsesRequest = isObject(request?.openResponsesRequest) ? request.openResponsesRequest : {};
17838
- const httpReferer = request?.httpReferer;
17839
- const xTitle = request?.xTitle;
17840
- const { input, ...metadata } = openResponsesRequest;
17841
- return {
17842
- input,
17843
- metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
17844
- };
17845
- },
17846
- extractOutput: (result) => extractOpenRouterResponseOutput(result),
17847
- extractMetadata: (result) => extractOpenRouterResponseMetadata(result),
17848
- extractMetrics: (result, startTime) => {
17849
- const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
17850
- if (startTime) {
17851
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
17852
- }
17853
- return metrics;
17854
- },
17855
- aggregateChunks: aggregateOpenRouterResponseStreamEvents
17856
- })
17857
- );
17858
- this.unsubscribers.push(
17859
- traceSyncStreamChannel(openRouterChannels.callModel, {
17860
- name: "openrouter.callModel",
17861
- type: "llm" /* LLM */,
17862
- extractInput: (args) => {
17863
- const request = getOpenRouterCallModelRequestArg(args);
17864
- return {
17865
- input: request ? extractOpenRouterCallModelInput(request) : void 0,
17866
- metadata: request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" }
17867
- };
17868
- },
17869
- patchResult: ({ endEvent, result, span }) => {
17870
- return patchOpenRouterCallModelResult(
17871
- span,
17872
- result,
17873
- getOpenRouterCallModelRequestArg(endEvent.arguments)
17874
- );
17875
- }
17876
- })
17877
- );
17878
- this.unsubscribers.push(
17879
- traceStreamingChannel(openRouterChannels.toolExecute, {
17880
- name: "openrouter.tool",
17881
- type: "tool" /* TOOL */,
17882
- extractInput: (args, event) => ({
17883
- input: args[0],
17884
- metadata: {
17885
- provider: "openrouter",
17886
- tool_name: event.toolName,
17887
- ...event.toolCallId ? { tool_call_id: event.toolCallId } : {}
17888
- }
17889
- }),
17890
- extractOutput: (result) => result,
17891
- extractMetrics: () => ({}),
17892
- aggregateChunks: (chunks) => ({
17893
- output: chunks.length > 0 ? chunks[chunks.length - 1] : void 0,
17894
- metrics: {}
17895
- })
17896
- })
17897
- );
17898
- const callModelChannel = openRouterChannels.callModel.tracingChannel();
17899
- const callModelHandlers = {
17900
- start: (event) => {
17901
- const request = getOpenRouterCallModelRequestArg(event.arguments);
17902
- if (!request) {
17903
- return;
17904
- }
17905
- patchOpenRouterCallModelRequestTools(request);
17906
- }
17907
- };
17908
- callModelChannel.subscribe(callModelHandlers);
17909
- this.unsubscribers.push(() => {
17910
- callModelChannel.unsubscribe(callModelHandlers);
17911
- });
17912
- }
17913
- };
17914
- function normalizeArgs(args) {
17915
- if (Array.isArray(args)) {
17916
- return args;
17917
- }
17918
- if (isArrayLike2(args)) {
17919
- return Array.from(args);
17920
- }
17921
- return [args];
17922
- }
17923
- function isArrayLike2(value) {
17924
- return isObject(value) && "length" in value && typeof value.length === "number" && Number.isInteger(value.length) && value.length >= 0;
17925
- }
17926
- function getOpenRouterRequestArg(args) {
17927
- const normalizedArgs = normalizeArgs(args);
17928
- const keyedCandidate = normalizedArgs.find(
17929
- (arg) => isObject(arg) && ("chatGenerationParams" in arg || "requestBody" in arg || "openResponsesRequest" in arg)
17930
- );
17931
- if (isObject(keyedCandidate)) {
17932
- return keyedCandidate;
17933
- }
17934
- const firstObjectArg = normalizedArgs.find((arg) => isObject(arg));
17935
- return isObject(firstObjectArg) ? firstObjectArg : void 0;
17936
- }
17937
- function getOpenRouterCallModelRequestArg(args) {
17938
- const firstObjectArg = normalizeArgs(args).find((arg) => isObject(arg));
17939
- return isObject(firstObjectArg) ? firstObjectArg : void 0;
17940
- }
17941
- function aggregateOpenRouterChatChunks(chunks) {
17942
- let role;
17943
- let content = "";
17944
- let toolCalls;
17945
- let finishReason;
17946
- let metrics = {};
17947
- for (const chunk of chunks) {
17948
- metrics = {
17949
- ...metrics,
17950
- ...parseOpenRouterMetricsFromUsage(chunk?.usage)
18339
+ return text;
18340
+ } catch (error2) {
18341
+ endSpanWithError(error2);
18342
+ throw error2;
18343
+ }
18344
+ });
17951
18345
  };
17952
- const choice = chunk?.choices?.[0];
17953
- const delta = choice?.delta;
17954
- if (!delta) {
17955
- if (choice?.finish_reason !== void 0) {
17956
- finishReason = choice.finish_reason;
17957
- }
18346
+ }
18347
+ for (const methodName of OPENROUTER_CALL_MODEL_CONTEXT_METHODS) {
18348
+ if (typeof resultLike[methodName] !== "function") {
17958
18349
  continue;
17959
18350
  }
17960
- if (!role && delta.role) {
17961
- role = delta.role;
17962
- }
17963
- if (typeof delta.content === "string") {
17964
- content += delta.content;
17965
- }
17966
- const choiceFinishReason = choice?.finishReason ?? choice?.finish_reason ?? void 0;
17967
- const deltaFinishReason = delta.finishReason ?? delta.finish_reason ?? void 0;
17968
- if (choiceFinishReason !== void 0) {
17969
- finishReason = choiceFinishReason;
17970
- } else if (deltaFinishReason !== void 0) {
17971
- finishReason = deltaFinishReason;
17972
- }
17973
- const toolCallDeltas = Array.isArray(delta.toolCalls) ? delta.toolCalls : Array.isArray(delta.tool_calls) ? delta.tool_calls : void 0;
17974
- if (!toolCallDeltas) {
18351
+ const originalMethod = resultLike[methodName];
18352
+ resultLike[methodName] = async (...args2) => {
18353
+ return await withCurrent(span, async () => {
18354
+ return await originalMethod.apply(resultLike, args2);
18355
+ });
18356
+ };
18357
+ }
18358
+ for (const methodName of OPENROUTER_CALL_MODEL_STREAM_METHODS) {
18359
+ if (typeof resultLike[methodName] !== "function") {
17975
18360
  continue;
17976
18361
  }
17977
- for (const toolDelta of toolCallDeltas) {
17978
- if (!toolDelta?.function) {
17979
- continue;
17980
- }
17981
- const toolIndex = toolDelta.index ?? 0;
17982
- const existingToolCall = toolCalls?.[toolIndex];
17983
- if (!existingToolCall || toolDelta.id && existingToolCall.id !== void 0 && existingToolCall.id !== toolDelta.id) {
17984
- const nextToolCalls = [...toolCalls || []];
17985
- nextToolCalls[toolIndex] = {
17986
- index: toolIndex,
17987
- id: toolDelta.id,
17988
- type: toolDelta.type,
17989
- function: {
17990
- name: toolDelta.function.name,
17991
- arguments: toolDelta.function.arguments || ""
17992
- }
17993
- };
17994
- toolCalls = nextToolCalls;
17995
- continue;
17996
- }
17997
- const current = existingToolCall;
17998
- if (toolDelta.id && !current.id) {
17999
- current.id = toolDelta.id;
18000
- }
18001
- if (toolDelta.type && !current.type) {
18002
- current.type = toolDelta.type;
18362
+ const originalMethod = resultLike[methodName];
18363
+ resultLike[methodName] = (...args2) => {
18364
+ const stream = withCurrent(
18365
+ span,
18366
+ () => originalMethod.apply(resultLike, args2)
18367
+ );
18368
+ if (!isAsyncIterable4(stream)) {
18369
+ return stream;
18003
18370
  }
18004
- if (toolDelta.function.name && !current.function.name) {
18005
- current.function.name = toolDelta.function.name;
18371
+ return wrapAsyncIterableWithSpan({
18372
+ finalize: finalizeFromResponse,
18373
+ iteratorFactory: () => stream[Symbol.asyncIterator](),
18374
+ onError: endSpanWithError,
18375
+ span
18376
+ });
18377
+ };
18378
+ }
18379
+ if (originalGetInitialResponse) {
18380
+ let initialTurnTraced = false;
18381
+ resultLike.getInitialResponse = async (...args2) => {
18382
+ if (initialTurnTraced) {
18383
+ return await withCurrent(span, async () => {
18384
+ return await originalGetInitialResponse(...args2);
18385
+ });
18006
18386
  }
18007
- current.function.arguments += toolDelta.function.arguments || "";
18008
- }
18387
+ initialTurnTraced = true;
18388
+ const step = tracedTurnCount + 1;
18389
+ const stepType = tracedTurnCount === 0 ? "initial" : "continue";
18390
+ const response = await traceOpenRouterCallModelTurn({
18391
+ fn: async () => {
18392
+ const nextResponse = await originalGetInitialResponse(...args2);
18393
+ tracedTurnCount++;
18394
+ return nextResponse;
18395
+ },
18396
+ parentSpan: span,
18397
+ request: getOpenRouterResolvedRequest(resultLike, request),
18398
+ step,
18399
+ stepType
18400
+ });
18401
+ return response;
18402
+ };
18009
18403
  }
18010
- return {
18011
- output: [
18012
- {
18013
- index: 0,
18014
- message: {
18015
- role,
18016
- content: content || void 0,
18017
- ...toolCalls ? { tool_calls: toolCalls } : {}
18404
+ if (originalMakeFollowupRequest) {
18405
+ resultLike.makeFollowupRequest = async (...args2) => {
18406
+ const currentResponse = args2[0];
18407
+ const toolResults = Array.isArray(args2[1]) ? args2[1] : [];
18408
+ const step = tracedTurnCount + 1;
18409
+ const response = await traceOpenRouterCallModelTurn({
18410
+ fn: async () => {
18411
+ const nextResponse = await originalMakeFollowupRequest(...args2);
18412
+ tracedTurnCount++;
18413
+ return nextResponse;
18018
18414
  },
18019
- logprobs: null,
18020
- finish_reason: finishReason
18021
- }
18022
- ],
18023
- metrics
18415
+ parentSpan: span,
18416
+ request: buildOpenRouterFollowupRequest(
18417
+ getOpenRouterResolvedRequest(resultLike, request),
18418
+ currentResponse,
18419
+ toolResults
18420
+ ),
18421
+ step,
18422
+ stepType: "continue"
18423
+ });
18424
+ return response;
18425
+ };
18426
+ }
18427
+ return true;
18428
+ }
18429
+ async function traceOpenRouterCallModelTurn(args) {
18430
+ const context2 = {
18431
+ arguments: [args.request],
18432
+ step: args.step,
18433
+ stepType: args.stepType
18024
18434
  };
18435
+ return await withCurrent(
18436
+ args.parentSpan,
18437
+ () => openRouterChannels.callModelTurn.tracePromise(args.fn, context2)
18438
+ );
18025
18439
  }
18026
- function aggregateOpenRouterResponseStreamEvents(chunks) {
18027
- let finalResponse;
18028
- for (const chunk of chunks) {
18029
- const response = chunk?.response;
18030
- if (!response) {
18031
- continue;
18032
- }
18033
- if (chunk.type === "response.completed" || chunk.type === "response.incomplete" || chunk.type === "response.failed") {
18034
- finalResponse = response;
18440
+ function isWrappedCallModelResult(value) {
18441
+ return Boolean(
18442
+ isObject(value) && value[OPENROUTER_WRAPPED_CALL_MODEL_RESULT]
18443
+ );
18444
+ }
18445
+ function extractOpenRouterCallModelResultMetadata(response, turnCount) {
18446
+ const combined = {
18447
+ ...extractOpenRouterResponseMetadata(response) || {},
18448
+ ...turnCount !== void 0 ? { turn_count: turnCount } : {}
18449
+ };
18450
+ return Object.keys(combined).length > 0 ? combined : void 0;
18451
+ }
18452
+ function getFinalOpenRouterCallModelResponse(result, response) {
18453
+ if (isObject(response)) {
18454
+ return response;
18455
+ }
18456
+ return isObject(result.finalResponse) ? result.finalResponse : void 0;
18457
+ }
18458
+ function getOpenRouterCallModelRounds(result) {
18459
+ if (!Array.isArray(result.allToolExecutionRounds)) {
18460
+ return [];
18461
+ }
18462
+ return result.allToolExecutionRounds.filter((round) => isObject(round)).map((round) => ({
18463
+ response: isObject(round.response) ? round.response : void 0,
18464
+ round: typeof round.round === "number" ? round.round : void 0,
18465
+ toolResults: Array.isArray(round.toolResults) ? round.toolResults : []
18466
+ })).filter((round) => round.response !== void 0);
18467
+ }
18468
+ function aggregateOpenRouterCallModelMetrics(rounds, finalResponse) {
18469
+ const metrics = {};
18470
+ const responses = [
18471
+ ...rounds.map((round) => round.response).filter(isObject),
18472
+ finalResponse
18473
+ ];
18474
+ for (const response of responses) {
18475
+ const responseMetrics = parseOpenRouterMetricsFromUsage(response.usage);
18476
+ for (const [name, value] of Object.entries(responseMetrics)) {
18477
+ metrics[name] = (metrics[name] || 0) + value;
18035
18478
  }
18036
18479
  }
18037
- if (!finalResponse) {
18038
- return {
18039
- output: void 0,
18040
- metrics: {}
18041
- };
18480
+ return metrics;
18481
+ }
18482
+ function buildNextOpenRouterCallModelInput(currentInput, response, toolResults) {
18483
+ const normalizedInput = Array.isArray(currentInput) ? [...currentInput] : currentInput === void 0 ? [] : [currentInput];
18484
+ const responseOutput = Array.isArray(response.output) ? response.output : response.output === void 0 ? [] : [response.output];
18485
+ return [...normalizedInput, ...responseOutput, ...toolResults].map(
18486
+ (entry) => sanitizeOpenRouterLoggedValue(entry)
18487
+ );
18488
+ }
18489
+ function getOpenRouterResolvedRequest(result, request) {
18490
+ if (isObject(result.resolvedRequest)) {
18491
+ return result.resolvedRequest;
18492
+ }
18493
+ return request;
18494
+ }
18495
+ function buildOpenRouterFollowupRequest(request, currentResponse, toolResults) {
18496
+ if (!request) {
18497
+ return void 0;
18042
18498
  }
18043
18499
  return {
18044
- output: extractOpenRouterResponseOutput(finalResponse),
18045
- metrics: parseOpenRouterMetricsFromUsage(finalResponse.usage),
18046
- ...extractOpenRouterResponseMetadata(finalResponse) ? { metadata: extractOpenRouterResponseMetadata(finalResponse) } : {}
18500
+ ...request,
18501
+ input: buildNextOpenRouterCallModelInput(
18502
+ extractOpenRouterCallModelInput(request),
18503
+ isObject(currentResponse) ? currentResponse : {},
18504
+ toolResults
18505
+ ),
18506
+ stream: false
18507
+ };
18508
+ }
18509
+ function wrapAsyncIterableWithSpan(args) {
18510
+ return {
18511
+ [Symbol.asyncIterator]() {
18512
+ const iterator = args.iteratorFactory();
18513
+ return {
18514
+ next(value) {
18515
+ return withCurrent(
18516
+ args.span,
18517
+ () => value === void 0 ? iterator.next() : iterator.next(value)
18518
+ ).then(
18519
+ async (result) => {
18520
+ if (result.done) {
18521
+ await args.finalize();
18522
+ }
18523
+ return result;
18524
+ },
18525
+ (error2) => {
18526
+ args.onError(error2);
18527
+ throw error2;
18528
+ }
18529
+ );
18530
+ },
18531
+ return(value) {
18532
+ if (typeof iterator.return !== "function") {
18533
+ return args.finalize().then(() => ({
18534
+ done: true,
18535
+ value
18536
+ }));
18537
+ }
18538
+ return withCurrent(args.span, () => iterator.return(value)).then(
18539
+ async (result) => {
18540
+ await args.finalize();
18541
+ return result;
18542
+ },
18543
+ (error2) => {
18544
+ args.onError(error2);
18545
+ throw error2;
18546
+ }
18547
+ );
18548
+ },
18549
+ throw(error2) {
18550
+ args.onError(error2);
18551
+ if (typeof iterator.throw !== "function") {
18552
+ return Promise.reject(error2);
18553
+ }
18554
+ return withCurrent(args.span, () => iterator.throw(error2));
18555
+ },
18556
+ [Symbol.asyncIterator]() {
18557
+ return this;
18558
+ }
18559
+ };
18560
+ }
18047
18561
  };
18048
18562
  }
18563
+ function isAsyncIterable4(value) {
18564
+ return !!value && (typeof value === "object" || typeof value === "function") && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
18565
+ }
18566
+ function normalizeError(error2) {
18567
+ return error2 instanceof Error ? error2 : new Error(String(error2));
18568
+ }
18049
18569
 
18050
18570
  // src/instrumentation/braintrust-plugin.ts
18051
18571
  var BraintrustPlugin = class extends BasePlugin {
@@ -18654,7 +19174,8 @@ var ScorerBuilder = class {
18654
19174
  type: "llm_classifier",
18655
19175
  use_cot: opts.useCot,
18656
19176
  choice_scores: opts.choiceScores
18657
- }
19177
+ },
19178
+ ...opts.templateFormat ? { template_format: opts.templateFormat } : {}
18658
19179
  };
18659
19180
  const codePrompt = new CodePrompt(
18660
19181
  this.project,