braintrust 3.3.0 → 3.5.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 (60) hide show
  1. package/README.md +52 -67
  2. package/dev/dist/index.d.mts +98 -14
  3. package/dev/dist/index.d.ts +98 -14
  4. package/dev/dist/index.js +2751 -1463
  5. package/dev/dist/index.mjs +2383 -1095
  6. package/dist/auto-instrumentations/bundler/esbuild.cjs +476 -31
  7. package/dist/auto-instrumentations/bundler/esbuild.d.mts +2 -2
  8. package/dist/auto-instrumentations/bundler/esbuild.d.ts +2 -2
  9. package/dist/auto-instrumentations/bundler/esbuild.mjs +2 -2
  10. package/dist/auto-instrumentations/bundler/rollup.cjs +476 -31
  11. package/dist/auto-instrumentations/bundler/rollup.mjs +2 -2
  12. package/dist/auto-instrumentations/bundler/vite.cjs +476 -31
  13. package/dist/auto-instrumentations/bundler/vite.d.mts +2 -2
  14. package/dist/auto-instrumentations/bundler/vite.d.ts +2 -2
  15. package/dist/auto-instrumentations/bundler/vite.mjs +2 -2
  16. package/dist/auto-instrumentations/bundler/webpack.cjs +476 -31
  17. package/dist/auto-instrumentations/bundler/webpack.d.mts +2 -2
  18. package/dist/auto-instrumentations/bundler/webpack.d.ts +2 -2
  19. package/dist/auto-instrumentations/bundler/webpack.mjs +2 -2
  20. package/dist/auto-instrumentations/chunk-DQTPSXJB.mjs +733 -0
  21. package/dist/auto-instrumentations/chunk-EVUKFMHG.mjs +41 -0
  22. package/dist/auto-instrumentations/{chunk-OLOPGWTJ.mjs → chunk-F3TJZ3Z2.mjs} +1 -1
  23. package/dist/auto-instrumentations/chunk-VLEJ5AEK.mjs +41 -0
  24. package/dist/auto-instrumentations/hook.mjs +540 -37
  25. package/dist/auto-instrumentations/index.cjs +476 -31
  26. package/dist/auto-instrumentations/index.d.mts +5 -5
  27. package/dist/auto-instrumentations/index.d.ts +5 -5
  28. package/dist/auto-instrumentations/index.mjs +1 -1
  29. package/dist/auto-instrumentations/loader/cjs-patch.cjs +32 -10
  30. package/dist/auto-instrumentations/loader/cjs-patch.mjs +10 -5
  31. package/dist/auto-instrumentations/loader/esm-hook.mjs +11 -12
  32. package/dist/auto-instrumentations/loader/get-package-version.cjs +28 -8
  33. package/dist/auto-instrumentations/loader/get-package-version.d.mts +2 -1
  34. package/dist/auto-instrumentations/loader/get-package-version.d.ts +2 -1
  35. package/dist/auto-instrumentations/loader/get-package-version.mjs +3 -1
  36. package/dist/browser.d.mts +806 -306
  37. package/dist/browser.d.ts +806 -306
  38. package/dist/browser.js +3235 -2317
  39. package/dist/browser.mjs +3235 -2317
  40. package/dist/cli.js +2672 -1347
  41. package/dist/edge-light.d.mts +1 -1
  42. package/dist/edge-light.d.ts +1 -1
  43. package/dist/edge-light.js +3035 -2087
  44. package/dist/edge-light.mjs +3035 -2087
  45. package/dist/index.d.mts +806 -306
  46. package/dist/index.d.ts +806 -306
  47. package/dist/index.js +3570 -2654
  48. package/dist/index.mjs +3235 -2319
  49. package/dist/instrumentation/index.d.mts +16 -22
  50. package/dist/instrumentation/index.d.ts +16 -22
  51. package/dist/instrumentation/index.js +2241 -1077
  52. package/dist/instrumentation/index.mjs +2241 -1077
  53. package/dist/workerd.d.mts +1 -1
  54. package/dist/workerd.d.ts +1 -1
  55. package/dist/workerd.js +3035 -2087
  56. package/dist/workerd.mjs +3035 -2087
  57. package/package.json +26 -7
  58. package/dist/auto-instrumentations/chunk-KVX7OFPD.mjs +0 -288
  59. package/dist/auto-instrumentations/chunk-XDBPUTVE.mjs +0 -22
  60. package/dist/auto-instrumentations/chunk-ZEC7BCL4.mjs +0 -22
@@ -1,5 +1,6 @@
1
1
  // src/node/config.ts
2
2
  import { AsyncLocalStorage } from "node:async_hooks";
3
+ import * as diagnostics_channel from "node:diagnostics_channel";
3
4
  import * as path from "node:path";
4
5
  import * as fs from "node:fs/promises";
5
6
  import * as os from "node:os";
@@ -21,6 +22,72 @@ var DefaultAsyncLocalStorage = class {
21
22
  return void 0;
22
23
  }
23
24
  };
