braintrust 3.3.0 → 3.4.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 (44) hide show
  1. package/README.md +52 -67
  2. package/dev/dist/index.d.mts +53 -9
  3. package/dev/dist/index.d.ts +53 -9
  4. package/dev/dist/index.js +1839 -1298
  5. package/dev/dist/index.mjs +1503 -962
  6. package/dist/auto-instrumentations/bundler/esbuild.cjs +270 -23
  7. package/dist/auto-instrumentations/bundler/esbuild.mjs +2 -2
  8. package/dist/auto-instrumentations/bundler/rollup.cjs +270 -23
  9. package/dist/auto-instrumentations/bundler/rollup.mjs +2 -2
  10. package/dist/auto-instrumentations/bundler/vite.cjs +270 -23
  11. package/dist/auto-instrumentations/bundler/vite.mjs +2 -2
  12. package/dist/auto-instrumentations/bundler/webpack.cjs +270 -23
  13. package/dist/auto-instrumentations/bundler/webpack.mjs +2 -2
  14. package/dist/auto-instrumentations/{chunk-OLOPGWTJ.mjs → chunk-D5ZPIUEL.mjs} +1 -1
  15. package/dist/auto-instrumentations/chunk-LVWWLUMN.mjs +535 -0
  16. package/dist/auto-instrumentations/hook.mjs +306 -23
  17. package/dist/auto-instrumentations/index.cjs +270 -23
  18. package/dist/auto-instrumentations/index.d.mts +5 -5
  19. package/dist/auto-instrumentations/index.d.ts +5 -5
  20. package/dist/auto-instrumentations/index.mjs +1 -1
  21. package/dist/auto-instrumentations/loader/esm-hook.mjs +7 -8
  22. package/dist/browser.d.mts +474 -47
  23. package/dist/browser.d.ts +474 -47
  24. package/dist/browser.js +2258 -2095
  25. package/dist/browser.mjs +2258 -2095
  26. package/dist/cli.js +1817 -1232
  27. package/dist/edge-light.d.mts +1 -1
  28. package/dist/edge-light.d.ts +1 -1
  29. package/dist/edge-light.js +2188 -2027
  30. package/dist/edge-light.mjs +2188 -2027
  31. package/dist/index.d.mts +474 -47
  32. package/dist/index.d.ts +474 -47
  33. package/dist/index.js +2576 -2415
  34. package/dist/index.mjs +2259 -2098
  35. package/dist/instrumentation/index.d.mts +16 -22
  36. package/dist/instrumentation/index.d.ts +16 -22
  37. package/dist/instrumentation/index.js +1558 -1068
  38. package/dist/instrumentation/index.mjs +1558 -1068
  39. package/dist/workerd.d.mts +1 -1
  40. package/dist/workerd.d.ts +1 -1
  41. package/dist/workerd.js +2188 -2027
  42. package/dist/workerd.mjs +2188 -2027
  43. package/package.json +6 -3
  44. package/dist/auto-instrumentations/chunk-KVX7OFPD.mjs +0 -288
@@ -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,26 @@ var DefaultAsyncLocalStorage = class {
21
22
  return void 0;
22
23
  }
23
24
  };
