braintrust 3.6.0 → 3.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dev/dist/index.js +2692 -1472
  2. package/dev/dist/index.mjs +2616 -1396
  3. package/dist/auto-instrumentations/bundler/esbuild.cjs +46 -21
  4. package/dist/auto-instrumentations/bundler/esbuild.mjs +2 -2
  5. package/dist/auto-instrumentations/bundler/rollup.cjs +46 -21
  6. package/dist/auto-instrumentations/bundler/rollup.mjs +2 -2
  7. package/dist/auto-instrumentations/bundler/vite.cjs +46 -21
  8. package/dist/auto-instrumentations/bundler/vite.mjs +2 -2
  9. package/dist/auto-instrumentations/bundler/webpack-loader.cjs +952 -0
  10. package/dist/auto-instrumentations/bundler/webpack-loader.d.ts +53 -0
  11. package/dist/auto-instrumentations/bundler/webpack.cjs +46 -21
  12. package/dist/auto-instrumentations/bundler/webpack.mjs +2 -2
  13. package/dist/auto-instrumentations/{chunk-WOUC73KB.mjs → chunk-NY4CGTN6.mjs} +1 -1
  14. package/dist/auto-instrumentations/{chunk-F7WAXFNM.mjs → chunk-YCKND42U.mjs} +46 -21
  15. package/dist/auto-instrumentations/hook.mjs +77 -26
  16. package/dist/auto-instrumentations/index.cjs +46 -21
  17. package/dist/auto-instrumentations/index.mjs +1 -1
  18. package/dist/browser.d.mts +8 -30
  19. package/dist/browser.d.ts +8 -30
  20. package/dist/browser.js +5051 -6344
  21. package/dist/browser.mjs +5051 -6344
  22. package/dist/cli.js +2622 -1398
  23. package/dist/edge-light.js +9456 -10773
  24. package/dist/edge-light.mjs +9456 -10773
  25. package/dist/index.d.mts +8 -30
  26. package/dist/index.d.ts +8 -30
  27. package/dist/index.js +5078 -6371
  28. package/dist/index.mjs +4870 -6163
  29. package/dist/instrumentation/index.js +2491 -1319
  30. package/dist/instrumentation/index.mjs +2491 -1319
  31. package/dist/workerd.js +9456 -10773
  32. package/dist/workerd.mjs +9456 -10773
  33. package/package.json +6 -2