25
+ var DefaultChannel = class {
26
+ constructor(name) {
27
+ this.name = name;
28
+ }
29
+ hasSubscribers = false;
30
+ subscribe(_subscription) {
31
+ }
32
+ unsubscribe(_subscription) {
33
+ return false;
34
+ }
35
+ bindStore(_store, _transform) {
36
+ }
37
+ unbindStore(_store) {
38
+ return false;
39
+ }
40
+ publish(_message) {
41
+ }
42
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
43
+ runStores(_message, fn, thisArg, ...args) {
44
+ return fn.apply(thisArg, args);
45
+ }
46
+ };
47
+ var DefaultTracingChannel = class {
48
+ start;
49
+ end;
50
+ asyncStart;
51
+ asyncEnd;
52
+ error;
53
+ constructor(nameOrChannels) {
54
+ if (typeof nameOrChannels === "string") {
55
+ this.start = new DefaultChannel(`tracing:${nameOrChannels}:start`);
56
+ this.end = new DefaultChannel(`tracing:${nameOrChannels}:end`);
57
+ this.asyncStart = new DefaultChannel(
58
+ `tracing:${nameOrChannels}:asyncStart`
59
+ );
60
+ this.asyncEnd = new DefaultChannel(`tracing:${nameOrChannels}:asyncEnd`);
61
+ this.error = new DefaultChannel(`tracing:${nameOrChannels}:error`);
62
+ return;
63
+ }
64
+ this.start = nameOrChannels.start ?? new DefaultChannel("tracing:start");
65
+ this.end = nameOrChannels.end ?? new DefaultChannel("tracing:end");
66
+ this.asyncStart = nameOrChannels.asyncStart ?? new DefaultChannel("tracing:asyncStart");
67
+ this.asyncEnd = nameOrChannels.asyncEnd ?? new DefaultChannel("tracing:asyncEnd");
68
+ this.error = nameOrChannels.error ?? new DefaultChannel("tracing:error");
69
+ }
70
+ get hasSubscribers() {
71
+ return this.start.hasSubscribers || this.end.hasSubscribers || this.asyncStart.hasSubscribers || this.asyncEnd.hasSubscribers || this.error.hasSubscribers;
72
+ }
73
+ subscribe(_handlers) {
74
+ }
75
+ unsubscribe(_handlers) {
76
+ return false;
77
+ }
78
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
79
+ traceSync(fn, _message, thisArg, ...args) {
80
+ return fn.apply(thisArg, args);
81
+ }
82
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
83
+ tracePromise(fn, _message, thisArg, ...args) {
84
+ return Promise.resolve(fn.apply(thisArg, args));
85
+ }
86
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
87
+ traceCallback(fn, _position, _message, thisArg, ...args) {
88
+ return fn.apply(thisArg, args);
89
+ }
90
+ };
24
91
  var iso = {
25
92
  buildType: "unknown",
26
93
  // Will be set by configureBrowser() or configureNode()
@@ -29,6 +96,8 @@ var iso = {
29
96
  getEnv: (_name) => void 0,
30
97
  getCallerLocation: () => void 0,
31
98
  newAsyncLocalStorage: () => new DefaultAsyncLocalStorage(),
99
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
100
+ newTracingChannel: (nameOrChannels) => new DefaultTracingChannel(nameOrChannels),
32
101
  processOn: (_0, _1) => {
33
102
  },
34
103
  basename: (filepath) => filepath.split(/[\\/]/).pop() || filepath,
@@ -36,6 +105,111 @@ var iso = {
36
105
  };
37
106
  var isomorph_default = iso;
38
107
 
108
+ // src/debug-logger.ts
109
+ var PREFIX = "[braintrust]";
110
+ var DEBUG_LOG_LEVEL_SYMBOL = Symbol.for("braintrust-debug-log-level");
111
+ var LOG_LEVEL_PRIORITY = {
112
+ error: 0,
113
+ warn: 1,
114
+ info: 2,
115
+ debug: 3
116
+ };
117
+ var hasWarnedAboutInvalidEnvValue = false;
118
+ var debugLogStateResolver = void 0;
119
+ function warnInvalidEnvValue(value) {
120
+ if (hasWarnedAboutInvalidEnvValue) {
121
+ return;
122
+ }
123
+ hasWarnedAboutInvalidEnvValue = true;
124
+ console.warn(
125
+ PREFIX,
126
+ `Invalid BRAINTRUST_DEBUG_LOG_LEVEL value "${value}". Expected "error", "warn", "info", or "debug".`
127
+ );
128
+ }
129
+ function normalizeDebugLogLevelOption(option) {
130
+ if (option === false) {
131
+ return void 0;
132
+ }
133
+ if (option === "error" || option === "warn" || option === "info" || option === "debug") {
134
+ return option;
135
+ }
136
+ throw new Error(
137
+ `Invalid debugLogLevel value "${option}". Expected false, "error", "warn", "info", or "debug".`
138
+ );
139
+ }
140
+ function parseDebugLogLevelEnv(value) {
141
+ if (!value) {
142
+ return void 0;
143
+ }
144
+ if (value === "error" || value === "warn" || value === "info" || value === "debug") {
145
+ return value;
146
+ }
147
+ warnInvalidEnvValue(value);
148
+ return void 0;
149
+ }
150
+ function getEnvDebugLogLevel() {
151
+ return parseDebugLogLevelEnv(isomorph_default.getEnv("BRAINTRUST_DEBUG_LOG_LEVEL"));
152
+ }
153
+ function setGlobalDebugLogLevel(level) {
154
+ globalThis[DEBUG_LOG_LEVEL_SYMBOL] = level;
155
+ }
156
+ function setDebugLogStateResolver(resolver) {
157
+ debugLogStateResolver = resolver;
158
+ }
159
+ function resolveDebugLogLevel(state) {
160
+ const stateLevel = state?.getDebugLogLevel?.();
161
+ const hasStateOverride = state?.hasDebugLogLevelOverride?.() ?? false;
162
+ if (hasStateOverride) {
163
+ return stateLevel;
164
+ }
165
+ const globalLevel = (
166
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
167
+ globalThis[DEBUG_LOG_LEVEL_SYMBOL]
168
+ );
169
+ if (globalLevel !== void 0) {
170
+ return globalLevel === false ? void 0 : globalLevel;
171
+ }
172
+ return getEnvDebugLogLevel();
173
+ }
174
+ function emit(method, state, args) {
175
+ const level = resolveDebugLogLevel(state);
176
+ if (!level || LOG_LEVEL_PRIORITY[method] > LOG_LEVEL_PRIORITY[level]) {
177
+ return;
178
+ }
179
+ if (method === "info") {
180
+ console.log(PREFIX, ...args);
181
+ } else if (method === "debug") {
182
+ console.debug(PREFIX, ...args);
183
+ } else if (method === "warn") {
184
+ console.warn(PREFIX, ...args);
185
+ } else {
186
+ console.error(PREFIX, ...args);
187
+ }
188
+ }
189
+ function createDebugLogger(state) {
190
+ const resolveState = () => state ?? debugLogStateResolver?.();
191
+ return {
192
+ info(...args) {
193
+ emit("info", resolveState(), args);
194
+ },
195
+ debug(...args) {
196
+ emit("debug", resolveState(), args);
197
+ },
198
+ warn(...args) {
199
+ emit("warn", resolveState(), args);
200
+ },
201
+ error(...args) {
202
+ emit("error", resolveState(), args);
203
+ }
204
+ };
205
+ }
206
+ var debugLogger = {
207
+ ...createDebugLogger(),
208
+ forState(state) {
209
+ return createDebugLogger(state);
210
+ }
211
+ };
212
+
39
213
  // src/gitutil.ts
40
214
  import { simpleGit } from "simple-git";
41
215
  var COMMON_BASE_BRANCHES = ["main", "master", "develop"];
@@ -116,7 +290,7 @@ async function getPastNAncestors(n = 1e3, remote = void 0) {
116
290
  try {
117
291
  ancestor = await getBaseBranchAncestor(remote);
118
292
  } catch (e) {
119
- console.warn(
293
+ debugLogger.warn(
120
294
  "Skipping git metadata. This is likely because the repository has not been published to a remote yet.",
121
295
  `${e}`
122
296
  );
@@ -261,7 +435,7 @@ var Queue = class {
261
435
  enforceSizeLimit = false;
262
436
  constructor(maxSize) {
263
437
  if (maxSize < 1) {
264
- console.warn(
438
+ debugLogger.warn(
265
439
  `maxSize ${maxSize} is <1, using default ${DEFAULT_QUEUE_SIZE}`
266
440
  );
267
441
  maxSize = DEFAULT_QUEUE_SIZE;
@@ -2206,6 +2380,8 @@ var Experiment = z6.object({
2206
2380
  deleted_at: z6.union([z6.string(), z6.null()]).optional(),
2207
2381
  dataset_id: z6.union([z6.string(), z6.null()]).optional(),
2208
2382
  dataset_version: z6.union([z6.string(), z6.null()]).optional(),
2383
+ parameters_id: z6.union([z6.string(), z6.null()]).optional(),
2384
+ parameters_version: z6.union([z6.string(), z6.null()]).optional(),
2209
2385
  public: z6.boolean(),
2210
2386
  user_id: z6.union([z6.string(), z6.null()]).optional(),
2211
2387
  metadata: z6.union([z6.object({}).partial().passthrough(), z6.null()]).optional(),
@@ -2228,7 +2404,11 @@ var SpanType = z6.union([
2228
2404
  z6.null()
2229
2405
  ]);
2230
2406
  var SpanAttributes = z6.union([
2231
- z6.object({ name: z6.union([z6.string(), z6.null()]), type: SpanType }).partial().passthrough(),
2407
+ z6.object({
2408
+ name: z6.union([z6.string(), z6.null()]),
2409
+ type: SpanType,
2410
+ purpose: z6.union([z6.literal("scorer"), z6.null()])
2411
+ }).partial().passthrough(),
2232
2412
  z6.null()
2233
2413
  ]);
2234
2414
  var ExperimentEvent = z6.object({
@@ -2668,6 +2848,7 @@ var FunctionId = z6.union([
2668
2848
  version: z6.string()
2669
2849
  }),
2670
2850
  code: z6.string(),
2851
+ function_type: FunctionTypeEnum.and(z6.unknown()).optional(),
2671
2852
  name: z6.union([z6.string(), z6.null()]).optional()
2672
2853
  }),
2673
2854
  z6.object({
@@ -2897,7 +3078,12 @@ var TopicAutomationConfig = z6.object({
2897
3078
  topic_map_functions: z6.array(TopicMapFunctionAutomation),
2898
3079
  scope: z6.union([SpanScope, TraceScope, GroupScope, z6.null()]).optional(),
2899
3080
  data_scope: TopicAutomationDataScope.optional(),
2900
- btql_filter: z6.union([z6.string(), z6.null()]).optional()
3081
+ btql_filter: z6.union([z6.string(), z6.null()]).optional(),
3082
+ backfill_time_range: z6.union([
3083
+ z6.string(),
3084
+ z6.object({ from: z6.string(), to: z6.string() }),
3085
+ z6.null()
3086
+ ]).optional()
2901
3087
  });
2902
3088
  var ProjectAutomation = z6.object({
2903
3089
  id: z6.string().uuid(),
@@ -4336,6 +4522,12 @@ var parametersRowSchema = z8.object({
4336
4522
  }),
4337
4523
  metadata: z8.union([z8.object({}).partial().passthrough(), z8.null()]).optional()
4338
4524
  });
4525
+ var InlineAttachmentReferenceSchema = z8.object({
4526
+ type: z8.literal("inline_attachment"),
4527
+ src: z8.string().min(1),
4528
+ content_type: z8.string().optional(),
4529
+ filename: z8.string().optional()
4530
+ });
4339
4531
  var LoginInvalidOrgError = class extends Error {
4340
4532
  constructor(message) {
4341
4533
  super(message);
@@ -4482,7 +4674,10 @@ var loginSchema = z8.strictObject({
4482
4674
  proxyUrl: z8.string(),
4483
4675
  loginToken: z8.string(),
4484
4676
  orgId: z8.string().nullish(),
4485
- gitMetadataSettings: GitMetadataSettings.nullish()
4677
+ gitMetadataSettings: GitMetadataSettings.nullish(),
4678
+ debugLogLevel: z8.enum(["error", "warn", "info", "debug"]).optional(),
4679
+ // Distinguishes explicit false from unset so env fallback stays disabled after deserialization.
4680
+ debugLogLevelDisabled: z8.boolean().optional()
4486
4681
  });
4487
4682
  var stateNonce = 0;
4488
4683
  var BraintrustState = class _BraintrustState {
@@ -4503,6 +4698,16 @@ var BraintrustState = class _BraintrustState {
4503
4698
  this._bgLogger = new SyncLazyValue(
4504
4699
  () => new HTTPBackgroundLogger(new LazyValue(defaultGetLogConn), loginParams)
4505
4700
  );
4701
+ if (loginParams.debugLogLevel !== void 0) {
4702
+ this.debugLogLevelConfigured = true;
4703
+ this.debugLogLevel = normalizeDebugLogLevelOption(
4704
+ loginParams.debugLogLevel
4705
+ );
4706
+ setGlobalDebugLogLevel(this.debugLogLevel ?? false);
4707
+ } else {
4708
+ this.debugLogLevel = getEnvDebugLogLevel();
4709
+ setGlobalDebugLogLevel(void 0);
4710
+ }
4506
4711
  this.resetLoginInfo();
4507
4712
  const memoryCache = new LRUCache({
4508
4713
  max: Number(isomorph_default.getEnv("BRAINTRUST_PROMPT_CACHE_MEMORY_MAX")) ?? 1 << 10
@@ -4547,6 +4752,8 @@ var BraintrustState = class _BraintrustState {
4547
4752
  proxyUrl = null;
4548
4753
  loggedIn = false;
4549
4754
  gitMetadataSettings;
4755
+ debugLogLevel;
4756
+ debugLogLevelConfigured = false;
4550
4757
  fetch = globalThis.fetch;
4551
4758
  _appConn = null;
4552
4759
  _apiConn = null;
@@ -4612,6 +4819,11 @@ var BraintrustState = class _BraintrustState {
4612
4819
  this.proxyUrl = other.proxyUrl;
4613
4820
  this.loggedIn = other.loggedIn;
4614
4821
  this.gitMetadataSettings = other.gitMetadataSettings;
4822
+ this.debugLogLevel = other.debugLogLevel;
4823
+ this.debugLogLevelConfigured = other.debugLogLevelConfigured;
4824
+ setGlobalDebugLogLevel(
4825
+ this.debugLogLevelConfigured ? this.debugLogLevel ?? false : void 0
4826
+ );
4615
4827
  this._appConn = other._appConn;
4616
4828
  this._apiConn = other._apiConn;
4617
4829
  this.loginReplaceApiConn(this.apiConn());
@@ -4636,7 +4848,9 @@ var BraintrustState = class _BraintrustState {
4636
4848
  orgName: this.orgName,
4637
4849
  apiUrl: this.apiUrl,
4638
4850
  proxyUrl: this.proxyUrl,
4639
- gitMetadataSettings: this.gitMetadataSettings
4851
+ gitMetadataSettings: this.gitMetadataSettings,
4852
+ ...this.debugLogLevel ? { debugLogLevel: this.debugLogLevel } : {},
4853
+ ...this.debugLogLevelConfigured && !this.debugLogLevel ? { debugLogLevelDisabled: true } : {}
4640
4854
  };
4641
4855
  }
4642
4856
  static deserialize(serialized, opts) {
@@ -4663,6 +4877,10 @@ var BraintrustState = class _BraintrustState {
4663
4877
  state.proxyConn().set_token(state.loginToken);
4664
4878
  }
4665
4879
  state.loggedIn = true;
4880
+ state.debugLogLevelConfigured = "debugLogLevel" in serializedParsed.data || !!serializedParsed.data.debugLogLevelDisabled;
4881
+ setGlobalDebugLogLevel(
4882
+ state.debugLogLevelConfigured ? state.debugLogLevel ?? false : void 0
4883
+ );
4666
4884
  state.loginReplaceApiConn(state.apiConn());
4667
4885
  return state;
4668
4886
  }
@@ -4675,7 +4893,22 @@ var BraintrustState = class _BraintrustState {
4675
4893
  setMaskingFunction(maskingFunction) {
4676
4894
  this.bgLogger().setMaskingFunction(maskingFunction);
4677
4895
  }
4896
+ setDebugLogLevel(option) {
4897
+ if (option === void 0) {
4898
+ return;
4899
+ }
4900
+ this.debugLogLevelConfigured = true;
4901
+ this.debugLogLevel = normalizeDebugLogLevelOption(option);
4902
+ setGlobalDebugLogLevel(this.debugLogLevel ?? false);
4903
+ }
4904
+ getDebugLogLevel() {
4905
+ return this.debugLogLevel;
4906
+ }
4907
+ hasDebugLogLevelOverride() {
4908
+ return this.debugLogLevelConfigured;
4909
+ }
4678
4910
  async login(loginParams) {
4911
+ this.setDebugLogLevel(loginParams.debugLogLevel);
4679
4912
  if (this.apiUrl && !loginParams.forceLogin) {
4680
4913
  return;
4681
4914
  }
@@ -4786,6 +5019,7 @@ function _internalSetInitialState() {
4786
5019
  _globalState = existing;
4787
5020
  }
4788
5021
  var _internalGetGlobalState = () => _globalState;
5022
+ setDebugLogStateResolver(() => _internalGetGlobalState());
4789
5023
  var FailedHTTPResponse = class extends Error {
4790
5024
  status;
4791
5025
  text;
@@ -4900,7 +5134,7 @@ var HTTPConnection = class _HTTPConnection {
4900
5134
  return await resp.json();
4901
5135
  } catch (e) {
4902
5136
  if (i < tries - 1) {
4903
- console.log(
5137
+ debugLogger.debug(
4904
5138
  `Retrying API request ${object_type} ${JSON.stringify(args)} ${e.status} ${e.text}`
4905
5139
  );
4906
5140
  continue;
@@ -5112,7 +5346,7 @@ with a Blob/ArrayBuffer, or run the program on Node.js.`
5112
5346
  try {
5113
5347
  statSync2(data);
5114
5348
  } catch (e) {
5115
- console.warn(`Failed to read file: ${e}`);
5349
+ debugLogger.warn(`Failed to read file: ${e}`);
5116
5350
  }
5117
5351
  }
5118
5352
  };
@@ -5824,7 +6058,7 @@ var HTTPBackgroundLogger = class _HTTPBackgroundLogger {
5824
6058
  this.queueDropLoggingPeriod = queueDropLoggingPeriodEnv;
5825
6059
  }
5826
6060
  if (isomorph_default.getEnv("BRAINTRUST_LOG_FLUSH_CHUNK_SIZE")) {
5827
- console.warn(
6061
+ debugLogger.warn(
5828
6062
  "BRAINTRUST_LOG_FLUSH_CHUNK_SIZE is deprecated and no longer has any effect. Log flushing now sends all items at once and batches them automatically. This environment variable will be removed in a future major release."
5829
6063
  );
5830
6064
  }
@@ -5886,7 +6120,10 @@ var HTTPBackgroundLogger = class _HTTPBackgroundLogger {
5886
6120
  const versionInfo = await conn.get_json("version");
5887
6121
  serverLimit = z8.object({ logs3_payload_max_bytes: z8.number().nullish() }).parse(versionInfo).logs3_payload_max_bytes ?? null;
5888
6122
  } catch (e) {
5889
- console.warn("Failed to fetch version info for payload limit:", e);
6123
+ debugLogger.warn(
6124
+ "Failed to fetch version info for payload limit:",
6125
+ e
6126
+ );
5890
6127
  }
5891
6128
  const validServerLimit = serverLimit !== null && serverLimit > 0 ? serverLimit : null;
5892
6129
  const canUseOverflow = validServerLimit !== null;
@@ -6030,16 +6267,16 @@ var HTTPBackgroundLogger = class _HTTPBackgroundLogger {
6030
6267
  if (isRetrying) {
6031
6268
  errmsg += ". Retrying";
6032
6269
  }
6033
- console.warn(errmsg);
6270
+ debugLogger.warn(errmsg);
6034
6271
  if (!isRetrying) {
6035
- console.warn(
6272
+ debugLogger.warn(
6036
6273
  `Failed to construct log records to flush after ${this.numTries} attempts. Dropping batch`
6037
6274
  );
6038
6275
  throw e;
6039
6276
  } else {
6040
- console.warn(e);
6277
+ debugLogger.warn(e);
6041
6278
  const sleepTimeS = BACKGROUND_LOGGER_BASE_SLEEP_TIME_S * 2 ** i;
6042
- console.info(`Sleeping for ${sleepTimeS}s`);
6279
+ debugLogger.info(`Sleeping for ${sleepTimeS}s`);
6043
6280
  await new Promise(
6044
6281
  (resolve) => setTimeout(resolve, sleepTimeS * 1e3)
6045
6282
  );
@@ -6140,15 +6377,15 @@ Error: ${errorText}`;
6140
6377
  this.logFailedPayloadsDir();
6141
6378
  }
6142
6379
  if (!isRetrying) {
6143
- console.warn(
6380
+ debugLogger.warn(
6144
6381
  `log request failed after ${this.numTries} retries. Dropping batch`
6145
6382
  );
6146
6383
  throw new Error(errMsg);
6147
6384
  } else {
6148
- console.warn(errMsg);
6385
+ debugLogger.warn(errMsg);
6149
6386
  if (isRetrying) {
6150
6387
  const sleepTimeS = BACKGROUND_LOGGER_BASE_SLEEP_TIME_S * 2 ** i;
6151
- console.info(`Sleeping for ${sleepTimeS}s`);
6388
+ debugLogger.info(`Sleeping for ${sleepTimeS}s`);
6152
6389
  await new Promise(
6153
6390
  (resolve) => setTimeout(resolve, sleepTimeS * 1e3)
6154
6391
  );
@@ -6163,7 +6400,7 @@ Error: ${errorText}`;
6163
6400
  this.queueDropLoggingState.numDropped += numItems;
6164
6401
  const timeNow = getCurrentUnixTimestamp();
6165
6402
  if (timeNow - this.queueDropLoggingState.lastLoggedTimestamp > this.queueDropLoggingPeriod) {
6166
- console.warn(
6403
+ debugLogger.warn(
6167
6404
  `Dropped ${this.queueDropLoggingState.numDropped} elements due to full queue`
6168
6405
  );
6169
6406
  if (this.failedPublishPayloadsDir) {
@@ -6195,7 +6432,7 @@ Error: ${errorText}`;
6195
6432
  await _HTTPBackgroundLogger.writePayloadToDir({ payloadDir, payload });
6196
6433
  }
6197
6434
  } catch (e) {
6198
- console.error(e);
6435
+ debugLogger.error(e);
6199
6436
  }
6200
6437
  }
6201
6438
  static async writePayloadToDir({
@@ -6203,7 +6440,7 @@ Error: ${errorText}`;
6203
6440
  payload
6204
6441
  }) {
6205
6442
  if (!(isomorph_default.pathJoin && isomorph_default.mkdir && isomorph_default.writeFile)) {
6206
- console.warn(
6443
+ debugLogger.warn(
6207
6444
  "Cannot dump payloads: filesystem-operations not supported on this platform"
6208
6445
  );
6209
6446
  return;
@@ -6216,7 +6453,7 @@ Error: ${errorText}`;
6216
6453
  await isomorph_default.mkdir(payloadDir, { recursive: true });
6217
6454
  await isomorph_default.writeFile(payloadFile, payload);
6218
6455
  } catch (e) {
6219
- console.error(
6456
+ debugLogger.error(
6220
6457
  `Failed to write failed payload to output file ${payloadFile}:
6221
6458
  `,
6222
6459
  e
@@ -6247,7 +6484,9 @@ Error: ${errorText}`;
6247
6484
  }
6248
6485
  }
6249
6486
  logFailedPayloadsDir() {
6250
- console.warn(`Logging failed payloads to ${this.failedPublishPayloadsDir}`);
6487
+ debugLogger.warn(
6488
+ `Logging failed payloads to ${this.failedPublishPayloadsDir}`
6489
+ );
6251
6490
  }
6252
6491
  // Should only be called by BraintrustState.
6253
6492
  internalReplaceApiConn(apiConn) {
@@ -6278,6 +6517,7 @@ function init(projectOrOptions, optionalOptions) {
6278
6517
  experiment,
6279
6518
  description,
6280
6519
  dataset,
6520
+ parameters,
6281
6521
  baseExperiment,
6282
6522
  isPublic,
6283
6523
  open: open2,
@@ -6395,6 +6635,17 @@ function init(projectOrOptions, optionalOptions) {
6395
6635
  args["dataset_version"] = await dataset.version();
6396
6636
  }
6397
6637
  }
6638
+ if (parameters !== void 0) {
6639
+ if (RemoteEvalParameters.isParameters(parameters)) {
6640
+ args["parameters_id"] = parameters.id;
6641
+ args["parameters_version"] = parameters.version;
6642
+ } else {
6643
+ args["parameters_id"] = parameters.id;
6644
+ if (parameters.version !== void 0) {
6645
+ args["parameters_version"] = parameters.version;
6646
+ }
6647
+ }
6648
+ }
6398
6649
  if (isPublic !== void 0) {
6399
6650
  args["public"] = isPublic;
6400
6651
  }
@@ -6412,9 +6663,7 @@ function init(projectOrOptions, optionalOptions) {
6412
6663
  break;
6413
6664
  } catch (e) {
6414
6665
  if (args["base_experiment"] && `${"data" in e && e.data}`.includes("base experiment")) {
6415
- console.warn(
6416
- `Base experiment ${args["base_experiment"]} not found.`
6417
- );
6666
+ debugLogger.forState(state).warn(`Base experiment ${args["base_experiment"]} not found.`);
6418
6667
  delete args["base_experiment"];
6419
6668
  } else {
6420
6669
  throw e;
@@ -6556,7 +6805,11 @@ async function computeLoggerMetadata(state, {
6556
6805
  }
6557
6806
  async function login(options = {}) {
6558
6807
  const { forceLogin = false } = options || {};
6808
+ if (!_internalGetGlobalState()) {
6809
+ _internalSetInitialState();
6810
+ }
6559
6811
  const state = _internalGetGlobalState();
6812
+ state.setDebugLogLevel(options.debugLogLevel);
6560
6813
  if (state.loggedIn && !forceLogin) {
6561
6814
  let checkUpdatedParam2 = function(varname, arg, orig) {
6562
6815
  if (!isEmpty2(arg) && !isEmpty2(orig) && arg !== orig) {
@@ -6575,9 +6828,6 @@ async function login(options = {}) {
6575
6828
  checkUpdatedParam2("orgName", options.orgName, state.orgName);
6576
6829
  return state;
6577
6830
  }
6578
- if (!state) {
6579
- _internalSetInitialState();
6580
- }
6581
6831
  await state.login(options);
6582
6832
  return state;
6583
6833
  }
@@ -6989,7 +7239,14 @@ var ObjectFetcher = class {
6989
7239
  async *fetchRecordsFromApi(batchSize) {
6990
7240
  const state = await this.getState();
6991
7241
  const objectId = await this.id;
6992
- const limit = batchSize ?? DEFAULT_FETCH_BATCH_SIZE;
7242
+ const batchLimit = batchSize ?? DEFAULT_FETCH_BATCH_SIZE;
7243
+ const internalLimit = this._internal_btql?.limit;
7244
+ const limit = batchSize !== void 0 ? batchSize : internalLimit ?? batchLimit;
7245
+ const internalBtqlWithoutReservedQueryKeys = Object.fromEntries(
7246
+ Object.entries(this._internal_btql ?? {}).filter(
7247
+ ([key]) => key !== "cursor" && key !== "limit" && key !== "select" && key !== "from"
7248
+ )
7249
+ );
6993
7250
  let cursor = void 0;
6994
7251
  let iterations = 0;
6995
7252
  while (true) {
@@ -6997,7 +7254,6 @@ var ObjectFetcher = class {
6997
7254
  `btql`,
6998
7255
  {
6999
7256
  query: {
7000
- ...this._internal_btql,
7001
7257
  select: [
7002
7258
  {
7003
7259
  op: "star"
@@ -7017,7 +7273,8 @@ var ObjectFetcher = class {
7017
7273
  ]
7018
7274
  },
7019
7275
  cursor,
7020
- limit
7276
+ limit,
7277
+ ...internalBtqlWithoutReservedQueryKeys
7021
7278
  },
7022
7279
  use_columnstore: false,
7023
7280
  brainstore_realtime: true,
@@ -7290,7 +7547,7 @@ var Experiment2 = class extends ObjectFetcher {
7290
7547
  scores = results["scores"];
7291
7548
  metrics = results["metrics"];
7292
7549
  } catch (e) {
7293
- console.warn(
7550
+ debugLogger.forState(state).warn(
7294
7551
  `Failed to fetch experiment scores and metrics: ${e}
7295
7552
 
7296
7553
  View complete results in Braintrust or run experiment.summarize() again.`
@@ -7367,7 +7624,7 @@ View complete results in Braintrust or run experiment.summarize() again.`
7367
7624
  * @deprecated This function is deprecated. You can simply remove it from your code.
7368
7625
  */
7369
7626
  async close() {
7370
- console.warn(
7627
+ debugLogger.forState(this.state).warn(
7371
7628
  "close is deprecated and will be removed in a future version of braintrust. It is now a no-op and can be removed"
7372
7629
  );
7373
7630
  return this.id;
@@ -7564,8 +7821,8 @@ var SpanImpl = class _SpanImpl {
7564
7821
  ...serializableInternalData,
7565
7822
  [IS_MERGE_FIELD]: this.isMerge
7566
7823
  });
7567
- if (partialRecord.metrics?.end) {
7568
- this.loggedEndTime = partialRecord.metrics?.end;
7824
+ if (typeof partialRecord.metrics?.end === "number") {
7825
+ this.loggedEndTime = partialRecord.metrics.end;
7569
7826
  }
7570
7827
  if (this.parentObjectType === 1 /* EXPERIMENT */) {
7571
7828
  const cachedSpan = {
@@ -7800,7 +8057,7 @@ var Dataset2 = class extends ObjectFetcher {
7800
8057
  constructor(state, lazyMetadata, pinnedVersion, legacy, _internal_btql) {
7801
8058
  const isLegacyDataset = legacy ?? DEFAULT_IS_LEGACY_DATASET;
7802
8059
  if (isLegacyDataset) {
7803
- console.warn(
8060
+ debugLogger.forState(state).warn(
7804
8061
  `Records will be fetched from this dataset in the legacy format, with the "expected" field renamed to "output". Please update your code to use "expected", and use \`braintrust.initDataset()\` with \`{ useOutput: false }\`, which will become the default in a future version of Braintrust.`
7805
8062
  );
7806
8063
  }
@@ -8031,7 +8288,7 @@ var Dataset2 = class extends ObjectFetcher {
8031
8288
  * @deprecated This function is deprecated. You can simply remove it from your code.
8032
8289
  */
8033
8290
  async close() {
8034
- console.warn(
8291
+ debugLogger.forState(this.state).warn(
8035
8292
  "close is deprecated and will be removed in a future version of braintrust. It is now a no-op and can be removed"
8036
8293
  );
8037
8294
  return this.id;
@@ -8040,40 +8297,82 @@ var Dataset2 = class extends ObjectFetcher {
8040
8297
  return typeof data === "object" && data !== null && "__braintrust_dataset_marker" in data;
8041
8298
  }
8042
8299
  };
8043
- function renderMessage(render, message) {
8300
+ function isAttachmentObject(value) {
8301
+ return BraintrustAttachmentReference.safeParse(value).success || InlineAttachmentReferenceSchema.safeParse(value).success || ExternalAttachmentReference.safeParse(value).success;
8302
+ }
8303
+ function isURL(url) {
8304
+ try {
8305
+ const parsedUrl = new URL(url.trim());
8306
+ return parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:";
8307
+ } catch {
8308
+ return false;
8309
+ }
8310
+ }
8311
+ function expandAttachmentArrayPreTemplate(content, variables) {
8312
+ if (typeof content !== "string") return null;
8313
+ const match = content.match(/^\{\{\s*([\w.]+)\s*\}\}$/);
8314
+ if (!match) return null;
8315
+ const varPath = match[1];
8316
+ const value = varPath.includes(".") ? getObjValueByPath(variables, varPath.split(".")) : variables[varPath];
8317
+ if (!Array.isArray(value)) return null;
8318
+ const allValid = value.every(
8319
+ (v) => isAttachmentObject(v) || typeof v === "string" && isURL(v)
8320
+ );
8321
+ if (!allValid) return null;
8322
+ return value.map((item) => ({
8323
+ type: "image_url",
8324
+ image_url: { url: item }
8325
+ }));
8326
+ }
8327
+ function renderMessageImpl(render, message, variables) {
8044
8328
  return {
8045
8329
  ...message,
8046
8330
  ..."content" in message ? {
8047
- content: isEmpty2(message.content) ? void 0 : typeof message.content === "string" ? render(message.content) : message.content.map((c) => {
8331
+ content: isEmpty2(message.content) ? void 0 : typeof message.content === "string" ? render(message.content) : message.content.flatMap((c) => {
8048
8332
  switch (c.type) {
8049
8333
  case "text":
8050
- return { ...c, text: render(c.text) };
8334
+ return [{ ...c, text: render(c.text) }];
8051
8335
  case "image_url":
8052
8336
  if (isObject(c.image_url.url)) {
8053
8337
  throw new Error(
8054
8338
  "Attachments must be replaced with URLs before calling `build()`"
8055
8339
  );
8056
8340
  }
8057
- return {
8058
- ...c,
8059
- image_url: {
8060
- ...c.image_url,
8061
- url: render(c.image_url.url)
8341
+ if (variables) {
8342
+ const expanded = expandAttachmentArrayPreTemplate(
8343
+ c.image_url.url,
8344
+ variables
8345
+ );
8346
+ if (expanded) {
8347
+ return expanded;
8062
8348
  }
8063
- };
8349
+ }
8350
+ return [
8351
+ {
8352
+ ...c,
8353
+ image_url: {
8354
+ ...c.image_url,
8355
+ url: render(c.image_url.url)
8356
+ }
8357
+ }
8358
+ ];
8064
8359
  case "file":
8065
- return {
8066
- ...c,
8067
- file: {
8068
- file_data: render(c.file.file_data || ""),
8069
- ...c.file.file_id && {
8070
- file_id: render(c.file.file_id)
8071
- },
8072
- ...c.file.filename && {
8073
- filename: render(c.file.filename)
8360
+ return [
8361
+ {
8362
+ ...c,
8363
+ file: {
8364
+ ...c.file.file_data && {
8365
+ file_data: render(c.file.file_data)
8366
+ },
8367
+ ...c.file.file_id && {
8368
+ file_id: render(c.file.file_id)
8369
+ },
8370
+ ...c.file.filename && {
8371
+ filename: render(c.file.filename)
8372
+ }
8074
8373
  }
8075
8374
  }
8076
- };
8375
+ ];
8077
8376
  default:
8078
8377
  const _exhaustiveCheck = c;
8079
8378
  return _exhaustiveCheck;
@@ -8230,17 +8529,19 @@ var Prompt2 = class _Prompt {
8230
8529
  }
8231
8530
  runBuild(buildArgs, options) {
8232
8531
  const { flavor } = options;
8233
- const params = {
8234
- ...this.defaults,
8235
- ...Object.fromEntries(
8236
- Object.entries(this.options.params || {}).filter(
8237
- ([k, _v]) => !BRAINTRUST_PARAMS.includes(k)
8238
- )
8239
- ),
8240
- ...!isEmpty2(this.options.model) ? {
8241
- model: this.options.model
8242
- } : {}
8243
- };
8532
+ const params = Object.fromEntries(
8533
+ Object.entries({
8534
+ ...this.defaults,
8535
+ ...Object.fromEntries(
8536
+ Object.entries(this.options.params || {}).filter(
8537
+ ([k, _v]) => !BRAINTRUST_PARAMS.includes(k)
8538
+ )
8539
+ ),
8540
+ ...!isEmpty2(this.options.model) ? {
8541
+ model: this.options.model
8542
+ } : {}
8543
+ }).filter(([key, value]) => key !== "response_format" || value !== null)
8544
+ );
8244
8545
  if (!("model" in params) || isEmpty2(params.model)) {
8245
8546
  throw new Error(
8246
8547
  "No model specified. Either specify it in the prompt or as a default"
@@ -8340,7 +8641,7 @@ var Prompt2 = class _Prompt {
8340
8641
  templateFormat
8341
8642
  });
8342
8643
  const baseMessages = (prompt.messages || []).map(
8343
- (m) => renderMessage(render, m)
8644
+ (m) => renderMessageImpl(render, m, variables)
8344
8645
  );
8345
8646
  const hasSystemPrompt = baseMessages.some((m) => m.role === "system");
8346
8647
  const messages = [
@@ -8447,9 +8748,6 @@ var RemoteEvalParameters = class {
8447
8748
  };
8448
8749
  var TEST_API_KEY = "___TEST_API_KEY__THIS_IS_NOT_REAL___";
8449
8750
 
8450
- // src/instrumentation/core/plugin.ts
8451
- import { tracingChannel } from "dc-browser";
8452
-
8453
8751
  // src/instrumentation/core/stream-patcher.ts
8454
8752
  function isAsyncIterable(value) {
8455
8753
  return value !== null && typeof value === "object" && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
@@ -8465,7 +8763,7 @@ function patchStreamIfNeeded(stream, options) {
8465
8763
  return stream;
8466
8764
  }
8467
8765
  const originalIteratorFn = stream[Symbol.asyncIterator];
8468
- if (originalIteratorFn.__braintrust_patched) {
8766
+ if ("__braintrust_patched" in originalIteratorFn && originalIteratorFn["__braintrust_patched"]) {
8469
8767
  return stream;
8470
8768
  }
8471
8769
  try {
@@ -8506,7 +8804,10 @@ function patchStreamIfNeeded(stream, options) {
8506
8804
  completed = true;
8507
8805
  if (options.onError) {
8508
8806
  try {
8509
- options.onError(error, chunks);
8807
+ options.onError(
8808
+ error instanceof Error ? error : new Error(String(error)),
8809
+ chunks
8810
+ );
8510
8811
  } catch (handlerError) {
8511
8812
  console.error("Error in stream onError handler:", handlerError);
8512
8813
  }
@@ -8534,7 +8835,8 @@ function patchStreamIfNeeded(stream, options) {
8534
8835
  iterator.throw = async function(...args) {
8535
8836
  if (!completed) {
8536
8837
  completed = true;
8537
- const error = args[0];
8838
+ const rawError = args[0];
8839
+ const error = rawError instanceof Error ? rawError : new Error(String(rawError));
8538
8840
  if (options.onError) {
8539
8841
  try {
8540
8842
  options.onError(error, chunks);
@@ -8548,7 +8850,9 @@ function patchStreamIfNeeded(stream, options) {
8548
8850
  }
8549
8851
  return iterator;
8550
8852
  };
8551
- patchedIteratorFn.__braintrust_patched = true;
8853
+ Object.defineProperty(patchedIteratorFn, "__braintrust_patched", {
8854
+ value: true
8855
+ });
8552
8856
  stream[Symbol.asyncIterator] = patchedIteratorFn;
8553
8857
  return stream;
8554
8858
  } catch (error) {
@@ -8557,6 +8861,49 @@ function patchStreamIfNeeded(stream, options) {
8557
8861
  }
8558
8862
  }
8559
8863
 
8864
+ // src/instrumentation/core/channel-tracing-utils.ts
8865
+ function hasChannelSpanInfo(value) {
8866
+ return isObject(value) && isObject(value.span_info);
8867
+ }
8868
+ function getChannelSpanInfo(event) {
8869
+ if (isObject(event.span_info)) {
8870
+ return event.span_info;
8871
+ }
8872
+ const firstArg = event.arguments?.[0];
8873
+ if (hasChannelSpanInfo(firstArg)) {
8874
+ return firstArg.span_info;
8875
+ }
8876
+ return void 0;
8877
+ }
8878
+ function buildStartSpanArgs(config, event) {
8879
+ const spanInfo = getChannelSpanInfo(event);
8880
+ const spanAttributes = {
8881
+ type: config.type
8882
+ };
8883
+ if (isObject(spanInfo?.spanAttributes)) {
8884
+ mergeDicts(spanAttributes, spanInfo.spanAttributes);
8885
+ }
8886
+ return {
8887
+ name: typeof spanInfo?.name === "string" && spanInfo.name ? spanInfo.name : config.name,
8888
+ spanAttributes,
8889
+ spanInfoMetadata: isObject(spanInfo?.metadata) ? spanInfo.metadata : void 0
8890
+ };
8891
+ }
8892
+ function mergeInputMetadata(metadata, spanInfoMetadata) {
8893
+ if (!spanInfoMetadata) {
8894
+ return isObject(metadata) ? (
8895
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
8896
+ metadata
8897
+ ) : void 0;
8898
+ }
8899
+ const mergedMetadata = {};
8900
+ mergeDicts(mergedMetadata, spanInfoMetadata);
8901
+ if (isObject(metadata)) {
8902
+ mergeDicts(mergedMetadata, metadata);
8903
+ }
8904
+ return mergedMetadata;
8905
+ }
8906
+
8560
8907
  // src/instrumentation/core/plugin.ts
8561
8908
  var BasePlugin = class {
8562
8909
  enabled = false;
@@ -8588,23 +8935,25 @@ var BasePlugin = class {
8588
8935
  * @param handlers - Event handlers
8589
8936
  */
8590
8937
  subscribe(channelName, handlers) {
8591
- const channel = tracingChannel(channelName);
8592
- channel.subscribe(handlers);
8938
+ const channel2 = isomorph_default.newTracingChannel(channelName);
8939
+ channel2.subscribe(handlers);
8593
8940
  }
8594
8941
  /**
8595
8942
  * Subscribe to a channel for async methods (non-streaming).
8596
8943
  * Creates a span and logs input/output/metrics.
8597
8944
  */
8598
8945
  subscribeToChannel(channelName, config) {
8599
- const channel = tracingChannel(channelName);
8946
+ const channel2 = isomorph_default.newTracingChannel(channelName);
8600
8947
  const spans = /* @__PURE__ */ new WeakMap();
8601
8948
  const handlers = {
8602
8949
  start: (event) => {
8950
+ const { name, spanAttributes, spanInfoMetadata } = buildStartSpanArgs(
8951
+ config,
8952
+ event
8953
+ );
8603
8954
  const span = startSpan({
8604
- name: config.name,
8605
- spanAttributes: {
8606
- type: config.type
8607
- }
8955
+ name,
8956
+ spanAttributes
8608
8957
  });
8609
8958
  const startTime = getCurrentUnixTimestamp();
8610
8959
  spans.set(event, { span, startTime });
@@ -8612,7 +8961,7 @@ var BasePlugin = class {
8612
8961
  const { input, metadata } = config.extractInput(event.arguments);
8613
8962
  span.log({
8614
8963
  input,
8615
- metadata
8964
+ metadata: mergeInputMetadata(metadata, spanInfoMetadata)
8616
8965
  });
8617
8966
  } catch (error) {
8618
8967
  console.error(`Error extracting input for ${channelName}:`, error);
@@ -8625,10 +8974,12 @@ var BasePlugin = class {
8625
8974
  }
8626
8975
  const { span, startTime } = spanData;
8627
8976
  try {
8628
- const output = config.extractOutput(event.result);
8629
- const metrics = config.extractMetrics(event.result, startTime);
8977
+ const output = config.extractOutput(event.result, event);
8978
+ const metrics = config.extractMetrics(event.result, startTime, event);
8979
+ const metadata = config.extractMetadata?.(event.result, event);
8630
8980
  span.log({
8631
8981
  output,
8982
+ ...metadata !== void 0 ? { metadata } : {},
8632
8983
  metrics
8633
8984
  });
8634
8985
  } catch (error) {
@@ -8651,9 +9002,9 @@ var BasePlugin = class {
8651
9002
  spans.delete(event);
8652
9003
  }
8653
9004
  };
8654
- channel.subscribe(handlers);
9005
+ channel2.subscribe(handlers);
8655
9006
  this.unsubscribers.push(() => {
8656
- channel.unsubscribe(handlers);
9007
+ channel2.unsubscribe(handlers);
8657
9008
  });
8658
9009
  }
8659
9010
  /**
@@ -8661,15 +9012,17 @@ var BasePlugin = class {
8661
9012
  * Handles both streaming and non-streaming responses.
8662
9013
  */
8663
9014
  subscribeToStreamingChannel(channelName, config) {
8664
- const channel = tracingChannel(channelName);
9015
+ const channel2 = isomorph_default.newTracingChannel(channelName);
8665
9016
  const spans = /* @__PURE__ */ new WeakMap();
8666
9017
  const handlers = {
8667
9018
  start: (event) => {
9019
+ const { name, spanAttributes, spanInfoMetadata } = buildStartSpanArgs(
9020
+ config,
9021
+ event
9022
+ );
8668
9023
  const span = startSpan({
8669
- name: config.name,
8670
- spanAttributes: {
8671
- type: config.type
8672
- }
9024
+ name,
9025
+ spanAttributes
8673
9026
  });
8674
9027
  const startTime = getCurrentUnixTimestamp();
8675
9028
  spans.set(event, { span, startTime });
@@ -8677,7 +9030,7 @@ var BasePlugin = class {
8677
9030
  const { input, metadata } = config.extractInput(event.arguments);
8678
9031
  span.log({
8679
9032
  input,
8680
- metadata
9033
+ metadata: mergeInputMetadata(metadata, spanInfoMetadata)
8681
9034
  });
8682
9035
  } catch (error) {
8683
9036
  console.error(`Error extracting input for ${channelName}:`, error);
@@ -8690,24 +9043,39 @@ var BasePlugin = class {
8690
9043
  }
8691
9044
  const { span, startTime } = spanData;
8692
9045
  if (isAsyncIterable(event.result)) {
9046
+ let firstChunkTime;
8693
9047
  patchStreamIfNeeded(event.result, {
9048
+ onChunk: () => {
9049
+ if (firstChunkTime === void 0) {
9050
+ firstChunkTime = getCurrentUnixTimestamp();
9051
+ }
9052
+ },
8694
9053
  onComplete: (chunks) => {
8695
9054
  try {
8696
9055
  let output;
8697
9056
  let metrics;
9057
+ let metadata;
8698
9058
  if (config.aggregateChunks) {
8699
- const aggregated = config.aggregateChunks(chunks);
9059
+ const aggregated = config.aggregateChunks(
9060
+ chunks,
9061
+ event.result,
9062
+ event
9063
+ );
8700
9064
  output = aggregated.output;
8701
9065
  metrics = aggregated.metrics;
9066
+ metadata = aggregated.metadata;
8702
9067
  } else {
8703
- output = config.extractOutput(chunks);
8704
- metrics = config.extractMetrics(chunks, startTime);
9068
+ output = config.extractOutput(chunks, event);
9069
+ metrics = config.extractMetrics(chunks, startTime, event);
8705
9070
  }
8706
- if (!metrics.time_to_first_token && chunks.length > 0) {
9071
+ if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
9072
+ metrics.time_to_first_token = firstChunkTime - startTime;
9073
+ } else if (metrics.time_to_first_token === void 0 && chunks.length > 0) {
8707
9074
  metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8708
9075
  }
8709
9076
  span.log({
8710
9077
  output,
9078
+ ...metadata !== void 0 ? { metadata } : {},
8711
9079
  metrics
8712
9080
  });
8713
9081
  } catch (error) {
@@ -8728,10 +9096,16 @@ var BasePlugin = class {
8728
9096
  });
8729
9097
  } else {
8730
9098
  try {
8731
- const output = config.extractOutput(event.result);
8732
- const metrics = config.extractMetrics(event.result, startTime);
9099
+ const output = config.extractOutput(event.result, event);
9100
+ const metadata = config.extractMetadata ? config.extractMetadata(event.result, event) : void 0;
9101
+ const metrics = config.extractMetrics(
9102
+ event.result,
9103
+ startTime,
9104
+ event
9105
+ );
8733
9106
  span.log({
8734
9107
  output,
9108
+ ...metadata !== void 0 ? { metadata } : {},
8735
9109
  metrics
8736
9110
  });
8737
9111
  } catch (error) {
@@ -8755,9 +9129,9 @@ var BasePlugin = class {
8755
9129
  spans.delete(event);
8756
9130
  }
8757
9131
  };
8758
- channel.subscribe(handlers);
9132
+ channel2.subscribe(handlers);
8759
9133
  this.unsubscribers.push(() => {
8760
- channel.unsubscribe(handlers);
9134
+ channel2.unsubscribe(handlers);
8761
9135
  });
8762
9136
  }
8763
9137
  /**
@@ -8765,15 +9139,17 @@ var BasePlugin = class {
8765
9139
  * Used for methods like beta.chat.completions.stream() and responses.stream().
8766
9140
  */
8767
9141
  subscribeToSyncStreamChannel(channelName, config) {
8768
- const channel = tracingChannel(channelName);
9142
+ const channel2 = isomorph_default.newTracingChannel(channelName);
8769
9143
  const spans = /* @__PURE__ */ new WeakMap();
8770
9144
  const handlers = {
8771
9145
  start: (event) => {
9146
+ const { name, spanAttributes, spanInfoMetadata } = buildStartSpanArgs(
9147
+ config,
9148
+ event
9149
+ );
8772
9150
  const span = startSpan({
8773
- name: config.name,
8774
- spanAttributes: {
8775
- type: config.type
8776
- }
9151
+ name,
9152
+ spanAttributes
8777
9153
  });
8778
9154
  const startTime = getCurrentUnixTimestamp();
8779
9155
  spans.set(event, { span, startTime });
@@ -8781,7 +9157,7 @@ var BasePlugin = class {
8781
9157
  const { input, metadata } = config.extractInput(event.arguments);
8782
9158
  span.log({
8783
9159
  input,
8784
- metadata
9160
+ metadata: mergeInputMetadata(metadata, spanInfoMetadata)
8785
9161
  });
8786
9162
  } catch (error) {
8787
9163
  console.error(`Error extracting input for ${channelName}:`, error);
@@ -8865,141 +9241,652 @@ var BasePlugin = class {
8865
9241
  spans.delete(event);
8866
9242
  }
8867
9243
  };
8868
- channel.subscribe(handlers);
9244
+ channel2.subscribe(handlers);
8869
9245
  this.unsubscribers.push(() => {
8870
- channel.unsubscribe(handlers);
9246
+ channel2.unsubscribe(handlers);
8871
9247
  });
8872
9248
  }
8873
9249
  };
8874
9250
 
8875
- // src/wrappers/attachment-utils.ts
8876
- function getExtensionFromMediaType(mediaType) {
8877
- const extensionMap = {
8878
- "image/png": "png",
8879
- "image/jpeg": "jpg",
8880
- "image/gif": "gif",
8881
- "image/webp": "webp",
8882
- "image/svg+xml": "svg",
8883
- "audio/mpeg": "mp3",
8884
- "audio/wav": "wav",
8885
- "audio/ogg": "ogg",
8886
- "video/mp4": "mp4",
8887
- "video/webm": "webm",
8888
- "application/pdf": "pdf",
8889
- "application/json": "json",
8890
- "text/plain": "txt",
8891
- "text/html": "html",
8892
- "text/csv": "csv"
8893
- };
8894
- return extensionMap[mediaType] || "bin";
9251
+ // src/instrumentation/core/channel-tracing.ts
9252
+ function isSyncStreamLike(value) {
9253
+ return !!value && typeof value === "object" && typeof value.on === "function";
8895
9254
  }
8896
- function convertDataToBlob(data, mediaType) {
9255
+ function hasChoices(value) {
9256
+ return !!value && typeof value === "object" && "choices" in value;
9257
+ }
9258
+ function normalizeMetadata(metadata) {
9259
+ return isObject(metadata) ? metadata : void 0;
9260
+ }
9261
+ function startSpanForEvent(config, event, channelName) {
9262
+ const { name, spanAttributes, spanInfoMetadata } = buildStartSpanArgs(
9263
+ config,
9264
+ event
9265
+ );
9266
+ const span = startSpan({
9267
+ name,
9268
+ spanAttributes
9269
+ });
9270
+ const startTime = getCurrentUnixTimestamp();
8897
9271
  try {
8898
- if (typeof data === "string") {
8899
- if (data.startsWith("data:")) {
8900
- const base64Match = data.match(/^data:[^;]+;base64,(.+)$/);
8901
- if (base64Match) {
8902
- const base64 = base64Match[1];
8903
- const binaryString = atob(base64);
8904
- const bytes = new Uint8Array(binaryString.length);
8905
- for (let i = 0; i < binaryString.length; i++) {
8906
- bytes[i] = binaryString.charCodeAt(i);
8907
- }
8908
- return new Blob([bytes], { type: mediaType });
8909
- }
8910
- } else if (data.startsWith("http://") || data.startsWith("https://")) {
8911
- return null;
8912
- } else {
8913
- const binaryString = atob(data);
8914
- const bytes = new Uint8Array(binaryString.length);
8915
- for (let i = 0; i < binaryString.length; i++) {
8916
- bytes[i] = binaryString.charCodeAt(i);
8917
- }
8918
- return new Blob([bytes], { type: mediaType });
8919
- }
8920
- } else if (data instanceof Uint8Array) {
8921
- return new Blob([data], { type: mediaType });
8922
- } else if (data instanceof ArrayBuffer) {
8923
- return new Blob([data], { type: mediaType });
8924
- } else if (typeof Buffer !== "undefined" && data instanceof Buffer) {
8925
- return new Blob([data], { type: mediaType });
8926
- }
8927
- } catch {
8928
- return null;
9272
+ const { input, metadata } = config.extractInput(
9273
+ event.arguments,
9274
+ event,
9275
+ span
9276
+ );
9277
+ span.log({
9278
+ input,
9279
+ metadata: mergeInputMetadata(metadata, spanInfoMetadata)
9280
+ });
9281
+ } catch (error) {
9282
+ console.error(`Error extracting input for ${channelName}:`, error);
8929
9283
  }
8930
- return null;
9284
+ return { span, startTime };
8931
9285
  }
8932
- function processInputAttachments(input) {
8933
- if (!input) {
8934
- return input;
9286
+ function logErrorAndEnd(states, event) {
9287
+ const spanData = states.get(event);
9288
+ if (!spanData) {
9289
+ return;
8935
9290
  }
8936
- let attachmentIndex = 0;
8937
- const processContentPart = (part) => {
8938
- if (!part || typeof part !== "object") {
8939
- return part;
8940
- }
8941
- if (part.type === "image" && part.image) {
8942
- let mediaType = "image/png";
8943
- if (typeof part.image === "string" && part.image.startsWith("data:")) {
8944
- const mediaTypeMatch = part.image.match(/^data:([^;]+);/);
8945
- if (mediaTypeMatch) {
8946
- mediaType = mediaTypeMatch[1];
8947
- }
8948
- } else if (part.mediaType) {
8949
- mediaType = part.mediaType;
9291
+ spanData.span.log({
9292
+ error: event.error.message
9293
+ });
9294
+ spanData.span.end();
9295
+ states.delete(event);
9296
+ }
9297
+ function traceAsyncChannel(channel2, config) {
9298
+ const tracingChannel2 = channel2.tracingChannel();
9299
+ const states = /* @__PURE__ */ new WeakMap();
9300
+ const channelName = channel2.channelName;
9301
+ const handlers = {
9302
+ start: (event) => {
9303
+ states.set(
9304
+ event,
9305
+ startSpanForEvent(
9306
+ config,
9307
+ event,
9308
+ channelName
9309
+ )
9310
+ );
9311
+ },
9312
+ asyncEnd: (event) => {
9313
+ const spanData = states.get(event);
9314
+ if (!spanData) {
9315
+ return;
8950
9316
  }
8951
- const blob = convertDataToBlob(part.image, mediaType);
8952
- if (blob) {
8953
- const filename = `input_image_${attachmentIndex}.${getExtensionFromMediaType(mediaType)}`;
8954
- attachmentIndex++;
8955
- const attachment = new Attachment({
8956
- data: blob,
8957
- filename,
8958
- contentType: mediaType
9317
+ const asyncEndEvent = event;
9318
+ const { span, startTime } = spanData;
9319
+ try {
9320
+ const output = config.extractOutput(
9321
+ asyncEndEvent.result,
9322
+ asyncEndEvent
9323
+ );
9324
+ const metrics = config.extractMetrics(
9325
+ asyncEndEvent.result,
9326
+ startTime,
9327
+ asyncEndEvent
9328
+ );
9329
+ const metadata = config.extractMetadata?.(
9330
+ asyncEndEvent.result,
9331
+ asyncEndEvent
9332
+ );
9333
+ span.log({
9334
+ output,
9335
+ ...normalizeMetadata(metadata) !== void 0 ? { metadata: normalizeMetadata(metadata) } : {},
9336
+ metrics
8959
9337
  });
8960
- return {
8961
- ...part,
8962
- image: attachment
8963
- };
9338
+ } catch (error) {
9339
+ console.error(`Error extracting output for ${channelName}:`, error);
9340
+ } finally {
9341
+ span.end();
9342
+ states.delete(event);
8964
9343
  }
9344
+ },
9345
+ error: (event) => {
9346
+ logErrorAndEnd(states, event);
8965
9347
  }
8966
- if (part.type === "file" && part.data) {
8967
- const mediaType = part.mediaType || "application/octet-stream";
8968
- const blob = convertDataToBlob(part.data, mediaType);
8969
- if (blob) {
8970
- const filename = part.filename || `input_file_${attachmentIndex}.${getExtensionFromMediaType(mediaType)}`;
8971
- attachmentIndex++;
8972
- const attachment = new Attachment({
8973
- data: blob,
8974
- filename,
8975
- contentType: mediaType
9348
+ };
9349
+ tracingChannel2.subscribe(handlers);
9350
+ return () => {
9351
+ tracingChannel2.unsubscribe(handlers);
9352
+ };
9353
+ }
9354
+ function traceStreamingChannel(channel2, config) {
9355
+ const tracingChannel2 = channel2.tracingChannel();
9356
+ const states = /* @__PURE__ */ new WeakMap();
9357
+ const channelName = channel2.channelName;
9358
+ const handlers = {
9359
+ start: (event) => {
9360
+ states.set(
9361
+ event,
9362
+ startSpanForEvent(
9363
+ config,
9364
+ event,
9365
+ channelName
9366
+ )
9367
+ );
9368
+ },
9369
+ asyncEnd: (event) => {
9370
+ const spanData = states.get(event);
9371
+ if (!spanData) {
9372
+ return;
9373
+ }
9374
+ const asyncEndEvent = event;
9375
+ const { span, startTime } = spanData;
9376
+ if (isAsyncIterable(asyncEndEvent.result)) {
9377
+ let firstChunkTime;
9378
+ patchStreamIfNeeded(asyncEndEvent.result, {
9379
+ onChunk: () => {
9380
+ if (firstChunkTime === void 0) {
9381
+ firstChunkTime = getCurrentUnixTimestamp();
9382
+ }
9383
+ },
9384
+ onComplete: (chunks) => {
9385
+ try {
9386
+ let output;
9387
+ let metrics;
9388
+ let metadata;
9389
+ if (config.aggregateChunks) {
9390
+ const aggregated = config.aggregateChunks(
9391
+ chunks,
9392
+ asyncEndEvent.result,
9393
+ asyncEndEvent,
9394
+ startTime
9395
+ );
9396
+ output = aggregated.output;
9397
+ metrics = aggregated.metrics;
9398
+ metadata = aggregated.metadata;
9399
+ } else {
9400
+ output = config.extractOutput(
9401
+ chunks,
9402
+ asyncEndEvent
9403
+ );
9404
+ metrics = config.extractMetrics(
9405
+ chunks,
9406
+ startTime,
9407
+ asyncEndEvent
9408
+ );
9409
+ }
9410
+ if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
9411
+ metrics.time_to_first_token = firstChunkTime - startTime;
9412
+ } else if (metrics.time_to_first_token === void 0 && chunks.length > 0) {
9413
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9414
+ }
9415
+ span.log({
9416
+ output,
9417
+ ...metadata !== void 0 ? { metadata } : {},
9418
+ metrics
9419
+ });
9420
+ } catch (error) {
9421
+ console.error(
9422
+ `Error extracting output for ${channelName}:`,
9423
+ error
9424
+ );
9425
+ } finally {
9426
+ span.end();
9427
+ states.delete(event);
9428
+ }
9429
+ },
9430
+ onError: (error) => {
9431
+ span.log({
9432
+ error: error.message
9433
+ });
9434
+ span.end();
9435
+ states.delete(event);
9436
+ }
9437
+ });
9438
+ return;
9439
+ }
9440
+ if (config.patchResult?.({
9441
+ channelName,
9442
+ endEvent: asyncEndEvent,
9443
+ result: asyncEndEvent.result,
9444
+ span,
9445
+ startTime
9446
+ })) {
9447
+ states.delete(event);
9448
+ return;
9449
+ }
9450
+ try {
9451
+ const output = config.extractOutput(
9452
+ asyncEndEvent.result,
9453
+ asyncEndEvent
9454
+ );
9455
+ const metrics = config.extractMetrics(
9456
+ asyncEndEvent.result,
9457
+ startTime,
9458
+ asyncEndEvent
9459
+ );
9460
+ const metadata = config.extractMetadata?.(
9461
+ asyncEndEvent.result,
9462
+ asyncEndEvent
9463
+ );
9464
+ span.log({
9465
+ output,
9466
+ ...normalizeMetadata(metadata) !== void 0 ? { metadata: normalizeMetadata(metadata) } : {},
9467
+ metrics
9468
+ });
9469
+ } catch (error) {
9470
+ console.error(`Error extracting output for ${channelName}:`, error);
9471
+ } finally {
9472
+ span.end();
9473
+ states.delete(event);
9474
+ }
9475
+ },
9476
+ error: (event) => {
9477
+ logErrorAndEnd(states, event);
9478
+ }
9479
+ };
9480
+ tracingChannel2.subscribe(handlers);
9481
+ return () => {
9482
+ tracingChannel2.unsubscribe(handlers);
9483
+ };
9484
+ }
9485
+ function traceSyncStreamChannel(channel2, config) {
9486
+ const tracingChannel2 = channel2.tracingChannel();
9487
+ const states = /* @__PURE__ */ new WeakMap();
9488
+ const channelName = channel2.channelName;
9489
+ const handlers = {
9490
+ start: (event) => {
9491
+ states.set(
9492
+ event,
9493
+ startSpanForEvent(
9494
+ config,
9495
+ event,
9496
+ channelName
9497
+ )
9498
+ );
9499
+ },
9500
+ end: (event) => {
9501
+ const spanData = states.get(event);
9502
+ if (!spanData) {
9503
+ return;
9504
+ }
9505
+ const { span, startTime } = spanData;
9506
+ const endEvent = event;
9507
+ if (config.patchResult?.({
9508
+ channelName,
9509
+ endEvent,
9510
+ result: endEvent.result,
9511
+ span,
9512
+ startTime
9513
+ })) {
9514
+ return;
9515
+ }
9516
+ const stream = endEvent.result;
9517
+ if (!isSyncStreamLike(stream)) {
9518
+ span.end();
9519
+ states.delete(event);
9520
+ return;
9521
+ }
9522
+ let first = true;
9523
+ stream.on("chunk", () => {
9524
+ if (first) {
9525
+ span.log({
9526
+ metrics: {
9527
+ time_to_first_token: getCurrentUnixTimestamp() - startTime
9528
+ }
9529
+ });
9530
+ first = false;
9531
+ }
9532
+ });
9533
+ stream.on("chatCompletion", (completion) => {
9534
+ try {
9535
+ if (hasChoices(completion)) {
9536
+ span.log({
9537
+ output: completion.choices
9538
+ });
9539
+ }
9540
+ } catch (error) {
9541
+ console.error(
9542
+ `Error extracting chatCompletion for ${channelName}:`,
9543
+ error
9544
+ );
9545
+ }
9546
+ });
9547
+ stream.on("event", (streamEvent) => {
9548
+ if (!config.extractFromEvent) {
9549
+ return;
9550
+ }
9551
+ try {
9552
+ if (first) {
9553
+ span.log({
9554
+ metrics: {
9555
+ time_to_first_token: getCurrentUnixTimestamp() - startTime
9556
+ }
9557
+ });
9558
+ first = false;
9559
+ }
9560
+ const extracted = config.extractFromEvent(streamEvent);
9561
+ if (extracted && Object.keys(extracted).length > 0) {
9562
+ span.log(extracted);
9563
+ }
9564
+ } catch (error) {
9565
+ console.error(`Error extracting event for ${channelName}:`, error);
9566
+ }
9567
+ });
9568
+ stream.on("end", () => {
9569
+ span.end();
9570
+ states.delete(event);
9571
+ });
9572
+ stream.on("error", (error) => {
9573
+ span.log({
9574
+ error: error.message
8976
9575
  });
9576
+ span.end();
9577
+ states.delete(event);
9578
+ });
9579
+ },
9580
+ error: (event) => {
9581
+ logErrorAndEnd(states, event);
9582
+ }
9583
+ };
9584
+ tracingChannel2.subscribe(handlers);
9585
+ return () => {
9586
+ tracingChannel2.unsubscribe(handlers);
9587
+ };
9588
+ }
9589
+ function unsubscribeAll(unsubscribers) {
9590
+ for (const unsubscribe of unsubscribers) {
9591
+ unsubscribe();
9592
+ }
9593
+ return [];
9594
+ }
9595
+
9596
+ // src/wrappers/attachment-utils.ts
9597
+ function getExtensionFromMediaType(mediaType) {
9598
+ const extensionMap = {
9599
+ "image/png": "png",
9600
+ "image/jpeg": "jpg",
9601
+ "image/gif": "gif",
9602
+ "image/webp": "webp",
9603
+ "image/svg+xml": "svg",
9604
+ "audio/mpeg": "mp3",
9605
+ "audio/wav": "wav",
9606
+ "audio/ogg": "ogg",
9607
+ "video/mp4": "mp4",
9608
+ "video/webm": "webm",
9609
+ "application/pdf": "pdf",
9610
+ "application/json": "json",
9611
+ "text/plain": "txt",
9612
+ "text/html": "html",
9613
+ "text/csv": "csv"
9614
+ };
9615
+ return extensionMap[mediaType] || "bin";
9616
+ }
9617
+ function convertDataToBlob(data, mediaType) {
9618
+ try {
9619
+ if (typeof data === "string") {
9620
+ if (data.startsWith("data:")) {
9621
+ const base64Match = data.match(/^data:[^;]+;base64,(.+)$/);
9622
+ if (base64Match) {
9623
+ const base64 = base64Match[1];
9624
+ const binaryString = atob(base64);
9625
+ const bytes = new Uint8Array(binaryString.length);
9626
+ for (let i = 0; i < binaryString.length; i++) {
9627
+ bytes[i] = binaryString.charCodeAt(i);
9628
+ }
9629
+ return new Blob([bytes], { type: mediaType });
9630
+ }
9631
+ } else if (data.startsWith("http://") || data.startsWith("https://")) {
9632
+ return null;
9633
+ } else {
9634
+ const binaryString = atob(data);
9635
+ const bytes = new Uint8Array(binaryString.length);
9636
+ for (let i = 0; i < binaryString.length; i++) {
9637
+ bytes[i] = binaryString.charCodeAt(i);
9638
+ }
9639
+ return new Blob([bytes], { type: mediaType });
9640
+ }
9641
+ } else if (data instanceof Uint8Array) {
9642
+ return new Blob([data], { type: mediaType });
9643
+ } else if (data instanceof ArrayBuffer) {
9644
+ return new Blob([data], { type: mediaType });
9645
+ } else if (typeof Buffer !== "undefined" && data instanceof Buffer) {
9646
+ return new Blob([data], { type: mediaType });
9647
+ }
9648
+ } catch {
9649
+ return null;
9650
+ }
9651
+ return null;
9652
+ }
9653
+ function processInputAttachments(input) {
9654
+ if (!input) {
9655
+ return input;
9656
+ }
9657
+ let attachmentIndex = 0;
9658
+ const inferMediaTypeFromDataUrl = (value, fallback2) => {
9659
+ const mediaTypeMatch = value.match(/^data:([^;]+);/);
9660
+ return mediaTypeMatch?.[1] || fallback2;
9661
+ };
9662
+ const toAttachment = (value, mediaType, filename) => {
9663
+ const blob = convertDataToBlob(value, mediaType);
9664
+ if (!blob) {
9665
+ return null;
9666
+ }
9667
+ return new Attachment({
9668
+ data: blob,
9669
+ filename,
9670
+ contentType: mediaType
9671
+ });
9672
+ };
9673
+ const processNode = (node) => {
9674
+ if (Array.isArray(node)) {
9675
+ return node.map(processNode);
9676
+ }
9677
+ if (!node || typeof node !== "object") {
9678
+ return node;
9679
+ }
9680
+ if (node.type === "image_url" && node.image_url && typeof node.image_url === "object" && typeof node.image_url.url === "string" && node.image_url.url.startsWith("data:")) {
9681
+ const mediaType = inferMediaTypeFromDataUrl(
9682
+ node.image_url.url,
9683
+ "image/png"
9684
+ );
9685
+ const filename = `image.${getExtensionFromMediaType(mediaType)}`;
9686
+ const attachment = toAttachment(node.image_url.url, mediaType, filename);
9687
+ if (attachment) {
8977
9688
  return {
8978
- ...part,
8979
- data: attachment
9689
+ ...node,
9690
+ image_url: {
9691
+ ...node.image_url,
9692
+ url: attachment
9693
+ }
8980
9694
  };
8981
9695
  }
8982
9696
  }
8983
- return part;
8984
- };
8985
- const processMessage = (message) => {
8986
- if (!message || typeof message !== "object") {
8987
- return message;
9697
+ if (node.type === "file" && node.file && typeof node.file === "object" && typeof node.file.file_data === "string" && node.file.file_data.startsWith("data:")) {
9698
+ const mediaType = inferMediaTypeFromDataUrl(
9699
+ node.file.file_data,
9700
+ "application/octet-stream"
9701
+ );
9702
+ const filename = typeof node.file.filename === "string" && node.file.filename ? node.file.filename : `document.${getExtensionFromMediaType(mediaType)}`;
9703
+ const attachment = toAttachment(node.file.file_data, mediaType, filename);
9704
+ if (attachment) {
9705
+ return {
9706
+ ...node,
9707
+ file: {
9708
+ ...node.file,
9709
+ file_data: attachment
9710
+ }
9711
+ };
9712
+ }
8988
9713
  }
8989
- if (Array.isArray(message.content)) {
8990
- return {
8991
- ...message,
8992
- content: message.content.map(processContentPart)
8993
- };
9714
+ if (node.type === "image" && node.image) {
9715
+ let mediaType = "image/png";
9716
+ if (typeof node.image === "string" && node.image.startsWith("data:")) {
9717
+ mediaType = inferMediaTypeFromDataUrl(node.image, mediaType);
9718
+ } else if (node.mediaType) {
9719
+ mediaType = node.mediaType;
9720
+ }
9721
+ const filename = `input_image_${attachmentIndex}.${getExtensionFromMediaType(mediaType)}`;
9722
+ const attachment = toAttachment(node.image, mediaType, filename);
9723
+ if (attachment) {
9724
+ attachmentIndex++;
9725
+ return {
9726
+ ...node,
9727
+ image: attachment
9728
+ };
9729
+ }
9730
+ }
9731
+ if (node.type === "file" && node.data) {
9732
+ const mediaType = node.mediaType || "application/octet-stream";
9733
+ const filename = node.filename || `input_file_${attachmentIndex}.${getExtensionFromMediaType(mediaType)}`;
9734
+ const attachment = toAttachment(node.data, mediaType, filename);
9735
+ if (attachment) {
9736
+ attachmentIndex++;
9737
+ return {
9738
+ ...node,
9739
+ data: attachment
9740
+ };
9741
+ }
8994
9742
  }
8995
- return message;
9743
+ const processed = {};
9744
+ for (const [key, value] of Object.entries(node)) {
9745
+ processed[key] = processNode(value);
9746
+ }
9747
+ return processed;
8996
9748
  };
8997
9749
  if (Array.isArray(input)) {
8998
- return input.map(processMessage);
8999
- } else if (typeof input === "object" && input.content) {
9000
- return processMessage(input);
9750
+ return input.map(processNode);
9001
9751
  }
9002
- return input;
9752
+ return processNode(input);
9753
+ }
9754
+
9755
+ // src/instrumentation/core/channel-definitions.ts
9756
+ function channel(spec) {
9757
+ return spec;
9758
+ }
9759
+ function defineChannels(pkg, channels) {
9760
+ return Object.fromEntries(
9761
+ Object.entries(channels).map(([key, spec]) => {
9762
+ const fullChannelName = `orchestrion:${pkg}:${spec.channelName}`;
9763
+ if (spec.kind === "async") {
9764
+ const asyncSpec = spec;
9765
+ const tracingChannel3 = () => isomorph_default.newTracingChannel(
9766
+ fullChannelName
9767
+ );
9768
+ return [
9769
+ key,
9770
+ {
9771
+ ...asyncSpec,
9772
+ tracingChannel: tracingChannel3,
9773
+ tracePromise: (fn, context) => tracingChannel3().tracePromise(
9774
+ fn,
9775
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
9776
+ context
9777
+ )
9778
+ }
9779
+ ];
9780
+ }
9781
+ const syncSpec = spec;
9782
+ const tracingChannel2 = () => isomorph_default.newTracingChannel(
9783
+ fullChannelName
9784
+ );
9785
+ return [
9786
+ key,
9787
+ {
9788
+ ...syncSpec,
9789
+ tracingChannel: tracingChannel2,
9790
+ traceSync: (fn, context) => tracingChannel2().traceSync(
9791
+ fn,
9792
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
9793
+ context
9794
+ )
9795
+ }
9796
+ ];
9797
+ })
9798
+ );
9799
+ }
9800
+
9801
+ // src/instrumentation/plugins/openai-channels.ts
9802
+ var openAIChannels = defineChannels("openai", {
9803
+ chatCompletionsCreate: channel({
9804
+ channelName: "chat.completions.create",
9805
+ kind: "async"
9806
+ }),
9807
+ embeddingsCreate: channel({
9808
+ channelName: "embeddings.create",
9809
+ kind: "async"
9810
+ }),
9811
+ betaChatCompletionsParse: channel({
9812
+ channelName: "beta.chat.completions.parse",
9813
+ kind: "async"
9814
+ }),
9815
+ betaChatCompletionsStream: channel({
9816
+ channelName: "beta.chat.completions.stream",
9817
+ kind: "sync-stream"
9818
+ }),
9819
+ moderationsCreate: channel({
9820
+ channelName: "moderations.create",
9821
+ kind: "async"
9822
+ }),
9823
+ responsesCreate: channel({
9824
+ channelName: "responses.create",
9825
+ kind: "async"
9826
+ }),
9827
+ responsesStream: channel({
9828
+ channelName: "responses.stream",
9829
+ kind: "sync-stream"
9830
+ }),
9831
+ responsesParse: channel({
9832
+ channelName: "responses.parse",
9833
+ kind: "async"
9834
+ })
9835
+ });
9836
+
9837
+ // src/openai-utils.ts
9838
+ var BRAINTRUST_CACHED_STREAM_METRIC = "__braintrust_cached_metric";
9839
+ var LEGACY_CACHED_HEADER = "x-cached";
9840
+ var X_CACHED_HEADER = "x-bt-cached";
9841
+ var TOKEN_NAME_MAP = {
9842
+ input_tokens: "prompt_tokens",
9843
+ output_tokens: "completion_tokens",
9844
+ total_tokens: "tokens"
9845
+ };
9846
+ var TOKEN_PREFIX_MAP = {
9847
+ input: "prompt",
9848
+ output: "completion"
9849
+ };
9850
+ function parseMetricsFromUsage(usage) {
9851
+ if (!usage) {
9852
+ return {};
9853
+ }
9854
+ const metrics = {};
9855
+ for (const [oaiName, value] of Object.entries(usage)) {
9856
+ if (typeof value === "number") {
9857
+ const metricName = TOKEN_NAME_MAP[oaiName] || oaiName;
9858
+ metrics[metricName] = value;
9859
+ continue;
9860
+ }
9861
+ if (!oaiName.endsWith("_tokens_details") || !isObject(value)) {
9862
+ continue;
9863
+ }
9864
+ const rawPrefix = oaiName.slice(0, -"_tokens_details".length);
9865
+ const prefix = TOKEN_PREFIX_MAP[rawPrefix] || rawPrefix;
9866
+ for (const [key, nestedValue] of Object.entries(value)) {
9867
+ if (typeof nestedValue !== "number") {
9868
+ continue;
9869
+ }
9870
+ metrics[`${prefix}_${key}`] = nestedValue;
9871
+ }
9872
+ }
9873
+ return metrics;
9874
+ }
9875
+ function parseCachedHeader(value) {
9876
+ if (!value) {
9877
+ return void 0;
9878
+ }
9879
+ return ["true", "hit"].includes(value.toLowerCase()) ? 1 : 0;
9880
+ }
9881
+ function getCachedMetricFromHeaders(headers) {
9882
+ if (!headers || typeof headers.get !== "function") {
9883
+ return void 0;
9884
+ }
9885
+ const cachedHeader = headers.get(X_CACHED_HEADER);
9886
+ if (cachedHeader) {
9887
+ return parseCachedHeader(cachedHeader);
9888
+ }
9889
+ return parseCachedHeader(headers.get(LEGACY_CACHED_HEADER));
9003
9890
  }
9004
9891
 
9005
9892
  // src/instrumentation/plugins/openai-plugin.ts
@@ -9008,13 +9895,63 @@ var OpenAIPlugin = class extends BasePlugin {
9008
9895
  super();
9009
9896
  }
9010
9897
  onEnable() {
9011
- this.subscribeToStreamingChannel(
9012
- "orchestrion:openai:chat.completions.create",
9013
- {
9898
+ this.unsubscribers.push(
9899
+ traceStreamingChannel(openAIChannels.chatCompletionsCreate, {
9900
+ name: "Chat Completion",
9901
+ type: "llm" /* LLM */,
9902
+ extractInput: ([params]) => {
9903
+ const { messages, ...metadata } = params;
9904
+ return {
9905
+ input: processInputAttachments(messages),
9906
+ metadata: { ...metadata, provider: "openai" }
9907
+ };
9908
+ },
9909
+ extractOutput: (result) => {
9910
+ return result?.choices;
9911
+ },
9912
+ extractMetrics: (result, startTime, endEvent) => {
9913
+ const metrics = withCachedMetric(
9914
+ parseMetricsFromUsage(result?.usage),
9915
+ result,
9916
+ endEvent
9917
+ );
9918
+ if (startTime) {
9919
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9920
+ }
9921
+ return metrics;
9922
+ },
9923
+ aggregateChunks: aggregateChatCompletionChunks
9924
+ })
9925
+ );
9926
+ this.unsubscribers.push(
9927
+ traceAsyncChannel(openAIChannels.embeddingsCreate, {
9928
+ name: "Embedding",
9929
+ type: "llm" /* LLM */,
9930
+ extractInput: ([params]) => {
9931
+ const { input, ...metadata } = params;
9932
+ return {
9933
+ input,
9934
+ metadata: { ...metadata, provider: "openai" }
9935
+ };
9936
+ },
9937
+ extractOutput: (result) => {
9938
+ const embedding = result?.data?.[0]?.embedding;
9939
+ return Array.isArray(embedding) ? { embedding_length: embedding.length } : void 0;
9940
+ },
9941
+ extractMetrics: (result, _startTime, endEvent) => {
9942
+ return withCachedMetric(
9943
+ parseMetricsFromUsage(result?.usage),
9944
+ result,
9945
+ endEvent
9946
+ );
9947
+ }
9948
+ })
9949
+ );
9950
+ this.unsubscribers.push(
9951
+ traceStreamingChannel(openAIChannels.betaChatCompletionsParse, {
9014
9952
  name: "Chat Completion",
9015
9953
  type: "llm" /* LLM */,
9016
- extractInput: (args) => {
9017
- const params = args[0] || {};
9954
+ extractInput: ([params]) => {
9018
9955
  const { messages, ...metadata } = params;
9019
9956
  return {
9020
9957
  input: processInputAttachments(messages),
@@ -9024,208 +9961,196 @@ var OpenAIPlugin = class extends BasePlugin {
9024
9961
  extractOutput: (result) => {
9025
9962
  return result?.choices;
9026
9963
  },
9027
- extractMetrics: (result, startTime) => {
9028
- const metrics = parseMetricsFromUsage(result?.usage);
9964
+ extractMetrics: (result, startTime, endEvent) => {
9965
+ const metrics = withCachedMetric(
9966
+ parseMetricsFromUsage(result?.usage),
9967
+ result,
9968
+ endEvent
9969
+ );
9029
9970
  if (startTime) {
9030
9971
  metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9031
9972
  }
9032
9973
  return metrics;
9033
9974
  },
9034
9975
  aggregateChunks: aggregateChatCompletionChunks
9035
- }
9976
+ })
9036
9977
  );
9037
- this.subscribeToChannel("orchestrion:openai:embeddings.create", {
9038
- name: "Embedding",
9039
- type: "llm" /* LLM */,
9040
- extractInput: (args) => {
9041
- const params = args[0] || {};
9042
- const { input, ...metadata } = params;
9043
- return {
9044
- input,
9045
- metadata: { ...metadata, provider: "openai" }
9046
- };
9047
- },
9048
- extractOutput: (result) => {
9049
- return result?.data?.map((d) => d.embedding);
9050
- },
9051
- extractMetrics: (result) => {
9052
- return parseMetricsFromUsage(result?.usage);
9053
- }
9054
- });
9055
- this.subscribeToStreamingChannel(
9056
- "orchestrion:openai:beta.chat.completions.parse",
9057
- {
9978
+ this.unsubscribers.push(
9979
+ traceSyncStreamChannel(openAIChannels.betaChatCompletionsStream, {
9058
9980
  name: "Chat Completion",
9059
9981
  type: "llm" /* LLM */,
9060
- extractInput: (args) => {
9061
- const params = args[0] || {};
9982
+ extractInput: ([params]) => {
9062
9983
  const { messages, ...metadata } = params;
9063
9984
  return {
9064
9985
  input: processInputAttachments(messages),
9065
9986
  metadata: { ...metadata, provider: "openai" }
9066
9987
  };
9988
+ }
9989
+ })
9990
+ );
9991
+ this.unsubscribers.push(
9992
+ traceAsyncChannel(openAIChannels.moderationsCreate, {
9993
+ name: "Moderation",
9994
+ type: "llm" /* LLM */,
9995
+ extractInput: ([params]) => {
9996
+ const { input, ...metadata } = params;
9997
+ return {
9998
+ input,
9999
+ metadata: { ...metadata, provider: "openai" }
10000
+ };
9067
10001
  },
9068
10002
  extractOutput: (result) => {
9069
- return result?.choices;
10003
+ return result?.results;
9070
10004
  },
9071
- extractMetrics: (result, startTime) => {
9072
- const metrics = parseMetricsFromUsage(result?.usage);
10005
+ extractMetrics: (result, _startTime, endEvent) => {
10006
+ return withCachedMetric(
10007
+ parseMetricsFromUsage(result?.usage),
10008
+ result,
10009
+ endEvent
10010
+ );
10011
+ }
10012
+ })
10013
+ );
10014
+ this.unsubscribers.push(
10015
+ traceStreamingChannel(openAIChannels.responsesCreate, {
10016
+ name: "openai.responses.create",
10017
+ type: "llm" /* LLM */,
10018
+ extractInput: ([params]) => {
10019
+ const { input, ...metadata } = params;
10020
+ return {
10021
+ input: processInputAttachments(input),
10022
+ metadata: { ...metadata, provider: "openai" }
10023
+ };
10024
+ },
10025
+ extractOutput: (result) => {
10026
+ return processImagesInOutput(result?.output);
10027
+ },
10028
+ extractMetadata: (result) => {
10029
+ if (!result) {
10030
+ return void 0;
10031
+ }
10032
+ const { output: _output, usage: _usage, ...metadata } = result;
10033
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
10034
+ },
10035
+ extractMetrics: (result, startTime, endEvent) => {
10036
+ const metrics = withCachedMetric(
10037
+ parseMetricsFromUsage(result?.usage),
10038
+ result,
10039
+ endEvent
10040
+ );
9073
10041
  if (startTime) {
9074
10042
  metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9075
10043
  }
9076
10044
  return metrics;
9077
10045
  },
9078
- aggregateChunks: aggregateChatCompletionChunks
9079
- }
10046
+ aggregateChunks: aggregateResponseStreamEvents
10047
+ })
9080
10048
  );
9081
- this.subscribeToSyncStreamChannel(
9082
- "orchestrion:openai:beta.chat.completions.stream",
9083
- {
9084
- name: "Chat Completion",
10049
+ this.unsubscribers.push(
10050
+ traceSyncStreamChannel(openAIChannels.responsesStream, {
10051
+ name: "openai.responses.create",
9085
10052
  type: "llm" /* LLM */,
9086
- extractInput: (args) => {
9087
- const params = args[0] || {};
9088
- const { messages, ...metadata } = params;
10053
+ extractInput: ([params]) => {
10054
+ const { input, ...metadata } = params;
9089
10055
  return {
9090
- input: processInputAttachments(messages),
10056
+ input: processInputAttachments(input),
9091
10057
  metadata: { ...metadata, provider: "openai" }
9092
10058
  };
9093
- }
9094
- }
9095
- );
9096
- this.subscribeToChannel("orchestrion:openai:moderations.create", {
9097
- name: "Moderation",
9098
- type: "llm" /* LLM */,
9099
- extractInput: (args) => {
9100
- const params = args[0] || {};
9101
- const { input, ...metadata } = params;
9102
- return {
9103
- input,
9104
- metadata: { ...metadata, provider: "openai" }
9105
- };
9106
- },
9107
- extractOutput: (result) => {
9108
- return result?.results;
9109
- },
9110
- extractMetrics: () => {
9111
- return {};
9112
- }
9113
- });
9114
- this.subscribeToStreamingChannel("orchestrion:openai:responses.create", {
9115
- name: "openai.responses.create",
9116
- type: "llm" /* LLM */,
9117
- extractInput: (args) => {
9118
- const params = args[0] || {};
9119
- const { input, ...metadata } = params;
9120
- return {
9121
- input: processInputAttachments(input),
9122
- metadata: { ...metadata, provider: "openai" }
9123
- };
9124
- },
9125
- extractOutput: (result) => {
9126
- return processImagesInOutput(result?.output);
9127
- },
9128
- extractMetrics: (result, startTime) => {
9129
- const metrics = parseMetricsFromUsage(result?.usage);
9130
- if (startTime) {
9131
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9132
- }
9133
- return metrics;
9134
- }
9135
- });
9136
- this.subscribeToSyncStreamChannel("orchestrion:openai:responses.stream", {
9137
- name: "openai.responses.stream",
9138
- type: "llm" /* LLM */,
9139
- extractInput: (args) => {
9140
- const params = args[0] || {};
9141
- const { input, ...metadata } = params;
9142
- return {
9143
- input: processInputAttachments(input),
9144
- metadata: { ...metadata, provider: "openai" }
9145
- };
9146
- },
9147
- extractFromEvent: (event) => {
9148
- if (!event || !event.type || !event.response) {
9149
- return {};
9150
- }
9151
- const response = event.response;
9152
- if (event.type === "response.completed") {
10059
+ },
10060
+ extractFromEvent: (event) => {
10061
+ if (event.type !== "response.completed" || !event.response) {
10062
+ return {};
10063
+ }
10064
+ const response = event.response;
9153
10065
  const data = {};
9154
- if (response?.output !== void 0) {
10066
+ if (response.output !== void 0) {
9155
10067
  data.output = processImagesInOutput(response.output);
9156
10068
  }
9157
- if (response) {
9158
- const { usage: _usage, output: _output, ...metadata } = response;
9159
- if (Object.keys(metadata).length > 0) {
9160
- data.metadata = metadata;
9161
- }
10069
+ const { usage: _usage, output: _output, ...metadata } = response;
10070
+ if (Object.keys(metadata).length > 0) {
10071
+ data.metadata = metadata;
9162
10072
  }
9163
- data.metrics = parseMetricsFromUsage(response?.usage);
10073
+ data.metrics = parseMetricsFromUsage(response.usage);
9164
10074
  return data;
9165
10075
  }
9166
- return {};
9167
- }
9168
- });
9169
- this.subscribeToStreamingChannel("orchestrion:openai:responses.parse", {
9170
- name: "openai.responses.parse",
9171
- type: "llm" /* LLM */,
9172
- extractInput: (args) => {
9173
- const params = args[0] || {};
9174
- const { input, ...metadata } = params;
9175
- return {
9176
- input: processInputAttachments(input),
9177
- metadata: { ...metadata, provider: "openai" }
9178
- };
9179
- },
9180
- extractOutput: (result) => {
9181
- return processImagesInOutput(result?.output);
9182
- },
9183
- extractMetrics: (result, startTime) => {
9184
- const metrics = parseMetricsFromUsage(result?.usage);
9185
- if (startTime) {
9186
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9187
- }
9188
- return metrics;
9189
- }
9190
- });
10076
+ })
10077
+ );
10078
+ this.unsubscribers.push(
10079
+ traceStreamingChannel(openAIChannels.responsesParse, {
10080
+ name: "openai.responses.parse",
10081
+ type: "llm" /* LLM */,
10082
+ extractInput: ([params]) => {
10083
+ const { input, ...metadata } = params;
10084
+ return {
10085
+ input: processInputAttachments(input),
10086
+ metadata: { ...metadata, provider: "openai" }
10087
+ };
10088
+ },
10089
+ extractOutput: (result) => {
10090
+ return processImagesInOutput(result?.output);
10091
+ },
10092
+ extractMetadata: (result) => {
10093
+ if (!result) {
10094
+ return void 0;
10095
+ }
10096
+ const { output: _output, usage: _usage, ...metadata } = result;
10097
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
10098
+ },
10099
+ extractMetrics: (result, startTime, endEvent) => {
10100
+ const metrics = withCachedMetric(
10101
+ parseMetricsFromUsage(result?.usage),
10102
+ result,
10103
+ endEvent
10104
+ );
10105
+ if (startTime) {
10106
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
10107
+ }
10108
+ return metrics;
10109
+ },
10110
+ aggregateChunks: aggregateResponseStreamEvents
10111
+ })
10112
+ );
9191
10113
  }
9192
10114
  onDisable() {
10115
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
9193
10116
  }
9194
10117
  };
9195
- var TOKEN_NAME_MAP = {
9196
- input_tokens: "prompt_tokens",
9197
- output_tokens: "completion_tokens",
9198
- total_tokens: "tokens"
9199
- };
9200
- var TOKEN_PREFIX_MAP = {
9201
- input: "prompt",
9202
- output: "completion"
9203
- };
9204
- function parseMetricsFromUsage(usage) {
9205
- if (!usage) {
9206
- return {};
10118
+ function getCachedMetricFromEndEvent(endEvent) {
10119
+ if (!isObject(endEvent)) {
10120
+ return void 0;
9207
10121
  }
9208
- const metrics = {};
9209
- for (const [oai_name, value] of Object.entries(usage)) {
9210
- if (typeof value === "number") {
9211
- const metricName = TOKEN_NAME_MAP[oai_name] || oai_name;
9212
- metrics[metricName] = value;
9213
- } else if (oai_name.endsWith("_tokens_details")) {
9214
- if (!isObject(value)) {
9215
- continue;
9216
- }
9217
- const rawPrefix = oai_name.slice(0, -"_tokens_details".length);
9218
- const prefix = TOKEN_PREFIX_MAP[rawPrefix] || rawPrefix;
9219
- for (const [key, n] of Object.entries(value)) {
9220
- if (typeof n !== "number") {
9221
- continue;
9222
- }
9223
- const metricName = `${prefix}_${key}`;
9224
- metrics[metricName] = n;
9225
- }
9226
- }
10122
+ const response = endEvent.response;
10123
+ if (!isObject(response)) {
10124
+ return void 0;
9227
10125
  }
9228
- return metrics;
10126
+ const headers = response.headers;
10127
+ if (!headers || typeof headers.get !== "function") {
10128
+ return void 0;
10129
+ }
10130
+ return getCachedMetricFromHeaders(headers);
10131
+ }
10132
+ function withCachedMetric(metrics, result, endEvent) {
10133
+ if (metrics.cached !== void 0) {
10134
+ return metrics;
10135
+ }
10136
+ const cachedFromEvent = getCachedMetricFromEndEvent(endEvent);
10137
+ if (cachedFromEvent !== void 0) {
10138
+ return {
10139
+ ...metrics,
10140
+ cached: cachedFromEvent
10141
+ };
10142
+ }
10143
+ if (!isObject(result)) {
10144
+ return metrics;
10145
+ }
10146
+ const cached = result[BRAINTRUST_CACHED_STREAM_METRIC];
10147
+ if (typeof cached !== "number") {
10148
+ return metrics;
10149
+ }
10150
+ return {
10151
+ ...metrics,
10152
+ cached
10153
+ };
9229
10154
  }
9230
10155
  function processImagesInOutput(output) {
9231
10156
  if (Array.isArray(output)) {
@@ -9256,7 +10181,7 @@ function processImagesInOutput(output) {
9256
10181
  }
9257
10182
  return output;
9258
10183
  }
9259
- function aggregateChatCompletionChunks(chunks) {
10184
+ function aggregateChatCompletionChunks(chunks, streamResult, endEvent) {
9260
10185
  let role = void 0;
9261
10186
  let content = void 0;
9262
10187
  let tool_calls = void 0;
@@ -9298,6 +10223,7 @@ function aggregateChatCompletionChunks(chunks) {
9298
10223
  }
9299
10224
  }
9300
10225
  }
10226
+ metrics = withCachedMetric(metrics, streamResult, endEvent);
9301
10227
  return {
9302
10228
  metrics,
9303
10229
  output: [
@@ -9314,9 +10240,33 @@ function aggregateChatCompletionChunks(chunks) {
9314
10240
  ]
9315
10241
  };
9316
10242
  }
9317
-
9318
- // src/instrumentation/plugins/anthropic-plugin.ts
9319
- import { tracingChannel as tracingChannel2 } from "dc-browser";
10243
+ function aggregateResponseStreamEvents(chunks, _streamResult, endEvent) {
10244
+ let output = void 0;
10245
+ let metrics = {};
10246
+ let metadata = void 0;
10247
+ for (const chunk of chunks) {
10248
+ if (!chunk || !chunk.type || !chunk.response) {
10249
+ continue;
10250
+ }
10251
+ if (chunk.type !== "response.completed") {
10252
+ continue;
10253
+ }
10254
+ const response = chunk.response;
10255
+ if (response?.output !== void 0) {
10256
+ output = processImagesInOutput(response.output);
10257
+ }
10258
+ const { usage: _usage, output: _output, ...rest } = response || {};
10259
+ if (Object.keys(rest).length > 0) {
10260
+ metadata = rest;
10261
+ }
10262
+ metrics = parseMetricsFromUsage(response?.usage);
10263
+ }
10264
+ return {
10265
+ output,
10266
+ metrics: withCachedMetric(metrics, void 0, endEvent),
10267
+ ...metadata !== void 0 ? { metadata } : {}
10268
+ };
10269
+ }
9320
10270
 
9321
10271
  // src/wrappers/anthropic-tokens-util.ts
9322
10272
  function finalizeAnthropicTokens(metrics) {
@@ -9338,215 +10288,75 @@ function extractAnthropicCacheTokens(cacheReadTokens = 0, cacheCreationTokens =
9338
10288
  return cacheTokens;
9339
10289
  }
9340
10290
 
10291
+ // src/instrumentation/plugins/anthropic-channels.ts
10292
+ var anthropicChannels = defineChannels("@anthropic-ai/sdk", {
10293
+ messagesCreate: channel({
10294
+ channelName: "messages.create",
10295
+ kind: "async"
10296
+ }),
10297
+ betaMessagesCreate: channel({
10298
+ channelName: "beta.messages.create",
10299
+ kind: "async"
10300
+ })
10301
+ });
10302
+
9341
10303
  // src/instrumentation/plugins/anthropic-plugin.ts
9342
10304
  var AnthropicPlugin = class extends BasePlugin {
9343
- unsubscribers = [];
9344
10305
  onEnable() {
9345
10306
  this.subscribeToAnthropicChannels();
9346
10307
  }
9347
10308
  onDisable() {
9348
- for (const unsubscribe of this.unsubscribers) {
9349
- unsubscribe();
9350
- }
9351
- this.unsubscribers = [];
10309
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
9352
10310
  }
9353
10311
  subscribeToAnthropicChannels() {
9354
- this.subscribeToStreamingChannel("orchestrion:anthropic:messages.create", {
10312
+ const anthropicConfig = {
9355
10313
  name: "anthropic.messages.create",
9356
10314
  type: "llm" /* LLM */,
9357
10315
  extractInput: (args) => {
9358
- const params = args[0] || {};
9359
- const input = coalesceInput(params.messages || [], params.system);
9360
- const metadata = filterFrom(params, ["messages", "system"]);
9361
- return {
9362
- input: processAttachmentsInInput(input),
9363
- metadata: { ...metadata, provider: "anthropic" }
9364
- };
9365
- },
9366
- extractOutput: (result) => {
9367
- return result ? { role: result.role, content: result.content } : null;
9368
- },
9369
- extractMetrics: (result, startTime) => {
9370
- const metrics = parseMetricsFromUsage2(result?.usage);
9371
- if (startTime) {
9372
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9373
- }
9374
- const finalized = finalizeAnthropicTokens(metrics);
9375
- return Object.fromEntries(
9376
- Object.entries(finalized).filter(([, v]) => v !== void 0)
9377
- );
9378
- },
9379
- extractMetadata: (result) => {
9380
- const metadata = {};
9381
- const metas = ["stop_reason", "stop_sequence"];
9382
- for (const m of metas) {
9383
- if (result?.[m] !== void 0) {
9384
- metadata[m] = result[m];
9385
- }
9386
- }
9387
- return metadata;
9388
- },
9389
- aggregateChunks: aggregateAnthropicStreamChunks,
9390
- isStreaming: (args) => {
9391
- return args[0]?.stream === true;
9392
- }
9393
- });
9394
- this.subscribeToStreamingChannel(
9395
- "orchestrion:anthropic:beta.messages.create",
9396
- {
9397
- name: "anthropic.beta.messages.create",
9398
- type: "llm" /* LLM */,
9399
- extractInput: (args) => {
9400
- const params = args[0] || {};
9401
- const input = coalesceInput(params.messages || [], params.system);
9402
- const metadata = filterFrom(params, ["messages", "system"]);
9403
- return {
9404
- input: processAttachmentsInInput(input),
9405
- metadata: { ...metadata, provider: "anthropic" }
9406
- };
9407
- },
9408
- extractOutput: (result) => {
9409
- return result ? { role: result.role, content: result.content } : null;
9410
- },
9411
- extractMetrics: (result, startTime) => {
9412
- const metrics = parseMetricsFromUsage2(result?.usage);
9413
- if (startTime) {
9414
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9415
- }
9416
- const finalized = finalizeAnthropicTokens(metrics);
9417
- return Object.fromEntries(
9418
- Object.entries(finalized).filter(([, v]) => v !== void 0)
9419
- );
9420
- },
9421
- extractMetadata: (result) => {
9422
- const metadata = {};
9423
- const metas = ["stop_reason", "stop_sequence"];
9424
- for (const m of metas) {
9425
- if (result?.[m] !== void 0) {
9426
- metadata[m] = result[m];
9427
- }
9428
- }
9429
- return metadata;
9430
- },
9431
- aggregateChunks: aggregateAnthropicStreamChunks,
9432
- isStreaming: (args) => {
9433
- return args[0]?.stream === true;
9434
- }
9435
- }
9436
- );
9437
- }
9438
- /**
9439
- * Subscribe to a channel for async methods that may return streams.
9440
- * Handles both streaming and non-streaming responses based on the stream parameter.
9441
- */
9442
- subscribeToStreamingChannel(channelName, config) {
9443
- const channel = tracingChannel2(channelName);
9444
- const spans = /* @__PURE__ */ new WeakMap();
9445
- const handlers = {
9446
- start: (event) => {
9447
- const span = startSpan({
9448
- name: config.name,
9449
- spanAttributes: {
9450
- type: config.type
9451
- }
9452
- });
9453
- const startTime = getCurrentUnixTimestamp();
9454
- spans.set(event, { span, startTime });
9455
- try {
9456
- const { input, metadata } = config.extractInput(event.arguments);
9457
- span.log({
9458
- input,
9459
- metadata
9460
- });
9461
- } catch (error) {
9462
- console.error(`Error extracting input for ${channelName}:`, error);
9463
- }
10316
+ const params = args[0] || {};
10317
+ const input = coalesceInput(params.messages || [], params.system);
10318
+ const metadata = filterFrom(params, ["messages", "system"]);
10319
+ return {
10320
+ input: processAttachmentsInInput(input),
10321
+ metadata: { ...metadata, provider: "anthropic" }
10322
+ };
9464
10323
  },
9465
- asyncEnd: (event) => {
9466
- const spanData = spans.get(event);
9467
- if (!spanData) {
9468
- return;
10324
+ extractOutput: (message) => {
10325
+ return message ? { role: message.role, content: message.content } : null;
10326
+ },
10327
+ extractMetrics: (message, startTime) => {
10328
+ const metrics = parseMetricsFromUsage2(message?.usage);
10329
+ if (startTime) {
10330
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9469
10331
  }
9470
- const { span, startTime } = spanData;
9471
- const isStreaming = config.isStreaming ? config.isStreaming(event.arguments) : isAsyncIterable(event.result);
9472
- if (isStreaming && isAsyncIterable(event.result)) {
9473
- patchStreamIfNeeded(event.result, {
9474
- onComplete: (chunks) => {
9475
- try {
9476
- let output;
9477
- let metrics;
9478
- let metadata = {};
9479
- if (config.aggregateChunks) {
9480
- const aggregated = config.aggregateChunks(chunks);
9481
- output = aggregated.output;
9482
- metrics = aggregated.metrics;
9483
- metadata = aggregated.metadata || {};
9484
- } else {
9485
- output = config.extractOutput(chunks);
9486
- metrics = config.extractMetrics(chunks, startTime);
9487
- if (config.extractMetadata) {
9488
- metadata = config.extractMetadata(chunks);
9489
- }
9490
- }
9491
- if (!metrics.time_to_first_token && chunks.length > 0) {
9492
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9493
- }
9494
- span.log({
9495
- output,
9496
- metrics,
9497
- metadata
9498
- });
9499
- } catch (error) {
9500
- console.error(
9501
- `Error extracting output for ${channelName}:`,
9502
- error
9503
- );
9504
- } finally {
9505
- span.end();
9506
- }
9507
- },
9508
- onError: (error) => {
9509
- span.log({
9510
- error: error.message
9511
- });
9512
- span.end();
9513
- }
9514
- });
9515
- } else {
9516
- try {
9517
- const output = config.extractOutput(event.result);
9518
- const metrics = config.extractMetrics(event.result, startTime);
9519
- const metadata = config.extractMetadata ? config.extractMetadata(event.result) : {};
9520
- span.log({
9521
- output,
9522
- metrics,
9523
- metadata
9524
- });
9525
- } catch (error) {
9526
- console.error(`Error extracting output for ${channelName}:`, error);
9527
- } finally {
9528
- span.end();
9529
- spans.delete(event);
10332
+ const finalized = finalizeAnthropicTokens(metrics);
10333
+ return Object.fromEntries(
10334
+ Object.entries(finalized).filter(
10335
+ (entry) => entry[1] !== void 0
10336
+ )
10337
+ );
10338
+ },
10339
+ extractMetadata: (message) => {
10340
+ const metadata = {};
10341
+ const metas = ["stop_reason", "stop_sequence"];
10342
+ for (const m of metas) {
10343
+ if (message?.[m] !== void 0) {
10344
+ metadata[m] = message[m];
9530
10345
  }
9531
10346
  }
10347
+ return metadata;
9532
10348
  },
9533
- error: (event) => {
9534
- const spanData = spans.get(event);
9535
- if (!spanData) {
9536
- return;
9537
- }
9538
- const { span } = spanData;
9539
- span.log({
9540
- error: event.error.message
9541
- });
9542
- span.end();
9543
- spans.delete(event);
9544
- }
10349
+ aggregateChunks: (chunks) => aggregateAnthropicStreamChunks(chunks)
9545
10350
  };
9546
- channel.subscribe(handlers);
9547
- this.unsubscribers.push(() => {
9548
- channel.unsubscribe(handlers);
9549
- });
10351
+ this.unsubscribers.push(
10352
+ traceStreamingChannel(anthropicChannels.messagesCreate, anthropicConfig)
10353
+ );
10354
+ this.unsubscribers.push(
10355
+ traceStreamingChannel(anthropicChannels.betaMessagesCreate, {
10356
+ ...anthropicConfig,
10357
+ name: "anthropic.beta.messages.create"
10358
+ })
10359
+ );
9550
10360
  }
9551
10361
  };
9552
10362
  function parseMetricsFromUsage2(usage) {
@@ -9570,29 +10380,29 @@ function aggregateAnthropicStreamChunks(chunks) {
9570
10380
  const deltas = [];
9571
10381
  let metrics = {};
9572
10382
  let metadata = {};
9573
- for (const chunk of chunks) {
9574
- switch (chunk?.type) {
10383
+ for (const event of chunks) {
10384
+ switch (event?.type) {
9575
10385
  case "message_start":
9576
- if (chunk.message?.usage) {
9577
- const initialMetrics = parseMetricsFromUsage2(chunk.message.usage);
10386
+ if (event.message?.usage) {
10387
+ const initialMetrics = parseMetricsFromUsage2(event.message.usage);
9578
10388
  metrics = { ...metrics, ...initialMetrics };
9579
10389
  }
9580
10390
  break;
9581
10391
  case "content_block_delta":
9582
- if (chunk.delta?.type === "text_delta") {
9583
- const text = chunk.delta?.text;
10392
+ if (event.delta?.type === "text_delta") {
10393
+ const text = event.delta.text;
9584
10394
  if (text) {
9585
10395
  deltas.push(text);
9586
10396
  }
9587
10397
  }
9588
10398
  break;
9589
10399
  case "message_delta":
9590
- if (chunk.usage) {
9591
- const finalMetrics = parseMetricsFromUsage2(chunk.usage);
10400
+ if (event.usage) {
10401
+ const finalMetrics = parseMetricsFromUsage2(event.usage);
9592
10402
  metrics = { ...metrics, ...finalMetrics };
9593
10403
  }
9594
- if (chunk.delta) {
9595
- metadata = { ...metadata, ...chunk.delta };
10404
+ if (event.delta) {
10405
+ metadata = { ...metadata, ...event.delta };
9596
10406
  }
9597
10407
  break;
9598
10408
  }
@@ -9600,7 +10410,9 @@ function aggregateAnthropicStreamChunks(chunks) {
9600
10410
  const output = deltas.join("");
9601
10411
  const finalized = finalizeAnthropicTokens(metrics);
9602
10412
  const filteredMetrics = Object.fromEntries(
9603
- Object.entries(finalized).filter(([, v]) => v !== void 0)
10413
+ Object.entries(finalized).filter(
10414
+ (entry) => entry[1] !== void 0
10415
+ )
9604
10416
  );
9605
10417
  return {
9606
10418
  output,
@@ -9608,6 +10420,9 @@ function aggregateAnthropicStreamChunks(chunks) {
9608
10420
  metadata
9609
10421
  };
9610
10422
  }
10423
+ function isAnthropicBase64ContentBlock(input) {
10424
+ return (input.type === "image" || input.type === "document") && isObject(input.source) && input.source.type === "base64";
10425
+ }
9611
10426
  function convertBase64ToAttachment(source, contentType) {
9612
10427
  const mediaType = typeof source.media_type === "string" ? source.media_type : "image/png";
9613
10428
  const base64Data = source.data;
@@ -9631,14 +10446,14 @@ function convertBase64ToAttachment(source, contentType) {
9631
10446
  data: attachment
9632
10447
  };
9633
10448
  }
9634
- return source;
10449
+ return { ...source };
9635
10450
  }
9636
10451
  function processAttachmentsInInput(input) {
9637
10452
  if (Array.isArray(input)) {
9638
10453
  return input.map(processAttachmentsInInput);
9639
10454
  }
9640
10455
  if (isObject(input)) {
9641
- if ((input.type === "image" || input.type === "document") && isObject(input.source) && input.source.type === "base64") {
10456
+ if (isAnthropicBase64ContentBlock(input)) {
9642
10457
  return {
9643
10458
  ...input,
9644
10459
  source: convertBase64ToAttachment(input.source, input.type)
@@ -9669,8 +10484,153 @@ function filterFrom(obj, fieldsToRemove) {
9669
10484
  return result;
9670
10485
  }
9671
10486
 
10487
+ // src/wrappers/ai-sdk/normalize-logged-output.ts
10488
+ var REMOVE_NORMALIZED_VALUE = Symbol("braintrust.ai-sdk.remove-normalized");
10489
+ function normalizeAISDKLoggedOutput(value) {
10490
+ const normalized = normalizeAISDKLoggedValue(value);
10491
+ return normalized === REMOVE_NORMALIZED_VALUE ? {} : normalized;
10492
+ }
10493
+ function normalizeAISDKLoggedValue(value, context = {}) {
10494
+ if (Array.isArray(value)) {
10495
+ return value.map((entry) => normalizeAISDKLoggedValue(entry, context)).filter((entry) => entry !== REMOVE_NORMALIZED_VALUE);
10496
+ }
10497
+ if (!value || typeof value !== "object") {
10498
+ return value;
10499
+ }
10500
+ const nextInProviderMetadata = context.inProviderMetadata || context.parentKey === "providerMetadata" || context.parentKey === "experimental_providerMetadata";
10501
+ const normalizedEntries = [];
10502
+ for (const [key, entry] of Object.entries(value)) {
10503
+ if (key === "cachedPromptTokens" && entry === 0) {
10504
+ continue;
10505
+ }
10506
+ if (context.parentKey === "request" && key === "body" && entry === "<omitted>") {
10507
+ continue;
10508
+ }
10509
+ const normalizedEntry = normalizeAISDKLoggedValue(entry, {
10510
+ inProviderMetadata: nextInProviderMetadata,
10511
+ parentKey: key
10512
+ });
10513
+ if (normalizedEntry === REMOVE_NORMALIZED_VALUE) {
10514
+ continue;
10515
+ }
10516
+ normalizedEntries.push([key, normalizedEntry]);
10517
+ }
10518
+ if (normalizedEntries.length === 0) {
10519
+ if (context.parentKey === "request" || nextInProviderMetadata) {
10520
+ return REMOVE_NORMALIZED_VALUE;
10521
+ }
10522
+ return {};
10523
+ }
10524
+ return Object.fromEntries(normalizedEntries);
10525
+ }
10526
+
10527
+ // src/zod/utils.ts
10528
+ import { zodToJsonSchema as zodToJsonSchemaV3 } from "zod-to-json-schema";
10529
+ import * as z42 from "zod/v4";
10530
+ function isZodV4(zodObject) {
10531
+ return typeof zodObject === "object" && zodObject !== null && "_zod" in zodObject && zodObject._zod !== void 0;
10532
+ }
10533
+ function zodToJsonSchema(schema) {
10534
+ if (isZodV4(schema)) {
10535
+ return z42.toJSONSchema(schema, {
10536
+ target: "draft-7"
10537
+ });
10538
+ }
10539
+ return zodToJsonSchemaV3(schema);
10540
+ }
10541
+
10542
+ // src/wrappers/ai-sdk/tool-serialization.ts
10543
+ function isZodSchema(value) {
10544
+ return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
10545
+ }
10546
+ function serializeZodSchema(schema) {
10547
+ try {
10548
+ return zodToJsonSchema(schema);
10549
+ } catch {
10550
+ return {
10551
+ type: "object",
10552
+ description: "Zod schema (conversion failed)"
10553
+ };
10554
+ }
10555
+ }
10556
+ function serializeTool(tool) {
10557
+ if (!tool || typeof tool !== "object") {
10558
+ return tool;
10559
+ }
10560
+ const serialized = { ...tool };
10561
+ if (isZodSchema(serialized.inputSchema)) {
10562
+ serialized.inputSchema = serializeZodSchema(serialized.inputSchema);
10563
+ }
10564
+ if (isZodSchema(serialized.parameters)) {
10565
+ serialized.parameters = serializeZodSchema(serialized.parameters);
10566
+ }
10567
+ if ("execute" in serialized) {
10568
+ delete serialized.execute;
10569
+ }
10570
+ if ("render" in serialized) {
10571
+ delete serialized.render;
10572
+ }
10573
+ return serialized;
10574
+ }
10575
+ function serializeAISDKToolsForLogging(tools) {
10576
+ if (!tools || typeof tools !== "object") {
10577
+ return tools;
10578
+ }
10579
+ if (Array.isArray(tools)) {
10580
+ return tools.map(serializeTool);
10581
+ }
10582
+ const serialized = {};
10583
+ for (const [key, tool] of Object.entries(tools)) {
10584
+ serialized[key] = serializeTool(tool);
10585
+ }
10586
+ return serialized;
10587
+ }
10588
+
10589
+ // src/instrumentation/plugins/ai-sdk-channels.ts
10590
+ var aiSDKChannels = defineChannels("ai", {
10591
+ generateText: channel({
10592
+ channelName: "generateText",
10593
+ kind: "async"
10594
+ }),
10595
+ streamText: channel({
10596
+ channelName: "streamText",
10597
+ kind: "async"
10598
+ }),
10599
+ streamTextSync: channel({
10600
+ channelName: "streamText.sync",
10601
+ kind: "sync-stream"
10602
+ }),
10603
+ generateObject: channel({
10604
+ channelName: "generateObject",
10605
+ kind: "async"
10606
+ }),
10607
+ streamObject: channel({
10608
+ channelName: "streamObject",
10609
+ kind: "async"
10610
+ }),
10611
+ streamObjectSync: channel({
10612
+ channelName: "streamObject.sync",
10613
+ kind: "sync-stream"
10614
+ }),
10615
+ agentGenerate: channel({
10616
+ channelName: "Agent.generate",
10617
+ kind: "async"
10618
+ }),
10619
+ agentStream: channel({
10620
+ channelName: "Agent.stream",
10621
+ kind: "async"
10622
+ }),
10623
+ toolLoopAgentGenerate: channel({
10624
+ channelName: "ToolLoopAgent.generate",
10625
+ kind: "async"
10626
+ }),
10627
+ toolLoopAgentStream: channel({
10628
+ channelName: "ToolLoopAgent.stream",
10629
+ kind: "async"
10630
+ })
10631
+ });
10632
+
9672
10633
  // src/instrumentation/plugins/ai-sdk-plugin.ts
9673
- import { tracingChannel as tracingChannel3 } from "dc-browser";
9674
10634
  var DEFAULT_DENY_OUTPUT_PATHS = [
9675
10635
  // v3
9676
10636
  "roundtrips[].request.body",
@@ -9685,8 +10645,9 @@ var DEFAULT_DENY_OUTPUT_PATHS = [
9685
10645
  "steps[].response.body",
9686
10646
  "steps[].response.headers"
9687
10647
  ];
10648
+ var AUTO_PATCHED_MODEL = Symbol.for("braintrust.ai-sdk.auto-patched-model");
10649
+ var AUTO_PATCHED_TOOL = Symbol.for("braintrust.ai-sdk.auto-patched-tool");
9688
10650
  var AISDKPlugin = class extends BasePlugin {
9689
- unsubscribers = [];
9690
10651
  config;
9691
10652
  constructor(config = {}) {
9692
10653
  super();
@@ -9696,276 +10657,579 @@ var AISDKPlugin = class extends BasePlugin {
9696
10657
  this.subscribeToAISDK();
9697
10658
  }
9698
10659
  onDisable() {
9699
- for (const unsubscribe of this.unsubscribers) {
9700
- unsubscribe();
9701
- }
9702
- this.unsubscribers = [];
10660
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
9703
10661
  }
9704
10662
  subscribeToAISDK() {
9705
10663
  const denyOutputPaths = this.config.denyOutputPaths || DEFAULT_DENY_OUTPUT_PATHS;
9706
- this.subscribeToStreamingChannel("orchestrion:ai-sdk:generateText", {
9707
- name: "generateText",
9708
- type: "llm" /* LLM */,
9709
- extractInput: (args) => {
9710
- const params = args[0] || {};
9711
- return {
9712
- input: processAISDKInput(params),
9713
- metadata: extractMetadataFromParams(params)
9714
- };
9715
- },
9716
- extractOutput: (result) => {
9717
- return processAISDKOutput(result, denyOutputPaths);
9718
- },
9719
- extractMetrics: (result, startTime) => {
9720
- const metrics = extractTokenMetrics(result);
9721
- if (startTime) {
9722
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9723
- }
9724
- return metrics;
9725
- },
9726
- aggregateChunks: aggregateAISDKChunks
9727
- });
9728
- this.subscribeToStreamingChannel("orchestrion:ai-sdk:streamText", {
9729
- name: "streamText",
9730
- type: "llm" /* LLM */,
9731
- extractInput: (args) => {
9732
- const params = args[0] || {};
9733
- return {
9734
- input: processAISDKInput(params),
9735
- metadata: extractMetadataFromParams(params)
9736
- };
9737
- },
9738
- extractOutput: (result) => {
9739
- return processAISDKOutput(result, denyOutputPaths);
9740
- },
9741
- extractMetrics: (result, startTime) => {
9742
- const metrics = extractTokenMetrics(result);
9743
- if (startTime) {
9744
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9745
- }
9746
- return metrics;
9747
- },
9748
- aggregateChunks: aggregateAISDKChunks
9749
- });
9750
- this.subscribeToStreamingChannel("orchestrion:ai-sdk:generateObject", {
9751
- name: "generateObject",
9752
- type: "llm" /* LLM */,
9753
- extractInput: (args) => {
9754
- const params = args[0] || {};
9755
- return {
9756
- input: processAISDKInput(params),
9757
- metadata: extractMetadataFromParams(params)
9758
- };
9759
- },
9760
- extractOutput: (result) => {
9761
- return processAISDKOutput(result, denyOutputPaths);
9762
- },
9763
- extractMetrics: (result, startTime) => {
9764
- const metrics = extractTokenMetrics(result);
9765
- if (startTime) {
9766
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9767
- }
9768
- return metrics;
9769
- },
9770
- aggregateChunks: aggregateAISDKChunks
9771
- });
9772
- this.subscribeToStreamingChannel("orchestrion:ai-sdk:streamObject", {
9773
- name: "streamObject",
9774
- type: "llm" /* LLM */,
9775
- extractInput: (args) => {
9776
- const params = args[0] || {};
9777
- return {
9778
- input: processAISDKInput(params),
9779
- metadata: extractMetadataFromParams(params)
9780
- };
9781
- },
9782
- extractOutput: (result) => {
9783
- return processAISDKOutput(result, denyOutputPaths);
9784
- },
9785
- extractMetrics: (result, startTime) => {
9786
- const metrics = extractTokenMetrics(result);
9787
- if (startTime) {
9788
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9789
- }
9790
- return metrics;
9791
- },
9792
- aggregateChunks: aggregateAISDKChunks
9793
- });
9794
- this.subscribeToStreamingChannel("orchestrion:ai-sdk:Agent.generate", {
9795
- name: "Agent.generate",
9796
- type: "llm" /* LLM */,
9797
- extractInput: (args) => {
9798
- const params = args[0] || {};
9799
- return {
9800
- input: processAISDKInput(params),
9801
- metadata: extractMetadataFromParams(params)
9802
- };
9803
- },
9804
- extractOutput: (result) => {
9805
- return processAISDKOutput(result, denyOutputPaths);
9806
- },
9807
- extractMetrics: (result, startTime) => {
9808
- const metrics = extractTokenMetrics(result);
9809
- if (startTime) {
9810
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9811
- }
9812
- return metrics;
9813
- },
9814
- aggregateChunks: aggregateAISDKChunks
9815
- });
9816
- this.subscribeToStreamingChannel("orchestrion:ai-sdk:Agent.stream", {
9817
- name: "Agent.stream",
9818
- type: "llm" /* LLM */,
9819
- extractInput: (args) => {
9820
- const params = args[0] || {};
9821
- return {
9822
- input: processAISDKInput(params),
9823
- metadata: extractMetadataFromParams(params)
9824
- };
9825
- },
9826
- extractOutput: (result) => {
9827
- return processAISDKOutput(result, denyOutputPaths);
9828
- },
9829
- extractMetrics: (result, startTime) => {
9830
- const metrics = extractTokenMetrics(result);
9831
- if (startTime) {
9832
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9833
- }
9834
- return metrics;
9835
- },
9836
- aggregateChunks: aggregateAISDKChunks
9837
- });
10664
+ this.unsubscribers.push(
10665
+ traceStreamingChannel(aiSDKChannels.generateText, {
10666
+ name: "generateText",
10667
+ type: "llm" /* LLM */,
10668
+ extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10669
+ extractOutput: (result, endEvent) => {
10670
+ finalizeAISDKChildTracing(endEvent);
10671
+ return processAISDKOutput(result, denyOutputPaths);
10672
+ },
10673
+ extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
10674
+ aggregateChunks: aggregateAISDKChunks
10675
+ })
10676
+ );
10677
+ this.unsubscribers.push(
10678
+ traceStreamingChannel(aiSDKChannels.streamText, {
10679
+ name: "streamText",
10680
+ type: "llm" /* LLM */,
10681
+ extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10682
+ extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
10683
+ extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
10684
+ aggregateChunks: aggregateAISDKChunks,
10685
+ patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
10686
+ denyOutputPaths,
10687
+ endEvent,
10688
+ result,
10689
+ span,
10690
+ startTime
10691
+ })
10692
+ })
10693
+ );
10694
+ this.unsubscribers.push(
10695
+ traceSyncStreamChannel(aiSDKChannels.streamTextSync, {
10696
+ name: "streamText",
10697
+ type: "llm" /* LLM */,
10698
+ extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10699
+ patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
10700
+ denyOutputPaths,
10701
+ endEvent,
10702
+ result,
10703
+ span,
10704
+ startTime
10705
+ })
10706
+ })
10707
+ );
10708
+ this.unsubscribers.push(
10709
+ traceStreamingChannel(aiSDKChannels.generateObject, {
10710
+ name: "generateObject",
10711
+ type: "llm" /* LLM */,
10712
+ extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10713
+ extractOutput: (result, endEvent) => {
10714
+ finalizeAISDKChildTracing(endEvent);
10715
+ return processAISDKOutput(result, denyOutputPaths);
10716
+ },
10717
+ extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
10718
+ aggregateChunks: aggregateAISDKChunks
10719
+ })
10720
+ );
10721
+ this.unsubscribers.push(
10722
+ traceStreamingChannel(aiSDKChannels.streamObject, {
10723
+ name: "streamObject",
10724
+ type: "llm" /* LLM */,
10725
+ extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10726
+ extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
10727
+ extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
10728
+ aggregateChunks: aggregateAISDKChunks,
10729
+ patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
10730
+ denyOutputPaths,
10731
+ endEvent,
10732
+ result,
10733
+ span,
10734
+ startTime
10735
+ })
10736
+ })
10737
+ );
10738
+ this.unsubscribers.push(
10739
+ traceSyncStreamChannel(aiSDKChannels.streamObjectSync, {
10740
+ name: "streamObject",
10741
+ type: "llm" /* LLM */,
10742
+ extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10743
+ patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
10744
+ denyOutputPaths,
10745
+ endEvent,
10746
+ result,
10747
+ span,
10748
+ startTime
10749
+ })
10750
+ })
10751
+ );
10752
+ this.unsubscribers.push(
10753
+ traceStreamingChannel(aiSDKChannels.agentGenerate, {
10754
+ name: "Agent.generate",
10755
+ type: "llm" /* LLM */,
10756
+ extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10757
+ extractOutput: (result, endEvent) => {
10758
+ finalizeAISDKChildTracing(endEvent);
10759
+ return processAISDKOutput(result, denyOutputPaths);
10760
+ },
10761
+ extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
10762
+ aggregateChunks: aggregateAISDKChunks
10763
+ })
10764
+ );
10765
+ this.unsubscribers.push(
10766
+ traceStreamingChannel(aiSDKChannels.agentStream, {
10767
+ name: "Agent.stream",
10768
+ type: "llm" /* LLM */,
10769
+ extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10770
+ extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
10771
+ extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
10772
+ aggregateChunks: aggregateAISDKChunks,
10773
+ patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
10774
+ denyOutputPaths,
10775
+ endEvent,
10776
+ result,
10777
+ span,
10778
+ startTime
10779
+ })
10780
+ })
10781
+ );
10782
+ this.unsubscribers.push(
10783
+ traceStreamingChannel(aiSDKChannels.toolLoopAgentGenerate, {
10784
+ name: "ToolLoopAgent.generate",
10785
+ type: "llm" /* LLM */,
10786
+ extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10787
+ extractOutput: (result, endEvent) => {
10788
+ finalizeAISDKChildTracing(endEvent);
10789
+ return processAISDKOutput(result, denyOutputPaths);
10790
+ },
10791
+ extractMetrics: (result, _startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent),
10792
+ aggregateChunks: aggregateAISDKChunks
10793
+ })
10794
+ );
10795
+ this.unsubscribers.push(
10796
+ traceStreamingChannel(aiSDKChannels.toolLoopAgentStream, {
10797
+ name: "ToolLoopAgent.stream",
10798
+ type: "llm" /* LLM */,
10799
+ extractInput: ([params], event, span) => prepareAISDKInput(params, event, span, denyOutputPaths),
10800
+ extractOutput: (result) => processAISDKOutput(result, denyOutputPaths),
10801
+ extractMetrics: (result, startTime, endEvent) => extractTopLevelAISDKMetrics(result, endEvent, startTime),
10802
+ aggregateChunks: aggregateAISDKChunks,
10803
+ patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
10804
+ denyOutputPaths,
10805
+ endEvent,
10806
+ result,
10807
+ span,
10808
+ startTime
10809
+ })
10810
+ })
10811
+ );
9838
10812
  }
9839
- /**
9840
- * Subscribe to a channel for async methods that may return streams.
9841
- * Handles both streaming and non-streaming responses.
9842
- */
9843
- subscribeToStreamingChannel(channelName, config) {
9844
- const channel = tracingChannel3(channelName);
9845
- const spans = /* @__PURE__ */ new WeakMap();
9846
- const handlers = {
9847
- start: (event) => {
9848
- const span = startSpan({
9849
- name: config.name,
9850
- spanAttributes: {
9851
- type: config.type
9852
- }
9853
- });
9854
- const startTime = getCurrentUnixTimestamp();
9855
- spans.set(event, { span, startTime });
9856
- try {
9857
- const { input, metadata } = config.extractInput(event.arguments);
10813
+ };
10814
+ function processAISDKInput(params) {
10815
+ if (!params) return params;
10816
+ const input = processInputAttachments(params);
10817
+ if (!input || typeof input !== "object" || Array.isArray(input)) {
10818
+ return input;
10819
+ }
10820
+ const { tools: _tools, ...rest } = input;
10821
+ return rest;
10822
+ }
10823
+ function prepareAISDKInput(params, event, span, denyOutputPaths) {
10824
+ const input = processAISDKInput(params);
10825
+ const metadata = extractMetadataFromParams(params, event.self);
10826
+ const childTracing = prepareAISDKChildTracing(
10827
+ params,
10828
+ event.self,
10829
+ span,
10830
+ denyOutputPaths
10831
+ );
10832
+ event.__braintrust_ai_sdk_model_wrapped = childTracing.modelWrapped;
10833
+ if (childTracing.cleanup) {
10834
+ event.__braintrust_ai_sdk_cleanup = childTracing.cleanup;
10835
+ }
10836
+ return {
10837
+ input,
10838
+ metadata
10839
+ };
10840
+ }
10841
+ function extractTopLevelAISDKMetrics(result, event, startTime) {
10842
+ const metrics = hasModelChildTracing(event) ? {} : extractTokenMetrics(result);
10843
+ if (startTime) {
10844
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
10845
+ }
10846
+ return metrics;
10847
+ }
10848
+ function hasModelChildTracing(event) {
10849
+ return event?.__braintrust_ai_sdk_model_wrapped === true;
10850
+ }
10851
+ function extractMetadataFromParams(params, self) {
10852
+ const metadata = {
10853
+ braintrust: {
10854
+ integration_name: "ai-sdk",
10855
+ sdk_language: "typescript"
10856
+ }
10857
+ };
10858
+ 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;
10859
+ const { model, provider } = serializeModelWithProvider(
10860
+ params.model ?? agentModel
10861
+ );
10862
+ if (model) {
10863
+ metadata.model = model;
10864
+ }
10865
+ if (provider) {
10866
+ metadata.provider = provider;
10867
+ }
10868
+ const tools = serializeAISDKToolsForLogging(params.tools);
10869
+ if (tools) {
10870
+ metadata.tools = tools;
10871
+ }
10872
+ return metadata;
10873
+ }
10874
+ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths) {
10875
+ const cleanup = [];
10876
+ const patchedModels = /* @__PURE__ */ new WeakSet();
10877
+ const patchedTools = /* @__PURE__ */ new WeakSet();
10878
+ let modelWrapped = false;
10879
+ const patchModel = (model) => {
10880
+ const resolvedModel = resolveAISDKModel(model);
10881
+ if (!resolvedModel || typeof resolvedModel !== "object" || typeof resolvedModel.doGenerate !== "function" || patchedModels.has(resolvedModel) || resolvedModel[AUTO_PATCHED_MODEL]) {
10882
+ return;
10883
+ }
10884
+ patchedModels.add(resolvedModel);
10885
+ resolvedModel[AUTO_PATCHED_MODEL] = true;
10886
+ modelWrapped = true;
10887
+ const originalDoGenerate = resolvedModel.doGenerate;
10888
+ const originalDoStream = resolvedModel.doStream;
10889
+ const baseMetadata = buildAISDKChildMetadata(resolvedModel);
10890
+ resolvedModel.doGenerate = async function doGeneratePatched(options) {
10891
+ return parentSpan.traced(
10892
+ async (span) => {
10893
+ const result = await Reflect.apply(
10894
+ originalDoGenerate,
10895
+ resolvedModel,
10896
+ [options]
10897
+ );
9858
10898
  span.log({
9859
- input,
9860
- metadata
10899
+ output: processAISDKOutput(result, denyOutputPaths),
10900
+ metrics: extractTokenMetrics(result),
10901
+ ...buildResolvedMetadataPayload(result)
9861
10902
  });
9862
- } catch (error) {
9863
- console.error(`Error extracting input for ${channelName}:`, error);
9864
- }
9865
- },
9866
- asyncEnd: (event) => {
9867
- const spanData = spans.get(event);
9868
- if (!spanData) {
9869
- return;
10903
+ return result;
10904
+ },
10905
+ {
10906
+ name: "doGenerate",
10907
+ spanAttributes: {
10908
+ type: "llm" /* LLM */
10909
+ },
10910
+ event: {
10911
+ input: processAISDKInput(options),
10912
+ metadata: baseMetadata
10913
+ }
9870
10914
  }
9871
- const { span, startTime } = spanData;
9872
- if (isAsyncIterable(event.result)) {
9873
- patchStreamIfNeeded(event.result, {
9874
- onComplete: (chunks) => {
9875
- try {
9876
- let output;
9877
- let metrics;
9878
- if (config.aggregateChunks) {
9879
- const aggregated = config.aggregateChunks(chunks);
9880
- output = aggregated.output;
9881
- metrics = aggregated.metrics;
9882
- } else {
9883
- output = config.extractOutput(chunks);
9884
- metrics = config.extractMetrics(chunks, startTime);
10915
+ );
10916
+ };
10917
+ if (originalDoStream) {
10918
+ resolvedModel.doStream = async function doStreamPatched(options) {
10919
+ const span = parentSpan.startSpan({
10920
+ name: "doStream",
10921
+ spanAttributes: {
10922
+ type: "llm" /* LLM */
10923
+ },
10924
+ event: {
10925
+ input: processAISDKInput(options),
10926
+ metadata: baseMetadata
10927
+ }
10928
+ });
10929
+ const result = await withCurrent(
10930
+ span,
10931
+ () => Reflect.apply(originalDoStream, resolvedModel, [options])
10932
+ );
10933
+ const output = {};
10934
+ let text = "";
10935
+ let reasoning = "";
10936
+ const toolCalls = [];
10937
+ let object = void 0;
10938
+ const transformStream = new TransformStream({
10939
+ transform(chunk, controller) {
10940
+ switch (chunk.type) {
10941
+ case "text-delta":
10942
+ text += extractTextDelta(chunk);
10943
+ break;
10944
+ case "reasoning-delta":
10945
+ if (chunk.delta) {
10946
+ reasoning += chunk.delta;
10947
+ } else if (chunk.text) {
10948
+ reasoning += chunk.text;
9885
10949
  }
9886
- if (!metrics.time_to_first_token && chunks.length > 0) {
9887
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
10950
+ break;
10951
+ case "tool-call":
10952
+ toolCalls.push(chunk);
10953
+ break;
10954
+ case "object":
10955
+ object = chunk.object;
10956
+ break;
10957
+ case "raw":
10958
+ if (chunk.rawValue) {
10959
+ const rawVal = chunk.rawValue;
10960
+ if (rawVal.delta?.content) {
10961
+ text += rawVal.delta.content;
10962
+ } else if (rawVal.choices?.[0]?.delta?.content) {
10963
+ text += rawVal.choices[0].delta.content;
10964
+ } else if (typeof rawVal.text === "string") {
10965
+ text += rawVal.text;
10966
+ } else if (typeof rawVal.content === "string") {
10967
+ text += rawVal.content;
10968
+ }
10969
+ }
10970
+ break;
10971
+ case "finish":
10972
+ output.text = text;
10973
+ output.reasoning = reasoning;
10974
+ output.toolCalls = toolCalls;
10975
+ output.finishReason = chunk.finishReason;
10976
+ output.usage = chunk.usage;
10977
+ if (object !== void 0) {
10978
+ output.object = object;
9888
10979
  }
9889
10980
  span.log({
9890
- output,
9891
- metrics
10981
+ output: processAISDKOutput(
10982
+ output,
10983
+ denyOutputPaths
10984
+ ),
10985
+ metrics: extractTokenMetrics(output),
10986
+ ...buildResolvedMetadataPayload(output)
9892
10987
  });
9893
- } catch (error) {
9894
- console.error(
9895
- `Error extracting output for ${channelName}:`,
9896
- error
9897
- );
9898
- } finally {
9899
10988
  span.end();
9900
- }
9901
- },
9902
- onError: (error) => {
9903
- span.log({
9904
- error: error.message
9905
- });
9906
- span.end();
10989
+ break;
10990
+ }
10991
+ controller.enqueue(chunk);
10992
+ }
10993
+ });
10994
+ return {
10995
+ ...result,
10996
+ stream: result.stream.pipeThrough(transformStream)
10997
+ };
10998
+ };
10999
+ }
11000
+ cleanup.push(() => {
11001
+ resolvedModel.doGenerate = originalDoGenerate;
11002
+ if (originalDoStream) {
11003
+ resolvedModel.doStream = originalDoStream;
11004
+ }
11005
+ delete resolvedModel[AUTO_PATCHED_MODEL];
11006
+ });
11007
+ };
11008
+ const patchTool = (tool, name) => {
11009
+ if (tool == null || typeof tool !== "object" || !("execute" in tool) || typeof tool.execute !== "function" || patchedTools.has(tool) || tool[AUTO_PATCHED_TOOL]) {
11010
+ return;
11011
+ }
11012
+ patchedTools.add(tool);
11013
+ tool[AUTO_PATCHED_TOOL] = true;
11014
+ const originalExecute = tool.execute;
11015
+ tool.execute = function executePatched(...args) {
11016
+ const result = Reflect.apply(originalExecute, this, args);
11017
+ if (isAsyncGenerator(result)) {
11018
+ return (async function* () {
11019
+ const span = parentSpan.startSpan({
11020
+ name,
11021
+ spanAttributes: {
11022
+ type: "tool" /* TOOL */
9907
11023
  }
9908
11024
  });
9909
- } else {
11025
+ span.log({ input: args.length === 1 ? args[0] : args });
9910
11026
  try {
9911
- const output = config.extractOutput(event.result);
9912
- const metrics = config.extractMetrics(event.result, startTime);
11027
+ let lastValue;
11028
+ for await (const value of result) {
11029
+ lastValue = value;
11030
+ yield value;
11031
+ }
11032
+ span.log({ output: lastValue });
11033
+ } catch (error) {
9913
11034
  span.log({
9914
- output,
9915
- metrics
11035
+ error: error instanceof Error ? error.message : String(error)
9916
11036
  });
9917
- } catch (error) {
9918
- console.error(`Error extracting output for ${channelName}:`, error);
11037
+ throw error;
9919
11038
  } finally {
9920
11039
  span.end();
9921
- spans.delete(event);
11040
+ }
11041
+ })();
11042
+ }
11043
+ return parentSpan.traced(
11044
+ async (span) => {
11045
+ span.log({ input: args.length === 1 ? args[0] : args });
11046
+ const awaitedResult = await result;
11047
+ span.log({ output: awaitedResult });
11048
+ return awaitedResult;
11049
+ },
11050
+ {
11051
+ name,
11052
+ spanAttributes: {
11053
+ type: "tool" /* TOOL */
9922
11054
  }
9923
11055
  }
9924
- },
9925
- error: (event) => {
9926
- const spanData = spans.get(event);
9927
- if (!spanData) {
9928
- return;
11056
+ );
11057
+ };
11058
+ cleanup.push(() => {
11059
+ tool.execute = originalExecute;
11060
+ delete tool[AUTO_PATCHED_TOOL];
11061
+ });
11062
+ };
11063
+ const patchTools = (tools) => {
11064
+ if (!tools) {
11065
+ return;
11066
+ }
11067
+ const inferName = (tool, fallback2) => tool && (tool.name || tool.toolName || tool.id) || fallback2;
11068
+ if (Array.isArray(tools)) {
11069
+ tools.forEach(
11070
+ (tool, index) => patchTool(tool, inferName(tool, `tool[${index}]`))
11071
+ );
11072
+ return;
11073
+ }
11074
+ for (const [key, tool] of Object.entries(tools)) {
11075
+ patchTool(tool, key);
11076
+ }
11077
+ };
11078
+ if (params && typeof params === "object") {
11079
+ patchModel(params.model);
11080
+ patchTools(params.tools);
11081
+ }
11082
+ if (self && typeof self === "object") {
11083
+ const selfRecord = self;
11084
+ if (selfRecord.model !== void 0) {
11085
+ patchModel(selfRecord.model);
11086
+ }
11087
+ if (selfRecord.settings && typeof selfRecord.settings === "object") {
11088
+ if (selfRecord.settings.model !== void 0) {
11089
+ patchModel(selfRecord.settings.model);
11090
+ }
11091
+ if (selfRecord.settings.tools !== void 0) {
11092
+ patchTools(selfRecord.settings.tools);
11093
+ }
11094
+ }
11095
+ }
11096
+ return {
11097
+ cleanup: cleanup.length > 0 ? () => {
11098
+ while (cleanup.length > 0) {
11099
+ cleanup.pop()?.();
11100
+ }
11101
+ } : void 0,
11102
+ modelWrapped
11103
+ };
11104
+ }
11105
+ function finalizeAISDKChildTracing(event) {
11106
+ const cleanup = event?.__braintrust_ai_sdk_cleanup;
11107
+ if (event && typeof cleanup === "function") {
11108
+ cleanup();
11109
+ delete event.__braintrust_ai_sdk_cleanup;
11110
+ }
11111
+ }
11112
+ function patchAISDKStreamingResult(args) {
11113
+ const { denyOutputPaths, endEvent, result, span, startTime } = args;
11114
+ if (!result || typeof result !== "object") {
11115
+ return false;
11116
+ }
11117
+ const resultRecord = result;
11118
+ if (!isReadableStreamLike(resultRecord.baseStream)) {
11119
+ return false;
11120
+ }
11121
+ let firstChunkTime;
11122
+ const wrappedBaseStream = resultRecord.baseStream.pipeThrough(
11123
+ new TransformStream({
11124
+ transform(chunk, controller) {
11125
+ if (firstChunkTime === void 0) {
11126
+ firstChunkTime = getCurrentUnixTimestamp();
9929
11127
  }
9930
- const { span } = spanData;
11128
+ controller.enqueue(chunk);
11129
+ },
11130
+ async flush() {
11131
+ const metrics = extractTopLevelAISDKMetrics(result, endEvent);
11132
+ if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
11133
+ metrics.time_to_first_token = firstChunkTime - startTime;
11134
+ }
11135
+ const output = await processAISDKStreamingOutput(
11136
+ result,
11137
+ denyOutputPaths
11138
+ );
11139
+ const metadata = buildResolvedMetadataPayload(result).metadata;
9931
11140
  span.log({
9932
- error: event.error.message
11141
+ output,
11142
+ ...metadata ? { metadata } : {},
11143
+ metrics
9933
11144
  });
11145
+ finalizeAISDKChildTracing(endEvent);
9934
11146
  span.end();
9935
- spans.delete(event);
9936
11147
  }
9937
- };
9938
- channel.subscribe(handlers);
9939
- this.unsubscribers.push(() => {
9940
- channel.unsubscribe(handlers);
9941
- });
11148
+ })
11149
+ );
11150
+ Object.defineProperty(resultRecord, "baseStream", {
11151
+ configurable: true,
11152
+ enumerable: true,
11153
+ value: wrappedBaseStream,
11154
+ writable: true
11155
+ });
11156
+ return true;
11157
+ }
11158
+ function isReadableStreamLike(value) {
11159
+ return value != null && typeof value === "object" && typeof value.pipeThrough === "function";
11160
+ }
11161
+ async function processAISDKStreamingOutput(result, denyOutputPaths) {
11162
+ const output = processAISDKOutput(result, denyOutputPaths);
11163
+ if (!output || typeof output !== "object") {
11164
+ return output;
9942
11165
  }
9943
- };
9944
- function processAISDKInput(params) {
9945
- if (!params) return params;
9946
- return processInputAttachments(params);
11166
+ const outputRecord = output;
11167
+ try {
11168
+ if ("text" in result && typeof result.text === "string") {
11169
+ outputRecord.text = result.text;
11170
+ }
11171
+ } catch {
11172
+ }
11173
+ try {
11174
+ if ("object" in result) {
11175
+ const resolvedObject = await Promise.resolve(result.object);
11176
+ if (resolvedObject !== void 0) {
11177
+ outputRecord.object = resolvedObject;
11178
+ }
11179
+ }
11180
+ } catch {
11181
+ }
11182
+ return outputRecord;
9947
11183
  }
9948
- function extractMetadataFromParams(params) {
9949
- const metadata = {
11184
+ function buildAISDKChildMetadata(model) {
11185
+ const { model: modelId, provider } = serializeModelWithProvider(model);
11186
+ return {
11187
+ ...modelId ? { model: modelId } : {},
11188
+ ...provider ? { provider } : {},
9950
11189
  braintrust: {
9951
11190
  integration_name: "ai-sdk",
9952
11191
  sdk_language: "typescript"
9953
11192
  }
9954
11193
  };
9955
- const { model, provider } = serializeModelWithProvider(params.model);
9956
- if (model) {
9957
- metadata.model = model;
11194
+ }
11195
+ function buildResolvedMetadataPayload(result) {
11196
+ const gatewayInfo = extractGatewayRoutingInfo(result);
11197
+ const metadata = {};
11198
+ if (gatewayInfo?.provider) {
11199
+ metadata.provider = gatewayInfo.provider;
9958
11200
  }
9959
- if (provider) {
9960
- metadata.provider = provider;
11201
+ if (gatewayInfo?.model) {
11202
+ metadata.model = gatewayInfo.model;
9961
11203
  }
9962
- return metadata;
11204
+ if (result.finishReason !== void 0) {
11205
+ metadata.finish_reason = result.finishReason;
11206
+ }
11207
+ return Object.keys(metadata).length > 0 ? { metadata } : {};
11208
+ }
11209
+ function resolveAISDKModel(model) {
11210
+ if (typeof model !== "string") {
11211
+ return model;
11212
+ }
11213
+ const provider = globalThis.AI_SDK_DEFAULT_PROVIDER ?? null;
11214
+ if (provider && typeof provider.languageModel === "function") {
11215
+ return provider.languageModel(model);
11216
+ }
11217
+ return model;
11218
+ }
11219
+ function extractTextDelta(chunk) {
11220
+ if (typeof chunk.textDelta === "string") return chunk.textDelta;
11221
+ if (typeof chunk.delta === "string") return chunk.delta;
11222
+ if (typeof chunk.text === "string") return chunk.text;
11223
+ if (typeof chunk.content === "string") return chunk.content;
11224
+ return "";
11225
+ }
11226
+ function isAsyncGenerator(value) {
11227
+ return value != null && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function" && typeof value.next === "function" && typeof value.return === "function" && typeof value.throw === "function";
9963
11228
  }
9964
11229
  function processAISDKOutput(output, denyOutputPaths) {
9965
11230
  if (!output) return output;
9966
- const getterValues = extractGetterValues(output);
9967
- const merged = { ...output, ...getterValues };
9968
- return omit(merged, denyOutputPaths);
11231
+ const merged = extractSerializableOutputFields(output);
11232
+ return normalizeAISDKLoggedOutput(omit(merged, denyOutputPaths));
9969
11233
  }
9970
11234
  function extractTokenMetrics(result) {
9971
11235
  const metrics = {};
@@ -10015,12 +11279,14 @@ function extractTokenMetrics(result) {
10015
11279
  }
10016
11280
  return metrics;
10017
11281
  }
10018
- function aggregateAISDKChunks(chunks) {
11282
+ function aggregateAISDKChunks(chunks, _result, endEvent) {
10019
11283
  const lastChunk = chunks[chunks.length - 1];
10020
11284
  const output = {};
10021
11285
  let metrics = {};
11286
+ let metadata;
10022
11287
  if (lastChunk) {
10023
- metrics = extractTokenMetrics(lastChunk);
11288
+ metrics = hasModelChildTracing(endEvent) ? {} : extractTokenMetrics(lastChunk);
11289
+ metadata = buildResolvedMetadataPayload(lastChunk).metadata;
10024
11290
  if (lastChunk.text !== void 0) {
10025
11291
  output.text = lastChunk.text;
10026
11292
  }
@@ -10034,7 +11300,8 @@ function aggregateAISDKChunks(chunks) {
10034
11300
  output.toolCalls = lastChunk.toolCalls;
10035
11301
  }
10036
11302
  }
10037
- return { output, metrics };
11303
+ finalizeAISDKChildTracing(endEvent);
11304
+ return { output, metrics, metadata };
10038
11305
  }
10039
11306
  function extractGetterValues(obj) {
10040
11307
  const getterValues = {};
@@ -10054,7 +11321,7 @@ function extractGetterValues(obj) {
10054
11321
  ];
10055
11322
  for (const name of getterNames) {
10056
11323
  try {
10057
- if (obj && name in obj && typeof obj[name] !== "function") {
11324
+ if (obj && name in obj && isSerializableOutputValue(obj[name])) {
10058
11325
  getterValues[name] = obj[name];
10059
11326
  }
10060
11327
  } catch {
@@ -10062,6 +11329,47 @@ function extractGetterValues(obj) {
10062
11329
  }
10063
11330
  return getterValues;
10064
11331
  }
11332
+ function extractSerializableOutputFields(output) {
11333
+ const serialized = {};
11334
+ const directFieldNames = [
11335
+ "steps",
11336
+ "request",
11337
+ "responseMessages",
11338
+ "warnings",
11339
+ "rawResponse",
11340
+ "response",
11341
+ "providerMetadata",
11342
+ "experimental_providerMetadata"
11343
+ ];
11344
+ for (const name of directFieldNames) {
11345
+ try {
11346
+ const value = output?.[name];
11347
+ if (isSerializableOutputValue(value)) {
11348
+ serialized[name] = value;
11349
+ }
11350
+ } catch {
11351
+ }
11352
+ }
11353
+ return {
11354
+ ...serialized,
11355
+ ...extractGetterValues(output)
11356
+ };
11357
+ }
11358
+ function isSerializableOutputValue(value) {
11359
+ if (typeof value === "function") {
11360
+ return false;
11361
+ }
11362
+ if (value && typeof value === "object" && typeof value.then === "function") {
11363
+ return false;
11364
+ }
11365
+ if (value && typeof value === "object" && typeof value.getReader === "function") {
11366
+ return false;
11367
+ }
11368
+ if (value && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function") {
11369
+ return false;
11370
+ }
11371
+ return true;
11372
+ }
10065
11373
  function serializeModelWithProvider(model) {
10066
11374
  const modelId = typeof model === "string" ? model : model?.modelId;
10067
11375
  const explicitProvider = typeof model === "object" ? model?.provider : void 0;
@@ -10087,6 +11395,25 @@ function parseGatewayModelString(modelString) {
10087
11395
  }
10088
11396
  return { model: modelString };
10089
11397
  }
11398
+ function extractGatewayRoutingInfo(result) {
11399
+ if (result?.steps && Array.isArray(result.steps) && result.steps.length > 0) {
11400
+ const routing2 = result.steps[0]?.providerMetadata?.gateway?.routing;
11401
+ if (routing2) {
11402
+ return {
11403
+ provider: routing2.resolvedProvider || routing2.finalProvider,
11404
+ model: routing2.resolvedProviderApiModelId
11405
+ };
11406
+ }
11407
+ }
11408
+ const routing = result?.providerMetadata?.gateway?.routing;
11409
+ if (routing) {
11410
+ return {
11411
+ provider: routing.resolvedProvider || routing.finalProvider,
11412
+ model: routing.resolvedProviderApiModelId
11413
+ };
11414
+ }
11415
+ return null;
11416
+ }
10090
11417
  function extractCostFromResult(result) {
10091
11418
  if (result?.steps && Array.isArray(result.steps) && result.steps.length > 0) {
10092
11419
  let totalCost = 0;
@@ -10180,7 +11507,10 @@ function omitAtPath(obj, keys) {
10180
11507
  if (Array.isArray(obj)) {
10181
11508
  obj.forEach((item) => {
10182
11509
  if (remainingKeys.length > 0) {
10183
- omitAtPath(item, remainingKeys);
11510
+ omitAtPath(
11511
+ item,
11512
+ remainingKeys
11513
+ );
10184
11514
  }
10185
11515
  });
10186
11516
  }
@@ -10190,7 +11520,10 @@ function omitAtPath(obj, keys) {
10190
11520
  }
10191
11521
  } else {
10192
11522
  if (obj && typeof obj === "object" && firstKey in obj) {
10193
- omitAtPath(obj[firstKey], remainingKeys);
11523
+ omitAtPath(
11524
+ obj[firstKey],
11525
+ remainingKeys
11526
+ );
10194
11527
  }
10195
11528
  }
10196
11529
  }
@@ -10203,8 +11536,18 @@ function omit(obj, paths) {
10203
11536
  return result;
10204
11537
  }
10205
11538
 
11539
+ // src/instrumentation/plugins/claude-agent-sdk-channels.ts
11540
+ var claudeAgentSDKChannels = defineChannels(
11541
+ "@anthropic-ai/claude-agent-sdk",
11542
+ {
11543
+ query: channel({
11544
+ channelName: "query",
11545
+ kind: "async"
11546
+ })
11547
+ }
11548
+ );
11549
+
10206
11550
  // src/instrumentation/plugins/claude-agent-sdk-plugin.ts
10207
- import { tracingChannel as tracingChannel4 } from "dc-browser";
10208
11551
  function filterSerializableOptions(options) {
10209
11552
  const allowedKeys = [
10210
11553
  "model",
@@ -10288,7 +11631,9 @@ async function createLLMSpanForMessages(messages, prompt, conversationHistory, o
10288
11631
  const input = buildLLMInput(prompt, conversationHistory);
10289
11632
  const outputs = messages.map(
10290
11633
  (m) => m.message?.content && m.message?.role ? { content: m.message.content, role: m.message.role } : void 0
10291
- ).filter((c) => c !== void 0);
11634
+ ).filter(
11635
+ (c) => c !== void 0
11636
+ );
10292
11637
  const span = startSpan({
10293
11638
  name: "anthropic.messages.create",
10294
11639
  spanAttributes: {
@@ -10307,7 +11652,6 @@ async function createLLMSpanForMessages(messages, prompt, conversationHistory, o
10307
11652
  return lastMessage.message?.content && lastMessage.message?.role ? { content: lastMessage.message.content, role: lastMessage.message.role } : void 0;
10308
11653
  }
10309
11654
  var ClaudeAgentSDKPlugin = class extends BasePlugin {
10310
- unsubscribers = [];
10311
11655
  onEnable() {
10312
11656
  this.subscribeToQuery();
10313
11657
  }
@@ -10323,12 +11667,13 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
10323
11667
  * and individual LLM calls.
10324
11668
  */
10325
11669
  subscribeToQuery() {
10326
- const channel = tracingChannel4("orchestrion:claude-agent-sdk:query");
11670
+ const channel2 = claudeAgentSDKChannels.query.tracingChannel();
10327
11671
  const spans = /* @__PURE__ */ new WeakMap();
10328
11672
  const handlers = {
10329
11673
  start: (event) => {
10330
- const params = event.arguments[0] ?? {};
10331
- const { prompt, options = {} } = params;
11674
+ const params = event.arguments[0];
11675
+ const prompt = params?.prompt;
11676
+ const options = params?.options ?? {};
10332
11677
  const span = startSpan({
10333
11678
  name: "Claude Agent",
10334
11679
  spanAttributes: {
@@ -10340,7 +11685,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
10340
11685
  span.log({
10341
11686
  input: typeof prompt === "string" ? prompt : {
10342
11687
  type: "streaming",
10343
- description: "AsyncIterable<SDKMessage>"
11688
+ description: "AsyncIterable<ClaudeAgentSDKMessage>"
10344
11689
  },
10345
11690
  metadata: filterSerializableOptions(options)
10346
11691
  });
@@ -10362,12 +11707,19 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
10362
11707
  if (!spanData) {
10363
11708
  return;
10364
11709
  }
10365
- if (isAsyncIterable(event.result)) {
10366
- patchStreamIfNeeded(event.result, {
11710
+ const eventResult = event.result;
11711
+ if (eventResult === void 0) {
11712
+ spanData.span.end();
11713
+ spans.delete(event);
11714
+ return;
11715
+ }
11716
+ if (isAsyncIterable(eventResult)) {
11717
+ patchStreamIfNeeded(eventResult, {
10367
11718
  onChunk: async (message) => {
10368
11719
  const currentTime = getCurrentUnixTimestamp();
10369
11720
  const params = event.arguments[0];
10370
- const { prompt, options = {} } = params;
11721
+ const prompt = params?.prompt;
11722
+ const options = params?.options ?? {};
10371
11723
  const messageId = message.message?.id;
10372
11724
  if (messageId && messageId !== spanData.currentMessageId) {
10373
11725
  if (spanData.currentMessages.length > 0) {
@@ -10426,7 +11778,8 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
10426
11778
  onComplete: async () => {
10427
11779
  try {
10428
11780
  const params = event.arguments[0];
10429
- const { prompt, options = {} } = params;
11781
+ const prompt = params?.prompt;
11782
+ const options = params?.options ?? {};
10430
11783
  if (spanData.currentMessages.length > 0) {
10431
11784
  const finalMessage = await createLLMSpanForMessages(
10432
11785
  spanData.currentMessages,
@@ -10464,7 +11817,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
10464
11817
  } else {
10465
11818
  try {
10466
11819
  spanData.span.log({
10467
- output: event.result
11820
+ output: eventResult
10468
11821
  });
10469
11822
  } catch (error) {
10470
11823
  console.error(
@@ -10479,7 +11832,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
10479
11832
  },
10480
11833
  error: (event) => {
10481
11834
  const spanData = spans.get(event);
10482
- if (!spanData) {
11835
+ if (!spanData || !event.error) {
10483
11836
  return;
10484
11837
  }
10485
11838
  const { span } = spanData;
@@ -10490,53 +11843,39 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
10490
11843
  spans.delete(event);
10491
11844
  }
10492
11845
  };
10493
- channel.subscribe(handlers);
11846
+ channel2.subscribe(handlers);
10494
11847
  this.unsubscribers.push(() => {
10495
- channel.unsubscribe(handlers);
11848
+ channel2.unsubscribe(handlers);
10496
11849
  });
10497
11850
  }
10498
11851
  };
10499
11852
 
11853
+ // src/instrumentation/plugins/google-genai-channels.ts
11854
+ var googleGenAIChannels = defineChannels("@google/genai", {
11855
+ generateContent: channel({
11856
+ channelName: "models.generateContent",
11857
+ kind: "async"
11858
+ }),
11859
+ generateContentStream: channel({
11860
+ channelName: "models.generateContentStream",
11861
+ kind: "async"
11862
+ })
11863
+ });
11864
+
10500
11865
  // src/instrumentation/plugins/google-genai-plugin.ts
10501
- import { tracingChannel as tracingChannel5 } from "dc-browser";
10502
11866
  var GoogleGenAIPlugin = class extends BasePlugin {
10503
- unsubscribers = [];
10504
11867
  onEnable() {
10505
11868
  this.subscribeToGoogleGenAIChannels();
10506
11869
  }
10507
11870
  onDisable() {
10508
- for (const unsubscribe of this.unsubscribers) {
10509
- unsubscribe();
10510
- }
10511
- this.unsubscribers = [];
11871
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
10512
11872
  }
10513
11873
  subscribeToGoogleGenAIChannels() {
10514
- this.subscribeToChannel("orchestrion:google-genai:models.generateContent", {
10515
- name: "google-genai.generateContent",
10516
- type: "llm" /* LLM */,
10517
- extractInput: (args) => {
10518
- const params = args[0] || {};
10519
- const input = serializeInput(params);
10520
- const metadata = extractMetadata(params);
10521
- return {
10522
- input,
10523
- metadata: { ...metadata, provider: "google-genai" }
10524
- };
10525
- },
10526
- extractOutput: (result) => {
10527
- return result;
10528
- },
10529
- extractMetrics: (result, startTime) => {
10530
- return extractGenerateContentMetrics(result, startTime);
10531
- }
10532
- });
10533
- this.subscribeToGoogleStreamingChannel(
10534
- "orchestrion:google-genai:models.generateContentStream",
10535
- {
10536
- name: "google-genai.generateContentStream",
11874
+ this.unsubscribers.push(
11875
+ traceAsyncChannel(googleGenAIChannels.generateContent, {
11876
+ name: "google-genai.generateContent",
10537
11877
  type: "llm" /* LLM */,
10538
- extractInput: (args) => {
10539
- const params = args[0] || {};
11878
+ extractInput: ([params]) => {
10540
11879
  const input = serializeInput(params);
10541
11880
  const metadata = extractMetadata(params);
10542
11881
  return {
@@ -10544,150 +11883,37 @@ var GoogleGenAIPlugin = class extends BasePlugin {
10544
11883
  metadata: { ...metadata, provider: "google-genai" }
10545
11884
  };
10546
11885
  },
10547
- aggregateChunks: aggregateGenerateContentChunks
10548
- }
10549
- );
10550
- }
10551
- subscribeToChannel(channelName, config) {
10552
- const channel = tracingChannel5(channelName);
10553
- const spans = /* @__PURE__ */ new WeakMap();
10554
- const handlers = {
10555
- start: (event) => {
10556
- const span = startSpan({
10557
- name: config.name,
10558
- spanAttributes: {
10559
- type: config.type
10560
- }
10561
- });
10562
- const startTime = getCurrentUnixTimestamp();
10563
- spans.set(event, { span, startTime });
10564
- try {
10565
- const { input, metadata } = config.extractInput(event.arguments);
10566
- span.log({
10567
- input,
10568
- metadata
10569
- });
10570
- } catch (error) {
10571
- console.error(`Error extracting input for ${channelName}:`, error);
10572
- }
10573
- },
10574
- asyncEnd: (event) => {
10575
- const spanData = spans.get(event);
10576
- if (!spanData) {
10577
- return;
10578
- }
10579
- const { span, startTime } = spanData;
10580
- try {
10581
- const output = config.extractOutput(event.result);
10582
- const metrics = config.extractMetrics(event.result, startTime);
10583
- span.log({
10584
- output,
10585
- metrics
10586
- });
10587
- } catch (error) {
10588
- console.error(`Error extracting output for ${channelName}:`, error);
10589
- } finally {
10590
- span.end();
10591
- spans.delete(event);
10592
- }
10593
- },
10594
- error: (event) => {
10595
- const spanData = spans.get(event);
10596
- if (!spanData) {
10597
- return;
11886
+ extractOutput: (result) => {
11887
+ return result;
11888
+ },
11889
+ extractMetrics: (result, startTime) => {
11890
+ return extractGenerateContentMetrics(result, startTime);
10598
11891
  }
10599
- const { span } = spanData;
10600
- span.log({
10601
- error: event.error.message
10602
- });
10603
- span.end();
10604
- spans.delete(event);
10605
- }
10606
- };
10607
- channel.subscribe(handlers);
10608
- this.unsubscribers.push(() => {
10609
- channel.unsubscribe(handlers);
10610
- });
10611
- }
10612
- subscribeToGoogleStreamingChannel(channelName, config) {
10613
- const channel = tracingChannel5(channelName);
10614
- const spans = /* @__PURE__ */ new WeakMap();
10615
- const handlers = {
10616
- start: (event) => {
10617
- const span = startSpan({
10618
- name: config.name,
10619
- spanAttributes: {
10620
- type: config.type
10621
- }
10622
- });
10623
- const startTime = getCurrentUnixTimestamp();
10624
- spans.set(event, { span, startTime });
10625
- try {
10626
- const { input, metadata } = config.extractInput(event.arguments);
10627
- span.log({
11892
+ })
11893
+ );
11894
+ this.unsubscribers.push(
11895
+ traceStreamingChannel(googleGenAIChannels.generateContentStream, {
11896
+ name: "google-genai.generateContentStream",
11897
+ type: "llm" /* LLM */,
11898
+ extractInput: ([params]) => {
11899
+ const input = serializeInput(params);
11900
+ const metadata = extractMetadata(params);
11901
+ return {
10628
11902
  input,
10629
- metadata
10630
- });
10631
- } catch (error) {
10632
- console.error(`Error extracting input for ${channelName}:`, error);
10633
- }
10634
- },
10635
- asyncEnd: (event) => {
10636
- const spanData = spans.get(event);
10637
- if (!spanData) {
10638
- return;
10639
- }
10640
- const { span, startTime } = spanData;
10641
- if (isAsyncIterable(event.result)) {
10642
- patchStreamIfNeeded(event.result, {
10643
- onComplete: (chunks) => {
10644
- try {
10645
- const { output, metrics } = config.aggregateChunks(
10646
- chunks,
10647
- startTime
10648
- );
10649
- span.log({
10650
- output,
10651
- metrics
10652
- });
10653
- } catch (error) {
10654
- console.error(
10655
- `Error extracting output for ${channelName}:`,
10656
- error
10657
- );
10658
- } finally {
10659
- span.end();
10660
- }
10661
- },
10662
- onError: (error) => {
10663
- span.log({
10664
- error: error.message
10665
- });
10666
- span.end();
10667
- }
10668
- });
10669
- } else {
10670
- span.end();
10671
- spans.delete(event);
10672
- }
10673
- },
10674
- error: (event) => {
10675
- const spanData = spans.get(event);
10676
- if (!spanData) {
10677
- return;
11903
+ metadata: { ...metadata, provider: "google-genai" }
11904
+ };
11905
+ },
11906
+ extractOutput: (result) => {
11907
+ return result;
11908
+ },
11909
+ extractMetrics: () => {
11910
+ return {};
11911
+ },
11912
+ aggregateChunks: (chunks, _result, _endEvent, startTime) => {
11913
+ return aggregateGenerateContentChunks(chunks, startTime);
10678
11914
  }
10679
- const { span } = spanData;
10680
- span.log({
10681
- error: event.error.message
10682
- });
10683
- span.end();
10684
- spans.delete(event);
10685
- }
10686
- };
10687
- channel.subscribe(handlers);
10688
- this.unsubscribers.push(() => {
10689
- channel.unsubscribe(handlers);
10690
- });
11915
+ })
11916
+ );
10691
11917
  }
10692
11918
  };
10693
11919
  function serializeInput(params) {
@@ -10743,8 +11969,12 @@ function serializePart(part) {
10743
11969
  const buffer = typeof data === "string" ? typeof Buffer !== "undefined" ? Buffer.from(data, "base64") : new Uint8Array(
10744
11970
  atob(data).split("").map((c) => c.charCodeAt(0))
10745
11971
  ) : typeof Buffer !== "undefined" ? Buffer.from(data) : new Uint8Array(data);
11972
+ const arrayBuffer = buffer instanceof Uint8Array ? buffer.buffer.slice(
11973
+ buffer.byteOffset,
11974
+ buffer.byteOffset + buffer.byteLength
11975
+ ) : buffer;
10746
11976
  const attachment = new Attachment({
10747
- data: buffer,
11977
+ data: arrayBuffer,
10748
11978
  filename,
10749
11979
  contentType: mimeType || "application/octet-stream"
10750
11980
  });
@@ -10793,33 +12023,36 @@ function extractGenerateContentMetrics(response, startTime) {
10793
12023
  const end = getCurrentUnixTimestamp();
10794
12024
  metrics.duration = end - startTime;
10795
12025
  }
10796
- if (response.usageMetadata) {
10797
- const usage = response.usageMetadata;
10798
- if (usage.promptTokenCount !== void 0) {
10799
- metrics.prompt_tokens = usage.promptTokenCount;
10800
- }
10801
- if (usage.candidatesTokenCount !== void 0) {
10802
- metrics.completion_tokens = usage.candidatesTokenCount;
10803
- }
10804
- if (usage.totalTokenCount !== void 0) {
10805
- metrics.tokens = usage.totalTokenCount;
10806
- }
10807
- if (usage.cachedContentTokenCount !== void 0) {
10808
- metrics.prompt_cached_tokens = usage.cachedContentTokenCount;
10809
- }
10810
- if (usage.thoughtsTokenCount !== void 0) {
10811
- metrics.completion_reasoning_tokens = usage.thoughtsTokenCount;
10812
- }
12026
+ if (response?.usageMetadata) {
12027
+ populateUsageMetrics(metrics, response.usageMetadata);
10813
12028
  }
10814
12029
  return metrics;
10815
12030
  }
12031
+ function populateUsageMetrics(metrics, usage) {
12032
+ if (usage.promptTokenCount !== void 0) {
12033
+ metrics.prompt_tokens = usage.promptTokenCount;
12034
+ }
12035
+ if (usage.candidatesTokenCount !== void 0) {
12036
+ metrics.completion_tokens = usage.candidatesTokenCount;
12037
+ }
12038
+ if (usage.totalTokenCount !== void 0) {
12039
+ metrics.tokens = usage.totalTokenCount;
12040
+ }
12041
+ if (usage.cachedContentTokenCount !== void 0) {
12042
+ metrics.prompt_cached_tokens = usage.cachedContentTokenCount;
12043
+ }
12044
+ if (usage.thoughtsTokenCount !== void 0) {
12045
+ metrics.completion_reasoning_tokens = usage.thoughtsTokenCount;
12046
+ }
12047
+ }
10816
12048
  function aggregateGenerateContentChunks(chunks, startTime) {
10817
- const end = getCurrentUnixTimestamp();
10818
- const metrics = {
10819
- duration: end - startTime
10820
- };
12049
+ const metrics = {};
12050
+ if (startTime !== void 0) {
12051
+ const end = getCurrentUnixTimestamp();
12052
+ metrics.duration = end - startTime;
12053
+ }
10821
12054
  let firstTokenTime = null;
10822
- if (chunks.length > 0 && firstTokenTime === null) {
12055
+ if (chunks.length > 0 && firstTokenTime === null && startTime !== void 0) {
10823
12056
  firstTokenTime = getCurrentUnixTimestamp();
10824
12057
  metrics.time_to_first_token = firstTokenTime - startTime;
10825
12058
  }
@@ -10890,21 +12123,7 @@ function aggregateGenerateContentChunks(chunks, startTime) {
10890
12123
  }
10891
12124
  if (usageMetadata) {
10892
12125
  output.usageMetadata = usageMetadata;
10893
- if (usageMetadata.promptTokenCount !== void 0) {
10894
- metrics.prompt_tokens = usageMetadata.promptTokenCount;
10895
- }
10896
- if (usageMetadata.candidatesTokenCount !== void 0) {
10897
- metrics.completion_tokens = usageMetadata.candidatesTokenCount;
10898
- }
10899
- if (usageMetadata.totalTokenCount !== void 0) {
10900
- metrics.tokens = usageMetadata.totalTokenCount;
10901
- }
10902
- if (usageMetadata.cachedContentTokenCount !== void 0) {
10903
- metrics.prompt_cached_tokens = usageMetadata.cachedContentTokenCount;
10904
- }
10905
- if (usageMetadata.thoughtsTokenCount !== void 0) {
10906
- metrics.completion_reasoning_tokens = usageMetadata.thoughtsTokenCount;
10907
- }
12126
+ populateUsageMetrics(metrics, usageMetadata);
10908
12127
  }
10909
12128
  if (text) {
10910
12129
  output.text = text;
@@ -10916,7 +12135,8 @@ function tryToDict(obj) {
10916
12135
  return null;
10917
12136
  }
10918
12137
  if (typeof obj === "object") {
10919
- if (typeof obj.toJSON === "function") {
12138
+ if ("toJSON" in obj && // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
12139
+ typeof obj.toJSON === "function") {
10920
12140
  return obj.toJSON();
10921
12141
  }
10922
12142
  return obj;
@@ -11080,6 +12300,7 @@ function configureNode() {
11080
12300
  isomorph_default.getEnv = (name) => process.env[name];
11081
12301
  isomorph_default.getCallerLocation = getCallerLocation;
11082
12302
  isomorph_default.newAsyncLocalStorage = () => new AsyncLocalStorage();
12303
+ isomorph_default.newTracingChannel = (nameOrChannels) => diagnostics_channel.tracingChannel(nameOrChannels);
11083
12304
  isomorph_default.processOn = (event, handler) => {
11084
12305
  process.on(event, handler);
11085
12306
  };
@@ -11113,7 +12334,7 @@ function configureNode() {
11113
12334
  import express from "express";
11114
12335
  import cors from "cors";
11115
12336
 
11116
- // ../node_modules/async/dist/async.mjs
12337
+ // ../node_modules/.pnpm/async@3.2.5/node_modules/async/dist/async.mjs
11117
12338
  function initialParams(fn) {
11118
12339
  return function(...args) {
11119
12340
  var callback = args.pop();
@@ -11181,7 +12402,7 @@ function invokeCallback(callback, error, value) {
11181
12402
  function isAsync(fn) {
11182
12403
  return fn[Symbol.toStringTag] === "AsyncFunction";
11183
12404
  }
11184
- function isAsyncGenerator(fn) {
12405
+ function isAsyncGenerator2(fn) {
11185
12406
  return fn[Symbol.toStringTag] === "AsyncGenerator";
11186
12407
  }
11187
12408
  function isAsyncIterable2(obj) {
@@ -11238,6 +12459,7 @@ function isArrayLike(value) {
11238
12459
  return value && typeof value.length === "number" && value.length >= 0 && value.length % 1 === 0;
11239
12460
  }
11240
12461
  var breakLoop = {};
12462
+ var breakLoop$1 = breakLoop;
11241
12463
  function once(fn) {
11242
12464
  function wrapper(...args) {
11243
12465
  if (fn === null) return;
@@ -11329,7 +12551,7 @@ function asyncEachOfLimit(generator, limit, iteratee, callback) {
11329
12551
  canceled = true;
11330
12552
  return;
11331
12553
  }
11332
- if (result === breakLoop || done && running <= 0) {
12554
+ if (result === breakLoop$1 || done && running <= 0) {
11333
12555
  done = true;
11334
12556
  return callback(null);
11335
12557
  }
@@ -11352,7 +12574,7 @@ var eachOfLimit$2 = (limit) => {
11352
12574
  if (!obj) {
11353
12575
  return callback(null);
11354
12576
  }
11355
- if (isAsyncGenerator(obj)) {
12577
+ if (isAsyncGenerator2(obj)) {
11356
12578
  return asyncEachOfLimit(obj, limit, iteratee, callback);
11357
12579
  }
11358
12580
  if (isAsyncIterable2(obj)) {
@@ -11372,7 +12594,7 @@ var eachOfLimit$2 = (limit) => {
11372
12594
  } else if (err === false) {
11373
12595
  done = true;
11374
12596
  canceled = true;
11375
- } else if (value === breakLoop || done && running <= 0) {
12597
+ } else if (value === breakLoop$1 || done && running <= 0) {
11376
12598
  done = true;
11377
12599
  return callback(null);
11378
12600
  } else if (!looping) {
@@ -11415,7 +12637,7 @@ function eachOfArrayLike(coll, iteratee, callback) {
11415
12637
  if (canceled === true) return;
11416
12638
  if (err) {
11417
12639
  callback(err);
11418
- } else if (++completed === length || value === breakLoop) {
12640
+ } else if (++completed === length || value === breakLoop$1) {
11419
12641
  callback(null);
11420
12642
  }
11421
12643
  }
@@ -11811,7 +13033,7 @@ function _createTester(check, getResult) {
11811
13033
  if (check(result) && !testResult) {
11812
13034
  testPassed = true;
11813
13035
  testResult = getResult(true, value);
11814
- return callback(null, breakLoop);
13036
+ return callback(null, breakLoop$1);
11815
13037
  }
11816
13038
  callback();
11817
13039
  });
@@ -12514,7 +13736,8 @@ var promptDefinitionSchema = promptContentsSchema.and(
12514
13736
  z9.object({
12515
13737
  model: z9.string(),
12516
13738
  params: ModelParams.optional(),
12517
- templateFormat: z9.enum(["mustache", "nunjucks", "none"]).optional()
13739
+ templateFormat: z9.enum(["mustache", "nunjucks", "none"]).optional(),
13740
+ environments: z9.array(z9.string()).optional()
12518
13741
  })
12519
13742
  );
12520
13743
  var promptDefinitionWithToolsSchema = promptDefinitionSchema.and(
@@ -12550,6 +13773,11 @@ var evalParametersSchema = z10.record(
12550
13773
  default: promptDefinitionWithToolsSchema.optional(),
12551
13774
  description: z10.string().optional()
12552
13775
  }),
13776
+ z10.object({
13777
+ type: z10.literal("model"),
13778
+ default: z10.string().optional(),
13779
+ description: z10.string().optional()
13780
+ }),
12553
13781
  z10.instanceof(z10.ZodType)
12554
13782
  // For Zod schemas
12555
13783
  ])
@@ -12592,6 +13820,17 @@ function validateParametersWithZod(parameters, parameterSchema) {
12592
13820
  throw new Error(`Parameter '${name}' is required`);
12593
13821
  }
12594
13822
  return [name, Prompt2.fromPromptData(name, promptData)];
13823
+ } else if ("type" in schema && schema.type === "model") {
13824
+ const model = value ?? schema.default;
13825
+ if (model === void 0) {
13826
+ throw new Error(`Parameter '${name}' is required`);
13827
+ }
13828
+ if (typeof model !== "string") {
13829
+ throw new Error(
13830
+ `Parameter '${name}' must be a string model identifier`
13831
+ );
13832
+ }
13833
+ return [name, model];
12595
13834
  } else {
12596
13835
  const schemaCasted = schema;
12597
13836
  return [name, schemaCasted.parse(value)];
@@ -12654,6 +13893,22 @@ function initExperiment(state, options = {}) {
12654
13893
  setCurrent: false
12655
13894
  });
12656
13895
  }
13896
+ async function getExperimentParametersRef(parameters) {
13897
+ if (!parameters) {
13898
+ return void 0;
13899
+ }
13900
+ const resolvedParameters = parameters instanceof Promise ? await parameters : parameters;
13901
+ if (!RemoteEvalParameters.isParameters(resolvedParameters)) {
13902
+ return void 0;
13903
+ }
13904
+ if (resolvedParameters.id === void 0) {
13905
+ return void 0;
13906
+ }
13907
+ return {
13908
+ id: resolvedParameters.id,
13909
+ version: resolvedParameters.version
13910
+ };
13911
+ }
12657
13912
  function callEvaluatorData(data) {
12658
13913
  const dataResult = typeof data === "function" ? data() : data;
12659
13914
  let baseExperiment = void 0;
@@ -12666,10 +13921,10 @@ function callEvaluatorData(data) {
12666
13921
  };
12667
13922
  }
12668
13923
  function isAsyncIterable3(value) {
12669
- return typeof value === "object" && value !== null && typeof value[Symbol.asyncIterator] === "function";
13924
+ return typeof value === "object" && value !== null && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
12670
13925
  }
12671
13926
  function isIterable(value) {
12672
- return typeof value === "object" && value !== null && typeof value[Symbol.iterator] === "function";
13927
+ return typeof value === "object" && value !== null && Symbol.iterator in value && typeof value[Symbol.iterator] === "function";
12673
13928
  }
12674
13929
  globalThis._evals = {
12675
13930
  functions: [],
@@ -12720,6 +13975,7 @@ async function Eval(name, evaluator, reporterOrOpts) {
12720
13975
  const { data, baseExperiment: defaultBaseExperiment } = callEvaluatorData(
12721
13976
  evaluator.data
12722
13977
  );
13978
+ const parameters = await getExperimentParametersRef(evaluator.parameters);
12723
13979
  const experiment = options.parent || options.noSendLogs ? null : initExperiment(evaluator.state, {
12724
13980
  ...evaluator.projectId ? { projectId: evaluator.projectId } : { project: name },
12725
13981
  experiment: evaluator.experimentName,
@@ -12732,7 +13988,8 @@ async function Eval(name, evaluator, reporterOrOpts) {
12732
13988
  baseExperimentId: evaluator.baseExperimentId,
12733
13989
  gitMetadataSettings: evaluator.gitMetadataSettings,
12734
13990
  repoInfo: evaluator.repoInfo,
12735
- dataset: Dataset2.isDataset(data) ? data : void 0
13991
+ dataset: Dataset2.isDataset(data) ? data : void 0,
13992
+ parameters
12736
13993
  });
12737
13994
  if (experiment && typeof process !== "undefined" && globalThis.BRAINTRUST_CONTEXT_MANAGER !== void 0) {
12738
13995
  await experiment._waitForId();
@@ -12787,7 +14044,7 @@ async function Eval(name, evaluator, reporterOrOpts) {
12787
14044
  if (experiment) {
12788
14045
  await experiment.flush().catch(console.error);
12789
14046
  } else if (options.parent) {
12790
- await flush().catch(console.error);
14047
+ await flush({ state: evaluator.state }).catch(console.error);
12791
14048
  }
12792
14049
  }
12793
14050
  } finally {
@@ -13105,7 +14362,7 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
13105
14362
  const names = Object.keys(scorerErrors).join(", ");
13106
14363
  const errors = failingScorersAndResults.map((item) => item.error);
13107
14364
  unhandledScores = Object.keys(scorerErrors);
13108
- console.warn(
14365
+ debugLogger.forState(evaluator.state).warn(
13109
14366
  `Found exceptions for the following scorers: ${names}`,
13110
14367
  errors
13111
14368
  );
@@ -13156,6 +14413,7 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
13156
14413
  },
13157
14414
  Math.max(evaluator.maxConcurrency ?? Number.MAX_SAFE_INTEGER, 1)
13158
14415
  );
14416
+ const queueErrors = [];
13159
14417
  const enqueuePromise = (async () => {
13160
14418
  for await (const datum of dataIterable) {
13161
14419
  if (cancelled) {
@@ -13171,7 +14429,11 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
13171
14429
  }
13172
14430
  scheduledTrials++;
13173
14431
  progressReporter.setTotal?.(evaluator.evalName, scheduledTrials);
13174
- q.push({ datum, trialIndex });
14432
+ q.pushAsync({ datum, trialIndex }).catch((e) => {
14433
+ if (queueErrors.length < 5) {
14434
+ queueErrors.push(e);
14435
+ }
14436
+ });
13175
14437
  }
13176
14438
  }
13177
14439
  })();
@@ -13219,11 +14481,17 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
13219
14481
  })();
13220
14482
  try {
13221
14483
  await Promise.race([waitForQueue, cancel()]);
14484
+ if (queueErrors.length > 0) {
14485
+ throw new AggregateError(
14486
+ queueErrors,
14487
+ `Encountered ${queueErrors.length} unhandled task errors`
14488
+ );
14489
+ }
13222
14490
  } catch (e) {
13223
14491
  q.kill();
13224
14492
  if (e instanceof InternalAbortError) {
13225
14493
  if (isomorph_default.getEnv("BRAINTRUST_VERBOSE")) {
13226
- console.warn("Evaluator cancelled:", e.message);
14494
+ debugLogger.forState(evaluator.state).warn("Evaluator cancelled:", e.message);
13227
14495
  }
13228
14496
  }
13229
14497
  throw e;
@@ -13318,7 +14586,11 @@ function reportFailures(evaluator, failingResults, { verbose, jsonl }) {
13318
14586
  }
13319
14587
  }
13320
14588
  if (!verbose && !jsonl) {
13321
- console.error(warning("Add --verbose to see full stack traces."));
14589
+ console.error(
14590
+ warning(
14591
+ "Use --debug-logging debug to see full stack traces and troubleshooting details."
14592
+ )
14593
+ );
13322
14594
  }
13323
14595
  }
13324
14596
  }
@@ -13619,6 +14891,11 @@ var staticParametersSchema = z12.record(
13619
14891
  default: PromptData.optional(),
13620
14892
  description: z12.string().optional()
13621
14893
  }),
14894
+ z12.object({
14895
+ type: z12.literal("model"),
14896
+ default: z12.string().optional(),
14897
+ description: z12.string().optional()
14898
+ }),
13622
14899
  z12.object({
13623
14900
  type: z12.literal("data"),
13624
14901
  schema: z12.record(z12.unknown()),
@@ -13671,23 +14948,6 @@ import { ValidationError } from "ajv";
13671
14948
 
13672
14949
  // src/framework2.ts
13673
14950
  import { z as z13 } from "zod/v3";
13674
-
13675
- // src/zod/utils.ts
13676
- import { zodToJsonSchema as zodToJsonSchemaV3 } from "zod-to-json-schema";
13677
- import * as z42 from "zod/v4";
13678
- function isZodV4(zodObject) {
13679
- return typeof zodObject === "object" && zodObject !== null && "_zod" in zodObject && zodObject._zod !== void 0;
13680
- }
13681
- function zodToJsonSchema(schema) {
13682
- if (isZodV4(schema)) {
13683
- return z42.toJSONSchema(schema, {
13684
- target: "draft-7"
13685
- });
13686
- }
13687
- return zodToJsonSchemaV3(schema);
13688
- }
13689
-
13690
- // src/framework2.ts
13691
14951
  var currentFilename = typeof __filename !== "undefined" ? __filename : "unknown";
13692
14952
  var ProjectBuilder = class {
13693
14953
  create(opts) {
@@ -13855,6 +15115,7 @@ var CodeFunction = class {
13855
15115
  this.description = opts.description;
13856
15116
  this.type = opts.type;
13857
15117
  this.ifExists = opts.ifExists;
15118
+ this.tags = opts.tags;
13858
15119
  this.metadata = opts.metadata;
13859
15120
  this.parameters = opts.parameters;
13860
15121
  this.returns = opts.returns;
@@ -13870,6 +15131,7 @@ var CodeFunction = class {
13870
15131
  parameters;
13871
15132
  returns;
13872
15133
  ifExists;
15134
+ tags;
13873
15135
  metadata;
13874
15136
  key() {
13875
15137
  return JSON.stringify([
@@ -13889,7 +15151,9 @@ var CodePrompt = class {
13889
15151
  id;
13890
15152
  functionType;
13891
15153
  toolFunctions;
15154
+ tags;
13892
15155
  metadata;
15156
+ environmentSlugs;
13893
15157
  constructor(project, prompt, toolFunctions, opts, functionType) {
13894
15158
  this.project = project;
13895
15159
  this.name = opts.name;
@@ -13900,7 +15164,9 @@ var CodePrompt = class {
13900
15164
  this.description = opts.description;
13901
15165
  this.id = opts.id;
13902
15166
  this.functionType = functionType;
15167
+ this.tags = opts.tags;
13903
15168
  this.metadata = opts.metadata;
15169
+ this.environmentSlugs = opts.environments;
13904
15170
  }
13905
15171
  async toFunctionDefinition(projectNameToId) {
13906
15172
  const prompt_data = {
@@ -13934,7 +15200,9 @@ var CodePrompt = class {
13934
15200
  function_type: this.functionType,
13935
15201
  prompt_data,
13936
15202
  if_exists: this.ifExists,
13937
- metadata: this.metadata
15203
+ tags: this.tags,
15204
+ metadata: this.metadata,
15205
+ environments: this.environmentSlugs && this.environmentSlugs.length > 0 ? this.environmentSlugs.map((slug) => ({ slug })) : void 0
13938
15206
  };
13939
15207
  }
13940
15208
  };
@@ -13962,6 +15230,7 @@ var PromptBuilder = class {
13962
15230
  name: opts.name,
13963
15231
  slug,
13964
15232
  prompt_data: promptData,
15233
+ tags: opts.tags,
13965
15234
  ...this.project.id !== void 0 ? { project_id: this.project.id } : {}
13966
15235
  };
13967
15236
  const prompt = new Prompt2(
@@ -14043,6 +15312,15 @@ function serializeEvalParametersToStaticParametersSchema(parameters) {
14043
15312
  description: value.description
14044
15313
  }
14045
15314
  ];
15315
+ } else if ("type" in value && value.type === "model") {
15316
+ return [
15317
+ name,
15318
+ {
15319
+ type: "model",
15320
+ default: value.default,
15321
+ description: value.description
15322
+ }
15323
+ ];
14046
15324
  } else {
14047
15325
  const schemaObj = zodToJsonSchema(value);
14048
15326
  return [
@@ -14073,6 +15351,16 @@ function serializeEvalParameterstoParametersSchema(parameters) {
14073
15351
  if (!defaultPromptData) {
14074
15352
  required.push(name);
14075
15353
  }
15354
+ } else if ("type" in value && value.type === "model") {
15355
+ properties[name] = {
15356
+ type: "string",
15357
+ "x-bt-type": "model",
15358
+ ...value.description ? { description: value.description } : {},
15359
+ ..."default" in value ? { default: value.default } : {}
15360
+ };
15361
+ if (!("default" in value)) {
15362
+ required.push(name);
15363
+ }
14076
15364
  } else {
14077
15365
  const schemaObj = zodToJsonSchema(value);
14078
15366
  properties[name] = schemaObj;