braintrust 3.4.0 → 3.6.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 (58) hide show
  1. package/dev/dist/index.d.mts +49 -7
  2. package/dev/dist/index.d.ts +49 -7
  3. package/dev/dist/index.js +2383 -494
  4. package/dev/dist/index.mjs +2213 -324
  5. package/dist/auto-instrumentations/bundler/esbuild.cjs +289 -10
  6. package/dist/auto-instrumentations/bundler/esbuild.d.mts +2 -2
  7. package/dist/auto-instrumentations/bundler/esbuild.d.ts +2 -2
  8. package/dist/auto-instrumentations/bundler/esbuild.mjs +2 -2
  9. package/dist/auto-instrumentations/bundler/rollup.cjs +289 -10
  10. package/dist/auto-instrumentations/bundler/rollup.mjs +2 -2
  11. package/dist/auto-instrumentations/bundler/vite.cjs +289 -10
  12. package/dist/auto-instrumentations/bundler/vite.d.mts +2 -2
  13. package/dist/auto-instrumentations/bundler/vite.d.ts +2 -2
  14. package/dist/auto-instrumentations/bundler/vite.mjs +2 -2
  15. package/dist/auto-instrumentations/bundler/webpack.cjs +289 -10
  16. package/dist/auto-instrumentations/bundler/webpack.d.mts +2 -2
  17. package/dist/auto-instrumentations/bundler/webpack.d.ts +2 -2
  18. package/dist/auto-instrumentations/bundler/webpack.mjs +2 -2
  19. package/dist/auto-instrumentations/chunk-EVUKFMHG.mjs +41 -0
  20. package/dist/auto-instrumentations/{chunk-LVWWLUMN.mjs → chunk-F7WAXFNM.mjs} +290 -11
  21. package/dist/auto-instrumentations/chunk-VLEJ5AEK.mjs +41 -0
  22. package/dist/auto-instrumentations/{chunk-D5ZPIUEL.mjs → chunk-WOUC73KB.mjs} +3 -1
  23. package/dist/auto-instrumentations/hook.mjs +358 -48
  24. package/dist/auto-instrumentations/index.cjs +290 -10
  25. package/dist/auto-instrumentations/index.d.mts +3 -1
  26. package/dist/auto-instrumentations/index.d.ts +3 -1
  27. package/dist/auto-instrumentations/index.mjs +3 -1
  28. package/dist/auto-instrumentations/loader/cjs-patch.cjs +32 -10
  29. package/dist/auto-instrumentations/loader/cjs-patch.mjs +10 -5
  30. package/dist/auto-instrumentations/loader/esm-hook.mjs +4 -4
  31. package/dist/auto-instrumentations/loader/get-package-version.cjs +28 -8
  32. package/dist/auto-instrumentations/loader/get-package-version.d.mts +2 -1
  33. package/dist/auto-instrumentations/loader/get-package-version.d.ts +2 -1
  34. package/dist/auto-instrumentations/loader/get-package-version.mjs +3 -1
  35. package/dist/browser.d.mts +357 -271
  36. package/dist/browser.d.ts +357 -271
  37. package/dist/browser.js +2345 -343
  38. package/dist/browser.mjs +2345 -343
  39. package/dist/cli.js +2296 -414
  40. package/dist/edge-light.d.mts +1 -1
  41. package/dist/edge-light.d.ts +1 -1
  42. package/dist/edge-light.js +2292 -315
  43. package/dist/edge-light.mjs +2292 -315
  44. package/dist/index.d.mts +370 -284
  45. package/dist/index.d.ts +370 -284
  46. package/dist/index.js +2642 -638
  47. package/dist/index.mjs +2385 -381
  48. package/dist/instrumentation/index.d.mts +3 -0
  49. package/dist/instrumentation/index.d.ts +3 -0
  50. package/dist/instrumentation/index.js +1955 -198
  51. package/dist/instrumentation/index.mjs +1955 -198
  52. package/dist/workerd.d.mts +1 -1
  53. package/dist/workerd.d.ts +1 -1
  54. package/dist/workerd.js +2292 -315
  55. package/dist/workerd.mjs +2292 -315
  56. package/package.json +22 -6
  57. package/dist/auto-instrumentations/chunk-XDBPUTVE.mjs +0 -22
  58. package/dist/auto-instrumentations/chunk-ZEC7BCL4.mjs +0 -22
@@ -52,8 +52,54 @@ var DefaultAsyncLocalStorage = class {
52
52
  return void 0;
53
53
  }
54
54
  };