25
+ var DefaultTracingChannel = class {
26
+ hasSubscribers = false;
27
+ subscribe(_handlers) {
28
+ }
29
+ unsubscribe(_handlers) {
30
+ return false;
31
+ }
32
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
33
+ traceSync(fn, _message, thisArg, ...args) {
34
+ return fn.apply(thisArg, args);
35
+ }
36
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
37
+ tracePromise(fn, _message, thisArg, ...args) {
38
+ return Promise.resolve(fn.apply(thisArg, args));
39
+ }
40
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
+ traceCallback(fn, _position, _message, thisArg, ...args) {
42
+ return fn.apply(thisArg, args);
43
+ }
44
+ };
24
45
  var iso = {
25
46
  buildType: "unknown",
26
47
  // Will be set by configureBrowser() or configureNode()
@@ -29,6 +50,8 @@ var iso = {
29
50
  getEnv: (_name) => void 0,
30
51
  getCallerLocation: () => void 0,
31
52
  newAsyncLocalStorage: () => new DefaultAsyncLocalStorage(),
53
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
54
+ newTracingChannel: (_nameOrChannels) => new DefaultTracingChannel(),
32
55
  processOn: (_0, _1) => {
33
56
  },
34
57
  basename: (filepath) => filepath.split(/[\\/]/).pop() || filepath,
@@ -36,6 +59,111 @@ var iso = {
36
59
  };
37
60
  var isomorph_default = iso;
38
61
 
62
+ // src/debug-logger.ts
63
+ var PREFIX = "[braintrust]";
64
+ var DEBUG_LOG_LEVEL_SYMBOL = Symbol.for("braintrust-debug-log-level");
65
+ var LOG_LEVEL_PRIORITY = {
66
+ error: 0,
67
+ warn: 1,
68
+ info: 2,
69
+ debug: 3
70
+ };
71
+ var hasWarnedAboutInvalidEnvValue = false;
72
+ var debugLogStateResolver = void 0;
73
+ function warnInvalidEnvValue(value) {
74
+ if (hasWarnedAboutInvalidEnvValue) {
75
+ return;
76
+ }
77
+ hasWarnedAboutInvalidEnvValue = true;
78
+ console.warn(
79
+ PREFIX,
80
+ `Invalid BRAINTRUST_DEBUG_LOG_LEVEL value "${value}". Expected "error", "warn", "info", or "debug".`
81
+ );
82
+ }
83
+ function normalizeDebugLogLevelOption(option) {
84
+ if (option === false) {
85
+ return void 0;
86
+ }
87
+ if (option === "error" || option === "warn" || option === "info" || option === "debug") {
88
+ return option;
89
+ }
90
+ throw new Error(
91
+ `Invalid debugLogLevel value "${option}". Expected false, "error", "warn", "info", or "debug".`
92
+ );
93
+ }
94
+ function parseDebugLogLevelEnv(value) {
95
+ if (!value) {
96
+ return void 0;
97
+ }
98
+ if (value === "error" || value === "warn" || value === "info" || value === "debug") {
99
+ return value;
100
+ }
101
+ warnInvalidEnvValue(value);
102
+ return void 0;
103
+ }
104
+ function getEnvDebugLogLevel() {
105
+ return parseDebugLogLevelEnv(isomorph_default.getEnv("BRAINTRUST_DEBUG_LOG_LEVEL"));
106
+ }
107
+ function setGlobalDebugLogLevel(level) {
108
+ globalThis[DEBUG_LOG_LEVEL_SYMBOL] = level;
109
+ }
110
+ function setDebugLogStateResolver(resolver) {
111
+ debugLogStateResolver = resolver;
112
+ }
113
+ function resolveDebugLogLevel(state) {
114
+ const stateLevel = state?.getDebugLogLevel?.();
115
+ const hasStateOverride = state?.hasDebugLogLevelOverride?.() ?? false;
116
+ if (hasStateOverride) {
117
+ return stateLevel;
118
+ }
119
+ const globalLevel = (
120
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
121
+ globalThis[DEBUG_LOG_LEVEL_SYMBOL]
122
+ );
123
+ if (globalLevel !== void 0) {
124
+ return globalLevel === false ? void 0 : globalLevel;
125
+ }
126
+ return getEnvDebugLogLevel();
127
+ }
128
+ function emit(method, state, args) {
129
+ const level = resolveDebugLogLevel(state);
130
+ if (!level || LOG_LEVEL_PRIORITY[method] > LOG_LEVEL_PRIORITY[level]) {
131
+ return;
132
+ }
133
+ if (method === "info") {
134
+ console.log(PREFIX, ...args);
135
+ } else if (method === "debug") {
136
+ console.debug(PREFIX, ...args);
137
+ } else if (method === "warn") {
138
+ console.warn(PREFIX, ...args);
139
+ } else {
140
+ console.error(PREFIX, ...args);
141
+ }
142
+ }
143
+ function createDebugLogger(state) {
144
+ const resolveState = () => state ?? debugLogStateResolver?.();
145
+ return {
146
+ info(...args) {
147
+ emit("info", resolveState(), args);
148
+ },
149
+ debug(...args) {
150
+ emit("debug", resolveState(), args);
151
+ },
152
+ warn(...args) {
153
+ emit("warn", resolveState(), args);
154
+ },
155
+ error(...args) {
156
+ emit("error", resolveState(), args);
157
+ }
158
+ };
159
+ }
160
+ var debugLogger = {
161
+ ...createDebugLogger(),
162
+ forState(state) {
163
+ return createDebugLogger(state);
164
+ }
165
+ };
166
+
39
167
  // src/gitutil.ts
40
168
  import { simpleGit } from "simple-git";
41
169
  var COMMON_BASE_BRANCHES = ["main", "master", "develop"];
@@ -116,7 +244,7 @@ async function getPastNAncestors(n = 1e3, remote = void 0) {
116
244
  try {
117
245
  ancestor = await getBaseBranchAncestor(remote);
118
246
  } catch (e) {
119
- console.warn(
247
+ debugLogger.warn(
120
248
  "Skipping git metadata. This is likely because the repository has not been published to a remote yet.",
121
249
  `${e}`
122
250
  );
@@ -261,7 +389,7 @@ var Queue = class {
261
389
  enforceSizeLimit = false;
262
390
  constructor(maxSize) {
263
391
  if (maxSize < 1) {
264
- console.warn(
392
+ debugLogger.warn(
265
393
  `maxSize ${maxSize} is <1, using default ${DEFAULT_QUEUE_SIZE}`
266
394
  );
267
395
  maxSize = DEFAULT_QUEUE_SIZE;
@@ -4482,7 +4610,10 @@ var loginSchema = z8.strictObject({
4482
4610
  proxyUrl: z8.string(),
4483
4611
  loginToken: z8.string(),
4484
4612
  orgId: z8.string().nullish(),
4485
- gitMetadataSettings: GitMetadataSettings.nullish()
4613
+ gitMetadataSettings: GitMetadataSettings.nullish(),
4614
+ debugLogLevel: z8.enum(["error", "warn", "info", "debug"]).optional(),
4615
+ // Distinguishes explicit false from unset so env fallback stays disabled after deserialization.
4616
+ debugLogLevelDisabled: z8.boolean().optional()
4486
4617
  });
4487
4618
  var stateNonce = 0;
4488
4619
  var BraintrustState = class _BraintrustState {
@@ -4503,6 +4634,16 @@ var BraintrustState = class _BraintrustState {
4503
4634
  this._bgLogger = new SyncLazyValue(
4504
4635
  () => new HTTPBackgroundLogger(new LazyValue(defaultGetLogConn), loginParams)
4505
4636
  );
4637
+ if (loginParams.debugLogLevel !== void 0) {
4638
+ this.debugLogLevelConfigured = true;
4639
+ this.debugLogLevel = normalizeDebugLogLevelOption(
4640
+ loginParams.debugLogLevel
4641
+ );
4642
+ setGlobalDebugLogLevel(this.debugLogLevel ?? false);
4643
+ } else {
4644
+ this.debugLogLevel = getEnvDebugLogLevel();
4645
+ setGlobalDebugLogLevel(void 0);
4646
+ }
4506
4647
  this.resetLoginInfo();
4507
4648
  const memoryCache = new LRUCache({
4508
4649
  max: Number(isomorph_default.getEnv("BRAINTRUST_PROMPT_CACHE_MEMORY_MAX")) ?? 1 << 10
@@ -4547,6 +4688,8 @@ var BraintrustState = class _BraintrustState {
4547
4688
  proxyUrl = null;
4548
4689
  loggedIn = false;
4549
4690
  gitMetadataSettings;
4691
+ debugLogLevel;
4692
+ debugLogLevelConfigured = false;
4550
4693
  fetch = globalThis.fetch;
4551
4694
  _appConn = null;
4552
4695
  _apiConn = null;
@@ -4612,6 +4755,11 @@ var BraintrustState = class _BraintrustState {
4612
4755
  this.proxyUrl = other.proxyUrl;
4613
4756
  this.loggedIn = other.loggedIn;
4614
4757
  this.gitMetadataSettings = other.gitMetadataSettings;
4758
+ this.debugLogLevel = other.debugLogLevel;
4759
+ this.debugLogLevelConfigured = other.debugLogLevelConfigured;
4760
+ setGlobalDebugLogLevel(
4761
+ this.debugLogLevelConfigured ? this.debugLogLevel ?? false : void 0
4762
+ );
4615
4763
  this._appConn = other._appConn;
4616
4764
  this._apiConn = other._apiConn;
4617
4765
  this.loginReplaceApiConn(this.apiConn());
@@ -4636,7 +4784,9 @@ var BraintrustState = class _BraintrustState {
4636
4784
  orgName: this.orgName,
4637
4785
  apiUrl: this.apiUrl,
4638
4786
  proxyUrl: this.proxyUrl,
4639
- gitMetadataSettings: this.gitMetadataSettings
4787
+ gitMetadataSettings: this.gitMetadataSettings,
4788
+ ...this.debugLogLevel ? { debugLogLevel: this.debugLogLevel } : {},
4789
+ ...this.debugLogLevelConfigured && !this.debugLogLevel ? { debugLogLevelDisabled: true } : {}
4640
4790
  };
4641
4791
  }
4642
4792
  static deserialize(serialized, opts) {
@@ -4663,6 +4813,10 @@ var BraintrustState = class _BraintrustState {
4663
4813
  state.proxyConn().set_token(state.loginToken);
4664
4814
  }
4665
4815
  state.loggedIn = true;
4816
+ state.debugLogLevelConfigured = "debugLogLevel" in serializedParsed.data || !!serializedParsed.data.debugLogLevelDisabled;
4817
+ setGlobalDebugLogLevel(
4818
+ state.debugLogLevelConfigured ? state.debugLogLevel ?? false : void 0
4819
+ );
4666
4820
  state.loginReplaceApiConn(state.apiConn());
4667
4821
  return state;
4668
4822
  }
@@ -4675,7 +4829,22 @@ var BraintrustState = class _BraintrustState {
4675
4829
  setMaskingFunction(maskingFunction) {
4676
4830
  this.bgLogger().setMaskingFunction(maskingFunction);
4677
4831
  }
4832
+ setDebugLogLevel(option) {
4833
+ if (option === void 0) {
4834
+ return;
4835
+ }
4836
+ this.debugLogLevelConfigured = true;
4837
+ this.debugLogLevel = normalizeDebugLogLevelOption(option);
4838
+ setGlobalDebugLogLevel(this.debugLogLevel ?? false);
4839
+ }
4840
+ getDebugLogLevel() {
4841
+ return this.debugLogLevel;
4842
+ }
4843
+ hasDebugLogLevelOverride() {
4844
+ return this.debugLogLevelConfigured;
4845
+ }
4678
4846
  async login(loginParams) {
4847
+ this.setDebugLogLevel(loginParams.debugLogLevel);
4679
4848
  if (this.apiUrl && !loginParams.forceLogin) {
4680
4849
  return;
4681
4850
  }
@@ -4786,6 +4955,7 @@ function _internalSetInitialState() {
4786
4955
  _globalState = existing;
4787
4956
  }
4788
4957
  var _internalGetGlobalState = () => _globalState;
4958
+ setDebugLogStateResolver(() => _internalGetGlobalState());
4789
4959
  var FailedHTTPResponse = class extends Error {
4790
4960
  status;
4791
4961
  text;
@@ -4900,7 +5070,7 @@ var HTTPConnection = class _HTTPConnection {
4900
5070
  return await resp.json();
4901
5071
  } catch (e) {
4902
5072
  if (i < tries - 1) {
4903
- console.log(
5073
+ debugLogger.debug(
4904
5074
  `Retrying API request ${object_type} ${JSON.stringify(args)} ${e.status} ${e.text}`
4905
5075
  );
4906
5076
  continue;
@@ -5112,7 +5282,7 @@ with a Blob/ArrayBuffer, or run the program on Node.js.`
5112
5282
  try {
5113
5283
  statSync2(data);
5114
5284
  } catch (e) {
5115
- console.warn(`Failed to read file: ${e}`);
5285
+ debugLogger.warn(`Failed to read file: ${e}`);
5116
5286
  }
5117
5287
  }
5118
5288
  };
@@ -5824,7 +5994,7 @@ var HTTPBackgroundLogger = class _HTTPBackgroundLogger {
5824
5994
  this.queueDropLoggingPeriod = queueDropLoggingPeriodEnv;
5825
5995
  }
5826
5996
  if (isomorph_default.getEnv("BRAINTRUST_LOG_FLUSH_CHUNK_SIZE")) {
5827
- console.warn(
5997
+ debugLogger.warn(
5828
5998
  "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
5999
  );
5830
6000
  }
@@ -5886,7 +6056,10 @@ var HTTPBackgroundLogger = class _HTTPBackgroundLogger {
5886
6056
  const versionInfo = await conn.get_json("version");
5887
6057
  serverLimit = z8.object({ logs3_payload_max_bytes: z8.number().nullish() }).parse(versionInfo).logs3_payload_max_bytes ?? null;
5888
6058
  } catch (e) {
5889
- console.warn("Failed to fetch version info for payload limit:", e);
6059
+ debugLogger.warn(
6060
+ "Failed to fetch version info for payload limit:",
6061
+ e
6062
+ );
5890
6063
  }
5891
6064
  const validServerLimit = serverLimit !== null && serverLimit > 0 ? serverLimit : null;
5892
6065
  const canUseOverflow = validServerLimit !== null;
@@ -6030,16 +6203,16 @@ var HTTPBackgroundLogger = class _HTTPBackgroundLogger {
6030
6203
  if (isRetrying) {
6031
6204
  errmsg += ". Retrying";
6032
6205
  }
6033
- console.warn(errmsg);
6206
+ debugLogger.warn(errmsg);
6034
6207
  if (!isRetrying) {
6035
- console.warn(
6208
+ debugLogger.warn(
6036
6209
  `Failed to construct log records to flush after ${this.numTries} attempts. Dropping batch`
6037
6210
  );
6038
6211
  throw e;
6039
6212
  } else {
6040
- console.warn(e);
6213
+ debugLogger.warn(e);
6041
6214
  const sleepTimeS = BACKGROUND_LOGGER_BASE_SLEEP_TIME_S * 2 ** i;
6042
- console.info(`Sleeping for ${sleepTimeS}s`);
6215
+ debugLogger.info(`Sleeping for ${sleepTimeS}s`);
6043
6216
  await new Promise(
6044
6217
  (resolve) => setTimeout(resolve, sleepTimeS * 1e3)
6045
6218
  );
@@ -6140,15 +6313,15 @@ Error: ${errorText}`;
6140
6313
  this.logFailedPayloadsDir();
6141
6314
  }
6142
6315
  if (!isRetrying) {
6143
- console.warn(
6316
+ debugLogger.warn(
6144
6317
  `log request failed after ${this.numTries} retries. Dropping batch`
6145
6318
  );
6146
6319
  throw new Error(errMsg);
6147
6320
  } else {
6148
- console.warn(errMsg);
6321
+ debugLogger.warn(errMsg);
6149
6322
  if (isRetrying) {
6150
6323
  const sleepTimeS = BACKGROUND_LOGGER_BASE_SLEEP_TIME_S * 2 ** i;
6151
- console.info(`Sleeping for ${sleepTimeS}s`);
6324
+ debugLogger.info(`Sleeping for ${sleepTimeS}s`);
6152
6325
  await new Promise(
6153
6326
  (resolve) => setTimeout(resolve, sleepTimeS * 1e3)
6154
6327
  );
@@ -6163,7 +6336,7 @@ Error: ${errorText}`;
6163
6336
  this.queueDropLoggingState.numDropped += numItems;
6164
6337
  const timeNow = getCurrentUnixTimestamp();
6165
6338
  if (timeNow - this.queueDropLoggingState.lastLoggedTimestamp > this.queueDropLoggingPeriod) {
6166
- console.warn(
6339
+ debugLogger.warn(
6167
6340
  `Dropped ${this.queueDropLoggingState.numDropped} elements due to full queue`
6168
6341
  );
6169
6342
  if (this.failedPublishPayloadsDir) {
@@ -6195,7 +6368,7 @@ Error: ${errorText}`;
6195
6368
  await _HTTPBackgroundLogger.writePayloadToDir({ payloadDir, payload });
6196
6369
  }
6197
6370
  } catch (e) {
6198
- console.error(e);
6371
+ debugLogger.error(e);
6199
6372
  }
6200
6373
  }
6201
6374
  static async writePayloadToDir({
@@ -6203,7 +6376,7 @@ Error: ${errorText}`;
6203
6376
  payload
6204
6377
  }) {
6205
6378
  if (!(isomorph_default.pathJoin && isomorph_default.mkdir && isomorph_default.writeFile)) {
6206
- console.warn(
6379
+ debugLogger.warn(
6207
6380
  "Cannot dump payloads: filesystem-operations not supported on this platform"
6208
6381
  );
6209
6382
  return;
@@ -6216,7 +6389,7 @@ Error: ${errorText}`;
6216
6389
  await isomorph_default.mkdir(payloadDir, { recursive: true });
6217
6390
  await isomorph_default.writeFile(payloadFile, payload);
6218
6391
  } catch (e) {
6219
- console.error(
6392
+ debugLogger.error(
6220
6393
  `Failed to write failed payload to output file ${payloadFile}:
6221
6394
  `,
6222
6395
  e
@@ -6247,7 +6420,9 @@ Error: ${errorText}`;
6247
6420
  }
6248
6421
  }
6249
6422
  logFailedPayloadsDir() {
6250
- console.warn(`Logging failed payloads to ${this.failedPublishPayloadsDir}`);
6423
+ debugLogger.warn(
6424
+ `Logging failed payloads to ${this.failedPublishPayloadsDir}`
6425
+ );
6251
6426
  }
6252
6427
  // Should only be called by BraintrustState.
6253
6428
  internalReplaceApiConn(apiConn) {
@@ -6412,9 +6587,7 @@ function init(projectOrOptions, optionalOptions) {
6412
6587
  break;
6413
6588
  } catch (e) {
6414
6589
  if (args["base_experiment"] && `${"data" in e && e.data}`.includes("base experiment")) {
6415
- console.warn(
6416
- `Base experiment ${args["base_experiment"]} not found.`
6417
- );
6590
+ debugLogger.forState(state).warn(`Base experiment ${args["base_experiment"]} not found.`);
6418
6591
  delete args["base_experiment"];
6419
6592
  } else {
6420
6593
  throw e;
@@ -6556,7 +6729,11 @@ async function computeLoggerMetadata(state, {
6556
6729
  }
6557
6730
  async function login(options = {}) {
6558
6731
  const { forceLogin = false } = options || {};
6732
+ if (!_internalGetGlobalState()) {
6733
+ _internalSetInitialState();
6734
+ }
6559
6735
  const state = _internalGetGlobalState();
6736
+ state.setDebugLogLevel(options.debugLogLevel);
6560
6737
  if (state.loggedIn && !forceLogin) {
6561
6738
  let checkUpdatedParam2 = function(varname, arg, orig) {
6562
6739
  if (!isEmpty2(arg) && !isEmpty2(orig) && arg !== orig) {
@@ -6575,9 +6752,6 @@ async function login(options = {}) {
6575
6752
  checkUpdatedParam2("orgName", options.orgName, state.orgName);
6576
6753
  return state;
6577
6754
  }
6578
- if (!state) {
6579
- _internalSetInitialState();
6580
- }
6581
6755
  await state.login(options);
6582
6756
  return state;
6583
6757
  }
@@ -6989,7 +7163,14 @@ var ObjectFetcher = class {
6989
7163
  async *fetchRecordsFromApi(batchSize) {
6990
7164
  const state = await this.getState();
6991
7165
  const objectId = await this.id;
6992
- const limit = batchSize ?? DEFAULT_FETCH_BATCH_SIZE;
7166
+ const batchLimit = batchSize ?? DEFAULT_FETCH_BATCH_SIZE;
7167
+ const internalLimit = this._internal_btql?.limit;
7168
+ const limit = batchSize !== void 0 ? batchSize : internalLimit ?? batchLimit;
7169
+ const internalBtqlWithoutReservedQueryKeys = Object.fromEntries(
7170
+ Object.entries(this._internal_btql ?? {}).filter(
7171
+ ([key]) => key !== "cursor" && key !== "limit" && key !== "select" && key !== "from"
7172
+ )
7173
+ );
6993
7174
  let cursor = void 0;
6994
7175
  let iterations = 0;
6995
7176
  while (true) {
@@ -6997,7 +7178,6 @@ var ObjectFetcher = class {
6997
7178
  `btql`,
6998
7179
  {
6999
7180
  query: {
7000
- ...this._internal_btql,
7001
7181
  select: [
7002
7182
  {
7003
7183
  op: "star"
@@ -7017,7 +7197,8 @@ var ObjectFetcher = class {
7017
7197
  ]
7018
7198
  },
7019
7199
  cursor,
7020
- limit
7200
+ limit,
7201
+ ...internalBtqlWithoutReservedQueryKeys
7021
7202
  },
7022
7203
  use_columnstore: false,
7023
7204
  brainstore_realtime: true,
@@ -7290,7 +7471,7 @@ var Experiment2 = class extends ObjectFetcher {
7290
7471
  scores = results["scores"];
7291
7472
  metrics = results["metrics"];
7292
7473
  } catch (e) {
7293
- console.warn(
7474
+ debugLogger.forState(state).warn(
7294
7475
  `Failed to fetch experiment scores and metrics: ${e}
7295
7476
 
7296
7477
  View complete results in Braintrust or run experiment.summarize() again.`
@@ -7367,7 +7548,7 @@ View complete results in Braintrust or run experiment.summarize() again.`
7367
7548
  * @deprecated This function is deprecated. You can simply remove it from your code.
7368
7549
  */
7369
7550
  async close() {
7370
- console.warn(
7551
+ debugLogger.forState(this.state).warn(
7371
7552
  "close is deprecated and will be removed in a future version of braintrust. It is now a no-op and can be removed"
7372
7553
  );
7373
7554
  return this.id;
@@ -7564,8 +7745,8 @@ var SpanImpl = class _SpanImpl {
7564
7745
  ...serializableInternalData,
7565
7746
  [IS_MERGE_FIELD]: this.isMerge
7566
7747
  });
7567
- if (partialRecord.metrics?.end) {
7568
- this.loggedEndTime = partialRecord.metrics?.end;
7748
+ if (typeof partialRecord.metrics?.end === "number") {
7749
+ this.loggedEndTime = partialRecord.metrics.end;
7569
7750
  }
7570
7751
  if (this.parentObjectType === 1 /* EXPERIMENT */) {
7571
7752
  const cachedSpan = {
@@ -7800,7 +7981,7 @@ var Dataset2 = class extends ObjectFetcher {
7800
7981
  constructor(state, lazyMetadata, pinnedVersion, legacy, _internal_btql) {
7801
7982
  const isLegacyDataset = legacy ?? DEFAULT_IS_LEGACY_DATASET;
7802
7983
  if (isLegacyDataset) {
7803
- console.warn(
7984
+ debugLogger.forState(state).warn(
7804
7985
  `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
7986
  );
7806
7987
  }
@@ -8031,7 +8212,7 @@ var Dataset2 = class extends ObjectFetcher {
8031
8212
  * @deprecated This function is deprecated. You can simply remove it from your code.
8032
8213
  */
8033
8214
  async close() {
8034
- console.warn(
8215
+ debugLogger.forState(this.state).warn(
8035
8216
  "close is deprecated and will be removed in a future version of braintrust. It is now a no-op and can be removed"
8036
8217
  );
8037
8218
  return this.id;
@@ -8447,9 +8628,6 @@ var RemoteEvalParameters = class {
8447
8628
  };
8448
8629
  var TEST_API_KEY = "___TEST_API_KEY__THIS_IS_NOT_REAL___";
8449
8630
 
8450
- // src/instrumentation/core/plugin.ts
8451
- import { tracingChannel } from "dc-browser";
8452
-
8453
8631
  // src/instrumentation/core/stream-patcher.ts
8454
8632
  function isAsyncIterable(value) {
8455
8633
  return value !== null && typeof value === "object" && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
@@ -8465,7 +8643,7 @@ function patchStreamIfNeeded(stream, options) {
8465
8643
  return stream;
8466
8644
  }
8467
8645
  const originalIteratorFn = stream[Symbol.asyncIterator];
8468
- if (originalIteratorFn.__braintrust_patched) {
8646
+ if ("__braintrust_patched" in originalIteratorFn && originalIteratorFn["__braintrust_patched"]) {
8469
8647
  return stream;
8470
8648
  }
8471
8649
  try {
@@ -8506,7 +8684,10 @@ function patchStreamIfNeeded(stream, options) {
8506
8684
  completed = true;
8507
8685
  if (options.onError) {
8508
8686
  try {
8509
- options.onError(error, chunks);
8687
+ options.onError(
8688
+ error instanceof Error ? error : new Error(String(error)),
8689
+ chunks
8690
+ );
8510
8691
  } catch (handlerError) {
8511
8692
  console.error("Error in stream onError handler:", handlerError);
8512
8693
  }
@@ -8534,7 +8715,8 @@ function patchStreamIfNeeded(stream, options) {
8534
8715
  iterator.throw = async function(...args) {
8535
8716
  if (!completed) {
8536
8717
  completed = true;
8537
- const error = args[0];
8718
+ const rawError = args[0];
8719
+ const error = rawError instanceof Error ? rawError : new Error(String(rawError));
8538
8720
  if (options.onError) {
8539
8721
  try {
8540
8722
  options.onError(error, chunks);
@@ -8548,7 +8730,9 @@ function patchStreamIfNeeded(stream, options) {
8548
8730
  }
8549
8731
  return iterator;
8550
8732
  };
8551
- patchedIteratorFn.__braintrust_patched = true;
8733
+ Object.defineProperty(patchedIteratorFn, "__braintrust_patched", {
8734
+ value: true
8735
+ });
8552
8736
  stream[Symbol.asyncIterator] = patchedIteratorFn;
8553
8737
  return stream;
8554
8738
  } catch (error) {
@@ -8557,6 +8741,49 @@ function patchStreamIfNeeded(stream, options) {
8557
8741
  }
8558
8742
  }
8559
8743
 
8744
+ // src/instrumentation/core/channel-tracing-utils.ts
8745
+ function hasChannelSpanInfo(value) {
8746
+ return isObject(value) && isObject(value.span_info);
8747
+ }
8748
+ function getChannelSpanInfo(event) {
8749
+ if (isObject(event.span_info)) {
8750
+ return event.span_info;
8751
+ }
8752
+ const firstArg = event.arguments?.[0];
8753
+ if (hasChannelSpanInfo(firstArg)) {
8754
+ return firstArg.span_info;
8755
+ }
8756
+ return void 0;
8757
+ }
8758
+ function buildStartSpanArgs(config, event) {
8759
+ const spanInfo = getChannelSpanInfo(event);
8760
+ const spanAttributes = {
8761
+ type: config.type
8762
+ };
8763
+ if (isObject(spanInfo?.spanAttributes)) {
8764
+ mergeDicts(spanAttributes, spanInfo.spanAttributes);
8765
+ }
8766
+ return {
8767
+ name: typeof spanInfo?.name === "string" && spanInfo.name ? spanInfo.name : config.name,
8768
+ spanAttributes,
8769
+ spanInfoMetadata: isObject(spanInfo?.metadata) ? spanInfo.metadata : void 0
8770
+ };
8771
+ }
8772
+ function mergeInputMetadata(metadata, spanInfoMetadata) {
8773
+ if (!spanInfoMetadata) {
8774
+ return isObject(metadata) ? (
8775
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
8776
+ metadata
8777
+ ) : void 0;
8778
+ }
8779
+ const mergedMetadata = {};
8780
+ mergeDicts(mergedMetadata, spanInfoMetadata);
8781
+ if (isObject(metadata)) {
8782
+ mergeDicts(mergedMetadata, metadata);
8783
+ }
8784
+ return mergedMetadata;
8785
+ }
8786
+
8560
8787
  // src/instrumentation/core/plugin.ts
8561
8788
  var BasePlugin = class {
8562
8789
  enabled = false;
@@ -8588,23 +8815,25 @@ var BasePlugin = class {
8588
8815
  * @param handlers - Event handlers
8589
8816
  */
8590
8817
  subscribe(channelName, handlers) {
8591
- const channel = tracingChannel(channelName);
8592
- channel.subscribe(handlers);
8818
+ const channel2 = isomorph_default.newTracingChannel(channelName);
8819
+ channel2.subscribe(handlers);
8593
8820
  }
8594
8821
  /**
8595
8822
  * Subscribe to a channel for async methods (non-streaming).
8596
8823
  * Creates a span and logs input/output/metrics.
8597
8824
  */
8598
8825
  subscribeToChannel(channelName, config) {
8599
- const channel = tracingChannel(channelName);
8826
+ const channel2 = isomorph_default.newTracingChannel(channelName);
8600
8827
  const spans = /* @__PURE__ */ new WeakMap();
8601
8828
  const handlers = {
8602
8829
  start: (event) => {
8830
+ const { name, spanAttributes, spanInfoMetadata } = buildStartSpanArgs(
8831
+ config,
8832
+ event
8833
+ );
8603
8834
  const span = startSpan({
8604
- name: config.name,
8605
- spanAttributes: {
8606
- type: config.type
8607
- }
8835
+ name,
8836
+ spanAttributes
8608
8837
  });
8609
8838
  const startTime = getCurrentUnixTimestamp();
8610
8839
  spans.set(event, { span, startTime });
@@ -8612,7 +8841,7 @@ var BasePlugin = class {
8612
8841
  const { input, metadata } = config.extractInput(event.arguments);
8613
8842
  span.log({
8614
8843
  input,
8615
- metadata
8844
+ metadata: mergeInputMetadata(metadata, spanInfoMetadata)
8616
8845
  });
8617
8846
  } catch (error) {
8618
8847
  console.error(`Error extracting input for ${channelName}:`, error);
@@ -8625,10 +8854,12 @@ var BasePlugin = class {
8625
8854
  }
8626
8855
  const { span, startTime } = spanData;
8627
8856
  try {
8628
- const output = config.extractOutput(event.result);
8629
- const metrics = config.extractMetrics(event.result, startTime);
8857
+ const output = config.extractOutput(event.result, event);
8858
+ const metrics = config.extractMetrics(event.result, startTime, event);
8859
+ const metadata = config.extractMetadata?.(event.result, event);
8630
8860
  span.log({
8631
8861
  output,
8862
+ ...metadata !== void 0 ? { metadata } : {},
8632
8863
  metrics
8633
8864
  });
8634
8865
  } catch (error) {
@@ -8651,9 +8882,9 @@ var BasePlugin = class {
8651
8882
  spans.delete(event);
8652
8883
  }
8653
8884
  };
8654
- channel.subscribe(handlers);
8885
+ channel2.subscribe(handlers);
8655
8886
  this.unsubscribers.push(() => {
8656
- channel.unsubscribe(handlers);
8887
+ channel2.unsubscribe(handlers);
8657
8888
  });
8658
8889
  }
8659
8890
  /**
@@ -8661,15 +8892,17 @@ var BasePlugin = class {
8661
8892
  * Handles both streaming and non-streaming responses.
8662
8893
  */
8663
8894
  subscribeToStreamingChannel(channelName, config) {
8664
- const channel = tracingChannel(channelName);
8895
+ const channel2 = isomorph_default.newTracingChannel(channelName);
8665
8896
  const spans = /* @__PURE__ */ new WeakMap();
8666
8897
  const handlers = {
8667
8898
  start: (event) => {
8899
+ const { name, spanAttributes, spanInfoMetadata } = buildStartSpanArgs(
8900
+ config,
8901
+ event
8902
+ );
8668
8903
  const span = startSpan({
8669
- name: config.name,
8670
- spanAttributes: {
8671
- type: config.type
8672
- }
8904
+ name,
8905
+ spanAttributes
8673
8906
  });
8674
8907
  const startTime = getCurrentUnixTimestamp();
8675
8908
  spans.set(event, { span, startTime });
@@ -8677,7 +8910,7 @@ var BasePlugin = class {
8677
8910
  const { input, metadata } = config.extractInput(event.arguments);
8678
8911
  span.log({
8679
8912
  input,
8680
- metadata
8913
+ metadata: mergeInputMetadata(metadata, spanInfoMetadata)
8681
8914
  });
8682
8915
  } catch (error) {
8683
8916
  console.error(`Error extracting input for ${channelName}:`, error);
@@ -8690,24 +8923,39 @@ var BasePlugin = class {
8690
8923
  }
8691
8924
  const { span, startTime } = spanData;
8692
8925
  if (isAsyncIterable(event.result)) {
8926
+ let firstChunkTime;
8693
8927
  patchStreamIfNeeded(event.result, {
8928
+ onChunk: () => {
8929
+ if (firstChunkTime === void 0) {
8930
+ firstChunkTime = getCurrentUnixTimestamp();
8931
+ }
8932
+ },
8694
8933
  onComplete: (chunks) => {
8695
8934
  try {
8696
8935
  let output;
8697
8936
  let metrics;
8937
+ let metadata;
8698
8938
  if (config.aggregateChunks) {
8699
- const aggregated = config.aggregateChunks(chunks);
8939
+ const aggregated = config.aggregateChunks(
8940
+ chunks,
8941
+ event.result,
8942
+ event
8943
+ );
8700
8944
  output = aggregated.output;
8701
8945
  metrics = aggregated.metrics;
8946
+ metadata = aggregated.metadata;
8702
8947
  } else {
8703
- output = config.extractOutput(chunks);
8704
- metrics = config.extractMetrics(chunks, startTime);
8948
+ output = config.extractOutput(chunks, event);
8949
+ metrics = config.extractMetrics(chunks, startTime, event);
8705
8950
  }
8706
- if (!metrics.time_to_first_token && chunks.length > 0) {
8951
+ if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
8952
+ metrics.time_to_first_token = firstChunkTime - startTime;
8953
+ } else if (metrics.time_to_first_token === void 0 && chunks.length > 0) {
8707
8954
  metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8708
8955
  }
8709
8956
  span.log({
8710
8957
  output,
8958
+ ...metadata !== void 0 ? { metadata } : {},
8711
8959
  metrics
8712
8960
  });
8713
8961
  } catch (error) {
@@ -8728,10 +8976,16 @@ var BasePlugin = class {
8728
8976
  });
8729
8977
  } else {
8730
8978
  try {
8731
- const output = config.extractOutput(event.result);
8732
- const metrics = config.extractMetrics(event.result, startTime);
8979
+ const output = config.extractOutput(event.result, event);
8980
+ const metadata = config.extractMetadata ? config.extractMetadata(event.result, event) : void 0;
8981
+ const metrics = config.extractMetrics(
8982
+ event.result,
8983
+ startTime,
8984
+ event
8985
+ );
8733
8986
  span.log({
8734
8987
  output,
8988
+ ...metadata !== void 0 ? { metadata } : {},
8735
8989
  metrics
8736
8990
  });
8737
8991
  } catch (error) {
@@ -8755,9 +9009,9 @@ var BasePlugin = class {
8755
9009
  spans.delete(event);
8756
9010
  }
8757
9011
  };
8758
- channel.subscribe(handlers);
9012
+ channel2.subscribe(handlers);
8759
9013
  this.unsubscribers.push(() => {
8760
- channel.unsubscribe(handlers);
9014
+ channel2.unsubscribe(handlers);
8761
9015
  });
8762
9016
  }
8763
9017
  /**
@@ -8765,15 +9019,17 @@ var BasePlugin = class {
8765
9019
  * Used for methods like beta.chat.completions.stream() and responses.stream().
8766
9020
  */
8767
9021
  subscribeToSyncStreamChannel(channelName, config) {
8768
- const channel = tracingChannel(channelName);
9022
+ const channel2 = isomorph_default.newTracingChannel(channelName);
8769
9023
  const spans = /* @__PURE__ */ new WeakMap();
8770
9024
  const handlers = {
8771
9025
  start: (event) => {
9026
+ const { name, spanAttributes, spanInfoMetadata } = buildStartSpanArgs(
9027
+ config,
9028
+ event
9029
+ );
8772
9030
  const span = startSpan({
8773
- name: config.name,
8774
- spanAttributes: {
8775
- type: config.type
8776
- }
9031
+ name,
9032
+ spanAttributes
8777
9033
  });
8778
9034
  const startTime = getCurrentUnixTimestamp();
8779
9035
  spans.set(event, { span, startTime });
@@ -8781,7 +9037,7 @@ var BasePlugin = class {
8781
9037
  const { input, metadata } = config.extractInput(event.arguments);
8782
9038
  span.log({
8783
9039
  input,
8784
- metadata
9040
+ metadata: mergeInputMetadata(metadata, spanInfoMetadata)
8785
9041
  });
8786
9042
  } catch (error) {
8787
9043
  console.error(`Error extracting input for ${channelName}:`, error);
@@ -8865,13 +9121,335 @@ var BasePlugin = class {
8865
9121
  spans.delete(event);
8866
9122
  }
8867
9123
  };
8868
- channel.subscribe(handlers);
9124
+ channel2.subscribe(handlers);
8869
9125
  this.unsubscribers.push(() => {
8870
- channel.unsubscribe(handlers);
9126
+ channel2.unsubscribe(handlers);
8871
9127
  });
8872
9128
  }
8873
9129
  };
8874
9130
 
9131
+ // src/instrumentation/core/channel-tracing.ts
9132
+ function isSyncStreamLike(value) {
9133
+ return !!value && typeof value === "object" && typeof value.on === "function";
9134
+ }
9135
+ function hasChoices(value) {
9136
+ return !!value && typeof value === "object" && "choices" in value;
9137
+ }
9138
+ function normalizeMetadata(metadata) {
9139
+ return isObject(metadata) ? metadata : void 0;
9140
+ }
9141
+ function startSpanForEvent(config, event, channelName) {
9142
+ const { name, spanAttributes, spanInfoMetadata } = buildStartSpanArgs(
9143
+ config,
9144
+ event
9145
+ );
9146
+ const span = startSpan({
9147
+ name,
9148
+ spanAttributes
9149
+ });
9150
+ const startTime = getCurrentUnixTimestamp();
9151
+ try {
9152
+ const { input, metadata } = config.extractInput(event.arguments);
9153
+ span.log({
9154
+ input,
9155
+ metadata: mergeInputMetadata(metadata, spanInfoMetadata)
9156
+ });
9157
+ } catch (error) {
9158
+ console.error(`Error extracting input for ${channelName}:`, error);
9159
+ }
9160
+ return { span, startTime };
9161
+ }
9162
+ function logErrorAndEnd(states, event) {
9163
+ const spanData = states.get(event);
9164
+ if (!spanData) {
9165
+ return;
9166
+ }
9167
+ spanData.span.log({
9168
+ error: event.error.message
9169
+ });
9170
+ spanData.span.end();
9171
+ states.delete(event);
9172
+ }
9173
+ function traceAsyncChannel(channel2, config) {
9174
+ const tracingChannel2 = channel2.tracingChannel();
9175
+ const states = /* @__PURE__ */ new WeakMap();
9176
+ const channelName = channel2.channelName;
9177
+ const handlers = {
9178
+ start: (event) => {
9179
+ states.set(
9180
+ event,
9181
+ startSpanForEvent(
9182
+ config,
9183
+ event,
9184
+ channelName
9185
+ )
9186
+ );
9187
+ },
9188
+ asyncEnd: (event) => {
9189
+ const spanData = states.get(event);
9190
+ if (!spanData) {
9191
+ return;
9192
+ }
9193
+ const asyncEndEvent = event;
9194
+ const { span, startTime } = spanData;
9195
+ try {
9196
+ const output = config.extractOutput(
9197
+ asyncEndEvent.result,
9198
+ asyncEndEvent
9199
+ );
9200
+ const metrics = config.extractMetrics(
9201
+ asyncEndEvent.result,
9202
+ startTime,
9203
+ asyncEndEvent
9204
+ );
9205
+ const metadata = config.extractMetadata?.(
9206
+ asyncEndEvent.result,
9207
+ asyncEndEvent
9208
+ );
9209
+ span.log({
9210
+ output,
9211
+ ...normalizeMetadata(metadata) !== void 0 ? { metadata: normalizeMetadata(metadata) } : {},
9212
+ metrics
9213
+ });
9214
+ } catch (error) {
9215
+ console.error(`Error extracting output for ${channelName}:`, error);
9216
+ } finally {
9217
+ span.end();
9218
+ states.delete(event);
9219
+ }
9220
+ },
9221
+ error: (event) => {
9222
+ logErrorAndEnd(states, event);
9223
+ }
9224
+ };
9225
+ tracingChannel2.subscribe(handlers);
9226
+ return () => {
9227
+ tracingChannel2.unsubscribe(handlers);
9228
+ };
9229
+ }
9230
+ function traceStreamingChannel(channel2, config) {
9231
+ const tracingChannel2 = channel2.tracingChannel();
9232
+ const states = /* @__PURE__ */ new WeakMap();
9233
+ const channelName = channel2.channelName;
9234
+ const handlers = {
9235
+ start: (event) => {
9236
+ states.set(
9237
+ event,
9238
+ startSpanForEvent(
9239
+ config,
9240
+ event,
9241
+ channelName
9242
+ )
9243
+ );
9244
+ },
9245
+ asyncEnd: (event) => {
9246
+ const spanData = states.get(event);
9247
+ if (!spanData) {
9248
+ return;
9249
+ }
9250
+ const asyncEndEvent = event;
9251
+ const { span, startTime } = spanData;
9252
+ if (isAsyncIterable(asyncEndEvent.result)) {
9253
+ let firstChunkTime;
9254
+ patchStreamIfNeeded(asyncEndEvent.result, {
9255
+ onChunk: () => {
9256
+ if (firstChunkTime === void 0) {
9257
+ firstChunkTime = getCurrentUnixTimestamp();
9258
+ }
9259
+ },
9260
+ onComplete: (chunks) => {
9261
+ try {
9262
+ let output;
9263
+ let metrics;
9264
+ let metadata;
9265
+ if (config.aggregateChunks) {
9266
+ const aggregated = config.aggregateChunks(
9267
+ chunks,
9268
+ asyncEndEvent.result,
9269
+ asyncEndEvent,
9270
+ startTime
9271
+ );
9272
+ output = aggregated.output;
9273
+ metrics = aggregated.metrics;
9274
+ metadata = aggregated.metadata;
9275
+ } else {
9276
+ output = config.extractOutput(
9277
+ chunks,
9278
+ asyncEndEvent
9279
+ );
9280
+ metrics = config.extractMetrics(
9281
+ chunks,
9282
+ startTime,
9283
+ asyncEndEvent
9284
+ );
9285
+ }
9286
+ if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
9287
+ metrics.time_to_first_token = firstChunkTime - startTime;
9288
+ } else if (metrics.time_to_first_token === void 0 && chunks.length > 0) {
9289
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9290
+ }
9291
+ span.log({
9292
+ output,
9293
+ ...metadata !== void 0 ? { metadata } : {},
9294
+ metrics
9295
+ });
9296
+ } catch (error) {
9297
+ console.error(
9298
+ `Error extracting output for ${channelName}:`,
9299
+ error
9300
+ );
9301
+ } finally {
9302
+ span.end();
9303
+ states.delete(event);
9304
+ }
9305
+ },
9306
+ onError: (error) => {
9307
+ span.log({
9308
+ error: error.message
9309
+ });
9310
+ span.end();
9311
+ states.delete(event);
9312
+ }
9313
+ });
9314
+ return;
9315
+ }
9316
+ try {
9317
+ const output = config.extractOutput(
9318
+ asyncEndEvent.result,
9319
+ asyncEndEvent
9320
+ );
9321
+ const metrics = config.extractMetrics(
9322
+ asyncEndEvent.result,
9323
+ startTime,
9324
+ asyncEndEvent
9325
+ );
9326
+ const metadata = config.extractMetadata?.(
9327
+ asyncEndEvent.result,
9328
+ asyncEndEvent
9329
+ );
9330
+ span.log({
9331
+ output,
9332
+ ...normalizeMetadata(metadata) !== void 0 ? { metadata: normalizeMetadata(metadata) } : {},
9333
+ metrics
9334
+ });
9335
+ } catch (error) {
9336
+ console.error(`Error extracting output for ${channelName}:`, error);
9337
+ } finally {
9338
+ span.end();
9339
+ states.delete(event);
9340
+ }
9341
+ },
9342
+ error: (event) => {
9343
+ logErrorAndEnd(states, event);
9344
+ }
9345
+ };
9346
+ tracingChannel2.subscribe(handlers);
9347
+ return () => {
9348
+ tracingChannel2.unsubscribe(handlers);
9349
+ };
9350
+ }
9351
+ function traceSyncStreamChannel(channel2, config) {
9352
+ const tracingChannel2 = channel2.tracingChannel();
9353
+ const states = /* @__PURE__ */ new WeakMap();
9354
+ const channelName = channel2.channelName;
9355
+ const handlers = {
9356
+ start: (event) => {
9357
+ states.set(
9358
+ event,
9359
+ startSpanForEvent(
9360
+ config,
9361
+ event,
9362
+ channelName
9363
+ )
9364
+ );
9365
+ },
9366
+ end: (event) => {
9367
+ const spanData = states.get(event);
9368
+ if (!spanData) {
9369
+ return;
9370
+ }
9371
+ const { span, startTime } = spanData;
9372
+ const resultEvent = event;
9373
+ const stream = resultEvent.result;
9374
+ if (!isSyncStreamLike(stream)) {
9375
+ span.end();
9376
+ states.delete(event);
9377
+ return;
9378
+ }
9379
+ let first = true;
9380
+ stream.on("chunk", () => {
9381
+ if (first) {
9382
+ span.log({
9383
+ metrics: {
9384
+ time_to_first_token: getCurrentUnixTimestamp() - startTime
9385
+ }
9386
+ });
9387
+ first = false;
9388
+ }
9389
+ });
9390
+ stream.on("chatCompletion", (completion) => {
9391
+ try {
9392
+ if (hasChoices(completion)) {
9393
+ span.log({
9394
+ output: completion.choices
9395
+ });
9396
+ }
9397
+ } catch (error) {
9398
+ console.error(
9399
+ `Error extracting chatCompletion for ${channelName}:`,
9400
+ error
9401
+ );
9402
+ }
9403
+ });
9404
+ stream.on("event", (streamEvent) => {
9405
+ if (!config.extractFromEvent) {
9406
+ return;
9407
+ }
9408
+ try {
9409
+ if (first) {
9410
+ span.log({
9411
+ metrics: {
9412
+ time_to_first_token: getCurrentUnixTimestamp() - startTime
9413
+ }
9414
+ });
9415
+ first = false;
9416
+ }
9417
+ const extracted = config.extractFromEvent(streamEvent);
9418
+ if (extracted && Object.keys(extracted).length > 0) {
9419
+ span.log(extracted);
9420
+ }
9421
+ } catch (error) {
9422
+ console.error(`Error extracting event for ${channelName}:`, error);
9423
+ }
9424
+ });
9425
+ stream.on("end", () => {
9426
+ span.end();
9427
+ states.delete(event);
9428
+ });
9429
+ stream.on("error", (error) => {
9430
+ span.log({
9431
+ error: error.message
9432
+ });
9433
+ span.end();
9434
+ states.delete(event);
9435
+ });
9436
+ },
9437
+ error: (event) => {
9438
+ logErrorAndEnd(states, event);
9439
+ }
9440
+ };
9441
+ tracingChannel2.subscribe(handlers);
9442
+ return () => {
9443
+ tracingChannel2.unsubscribe(handlers);
9444
+ };
9445
+ }
9446
+ function unsubscribeAll(unsubscribers) {
9447
+ for (const unsubscribe of unsubscribers) {
9448
+ unsubscribe();
9449
+ }
9450
+ return [];
9451
+ }
9452
+
8875
9453
  // src/wrappers/attachment-utils.ts
8876
9454
  function getExtensionFromMediaType(mediaType) {
8877
9455
  const extensionMap = {
@@ -8934,72 +9512,238 @@ function processInputAttachments(input) {
8934
9512
  return input;
8935
9513
  }
8936
9514
  let attachmentIndex = 0;
8937
- const processContentPart = (part) => {
8938
- if (!part || typeof part !== "object") {
8939
- return part;
9515
+ const inferMediaTypeFromDataUrl = (value, fallback2) => {
9516
+ const mediaTypeMatch = value.match(/^data:([^;]+);/);
9517
+ return mediaTypeMatch?.[1] || fallback2;
9518
+ };
9519
+ const toAttachment = (value, mediaType, filename) => {
9520
+ const blob = convertDataToBlob(value, mediaType);
9521
+ if (!blob) {
9522
+ return null;
8940
9523
  }
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;
8950
- }
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
8959
- });
8960
- return {
8961
- ...part,
8962
- image: attachment
8963
- };
9524
+ return new Attachment({
9525
+ data: blob,
9526
+ filename,
9527
+ contentType: mediaType
9528
+ });
9529
+ };
9530
+ const processNode = (node) => {
9531
+ if (Array.isArray(node)) {
9532
+ return node.map(processNode);
9533
+ }
9534
+ if (!node || typeof node !== "object") {
9535
+ return node;
9536
+ }
9537
+ 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:")) {
9538
+ const mediaType = inferMediaTypeFromDataUrl(
9539
+ node.image_url.url,
9540
+ "image/png"
9541
+ );
9542
+ const filename = `image.${getExtensionFromMediaType(mediaType)}`;
9543
+ const attachment = toAttachment(node.image_url.url, mediaType, filename);
9544
+ if (attachment) {
9545
+ return {
9546
+ ...node,
9547
+ image_url: {
9548
+ ...node.image_url,
9549
+ url: attachment
9550
+ }
9551
+ };
8964
9552
  }
8965
9553
  }
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)}`;
9554
+ if (node.type === "file" && node.file && typeof node.file === "object" && typeof node.file.file_data === "string" && node.file.file_data.startsWith("data:")) {
9555
+ const mediaType = inferMediaTypeFromDataUrl(
9556
+ node.file.file_data,
9557
+ "application/octet-stream"
9558
+ );
9559
+ const filename = typeof node.file.filename === "string" && node.file.filename ? node.file.filename : `document.${getExtensionFromMediaType(mediaType)}`;
9560
+ const attachment = toAttachment(node.file.file_data, mediaType, filename);
9561
+ if (attachment) {
9562
+ return {
9563
+ ...node,
9564
+ file: {
9565
+ ...node.file,
9566
+ file_data: attachment
9567
+ }
9568
+ };
9569
+ }
9570
+ }
9571
+ if (node.type === "image" && node.image) {
9572
+ let mediaType = "image/png";
9573
+ if (typeof node.image === "string" && node.image.startsWith("data:")) {
9574
+ mediaType = inferMediaTypeFromDataUrl(node.image, mediaType);
9575
+ } else if (node.mediaType) {
9576
+ mediaType = node.mediaType;
9577
+ }
9578
+ const filename = `input_image_${attachmentIndex}.${getExtensionFromMediaType(mediaType)}`;
9579
+ const attachment = toAttachment(node.image, mediaType, filename);
9580
+ if (attachment) {
8971
9581
  attachmentIndex++;
8972
- const attachment = new Attachment({
8973
- data: blob,
8974
- filename,
8975
- contentType: mediaType
8976
- });
8977
9582
  return {
8978
- ...part,
8979
- data: attachment
9583
+ ...node,
9584
+ image: attachment
8980
9585
  };
8981
9586
  }
8982
9587
  }
8983
- return part;
8984
- };
8985
- const processMessage = (message) => {
8986
- if (!message || typeof message !== "object") {
8987
- return message;
9588
+ if (node.type === "file" && node.data) {
9589
+ const mediaType = node.mediaType || "application/octet-stream";
9590
+ const filename = node.filename || `input_file_${attachmentIndex}.${getExtensionFromMediaType(mediaType)}`;
9591
+ const attachment = toAttachment(node.data, mediaType, filename);
9592
+ if (attachment) {
9593
+ attachmentIndex++;
9594
+ return {
9595
+ ...node,
9596
+ data: attachment
9597
+ };
9598
+ }
8988
9599
  }
8989
- if (Array.isArray(message.content)) {
8990
- return {
8991
- ...message,
8992
- content: message.content.map(processContentPart)
8993
- };
9600
+ const processed = {};
9601
+ for (const [key, value] of Object.entries(node)) {
9602
+ processed[key] = processNode(value);
8994
9603
  }
8995
- return message;
9604
+ return processed;
8996
9605
  };
8997
9606
  if (Array.isArray(input)) {
8998
- return input.map(processMessage);
8999
- } else if (typeof input === "object" && input.content) {
9000
- return processMessage(input);
9607
+ return input.map(processNode);
9001
9608
  }
9002
- return input;
9609
+ return processNode(input);
9610
+ }
9611
+
9612
+ // src/instrumentation/core/channel-definitions.ts
9613
+ function channel(spec) {
9614
+ return spec;
9615
+ }
9616
+ function defineChannels(pkg, channels) {
9617
+ return Object.fromEntries(
9618
+ Object.entries(channels).map(([key, spec]) => {
9619
+ const fullChannelName = `orchestrion:${pkg}:${spec.channelName}`;
9620
+ if (spec.kind === "async") {
9621
+ const asyncSpec = spec;
9622
+ const tracingChannel3 = () => isomorph_default.newTracingChannel(
9623
+ fullChannelName
9624
+ );
9625
+ return [
9626
+ key,
9627
+ {
9628
+ ...asyncSpec,
9629
+ tracingChannel: tracingChannel3,
9630
+ tracePromise: (fn, context) => tracingChannel3().tracePromise(
9631
+ fn,
9632
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
9633
+ context
9634
+ )
9635
+ }
9636
+ ];
9637
+ }
9638
+ const syncSpec = spec;
9639
+ const tracingChannel2 = () => isomorph_default.newTracingChannel(
9640
+ fullChannelName
9641
+ );
9642
+ return [
9643
+ key,
9644
+ {
9645
+ ...syncSpec,
9646
+ tracingChannel: tracingChannel2,
9647
+ traceSync: (fn, context) => tracingChannel2().traceSync(
9648
+ fn,
9649
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
9650
+ context
9651
+ )
9652
+ }
9653
+ ];
9654
+ })
9655
+ );
9656
+ }
9657
+
9658
+ // src/instrumentation/plugins/openai-channels.ts
9659
+ var openAIChannels = defineChannels("openai", {
9660
+ chatCompletionsCreate: channel({
9661
+ channelName: "chat.completions.create",
9662
+ kind: "async"
9663
+ }),
9664
+ embeddingsCreate: channel({
9665
+ channelName: "embeddings.create",
9666
+ kind: "async"
9667
+ }),
9668
+ betaChatCompletionsParse: channel({
9669
+ channelName: "beta.chat.completions.parse",
9670
+ kind: "async"
9671
+ }),
9672
+ betaChatCompletionsStream: channel({
9673
+ channelName: "beta.chat.completions.stream",
9674
+ kind: "sync-stream"
9675
+ }),
9676
+ moderationsCreate: channel({
9677
+ channelName: "moderations.create",
9678
+ kind: "async"
9679
+ }),
9680
+ responsesCreate: channel({
9681
+ channelName: "responses.create",
9682
+ kind: "async"
9683
+ }),
9684
+ responsesStream: channel({
9685
+ channelName: "responses.stream",
9686
+ kind: "sync-stream"
9687
+ }),
9688
+ responsesParse: channel({
9689
+ channelName: "responses.parse",
9690
+ kind: "async"
9691
+ })
9692
+ });
9693
+
9694
+ // src/openai-utils.ts
9695
+ var BRAINTRUST_CACHED_STREAM_METRIC = "__braintrust_cached_metric";
9696
+ var LEGACY_CACHED_HEADER = "x-cached";
9697
+ var X_CACHED_HEADER = "x-bt-cached";
9698
+ var TOKEN_NAME_MAP = {
9699
+ input_tokens: "prompt_tokens",
9700
+ output_tokens: "completion_tokens",
9701
+ total_tokens: "tokens"
9702
+ };
9703
+ var TOKEN_PREFIX_MAP = {
9704
+ input: "prompt",
9705
+ output: "completion"
9706
+ };
9707
+ function parseMetricsFromUsage(usage) {
9708
+ if (!usage) {
9709
+ return {};
9710
+ }
9711
+ const metrics = {};
9712
+ for (const [oaiName, value] of Object.entries(usage)) {
9713
+ if (typeof value === "number") {
9714
+ const metricName = TOKEN_NAME_MAP[oaiName] || oaiName;
9715
+ metrics[metricName] = value;
9716
+ continue;
9717
+ }
9718
+ if (!oaiName.endsWith("_tokens_details") || !isObject(value)) {
9719
+ continue;
9720
+ }
9721
+ const rawPrefix = oaiName.slice(0, -"_tokens_details".length);
9722
+ const prefix = TOKEN_PREFIX_MAP[rawPrefix] || rawPrefix;
9723
+ for (const [key, nestedValue] of Object.entries(value)) {
9724
+ if (typeof nestedValue !== "number") {
9725
+ continue;
9726
+ }
9727
+ metrics[`${prefix}_${key}`] = nestedValue;
9728
+ }
9729
+ }
9730
+ return metrics;
9731
+ }
9732
+ function parseCachedHeader(value) {
9733
+ if (!value) {
9734
+ return void 0;
9735
+ }
9736
+ return ["true", "hit"].includes(value.toLowerCase()) ? 1 : 0;
9737
+ }
9738
+ function getCachedMetricFromHeaders(headers) {
9739
+ if (!headers || typeof headers.get !== "function") {
9740
+ return void 0;
9741
+ }
9742
+ const cachedHeader = headers.get(X_CACHED_HEADER);
9743
+ if (cachedHeader) {
9744
+ return parseCachedHeader(cachedHeader);
9745
+ }
9746
+ return parseCachedHeader(headers.get(LEGACY_CACHED_HEADER));
9003
9747
  }
9004
9748
 
9005
9749
  // src/instrumentation/plugins/openai-plugin.ts
@@ -9008,13 +9752,11 @@ var OpenAIPlugin = class extends BasePlugin {
9008
9752
  super();
9009
9753
  }
9010
9754
  onEnable() {
9011
- this.subscribeToStreamingChannel(
9012
- "orchestrion:openai:chat.completions.create",
9013
- {
9755
+ this.unsubscribers.push(
9756
+ traceStreamingChannel(openAIChannels.chatCompletionsCreate, {
9014
9757
  name: "Chat Completion",
9015
9758
  type: "llm" /* LLM */,
9016
- extractInput: (args) => {
9017
- const params = args[0] || {};
9759
+ extractInput: ([params]) => {
9018
9760
  const { messages, ...metadata } = params;
9019
9761
  return {
9020
9762
  input: processInputAttachments(messages),
@@ -9024,41 +9766,49 @@ var OpenAIPlugin = class extends BasePlugin {
9024
9766
  extractOutput: (result) => {
9025
9767
  return result?.choices;
9026
9768
  },
9027
- extractMetrics: (result, startTime) => {
9028
- const metrics = parseMetricsFromUsage(result?.usage);
9769
+ extractMetrics: (result, startTime, endEvent) => {
9770
+ const metrics = withCachedMetric(
9771
+ parseMetricsFromUsage(result?.usage),
9772
+ result,
9773
+ endEvent
9774
+ );
9029
9775
  if (startTime) {
9030
9776
  metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9031
9777
  }
9032
9778
  return metrics;
9033
9779
  },
9034
9780
  aggregateChunks: aggregateChatCompletionChunks
9035
- }
9781
+ })
9036
9782
  );
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
- {
9783
+ this.unsubscribers.push(
9784
+ traceAsyncChannel(openAIChannels.embeddingsCreate, {
9785
+ name: "Embedding",
9786
+ type: "llm" /* LLM */,
9787
+ extractInput: ([params]) => {
9788
+ const { input, ...metadata } = params;
9789
+ return {
9790
+ input,
9791
+ metadata: { ...metadata, provider: "openai" }
9792
+ };
9793
+ },
9794
+ extractOutput: (result) => {
9795
+ const embedding = result?.data?.[0]?.embedding;
9796
+ return Array.isArray(embedding) ? { embedding_length: embedding.length } : void 0;
9797
+ },
9798
+ extractMetrics: (result, _startTime, endEvent) => {
9799
+ return withCachedMetric(
9800
+ parseMetricsFromUsage(result?.usage),
9801
+ result,
9802
+ endEvent
9803
+ );
9804
+ }
9805
+ })
9806
+ );
9807
+ this.unsubscribers.push(
9808
+ traceStreamingChannel(openAIChannels.betaChatCompletionsParse, {
9058
9809
  name: "Chat Completion",
9059
9810
  type: "llm" /* LLM */,
9060
- extractInput: (args) => {
9061
- const params = args[0] || {};
9811
+ extractInput: ([params]) => {
9062
9812
  const { messages, ...metadata } = params;
9063
9813
  return {
9064
9814
  input: processInputAttachments(messages),
@@ -9068,164 +9818,196 @@ var OpenAIPlugin = class extends BasePlugin {
9068
9818
  extractOutput: (result) => {
9069
9819
  return result?.choices;
9070
9820
  },
9071
- extractMetrics: (result, startTime) => {
9072
- const metrics = parseMetricsFromUsage(result?.usage);
9821
+ extractMetrics: (result, startTime, endEvent) => {
9822
+ const metrics = withCachedMetric(
9823
+ parseMetricsFromUsage(result?.usage),
9824
+ result,
9825
+ endEvent
9826
+ );
9073
9827
  if (startTime) {
9074
9828
  metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9075
9829
  }
9076
9830
  return metrics;
9077
9831
  },
9078
9832
  aggregateChunks: aggregateChatCompletionChunks
9079
- }
9833
+ })
9080
9834
  );
9081
- this.subscribeToSyncStreamChannel(
9082
- "orchestrion:openai:beta.chat.completions.stream",
9083
- {
9835
+ this.unsubscribers.push(
9836
+ traceSyncStreamChannel(openAIChannels.betaChatCompletionsStream, {
9084
9837
  name: "Chat Completion",
9085
9838
  type: "llm" /* LLM */,
9086
- extractInput: (args) => {
9087
- const params = args[0] || {};
9839
+ extractInput: ([params]) => {
9088
9840
  const { messages, ...metadata } = params;
9089
9841
  return {
9090
9842
  input: processInputAttachments(messages),
9091
9843
  metadata: { ...metadata, provider: "openai" }
9092
9844
  };
9093
9845
  }
9094
- }
9846
+ })
9095
9847
  );
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 {};
9848
+ this.unsubscribers.push(
9849
+ traceAsyncChannel(openAIChannels.moderationsCreate, {
9850
+ name: "Moderation",
9851
+ type: "llm" /* LLM */,
9852
+ extractInput: ([params]) => {
9853
+ const { input, ...metadata } = params;
9854
+ return {
9855
+ input,
9856
+ metadata: { ...metadata, provider: "openai" }
9857
+ };
9858
+ },
9859
+ extractOutput: (result) => {
9860
+ return result?.results;
9861
+ },
9862
+ extractMetrics: (result, _startTime, endEvent) => {
9863
+ return withCachedMetric(
9864
+ parseMetricsFromUsage(result?.usage),
9865
+ result,
9866
+ endEvent
9867
+ );
9150
9868
  }
9151
- const response = event.response;
9152
- if (event.type === "response.completed") {
9869
+ })
9870
+ );
9871
+ this.unsubscribers.push(
9872
+ traceStreamingChannel(openAIChannels.responsesCreate, {
9873
+ name: "openai.responses.create",
9874
+ type: "llm" /* LLM */,
9875
+ extractInput: ([params]) => {
9876
+ const { input, ...metadata } = params;
9877
+ return {
9878
+ input: processInputAttachments(input),
9879
+ metadata: { ...metadata, provider: "openai" }
9880
+ };
9881
+ },
9882
+ extractOutput: (result) => {
9883
+ return processImagesInOutput(result?.output);
9884
+ },
9885
+ extractMetadata: (result) => {
9886
+ if (!result) {
9887
+ return void 0;
9888
+ }
9889
+ const { output: _output, usage: _usage, ...metadata } = result;
9890
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
9891
+ },
9892
+ extractMetrics: (result, startTime, endEvent) => {
9893
+ const metrics = withCachedMetric(
9894
+ parseMetricsFromUsage(result?.usage),
9895
+ result,
9896
+ endEvent
9897
+ );
9898
+ if (startTime) {
9899
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9900
+ }
9901
+ return metrics;
9902
+ },
9903
+ aggregateChunks: aggregateResponseStreamEvents
9904
+ })
9905
+ );
9906
+ this.unsubscribers.push(
9907
+ traceSyncStreamChannel(openAIChannels.responsesStream, {
9908
+ name: "openai.responses.create",
9909
+ type: "llm" /* LLM */,
9910
+ extractInput: ([params]) => {
9911
+ const { input, ...metadata } = params;
9912
+ return {
9913
+ input: processInputAttachments(input),
9914
+ metadata: { ...metadata, provider: "openai" }
9915
+ };
9916
+ },
9917
+ extractFromEvent: (event) => {
9918
+ if (event.type !== "response.completed" || !event.response) {
9919
+ return {};
9920
+ }
9921
+ const response = event.response;
9153
9922
  const data = {};
9154
- if (response?.output !== void 0) {
9923
+ if (response.output !== void 0) {
9155
9924
  data.output = processImagesInOutput(response.output);
9156
9925
  }
9157
- if (response) {
9158
- const { usage: _usage, output: _output, ...metadata } = response;
9159
- if (Object.keys(metadata).length > 0) {
9160
- data.metadata = metadata;
9161
- }
9926
+ const { usage: _usage, output: _output, ...metadata } = response;
9927
+ if (Object.keys(metadata).length > 0) {
9928
+ data.metadata = metadata;
9162
9929
  }
9163
- data.metrics = parseMetricsFromUsage(response?.usage);
9930
+ data.metrics = parseMetricsFromUsage(response.usage);
9164
9931
  return data;
9165
9932
  }
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
- });
9933
+ })
9934
+ );
9935
+ this.unsubscribers.push(
9936
+ traceStreamingChannel(openAIChannels.responsesParse, {
9937
+ name: "openai.responses.parse",
9938
+ type: "llm" /* LLM */,
9939
+ extractInput: ([params]) => {
9940
+ const { input, ...metadata } = params;
9941
+ return {
9942
+ input: processInputAttachments(input),
9943
+ metadata: { ...metadata, provider: "openai" }
9944
+ };
9945
+ },
9946
+ extractOutput: (result) => {
9947
+ return processImagesInOutput(result?.output);
9948
+ },
9949
+ extractMetadata: (result) => {
9950
+ if (!result) {
9951
+ return void 0;
9952
+ }
9953
+ const { output: _output, usage: _usage, ...metadata } = result;
9954
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
9955
+ },
9956
+ extractMetrics: (result, startTime, endEvent) => {
9957
+ const metrics = withCachedMetric(
9958
+ parseMetricsFromUsage(result?.usage),
9959
+ result,
9960
+ endEvent
9961
+ );
9962
+ if (startTime) {
9963
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9964
+ }
9965
+ return metrics;
9966
+ },
9967
+ aggregateChunks: aggregateResponseStreamEvents
9968
+ })
9969
+ );
9191
9970
  }
9192
9971
  onDisable() {
9972
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
9193
9973
  }
9194
9974
  };
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 {};
9975
+ function getCachedMetricFromEndEvent(endEvent) {
9976
+ if (!isObject(endEvent)) {
9977
+ return void 0;
9207
9978
  }
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
- }
9979
+ const response = endEvent.response;
9980
+ if (!isObject(response)) {
9981
+ return void 0;
9227
9982
  }
9228
- return metrics;
9983
+ const headers = response.headers;
9984
+ if (!headers || typeof headers.get !== "function") {
9985
+ return void 0;
9986
+ }
9987
+ return getCachedMetricFromHeaders(headers);
9988
+ }
9989
+ function withCachedMetric(metrics, result, endEvent) {
9990
+ if (metrics.cached !== void 0) {
9991
+ return metrics;
9992
+ }
9993
+ const cachedFromEvent = getCachedMetricFromEndEvent(endEvent);
9994
+ if (cachedFromEvent !== void 0) {
9995
+ return {
9996
+ ...metrics,
9997
+ cached: cachedFromEvent
9998
+ };
9999
+ }
10000
+ if (!isObject(result)) {
10001
+ return metrics;
10002
+ }
10003
+ const cached = result[BRAINTRUST_CACHED_STREAM_METRIC];
10004
+ if (typeof cached !== "number") {
10005
+ return metrics;
10006
+ }
10007
+ return {
10008
+ ...metrics,
10009
+ cached
10010
+ };
9229
10011
  }
9230
10012
  function processImagesInOutput(output) {
9231
10013
  if (Array.isArray(output)) {
@@ -9256,7 +10038,7 @@ function processImagesInOutput(output) {
9256
10038
  }
9257
10039
  return output;
9258
10040
  }
9259
- function aggregateChatCompletionChunks(chunks) {
10041
+ function aggregateChatCompletionChunks(chunks, streamResult, endEvent) {
9260
10042
  let role = void 0;
9261
10043
  let content = void 0;
9262
10044
  let tool_calls = void 0;
@@ -9298,6 +10080,7 @@ function aggregateChatCompletionChunks(chunks) {
9298
10080
  }
9299
10081
  }
9300
10082
  }
10083
+ metrics = withCachedMetric(metrics, streamResult, endEvent);
9301
10084
  return {
9302
10085
  metrics,
9303
10086
  output: [
@@ -9314,9 +10097,33 @@ function aggregateChatCompletionChunks(chunks) {
9314
10097
  ]
9315
10098
  };
9316
10099
  }
9317
-
9318
- // src/instrumentation/plugins/anthropic-plugin.ts
9319
- import { tracingChannel as tracingChannel2 } from "dc-browser";
10100
+ function aggregateResponseStreamEvents(chunks, _streamResult, endEvent) {
10101
+ let output = void 0;
10102
+ let metrics = {};
10103
+ let metadata = void 0;
10104
+ for (const chunk of chunks) {
10105
+ if (!chunk || !chunk.type || !chunk.response) {
10106
+ continue;
10107
+ }
10108
+ if (chunk.type !== "response.completed") {
10109
+ continue;
10110
+ }
10111
+ const response = chunk.response;
10112
+ if (response?.output !== void 0) {
10113
+ output = processImagesInOutput(response.output);
10114
+ }
10115
+ const { usage: _usage, output: _output, ...rest } = response || {};
10116
+ if (Object.keys(rest).length > 0) {
10117
+ metadata = rest;
10118
+ }
10119
+ metrics = parseMetricsFromUsage(response?.usage);
10120
+ }
10121
+ return {
10122
+ output,
10123
+ metrics: withCachedMetric(metrics, void 0, endEvent),
10124
+ ...metadata !== void 0 ? { metadata } : {}
10125
+ };
10126
+ }
9320
10127
 
9321
10128
  // src/wrappers/anthropic-tokens-util.ts
9322
10129
  function finalizeAnthropicTokens(metrics) {
@@ -9338,20 +10145,28 @@ function extractAnthropicCacheTokens(cacheReadTokens = 0, cacheCreationTokens =
9338
10145
  return cacheTokens;
9339
10146
  }
9340
10147
 
10148
+ // src/instrumentation/plugins/anthropic-channels.ts
10149
+ var anthropicChannels = defineChannels("@anthropic-ai/sdk", {
10150
+ messagesCreate: channel({
10151
+ channelName: "messages.create",
10152
+ kind: "async"
10153
+ }),
10154
+ betaMessagesCreate: channel({
10155
+ channelName: "beta.messages.create",
10156
+ kind: "async"
10157
+ })
10158
+ });
10159
+
9341
10160
  // src/instrumentation/plugins/anthropic-plugin.ts
9342
10161
  var AnthropicPlugin = class extends BasePlugin {
9343
- unsubscribers = [];
9344
10162
  onEnable() {
9345
10163
  this.subscribeToAnthropicChannels();
9346
10164
  }
9347
10165
  onDisable() {
9348
- for (const unsubscribe of this.unsubscribers) {
9349
- unsubscribe();
9350
- }
9351
- this.unsubscribers = [];
10166
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
9352
10167
  }
9353
10168
  subscribeToAnthropicChannels() {
9354
- this.subscribeToStreamingChannel("orchestrion:anthropic:messages.create", {
10169
+ const anthropicConfig = {
9355
10170
  name: "anthropic.messages.create",
9356
10171
  type: "llm" /* LLM */,
9357
10172
  extractInput: (args) => {
@@ -9363,190 +10178,42 @@ var AnthropicPlugin = class extends BasePlugin {
9363
10178
  metadata: { ...metadata, provider: "anthropic" }
9364
10179
  };
9365
10180
  },
9366
- extractOutput: (result) => {
9367
- return result ? { role: result.role, content: result.content } : null;
10181
+ extractOutput: (message) => {
10182
+ return message ? { role: message.role, content: message.content } : null;
9368
10183
  },
9369
- extractMetrics: (result, startTime) => {
9370
- const metrics = parseMetricsFromUsage2(result?.usage);
10184
+ extractMetrics: (message, startTime) => {
10185
+ const metrics = parseMetricsFromUsage2(message?.usage);
9371
10186
  if (startTime) {
9372
10187
  metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9373
10188
  }
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
- }
9464
- },
9465
- asyncEnd: (event) => {
9466
- const spanData = spans.get(event);
9467
- if (!spanData) {
9468
- return;
9469
- }
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);
9530
- }
9531
- }
10189
+ const finalized = finalizeAnthropicTokens(metrics);
10190
+ return Object.fromEntries(
10191
+ Object.entries(finalized).filter(
10192
+ (entry) => entry[1] !== void 0
10193
+ )
10194
+ );
9532
10195
  },
9533
- error: (event) => {
9534
- const spanData = spans.get(event);
9535
- if (!spanData) {
9536
- return;
10196
+ extractMetadata: (message) => {
10197
+ const metadata = {};
10198
+ const metas = ["stop_reason", "stop_sequence"];
10199
+ for (const m of metas) {
10200
+ if (message?.[m] !== void 0) {
10201
+ metadata[m] = message[m];
10202
+ }
9537
10203
  }
9538
- const { span } = spanData;
9539
- span.log({
9540
- error: event.error.message
9541
- });
9542
- span.end();
9543
- spans.delete(event);
9544
- }
10204
+ return metadata;
10205
+ },
10206
+ aggregateChunks: (chunks) => aggregateAnthropicStreamChunks(chunks)
9545
10207
  };
9546
- channel.subscribe(handlers);
9547
- this.unsubscribers.push(() => {
9548
- channel.unsubscribe(handlers);
9549
- });
10208
+ this.unsubscribers.push(
10209
+ traceStreamingChannel(anthropicChannels.messagesCreate, anthropicConfig)
10210
+ );
10211
+ this.unsubscribers.push(
10212
+ traceStreamingChannel(anthropicChannels.betaMessagesCreate, {
10213
+ ...anthropicConfig,
10214
+ name: "anthropic.beta.messages.create"
10215
+ })
10216
+ );
9550
10217
  }
9551
10218
  };
9552
10219
  function parseMetricsFromUsage2(usage) {
@@ -9570,29 +10237,29 @@ function aggregateAnthropicStreamChunks(chunks) {
9570
10237
  const deltas = [];
9571
10238
  let metrics = {};
9572
10239
  let metadata = {};
9573
- for (const chunk of chunks) {
9574
- switch (chunk?.type) {
10240
+ for (const event of chunks) {
10241
+ switch (event?.type) {
9575
10242
  case "message_start":
9576
- if (chunk.message?.usage) {
9577
- const initialMetrics = parseMetricsFromUsage2(chunk.message.usage);
10243
+ if (event.message?.usage) {
10244
+ const initialMetrics = parseMetricsFromUsage2(event.message.usage);
9578
10245
  metrics = { ...metrics, ...initialMetrics };
9579
10246
  }
9580
10247
  break;
9581
10248
  case "content_block_delta":
9582
- if (chunk.delta?.type === "text_delta") {
9583
- const text = chunk.delta?.text;
10249
+ if (event.delta?.type === "text_delta") {
10250
+ const text = event.delta.text;
9584
10251
  if (text) {
9585
10252
  deltas.push(text);
9586
10253
  }
9587
10254
  }
9588
10255
  break;
9589
10256
  case "message_delta":
9590
- if (chunk.usage) {
9591
- const finalMetrics = parseMetricsFromUsage2(chunk.usage);
10257
+ if (event.usage) {
10258
+ const finalMetrics = parseMetricsFromUsage2(event.usage);
9592
10259
  metrics = { ...metrics, ...finalMetrics };
9593
10260
  }
9594
- if (chunk.delta) {
9595
- metadata = { ...metadata, ...chunk.delta };
10261
+ if (event.delta) {
10262
+ metadata = { ...metadata, ...event.delta };
9596
10263
  }
9597
10264
  break;
9598
10265
  }
@@ -9600,7 +10267,9 @@ function aggregateAnthropicStreamChunks(chunks) {
9600
10267
  const output = deltas.join("");
9601
10268
  const finalized = finalizeAnthropicTokens(metrics);
9602
10269
  const filteredMetrics = Object.fromEntries(
9603
- Object.entries(finalized).filter(([, v]) => v !== void 0)
10270
+ Object.entries(finalized).filter(
10271
+ (entry) => entry[1] !== void 0
10272
+ )
9604
10273
  );
9605
10274
  return {
9606
10275
  output,
@@ -9608,6 +10277,9 @@ function aggregateAnthropicStreamChunks(chunks) {
9608
10277
  metadata
9609
10278
  };
9610
10279
  }
10280
+ function isAnthropicBase64ContentBlock(input) {
10281
+ return (input.type === "image" || input.type === "document") && isObject(input.source) && input.source.type === "base64";
10282
+ }
9611
10283
  function convertBase64ToAttachment(source, contentType) {
9612
10284
  const mediaType = typeof source.media_type === "string" ? source.media_type : "image/png";
9613
10285
  const base64Data = source.data;
@@ -9631,14 +10303,14 @@ function convertBase64ToAttachment(source, contentType) {
9631
10303
  data: attachment
9632
10304
  };
9633
10305
  }
9634
- return source;
10306
+ return { ...source };
9635
10307
  }
9636
10308
  function processAttachmentsInInput(input) {
9637
10309
  if (Array.isArray(input)) {
9638
10310
  return input.map(processAttachmentsInInput);
9639
10311
  }
9640
10312
  if (isObject(input)) {
9641
- if ((input.type === "image" || input.type === "document") && isObject(input.source) && input.source.type === "base64") {
10313
+ if (isAnthropicBase64ContentBlock(input)) {
9642
10314
  return {
9643
10315
  ...input,
9644
10316
  source: convertBase64ToAttachment(input.source, input.type)
@@ -9669,8 +10341,35 @@ function filterFrom(obj, fieldsToRemove) {
9669
10341
  return result;
9670
10342
  }
9671
10343
 
10344
+ // src/instrumentation/plugins/ai-sdk-channels.ts
10345
+ var aiSDKChannels = defineChannels("ai", {
10346
+ generateText: channel({
10347
+ channelName: "generateText",
10348
+ kind: "async"
10349
+ }),
10350
+ streamText: channel({
10351
+ channelName: "streamText",
10352
+ kind: "async"
10353
+ }),
10354
+ generateObject: channel({
10355
+ channelName: "generateObject",
10356
+ kind: "async"
10357
+ }),
10358
+ streamObject: channel({
10359
+ channelName: "streamObject",
10360
+ kind: "async"
10361
+ }),
10362
+ agentGenerate: channel({
10363
+ channelName: "Agent.generate",
10364
+ kind: "async"
10365
+ }),
10366
+ agentStream: channel({
10367
+ channelName: "Agent.stream",
10368
+ kind: "async"
10369
+ })
10370
+ });
10371
+
9672
10372
  // src/instrumentation/plugins/ai-sdk-plugin.ts
9673
- import { tracingChannel as tracingChannel3 } from "dc-browser";
9674
10373
  var DEFAULT_DENY_OUTPUT_PATHS = [
9675
10374
  // v3
9676
10375
  "roundtrips[].request.body",
@@ -9686,7 +10385,6 @@ var DEFAULT_DENY_OUTPUT_PATHS = [
9686
10385
  "steps[].response.headers"
9687
10386
  ];
9688
10387
  var AISDKPlugin = class extends BasePlugin {
9689
- unsubscribers = [];
9690
10388
  config;
9691
10389
  constructor(config = {}) {
9692
10390
  super();
@@ -9696,249 +10394,148 @@ var AISDKPlugin = class extends BasePlugin {
9696
10394
  this.subscribeToAISDK();
9697
10395
  }
9698
10396
  onDisable() {
9699
- for (const unsubscribe of this.unsubscribers) {
9700
- unsubscribe();
9701
- }
9702
- this.unsubscribers = [];
10397
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
9703
10398
  }
9704
10399
  subscribeToAISDK() {
9705
10400
  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
- });
9838
- }
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
10401
+ this.unsubscribers.push(
10402
+ traceStreamingChannel(aiSDKChannels.generateText, {
10403
+ name: "generateText",
10404
+ type: "llm" /* LLM */,
10405
+ extractInput: ([params]) => {
10406
+ return {
10407
+ input: processAISDKInput(params),
10408
+ metadata: extractMetadataFromParams(params)
10409
+ };
10410
+ },
10411
+ extractOutput: (result) => {
10412
+ return processAISDKOutput(result, denyOutputPaths);
10413
+ },
10414
+ extractMetrics: (result, startTime) => {
10415
+ const metrics = extractTokenMetrics(result);
10416
+ if (startTime) {
10417
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9852
10418
  }
9853
- });
9854
- const startTime = getCurrentUnixTimestamp();
9855
- spans.set(event, { span, startTime });
9856
- try {
9857
- const { input, metadata } = config.extractInput(event.arguments);
9858
- span.log({
9859
- input,
9860
- metadata
9861
- });
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;
9870
- }
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);
9885
- }
9886
- if (!metrics.time_to_first_token && chunks.length > 0) {
9887
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9888
- }
9889
- span.log({
9890
- output,
9891
- metrics
9892
- });
9893
- } catch (error) {
9894
- console.error(
9895
- `Error extracting output for ${channelName}:`,
9896
- error
9897
- );
9898
- } finally {
9899
- span.end();
9900
- }
9901
- },
9902
- onError: (error) => {
9903
- span.log({
9904
- error: error.message
9905
- });
9906
- span.end();
9907
- }
9908
- });
9909
- } else {
9910
- try {
9911
- const output = config.extractOutput(event.result);
9912
- const metrics = config.extractMetrics(event.result, startTime);
9913
- span.log({
9914
- output,
9915
- metrics
9916
- });
9917
- } catch (error) {
9918
- console.error(`Error extracting output for ${channelName}:`, error);
9919
- } finally {
9920
- span.end();
9921
- spans.delete(event);
10419
+ return metrics;
10420
+ },
10421
+ aggregateChunks: aggregateAISDKChunks
10422
+ })
10423
+ );
10424
+ this.unsubscribers.push(
10425
+ traceStreamingChannel(aiSDKChannels.streamText, {
10426
+ name: "streamText",
10427
+ type: "llm" /* LLM */,
10428
+ extractInput: ([params]) => {
10429
+ return {
10430
+ input: processAISDKInput(params),
10431
+ metadata: extractMetadataFromParams(params)
10432
+ };
10433
+ },
10434
+ extractOutput: (result) => {
10435
+ return processAISDKOutput(result, denyOutputPaths);
10436
+ },
10437
+ extractMetrics: (result, startTime) => {
10438
+ const metrics = extractTokenMetrics(result);
10439
+ if (startTime) {
10440
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
10441
+ }
10442
+ return metrics;
10443
+ },
10444
+ aggregateChunks: aggregateAISDKChunks
10445
+ })
10446
+ );
10447
+ this.unsubscribers.push(
10448
+ traceStreamingChannel(aiSDKChannels.generateObject, {
10449
+ name: "generateObject",
10450
+ type: "llm" /* LLM */,
10451
+ extractInput: ([params]) => {
10452
+ return {
10453
+ input: processAISDKInput(params),
10454
+ metadata: extractMetadataFromParams(params)
10455
+ };
10456
+ },
10457
+ extractOutput: (result) => {
10458
+ return processAISDKOutput(result, denyOutputPaths);
10459
+ },
10460
+ extractMetrics: (result, startTime) => {
10461
+ const metrics = extractTokenMetrics(result);
10462
+ if (startTime) {
10463
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
10464
+ }
10465
+ return metrics;
10466
+ },
10467
+ aggregateChunks: aggregateAISDKChunks
10468
+ })
10469
+ );
10470
+ this.unsubscribers.push(
10471
+ traceStreamingChannel(aiSDKChannels.streamObject, {
10472
+ name: "streamObject",
10473
+ type: "llm" /* LLM */,
10474
+ extractInput: ([params]) => {
10475
+ return {
10476
+ input: processAISDKInput(params),
10477
+ metadata: extractMetadataFromParams(params)
10478
+ };
10479
+ },
10480
+ extractOutput: (result) => {
10481
+ return processAISDKOutput(result, denyOutputPaths);
10482
+ },
10483
+ extractMetrics: (result, startTime) => {
10484
+ const metrics = extractTokenMetrics(result);
10485
+ if (startTime) {
10486
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
10487
+ }
10488
+ return metrics;
10489
+ },
10490
+ aggregateChunks: aggregateAISDKChunks
10491
+ })
10492
+ );
10493
+ this.unsubscribers.push(
10494
+ traceStreamingChannel(aiSDKChannels.agentGenerate, {
10495
+ name: "Agent.generate",
10496
+ type: "llm" /* LLM */,
10497
+ extractInput: ([params]) => {
10498
+ return {
10499
+ input: processAISDKInput(params),
10500
+ metadata: extractMetadataFromParams(params)
10501
+ };
10502
+ },
10503
+ extractOutput: (result) => {
10504
+ return processAISDKOutput(result, denyOutputPaths);
10505
+ },
10506
+ extractMetrics: (result, startTime) => {
10507
+ const metrics = extractTokenMetrics(result);
10508
+ if (startTime) {
10509
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
10510
+ }
10511
+ return metrics;
10512
+ },
10513
+ aggregateChunks: aggregateAISDKChunks
10514
+ })
10515
+ );
10516
+ this.unsubscribers.push(
10517
+ traceStreamingChannel(aiSDKChannels.agentStream, {
10518
+ name: "Agent.stream",
10519
+ type: "llm" /* LLM */,
10520
+ extractInput: ([params]) => {
10521
+ return {
10522
+ input: processAISDKInput(params),
10523
+ metadata: extractMetadataFromParams(params)
10524
+ };
10525
+ },
10526
+ extractOutput: (result) => {
10527
+ return processAISDKOutput(result, denyOutputPaths);
10528
+ },
10529
+ extractMetrics: (result, startTime) => {
10530
+ const metrics = extractTokenMetrics(result);
10531
+ if (startTime) {
10532
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9922
10533
  }
9923
- }
9924
- },
9925
- error: (event) => {
9926
- const spanData = spans.get(event);
9927
- if (!spanData) {
9928
- return;
9929
- }
9930
- const { span } = spanData;
9931
- span.log({
9932
- error: event.error.message
9933
- });
9934
- span.end();
9935
- spans.delete(event);
9936
- }
9937
- };
9938
- channel.subscribe(handlers);
9939
- this.unsubscribers.push(() => {
9940
- channel.unsubscribe(handlers);
9941
- });
10534
+ return metrics;
10535
+ },
10536
+ aggregateChunks: aggregateAISDKChunks
10537
+ })
10538
+ );
9942
10539
  }
9943
10540
  };
9944
10541
  function processAISDKInput(params) {
@@ -10180,7 +10777,10 @@ function omitAtPath(obj, keys) {
10180
10777
  if (Array.isArray(obj)) {
10181
10778
  obj.forEach((item) => {
10182
10779
  if (remainingKeys.length > 0) {
10183
- omitAtPath(item, remainingKeys);
10780
+ omitAtPath(
10781
+ item,
10782
+ remainingKeys
10783
+ );
10184
10784
  }
10185
10785
  });
10186
10786
  }
@@ -10190,7 +10790,10 @@ function omitAtPath(obj, keys) {
10190
10790
  }
10191
10791
  } else {
10192
10792
  if (obj && typeof obj === "object" && firstKey in obj) {
10193
- omitAtPath(obj[firstKey], remainingKeys);
10793
+ omitAtPath(
10794
+ obj[firstKey],
10795
+ remainingKeys
10796
+ );
10194
10797
  }
10195
10798
  }
10196
10799
  }
@@ -10203,8 +10806,18 @@ function omit(obj, paths) {
10203
10806
  return result;
10204
10807
  }
10205
10808
 
10809
+ // src/instrumentation/plugins/claude-agent-sdk-channels.ts
10810
+ var claudeAgentSDKChannels = defineChannels(
10811
+ "@anthropic-ai/claude-agent-sdk",
10812
+ {
10813
+ query: channel({
10814
+ channelName: "query",
10815
+ kind: "async"
10816
+ })
10817
+ }
10818
+ );
10819
+
10206
10820
  // src/instrumentation/plugins/claude-agent-sdk-plugin.ts
10207
- import { tracingChannel as tracingChannel4 } from "dc-browser";
10208
10821
  function filterSerializableOptions(options) {
10209
10822
  const allowedKeys = [
10210
10823
  "model",
@@ -10288,7 +10901,9 @@ async function createLLMSpanForMessages(messages, prompt, conversationHistory, o
10288
10901
  const input = buildLLMInput(prompt, conversationHistory);
10289
10902
  const outputs = messages.map(
10290
10903
  (m) => m.message?.content && m.message?.role ? { content: m.message.content, role: m.message.role } : void 0
10291
- ).filter((c) => c !== void 0);
10904
+ ).filter(
10905
+ (c) => c !== void 0
10906
+ );
10292
10907
  const span = startSpan({
10293
10908
  name: "anthropic.messages.create",
10294
10909
  spanAttributes: {
@@ -10307,7 +10922,6 @@ async function createLLMSpanForMessages(messages, prompt, conversationHistory, o
10307
10922
  return lastMessage.message?.content && lastMessage.message?.role ? { content: lastMessage.message.content, role: lastMessage.message.role } : void 0;
10308
10923
  }
10309
10924
  var ClaudeAgentSDKPlugin = class extends BasePlugin {
10310
- unsubscribers = [];
10311
10925
  onEnable() {
10312
10926
  this.subscribeToQuery();
10313
10927
  }
@@ -10323,12 +10937,13 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
10323
10937
  * and individual LLM calls.
10324
10938
  */
10325
10939
  subscribeToQuery() {
10326
- const channel = tracingChannel4("orchestrion:claude-agent-sdk:query");
10940
+ const channel2 = claudeAgentSDKChannels.query.tracingChannel();
10327
10941
  const spans = /* @__PURE__ */ new WeakMap();
10328
10942
  const handlers = {
10329
10943
  start: (event) => {
10330
- const params = event.arguments[0] ?? {};
10331
- const { prompt, options = {} } = params;
10944
+ const params = event.arguments[0];
10945
+ const prompt = params?.prompt;
10946
+ const options = params?.options ?? {};
10332
10947
  const span = startSpan({
10333
10948
  name: "Claude Agent",
10334
10949
  spanAttributes: {
@@ -10340,7 +10955,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
10340
10955
  span.log({
10341
10956
  input: typeof prompt === "string" ? prompt : {
10342
10957
  type: "streaming",
10343
- description: "AsyncIterable<SDKMessage>"
10958
+ description: "AsyncIterable<ClaudeAgentSDKMessage>"
10344
10959
  },
10345
10960
  metadata: filterSerializableOptions(options)
10346
10961
  });
@@ -10362,12 +10977,19 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
10362
10977
  if (!spanData) {
10363
10978
  return;
10364
10979
  }
10365
- if (isAsyncIterable(event.result)) {
10366
- patchStreamIfNeeded(event.result, {
10980
+ const eventResult = event.result;
10981
+ if (eventResult === void 0) {
10982
+ spanData.span.end();
10983
+ spans.delete(event);
10984
+ return;
10985
+ }
10986
+ if (isAsyncIterable(eventResult)) {
10987
+ patchStreamIfNeeded(eventResult, {
10367
10988
  onChunk: async (message) => {
10368
10989
  const currentTime = getCurrentUnixTimestamp();
10369
10990
  const params = event.arguments[0];
10370
- const { prompt, options = {} } = params;
10991
+ const prompt = params?.prompt;
10992
+ const options = params?.options ?? {};
10371
10993
  const messageId = message.message?.id;
10372
10994
  if (messageId && messageId !== spanData.currentMessageId) {
10373
10995
  if (spanData.currentMessages.length > 0) {
@@ -10426,7 +11048,8 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
10426
11048
  onComplete: async () => {
10427
11049
  try {
10428
11050
  const params = event.arguments[0];
10429
- const { prompt, options = {} } = params;
11051
+ const prompt = params?.prompt;
11052
+ const options = params?.options ?? {};
10430
11053
  if (spanData.currentMessages.length > 0) {
10431
11054
  const finalMessage = await createLLMSpanForMessages(
10432
11055
  spanData.currentMessages,
@@ -10464,7 +11087,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
10464
11087
  } else {
10465
11088
  try {
10466
11089
  spanData.span.log({
10467
- output: event.result
11090
+ output: eventResult
10468
11091
  });
10469
11092
  } catch (error) {
10470
11093
  console.error(
@@ -10479,7 +11102,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
10479
11102
  },
10480
11103
  error: (event) => {
10481
11104
  const spanData = spans.get(event);
10482
- if (!spanData) {
11105
+ if (!spanData || !event.error) {
10483
11106
  return;
10484
11107
  }
10485
11108
  const { span } = spanData;
@@ -10490,53 +11113,39 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
10490
11113
  spans.delete(event);
10491
11114
  }
10492
11115
  };
10493
- channel.subscribe(handlers);
11116
+ channel2.subscribe(handlers);
10494
11117
  this.unsubscribers.push(() => {
10495
- channel.unsubscribe(handlers);
11118
+ channel2.unsubscribe(handlers);
10496
11119
  });
10497
11120
  }
10498
11121
  };
10499
11122
 
11123
+ // src/instrumentation/plugins/google-genai-channels.ts
11124
+ var googleGenAIChannels = defineChannels("@google/genai", {
11125
+ generateContent: channel({
11126
+ channelName: "models.generateContent",
11127
+ kind: "async"
11128
+ }),
11129
+ generateContentStream: channel({
11130
+ channelName: "models.generateContentStream",
11131
+ kind: "async"
11132
+ })
11133
+ });
11134
+
10500
11135
  // src/instrumentation/plugins/google-genai-plugin.ts
10501
- import { tracingChannel as tracingChannel5 } from "dc-browser";
10502
11136
  var GoogleGenAIPlugin = class extends BasePlugin {
10503
- unsubscribers = [];
10504
11137
  onEnable() {
10505
11138
  this.subscribeToGoogleGenAIChannels();
10506
11139
  }
10507
11140
  onDisable() {
10508
- for (const unsubscribe of this.unsubscribers) {
10509
- unsubscribe();
10510
- }
10511
- this.unsubscribers = [];
11141
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
10512
11142
  }
10513
11143
  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",
11144
+ this.unsubscribers.push(
11145
+ traceAsyncChannel(googleGenAIChannels.generateContent, {
11146
+ name: "google-genai.generateContent",
10537
11147
  type: "llm" /* LLM */,
10538
- extractInput: (args) => {
10539
- const params = args[0] || {};
11148
+ extractInput: ([params]) => {
10540
11149
  const input = serializeInput(params);
10541
11150
  const metadata = extractMetadata(params);
10542
11151
  return {
@@ -10544,150 +11153,37 @@ var GoogleGenAIPlugin = class extends BasePlugin {
10544
11153
  metadata: { ...metadata, provider: "google-genai" }
10545
11154
  };
10546
11155
  },
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;
11156
+ extractOutput: (result) => {
11157
+ return result;
11158
+ },
11159
+ extractMetrics: (result, startTime) => {
11160
+ return extractGenerateContentMetrics(result, startTime);
10598
11161
  }
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({
11162
+ })
11163
+ );
11164
+ this.unsubscribers.push(
11165
+ traceStreamingChannel(googleGenAIChannels.generateContentStream, {
11166
+ name: "google-genai.generateContentStream",
11167
+ type: "llm" /* LLM */,
11168
+ extractInput: ([params]) => {
11169
+ const input = serializeInput(params);
11170
+ const metadata = extractMetadata(params);
11171
+ return {
10628
11172
  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;
11173
+ metadata: { ...metadata, provider: "google-genai" }
11174
+ };
11175
+ },
11176
+ extractOutput: (result) => {
11177
+ return result;
11178
+ },
11179
+ extractMetrics: () => {
11180
+ return {};
11181
+ },
11182
+ aggregateChunks: (chunks, _result, _endEvent, startTime) => {
11183
+ return aggregateGenerateContentChunks(chunks, startTime);
10678
11184
  }
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
- });
11185
+ })
11186
+ );
10691
11187
  }
10692
11188
  };
10693
11189
  function serializeInput(params) {
@@ -10743,8 +11239,12 @@ function serializePart(part) {
10743
11239
  const buffer = typeof data === "string" ? typeof Buffer !== "undefined" ? Buffer.from(data, "base64") : new Uint8Array(
10744
11240
  atob(data).split("").map((c) => c.charCodeAt(0))
10745
11241
  ) : typeof Buffer !== "undefined" ? Buffer.from(data) : new Uint8Array(data);
11242
+ const arrayBuffer = buffer instanceof Uint8Array ? buffer.buffer.slice(
11243
+ buffer.byteOffset,
11244
+ buffer.byteOffset + buffer.byteLength
11245
+ ) : buffer;
10746
11246
  const attachment = new Attachment({
10747
- data: buffer,
11247
+ data: arrayBuffer,
10748
11248
  filename,
10749
11249
  contentType: mimeType || "application/octet-stream"
10750
11250
  });
@@ -10793,33 +11293,36 @@ function extractGenerateContentMetrics(response, startTime) {
10793
11293
  const end = getCurrentUnixTimestamp();
10794
11294
  metrics.duration = end - startTime;
10795
11295
  }
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
- }
11296
+ if (response?.usageMetadata) {
11297
+ populateUsageMetrics(metrics, response.usageMetadata);
10813
11298
  }
10814
11299
  return metrics;
10815
11300
  }
11301
+ function populateUsageMetrics(metrics, usage) {
11302
+ if (usage.promptTokenCount !== void 0) {
11303
+ metrics.prompt_tokens = usage.promptTokenCount;
11304
+ }
11305
+ if (usage.candidatesTokenCount !== void 0) {
11306
+ metrics.completion_tokens = usage.candidatesTokenCount;
11307
+ }
11308
+ if (usage.totalTokenCount !== void 0) {
11309
+ metrics.tokens = usage.totalTokenCount;
11310
+ }
11311
+ if (usage.cachedContentTokenCount !== void 0) {
11312
+ metrics.prompt_cached_tokens = usage.cachedContentTokenCount;
11313
+ }
11314
+ if (usage.thoughtsTokenCount !== void 0) {
11315
+ metrics.completion_reasoning_tokens = usage.thoughtsTokenCount;
11316
+ }
11317
+ }
10816
11318
  function aggregateGenerateContentChunks(chunks, startTime) {
10817
- const end = getCurrentUnixTimestamp();
10818
- const metrics = {
10819
- duration: end - startTime
10820
- };
11319
+ const metrics = {};
11320
+ if (startTime !== void 0) {
11321
+ const end = getCurrentUnixTimestamp();
11322
+ metrics.duration = end - startTime;
11323
+ }
10821
11324
  let firstTokenTime = null;
10822
- if (chunks.length > 0 && firstTokenTime === null) {
11325
+ if (chunks.length > 0 && firstTokenTime === null && startTime !== void 0) {
10823
11326
  firstTokenTime = getCurrentUnixTimestamp();
10824
11327
  metrics.time_to_first_token = firstTokenTime - startTime;
10825
11328
  }
@@ -10890,21 +11393,7 @@ function aggregateGenerateContentChunks(chunks, startTime) {
10890
11393
  }
10891
11394
  if (usageMetadata) {
10892
11395
  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
- }
11396
+ populateUsageMetrics(metrics, usageMetadata);
10908
11397
  }
10909
11398
  if (text) {
10910
11399
  output.text = text;
@@ -10916,7 +11405,8 @@ function tryToDict(obj) {
10916
11405
  return null;
10917
11406
  }
10918
11407
  if (typeof obj === "object") {
10919
- if (typeof obj.toJSON === "function") {
11408
+ if ("toJSON" in obj && // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
11409
+ typeof obj.toJSON === "function") {
10920
11410
  return obj.toJSON();
10921
11411
  }
10922
11412
  return obj;
@@ -11080,6 +11570,7 @@ function configureNode() {
11080
11570
  isomorph_default.getEnv = (name) => process.env[name];
11081
11571
  isomorph_default.getCallerLocation = getCallerLocation;
11082
11572
  isomorph_default.newAsyncLocalStorage = () => new AsyncLocalStorage();
11573
+ isomorph_default.newTracingChannel = (nameOrChannels) => diagnostics_channel.tracingChannel(nameOrChannels);
11083
11574
  isomorph_default.processOn = (event, handler) => {
11084
11575
  process.on(event, handler);
11085
11576
  };
@@ -12550,6 +13041,11 @@ var evalParametersSchema = z10.record(
12550
13041
  default: promptDefinitionWithToolsSchema.optional(),
12551
13042
  description: z10.string().optional()
12552
13043
  }),
13044
+ z10.object({
13045
+ type: z10.literal("model"),
13046
+ default: z10.string().optional(),
13047
+ description: z10.string().optional()
13048
+ }),
12553
13049
  z10.instanceof(z10.ZodType)
12554
13050
  // For Zod schemas
12555
13051
  ])
@@ -12592,6 +13088,17 @@ function validateParametersWithZod(parameters, parameterSchema) {
12592
13088
  throw new Error(`Parameter '${name}' is required`);
12593
13089
  }
12594
13090
  return [name, Prompt2.fromPromptData(name, promptData)];
13091
+ } else if ("type" in schema && schema.type === "model") {
13092
+ const model = value ?? schema.default;
13093
+ if (model === void 0) {
13094
+ throw new Error(`Parameter '${name}' is required`);
13095
+ }
13096
+ if (typeof model !== "string") {
13097
+ throw new Error(
13098
+ `Parameter '${name}' must be a string model identifier`
13099
+ );
13100
+ }
13101
+ return [name, model];
12595
13102
  } else {
12596
13103
  const schemaCasted = schema;
12597
13104
  return [name, schemaCasted.parse(value)];
@@ -12666,10 +13173,10 @@ function callEvaluatorData(data) {
12666
13173
  };
12667
13174
  }
12668
13175
  function isAsyncIterable3(value) {
12669
- return typeof value === "object" && value !== null && typeof value[Symbol.asyncIterator] === "function";
13176
+ return typeof value === "object" && value !== null && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
12670
13177
  }
12671
13178
  function isIterable(value) {
12672
- return typeof value === "object" && value !== null && typeof value[Symbol.iterator] === "function";
13179
+ return typeof value === "object" && value !== null && Symbol.iterator in value && typeof value[Symbol.iterator] === "function";
12673
13180
  }
12674
13181
  globalThis._evals = {
12675
13182
  functions: [],
@@ -13105,7 +13612,7 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
13105
13612
  const names = Object.keys(scorerErrors).join(", ");
13106
13613
  const errors = failingScorersAndResults.map((item) => item.error);
13107
13614
  unhandledScores = Object.keys(scorerErrors);
13108
- console.warn(
13615
+ debugLogger.forState(evaluator.state).warn(
13109
13616
  `Found exceptions for the following scorers: ${names}`,
13110
13617
  errors
13111
13618
  );
@@ -13223,7 +13730,7 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
13223
13730
  q.kill();
13224
13731
  if (e instanceof InternalAbortError) {
13225
13732
  if (isomorph_default.getEnv("BRAINTRUST_VERBOSE")) {
13226
- console.warn("Evaluator cancelled:", e.message);
13733
+ debugLogger.forState(evaluator.state).warn("Evaluator cancelled:", e.message);
13227
13734
  }
13228
13735
  }
13229
13736
  throw e;
@@ -13318,7 +13825,11 @@ function reportFailures(evaluator, failingResults, { verbose, jsonl }) {
13318
13825
  }
13319
13826
  }
13320
13827
  if (!verbose && !jsonl) {
13321
- console.error(warning("Add --verbose to see full stack traces."));
13828
+ console.error(
13829
+ warning(
13830
+ "Use --debug-logging debug to see full stack traces and troubleshooting details."
13831
+ )
13832
+ );
13322
13833
  }
13323
13834
  }
13324
13835
  }
@@ -13619,6 +14130,11 @@ var staticParametersSchema = z12.record(
13619
14130
  default: PromptData.optional(),
13620
14131
  description: z12.string().optional()
13621
14132
  }),
14133
+ z12.object({
14134
+ type: z12.literal("model"),
14135
+ default: z12.string().optional(),
14136
+ description: z12.string().optional()
14137
+ }),
13622
14138
  z12.object({
13623
14139
  type: z12.literal("data"),
13624
14140
  schema: z12.record(z12.unknown()),
@@ -13855,6 +14371,7 @@ var CodeFunction = class {
13855
14371
  this.description = opts.description;
13856
14372
  this.type = opts.type;
13857
14373
  this.ifExists = opts.ifExists;
14374
+ this.tags = opts.tags;
13858
14375
  this.metadata = opts.metadata;
13859
14376
  this.parameters = opts.parameters;
13860
14377
  this.returns = opts.returns;
@@ -13870,6 +14387,7 @@ var CodeFunction = class {
13870
14387
  parameters;
13871
14388
  returns;
13872
14389
  ifExists;
14390
+ tags;
13873
14391
  metadata;
13874
14392
  key() {
13875
14393
  return JSON.stringify([
@@ -13889,6 +14407,7 @@ var CodePrompt = class {
13889
14407
  id;
13890
14408
  functionType;
13891
14409
  toolFunctions;
14410
+ tags;
13892
14411
  metadata;
13893
14412
  constructor(project, prompt, toolFunctions, opts, functionType) {
13894
14413
  this.project = project;
@@ -13900,6 +14419,7 @@ var CodePrompt = class {
13900
14419
  this.description = opts.description;
13901
14420
  this.id = opts.id;
13902
14421
  this.functionType = functionType;
14422
+ this.tags = opts.tags;
13903
14423
  this.metadata = opts.metadata;
13904
14424
  }
13905
14425
  async toFunctionDefinition(projectNameToId) {
@@ -13934,6 +14454,7 @@ var CodePrompt = class {
13934
14454
  function_type: this.functionType,
13935
14455
  prompt_data,
13936
14456
  if_exists: this.ifExists,
14457
+ tags: this.tags,
13937
14458
  metadata: this.metadata
13938
14459
  };
13939
14460
  }
@@ -13962,6 +14483,7 @@ var PromptBuilder = class {
13962
14483
  name: opts.name,
13963
14484
  slug,
13964
14485
  prompt_data: promptData,
14486
+ tags: opts.tags,
13965
14487
  ...this.project.id !== void 0 ? { project_id: this.project.id } : {}
13966
14488
  };
13967
14489
  const prompt = new Prompt2(
@@ -14043,6 +14565,15 @@ function serializeEvalParametersToStaticParametersSchema(parameters) {
14043
14565
  description: value.description
14044
14566
  }
14045
14567
  ];
14568
+ } else if ("type" in value && value.type === "model") {
14569
+ return [
14570
+ name,
14571
+ {
14572
+ type: "model",
14573
+ default: value.default,
14574
+ description: value.description
14575
+ }
14576
+ ];
14046
14577
  } else {
14047
14578
  const schemaObj = zodToJsonSchema(value);
14048
14579
  return [
@@ -14073,6 +14604,16 @@ function serializeEvalParameterstoParametersSchema(parameters) {
14073
14604
  if (!defaultPromptData) {
14074
14605
  required.push(name);
14075
14606
  }
14607
+ } else if ("type" in value && value.type === "model") {
14608
+ properties[name] = {
14609
+ type: "string",
14610
+ "x-bt-type": "model",
14611
+ ...value.description ? { description: value.description } : {},
14612
+ ..."default" in value ? { default: value.default } : {}
14613
+ };
14614
+ if (!("default" in value)) {
14615
+ required.push(name);
14616
+ }
14076
14617
  } else {
14077
14618
  const schemaObj = zodToJsonSchema(value);
14078
14619
  properties[name] = schemaObj;