braintrust 3.7.0 → 3.8.0

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 (53) hide show
  1. package/LICENSE +201 -0
  2. package/dev/dist/index.d.mts +144 -2
  3. package/dev/dist/index.d.ts +144 -2
  4. package/dev/dist/index.js +3322 -799
  5. package/dev/dist/index.mjs +3214 -691
  6. package/dist/auto-instrumentations/bundler/esbuild.cjs +377 -13
  7. package/dist/auto-instrumentations/bundler/esbuild.mjs +2 -2
  8. package/dist/auto-instrumentations/bundler/rollup.cjs +377 -13
  9. package/dist/auto-instrumentations/bundler/rollup.mjs +2 -2
  10. package/dist/auto-instrumentations/bundler/vite.cjs +377 -13
  11. package/dist/auto-instrumentations/bundler/vite.mjs +2 -2
  12. package/dist/auto-instrumentations/bundler/webpack-loader.cjs +377 -13
  13. package/dist/auto-instrumentations/bundler/webpack-loader.d.ts +11 -9
  14. package/dist/auto-instrumentations/bundler/webpack.cjs +377 -13
  15. package/dist/auto-instrumentations/bundler/webpack.mjs +2 -2
  16. package/dist/auto-instrumentations/{chunk-EVUKFMHG.mjs → chunk-ITP7RAUY.mjs} +21 -3
  17. package/dist/auto-instrumentations/{chunk-ZK2IYER2.mjs → chunk-MD7W27YH.mjs} +5 -1
  18. package/dist/auto-instrumentations/{chunk-AKEXR4AL.mjs → chunk-OLBMPZXE.mjs} +378 -14
  19. package/dist/auto-instrumentations/{chunk-VLEJ5AEK.mjs → chunk-P5YLNB2A.mjs} +21 -3
  20. package/dist/auto-instrumentations/hook.mjs +393 -19
  21. package/dist/auto-instrumentations/index.cjs +379 -13
  22. package/dist/auto-instrumentations/index.d.mts +5 -1
  23. package/dist/auto-instrumentations/index.d.ts +5 -1
  24. package/dist/auto-instrumentations/index.mjs +5 -1
  25. package/dist/auto-instrumentations/loader/cjs-patch.cjs +34 -6
  26. package/dist/auto-instrumentations/loader/cjs-patch.d.mts +1 -0
  27. package/dist/auto-instrumentations/loader/cjs-patch.d.ts +1 -0
  28. package/dist/auto-instrumentations/loader/cjs-patch.mjs +15 -5
  29. package/dist/auto-instrumentations/loader/esm-hook.mjs +8 -3
  30. package/dist/auto-instrumentations/loader/get-package-version.cjs +20 -2
  31. package/dist/auto-instrumentations/loader/get-package-version.mjs +1 -1
  32. package/dist/browser.d.mts +199 -44
  33. package/dist/browser.d.ts +199 -44
  34. package/dist/browser.js +7086 -6747
  35. package/dist/browser.mjs +7086 -6747
  36. package/dist/cli.js +3200 -677
  37. package/dist/edge-light.d.mts +1 -1
  38. package/dist/edge-light.d.ts +1 -1
  39. package/dist/edge-light.js +12645 -12215
  40. package/dist/edge-light.mjs +12645 -12215
  41. package/dist/index.d.mts +199 -44
  42. package/dist/index.d.ts +199 -44
  43. package/dist/index.js +7173 -6834
  44. package/dist/index.mjs +7022 -6683
  45. package/dist/instrumentation/index.d.mts +7 -0
  46. package/dist/instrumentation/index.d.ts +7 -0
  47. package/dist/instrumentation/index.js +3170 -669
  48. package/dist/instrumentation/index.mjs +3170 -669
  49. package/dist/workerd.d.mts +1 -1
  50. package/dist/workerd.d.ts +1 -1
  51. package/dist/workerd.js +12645 -12215
  52. package/dist/workerd.mjs +12645 -12215
  53. package/package.json +45 -45
@@ -70,7 +70,7 @@ var DefaultTracingChannel = class {
70
70
  }
71
71
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
72
72
  tracePromise(fn, _message, thisArg, ...args) {
73
- return Promise.resolve(fn.apply(thisArg, args));
73
+ return fn.apply(thisArg, args);
74
74
  }