55
- var DefaultTracingChannel = class {
55
+ var DefaultChannel = class {
56
+ constructor(name) {
57
+ this.name = name;
58
+ }
56
59
  hasSubscribers = false;
60
+ subscribe(_subscription) {
61
+ }
62
+ unsubscribe(_subscription) {
63
+ return false;
64
+ }
65
+ bindStore(_store, _transform) {
66
+ }
67
+ unbindStore(_store) {
68
+ return false;
69
+ }
70
+ publish(_message) {
71
+ }
72
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
+ runStores(_message, fn, thisArg, ...args) {
74
+ return fn.apply(thisArg, args);
75
+ }
76
+ };
77
+ var DefaultTracingChannel = class {
78
+ start;
79
+ end;
80
+ asyncStart;
81
+ asyncEnd;
82
+ error;
83
+ constructor(nameOrChannels) {
84
+ if (typeof nameOrChannels === "string") {
85
+ this.start = new DefaultChannel(`tracing:${nameOrChannels}:start`);
86
+ this.end = new DefaultChannel(`tracing:${nameOrChannels}:end`);
87
+ this.asyncStart = new DefaultChannel(
88
+ `tracing:${nameOrChannels}:asyncStart`
89
+ );
90
+ this.asyncEnd = new DefaultChannel(`tracing:${nameOrChannels}:asyncEnd`);
91
+ this.error = new DefaultChannel(`tracing:${nameOrChannels}:error`);
92
+ return;
93
+ }
94
+ this.start = nameOrChannels.start ?? new DefaultChannel("tracing:start");
95
+ this.end = nameOrChannels.end ?? new DefaultChannel("tracing:end");
96
+ this.asyncStart = nameOrChannels.asyncStart ?? new DefaultChannel("tracing:asyncStart");
97
+ this.asyncEnd = nameOrChannels.asyncEnd ?? new DefaultChannel("tracing:asyncEnd");
98
+ this.error = nameOrChannels.error ?? new DefaultChannel("tracing:error");
99
+ }
100
+ get hasSubscribers() {
101
+ return this.start.hasSubscribers || this.end.hasSubscribers || this.asyncStart.hasSubscribers || this.asyncEnd.hasSubscribers || this.error.hasSubscribers;
102
+ }
57
103
  subscribe(_handlers) {
58
104
  }
59
105
  unsubscribe(_handlers) {
@@ -81,7 +127,7 @@ var iso = {
81
127
  getCallerLocation: () => void 0,
82
128
  newAsyncLocalStorage: () => new DefaultAsyncLocalStorage(),
83
129
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
84
- newTracingChannel: (_nameOrChannels) => new DefaultTracingChannel(),
130
+ newTracingChannel: (nameOrChannels) => new DefaultTracingChannel(nameOrChannels),
85
131
  processOn: (_0, _1) => {
86
132
  },
87
133
  basename: (filepath) => filepath.split(/[\\/]/).pop() || filepath,
@@ -2115,6 +2161,8 @@ var Experiment = import_v36.z.object({
2115
2161
  deleted_at: import_v36.z.union([import_v36.z.string(), import_v36.z.null()]).optional(),
2116
2162
  dataset_id: import_v36.z.union([import_v36.z.string(), import_v36.z.null()]).optional(),
2117
2163
  dataset_version: import_v36.z.union([import_v36.z.string(), import_v36.z.null()]).optional(),
2164
+ parameters_id: import_v36.z.union([import_v36.z.string(), import_v36.z.null()]).optional(),
2165
+ parameters_version: import_v36.z.union([import_v36.z.string(), import_v36.z.null()]).optional(),
2118
2166
  public: import_v36.z.boolean(),
2119
2167
  user_id: import_v36.z.union([import_v36.z.string(), import_v36.z.null()]).optional(),
2120
2168
  metadata: import_v36.z.union([import_v36.z.object({}).partial().passthrough(), import_v36.z.null()]).optional(),
@@ -2137,7 +2185,11 @@ var SpanType = import_v36.z.union([
2137
2185
  import_v36.z.null()
2138
2186
  ]);
2139
2187
  var SpanAttributes = import_v36.z.union([
2140
- import_v36.z.object({ name: import_v36.z.union([import_v36.z.string(), import_v36.z.null()]), type: SpanType }).partial().passthrough(),
2188
+ import_v36.z.object({
2189
+ name: import_v36.z.union([import_v36.z.string(), import_v36.z.null()]),
2190
+ type: SpanType,
2191
+ purpose: import_v36.z.union([import_v36.z.literal("scorer"), import_v36.z.null()])
2192
+ }).partial().passthrough(),
2141
2193
  import_v36.z.null()
2142
2194
  ]);
2143
2195
  var ExperimentEvent = import_v36.z.object({
@@ -2577,6 +2629,7 @@ var FunctionId = import_v36.z.union([
2577
2629
  version: import_v36.z.string()
2578
2630
  }),
2579
2631
  code: import_v36.z.string(),
2632
+ function_type: FunctionTypeEnum.and(import_v36.z.unknown()).optional(),
2580
2633
  name: import_v36.z.union([import_v36.z.string(), import_v36.z.null()]).optional()
2581
2634
  }),
2582
2635
  import_v36.z.object({
@@ -2806,7 +2859,12 @@ var TopicAutomationConfig = import_v36.z.object({
2806
2859
  topic_map_functions: import_v36.z.array(TopicMapFunctionAutomation),
2807
2860
  scope: import_v36.z.union([SpanScope, TraceScope, GroupScope, import_v36.z.null()]).optional(),
2808
2861
  data_scope: TopicAutomationDataScope.optional(),
2809
- btql_filter: import_v36.z.union([import_v36.z.string(), import_v36.z.null()]).optional()
2862
+ btql_filter: import_v36.z.union([import_v36.z.string(), import_v36.z.null()]).optional(),
2863
+ backfill_time_range: import_v36.z.union([
2864
+ import_v36.z.string(),
2865
+ import_v36.z.object({ from: import_v36.z.string(), to: import_v36.z.string() }),
2866
+ import_v36.z.null()
2867
+ ]).optional()
2810
2868
  });
2811
2869
  var ProjectAutomation = import_v36.z.object({
2812
2870
  id: import_v36.z.string().uuid(),
@@ -4199,6 +4257,9 @@ var BRAINTRUST_ATTACHMENT = BraintrustAttachmentReference.shape.type.value;
4199
4257
  var EXTERNAL_ATTACHMENT = ExternalAttachmentReference.shape.type.value;
4200
4258
  var LOGS3_OVERFLOW_REFERENCE_TYPE = "logs3_overflow";
4201
4259
  var BRAINTRUST_PARAMS = Object.keys(BraintrustModelParams.shape);
4260
+ var RESET_CONTEXT_MANAGER_STATE = Symbol.for(
4261
+ "braintrust.resetContextManagerState"
4262
+ );
4202
4263
  var DEFAULT_MAX_REQUEST_SIZE = 6 * 1024 * 1024;
4203
4264
  var parametersRowSchema = import_v38.z.object({
4204
4265
  id: import_v38.z.string().uuid(),
@@ -4215,6 +4276,12 @@ var parametersRowSchema = import_v38.z.object({
4215
4276
  }),
4216
4277
  metadata: import_v38.z.union([import_v38.z.object({}).partial().passthrough(), import_v38.z.null()]).optional()
4217
4278
  });
4279
+ var InlineAttachmentReferenceSchema = import_v38.z.object({
4280
+ type: import_v38.z.literal("inline_attachment"),
4281
+ src: import_v38.z.string().min(1),
4282
+ content_type: import_v38.z.string().optional(),
4283
+ filename: import_v38.z.string().optional()
4284
+ });
4218
4285
  var LoginInvalidOrgError = class extends Error {
4219
4286
  constructor(message) {
4220
4287
  super(message);
@@ -4255,13 +4322,18 @@ function applyMaskingToField(maskingFunction, data, fieldName) {
4255
4322
  return `ERROR: Failed to mask field '${fieldName}' - ${errorType}`;
4256
4323
  }
4257
4324
  }
4325
+ var BRAINTRUST_CURRENT_SPAN_STORE = Symbol.for(
4326
+ "braintrust.currentSpanStore"
4327
+ );
4258
4328
  var ContextManager = class {
4259
4329
  };
4260
4330
  var BraintrustContextManager = class extends ContextManager {
4261
4331
  _currentSpan;
4332
+ [BRAINTRUST_CURRENT_SPAN_STORE];
4262
4333
  constructor() {
4263
4334
  super();
4264
4335
  this._currentSpan = isomorph_default.newAsyncLocalStorage();
4336
+ this[BRAINTRUST_CURRENT_SPAN_STORE] = this._currentSpan;
4265
4337
  }
4266
4338
  getParentSpanIds() {
4267
4339
  const currentSpan2 = this._currentSpan.getStore();
@@ -4468,6 +4540,9 @@ var BraintrustState = class _BraintrustState {
4468
4540
  resetIdGenState() {
4469
4541
  this._idGenerator = null;
4470
4542
  }
4543
+ [RESET_CONTEXT_MANAGER_STATE]() {
4544
+ this._contextManager = null;
4545
+ }
4471
4546
  get idGenerator() {
4472
4547
  if (this._idGenerator === null) {
4473
4548
  this._idGenerator = getIdGenerator();
@@ -8017,7 +8092,11 @@ function startSpanForEvent(config, event, channelName) {
8017
8092
  });
8018
8093
  const startTime = getCurrentUnixTimestamp();
8019
8094
  try {
8020
- const { input, metadata } = config.extractInput(event.arguments);
8095
+ const { input, metadata } = config.extractInput(
8096
+ event.arguments,
8097
+ event,
8098
+ span
8099
+ );
8021
8100
  span.log({
8022
8101
  input,
8023
8102
  metadata: mergeInputMetadata(metadata, spanInfoMetadata)
@@ -8027,6 +8106,36 @@ function startSpanForEvent(config, event, channelName) {
8027
8106
  }
8028
8107
  return { span, startTime };
8029
8108
  }
8109
+ function ensureSpanStateForEvent(states, config, event, channelName) {
8110
+ const key = event;
8111
+ const existing = states.get(key);
8112
+ if (existing) {
8113
+ return existing;
8114
+ }
8115
+ const created = startSpanForEvent(config, event, channelName);
8116
+ states.set(key, created);
8117
+ return created;
8118
+ }
8119
+ function bindCurrentSpanStoreToStart(tracingChannel, states, config, channelName) {
8120
+ const state = _internalGetGlobalState();
8121
+ const startChannel = tracingChannel.start;
8122
+ const currentSpanStore = state?.contextManager ? state.contextManager[BRAINTRUST_CURRENT_SPAN_STORE] : void 0;
8123
+ if (!currentSpanStore || !startChannel) {
8124
+ return void 0;
8125
+ }
8126
+ startChannel.bindStore(
8127
+ currentSpanStore,
8128
+ (event) => ensureSpanStateForEvent(
8129
+ states,
8130
+ config,
8131
+ event,
8132
+ channelName
8133
+ ).span
8134
+ );
8135
+ return () => {
8136
+ startChannel.unbindStore(currentSpanStore);
8137
+ };
8138
+ }
8030
8139
  function logErrorAndEnd(states, event) {
8031
8140
  const spanData = states.get(event);
8032
8141
  if (!spanData) {
@@ -8042,15 +8151,19 @@ function traceAsyncChannel(channel2, config) {
8042
8151
  const tracingChannel = channel2.tracingChannel();
8043
8152
  const states = /* @__PURE__ */ new WeakMap();
8044
8153
  const channelName = channel2.channelName;
8154
+ const unbindCurrentSpanStore = bindCurrentSpanStoreToStart(
8155
+ tracingChannel,
8156
+ states,
8157
+ config,
8158
+ channelName
8159
+ );
8045
8160
  const handlers = {
8046
8161
  start: (event) => {
8047
- states.set(
8162
+ ensureSpanStateForEvent(
8163
+ states,
8164
+ config,
8048
8165
  event,
8049
- startSpanForEvent(
8050
- config,
8051
- event,
8052
- channelName
8053
- )
8166
+ channelName
8054
8167
  );
8055
8168
  },
8056
8169
  asyncEnd: (event) => {
@@ -8092,6 +8205,7 @@ function traceAsyncChannel(channel2, config) {
8092
8205
  };
8093
8206
  tracingChannel.subscribe(handlers);
8094
8207
  return () => {
8208
+ unbindCurrentSpanStore?.();
8095
8209
  tracingChannel.unsubscribe(handlers);
8096
8210
  };
8097
8211
  }
@@ -8099,15 +8213,19 @@ function traceStreamingChannel(channel2, config) {
8099
8213
  const tracingChannel = channel2.tracingChannel();
8100
8214
  const states = /* @__PURE__ */ new WeakMap();
8101
8215
  const channelName = channel2.channelName;
8216
+ const unbindCurrentSpanStore = bindCurrentSpanStoreToStart(
8217
+ tracingChannel,
8218
+ states,
8219
+ config,
8220
+ channelName
8221
+ );
8102
8222
  const handlers = {
8103
8223
  start: (event) => {
8104
- states.set(
8224
+ ensureSpanStateForEvent(
8225
+ states,
8226
+ config,
8105
8227
  event,
8106
- startSpanForEvent(
8107
- config,
8108
- event,
8109
- channelName
8110
- )
8228
+ channelName
8111
8229
  );
8112
8230
  },
8113
8231
  asyncEnd: (event) => {
@@ -8181,6 +8299,16 @@ function traceStreamingChannel(channel2, config) {
8181
8299
  });
8182
8300
  return;
8183
8301
  }
8302
+ if (config.patchResult?.({
8303
+ channelName,
8304
+ endEvent: asyncEndEvent,
8305
+ result: asyncEndEvent.result,
8306
+ span,
8307
+ startTime
8308
+ })) {
8309
+ states.delete(event);
8310
+ return;
8311
+ }
8184
8312
  try {
8185
8313
  const output = config.extractOutput(
8186
8314
  asyncEndEvent.result,
@@ -8213,6 +8341,7 @@ function traceStreamingChannel(channel2, config) {
8213
8341
  };
8214
8342
  tracingChannel.subscribe(handlers);
8215
8343
  return () => {
8344
+ unbindCurrentSpanStore?.();
8216
8345
  tracingChannel.unsubscribe(handlers);
8217
8346
  };
8218
8347
  }
@@ -8220,15 +8349,19 @@ function traceSyncStreamChannel(channel2, config) {
8220
8349
  const tracingChannel = channel2.tracingChannel();
8221
8350
  const states = /* @__PURE__ */ new WeakMap();
8222
8351
  const channelName = channel2.channelName;
8352
+ const unbindCurrentSpanStore = bindCurrentSpanStoreToStart(
8353
+ tracingChannel,
8354
+ states,
8355
+ config,
8356
+ channelName
8357
+ );
8223
8358
  const handlers = {
8224
8359
  start: (event) => {
8225
- states.set(
8360
+ ensureSpanStateForEvent(
8361
+ states,
8362
+ config,
8226
8363
  event,
8227
- startSpanForEvent(
8228
- config,
8229
- event,
8230
- channelName
8231
- )
8364
+ channelName
8232
8365
  );
8233
8366
  },
8234
8367
  end: (event) => {
@@ -8237,8 +8370,17 @@ function traceSyncStreamChannel(channel2, config) {
8237
8370
  return;
8238
8371
  }
8239
8372
  const { span, startTime } = spanData;
8240
- const resultEvent = event;
8241
- const stream = resultEvent.result;
8373
+ const endEvent = event;
8374
+ if (config.patchResult?.({
8375
+ channelName,
8376
+ endEvent,
8377
+ result: endEvent.result,
8378
+ span,
8379
+ startTime
8380
+ })) {
8381
+ return;
8382
+ }
8383
+ const stream = endEvent.result;
8242
8384
  if (!isSyncStreamLike(stream)) {
8243
8385
  span.end();
8244
8386
  states.delete(event);
@@ -8308,6 +8450,7 @@ function traceSyncStreamChannel(channel2, config) {
8308
8450
  };
8309
8451
  tracingChannel.subscribe(handlers);
8310
8452
  return () => {
8453
+ unbindCurrentSpanStore?.();
8311
8454
  tracingChannel.unsubscribe(handlers);
8312
8455
  };
8313
8456
  }
@@ -9209,6 +9352,108 @@ function filterFrom(obj, fieldsToRemove) {
9209
9352
  return result;
9210
9353
  }
9211
9354
 
9355
+ // src/wrappers/ai-sdk/normalize-logged-output.ts
9356
+ var REMOVE_NORMALIZED_VALUE = Symbol("braintrust.ai-sdk.remove-normalized");
9357
+ function normalizeAISDKLoggedOutput(value) {
9358
+ const normalized = normalizeAISDKLoggedValue(value);
9359
+ return normalized === REMOVE_NORMALIZED_VALUE ? {} : normalized;
9360
+ }
9361
+ function normalizeAISDKLoggedValue(value, context = {}) {
9362
+ if (Array.isArray(value)) {
9363
+ return value.map((entry) => normalizeAISDKLoggedValue(entry, context)).filter((entry) => entry !== REMOVE_NORMALIZED_VALUE);
9364
+ }
9365
+ if (!value || typeof value !== "object") {
9366
+ return value;
9367
+ }
9368
+ const nextInProviderMetadata = context.inProviderMetadata || context.parentKey === "providerMetadata" || context.parentKey === "experimental_providerMetadata";
9369
+ const normalizedEntries = [];
9370
+ for (const [key, entry] of Object.entries(value)) {
9371
+ if (key === "cachedPromptTokens" && entry === 0) {
9372
+ continue;
9373
+ }
9374
+ if (context.parentKey === "request" && key === "body" && entry === "<omitted>") {
9375
+ continue;
9376
+ }
9377
+ const normalizedEntry = normalizeAISDKLoggedValue(entry, {
9378
+ inProviderMetadata: nextInProviderMetadata,
9379
+ parentKey: key
9380
+ });
9381
+ if (normalizedEntry === REMOVE_NORMALIZED_VALUE) {
9382
+ continue;
9383
+ }
9384
+ normalizedEntries.push([key, normalizedEntry]);
9385
+ }
9386
+ if (normalizedEntries.length === 0) {
9387
+ if (context.parentKey === "request" || nextInProviderMetadata) {
9388
+ return REMOVE_NORMALIZED_VALUE;
9389
+ }
9390
+ return {};
9391
+ }
9392
+ return Object.fromEntries(normalizedEntries);
9393
+ }
9394
+
9395
+ // src/zod/utils.ts
9396
+ var import_zod_to_json_schema = require("zod-to-json-schema");
9397
+ var z42 = __toESM(require("zod/v4"));
9398
+ function isZodV4(zodObject) {
9399
+ return typeof zodObject === "object" && zodObject !== null && "_zod" in zodObject && zodObject._zod !== void 0;
9400
+ }
9401
+ function zodToJsonSchema(schema) {
9402
+ if (isZodV4(schema)) {
9403
+ return z42.toJSONSchema(schema, {
9404
+ target: "draft-7"
9405
+ });
9406
+ }
9407
+ return (0, import_zod_to_json_schema.zodToJsonSchema)(schema);
9408
+ }
9409
+
9410
+ // src/wrappers/ai-sdk/tool-serialization.ts
9411
+ function isZodSchema(value) {
9412
+ return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
9413
+ }
9414
+ function serializeZodSchema(schema) {
9415
+ try {
9416
+ return zodToJsonSchema(schema);
9417
+ } catch {
9418
+ return {
9419
+ type: "object",
9420
+ description: "Zod schema (conversion failed)"
9421
+ };
9422
+ }
9423
+ }
9424
+ function serializeTool(tool) {
9425
+ if (!tool || typeof tool !== "object") {
9426
+ return tool;
9427
+ }
9428
+ const serialized = { ...tool };
9429
+ if (isZodSchema(serialized.inputSchema)) {
9430
+ serialized.inputSchema = serializeZodSchema(serialized.inputSchema);
9431
+ }
9432
+ if (isZodSchema(serialized.parameters)) {
9433
+ serialized.parameters = serializeZodSchema(serialized.parameters);
9434
+ }
9435
+ if ("execute" in serialized) {
9436
+ delete serialized.execute;
9437
+ }
9438
+ if ("render" in serialized) {
9439
+ delete serialized.render;
9440
+ }
9441
+ return serialized;
9442
+ }
9443
+ function serializeAISDKToolsForLogging(tools) {
9444
+ if (!tools || typeof tools !== "object") {
9445
+ return tools;
9446
+ }
9447
+ if (Array.isArray(tools)) {
9448
+ return tools.map(serializeTool);
9449
+ }
9450
+ const serialized = {};
9451
+ for (const [key, tool] of Object.entries(tools)) {
9452
+ serialized[key] = serializeTool(tool);
9453
+ }
9454
+ return serialized;
9455
+ }
9456
+
9212
9457
  // src/instrumentation/plugins/ai-sdk-channels.ts
9213
9458
  var aiSDKChannels = defineChannels("ai", {
9214
9459
  generateText: channel({
@@ -9219,6 +9464,10 @@ var aiSDKChannels = defineChannels("ai", {
9219
9464
  channelName: "streamText",
9220
9465
  kind: "async"
9221
9466
  }),
9467
+ streamTextSync: channel({
9468
+ channelName: "streamText.sync",
9469
+ kind: "sync-stream"
9470
+ }),
9222
9471
  generateObject: channel({
9223
9472
  channelName: "generateObject",
9224
9473
  kind: "async"
@@ -9227,6 +9476,10 @@ var aiSDKChannels = defineChannels("ai", {
9227
9476
  channelName: "streamObject",
9228
9477
  kind: "async"
9229
9478
  }),
9479
+ streamObjectSync: channel({
9480
+ channelName: "streamObject.sync",
9481
+ kind: "sync-stream"
9482
+ }),
9230
9483
  agentGenerate: channel({
9231
9484
  channelName: "Agent.generate",
9232
9485
  kind: "async"
@@ -9234,6 +9487,14 @@ var aiSDKChannels = defineChannels("ai", {
9234
9487
  agentStream: channel({
9235
9488
  channelName: "Agent.stream",
9236
9489
  kind: "async"
9490
+ }),
9491
+ toolLoopAgentGenerate: channel({
9492
+ channelName: "ToolLoopAgent.generate",
9493
+ kind: "async"
9494
+ }),
9495
+ toolLoopAgentStream: channel({
9496
+ channelName: "ToolLoopAgent.stream",
9497
+ kind: "async"
9237
9498
  })
9238
9499
  });
9239
9500
 
@@ -9252,6 +9513,8 @@ var DEFAULT_DENY_OUTPUT_PATHS = [
9252
9513
  "steps[].response.body",
9253
9514
  "steps[].response.headers"
9254
9515
  ];
9516
+ var AUTO_PATCHED_MODEL = Symbol.for("braintrust.ai-sdk.auto-patched-model");
9517
+ var AUTO_PATCHED_TOOL = Symbol.for("braintrust.ai-sdk.auto-patched-tool");
9255
9518
  var AISDKPlugin = class extends BasePlugin {
9256
9519
  config;
9257
9520
  constructor(config = {}) {
@@ -9270,22 +9533,12 @@ var AISDKPlugin = class extends BasePlugin {
9270
9533
  traceStreamingChannel(aiSDKChannels.generateText, {
9271
9534
  name: "generateText",
9272
9535
  type: "llm" /* LLM */,
9273
- extractInput: ([params]) => {
9274
- return {
9275
- input: processAISDKInput(params),
9276
- metadata: extractMetadataFromParams(params)
9277
- };
9278
- },
9279
- extractOutput: (result) => {
9536
+ extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9537
+ extractOutput: (result, endEvent) => {
9538
+ finalizeAISDKChildTracing(endEvent);
9280
9539
  return processAISDKOutput(result, denyOutputPaths);
9281
9540
  },
9282
- extractMetrics: (result, startTime) => {
9283
- const metrics = extractTokenMetrics(result);
9284
- if (startTime) {
9285
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9286
- }
9287
- return metrics;
9288
- },
9541
+ extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
9289
9542
  aggregateChunks: aggregateAISDKChunks
9290
9543
  })
9291
9544
  );
@@ -9293,45 +9546,43 @@ var AISDKPlugin = class extends BasePlugin {
9293
9546
  traceStreamingChannel(aiSDKChannels.streamText, {
9294
9547
  name: "streamText",
9295
9548
  type: "llm" /* LLM */,
9296
- extractInput: ([params]) => {
9297
- return {
9298
- input: processAISDKInput(params),
9299
- metadata: extractMetadataFromParams(params)
9300
- };
9301
- },
9302
- extractOutput: (result) => {
9303
- return processAISDKOutput(result, denyOutputPaths);
9304
- },
9305
- extractMetrics: (result, startTime) => {
9306
- const metrics = extractTokenMetrics(result);
9307
- if (startTime) {
9308
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9309
- }
9310
- return metrics;
9311
- },
9312
- aggregateChunks: aggregateAISDKChunks
9549
+ extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9550
+ extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
9551
+ extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
9552
+ aggregateChunks: aggregateAISDKChunks,
9553
+ patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9554
+ denyOutputPaths,
9555
+ endEvent,
9556
+ result,
9557
+ span,
9558
+ startTime
9559
+ })
9560
+ })
9561
+ );
9562
+ this.unsubscribers.push(
9563
+ traceSyncStreamChannel(aiSDKChannels.streamTextSync, {
9564
+ name: "streamText",
9565
+ type: "llm" /* LLM */,
9566
+ extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9567
+ patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9568
+ denyOutputPaths,
9569
+ endEvent,
9570
+ result,
9571
+ span,
9572
+ startTime
9573
+ })
9313
9574
  })
9314
9575
  );
9315
9576
  this.unsubscribers.push(
9316
9577
  traceStreamingChannel(aiSDKChannels.generateObject, {
9317
9578
  name: "generateObject",
9318
9579
  type: "llm" /* LLM */,
9319
- extractInput: ([params]) => {
9320
- return {
9321
- input: processAISDKInput(params),
9322
- metadata: extractMetadataFromParams(params)
9323
- };
9324
- },
9325
- extractOutput: (result) => {
9580
+ extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9581
+ extractOutput: (result, endEvent) => {
9582
+ finalizeAISDKChildTracing(endEvent);
9326
9583
  return processAISDKOutput(result, denyOutputPaths);
9327
9584
  },
9328
- extractMetrics: (result, startTime) => {
9329
- const metrics = extractTokenMetrics(result);
9330
- if (startTime) {
9331
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9332
- }
9333
- return metrics;
9334
- },
9585
+ extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
9335
9586
  aggregateChunks: aggregateAISDKChunks
9336
9587
  })
9337
9588
  );
@@ -9339,45 +9590,43 @@ var AISDKPlugin = class extends BasePlugin {
9339
9590
  traceStreamingChannel(aiSDKChannels.streamObject, {
9340
9591
  name: "streamObject",
9341
9592
  type: "llm" /* LLM */,
9342
- extractInput: ([params]) => {
9343
- return {
9344
- input: processAISDKInput(params),
9345
- metadata: extractMetadataFromParams(params)
9346
- };
9347
- },
9348
- extractOutput: (result) => {
9349
- return processAISDKOutput(result, denyOutputPaths);
9350
- },
9351
- extractMetrics: (result, startTime) => {
9352
- const metrics = extractTokenMetrics(result);
9353
- if (startTime) {
9354
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9355
- }
9356
- return metrics;
9357
- },
9358
- aggregateChunks: aggregateAISDKChunks
9593
+ extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9594
+ extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
9595
+ extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
9596
+ aggregateChunks: aggregateAISDKChunks,
9597
+ patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9598
+ denyOutputPaths,
9599
+ endEvent,
9600
+ result,
9601
+ span,
9602
+ startTime
9603
+ })
9604
+ })
9605
+ );
9606
+ this.unsubscribers.push(
9607
+ traceSyncStreamChannel(aiSDKChannels.streamObjectSync, {
9608
+ name: "streamObject",
9609
+ type: "llm" /* LLM */,
9610
+ extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9611
+ patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9612
+ denyOutputPaths,
9613
+ endEvent,
9614
+ result,
9615
+ span,
9616
+ startTime
9617
+ })
9359
9618
  })
9360
9619
  );
9361
9620
  this.unsubscribers.push(
9362
9621
  traceStreamingChannel(aiSDKChannels.agentGenerate, {
9363
9622
  name: "Agent.generate",
9364
9623
  type: "llm" /* LLM */,
9365
- extractInput: ([params]) => {
9366
- return {
9367
- input: processAISDKInput(params),
9368
- metadata: extractMetadataFromParams(params)
9369
- };
9370
- },
9371
- extractOutput: (result) => {
9624
+ extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9625
+ extractOutput: (result, endEvent) => {
9626
+ finalizeAISDKChildTracing(endEvent);
9372
9627
  return processAISDKOutput(result, denyOutputPaths);
9373
9628
  },
9374
- extractMetrics: (result, startTime) => {
9375
- const metrics = extractTokenMetrics(result);
9376
- if (startTime) {
9377
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9378
- }
9379
- return metrics;
9380
- },
9629
+ extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
9381
9630
  aggregateChunks: aggregateAISDKChunks
9382
9631
  })
9383
9632
  );
@@ -9385,88 +9634,506 @@ var AISDKPlugin = class extends BasePlugin {
9385
9634
  traceStreamingChannel(aiSDKChannels.agentStream, {
9386
9635
  name: "Agent.stream",
9387
9636
  type: "llm" /* LLM */,
9388
- extractInput: ([params]) => {
9389
- return {
9390
- input: processAISDKInput(params),
9391
- metadata: extractMetadataFromParams(params)
9392
- };
9393
- },
9394
- extractOutput: (result) => {
9637
+ extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9638
+ extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
9639
+ extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
9640
+ aggregateChunks: aggregateAISDKChunks,
9641
+ patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9642
+ denyOutputPaths,
9643
+ endEvent,
9644
+ result,
9645
+ span,
9646
+ startTime
9647
+ })
9648
+ })
9649
+ );
9650
+ this.unsubscribers.push(
9651
+ traceStreamingChannel(aiSDKChannels.toolLoopAgentGenerate, {
9652
+ name: "ToolLoopAgent.generate",
9653
+ type: "llm" /* LLM */,
9654
+ extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9655
+ extractOutput: (result, endEvent) => {
9656
+ finalizeAISDKChildTracing(endEvent);
9395
9657
  return processAISDKOutput(result, denyOutputPaths);
9396
9658
  },
9397
- extractMetrics: (result, startTime) => {
9398
- const metrics = extractTokenMetrics(result);
9399
- if (startTime) {
9400
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9401
- }
9402
- return metrics;
9403
- },
9659
+ extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
9404
9660
  aggregateChunks: aggregateAISDKChunks
9405
9661
  })
9406
9662
  );
9663
+ this.unsubscribers.push(
9664
+ traceStreamingChannel(aiSDKChannels.toolLoopAgentStream, {
9665
+ name: "ToolLoopAgent.stream",
9666
+ type: "llm" /* LLM */,
9667
+ extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
9668
+ extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
9669
+ extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
9670
+ aggregateChunks: aggregateAISDKChunks,
9671
+ patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
9672
+ denyOutputPaths,
9673
+ endEvent,
9674
+ result,
9675
+ span,
9676
+ startTime
9677
+ })
9678
+ })
9679
+ );
9407
9680
  }
9408
9681
  };
9409
9682
  function processAISDKInput(params) {
9410
9683
  if (!params) return params;
9411
- return processInputAttachments(params);
9684
+ const input = processInputAttachments(params);
9685
+ if (!input || typeof input !== "object" || Array.isArray(input)) {
9686
+ return input;
9687
+ }
9688
+ const { tools: _tools, ...rest } = input;
9689
+ return rest;
9690
+ }
9691
+ function prepareAISDKInput(params, event, span, denyOutputPaths) {
9692
+ const input = processAISDKInput(params);
9693
+ const metadata = extractMetadataFromParams(params, event.self);
9694
+ const childTracing = prepareAISDKChildTracing(
9695
+ params,
9696
+ event.self,
9697
+ span,
9698
+ denyOutputPaths
9699
+ );
9700
+ event.__braintrust_ai_sdk_model_wrapped = childTracing.modelWrapped;
9701
+ if (childTracing.cleanup) {
9702
+ event.__braintrust_ai_sdk_cleanup = childTracing.cleanup;
9703
+ }
9704
+ return {
9705
+ input,
9706
+ metadata
9707
+ };
9708
+ }
9709
+ function extractTopLevelAISDKMetrics(result, event, startTime) {
9710
+ const metrics = hasModelChildTracing(event) ? {} : extractTokenMetrics(result);
9711
+ if (startTime) {
9712
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9713
+ }
9714
+ return metrics;
9715
+ }
9716
+ function hasModelChildTracing(event) {
9717
+ return event?.__braintrust_ai_sdk_model_wrapped === true;
9412
9718
  }
9413
- function extractMetadataFromParams(params) {
9719
+ function extractMetadataFromParams(params, self) {
9414
9720
  const metadata = {
9415
9721
  braintrust: {
9416
9722
  integration_name: "ai-sdk",
9417
9723
  sdk_language: "typescript"
9418
9724
  }
9419
9725
  };
9420
- const { model, provider } = serializeModelWithProvider(params.model);
9726
+ 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;
9727
+ const { model, provider } = serializeModelWithProvider(
9728
+ params.model ?? agentModel
9729
+ );
9421
9730
  if (model) {
9422
9731
  metadata.model = model;
9423
9732
  }
9424
9733
  if (provider) {
9425
9734
  metadata.provider = provider;
9426
9735
  }
9736
+ const tools = serializeAISDKToolsForLogging(params.tools);
9737
+ if (tools) {
9738
+ metadata.tools = tools;
9739
+ }
9427
9740
  return metadata;
9428
9741
  }
9429
- function processAISDKOutput(output, denyOutputPaths) {
9430
- if (!output) return output;
9431
- const getterValues = extractGetterValues(output);
9432
- const merged = { ...output, ...getterValues };
9433
- return omit(merged, denyOutputPaths);
9434
- }
9435
- function extractTokenMetrics(result) {
9436
- const metrics = {};
9437
- let usage = result?.totalUsage || result?.usage;
9438
- if (!usage && result) {
9439
- try {
9440
- if ("totalUsage" in result && typeof result.totalUsage !== "function") {
9441
- usage = result.totalUsage;
9442
- } else if ("usage" in result && typeof result.usage !== "function") {
9443
- usage = result.usage;
9444
- }
9445
- } catch {
9742
+ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
9743
+ const cleanup = [];
9744
+ const patchedModels = /* @__PURE__ */ new WeakSet();
9745
+ const patchedTools = /* @__PURE__ */ new WeakSet();
9746
+ let modelWrapped = false;
9747
+ const patchModel = (model) => {
9748
+ const resolvedModel = resolveAISDKModel(model);
9749
+ if (!resolvedModel || typeof resolvedModel !== "object" || typeof resolvedModel.doGenerate !== "function" || patchedModels.has(resolvedModel) || resolvedModel[AUTO_PATCHED_MODEL]) {
9750
+ return;
9446
9751
  }
9447
- }
9448
- if (!usage) {
9449
- return metrics;
9450
- }
9451
- const promptTokens = firstNumber(
9452
- usage.inputTokens?.total,
9453
- usage.inputTokens,
9454
- usage.promptTokens,
9455
- usage.prompt_tokens
9456
- );
9457
- if (promptTokens !== void 0) {
9458
- metrics.prompt_tokens = promptTokens;
9459
- }
9460
- const completionTokens = firstNumber(
9461
- usage.outputTokens?.total,
9462
- usage.outputTokens,
9463
- usage.completionTokens,
9464
- usage.completion_tokens
9465
- );
9466
- if (completionTokens !== void 0) {
9467
- metrics.completion_tokens = completionTokens;
9468
- }
9469
- const totalTokens = firstNumber(
9752
+ patchedModels.add(resolvedModel);
9753
+ resolvedModel[AUTO_PATCHED_MODEL] = true;
9754
+ modelWrapped = true;
9755
+ const originalDoGenerate = resolvedModel.doGenerate;
9756
+ const originalDoStream = resolvedModel.doStream;
9757
+ const baseMetadata = buildAISDKChildMetadata(resolvedModel);
9758
+ resolvedModel.doGenerate = async function doGeneratePatched(options) {
9759
+ return parentSpan.traced(
9760
+ async (span) => {
9761
+ const result = await Reflect.apply(
9762
+ originalDoGenerate,
9763
+ resolvedModel,
9764
+ [options]
9765
+ );
9766
+ span.log({
9767
+ output: processAISDKOutput(result, denyOutputPaths),
9768
+ metrics: extractTokenMetrics(result),
9769
+ ...buildResolvedMetadataPayload(result)
9770
+ });
9771
+ return result;
9772
+ },
9773
+ {
9774
+ name: "doGenerate",
9775
+ spanAttributes: {
9776
+ type: "llm" /* LLM */
9777
+ },
9778
+ event: {
9779
+ input: processAISDKInput(options),
9780
+ metadata: baseMetadata
9781
+ }
9782
+ }
9783
+ );
9784
+ };
9785
+ if (originalDoStream) {
9786
+ resolvedModel.doStream = async function doStreamPatched(options) {
9787
+ const span = parentSpan.startSpan({
9788
+ name: "doStream",
9789
+ spanAttributes: {
9790
+ type: "llm" /* LLM */
9791
+ },
9792
+ event: {
9793
+ input: processAISDKInput(options),
9794
+ metadata: baseMetadata
9795
+ }
9796
+ });
9797
+ const result = await withCurrent(
9798
+ span,
9799
+ () => Reflect.apply(originalDoStream, resolvedModel, [options])
9800
+ );
9801
+ const output = {};
9802
+ let text = "";
9803
+ let reasoning = "";
9804
+ const toolCalls = [];
9805
+ let object = void 0;
9806
+ const transformStream = new TransformStream({
9807
+ transform(chunk, controller) {
9808
+ switch (chunk.type) {
9809
+ case "text-delta":
9810
+ text += extractTextDelta(chunk);
9811
+ break;
9812
+ case "reasoning-delta":
9813
+ if (chunk.delta) {
9814
+ reasoning += chunk.delta;
9815
+ } else if (chunk.text) {
9816
+ reasoning += chunk.text;
9817
+ }
9818
+ break;
9819
+ case "tool-call":
9820
+ toolCalls.push(chunk);
9821
+ break;
9822
+ case "object":
9823
+ object = chunk.object;
9824
+ break;
9825
+ case "raw":
9826
+ if (chunk.rawValue) {
9827
+ const rawVal = chunk.rawValue;
9828
+ if (rawVal.delta?.content) {
9829
+ text += rawVal.delta.content;
9830
+ } else if (rawVal.choices?.[0]?.delta?.content) {
9831
+ text += rawVal.choices[0].delta.content;
9832
+ } else if (typeof rawVal.text === "string") {
9833
+ text += rawVal.text;
9834
+ } else if (typeof rawVal.content === "string") {
9835
+ text += rawVal.content;
9836
+ }
9837
+ }
9838
+ break;
9839
+ case "finish":
9840
+ output.text = text;
9841
+ output.reasoning = reasoning;
9842
+ output.toolCalls = toolCalls;
9843
+ output.finishReason = chunk.finishReason;
9844
+ output.usage = chunk.usage;
9845
+ if (object !== void 0) {
9846
+ output.object = object;
9847
+ }
9848
+ span.log({
9849
+ output: processAISDKOutput(
9850
+ output,
9851
+ denyOutputPaths
9852
+ ),
9853
+ metrics: extractTokenMetrics(output),
9854
+ ...buildResolvedMetadataPayload(output)
9855
+ });
9856
+ span.end();
9857
+ break;
9858
+ }
9859
+ controller.enqueue(chunk);
9860
+ }
9861
+ });
9862
+ return {
9863
+ ...result,
9864
+ stream: result.stream.pipeThrough(transformStream)
9865
+ };
9866
+ };
9867
+ }
9868
+ cleanup.push(() => {
9869
+ resolvedModel.doGenerate = originalDoGenerate;
9870
+ if (originalDoStream) {
9871
+ resolvedModel.doStream = originalDoStream;
9872
+ }
9873
+ delete resolvedModel[AUTO_PATCHED_MODEL];
9874
+ });
9875
+ };
9876
+ const patchTool = (tool, name) => {
9877
+ if (tool == null || typeof tool !== "object" || !("execute" in tool) || typeof tool.execute !== "function" || patchedTools.has(tool) || tool[AUTO_PATCHED_TOOL]) {
9878
+ return;
9879
+ }
9880
+ patchedTools.add(tool);
9881
+ tool[AUTO_PATCHED_TOOL] = true;
9882
+ const originalExecute = tool.execute;
9883
+ tool.execute = function executePatched(...args) {
9884
+ const result = Reflect.apply(originalExecute, this, args);
9885
+ if (isAsyncGenerator(result)) {
9886
+ return (async function* () {
9887
+ const span = parentSpan.startSpan({
9888
+ name,
9889
+ spanAttributes: {
9890
+ type: "tool" /* TOOL */
9891
+ }
9892
+ });
9893
+ span.log({ input: args.length === 1 ? args[0] : args });
9894
+ try {
9895
+ let lastValue;
9896
+ for await (const value of result) {
9897
+ lastValue = value;
9898
+ yield value;
9899
+ }
9900
+ span.log({ output: lastValue });
9901
+ } catch (error) {
9902
+ span.log({
9903
+ error: error instanceof Error ? error.message : String(error)
9904
+ });
9905
+ throw error;
9906
+ } finally {
9907
+ span.end();
9908
+ }
9909
+ })();
9910
+ }
9911
+ return parentSpan.traced(
9912
+ async (span) => {
9913
+ span.log({ input: args.length === 1 ? args[0] : args });
9914
+ const awaitedResult = await result;
9915
+ span.log({ output: awaitedResult });
9916
+ return awaitedResult;
9917
+ },
9918
+ {
9919
+ name,
9920
+ spanAttributes: {
9921
+ type: "tool" /* TOOL */
9922
+ }
9923
+ }
9924
+ );
9925
+ };
9926
+ cleanup.push(() => {
9927
+ tool.execute = originalExecute;
9928
+ delete tool[AUTO_PATCHED_TOOL];
9929
+ });
9930
+ };
9931
+ const patchTools = (tools) => {
9932
+ if (!tools) {
9933
+ return;
9934
+ }
9935
+ const inferName = (tool, fallback) => tool && (tool.name || tool.toolName || tool.id) || fallback;
9936
+ if (Array.isArray(tools)) {
9937
+ tools.forEach(
9938
+ (tool, index) => patchTool(tool, inferName(tool, `tool[${index}]`))
9939
+ );
9940
+ return;
9941
+ }
9942
+ for (const [key, tool] of Object.entries(tools)) {
9943
+ patchTool(tool, key);
9944
+ }
9945
+ };
9946
+ if (params && typeof params === "object") {
9947
+ patchModel(params.model);
9948
+ patchTools(params.tools);
9949
+ }
9950
+ if (self && typeof self === "object") {
9951
+ const selfRecord = self;
9952
+ if (selfRecord.model !== void 0) {
9953
+ patchModel(selfRecord.model);
9954
+ }
9955
+ if (selfRecord.settings && typeof selfRecord.settings === "object") {
9956
+ if (selfRecord.settings.model !== void 0) {
9957
+ patchModel(selfRecord.settings.model);
9958
+ }
9959
+ if (selfRecord.settings.tools !== void 0) {
9960
+ patchTools(selfRecord.settings.tools);
9961
+ }
9962
+ }
9963
+ }
9964
+ return {
9965
+ cleanup: cleanup.length > 0 ? () => {
9966
+ while (cleanup.length > 0) {
9967
+ cleanup.pop()?.();
9968
+ }
9969
+ } : void 0,
9970
+ modelWrapped
9971
+ };
9972
+ }
9973
+ function finalizeAISDKChildTracing(event) {
9974
+ const cleanup = event?.__braintrust_ai_sdk_cleanup;
9975
+ if (event && typeof cleanup === "function") {
9976
+ cleanup();
9977
+ delete event.__braintrust_ai_sdk_cleanup;
9978
+ }
9979
+ }
9980
+ function patchAISDKStreamingResult(args) {
9981
+ const { denyOutputPaths, endEvent, result, span, startTime } = args;
9982
+ if (!result || typeof result !== "object") {
9983
+ return false;
9984
+ }
9985
+ const resultRecord = result;
9986
+ if (!isReadableStreamLike(resultRecord.baseStream)) {
9987
+ return false;
9988
+ }
9989
+ let firstChunkTime;
9990
+ const wrappedBaseStream = resultRecord.baseStream.pipeThrough(
9991
+ new TransformStream({
9992
+ transform(chunk, controller) {
9993
+ if (firstChunkTime === void 0) {
9994
+ firstChunkTime = getCurrentUnixTimestamp();
9995
+ }
9996
+ controller.enqueue(chunk);
9997
+ },
9998
+ async flush() {
9999
+ const metrics = extractTopLevelAISDKMetrics(result, endEvent);
10000
+ if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
10001
+ metrics.time_to_first_token = firstChunkTime - startTime;
10002
+ }
10003
+ const output = await processAISDKStreamingOutput(
10004
+ result,
10005
+ denyOutputPaths
10006
+ );
10007
+ const metadata = buildResolvedMetadataPayload(result).metadata;
10008
+ span.log({
10009
+ output,
10010
+ ...metadata ? { metadata } : {},
10011
+ metrics
10012
+ });
10013
+ finalizeAISDKChildTracing(endEvent);
10014
+ span.end();
10015
+ }
10016
+ })
10017
+ );
10018
+ Object.defineProperty(resultRecord, "baseStream", {
10019
+ configurable: true,
10020
+ enumerable: true,
10021
+ value: wrappedBaseStream,
10022
+ writable: true
10023
+ });
10024
+ return true;
10025
+ }
10026
+ function isReadableStreamLike(value) {
10027
+ return value != null && typeof value === "object" && typeof value.pipeThrough === "function";
10028
+ }
10029
+ async function processAISDKStreamingOutput(result, denyOutputPaths) {
10030
+ const output = processAISDKOutput(result, denyOutputPaths);
10031
+ if (!output || typeof output !== "object") {
10032
+ return output;
10033
+ }
10034
+ const outputRecord = output;
10035
+ try {
10036
+ if ("text" in result && typeof result.text === "string") {
10037
+ outputRecord.text = result.text;
10038
+ }
10039
+ } catch {
10040
+ }
10041
+ try {
10042
+ if ("object" in result) {
10043
+ const resolvedObject = await Promise.resolve(result.object);
10044
+ if (resolvedObject !== void 0) {
10045
+ outputRecord.object = resolvedObject;
10046
+ }
10047
+ }
10048
+ } catch {
10049
+ }
10050
+ return outputRecord;
10051
+ }
10052
+ function buildAISDKChildMetadata(model) {
10053
+ const { model: modelId, provider } = serializeModelWithProvider(model);
10054
+ return {
10055
+ ...modelId ? { model: modelId } : {},
10056
+ ...provider ? { provider } : {},
10057
+ braintrust: {
10058
+ integration_name: "ai-sdk",
10059
+ sdk_language: "typescript"
10060
+ }
10061
+ };
10062
+ }
10063
+ function buildResolvedMetadataPayload(result) {
10064
+ const gatewayInfo = extractGatewayRoutingInfo(result);
10065
+ const metadata = {};
10066
+ if (gatewayInfo?.provider) {
10067
+ metadata.provider = gatewayInfo.provider;
10068
+ }
10069
+ if (gatewayInfo?.model) {
10070
+ metadata.model = gatewayInfo.model;
10071
+ }
10072
+ if (result.finishReason !== void 0) {
10073
+ metadata.finish_reason = result.finishReason;
10074
+ }
10075
+ return Object.keys(metadata).length > 0 ? { metadata } : {};
10076
+ }
10077
+ function resolveAISDKModel(model) {
10078
+ if (typeof model !== "string") {
10079
+ return model;
10080
+ }
10081
+ const provider = globalThis.AI_SDK_DEFAULT_PROVIDER ?? null;
10082
+ if (provider && typeof provider.languageModel === "function") {
10083
+ return provider.languageModel(model);
10084
+ }
10085
+ return model;
10086
+ }
10087
+ function extractTextDelta(chunk) {
10088
+ if (typeof chunk.textDelta === "string") return chunk.textDelta;
10089
+ if (typeof chunk.delta === "string") return chunk.delta;
10090
+ if (typeof chunk.text === "string") return chunk.text;
10091
+ if (typeof chunk.content === "string") return chunk.content;
10092
+ return "";
10093
+ }
10094
+ function isAsyncGenerator(value) {
10095
+ return value != null && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function" && typeof value.next === "function" && typeof value.return === "function" && typeof value.throw === "function";
10096
+ }
10097
+ function processAISDKOutput(output, denyOutputPaths) {
10098
+ if (!output) return output;
10099
+ const merged = extractSerializableOutputFields(output);
10100
+ return normalizeAISDKLoggedOutput(omit(merged, denyOutputPaths));
10101
+ }
10102
+ function extractTokenMetrics(result) {
10103
+ const metrics = {};
10104
+ let usage = result?.totalUsage || result?.usage;
10105
+ if (!usage && result) {
10106
+ try {
10107
+ if ("totalUsage" in result && typeof result.totalUsage !== "function") {
10108
+ usage = result.totalUsage;
10109
+ } else if ("usage" in result && typeof result.usage !== "function") {
10110
+ usage = result.usage;
10111
+ }
10112
+ } catch {
10113
+ }
10114
+ }
10115
+ if (!usage) {
10116
+ return metrics;
10117
+ }
10118
+ const promptTokens = firstNumber(
10119
+ usage.inputTokens?.total,
10120
+ usage.inputTokens,
10121
+ usage.promptTokens,
10122
+ usage.prompt_tokens
10123
+ );
10124
+ if (promptTokens !== void 0) {
10125
+ metrics.prompt_tokens = promptTokens;
10126
+ }
10127
+ const completionTokens = firstNumber(
10128
+ usage.outputTokens?.total,
10129
+ usage.outputTokens,
10130
+ usage.completionTokens,
10131
+ usage.completion_tokens
10132
+ );
10133
+ if (completionTokens !== void 0) {
10134
+ metrics.completion_tokens = completionTokens;
10135
+ }
10136
+ const totalTokens = firstNumber(
9470
10137
  usage.totalTokens,
9471
10138
  usage.tokens,
9472
10139
  usage.total_tokens
@@ -9480,12 +10147,14 @@ function extractTokenMetrics(result) {
9480
10147
  }
9481
10148
  return metrics;
9482
10149
  }
9483
- function aggregateAISDKChunks(chunks) {
10150
+ function aggregateAISDKChunks(chunks, _result, endEvent) {
9484
10151
  const lastChunk = chunks[chunks.length - 1];
9485
10152
  const output = {};
9486
10153
  let metrics = {};
10154
+ let metadata;
9487
10155
  if (lastChunk) {
9488
- metrics = extractTokenMetrics(lastChunk);
10156
+ metrics = hasModelChildTracing(endEvent) ? {} : extractTokenMetrics(lastChunk);
10157
+ metadata = buildResolvedMetadataPayload(lastChunk).metadata;
9489
10158
  if (lastChunk.text !== void 0) {
9490
10159
  output.text = lastChunk.text;
9491
10160
  }
@@ -9499,7 +10168,8 @@ function aggregateAISDKChunks(chunks) {
9499
10168
  output.toolCalls = lastChunk.toolCalls;
9500
10169
  }
9501
10170
  }
9502
- return { output, metrics };
10171
+ finalizeAISDKChildTracing(endEvent);
10172
+ return { output, metrics, metadata };
9503
10173
  }
9504
10174
  function extractGetterValues(obj) {
9505
10175
  const getterValues = {};
@@ -9519,7 +10189,7 @@ function extractGetterValues(obj) {
9519
10189
  ];
9520
10190
  for (const name of getterNames) {
9521
10191
  try {
9522
- if (obj && name in obj && typeof obj[name] !== "function") {
10192
+ if (obj && name in obj && isSerializableOutputValue(obj[name])) {
9523
10193
  getterValues[name] = obj[name];
9524
10194
  }
9525
10195
  } catch {
@@ -9527,6 +10197,47 @@ function extractGetterValues(obj) {
9527
10197
  }
9528
10198
  return getterValues;
9529
10199
  }
10200
+ function extractSerializableOutputFields(output) {
10201
+ const serialized = {};
10202
+ const directFieldNames = [
10203
+ "steps",
10204
+ "request",
10205
+ "responseMessages",
10206
+ "warnings",
10207
+ "rawResponse",
10208
+ "response",
10209
+ "providerMetadata",
10210
+ "experimental_providerMetadata"
10211
+ ];
10212
+ for (const name of directFieldNames) {
10213
+ try {
10214
+ const value = output?.[name];
10215
+ if (isSerializableOutputValue(value)) {
10216
+ serialized[name] = value;
10217
+ }
10218
+ } catch {
10219
+ }
10220
+ }
10221
+ return {
10222
+ ...serialized,
10223
+ ...extractGetterValues(output)
10224
+ };
10225
+ }
10226
+ function isSerializableOutputValue(value) {
10227
+ if (typeof value === "function") {
10228
+ return false;
10229
+ }
10230
+ if (value && typeof value === "object" && typeof value.then === "function") {
10231
+ return false;
10232
+ }
10233
+ if (value && typeof value === "object" && typeof value.getReader === "function") {
10234
+ return false;
10235
+ }
10236
+ if (value && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function") {
10237
+ return false;
10238
+ }
10239
+ return true;
10240
+ }
9530
10241
  function serializeModelWithProvider(model) {
9531
10242
  const modelId = typeof model === "string" ? model : model?.modelId;
9532
10243
  const explicitProvider = typeof model === "object" ? model?.provider : void 0;
@@ -9552,6 +10263,25 @@ function parseGatewayModelString(modelString) {
9552
10263
  }
9553
10264
  return { model: modelString };
9554
10265
  }
10266
+ function extractGatewayRoutingInfo(result) {
10267
+ if (result?.steps && Array.isArray(result.steps) && result.steps.length > 0) {
10268
+ const routing2 = result.steps[0]?.providerMetadata?.gateway?.routing;
10269
+ if (routing2) {
10270
+ return {
10271
+ provider: routing2.resolvedProvider || routing2.finalProvider,
10272
+ model: routing2.resolvedProviderApiModelId
10273
+ };
10274
+ }
10275
+ }
10276
+ const routing = result?.providerMetadata?.gateway?.routing;
10277
+ if (routing) {
10278
+ return {
10279
+ provider: routing.resolvedProvider || routing.finalProvider,
10280
+ model: routing.resolvedProviderApiModelId
10281
+ };
10282
+ }
10283
+ return null;
10284
+ }
9555
10285
  function extractCostFromResult(result) {
9556
10286
  if (result?.steps && Array.isArray(result.steps) && result.steps.length > 0) {
9557
10287
  let totalCost = 0;
@@ -10282,47 +11012,1069 @@ function tryToDict(obj) {
10282
11012
  return null;
10283
11013
  }
10284
11014
 
10285
- // src/instrumentation/braintrust-plugin.ts
10286
- var BraintrustPlugin = class extends BasePlugin {
10287
- config;
10288
- openaiPlugin = null;
10289
- anthropicPlugin = null;
10290
- aiSDKPlugin = null;
10291
- claudeAgentSDKPlugin = null;
10292
- googleGenAIPlugin = null;
10293
- constructor(config = {}) {
10294
- super();
10295
- this.config = config;
11015
+ // src/instrumentation/plugins/openrouter-channels.ts
11016
+ var openRouterChannels = defineChannels("@openrouter/sdk", {
11017
+ chatSend: channel({
11018
+ channelName: "chat.send",
11019
+ kind: "async"
11020
+ }),
11021
+ embeddingsGenerate: channel({
11022
+ channelName: "embeddings.generate",
11023
+ kind: "async"
11024
+ }),
11025
+ betaResponsesSend: channel({
11026
+ channelName: "beta.responses.send",
11027
+ kind: "async"
11028
+ }),
11029
+ callModel: channel({
11030
+ channelName: "callModel",
11031
+ kind: "sync-stream"
11032
+ }),
11033
+ toolExecute: channel({
11034
+ channelName: "tool.execute",
11035
+ kind: "async"
11036
+ })
11037
+ });
11038
+
11039
+ // src/openrouter-utils.ts
11040
+ var TOKEN_NAME_MAP2 = {
11041
+ promptTokens: "prompt_tokens",
11042
+ inputTokens: "prompt_tokens",
11043
+ completionTokens: "completion_tokens",
11044
+ outputTokens: "completion_tokens",
11045
+ totalTokens: "tokens",
11046
+ prompt_tokens: "prompt_tokens",
11047
+ input_tokens: "prompt_tokens",
11048
+ completion_tokens: "completion_tokens",
11049
+ output_tokens: "completion_tokens",
11050
+ total_tokens: "tokens"
11051
+ };
11052
+ var TOKEN_DETAIL_PREFIX_MAP = {
11053
+ promptTokensDetails: "prompt",
11054
+ inputTokensDetails: "prompt",
11055
+ completionTokensDetails: "completion",
11056
+ outputTokensDetails: "completion",
11057
+ costDetails: "cost",
11058
+ prompt_tokens_details: "prompt",
11059
+ input_tokens_details: "prompt",
11060
+ completion_tokens_details: "completion",
11061
+ output_tokens_details: "completion",
11062
+ cost_details: "cost"
11063
+ };
11064
+ function camelToSnake(value) {
11065
+ return value.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`);
11066
+ }
11067
+ function parseOpenRouterMetricsFromUsage(usage) {
11068
+ if (!isObject(usage)) {
11069
+ return {};
10296
11070
  }
10297
- onEnable() {
10298
- const integrations = this.config.integrations || {};
10299
- if (integrations.openai !== false) {
10300
- this.openaiPlugin = new OpenAIPlugin();
10301
- this.openaiPlugin.enable();
10302
- }
10303
- if (integrations.anthropic !== false) {
10304
- this.anthropicPlugin = new AnthropicPlugin();
10305
- this.anthropicPlugin.enable();
11071
+ const metrics = {};
11072
+ for (const [name, value] of Object.entries(usage)) {
11073
+ if (typeof value === "number") {
11074
+ metrics[TOKEN_NAME_MAP2[name] || camelToSnake(name)] = value;
11075
+ continue;
10306
11076
  }
10307
- if (integrations.aisdk !== false && integrations.vercel !== false) {
10308
- this.aiSDKPlugin = new AISDKPlugin();
10309
- this.aiSDKPlugin.enable();
11077
+ if (!isObject(value)) {
11078
+ continue;
10310
11079
  }
10311
- if (integrations.claudeAgentSDK !== false) {
10312
- this.claudeAgentSDKPlugin = new ClaudeAgentSDKPlugin();
10313
- this.claudeAgentSDKPlugin.enable();
11080
+ const prefix = TOKEN_DETAIL_PREFIX_MAP[name];
11081
+ if (!prefix) {
11082
+ continue;
10314
11083
  }
10315
- if (integrations.googleGenAI !== false && integrations.google !== false) {
10316
- this.googleGenAIPlugin = new GoogleGenAIPlugin();
10317
- this.googleGenAIPlugin.enable();
11084
+ for (const [nestedName, nestedValue] of Object.entries(value)) {
11085
+ if (typeof nestedValue !== "number") {
11086
+ continue;
11087
+ }
11088
+ metrics[`${prefix}_${camelToSnake(nestedName)}`] = nestedValue;
10318
11089
  }
10319
11090
  }
10320
- onDisable() {
10321
- if (this.openaiPlugin) {
10322
- this.openaiPlugin.disable();
10323
- this.openaiPlugin = null;
10324
- }
10325
- if (this.anthropicPlugin) {
11091
+ return metrics;
11092
+ }
11093
+ function extractOpenRouterUsageMetadata(usage) {
11094
+ if (!isObject(usage)) {
11095
+ return void 0;
11096
+ }
11097
+ const metadata = {};
11098
+ if (typeof usage.isByok === "boolean") {
11099
+ metadata.is_byok = usage.isByok;
11100
+ } else if (typeof usage.is_byok === "boolean") {
11101
+ metadata.is_byok = usage.is_byok;
11102
+ }
11103
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
11104
+ }
11105
+
11106
+ // src/openrouter-logging.ts
11107
+ var OMITTED_OPENROUTER_KEYS = /* @__PURE__ */ new Set([
11108
+ "execute",
11109
+ "render",
11110
+ "nextTurnParams",
11111
+ "requireApproval"
11112
+ ]);
11113
+ function parseOpenRouterModelString(model) {
11114
+ if (typeof model !== "string") {
11115
+ return { model };
11116
+ }
11117
+ const slashIndex = model.indexOf("/");
11118
+ if (slashIndex > 0 && slashIndex < model.length - 1) {
11119
+ return {
11120
+ provider: model.substring(0, slashIndex),
11121
+ model: model.substring(slashIndex + 1)
11122
+ };
11123
+ }
11124
+ return { model };
11125
+ }
11126
+ function isZodSchema2(value) {
11127
+ return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
11128
+ }
11129
+ function serializeZodSchema2(schema) {
11130
+ try {
11131
+ return zodToJsonSchema(schema);
11132
+ } catch {
11133
+ return {
11134
+ type: "object",
11135
+ description: "Zod schema (conversion failed)"
11136
+ };
11137
+ }
11138
+ }
11139
+ function serializeOpenRouterTool(tool) {
11140
+ if (!isObject(tool)) {
11141
+ return tool;
11142
+ }
11143
+ const serialized = {};
11144
+ for (const [key, value] of Object.entries(tool)) {
11145
+ if (OMITTED_OPENROUTER_KEYS.has(key)) {
11146
+ continue;
11147
+ }
11148
+ if (key === "function" && isObject(value)) {
11149
+ serialized.function = sanitizeOpenRouterLoggedValue(value);
11150
+ continue;
11151
+ }
11152
+ serialized[key] = sanitizeOpenRouterLoggedValue(value);
11153
+ }
11154
+ return serialized;
11155
+ }
11156
+ function serializeOpenRouterToolsForLogging(tools) {
11157
+ if (!Array.isArray(tools)) {
11158
+ return void 0;
11159
+ }
11160
+ return tools.map((tool) => serializeOpenRouterTool(tool));
11161
+ }
11162
+ function sanitizeOpenRouterLoggedValue(value) {
11163
+ if (isZodSchema2(value)) {
11164
+ return serializeZodSchema2(value);
11165
+ }
11166
+ if (typeof value === "function") {
11167
+ return "[Function]";
11168
+ }
11169
+ if (Array.isArray(value)) {
11170
+ return value.map((entry) => sanitizeOpenRouterLoggedValue(entry));
11171
+ }
11172
+ if (!isObject(value)) {
11173
+ return value;
11174
+ }
11175
+ const sanitized = {};
11176
+ for (const [key, entry] of Object.entries(value)) {
11177
+ if (OMITTED_OPENROUTER_KEYS.has(key)) {
11178
+ continue;
11179
+ }
11180
+ if (key === "tools" && Array.isArray(entry)) {
11181
+ sanitized.tools = serializeOpenRouterToolsForLogging(entry);
11182
+ continue;
11183
+ }
11184
+ sanitized[key] = sanitizeOpenRouterLoggedValue(entry);
11185
+ }
11186
+ return sanitized;
11187
+ }
11188
+ function buildOpenRouterMetadata(metadata, httpReferer, xTitle) {
11189
+ const sanitized = sanitizeOpenRouterLoggedValue(metadata);
11190
+ const metadataRecord = isObject(sanitized) ? sanitized : {};
11191
+ const { model, provider: providerRouting, ...rest } = metadataRecord;
11192
+ const normalizedModel = parseOpenRouterModelString(model);
11193
+ return {
11194
+ ...rest,
11195
+ ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
11196
+ ...providerRouting !== void 0 ? { providerRouting } : {},
11197
+ ...httpReferer !== void 0 ? { httpReferer } : {},
11198
+ ...xTitle !== void 0 ? { xTitle } : {},
11199
+ provider: normalizedModel.provider || "openrouter"
11200
+ };
11201
+ }
11202
+ function buildOpenRouterEmbeddingMetadata(metadata, httpReferer, xTitle) {
11203
+ const normalized = buildOpenRouterMetadata(metadata, httpReferer, xTitle);
11204
+ return typeof normalized.model === "string" ? {
11205
+ ...normalized,
11206
+ embedding_model: normalized.model
11207
+ } : normalized;
11208
+ }
11209
+ function extractOpenRouterCallModelInput(request) {
11210
+ return isObject(request) && "input" in request ? sanitizeOpenRouterLoggedValue(request.input) : void 0;
11211
+ }
11212
+ function extractOpenRouterCallModelMetadata(request) {
11213
+ if (!isObject(request)) {
11214
+ return { provider: "openrouter" };
11215
+ }
11216
+ const { input: _input, ...metadata } = request;
11217
+ return buildOpenRouterMetadata(metadata, void 0, void 0);
11218
+ }
11219
+ function extractOpenRouterResponseMetadata(result) {
11220
+ if (!isObject(result)) {
11221
+ return void 0;
11222
+ }
11223
+ const { output: _output, data: _data, usage, ...metadata } = result;
11224
+ const sanitized = sanitizeOpenRouterLoggedValue(metadata);
11225
+ const metadataRecord = isObject(sanitized) ? sanitized : {};
11226
+ const { model, provider, ...rest } = metadataRecord;
11227
+ const normalizedModel = parseOpenRouterModelString(model);
11228
+ const normalizedProvider = (typeof provider === "string" ? provider : void 0) || normalizedModel.provider;
11229
+ const usageMetadata = extractOpenRouterUsageMetadata(usage);
11230
+ const combined = {
11231
+ ...rest,
11232
+ ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
11233
+ ...usageMetadata || {},
11234
+ ...normalizedProvider !== void 0 ? { provider: normalizedProvider } : {}
11235
+ };
11236
+ return Object.keys(combined).length > 0 ? combined : void 0;
11237
+ }
11238
+ function extractOpenRouterResponseOutput(response, fallbackOutput) {
11239
+ if (isObject(response) && "output" in response && response.output !== void 0) {
11240
+ return sanitizeOpenRouterLoggedValue(response.output);
11241
+ }
11242
+ if (fallbackOutput !== void 0) {
11243
+ return sanitizeOpenRouterLoggedValue(fallbackOutput);
11244
+ }
11245
+ return void 0;
11246
+ }
11247
+
11248
+ // src/openrouter-tool-wrapping.ts
11249
+ var OPENROUTER_WRAPPED_TOOL = Symbol("braintrust.openrouter.wrappedTool");
11250
+ var OPENROUTER_WRAPPED_CALL_MODEL_RESULT = Symbol(
11251
+ "braintrust.openrouter.wrappedCallModelResult"
11252
+ );
11253
+ var OPENROUTER_CALL_MODEL_STREAM_METHODS = [
11254
+ "getFullResponsesStream",
11255
+ "getItemsStream",
11256
+ "getNewMessagesStream",
11257
+ "getReasoningStream",
11258
+ "getTextStream",
11259
+ "getToolCallsStream",
11260
+ "getToolStream"
11261
+ ];
11262
+ var OPENROUTER_CALL_MODEL_CONTEXT_METHODS = [
11263
+ "cancel",
11264
+ "getPendingToolCalls",
11265
+ "getState",
11266
+ "getToolCalls",
11267
+ "requiresApproval"
11268
+ ];
11269
+ function patchOpenRouterCallModelRequestTools(request) {
11270
+ if (!Array.isArray(request.tools) || request.tools.length === 0) {
11271
+ return void 0;
11272
+ }
11273
+ const originalTools = request.tools;
11274
+ const wrappedTools = originalTools.map((tool) => wrapOpenRouterTool(tool));
11275
+ const didPatch = wrappedTools.some(
11276
+ (tool, index) => tool !== originalTools[index]
11277
+ );
11278
+ if (!didPatch) {
11279
+ return void 0;
11280
+ }
11281
+ request.tools = wrappedTools;
11282
+ return () => {
11283
+ request.tools = originalTools;
11284
+ };
11285
+ }
11286
+ function patchOpenRouterCallModelResult(span, result, request) {
11287
+ if (!isObject(result) || isWrappedCallModelResult(result)) {
11288
+ return false;
11289
+ }
11290
+ const resultLike = result;
11291
+ const hasInstrumentableMethod = typeof resultLike.getResponse === "function" || typeof resultLike.getText === "function" || OPENROUTER_CALL_MODEL_STREAM_METHODS.some(
11292
+ (methodName) => typeof resultLike[methodName] === "function"
11293
+ );
11294
+ if (!hasInstrumentableMethod) {
11295
+ return false;
11296
+ }
11297
+ Object.defineProperty(resultLike, OPENROUTER_WRAPPED_CALL_MODEL_RESULT, {
11298
+ value: true,
11299
+ enumerable: false,
11300
+ configurable: false
11301
+ });
11302
+ const originalGetResponse = typeof resultLike.getResponse === "function" ? resultLike.getResponse.bind(resultLike) : void 0;
11303
+ const originalGetInitialResponse = typeof resultLike.getInitialResponse === "function" ? resultLike.getInitialResponse.bind(resultLike) : void 0;
11304
+ const originalMakeFollowupRequest = typeof resultLike.makeFollowupRequest === "function" ? resultLike.makeFollowupRequest.bind(resultLike) : void 0;
11305
+ let ended = false;
11306
+ let tracedTurnCount = 0;
11307
+ const endSpanWithResult = async (response, fallbackOutput) => {
11308
+ if (ended) {
11309
+ return;
11310
+ }
11311
+ ended = true;
11312
+ const finalResponse = getFinalOpenRouterCallModelResponse(
11313
+ resultLike,
11314
+ response
11315
+ );
11316
+ if (finalResponse) {
11317
+ const rounds = getOpenRouterCallModelRounds(resultLike);
11318
+ const metadata = extractOpenRouterCallModelResultMetadata(
11319
+ finalResponse,
11320
+ rounds.length + 1
11321
+ );
11322
+ span.log({
11323
+ output: extractOpenRouterResponseOutput(finalResponse, fallbackOutput),
11324
+ ...metadata ? { metadata } : {},
11325
+ metrics: aggregateOpenRouterCallModelMetrics(rounds, finalResponse)
11326
+ });
11327
+ span.end();
11328
+ return;
11329
+ }
11330
+ if (fallbackOutput !== void 0) {
11331
+ span.log({
11332
+ output: fallbackOutput
11333
+ });
11334
+ }
11335
+ span.end();
11336
+ };
11337
+ const endSpanWithError = (error) => {
11338
+ if (ended) {
11339
+ return;
11340
+ }
11341
+ ended = true;
11342
+ span.log({
11343
+ error: normalizeError(error).message
11344
+ });
11345
+ span.end();
11346
+ };
11347
+ const finalizeFromResponse = async (fallbackOutput) => {
11348
+ if (!originalGetResponse) {
11349
+ await endSpanWithResult(void 0, fallbackOutput);
11350
+ return;
11351
+ }
11352
+ try {
11353
+ await endSpanWithResult(await originalGetResponse(), fallbackOutput);
11354
+ } catch {
11355
+ await endSpanWithResult(void 0, fallbackOutput);
11356
+ }
11357
+ };
11358
+ if (originalGetResponse) {
11359
+ resultLike.getResponse = async (...args) => {
11360
+ return await withCurrent(span, async () => {
11361
+ try {
11362
+ const response = await originalGetResponse(...args);
11363
+ await endSpanWithResult(response);
11364
+ return response;
11365
+ } catch (error) {
11366
+ endSpanWithError(error);
11367
+ throw error;
11368
+ }
11369
+ });
11370
+ };
11371
+ }
11372
+ if (typeof resultLike.getText === "function") {
11373
+ const originalGetText = resultLike.getText.bind(resultLike);
11374
+ resultLike.getText = async (...args) => {
11375
+ return await withCurrent(span, async () => {
11376
+ try {
11377
+ const text = await originalGetText(...args);
11378
+ await finalizeFromResponse(text);
11379
+ return text;
11380
+ } catch (error) {
11381
+ endSpanWithError(error);
11382
+ throw error;
11383
+ }
11384
+ });
11385
+ };
11386
+ }
11387
+ for (const methodName of OPENROUTER_CALL_MODEL_CONTEXT_METHODS) {
11388
+ if (typeof resultLike[methodName] !== "function") {
11389
+ continue;
11390
+ }
11391
+ const originalMethod = resultLike[methodName];
11392
+ resultLike[methodName] = async (...args) => {
11393
+ return await withCurrent(span, async () => {
11394
+ return await originalMethod.apply(resultLike, args);
11395
+ });
11396
+ };
11397
+ }
11398
+ for (const methodName of OPENROUTER_CALL_MODEL_STREAM_METHODS) {
11399
+ if (typeof resultLike[methodName] !== "function") {
11400
+ continue;
11401
+ }
11402
+ const originalMethod = resultLike[methodName];
11403
+ resultLike[methodName] = (...args) => {
11404
+ const stream = withCurrent(
11405
+ span,
11406
+ () => originalMethod.apply(resultLike, args)
11407
+ );
11408
+ if (!isAsyncIterable2(stream)) {
11409
+ return stream;
11410
+ }
11411
+ return wrapAsyncIterableWithSpan({
11412
+ finalize: finalizeFromResponse,
11413
+ iteratorFactory: () => stream[Symbol.asyncIterator](),
11414
+ onError: endSpanWithError,
11415
+ span
11416
+ });
11417
+ };
11418
+ }
11419
+ if (originalGetInitialResponse) {
11420
+ let initialTurnTraced = false;
11421
+ resultLike.getInitialResponse = async (...args) => {
11422
+ if (initialTurnTraced) {
11423
+ return await withCurrent(span, async () => {
11424
+ return await originalGetInitialResponse(...args);
11425
+ });
11426
+ }
11427
+ initialTurnTraced = true;
11428
+ const resolvedRequest = getOpenRouterResolvedRequest(resultLike, request);
11429
+ const childSpan = startOpenRouterCallModelTurnSpan({
11430
+ request: resolvedRequest,
11431
+ step: tracedTurnCount + 1,
11432
+ stepType: tracedTurnCount === 0 ? "initial" : "continue"
11433
+ });
11434
+ return await withCurrent(childSpan, async () => {
11435
+ try {
11436
+ const response = await originalGetInitialResponse(...args);
11437
+ tracedTurnCount++;
11438
+ finishOpenRouterCallModelTurnSpan({
11439
+ response,
11440
+ step: tracedTurnCount,
11441
+ stepType: tracedTurnCount === 1 ? "initial" : "continue",
11442
+ span: childSpan
11443
+ });
11444
+ return response;
11445
+ } catch (error) {
11446
+ childSpan.log({
11447
+ error: normalizeError(error).message
11448
+ });
11449
+ childSpan.end();
11450
+ throw error;
11451
+ }
11452
+ });
11453
+ };
11454
+ }
11455
+ if (originalMakeFollowupRequest) {
11456
+ resultLike.makeFollowupRequest = async (...args) => {
11457
+ const currentResponse = args[0];
11458
+ const toolResults = Array.isArray(args[1]) ? args[1] : [];
11459
+ const resolvedRequest = getOpenRouterResolvedRequest(resultLike, request);
11460
+ const followupRequest = buildOpenRouterFollowupRequest(
11461
+ resolvedRequest,
11462
+ currentResponse,
11463
+ toolResults
11464
+ );
11465
+ const childSpan = startOpenRouterCallModelTurnSpan({
11466
+ request: followupRequest,
11467
+ step: tracedTurnCount + 1,
11468
+ stepType: "continue"
11469
+ });
11470
+ return await withCurrent(childSpan, async () => {
11471
+ try {
11472
+ const response = await originalMakeFollowupRequest(...args);
11473
+ tracedTurnCount++;
11474
+ finishOpenRouterCallModelTurnSpan({
11475
+ response,
11476
+ step: tracedTurnCount,
11477
+ stepType: "continue",
11478
+ span: childSpan
11479
+ });
11480
+ return response;
11481
+ } catch (error) {
11482
+ childSpan.log({
11483
+ error: normalizeError(error).message
11484
+ });
11485
+ childSpan.end();
11486
+ throw error;
11487
+ }
11488
+ });
11489
+ };
11490
+ }
11491
+ return true;
11492
+ }
11493
+ function wrapOpenRouterTool(tool) {
11494
+ if (isWrappedTool(tool) || !tool.function || typeof tool.function !== "object" || typeof tool.function.execute !== "function") {
11495
+ return tool;
11496
+ }
11497
+ const toolName = tool.function.name || "tool";
11498
+ const originalExecute = tool.function.execute;
11499
+ const wrappedTool = {
11500
+ ...tool,
11501
+ function: {
11502
+ ...tool.function,
11503
+ execute(...args) {
11504
+ return traceToolExecution({
11505
+ args,
11506
+ execute: () => Reflect.apply(originalExecute, this, args),
11507
+ toolCallId: getToolCallId(args[1]),
11508
+ toolName
11509
+ });
11510
+ }
11511
+ }
11512
+ };
11513
+ Object.defineProperty(wrappedTool, OPENROUTER_WRAPPED_TOOL, {
11514
+ value: true,
11515
+ enumerable: false,
11516
+ configurable: false
11517
+ });
11518
+ return wrappedTool;
11519
+ }
11520
+ function isWrappedTool(tool) {
11521
+ return Boolean(tool[OPENROUTER_WRAPPED_TOOL]);
11522
+ }
11523
+ function isWrappedCallModelResult(value) {
11524
+ return Boolean(
11525
+ isObject(value) && value[OPENROUTER_WRAPPED_CALL_MODEL_RESULT]
11526
+ );
11527
+ }
11528
+ function traceToolExecution(args) {
11529
+ const tracingChannel = openRouterChannels.toolExecute.tracingChannel();
11530
+ const input = args.args.length > 0 ? args.args[0] : void 0;
11531
+ const event = {
11532
+ arguments: [input],
11533
+ span_info: {
11534
+ name: args.toolName
11535
+ },
11536
+ toolCallId: args.toolCallId,
11537
+ toolName: args.toolName
11538
+ };
11539
+ tracingChannel.start.publish(event);
11540
+ try {
11541
+ const result = args.execute();
11542
+ return publishToolResult(tracingChannel, event, result);
11543
+ } catch (error) {
11544
+ event.error = normalizeError(error);
11545
+ tracingChannel.error.publish(event);
11546
+ throw error;
11547
+ }
11548
+ }
11549
+ function publishToolResult(tracingChannel, event, result) {
11550
+ if (isPromiseLike(result)) {
11551
+ return result.then(
11552
+ (resolved) => {
11553
+ event.result = resolved;
11554
+ tracingChannel.asyncEnd.publish(event);
11555
+ return resolved;
11556
+ },
11557
+ (error) => {
11558
+ event.error = normalizeError(error);
11559
+ tracingChannel.error.publish(event);
11560
+ throw error;
11561
+ }
11562
+ );
11563
+ }
11564
+ event.result = result;
11565
+ tracingChannel.asyncEnd.publish(event);
11566
+ return result;
11567
+ }
11568
+ function getToolCallId(context) {
11569
+ const toolContext = context;
11570
+ return typeof toolContext?.toolCall?.id === "string" ? toolContext.toolCall.id : void 0;
11571
+ }
11572
+ function extractOpenRouterCallModelResultMetadata(response, turnCount) {
11573
+ const combined = {
11574
+ ...extractOpenRouterResponseMetadata(response) || {},
11575
+ ...turnCount !== void 0 ? { turn_count: turnCount } : {}
11576
+ };
11577
+ return Object.keys(combined).length > 0 ? combined : void 0;
11578
+ }
11579
+ function getFinalOpenRouterCallModelResponse(result, response) {
11580
+ if (isObject(response)) {
11581
+ return response;
11582
+ }
11583
+ return isObject(result.finalResponse) ? result.finalResponse : void 0;
11584
+ }
11585
+ function getOpenRouterCallModelRounds(result) {
11586
+ if (!Array.isArray(result.allToolExecutionRounds)) {
11587
+ return [];
11588
+ }
11589
+ return result.allToolExecutionRounds.filter((round) => isObject(round)).map((round) => ({
11590
+ response: isObject(round.response) ? round.response : void 0,
11591
+ round: typeof round.round === "number" ? round.round : void 0,
11592
+ toolResults: Array.isArray(round.toolResults) ? round.toolResults : []
11593
+ })).filter((round) => round.response !== void 0);
11594
+ }
11595
+ function aggregateOpenRouterCallModelMetrics(rounds, finalResponse) {
11596
+ const metrics = {};
11597
+ const responses = [
11598
+ ...rounds.map((round) => round.response).filter(isObject),
11599
+ finalResponse
11600
+ ];
11601
+ for (const response of responses) {
11602
+ const responseMetrics = parseOpenRouterMetricsFromUsage(response.usage);
11603
+ for (const [name, value] of Object.entries(responseMetrics)) {
11604
+ metrics[name] = (metrics[name] || 0) + value;
11605
+ }
11606
+ }
11607
+ return metrics;
11608
+ }
11609
+ function buildNextOpenRouterCallModelInput(currentInput, response, toolResults) {
11610
+ const normalizedInput = Array.isArray(currentInput) ? [...currentInput] : currentInput === void 0 ? [] : [currentInput];
11611
+ const responseOutput = Array.isArray(response.output) ? response.output : response.output === void 0 ? [] : [response.output];
11612
+ return [...normalizedInput, ...responseOutput, ...toolResults].map(
11613
+ (entry) => sanitizeOpenRouterLoggedValue(entry)
11614
+ );
11615
+ }
11616
+ function startOpenRouterCallModelTurnSpan(args) {
11617
+ const requestRecord = isObject(args.request) ? args.request : void 0;
11618
+ const metadata = requestRecord ? extractOpenRouterCallModelMetadata(requestRecord) : { provider: "openrouter" };
11619
+ if (isObject(metadata) && "tools" in metadata) {
11620
+ delete metadata.tools;
11621
+ }
11622
+ return startSpan({
11623
+ name: "openrouter.beta.responses.send",
11624
+ spanAttributes: {
11625
+ type: "llm" /* LLM */
11626
+ },
11627
+ event: {
11628
+ input: requestRecord ? extractOpenRouterCallModelInput(requestRecord) : void 0,
11629
+ metadata: {
11630
+ ...metadata,
11631
+ step: args.step,
11632
+ step_type: args.stepType
11633
+ }
11634
+ }
11635
+ });
11636
+ }
11637
+ function finishOpenRouterCallModelTurnSpan(args) {
11638
+ if (!isObject(args.response)) {
11639
+ args.span.end();
11640
+ return;
11641
+ }
11642
+ args.span.log({
11643
+ output: extractOpenRouterResponseOutput(args.response),
11644
+ ...extractOpenRouterResponseMetadata(args.response) ? {
11645
+ metadata: {
11646
+ ...extractOpenRouterResponseMetadata(args.response),
11647
+ ...args.step !== void 0 ? { step: args.step } : {},
11648
+ ...args.stepType ? { step_type: args.stepType } : {}
11649
+ }
11650
+ } : {},
11651
+ metrics: parseOpenRouterMetricsFromUsage(args.response.usage)
11652
+ });
11653
+ args.span.end();
11654
+ }
11655
+ function getOpenRouterResolvedRequest(result, request) {
11656
+ if (isObject(result.resolvedRequest)) {
11657
+ return result.resolvedRequest;
11658
+ }
11659
+ return request;
11660
+ }
11661
+ function buildOpenRouterFollowupRequest(request, currentResponse, toolResults) {
11662
+ if (!request) {
11663
+ return void 0;
11664
+ }
11665
+ return {
11666
+ ...request,
11667
+ input: buildNextOpenRouterCallModelInput(
11668
+ extractOpenRouterCallModelInput(request),
11669
+ isObject(currentResponse) ? currentResponse : {},
11670
+ toolResults
11671
+ ),
11672
+ stream: false
11673
+ };
11674
+ }
11675
+ function wrapAsyncIterableWithSpan(args) {
11676
+ return {
11677
+ [Symbol.asyncIterator]() {
11678
+ const iterator = args.iteratorFactory();
11679
+ return {
11680
+ next(value) {
11681
+ return withCurrent(
11682
+ args.span,
11683
+ () => value === void 0 ? iterator.next() : iterator.next(value)
11684
+ ).then(
11685
+ async (result) => {
11686
+ if (result.done) {
11687
+ await args.finalize();
11688
+ }
11689
+ return result;
11690
+ },
11691
+ (error) => {
11692
+ args.onError(error);
11693
+ throw error;
11694
+ }
11695
+ );
11696
+ },
11697
+ return(value) {
11698
+ if (typeof iterator.return !== "function") {
11699
+ return args.finalize().then(() => ({
11700
+ done: true,
11701
+ value
11702
+ }));
11703
+ }
11704
+ return withCurrent(args.span, () => iterator.return(value)).then(
11705
+ async (result) => {
11706
+ await args.finalize();
11707
+ return result;
11708
+ },
11709
+ (error) => {
11710
+ args.onError(error);
11711
+ throw error;
11712
+ }
11713
+ );
11714
+ },
11715
+ throw(error) {
11716
+ args.onError(error);
11717
+ if (typeof iterator.throw !== "function") {
11718
+ return Promise.reject(error);
11719
+ }
11720
+ return withCurrent(args.span, () => iterator.throw(error));
11721
+ },
11722
+ [Symbol.asyncIterator]() {
11723
+ return this;
11724
+ }
11725
+ };
11726
+ }
11727
+ };
11728
+ }
11729
+ function isAsyncIterable2(value) {
11730
+ return !!value && (typeof value === "object" || typeof value === "function") && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
11731
+ }
11732
+ function isPromiseLike(value) {
11733
+ return !!value && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
11734
+ }
11735
+ function normalizeError(error) {
11736
+ return error instanceof Error ? error : new Error(String(error));
11737
+ }
11738
+
11739
+ // src/instrumentation/plugins/openrouter-plugin.ts
11740
+ var OpenRouterPlugin = class extends BasePlugin {
11741
+ onEnable() {
11742
+ this.subscribeToOpenRouterChannels();
11743
+ }
11744
+ onDisable() {
11745
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
11746
+ }
11747
+ subscribeToOpenRouterChannels() {
11748
+ this.unsubscribers.push(
11749
+ traceStreamingChannel(openRouterChannels.chatSend, {
11750
+ name: "openrouter.chat.send",
11751
+ type: "llm" /* LLM */,
11752
+ extractInput: (args) => {
11753
+ const request = getOpenRouterRequestArg(args);
11754
+ const chatGenerationParams = isObject(request?.chatGenerationParams) ? request.chatGenerationParams : {};
11755
+ const httpReferer = request?.httpReferer;
11756
+ const xTitle = request?.xTitle;
11757
+ const { messages, ...metadata } = chatGenerationParams;
11758
+ return {
11759
+ input: messages,
11760
+ metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
11761
+ };
11762
+ },
11763
+ extractOutput: (result) => {
11764
+ return isObject(result) ? result.choices : void 0;
11765
+ },
11766
+ extractMetrics: (result, startTime) => {
11767
+ const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
11768
+ if (startTime) {
11769
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
11770
+ }
11771
+ return metrics;
11772
+ },
11773
+ aggregateChunks: aggregateOpenRouterChatChunks
11774
+ })
11775
+ );
11776
+ this.unsubscribers.push(
11777
+ traceAsyncChannel(openRouterChannels.embeddingsGenerate, {
11778
+ name: "openrouter.embeddings.generate",
11779
+ type: "llm" /* LLM */,
11780
+ extractInput: (args) => {
11781
+ const request = getOpenRouterRequestArg(args);
11782
+ const requestBody = isObject(request?.requestBody) ? request.requestBody : {};
11783
+ const httpReferer = request?.httpReferer;
11784
+ const xTitle = request?.xTitle;
11785
+ const { input, ...metadata } = requestBody;
11786
+ return {
11787
+ input,
11788
+ metadata: buildOpenRouterEmbeddingMetadata(
11789
+ metadata,
11790
+ httpReferer,
11791
+ xTitle
11792
+ )
11793
+ };
11794
+ },
11795
+ extractOutput: (result) => {
11796
+ if (!isObject(result)) {
11797
+ return void 0;
11798
+ }
11799
+ const embedding = result.data?.[0]?.embedding;
11800
+ return Array.isArray(embedding) ? { embedding_length: embedding.length } : void 0;
11801
+ },
11802
+ extractMetadata: (result) => {
11803
+ if (!isObject(result)) {
11804
+ return void 0;
11805
+ }
11806
+ return extractOpenRouterResponseMetadata(result);
11807
+ },
11808
+ extractMetrics: (result) => {
11809
+ return isObject(result) ? parseOpenRouterMetricsFromUsage(result.usage) : {};
11810
+ }
11811
+ })
11812
+ );
11813
+ this.unsubscribers.push(
11814
+ traceStreamingChannel(openRouterChannels.betaResponsesSend, {
11815
+ name: "openrouter.beta.responses.send",
11816
+ type: "llm" /* LLM */,
11817
+ extractInput: (args) => {
11818
+ const request = getOpenRouterRequestArg(args);
11819
+ const openResponsesRequest = isObject(request?.openResponsesRequest) ? request.openResponsesRequest : {};
11820
+ const httpReferer = request?.httpReferer;
11821
+ const xTitle = request?.xTitle;
11822
+ const { input, ...metadata } = openResponsesRequest;
11823
+ return {
11824
+ input,
11825
+ metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
11826
+ };
11827
+ },
11828
+ extractOutput: (result) => extractOpenRouterResponseOutput(result),
11829
+ extractMetadata: (result) => extractOpenRouterResponseMetadata(result),
11830
+ extractMetrics: (result, startTime) => {
11831
+ const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
11832
+ if (startTime) {
11833
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
11834
+ }
11835
+ return metrics;
11836
+ },
11837
+ aggregateChunks: aggregateOpenRouterResponseStreamEvents
11838
+ })
11839
+ );
11840
+ this.unsubscribers.push(
11841
+ traceSyncStreamChannel(openRouterChannels.callModel, {
11842
+ name: "openrouter.callModel",
11843
+ type: "llm" /* LLM */,
11844
+ extractInput: (args) => {
11845
+ const request = getOpenRouterCallModelRequestArg(args);
11846
+ return {
11847
+ input: request ? extractOpenRouterCallModelInput(request) : void 0,
11848
+ metadata: request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" }
11849
+ };
11850
+ },
11851
+ patchResult: ({ endEvent, result, span }) => {
11852
+ return patchOpenRouterCallModelResult(
11853
+ span,
11854
+ result,
11855
+ getOpenRouterCallModelRequestArg(endEvent.arguments)
11856
+ );
11857
+ }
11858
+ })
11859
+ );
11860
+ this.unsubscribers.push(
11861
+ traceStreamingChannel(openRouterChannels.toolExecute, {
11862
+ name: "openrouter.tool",
11863
+ type: "tool" /* TOOL */,
11864
+ extractInput: (args, event) => ({
11865
+ input: args[0],
11866
+ metadata: {
11867
+ provider: "openrouter",
11868
+ tool_name: event.toolName,
11869
+ ...event.toolCallId ? { tool_call_id: event.toolCallId } : {}
11870
+ }
11871
+ }),
11872
+ extractOutput: (result) => result,
11873
+ extractMetrics: () => ({}),
11874
+ aggregateChunks: (chunks) => ({
11875
+ output: chunks.length > 0 ? chunks[chunks.length - 1] : void 0,
11876
+ metrics: {}
11877
+ })
11878
+ })
11879
+ );
11880
+ const callModelChannel = openRouterChannels.callModel.tracingChannel();
11881
+ const callModelHandlers = {
11882
+ start: (event) => {
11883
+ const request = getOpenRouterCallModelRequestArg(event.arguments);
11884
+ if (!request) {
11885
+ return;
11886
+ }
11887
+ patchOpenRouterCallModelRequestTools(request);
11888
+ }
11889
+ };
11890
+ callModelChannel.subscribe(callModelHandlers);
11891
+ this.unsubscribers.push(() => {
11892
+ callModelChannel.unsubscribe(callModelHandlers);
11893
+ });
11894
+ }
11895
+ };
11896
+ function normalizeArgs(args) {
11897
+ if (Array.isArray(args)) {
11898
+ return args;
11899
+ }
11900
+ if (isArrayLike(args)) {
11901
+ return Array.from(args);
11902
+ }
11903
+ return [args];
11904
+ }
11905
+ function isArrayLike(value) {
11906
+ return isObject(value) && "length" in value && typeof value.length === "number" && Number.isInteger(value.length) && value.length >= 0;
11907
+ }
11908
+ function getOpenRouterRequestArg(args) {
11909
+ const normalizedArgs = normalizeArgs(args);
11910
+ const keyedCandidate = normalizedArgs.find(
11911
+ (arg) => isObject(arg) && ("chatGenerationParams" in arg || "requestBody" in arg || "openResponsesRequest" in arg)
11912
+ );
11913
+ if (isObject(keyedCandidate)) {
11914
+ return keyedCandidate;
11915
+ }
11916
+ const firstObjectArg = normalizedArgs.find((arg) => isObject(arg));
11917
+ return isObject(firstObjectArg) ? firstObjectArg : void 0;
11918
+ }
11919
+ function getOpenRouterCallModelRequestArg(args) {
11920
+ const firstObjectArg = normalizeArgs(args).find((arg) => isObject(arg));
11921
+ return isObject(firstObjectArg) ? firstObjectArg : void 0;
11922
+ }
11923
+ function aggregateOpenRouterChatChunks(chunks) {
11924
+ let role;
11925
+ let content = "";
11926
+ let toolCalls;
11927
+ let finishReason;
11928
+ let metrics = {};
11929
+ for (const chunk of chunks) {
11930
+ metrics = {
11931
+ ...metrics,
11932
+ ...parseOpenRouterMetricsFromUsage(chunk?.usage)
11933
+ };
11934
+ const choice = chunk?.choices?.[0];
11935
+ const delta = choice?.delta;
11936
+ if (!delta) {
11937
+ if (choice?.finish_reason !== void 0) {
11938
+ finishReason = choice.finish_reason;
11939
+ }
11940
+ continue;
11941
+ }
11942
+ if (!role && delta.role) {
11943
+ role = delta.role;
11944
+ }
11945
+ if (typeof delta.content === "string") {
11946
+ content += delta.content;
11947
+ }
11948
+ const choiceFinishReason = choice?.finishReason ?? choice?.finish_reason ?? void 0;
11949
+ const deltaFinishReason = delta.finishReason ?? delta.finish_reason ?? void 0;
11950
+ if (choiceFinishReason !== void 0) {
11951
+ finishReason = choiceFinishReason;
11952
+ } else if (deltaFinishReason !== void 0) {
11953
+ finishReason = deltaFinishReason;
11954
+ }
11955
+ const toolCallDeltas = Array.isArray(delta.toolCalls) ? delta.toolCalls : Array.isArray(delta.tool_calls) ? delta.tool_calls : void 0;
11956
+ if (!toolCallDeltas) {
11957
+ continue;
11958
+ }
11959
+ for (const toolDelta of toolCallDeltas) {
11960
+ if (!toolDelta?.function) {
11961
+ continue;
11962
+ }
11963
+ const toolIndex = toolDelta.index ?? 0;
11964
+ const existingToolCall = toolCalls?.[toolIndex];
11965
+ if (!existingToolCall || toolDelta.id && existingToolCall.id !== void 0 && existingToolCall.id !== toolDelta.id) {
11966
+ const nextToolCalls = [...toolCalls || []];
11967
+ nextToolCalls[toolIndex] = {
11968
+ index: toolIndex,
11969
+ id: toolDelta.id,
11970
+ type: toolDelta.type,
11971
+ function: {
11972
+ name: toolDelta.function.name,
11973
+ arguments: toolDelta.function.arguments || ""
11974
+ }
11975
+ };
11976
+ toolCalls = nextToolCalls;
11977
+ continue;
11978
+ }
11979
+ const current = existingToolCall;
11980
+ if (toolDelta.id && !current.id) {
11981
+ current.id = toolDelta.id;
11982
+ }
11983
+ if (toolDelta.type && !current.type) {
11984
+ current.type = toolDelta.type;
11985
+ }
11986
+ if (toolDelta.function.name && !current.function.name) {
11987
+ current.function.name = toolDelta.function.name;
11988
+ }
11989
+ current.function.arguments += toolDelta.function.arguments || "";
11990
+ }
11991
+ }
11992
+ return {
11993
+ output: [
11994
+ {
11995
+ index: 0,
11996
+ message: {
11997
+ role,
11998
+ content: content || void 0,
11999
+ ...toolCalls ? { tool_calls: toolCalls } : {}
12000
+ },
12001
+ logprobs: null,
12002
+ finish_reason: finishReason
12003
+ }
12004
+ ],
12005
+ metrics
12006
+ };
12007
+ }
12008
+ function aggregateOpenRouterResponseStreamEvents(chunks) {
12009
+ let finalResponse;
12010
+ for (const chunk of chunks) {
12011
+ const response = chunk?.response;
12012
+ if (!response) {
12013
+ continue;
12014
+ }
12015
+ if (chunk.type === "response.completed" || chunk.type === "response.incomplete" || chunk.type === "response.failed") {
12016
+ finalResponse = response;
12017
+ }
12018
+ }
12019
+ if (!finalResponse) {
12020
+ return {
12021
+ output: void 0,
12022
+ metrics: {}
12023
+ };
12024
+ }
12025
+ return {
12026
+ output: extractOpenRouterResponseOutput(finalResponse),
12027
+ metrics: parseOpenRouterMetricsFromUsage(finalResponse.usage),
12028
+ ...extractOpenRouterResponseMetadata(finalResponse) ? { metadata: extractOpenRouterResponseMetadata(finalResponse) } : {}
12029
+ };
12030
+ }
12031
+
12032
+ // src/instrumentation/braintrust-plugin.ts
12033
+ var BraintrustPlugin = class extends BasePlugin {
12034
+ config;
12035
+ openaiPlugin = null;
12036
+ anthropicPlugin = null;
12037
+ aiSDKPlugin = null;
12038
+ claudeAgentSDKPlugin = null;
12039
+ googleGenAIPlugin = null;
12040
+ openRouterPlugin = null;
12041
+ constructor(config = {}) {
12042
+ super();
12043
+ this.config = config;
12044
+ }
12045
+ onEnable() {
12046
+ const integrations = this.config.integrations || {};
12047
+ if (integrations.openai !== false) {
12048
+ this.openaiPlugin = new OpenAIPlugin();
12049
+ this.openaiPlugin.enable();
12050
+ }
12051
+ if (integrations.anthropic !== false) {
12052
+ this.anthropicPlugin = new AnthropicPlugin();
12053
+ this.anthropicPlugin.enable();
12054
+ }
12055
+ if (integrations.aisdk !== false && integrations.vercel !== false) {
12056
+ this.aiSDKPlugin = new AISDKPlugin();
12057
+ this.aiSDKPlugin.enable();
12058
+ }
12059
+ if (integrations.claudeAgentSDK !== false) {
12060
+ this.claudeAgentSDKPlugin = new ClaudeAgentSDKPlugin();
12061
+ this.claudeAgentSDKPlugin.enable();
12062
+ }
12063
+ if (integrations.googleGenAI !== false && integrations.google !== false) {
12064
+ this.googleGenAIPlugin = new GoogleGenAIPlugin();
12065
+ this.googleGenAIPlugin.enable();
12066
+ }
12067
+ if (integrations.openrouter !== false) {
12068
+ this.openRouterPlugin = new OpenRouterPlugin();
12069
+ this.openRouterPlugin.enable();
12070
+ }
12071
+ }
12072
+ onDisable() {
12073
+ if (this.openaiPlugin) {
12074
+ this.openaiPlugin.disable();
12075
+ this.openaiPlugin = null;
12076
+ }
12077
+ if (this.anthropicPlugin) {
10326
12078
  this.anthropicPlugin.disable();
10327
12079
  this.anthropicPlugin = null;
10328
12080
  }
@@ -10338,6 +12090,10 @@ var BraintrustPlugin = class extends BasePlugin {
10338
12090
  this.googleGenAIPlugin.disable();
10339
12091
  this.googleGenAIPlugin = null;
10340
12092
  }
12093
+ if (this.openRouterPlugin) {
12094
+ this.openRouterPlugin.disable();
12095
+ this.openRouterPlugin = null;
12096
+ }
10341
12097
  }
10342
12098
  };
10343
12099
 
@@ -10409,7 +12165,8 @@ var PluginRegistry = class {
10409
12165
  vercel: true,
10410
12166
  aisdk: true,
10411
12167
  google: true,
10412
- claudeAgentSDK: true
12168
+ claudeAgentSDK: true,
12169
+ openrouter: true
10413
12170
  };
10414
12171
  }
10415
12172
  /**