@@ -24,25 +24,51 @@ function patchTracingChannel(tracingChannelFn) {
24
24
  if (TracingChannel.prototype.tracePromise) {
25
25
  TracingChannel.prototype.tracePromise = function(fn, context = {}, thisArg, ...args) {
26
26
  const { start, end, asyncStart, asyncEnd, error } = this;
27
- function reject2(err) {
27
+ function publishRejected(err) {
28
28
  context.error = err;
29
29
  error?.publish(context);
30
30
  asyncStart?.publish(context);
31
31
  asyncEnd?.publish(context);
32
- return Promise.reject(err);
33
32
  }
34
- function resolve(result) {
33
+ function publishResolved(result) {
35
34
  context.result = result;
36
35
  asyncStart?.publish(context);
37
36
  asyncEnd?.publish(context);
38
- return result;
39
37
  }
40
38
  return start.runStores(context, () => {
41
39
  try {
42
40
  const result = Reflect.apply(fn, thisArg, args);
43
41
  end?.publish(context);
44
42
  if (result && (typeof result === "object" || typeof result === "function") && typeof result.then === "function") {
45
- return result.then(resolve, reject2);
43
+ if (result.constructor === Promise) {
44
+ return result.then(
45
+ (res) => {
46
+ publishResolved(res);
47
+ return res;
48
+ },
49
+ (err) => {
50
+ publishRejected(err);
51
+ return Promise.reject(err);
52
+ }
53
+ );
54
+ }
55
+ void result.then(
56
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
57
+ (resolved) => {
58
+ try {
59
+ publishResolved(resolved);
60
+ } catch {
61
+ }
62
+ },
63
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
64
+ (err) => {
65
+ try {
66
+ publishRejected(err);
67
+ } catch {
68
+ }
69
+ }
70
+ );
71
+ return result;
46
72
  }
47
73
  context.result = result;
48
74
  asyncStart?.publish(context);
@@ -139,7 +165,7 @@ var DefaultTracingChannel = class {
139
165
  }
140
166
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
141
167
  tracePromise(fn, _message, thisArg, ...args) {
142
- return Promise.resolve(fn.apply(thisArg, args));
168
+ return fn.apply(thisArg, args);
143
169
  }
144
170
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
145
171
  traceCallback(fn, _position, _message, thisArg, ...args) {
@@ -159,6 +185,7 @@ var iso = {
159
185
  processOn: (_0, _1) => {
160
186
  },
161
187
  basename: (filepath) => filepath.split(/[\\/]/).pop() || filepath,
188
+ // eslint-disable-next-line no-restricted-properties -- preserving intentional console usage.
162
189
  writeln: (text) => console.log(text)
163
190
  };
164
191
  var isomorph_default = iso;
@@ -1855,6 +1882,15 @@ var InternalAbortError = class extends Error {
1855
1882
  this.name = "InternalAbortError";
1856
1883
  }
1857
1884
  };
1885
+ function filterFrom(record, keys) {
1886
+ const out = {};
1887
+ for (const k of Object.keys(record)) {
1888
+ if (!keys.includes(k)) {
1889
+ out[k] = record[k];
1890
+ }
1891
+ }
1892
+ return out;
1893
+ }
1858
1894
 
1859
1895
  // src/generated_types.ts
1860
1896
  import { z as z6 } from "zod/v3";
@@ -8848,7 +8884,7 @@ function patchStreamIfNeeded(stream, options) {
8848
8884
  if (!completed) {
8849
8885
  completed = true;
8850
8886
  try {
8851
- options.onComplete(chunks);
8887
+ await options.onComplete(chunks);
8852
8888
  } catch (error) {
8853
8889
  console.error("Error in stream onComplete handler:", error);
8854
8890
  }
@@ -8860,7 +8896,7 @@ function patchStreamIfNeeded(stream, options) {
8860
8896
  chunks.push(chunk);
8861
8897
  if (options.onChunk) {
8862
8898
  try {
8863
- options.onChunk(chunk);
8899
+ await options.onChunk(chunk);
8864
8900
  } catch (error) {
8865
8901
  console.error("Error in stream onChunk handler:", error);
8866
8902
  }
@@ -8873,7 +8909,7 @@ function patchStreamIfNeeded(stream, options) {
8873
8909
  completed = true;
8874
8910
  if (options.onError) {
8875
8911
  try {
8876
- options.onError(
8912
+ await options.onError(
8877
8913
  error instanceof Error ? error : new Error(String(error)),
8878
8914
  chunks
8879
8915
  );
@@ -8891,7 +8927,7 @@ function patchStreamIfNeeded(stream, options) {
8891
8927
  if (!completed) {
8892
8928
  completed = true;
8893
8929
  try {
8894
- options.onComplete(chunks);
8930
+ await options.onComplete(chunks);
8895
8931
  } catch (error) {
8896
8932
  console.error("Error in stream onComplete handler:", error);
8897
8933
  }
@@ -8908,7 +8944,7 @@ function patchStreamIfNeeded(stream, options) {
8908
8944
  const error = rawError instanceof Error ? rawError : new Error(String(rawError));
8909
8945
  if (options.onError) {
8910
8946
  try {
8911
- options.onError(error, chunks);
8947
+ await options.onError(error, chunks);
8912
8948
  } catch (handlerError) {
8913
8949
  console.error("Error in stream onError handler:", handlerError);
8914
8950
  }
@@ -9617,51 +9653,28 @@ function traceSyncStreamChannel(channel2, config) {
9617
9653
  }
9618
9654
  const { span, startTime } = spanData;
9619
9655
  const endEvent = event;
9620
- if (config.patchResult?.({
9621
- channelName,
9622
- endEvent,
9623
- result: endEvent.result,
9624
- span,
9625
- startTime
9626
- })) {
9627
- return;
9628
- }
9629
- const stream = endEvent.result;
9630
- if (!isSyncStreamLike(stream)) {
9631
- span.end();
9632
- states.delete(event);
9633
- return;
9634
- }
9635
- let first = true;
9636
- stream.on("chunk", () => {
9637
- if (first) {
9638
- span.log({
9639
- metrics: {
9640
- time_to_first_token: getCurrentUnixTimestamp() - startTime
9641
- }
9642
- });
9643
- first = false;
9644
- }
9645
- });
9646
- stream.on("chatCompletion", (completion) => {
9647
- try {
9648
- if (hasChoices(completion)) {
9649
- span.log({
9650
- output: completion.choices
9651
- });
9652
- }
9653
- } catch (error) {
9654
- console.error(
9655
- `Error extracting chatCompletion for ${channelName}:`,
9656
- error
9657
- );
9656
+ const handleResolvedResult = (result) => {
9657
+ const resolvedEndEvent = {
9658
+ ...endEvent,
9659
+ result
9660
+ };
9661
+ if (config.patchResult?.({
9662
+ channelName,
9663
+ endEvent: resolvedEndEvent,
9664
+ result,
9665
+ span,
9666
+ startTime
9667
+ })) {
9668
+ return;
9658
9669
  }
9659
- });
9660
- stream.on("event", (streamEvent) => {
9661
- if (!config.extractFromEvent) {
9670
+ const stream = result;
9671
+ if (!isSyncStreamLike(stream)) {
9672
+ span.end();
9673
+ states.delete(event);
9662
9674
  return;
9663
9675
  }
9664
- try {
9676
+ let first = true;
9677
+ stream.on("chunk", () => {
9665
9678
  if (first) {
9666
9679
  span.log({
9667
9680
  metrics: {
@@ -9670,25 +9683,55 @@ function traceSyncStreamChannel(channel2, config) {
9670
9683
  });
9671
9684
  first = false;
9672
9685
  }
9673
- const extracted = config.extractFromEvent(streamEvent);
9674
- if (extracted && Object.keys(extracted).length > 0) {
9675
- span.log(extracted);
9686
+ });
9687
+ stream.on("chatCompletion", (completion) => {
9688
+ try {
9689
+ if (hasChoices(completion)) {
9690
+ span.log({
9691
+ output: completion.choices
9692
+ });
9693
+ }
9694
+ } catch (error) {
9695
+ console.error(
9696
+ `Error extracting chatCompletion for ${channelName}:`,
9697
+ error
9698
+ );
9676
9699
  }
9677
- } catch (error) {
9678
- console.error(`Error extracting event for ${channelName}:`, error);
9679
- }
9680
- });
9681
- stream.on("end", () => {
9682
- span.end();
9683
- states.delete(event);
9684
- });
9685
- stream.on("error", (error) => {
9686
- span.log({
9687
- error: error.message
9688
9700
  });
9689
- span.end();
9690
- states.delete(event);
9691
- });
9701
+ stream.on("event", (streamEvent) => {
9702
+ if (!config.extractFromEvent) {
9703
+ return;
9704
+ }
9705
+ try {
9706
+ if (first) {
9707
+ span.log({
9708
+ metrics: {
9709
+ time_to_first_token: getCurrentUnixTimestamp() - startTime
9710
+ }
9711
+ });
9712
+ first = false;
9713
+ }
9714
+ const extracted = config.extractFromEvent(streamEvent);
9715
+ if (extracted && Object.keys(extracted).length > 0) {
9716
+ span.log(extracted);
9717
+ }
9718
+ } catch (error) {
9719
+ console.error(`Error extracting event for ${channelName}:`, error);
9720
+ }
9721
+ });
9722
+ stream.on("end", () => {
9723
+ span.end();
9724
+ states.delete(event);
9725
+ });
9726
+ stream.on("error", (error) => {
9727
+ span.log({
9728
+ error: error.message
9729
+ });
9730
+ span.end();
9731
+ states.delete(event);
9732
+ });
9733
+ };
9734
+ handleResolvedResult(endEvent.result);
9692
9735
  },
9693
9736
  error: (event) => {
9694
9737
  logErrorAndEnd(states, event);
@@ -10468,7 +10511,7 @@ var AnthropicPlugin = class extends BasePlugin {
10468
10511
  this.unsubscribers.push(
10469
10512
  traceStreamingChannel(anthropicChannels.betaMessagesCreate, {
10470
10513
  ...anthropicConfig,
10471
- name: "anthropic.beta.messages.create"
10514
+ name: "anthropic.messages.create"
10472
10515
  })
10473
10516
  );
10474
10517
  }
@@ -10491,9 +10534,12 @@ function parseMetricsFromUsage2(usage) {
10491
10534
  return metrics;
10492
10535
  }
10493
10536
  function aggregateAnthropicStreamChunks(chunks) {
10494
- const deltas = [];
10537
+ const fallbackTextDeltas = [];
10538
+ const contentBlocks = {};
10539
+ const contentBlockDeltas = {};
10495
10540
  let metrics = {};
10496
10541
  let metadata = {};
10542
+ let role;
10497
10543
  for (const event of chunks) {
10498
10544
  switch (event?.type) {
10499
10545
  case "message_start":
@@ -10501,15 +10547,55 @@ function aggregateAnthropicStreamChunks(chunks) {
10501
10547
  const initialMetrics = parseMetricsFromUsage2(event.message.usage);
10502
10548
  metrics = { ...metrics, ...initialMetrics };
10503
10549
  }
10550
+ if (typeof event.message?.role === "string") {
10551
+ role = event.message.role;
10552
+ }
10553
+ break;
10554
+ case "content_block_start":
10555
+ if (event.content_block) {
10556
+ contentBlocks[event.index] = event.content_block;
10557
+ contentBlockDeltas[event.index] = { textDeltas: [], citations: [] };
10558
+ }
10504
10559
  break;
10505
- case "content_block_delta":
10506
- if (event.delta?.type === "text_delta") {
10507
- const text = event.delta.text;
10560
+ case "content_block_delta": {
10561
+ const acc = contentBlockDeltas[event.index];
10562
+ const delta = event.delta;
10563
+ if (!delta) break;
10564
+ if (delta.type === "text_delta" && "text" in delta) {
10565
+ const text = delta.text;
10508
10566
  if (text) {
10509
- deltas.push(text);
10567
+ if (acc !== void 0) {
10568
+ acc.textDeltas.push(text);
10569
+ } else {
10570
+ fallbackTextDeltas.push(text);
10571
+ }
10572
+ }
10573
+ } else if (delta.type === "input_json_delta" && "partial_json" in delta) {
10574
+ const partialJson = delta.partial_json;
10575
+ if (partialJson && acc !== void 0) {
10576
+ acc.textDeltas.push(partialJson);
10577
+ }
10578
+ } else if (delta.type === "thinking_delta" && "thinking" in delta) {
10579
+ const thinking = delta.thinking;
10580
+ if (thinking && acc !== void 0) {
10581
+ acc.textDeltas.push(thinking);
10582
+ }
10583
+ } else if (delta.type === "citations_delta" && "citation" in delta) {
10584
+ const citation = delta.citation;
10585
+ if (citation && acc !== void 0) {
10586
+ acc.citations.push(citation);
10510
10587
  }
10511
10588
  }
10512
10589
  break;
10590
+ }
10591
+ case "content_block_stop":
10592
+ finalizeContentBlock(
10593
+ event.index,
10594
+ contentBlocks,
10595
+ contentBlockDeltas,
10596
+ fallbackTextDeltas
10597
+ );
10598
+ break;
10513
10599
  case "message_delta":
10514
10600
  if (event.usage) {
10515
10601
  const finalMetrics = parseMetricsFromUsage2(event.usage);
@@ -10521,7 +10607,21 @@ function aggregateAnthropicStreamChunks(chunks) {
10521
10607
  break;
10522
10608
  }
10523
10609
  }
10524
- const output = deltas.join("");
10610
+ const orderedContent = Object.entries(contentBlocks).map(([index, block]) => ({
10611
+ block,
10612
+ index: Number(index)
10613
+ })).filter(({ block }) => block !== void 0).sort((left, right) => left.index - right.index).map(({ block }) => block);
10614
+ let output = fallbackTextDeltas.join("");
10615
+ if (orderedContent.length > 0) {
10616
+ if (orderedContent.every(isTextContentBlock) && orderedContent.every((block) => !block.citations?.length)) {
10617
+ output = orderedContent.map((block) => block.text).join("");
10618
+ } else {
10619
+ output = {
10620
+ ...role ? { role } : {},
10621
+ content: orderedContent
10622
+ };
10623
+ }
10624
+ }
10525
10625
  const finalized = finalizeAnthropicTokens(metrics);
10526
10626
  const filteredMetrics = Object.fromEntries(
10527
10627
  Object.entries(finalized).filter(
@@ -10534,6 +10634,61 @@ function aggregateAnthropicStreamChunks(chunks) {
10534
10634
  metadata
10535
10635
  };
10536
10636
  }
10637
+ function finalizeContentBlock(index, contentBlocks, contentBlockDeltas, fallbackTextDeltas) {
10638
+ const contentBlock = contentBlocks[index];
10639
+ if (!contentBlock) {
10640
+ return;
10641
+ }
10642
+ const acc = contentBlockDeltas[index];
10643
+ const text = acc?.textDeltas.join("") ?? "";
10644
+ if (isToolUseContentBlock(contentBlock)) {
10645
+ if (!text) {
10646
+ return;
10647
+ }
10648
+ try {
10649
+ contentBlocks[index] = {
10650
+ ...contentBlock,
10651
+ input: JSON.parse(text)
10652
+ };
10653
+ } catch {
10654
+ fallbackTextDeltas.push(text);
10655
+ delete contentBlocks[index];
10656
+ }
10657
+ return;
10658
+ }
10659
+ if (isTextContentBlock(contentBlock)) {
10660
+ if (!text) {
10661
+ delete contentBlocks[index];
10662
+ return;
10663
+ }
10664
+ const updated = { ...contentBlock, text };
10665
+ if (acc?.citations.length) {
10666
+ updated.citations = acc.citations;
10667
+ }
10668
+ contentBlocks[index] = updated;
10669
+ return;
10670
+ }
10671
+ if (isThinkingContentBlock(contentBlock)) {
10672
+ if (!text) {
10673
+ delete contentBlocks[index];
10674
+ return;
10675
+ }
10676
+ contentBlocks[index] = {
10677
+ ...contentBlock,
10678
+ thinking: text
10679
+ };
10680
+ return;
10681
+ }
10682
+ }
10683
+ function isTextContentBlock(contentBlock) {
10684
+ return contentBlock.type === "text";
10685
+ }
10686
+ function isToolUseContentBlock(contentBlock) {
10687
+ return contentBlock.type === "tool_use";
10688
+ }
10689
+ function isThinkingContentBlock(contentBlock) {
10690
+ return contentBlock.type === "thinking";
10691
+ }
10537
10692
  function isAnthropicBase64ContentBlock(input) {
10538
10693
  return (input.type === "image" || input.type === "document") && isObject(input.source) && input.source.type === "base64";
10539
10694
  }
@@ -10588,15 +10743,6 @@ function coalesceInput(messages, system) {
10588
10743
  }
10589
10744
  return input;
10590
10745
  }
10591
- function filterFrom(obj, fieldsToRemove) {
10592
- const result = {};
10593
- for (const [key, value] of Object.entries(obj)) {
10594
- if (!fieldsToRemove.includes(key)) {
10595
- result[key] = value;
10596
- }
10597
- }
10598
- return result;
10599
- }
10600
10746
 
10601
10747
  // src/wrappers/ai-sdk/normalize-logged-output.ts
10602
10748
  var REMOVE_NORMALIZED_VALUE = Symbol("braintrust.ai-sdk.remove-normalized");
@@ -10710,10 +10856,6 @@ var aiSDKChannels = defineChannels("ai", {
10710
10856
  channelName: "streamText",
10711
10857
  kind: "async"
10712
10858
  }),
10713
- streamTextSync: channel({
10714
- channelName: "streamText.sync",
10715
- kind: "sync-stream"
10716
- }),
10717
10859
  generateObject: channel({
10718
10860
  channelName: "generateObject",
10719
10861
  kind: "async"
@@ -10722,10 +10864,6 @@ var aiSDKChannels = defineChannels("ai", {
10722
10864
  channelName: "streamObject",
10723
10865
  kind: "async"
10724
10866
  }),
10725
- streamObjectSync: channel({
10726
- channelName: "streamObject.sync",
10727
- kind: "sync-stream"
10728
- }),
10729
10867
  agentGenerate: channel({
10730
10868
  channelName: "Agent.generate",
10731
10869
  kind: "async"
@@ -10761,6 +10899,9 @@ var DEFAULT_DENY_OUTPUT_PATHS = [
10761
10899
  ];
10762
10900
  var AUTO_PATCHED_MODEL = Symbol.for("braintrust.ai-sdk.auto-patched-model");
10763
10901
  var AUTO_PATCHED_TOOL = Symbol.for("braintrust.ai-sdk.auto-patched-tool");
10902
+ var RUNTIME_DENY_OUTPUT_PATHS = Symbol.for(
10903
+ "braintrust.ai-sdk.deny-output-paths"
10904
+ );
10764
10905
  var AISDKPlugin = class extends BasePlugin {
10765
10906
  config;
10766
10907
  constructor(config = {}) {
@@ -10782,7 +10923,10 @@ var AISDKPlugin = class extends BasePlugin {
10782
10923
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10783
10924
  extractOutput: (result, endEvent) => {
10784
10925
  finalizeAISDKChildTracing(endEvent);
10785
- return processAISDKOutput(result, denyOutputPaths);
10926
+ return processAISDKOutput(
10927
+ result,
10928
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
10929
+ );
10786
10930
  },
10787
10931
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
10788
10932
  aggregateChunks: aggregateAISDKChunks
@@ -10793,25 +10937,14 @@ var AISDKPlugin = class extends BasePlugin {
10793
10937
  name: "streamText",
10794
10938
  type: "llm" /* LLM */,
10795
10939
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10796
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
10940
+ extractOutput: (result, endEvent) => processAISDKOutput(
10941
+ result,
10942
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
10943
+ ),
10797
10944
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
10798
10945
  aggregateChunks: aggregateAISDKChunks,
10799
10946
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
10800
- denyOutputPaths,
10801
- endEvent,
10802
- result,
10803
- span,
10804
- startTime
10805
- })
10806
- })
10807
- );
10808
- this.unsubscribers.push(
10809
- traceSyncStreamChannel(aiSDKChannels.streamTextSync, {
10810
- name: "streamText",
10811
- type: "llm" /* LLM */,
10812
- extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10813
- patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
10814
- denyOutputPaths,
10947
+ defaultDenyOutputPaths: denyOutputPaths,
10815
10948
  endEvent,
10816
10949
  result,
10817
10950
  span,
@@ -10826,7 +10959,10 @@ var AISDKPlugin = class extends BasePlugin {
10826
10959
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10827
10960
  extractOutput: (result, endEvent) => {
10828
10961
  finalizeAISDKChildTracing(endEvent);
10829
- return processAISDKOutput(result, denyOutputPaths);
10962
+ return processAISDKOutput(
10963
+ result,
10964
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
10965
+ );
10830
10966
  },
10831
10967
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
10832
10968
  aggregateChunks: aggregateAISDKChunks
@@ -10837,25 +10973,14 @@ var AISDKPlugin = class extends BasePlugin {
10837
10973
  name: "streamObject",
10838
10974
  type: "llm" /* LLM */,
10839
10975
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10840
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
10976
+ extractOutput: (result, endEvent) => processAISDKOutput(
10977
+ result,
10978
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
10979
+ ),
10841
10980
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
10842
10981
  aggregateChunks: aggregateAISDKChunks,
10843
10982
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
10844
- denyOutputPaths,
10845
- endEvent,
10846
- result,
10847
- span,
10848
- startTime
10849
- })
10850
- })
10851
- );
10852
- this.unsubscribers.push(
10853
- traceSyncStreamChannel(aiSDKChannels.streamObjectSync, {
10854
- name: "streamObject",
10855
- type: "llm" /* LLM */,
10856
- extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10857
- patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
10858
- denyOutputPaths,
10983
+ defaultDenyOutputPaths: denyOutputPaths,
10859
10984
  endEvent,
10860
10985
  result,
10861
10986
  span,
@@ -10870,7 +10995,10 @@ var AISDKPlugin = class extends BasePlugin {
10870
10995
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10871
10996
  extractOutput: (result, endEvent) => {
10872
10997
  finalizeAISDKChildTracing(endEvent);
10873
- return processAISDKOutput(result, denyOutputPaths);
10998
+ return processAISDKOutput(
10999
+ result,
11000
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
11001
+ );
10874
11002
  },
10875
11003
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
10876
11004
  aggregateChunks: aggregateAISDKChunks
@@ -10881,11 +11009,14 @@ var AISDKPlugin = class extends BasePlugin {
10881
11009
  name: "Agent.stream",
10882
11010
  type: "llm" /* LLM */,
10883
11011
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10884
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
11012
+ extractOutput: (result, endEvent) => processAISDKOutput(
11013
+ result,
11014
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
11015
+ ),
10885
11016
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
10886
11017
  aggregateChunks: aggregateAISDKChunks,
10887
11018
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
10888
- denyOutputPaths,
11019
+ defaultDenyOutputPaths: denyOutputPaths,
10889
11020
  endEvent,
10890
11021
  result,
10891
11022
  span,
@@ -10900,7 +11031,10 @@ var AISDKPlugin = class extends BasePlugin {
10900
11031
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10901
11032
  extractOutput: (result, endEvent) => {
10902
11033
  finalizeAISDKChildTracing(endEvent);
10903
- return processAISDKOutput(result, denyOutputPaths);
11034
+ return processAISDKOutput(
11035
+ result,
11036
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
11037
+ );
10904
11038
  },
10905
11039
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
10906
11040
  aggregateChunks: aggregateAISDKChunks
@@ -10911,11 +11045,14 @@ var AISDKPlugin = class extends BasePlugin {
10911
11045
  name: "ToolLoopAgent.stream",
10912
11046
  type: "llm" /* LLM */,
10913
11047
  extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10914
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
11048
+ extractOutput: (result, endEvent) => processAISDKOutput(
11049
+ result,
11050
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
11051
+ ),
10915
11052
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
10916
11053
  aggregateChunks: aggregateAISDKChunks,
10917
11054
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
10918
- denyOutputPaths,
11055
+ defaultDenyOutputPaths: denyOutputPaths,
10919
11056
  endEvent,
10920
11057
  result,
10921
11058
  span,
@@ -10925,77 +11062,375 @@ var AISDKPlugin = class extends BasePlugin {
10925
11062
  );
10926
11063
  }
10927
11064
  };
10928
- function processAISDKInput(params) {
10929
- if (!params) return params;
10930
- const input = processInputAttachments(params);
10931
- if (!input || typeof input !== "object" || Array.isArray(input)) {
10932
- return input;
11065
+ function resolveDenyOutputPaths(event, defaultDenyOutputPaths) {
11066
+ if (Array.isArray(event?.denyOutputPaths)) {
11067
+ return event.denyOutputPaths;
10933
11068
  }
10934
- const { tools: _tools, ...rest } = input;
10935
- return rest;
10936
- }
10937
- function prepareAISDKInput(params, event, span, denyOutputPaths) {
10938
- const input = processAISDKInput(params);
10939
- const metadata = extractMetadataFromParams(params, event.self);
10940
- const childTracing = prepareAISDKChildTracing(
10941
- params,
10942
- event.self,
10943
- span,
10944
- denyOutputPaths
10945
- );
10946
- event.__braintrust_ai_sdk_model_wrapped = childTracing.modelWrapped;
10947
- if (childTracing.cleanup) {
10948
- event.__braintrust_ai_sdk_cleanup = childTracing.cleanup;
11069
+ const firstArgument = event?.arguments && event.arguments.length > 0 ? event.arguments[0] : void 0;
11070
+ if (!firstArgument || typeof firstArgument !== "object") {
11071
+ return defaultDenyOutputPaths;
10949
11072
  }
10950
- return {
10951
- input,
10952
- metadata
10953
- };
10954
- }
10955
- function extractTopLevelAISDKMetrics(result, event, startTime) {
10956
- const metrics = hasModelChildTracing(event) ? {} : extractTokenMetrics(result);
10957
- if (startTime) {
10958
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
11073
+ const runtimeDenyOutputPaths = firstArgument[RUNTIME_DENY_OUTPUT_PATHS];
11074
+ if (Array.isArray(runtimeDenyOutputPaths) && runtimeDenyOutputPaths.every((path2) => typeof path2 === "string")) {
11075
+ return runtimeDenyOutputPaths;
10959
11076
  }
10960
- return metrics;
10961
- }
10962
- function hasModelChildTracing(event) {
10963
- return event?.__braintrust_ai_sdk_model_wrapped === true;
11077
+ return defaultDenyOutputPaths;
10964
11078
  }
10965
- function extractMetadataFromParams(params, self) {
10966
- const metadata = {
10967
- braintrust: {
10968
- integration_name: "ai-sdk",
10969
- sdk_language: "typescript"
10970
- }
10971
- };
10972
- 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;
10973
- const { model, provider } = serializeModelWithProvider(
10974
- params.model ?? agentModel
10975
- );
10976
- if (model) {
10977
- metadata.model = model;
11079
+ var isZodSchema2 = (value) => {
11080
+ return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
11081
+ };
11082
+ var serializeZodSchema2 = (schema) => {
11083
+ try {
11084
+ return zodToJsonSchema(schema);
11085
+ } catch {
11086
+ return {
11087
+ type: "object",
11088
+ description: "Zod schema (conversion failed)"
11089
+ };
10978
11090
  }
10979
- if (provider) {
10980
- metadata.provider = provider;
11091
+ };
11092
+ var isOutputObject = (value) => {
11093
+ if (value == null || typeof value !== "object") {
11094
+ return false;
10981
11095
  }
10982
- const tools = serializeAISDKToolsForLogging(params.tools);
10983
- if (tools) {
10984
- metadata.tools = tools;
11096
+ const output = value;
11097
+ if (!("responseFormat" in output)) {
11098
+ return false;
10985
11099
  }
10986
- return metadata;
10987
- }
10988
- function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
10989
- const cleanup = [];
10990
- const patchedModels = /* @__PURE__ */ new WeakSet();
10991
- const patchedTools = /* @__PURE__ */ new WeakSet();
10992
- let modelWrapped = false;
10993
- const patchModel = (model) => {
10994
- const resolvedModel = resolveAISDKModel(model);
10995
- if (!resolvedModel || typeof resolvedModel !== "object" || typeof resolvedModel.doGenerate !== "function" || patchedModels.has(resolvedModel) || resolvedModel[AUTO_PATCHED_MODEL]) {
10996
- return;
11100
+ if (output.type === "object" || output.type === "text") {
11101
+ return true;
11102
+ }
11103
+ if (typeof output.responseFormat === "function" || typeof output.responseFormat === "object") {
11104
+ return true;
11105
+ }
11106
+ return false;
11107
+ };
11108
+ var serializeOutputObject = (output, model) => {
11109
+ try {
11110
+ const result = {
11111
+ response_format: null
11112
+ };
11113
+ if (output.type) {
11114
+ result.type = output.type;
11115
+ }
11116
+ let responseFormat;
11117
+ if (typeof output.responseFormat === "function") {
11118
+ const mockModelForSchema = {
11119
+ supportsStructuredOutputs: true,
11120
+ ...model && typeof model === "object" ? model : {}
11121
+ };
11122
+ responseFormat = output.responseFormat({ model: mockModelForSchema });
11123
+ } else if (output.responseFormat != null && typeof output.responseFormat === "object") {
11124
+ responseFormat = output.responseFormat;
11125
+ }
11126
+ if (responseFormat) {
11127
+ if (typeof responseFormat.then === "function") {
11128
+ result.response_format = Promise.resolve(responseFormat).then(
11129
+ (resolved) => {
11130
+ if (resolved.schema && isZodSchema2(resolved.schema)) {
11131
+ return {
11132
+ ...resolved,
11133
+ schema: serializeZodSchema2(resolved.schema)
11134
+ };
11135
+ }
11136
+ return resolved;
11137
+ }
11138
+ );
11139
+ } else {
11140
+ const syncResponseFormat = responseFormat;
11141
+ if (syncResponseFormat.schema && isZodSchema2(syncResponseFormat.schema)) {
11142
+ responseFormat = {
11143
+ ...syncResponseFormat,
11144
+ schema: serializeZodSchema2(syncResponseFormat.schema)
11145
+ };
11146
+ }
11147
+ result.response_format = responseFormat;
11148
+ }
10997
11149
  }
10998
- patchedModels.add(resolvedModel);
11150
+ return result;
11151
+ } catch {
11152
+ return {
11153
+ response_format: null
11154
+ };
11155
+ }
11156
+ };
11157
+ var processInputAttachmentsSync = (input) => {
11158
+ if (!input) return { input };
11159
+ const processed = { ...input };
11160
+ if (input.messages && Array.isArray(input.messages)) {
11161
+ processed.messages = input.messages.map(processMessage);
11162
+ }
11163
+ if (input.prompt && typeof input.prompt === "object") {
11164
+ if (Array.isArray(input.prompt)) {
11165
+ processed.prompt = input.prompt.map(processMessage);
11166
+ } else {
11167
+ processed.prompt = processPromptContent(input.prompt);
11168
+ }
11169
+ }
11170
+ if (input.schema && isZodSchema2(input.schema)) {
11171
+ processed.schema = serializeZodSchema2(input.schema);
11172
+ }
11173
+ if (input.callOptionsSchema && isZodSchema2(input.callOptionsSchema)) {
11174
+ processed.callOptionsSchema = serializeZodSchema2(input.callOptionsSchema);
11175
+ }
11176
+ if (input.tools) {
11177
+ processed.tools = serializeAISDKToolsForLogging(input.tools);
11178
+ }
11179
+ let outputPromise;
11180
+ if (input.output && isOutputObject(input.output)) {
11181
+ const serialized = serializeOutputObject(input.output, input.model);
11182
+ if (serialized.response_format && typeof serialized.response_format.then === "function") {
11183
+ processed.output = { ...serialized, response_format: {} };
11184
+ outputPromise = serialized.response_format.then(
11185
+ (resolvedFormat) => ({
11186
+ output: { ...serialized, response_format: resolvedFormat }
11187
+ })
11188
+ );
11189
+ } else {
11190
+ processed.output = serialized;
11191
+ }
11192
+ }
11193
+ if ("prepareCall" in processed && typeof processed.prepareCall === "function") {
11194
+ processed.prepareCall = "[Function]";
11195
+ }
11196
+ return { input: processed, outputPromise };
11197
+ };
11198
+ var processMessage = (message) => {
11199
+ if (!message || typeof message !== "object") return message;
11200
+ if (Array.isArray(message.content)) {
11201
+ return {
11202
+ ...message,
11203
+ content: message.content.map(processContentPart)
11204
+ };
11205
+ }
11206
+ if (typeof message.content === "object" && message.content !== null) {
11207
+ return {
11208
+ ...message,
11209
+ content: processContentPart(message.content)
11210
+ };
11211
+ }
11212
+ return message;
11213
+ };
11214
+ var processPromptContent = (prompt) => {
11215
+ if (Array.isArray(prompt)) {
11216
+ return prompt.map(processContentPart);
11217
+ }
11218
+ if (prompt.content) {
11219
+ if (Array.isArray(prompt.content)) {
11220
+ return {
11221
+ ...prompt,
11222
+ content: prompt.content.map(processContentPart)
11223
+ };
11224
+ } else if (typeof prompt.content === "object") {
11225
+ return {
11226
+ ...prompt,
11227
+ content: processContentPart(prompt.content)
11228
+ };
11229
+ }
11230
+ }
11231
+ return prompt;
11232
+ };
11233
+ var processContentPart = (part) => {
11234
+ if (!part || typeof part !== "object") return part;
11235
+ try {
11236
+ if (part.type === "image" && part.image) {
11237
+ const imageAttachment = convertImageToAttachment(
11238
+ part.image,
11239
+ part.mimeType || part.mediaType
11240
+ );
11241
+ if (imageAttachment) {
11242
+ return {
11243
+ ...part,
11244
+ image: imageAttachment
11245
+ };
11246
+ }
11247
+ }
11248
+ if (part.type === "file" && part.data && (part.mimeType || part.mediaType)) {
11249
+ const fileAttachment = convertDataToAttachment(
11250
+ part.data,
11251
+ part.mimeType || part.mediaType,
11252
+ part.name || part.filename
11253
+ );
11254
+ if (fileAttachment) {
11255
+ return {
11256
+ ...part,
11257
+ data: fileAttachment
11258
+ };
11259
+ }
11260
+ }
11261
+ if (part.type === "image_url" && part.image_url) {
11262
+ if (typeof part.image_url === "object" && part.image_url.url) {
11263
+ const imageAttachment = convertImageToAttachment(part.image_url.url);
11264
+ if (imageAttachment) {
11265
+ return {
11266
+ ...part,
11267
+ image_url: {
11268
+ ...part.image_url,
11269
+ url: imageAttachment
11270
+ }
11271
+ };
11272
+ }
11273
+ }
11274
+ }
11275
+ } catch (error) {
11276
+ console.warn("Error processing content part:", error);
11277
+ }
11278
+ return part;
11279
+ };
11280
+ var convertImageToAttachment = (image, explicitMimeType) => {
11281
+ try {
11282
+ if (typeof image === "string" && image.startsWith("data:")) {
11283
+ const [mimeTypeSection, base64Data] = image.split(",");
11284
+ const mimeType = mimeTypeSection.match(/data:(.*?);/)?.[1];
11285
+ if (mimeType && base64Data) {
11286
+ const blob = convertDataToBlob(base64Data, mimeType);
11287
+ if (blob) {
11288
+ return new Attachment({
11289
+ data: blob,
11290
+ filename: `image.${getExtensionFromMediaType(mimeType)}`,
11291
+ contentType: mimeType
11292
+ });
11293
+ }
11294
+ }
11295
+ }
11296
+ if (explicitMimeType) {
11297
+ if (image instanceof Uint8Array) {
11298
+ return new Attachment({
11299
+ data: new Blob([image], { type: explicitMimeType }),
11300
+ filename: `image.${getExtensionFromMediaType(explicitMimeType)}`,
11301
+ contentType: explicitMimeType
11302
+ });
11303
+ }
11304
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer(image)) {
11305
+ return new Attachment({
11306
+ data: new Blob([image], { type: explicitMimeType }),
11307
+ filename: `image.${getExtensionFromMediaType(explicitMimeType)}`,
11308
+ contentType: explicitMimeType
11309
+ });
11310
+ }
11311
+ }
11312
+ if (image instanceof Blob && image.type) {
11313
+ return new Attachment({
11314
+ data: image,
11315
+ filename: `image.${getExtensionFromMediaType(image.type)}`,
11316
+ contentType: image.type
11317
+ });
11318
+ }
11319
+ if (image instanceof Attachment) {
11320
+ return image;
11321
+ }
11322
+ } catch (error) {
11323
+ console.warn("Error converting image to attachment:", error);
11324
+ }
11325
+ return null;
11326
+ };
11327
+ var convertDataToAttachment = (data, mimeType, filename) => {
11328
+ if (!mimeType) return null;
11329
+ try {
11330
+ let blob = null;
11331
+ if (typeof data === "string" && data.startsWith("data:")) {
11332
+ const [, base64Data] = data.split(",");
11333
+ if (base64Data) {
11334
+ blob = convertDataToBlob(base64Data, mimeType);
11335
+ }
11336
+ } else if (typeof data === "string" && data.length > 0) {
11337
+ blob = convertDataToBlob(data, mimeType);
11338
+ } else if (data instanceof Uint8Array) {
11339
+ blob = new Blob([data], { type: mimeType });
11340
+ } else if (typeof Buffer !== "undefined" && Buffer.isBuffer(data)) {
11341
+ blob = new Blob([data], { type: mimeType });
11342
+ } else if (data instanceof Blob) {
11343
+ blob = data;
11344
+ }
11345
+ if (blob) {
11346
+ return new Attachment({
11347
+ data: blob,
11348
+ filename: filename || `file.${getExtensionFromMediaType(mimeType)}`,
11349
+ contentType: mimeType
11350
+ });
11351
+ }
11352
+ } catch (error) {
11353
+ console.warn("Error converting data to attachment:", error);
11354
+ }
11355
+ return null;
11356
+ };
11357
+ function processAISDKInput(params) {
11358
+ return processInputAttachmentsSync(params);
11359
+ }
11360
+ function prepareAISDKInput(params, event, span, defaultDenyOutputPaths) {
11361
+ const { input, outputPromise } = processAISDKInput(params);
11362
+ if (outputPromise && input && typeof input === "object") {
11363
+ outputPromise.then((resolvedData) => {
11364
+ span.log({
11365
+ input: {
11366
+ ...input,
11367
+ ...resolvedData
11368
+ }
11369
+ });
11370
+ }).catch(() => {
11371
+ });
11372
+ }
11373
+ const metadata = extractMetadataFromParams(params, event.self);
11374
+ const childTracing = prepareAISDKChildTracing(
11375
+ params,
11376
+ event.self,
11377
+ span,
11378
+ defaultDenyOutputPaths,
11379
+ event.aiSDK
11380
+ );
11381
+ event.modelWrapped = childTracing.modelWrapped;
11382
+ if (childTracing.cleanup) {
11383
+ event.__braintrust_ai_sdk_cleanup = childTracing.cleanup;
11384
+ }
11385
+ return {
11386
+ input,
11387
+ metadata
11388
+ };
11389
+ }
11390
+ function extractTopLevelAISDKMetrics(result, event, startTime) {
11391
+ const metrics = hasModelChildTracing(event) ? {} : extractTokenMetrics(result);
11392
+ if (startTime) {
11393
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
11394
+ }
11395
+ return metrics;
11396
+ }
11397
+ function hasModelChildTracing(event) {
11398
+ return event?.modelWrapped === true || event?.__braintrust_ai_sdk_model_wrapped === true;
11399
+ }
11400
+ function extractMetadataFromParams(params, self) {
11401
+ const metadata = {
11402
+ braintrust: {
11403
+ integration_name: "ai-sdk",
11404
+ sdk_language: "typescript"
11405
+ }
11406
+ };
11407
+ const agentModel = self && typeof self === "object" && "model" in self && self.model ? self.model : self && typeof self === "object" && "settings" in self && self.settings?.model ? self.settings?.model : void 0;
11408
+ const { model, provider } = serializeModelWithProvider(
11409
+ params.model ?? agentModel
11410
+ );
11411
+ if (model) {
11412
+ metadata.model = model;
11413
+ }
11414
+ if (provider) {
11415
+ metadata.provider = provider;
11416
+ }
11417
+ const tools = serializeAISDKToolsForLogging(params.tools);
11418
+ if (tools) {
11419
+ metadata.tools = tools;
11420
+ }
11421
+ return metadata;
11422
+ }
11423
+ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiSDK) {
11424
+ const cleanup = [];
11425
+ const patchedModels = /* @__PURE__ */ new WeakSet();
11426
+ const patchedTools = /* @__PURE__ */ new WeakSet();
11427
+ let modelWrapped = false;
11428
+ const patchModel = (model) => {
11429
+ const resolvedModel = resolveAISDKModel(model, aiSDK);
11430
+ if (!resolvedModel || typeof resolvedModel !== "object" || typeof resolvedModel.doGenerate !== "function" || patchedModels.has(resolvedModel) || resolvedModel[AUTO_PATCHED_MODEL]) {
11431
+ return resolvedModel;
11432
+ }
11433
+ patchedModels.add(resolvedModel);
10999
11434
  resolvedModel[AUTO_PATCHED_MODEL] = true;
11000
11435
  modelWrapped = true;
11001
11436
  const originalDoGenerate = resolvedModel.doGenerate;
@@ -11022,7 +11457,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
11022
11457
  type: "llm" /* LLM */
11023
11458
  },
11024
11459
  event: {
11025
- input: processAISDKInput(options),
11460
+ input: processAISDKInput(options).input,
11026
11461
  metadata: baseMetadata
11027
11462
  }
11028
11463
  }
@@ -11036,7 +11471,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
11036
11471
  type: "llm" /* LLM */
11037
11472
  },
11038
11473
  event: {
11039
- input: processAISDKInput(options),
11474
+ input: processAISDKInput(options).input,
11040
11475
  metadata: baseMetadata
11041
11476
  }
11042
11477
  });
@@ -11044,6 +11479,8 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
11044
11479
  span,
11045
11480
  () => Reflect.apply(originalDoStream, resolvedModel, [options])
11046
11481
  );
11482
+ const streamStartTime = getCurrentUnixTimestamp();
11483
+ let firstChunkTime;
11047
11484
  const output = {};
11048
11485
  let text = "";
11049
11486
  let reasoning = "";
@@ -11051,6 +11488,9 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
11051
11488
  let object = void 0;
11052
11489
  const transformStream = new TransformStream({
11053
11490
  transform(chunk, controller) {
11491
+ if (firstChunkTime === void 0) {
11492
+ firstChunkTime = getCurrentUnixTimestamp();
11493
+ }
11054
11494
  switch (chunk.type) {
11055
11495
  case "text-delta":
11056
11496
  text += extractTextDelta(chunk);
@@ -11091,12 +11531,19 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
11091
11531
  if (object !== void 0) {
11092
11532
  output.object = object;
11093
11533
  }
11534
+ const metrics = extractTokenMetrics(output);
11535
+ if (firstChunkTime !== void 0) {
11536
+ metrics.time_to_first_token = Math.max(
11537
+ firstChunkTime - streamStartTime,
11538
+ 1e-6
11539
+ );
11540
+ }
11094
11541
  span.log({
11095
11542
  output: processAISDKOutput(
11096
11543
  output,
11097
11544
  denyOutputPaths
11098
11545
  ),
11099
- metrics: extractTokenMetrics(output),
11546
+ metrics,
11100
11547
  ...buildResolvedMetadataPayload(output)
11101
11548
  });
11102
11549
  span.end();
@@ -11118,6 +11565,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
11118
11565
  }
11119
11566
  delete resolvedModel[AUTO_PATCHED_MODEL];
11120
11567
  });
11568
+ return resolvedModel;
11121
11569
  };
11122
11570
  const patchTool = (tool, name) => {
11123
11571
  if (tool == null || typeof tool !== "object" || !("execute" in tool) || typeof tool.execute !== "function" || patchedTools.has(tool) || tool[AUTO_PATCHED_TOOL]) {
@@ -11190,17 +11638,26 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
11190
11638
  }
11191
11639
  };
11192
11640
  if (params && typeof params === "object") {
11193
- patchModel(params.model);
11641
+ const patchedParamModel = patchModel(params.model);
11642
+ if (typeof params.model === "string" && patchedParamModel && typeof patchedParamModel === "object") {
11643
+ params.model = patchedParamModel;
11644
+ }
11194
11645
  patchTools(params.tools);
11195
11646
  }
11196
11647
  if (self && typeof self === "object") {
11197
11648
  const selfRecord = self;
11198
11649
  if (selfRecord.model !== void 0) {
11199
- patchModel(selfRecord.model);
11650
+ const patchedSelfModel = patchModel(selfRecord.model);
11651
+ if (typeof selfRecord.model === "string" && patchedSelfModel && typeof patchedSelfModel === "object") {
11652
+ selfRecord.model = patchedSelfModel;
11653
+ }
11200
11654
  }
11201
11655
  if (selfRecord.settings && typeof selfRecord.settings === "object") {
11202
11656
  if (selfRecord.settings.model !== void 0) {
11203
- patchModel(selfRecord.settings.model);
11657
+ const patchedSettingsModel = patchModel(selfRecord.settings.model);
11658
+ if (typeof selfRecord.settings.model === "string" && patchedSettingsModel && typeof patchedSettingsModel === "object") {
11659
+ selfRecord.settings.model = patchedSettingsModel;
11660
+ }
11204
11661
  }
11205
11662
  if (selfRecord.settings.tools !== void 0) {
11206
11663
  patchTools(selfRecord.settings.tools);
@@ -11224,63 +11681,173 @@ function finalizeAISDKChildTracing(event) {
11224
11681
  }
11225
11682
  }
11226
11683
  function patchAISDKStreamingResult(args) {
11227
- const { denyOutputPaths, endEvent, result, span, startTime } = args;
11684
+ const { defaultDenyOutputPaths, endEvent, result, span, startTime } = args;
11228
11685
  if (!result || typeof result !== "object") {
11229
11686
  return false;
11230
11687
  }
11231
11688
  const resultRecord = result;
11232
- if (!isReadableStreamLike(resultRecord.baseStream)) {
11689
+ attachKnownResultPromiseHandlers(resultRecord);
11690
+ if (isReadableStreamLike(resultRecord.baseStream)) {
11691
+ let firstChunkTime2;
11692
+ const wrappedBaseStream = resultRecord.baseStream.pipeThrough(
11693
+ new TransformStream({
11694
+ transform(chunk, controller) {
11695
+ if (firstChunkTime2 === void 0) {
11696
+ firstChunkTime2 = getCurrentUnixTimestamp();
11697
+ }
11698
+ controller.enqueue(chunk);
11699
+ },
11700
+ async flush() {
11701
+ const metrics = extractTopLevelAISDKMetrics(result, endEvent);
11702
+ if (metrics.time_to_first_token === void 0 && firstChunkTime2 !== void 0) {
11703
+ metrics.time_to_first_token = firstChunkTime2 - startTime;
11704
+ }
11705
+ const output = await processAISDKStreamingOutput(
11706
+ result,
11707
+ resolveDenyOutputPaths(endEvent, defaultDenyOutputPaths)
11708
+ );
11709
+ const metadata = buildResolvedMetadataPayload(result).metadata;
11710
+ span.log({
11711
+ output,
11712
+ ...metadata ? { metadata } : {},
11713
+ metrics
11714
+ });
11715
+ finalizeAISDKChildTracing(endEvent);
11716
+ span.end();
11717
+ }
11718
+ })
11719
+ );
11720
+ Object.defineProperty(resultRecord, "baseStream", {
11721
+ configurable: true,
11722
+ enumerable: true,
11723
+ value: wrappedBaseStream,
11724
+ writable: true
11725
+ });
11726
+ return true;
11727
+ }
11728
+ const streamField = findAsyncIterableField(resultRecord, [
11729
+ "partialObjectStream",
11730
+ "textStream",
11731
+ "fullStream",
11732
+ "stream"
11733
+ ]);
11734
+ if (!streamField) {
11233
11735
  return false;
11234
11736
  }
11235
11737
  let firstChunkTime;
11236
- const wrappedBaseStream = resultRecord.baseStream.pipeThrough(
11237
- new TransformStream({
11238
- transform(chunk, controller) {
11239
- if (firstChunkTime === void 0) {
11240
- firstChunkTime = getCurrentUnixTimestamp();
11241
- }
11242
- controller.enqueue(chunk);
11243
- },
11244
- async flush() {
11245
- const metrics = extractTopLevelAISDKMetrics(result, endEvent);
11246
- if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
11247
- metrics.time_to_first_token = firstChunkTime - startTime;
11248
- }
11249
- const output = await processAISDKStreamingOutput(
11250
- result,
11251
- denyOutputPaths
11252
- );
11253
- const metadata = buildResolvedMetadataPayload(result).metadata;
11254
- span.log({
11255
- output,
11256
- ...metadata ? { metadata } : {},
11257
- metrics
11258
- });
11259
- finalizeAISDKChildTracing(endEvent);
11260
- span.end();
11738
+ const wrappedStream = createPatchedAsyncIterable(streamField.stream, {
11739
+ onChunk: () => {
11740
+ if (firstChunkTime === void 0) {
11741
+ firstChunkTime = getCurrentUnixTimestamp();
11261
11742
  }
11262
- })
11263
- );
11264
- Object.defineProperty(resultRecord, "baseStream", {
11743
+ },
11744
+ onComplete: async () => {
11745
+ const metrics = extractTopLevelAISDKMetrics(result, endEvent);
11746
+ if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
11747
+ metrics.time_to_first_token = firstChunkTime - startTime;
11748
+ }
11749
+ const output = await processAISDKStreamingOutput(
11750
+ result,
11751
+ resolveDenyOutputPaths(endEvent, defaultDenyOutputPaths)
11752
+ );
11753
+ const metadata = buildResolvedMetadataPayload(result).metadata;
11754
+ span.log({
11755
+ output,
11756
+ ...metadata ? { metadata } : {},
11757
+ metrics
11758
+ });
11759
+ finalizeAISDKChildTracing(endEvent);
11760
+ span.end();
11761
+ },
11762
+ onError: (error) => {
11763
+ span.log({
11764
+ error: error.message
11765
+ });
11766
+ finalizeAISDKChildTracing(endEvent);
11767
+ span.end();
11768
+ }
11769
+ });
11770
+ Object.defineProperty(resultRecord, streamField.field, {
11265
11771
  configurable: true,
11266
11772
  enumerable: true,
11267
- value: wrappedBaseStream,
11773
+ value: wrappedStream,
11268
11774
  writable: true
11269
11775
  });
11270
11776
  return true;
11271
11777
  }
11778
+ function attachKnownResultPromiseHandlers(result) {
11779
+ const promiseLikeFields = [
11780
+ "content",
11781
+ "text",
11782
+ "object",
11783
+ "finishReason",
11784
+ "usage",
11785
+ "totalUsage",
11786
+ "steps"
11787
+ ];
11788
+ for (const field of promiseLikeFields) {
11789
+ try {
11790
+ if (!(field in result)) {
11791
+ continue;
11792
+ }
11793
+ const value = result[field];
11794
+ if (isPromiseLike(value)) {
11795
+ void Promise.resolve(value).catch(() => {
11796
+ });
11797
+ }
11798
+ } catch {
11799
+ }
11800
+ }
11801
+ }
11272
11802
  function isReadableStreamLike(value) {
11273
11803
  return value != null && typeof value === "object" && typeof value.pipeThrough === "function";
11274
11804
  }
11805
+ function isAsyncIterableLike(value) {
11806
+ return value != null && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function";
11807
+ }
11808
+ function findAsyncIterableField(result, candidateFields) {
11809
+ for (const field of candidateFields) {
11810
+ try {
11811
+ const stream = result[field];
11812
+ if (isAsyncIterableLike(stream)) {
11813
+ return { field, stream };
11814
+ }
11815
+ } catch {
11816
+ }
11817
+ }
11818
+ return null;
11819
+ }
11820
+ function createPatchedAsyncIterable(stream, hooks) {
11821
+ return {
11822
+ async *[Symbol.asyncIterator]() {
11823
+ try {
11824
+ for await (const chunk of stream) {
11825
+ hooks.onChunk(chunk);
11826
+ yield chunk;
11827
+ }
11828
+ await hooks.onComplete();
11829
+ } catch (error) {
11830
+ hooks.onError(
11831
+ error instanceof Error ? error : new Error(String(error))
11832
+ );
11833
+ throw error;
11834
+ }
11835
+ }
11836
+ };
11837
+ }
11275
11838
  async function processAISDKStreamingOutput(result, denyOutputPaths) {
11276
11839
  const output = processAISDKOutput(result, denyOutputPaths);
11277
11840
  if (!output || typeof output !== "object") {
11278
11841
  return output;
11279
11842
  }
11280
11843
  const outputRecord = output;
11844
+ const isObjectStreamingResult = result != null && typeof result === "object" && "partialObjectStream" in result;
11281
11845
  try {
11282
- if ("text" in result && typeof result.text === "string") {
11283
- outputRecord.text = result.text;
11846
+ if (!isObjectStreamingResult && "text" in result) {
11847
+ const resolvedText = await Promise.resolve(result.text);
11848
+ if (typeof resolvedText === "string") {
11849
+ outputRecord.text = resolvedText;
11850
+ }
11284
11851
  }
11285
11852
  } catch {
11286
11853
  }
@@ -11293,6 +11860,15 @@ async function processAISDKStreamingOutput(result, denyOutputPaths) {
11293
11860
  }
11294
11861
  } catch {
11295
11862
  }
11863
+ try {
11864
+ if ("finishReason" in result) {
11865
+ const resolvedFinishReason = await Promise.resolve(result.finishReason);
11866
+ if (resolvedFinishReason !== void 0) {
11867
+ outputRecord.finishReason = resolvedFinishReason;
11868
+ }
11869
+ }
11870
+ } catch {
11871
+ }
11296
11872
  return outputRecord;
11297
11873
  }
11298
11874
  function buildAISDKChildMetadata(model) {
@@ -11315,16 +11891,25 @@ function buildResolvedMetadataPayload(result) {
11315
11891
  if (gatewayInfo?.model) {
11316
11892
  metadata.model = gatewayInfo.model;
11317
11893
  }
11318
- if (result.finishReason !== void 0) {
11319
- metadata.finish_reason = result.finishReason;
11894
+ let finishReason;
11895
+ try {
11896
+ finishReason = result.finishReason;
11897
+ } catch {
11898
+ finishReason = void 0;
11899
+ }
11900
+ if (isPromiseLike(finishReason)) {
11901
+ void Promise.resolve(finishReason).catch(() => {
11902
+ });
11903
+ } else if (finishReason !== void 0) {
11904
+ metadata.finish_reason = finishReason;
11320
11905
  }
11321
11906
  return Object.keys(metadata).length > 0 ? { metadata } : {};
11322
11907
  }
11323
- function resolveAISDKModel(model) {
11908
+ function resolveAISDKModel(model, aiSDK) {
11324
11909
  if (typeof model !== "string") {
11325
11910
  return model;
11326
11911
  }
11327
- const provider = globalThis.AI_SDK_DEFAULT_PROVIDER ?? null;
11912
+ const provider = globalThis.AI_SDK_DEFAULT_PROVIDER ?? aiSDK?.gateway ?? null;
11328
11913
  if (provider && typeof provider.languageModel === "function") {
11329
11914
  return provider.languageModel(model);
11330
11915
  }
@@ -11347,15 +11932,15 @@ function processAISDKOutput(output, denyOutputPaths) {
11347
11932
  }
11348
11933
  function extractTokenMetrics(result) {
11349
11934
  const metrics = {};
11350
- let usage = result?.totalUsage || result?.usage;
11351
- if (!usage && result) {
11352
- try {
11353
- if ("totalUsage" in result && typeof result.totalUsage !== "function") {
11354
- usage = result.totalUsage;
11355
- } else if ("usage" in result && typeof result.usage !== "function") {
11356
- usage = result.usage;
11357
- }
11358
- } catch {
11935
+ let usage;
11936
+ const totalUsageValue = safeResultFieldRead(result, "totalUsage");
11937
+ if (totalUsageValue !== void 0 && !isPromiseLike(totalUsageValue)) {
11938
+ usage = totalUsageValue;
11939
+ }
11940
+ if (!usage) {
11941
+ const usageValue = safeResultFieldRead(result, "usage");
11942
+ if (usageValue !== void 0 && !isPromiseLike(usageValue)) {
11943
+ usage = usageValue;
11359
11944
  }
11360
11945
  }
11361
11946
  if (!usage) {
@@ -11393,25 +11978,45 @@ function extractTokenMetrics(result) {
11393
11978
  }
11394
11979
  return metrics;
11395
11980
  }
11396
- function aggregateAISDKChunks(chunks, _result, endEvent) {
11397
- const lastChunk = chunks[chunks.length - 1];
11398
- const output = {};
11399
- let metrics = {};
11400
- let metadata;
11401
- if (lastChunk) {
11402
- metrics = hasModelChildTracing(endEvent) ? {} : extractTokenMetrics(lastChunk);
11403
- metadata = buildResolvedMetadataPayload(lastChunk).metadata;
11404
- if (lastChunk.text !== void 0) {
11405
- output.text = lastChunk.text;
11406
- }
11407
- if (lastChunk.object !== void 0) {
11408
- output.object = lastChunk.object;
11981
+ function safeResultFieldRead(result, field) {
11982
+ return safeSerializableFieldRead(result, field);
11983
+ }
11984
+ function safeSerializableFieldRead(obj, field) {
11985
+ try {
11986
+ const value = obj?.[field];
11987
+ if (isPromiseLike(value)) {
11988
+ void Promise.resolve(value).catch(() => {
11989
+ });
11990
+ return void 0;
11991
+ }
11992
+ return value;
11993
+ } catch {
11994
+ return void 0;
11995
+ }
11996
+ }
11997
+ function aggregateAISDKChunks(chunks, _result, endEvent) {
11998
+ const lastChunk = chunks[chunks.length - 1];
11999
+ const output = {};
12000
+ let metrics = {};
12001
+ let metadata;
12002
+ if (lastChunk) {
12003
+ metrics = hasModelChildTracing(endEvent) ? {} : extractTokenMetrics(lastChunk);
12004
+ metadata = buildResolvedMetadataPayload(lastChunk).metadata;
12005
+ const text = safeSerializableFieldRead(lastChunk, "text");
12006
+ if (text !== void 0) {
12007
+ output.text = text;
11409
12008
  }
11410
- if (lastChunk.finishReason !== void 0) {
11411
- output.finishReason = lastChunk.finishReason;
12009
+ const objectValue = safeSerializableFieldRead(lastChunk, "object");
12010
+ if (objectValue !== void 0) {
12011
+ output.object = objectValue;
11412
12012
  }
11413
- if (lastChunk.toolCalls !== void 0) {
11414
- output.toolCalls = lastChunk.toolCalls;
12013
+ const finishReason = safeSerializableFieldRead(lastChunk, "finishReason");
12014
+ if (finishReason !== void 0) {
12015
+ output.finishReason = finishReason;
12016
+ }
12017
+ const toolCalls = safeSerializableFieldRead(lastChunk, "toolCalls");
12018
+ if (toolCalls !== void 0) {
12019
+ output.toolCalls = toolCalls;
11415
12020
  }
11416
12021
  }
11417
12022
  finalizeAISDKChildTracing(endEvent);
@@ -11420,6 +12025,7 @@ function aggregateAISDKChunks(chunks, _result, endEvent) {
11420
12025
  function extractGetterValues(obj) {
11421
12026
  const getterValues = {};
11422
12027
  const getterNames = [
12028
+ "content",
11423
12029
  "text",
11424
12030
  "object",
11425
12031
  "finishReason",
@@ -11435,8 +12041,17 @@ function extractGetterValues(obj) {
11435
12041
  ];
11436
12042
  for (const name of getterNames) {
11437
12043
  try {
11438
- if (obj && name in obj && isSerializableOutputValue(obj[name])) {
11439
- getterValues[name] = obj[name];
12044
+ if (!obj || !(name in obj)) {
12045
+ continue;
12046
+ }
12047
+ const value = obj[name];
12048
+ if (isPromiseLike(value)) {
12049
+ void Promise.resolve(value).catch(() => {
12050
+ });
12051
+ continue;
12052
+ }
12053
+ if (isSerializableOutputValue(value)) {
12054
+ getterValues[name] = value;
11440
12055
  }
11441
12056
  } catch {
11442
12057
  }
@@ -11458,6 +12073,11 @@ function extractSerializableOutputFields(output) {
11458
12073
  for (const name of directFieldNames) {
11459
12074
  try {
11460
12075
  const value = output?.[name];
12076
+ if (isPromiseLike(value)) {
12077
+ void Promise.resolve(value).catch(() => {
12078
+ });
12079
+ continue;
12080
+ }
11461
12081
  if (isSerializableOutputValue(value)) {
11462
12082
  serialized[name] = value;
11463
12083
  }
@@ -11469,6 +12089,9 @@ function extractSerializableOutputFields(output) {
11469
12089
  ...extractGetterValues(output)
11470
12090
  };
11471
12091
  }
12092
+ function isPromiseLike(value) {
12093
+ return value != null && typeof value === "object" && typeof value.then === "function";
12094
+ }
11472
12095
  function isSerializableOutputValue(value) {
11473
12096
  if (typeof value === "function") {
11474
12097
  return false;
@@ -11510,8 +12133,9 @@ function parseGatewayModelString(modelString) {
11510
12133
  return { model: modelString };
11511
12134
  }
11512
12135
  function extractGatewayRoutingInfo(result) {
11513
- if (result?.steps && Array.isArray(result.steps) && result.steps.length > 0) {
11514
- const routing2 = result.steps[0]?.providerMetadata?.gateway?.routing;
12136
+ const steps = safeSerializableFieldRead(result, "steps");
12137
+ if (Array.isArray(steps) && steps.length > 0) {
12138
+ const routing2 = steps[0]?.providerMetadata?.gateway?.routing;
11515
12139
  if (routing2) {
11516
12140
  return {
11517
12141
  provider: routing2.resolvedProvider || routing2.finalProvider,
@@ -11519,7 +12143,11 @@ function extractGatewayRoutingInfo(result) {
11519
12143
  };
11520
12144
  }
11521
12145
  }
11522
- const routing = result?.providerMetadata?.gateway?.routing;
12146
+ const providerMetadata = safeSerializableFieldRead(
12147
+ result,
12148
+ "providerMetadata"
12149
+ );
12150
+ const routing = providerMetadata?.gateway?.routing;
11523
12151
  if (routing) {
11524
12152
  return {
11525
12153
  provider: routing.resolvedProvider || routing.finalProvider,
@@ -11529,10 +12157,11 @@ function extractGatewayRoutingInfo(result) {
11529
12157
  return null;
11530
12158
  }
11531
12159
  function extractCostFromResult(result) {
11532
- if (result?.steps && Array.isArray(result.steps) && result.steps.length > 0) {
12160
+ const steps = safeSerializableFieldRead(result, "steps");
12161
+ if (Array.isArray(steps) && steps.length > 0) {
11533
12162
  let totalCost = 0;
11534
12163
  let foundCost = false;
11535
- for (const step of result.steps) {
12164
+ for (const step of steps) {
11536
12165
  const gateway2 = step?.providerMetadata?.gateway;
11537
12166
  const stepCost = parseGatewayCost(gateway2?.cost) || parseGatewayCost(gateway2?.marketCost);
11538
12167
  if (stepCost !== void 0 && stepCost > 0) {
@@ -11544,7 +12173,11 @@ function extractCostFromResult(result) {
11544
12173
  return totalCost;
11545
12174
  }
11546
12175
  }
11547
- const gateway = result?.providerMetadata?.gateway;
12176
+ const providerMetadata = safeSerializableFieldRead(
12177
+ result,
12178
+ "providerMetadata"
12179
+ );
12180
+ const gateway = providerMetadata?.gateway;
11548
12181
  const directCost = parseGatewayCost(gateway?.cost) || parseGatewayCost(gateway?.marketCost);
11549
12182
  if (directCost !== void 0 && directCost > 0) {
11550
12183
  return directCost;
@@ -11656,12 +12289,15 @@ var claudeAgentSDKChannels = defineChannels(
11656
12289
  {
11657
12290
  query: channel({
11658
12291
  channelName: "query",
11659
- kind: "async"
12292
+ kind: "sync-stream"
11660
12293
  })
11661
12294
  }
11662
12295
  );
11663
12296
 
11664
12297
  // src/instrumentation/plugins/claude-agent-sdk-plugin.ts
12298
+ function isSubAgentToolName(toolName) {
12299
+ return toolName === "Agent" || toolName === "Task";
12300
+ }
11665
12301
  function filterSerializableOptions(options) {
11666
12302
  const allowedKeys = [
11667
12303
  "model",
@@ -11715,34 +12351,50 @@ function extractUsageFromMessage(message) {
11715
12351
  const cacheReadTokens = getNumberProperty(usage, "cache_read_input_tokens") || 0;
11716
12352
  const cacheCreationTokens = getNumberProperty(usage, "cache_creation_input_tokens") || 0;
11717
12353
  if (cacheReadTokens > 0 || cacheCreationTokens > 0) {
11718
- const cacheTokens = extractAnthropicCacheTokens(
11719
- cacheReadTokens,
11720
- cacheCreationTokens
12354
+ Object.assign(
12355
+ metrics,
12356
+ extractAnthropicCacheTokens(cacheReadTokens, cacheCreationTokens)
11721
12357
  );
11722
- Object.assign(metrics, cacheTokens);
11723
12358
  }
11724
12359
  if (Object.keys(metrics).length > 0) {
11725
12360
  Object.assign(metrics, finalizeAnthropicTokens(metrics));
11726
12361
  }
11727
12362
  return metrics;
11728
12363
  }
11729
- function buildLLMInput(prompt, conversationHistory) {
11730
- const promptMessage = typeof prompt === "string" ? { content: prompt, role: "user" } : void 0;
11731
- const inputParts = [
11732
- ...promptMessage ? [promptMessage] : [],
11733
- ...conversationHistory
11734
- ];
12364
+ function buildLLMInput(prompt, conversationHistory, capturedPromptMessages) {
12365
+ const promptMessages = [];
12366
+ if (typeof prompt === "string") {
12367
+ promptMessages.push({ content: prompt, role: "user" });
12368
+ } else if (capturedPromptMessages && capturedPromptMessages.length > 0) {
12369
+ for (const msg of capturedPromptMessages) {
12370
+ const role = msg.message?.role;
12371
+ const content = msg.message?.content;
12372
+ if (role && content !== void 0) {
12373
+ promptMessages.push({ content, role });
12374
+ }
12375
+ }
12376
+ }
12377
+ const inputParts = [...promptMessages, ...conversationHistory];
11735
12378
  return inputParts.length > 0 ? inputParts : void 0;
11736
12379
  }
11737
- async function createLLMSpanForMessages(messages, prompt, conversationHistory, options, startTime, parentSpan) {
11738
- if (messages.length === 0) return void 0;
12380
+ function formatCapturedMessages(messages) {
12381
+ return messages.length > 0 ? messages : [];
12382
+ }
12383
+ async function createLLMSpanForMessages(messages, prompt, conversationHistory, options, startTime, capturedPromptMessages, parentSpan) {
12384
+ if (messages.length === 0) {
12385
+ return void 0;
12386
+ }
11739
12387
  const lastMessage = messages[messages.length - 1];
11740
12388
  if (lastMessage.type !== "assistant" || !lastMessage.message?.usage) {
11741
12389
  return void 0;
11742
12390
  }
11743
12391
  const model = lastMessage.message.model || options.model;
11744
12392
  const usage = extractUsageFromMessage(lastMessage);
11745
- const input = buildLLMInput(prompt, conversationHistory);
12393
+ const input = buildLLMInput(
12394
+ prompt,
12395
+ conversationHistory,
12396
+ capturedPromptMessages
12397
+ );
11746
12398
  const outputs = messages.map(
11747
12399
  (m) => m.message?.content && m.message?.role ? { content: m.message.content, role: m.message.role } : void 0
11748
12400
  ).filter(
@@ -11750,21 +12402,359 @@ async function createLLMSpanForMessages(messages, prompt, conversationHistory, o
11750
12402
  );
11751
12403
  const span = startSpan({
11752
12404
  name: "anthropic.messages.create",
12405
+ parent: parentSpan,
11753
12406
  spanAttributes: {
11754
12407
  type: "llm" /* LLM */
11755
12408
  },
11756
- startTime,
11757
- parent: parentSpan
12409
+ startTime
11758
12410
  });
11759
12411
  span.log({
11760
12412
  input,
11761
- output: outputs,
11762
12413
  metadata: model ? { model } : void 0,
11763
- metrics: usage
12414
+ metrics: usage,
12415
+ output: outputs
11764
12416
  });
11765
12417
  await span.end();
11766
12418
  return lastMessage.message?.content && lastMessage.message?.role ? { content: lastMessage.message.content, role: lastMessage.message.role } : void 0;
11767
12419
  }
12420
+ function getMcpServerMetadata(serverName, mcpServers) {
12421
+ if (!serverName || !mcpServers) {
12422
+ return {};
12423
+ }
12424
+ const serverConfig = mcpServers[serverName];
12425
+ if (!serverConfig) {
12426
+ return {};
12427
+ }
12428
+ const metadata = {};
12429
+ if (serverConfig.type) {
12430
+ metadata["mcp.type"] = serverConfig.type;
12431
+ } else if (typeof serverConfig === "object" && "transport" in serverConfig) {
12432
+ metadata["mcp.type"] = "sdk";
12433
+ }
12434
+ if (serverConfig.url) {
12435
+ metadata["mcp.url"] = serverConfig.url;
12436
+ }
12437
+ if (serverConfig.command) {
12438
+ metadata["mcp.command"] = serverConfig.command;
12439
+ if (serverConfig.args) {
12440
+ metadata["mcp.args"] = serverConfig.args.join(" ");
12441
+ }
12442
+ }
12443
+ return metadata;
12444
+ }
12445
+ function parseToolName(rawToolName) {
12446
+ const mcpMatch = rawToolName.match(/^mcp__([^_]+)__(.+)$/);
12447
+ if (mcpMatch) {
12448
+ const [, mcpServer, toolName] = mcpMatch;
12449
+ return {
12450
+ displayName: `tool: ${mcpServer}/${toolName}`,
12451
+ mcpServer,
12452
+ rawToolName,
12453
+ toolName
12454
+ };
12455
+ }
12456
+ return {
12457
+ displayName: `tool: ${rawToolName}`,
12458
+ rawToolName,
12459
+ toolName: rawToolName
12460
+ };
12461
+ }
12462
+ function createToolTracingHooks(resolveParentSpan, activeToolSpans, mcpServers, subAgentSpans, endedSubAgentSpans) {
12463
+ const preToolUse = async (input, toolUseID) => {
12464
+ if (input.hook_event_name !== "PreToolUse" || !toolUseID) {
12465
+ return {};
12466
+ }
12467
+ if (isSubAgentToolName(input.tool_name)) {
12468
+ return {};
12469
+ }
12470
+ const parsed = parseToolName(input.tool_name);
12471
+ const toolSpan = startSpan({
12472
+ event: {
12473
+ input: input.tool_input,
12474
+ metadata: {
12475
+ "claude_agent_sdk.cwd": input.cwd,
12476
+ "claude_agent_sdk.raw_tool_name": parsed.rawToolName,
12477
+ "claude_agent_sdk.session_id": input.session_id,
12478
+ "gen_ai.tool.call.id": toolUseID,
12479
+ "gen_ai.tool.name": parsed.toolName,
12480
+ ...parsed.mcpServer && { "mcp.server": parsed.mcpServer },
12481
+ ...getMcpServerMetadata(parsed.mcpServer, mcpServers)
12482
+ }
12483
+ },
12484
+ name: parsed.displayName,
12485
+ parent: await resolveParentSpan(toolUseID),
12486
+ spanAttributes: { type: "tool" /* TOOL */ }
12487
+ });
12488
+ activeToolSpans.set(toolUseID, toolSpan);
12489
+ return {};
12490
+ };
12491
+ const postToolUse = async (input, toolUseID) => {
12492
+ if (input.hook_event_name !== "PostToolUse" || !toolUseID) {
12493
+ return {};
12494
+ }
12495
+ const subAgentSpan = subAgentSpans.get(toolUseID);
12496
+ if (subAgentSpan) {
12497
+ try {
12498
+ const response = input.tool_response;
12499
+ const metadata = {};
12500
+ if (response?.status) {
12501
+ metadata["claude_agent_sdk.status"] = response.status;
12502
+ }
12503
+ if (response?.totalDurationMs) {
12504
+ metadata["claude_agent_sdk.duration_ms"] = response.totalDurationMs;
12505
+ }
12506
+ if (response?.totalToolUseCount !== void 0) {
12507
+ metadata["claude_agent_sdk.tool_use_count"] = response.totalToolUseCount;
12508
+ }
12509
+ subAgentSpan.log({
12510
+ metadata,
12511
+ output: response?.content
12512
+ });
12513
+ } finally {
12514
+ subAgentSpan.end();
12515
+ endedSubAgentSpans.add(toolUseID);
12516
+ }
12517
+ return {};
12518
+ }
12519
+ const toolSpan = activeToolSpans.get(toolUseID);
12520
+ if (!toolSpan) {
12521
+ return {};
12522
+ }
12523
+ try {
12524
+ toolSpan.log({ output: input.tool_response });
12525
+ } finally {
12526
+ toolSpan.end();
12527
+ activeToolSpans.delete(toolUseID);
12528
+ }
12529
+ return {};
12530
+ };
12531
+ const postToolUseFailure = async (input, toolUseID) => {
12532
+ if (input.hook_event_name !== "PostToolUseFailure" || !toolUseID) {
12533
+ return {};
12534
+ }
12535
+ const subAgentSpan = subAgentSpans.get(toolUseID);
12536
+ if (subAgentSpan) {
12537
+ try {
12538
+ subAgentSpan.log({ error: input.error });
12539
+ } finally {
12540
+ subAgentSpan.end();
12541
+ endedSubAgentSpans.add(toolUseID);
12542
+ }
12543
+ return {};
12544
+ }
12545
+ const toolSpan = activeToolSpans.get(toolUseID);
12546
+ if (!toolSpan) {
12547
+ return {};
12548
+ }
12549
+ const parsed = parseToolName(input.tool_name);
12550
+ try {
12551
+ toolSpan.log({
12552
+ error: input.error,
12553
+ metadata: {
12554
+ "claude_agent_sdk.is_interrupt": input.is_interrupt,
12555
+ "claude_agent_sdk.session_id": input.session_id,
12556
+ "gen_ai.tool.call.id": toolUseID,
12557
+ "gen_ai.tool.name": parsed.toolName,
12558
+ ...parsed.mcpServer && { "mcp.server": parsed.mcpServer }
12559
+ }
12560
+ });
12561
+ } finally {
12562
+ toolSpan.end();
12563
+ activeToolSpans.delete(toolUseID);
12564
+ }
12565
+ return {};
12566
+ };
12567
+ return { postToolUse, postToolUseFailure, preToolUse };
12568
+ }
12569
+ function injectTracingHooks(options, resolveParentSpan, activeToolSpans, subAgentSpans, endedSubAgentSpans) {
12570
+ const { preToolUse, postToolUse, postToolUseFailure } = createToolTracingHooks(
12571
+ resolveParentSpan,
12572
+ activeToolSpans,
12573
+ options.mcpServers,
12574
+ subAgentSpans,
12575
+ endedSubAgentSpans
12576
+ );
12577
+ const existingHooks = options.hooks ?? {};
12578
+ return {
12579
+ ...options,
12580
+ hooks: {
12581
+ ...existingHooks,
12582
+ PostToolUse: [
12583
+ ...existingHooks.PostToolUse ?? [],
12584
+ { hooks: [postToolUse] }
12585
+ ],
12586
+ PostToolUseFailure: [
12587
+ ...existingHooks.PostToolUseFailure ?? [],
12588
+ {
12589
+ hooks: [postToolUseFailure]
12590
+ }
12591
+ ],
12592
+ PreToolUse: [
12593
+ ...existingHooks.PreToolUse ?? [],
12594
+ { hooks: [preToolUse] }
12595
+ ]
12596
+ }
12597
+ };
12598
+ }
12599
+ async function finalizeCurrentMessageGroup(state) {
12600
+ if (state.currentMessages.length === 0) {
12601
+ return;
12602
+ }
12603
+ const parentToolUseId = state.currentMessages[0]?.parent_tool_use_id ?? null;
12604
+ let parentSpan = await state.span.export();
12605
+ if (parentToolUseId) {
12606
+ const subAgentSpan = state.subAgentSpans.get(parentToolUseId);
12607
+ if (subAgentSpan) {
12608
+ parentSpan = await subAgentSpan.export();
12609
+ }
12610
+ }
12611
+ const finalMessage = await createLLMSpanForMessages(
12612
+ state.currentMessages,
12613
+ state.originalPrompt,
12614
+ state.finalResults,
12615
+ state.options,
12616
+ state.currentMessageStartTime,
12617
+ state.capturedPromptMessages,
12618
+ parentSpan
12619
+ );
12620
+ if (finalMessage) {
12621
+ state.finalResults.push(finalMessage);
12622
+ }
12623
+ const lastMessage = state.currentMessages[state.currentMessages.length - 1];
12624
+ if (lastMessage?.message?.usage) {
12625
+ state.accumulatedOutputTokens += getNumberProperty(lastMessage.message.usage, "output_tokens") || 0;
12626
+ }
12627
+ state.currentMessages.length = 0;
12628
+ }
12629
+ function maybeTrackToolUseContext(state, message) {
12630
+ if (message.type !== "assistant" || !Array.isArray(message.message?.content)) {
12631
+ return;
12632
+ }
12633
+ const parentToolUseId = message.parent_tool_use_id ?? null;
12634
+ for (const block of message.message.content) {
12635
+ if (typeof block !== "object" || block === null || !("type" in block) || block.type !== "tool_use" || !("id" in block) || typeof block.id !== "string") {
12636
+ continue;
12637
+ }
12638
+ state.toolUseToParent.set(block.id, parentToolUseId);
12639
+ if (block.name === "Task" && typeof block.input === "object" && block.input !== null && "subagent_type" in block.input && typeof block.input.subagent_type === "string") {
12640
+ state.pendingSubAgentNames.set(block.id, block.input.subagent_type);
12641
+ }
12642
+ }
12643
+ }
12644
+ async function maybeStartSubAgentSpan(state, message) {
12645
+ if (!("parent_tool_use_id" in message)) {
12646
+ return;
12647
+ }
12648
+ const parentToolUseId = message.parent_tool_use_id;
12649
+ if (!parentToolUseId) {
12650
+ return;
12651
+ }
12652
+ await ensureSubAgentSpan(
12653
+ state.pendingSubAgentNames,
12654
+ state.span,
12655
+ state.subAgentSpans,
12656
+ parentToolUseId
12657
+ );
12658
+ }
12659
+ async function ensureSubAgentSpan(pendingSubAgentNames, rootSpan, subAgentSpans, parentToolUseId) {
12660
+ const existingSpan = subAgentSpans.get(parentToolUseId);
12661
+ if (existingSpan) {
12662
+ return existingSpan;
12663
+ }
12664
+ const agentName = pendingSubAgentNames.get(parentToolUseId);
12665
+ const spanName = agentName ? `Agent: ${agentName}` : "Agent: sub-agent";
12666
+ const subAgentSpan = startSpan({
12667
+ event: {
12668
+ metadata: {
12669
+ ...agentName && { "claude_agent_sdk.agent_type": agentName }
12670
+ }
12671
+ },
12672
+ name: spanName,
12673
+ parent: await rootSpan.export(),
12674
+ spanAttributes: { type: "task" /* TASK */ }
12675
+ });
12676
+ subAgentSpans.set(parentToolUseId, subAgentSpan);
12677
+ return subAgentSpan;
12678
+ }
12679
+ async function handleStreamMessage(state, message) {
12680
+ maybeTrackToolUseContext(state, message);
12681
+ await maybeStartSubAgentSpan(state, message);
12682
+ const messageId = message.message?.id;
12683
+ if (messageId && messageId !== state.currentMessageId) {
12684
+ await finalizeCurrentMessageGroup(state);
12685
+ state.currentMessageId = messageId;
12686
+ state.currentMessageStartTime = getCurrentUnixTimestamp();
12687
+ }
12688
+ if (message.type === "assistant" && message.message?.usage) {
12689
+ state.currentMessages.push(message);
12690
+ }
12691
+ if (message.type !== "result" || !message.usage) {
12692
+ return;
12693
+ }
12694
+ const finalUsageMetrics = extractUsageFromMessage(message);
12695
+ if (state.currentMessages.length > 0 && finalUsageMetrics.completion_tokens !== void 0) {
12696
+ const lastMessage = state.currentMessages[state.currentMessages.length - 1];
12697
+ if (lastMessage?.message?.usage) {
12698
+ const adjustedTokens = finalUsageMetrics.completion_tokens - state.accumulatedOutputTokens;
12699
+ if (adjustedTokens >= 0) {
12700
+ lastMessage.message.usage.output_tokens = adjustedTokens;
12701
+ }
12702
+ const resultUsage = message.usage;
12703
+ if (resultUsage && typeof resultUsage === "object") {
12704
+ const cacheReadTokens = getNumberProperty(
12705
+ resultUsage,
12706
+ "cache_read_input_tokens"
12707
+ );
12708
+ if (cacheReadTokens !== void 0) {
12709
+ lastMessage.message.usage.cache_read_input_tokens = cacheReadTokens;
12710
+ }
12711
+ const cacheCreationTokens = getNumberProperty(
12712
+ resultUsage,
12713
+ "cache_creation_input_tokens"
12714
+ );
12715
+ if (cacheCreationTokens !== void 0) {
12716
+ lastMessage.message.usage.cache_creation_input_tokens = cacheCreationTokens;
12717
+ }
12718
+ }
12719
+ }
12720
+ }
12721
+ const metadata = {};
12722
+ if (message.num_turns !== void 0) {
12723
+ metadata.num_turns = message.num_turns;
12724
+ }
12725
+ if (message.session_id !== void 0) {
12726
+ metadata.session_id = message.session_id;
12727
+ }
12728
+ if (Object.keys(metadata).length > 0) {
12729
+ state.span.log({ metadata });
12730
+ }
12731
+ }
12732
+ async function finalizeQuerySpan(state) {
12733
+ try {
12734
+ await finalizeCurrentMessageGroup(state);
12735
+ state.span.log({
12736
+ output: state.finalResults.length > 0 ? state.finalResults[state.finalResults.length - 1] : void 0
12737
+ });
12738
+ if (state.capturedPromptMessages) {
12739
+ if (state.promptStarted()) {
12740
+ await state.promptDone;
12741
+ }
12742
+ if (state.capturedPromptMessages.length > 0) {
12743
+ state.span.log({
12744
+ input: formatCapturedMessages(state.capturedPromptMessages)
12745
+ });
12746
+ }
12747
+ }
12748
+ } finally {
12749
+ for (const [id, subAgentSpan] of state.subAgentSpans) {
12750
+ if (!state.endedSubAgentSpans.has(id)) {
12751
+ subAgentSpan.end();
12752
+ }
12753
+ }
12754
+ state.subAgentSpans.clear();
12755
+ state.span.end();
12756
+ }
12757
+ }
11768
12758
  var ClaudeAgentSDKPlugin = class extends BasePlugin {
11769
12759
  onEnable() {
11770
12760
  this.subscribeToQuery();
@@ -11775,19 +12765,36 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
11775
12765
  }
11776
12766
  this.unsubscribers = [];
11777
12767
  }
11778
- /**
11779
- * Subscribe to the query channel for agent interactions.
11780
- * Handles streaming responses and traces both the top-level agent task
11781
- * and individual LLM calls.
11782
- */
11783
12768
  subscribeToQuery() {
11784
12769
  const channel2 = claudeAgentSDKChannels.query.tracingChannel();
11785
12770
  const spans = /* @__PURE__ */ new WeakMap();
11786
12771
  const handlers = {
11787
12772
  start: (event) => {
11788
- const params = event.arguments[0];
11789
- const prompt = params?.prompt;
11790
- const options = params?.options ?? {};
12773
+ const params = event.arguments[0] ?? {};
12774
+ const originalPrompt = params.prompt;
12775
+ const options = params.options ?? {};
12776
+ const promptIsAsyncIterable = isAsyncIterable(originalPrompt);
12777
+ let promptStarted = false;
12778
+ let capturedPromptMessages;
12779
+ let resolvePromptDone;
12780
+ const promptDone = new Promise((resolve) => {
12781
+ resolvePromptDone = resolve;
12782
+ });
12783
+ if (promptIsAsyncIterable) {
12784
+ capturedPromptMessages = [];
12785
+ const promptStream = originalPrompt;
12786
+ params.prompt = (async function* () {
12787
+ promptStarted = true;
12788
+ try {
12789
+ for await (const message of promptStream) {
12790
+ capturedPromptMessages.push(message);
12791
+ yield message;
12792
+ }
12793
+ } finally {
12794
+ resolvePromptDone?.();
12795
+ }
12796
+ })();
12797
+ }
11791
12798
  const span = startSpan({
11792
12799
  name: "Claude Agent",
11793
12800
  spanAttributes: {
@@ -11797,163 +12804,111 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
11797
12804
  const startTime = getCurrentUnixTimestamp();
11798
12805
  try {
11799
12806
  span.log({
11800
- input: typeof prompt === "string" ? prompt : {
11801
- type: "streaming",
11802
- description: "AsyncIterable<ClaudeAgentSDKMessage>"
11803
- },
12807
+ input: typeof originalPrompt === "string" ? originalPrompt : promptIsAsyncIterable ? void 0 : originalPrompt !== void 0 ? String(originalPrompt) : void 0,
11804
12808
  metadata: filterSerializableOptions(options)
11805
12809
  });
11806
12810
  } catch (error) {
11807
12811
  console.error("Error extracting input for Claude Agent SDK:", error);
11808
12812
  }
12813
+ const activeToolSpans = /* @__PURE__ */ new Map();
12814
+ const subAgentSpans = /* @__PURE__ */ new Map();
12815
+ const endedSubAgentSpans = /* @__PURE__ */ new Set();
12816
+ const toolUseToParent = /* @__PURE__ */ new Map();
12817
+ const pendingSubAgentNames = /* @__PURE__ */ new Map();
12818
+ const optionsWithHooks = injectTracingHooks(
12819
+ options,
12820
+ async (toolUseID) => {
12821
+ const parentToolUseId = toolUseToParent.get(toolUseID);
12822
+ if (parentToolUseId) {
12823
+ const subAgentSpan = await ensureSubAgentSpan(
12824
+ pendingSubAgentNames,
12825
+ span,
12826
+ subAgentSpans,
12827
+ parentToolUseId
12828
+ );
12829
+ return subAgentSpan.export();
12830
+ }
12831
+ return span.export();
12832
+ },
12833
+ activeToolSpans,
12834
+ subAgentSpans,
12835
+ endedSubAgentSpans
12836
+ );
12837
+ params.options = optionsWithHooks;
12838
+ event.arguments[0] = params;
11809
12839
  spans.set(event, {
11810
- span,
11811
- startTime,
11812
- conversationHistory: [],
11813
- currentMessages: [],
12840
+ accumulatedOutputTokens: 0,
12841
+ activeToolSpans,
12842
+ capturedPromptMessages,
11814
12843
  currentMessageId: void 0,
11815
12844
  currentMessageStartTime: startTime,
11816
- accumulatedOutputTokens: 0
12845
+ currentMessages: [],
12846
+ endedSubAgentSpans,
12847
+ finalResults: [],
12848
+ options: optionsWithHooks,
12849
+ originalPrompt,
12850
+ pendingSubAgentNames,
12851
+ processing: Promise.resolve(),
12852
+ promptDone,
12853
+ promptStarted: () => promptStarted,
12854
+ span,
12855
+ subAgentSpans,
12856
+ toolUseToParent
11817
12857
  });
11818
12858
  },
11819
- asyncEnd: (event) => {
11820
- const spanData = spans.get(event);
11821
- if (!spanData) {
12859
+ end: (event) => {
12860
+ const state = spans.get(event);
12861
+ if (!state) {
11822
12862
  return;
11823
12863
  }
11824
12864
  const eventResult = event.result;
11825
12865
  if (eventResult === void 0) {
11826
- spanData.span.end();
12866
+ state.span.end();
11827
12867
  spans.delete(event);
11828
12868
  return;
11829
12869
  }
11830
12870
  if (isAsyncIterable(eventResult)) {
11831
12871
  patchStreamIfNeeded(eventResult, {
11832
- onChunk: async (message) => {
11833
- const currentTime = getCurrentUnixTimestamp();
11834
- const params = event.arguments[0];
11835
- const prompt = params?.prompt;
11836
- const options = params?.options ?? {};
11837
- const messageId = message.message?.id;
11838
- if (messageId && messageId !== spanData.currentMessageId) {
11839
- if (spanData.currentMessages.length > 0) {
11840
- const finalMessage = await createLLMSpanForMessages(
11841
- spanData.currentMessages,
11842
- prompt,
11843
- spanData.conversationHistory,
11844
- options,
11845
- spanData.currentMessageStartTime,
11846
- await spanData.span.export()
11847
- );
11848
- if (finalMessage) {
11849
- spanData.conversationHistory.push(finalMessage);
11850
- }
11851
- const lastMessage = spanData.currentMessages[spanData.currentMessages.length - 1];
11852
- if (lastMessage?.message?.usage) {
11853
- const outputTokens = getNumberProperty(
11854
- lastMessage.message.usage,
11855
- "output_tokens"
11856
- ) || 0;
11857
- spanData.accumulatedOutputTokens += outputTokens;
11858
- }
11859
- spanData.currentMessages = [];
11860
- }
11861
- spanData.currentMessageId = messageId;
11862
- spanData.currentMessageStartTime = currentTime;
11863
- }
11864
- if (message.type === "assistant" && message.message?.usage) {
11865
- spanData.currentMessages.push(message);
11866
- }
11867
- if (message.type === "result" && message.usage) {
11868
- const finalUsageMetrics = extractUsageFromMessage(message);
11869
- if (spanData.currentMessages.length > 0 && finalUsageMetrics.completion_tokens !== void 0) {
11870
- const lastMessage = spanData.currentMessages[spanData.currentMessages.length - 1];
11871
- if (lastMessage?.message?.usage) {
11872
- const adjustedTokens = finalUsageMetrics.completion_tokens - spanData.accumulatedOutputTokens;
11873
- if (adjustedTokens >= 0) {
11874
- lastMessage.message.usage.output_tokens = adjustedTokens;
11875
- }
11876
- }
11877
- }
11878
- const result_metadata = {};
11879
- if (message.num_turns !== void 0) {
11880
- result_metadata.num_turns = message.num_turns;
11881
- }
11882
- if (message.session_id !== void 0) {
11883
- result_metadata.session_id = message.session_id;
11884
- }
11885
- if (Object.keys(result_metadata).length > 0) {
11886
- spanData.span.log({
11887
- metadata: result_metadata
11888
- });
11889
- }
11890
- }
11891
- },
11892
- onComplete: async () => {
11893
- try {
11894
- const params = event.arguments[0];
11895
- const prompt = params?.prompt;
11896
- const options = params?.options ?? {};
11897
- if (spanData.currentMessages.length > 0) {
11898
- const finalMessage = await createLLMSpanForMessages(
11899
- spanData.currentMessages,
11900
- prompt,
11901
- spanData.conversationHistory,
11902
- options,
11903
- spanData.currentMessageStartTime,
11904
- await spanData.span.export()
11905
- );
11906
- if (finalMessage) {
11907
- spanData.conversationHistory.push(finalMessage);
11908
- }
11909
- }
11910
- spanData.span.log({
11911
- output: spanData.conversationHistory.length > 0 ? spanData.conversationHistory[spanData.conversationHistory.length - 1] : void 0
11912
- });
11913
- } catch (error) {
12872
+ onChunk: (message) => {
12873
+ maybeTrackToolUseContext(state, message);
12874
+ state.processing = state.processing.then(() => handleStreamMessage(state, message)).catch((error) => {
11914
12875
  console.error(
11915
- "Error extracting output for Claude Agent SDK:",
12876
+ "Error processing Claude Agent SDK stream chunk:",
11916
12877
  error
11917
12878
  );
11918
- } finally {
11919
- spanData.span.end();
11920
- spans.delete(event);
11921
- }
12879
+ });
11922
12880
  },
11923
- onError: (error) => {
11924
- spanData.span.log({
12881
+ onComplete: () => state.processing.then(() => finalizeQuerySpan(state)).finally(() => {
12882
+ spans.delete(event);
12883
+ }),
12884
+ onError: (error) => state.processing.then(() => {
12885
+ state.span.log({
11925
12886
  error: error.message
11926
12887
  });
11927
- spanData.span.end();
12888
+ }).then(() => finalizeQuerySpan(state)).finally(() => {
11928
12889
  spans.delete(event);
11929
- }
12890
+ })
11930
12891
  });
11931
- } else {
11932
- try {
11933
- spanData.span.log({
11934
- output: eventResult
11935
- });
11936
- } catch (error) {
11937
- console.error(
11938
- "Error extracting output for Claude Agent SDK:",
11939
- error
11940
- );
11941
- } finally {
11942
- spanData.span.end();
11943
- spans.delete(event);
11944
- }
12892
+ return;
12893
+ }
12894
+ try {
12895
+ state.span.log({ output: eventResult });
12896
+ } catch (error) {
12897
+ console.error("Error extracting output for Claude Agent SDK:", error);
12898
+ } finally {
12899
+ state.span.end();
12900
+ spans.delete(event);
11945
12901
  }
11946
12902
  },
11947
12903
  error: (event) => {
11948
- const spanData = spans.get(event);
11949
- if (!spanData || !event.error) {
12904
+ const state = spans.get(event);
12905
+ if (!state || !event.error) {
11950
12906
  return;
11951
12907
  }
11952
- const { span } = spanData;
11953
- span.log({
12908
+ state.span.log({
11954
12909
  error: event.error.message
11955
12910
  });
11956
- span.end();
12911
+ state.span.end();
11957
12912
  spans.delete(event);
11958
12913
  }
11959
12914
  };
@@ -11977,6 +12932,18 @@ var googleGenAIChannels = defineChannels("@google/genai", {
11977
12932
  });
11978
12933
 
11979
12934
  // src/instrumentation/plugins/google-genai-plugin.ts
12935
+ var GOOGLE_GENAI_INTERNAL_CONTEXT = {
12936
+ caller_filename: "<node-internal>",
12937
+ caller_functionname: "<node-internal>",
12938
+ caller_lineno: 0
12939
+ };
12940
+ function createWrapperParityEvent(args) {
12941
+ return {
12942
+ context: GOOGLE_GENAI_INTERNAL_CONTEXT,
12943
+ input: args.input,
12944
+ metadata: args.metadata
12945
+ };
12946
+ }
11980
12947
  var GoogleGenAIPlugin = class extends BasePlugin {
11981
12948
  onEnable() {
11982
12949
  this.subscribeToGoogleGenAIChannels();
@@ -11985,51 +12952,283 @@ var GoogleGenAIPlugin = class extends BasePlugin {
11985
12952
  this.unsubscribers = unsubscribeAll(this.unsubscribers);
11986
12953
  }
11987
12954
  subscribeToGoogleGenAIChannels() {
11988
- this.unsubscribers.push(
11989
- traceAsyncChannel(googleGenAIChannels.generateContent, {
11990
- name: "google-genai.generateContent",
11991
- type: "llm" /* LLM */,
11992
- extractInput: ([params]) => {
12955
+ this.subscribeToGenerateContentChannel();
12956
+ this.subscribeToGenerateContentStreamChannel();
12957
+ }
12958
+ subscribeToGenerateContentChannel() {
12959
+ const tracingChannel2 = googleGenAIChannels.generateContent.tracingChannel();
12960
+ const states = /* @__PURE__ */ new WeakMap();
12961
+ const unbindCurrentSpanStore = bindCurrentSpanStoreToStart2(
12962
+ tracingChannel2,
12963
+ states,
12964
+ (event) => {
12965
+ const params = event.arguments[0];
12966
+ const input = serializeInput(params);
12967
+ const metadata = extractMetadata(params);
12968
+ const span = startSpan({
12969
+ name: "generate_content",
12970
+ spanAttributes: {
12971
+ type: "llm" /* LLM */
12972
+ },
12973
+ event: createWrapperParityEvent({ input, metadata })
12974
+ });
12975
+ return {
12976
+ span,
12977
+ startTime: getCurrentUnixTimestamp()
12978
+ };
12979
+ }
12980
+ );
12981
+ const handlers = {
12982
+ start: (event) => {
12983
+ ensureSpanState(states, event, () => {
12984
+ const params = event.arguments[0];
11993
12985
  const input = serializeInput(params);
11994
12986
  const metadata = extractMetadata(params);
12987
+ const span = startSpan({
12988
+ name: "generate_content",
12989
+ spanAttributes: {
12990
+ type: "llm" /* LLM */
12991
+ },
12992
+ event: createWrapperParityEvent({ input, metadata })
12993
+ });
11995
12994
  return {
11996
- input,
11997
- metadata: { ...metadata, provider: "google-genai" }
12995
+ span,
12996
+ startTime: getCurrentUnixTimestamp()
11998
12997
  };
11999
- },
12000
- extractOutput: (result) => {
12001
- return result;
12002
- },
12003
- extractMetrics: (result, startTime) => {
12004
- return extractGenerateContentMetrics(result, startTime);
12998
+ });
12999
+ },
13000
+ asyncEnd: (event) => {
13001
+ const spanState = states.get(event);
13002
+ if (!spanState) {
13003
+ return;
12005
13004
  }
12006
- })
12007
- );
12008
- this.unsubscribers.push(
12009
- traceStreamingChannel(googleGenAIChannels.generateContentStream, {
12010
- name: "google-genai.generateContentStream",
12011
- type: "llm" /* LLM */,
12012
- extractInput: ([params]) => {
12013
- const input = serializeInput(params);
12014
- const metadata = extractMetadata(params);
12015
- return {
12016
- input,
12017
- metadata: { ...metadata, provider: "google-genai" }
12018
- };
12019
- },
12020
- extractOutput: (result) => {
12021
- return result;
12022
- },
12023
- extractMetrics: () => {
12024
- return {};
12025
- },
12026
- aggregateChunks: (chunks, _result, _endEvent, startTime) => {
12027
- return aggregateGenerateContentChunks(chunks, startTime);
13005
+ try {
13006
+ spanState.span.log({
13007
+ metrics: cleanMetrics(
13008
+ extractGenerateContentMetrics(
13009
+ event.result,
13010
+ spanState.startTime
13011
+ )
13012
+ ),
13013
+ output: event.result
13014
+ });
13015
+ } finally {
13016
+ spanState.span.end();
13017
+ states.delete(event);
12028
13018
  }
12029
- })
12030
- );
13019
+ },
13020
+ error: (event) => {
13021
+ logErrorAndEndSpan(states, event);
13022
+ }
13023
+ };
13024
+ tracingChannel2.subscribe(handlers);
13025
+ this.unsubscribers.push(() => {
13026
+ unbindCurrentSpanStore?.();
13027
+ tracingChannel2.unsubscribe(handlers);
13028
+ });
13029
+ }
13030
+ subscribeToGenerateContentStreamChannel() {
13031
+ const tracingChannel2 = googleGenAIChannels.generateContentStream.tracingChannel();
13032
+ const handlers = {
13033
+ start: (event) => {
13034
+ const streamEvent = event;
13035
+ const params = event.arguments[0];
13036
+ streamEvent.googleGenAIInput = serializeInput(params);
13037
+ streamEvent.googleGenAIMetadata = extractMetadata(params);
13038
+ streamEvent.googleGenAIStartTime = getCurrentUnixTimestamp();
13039
+ },
13040
+ asyncEnd: (event) => {
13041
+ const streamEvent = event;
13042
+ patchGoogleGenAIStreamingResult({
13043
+ input: streamEvent.googleGenAIInput,
13044
+ metadata: streamEvent.googleGenAIMetadata,
13045
+ startTime: streamEvent.googleGenAIStartTime,
13046
+ result: streamEvent.result
13047
+ });
13048
+ },
13049
+ error: () => {
13050
+ }
13051
+ };
13052
+ tracingChannel2.subscribe(handlers);
13053
+ this.unsubscribers.push(() => {
13054
+ tracingChannel2.unsubscribe(handlers);
13055
+ });
12031
13056
  }
12032
13057
  };
13058
+ function ensureSpanState(states, event, create) {
13059
+ const existing = states.get(event);
13060
+ if (existing) {
13061
+ return existing;
13062
+ }
13063
+ const created = create();
13064
+ states.set(event, created);
13065
+ return created;
13066
+ }
13067
+ function bindCurrentSpanStoreToStart2(tracingChannel2, states, create) {
13068
+ const state = _internalGetGlobalState();
13069
+ const startChannel = tracingChannel2.start;
13070
+ const currentSpanStore = state?.contextManager ? state.contextManager[BRAINTRUST_CURRENT_SPAN_STORE] : void 0;
13071
+ if (!startChannel?.bindStore || !currentSpanStore) {
13072
+ return void 0;
13073
+ }
13074
+ startChannel.bindStore(
13075
+ currentSpanStore,
13076
+ (event) => ensureSpanState(
13077
+ states,
13078
+ event,
13079
+ () => create(event)
13080
+ ).span
13081
+ );
13082
+ return () => {
13083
+ startChannel.unbindStore?.(currentSpanStore);
13084
+ };
13085
+ }
13086
+ function logErrorAndEndSpan(states, event) {
13087
+ const spanState = states.get(event);
13088
+ if (!spanState) {
13089
+ return;
13090
+ }
13091
+ spanState.span.log({
13092
+ error: event.error.message
13093
+ });
13094
+ spanState.span.end();
13095
+ states.delete(event);
13096
+ }
13097
+ function patchGoogleGenAIStreamingResult(args) {
13098
+ const { input, metadata, result, startTime } = args;
13099
+ if (!input || !metadata || !result || typeof result !== "object" || typeof result.next !== "function") {
13100
+ return false;
13101
+ }
13102
+ const chunks = [];
13103
+ let firstTokenTime = null;
13104
+ let finalized = false;
13105
+ let span = null;
13106
+ const requestStartTime = startTime ?? getCurrentUnixTimestamp();
13107
+ const ensureSpan = () => {
13108
+ if (!span) {
13109
+ span = startSpan({
13110
+ name: "generate_content_stream",
13111
+ spanAttributes: {
13112
+ type: "llm" /* LLM */
13113
+ },
13114
+ event: {
13115
+ input,
13116
+ metadata
13117
+ }
13118
+ });
13119
+ }
13120
+ return span;
13121
+ };
13122
+ const finalize = (options) => {
13123
+ if (finalized || !span) {
13124
+ return;
13125
+ }
13126
+ finalized = true;
13127
+ if (options.result) {
13128
+ const { end, ...metricsWithoutEnd } = options.result.metrics;
13129
+ span.log({
13130
+ metrics: cleanMetrics(metricsWithoutEnd),
13131
+ output: options.result.aggregated
13132
+ });
13133
+ span.end(typeof end === "number" ? { endTime: end } : void 0);
13134
+ return;
13135
+ }
13136
+ if (options.error !== void 0) {
13137
+ span.log({
13138
+ error: options.error instanceof Error ? options.error.message : String(options.error)
13139
+ });
13140
+ }
13141
+ span.end();
13142
+ };
13143
+ const patchIterator = (iterator) => {
13144
+ if (typeof iterator !== "object" || iterator === null || "__braintrustGoogleGenAIPatched" in iterator) {
13145
+ return iterator;
13146
+ }
13147
+ const iteratorRecord = iterator;
13148
+ const originalNext = typeof iteratorRecord.next === "function" ? iteratorRecord.next.bind(iterator) : void 0;
13149
+ const originalReturn = typeof iteratorRecord.return === "function" ? iteratorRecord.return.bind(iterator) : void 0;
13150
+ const originalThrow = typeof iteratorRecord.throw === "function" ? iteratorRecord.throw.bind(iterator) : void 0;
13151
+ const asyncIteratorMethod = iteratorRecord[Symbol.asyncIterator];
13152
+ const originalAsyncIterator = typeof asyncIteratorMethod === "function" ? asyncIteratorMethod.bind(iterator) : void 0;
13153
+ Object.defineProperty(iteratorRecord, "__braintrustGoogleGenAIPatched", {
13154
+ configurable: true,
13155
+ enumerable: false,
13156
+ value: true,
13157
+ writable: false
13158
+ });
13159
+ if (originalNext) {
13160
+ iteratorRecord.next = async (...nextArgs) => {
13161
+ ensureSpan();
13162
+ try {
13163
+ const nextResult = await originalNext(
13164
+ ...nextArgs
13165
+ );
13166
+ if (!nextResult.done && nextResult.value) {
13167
+ if (firstTokenTime === null) {
13168
+ firstTokenTime = getCurrentUnixTimestamp();
13169
+ }
13170
+ chunks.push(nextResult.value);
13171
+ }
13172
+ if (nextResult.done) {
13173
+ finalize({
13174
+ result: aggregateGenerateContentChunks(
13175
+ chunks,
13176
+ requestStartTime,
13177
+ firstTokenTime
13178
+ )
13179
+ });
13180
+ }
13181
+ return nextResult;
13182
+ } catch (error) {
13183
+ finalize({ error });
13184
+ throw error;
13185
+ }
13186
+ };
13187
+ }
13188
+ if (originalReturn) {
13189
+ iteratorRecord.return = async (...returnArgs) => {
13190
+ ensureSpan();
13191
+ try {
13192
+ return await originalReturn(
13193
+ ...returnArgs
13194
+ );
13195
+ } finally {
13196
+ if (chunks.length > 0) {
13197
+ finalize({
13198
+ result: aggregateGenerateContentChunks(
13199
+ chunks,
13200
+ requestStartTime,
13201
+ firstTokenTime
13202
+ )
13203
+ });
13204
+ } else {
13205
+ finalize({});
13206
+ }
13207
+ }
13208
+ };
13209
+ }
13210
+ if (originalThrow) {
13211
+ iteratorRecord.throw = async (...throwArgs) => {
13212
+ ensureSpan();
13213
+ try {
13214
+ return await originalThrow(
13215
+ ...throwArgs
13216
+ );
13217
+ } catch (error) {
13218
+ finalize({ error });
13219
+ throw error;
13220
+ }
13221
+ };
13222
+ }
13223
+ iteratorRecord[Symbol.asyncIterator] = () => {
13224
+ const asyncIterator = originalAsyncIterator ? originalAsyncIterator() : iterator;
13225
+ return patchIterator(asyncIterator);
13226
+ };
13227
+ return iterator;
13228
+ };
13229
+ patchIterator(result);
13230
+ return true;
13231
+ }
12033
13232
  function serializeInput(params) {
12034
13233
  const input = {
12035
13234
  model: params.model,
@@ -12038,11 +13237,13 @@ function serializeInput(params) {
12038
13237
  if (params.config) {
12039
13238
  const config = tryToDict(params.config);
12040
13239
  if (config) {
12041
- const tools = serializeTools(params);
12042
- if (tools) {
12043
- config.tools = tools;
12044
- }
12045
- input.config = config;
13240
+ const filteredConfig = {};
13241
+ Object.keys(config).forEach((key) => {
13242
+ if (key !== "tools") {
13243
+ filteredConfig[key] = config[key];
13244
+ }
13245
+ });
13246
+ input.config = filteredConfig;
12046
13247
  }
12047
13248
  }
12048
13249
  return input;
@@ -12129,12 +13330,18 @@ function extractMetadata(params) {
12129
13330
  });
12130
13331
  }
12131
13332
  }
13333
+ const tools = serializeTools(params);
13334
+ if (tools) {
13335
+ metadata.tools = tools;
13336
+ }
12132
13337
  return metadata;
12133
13338
  }
12134
13339
  function extractGenerateContentMetrics(response, startTime) {
12135
13340
  const metrics = {};
12136
- if (startTime) {
13341
+ if (startTime !== void 0) {
12137
13342
  const end = getCurrentUnixTimestamp();
13343
+ metrics.start = startTime;
13344
+ metrics.end = end;
12138
13345
  metrics.duration = end - startTime;
12139
13346
  }
12140
13347
  if (response?.usageMetadata) {
@@ -12159,19 +13366,18 @@ function populateUsageMetrics(metrics, usage) {
12159
13366
  metrics.completion_reasoning_tokens = usage.thoughtsTokenCount;
12160
13367
  }
12161
13368
  }
12162
- function aggregateGenerateContentChunks(chunks, startTime) {
12163
- const metrics = {};
12164
- if (startTime !== void 0) {
12165
- const end = getCurrentUnixTimestamp();
12166
- metrics.duration = end - startTime;
12167
- }
12168
- let firstTokenTime = null;
12169
- if (chunks.length > 0 && firstTokenTime === null && startTime !== void 0) {
12170
- firstTokenTime = getCurrentUnixTimestamp();
13369
+ function aggregateGenerateContentChunks(chunks, startTime, firstTokenTime) {
13370
+ const end = getCurrentUnixTimestamp();
13371
+ const metrics = {
13372
+ start: startTime,
13373
+ end,
13374
+ duration: end - startTime
13375
+ };
13376
+ if (firstTokenTime !== null) {
12171
13377
  metrics.time_to_first_token = firstTokenTime - startTime;
12172
13378
  }
12173
13379
  if (chunks.length === 0) {
12174
- return { output: {}, metrics };
13380
+ return { aggregated: {}, metrics };
12175
13381
  }
12176
13382
  let text = "";
12177
13383
  let thoughtText = "";
@@ -12207,7 +13413,7 @@ function aggregateGenerateContentChunks(chunks, startTime) {
12207
13413
  }
12208
13414
  }
12209
13415
  }
12210
- const output = {};
13416
+ const aggregated = {};
12211
13417
  const parts = [];
12212
13418
  if (thoughtText) {
12213
13419
  parts.push({ text: thoughtText, thought: true });
@@ -12233,16 +13439,25 @@ function aggregateGenerateContentChunks(chunks, startTime) {
12233
13439
  }
12234
13440
  candidates.push(candidateDict);
12235
13441
  }
12236
- output.candidates = candidates;
13442
+ aggregated.candidates = candidates;
12237
13443
  }
12238
13444
  if (usageMetadata) {
12239
- output.usageMetadata = usageMetadata;
13445
+ aggregated.usageMetadata = usageMetadata;
12240
13446
  populateUsageMetrics(metrics, usageMetadata);
12241
13447
  }
12242
13448
  if (text) {
12243
- output.text = text;
13449
+ aggregated.text = text;
13450
+ }
13451
+ return { aggregated, metrics };
13452
+ }
13453
+ function cleanMetrics(metrics) {
13454
+ const cleaned = {};
13455
+ for (const [key, value] of Object.entries(metrics)) {
13456
+ if (value !== null && value !== void 0) {
13457
+ cleaned[key] = value;
13458
+ }
12244
13459
  }
12245
- return { output, metrics };
13460
+ return cleaned;
12246
13461
  }
12247
13462
  function tryToDict(obj) {
12248
13463
  if (obj === null || obj === void 0) {
@@ -12276,895 +13491,535 @@ var openRouterChannels = defineChannels("@openrouter/sdk", {
12276
13491
  channelName: "callModel",
12277
13492
  kind: "sync-stream"
12278
13493
  }),
13494
+ callModelTurn: channel({
13495
+ channelName: "callModel.turn",
13496
+ kind: "async"
13497
+ }),
12279
13498
  toolExecute: channel({
12280
13499
  channelName: "tool.execute",
12281
13500
  kind: "async"
12282
13501
  })
12283
13502
  });
12284
13503
 
12285
- // src/openrouter-utils.ts
12286
- var TOKEN_NAME_MAP2 = {
12287
- promptTokens: "prompt_tokens",
12288
- inputTokens: "prompt_tokens",
12289
- completionTokens: "completion_tokens",
12290
- outputTokens: "completion_tokens",
12291
- totalTokens: "tokens",
12292
- prompt_tokens: "prompt_tokens",
12293
- input_tokens: "prompt_tokens",
12294
- completion_tokens: "completion_tokens",
12295
- output_tokens: "completion_tokens",
12296
- total_tokens: "tokens"
12297
- };
12298
- var TOKEN_DETAIL_PREFIX_MAP = {
12299
- promptTokensDetails: "prompt",
12300
- inputTokensDetails: "prompt",
12301
- completionTokensDetails: "completion",
12302
- outputTokensDetails: "completion",
12303
- costDetails: "cost",
12304
- prompt_tokens_details: "prompt",
12305
- input_tokens_details: "prompt",
12306
- completion_tokens_details: "completion",
12307
- output_tokens_details: "completion",
12308
- cost_details: "cost"
12309
- };
12310
- function camelToSnake(value) {
12311
- return value.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`);
12312
- }
12313
- function parseOpenRouterMetricsFromUsage(usage) {
12314
- if (!isObject(usage)) {
12315
- return {};
12316
- }
12317
- const metrics = {};
12318
- for (const [name, value] of Object.entries(usage)) {
12319
- if (typeof value === "number") {
12320
- metrics[TOKEN_NAME_MAP2[name] || camelToSnake(name)] = value;
12321
- continue;
12322
- }
12323
- if (!isObject(value)) {
12324
- continue;
12325
- }
12326
- const prefix = TOKEN_DETAIL_PREFIX_MAP[name];
12327
- if (!prefix) {
12328
- continue;
12329
- }
12330
- for (const [nestedName, nestedValue] of Object.entries(value)) {
12331
- if (typeof nestedValue !== "number") {
12332
- continue;
12333
- }
12334
- metrics[`${prefix}_${camelToSnake(nestedName)}`] = nestedValue;
12335
- }
12336
- }
12337
- return metrics;
12338
- }
12339
- function extractOpenRouterUsageMetadata(usage) {
12340
- if (!isObject(usage)) {
12341
- return void 0;
12342
- }
12343
- const metadata = {};
12344
- if (typeof usage.isByok === "boolean") {
12345
- metadata.is_byok = usage.isByok;
12346
- } else if (typeof usage.is_byok === "boolean") {
12347
- metadata.is_byok = usage.is_byok;
12348
- }
12349
- return Object.keys(metadata).length > 0 ? metadata : void 0;
12350
- }
12351
-
12352
- // src/openrouter-logging.ts
12353
- var OMITTED_OPENROUTER_KEYS = /* @__PURE__ */ new Set([
12354
- "execute",
12355
- "render",
12356
- "nextTurnParams",
12357
- "requireApproval"
12358
- ]);
12359
- function parseOpenRouterModelString(model) {
12360
- if (typeof model !== "string") {
12361
- return { model };
12362
- }
12363
- const slashIndex = model.indexOf("/");
12364
- if (slashIndex > 0 && slashIndex < model.length - 1) {
12365
- return {
12366
- provider: model.substring(0, slashIndex),
12367
- model: model.substring(slashIndex + 1)
12368
- };
12369
- }
12370
- return { model };
12371
- }
12372
- function isZodSchema2(value) {
12373
- return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
12374
- }
12375
- function serializeZodSchema2(schema) {
12376
- try {
12377
- return zodToJsonSchema(schema);
12378
- } catch {
12379
- return {
12380
- type: "object",
12381
- description: "Zod schema (conversion failed)"
12382
- };
13504
+ // src/instrumentation/plugins/openrouter-plugin.ts
13505
+ var OpenRouterPlugin = class extends BasePlugin {
13506
+ onEnable() {
13507
+ this.subscribeToOpenRouterChannels();
12383
13508
  }
12384
- }
12385
- function serializeOpenRouterTool(tool) {
12386
- if (!isObject(tool)) {
12387
- return tool;
13509
+ onDisable() {
13510
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
12388
13511
  }
12389
- const serialized = {};
12390
- for (const [key, value] of Object.entries(tool)) {
12391
- if (OMITTED_OPENROUTER_KEYS.has(key)) {
12392
- continue;
12393
- }
12394
- if (key === "function" && isObject(value)) {
12395
- serialized.function = sanitizeOpenRouterLoggedValue(value);
12396
- continue;
12397
- }
12398
- serialized[key] = sanitizeOpenRouterLoggedValue(value);
12399
- }
12400
- return serialized;
12401
- }
12402
- function serializeOpenRouterToolsForLogging(tools) {
12403
- if (!Array.isArray(tools)) {
12404
- return void 0;
12405
- }
12406
- return tools.map((tool) => serializeOpenRouterTool(tool));
12407
- }
12408
- function sanitizeOpenRouterLoggedValue(value) {
12409
- if (isZodSchema2(value)) {
12410
- return serializeZodSchema2(value);
12411
- }
12412
- if (typeof value === "function") {
12413
- return "[Function]";
12414
- }
12415
- if (Array.isArray(value)) {
12416
- return value.map((entry) => sanitizeOpenRouterLoggedValue(entry));
12417
- }
12418
- if (!isObject(value)) {
12419
- return value;
12420
- }
12421
- const sanitized = {};
12422
- for (const [key, entry] of Object.entries(value)) {
12423
- if (OMITTED_OPENROUTER_KEYS.has(key)) {
12424
- continue;
12425
- }
12426
- if (key === "tools" && Array.isArray(entry)) {
12427
- sanitized.tools = serializeOpenRouterToolsForLogging(entry);
12428
- continue;
12429
- }
12430
- sanitized[key] = sanitizeOpenRouterLoggedValue(entry);
12431
- }
12432
- return sanitized;
12433
- }
12434
- function buildOpenRouterMetadata(metadata, httpReferer, xTitle) {
12435
- const sanitized = sanitizeOpenRouterLoggedValue(metadata);
12436
- const metadataRecord = isObject(sanitized) ? sanitized : {};
12437
- const { model, provider: providerRouting, ...rest } = metadataRecord;
12438
- const normalizedModel = parseOpenRouterModelString(model);
12439
- return {
12440
- ...rest,
12441
- ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
12442
- ...providerRouting !== void 0 ? { providerRouting } : {},
12443
- ...httpReferer !== void 0 ? { httpReferer } : {},
12444
- ...xTitle !== void 0 ? { xTitle } : {},
12445
- provider: normalizedModel.provider || "openrouter"
12446
- };
12447
- }
12448
- function buildOpenRouterEmbeddingMetadata(metadata, httpReferer, xTitle) {
12449
- const normalized = buildOpenRouterMetadata(metadata, httpReferer, xTitle);
12450
- return typeof normalized.model === "string" ? {
12451
- ...normalized,
12452
- embedding_model: normalized.model
12453
- } : normalized;
12454
- }
12455
- function extractOpenRouterCallModelInput(request) {
12456
- return isObject(request) && "input" in request ? sanitizeOpenRouterLoggedValue(request.input) : void 0;
12457
- }
12458
- function extractOpenRouterCallModelMetadata(request) {
12459
- if (!isObject(request)) {
12460
- return { provider: "openrouter" };
12461
- }
12462
- const { input: _input, ...metadata } = request;
12463
- return buildOpenRouterMetadata(metadata, void 0, void 0);
12464
- }
12465
- function extractOpenRouterResponseMetadata(result) {
12466
- if (!isObject(result)) {
12467
- return void 0;
12468
- }
12469
- const { output: _output, data: _data, usage, ...metadata } = result;
12470
- const sanitized = sanitizeOpenRouterLoggedValue(metadata);
12471
- const metadataRecord = isObject(sanitized) ? sanitized : {};
12472
- const { model, provider, ...rest } = metadataRecord;
12473
- const normalizedModel = parseOpenRouterModelString(model);
12474
- const normalizedProvider = (typeof provider === "string" ? provider : void 0) || normalizedModel.provider;
12475
- const usageMetadata = extractOpenRouterUsageMetadata(usage);
12476
- const combined = {
12477
- ...rest,
12478
- ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
12479
- ...usageMetadata || {},
12480
- ...normalizedProvider !== void 0 ? { provider: normalizedProvider } : {}
12481
- };
12482
- return Object.keys(combined).length > 0 ? combined : void 0;
12483
- }
12484
- function extractOpenRouterResponseOutput(response, fallbackOutput) {
12485
- if (isObject(response) && "output" in response && response.output !== void 0) {
12486
- return sanitizeOpenRouterLoggedValue(response.output);
12487
- }
12488
- if (fallbackOutput !== void 0) {
12489
- return sanitizeOpenRouterLoggedValue(fallbackOutput);
12490
- }
12491
- return void 0;
12492
- }
12493
-
12494
- // src/openrouter-tool-wrapping.ts
12495
- var OPENROUTER_WRAPPED_TOOL = Symbol("braintrust.openrouter.wrappedTool");
12496
- var OPENROUTER_WRAPPED_CALL_MODEL_RESULT = Symbol(
12497
- "braintrust.openrouter.wrappedCallModelResult"
12498
- );
12499
- var OPENROUTER_CALL_MODEL_STREAM_METHODS = [
12500
- "getFullResponsesStream",
12501
- "getItemsStream",
12502
- "getNewMessagesStream",
12503
- "getReasoningStream",
12504
- "getTextStream",
12505
- "getToolCallsStream",
12506
- "getToolStream"
12507
- ];
12508
- var OPENROUTER_CALL_MODEL_CONTEXT_METHODS = [
12509
- "cancel",
12510
- "getPendingToolCalls",
12511
- "getState",
12512
- "getToolCalls",
12513
- "requiresApproval"
12514
- ];
12515
- function patchOpenRouterCallModelRequestTools(request) {
12516
- if (!Array.isArray(request.tools) || request.tools.length === 0) {
12517
- return void 0;
12518
- }
12519
- const originalTools = request.tools;
12520
- const wrappedTools = originalTools.map((tool) => wrapOpenRouterTool(tool));
12521
- const didPatch = wrappedTools.some(
12522
- (tool, index) => tool !== originalTools[index]
12523
- );
12524
- if (!didPatch) {
12525
- return void 0;
12526
- }
12527
- request.tools = wrappedTools;
12528
- return () => {
12529
- request.tools = originalTools;
12530
- };
12531
- }
12532
- function patchOpenRouterCallModelResult(span, result, request) {
12533
- if (!isObject(result) || isWrappedCallModelResult(result)) {
12534
- return false;
12535
- }
12536
- const resultLike = result;
12537
- const hasInstrumentableMethod = typeof resultLike.getResponse === "function" || typeof resultLike.getText === "function" || OPENROUTER_CALL_MODEL_STREAM_METHODS.some(
12538
- (methodName) => typeof resultLike[methodName] === "function"
12539
- );
12540
- if (!hasInstrumentableMethod) {
12541
- return false;
12542
- }
12543
- Object.defineProperty(resultLike, OPENROUTER_WRAPPED_CALL_MODEL_RESULT, {
12544
- value: true,
12545
- enumerable: false,
12546
- configurable: false
12547
- });
12548
- const originalGetResponse = typeof resultLike.getResponse === "function" ? resultLike.getResponse.bind(resultLike) : void 0;
12549
- const originalGetInitialResponse = typeof resultLike.getInitialResponse === "function" ? resultLike.getInitialResponse.bind(resultLike) : void 0;
12550
- const originalMakeFollowupRequest = typeof resultLike.makeFollowupRequest === "function" ? resultLike.makeFollowupRequest.bind(resultLike) : void 0;
12551
- let ended = false;
12552
- let tracedTurnCount = 0;
12553
- const endSpanWithResult = async (response, fallbackOutput) => {
12554
- if (ended) {
12555
- return;
12556
- }
12557
- ended = true;
12558
- const finalResponse = getFinalOpenRouterCallModelResponse(
12559
- resultLike,
12560
- response
13512
+ subscribeToOpenRouterChannels() {
13513
+ this.unsubscribers.push(
13514
+ traceStreamingChannel(openRouterChannels.chatSend, {
13515
+ name: "openrouter.chat.send",
13516
+ type: "llm" /* LLM */,
13517
+ extractInput: (args) => {
13518
+ const request = getOpenRouterRequestArg(args);
13519
+ const chatGenerationParams = isObject(request?.chatGenerationParams) ? request.chatGenerationParams : {};
13520
+ const httpReferer = request?.httpReferer;
13521
+ const xTitle = request?.xTitle;
13522
+ const { messages, ...metadata } = chatGenerationParams;
13523
+ return {
13524
+ input: messages,
13525
+ metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
13526
+ };
13527
+ },
13528
+ extractOutput: (result) => {
13529
+ return isObject(result) ? result.choices : void 0;
13530
+ },
13531
+ extractMetrics: (result, startTime) => {
13532
+ const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
13533
+ if (startTime) {
13534
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
13535
+ }
13536
+ return metrics;
13537
+ },
13538
+ aggregateChunks: aggregateOpenRouterChatChunks
13539
+ })
12561
13540
  );
12562
- if (finalResponse) {
12563
- const rounds = getOpenRouterCallModelRounds(resultLike);
12564
- const metadata = extractOpenRouterCallModelResultMetadata(
12565
- finalResponse,
12566
- rounds.length + 1
12567
- );
12568
- span.log({
12569
- output: extractOpenRouterResponseOutput(finalResponse, fallbackOutput),
12570
- ...metadata ? { metadata } : {},
12571
- metrics: aggregateOpenRouterCallModelMetrics(rounds, finalResponse)
12572
- });
12573
- span.end();
12574
- return;
12575
- }
12576
- if (fallbackOutput !== void 0) {
12577
- span.log({
12578
- output: fallbackOutput
12579
- });
12580
- }
12581
- span.end();
12582
- };
12583
- const endSpanWithError = (error) => {
12584
- if (ended) {
12585
- return;
12586
- }
12587
- ended = true;
12588
- span.log({
12589
- error: normalizeError(error).message
12590
- });
12591
- span.end();
12592
- };
12593
- const finalizeFromResponse = async (fallbackOutput) => {
12594
- if (!originalGetResponse) {
12595
- await endSpanWithResult(void 0, fallbackOutput);
12596
- return;
12597
- }
12598
- try {
12599
- await endSpanWithResult(await originalGetResponse(), fallbackOutput);
12600
- } catch {
12601
- await endSpanWithResult(void 0, fallbackOutput);
12602
- }
12603
- };
12604
- if (originalGetResponse) {
12605
- resultLike.getResponse = async (...args) => {
12606
- return await withCurrent(span, async () => {
12607
- try {
12608
- const response = await originalGetResponse(...args);
12609
- await endSpanWithResult(response);
12610
- return response;
12611
- } catch (error) {
12612
- endSpanWithError(error);
12613
- throw error;
12614
- }
12615
- });
12616
- };
12617
- }
12618
- if (typeof resultLike.getText === "function") {
12619
- const originalGetText = resultLike.getText.bind(resultLike);
12620
- resultLike.getText = async (...args) => {
12621
- return await withCurrent(span, async () => {
12622
- try {
12623
- const text = await originalGetText(...args);
12624
- await finalizeFromResponse(text);
12625
- return text;
12626
- } catch (error) {
12627
- endSpanWithError(error);
12628
- throw error;
13541
+ this.unsubscribers.push(
13542
+ traceAsyncChannel(openRouterChannels.embeddingsGenerate, {
13543
+ name: "openrouter.embeddings.generate",
13544
+ type: "llm" /* LLM */,
13545
+ extractInput: (args) => {
13546
+ const request = getOpenRouterRequestArg(args);
13547
+ const requestBody = isObject(request?.requestBody) ? request.requestBody : {};
13548
+ const httpReferer = request?.httpReferer;
13549
+ const xTitle = request?.xTitle;
13550
+ const { input, ...metadata } = requestBody;
13551
+ return {
13552
+ input,
13553
+ metadata: buildOpenRouterEmbeddingMetadata(
13554
+ metadata,
13555
+ httpReferer,
13556
+ xTitle
13557
+ )
13558
+ };
13559
+ },
13560
+ extractOutput: (result) => {
13561
+ if (!isObject(result)) {
13562
+ return void 0;
13563
+ }
13564
+ const embedding = result.data?.[0]?.embedding;
13565
+ return Array.isArray(embedding) ? { embedding_length: embedding.length } : void 0;
13566
+ },
13567
+ extractMetadata: (result) => {
13568
+ if (!isObject(result)) {
13569
+ return void 0;
13570
+ }
13571
+ return extractOpenRouterResponseMetadata(result);
13572
+ },
13573
+ extractMetrics: (result) => {
13574
+ return isObject(result) ? parseOpenRouterMetricsFromUsage(result.usage) : {};
12629
13575
  }
12630
- });
12631
- };
12632
- }
12633
- for (const methodName of OPENROUTER_CALL_MODEL_CONTEXT_METHODS) {
12634
- if (typeof resultLike[methodName] !== "function") {
12635
- continue;
12636
- }
12637
- const originalMethod = resultLike[methodName];
12638
- resultLike[methodName] = async (...args) => {
12639
- return await withCurrent(span, async () => {
12640
- return await originalMethod.apply(resultLike, args);
12641
- });
12642
- };
12643
- }
12644
- for (const methodName of OPENROUTER_CALL_MODEL_STREAM_METHODS) {
12645
- if (typeof resultLike[methodName] !== "function") {
12646
- continue;
12647
- }
12648
- const originalMethod = resultLike[methodName];
12649
- resultLike[methodName] = (...args) => {
12650
- const stream = withCurrent(
12651
- span,
12652
- () => originalMethod.apply(resultLike, args)
12653
- );
12654
- if (!isAsyncIterable2(stream)) {
12655
- return stream;
12656
- }
12657
- return wrapAsyncIterableWithSpan({
12658
- finalize: finalizeFromResponse,
12659
- iteratorFactory: () => stream[Symbol.asyncIterator](),
12660
- onError: endSpanWithError,
12661
- span
12662
- });
12663
- };
12664
- }
12665
- if (originalGetInitialResponse) {
12666
- let initialTurnTraced = false;
12667
- resultLike.getInitialResponse = async (...args) => {
12668
- if (initialTurnTraced) {
12669
- return await withCurrent(span, async () => {
12670
- return await originalGetInitialResponse(...args);
12671
- });
12672
- }
12673
- initialTurnTraced = true;
12674
- const resolvedRequest = getOpenRouterResolvedRequest(resultLike, request);
12675
- const childSpan = startOpenRouterCallModelTurnSpan({
12676
- request: resolvedRequest,
12677
- step: tracedTurnCount + 1,
12678
- stepType: tracedTurnCount === 0 ? "initial" : "continue"
12679
- });
12680
- return await withCurrent(childSpan, async () => {
12681
- try {
12682
- const response = await originalGetInitialResponse(...args);
12683
- tracedTurnCount++;
12684
- finishOpenRouterCallModelTurnSpan({
12685
- response,
12686
- step: tracedTurnCount,
12687
- stepType: tracedTurnCount === 1 ? "initial" : "continue",
12688
- span: childSpan
12689
- });
12690
- return response;
12691
- } catch (error) {
12692
- childSpan.log({
12693
- error: normalizeError(error).message
13576
+ })
13577
+ );
13578
+ this.unsubscribers.push(
13579
+ traceStreamingChannel(openRouterChannels.betaResponsesSend, {
13580
+ name: "openrouter.beta.responses.send",
13581
+ type: "llm" /* LLM */,
13582
+ extractInput: (args) => {
13583
+ const request = getOpenRouterRequestArg(args);
13584
+ const openResponsesRequest = isObject(request?.openResponsesRequest) ? request.openResponsesRequest : {};
13585
+ const httpReferer = request?.httpReferer;
13586
+ const xTitle = request?.xTitle;
13587
+ const { input, ...metadata } = openResponsesRequest;
13588
+ return {
13589
+ input,
13590
+ metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
13591
+ };
13592
+ },
13593
+ extractOutput: (result) => extractOpenRouterResponseOutput(result),
13594
+ extractMetadata: (result) => extractOpenRouterResponseMetadata(result),
13595
+ extractMetrics: (result, startTime) => {
13596
+ const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
13597
+ if (startTime) {
13598
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
13599
+ }
13600
+ return metrics;
13601
+ },
13602
+ aggregateChunks: aggregateOpenRouterResponseStreamEvents
13603
+ })
13604
+ );
13605
+ this.unsubscribers.push(
13606
+ traceSyncStreamChannel(openRouterChannels.callModel, {
13607
+ name: "openrouter.callModel",
13608
+ type: "llm" /* LLM */,
13609
+ extractInput: (args) => {
13610
+ const request = getOpenRouterCallModelRequestArg(args);
13611
+ return {
13612
+ input: request ? extractOpenRouterCallModelInput(request) : void 0,
13613
+ metadata: request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" }
13614
+ };
13615
+ },
13616
+ patchResult: ({ endEvent, result, span }) => {
13617
+ return patchOpenRouterCallModelResult({
13618
+ request: getOpenRouterCallModelRequestArg(endEvent.arguments),
13619
+ result,
13620
+ span
12694
13621
  });
12695
- childSpan.end();
12696
- throw error;
12697
13622
  }
12698
- });
12699
- };
12700
- }
12701
- if (originalMakeFollowupRequest) {
12702
- resultLike.makeFollowupRequest = async (...args) => {
12703
- const currentResponse = args[0];
12704
- const toolResults = Array.isArray(args[1]) ? args[1] : [];
12705
- const resolvedRequest = getOpenRouterResolvedRequest(resultLike, request);
12706
- const followupRequest = buildOpenRouterFollowupRequest(
12707
- resolvedRequest,
12708
- currentResponse,
12709
- toolResults
12710
- );
12711
- const childSpan = startOpenRouterCallModelTurnSpan({
12712
- request: followupRequest,
12713
- step: tracedTurnCount + 1,
12714
- stepType: "continue"
12715
- });
12716
- return await withCurrent(childSpan, async () => {
12717
- try {
12718
- const response = await originalMakeFollowupRequest(...args);
12719
- tracedTurnCount++;
12720
- finishOpenRouterCallModelTurnSpan({
12721
- response,
12722
- step: tracedTurnCount,
12723
- stepType: "continue",
12724
- span: childSpan
12725
- });
12726
- return response;
12727
- } catch (error) {
12728
- childSpan.log({
12729
- error: normalizeError(error).message
12730
- });
12731
- childSpan.end();
12732
- throw error;
13623
+ })
13624
+ );
13625
+ this.unsubscribers.push(
13626
+ traceAsyncChannel(openRouterChannels.callModelTurn, {
13627
+ name: "openrouter.beta.responses.send",
13628
+ type: "llm" /* LLM */,
13629
+ extractInput: (args, event) => {
13630
+ const request = getOpenRouterCallModelRequestArg(args);
13631
+ const metadata = request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" };
13632
+ if (isObject(metadata) && "tools" in metadata) {
13633
+ delete metadata.tools;
13634
+ }
13635
+ return {
13636
+ input: request ? extractOpenRouterCallModelInput(request) : void 0,
13637
+ metadata: {
13638
+ ...metadata,
13639
+ step: event.step,
13640
+ step_type: event.stepType
13641
+ }
13642
+ };
13643
+ },
13644
+ extractOutput: (result) => extractOpenRouterResponseOutput(result),
13645
+ extractMetadata: (result, event) => {
13646
+ if (!isObject(result)) {
13647
+ return {
13648
+ step: event?.step,
13649
+ step_type: event?.stepType
13650
+ };
13651
+ }
13652
+ return {
13653
+ ...extractOpenRouterResponseMetadata(result) || {},
13654
+ ...event?.step !== void 0 ? { step: event.step } : {},
13655
+ ...event?.stepType ? { step_type: event.stepType } : {}
13656
+ };
13657
+ },
13658
+ extractMetrics: (result) => isObject(result) ? parseOpenRouterMetricsFromUsage(result.usage) : {}
13659
+ })
13660
+ );
13661
+ this.unsubscribers.push(
13662
+ traceStreamingChannel(openRouterChannels.toolExecute, {
13663
+ name: "openrouter.tool",
13664
+ type: "tool" /* TOOL */,
13665
+ extractInput: (args, event) => ({
13666
+ input: args[0],
13667
+ metadata: {
13668
+ provider: "openrouter",
13669
+ tool_name: event.toolName,
13670
+ ...event.toolCallId ? { tool_call_id: event.toolCallId } : {}
13671
+ }
13672
+ }),
13673
+ extractOutput: (result) => result,
13674
+ extractMetrics: () => ({}),
13675
+ aggregateChunks: (chunks) => ({
13676
+ output: chunks.length > 0 ? chunks[chunks.length - 1] : void 0,
13677
+ metrics: {}
13678
+ })
13679
+ })
13680
+ );
13681
+ const callModelChannel = openRouterChannels.callModel.tracingChannel();
13682
+ const callModelHandlers = {
13683
+ start: (event) => {
13684
+ const request = getOpenRouterCallModelRequestArg(event.arguments);
13685
+ if (!request) {
13686
+ return;
12733
13687
  }
12734
- });
13688
+ patchOpenRouterCallModelRequestTools(request);
13689
+ }
12735
13690
  };
13691
+ callModelChannel.subscribe(callModelHandlers);
13692
+ this.unsubscribers.push(() => {
13693
+ callModelChannel.unsubscribe(callModelHandlers);
13694
+ });
12736
13695
  }
12737
- return true;
12738
- }
12739
- function wrapOpenRouterTool(tool) {
12740
- if (isWrappedTool(tool) || !tool.function || typeof tool.function !== "object" || typeof tool.function.execute !== "function") {
12741
- return tool;
12742
- }
12743
- const toolName = tool.function.name || "tool";
12744
- const originalExecute = tool.function.execute;
12745
- const wrappedTool = {
12746
- ...tool,
12747
- function: {
12748
- ...tool.function,
12749
- execute(...args) {
12750
- return traceToolExecution({
12751
- args,
12752
- execute: () => Reflect.apply(originalExecute, this, args),
12753
- toolCallId: getToolCallId(args[1]),
12754
- toolName
12755
- });
12756
- }
12757
- }
12758
- };
12759
- Object.defineProperty(wrappedTool, OPENROUTER_WRAPPED_TOOL, {
12760
- value: true,
12761
- enumerable: false,
12762
- configurable: false
12763
- });
12764
- return wrappedTool;
12765
- }
12766
- function isWrappedTool(tool) {
12767
- return Boolean(tool[OPENROUTER_WRAPPED_TOOL]);
12768
- }
12769
- function isWrappedCallModelResult(value) {
12770
- return Boolean(
12771
- isObject(value) && value[OPENROUTER_WRAPPED_CALL_MODEL_RESULT]
12772
- );
12773
- }
12774
- function traceToolExecution(args) {
12775
- const tracingChannel2 = openRouterChannels.toolExecute.tracingChannel();
12776
- const input = args.args.length > 0 ? args.args[0] : void 0;
12777
- const event = {
12778
- arguments: [input],
12779
- span_info: {
12780
- name: args.toolName
12781
- },
12782
- toolCallId: args.toolCallId,
12783
- toolName: args.toolName
12784
- };
12785
- tracingChannel2.start.publish(event);
12786
- try {
12787
- const result = args.execute();
12788
- return publishToolResult(tracingChannel2, event, result);
12789
- } catch (error) {
12790
- event.error = normalizeError(error);
12791
- tracingChannel2.error.publish(event);
12792
- throw error;
13696
+ };
13697
+ function normalizeArgs(args) {
13698
+ if (Array.isArray(args)) {
13699
+ return args;
12793
13700
  }
12794
- }
12795
- function publishToolResult(tracingChannel2, event, result) {
12796
- if (isPromiseLike(result)) {
12797
- return result.then(
12798
- (resolved) => {
12799
- event.result = resolved;
12800
- tracingChannel2.asyncEnd.publish(event);
12801
- return resolved;
12802
- },
12803
- (error) => {
12804
- event.error = normalizeError(error);
12805
- tracingChannel2.error.publish(event);
12806
- throw error;
12807
- }
12808
- );
13701
+ if (isArrayLike(args)) {
13702
+ return Array.from(args);
12809
13703
  }
12810
- event.result = result;
12811
- tracingChannel2.asyncEnd.publish(event);
12812
- return result;
12813
- }
12814
- function getToolCallId(context) {
12815
- const toolContext = context;
12816
- return typeof toolContext?.toolCall?.id === "string" ? toolContext.toolCall.id : void 0;
13704
+ return [args];
12817
13705
  }
12818
- function extractOpenRouterCallModelResultMetadata(response, turnCount) {
12819
- const combined = {
12820
- ...extractOpenRouterResponseMetadata(response) || {},
12821
- ...turnCount !== void 0 ? { turn_count: turnCount } : {}
12822
- };
12823
- return Object.keys(combined).length > 0 ? combined : void 0;
13706
+ function isArrayLike(value) {
13707
+ return isObject(value) && "length" in value && typeof value.length === "number" && Number.isInteger(value.length) && value.length >= 0;
12824
13708
  }
12825
- function getFinalOpenRouterCallModelResponse(result, response) {
12826
- if (isObject(response)) {
12827
- return response;
13709
+ function getOpenRouterRequestArg(args) {
13710
+ const normalizedArgs = normalizeArgs(args);
13711
+ const keyedCandidate = normalizedArgs.find(
13712
+ (arg) => isObject(arg) && ("chatGenerationParams" in arg || "requestBody" in arg || "openResponsesRequest" in arg)
13713
+ );
13714
+ if (isObject(keyedCandidate)) {
13715
+ return keyedCandidate;
12828
13716
  }
12829
- return isObject(result.finalResponse) ? result.finalResponse : void 0;
13717
+ const firstObjectArg = normalizedArgs.find((arg) => isObject(arg));
13718
+ return isObject(firstObjectArg) ? firstObjectArg : void 0;
12830
13719
  }
12831
- function getOpenRouterCallModelRounds(result) {
12832
- if (!Array.isArray(result.allToolExecutionRounds)) {
12833
- return [];
12834
- }
12835
- return result.allToolExecutionRounds.filter((round) => isObject(round)).map((round) => ({
12836
- response: isObject(round.response) ? round.response : void 0,
12837
- round: typeof round.round === "number" ? round.round : void 0,
12838
- toolResults: Array.isArray(round.toolResults) ? round.toolResults : []
12839
- })).filter((round) => round.response !== void 0);
13720
+ function getOpenRouterCallModelRequestArg(args) {
13721
+ const firstObjectArg = normalizeArgs(args).find((arg) => isObject(arg));
13722
+ return isObject(firstObjectArg) ? firstObjectArg : void 0;
12840
13723
  }
12841
- function aggregateOpenRouterCallModelMetrics(rounds, finalResponse) {
13724
+ var TOKEN_NAME_MAP2 = {
13725
+ promptTokens: "prompt_tokens",
13726
+ inputTokens: "prompt_tokens",
13727
+ completionTokens: "completion_tokens",
13728
+ outputTokens: "completion_tokens",
13729
+ totalTokens: "tokens",
13730
+ prompt_tokens: "prompt_tokens",
13731
+ input_tokens: "prompt_tokens",
13732
+ completion_tokens: "completion_tokens",
13733
+ output_tokens: "completion_tokens",
13734
+ total_tokens: "tokens"
13735
+ };
13736
+ var TOKEN_DETAIL_PREFIX_MAP = {
13737
+ promptTokensDetails: "prompt",
13738
+ inputTokensDetails: "prompt",
13739
+ completionTokensDetails: "completion",
13740
+ outputTokensDetails: "completion",
13741
+ costDetails: "cost",
13742
+ prompt_tokens_details: "prompt",
13743
+ input_tokens_details: "prompt",
13744
+ completion_tokens_details: "completion",
13745
+ output_tokens_details: "completion",
13746
+ cost_details: "cost"
13747
+ };
13748
+ function camelToSnake(value) {
13749
+ return value.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`);
13750
+ }
13751
+ function parseOpenRouterMetricsFromUsage(usage) {
13752
+ if (!isObject(usage)) {
13753
+ return {};
13754
+ }
12842
13755
  const metrics = {};
12843
- const responses = [
12844
- ...rounds.map((round) => round.response).filter(isObject),
12845
- finalResponse
12846
- ];
12847
- for (const response of responses) {
12848
- const responseMetrics = parseOpenRouterMetricsFromUsage(response.usage);
12849
- for (const [name, value] of Object.entries(responseMetrics)) {
12850
- metrics[name] = (metrics[name] || 0) + value;
13756
+ for (const [name, value] of Object.entries(usage)) {
13757
+ if (typeof value === "number") {
13758
+ metrics[TOKEN_NAME_MAP2[name] || camelToSnake(name)] = value;
13759
+ continue;
13760
+ }
13761
+ if (!isObject(value)) {
13762
+ continue;
13763
+ }
13764
+ const prefix = TOKEN_DETAIL_PREFIX_MAP[name];
13765
+ if (!prefix) {
13766
+ continue;
13767
+ }
13768
+ for (const [nestedName, nestedValue] of Object.entries(value)) {
13769
+ if (typeof nestedValue !== "number") {
13770
+ continue;
13771
+ }
13772
+ metrics[`${prefix}_${camelToSnake(nestedName)}`] = nestedValue;
12851
13773
  }
12852
13774
  }
12853
13775
  return metrics;
12854
13776
  }
12855
- function buildNextOpenRouterCallModelInput(currentInput, response, toolResults) {
12856
- const normalizedInput = Array.isArray(currentInput) ? [...currentInput] : currentInput === void 0 ? [] : [currentInput];
12857
- const responseOutput = Array.isArray(response.output) ? response.output : response.output === void 0 ? [] : [response.output];
12858
- return [...normalizedInput, ...responseOutput, ...toolResults].map(
12859
- (entry) => sanitizeOpenRouterLoggedValue(entry)
12860
- );
13777
+ function extractOpenRouterUsageMetadata(usage) {
13778
+ if (!isObject(usage)) {
13779
+ return void 0;
13780
+ }
13781
+ const metadata = {};
13782
+ if (typeof usage.isByok === "boolean") {
13783
+ metadata.is_byok = usage.isByok;
13784
+ } else if (typeof usage.is_byok === "boolean") {
13785
+ metadata.is_byok = usage.is_byok;
13786
+ }
13787
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
12861
13788
  }
12862
- function startOpenRouterCallModelTurnSpan(args) {
12863
- const requestRecord = isObject(args.request) ? args.request : void 0;
12864
- const metadata = requestRecord ? extractOpenRouterCallModelMetadata(requestRecord) : { provider: "openrouter" };
12865
- if (isObject(metadata) && "tools" in metadata) {
12866
- delete metadata.tools;
13789
+ var OMITTED_OPENROUTER_KEYS = /* @__PURE__ */ new Set([
13790
+ "execute",
13791
+ "render",
13792
+ "nextTurnParams",
13793
+ "requireApproval"
13794
+ ]);
13795
+ function parseOpenRouterModelString(model) {
13796
+ if (typeof model !== "string") {
13797
+ return { model };
12867
13798
  }
12868
- return startSpan({
12869
- name: "openrouter.beta.responses.send",
12870
- spanAttributes: {
12871
- type: "llm" /* LLM */
12872
- },
12873
- event: {
12874
- input: requestRecord ? extractOpenRouterCallModelInput(requestRecord) : void 0,
12875
- metadata: {
12876
- ...metadata,
12877
- step: args.step,
12878
- step_type: args.stepType
12879
- }
12880
- }
12881
- });
13799
+ const slashIndex = model.indexOf("/");
13800
+ if (slashIndex > 0 && slashIndex < model.length - 1) {
13801
+ return {
13802
+ provider: model.substring(0, slashIndex),
13803
+ model: model.substring(slashIndex + 1)
13804
+ };
13805
+ }
13806
+ return { model };
12882
13807
  }
12883
- function finishOpenRouterCallModelTurnSpan(args) {
12884
- if (!isObject(args.response)) {
12885
- args.span.end();
12886
- return;
13808
+ function isZodSchema3(value) {
13809
+ return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
13810
+ }
13811
+ function serializeZodSchema3(schema) {
13812
+ try {
13813
+ return zodToJsonSchema(schema);
13814
+ } catch {
13815
+ return {
13816
+ type: "object",
13817
+ description: "Zod schema (conversion failed)"
13818
+ };
12887
13819
  }
12888
- args.span.log({
12889
- output: extractOpenRouterResponseOutput(args.response),
12890
- ...extractOpenRouterResponseMetadata(args.response) ? {
12891
- metadata: {
12892
- ...extractOpenRouterResponseMetadata(args.response),
12893
- ...args.step !== void 0 ? { step: args.step } : {},
12894
- ...args.stepType ? { step_type: args.stepType } : {}
12895
- }
12896
- } : {},
12897
- metrics: parseOpenRouterMetricsFromUsage(args.response.usage)
12898
- });
12899
- args.span.end();
12900
13820
  }
12901
- function getOpenRouterResolvedRequest(result, request) {
12902
- if (isObject(result.resolvedRequest)) {
12903
- return result.resolvedRequest;
13821
+ function serializeOpenRouterTool(tool) {
13822
+ if (!isObject(tool)) {
13823
+ return tool;
12904
13824
  }
12905
- return request;
13825
+ const serialized = {};
13826
+ for (const [key, value] of Object.entries(tool)) {
13827
+ if (OMITTED_OPENROUTER_KEYS.has(key)) {
13828
+ continue;
13829
+ }
13830
+ if (key === "function" && isObject(value)) {
13831
+ serialized.function = sanitizeOpenRouterLoggedValue(value);
13832
+ continue;
13833
+ }
13834
+ serialized[key] = sanitizeOpenRouterLoggedValue(value);
13835
+ }
13836
+ return serialized;
12906
13837
  }
12907
- function buildOpenRouterFollowupRequest(request, currentResponse, toolResults) {
12908
- if (!request) {
13838
+ function serializeOpenRouterToolsForLogging(tools) {
13839
+ if (!Array.isArray(tools)) {
12909
13840
  return void 0;
12910
13841
  }
12911
- return {
12912
- ...request,
12913
- input: buildNextOpenRouterCallModelInput(
12914
- extractOpenRouterCallModelInput(request),
12915
- isObject(currentResponse) ? currentResponse : {},
12916
- toolResults
12917
- ),
12918
- stream: false
12919
- };
13842
+ return tools.map((tool) => serializeOpenRouterTool(tool));
12920
13843
  }
12921
- function wrapAsyncIterableWithSpan(args) {
12922
- return {
12923
- [Symbol.asyncIterator]() {
12924
- const iterator = args.iteratorFactory();
12925
- return {
12926
- next(value) {
12927
- return withCurrent(
12928
- args.span,
12929
- () => value === void 0 ? iterator.next() : iterator.next(value)
12930
- ).then(
12931
- async (result) => {
12932
- if (result.done) {
12933
- await args.finalize();
12934
- }
12935
- return result;
12936
- },
12937
- (error) => {
12938
- args.onError(error);
12939
- throw error;
12940
- }
12941
- );
12942
- },
12943
- return(value) {
12944
- if (typeof iterator.return !== "function") {
12945
- return args.finalize().then(() => ({
12946
- done: true,
12947
- value
12948
- }));
12949
- }
12950
- return withCurrent(args.span, () => iterator.return(value)).then(
12951
- async (result) => {
12952
- await args.finalize();
12953
- return result;
12954
- },
12955
- (error) => {
12956
- args.onError(error);
12957
- throw error;
12958
- }
12959
- );
12960
- },
12961
- throw(error) {
12962
- args.onError(error);
12963
- if (typeof iterator.throw !== "function") {
12964
- return Promise.reject(error);
12965
- }
12966
- return withCurrent(args.span, () => iterator.throw(error));
12967
- },
12968
- [Symbol.asyncIterator]() {
12969
- return this;
12970
- }
12971
- };
13844
+ function sanitizeOpenRouterLoggedValue(value) {
13845
+ if (isZodSchema3(value)) {
13846
+ return serializeZodSchema3(value);
13847
+ }
13848
+ if (typeof value === "function") {
13849
+ return "[Function]";
13850
+ }
13851
+ if (Array.isArray(value)) {
13852
+ return value.map((entry) => sanitizeOpenRouterLoggedValue(entry));
13853
+ }
13854
+ if (!isObject(value)) {
13855
+ return value;
13856
+ }
13857
+ const sanitized = {};
13858
+ for (const [key, entry] of Object.entries(value)) {
13859
+ if (OMITTED_OPENROUTER_KEYS.has(key)) {
13860
+ continue;
13861
+ }
13862
+ if (key === "tools" && Array.isArray(entry)) {
13863
+ sanitized.tools = serializeOpenRouterToolsForLogging(entry);
13864
+ continue;
12972
13865
  }
13866
+ sanitized[key] = sanitizeOpenRouterLoggedValue(entry);
13867
+ }
13868
+ return sanitized;
13869
+ }
13870
+ function buildOpenRouterMetadata(metadata, httpReferer, xTitle) {
13871
+ const sanitized = sanitizeOpenRouterLoggedValue(metadata);
13872
+ const metadataRecord = isObject(sanitized) ? sanitized : {};
13873
+ const { model, provider: providerRouting, ...rest } = metadataRecord;
13874
+ const normalizedModel = parseOpenRouterModelString(model);
13875
+ return {
13876
+ ...rest,
13877
+ ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
13878
+ ...providerRouting !== void 0 ? { providerRouting } : {},
13879
+ ...httpReferer !== void 0 ? { httpReferer } : {},
13880
+ ...xTitle !== void 0 ? { xTitle } : {},
13881
+ provider: normalizedModel.provider || "openrouter"
12973
13882
  };
12974
13883
  }
12975
- function isAsyncIterable2(value) {
12976
- return !!value && (typeof value === "object" || typeof value === "function") && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
13884
+ function buildOpenRouterEmbeddingMetadata(metadata, httpReferer, xTitle) {
13885
+ const normalized = buildOpenRouterMetadata(metadata, httpReferer, xTitle);
13886
+ return typeof normalized.model === "string" ? {
13887
+ ...normalized,
13888
+ embedding_model: normalized.model
13889
+ } : normalized;
12977
13890
  }
12978
- function isPromiseLike(value) {
12979
- return !!value && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
13891
+ function extractOpenRouterCallModelInput(request) {
13892
+ return isObject(request) && "input" in request ? sanitizeOpenRouterLoggedValue(request.input) : void 0;
12980
13893
  }
12981
- function normalizeError(error) {
12982
- return error instanceof Error ? error : new Error(String(error));
13894
+ function extractOpenRouterCallModelMetadata(request) {
13895
+ if (!isObject(request)) {
13896
+ return { provider: "openrouter" };
13897
+ }
13898
+ const { input: _input, ...metadata } = request;
13899
+ return buildOpenRouterMetadata(metadata, void 0, void 0);
12983
13900
  }
12984
-
12985
- // src/instrumentation/plugins/openrouter-plugin.ts
12986
- var OpenRouterPlugin = class extends BasePlugin {
12987
- onEnable() {
12988
- this.subscribeToOpenRouterChannels();
13901
+ function extractOpenRouterResponseMetadata(result) {
13902
+ if (!isObject(result)) {
13903
+ return void 0;
12989
13904
  }
12990
- onDisable() {
12991
- this.unsubscribers = unsubscribeAll(this.unsubscribers);
13905
+ const { output: _output, data: _data, usage, ...metadata } = result;
13906
+ const sanitized = sanitizeOpenRouterLoggedValue(metadata);
13907
+ const metadataRecord = isObject(sanitized) ? sanitized : {};
13908
+ const { model, provider, ...rest } = metadataRecord;
13909
+ const normalizedModel = parseOpenRouterModelString(model);
13910
+ const normalizedProvider = (typeof provider === "string" ? provider : void 0) || normalizedModel.provider;
13911
+ const usageMetadata = extractOpenRouterUsageMetadata(usage);
13912
+ const combined = {
13913
+ ...rest,
13914
+ ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
13915
+ ...usageMetadata || {},
13916
+ ...normalizedProvider !== void 0 ? { provider: normalizedProvider } : {}
13917
+ };
13918
+ return Object.keys(combined).length > 0 ? combined : void 0;
13919
+ }
13920
+ function extractOpenRouterResponseOutput(response, fallbackOutput) {
13921
+ if (isObject(response) && "output" in response && response.output !== void 0) {
13922
+ return sanitizeOpenRouterLoggedValue(response.output);
12992
13923
  }
12993
- subscribeToOpenRouterChannels() {
12994
- this.unsubscribers.push(
12995
- traceStreamingChannel(openRouterChannels.chatSend, {
12996
- name: "openrouter.chat.send",
12997
- type: "llm" /* LLM */,
12998
- extractInput: (args) => {
12999
- const request = getOpenRouterRequestArg(args);
13000
- const chatGenerationParams = isObject(request?.chatGenerationParams) ? request.chatGenerationParams : {};
13001
- const httpReferer = request?.httpReferer;
13002
- const xTitle = request?.xTitle;
13003
- const { messages, ...metadata } = chatGenerationParams;
13004
- return {
13005
- input: messages,
13006
- metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
13007
- };
13008
- },
13009
- extractOutput: (result) => {
13010
- return isObject(result) ? result.choices : void 0;
13011
- },
13012
- extractMetrics: (result, startTime) => {
13013
- const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
13014
- if (startTime) {
13015
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
13016
- }
13017
- return metrics;
13018
- },
13019
- aggregateChunks: aggregateOpenRouterChatChunks
13020
- })
13021
- );
13022
- this.unsubscribers.push(
13023
- traceAsyncChannel(openRouterChannels.embeddingsGenerate, {
13024
- name: "openrouter.embeddings.generate",
13025
- type: "llm" /* LLM */,
13026
- extractInput: (args) => {
13027
- const request = getOpenRouterRequestArg(args);
13028
- const requestBody = isObject(request?.requestBody) ? request.requestBody : {};
13029
- const httpReferer = request?.httpReferer;
13030
- const xTitle = request?.xTitle;
13031
- const { input, ...metadata } = requestBody;
13032
- return {
13033
- input,
13034
- metadata: buildOpenRouterEmbeddingMetadata(
13035
- metadata,
13036
- httpReferer,
13037
- xTitle
13038
- )
13039
- };
13040
- },
13041
- extractOutput: (result) => {
13042
- if (!isObject(result)) {
13043
- return void 0;
13044
- }
13045
- const embedding = result.data?.[0]?.embedding;
13046
- return Array.isArray(embedding) ? { embedding_length: embedding.length } : void 0;
13047
- },
13048
- extractMetadata: (result) => {
13049
- if (!isObject(result)) {
13050
- return void 0;
13051
- }
13052
- return extractOpenRouterResponseMetadata(result);
13053
- },
13054
- extractMetrics: (result) => {
13055
- return isObject(result) ? parseOpenRouterMetricsFromUsage(result.usage) : {};
13056
- }
13057
- })
13058
- );
13059
- this.unsubscribers.push(
13060
- traceStreamingChannel(openRouterChannels.betaResponsesSend, {
13061
- name: "openrouter.beta.responses.send",
13062
- type: "llm" /* LLM */,
13063
- extractInput: (args) => {
13064
- const request = getOpenRouterRequestArg(args);
13065
- const openResponsesRequest = isObject(request?.openResponsesRequest) ? request.openResponsesRequest : {};
13066
- const httpReferer = request?.httpReferer;
13067
- const xTitle = request?.xTitle;
13068
- const { input, ...metadata } = openResponsesRequest;
13069
- return {
13070
- input,
13071
- metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
13072
- };
13073
- },
13074
- extractOutput: (result) => extractOpenRouterResponseOutput(result),
13075
- extractMetadata: (result) => extractOpenRouterResponseMetadata(result),
13076
- extractMetrics: (result, startTime) => {
13077
- const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
13078
- if (startTime) {
13079
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
13080
- }
13081
- return metrics;
13082
- },
13083
- aggregateChunks: aggregateOpenRouterResponseStreamEvents
13084
- })
13085
- );
13086
- this.unsubscribers.push(
13087
- traceSyncStreamChannel(openRouterChannels.callModel, {
13088
- name: "openrouter.callModel",
13089
- type: "llm" /* LLM */,
13090
- extractInput: (args) => {
13091
- const request = getOpenRouterCallModelRequestArg(args);
13092
- return {
13093
- input: request ? extractOpenRouterCallModelInput(request) : void 0,
13094
- metadata: request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" }
13095
- };
13096
- },
13097
- patchResult: ({ endEvent, result, span }) => {
13098
- return patchOpenRouterCallModelResult(
13099
- span,
13100
- result,
13101
- getOpenRouterCallModelRequestArg(endEvent.arguments)
13102
- );
13103
- }
13104
- })
13105
- );
13106
- this.unsubscribers.push(
13107
- traceStreamingChannel(openRouterChannels.toolExecute, {
13108
- name: "openrouter.tool",
13109
- type: "tool" /* TOOL */,
13110
- extractInput: (args, event) => ({
13111
- input: args[0],
13112
- metadata: {
13113
- provider: "openrouter",
13114
- tool_name: event.toolName,
13115
- ...event.toolCallId ? { tool_call_id: event.toolCallId } : {}
13116
- }
13117
- }),
13118
- extractOutput: (result) => result,
13119
- extractMetrics: () => ({}),
13120
- aggregateChunks: (chunks) => ({
13121
- output: chunks.length > 0 ? chunks[chunks.length - 1] : void 0,
13122
- metrics: {}
13123
- })
13124
- })
13125
- );
13126
- const callModelChannel = openRouterChannels.callModel.tracingChannel();
13127
- const callModelHandlers = {
13128
- start: (event) => {
13129
- const request = getOpenRouterCallModelRequestArg(event.arguments);
13130
- if (!request) {
13131
- return;
13132
- }
13133
- patchOpenRouterCallModelRequestTools(request);
13134
- }
13135
- };
13136
- callModelChannel.subscribe(callModelHandlers);
13137
- this.unsubscribers.push(() => {
13138
- callModelChannel.unsubscribe(callModelHandlers);
13139
- });
13924
+ if (fallbackOutput !== void 0) {
13925
+ return sanitizeOpenRouterLoggedValue(fallbackOutput);
13140
13926
  }
13141
- };
13142
- function normalizeArgs(args) {
13143
- if (Array.isArray(args)) {
13144
- return args;
13927
+ return void 0;
13928
+ }
13929
+ var OPENROUTER_WRAPPED_TOOL = Symbol("braintrust.openrouter.wrappedTool");
13930
+ function patchOpenRouterCallModelRequestTools(request) {
13931
+ if (!Array.isArray(request.tools) || request.tools.length === 0) {
13932
+ return void 0;
13145
13933
  }
13146
- if (isArrayLike(args)) {
13147
- return Array.from(args);
13934
+ const originalTools = request.tools;
13935
+ const wrappedTools = originalTools.map((tool) => wrapOpenRouterTool(tool));
13936
+ const didPatch = wrappedTools.some(
13937
+ (tool, index) => tool !== originalTools[index]
13938
+ );
13939
+ if (!didPatch) {
13940
+ return void 0;
13941
+ }
13942
+ request.tools = wrappedTools;
13943
+ return () => {
13944
+ request.tools = originalTools;
13945
+ };
13946
+ }
13947
+ function wrapOpenRouterTool(tool) {
13948
+ if (isWrappedTool(tool) || !tool.function || typeof tool.function !== "object" || typeof tool.function.execute !== "function") {
13949
+ return tool;
13950
+ }
13951
+ const toolName = tool.function.name || "tool";
13952
+ const originalExecute = tool.function.execute;
13953
+ const wrappedTool = {
13954
+ ...tool,
13955
+ function: {
13956
+ ...tool.function,
13957
+ execute(...args) {
13958
+ return traceToolExecution({
13959
+ args,
13960
+ execute: () => Reflect.apply(originalExecute, this, args),
13961
+ toolCallId: getToolCallId(args[1]),
13962
+ toolName
13963
+ });
13964
+ }
13965
+ }
13966
+ };
13967
+ Object.defineProperty(wrappedTool, OPENROUTER_WRAPPED_TOOL, {
13968
+ value: true,
13969
+ enumerable: false,
13970
+ configurable: false
13971
+ });
13972
+ return wrappedTool;
13973
+ }
13974
+ function isWrappedTool(tool) {
13975
+ return Boolean(tool[OPENROUTER_WRAPPED_TOOL]);
13976
+ }
13977
+ function traceToolExecution(args) {
13978
+ const tracingChannel2 = openRouterChannels.toolExecute.tracingChannel();
13979
+ const input = args.args.length > 0 ? args.args[0] : void 0;
13980
+ const event = {
13981
+ arguments: [input],
13982
+ span_info: {
13983
+ name: args.toolName
13984
+ },
13985
+ toolCallId: args.toolCallId,
13986
+ toolName: args.toolName
13987
+ };
13988
+ tracingChannel2.start.publish(event);
13989
+ try {
13990
+ const result = args.execute();
13991
+ return publishToolResult(tracingChannel2, event, result);
13992
+ } catch (error) {
13993
+ event.error = normalizeError(error);
13994
+ tracingChannel2.error.publish(event);
13995
+ throw error;
13148
13996
  }
13149
- return [args];
13150
- }
13151
- function isArrayLike(value) {
13152
- return isObject(value) && "length" in value && typeof value.length === "number" && Number.isInteger(value.length) && value.length >= 0;
13153
13997
  }
13154
- function getOpenRouterRequestArg(args) {
13155
- const normalizedArgs = normalizeArgs(args);
13156
- const keyedCandidate = normalizedArgs.find(
13157
- (arg) => isObject(arg) && ("chatGenerationParams" in arg || "requestBody" in arg || "openResponsesRequest" in arg)
13158
- );
13159
- if (isObject(keyedCandidate)) {
13160
- return keyedCandidate;
13998
+ function publishToolResult(tracingChannel2, event, result) {
13999
+ if (isPromiseLike2(result)) {
14000
+ return result.then(
14001
+ (resolved) => {
14002
+ event.result = resolved;
14003
+ tracingChannel2.asyncEnd.publish(event);
14004
+ return resolved;
14005
+ },
14006
+ (error) => {
14007
+ event.error = normalizeError(error);
14008
+ tracingChannel2.error.publish(event);
14009
+ throw error;
14010
+ }
14011
+ );
13161
14012
  }
13162
- const firstObjectArg = normalizedArgs.find((arg) => isObject(arg));
13163
- return isObject(firstObjectArg) ? firstObjectArg : void 0;
14013
+ event.result = result;
14014
+ tracingChannel2.asyncEnd.publish(event);
14015
+ return result;
13164
14016
  }
13165
- function getOpenRouterCallModelRequestArg(args) {
13166
- const firstObjectArg = normalizeArgs(args).find((arg) => isObject(arg));
13167
- return isObject(firstObjectArg) ? firstObjectArg : void 0;
14017
+ function getToolCallId(context) {
14018
+ const toolContext = context;
14019
+ return typeof toolContext?.toolCall?.id === "string" ? toolContext.toolCall.id : void 0;
14020
+ }
14021
+ function isPromiseLike2(value) {
14022
+ return !!value && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
13168
14023
  }
13169
14024
  function aggregateOpenRouterChatChunks(chunks) {
13170
14025
  let role;
@@ -13185,95 +14040,438 @@ function aggregateOpenRouterChatChunks(chunks) {
13185
14040
  }
13186
14041
  continue;
13187
14042
  }
13188
- if (!role && delta.role) {
13189
- role = delta.role;
13190
- }
13191
- if (typeof delta.content === "string") {
13192
- content += delta.content;
13193
- }
13194
- const choiceFinishReason = choice?.finishReason ?? choice?.finish_reason ?? void 0;
13195
- const deltaFinishReason = delta.finishReason ?? delta.finish_reason ?? void 0;
13196
- if (choiceFinishReason !== void 0) {
13197
- finishReason = choiceFinishReason;
13198
- } else if (deltaFinishReason !== void 0) {
13199
- finishReason = deltaFinishReason;
13200
- }
13201
- const toolCallDeltas = Array.isArray(delta.toolCalls) ? delta.toolCalls : Array.isArray(delta.tool_calls) ? delta.tool_calls : void 0;
13202
- if (!toolCallDeltas) {
14043
+ if (!role && delta.role) {
14044
+ role = delta.role;
14045
+ }
14046
+ if (typeof delta.content === "string") {
14047
+ content += delta.content;
14048
+ }
14049
+ const choiceFinishReason = choice?.finishReason ?? choice?.finish_reason ?? void 0;
14050
+ const deltaFinishReason = delta.finishReason ?? delta.finish_reason ?? void 0;
14051
+ if (choiceFinishReason !== void 0) {
14052
+ finishReason = choiceFinishReason;
14053
+ } else if (deltaFinishReason !== void 0) {
14054
+ finishReason = deltaFinishReason;
14055
+ }
14056
+ const toolCallDeltas = Array.isArray(delta.toolCalls) ? delta.toolCalls : Array.isArray(delta.tool_calls) ? delta.tool_calls : void 0;
14057
+ if (!toolCallDeltas) {
14058
+ continue;
14059
+ }
14060
+ for (const toolDelta of toolCallDeltas) {
14061
+ if (!toolDelta?.function) {
14062
+ continue;
14063
+ }
14064
+ const toolIndex = toolDelta.index ?? 0;
14065
+ const existingToolCall = toolCalls?.[toolIndex];
14066
+ if (!existingToolCall || toolDelta.id && existingToolCall.id !== void 0 && existingToolCall.id !== toolDelta.id) {
14067
+ const nextToolCalls = [...toolCalls || []];
14068
+ nextToolCalls[toolIndex] = {
14069
+ index: toolIndex,
14070
+ id: toolDelta.id,
14071
+ type: toolDelta.type,
14072
+ function: {
14073
+ name: toolDelta.function.name,
14074
+ arguments: toolDelta.function.arguments || ""
14075
+ }
14076
+ };
14077
+ toolCalls = nextToolCalls;
14078
+ continue;
14079
+ }
14080
+ const current = existingToolCall;
14081
+ if (toolDelta.id && !current.id) {
14082
+ current.id = toolDelta.id;
14083
+ }
14084
+ if (toolDelta.type && !current.type) {
14085
+ current.type = toolDelta.type;
14086
+ }
14087
+ if (toolDelta.function.name && !current.function.name) {
14088
+ current.function.name = toolDelta.function.name;
14089
+ }
14090
+ current.function.arguments += toolDelta.function.arguments || "";
14091
+ }
14092
+ }
14093
+ return {
14094
+ output: [
14095
+ {
14096
+ index: 0,
14097
+ message: {
14098
+ role,
14099
+ content: content || void 0,
14100
+ ...toolCalls ? { tool_calls: toolCalls } : {}
14101
+ },
14102
+ logprobs: null,
14103
+ finish_reason: finishReason
14104
+ }
14105
+ ],
14106
+ metrics
14107
+ };
14108
+ }
14109
+ function aggregateOpenRouterResponseStreamEvents(chunks) {
14110
+ let finalResponse;
14111
+ for (const chunk of chunks) {
14112
+ const response = chunk?.response;
14113
+ if (!response) {
14114
+ continue;
14115
+ }
14116
+ if (chunk.type === "response.completed" || chunk.type === "response.incomplete" || chunk.type === "response.failed") {
14117
+ finalResponse = response;
14118
+ }
14119
+ }
14120
+ if (!finalResponse) {
14121
+ return {
14122
+ output: void 0,
14123
+ metrics: {}
14124
+ };
14125
+ }
14126
+ return {
14127
+ output: extractOpenRouterResponseOutput(finalResponse),
14128
+ metrics: parseOpenRouterMetricsFromUsage(finalResponse.usage),
14129
+ ...extractOpenRouterResponseMetadata(finalResponse) ? { metadata: extractOpenRouterResponseMetadata(finalResponse) } : {}
14130
+ };
14131
+ }
14132
+ var OPENROUTER_WRAPPED_CALL_MODEL_RESULT = Symbol(
14133
+ "braintrust.openrouter.wrappedCallModelResult"
14134
+ );
14135
+ var OPENROUTER_CALL_MODEL_STREAM_METHODS = [
14136
+ "getFullResponsesStream",
14137
+ "getItemsStream",
14138
+ "getNewMessagesStream",
14139
+ "getReasoningStream",
14140
+ "getTextStream",
14141
+ "getToolCallsStream",
14142
+ "getToolStream"
14143
+ ];
14144
+ var OPENROUTER_CALL_MODEL_CONTEXT_METHODS = [
14145
+ "cancel",
14146
+ "getPendingToolCalls",
14147
+ "getState",
14148
+ "getToolCalls",
14149
+ "requiresApproval"
14150
+ ];
14151
+ function patchOpenRouterCallModelResult(args) {
14152
+ const { request, result, span } = args;
14153
+ if (!isObject(result) || isWrappedCallModelResult(result)) {
14154
+ return false;
14155
+ }
14156
+ const resultLike = result;
14157
+ const hasInstrumentableMethod = typeof resultLike.getResponse === "function" || typeof resultLike.getText === "function" || OPENROUTER_CALL_MODEL_STREAM_METHODS.some(
14158
+ (methodName) => typeof resultLike[methodName] === "function"
14159
+ );
14160
+ if (!hasInstrumentableMethod) {
14161
+ return false;
14162
+ }
14163
+ Object.defineProperty(resultLike, OPENROUTER_WRAPPED_CALL_MODEL_RESULT, {
14164
+ value: true,
14165
+ enumerable: false,
14166
+ configurable: false
14167
+ });
14168
+ const originalGetResponse = typeof resultLike.getResponse === "function" ? resultLike.getResponse.bind(resultLike) : void 0;
14169
+ const originalGetInitialResponse = typeof resultLike.getInitialResponse === "function" ? resultLike.getInitialResponse.bind(resultLike) : void 0;
14170
+ const originalMakeFollowupRequest = typeof resultLike.makeFollowupRequest === "function" ? resultLike.makeFollowupRequest.bind(resultLike) : void 0;
14171
+ let ended = false;
14172
+ let tracedTurnCount = 0;
14173
+ const endSpanWithResult = async (response, fallbackOutput) => {
14174
+ if (ended) {
14175
+ return;
14176
+ }
14177
+ ended = true;
14178
+ const finalResponse = getFinalOpenRouterCallModelResponse(
14179
+ resultLike,
14180
+ response
14181
+ );
14182
+ if (finalResponse) {
14183
+ const rounds = getOpenRouterCallModelRounds(resultLike);
14184
+ const metadata = extractOpenRouterCallModelResultMetadata(
14185
+ finalResponse,
14186
+ rounds.length + 1
14187
+ );
14188
+ span.log({
14189
+ output: extractOpenRouterResponseOutput(finalResponse, fallbackOutput),
14190
+ ...metadata ? { metadata } : {},
14191
+ metrics: aggregateOpenRouterCallModelMetrics(rounds, finalResponse)
14192
+ });
14193
+ span.end();
14194
+ return;
14195
+ }
14196
+ if (fallbackOutput !== void 0) {
14197
+ span.log({
14198
+ output: fallbackOutput
14199
+ });
14200
+ }
14201
+ span.end();
14202
+ };
14203
+ const endSpanWithError = (error) => {
14204
+ if (ended) {
14205
+ return;
14206
+ }
14207
+ ended = true;
14208
+ span.log({
14209
+ error: normalizeError(error).message
14210
+ });
14211
+ span.end();
14212
+ };
14213
+ const finalizeFromResponse = async (fallbackOutput) => {
14214
+ if (!originalGetResponse) {
14215
+ await endSpanWithResult(void 0, fallbackOutput);
14216
+ return;
14217
+ }
14218
+ try {
14219
+ await endSpanWithResult(await originalGetResponse(), fallbackOutput);
14220
+ } catch {
14221
+ await endSpanWithResult(void 0, fallbackOutput);
14222
+ }
14223
+ };
14224
+ if (originalGetResponse) {
14225
+ resultLike.getResponse = async (...args2) => {
14226
+ return await withCurrent(span, async () => {
14227
+ try {
14228
+ const response = await originalGetResponse(...args2);
14229
+ await endSpanWithResult(response);
14230
+ return response;
14231
+ } catch (error) {
14232
+ endSpanWithError(error);
14233
+ throw error;
14234
+ }
14235
+ });
14236
+ };
14237
+ }
14238
+ if (typeof resultLike.getText === "function") {
14239
+ const originalGetText = resultLike.getText.bind(resultLike);
14240
+ resultLike.getText = async (...args2) => {
14241
+ return await withCurrent(span, async () => {
14242
+ try {
14243
+ const text = await originalGetText(...args2);
14244
+ await finalizeFromResponse(text);
14245
+ return text;
14246
+ } catch (error) {
14247
+ endSpanWithError(error);
14248
+ throw error;
14249
+ }
14250
+ });
14251
+ };
14252
+ }
14253
+ for (const methodName of OPENROUTER_CALL_MODEL_CONTEXT_METHODS) {
14254
+ if (typeof resultLike[methodName] !== "function") {
14255
+ continue;
14256
+ }
14257
+ const originalMethod = resultLike[methodName];
14258
+ resultLike[methodName] = async (...args2) => {
14259
+ return await withCurrent(span, async () => {
14260
+ return await originalMethod.apply(resultLike, args2);
14261
+ });
14262
+ };
14263
+ }
14264
+ for (const methodName of OPENROUTER_CALL_MODEL_STREAM_METHODS) {
14265
+ if (typeof resultLike[methodName] !== "function") {
13203
14266
  continue;
13204
14267
  }
13205
- for (const toolDelta of toolCallDeltas) {
13206
- if (!toolDelta?.function) {
13207
- continue;
13208
- }
13209
- const toolIndex = toolDelta.index ?? 0;
13210
- const existingToolCall = toolCalls?.[toolIndex];
13211
- if (!existingToolCall || toolDelta.id && existingToolCall.id !== void 0 && existingToolCall.id !== toolDelta.id) {
13212
- const nextToolCalls = [...toolCalls || []];
13213
- nextToolCalls[toolIndex] = {
13214
- index: toolIndex,
13215
- id: toolDelta.id,
13216
- type: toolDelta.type,
13217
- function: {
13218
- name: toolDelta.function.name,
13219
- arguments: toolDelta.function.arguments || ""
13220
- }
13221
- };
13222
- toolCalls = nextToolCalls;
13223
- continue;
13224
- }
13225
- const current = existingToolCall;
13226
- if (toolDelta.id && !current.id) {
13227
- current.id = toolDelta.id;
13228
- }
13229
- if (toolDelta.type && !current.type) {
13230
- current.type = toolDelta.type;
14268
+ const originalMethod = resultLike[methodName];
14269
+ resultLike[methodName] = (...args2) => {
14270
+ const stream = withCurrent(
14271
+ span,
14272
+ () => originalMethod.apply(resultLike, args2)
14273
+ );
14274
+ if (!isAsyncIterable2(stream)) {
14275
+ return stream;
13231
14276
  }
13232
- if (toolDelta.function.name && !current.function.name) {
13233
- current.function.name = toolDelta.function.name;
14277
+ return wrapAsyncIterableWithSpan({
14278
+ finalize: finalizeFromResponse,
14279
+ iteratorFactory: () => stream[Symbol.asyncIterator](),
14280
+ onError: endSpanWithError,
14281
+ span
14282
+ });
14283
+ };
14284
+ }
14285
+ if (originalGetInitialResponse) {
14286
+ let initialTurnTraced = false;
14287
+ resultLike.getInitialResponse = async (...args2) => {
14288
+ if (initialTurnTraced) {
14289
+ return await withCurrent(span, async () => {
14290
+ return await originalGetInitialResponse(...args2);
14291
+ });
13234
14292
  }
13235
- current.function.arguments += toolDelta.function.arguments || "";
13236
- }
14293
+ initialTurnTraced = true;
14294
+ const step = tracedTurnCount + 1;
14295
+ const stepType = tracedTurnCount === 0 ? "initial" : "continue";
14296
+ const response = await traceOpenRouterCallModelTurn({
14297
+ fn: async () => {
14298
+ const nextResponse = await originalGetInitialResponse(...args2);
14299
+ tracedTurnCount++;
14300
+ return nextResponse;
14301
+ },
14302
+ parentSpan: span,
14303
+ request: getOpenRouterResolvedRequest(resultLike, request),
14304
+ step,
14305
+ stepType
14306
+ });
14307
+ return response;
14308
+ };
13237
14309
  }
13238
- return {
13239
- output: [
13240
- {
13241
- index: 0,
13242
- message: {
13243
- role,
13244
- content: content || void 0,
13245
- ...toolCalls ? { tool_calls: toolCalls } : {}
14310
+ if (originalMakeFollowupRequest) {
14311
+ resultLike.makeFollowupRequest = async (...args2) => {
14312
+ const currentResponse = args2[0];
14313
+ const toolResults = Array.isArray(args2[1]) ? args2[1] : [];
14314
+ const step = tracedTurnCount + 1;
14315
+ const response = await traceOpenRouterCallModelTurn({
14316
+ fn: async () => {
14317
+ const nextResponse = await originalMakeFollowupRequest(...args2);
14318
+ tracedTurnCount++;
14319
+ return nextResponse;
13246
14320
  },
13247
- logprobs: null,
13248
- finish_reason: finishReason
13249
- }
13250
- ],
13251
- metrics
14321
+ parentSpan: span,
14322
+ request: buildOpenRouterFollowupRequest(
14323
+ getOpenRouterResolvedRequest(resultLike, request),
14324
+ currentResponse,
14325
+ toolResults
14326
+ ),
14327
+ step,
14328
+ stepType: "continue"
14329
+ });
14330
+ return response;
14331
+ };
14332
+ }
14333
+ return true;
14334
+ }
14335
+ async function traceOpenRouterCallModelTurn(args) {
14336
+ const context = {
14337
+ arguments: [args.request],
14338
+ step: args.step,
14339
+ stepType: args.stepType
13252
14340
  };
14341
+ return await withCurrent(
14342
+ args.parentSpan,
14343
+ () => openRouterChannels.callModelTurn.tracePromise(args.fn, context)
14344
+ );
13253
14345
  }
13254
- function aggregateOpenRouterResponseStreamEvents(chunks) {
13255
- let finalResponse;
13256
- for (const chunk of chunks) {
13257
- const response = chunk?.response;
13258
- if (!response) {
13259
- continue;
13260
- }
13261
- if (chunk.type === "response.completed" || chunk.type === "response.incomplete" || chunk.type === "response.failed") {
13262
- finalResponse = response;
14346
+ function isWrappedCallModelResult(value) {
14347
+ return Boolean(
14348
+ isObject(value) && value[OPENROUTER_WRAPPED_CALL_MODEL_RESULT]
14349
+ );
14350
+ }
14351
+ function extractOpenRouterCallModelResultMetadata(response, turnCount) {
14352
+ const combined = {
14353
+ ...extractOpenRouterResponseMetadata(response) || {},
14354
+ ...turnCount !== void 0 ? { turn_count: turnCount } : {}
14355
+ };
14356
+ return Object.keys(combined).length > 0 ? combined : void 0;
14357
+ }
14358
+ function getFinalOpenRouterCallModelResponse(result, response) {
14359
+ if (isObject(response)) {
14360
+ return response;
14361
+ }
14362
+ return isObject(result.finalResponse) ? result.finalResponse : void 0;
14363
+ }
14364
+ function getOpenRouterCallModelRounds(result) {
14365
+ if (!Array.isArray(result.allToolExecutionRounds)) {
14366
+ return [];
14367
+ }
14368
+ return result.allToolExecutionRounds.filter((round) => isObject(round)).map((round) => ({
14369
+ response: isObject(round.response) ? round.response : void 0,
14370
+ round: typeof round.round === "number" ? round.round : void 0,
14371
+ toolResults: Array.isArray(round.toolResults) ? round.toolResults : []
14372
+ })).filter((round) => round.response !== void 0);
14373
+ }
14374
+ function aggregateOpenRouterCallModelMetrics(rounds, finalResponse) {
14375
+ const metrics = {};
14376
+ const responses = [
14377
+ ...rounds.map((round) => round.response).filter(isObject),
14378
+ finalResponse
14379
+ ];
14380
+ for (const response of responses) {
14381
+ const responseMetrics = parseOpenRouterMetricsFromUsage(response.usage);
14382
+ for (const [name, value] of Object.entries(responseMetrics)) {
14383
+ metrics[name] = (metrics[name] || 0) + value;
13263
14384
  }
13264
14385
  }
13265
- if (!finalResponse) {
13266
- return {
13267
- output: void 0,
13268
- metrics: {}
13269
- };
14386
+ return metrics;
14387
+ }
14388
+ function buildNextOpenRouterCallModelInput(currentInput, response, toolResults) {
14389
+ const normalizedInput = Array.isArray(currentInput) ? [...currentInput] : currentInput === void 0 ? [] : [currentInput];
14390
+ const responseOutput = Array.isArray(response.output) ? response.output : response.output === void 0 ? [] : [response.output];
14391
+ return [...normalizedInput, ...responseOutput, ...toolResults].map(
14392
+ (entry) => sanitizeOpenRouterLoggedValue(entry)
14393
+ );
14394
+ }
14395
+ function getOpenRouterResolvedRequest(result, request) {
14396
+ if (isObject(result.resolvedRequest)) {
14397
+ return result.resolvedRequest;
14398
+ }
14399
+ return request;
14400
+ }
14401
+ function buildOpenRouterFollowupRequest(request, currentResponse, toolResults) {
14402
+ if (!request) {
14403
+ return void 0;
13270
14404
  }
13271
14405
  return {
13272
- output: extractOpenRouterResponseOutput(finalResponse),
13273
- metrics: parseOpenRouterMetricsFromUsage(finalResponse.usage),
13274
- ...extractOpenRouterResponseMetadata(finalResponse) ? { metadata: extractOpenRouterResponseMetadata(finalResponse) } : {}
14406
+ ...request,
14407
+ input: buildNextOpenRouterCallModelInput(
14408
+ extractOpenRouterCallModelInput(request),
14409
+ isObject(currentResponse) ? currentResponse : {},
14410
+ toolResults
14411
+ ),
14412
+ stream: false
14413
+ };
14414
+ }
14415
+ function wrapAsyncIterableWithSpan(args) {
14416
+ return {
14417
+ [Symbol.asyncIterator]() {
14418
+ const iterator = args.iteratorFactory();
14419
+ return {
14420
+ next(value) {
14421
+ return withCurrent(
14422
+ args.span,
14423
+ () => value === void 0 ? iterator.next() : iterator.next(value)
14424
+ ).then(
14425
+ async (result) => {
14426
+ if (result.done) {
14427
+ await args.finalize();
14428
+ }
14429
+ return result;
14430
+ },
14431
+ (error) => {
14432
+ args.onError(error);
14433
+ throw error;
14434
+ }
14435
+ );
14436
+ },
14437
+ return(value) {
14438
+ if (typeof iterator.return !== "function") {
14439
+ return args.finalize().then(() => ({
14440
+ done: true,
14441
+ value
14442
+ }));
14443
+ }
14444
+ return withCurrent(args.span, () => iterator.return(value)).then(
14445
+ async (result) => {
14446
+ await args.finalize();
14447
+ return result;
14448
+ },
14449
+ (error) => {
14450
+ args.onError(error);
14451
+ throw error;
14452
+ }
14453
+ );
14454
+ },
14455
+ throw(error) {
14456
+ args.onError(error);
14457
+ if (typeof iterator.throw !== "function") {
14458
+ return Promise.reject(error);
14459
+ }
14460
+ return withCurrent(args.span, () => iterator.throw(error));
14461
+ },
14462
+ [Symbol.asyncIterator]() {
14463
+ return this;
14464
+ }
14465
+ };
14466
+ }
13275
14467
  };
13276
14468
  }
14469
+ function isAsyncIterable2(value) {
14470
+ return !!value && (typeof value === "object" || typeof value === "function") && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
14471
+ }
14472
+ function normalizeError(error) {
14473
+ return error instanceof Error ? error : new Error(String(error));
14474
+ }
13277
14475
 
13278
14476
  // src/instrumentation/braintrust-plugin.ts
13279
14477
  var BraintrustPlugin = class extends BasePlugin {
@@ -14996,7 +16194,28 @@ function validateParametersWithJsonSchema(parameters, schema) {
14996
16194
  }).join(", ");
14997
16195
  throw Error(`Invalid parameters: ${errorMessages}`);
14998
16196
  }
14999
- return parameters;
16197
+ return rehydrateRemoteParameters(parameters, schema);
16198
+ }
16199
+ function rehydrateRemoteParameters(parameters, schema) {
16200
+ const schemaProperties = schema.properties;
16201
+ if (typeof schemaProperties !== "object" || schemaProperties === null) {
16202
+ return parameters;
16203
+ }
16204
+ return Object.fromEntries(
16205
+ Object.entries(parameters).map(([name, value]) => {
16206
+ const propertySchema = Reflect.get(schemaProperties, name);
16207
+ if (typeof propertySchema !== "object" || propertySchema === null) {
16208
+ return [name, value];
16209
+ }
16210
+ if (Reflect.get(propertySchema, "x-bt-type") === "prompt") {
16211
+ return [
16212
+ name,
16213
+ Prompt2.fromPromptData(name, PromptData.parse(value))
16214
+ ];
16215
+ }
16216
+ return [name, value];
16217
+ })
16218
+ );
15000
16219
  }
15001
16220
 
15002
16221
  // src/framework.ts
@@ -16231,7 +17450,8 @@ var ScorerBuilder = class {
16231
17450
  type: "llm_classifier",
16232
17451
  use_cot: opts.useCot,
16233
17452
  choice_scores: opts.choiceScores
16234
- }
17453
+ },
17454
+ ...opts.templateFormat ? { template_format: opts.templateFormat } : {}
16235
17455
  };
16236
17456
  const codePrompt = new CodePrompt(
16237
17457
  this.project,