75
75
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
76
76
  traceCallback(fn, _position, _message, thisArg, ...args) {
@@ -90,6 +90,7 @@ var iso = {
90
90
  processOn: (_0, _1) => {
91
91
  },
92
92
  basename: (filepath) => filepath.split(/[\\/]/).pop() || filepath,
93
+ // eslint-disable-next-line no-restricted-properties -- preserving intentional console usage.
93
94
  writeln: (text) => console.log(text)
94
95
  };
95
96
  var isomorph_default = iso;
@@ -125,7 +126,7 @@ function patchStreamIfNeeded(stream, options) {
125
126
  if (!completed) {
126
127
  completed = true;
127
128
  try {
128
- options.onComplete(chunks);
129
+ await options.onComplete(chunks);
129
130
  } catch (error) {
130
131
  console.error("Error in stream onComplete handler:", error);
131
132
  }
@@ -137,7 +138,7 @@ function patchStreamIfNeeded(stream, options) {
137
138
  chunks.push(chunk);
138
139
  if (options.onChunk) {
139
140
  try {
140
- options.onChunk(chunk);
141
+ await options.onChunk(chunk);
141
142
  } catch (error) {
142
143
  console.error("Error in stream onChunk handler:", error);
143
144
  }
@@ -150,7 +151,7 @@ function patchStreamIfNeeded(stream, options) {
150
151
  completed = true;
151
152
  if (options.onError) {
152
153
  try {
153
- options.onError(
154
+ await options.onError(
154
155
  error instanceof Error ? error : new Error(String(error)),
155
156
  chunks
156
157
  );
@@ -168,7 +169,7 @@ function patchStreamIfNeeded(stream, options) {
168
169
  if (!completed) {
169
170
  completed = true;
170
171
  try {
171
- options.onComplete(chunks);
172
+ await options.onComplete(chunks);
172
173
  } catch (error) {
173
174
  console.error("Error in stream onComplete handler:", error);
174
175
  }
@@ -185,7 +186,7 @@ function patchStreamIfNeeded(stream, options) {
185
186
  const error = rawError instanceof Error ? rawError : new Error(String(rawError));
186
187
  if (options.onError) {
187
188
  try {
188
- options.onError(error, chunks);
189
+ await options.onError(error, chunks);
189
190
  } catch (handlerError) {
190
191
  console.error("Error in stream onError handler:", handlerError);
191
192
  }
@@ -1537,6 +1538,15 @@ function addAzureBlobHeaders(headers, url) {
1537
1538
  headers["x-ms-blob-type"] = "BlockBlob";
1538
1539
  }
1539
1540
  }
1541
+ function filterFrom(record, keys) {
1542
+ const out = {};
1543
+ for (const k of Object.keys(record)) {
1544
+ if (!keys.includes(k)) {
1545
+ out[k] = record[k];
1546
+ }
1547
+ }
1548
+ return out;
1549
+ }
1540
1550
 
1541
1551
  // src/generated_types.ts
1542
1552
  import { z as z6 } from "zod/v3";
@@ -1748,6 +1758,16 @@ var NullableSavedFunctionId = z6.union([
1748
1758
  }),
1749
1759
  z6.null()
1750
1760
  ]);
1761
+ var TopicMapGenerationSettings = z6.object({
1762
+ algorithm: z6.enum(["hdbscan", "kmeans"]),
1763
+ dimension_reduction: z6.enum(["umap", "pca", "none"]),
1764
+ sample_size: z6.number().int().gt(0).optional(),
1765
+ n_clusters: z6.number().int().gt(0).optional(),
1766
+ min_cluster_size: z6.number().int().gt(0).optional(),
1767
+ min_samples: z6.number().int().gt(0).optional(),
1768
+ hierarchy_threshold: z6.number().int().gt(0).optional(),
1769
+ naming_model: z6.string().optional()
1770
+ });
1751
1771
  var TopicMapData = z6.object({
1752
1772
  type: z6.literal("topic_map"),
1753
1773
  source_facet: z6.string(),
@@ -1755,6 +1775,7 @@ var TopicMapData = z6.object({
1755
1775
  bundle_key: z6.string().optional(),
1756
1776
  report_key: z6.string().optional(),
1757
1777
  topic_names: z6.record(z6.string()).optional(),
1778
+ generation_settings: TopicMapGenerationSettings.optional(),
1758
1779
  distance_threshold: z6.number().optional()
1759
1780
  });
1760
1781
  var BatchedFacetData = z6.object({
@@ -1979,6 +2000,7 @@ var Dataset = z6.object({
1979
2000
  created: z6.union([z6.string(), z6.null()]).optional(),
1980
2001
  deleted_at: z6.union([z6.string(), z6.null()]).optional(),
1981
2002
  user_id: z6.union([z6.string(), z6.null()]).optional(),
2003
+ tags: z6.union([z6.array(z6.string()), z6.null()]).optional(),
1982
2004
  metadata: z6.union([z6.object({}).partial().passthrough(), z6.null()]).optional(),
1983
2005
  url_slug: z6.string()
1984
2006
  });
@@ -2061,6 +2083,14 @@ var DatasetEvent = z6.object({
2061
2083
  z6.null()
2062
2084
  ]).optional()
2063
2085
  });
2086
+ var DatasetSnapshot = z6.object({
2087
+ id: z6.string().uuid(),
2088
+ dataset_id: z6.string().uuid(),
2089
+ name: z6.string(),
2090
+ description: z6.union([z6.string(), z6.null()]),
2091
+ xact_id: z6.string(),
2092
+ created: z6.union([z6.string(), z6.null()])
2093
+ });
2064
2094
  var EnvVar = z6.object({
2065
2095
  id: z6.string().uuid(),
2066
2096
  object_type: z6.enum(["organization", "project", "function"]),
@@ -2819,6 +2849,8 @@ var TopicAutomationConfig = z6.object({
2819
2849
  scope: z6.union([SpanScope, TraceScope, GroupScope, z6.null()]).optional(),
2820
2850
  data_scope: TopicAutomationDataScope.optional(),
2821
2851
  btql_filter: z6.union([z6.string(), z6.null()]).optional(),
2852
+ rerun_seconds: z6.union([z6.number(), z6.null()]).optional(),
2853
+ relabel_overlap_seconds: z6.union([z6.number(), z6.null()]).optional(),
2822
2854
  backfill_time_range: z6.union([
2823
2855
  z6.string(),
2824
2856
  z6.object({ from: z6.string(), to: z6.string() }),
@@ -3155,7 +3187,8 @@ var User = z6.object({
3155
3187
  family_name: z6.union([z6.string(), z6.null()]).optional(),
3156
3188
  email: z6.union([z6.string(), z6.null()]).optional(),
3157
3189
  avatar_url: z6.union([z6.string(), z6.null()]).optional(),
3158
- created: z6.union([z6.string(), z6.null()]).optional()
3190
+ created: z6.union([z6.string(), z6.null()]).optional(),
3191
+ last_active_at: z6.union([z6.number(), z6.null()]).optional()
3159
3192
  });
3160
3193
  var ViewDataSearch = z6.union([
3161
3194
  z6.object({
@@ -4287,6 +4320,15 @@ var BRAINTRUST_CURRENT_SPAN_STORE = Symbol.for(
4287
4320
  "braintrust.currentSpanStore"
4288
4321
  );
4289
4322
  var ContextManager = class {
4323
+ /**
4324
+ * Returns the value to store in the ALS bound to a TracingChannel's start event.
4325
+ * In default mode this is the Span itself; in OTEL mode it is the OTEL Context
4326
+ * containing the span so that OTEL's own ALS stores a proper Context object.
4327
+ */
4328
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
4329
+ wrapSpanForStore(span) {
4330
+ return span;
4331
+ }
4290
4332
  };
4291
4333
  var BraintrustContextManager = class extends ContextManager {
4292
4334
  _currentSpan;
@@ -8080,18 +8122,22 @@ function ensureSpanStateForEvent(states, config, event, channelName) {
8080
8122
  function bindCurrentSpanStoreToStart(tracingChannel, states, config, channelName) {
8081
8123
  const state = _internalGetGlobalState();
8082
8124
  const startChannel = tracingChannel.start;
8083
- const currentSpanStore = state?.contextManager ? state.contextManager[BRAINTRUST_CURRENT_SPAN_STORE] : void 0;
8125
+ const contextManager = state?.contextManager;
8126
+ const currentSpanStore = contextManager ? contextManager[BRAINTRUST_CURRENT_SPAN_STORE] : void 0;
8084
8127
  if (!currentSpanStore || !startChannel) {
8085
8128
  return void 0;
8086
8129
  }
8087
8130
  startChannel.bindStore(
8088
8131
  currentSpanStore,
8089
- (event) => ensureSpanStateForEvent(
8090
- states,
8091
- config,
8092
- event,
8093
- channelName
8094
- ).span
8132
+ (event) => {
8133
+ const span = ensureSpanStateForEvent(
8134
+ states,
8135
+ config,
8136
+ event,
8137
+ channelName
8138
+ ).span;
8139
+ return contextManager.wrapSpanForStore(span);
8140
+ }
8095
8141
  );
8096
8142
  return () => {
8097
8143
  startChannel.unbindStore(currentSpanStore);
@@ -8108,6 +8154,26 @@ function logErrorAndEnd(states, event) {
8108
8154
  spanData.span.end();
8109
8155
  states.delete(event);
8110
8156
  }
8157
+ function runStreamingCompletionHook(args) {
8158
+ if (!args.config.onComplete) {
8159
+ return;
8160
+ }
8161
+ try {
8162
+ args.config.onComplete({
8163
+ channelName: args.channelName,
8164
+ ...args.chunks ? { chunks: args.chunks } : {},
8165
+ endEvent: args.endEvent,
8166
+ ...args.metadata !== void 0 ? { metadata: args.metadata } : {},
8167
+ metrics: args.metrics,
8168
+ output: args.output,
8169
+ result: args.result,
8170
+ span: args.span,
8171
+ startTime: args.startTime
8172
+ });
8173
+ } catch (error) {
8174
+ console.error(`Error in onComplete hook for ${args.channelName}:`, error);
8175
+ }
8176
+ }
8111
8177
  function traceAsyncChannel(channel2, config) {
8112
8178
  const tracingChannel = channel2.tracingChannel();
8113
8179
  const states = /* @__PURE__ */ new WeakMap();
@@ -8235,6 +8301,18 @@ function traceStreamingChannel(channel2, config) {
8235
8301
  } else if (metrics.time_to_first_token === void 0 && chunks.length > 0) {
8236
8302
  metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8237
8303
  }
8304
+ runStreamingCompletionHook({
8305
+ channelName,
8306
+ chunks,
8307
+ config,
8308
+ endEvent: asyncEndEvent,
8309
+ ...metadata !== void 0 ? { metadata } : {},
8310
+ metrics,
8311
+ output,
8312
+ result: asyncEndEvent.result,
8313
+ span,
8314
+ startTime
8315
+ });
8238
8316
  span.log({
8239
8317
  output,
8240
8318
  ...metadata !== void 0 ? { metadata } : {},
@@ -8284,6 +8362,17 @@ function traceStreamingChannel(channel2, config) {
8284
8362
  asyncEndEvent.result,
8285
8363
  asyncEndEvent
8286
8364
  );
8365
+ runStreamingCompletionHook({
8366
+ channelName,
8367
+ config,
8368
+ endEvent: asyncEndEvent,
8369
+ ...normalizeMetadata(metadata) !== void 0 ? { metadata: normalizeMetadata(metadata) } : {},
8370
+ metrics,
8371
+ output,
8372
+ result: asyncEndEvent.result,
8373
+ span,
8374
+ startTime
8375
+ });
8287
8376
  span.log({
8288
8377
  output,
8289
8378
  ...normalizeMetadata(metadata) !== void 0 ? { metadata: normalizeMetadata(metadata) } : {},
@@ -8332,51 +8421,28 @@ function traceSyncStreamChannel(channel2, config) {
8332
8421
  }
8333
8422
  const { span, startTime } = spanData;
8334
8423
  const endEvent = event;
8335
- if (config.patchResult?.({
8336
- channelName,
8337
- endEvent,
8338
- result: endEvent.result,
8339
- span,
8340
- startTime
8341
- })) {
8342
- return;
8343
- }
8344
- const stream = endEvent.result;
8345
- if (!isSyncStreamLike(stream)) {
8346
- span.end();
8347
- states.delete(event);
8348
- return;
8349
- }
8350
- let first = true;
8351
- stream.on("chunk", () => {
8352
- if (first) {
8353
- span.log({
8354
- metrics: {
8355
- time_to_first_token: getCurrentUnixTimestamp() - startTime
8356
- }
8357
- });
8358
- first = false;
8359
- }
8360
- });
8361
- stream.on("chatCompletion", (completion) => {
8362
- try {
8363
- if (hasChoices(completion)) {
8364
- span.log({
8365
- output: completion.choices
8366
- });
8367
- }
8368
- } catch (error) {
8369
- console.error(
8370
- `Error extracting chatCompletion for ${channelName}:`,
8371
- error
8372
- );
8424
+ const handleResolvedResult = (result) => {
8425
+ const resolvedEndEvent = {
8426
+ ...endEvent,
8427
+ result
8428
+ };
8429
+ if (config.patchResult?.({
8430
+ channelName,
8431
+ endEvent: resolvedEndEvent,
8432
+ result,
8433
+ span,
8434
+ startTime
8435
+ })) {
8436
+ return;
8373
8437
  }
8374
- });
8375
- stream.on("event", (streamEvent) => {
8376
- if (!config.extractFromEvent) {
8438
+ const stream = result;
8439
+ if (!isSyncStreamLike(stream)) {
8440
+ span.end();
8441
+ states.delete(event);
8377
8442
  return;
8378
8443
  }
8379
- try {
8444
+ let first = true;
8445
+ stream.on("chunk", () => {
8380
8446
  if (first) {
8381
8447
  span.log({
8382
8448
  metrics: {
@@ -8385,25 +8451,55 @@ function traceSyncStreamChannel(channel2, config) {
8385
8451
  });
8386
8452
  first = false;
8387
8453
  }
8388
- const extracted = config.extractFromEvent(streamEvent);
8389
- if (extracted && Object.keys(extracted).length > 0) {
8390
- span.log(extracted);
8454
+ });
8455
+ stream.on("chatCompletion", (completion) => {
8456
+ try {
8457
+ if (hasChoices(completion)) {
8458
+ span.log({
8459
+ output: completion.choices
8460
+ });
8461
+ }
8462
+ } catch (error) {
8463
+ console.error(
8464
+ `Error extracting chatCompletion for ${channelName}:`,
8465
+ error
8466
+ );
8391
8467
  }
8392
- } catch (error) {
8393
- console.error(`Error extracting event for ${channelName}:`, error);
8394
- }
8395
- });
8396
- stream.on("end", () => {
8397
- span.end();
8398
- states.delete(event);
8399
- });
8400
- stream.on("error", (error) => {
8401
- span.log({
8402
- error: error.message
8403
8468
  });
8404
- span.end();
8405
- states.delete(event);
8406
- });
8469
+ stream.on("event", (streamEvent) => {
8470
+ if (!config.extractFromEvent) {
8471
+ return;
8472
+ }
8473
+ try {
8474
+ if (first) {
8475
+ span.log({
8476
+ metrics: {
8477
+ time_to_first_token: getCurrentUnixTimestamp() - startTime
8478
+ }
8479
+ });
8480
+ first = false;
8481
+ }
8482
+ const extracted = config.extractFromEvent(streamEvent);
8483
+ if (extracted && Object.keys(extracted).length > 0) {
8484
+ span.log(extracted);
8485
+ }
8486
+ } catch (error) {
8487
+ console.error(`Error extracting event for ${channelName}:`, error);
8488
+ }
8489
+ });
8490
+ stream.on("end", () => {
8491
+ span.end();
8492
+ states.delete(event);
8493
+ });
8494
+ stream.on("error", (error) => {
8495
+ span.log({
8496
+ error: error.message
8497
+ });
8498
+ span.end();
8499
+ states.delete(event);
8500
+ });
8501
+ };
8502
+ handleResolvedResult(endEvent.result);
8407
8503
  },
8408
8504
  error: (event) => {
8409
8505
  logErrorAndEnd(states, event);
@@ -8660,6 +8756,10 @@ var openAIChannels = defineChannels("openai", {
8660
8756
  responsesParse: channel({
8661
8757
  channelName: "responses.parse",
8662
8758
  kind: "async"
8759
+ }),
8760
+ responsesCompact: channel({
8761
+ channelName: "responses.compact",
8762
+ kind: "async"
8663
8763
  })
8664
8764
  });
8665
8765
 
@@ -8939,6 +9039,40 @@ var OpenAIPlugin = class extends BasePlugin {
8939
9039
  aggregateChunks: aggregateResponseStreamEvents
8940
9040
  })
8941
9041
  );
9042
+ this.unsubscribers.push(
9043
+ traceAsyncChannel(openAIChannels.responsesCompact, {
9044
+ name: "openai.responses.compact",
9045
+ type: "llm" /* LLM */,
9046
+ extractInput: ([params]) => {
9047
+ const { input, ...metadata } = params;
9048
+ return {
9049
+ input: processInputAttachments(input),
9050
+ metadata: { ...metadata, provider: "openai" }
9051
+ };
9052
+ },
9053
+ extractOutput: (result) => {
9054
+ return processImagesInOutput(result?.output);
9055
+ },
9056
+ extractMetadata: (result) => {
9057
+ if (!result) {
9058
+ return void 0;
9059
+ }
9060
+ const { output: _output, usage: _usage, ...metadata } = result;
9061
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
9062
+ },
9063
+ extractMetrics: (result, startTime, endEvent) => {
9064
+ const metrics = withCachedMetric(
9065
+ parseMetricsFromUsage(result?.usage),
9066
+ result,
9067
+ endEvent
9068
+ );
9069
+ if (startTime) {
9070
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9071
+ }
9072
+ return metrics;
9073
+ }
9074
+ })
9075
+ );
8942
9076
  }
8943
9077
  onDisable() {
8944
9078
  this.unsubscribers = unsubscribeAll(this.unsubscribers);
@@ -9226,28 +9360,40 @@ function aggregateAnthropicStreamChunks(chunks) {
9226
9360
  case "content_block_start":
9227
9361
  if (event.content_block) {
9228
9362
  contentBlocks[event.index] = event.content_block;
9229
- contentBlockDeltas[event.index] = [];
9363
+ contentBlockDeltas[event.index] = { textDeltas: [], citations: [] };
9230
9364
  }
9231
9365
  break;
9232
- case "content_block_delta":
9233
- if (event.delta?.type === "text_delta") {
9234
- const text = event.delta.text;
9366
+ case "content_block_delta": {
9367
+ const acc = contentBlockDeltas[event.index];
9368
+ const delta = event.delta;
9369
+ if (!delta) break;
9370
+ if (delta.type === "text_delta" && "text" in delta) {
9371
+ const text = delta.text;
9235
9372
  if (text) {
9236
- if (contentBlocks[event.index] !== void 0 || contentBlockDeltas[event.index] !== void 0) {
9237
- contentBlockDeltas[event.index] ??= [];
9238
- contentBlockDeltas[event.index].push(text);
9373
+ if (acc !== void 0) {
9374
+ acc.textDeltas.push(text);
9239
9375
  } else {
9240
9376
  fallbackTextDeltas.push(text);
9241
9377
  }
9242
9378
  }
9243
- } else if (event.delta?.type === "input_json_delta") {
9244
- const partialJson = event.delta.partial_json;
9245
- if (partialJson) {
9246
- contentBlockDeltas[event.index] ??= [];
9247
- contentBlockDeltas[event.index].push(partialJson);
9379
+ } else if (delta.type === "input_json_delta" && "partial_json" in delta) {
9380
+ const partialJson = delta.partial_json;
9381
+ if (partialJson && acc !== void 0) {
9382
+ acc.textDeltas.push(partialJson);
9383
+ }
9384
+ } else if (delta.type === "thinking_delta" && "thinking" in delta) {
9385
+ const thinking = delta.thinking;
9386
+ if (thinking && acc !== void 0) {
9387
+ acc.textDeltas.push(thinking);
9388
+ }
9389
+ } else if (delta.type === "citations_delta" && "citation" in delta) {
9390
+ const citation = delta.citation;
9391
+ if (citation && acc !== void 0) {
9392
+ acc.citations.push(citation);
9248
9393
  }
9249
9394
  }
9250
9395
  break;
9396
+ }
9251
9397
  case "content_block_stop":
9252
9398
  finalizeContentBlock(
9253
9399
  event.index,
@@ -9273,7 +9419,7 @@ function aggregateAnthropicStreamChunks(chunks) {
9273
9419
  })).filter(({ block }) => block !== void 0).sort((left, right) => left.index - right.index).map(({ block }) => block);
9274
9420
  let output = fallbackTextDeltas.join("");
9275
9421
  if (orderedContent.length > 0) {
9276
- if (orderedContent.every(isTextContentBlock)) {
9422
+ if (orderedContent.every(isTextContentBlock) && orderedContent.every((block) => !block.citations?.length)) {
9277
9423
  output = orderedContent.map((block) => block.text).join("");
9278
9424
  } else {
9279
9425
  output = {
@@ -9299,7 +9445,8 @@ function finalizeContentBlock(index, contentBlocks, contentBlockDeltas, fallback
9299
9445
  if (!contentBlock) {
9300
9446
  return;
9301
9447
  }
9302
- const text = contentBlockDeltas[index]?.join("") ?? "";
9448
+ const acc = contentBlockDeltas[index];
9449
+ const text = acc?.textDeltas.join("") ?? "";
9303
9450
  if (isToolUseContentBlock(contentBlock)) {
9304
9451
  if (!text) {
9305
9452
  return;
@@ -9316,20 +9463,28 @@ function finalizeContentBlock(index, contentBlocks, contentBlockDeltas, fallback
9316
9463
  return;
9317
9464
  }
9318
9465
  if (isTextContentBlock(contentBlock)) {
9466
+ if (!text) {
9467
+ delete contentBlocks[index];
9468
+ return;
9469
+ }
9470
+ const updated = { ...contentBlock, text };
9471
+ if (acc?.citations.length) {
9472
+ updated.citations = acc.citations;
9473
+ }
9474
+ contentBlocks[index] = updated;
9475
+ return;
9476
+ }
9477
+ if (isThinkingContentBlock(contentBlock)) {
9319
9478
  if (!text) {
9320
9479
  delete contentBlocks[index];
9321
9480
  return;
9322
9481
  }
9323
9482
  contentBlocks[index] = {
9324
9483
  ...contentBlock,
9325
- text
9484
+ thinking: text
9326
9485
  };
9327
9486
  return;
9328
9487
  }
9329
- if (text) {
9330
- fallbackTextDeltas.push(text);
9331
- }
9332
- delete contentBlocks[index];
9333
9488
  }
9334
9489
  function isTextContentBlock(contentBlock) {
9335
9490
  return contentBlock.type === "text";
@@ -9337,6 +9492,9 @@ function isTextContentBlock(contentBlock) {
9337
9492
  function isToolUseContentBlock(contentBlock) {
9338
9493
  return contentBlock.type === "tool_use";
9339
9494
  }
9495
+ function isThinkingContentBlock(contentBlock) {
9496
+ return contentBlock.type === "thinking";
9497
+ }
9340
9498
  function isAnthropicBase64ContentBlock(input) {
9341
9499
  return (input.type === "image" || input.type === "document") && isObject(input.source) && input.source.type === "base64";
9342
9500
  }
@@ -9391,15 +9549,6 @@ function coalesceInput(messages, system) {
9391
9549
  }
9392
9550
  return input;
9393
9551
  }
9394
- function filterFrom(obj, fieldsToRemove) {
9395
- const result = {};
9396
- for (const [key, value] of Object.entries(obj)) {
9397
- if (!fieldsToRemove.includes(key)) {
9398
- result[key] = value;
9399
- }
9400
- }
9401
- return result;
9402
- }
9403
9552
 
9404
9553
  // src/wrappers/ai-sdk/normalize-logged-output.ts
9405
9554
  var REMOVE_NORMALIZED_VALUE = Symbol("braintrust.ai-sdk.remove-normalized");
@@ -9529,6 +9678,16 @@ var aiSDKChannels = defineChannels("ai", {
9529
9678
  channelName: "streamObject.sync",
9530
9679
  kind: "sync-stream"
9531
9680
  }),
9681
+ embed: channel(
9682
+ {
9683
+ channelName: "embed",
9684
+ kind: "async"
9685
+ }
9686
+ ),
9687
+ embedMany: channel({
9688
+ channelName: "embedMany",
9689
+ kind: "async"
9690
+ }),
9532
9691
  agentGenerate: channel({
9533
9692
  channelName: "Agent.generate",
9534
9693
  kind: "async"
@@ -9537,6 +9696,10 @@ var aiSDKChannels = defineChannels("ai", {
9537
9696
  channelName: "Agent.stream",
9538
9697
  kind: "async"
9539
9698
  }),
9699
+ agentStreamSync: channel({
9700
+ channelName: "Agent.stream.sync",
9701
+ kind: "sync-stream"
9702
+ }),
9540
9703
  toolLoopAgentGenerate: channel({
9541
9704
  channelName: "ToolLoopAgent.generate",
9542
9705
  kind: "async"
@@ -9564,6 +9727,9 @@ var DEFAULT_DENY_OUTPUT_PATHS = [
9564
9727
  ];
9565
9728
  var AUTO_PATCHED_MODEL = Symbol.for("braintrust.ai-sdk.auto-patched-model");
9566
9729
  var AUTO_PATCHED_TOOL = Symbol.for("braintrust.ai-sdk.auto-patched-tool");
9730
+ var RUNTIME_DENY_OUTPUT_PATHS = Symbol.for(
9731
+ "braintrust.ai-sdk.deny-output-paths"
9732
+ );
9567
9733
  var AISDKPlugin = class extends BasePlugin {
9568
9734
  config;
9569
9735
  constructor(config = {}) {
@@ -9582,10 +9748,13 @@ var AISDKPlugin = class extends BasePlugin {
9582
9748
  traceStreamingChannel(aiSDKChannels.generateText, {
9583
9749
  name: "generateText",
9584
9750
  type: "llm" /* LLM */,
9585
- extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9751
+ extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths),
9586
9752
  extractOutput: (result, endEvent) => {
9587
9753
  finalizeAISDKChildTracing(endEvent);
9588
- return processAISDKOutput(result, denyOutputPaths);
9754
+ return processAISDKOutput(
9755
+ result,
9756
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9757
+ );
9589
9758
  },
9590
9759
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
9591
9760
  aggregateChunks: aggregateAISDKChunks
@@ -9595,12 +9764,15 @@ var AISDKPlugin = class extends BasePlugin {
9595
9764
  traceStreamingChannel(aiSDKChannels.streamText, {
9596
9765
  name: "streamText",
9597
9766
  type: "llm" /* LLM */,
9598
- extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9599
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
9767
+ extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths),
9768
+ extractOutput: (result, endEvent) => processAISDKOutput(
9769
+ result,
9770
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9771
+ ),
9600
9772
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
9601
9773
  aggregateChunks: aggregateAISDKChunks,
9602
9774
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9603
- denyOutputPaths,
9775
+ defaultDenyOutputPaths: denyOutputPaths,
9604
9776
  endEvent,
9605
9777
  result,
9606
9778
  span,
@@ -9612,9 +9784,9 @@ var AISDKPlugin = class extends BasePlugin {
9612
9784
  traceSyncStreamChannel(aiSDKChannels.streamTextSync, {
9613
9785
  name: "streamText",
9614
9786
  type: "llm" /* LLM */,
9615
- extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9787
+ extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths),
9616
9788
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9617
- denyOutputPaths,
9789
+ defaultDenyOutputPaths: denyOutputPaths,
9618
9790
  endEvent,
9619
9791
  result,
9620
9792
  span,
@@ -9626,10 +9798,13 @@ var AISDKPlugin = class extends BasePlugin {
9626
9798
  traceStreamingChannel(aiSDKChannels.generateObject, {
9627
9799
  name: "generateObject",
9628
9800
  type: "llm" /* LLM */,
9629
- extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9801
+ extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths),
9630
9802
  extractOutput: (result, endEvent) => {
9631
9803
  finalizeAISDKChildTracing(endEvent);
9632
- return processAISDKOutput(result, denyOutputPaths);
9804
+ return processAISDKOutput(
9805
+ result,
9806
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9807
+ );
9633
9808
  },
9634
9809
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
9635
9810
  aggregateChunks: aggregateAISDKChunks
@@ -9639,12 +9814,15 @@ var AISDKPlugin = class extends BasePlugin {
9639
9814
  traceStreamingChannel(aiSDKChannels.streamObject, {
9640
9815
  name: "streamObject",
9641
9816
  type: "llm" /* LLM */,
9642
- extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9643
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
9817
+ extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths),
9818
+ extractOutput: (result, endEvent) => processAISDKOutput(
9819
+ result,
9820
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9821
+ ),
9644
9822
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
9645
9823
  aggregateChunks: aggregateAISDKChunks,
9646
9824
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9647
- denyOutputPaths,
9825
+ defaultDenyOutputPaths: denyOutputPaths,
9648
9826
  endEvent,
9649
9827
  result,
9650
9828
  span,
@@ -9656,9 +9834,9 @@ var AISDKPlugin = class extends BasePlugin {
9656
9834
  traceSyncStreamChannel(aiSDKChannels.streamObjectSync, {
9657
9835
  name: "streamObject",
9658
9836
  type: "llm" /* LLM */,
9659
- extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9837
+ extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths),
9660
9838
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9661
- denyOutputPaths,
9839
+ defaultDenyOutputPaths: denyOutputPaths,
9662
9840
  endEvent,
9663
9841
  result,
9664
9842
  span,
@@ -9666,14 +9844,41 @@ var AISDKPlugin = class extends BasePlugin {
9666
9844
  })
9667
9845
  })
9668
9846
  );
9847
+ this.unsubscribers.push(
9848
+ traceAsyncChannel(aiSDKChannels.embed, {
9849
+ name: "embed",
9850
+ type: "llm" /* LLM */,
9851
+ extractInput: ([params], event) => prepareAISDKEmbedInput(params, event.self),
9852
+ extractOutput: (result, endEvent) => processAISDKEmbeddingOutput(
9853
+ result,
9854
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9855
+ ),
9856
+ extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent)
9857
+ })
9858
+ );
9859
+ this.unsubscribers.push(
9860
+ traceAsyncChannel(aiSDKChannels.embedMany, {
9861
+ name: "embedMany",
9862
+ type: "llm" /* LLM */,
9863
+ extractInput: ([params], event) => prepareAISDKEmbedInput(params, event.self),
9864
+ extractOutput: (result, endEvent) => processAISDKEmbeddingOutput(
9865
+ result,
9866
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9867
+ ),
9868
+ extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent)
9869
+ })
9870
+ );
9669
9871
  this.unsubscribers.push(
9670
9872
  traceStreamingChannel(aiSDKChannels.agentGenerate, {
9671
9873
  name: "Agent.generate",
9672
9874
  type: "llm" /* LLM */,
9673
- extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9875
+ extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths),
9674
9876
  extractOutput: (result, endEvent) => {
9675
9877
  finalizeAISDKChildTracing(endEvent);
9676
- return processAISDKOutput(result, denyOutputPaths);
9878
+ return processAISDKOutput(
9879
+ result,
9880
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9881
+ );
9677
9882
  },
9678
9883
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
9679
9884
  aggregateChunks: aggregateAISDKChunks
@@ -9683,12 +9888,29 @@ var AISDKPlugin = class extends BasePlugin {
9683
9888
  traceStreamingChannel(aiSDKChannels.agentStream, {
9684
9889
  name: "Agent.stream",
9685
9890
  type: "llm" /* LLM */,
9686
- extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9687
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
9891
+ extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths),
9892
+ extractOutput: (result, endEvent) => processAISDKOutput(
9893
+ result,
9894
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9895
+ ),
9688
9896
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
9689
9897
  aggregateChunks: aggregateAISDKChunks,
9690
9898
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9691
- denyOutputPaths,
9899
+ defaultDenyOutputPaths: denyOutputPaths,
9900
+ endEvent,
9901
+ result,
9902
+ span,
9903
+ startTime
9904
+ })
9905
+ })
9906
+ );
9907
+ this.unsubscribers.push(
9908
+ traceSyncStreamChannel(aiSDKChannels.agentStreamSync, {
9909
+ name: "Agent.stream",
9910
+ type: "llm" /* LLM */,
9911
+ extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths),
9912
+ patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9913
+ defaultDenyOutputPaths: denyOutputPaths,
9692
9914
  endEvent,
9693
9915
  result,
9694
9916
  span,
@@ -9700,10 +9922,13 @@ var AISDKPlugin = class extends BasePlugin {
9700
9922
  traceStreamingChannel(aiSDKChannels.toolLoopAgentGenerate, {
9701
9923
  name: "ToolLoopAgent.generate",
9702
9924
  type: "llm" /* LLM */,
9703
- extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9925
+ extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths),
9704
9926
  extractOutput: (result, endEvent) => {
9705
9927
  finalizeAISDKChildTracing(endEvent);
9706
- return processAISDKOutput(result, denyOutputPaths);
9928
+ return processAISDKOutput(
9929
+ result,
9930
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9931
+ );
9707
9932
  },
9708
9933
  extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
9709
9934
  aggregateChunks: aggregateAISDKChunks
@@ -9713,12 +9938,15 @@ var AISDKPlugin = class extends BasePlugin {
9713
9938
  traceStreamingChannel(aiSDKChannels.toolLoopAgentStream, {
9714
9939
  name: "ToolLoopAgent.stream",
9715
9940
  type: "llm" /* LLM */,
9716
- extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9717
- extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
9941
+ extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths),
9942
+ extractOutput: (result, endEvent) => processAISDKOutput(
9943
+ result,
9944
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
9945
+ ),
9718
9946
  extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
9719
9947
  aggregateChunks: aggregateAISDKChunks,
9720
9948
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9721
- denyOutputPaths,
9949
+ defaultDenyOutputPaths: denyOutputPaths,
9722
9950
  endEvent,
9723
9951
  result,
9724
9952
  span,
@@ -9728,75 +9956,391 @@ var AISDKPlugin = class extends BasePlugin {
9728
9956
  );
9729
9957
  }
9730
9958
  };
9731
- function processAISDKInput(params) {
9732
- if (!params) return params;
9733
- const input = processInputAttachments(params);
9734
- if (!input || typeof input !== "object" || Array.isArray(input)) {
9735
- return input;
9959
+ function resolveDenyOutputPaths(event, defaultDenyOutputPaths) {
9960
+ if (Array.isArray(event?.denyOutputPaths)) {
9961
+ return event.denyOutputPaths;
9736
9962
  }
9737
- const { tools: _tools, ...rest } = input;
9738
- return rest;
9739
- }
9740
- function prepareAISDKInput(params, event, span, denyOutputPaths) {
9741
- const input = processAISDKInput(params);
9742
- const metadata = extractMetadataFromParams(params, event.self);
9743
- const childTracing = prepareAISDKChildTracing(
9744
- params,
9745
- event.self,
9746
- span,
9747
- denyOutputPaths
9748
- );
9749
- event.__braintrust_ai_sdk_model_wrapped = childTracing.modelWrapped;
9750
- if (childTracing.cleanup) {
9751
- event.__braintrust_ai_sdk_cleanup = childTracing.cleanup;
9963
+ const firstArgument = event?.arguments && event.arguments.length > 0 ? event.arguments[0] : void 0;
9964
+ if (!firstArgument || typeof firstArgument !== "object") {
9965
+ return defaultDenyOutputPaths;
9752
9966
  }
9753
- return {
9754
- input,
9755
- metadata
9756
- };
9757
- }
9758
- function extractTopLevelAISDKMetrics(result, event, startTime) {
9759
- const metrics = hasModelChildTracing(event) ? {} : extractTokenMetrics(result);
9760
- if (startTime) {
9761
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9967
+ const runtimeDenyOutputPaths = firstArgument[RUNTIME_DENY_OUTPUT_PATHS];
9968
+ if (Array.isArray(runtimeDenyOutputPaths) && runtimeDenyOutputPaths.every((path) => typeof path === "string")) {
9969
+ return runtimeDenyOutputPaths;
9762
9970
  }
9763
- return metrics;
9764
- }
9765
- function hasModelChildTracing(event) {
9766
- return event?.__braintrust_ai_sdk_model_wrapped === true;
9971
+ return defaultDenyOutputPaths;
9767
9972
  }
9768
- function extractMetadataFromParams(params, self) {
9769
- const metadata = {
9770
- braintrust: {
9771
- integration_name: "ai-sdk",
9772
- sdk_language: "typescript"
9773
- }
9774
- };
9775
- const agentModel = self && typeof self === "object" && "model" in self && self.model ? self.model : self && typeof self === "object" && "settings" in self && self.settings?.model ? self.settings?.model : void 0;
9776
- const { model, provider } = serializeModelWithProvider(
9777
- params.model ?? agentModel
9778
- );
9779
- if (model) {
9780
- metadata.model = model;
9973
+ var isZodSchema2 = (value) => {
9974
+ return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
9975
+ };
9976
+ var serializeZodSchema2 = (schema) => {
9977
+ try {
9978
+ return zodToJsonSchema(schema);
9979
+ } catch {
9980
+ return {
9981
+ type: "object",
9982
+ description: "Zod schema (conversion failed)"
9983
+ };
9781
9984
  }
9782
- if (provider) {
9783
- metadata.provider = provider;
9985
+ };
9986
+ var isOutputObject = (value) => {
9987
+ if (value == null || typeof value !== "object") {
9988
+ return false;
9784
9989
  }
9785
- const tools = serializeAISDKToolsForLogging(params.tools);
9786
- if (tools) {
9787
- metadata.tools = tools;
9990
+ const output = value;
9991
+ if (!("responseFormat" in output)) {
9992
+ return false;
9788
9993
  }
9789
- return metadata;
9790
- }
9791
- function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9792
- const cleanup = [];
9793
- const patchedModels = /* @__PURE__ */ new WeakSet();
9994
+ if (output.type === "object" || output.type === "text") {
9995
+ return true;
9996
+ }
9997
+ if (typeof output.responseFormat === "function" || typeof output.responseFormat === "object") {
9998
+ return true;
9999
+ }
10000
+ return false;
10001
+ };
10002
+ var serializeOutputObject = (output, model) => {
10003
+ try {
10004
+ const result = {
10005
+ response_format: null
10006
+ };
10007
+ if (output.type) {
10008
+ result.type = output.type;
10009
+ }
10010
+ let responseFormat;
10011
+ if (typeof output.responseFormat === "function") {
10012
+ const mockModelForSchema = {
10013
+ supportsStructuredOutputs: true,
10014
+ ...model && typeof model === "object" ? model : {}
10015
+ };
10016
+ responseFormat = output.responseFormat({ model: mockModelForSchema });
10017
+ } else if (output.responseFormat != null && typeof output.responseFormat === "object") {
10018
+ responseFormat = output.responseFormat;
10019
+ }
10020
+ if (responseFormat) {
10021
+ if (typeof responseFormat.then === "function") {
10022
+ result.response_format = Promise.resolve(responseFormat).then(
10023
+ (resolved) => {
10024
+ if (resolved.schema && isZodSchema2(resolved.schema)) {
10025
+ return {
10026
+ ...resolved,
10027
+ schema: serializeZodSchema2(resolved.schema)
10028
+ };
10029
+ }
10030
+ return resolved;
10031
+ }
10032
+ );
10033
+ } else {
10034
+ const syncResponseFormat = responseFormat;
10035
+ if (syncResponseFormat.schema && isZodSchema2(syncResponseFormat.schema)) {
10036
+ responseFormat = {
10037
+ ...syncResponseFormat,
10038
+ schema: serializeZodSchema2(syncResponseFormat.schema)
10039
+ };
10040
+ }
10041
+ result.response_format = responseFormat;
10042
+ }
10043
+ }
10044
+ return result;
10045
+ } catch {
10046
+ return {
10047
+ response_format: null
10048
+ };
10049
+ }
10050
+ };
10051
+ var processInputAttachmentsSync = (input) => {
10052
+ if (!input) return { input };
10053
+ const processed = { ...input };
10054
+ if (input.messages && Array.isArray(input.messages)) {
10055
+ processed.messages = input.messages.map(processMessage);
10056
+ }
10057
+ if (input.prompt && typeof input.prompt === "object") {
10058
+ if (Array.isArray(input.prompt)) {
10059
+ processed.prompt = input.prompt.map(processMessage);
10060
+ } else {
10061
+ processed.prompt = processPromptContent(input.prompt);
10062
+ }
10063
+ }
10064
+ if (input.schema && isZodSchema2(input.schema)) {
10065
+ processed.schema = serializeZodSchema2(input.schema);
10066
+ }
10067
+ if (input.callOptionsSchema && isZodSchema2(input.callOptionsSchema)) {
10068
+ processed.callOptionsSchema = serializeZodSchema2(input.callOptionsSchema);
10069
+ }
10070
+ if (input.tools) {
10071
+ processed.tools = serializeAISDKToolsForLogging(input.tools);
10072
+ }
10073
+ let outputPromise;
10074
+ if (input.output && isOutputObject(input.output)) {
10075
+ const serialized = serializeOutputObject(input.output, input.model);
10076
+ if (serialized.response_format && typeof serialized.response_format.then === "function") {
10077
+ processed.output = { ...serialized, response_format: {} };
10078
+ outputPromise = serialized.response_format.then(
10079
+ (resolvedFormat) => ({
10080
+ output: { ...serialized, response_format: resolvedFormat }
10081
+ })
10082
+ );
10083
+ } else {
10084
+ processed.output = serialized;
10085
+ }
10086
+ }
10087
+ if ("prepareCall" in processed && typeof processed.prepareCall === "function") {
10088
+ processed.prepareCall = "[Function]";
10089
+ }
10090
+ return { input: processed, outputPromise };
10091
+ };
10092
+ var processMessage = (message) => {
10093
+ if (!message || typeof message !== "object") return message;
10094
+ if (Array.isArray(message.content)) {
10095
+ return {
10096
+ ...message,
10097
+ content: message.content.map(processContentPart)
10098
+ };
10099
+ }
10100
+ if (typeof message.content === "object" && message.content !== null) {
10101
+ return {
10102
+ ...message,
10103
+ content: processContentPart(message.content)
10104
+ };
10105
+ }
10106
+ return message;
10107
+ };
10108
+ var processPromptContent = (prompt) => {
10109
+ if (Array.isArray(prompt)) {
10110
+ return prompt.map(processContentPart);
10111
+ }
10112
+ if (prompt.content) {
10113
+ if (Array.isArray(prompt.content)) {
10114
+ return {
10115
+ ...prompt,
10116
+ content: prompt.content.map(processContentPart)
10117
+ };
10118
+ } else if (typeof prompt.content === "object") {
10119
+ return {
10120
+ ...prompt,
10121
+ content: processContentPart(prompt.content)
10122
+ };
10123
+ }
10124
+ }
10125
+ return prompt;
10126
+ };
10127
+ var processContentPart = (part) => {
10128
+ if (!part || typeof part !== "object") return part;
10129
+ try {
10130
+ if (part.type === "image" && part.image) {
10131
+ const imageAttachment = convertImageToAttachment(
10132
+ part.image,
10133
+ part.mimeType || part.mediaType
10134
+ );
10135
+ if (imageAttachment) {
10136
+ return {
10137
+ ...part,
10138
+ image: imageAttachment
10139
+ };
10140
+ }
10141
+ }
10142
+ if (part.type === "file" && part.data && (part.mimeType || part.mediaType)) {
10143
+ const fileAttachment = convertDataToAttachment(
10144
+ part.data,
10145
+ part.mimeType || part.mediaType,
10146
+ part.name || part.filename
10147
+ );
10148
+ if (fileAttachment) {
10149
+ return {
10150
+ ...part,
10151
+ data: fileAttachment
10152
+ };
10153
+ }
10154
+ }
10155
+ if (part.type === "image_url" && part.image_url) {
10156
+ if (typeof part.image_url === "object" && part.image_url.url) {
10157
+ const imageAttachment = convertImageToAttachment(part.image_url.url);
10158
+ if (imageAttachment) {
10159
+ return {
10160
+ ...part,
10161
+ image_url: {
10162
+ ...part.image_url,
10163
+ url: imageAttachment
10164
+ }
10165
+ };
10166
+ }
10167
+ }
10168
+ }
10169
+ } catch (error) {
10170
+ console.warn("Error processing content part:", error);
10171
+ }
10172
+ return part;
10173
+ };
10174
+ var convertImageToAttachment = (image, explicitMimeType) => {
10175
+ try {
10176
+ if (typeof image === "string" && image.startsWith("data:")) {
10177
+ const [mimeTypeSection, base64Data] = image.split(",");
10178
+ const mimeType = mimeTypeSection.match(/data:(.*?);/)?.[1];
10179
+ if (mimeType && base64Data) {
10180
+ const blob = convertDataToBlob(base64Data, mimeType);
10181
+ if (blob) {
10182
+ return new Attachment({
10183
+ data: blob,
10184
+ filename: `image.${getExtensionFromMediaType(mimeType)}`,
10185
+ contentType: mimeType
10186
+ });
10187
+ }
10188
+ }
10189
+ }
10190
+ if (explicitMimeType) {
10191
+ if (image instanceof Uint8Array) {
10192
+ return new Attachment({
10193
+ data: new Blob([image], { type: explicitMimeType }),
10194
+ filename: `image.${getExtensionFromMediaType(explicitMimeType)}`,
10195
+ contentType: explicitMimeType
10196
+ });
10197
+ }
10198
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer(image)) {
10199
+ return new Attachment({
10200
+ data: new Blob([image], { type: explicitMimeType }),
10201
+ filename: `image.${getExtensionFromMediaType(explicitMimeType)}`,
10202
+ contentType: explicitMimeType
10203
+ });
10204
+ }
10205
+ }
10206
+ if (image instanceof Blob && image.type) {
10207
+ return new Attachment({
10208
+ data: image,
10209
+ filename: `image.${getExtensionFromMediaType(image.type)}`,
10210
+ contentType: image.type
10211
+ });
10212
+ }
10213
+ if (image instanceof Attachment) {
10214
+ return image;
10215
+ }
10216
+ } catch (error) {
10217
+ console.warn("Error converting image to attachment:", error);
10218
+ }
10219
+ return null;
10220
+ };
10221
+ var convertDataToAttachment = (data, mimeType, filename) => {
10222
+ if (!mimeType) return null;
10223
+ try {
10224
+ let blob = null;
10225
+ if (typeof data === "string" && data.startsWith("data:")) {
10226
+ const [, base64Data] = data.split(",");
10227
+ if (base64Data) {
10228
+ blob = convertDataToBlob(base64Data, mimeType);
10229
+ }
10230
+ } else if (typeof data === "string" && data.length > 0) {
10231
+ blob = convertDataToBlob(data, mimeType);
10232
+ } else if (data instanceof Uint8Array) {
10233
+ blob = new Blob([data], { type: mimeType });
10234
+ } else if (typeof Buffer !== "undefined" && Buffer.isBuffer(data)) {
10235
+ blob = new Blob([data], { type: mimeType });
10236
+ } else if (data instanceof Blob) {
10237
+ blob = data;
10238
+ }
10239
+ if (blob) {
10240
+ return new Attachment({
10241
+ data: blob,
10242
+ filename: filename || `file.${getExtensionFromMediaType(mimeType)}`,
10243
+ contentType: mimeType
10244
+ });
10245
+ }
10246
+ } catch (error) {
10247
+ console.warn("Error converting data to attachment:", error);
10248
+ }
10249
+ return null;
10250
+ };
10251
+ function processAISDKCallInput(params) {
10252
+ return processInputAttachmentsSync(params);
10253
+ }
10254
+ function prepareAISDKCallInput(params, event, span, defaultDenyOutputPaths) {
10255
+ const { input, outputPromise } = processAISDKCallInput(params);
10256
+ if (outputPromise && input && typeof input === "object") {
10257
+ outputPromise.then((resolvedData) => {
10258
+ span.log({
10259
+ input: {
10260
+ ...input,
10261
+ ...resolvedData
10262
+ }
10263
+ });
10264
+ }).catch(() => {
10265
+ });
10266
+ }
10267
+ const metadata = extractMetadataFromCallParams(params, event.self);
10268
+ const childTracing = prepareAISDKChildTracing(
10269
+ params,
10270
+ event.self,
10271
+ span,
10272
+ defaultDenyOutputPaths,
10273
+ event.aiSDK
10274
+ );
10275
+ event.modelWrapped = childTracing.modelWrapped;
10276
+ if (childTracing.cleanup) {
10277
+ event.__braintrust_ai_sdk_cleanup = childTracing.cleanup;
10278
+ }
10279
+ return {
10280
+ input,
10281
+ metadata
10282
+ };
10283
+ }
10284
+ function prepareAISDKEmbedInput(params, self) {
10285
+ return {
10286
+ input: { ...params },
10287
+ metadata: extractMetadataFromEmbedParams(params, self)
10288
+ };
10289
+ }
10290
+ function extractTopLevelAISDKMetrics(result, event, startTime) {
10291
+ const metrics = hasModelChildTracing(event) ? {} : extractTokenMetrics(result);
10292
+ if (startTime) {
10293
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
10294
+ }
10295
+ return metrics;
10296
+ }
10297
+ function hasModelChildTracing(event) {
10298
+ return event?.modelWrapped === true || event?.__braintrust_ai_sdk_model_wrapped === true;
10299
+ }
10300
+ function createAISDKIntegrationMetadata() {
10301
+ return {
10302
+ braintrust: {
10303
+ integration_name: "ai-sdk",
10304
+ sdk_language: "typescript"
10305
+ }
10306
+ };
10307
+ }
10308
+ function resolveModelFromSelf(self) {
10309
+ return 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;
10310
+ }
10311
+ function extractBaseMetadata(model, self) {
10312
+ const metadata = createAISDKIntegrationMetadata();
10313
+ const { model: modelId, provider } = serializeModelWithProvider(
10314
+ model ?? resolveModelFromSelf(self)
10315
+ );
10316
+ if (modelId) {
10317
+ metadata.model = modelId;
10318
+ }
10319
+ if (provider) {
10320
+ metadata.provider = provider;
10321
+ }
10322
+ return metadata;
10323
+ }
10324
+ function extractMetadataFromCallParams(params, self) {
10325
+ const metadata = extractBaseMetadata(params.model, self);
10326
+ const tools = serializeAISDKToolsForLogging(params.tools);
10327
+ if (tools) {
10328
+ metadata.tools = tools;
10329
+ }
10330
+ return metadata;
10331
+ }
10332
+ function extractMetadataFromEmbedParams(params, self) {
10333
+ return extractBaseMetadata(params.model, self);
10334
+ }
10335
+ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiSDK) {
10336
+ const cleanup = [];
10337
+ const patchedModels = /* @__PURE__ */ new WeakSet();
9794
10338
  const patchedTools = /* @__PURE__ */ new WeakSet();
9795
10339
  let modelWrapped = false;
9796
10340
  const patchModel = (model) => {
9797
- const resolvedModel = resolveAISDKModel(model);
10341
+ const resolvedModel = resolveAISDKModel(model, aiSDK);
9798
10342
  if (!resolvedModel || typeof resolvedModel !== "object" || typeof resolvedModel.doGenerate !== "function" || patchedModels.has(resolvedModel) || resolvedModel[AUTO_PATCHED_MODEL]) {
9799
- return;
10343
+ return resolvedModel;
9800
10344
  }
9801
10345
  patchedModels.add(resolvedModel);
9802
10346
  resolvedModel[AUTO_PATCHED_MODEL] = true;
@@ -9825,7 +10369,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9825
10369
  type: "llm" /* LLM */
9826
10370
  },
9827
10371
  event: {
9828
- input: processAISDKInput(options),
10372
+ input: processAISDKCallInput(options).input,
9829
10373
  metadata: baseMetadata
9830
10374
  }
9831
10375
  }
@@ -9839,7 +10383,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9839
10383
  type: "llm" /* LLM */
9840
10384
  },
9841
10385
  event: {
9842
- input: processAISDKInput(options),
10386
+ input: processAISDKCallInput(options).input,
9843
10387
  metadata: baseMetadata
9844
10388
  }
9845
10389
  });
@@ -9847,6 +10391,8 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9847
10391
  span,
9848
10392
  () => Reflect.apply(originalDoStream, resolvedModel, [options])
9849
10393
  );
10394
+ const streamStartTime = getCurrentUnixTimestamp();
10395
+ let firstChunkTime;
9850
10396
  const output = {};
9851
10397
  let text = "";
9852
10398
  let reasoning = "";
@@ -9854,6 +10400,9 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9854
10400
  let object = void 0;
9855
10401
  const transformStream = new TransformStream({
9856
10402
  transform(chunk, controller) {
10403
+ if (firstChunkTime === void 0) {
10404
+ firstChunkTime = getCurrentUnixTimestamp();
10405
+ }
9857
10406
  switch (chunk.type) {
9858
10407
  case "text-delta":
9859
10408
  text += extractTextDelta(chunk);
@@ -9894,12 +10443,19 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9894
10443
  if (object !== void 0) {
9895
10444
  output.object = object;
9896
10445
  }
10446
+ const metrics = extractTokenMetrics(output);
10447
+ if (firstChunkTime !== void 0) {
10448
+ metrics.time_to_first_token = Math.max(
10449
+ firstChunkTime - streamStartTime,
10450
+ 1e-6
10451
+ );
10452
+ }
9897
10453
  span.log({
9898
10454
  output: processAISDKOutput(
9899
10455
  output,
9900
10456
  denyOutputPaths
9901
10457
  ),
9902
- metrics: extractTokenMetrics(output),
10458
+ metrics,
9903
10459
  ...buildResolvedMetadataPayload(output)
9904
10460
  });
9905
10461
  span.end();
@@ -9921,6 +10477,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9921
10477
  }
9922
10478
  delete resolvedModel[AUTO_PATCHED_MODEL];
9923
10479
  });
10480
+ return resolvedModel;
9924
10481
  };
9925
10482
  const patchTool = (tool, name) => {
9926
10483
  if (tool == null || typeof tool !== "object" || !("execute" in tool) || typeof tool.execute !== "function" || patchedTools.has(tool) || tool[AUTO_PATCHED_TOOL]) {
@@ -9993,17 +10550,26 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9993
10550
  }
9994
10551
  };
9995
10552
  if (params && typeof params === "object") {
9996
- patchModel(params.model);
10553
+ const patchedParamModel = patchModel(params.model);
10554
+ if (typeof params.model === "string" && patchedParamModel && typeof patchedParamModel === "object") {
10555
+ params.model = patchedParamModel;
10556
+ }
9997
10557
  patchTools(params.tools);
9998
10558
  }
9999
10559
  if (self && typeof self === "object") {
10000
10560
  const selfRecord = self;
10001
10561
  if (selfRecord.model !== void 0) {
10002
- patchModel(selfRecord.model);
10562
+ const patchedSelfModel = patchModel(selfRecord.model);
10563
+ if (typeof selfRecord.model === "string" && patchedSelfModel && typeof patchedSelfModel === "object") {
10564
+ selfRecord.model = patchedSelfModel;
10565
+ }
10003
10566
  }
10004
10567
  if (selfRecord.settings && typeof selfRecord.settings === "object") {
10005
10568
  if (selfRecord.settings.model !== void 0) {
10006
- patchModel(selfRecord.settings.model);
10569
+ const patchedSettingsModel = patchModel(selfRecord.settings.model);
10570
+ if (typeof selfRecord.settings.model === "string" && patchedSettingsModel && typeof patchedSettingsModel === "object") {
10571
+ selfRecord.settings.model = patchedSettingsModel;
10572
+ }
10007
10573
  }
10008
10574
  if (selfRecord.settings.tools !== void 0) {
10009
10575
  patchTools(selfRecord.settings.tools);
@@ -10027,63 +10593,178 @@ function finalizeAISDKChildTracing(event) {
10027
10593
  }
10028
10594
  }
10029
10595
  function patchAISDKStreamingResult(args) {
10030
- const { denyOutputPaths, endEvent, result, span, startTime } = args;
10596
+ const { defaultDenyOutputPaths, endEvent, result, span, startTime } = args;
10031
10597
  if (!result || typeof result !== "object") {
10032
10598
  return false;
10033
10599
  }
10034
10600
  const resultRecord = result;
10035
- if (!isReadableStreamLike(resultRecord.baseStream)) {
10601
+ attachKnownResultPromiseHandlers(resultRecord);
10602
+ if (isReadableStreamLike(resultRecord.baseStream)) {
10603
+ let firstChunkTime2;
10604
+ const wrappedBaseStream = resultRecord.baseStream.pipeThrough(
10605
+ new TransformStream({
10606
+ transform(chunk, controller) {
10607
+ if (firstChunkTime2 === void 0) {
10608
+ firstChunkTime2 = getCurrentUnixTimestamp();
10609
+ }
10610
+ controller.enqueue(chunk);
10611
+ },
10612
+ async flush() {
10613
+ const metrics = extractTopLevelAISDKMetrics(result, endEvent);
10614
+ if (metrics.time_to_first_token === void 0 && firstChunkTime2 !== void 0) {
10615
+ metrics.time_to_first_token = firstChunkTime2 - startTime;
10616
+ }
10617
+ const output = await processAISDKStreamingOutput(
10618
+ result,
10619
+ resolveDenyOutputPaths(endEvent, defaultDenyOutputPaths)
10620
+ );
10621
+ const metadata = buildResolvedMetadataPayload(result).metadata;
10622
+ span.log({
10623
+ output,
10624
+ ...metadata ? { metadata } : {},
10625
+ metrics
10626
+ });
10627
+ finalizeAISDKChildTracing(endEvent);
10628
+ span.end();
10629
+ }
10630
+ })
10631
+ );
10632
+ Object.defineProperty(resultRecord, "baseStream", {
10633
+ configurable: true,
10634
+ enumerable: true,
10635
+ value: wrappedBaseStream,
10636
+ writable: true
10637
+ });
10638
+ return true;
10639
+ }
10640
+ const streamField = findAsyncIterableField(resultRecord, [
10641
+ "partialObjectStream",
10642
+ "textStream",
10643
+ "fullStream",
10644
+ "stream"
10645
+ ]);
10646
+ if (!streamField) {
10036
10647
  return false;
10037
10648
  }
10038
10649
  let firstChunkTime;
10039
- const wrappedBaseStream = resultRecord.baseStream.pipeThrough(
10040
- new TransformStream({
10041
- transform(chunk, controller) {
10042
- if (firstChunkTime === void 0) {
10043
- firstChunkTime = getCurrentUnixTimestamp();
10044
- }
10045
- controller.enqueue(chunk);
10046
- },
10047
- async flush() {
10048
- const metrics = extractTopLevelAISDKMetrics(result, endEvent);
10049
- if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
10050
- metrics.time_to_first_token = firstChunkTime - startTime;
10051
- }
10052
- const output = await processAISDKStreamingOutput(
10053
- result,
10054
- denyOutputPaths
10055
- );
10056
- const metadata = buildResolvedMetadataPayload(result).metadata;
10057
- span.log({
10058
- output,
10059
- ...metadata ? { metadata } : {},
10060
- metrics
10061
- });
10062
- finalizeAISDKChildTracing(endEvent);
10063
- span.end();
10650
+ const wrappedStream = createPatchedAsyncIterable(streamField.stream, {
10651
+ onChunk: () => {
10652
+ if (firstChunkTime === void 0) {
10653
+ firstChunkTime = getCurrentUnixTimestamp();
10064
10654
  }
10065
- })
10066
- );
10067
- Object.defineProperty(resultRecord, "baseStream", {
10655
+ },
10656
+ onComplete: async () => {
10657
+ const metrics = extractTopLevelAISDKMetrics(result, endEvent);
10658
+ if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
10659
+ metrics.time_to_first_token = firstChunkTime - startTime;
10660
+ }
10661
+ const output = await processAISDKStreamingOutput(
10662
+ result,
10663
+ resolveDenyOutputPaths(endEvent, defaultDenyOutputPaths)
10664
+ );
10665
+ const metadata = buildResolvedMetadataPayload(result).metadata;
10666
+ span.log({
10667
+ output,
10668
+ ...metadata ? { metadata } : {},
10669
+ metrics
10670
+ });
10671
+ finalizeAISDKChildTracing(endEvent);
10672
+ span.end();
10673
+ },
10674
+ onError: (error) => {
10675
+ span.log({
10676
+ error: error.message
10677
+ });
10678
+ finalizeAISDKChildTracing(endEvent);
10679
+ span.end();
10680
+ }
10681
+ });
10682
+ Object.defineProperty(resultRecord, streamField.field, {
10068
10683
  configurable: true,
10069
10684
  enumerable: true,
10070
- value: wrappedBaseStream,
10685
+ value: wrappedStream,
10071
10686
  writable: true
10072
10687
  });
10073
10688
  return true;
10074
10689
  }
10690
+ function attachKnownResultPromiseHandlers(result) {
10691
+ const promiseLikeFields = [
10692
+ "content",
10693
+ "text",
10694
+ "object",
10695
+ "value",
10696
+ "values",
10697
+ "finishReason",
10698
+ "embedding",
10699
+ "embeddings",
10700
+ "usage",
10701
+ "totalUsage",
10702
+ "responses",
10703
+ "steps"
10704
+ ];
10705
+ for (const field of promiseLikeFields) {
10706
+ try {
10707
+ if (!(field in result)) {
10708
+ continue;
10709
+ }
10710
+ const value = result[field];
10711
+ if (isPromiseLike(value)) {
10712
+ void Promise.resolve(value).catch(() => {
10713
+ });
10714
+ }
10715
+ } catch {
10716
+ }
10717
+ }
10718
+ }
10075
10719
  function isReadableStreamLike(value) {
10076
10720
  return value != null && typeof value === "object" && typeof value.pipeThrough === "function";
10077
10721
  }
10722
+ function isAsyncIterableLike(value) {
10723
+ return value != null && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function";
10724
+ }
10725
+ function findAsyncIterableField(result, candidateFields) {
10726
+ for (const field of candidateFields) {
10727
+ try {
10728
+ const stream = result[field];
10729
+ if (isAsyncIterableLike(stream)) {
10730
+ return { field, stream };
10731
+ }
10732
+ } catch {
10733
+ }
10734
+ }
10735
+ return null;
10736
+ }
10737
+ function createPatchedAsyncIterable(stream, hooks) {
10738
+ return {
10739
+ async *[Symbol.asyncIterator]() {
10740
+ try {
10741
+ for await (const chunk of stream) {
10742
+ hooks.onChunk(chunk);
10743
+ yield chunk;
10744
+ }
10745
+ await hooks.onComplete();
10746
+ } catch (error) {
10747
+ hooks.onError(
10748
+ error instanceof Error ? error : new Error(String(error))
10749
+ );
10750
+ throw error;
10751
+ }
10752
+ }
10753
+ };
10754
+ }
10078
10755
  async function processAISDKStreamingOutput(result, denyOutputPaths) {
10079
10756
  const output = processAISDKOutput(result, denyOutputPaths);
10080
10757
  if (!output || typeof output !== "object") {
10081
10758
  return output;
10082
10759
  }
10083
10760
  const outputRecord = output;
10761
+ const isObjectStreamingResult = result != null && typeof result === "object" && "partialObjectStream" in result;
10084
10762
  try {
10085
- if ("text" in result && typeof result.text === "string") {
10086
- outputRecord.text = result.text;
10763
+ if (!isObjectStreamingResult && "text" in result) {
10764
+ const resolvedText = await Promise.resolve(result.text);
10765
+ if (typeof resolvedText === "string") {
10766
+ outputRecord.text = resolvedText;
10767
+ }
10087
10768
  }
10088
10769
  } catch {
10089
10770
  }
@@ -10096,6 +10777,15 @@ async function processAISDKStreamingOutput(result, denyOutputPaths) {
10096
10777
  }
10097
10778
  } catch {
10098
10779
  }
10780
+ try {
10781
+ if ("finishReason" in result) {
10782
+ const resolvedFinishReason = await Promise.resolve(result.finishReason);
10783
+ if (resolvedFinishReason !== void 0) {
10784
+ outputRecord.finishReason = resolvedFinishReason;
10785
+ }
10786
+ }
10787
+ } catch {
10788
+ }
10099
10789
  return outputRecord;
10100
10790
  }
10101
10791
  function buildAISDKChildMetadata(model) {
@@ -10118,16 +10808,25 @@ function buildResolvedMetadataPayload(result) {
10118
10808
  if (gatewayInfo?.model) {
10119
10809
  metadata.model = gatewayInfo.model;
10120
10810
  }
10121
- if (result.finishReason !== void 0) {
10122
- metadata.finish_reason = result.finishReason;
10811
+ let finishReason;
10812
+ try {
10813
+ finishReason = result.finishReason;
10814
+ } catch {
10815
+ finishReason = void 0;
10816
+ }
10817
+ if (isPromiseLike(finishReason)) {
10818
+ void Promise.resolve(finishReason).catch(() => {
10819
+ });
10820
+ } else if (finishReason !== void 0) {
10821
+ metadata.finish_reason = finishReason;
10123
10822
  }
10124
10823
  return Object.keys(metadata).length > 0 ? { metadata } : {};
10125
10824
  }
10126
- function resolveAISDKModel(model) {
10825
+ function resolveAISDKModel(model, aiSDK) {
10127
10826
  if (typeof model !== "string") {
10128
10827
  return model;
10129
10828
  }
10130
- const provider = globalThis.AI_SDK_DEFAULT_PROVIDER ?? null;
10829
+ const provider = globalThis.AI_SDK_DEFAULT_PROVIDER ?? aiSDK?.gateway ?? null;
10131
10830
  if (provider && typeof provider.languageModel === "function") {
10132
10831
  return provider.languageModel(model);
10133
10832
  }
@@ -10148,18 +10847,50 @@ function processAISDKOutput(output, denyOutputPaths) {
10148
10847
  const merged = extractSerializableOutputFields(output);
10149
10848
  return normalizeAISDKLoggedOutput(omit(merged, denyOutputPaths));
10150
10849
  }
10151
- function extractTokenMetrics(result) {
10152
- const metrics = {};
10153
- let usage = result?.totalUsage || result?.usage;
10154
- if (!usage && result) {
10155
- try {
10156
- if ("totalUsage" in result && typeof result.totalUsage !== "function") {
10157
- usage = result.totalUsage;
10158
- } else if ("usage" in result && typeof result.usage !== "function") {
10159
- usage = result.usage;
10160
- }
10161
- } catch {
10162
- }
10850
+ function processAISDKEmbeddingOutput(output, denyOutputPaths) {
10851
+ if (!output || typeof output !== "object") {
10852
+ return output;
10853
+ }
10854
+ const summarized = {};
10855
+ const whitelistedFields = [
10856
+ "usage",
10857
+ "totalUsage",
10858
+ "warnings",
10859
+ "providerMetadata",
10860
+ "experimental_providerMetadata"
10861
+ ];
10862
+ for (const field of whitelistedFields) {
10863
+ const value = safeSerializableFieldRead(output, field);
10864
+ if (value !== void 0 && isSerializableOutputValue(value)) {
10865
+ summarized[field] = value;
10866
+ }
10867
+ }
10868
+ const embedding = safeSerializableFieldRead(output, "embedding");
10869
+ if (Array.isArray(embedding)) {
10870
+ summarized.embedding_length = embedding.length;
10871
+ }
10872
+ const embeddings = safeSerializableFieldRead(output, "embeddings");
10873
+ if (Array.isArray(embeddings)) {
10874
+ summarized.embedding_count = embeddings.length;
10875
+ const firstEmbedding = embeddings.find((item) => Array.isArray(item));
10876
+ if (Array.isArray(firstEmbedding)) {
10877
+ summarized.embedding_length = firstEmbedding.length;
10878
+ }
10879
+ }
10880
+ return normalizeAISDKLoggedOutput(omit(summarized, denyOutputPaths));
10881
+ }
10882
+ function extractTokenMetrics(result) {
10883
+ const metrics = {};
10884
+ let usage;
10885
+ const totalUsageValue = safeResultFieldRead(result, "totalUsage");
10886
+ if (totalUsageValue !== void 0 && !isPromiseLike(totalUsageValue)) {
10887
+ usage = totalUsageValue;
10888
+ }
10889
+ if (!usage) {
10890
+ const usageValue = safeResultFieldRead(result, "usage");
10891
+ if (usageValue !== void 0 && !isPromiseLike(usageValue)) {
10892
+ usage = usageValue;
10893
+ }
10163
10894
  }
10164
10895
  if (!usage) {
10165
10896
  return metrics;
@@ -10196,6 +10927,22 @@ function extractTokenMetrics(result) {
10196
10927
  }
10197
10928
  return metrics;
10198
10929
  }
10930
+ function safeResultFieldRead(result, field) {
10931
+ return safeSerializableFieldRead(result, field);
10932
+ }
10933
+ function safeSerializableFieldRead(obj, field) {
10934
+ try {
10935
+ const value = obj?.[field];
10936
+ if (isPromiseLike(value)) {
10937
+ void Promise.resolve(value).catch(() => {
10938
+ });
10939
+ return void 0;
10940
+ }
10941
+ return value;
10942
+ } catch {
10943
+ return void 0;
10944
+ }
10945
+ }
10199
10946
  function aggregateAISDKChunks(chunks, _result, endEvent) {
10200
10947
  const lastChunk = chunks[chunks.length - 1];
10201
10948
  const output = {};
@@ -10204,17 +10951,21 @@ function aggregateAISDKChunks(chunks, _result, endEvent) {
10204
10951
  if (lastChunk) {
10205
10952
  metrics = hasModelChildTracing(endEvent) ? {} : extractTokenMetrics(lastChunk);
10206
10953
  metadata = buildResolvedMetadataPayload(lastChunk).metadata;
10207
- if (lastChunk.text !== void 0) {
10208
- output.text = lastChunk.text;
10954
+ const text = safeSerializableFieldRead(lastChunk, "text");
10955
+ if (text !== void 0) {
10956
+ output.text = text;
10209
10957
  }
10210
- if (lastChunk.object !== void 0) {
10211
- output.object = lastChunk.object;
10958
+ const objectValue = safeSerializableFieldRead(lastChunk, "object");
10959
+ if (objectValue !== void 0) {
10960
+ output.object = objectValue;
10212
10961
  }
10213
- if (lastChunk.finishReason !== void 0) {
10214
- output.finishReason = lastChunk.finishReason;
10962
+ const finishReason = safeSerializableFieldRead(lastChunk, "finishReason");
10963
+ if (finishReason !== void 0) {
10964
+ output.finishReason = finishReason;
10215
10965
  }
10216
- if (lastChunk.toolCalls !== void 0) {
10217
- output.toolCalls = lastChunk.toolCalls;
10966
+ const toolCalls = safeSerializableFieldRead(lastChunk, "toolCalls");
10967
+ if (toolCalls !== void 0) {
10968
+ output.toolCalls = toolCalls;
10218
10969
  }
10219
10970
  }
10220
10971
  finalizeAISDKChildTracing(endEvent);
@@ -10223,14 +10974,20 @@ function aggregateAISDKChunks(chunks, _result, endEvent) {
10223
10974
  function extractGetterValues(obj) {
10224
10975
  const getterValues = {};
10225
10976
  const getterNames = [
10977
+ "content",
10226
10978
  "text",
10227
10979
  "object",
10980
+ "value",
10981
+ "values",
10982
+ "embedding",
10983
+ "embeddings",
10228
10984
  "finishReason",
10229
10985
  "usage",
10230
10986
  "totalUsage",
10231
10987
  "toolCalls",
10232
10988
  "toolResults",
10233
10989
  "warnings",
10990
+ "responses",
10234
10991
  "experimental_providerMetadata",
10235
10992
  "providerMetadata",
10236
10993
  "rawResponse",
@@ -10238,8 +10995,17 @@ function extractGetterValues(obj) {
10238
10995
  ];
10239
10996
  for (const name of getterNames) {
10240
10997
  try {
10241
- if (obj && name in obj && isSerializableOutputValue(obj[name])) {
10242
- getterValues[name] = obj[name];
10998
+ if (!obj || !(name in obj)) {
10999
+ continue;
11000
+ }
11001
+ const value = obj[name];
11002
+ if (isPromiseLike(value)) {
11003
+ void Promise.resolve(value).catch(() => {
11004
+ });
11005
+ continue;
11006
+ }
11007
+ if (isSerializableOutputValue(value)) {
11008
+ getterValues[name] = value;
10243
11009
  }
10244
11010
  } catch {
10245
11011
  }
@@ -10261,6 +11027,11 @@ function extractSerializableOutputFields(output) {
10261
11027
  for (const name of directFieldNames) {
10262
11028
  try {
10263
11029
  const value = output?.[name];
11030
+ if (isPromiseLike(value)) {
11031
+ void Promise.resolve(value).catch(() => {
11032
+ });
11033
+ continue;
11034
+ }
10264
11035
  if (isSerializableOutputValue(value)) {
10265
11036
  serialized[name] = value;
10266
11037
  }
@@ -10272,6 +11043,9 @@ function extractSerializableOutputFields(output) {
10272
11043
  ...extractGetterValues(output)
10273
11044
  };
10274
11045
  }
11046
+ function isPromiseLike(value) {
11047
+ return value != null && typeof value === "object" && typeof value.then === "function";
11048
+ }
10275
11049
  function isSerializableOutputValue(value) {
10276
11050
  if (typeof value === "function") {
10277
11051
  return false;
@@ -10313,8 +11087,9 @@ function parseGatewayModelString(modelString) {
10313
11087
  return { model: modelString };
10314
11088
  }
10315
11089
  function extractGatewayRoutingInfo(result) {
10316
- if (result?.steps && Array.isArray(result.steps) && result.steps.length > 0) {
10317
- const routing2 = result.steps[0]?.providerMetadata?.gateway?.routing;
11090
+ const steps = safeSerializableFieldRead(result, "steps");
11091
+ if (Array.isArray(steps) && steps.length > 0) {
11092
+ const routing2 = steps[0]?.providerMetadata?.gateway?.routing;
10318
11093
  if (routing2) {
10319
11094
  return {
10320
11095
  provider: routing2.resolvedProvider || routing2.finalProvider,
@@ -10322,7 +11097,11 @@ function extractGatewayRoutingInfo(result) {
10322
11097
  };
10323
11098
  }
10324
11099
  }
10325
- const routing = result?.providerMetadata?.gateway?.routing;
11100
+ const providerMetadata = safeSerializableFieldRead(
11101
+ result,
11102
+ "providerMetadata"
11103
+ );
11104
+ const routing = providerMetadata?.gateway?.routing;
10326
11105
  if (routing) {
10327
11106
  return {
10328
11107
  provider: routing.resolvedProvider || routing.finalProvider,
@@ -10332,10 +11111,11 @@ function extractGatewayRoutingInfo(result) {
10332
11111
  return null;
10333
11112
  }
10334
11113
  function extractCostFromResult(result) {
10335
- if (result?.steps && Array.isArray(result.steps) && result.steps.length > 0) {
11114
+ const steps = safeSerializableFieldRead(result, "steps");
11115
+ if (Array.isArray(steps) && steps.length > 0) {
10336
11116
  let totalCost = 0;
10337
11117
  let foundCost = false;
10338
- for (const step of result.steps) {
11118
+ for (const step of steps) {
10339
11119
  const gateway2 = step?.providerMetadata?.gateway;
10340
11120
  const stepCost = parseGatewayCost(gateway2?.cost) || parseGatewayCost(gateway2?.marketCost);
10341
11121
  if (stepCost !== void 0 && stepCost > 0) {
@@ -10347,7 +11127,11 @@ function extractCostFromResult(result) {
10347
11127
  return totalCost;
10348
11128
  }
10349
11129
  }
10350
- const gateway = result?.providerMetadata?.gateway;
11130
+ const providerMetadata = safeSerializableFieldRead(
11131
+ result,
11132
+ "providerMetadata"
11133
+ );
11134
+ const gateway = providerMetadata?.gateway;
10351
11135
  const directCost = parseGatewayCost(gateway?.cost) || parseGatewayCost(gateway?.marketCost);
10352
11136
  if (directCost !== void 0 && directCost > 0) {
10353
11137
  return directCost;
@@ -10464,7 +11248,307 @@ var claudeAgentSDKChannels = defineChannels(
10464
11248
  }
10465
11249
  );
10466
11250
 
11251
+ // src/instrumentation/plugins/claude-agent-sdk-instrumentation-constants.ts
11252
+ var CLAUDE_AGENT_SDK_SKIP_LOCAL_TOOL_HOOKS_OPTION = "__braintrust_skip_local_tool_hooks";
11253
+
11254
+ // src/instrumentation/plugins/claude-agent-sdk-local-tool-context.ts
11255
+ var LOCAL_TOOL_CONTEXT_ASYNC_ITERATOR_PATCHED = Symbol.for(
11256
+ "braintrust.claude_agent_sdk.local_tool_context_async_iterator_patched"
11257
+ );
11258
+ function createLocalToolContextStore() {
11259
+ const maybeIsoWithAsyncLocalStorage = isomorph_default;
11260
+ if (typeof maybeIsoWithAsyncLocalStorage.newAsyncLocalStorage === "function") {
11261
+ return maybeIsoWithAsyncLocalStorage.newAsyncLocalStorage();
11262
+ }
11263
+ let currentStore;
11264
+ return {
11265
+ enterWith(store) {
11266
+ currentStore = store;
11267
+ },
11268
+ getStore() {
11269
+ return currentStore;
11270
+ },
11271
+ run(store, callback) {
11272
+ const previousStore = currentStore;
11273
+ currentStore = store;
11274
+ try {
11275
+ return callback();
11276
+ } finally {
11277
+ currentStore = previousStore;
11278
+ }
11279
+ }
11280
+ };
11281
+ }
11282
+ var localToolContextStore = createLocalToolContextStore();
11283
+ var fallbackLocalToolParentResolver;
11284
+ function createClaudeLocalToolContext() {
11285
+ return {};
11286
+ }
11287
+ function runWithClaudeLocalToolContext(callback, context) {
11288
+ return localToolContextStore.run(
11289
+ context ?? createClaudeLocalToolContext(),
11290
+ callback
11291
+ );
11292
+ }
11293
+ function ensureClaudeLocalToolContext() {
11294
+ const existing = localToolContextStore.getStore();
11295
+ if (existing) {
11296
+ return existing;
11297
+ }
11298
+ const created = {};
11299
+ localToolContextStore.enterWith(created);
11300
+ return created;
11301
+ }
11302
+ function setClaudeLocalToolParentResolver(resolver) {
11303
+ fallbackLocalToolParentResolver = resolver;
11304
+ const context = ensureClaudeLocalToolContext();
11305
+ if (!context) {
11306
+ return;
11307
+ }
11308
+ context.resolveLocalToolParent = resolver;
11309
+ }
11310
+ function getClaudeLocalToolParentResolver() {
11311
+ return localToolContextStore.getStore()?.resolveLocalToolParent ?? fallbackLocalToolParentResolver;
11312
+ }
11313
+ function isAsyncIterable2(value) {
11314
+ return value !== null && typeof value === "object" && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
11315
+ }
11316
+ function bindClaudeLocalToolContextToAsyncIterable(result, localToolContext) {
11317
+ if (!isAsyncIterable2(result) || Object.isFrozen(result) || Object.isSealed(result)) {
11318
+ return result;
11319
+ }
11320
+ const stream = result;
11321
+ const originalAsyncIterator = stream[Symbol.asyncIterator];
11322
+ if (originalAsyncIterator[LOCAL_TOOL_CONTEXT_ASYNC_ITERATOR_PATCHED]) {
11323
+ return result;
11324
+ }
11325
+ const patchedAsyncIterator = function() {
11326
+ return runWithClaudeLocalToolContext(() => {
11327
+ const iterator = Reflect.apply(originalAsyncIterator, this, []);
11328
+ if (!iterator || typeof iterator !== "object") {
11329
+ return iterator;
11330
+ }
11331
+ const patchMethod = (methodName) => {
11332
+ const originalMethod = Reflect.get(iterator, methodName);
11333
+ if (typeof originalMethod !== "function") {
11334
+ return;
11335
+ }
11336
+ Reflect.set(
11337
+ iterator,
11338
+ methodName,
11339
+ (...args) => runWithClaudeLocalToolContext(
11340
+ () => Reflect.apply(
11341
+ originalMethod,
11342
+ iterator,
11343
+ args
11344
+ ),
11345
+ localToolContext
11346
+ )
11347
+ );
11348
+ };
11349
+ patchMethod("next");
11350
+ patchMethod("return");
11351
+ patchMethod("throw");
11352
+ return iterator;
11353
+ }, localToolContext);
11354
+ };
11355
+ Object.defineProperty(
11356
+ patchedAsyncIterator,
11357
+ LOCAL_TOOL_CONTEXT_ASYNC_ITERATOR_PATCHED,
11358
+ {
11359
+ configurable: false,
11360
+ enumerable: false,
11361
+ value: true,
11362
+ writable: false
11363
+ }
11364
+ );
11365
+ Reflect.set(stream, Symbol.asyncIterator, patchedAsyncIterator);
11366
+ return result;
11367
+ }
11368
+
11369
+ // src/instrumentation/plugins/claude-agent-sdk-local-tool-spans.ts
11370
+ var LOCAL_TOOL_HANDLER_WRAPPED = Symbol.for(
11371
+ "braintrust.claude_agent_sdk.local_tool_handler_wrapped"
11372
+ );
11373
+ function toErrorMessage(error) {
11374
+ return error instanceof Error ? error.message : String(error);
11375
+ }
11376
+ function isPromiseLike2(value) {
11377
+ return value !== null && typeof value === "object" && "then" in value && typeof value.then === "function";
11378
+ }
11379
+ function getToolUseIdFromExtra(extra) {
11380
+ if (!extra || typeof extra !== "object" || !("_meta" in extra)) {
11381
+ return void 0;
11382
+ }
11383
+ const meta = Reflect.get(extra, "_meta");
11384
+ if (!meta || typeof meta !== "object") {
11385
+ return void 0;
11386
+ }
11387
+ const toolUseId = Reflect.get(meta, "claudecode/toolUseId");
11388
+ return typeof toolUseId === "string" ? toolUseId : void 0;
11389
+ }
11390
+ function wrapLocalClaudeToolHandler(handler, getMetadata) {
11391
+ if (handler[LOCAL_TOOL_HANDLER_WRAPPED]) {
11392
+ return handler;
11393
+ }
11394
+ const wrappedHandler = function wrappedLocalToolHandler(...handlerArgs) {
11395
+ const metadata = getMetadata();
11396
+ const rawToolName = metadata.serverName ? `mcp__${metadata.serverName}__${metadata.toolName}` : metadata.toolName;
11397
+ const toolUseId = getToolUseIdFromExtra(handlerArgs[1]);
11398
+ const localToolParentResolver = getClaudeLocalToolParentResolver();
11399
+ const spanName = metadata.serverName ? `tool: ${metadata.serverName}/${metadata.toolName}` : `tool: ${metadata.toolName}`;
11400
+ const runWithResolvedParent = async () => {
11401
+ const parent = toolUseId && localToolParentResolver ? await localToolParentResolver(toolUseId).catch(() => void 0) : void 0;
11402
+ const span = startSpan({
11403
+ event: {
11404
+ input: handlerArgs[0],
11405
+ metadata: {
11406
+ "claude_agent_sdk.raw_tool_name": rawToolName,
11407
+ "gen_ai.tool.name": metadata.toolName,
11408
+ ...toolUseId && { "gen_ai.tool.call.id": toolUseId },
11409
+ ...metadata.serverName && {
11410
+ "mcp.server": metadata.serverName
11411
+ }
11412
+ }
11413
+ },
11414
+ name: spanName,
11415
+ ...parent && { parent },
11416
+ spanAttributes: { type: "tool" /* TOOL */ }
11417
+ });
11418
+ const runHandler = () => Reflect.apply(handler, this, handlerArgs);
11419
+ const finalizeSuccess = (result) => {
11420
+ span.log({ output: result });
11421
+ span.end();
11422
+ return result;
11423
+ };
11424
+ const finalizeError = (error) => {
11425
+ span.log({ error: toErrorMessage(error) });
11426
+ span.end();
11427
+ throw error;
11428
+ };
11429
+ return withCurrent(span, () => {
11430
+ try {
11431
+ const result = runHandler();
11432
+ if (isPromiseLike2(result)) {
11433
+ return result.then(finalizeSuccess, finalizeError);
11434
+ }
11435
+ return finalizeSuccess(result);
11436
+ } catch (error) {
11437
+ return finalizeError(error);
11438
+ }
11439
+ });
11440
+ };
11441
+ return runWithResolvedParent();
11442
+ };
11443
+ Object.defineProperty(wrappedHandler, LOCAL_TOOL_HANDLER_WRAPPED, {
11444
+ configurable: false,
11445
+ enumerable: false,
11446
+ value: true,
11447
+ writable: false
11448
+ });
11449
+ return wrappedHandler;
11450
+ }
11451
+ function getRegisteredTools(instance) {
11452
+ if (!instance || typeof instance !== "object") {
11453
+ return void 0;
11454
+ }
11455
+ if (!("_registeredTools" in instance)) {
11456
+ return void 0;
11457
+ }
11458
+ const registeredTools = Reflect.get(instance, "_registeredTools");
11459
+ if (registeredTools instanceof Map) {
11460
+ return registeredTools;
11461
+ }
11462
+ if (registeredTools && typeof registeredTools === "object") {
11463
+ return registeredTools;
11464
+ }
11465
+ return void 0;
11466
+ }
11467
+ function wrapLocalMcpServerToolHandlers(serverName, serverConfig) {
11468
+ if (!serverConfig || typeof serverConfig !== "object") {
11469
+ return false;
11470
+ }
11471
+ if (!("instance" in serverConfig)) {
11472
+ return false;
11473
+ }
11474
+ const instance = Reflect.get(serverConfig, "instance");
11475
+ const registeredTools = getRegisteredTools(instance);
11476
+ if (!registeredTools) {
11477
+ return false;
11478
+ }
11479
+ let wrappedAny = false;
11480
+ const wrapHandler = (toolName, registration) => {
11481
+ if (!registration || typeof registration !== "object") {
11482
+ return;
11483
+ }
11484
+ const handler = Reflect.get(registration, "handler");
11485
+ if (typeof handler !== "function") {
11486
+ return;
11487
+ }
11488
+ const wrappedHandler = wrapLocalClaudeToolHandler(handler, () => ({
11489
+ serverName,
11490
+ toolName
11491
+ }));
11492
+ if (wrappedHandler !== handler) {
11493
+ Reflect.set(registration, "handler", wrappedHandler);
11494
+ wrappedAny = true;
11495
+ }
11496
+ };
11497
+ if (registeredTools instanceof Map) {
11498
+ for (const [toolName, registration] of registeredTools.entries()) {
11499
+ wrapHandler(toolName, registration);
11500
+ }
11501
+ return wrappedAny;
11502
+ }
11503
+ for (const [toolName, registration] of Object.entries(registeredTools)) {
11504
+ wrapHandler(toolName, registration);
11505
+ }
11506
+ return wrappedAny;
11507
+ }
11508
+ function collectLocalMcpServerToolHookNames(serverName, serverConfig) {
11509
+ const toolNames = /* @__PURE__ */ new Set();
11510
+ if (!serverConfig || typeof serverConfig !== "object") {
11511
+ return toolNames;
11512
+ }
11513
+ if ("instance" in serverConfig) {
11514
+ const instance = Reflect.get(serverConfig, "instance");
11515
+ const registeredTools = getRegisteredTools(instance);
11516
+ if (registeredTools instanceof Map) {
11517
+ for (const toolName of registeredTools.keys()) {
11518
+ toolNames.add(toolName);
11519
+ toolNames.add(`mcp__${serverName}__${toolName}`);
11520
+ }
11521
+ } else if (registeredTools) {
11522
+ for (const toolName of Object.keys(registeredTools)) {
11523
+ toolNames.add(toolName);
11524
+ toolNames.add(`mcp__${serverName}__${toolName}`);
11525
+ }
11526
+ }
11527
+ }
11528
+ if ("tools" in serverConfig) {
11529
+ const rawTools = Reflect.get(serverConfig, "tools");
11530
+ if (Array.isArray(rawTools)) {
11531
+ for (const tool of rawTools) {
11532
+ if (!tool || typeof tool !== "object") {
11533
+ continue;
11534
+ }
11535
+ const toolName = Reflect.get(tool, "name");
11536
+ if (typeof toolName !== "string") {
11537
+ continue;
11538
+ }
11539
+ toolNames.add(toolName);
11540
+ toolNames.add(`mcp__${serverName}__${toolName}`);
11541
+ }
11542
+ }
11543
+ }
11544
+ return toolNames;
11545
+ }
11546
+
10467
11547
  // src/instrumentation/plugins/claude-agent-sdk-plugin.ts
11548
+ var ROOT_LLM_PARENT_KEY = "__root__";
11549
+ function llmParentKey(parentToolUseId) {
11550
+ return parentToolUseId ?? ROOT_LLM_PARENT_KEY;
11551
+ }
10468
11552
  function isSubAgentToolName(toolName) {
10469
11553
  return toolName === "Agent" || toolName === "Task";
10470
11554
  }
@@ -10550,7 +11634,7 @@ function buildLLMInput(prompt, conversationHistory, capturedPromptMessages) {
10550
11634
  function formatCapturedMessages(messages) {
10551
11635
  return messages.length > 0 ? messages : [];
10552
11636
  }
10553
- async function createLLMSpanForMessages(messages, prompt, conversationHistory, options, startTime, capturedPromptMessages, parentSpan) {
11637
+ async function createLLMSpanForMessages(messages, prompt, conversationHistory, options, startTime, capturedPromptMessages, parentSpan, existingSpan) {
10554
11638
  if (messages.length === 0) {
10555
11639
  return void 0;
10556
11640
  }
@@ -10570,7 +11654,7 @@ async function createLLMSpanForMessages(messages, prompt, conversationHistory, o
10570
11654
  ).filter(
10571
11655
  (c) => c !== void 0
10572
11656
  );
10573
- const span = startSpan({
11657
+ const span = existingSpan ?? startSpan({
10574
11658
  name: "anthropic.messages.create",
10575
11659
  parent: parentSpan,
10576
11660
  spanAttributes: {
@@ -10584,8 +11668,13 @@ async function createLLMSpanForMessages(messages, prompt, conversationHistory, o
10584
11668
  metrics: usage,
10585
11669
  output: outputs
10586
11670
  });
11671
+ const spanExport = await span.export();
10587
11672
  await span.end();
10588
- return lastMessage.message?.content && lastMessage.message?.role ? { content: lastMessage.message.content, role: lastMessage.message.role } : void 0;
11673
+ const finalMessage = lastMessage.message?.content && lastMessage.message?.role ? { content: lastMessage.message.content, role: lastMessage.message.role } : void 0;
11674
+ return {
11675
+ finalMessage,
11676
+ spanExport
11677
+ };
10589
11678
  }
10590
11679
  function getMcpServerMetadata(serverName, mcpServers) {
10591
11680
  if (!serverName || !mcpServers) {
@@ -10629,11 +11718,51 @@ function parseToolName(rawToolName) {
10629
11718
  toolName: rawToolName
10630
11719
  };
10631
11720
  }
10632
- function createToolTracingHooks(resolveParentSpan, activeToolSpans, mcpServers, subAgentSpans, endedSubAgentSpans) {
11721
+ function isLocalToolUse(rawToolName, mcpServers) {
11722
+ const parsed = parseToolName(rawToolName);
11723
+ if (!parsed.mcpServer || !mcpServers) {
11724
+ return false;
11725
+ }
11726
+ const serverConfig = mcpServers[parsed.mcpServer];
11727
+ if (!serverConfig || typeof serverConfig !== "object") {
11728
+ return false;
11729
+ }
11730
+ return serverConfig.type === "sdk" || "transport" in serverConfig;
11731
+ }
11732
+ function prepareLocalToolHandlersInMcpServers(mcpServers) {
11733
+ const localToolHookNames = /* @__PURE__ */ new Set();
11734
+ if (!mcpServers) {
11735
+ return {
11736
+ hasLocalToolHandlers: false,
11737
+ localToolHookNames
11738
+ };
11739
+ }
11740
+ let hasLocalToolHandlers = false;
11741
+ for (const [serverName, serverConfig] of Object.entries(mcpServers)) {
11742
+ const toolNames = collectLocalMcpServerToolHookNames(
11743
+ serverName,
11744
+ serverConfig
11745
+ );
11746
+ for (const toolName of toolNames) {
11747
+ localToolHookNames.add(toolName);
11748
+ }
11749
+ if (toolNames.size > 0) {
11750
+ hasLocalToolHandlers = true;
11751
+ }
11752
+ if (wrapLocalMcpServerToolHandlers(serverName, serverConfig)) {
11753
+ hasLocalToolHandlers = true;
11754
+ }
11755
+ }
11756
+ return { hasLocalToolHandlers, localToolHookNames };
11757
+ }
11758
+ function createToolTracingHooks(resolveParentSpan, activeToolSpans, mcpServers, localToolHookNames, skipLocalToolHooks, subAgentSpans, endedSubAgentSpans) {
10633
11759
  const preToolUse = async (input, toolUseID) => {
10634
11760
  if (input.hook_event_name !== "PreToolUse" || !toolUseID) {
10635
11761
  return {};
10636
11762
  }
11763
+ if (skipLocalToolHooks && (isLocalToolUse(input.tool_name, mcpServers) || localToolHookNames.has(input.tool_name))) {
11764
+ return {};
11765
+ }
10637
11766
  if (isSubAgentToolName(input.tool_name)) {
10638
11767
  return {};
10639
11768
  }
@@ -10662,6 +11791,9 @@ function createToolTracingHooks(resolveParentSpan, activeToolSpans, mcpServers,
10662
11791
  if (input.hook_event_name !== "PostToolUse" || !toolUseID) {
10663
11792
  return {};
10664
11793
  }
11794
+ if (skipLocalToolHooks && (isLocalToolUse(input.tool_name, mcpServers) || localToolHookNames.has(input.tool_name))) {
11795
+ return {};
11796
+ }
10665
11797
  const subAgentSpan = subAgentSpans.get(toolUseID);
10666
11798
  if (subAgentSpan) {
10667
11799
  try {
@@ -10702,6 +11834,9 @@ function createToolTracingHooks(resolveParentSpan, activeToolSpans, mcpServers,
10702
11834
  if (input.hook_event_name !== "PostToolUseFailure" || !toolUseID) {
10703
11835
  return {};
10704
11836
  }
11837
+ if (skipLocalToolHooks && (isLocalToolUse(input.tool_name, mcpServers) || localToolHookNames.has(input.tool_name))) {
11838
+ return {};
11839
+ }
10705
11840
  const subAgentSpan = subAgentSpans.get(toolUseID);
10706
11841
  if (subAgentSpan) {
10707
11842
  try {
@@ -10736,11 +11871,13 @@ function createToolTracingHooks(resolveParentSpan, activeToolSpans, mcpServers,
10736
11871
  };
10737
11872
  return { postToolUse, postToolUseFailure, preToolUse };
10738
11873
  }
10739
- function injectTracingHooks(options, resolveParentSpan, activeToolSpans, subAgentSpans, endedSubAgentSpans) {
11874
+ function injectTracingHooks(options, resolveParentSpan, activeToolSpans, localToolHookNames, skipLocalToolHooks, subAgentSpans, endedSubAgentSpans) {
10740
11875
  const { preToolUse, postToolUse, postToolUseFailure } = createToolTracingHooks(
10741
11876
  resolveParentSpan,
10742
11877
  activeToolSpans,
10743
11878
  options.mcpServers,
11879
+ localToolHookNames,
11880
+ skipLocalToolHooks,
10744
11881
  subAgentSpans,
10745
11882
  endedSubAgentSpans
10746
11883
  );
@@ -10771,6 +11908,7 @@ async function finalizeCurrentMessageGroup(state) {
10771
11908
  return;
10772
11909
  }
10773
11910
  const parentToolUseId = state.currentMessages[0]?.parent_tool_use_id ?? null;
11911
+ const parentKey = llmParentKey(parentToolUseId);
10774
11912
  let parentSpan = await state.span.export();
10775
11913
  if (parentToolUseId) {
10776
11914
  const subAgentSpan = state.subAgentSpans.get(parentToolUseId);
@@ -10778,17 +11916,30 @@ async function finalizeCurrentMessageGroup(state) {
10778
11916
  parentSpan = await subAgentSpan.export();
10779
11917
  }
10780
11918
  }
10781
- const finalMessage = await createLLMSpanForMessages(
11919
+ const existingLlmSpan = state.activeLlmSpansByParentToolUse.get(parentKey);
11920
+ const llmSpanResult = await createLLMSpanForMessages(
10782
11921
  state.currentMessages,
10783
11922
  state.originalPrompt,
10784
11923
  state.finalResults,
10785
11924
  state.options,
10786
11925
  state.currentMessageStartTime,
10787
11926
  state.capturedPromptMessages,
10788
- parentSpan
11927
+ parentSpan,
11928
+ existingLlmSpan
10789
11929
  );
10790
- if (finalMessage) {
10791
- state.finalResults.push(finalMessage);
11930
+ state.activeLlmSpansByParentToolUse.delete(parentKey);
11931
+ if (llmSpanResult) {
11932
+ if (parentToolUseId) {
11933
+ state.latestLlmParentBySubAgentToolUse.set(
11934
+ parentToolUseId,
11935
+ llmSpanResult.spanExport
11936
+ );
11937
+ } else {
11938
+ state.latestRootLlmParentRef.value = llmSpanResult.spanExport;
11939
+ }
11940
+ if (llmSpanResult.finalMessage) {
11941
+ state.finalResults.push(llmSpanResult.finalMessage);
11942
+ }
10792
11943
  }
10793
11944
  const lastMessage = state.currentMessages[state.currentMessages.length - 1];
10794
11945
  if (lastMessage?.message?.usage) {
@@ -10856,6 +12007,29 @@ async function handleStreamMessage(state, message) {
10856
12007
  state.currentMessageStartTime = getCurrentUnixTimestamp();
10857
12008
  }
10858
12009
  if (message.type === "assistant" && message.message?.usage) {
12010
+ const parentToolUseId = message.parent_tool_use_id ?? null;
12011
+ const parentKey = llmParentKey(parentToolUseId);
12012
+ if (!state.activeLlmSpansByParentToolUse.has(parentKey)) {
12013
+ let llmParentSpan = await state.span.export();
12014
+ if (parentToolUseId) {
12015
+ const subAgentSpan = await ensureSubAgentSpan(
12016
+ state.pendingSubAgentNames,
12017
+ state.span,
12018
+ state.subAgentSpans,
12019
+ parentToolUseId
12020
+ );
12021
+ llmParentSpan = await subAgentSpan.export();
12022
+ }
12023
+ const llmSpan = startSpan({
12024
+ name: "anthropic.messages.create",
12025
+ parent: llmParentSpan,
12026
+ spanAttributes: {
12027
+ type: "llm" /* LLM */
12028
+ },
12029
+ startTime: state.currentMessageStartTime
12030
+ });
12031
+ state.activeLlmSpansByParentToolUse.set(parentKey, llmSpan);
12032
+ }
10859
12033
  state.currentMessages.push(message);
10860
12034
  }
10861
12035
  if (message.type !== "result" || !message.usage) {
@@ -10916,6 +12090,10 @@ async function finalizeQuerySpan(state) {
10916
12090
  }
10917
12091
  }
10918
12092
  } finally {
12093
+ for (const llmSpan of state.activeLlmSpansByParentToolUse.values()) {
12094
+ llmSpan.end();
12095
+ }
12096
+ state.activeLlmSpansByParentToolUse.clear();
10919
12097
  for (const [id, subAgentSpan] of state.subAgentSpans) {
10920
12098
  if (!state.endedSubAgentSpans.has(id)) {
10921
12099
  subAgentSpan.end();
@@ -10981,26 +12159,51 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
10981
12159
  console.error("Error extracting input for Claude Agent SDK:", error);
10982
12160
  }
10983
12161
  const activeToolSpans = /* @__PURE__ */ new Map();
12162
+ const activeLlmSpansByParentToolUse = /* @__PURE__ */ new Map();
10984
12163
  const subAgentSpans = /* @__PURE__ */ new Map();
10985
12164
  const endedSubAgentSpans = /* @__PURE__ */ new Set();
10986
12165
  const toolUseToParent = /* @__PURE__ */ new Map();
12166
+ const latestLlmParentBySubAgentToolUse = /* @__PURE__ */ new Map();
12167
+ const latestRootLlmParentRef = {
12168
+ value: void 0
12169
+ };
10987
12170
  const pendingSubAgentNames = /* @__PURE__ */ new Map();
12171
+ const localToolContext = createClaudeLocalToolContext();
12172
+ const { hasLocalToolHandlers, localToolHookNames } = prepareLocalToolHandlersInMcpServers(options.mcpServers);
12173
+ const skipLocalToolHooks = options[CLAUDE_AGENT_SDK_SKIP_LOCAL_TOOL_HOOKS_OPTION] === true || hasLocalToolHandlers;
12174
+ const resolveToolUseParentSpan = async (toolUseID) => {
12175
+ const parentToolUseId = toolUseToParent.get(toolUseID) ?? null;
12176
+ const parentKey = llmParentKey(parentToolUseId);
12177
+ const activeLlmSpan = activeLlmSpansByParentToolUse.get(parentKey);
12178
+ if (activeLlmSpan) {
12179
+ return activeLlmSpan.export();
12180
+ }
12181
+ if (parentToolUseId) {
12182
+ const parentLlm = latestLlmParentBySubAgentToolUse.get(parentToolUseId);
12183
+ if (parentLlm) {
12184
+ return parentLlm;
12185
+ }
12186
+ const subAgentSpan = await ensureSubAgentSpan(
12187
+ pendingSubAgentNames,
12188
+ span,
12189
+ subAgentSpans,
12190
+ parentToolUseId
12191
+ );
12192
+ return subAgentSpan.export();
12193
+ }
12194
+ if (latestRootLlmParentRef.value) {
12195
+ return latestRootLlmParentRef.value;
12196
+ }
12197
+ return span.export();
12198
+ };
12199
+ localToolContext.resolveLocalToolParent = resolveToolUseParentSpan;
12200
+ setClaudeLocalToolParentResolver(resolveToolUseParentSpan);
10988
12201
  const optionsWithHooks = injectTracingHooks(
10989
12202
  options,
10990
- async (toolUseID) => {
10991
- const parentToolUseId = toolUseToParent.get(toolUseID);
10992
- if (parentToolUseId) {
10993
- const subAgentSpan = await ensureSubAgentSpan(
10994
- pendingSubAgentNames,
10995
- span,
10996
- subAgentSpans,
10997
- parentToolUseId
10998
- );
10999
- return subAgentSpan.export();
11000
- }
11001
- return span.export();
11002
- },
12203
+ resolveToolUseParentSpan,
11003
12204
  activeToolSpans,
12205
+ localToolHookNames,
12206
+ skipLocalToolHooks,
11004
12207
  subAgentSpans,
11005
12208
  endedSubAgentSpans
11006
12209
  );
@@ -11008,6 +12211,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
11008
12211
  event.arguments[0] = params;
11009
12212
  spans.set(event, {
11010
12213
  accumulatedOutputTokens: 0,
12214
+ activeLlmSpansByParentToolUse,
11011
12215
  activeToolSpans,
11012
12216
  capturedPromptMessages,
11013
12217
  currentMessageId: void 0,
@@ -11023,7 +12227,10 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
11023
12227
  promptStarted: () => promptStarted,
11024
12228
  span,
11025
12229
  subAgentSpans,
11026
- toolUseToParent
12230
+ latestLlmParentBySubAgentToolUse,
12231
+ latestRootLlmParentRef,
12232
+ toolUseToParent,
12233
+ localToolContext
11027
12234
  });
11028
12235
  },
11029
12236
  end: (event) => {
@@ -11031,7 +12238,10 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
11031
12238
  if (!state) {
11032
12239
  return;
11033
12240
  }
11034
- const eventResult = event.result;
12241
+ const eventResult = bindClaudeLocalToolContextToAsyncIterable(
12242
+ event.result,
12243
+ state.localToolContext
12244
+ );
11035
12245
  if (eventResult === void 0) {
11036
12246
  state.span.end();
11037
12247
  spans.delete(event);
@@ -11048,20 +12258,16 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
11048
12258
  );
11049
12259
  });
11050
12260
  },
11051
- onComplete: () => {
11052
- void state.processing.then(() => finalizeQuerySpan(state)).finally(() => {
11053
- spans.delete(event);
11054
- });
11055
- },
11056
- onError: (error) => {
11057
- void state.processing.then(() => {
11058
- state.span.log({
11059
- error: error.message
11060
- });
11061
- }).then(() => finalizeQuerySpan(state)).finally(() => {
11062
- spans.delete(event);
12261
+ onComplete: () => state.processing.then(() => finalizeQuerySpan(state)).finally(() => {
12262
+ spans.delete(event);
12263
+ }),
12264
+ onError: (error) => state.processing.then(() => {
12265
+ state.span.log({
12266
+ error: error.message
11063
12267
  });
11064
- }
12268
+ }).then(() => finalizeQuerySpan(state)).finally(() => {
12269
+ spans.delete(event);
12270
+ })
11065
12271
  });
11066
12272
  return;
11067
12273
  }
@@ -11209,12 +12415,14 @@ var GoogleGenAIPlugin = class extends BasePlugin {
11209
12415
  const params = event.arguments[0];
11210
12416
  streamEvent.googleGenAIInput = serializeInput(params);
11211
12417
  streamEvent.googleGenAIMetadata = extractMetadata(params);
12418
+ streamEvent.googleGenAIStartTime = getCurrentUnixTimestamp();
11212
12419
  },
11213
12420
  asyncEnd: (event) => {
11214
12421
  const streamEvent = event;
11215
12422
  patchGoogleGenAIStreamingResult({
11216
12423
  input: streamEvent.googleGenAIInput,
11217
12424
  metadata: streamEvent.googleGenAIMetadata,
12425
+ startTime: streamEvent.googleGenAIStartTime,
11218
12426
  result: streamEvent.result
11219
12427
  });
11220
12428
  },
@@ -11238,19 +12446,20 @@ function ensureSpanState(states, event, create) {
11238
12446
  }
11239
12447
  function bindCurrentSpanStoreToStart2(tracingChannel, states, create) {
11240
12448
  const state = _internalGetGlobalState();
12449
+ const contextManager = state?.contextManager;
11241
12450
  const startChannel = tracingChannel.start;
11242
- const currentSpanStore = state?.contextManager ? state.contextManager[BRAINTRUST_CURRENT_SPAN_STORE] : void 0;
12451
+ const currentSpanStore = contextManager ? contextManager[BRAINTRUST_CURRENT_SPAN_STORE] : void 0;
11243
12452
  if (!startChannel?.bindStore || !currentSpanStore) {
11244
12453
  return void 0;
11245
12454
  }
11246
- startChannel.bindStore(
11247
- currentSpanStore,
11248
- (event) => ensureSpanState(
12455
+ startChannel.bindStore(currentSpanStore, (event) => {
12456
+ const span = ensureSpanState(
11249
12457
  states,
11250
12458
  event,
11251
12459
  () => create(event)
11252
- ).span
11253
- );
12460
+ ).span;
12461
+ return contextManager.wrapSpanForStore(span);
12462
+ });
11254
12463
  return () => {
11255
12464
  startChannel.unbindStore?.(currentSpanStore);
11256
12465
  };
@@ -11267,7 +12476,7 @@ function logErrorAndEndSpan(states, event) {
11267
12476
  states.delete(event);
11268
12477
  }
11269
12478
  function patchGoogleGenAIStreamingResult(args) {
11270
- const { input, metadata, result } = args;
12479
+ const { input, metadata, result, startTime } = args;
11271
12480
  if (!input || !metadata || !result || typeof result !== "object" || typeof result.next !== "function") {
11272
12481
  return false;
11273
12482
  }
@@ -11275,7 +12484,7 @@ function patchGoogleGenAIStreamingResult(args) {
11275
12484
  let firstTokenTime = null;
11276
12485
  let finalized = false;
11277
12486
  let span = null;
11278
- let startTime = null;
12487
+ const requestStartTime = startTime ?? getCurrentUnixTimestamp();
11279
12488
  const ensureSpan = () => {
11280
12489
  if (!span) {
11281
12490
  span = startSpan({
@@ -11288,7 +12497,6 @@ function patchGoogleGenAIStreamingResult(args) {
11288
12497
  metadata
11289
12498
  }
11290
12499
  });
11291
- startTime = getCurrentUnixTimestamp();
11292
12500
  }
11293
12501
  return span;
11294
12502
  };
@@ -11342,11 +12550,11 @@ function patchGoogleGenAIStreamingResult(args) {
11342
12550
  }
11343
12551
  chunks.push(nextResult.value);
11344
12552
  }
11345
- if (nextResult.done && startTime !== null) {
12553
+ if (nextResult.done) {
11346
12554
  finalize({
11347
12555
  result: aggregateGenerateContentChunks(
11348
12556
  chunks,
11349
- startTime,
12557
+ requestStartTime,
11350
12558
  firstTokenTime
11351
12559
  )
11352
12560
  });
@@ -11366,13 +12574,13 @@ function patchGoogleGenAIStreamingResult(args) {
11366
12574
  ...returnArgs
11367
12575
  );
11368
12576
  } finally {
11369
- if (startTime !== null) {
12577
+ if (chunks.length > 0) {
11370
12578
  finalize({
11371
- result: chunks.length > 0 ? aggregateGenerateContentChunks(
12579
+ result: aggregateGenerateContentChunks(
11372
12580
  chunks,
11373
- startTime,
12581
+ requestStartTime,
11374
12582
  firstTokenTime
11375
- ) : void 0
12583
+ )
11376
12584
  });
11377
12585
  } else {
11378
12586
  finalize({});
@@ -11646,50 +12854,165 @@ function tryToDict(obj) {
11646
12854
  return null;
11647
12855
  }
11648
12856
 
11649
- // src/instrumentation/plugins/openrouter-channels.ts
11650
- var openRouterChannels = defineChannels("@openrouter/sdk", {
11651
- chatSend: channel({
11652
- channelName: "chat.send",
11653
- kind: "async"
11654
- }),
11655
- embeddingsGenerate: channel({
11656
- channelName: "embeddings.generate",
11657
- kind: "async"
11658
- }),
11659
- betaResponsesSend: channel({
11660
- channelName: "beta.responses.send",
11661
- kind: "async"
11662
- }),
12857
+ // src/instrumentation/plugins/openrouter-agent-channels.ts
12858
+ var openRouterAgentChannels = defineChannels("@openrouter/agent", {
11663
12859
  callModel: channel({
11664
12860
  channelName: "callModel",
11665
12861
  kind: "sync-stream"
11666
12862
  }),
12863
+ callModelTurn: channel({
12864
+ channelName: "callModel.turn",
12865
+ kind: "async"
12866
+ }),
11667
12867
  toolExecute: channel({
11668
12868
  channelName: "tool.execute",
11669
12869
  kind: "async"
11670
12870
  })
11671
12871
  });
11672
12872
 
11673
- // src/openrouter-utils.ts
11674
- var TOKEN_NAME_MAP2 = {
11675
- promptTokens: "prompt_tokens",
11676
- inputTokens: "prompt_tokens",
11677
- completionTokens: "completion_tokens",
11678
- outputTokens: "completion_tokens",
11679
- totalTokens: "tokens",
11680
- prompt_tokens: "prompt_tokens",
11681
- input_tokens: "prompt_tokens",
11682
- completion_tokens: "completion_tokens",
11683
- output_tokens: "completion_tokens",
11684
- total_tokens: "tokens"
11685
- };
11686
- var TOKEN_DETAIL_PREFIX_MAP = {
11687
- promptTokensDetails: "prompt",
11688
- inputTokensDetails: "prompt",
11689
- completionTokensDetails: "completion",
11690
- outputTokensDetails: "completion",
11691
- costDetails: "cost",
11692
- prompt_tokens_details: "prompt",
12873
+ // src/instrumentation/plugins/openrouter-agent-plugin.ts
12874
+ var OpenRouterAgentPlugin = class extends BasePlugin {
12875
+ onEnable() {
12876
+ this.subscribeToOpenRouterAgentChannels();
12877
+ }
12878
+ onDisable() {
12879
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
12880
+ }
12881
+ subscribeToOpenRouterAgentChannels() {
12882
+ this.unsubscribers.push(
12883
+ traceSyncStreamChannel(openRouterAgentChannels.callModel, {
12884
+ name: "openrouter.callModel",
12885
+ type: "llm" /* LLM */,
12886
+ extractInput: (args) => {
12887
+ const request = getOpenRouterCallModelRequestArg(args);
12888
+ return {
12889
+ input: request ? extractOpenRouterCallModelInput(request) : void 0,
12890
+ metadata: request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" }
12891
+ };
12892
+ },
12893
+ patchResult: ({ endEvent, result, span }) => {
12894
+ return patchOpenRouterCallModelResult({
12895
+ request: getOpenRouterCallModelRequestArg(endEvent.arguments),
12896
+ result,
12897
+ span
12898
+ });
12899
+ }
12900
+ })
12901
+ );
12902
+ this.unsubscribers.push(
12903
+ traceAsyncChannel(openRouterAgentChannels.callModelTurn, {
12904
+ name: "openrouter.beta.responses.send",
12905
+ type: "llm" /* LLM */,
12906
+ extractInput: (args, event) => {
12907
+ const request = getOpenRouterCallModelRequestArg(args);
12908
+ const metadata = request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" };
12909
+ if (isObject(metadata) && "tools" in metadata) {
12910
+ delete metadata.tools;
12911
+ }
12912
+ return {
12913
+ input: request ? extractOpenRouterCallModelInput(request) : void 0,
12914
+ metadata: {
12915
+ ...metadata,
12916
+ step: event.step,
12917
+ step_type: event.stepType
12918
+ }
12919
+ };
12920
+ },
12921
+ extractOutput: (result) => extractOpenRouterResponseOutput(result),
12922
+ extractMetadata: (result, event) => {
12923
+ if (!isObject(result)) {
12924
+ return {
12925
+ step: event?.step,
12926
+ step_type: event?.stepType
12927
+ };
12928
+ }
12929
+ return {
12930
+ ...extractOpenRouterResponseMetadata(result) || {},
12931
+ ...event?.step !== void 0 ? { step: event.step } : {},
12932
+ ...event?.stepType ? { step_type: event.stepType } : {}
12933
+ };
12934
+ },
12935
+ extractMetrics: (result) => isObject(result) ? parseOpenRouterMetricsFromUsage(result.usage) : {}
12936
+ })
12937
+ );
12938
+ this.unsubscribers.push(
12939
+ traceStreamingChannel(openRouterAgentChannels.toolExecute, {
12940
+ name: "openrouter.tool",
12941
+ type: "tool" /* TOOL */,
12942
+ extractInput: (args, event) => ({
12943
+ input: args[0],
12944
+ metadata: {
12945
+ provider: "openrouter",
12946
+ tool_name: event.toolName,
12947
+ ...event.toolCallId ? { tool_call_id: event.toolCallId } : {}
12948
+ }
12949
+ }),
12950
+ extractOutput: (result) => result,
12951
+ extractMetrics: () => ({}),
12952
+ aggregateChunks: (chunks) => ({
12953
+ output: chunks.length > 0 ? chunks[chunks.length - 1] : void 0,
12954
+ metrics: {}
12955
+ })
12956
+ })
12957
+ );
12958
+ const callModelChannel = openRouterAgentChannels.callModel.tracingChannel();
12959
+ const callModelHandlers = {
12960
+ start: (event) => {
12961
+ const request = getOpenRouterCallModelRequestArg(event.arguments);
12962
+ if (!request) {
12963
+ return;
12964
+ }
12965
+ patchOpenRouterCallModelRequestTools(request);
12966
+ }
12967
+ };
12968
+ callModelChannel.subscribe(callModelHandlers);
12969
+ this.unsubscribers.push(() => {
12970
+ callModelChannel.unsubscribe(callModelHandlers);
12971
+ });
12972
+ }
12973
+ };
12974
+ function normalizeArgs(args) {
12975
+ if (Array.isArray(args)) {
12976
+ return args;
12977
+ }
12978
+ if (isArrayLike(args)) {
12979
+ return Array.from(args);
12980
+ }
12981
+ return [args];
12982
+ }
12983
+ function isArrayLike(value) {
12984
+ return isObject(value) && "length" in value && typeof value.length === "number" && Number.isInteger(value.length) && value.length >= 0;
12985
+ }
12986
+ function getOpenRouterCallModelRequestArg(args) {
12987
+ const normalizedArgs = normalizeArgs(args);
12988
+ const keyedRequestArg = normalizedArgs.find(
12989
+ (arg) => isObject(arg) && ("input" in arg || "model" in arg || "tools" in arg)
12990
+ );
12991
+ if (isObject(keyedRequestArg)) {
12992
+ return keyedRequestArg;
12993
+ }
12994
+ const firstObjectArg = normalizedArgs.find((arg) => isObject(arg));
12995
+ return isObject(firstObjectArg) ? firstObjectArg : void 0;
12996
+ }
12997
+ var TOKEN_NAME_MAP2 = {
12998
+ promptTokens: "prompt_tokens",
12999
+ inputTokens: "prompt_tokens",
13000
+ completionTokens: "completion_tokens",
13001
+ outputTokens: "completion_tokens",
13002
+ totalTokens: "tokens",
13003
+ prompt_tokens: "prompt_tokens",
13004
+ input_tokens: "prompt_tokens",
13005
+ completion_tokens: "completion_tokens",
13006
+ output_tokens: "completion_tokens",
13007
+ total_tokens: "tokens"
13008
+ };
13009
+ var TOKEN_DETAIL_PREFIX_MAP = {
13010
+ promptTokensDetails: "prompt",
13011
+ inputTokensDetails: "prompt",
13012
+ completionTokensDetails: "completion",
13013
+ outputTokensDetails: "completion",
13014
+ costDetails: "cost",
13015
+ prompt_tokens_details: "prompt",
11693
13016
  input_tokens_details: "prompt",
11694
13017
  completion_tokens_details: "completion",
11695
13018
  output_tokens_details: "completion",
@@ -11736,8 +13059,6 @@ function extractOpenRouterUsageMetadata(usage) {
11736
13059
  }
11737
13060
  return Object.keys(metadata).length > 0 ? metadata : void 0;
11738
13061
  }
11739
-
11740
- // src/openrouter-logging.ts
11741
13062
  var OMITTED_OPENROUTER_KEYS = /* @__PURE__ */ new Set([
11742
13063
  "execute",
11743
13064
  "render",
@@ -11757,10 +13078,10 @@ function parseOpenRouterModelString(model) {
11757
13078
  }
11758
13079
  return { model };
11759
13080
  }
11760
- function isZodSchema2(value) {
13081
+ function isZodSchema3(value) {
11761
13082
  return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
11762
13083
  }
11763
- function serializeZodSchema2(schema) {
13084
+ function serializeZodSchema3(schema) {
11764
13085
  try {
11765
13086
  return zodToJsonSchema(schema);
11766
13087
  } catch {
@@ -11794,8 +13115,8 @@ function serializeOpenRouterToolsForLogging(tools) {
11794
13115
  return tools.map((tool) => serializeOpenRouterTool(tool));
11795
13116
  }
11796
13117
  function sanitizeOpenRouterLoggedValue(value) {
11797
- if (isZodSchema2(value)) {
11798
- return serializeZodSchema2(value);
13118
+ if (isZodSchema3(value)) {
13119
+ return serializeZodSchema3(value);
11799
13120
  }
11800
13121
  if (typeof value === "function") {
11801
13122
  return "[Function]";
@@ -11819,7 +13140,7 @@ function sanitizeOpenRouterLoggedValue(value) {
11819
13140
  }
11820
13141
  return sanitized;
11821
13142
  }
11822
- function buildOpenRouterMetadata(metadata, httpReferer, xTitle) {
13143
+ function buildOpenRouterMetadata(metadata, httpReferer, appTitle, appCategories, xTitle) {
11823
13144
  const sanitized = sanitizeOpenRouterLoggedValue(metadata);
11824
13145
  const metadataRecord = isObject(sanitized) ? sanitized : {};
11825
13146
  const { model, provider: providerRouting, ...rest } = metadataRecord;
@@ -11829,17 +13150,12 @@ function buildOpenRouterMetadata(metadata, httpReferer, xTitle) {
11829
13150
  ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
11830
13151
  ...providerRouting !== void 0 ? { providerRouting } : {},
11831
13152
  ...httpReferer !== void 0 ? { httpReferer } : {},
13153
+ ...appTitle !== void 0 ? { appTitle } : {},
13154
+ ...appCategories !== void 0 ? { appCategories } : {},
11832
13155
  ...xTitle !== void 0 ? { xTitle } : {},
11833
13156
  provider: normalizedModel.provider || "openrouter"
11834
13157
  };
11835
13158
  }
11836
- function buildOpenRouterEmbeddingMetadata(metadata, httpReferer, xTitle) {
11837
- const normalized = buildOpenRouterMetadata(metadata, httpReferer, xTitle);
11838
- return typeof normalized.model === "string" ? {
11839
- ...normalized,
11840
- embedding_model: normalized.model
11841
- } : normalized;
11842
- }
11843
13159
  function extractOpenRouterCallModelInput(request) {
11844
13160
  return isObject(request) && "input" in request ? sanitizeOpenRouterLoggedValue(request.input) : void 0;
11845
13161
  }
@@ -11848,7 +13164,13 @@ function extractOpenRouterCallModelMetadata(request) {
11848
13164
  return { provider: "openrouter" };
11849
13165
  }
11850
13166
  const { input: _input, ...metadata } = request;
11851
- return buildOpenRouterMetadata(metadata, void 0, void 0);
13167
+ return buildOpenRouterMetadata(
13168
+ metadata,
13169
+ void 0,
13170
+ void 0,
13171
+ void 0,
13172
+ void 0
13173
+ );
11852
13174
  }
11853
13175
  function extractOpenRouterResponseMetadata(result) {
11854
13176
  if (!isObject(result)) {
@@ -11878,9 +13200,101 @@ function extractOpenRouterResponseOutput(response, fallbackOutput) {
11878
13200
  }
11879
13201
  return void 0;
11880
13202
  }
11881
-
11882
- // src/openrouter-tool-wrapping.ts
11883
13203
  var OPENROUTER_WRAPPED_TOOL = Symbol("braintrust.openrouter.wrappedTool");
13204
+ function patchOpenRouterCallModelRequestTools(request) {
13205
+ if (!Array.isArray(request.tools) || request.tools.length === 0) {
13206
+ return void 0;
13207
+ }
13208
+ const originalTools = request.tools;
13209
+ const wrappedTools = originalTools.map((tool) => wrapOpenRouterTool(tool));
13210
+ const didPatch = wrappedTools.some(
13211
+ (tool, index) => tool !== originalTools[index]
13212
+ );
13213
+ if (!didPatch) {
13214
+ return void 0;
13215
+ }
13216
+ request.tools = wrappedTools;
13217
+ return () => {
13218
+ request.tools = originalTools;
13219
+ };
13220
+ }
13221
+ function wrapOpenRouterTool(tool) {
13222
+ if (isWrappedTool(tool) || !tool.function || typeof tool.function !== "object" || typeof tool.function.execute !== "function") {
13223
+ return tool;
13224
+ }
13225
+ const toolName = tool.function.name || "tool";
13226
+ const originalExecute = tool.function.execute;
13227
+ const wrappedTool = {
13228
+ ...tool,
13229
+ function: {
13230
+ ...tool.function,
13231
+ execute(...args) {
13232
+ return traceToolExecution({
13233
+ args,
13234
+ execute: () => Reflect.apply(originalExecute, this, args),
13235
+ toolCallId: getToolCallId(args[1]),
13236
+ toolName
13237
+ });
13238
+ }
13239
+ }
13240
+ };
13241
+ Object.defineProperty(wrappedTool, OPENROUTER_WRAPPED_TOOL, {
13242
+ value: true,
13243
+ enumerable: false,
13244
+ configurable: false
13245
+ });
13246
+ return wrappedTool;
13247
+ }
13248
+ function isWrappedTool(tool) {
13249
+ return Boolean(tool[OPENROUTER_WRAPPED_TOOL]);
13250
+ }
13251
+ function traceToolExecution(args) {
13252
+ const tracingChannel = openRouterAgentChannels.toolExecute.tracingChannel();
13253
+ const input = args.args.length > 0 ? args.args[0] : void 0;
13254
+ const event = {
13255
+ arguments: [input],
13256
+ span_info: {
13257
+ name: args.toolName
13258
+ },
13259
+ toolCallId: args.toolCallId,
13260
+ toolName: args.toolName
13261
+ };
13262
+ tracingChannel.start.publish(event);
13263
+ try {
13264
+ const result = args.execute();
13265
+ return publishToolResult(tracingChannel, event, result);
13266
+ } catch (error) {
13267
+ event.error = normalizeError(error);
13268
+ tracingChannel.error.publish(event);
13269
+ throw error;
13270
+ }
13271
+ }
13272
+ function publishToolResult(tracingChannel, event, result) {
13273
+ if (isPromiseLike3(result)) {
13274
+ return result.then(
13275
+ (resolved) => {
13276
+ event.result = resolved;
13277
+ tracingChannel.asyncEnd.publish(event);
13278
+ return resolved;
13279
+ },
13280
+ (error) => {
13281
+ event.error = normalizeError(error);
13282
+ tracingChannel.error.publish(event);
13283
+ throw error;
13284
+ }
13285
+ );
13286
+ }
13287
+ event.result = result;
13288
+ tracingChannel.asyncEnd.publish(event);
13289
+ return result;
13290
+ }
13291
+ function getToolCallId(context) {
13292
+ const toolContext = context;
13293
+ return typeof toolContext?.toolCall?.id === "string" ? toolContext.toolCall.id : void 0;
13294
+ }
13295
+ function isPromiseLike3(value) {
13296
+ return !!value && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
13297
+ }
11884
13298
  var OPENROUTER_WRAPPED_CALL_MODEL_RESULT = Symbol(
11885
13299
  "braintrust.openrouter.wrappedCallModelResult"
11886
13300
  );
@@ -11900,24 +13314,8 @@ var OPENROUTER_CALL_MODEL_CONTEXT_METHODS = [
11900
13314
  "getToolCalls",
11901
13315
  "requiresApproval"
11902
13316
  ];
11903
- function patchOpenRouterCallModelRequestTools(request) {
11904
- if (!Array.isArray(request.tools) || request.tools.length === 0) {
11905
- return void 0;
11906
- }
11907
- const originalTools = request.tools;
11908
- const wrappedTools = originalTools.map((tool) => wrapOpenRouterTool(tool));
11909
- const didPatch = wrappedTools.some(
11910
- (tool, index) => tool !== originalTools[index]
11911
- );
11912
- if (!didPatch) {
11913
- return void 0;
11914
- }
11915
- request.tools = wrappedTools;
11916
- return () => {
11917
- request.tools = originalTools;
11918
- };
11919
- }
11920
- function patchOpenRouterCallModelResult(span, result, request) {
13317
+ function patchOpenRouterCallModelResult(args) {
13318
+ const { request, result, span } = args;
11921
13319
  if (!isObject(result) || isWrappedCallModelResult(result)) {
11922
13320
  return false;
11923
13321
  }
@@ -11990,10 +13388,10 @@ function patchOpenRouterCallModelResult(span, result, request) {
11990
13388
  }
11991
13389
  };
11992
13390
  if (originalGetResponse) {
11993
- resultLike.getResponse = async (...args) => {
13391
+ resultLike.getResponse = async (...args2) => {
11994
13392
  return await withCurrent(span, async () => {
11995
13393
  try {
11996
- const response = await originalGetResponse(...args);
13394
+ const response = await originalGetResponse(...args2);
11997
13395
  await endSpanWithResult(response);
11998
13396
  return response;
11999
13397
  } catch (error) {
@@ -12005,10 +13403,10 @@ function patchOpenRouterCallModelResult(span, result, request) {
12005
13403
  }
12006
13404
  if (typeof resultLike.getText === "function") {
12007
13405
  const originalGetText = resultLike.getText.bind(resultLike);
12008
- resultLike.getText = async (...args) => {
13406
+ resultLike.getText = async (...args2) => {
12009
13407
  return await withCurrent(span, async () => {
12010
13408
  try {
12011
- const text = await originalGetText(...args);
13409
+ const text = await originalGetText(...args2);
12012
13410
  await finalizeFromResponse(text);
12013
13411
  return text;
12014
13412
  } catch (error) {
@@ -12023,9 +13421,9 @@ function patchOpenRouterCallModelResult(span, result, request) {
12023
13421
  continue;
12024
13422
  }
12025
13423
  const originalMethod = resultLike[methodName];
12026
- resultLike[methodName] = async (...args) => {
13424
+ resultLike[methodName] = async (...args2) => {
12027
13425
  return await withCurrent(span, async () => {
12028
- return await originalMethod.apply(resultLike, args);
13426
+ return await originalMethod.apply(resultLike, args2);
12029
13427
  });
12030
13428
  };
12031
13429
  }
@@ -12034,12 +13432,12 @@ function patchOpenRouterCallModelResult(span, result, request) {
12034
13432
  continue;
12035
13433
  }
12036
13434
  const originalMethod = resultLike[methodName];
12037
- resultLike[methodName] = (...args) => {
13435
+ resultLike[methodName] = (...args2) => {
12038
13436
  const stream = withCurrent(
12039
13437
  span,
12040
- () => originalMethod.apply(resultLike, args)
13438
+ () => originalMethod.apply(resultLike, args2)
12041
13439
  );
12042
- if (!isAsyncIterable2(stream)) {
13440
+ if (!isAsyncIterable3(stream)) {
12043
13441
  return stream;
12044
13442
  }
12045
13443
  return wrapAsyncIterableWithSpan({
@@ -12052,157 +13450,70 @@ function patchOpenRouterCallModelResult(span, result, request) {
12052
13450
  }
12053
13451
  if (originalGetInitialResponse) {
12054
13452
  let initialTurnTraced = false;
12055
- resultLike.getInitialResponse = async (...args) => {
13453
+ resultLike.getInitialResponse = async (...args2) => {
12056
13454
  if (initialTurnTraced) {
12057
13455
  return await withCurrent(span, async () => {
12058
- return await originalGetInitialResponse(...args);
13456
+ return await originalGetInitialResponse(...args2);
12059
13457
  });
12060
13458
  }
12061
13459
  initialTurnTraced = true;
12062
- const resolvedRequest = getOpenRouterResolvedRequest(resultLike, request);
12063
- const childSpan = startOpenRouterCallModelTurnSpan({
12064
- request: resolvedRequest,
12065
- step: tracedTurnCount + 1,
12066
- stepType: tracedTurnCount === 0 ? "initial" : "continue"
12067
- });
12068
- return await withCurrent(childSpan, async () => {
12069
- try {
12070
- const response = await originalGetInitialResponse(...args);
13460
+ const step = tracedTurnCount + 1;
13461
+ const stepType = tracedTurnCount === 0 ? "initial" : "continue";
13462
+ const response = await traceOpenRouterCallModelTurn({
13463
+ fn: async () => {
13464
+ const nextResponse = await originalGetInitialResponse(...args2);
12071
13465
  tracedTurnCount++;
12072
- finishOpenRouterCallModelTurnSpan({
12073
- response,
12074
- step: tracedTurnCount,
12075
- stepType: tracedTurnCount === 1 ? "initial" : "continue",
12076
- span: childSpan
12077
- });
12078
- return response;
12079
- } catch (error) {
12080
- childSpan.log({
12081
- error: normalizeError(error).message
12082
- });
12083
- childSpan.end();
12084
- throw error;
12085
- }
13466
+ return nextResponse;
13467
+ },
13468
+ parentSpan: span,
13469
+ request: getOpenRouterResolvedRequest(resultLike, request),
13470
+ step,
13471
+ stepType
12086
13472
  });
13473
+ return response;
12087
13474
  };
12088
13475
  }
12089
13476
  if (originalMakeFollowupRequest) {
12090
- resultLike.makeFollowupRequest = async (...args) => {
12091
- const currentResponse = args[0];
12092
- const toolResults = Array.isArray(args[1]) ? args[1] : [];
12093
- const resolvedRequest = getOpenRouterResolvedRequest(resultLike, request);
12094
- const followupRequest = buildOpenRouterFollowupRequest(
12095
- resolvedRequest,
12096
- currentResponse,
12097
- toolResults
12098
- );
12099
- const childSpan = startOpenRouterCallModelTurnSpan({
12100
- request: followupRequest,
12101
- step: tracedTurnCount + 1,
12102
- stepType: "continue"
12103
- });
12104
- return await withCurrent(childSpan, async () => {
12105
- try {
12106
- const response = await originalMakeFollowupRequest(...args);
13477
+ resultLike.makeFollowupRequest = async (...args2) => {
13478
+ const currentResponse = args2[0];
13479
+ const toolResults = Array.isArray(args2[1]) ? args2[1] : [];
13480
+ const step = tracedTurnCount + 1;
13481
+ const response = await traceOpenRouterCallModelTurn({
13482
+ fn: async () => {
13483
+ const nextResponse = await originalMakeFollowupRequest(...args2);
12107
13484
  tracedTurnCount++;
12108
- finishOpenRouterCallModelTurnSpan({
12109
- response,
12110
- step: tracedTurnCount,
12111
- stepType: "continue",
12112
- span: childSpan
12113
- });
12114
- return response;
12115
- } catch (error) {
12116
- childSpan.log({
12117
- error: normalizeError(error).message
12118
- });
12119
- childSpan.end();
12120
- throw error;
12121
- }
13485
+ return nextResponse;
13486
+ },
13487
+ parentSpan: span,
13488
+ request: buildOpenRouterFollowupRequest(
13489
+ getOpenRouterResolvedRequest(resultLike, request),
13490
+ currentResponse,
13491
+ toolResults
13492
+ ),
13493
+ step,
13494
+ stepType: "continue"
12122
13495
  });
13496
+ return response;
12123
13497
  };
12124
13498
  }
12125
13499
  return true;
12126
13500
  }
12127
- function wrapOpenRouterTool(tool) {
12128
- if (isWrappedTool(tool) || !tool.function || typeof tool.function !== "object" || typeof tool.function.execute !== "function") {
12129
- return tool;
12130
- }
12131
- const toolName = tool.function.name || "tool";
12132
- const originalExecute = tool.function.execute;
12133
- const wrappedTool = {
12134
- ...tool,
12135
- function: {
12136
- ...tool.function,
12137
- execute(...args) {
12138
- return traceToolExecution({
12139
- args,
12140
- execute: () => Reflect.apply(originalExecute, this, args),
12141
- toolCallId: getToolCallId(args[1]),
12142
- toolName
12143
- });
12144
- }
12145
- }
13501
+ async function traceOpenRouterCallModelTurn(args) {
13502
+ const context = {
13503
+ arguments: [args.request],
13504
+ step: args.step,
13505
+ stepType: args.stepType
12146
13506
  };
12147
- Object.defineProperty(wrappedTool, OPENROUTER_WRAPPED_TOOL, {
12148
- value: true,
12149
- enumerable: false,
12150
- configurable: false
12151
- });
12152
- return wrappedTool;
12153
- }
12154
- function isWrappedTool(tool) {
12155
- return Boolean(tool[OPENROUTER_WRAPPED_TOOL]);
13507
+ return await withCurrent(
13508
+ args.parentSpan,
13509
+ () => openRouterAgentChannels.callModelTurn.tracePromise(args.fn, context)
13510
+ );
12156
13511
  }
12157
13512
  function isWrappedCallModelResult(value) {
12158
13513
  return Boolean(
12159
13514
  isObject(value) && value[OPENROUTER_WRAPPED_CALL_MODEL_RESULT]
12160
13515
  );
12161
13516
  }
12162
- function traceToolExecution(args) {
12163
- const tracingChannel = openRouterChannels.toolExecute.tracingChannel();
12164
- const input = args.args.length > 0 ? args.args[0] : void 0;
12165
- const event = {
12166
- arguments: [input],
12167
- span_info: {
12168
- name: args.toolName
12169
- },
12170
- toolCallId: args.toolCallId,
12171
- toolName: args.toolName
12172
- };
12173
- tracingChannel.start.publish(event);
12174
- try {
12175
- const result = args.execute();
12176
- return publishToolResult(tracingChannel, event, result);
12177
- } catch (error) {
12178
- event.error = normalizeError(error);
12179
- tracingChannel.error.publish(event);
12180
- throw error;
12181
- }
12182
- }
12183
- function publishToolResult(tracingChannel, event, result) {
12184
- if (isPromiseLike(result)) {
12185
- return result.then(
12186
- (resolved) => {
12187
- event.result = resolved;
12188
- tracingChannel.asyncEnd.publish(event);
12189
- return resolved;
12190
- },
12191
- (error) => {
12192
- event.error = normalizeError(error);
12193
- tracingChannel.error.publish(event);
12194
- throw error;
12195
- }
12196
- );
12197
- }
12198
- event.result = result;
12199
- tracingChannel.asyncEnd.publish(event);
12200
- return result;
12201
- }
12202
- function getToolCallId(context) {
12203
- const toolContext = context;
12204
- return typeof toolContext?.toolCall?.id === "string" ? toolContext.toolCall.id : void 0;
12205
- }
12206
13517
  function extractOpenRouterCallModelResultMetadata(response, turnCount) {
12207
13518
  const combined = {
12208
13519
  ...extractOpenRouterResponseMetadata(response) || {},
@@ -12247,45 +13558,6 @@ function buildNextOpenRouterCallModelInput(currentInput, response, toolResults)
12247
13558
  (entry) => sanitizeOpenRouterLoggedValue(entry)
12248
13559
  );
12249
13560
  }
12250
- function startOpenRouterCallModelTurnSpan(args) {
12251
- const requestRecord = isObject(args.request) ? args.request : void 0;
12252
- const metadata = requestRecord ? extractOpenRouterCallModelMetadata(requestRecord) : { provider: "openrouter" };
12253
- if (isObject(metadata) && "tools" in metadata) {
12254
- delete metadata.tools;
12255
- }
12256
- return startSpan({
12257
- name: "openrouter.beta.responses.send",
12258
- spanAttributes: {
12259
- type: "llm" /* LLM */
12260
- },
12261
- event: {
12262
- input: requestRecord ? extractOpenRouterCallModelInput(requestRecord) : void 0,
12263
- metadata: {
12264
- ...metadata,
12265
- step: args.step,
12266
- step_type: args.stepType
12267
- }
12268
- }
12269
- });
12270
- }
12271
- function finishOpenRouterCallModelTurnSpan(args) {
12272
- if (!isObject(args.response)) {
12273
- args.span.end();
12274
- return;
12275
- }
12276
- args.span.log({
12277
- output: extractOpenRouterResponseOutput(args.response),
12278
- ...extractOpenRouterResponseMetadata(args.response) ? {
12279
- metadata: {
12280
- ...extractOpenRouterResponseMetadata(args.response),
12281
- ...args.step !== void 0 ? { step: args.step } : {},
12282
- ...args.stepType ? { step_type: args.stepType } : {}
12283
- }
12284
- } : {},
12285
- metrics: parseOpenRouterMetricsFromUsage(args.response.usage)
12286
- });
12287
- args.span.end();
12288
- }
12289
13561
  function getOpenRouterResolvedRequest(result, request) {
12290
13562
  if (isObject(result.resolvedRequest)) {
12291
13563
  return result.resolvedRequest;
@@ -12360,16 +13632,41 @@ function wrapAsyncIterableWithSpan(args) {
12360
13632
  }
12361
13633
  };
12362
13634
  }
12363
- function isAsyncIterable2(value) {
13635
+ function isAsyncIterable3(value) {
12364
13636
  return !!value && (typeof value === "object" || typeof value === "function") && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
12365
13637
  }
12366
- function isPromiseLike(value) {
12367
- return !!value && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
12368
- }
12369
13638
  function normalizeError(error) {
12370
13639
  return error instanceof Error ? error : new Error(String(error));
12371
13640
  }
12372
13641
 
13642
+ // src/instrumentation/plugins/openrouter-channels.ts
13643
+ var openRouterChannels = defineChannels("@openrouter/sdk", {
13644
+ chatSend: channel({
13645
+ channelName: "chat.send",
13646
+ kind: "async"
13647
+ }),
13648
+ embeddingsGenerate: channel({
13649
+ channelName: "embeddings.generate",
13650
+ kind: "async"
13651
+ }),
13652
+ betaResponsesSend: channel({
13653
+ channelName: "beta.responses.send",
13654
+ kind: "async"
13655
+ }),
13656
+ callModel: channel({
13657
+ channelName: "callModel",
13658
+ kind: "sync-stream"
13659
+ }),
13660
+ callModelTurn: channel({
13661
+ channelName: "callModel.turn",
13662
+ kind: "async"
13663
+ }),
13664
+ toolExecute: channel({
13665
+ channelName: "tool.execute",
13666
+ kind: "async"
13667
+ })
13668
+ });
13669
+
12373
13670
  // src/instrumentation/plugins/openrouter-plugin.ts
12374
13671
  var OpenRouterPlugin = class extends BasePlugin {
12375
13672
  onEnable() {
@@ -12391,14 +13688,14 @@ var OpenRouterPlugin = class extends BasePlugin {
12391
13688
  const { messages, ...metadata } = chatGenerationParams;
12392
13689
  return {
12393
13690
  input: messages,
12394
- metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
13691
+ metadata: buildOpenRouterMetadata2(metadata, httpReferer, xTitle)
12395
13692
  };
12396
13693
  },
12397
13694
  extractOutput: (result) => {
12398
13695
  return isObject(result) ? result.choices : void 0;
12399
13696
  },
12400
13697
  extractMetrics: (result, startTime) => {
12401
- const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
13698
+ const metrics = parseOpenRouterMetricsFromUsage2(result?.usage);
12402
13699
  if (startTime) {
12403
13700
  metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
12404
13701
  }
@@ -12437,10 +13734,10 @@ var OpenRouterPlugin = class extends BasePlugin {
12437
13734
  if (!isObject(result)) {
12438
13735
  return void 0;
12439
13736
  }
12440
- return extractOpenRouterResponseMetadata(result);
13737
+ return extractOpenRouterResponseMetadata2(result);
12441
13738
  },
12442
13739
  extractMetrics: (result) => {
12443
- return isObject(result) ? parseOpenRouterMetricsFromUsage(result.usage) : {};
13740
+ return isObject(result) ? parseOpenRouterMetricsFromUsage2(result.usage) : {};
12444
13741
  }
12445
13742
  })
12446
13743
  );
@@ -12456,13 +13753,13 @@ var OpenRouterPlugin = class extends BasePlugin {
12456
13753
  const { input, ...metadata } = openResponsesRequest;
12457
13754
  return {
12458
13755
  input,
12459
- metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
13756
+ metadata: buildOpenRouterMetadata2(metadata, httpReferer, xTitle)
12460
13757
  };
12461
13758
  },
12462
- extractOutput: (result) => extractOpenRouterResponseOutput(result),
12463
- extractMetadata: (result) => extractOpenRouterResponseMetadata(result),
13759
+ extractOutput: (result) => extractOpenRouterResponseOutput2(result),
13760
+ extractMetadata: (result) => extractOpenRouterResponseMetadata2(result),
12464
13761
  extractMetrics: (result, startTime) => {
12465
- const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
13762
+ const metrics = parseOpenRouterMetricsFromUsage2(result?.usage);
12466
13763
  if (startTime) {
12467
13764
  metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
12468
13765
  }
@@ -12476,21 +13773,57 @@ var OpenRouterPlugin = class extends BasePlugin {
12476
13773
  name: "openrouter.callModel",
12477
13774
  type: "llm" /* LLM */,
12478
13775
  extractInput: (args) => {
12479
- const request = getOpenRouterCallModelRequestArg(args);
13776
+ const request = getOpenRouterCallModelRequestArg2(args);
12480
13777
  return {
12481
- input: request ? extractOpenRouterCallModelInput(request) : void 0,
12482
- metadata: request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" }
13778
+ input: request ? extractOpenRouterCallModelInput2(request) : void 0,
13779
+ metadata: request ? extractOpenRouterCallModelMetadata2(request) : { provider: "openrouter" }
12483
13780
  };
12484
13781
  },
12485
13782
  patchResult: ({ endEvent, result, span }) => {
12486
- return patchOpenRouterCallModelResult(
12487
- span,
13783
+ return patchOpenRouterCallModelResult2({
13784
+ request: getOpenRouterCallModelRequestArg2(endEvent.arguments),
12488
13785
  result,
12489
- getOpenRouterCallModelRequestArg(endEvent.arguments)
12490
- );
13786
+ span
13787
+ });
12491
13788
  }
12492
13789
  })
12493
13790
  );
13791
+ this.unsubscribers.push(
13792
+ traceAsyncChannel(openRouterChannels.callModelTurn, {
13793
+ name: "openrouter.beta.responses.send",
13794
+ type: "llm" /* LLM */,
13795
+ extractInput: (args, event) => {
13796
+ const request = getOpenRouterCallModelRequestArg2(args);
13797
+ const metadata = request ? extractOpenRouterCallModelMetadata2(request) : { provider: "openrouter" };
13798
+ if (isObject(metadata) && "tools" in metadata) {
13799
+ delete metadata.tools;
13800
+ }
13801
+ return {
13802
+ input: request ? extractOpenRouterCallModelInput2(request) : void 0,
13803
+ metadata: {
13804
+ ...metadata,
13805
+ step: event.step,
13806
+ step_type: event.stepType
13807
+ }
13808
+ };
13809
+ },
13810
+ extractOutput: (result) => extractOpenRouterResponseOutput2(result),
13811
+ extractMetadata: (result, event) => {
13812
+ if (!isObject(result)) {
13813
+ return {
13814
+ step: event?.step,
13815
+ step_type: event?.stepType
13816
+ };
13817
+ }
13818
+ return {
13819
+ ...extractOpenRouterResponseMetadata2(result) || {},
13820
+ ...event?.step !== void 0 ? { step: event.step } : {},
13821
+ ...event?.stepType ? { step_type: event.stepType } : {}
13822
+ };
13823
+ },
13824
+ extractMetrics: (result) => isObject(result) ? parseOpenRouterMetricsFromUsage2(result.usage) : {}
13825
+ })
13826
+ );
12494
13827
  this.unsubscribers.push(
12495
13828
  traceStreamingChannel(openRouterChannels.toolExecute, {
12496
13829
  name: "openrouter.tool",
@@ -12514,11 +13847,11 @@ var OpenRouterPlugin = class extends BasePlugin {
12514
13847
  const callModelChannel = openRouterChannels.callModel.tracingChannel();
12515
13848
  const callModelHandlers = {
12516
13849
  start: (event) => {
12517
- const request = getOpenRouterCallModelRequestArg(event.arguments);
13850
+ const request = getOpenRouterCallModelRequestArg2(event.arguments);
12518
13851
  if (!request) {
12519
13852
  return;
12520
13853
  }
12521
- patchOpenRouterCallModelRequestTools(request);
13854
+ patchOpenRouterCallModelRequestTools2(request);
12522
13855
  }
12523
13856
  };
12524
13857
  callModelChannel.subscribe(callModelHandlers);
@@ -12527,20 +13860,20 @@ var OpenRouterPlugin = class extends BasePlugin {
12527
13860
  });
12528
13861
  }
12529
13862
  };
12530
- function normalizeArgs(args) {
13863
+ function normalizeArgs2(args) {
12531
13864
  if (Array.isArray(args)) {
12532
13865
  return args;
12533
13866
  }
12534
- if (isArrayLike(args)) {
13867
+ if (isArrayLike2(args)) {
12535
13868
  return Array.from(args);
12536
13869
  }
12537
13870
  return [args];
12538
13871
  }
12539
- function isArrayLike(value) {
13872
+ function isArrayLike2(value) {
12540
13873
  return isObject(value) && "length" in value && typeof value.length === "number" && Number.isInteger(value.length) && value.length >= 0;
12541
13874
  }
12542
13875
  function getOpenRouterRequestArg(args) {
12543
- const normalizedArgs = normalizeArgs(args);
13876
+ const normalizedArgs = normalizeArgs2(args);
12544
13877
  const keyedCandidate = normalizedArgs.find(
12545
13878
  (arg) => isObject(arg) && ("chatGenerationParams" in arg || "requestBody" in arg || "openResponsesRequest" in arg)
12546
13879
  );
@@ -12550,116 +13883,1264 @@ function getOpenRouterRequestArg(args) {
12550
13883
  const firstObjectArg = normalizedArgs.find((arg) => isObject(arg));
12551
13884
  return isObject(firstObjectArg) ? firstObjectArg : void 0;
12552
13885
  }
12553
- function getOpenRouterCallModelRequestArg(args) {
12554
- const firstObjectArg = normalizeArgs(args).find((arg) => isObject(arg));
13886
+ function getOpenRouterCallModelRequestArg2(args) {
13887
+ const firstObjectArg = normalizeArgs2(args).find((arg) => isObject(arg));
12555
13888
  return isObject(firstObjectArg) ? firstObjectArg : void 0;
12556
13889
  }
12557
- function aggregateOpenRouterChatChunks(chunks) {
12558
- let role;
12559
- let content = "";
12560
- let toolCalls;
12561
- let finishReason;
12562
- let metrics = {};
12563
- for (const chunk of chunks) {
12564
- metrics = {
12565
- ...metrics,
12566
- ...parseOpenRouterMetricsFromUsage(chunk?.usage)
12567
- };
12568
- const choice = chunk?.choices?.[0];
12569
- const delta = choice?.delta;
12570
- if (!delta) {
12571
- if (choice?.finish_reason !== void 0) {
12572
- finishReason = choice.finish_reason;
12573
- }
13890
+ var TOKEN_NAME_MAP3 = {
13891
+ promptTokens: "prompt_tokens",
13892
+ inputTokens: "prompt_tokens",
13893
+ completionTokens: "completion_tokens",
13894
+ outputTokens: "completion_tokens",
13895
+ totalTokens: "tokens",
13896
+ prompt_tokens: "prompt_tokens",
13897
+ input_tokens: "prompt_tokens",
13898
+ completion_tokens: "completion_tokens",
13899
+ output_tokens: "completion_tokens",
13900
+ total_tokens: "tokens"
13901
+ };
13902
+ var TOKEN_DETAIL_PREFIX_MAP2 = {
13903
+ promptTokensDetails: "prompt",
13904
+ inputTokensDetails: "prompt",
13905
+ completionTokensDetails: "completion",
13906
+ outputTokensDetails: "completion",
13907
+ costDetails: "cost",
13908
+ prompt_tokens_details: "prompt",
13909
+ input_tokens_details: "prompt",
13910
+ completion_tokens_details: "completion",
13911
+ output_tokens_details: "completion",
13912
+ cost_details: "cost"
13913
+ };
13914
+ function camelToSnake2(value) {
13915
+ return value.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`);
13916
+ }
13917
+ function parseOpenRouterMetricsFromUsage2(usage) {
13918
+ if (!isObject(usage)) {
13919
+ return {};
13920
+ }
13921
+ const metrics = {};
13922
+ for (const [name, value] of Object.entries(usage)) {
13923
+ if (typeof value === "number") {
13924
+ metrics[TOKEN_NAME_MAP3[name] || camelToSnake2(name)] = value;
12574
13925
  continue;
12575
13926
  }
12576
- if (!role && delta.role) {
12577
- role = delta.role;
12578
- }
12579
- if (typeof delta.content === "string") {
12580
- content += delta.content;
12581
- }
12582
- const choiceFinishReason = choice?.finishReason ?? choice?.finish_reason ?? void 0;
12583
- const deltaFinishReason = delta.finishReason ?? delta.finish_reason ?? void 0;
12584
- if (choiceFinishReason !== void 0) {
12585
- finishReason = choiceFinishReason;
12586
- } else if (deltaFinishReason !== void 0) {
12587
- finishReason = deltaFinishReason;
13927
+ if (!isObject(value)) {
13928
+ continue;
12588
13929
  }
12589
- const toolCallDeltas = Array.isArray(delta.toolCalls) ? delta.toolCalls : Array.isArray(delta.tool_calls) ? delta.tool_calls : void 0;
12590
- if (!toolCallDeltas) {
13930
+ const prefix = TOKEN_DETAIL_PREFIX_MAP2[name];
13931
+ if (!prefix) {
12591
13932
  continue;
12592
13933
  }
12593
- for (const toolDelta of toolCallDeltas) {
12594
- if (!toolDelta?.function) {
12595
- continue;
12596
- }
12597
- const toolIndex = toolDelta.index ?? 0;
12598
- const existingToolCall = toolCalls?.[toolIndex];
12599
- if (!existingToolCall || toolDelta.id && existingToolCall.id !== void 0 && existingToolCall.id !== toolDelta.id) {
12600
- const nextToolCalls = [...toolCalls || []];
12601
- nextToolCalls[toolIndex] = {
12602
- index: toolIndex,
12603
- id: toolDelta.id,
12604
- type: toolDelta.type,
12605
- function: {
12606
- name: toolDelta.function.name,
12607
- arguments: toolDelta.function.arguments || ""
12608
- }
12609
- };
12610
- toolCalls = nextToolCalls;
13934
+ for (const [nestedName, nestedValue] of Object.entries(value)) {
13935
+ if (typeof nestedValue !== "number") {
12611
13936
  continue;
12612
13937
  }
12613
- const current = existingToolCall;
12614
- if (toolDelta.id && !current.id) {
12615
- current.id = toolDelta.id;
12616
- }
12617
- if (toolDelta.type && !current.type) {
12618
- current.type = toolDelta.type;
12619
- }
12620
- if (toolDelta.function.name && !current.function.name) {
12621
- current.function.name = toolDelta.function.name;
12622
- }
12623
- current.function.arguments += toolDelta.function.arguments || "";
13938
+ metrics[`${prefix}_${camelToSnake2(nestedName)}`] = nestedValue;
12624
13939
  }
12625
13940
  }
12626
- return {
12627
- output: [
12628
- {
12629
- index: 0,
12630
- message: {
12631
- role,
12632
- content: content || void 0,
12633
- ...toolCalls ? { tool_calls: toolCalls } : {}
12634
- },
12635
- logprobs: null,
12636
- finish_reason: finishReason
12637
- }
12638
- ],
12639
- metrics
12640
- };
13941
+ return metrics;
12641
13942
  }
12642
- function aggregateOpenRouterResponseStreamEvents(chunks) {
12643
- let finalResponse;
12644
- for (const chunk of chunks) {
12645
- const response = chunk?.response;
12646
- if (!response) {
13943
+ function extractOpenRouterUsageMetadata2(usage) {
13944
+ if (!isObject(usage)) {
13945
+ return void 0;
13946
+ }
13947
+ const metadata = {};
13948
+ if (typeof usage.isByok === "boolean") {
13949
+ metadata.is_byok = usage.isByok;
13950
+ } else if (typeof usage.is_byok === "boolean") {
13951
+ metadata.is_byok = usage.is_byok;
13952
+ }
13953
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
13954
+ }
13955
+ var OMITTED_OPENROUTER_KEYS2 = /* @__PURE__ */ new Set([
13956
+ "execute",
13957
+ "render",
13958
+ "nextTurnParams",
13959
+ "requireApproval"
13960
+ ]);
13961
+ function parseOpenRouterModelString2(model) {
13962
+ if (typeof model !== "string") {
13963
+ return { model };
13964
+ }
13965
+ const slashIndex = model.indexOf("/");
13966
+ if (slashIndex > 0 && slashIndex < model.length - 1) {
13967
+ return {
13968
+ provider: model.substring(0, slashIndex),
13969
+ model: model.substring(slashIndex + 1)
13970
+ };
13971
+ }
13972
+ return { model };
13973
+ }
13974
+ function isZodSchema4(value) {
13975
+ return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
13976
+ }
13977
+ function serializeZodSchema4(schema) {
13978
+ try {
13979
+ return zodToJsonSchema(schema);
13980
+ } catch {
13981
+ return {
13982
+ type: "object",
13983
+ description: "Zod schema (conversion failed)"
13984
+ };
13985
+ }
13986
+ }
13987
+ function serializeOpenRouterTool2(tool) {
13988
+ if (!isObject(tool)) {
13989
+ return tool;
13990
+ }
13991
+ const serialized = {};
13992
+ for (const [key, value] of Object.entries(tool)) {
13993
+ if (OMITTED_OPENROUTER_KEYS2.has(key)) {
13994
+ continue;
13995
+ }
13996
+ if (key === "function" && isObject(value)) {
13997
+ serialized.function = sanitizeOpenRouterLoggedValue2(value);
13998
+ continue;
13999
+ }
14000
+ serialized[key] = sanitizeOpenRouterLoggedValue2(value);
14001
+ }
14002
+ return serialized;
14003
+ }
14004
+ function serializeOpenRouterToolsForLogging2(tools) {
14005
+ if (!Array.isArray(tools)) {
14006
+ return void 0;
14007
+ }
14008
+ return tools.map((tool) => serializeOpenRouterTool2(tool));
14009
+ }
14010
+ function sanitizeOpenRouterLoggedValue2(value) {
14011
+ if (isZodSchema4(value)) {
14012
+ return serializeZodSchema4(value);
14013
+ }
14014
+ if (typeof value === "function") {
14015
+ return "[Function]";
14016
+ }
14017
+ if (Array.isArray(value)) {
14018
+ return value.map((entry) => sanitizeOpenRouterLoggedValue2(entry));
14019
+ }
14020
+ if (!isObject(value)) {
14021
+ return value;
14022
+ }
14023
+ const sanitized = {};
14024
+ for (const [key, entry] of Object.entries(value)) {
14025
+ if (OMITTED_OPENROUTER_KEYS2.has(key)) {
14026
+ continue;
14027
+ }
14028
+ if (key === "tools" && Array.isArray(entry)) {
14029
+ sanitized.tools = serializeOpenRouterToolsForLogging2(entry);
14030
+ continue;
14031
+ }
14032
+ sanitized[key] = sanitizeOpenRouterLoggedValue2(entry);
14033
+ }
14034
+ return sanitized;
14035
+ }
14036
+ function buildOpenRouterMetadata2(metadata, httpReferer, xTitle) {
14037
+ const sanitized = sanitizeOpenRouterLoggedValue2(metadata);
14038
+ const metadataRecord = isObject(sanitized) ? sanitized : {};
14039
+ const { model, provider: providerRouting, ...rest } = metadataRecord;
14040
+ const normalizedModel = parseOpenRouterModelString2(model);
14041
+ return {
14042
+ ...rest,
14043
+ ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
14044
+ ...providerRouting !== void 0 ? { providerRouting } : {},
14045
+ ...httpReferer !== void 0 ? { httpReferer } : {},
14046
+ ...xTitle !== void 0 ? { xTitle } : {},
14047
+ provider: normalizedModel.provider || "openrouter"
14048
+ };
14049
+ }
14050
+ function buildOpenRouterEmbeddingMetadata(metadata, httpReferer, xTitle) {
14051
+ const normalized = buildOpenRouterMetadata2(metadata, httpReferer, xTitle);
14052
+ return typeof normalized.model === "string" ? {
14053
+ ...normalized,
14054
+ embedding_model: normalized.model
14055
+ } : normalized;
14056
+ }
14057
+ function extractOpenRouterCallModelInput2(request) {
14058
+ return isObject(request) && "input" in request ? sanitizeOpenRouterLoggedValue2(request.input) : void 0;
14059
+ }
14060
+ function extractOpenRouterCallModelMetadata2(request) {
14061
+ if (!isObject(request)) {
14062
+ return { provider: "openrouter" };
14063
+ }
14064
+ const { input: _input, ...metadata } = request;
14065
+ return buildOpenRouterMetadata2(metadata, void 0, void 0);
14066
+ }
14067
+ function extractOpenRouterResponseMetadata2(result) {
14068
+ if (!isObject(result)) {
14069
+ return void 0;
14070
+ }
14071
+ const { output: _output, data: _data, usage, ...metadata } = result;
14072
+ const sanitized = sanitizeOpenRouterLoggedValue2(metadata);
14073
+ const metadataRecord = isObject(sanitized) ? sanitized : {};
14074
+ const { model, provider, ...rest } = metadataRecord;
14075
+ const normalizedModel = parseOpenRouterModelString2(model);
14076
+ const normalizedProvider = (typeof provider === "string" ? provider : void 0) || normalizedModel.provider;
14077
+ const usageMetadata = extractOpenRouterUsageMetadata2(usage);
14078
+ const combined = {
14079
+ ...rest,
14080
+ ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
14081
+ ...usageMetadata || {},
14082
+ ...normalizedProvider !== void 0 ? { provider: normalizedProvider } : {}
14083
+ };
14084
+ return Object.keys(combined).length > 0 ? combined : void 0;
14085
+ }
14086
+ function extractOpenRouterResponseOutput2(response, fallbackOutput) {
14087
+ if (isObject(response) && "output" in response && response.output !== void 0) {
14088
+ return sanitizeOpenRouterLoggedValue2(response.output);
14089
+ }
14090
+ if (fallbackOutput !== void 0) {
14091
+ return sanitizeOpenRouterLoggedValue2(fallbackOutput);
14092
+ }
14093
+ return void 0;
14094
+ }
14095
+ var OPENROUTER_WRAPPED_TOOL2 = Symbol("braintrust.openrouter.wrappedTool");
14096
+ function patchOpenRouterCallModelRequestTools2(request) {
14097
+ if (!Array.isArray(request.tools) || request.tools.length === 0) {
14098
+ return void 0;
14099
+ }
14100
+ const originalTools = request.tools;
14101
+ const wrappedTools = originalTools.map((tool) => wrapOpenRouterTool2(tool));
14102
+ const didPatch = wrappedTools.some(
14103
+ (tool, index) => tool !== originalTools[index]
14104
+ );
14105
+ if (!didPatch) {
14106
+ return void 0;
14107
+ }
14108
+ request.tools = wrappedTools;
14109
+ return () => {
14110
+ request.tools = originalTools;
14111
+ };
14112
+ }
14113
+ function wrapOpenRouterTool2(tool) {
14114
+ if (isWrappedTool2(tool) || !tool.function || typeof tool.function !== "object" || typeof tool.function.execute !== "function") {
14115
+ return tool;
14116
+ }
14117
+ const toolName = tool.function.name || "tool";
14118
+ const originalExecute = tool.function.execute;
14119
+ const wrappedTool = {
14120
+ ...tool,
14121
+ function: {
14122
+ ...tool.function,
14123
+ execute(...args) {
14124
+ return traceToolExecution2({
14125
+ args,
14126
+ execute: () => Reflect.apply(originalExecute, this, args),
14127
+ toolCallId: getToolCallId2(args[1]),
14128
+ toolName
14129
+ });
14130
+ }
14131
+ }
14132
+ };
14133
+ Object.defineProperty(wrappedTool, OPENROUTER_WRAPPED_TOOL2, {
14134
+ value: true,
14135
+ enumerable: false,
14136
+ configurable: false
14137
+ });
14138
+ return wrappedTool;
14139
+ }
14140
+ function isWrappedTool2(tool) {
14141
+ return Boolean(tool[OPENROUTER_WRAPPED_TOOL2]);
14142
+ }
14143
+ function traceToolExecution2(args) {
14144
+ const tracingChannel = openRouterChannels.toolExecute.tracingChannel();
14145
+ const input = args.args.length > 0 ? args.args[0] : void 0;
14146
+ const event = {
14147
+ arguments: [input],
14148
+ span_info: {
14149
+ name: args.toolName
14150
+ },
14151
+ toolCallId: args.toolCallId,
14152
+ toolName: args.toolName
14153
+ };
14154
+ tracingChannel.start.publish(event);
14155
+ try {
14156
+ const result = args.execute();
14157
+ return publishToolResult2(tracingChannel, event, result);
14158
+ } catch (error) {
14159
+ event.error = normalizeError2(error);
14160
+ tracingChannel.error.publish(event);
14161
+ throw error;
14162
+ }
14163
+ }
14164
+ function publishToolResult2(tracingChannel, event, result) {
14165
+ if (isPromiseLike4(result)) {
14166
+ return result.then(
14167
+ (resolved) => {
14168
+ event.result = resolved;
14169
+ tracingChannel.asyncEnd.publish(event);
14170
+ return resolved;
14171
+ },
14172
+ (error) => {
14173
+ event.error = normalizeError2(error);
14174
+ tracingChannel.error.publish(event);
14175
+ throw error;
14176
+ }
14177
+ );
14178
+ }
14179
+ event.result = result;
14180
+ tracingChannel.asyncEnd.publish(event);
14181
+ return result;
14182
+ }
14183
+ function getToolCallId2(context) {
14184
+ const toolContext = context;
14185
+ return typeof toolContext?.toolCall?.id === "string" ? toolContext.toolCall.id : void 0;
14186
+ }
14187
+ function isPromiseLike4(value) {
14188
+ return !!value && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
14189
+ }
14190
+ function aggregateOpenRouterChatChunks(chunks) {
14191
+ let role;
14192
+ let content = "";
14193
+ let toolCalls;
14194
+ let finishReason;
14195
+ let metrics = {};
14196
+ for (const chunk of chunks) {
14197
+ metrics = {
14198
+ ...metrics,
14199
+ ...parseOpenRouterMetricsFromUsage2(chunk?.usage)
14200
+ };
14201
+ const choice = chunk?.choices?.[0];
14202
+ const delta = choice?.delta;
14203
+ if (!delta) {
14204
+ if (choice?.finish_reason !== void 0) {
14205
+ finishReason = choice.finish_reason;
14206
+ }
14207
+ continue;
14208
+ }
14209
+ if (!role && delta.role) {
14210
+ role = delta.role;
14211
+ }
14212
+ if (typeof delta.content === "string") {
14213
+ content += delta.content;
14214
+ }
14215
+ const choiceFinishReason = choice?.finishReason ?? choice?.finish_reason ?? void 0;
14216
+ const deltaFinishReason = delta.finishReason ?? delta.finish_reason ?? void 0;
14217
+ if (choiceFinishReason !== void 0) {
14218
+ finishReason = choiceFinishReason;
14219
+ } else if (deltaFinishReason !== void 0) {
14220
+ finishReason = deltaFinishReason;
14221
+ }
14222
+ const toolCallDeltas = Array.isArray(delta.toolCalls) ? delta.toolCalls : Array.isArray(delta.tool_calls) ? delta.tool_calls : void 0;
14223
+ if (!toolCallDeltas) {
14224
+ continue;
14225
+ }
14226
+ for (const toolDelta of toolCallDeltas) {
14227
+ if (!toolDelta?.function) {
14228
+ continue;
14229
+ }
14230
+ const toolIndex = toolDelta.index ?? 0;
14231
+ const existingToolCall = toolCalls?.[toolIndex];
14232
+ if (!existingToolCall || toolDelta.id && existingToolCall.id !== void 0 && existingToolCall.id !== toolDelta.id) {
14233
+ const nextToolCalls = [...toolCalls || []];
14234
+ nextToolCalls[toolIndex] = {
14235
+ index: toolIndex,
14236
+ id: toolDelta.id,
14237
+ type: toolDelta.type,
14238
+ function: {
14239
+ name: toolDelta.function.name,
14240
+ arguments: toolDelta.function.arguments || ""
14241
+ }
14242
+ };
14243
+ toolCalls = nextToolCalls;
14244
+ continue;
14245
+ }
14246
+ const current = existingToolCall;
14247
+ if (toolDelta.id && !current.id) {
14248
+ current.id = toolDelta.id;
14249
+ }
14250
+ if (toolDelta.type && !current.type) {
14251
+ current.type = toolDelta.type;
14252
+ }
14253
+ if (toolDelta.function.name && !current.function.name) {
14254
+ current.function.name = toolDelta.function.name;
14255
+ }
14256
+ current.function.arguments += toolDelta.function.arguments || "";
14257
+ }
14258
+ }
14259
+ return {
14260
+ output: [
14261
+ {
14262
+ index: 0,
14263
+ message: {
14264
+ role,
14265
+ content: content || void 0,
14266
+ ...toolCalls ? { tool_calls: toolCalls } : {}
14267
+ },
14268
+ logprobs: null,
14269
+ finish_reason: finishReason
14270
+ }
14271
+ ],
14272
+ metrics
14273
+ };
14274
+ }
14275
+ function aggregateOpenRouterResponseStreamEvents(chunks) {
14276
+ let finalResponse;
14277
+ for (const chunk of chunks) {
14278
+ const response = chunk?.response;
14279
+ if (!response) {
14280
+ continue;
14281
+ }
14282
+ if (chunk.type === "response.completed" || chunk.type === "response.incomplete" || chunk.type === "response.failed") {
14283
+ finalResponse = response;
14284
+ }
14285
+ }
14286
+ if (!finalResponse) {
14287
+ return {
14288
+ output: void 0,
14289
+ metrics: {}
14290
+ };
14291
+ }
14292
+ return {
14293
+ output: extractOpenRouterResponseOutput2(finalResponse),
14294
+ metrics: parseOpenRouterMetricsFromUsage2(finalResponse.usage),
14295
+ ...extractOpenRouterResponseMetadata2(finalResponse) ? { metadata: extractOpenRouterResponseMetadata2(finalResponse) } : {}
14296
+ };
14297
+ }
14298
+ var OPENROUTER_WRAPPED_CALL_MODEL_RESULT2 = Symbol(
14299
+ "braintrust.openrouter.wrappedCallModelResult"
14300
+ );
14301
+ var OPENROUTER_CALL_MODEL_STREAM_METHODS2 = [
14302
+ "getFullResponsesStream",
14303
+ "getItemsStream",
14304
+ "getNewMessagesStream",
14305
+ "getReasoningStream",
14306
+ "getTextStream",
14307
+ "getToolCallsStream",
14308
+ "getToolStream"
14309
+ ];
14310
+ var OPENROUTER_CALL_MODEL_CONTEXT_METHODS2 = [
14311
+ "cancel",
14312
+ "getPendingToolCalls",
14313
+ "getState",
14314
+ "getToolCalls",
14315
+ "requiresApproval"
14316
+ ];
14317
+ function patchOpenRouterCallModelResult2(args) {
14318
+ const { request, result, span } = args;
14319
+ if (!isObject(result) || isWrappedCallModelResult2(result)) {
14320
+ return false;
14321
+ }
14322
+ const resultLike = result;
14323
+ const hasInstrumentableMethod = typeof resultLike.getResponse === "function" || typeof resultLike.getText === "function" || OPENROUTER_CALL_MODEL_STREAM_METHODS2.some(
14324
+ (methodName) => typeof resultLike[methodName] === "function"
14325
+ );
14326
+ if (!hasInstrumentableMethod) {
14327
+ return false;
14328
+ }
14329
+ Object.defineProperty(resultLike, OPENROUTER_WRAPPED_CALL_MODEL_RESULT2, {
14330
+ value: true,
14331
+ enumerable: false,
14332
+ configurable: false
14333
+ });
14334
+ const originalGetResponse = typeof resultLike.getResponse === "function" ? resultLike.getResponse.bind(resultLike) : void 0;
14335
+ const originalGetInitialResponse = typeof resultLike.getInitialResponse === "function" ? resultLike.getInitialResponse.bind(resultLike) : void 0;
14336
+ const originalMakeFollowupRequest = typeof resultLike.makeFollowupRequest === "function" ? resultLike.makeFollowupRequest.bind(resultLike) : void 0;
14337
+ let ended = false;
14338
+ let tracedTurnCount = 0;
14339
+ const endSpanWithResult = async (response, fallbackOutput) => {
14340
+ if (ended) {
14341
+ return;
14342
+ }
14343
+ ended = true;
14344
+ const finalResponse = getFinalOpenRouterCallModelResponse2(
14345
+ resultLike,
14346
+ response
14347
+ );
14348
+ if (finalResponse) {
14349
+ const rounds = getOpenRouterCallModelRounds2(resultLike);
14350
+ const metadata = extractOpenRouterCallModelResultMetadata2(
14351
+ finalResponse,
14352
+ rounds.length + 1
14353
+ );
14354
+ span.log({
14355
+ output: extractOpenRouterResponseOutput2(finalResponse, fallbackOutput),
14356
+ ...metadata ? { metadata } : {},
14357
+ metrics: aggregateOpenRouterCallModelMetrics2(rounds, finalResponse)
14358
+ });
14359
+ span.end();
14360
+ return;
14361
+ }
14362
+ if (fallbackOutput !== void 0) {
14363
+ span.log({
14364
+ output: fallbackOutput
14365
+ });
14366
+ }
14367
+ span.end();
14368
+ };
14369
+ const endSpanWithError = (error) => {
14370
+ if (ended) {
14371
+ return;
14372
+ }
14373
+ ended = true;
14374
+ span.log({
14375
+ error: normalizeError2(error).message
14376
+ });
14377
+ span.end();
14378
+ };
14379
+ const finalizeFromResponse = async (fallbackOutput) => {
14380
+ if (!originalGetResponse) {
14381
+ await endSpanWithResult(void 0, fallbackOutput);
14382
+ return;
14383
+ }
14384
+ try {
14385
+ await endSpanWithResult(await originalGetResponse(), fallbackOutput);
14386
+ } catch {
14387
+ await endSpanWithResult(void 0, fallbackOutput);
14388
+ }
14389
+ };
14390
+ if (originalGetResponse) {
14391
+ resultLike.getResponse = async (...args2) => {
14392
+ return await withCurrent(span, async () => {
14393
+ try {
14394
+ const response = await originalGetResponse(...args2);
14395
+ await endSpanWithResult(response);
14396
+ return response;
14397
+ } catch (error) {
14398
+ endSpanWithError(error);
14399
+ throw error;
14400
+ }
14401
+ });
14402
+ };
14403
+ }
14404
+ if (typeof resultLike.getText === "function") {
14405
+ const originalGetText = resultLike.getText.bind(resultLike);
14406
+ resultLike.getText = async (...args2) => {
14407
+ return await withCurrent(span, async () => {
14408
+ try {
14409
+ const text = await originalGetText(...args2);
14410
+ await finalizeFromResponse(text);
14411
+ return text;
14412
+ } catch (error) {
14413
+ endSpanWithError(error);
14414
+ throw error;
14415
+ }
14416
+ });
14417
+ };
14418
+ }
14419
+ for (const methodName of OPENROUTER_CALL_MODEL_CONTEXT_METHODS2) {
14420
+ if (typeof resultLike[methodName] !== "function") {
14421
+ continue;
14422
+ }
14423
+ const originalMethod = resultLike[methodName];
14424
+ resultLike[methodName] = async (...args2) => {
14425
+ return await withCurrent(span, async () => {
14426
+ return await originalMethod.apply(resultLike, args2);
14427
+ });
14428
+ };
14429
+ }
14430
+ for (const methodName of OPENROUTER_CALL_MODEL_STREAM_METHODS2) {
14431
+ if (typeof resultLike[methodName] !== "function") {
14432
+ continue;
14433
+ }
14434
+ const originalMethod = resultLike[methodName];
14435
+ resultLike[methodName] = (...args2) => {
14436
+ const stream = withCurrent(
14437
+ span,
14438
+ () => originalMethod.apply(resultLike, args2)
14439
+ );
14440
+ if (!isAsyncIterable4(stream)) {
14441
+ return stream;
14442
+ }
14443
+ return wrapAsyncIterableWithSpan2({
14444
+ finalize: finalizeFromResponse,
14445
+ iteratorFactory: () => stream[Symbol.asyncIterator](),
14446
+ onError: endSpanWithError,
14447
+ span
14448
+ });
14449
+ };
14450
+ }
14451
+ if (originalGetInitialResponse) {
14452
+ let initialTurnTraced = false;
14453
+ resultLike.getInitialResponse = async (...args2) => {
14454
+ if (initialTurnTraced) {
14455
+ return await withCurrent(span, async () => {
14456
+ return await originalGetInitialResponse(...args2);
14457
+ });
14458
+ }
14459
+ initialTurnTraced = true;
14460
+ const step = tracedTurnCount + 1;
14461
+ const stepType = tracedTurnCount === 0 ? "initial" : "continue";
14462
+ const response = await traceOpenRouterCallModelTurn2({
14463
+ fn: async () => {
14464
+ const nextResponse = await originalGetInitialResponse(...args2);
14465
+ tracedTurnCount++;
14466
+ return nextResponse;
14467
+ },
14468
+ parentSpan: span,
14469
+ request: getOpenRouterResolvedRequest2(resultLike, request),
14470
+ step,
14471
+ stepType
14472
+ });
14473
+ return response;
14474
+ };
14475
+ }
14476
+ if (originalMakeFollowupRequest) {
14477
+ resultLike.makeFollowupRequest = async (...args2) => {
14478
+ const currentResponse = args2[0];
14479
+ const toolResults = Array.isArray(args2[1]) ? args2[1] : [];
14480
+ const step = tracedTurnCount + 1;
14481
+ const response = await traceOpenRouterCallModelTurn2({
14482
+ fn: async () => {
14483
+ const nextResponse = await originalMakeFollowupRequest(...args2);
14484
+ tracedTurnCount++;
14485
+ return nextResponse;
14486
+ },
14487
+ parentSpan: span,
14488
+ request: buildOpenRouterFollowupRequest2(
14489
+ getOpenRouterResolvedRequest2(resultLike, request),
14490
+ currentResponse,
14491
+ toolResults
14492
+ ),
14493
+ step,
14494
+ stepType: "continue"
14495
+ });
14496
+ return response;
14497
+ };
14498
+ }
14499
+ return true;
14500
+ }
14501
+ async function traceOpenRouterCallModelTurn2(args) {
14502
+ const context = {
14503
+ arguments: [args.request],
14504
+ step: args.step,
14505
+ stepType: args.stepType
14506
+ };
14507
+ return await withCurrent(
14508
+ args.parentSpan,
14509
+ () => openRouterChannels.callModelTurn.tracePromise(args.fn, context)
14510
+ );
14511
+ }
14512
+ function isWrappedCallModelResult2(value) {
14513
+ return Boolean(
14514
+ isObject(value) && value[OPENROUTER_WRAPPED_CALL_MODEL_RESULT2]
14515
+ );
14516
+ }
14517
+ function extractOpenRouterCallModelResultMetadata2(response, turnCount) {
14518
+ const combined = {
14519
+ ...extractOpenRouterResponseMetadata2(response) || {},
14520
+ ...turnCount !== void 0 ? { turn_count: turnCount } : {}
14521
+ };
14522
+ return Object.keys(combined).length > 0 ? combined : void 0;
14523
+ }
14524
+ function getFinalOpenRouterCallModelResponse2(result, response) {
14525
+ if (isObject(response)) {
14526
+ return response;
14527
+ }
14528
+ return isObject(result.finalResponse) ? result.finalResponse : void 0;
14529
+ }
14530
+ function getOpenRouterCallModelRounds2(result) {
14531
+ if (!Array.isArray(result.allToolExecutionRounds)) {
14532
+ return [];
14533
+ }
14534
+ return result.allToolExecutionRounds.filter((round) => isObject(round)).map((round) => ({
14535
+ response: isObject(round.response) ? round.response : void 0,
14536
+ round: typeof round.round === "number" ? round.round : void 0,
14537
+ toolResults: Array.isArray(round.toolResults) ? round.toolResults : []
14538
+ })).filter((round) => round.response !== void 0);
14539
+ }
14540
+ function aggregateOpenRouterCallModelMetrics2(rounds, finalResponse) {
14541
+ const metrics = {};
14542
+ const responses = [
14543
+ ...rounds.map((round) => round.response).filter(isObject),
14544
+ finalResponse
14545
+ ];
14546
+ for (const response of responses) {
14547
+ const responseMetrics = parseOpenRouterMetricsFromUsage2(response.usage);
14548
+ for (const [name, value] of Object.entries(responseMetrics)) {
14549
+ metrics[name] = (metrics[name] || 0) + value;
14550
+ }
14551
+ }
14552
+ return metrics;
14553
+ }
14554
+ function buildNextOpenRouterCallModelInput2(currentInput, response, toolResults) {
14555
+ const normalizedInput = Array.isArray(currentInput) ? [...currentInput] : currentInput === void 0 ? [] : [currentInput];
14556
+ const responseOutput = Array.isArray(response.output) ? response.output : response.output === void 0 ? [] : [response.output];
14557
+ return [...normalizedInput, ...responseOutput, ...toolResults].map(
14558
+ (entry) => sanitizeOpenRouterLoggedValue2(entry)
14559
+ );
14560
+ }
14561
+ function getOpenRouterResolvedRequest2(result, request) {
14562
+ if (isObject(result.resolvedRequest)) {
14563
+ return result.resolvedRequest;
14564
+ }
14565
+ return request;
14566
+ }
14567
+ function buildOpenRouterFollowupRequest2(request, currentResponse, toolResults) {
14568
+ if (!request) {
14569
+ return void 0;
14570
+ }
14571
+ return {
14572
+ ...request,
14573
+ input: buildNextOpenRouterCallModelInput2(
14574
+ extractOpenRouterCallModelInput2(request),
14575
+ isObject(currentResponse) ? currentResponse : {},
14576
+ toolResults
14577
+ ),
14578
+ stream: false
14579
+ };
14580
+ }
14581
+ function wrapAsyncIterableWithSpan2(args) {
14582
+ return {
14583
+ [Symbol.asyncIterator]() {
14584
+ const iterator = args.iteratorFactory();
14585
+ return {
14586
+ next(value) {
14587
+ return withCurrent(
14588
+ args.span,
14589
+ () => value === void 0 ? iterator.next() : iterator.next(value)
14590
+ ).then(
14591
+ async (result) => {
14592
+ if (result.done) {
14593
+ await args.finalize();
14594
+ }
14595
+ return result;
14596
+ },
14597
+ (error) => {
14598
+ args.onError(error);
14599
+ throw error;
14600
+ }
14601
+ );
14602
+ },
14603
+ return(value) {
14604
+ if (typeof iterator.return !== "function") {
14605
+ return args.finalize().then(() => ({
14606
+ done: true,
14607
+ value
14608
+ }));
14609
+ }
14610
+ return withCurrent(args.span, () => iterator.return(value)).then(
14611
+ async (result) => {
14612
+ await args.finalize();
14613
+ return result;
14614
+ },
14615
+ (error) => {
14616
+ args.onError(error);
14617
+ throw error;
14618
+ }
14619
+ );
14620
+ },
14621
+ throw(error) {
14622
+ args.onError(error);
14623
+ if (typeof iterator.throw !== "function") {
14624
+ return Promise.reject(error);
14625
+ }
14626
+ return withCurrent(args.span, () => iterator.throw(error));
14627
+ },
14628
+ [Symbol.asyncIterator]() {
14629
+ return this;
14630
+ }
14631
+ };
14632
+ }
14633
+ };
14634
+ }
14635
+ function isAsyncIterable4(value) {
14636
+ return !!value && (typeof value === "object" || typeof value === "function") && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
14637
+ }
14638
+ function normalizeError2(error) {
14639
+ return error instanceof Error ? error : new Error(String(error));
14640
+ }
14641
+
14642
+ // src/instrumentation/plugins/mistral-channels.ts
14643
+ var mistralChannels = defineChannels("@mistralai/mistralai", {
14644
+ chatComplete: channel({
14645
+ channelName: "chat.complete",
14646
+ kind: "async"
14647
+ }),
14648
+ chatStream: channel({
14649
+ channelName: "chat.stream",
14650
+ kind: "async"
14651
+ }),
14652
+ embeddingsCreate: channel({
14653
+ channelName: "embeddings.create",
14654
+ kind: "async"
14655
+ }),
14656
+ fimComplete: channel({
14657
+ channelName: "fim.complete",
14658
+ kind: "async"
14659
+ }),
14660
+ fimStream: channel({
14661
+ channelName: "fim.stream",
14662
+ kind: "async"
14663
+ }),
14664
+ agentsComplete: channel({
14665
+ channelName: "agents.complete",
14666
+ kind: "async"
14667
+ }),
14668
+ agentsStream: channel({
14669
+ channelName: "agents.stream",
14670
+ kind: "async"
14671
+ })
14672
+ });
14673
+
14674
+ // src/instrumentation/plugins/mistral-plugin.ts
14675
+ var MistralPlugin = class extends BasePlugin {
14676
+ onEnable() {
14677
+ this.subscribeToMistralChannels();
14678
+ }
14679
+ onDisable() {
14680
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
14681
+ }
14682
+ subscribeToMistralChannels() {
14683
+ this.unsubscribers.push(
14684
+ traceStreamingChannel(mistralChannels.chatComplete, {
14685
+ name: "mistral.chat.complete",
14686
+ type: "llm" /* LLM */,
14687
+ extractInput: extractMessagesInputWithMetadata,
14688
+ extractOutput: (result) => {
14689
+ return result?.choices;
14690
+ },
14691
+ extractMetadata: (result) => extractMistralResponseMetadata(result),
14692
+ extractMetrics: (result, startTime) => extractMistralMetrics(result?.usage, startTime)
14693
+ })
14694
+ );
14695
+ this.unsubscribers.push(
14696
+ traceStreamingChannel(mistralChannels.chatStream, {
14697
+ name: "mistral.chat.stream",
14698
+ type: "llm" /* LLM */,
14699
+ extractInput: extractMessagesInputWithMetadata,
14700
+ extractOutput: extractMistralStreamOutput,
14701
+ extractMetadata: (result) => extractMistralResponseMetadata(result),
14702
+ extractMetrics: (result, startTime) => extractMistralStreamingMetrics(result, startTime),
14703
+ aggregateChunks: aggregateMistralStreamChunks
14704
+ })
14705
+ );
14706
+ this.unsubscribers.push(
14707
+ traceAsyncChannel(mistralChannels.embeddingsCreate, {
14708
+ name: "mistral.embeddings.create",
14709
+ type: "llm" /* LLM */,
14710
+ extractInput: extractEmbeddingInputWithMetadata,
14711
+ extractOutput: (result) => {
14712
+ const embedding = result?.data?.[0]?.embedding;
14713
+ return Array.isArray(embedding) ? { embedding_length: embedding.length } : void 0;
14714
+ },
14715
+ extractMetadata: (result) => extractMistralResponseMetadata(result),
14716
+ extractMetrics: (result) => parseMistralMetricsFromUsage(result?.usage)
14717
+ })
14718
+ );
14719
+ this.unsubscribers.push(
14720
+ traceStreamingChannel(mistralChannels.fimComplete, {
14721
+ name: "mistral.fim.complete",
14722
+ type: "llm" /* LLM */,
14723
+ extractInput: extractPromptInputWithMetadata,
14724
+ extractOutput: (result) => {
14725
+ return result?.choices;
14726
+ },
14727
+ extractMetadata: (result) => extractMistralResponseMetadata(result),
14728
+ extractMetrics: (result, startTime) => extractMistralMetrics(result?.usage, startTime)
14729
+ })
14730
+ );
14731
+ this.unsubscribers.push(
14732
+ traceStreamingChannel(mistralChannels.fimStream, {
14733
+ name: "mistral.fim.stream",
14734
+ type: "llm" /* LLM */,
14735
+ extractInput: extractPromptInputWithMetadata,
14736
+ extractOutput: extractMistralStreamOutput,
14737
+ extractMetadata: (result) => extractMistralResponseMetadata(result),
14738
+ extractMetrics: (result, startTime) => extractMistralStreamingMetrics(result, startTime),
14739
+ aggregateChunks: aggregateMistralStreamChunks
14740
+ })
14741
+ );
14742
+ this.unsubscribers.push(
14743
+ traceStreamingChannel(mistralChannels.agentsComplete, {
14744
+ name: "mistral.agents.complete",
14745
+ type: "llm" /* LLM */,
14746
+ extractInput: extractMessagesInputWithMetadata,
14747
+ extractOutput: (result) => {
14748
+ return result?.choices;
14749
+ },
14750
+ extractMetadata: (result) => extractMistralResponseMetadata(result),
14751
+ extractMetrics: (result, startTime) => extractMistralMetrics(result?.usage, startTime)
14752
+ })
14753
+ );
14754
+ this.unsubscribers.push(
14755
+ traceStreamingChannel(mistralChannels.agentsStream, {
14756
+ name: "mistral.agents.stream",
14757
+ type: "llm" /* LLM */,
14758
+ extractInput: extractMessagesInputWithMetadata,
14759
+ extractOutput: extractMistralStreamOutput,
14760
+ extractMetadata: (result) => extractMistralResponseMetadata(result),
14761
+ extractMetrics: (result, startTime) => extractMistralStreamingMetrics(result, startTime),
14762
+ aggregateChunks: aggregateMistralStreamChunks
14763
+ })
14764
+ );
14765
+ }
14766
+ };
14767
+ var TOKEN_NAME_MAP4 = {
14768
+ promptTokens: "prompt_tokens",
14769
+ inputTokens: "prompt_tokens",
14770
+ completionTokens: "completion_tokens",
14771
+ outputTokens: "completion_tokens",
14772
+ totalTokens: "tokens",
14773
+ prompt_tokens: "prompt_tokens",
14774
+ input_tokens: "prompt_tokens",
14775
+ completion_tokens: "completion_tokens",
14776
+ output_tokens: "completion_tokens",
14777
+ total_tokens: "tokens",
14778
+ promptAudioSeconds: "prompt_audio_seconds",
14779
+ prompt_audio_seconds: "prompt_audio_seconds"
14780
+ };
14781
+ var TOKEN_DETAIL_PREFIX_MAP3 = {
14782
+ promptTokensDetails: "prompt",
14783
+ inputTokensDetails: "prompt",
14784
+ completionTokensDetails: "completion",
14785
+ outputTokensDetails: "completion",
14786
+ prompt_tokens_details: "prompt",
14787
+ input_tokens_details: "prompt",
14788
+ completion_tokens_details: "completion",
14789
+ output_tokens_details: "completion"
14790
+ };
14791
+ var MISTRAL_REQUEST_METADATA_ALLOWLIST = /* @__PURE__ */ new Set([
14792
+ "agentId",
14793
+ "agent_id",
14794
+ "encodingFormat",
14795
+ "encoding_format",
14796
+ "frequencyPenalty",
14797
+ "frequency_penalty",
14798
+ "maxTokens",
14799
+ "max_tokens",
14800
+ "model",
14801
+ "n",
14802
+ "presencePenalty",
14803
+ "presence_penalty",
14804
+ "randomSeed",
14805
+ "random_seed",
14806
+ "responseFormat",
14807
+ "response_format",
14808
+ "safePrompt",
14809
+ "safe_prompt",
14810
+ "stream",
14811
+ "stop",
14812
+ "temperature",
14813
+ "toolChoice",
14814
+ "tool_choice",
14815
+ "topP",
14816
+ "top_p"
14817
+ ]);
14818
+ var MISTRAL_RESPONSE_METADATA_ALLOWLIST = /* @__PURE__ */ new Set([
14819
+ "agentId",
14820
+ "agent_id",
14821
+ "created",
14822
+ "id",
14823
+ "model",
14824
+ "object"
14825
+ ]);
14826
+ function camelToSnake3(value) {
14827
+ return value.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`);
14828
+ }
14829
+ function normalizeArgs3(args) {
14830
+ if (Array.isArray(args)) {
14831
+ return args;
14832
+ }
14833
+ if (isArrayLike3(args)) {
14834
+ return Array.from(args);
14835
+ }
14836
+ return [args];
14837
+ }
14838
+ function isArrayLike3(value) {
14839
+ return isObject(value) && "length" in value && typeof value.length === "number" && Number.isInteger(value.length) && value.length >= 0;
14840
+ }
14841
+ function getMistralRequestArg(args) {
14842
+ const firstObjectArg = normalizeArgs3(args).find((arg) => isObject(arg));
14843
+ return isObject(firstObjectArg) ? firstObjectArg : void 0;
14844
+ }
14845
+ function addMistralProviderMetadata(metadata) {
14846
+ return {
14847
+ ...metadata,
14848
+ provider: "mistral"
14849
+ };
14850
+ }
14851
+ function pickAllowedMetadata(metadata, allowlist) {
14852
+ if (!metadata) {
14853
+ return {};
14854
+ }
14855
+ const picked = {};
14856
+ for (const key of allowlist) {
14857
+ const value = metadata[key];
14858
+ if (value !== void 0) {
14859
+ picked[key] = value;
14860
+ }
14861
+ }
14862
+ return picked;
14863
+ }
14864
+ function extractMistralRequestMetadata(metadata) {
14865
+ return pickAllowedMetadata(metadata, MISTRAL_REQUEST_METADATA_ALLOWLIST);
14866
+ }
14867
+ function isMistralChatCompletionChunk(value) {
14868
+ return isObject(value);
14869
+ }
14870
+ function isMistralChunkChoice(value) {
14871
+ return isObject(value);
14872
+ }
14873
+ function extractMessagesInputWithMetadata(args) {
14874
+ const params = getMistralRequestArg(args);
14875
+ const { messages, ...rawMetadata } = params || {};
14876
+ return {
14877
+ input: processInputAttachments(messages),
14878
+ metadata: addMistralProviderMetadata(
14879
+ extractMistralRequestMetadata(rawMetadata)
14880
+ )
14881
+ };
14882
+ }
14883
+ function extractEmbeddingInputWithMetadata(args) {
14884
+ const params = getMistralRequestArg(args);
14885
+ const { inputs, ...rawMetadata } = params || {};
14886
+ return {
14887
+ input: inputs,
14888
+ metadata: addMistralProviderMetadata(
14889
+ extractMistralRequestMetadata(rawMetadata)
14890
+ )
14891
+ };
14892
+ }
14893
+ function extractPromptInputWithMetadata(args) {
14894
+ const params = getMistralRequestArg(args);
14895
+ const { prompt, ...rawMetadata } = params || {};
14896
+ return {
14897
+ input: prompt,
14898
+ metadata: addMistralProviderMetadata(
14899
+ extractMistralRequestMetadata(rawMetadata)
14900
+ )
14901
+ };
14902
+ }
14903
+ function extractMistralResponseMetadata(result) {
14904
+ if (!isObject(result)) {
14905
+ return void 0;
14906
+ }
14907
+ const { choices: _choices, usage: _usage, data: _data, ...metadata } = result;
14908
+ const picked = pickAllowedMetadata(
14909
+ metadata,
14910
+ MISTRAL_RESPONSE_METADATA_ALLOWLIST
14911
+ );
14912
+ return Object.keys(picked).length > 0 ? picked : void 0;
14913
+ }
14914
+ function extractMistralMetrics(usage, startTime) {
14915
+ const metrics = parseMistralMetricsFromUsage(usage);
14916
+ if (startTime) {
14917
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
14918
+ }
14919
+ return metrics;
14920
+ }
14921
+ function extractMistralStreamOutput(result) {
14922
+ return isObject(result) ? result.choices : void 0;
14923
+ }
14924
+ function extractMistralStreamingMetrics(result, startTime) {
14925
+ const metrics = isObject(result) ? parseMistralMetricsFromUsage(result.usage) : {};
14926
+ if (startTime) {
14927
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
14928
+ }
14929
+ return metrics;
14930
+ }
14931
+ function extractDeltaText(content) {
14932
+ if (typeof content === "string") {
14933
+ return content;
14934
+ }
14935
+ if (!Array.isArray(content)) {
14936
+ return void 0;
14937
+ }
14938
+ const textParts = content.map((part) => {
14939
+ if (!isObject(part) || part.type !== "text") {
14940
+ return "";
14941
+ }
14942
+ return typeof part.text === "string" ? part.text : "";
14943
+ }).filter((part) => part.length > 0);
14944
+ return textParts.length > 0 ? textParts.join("") : void 0;
14945
+ }
14946
+ function getDeltaToolCalls(delta) {
14947
+ const toolCalls = Array.isArray(delta.toolCalls) && delta.toolCalls || Array.isArray(delta.tool_calls) && delta.tool_calls || [];
14948
+ return toolCalls.filter((toolCall) => isObject(toolCall));
14949
+ }
14950
+ function getToolCallIndex(toolCall) {
14951
+ return typeof toolCall.index === "number" && toolCall.index >= 0 ? toolCall.index : void 0;
14952
+ }
14953
+ function createMergedToolCallDelta(delta) {
14954
+ return {
14955
+ ...delta,
14956
+ function: {
14957
+ ...delta.function,
14958
+ arguments: typeof delta.function?.arguments === "string" ? delta.function.arguments : ""
14959
+ }
14960
+ };
14961
+ }
14962
+ function mergeToolCallDeltaPair(current, delta) {
14963
+ const currentArguments = typeof current.function?.arguments === "string" ? current.function.arguments : "";
14964
+ const deltaArguments = typeof delta.function?.arguments === "string" ? delta.function.arguments : "";
14965
+ return {
14966
+ ...current,
14967
+ ...delta,
14968
+ function: {
14969
+ ...current.function || {},
14970
+ ...delta.function || {},
14971
+ arguments: `${currentArguments}${deltaArguments}`
14972
+ }
14973
+ };
14974
+ }
14975
+ function mergeToolCallDeltas(toolCalls, deltas) {
14976
+ if (deltas.length === 0) {
14977
+ return toolCalls;
14978
+ }
14979
+ const merged = toolCalls ? [...toolCalls] : [];
14980
+ const indexToPosition = /* @__PURE__ */ new Map();
14981
+ const idToPosition = /* @__PURE__ */ new Map();
14982
+ for (const [position, toolCall] of merged.entries()) {
14983
+ const index = getToolCallIndex(toolCall);
14984
+ if (index !== void 0 && !indexToPosition.has(index)) {
14985
+ indexToPosition.set(index, position);
14986
+ }
14987
+ if (typeof toolCall.id === "string" && !idToPosition.has(toolCall.id)) {
14988
+ idToPosition.set(toolCall.id, position);
14989
+ }
14990
+ }
14991
+ for (const delta of deltas) {
14992
+ const deltaIndex = getToolCallIndex(delta);
14993
+ const existingByIndex = deltaIndex !== void 0 ? indexToPosition.get(deltaIndex) : void 0;
14994
+ const existingById = typeof delta.id === "string" ? idToPosition.get(delta.id) : void 0;
14995
+ const existingPosition = existingByIndex ?? existingById;
14996
+ if (existingPosition === void 0) {
14997
+ const newToolCall = createMergedToolCallDelta(delta);
14998
+ merged.push(newToolCall);
14999
+ const newPosition = merged.length - 1;
15000
+ const newIndex = getToolCallIndex(newToolCall);
15001
+ if (newIndex !== void 0 && !indexToPosition.has(newIndex)) {
15002
+ indexToPosition.set(newIndex, newPosition);
15003
+ }
15004
+ if (typeof newToolCall.id === "string" && !idToPosition.has(newToolCall.id)) {
15005
+ idToPosition.set(newToolCall.id, newPosition);
15006
+ }
12647
15007
  continue;
12648
15008
  }
12649
- if (chunk.type === "response.completed" || chunk.type === "response.incomplete" || chunk.type === "response.failed") {
12650
- finalResponse = response;
15009
+ const mergedToolCall = mergeToolCallDeltaPair(
15010
+ merged[existingPosition],
15011
+ delta
15012
+ );
15013
+ merged[existingPosition] = mergedToolCall;
15014
+ const mergedIndex = getToolCallIndex(mergedToolCall);
15015
+ if (mergedIndex !== void 0 && !indexToPosition.has(mergedIndex)) {
15016
+ indexToPosition.set(mergedIndex, existingPosition);
15017
+ }
15018
+ if (typeof mergedToolCall.id === "string" && !idToPosition.has(mergedToolCall.id)) {
15019
+ idToPosition.set(mergedToolCall.id, existingPosition);
12651
15020
  }
12652
15021
  }
12653
- if (!finalResponse) {
12654
- return {
12655
- output: void 0,
12656
- metrics: {}
12657
- };
15022
+ return merged.length > 0 ? merged : void 0;
15023
+ }
15024
+ function getChoiceFinishReason(choice) {
15025
+ if (typeof choice.finishReason === "string" || choice.finishReason === null) {
15026
+ return choice.finishReason;
15027
+ }
15028
+ if (typeof choice.finish_reason === "string" || choice.finish_reason === null) {
15029
+ return choice.finish_reason;
15030
+ }
15031
+ return void 0;
15032
+ }
15033
+ function parseMistralMetricsFromUsage(usage) {
15034
+ if (!isObject(usage)) {
15035
+ return {};
15036
+ }
15037
+ const metrics = {};
15038
+ for (const [name, value] of Object.entries(usage)) {
15039
+ if (typeof value === "number") {
15040
+ metrics[TOKEN_NAME_MAP4[name] || camelToSnake3(name)] = value;
15041
+ continue;
15042
+ }
15043
+ if (!isObject(value)) {
15044
+ continue;
15045
+ }
15046
+ const prefix = TOKEN_DETAIL_PREFIX_MAP3[name];
15047
+ if (!prefix) {
15048
+ continue;
15049
+ }
15050
+ for (const [nestedName, nestedValue] of Object.entries(value)) {
15051
+ if (typeof nestedValue !== "number") {
15052
+ continue;
15053
+ }
15054
+ metrics[`${prefix}_${camelToSnake3(nestedName)}`] = nestedValue;
15055
+ }
15056
+ }
15057
+ return metrics;
15058
+ }
15059
+ function aggregateMistralStreamChunks(chunks) {
15060
+ const choiceAccumulators = /* @__PURE__ */ new Map();
15061
+ const indexToAccumulatorKey = /* @__PURE__ */ new Map();
15062
+ const positionToAccumulatorKey = /* @__PURE__ */ new Map();
15063
+ let nextAccumulatorOrder = 0;
15064
+ let metrics = {};
15065
+ let metadata;
15066
+ for (const event of chunks) {
15067
+ const chunk = isMistralChatCompletionChunk(event?.data) ? event.data : void 0;
15068
+ if (!chunk) {
15069
+ continue;
15070
+ }
15071
+ if (isObject(chunk.usage)) {
15072
+ metrics = {
15073
+ ...metrics,
15074
+ ...parseMistralMetricsFromUsage(chunk.usage)
15075
+ };
15076
+ }
15077
+ const chunkMetadata = extractMistralResponseMetadata(chunk);
15078
+ if (chunkMetadata) {
15079
+ metadata = { ...metadata || {}, ...chunkMetadata };
15080
+ }
15081
+ for (const [choicePosition, rawChoice] of (chunk.choices || []).entries()) {
15082
+ if (!isMistralChunkChoice(rawChoice)) {
15083
+ continue;
15084
+ }
15085
+ const choice = rawChoice;
15086
+ const choiceIndex = typeof choice.index === "number" && choice.index >= 0 ? choice.index : void 0;
15087
+ let accumulatorKey = choiceIndex !== void 0 ? indexToAccumulatorKey.get(choiceIndex) : void 0;
15088
+ if (!accumulatorKey) {
15089
+ accumulatorKey = positionToAccumulatorKey.get(choicePosition);
15090
+ }
15091
+ if (!accumulatorKey) {
15092
+ const initialIndex = choiceIndex ?? choicePosition;
15093
+ const keyPrefix = choiceIndex !== void 0 ? "index" : "position";
15094
+ accumulatorKey = `${keyPrefix}:${initialIndex}`;
15095
+ choiceAccumulators.set(accumulatorKey, {
15096
+ index: initialIndex,
15097
+ order: nextAccumulatorOrder++
15098
+ });
15099
+ }
15100
+ const accumulator = choiceAccumulators.get(accumulatorKey);
15101
+ if (!accumulator) {
15102
+ continue;
15103
+ }
15104
+ if (choiceIndex !== void 0) {
15105
+ accumulator.index = choiceIndex;
15106
+ indexToAccumulatorKey.set(choiceIndex, accumulatorKey);
15107
+ }
15108
+ positionToAccumulatorKey.set(choicePosition, accumulatorKey);
15109
+ const delta = isObject(choice.delta) ? choice.delta : void 0;
15110
+ if (delta) {
15111
+ if (!accumulator.role && typeof delta.role === "string") {
15112
+ accumulator.role = delta.role;
15113
+ }
15114
+ const deltaText = extractDeltaText(delta.content);
15115
+ if (deltaText) {
15116
+ accumulator.content = `${accumulator.content || ""}${deltaText}`;
15117
+ }
15118
+ accumulator.toolCalls = mergeToolCallDeltas(
15119
+ accumulator.toolCalls,
15120
+ getDeltaToolCalls(delta)
15121
+ );
15122
+ }
15123
+ const choiceFinishReason = getChoiceFinishReason(choice);
15124
+ if (choiceFinishReason !== void 0) {
15125
+ accumulator.finishReason = choiceFinishReason;
15126
+ }
15127
+ }
12658
15128
  }
15129
+ const output = Array.from(choiceAccumulators.values()).sort(
15130
+ (left, right) => left.index === right.index ? left.order - right.order : left.index - right.index
15131
+ ).map((choice) => ({
15132
+ index: choice.index,
15133
+ message: {
15134
+ ...choice.role ? { role: choice.role } : {},
15135
+ content: choice.content ?? null,
15136
+ ...choice.toolCalls ? { toolCalls: choice.toolCalls } : {}
15137
+ },
15138
+ ...choice.finishReason !== void 0 ? { finishReason: choice.finishReason } : {}
15139
+ }));
12659
15140
  return {
12660
- output: extractOpenRouterResponseOutput(finalResponse),
12661
- metrics: parseOpenRouterMetricsFromUsage(finalResponse.usage),
12662
- ...extractOpenRouterResponseMetadata(finalResponse) ? { metadata: extractOpenRouterResponseMetadata(finalResponse) } : {}
15141
+ output,
15142
+ metrics,
15143
+ ...metadata ? { metadata } : {}
12663
15144
  };
12664
15145
  }
12665
15146
 
@@ -12672,6 +15153,8 @@ var BraintrustPlugin = class extends BasePlugin {
12672
15153
  claudeAgentSDKPlugin = null;
12673
15154
  googleGenAIPlugin = null;
12674
15155
  openRouterPlugin = null;
15156
+ openRouterAgentPlugin = null;
15157
+ mistralPlugin = null;
12675
15158
  constructor(config = {}) {
12676
15159
  super();
12677
15160
  this.config = config;
@@ -12702,6 +15185,14 @@ var BraintrustPlugin = class extends BasePlugin {
12702
15185
  this.openRouterPlugin = new OpenRouterPlugin();
12703
15186
  this.openRouterPlugin.enable();
12704
15187
  }
15188
+ if (integrations.openrouterAgent !== false) {
15189
+ this.openRouterAgentPlugin = new OpenRouterAgentPlugin();
15190
+ this.openRouterAgentPlugin.enable();
15191
+ }
15192
+ if (integrations.mistral !== false) {
15193
+ this.mistralPlugin = new MistralPlugin();
15194
+ this.mistralPlugin.enable();
15195
+ }
12705
15196
  }
12706
15197
  onDisable() {
12707
15198
  if (this.openaiPlugin) {
@@ -12728,6 +15219,14 @@ var BraintrustPlugin = class extends BasePlugin {
12728
15219
  this.openRouterPlugin.disable();
12729
15220
  this.openRouterPlugin = null;
12730
15221
  }
15222
+ if (this.openRouterAgentPlugin) {
15223
+ this.openRouterAgentPlugin.disable();
15224
+ this.openRouterAgentPlugin = null;
15225
+ }
15226
+ if (this.mistralPlugin) {
15227
+ this.mistralPlugin.disable();
15228
+ this.mistralPlugin = null;
15229
+ }
12731
15230
  }
12732
15231
  };
12733
15232
 
@@ -12800,7 +15299,9 @@ var PluginRegistry = class {
12800
15299
  aisdk: true,
12801
15300
  google: true,
12802
15301
  claudeAgentSDK: true,
12803
- openrouter: true
15302
+ openrouter: true,
15303
+ openrouterAgent: true,
15304
+ mistral: true
12804
15305
  };
12805
15306
  }
12806
15307
  /**