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
@@ -39,8 +39,55 @@ __export(index_exports, {
39
39
  });
40
40
  module.exports = __toCommonJS(index_exports);
41
41
 
42
- // src/instrumentation/core/plugin.ts
43
- var import_dc_browser = require("dc-browser");
42
+ // src/isomorph.ts
43
+ var DefaultAsyncLocalStorage = class {
44
+ constructor() {
45
+ }
46
+ enterWith(_) {
47
+ }
48
+ run(_, callback) {
49
+ return callback();
50
+ }
51
+ getStore() {
52
+ return void 0;
53
+ }
54
+ };
55
+ var DefaultTracingChannel = class {
56
+ hasSubscribers = false;
57
+ subscribe(_handlers) {
58
+ }
59
+ unsubscribe(_handlers) {
60
+ return false;
61
+ }
62
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
+ traceSync(fn, _message, thisArg, ...args) {
64
+ return fn.apply(thisArg, args);
65
+ }
66
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
67
+ tracePromise(fn, _message, thisArg, ...args) {
68
+ return Promise.resolve(fn.apply(thisArg, args));
69
+ }
70
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
71
+ traceCallback(fn, _position, _message, thisArg, ...args) {
72
+ return fn.apply(thisArg, args);
73
+ }
74
+ };
75
+ var iso = {
76
+ buildType: "unknown",
77
+ // Will be set by configureBrowser() or configureNode()
78
+ getRepoInfo: async (_settings) => void 0,
79
+ getPastNAncestors: async () => [],
80
+ getEnv: (_name) => void 0,
81
+ getCallerLocation: () => void 0,
82
+ newAsyncLocalStorage: () => new DefaultAsyncLocalStorage(),
83
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
84
+ newTracingChannel: (_nameOrChannels) => new DefaultTracingChannel(),
85
+ processOn: (_0, _1) => {
86
+ },
87
+ basename: (filepath) => filepath.split(/[\\/]/).pop() || filepath,
88
+ writeln: (text) => console.log(text)
89
+ };
90
+ var isomorph_default = iso;
44
91
 
45
92
  // src/instrumentation/core/stream-patcher.ts
46
93
  function isAsyncIterable(value) {
@@ -57,7 +104,7 @@ function patchStreamIfNeeded(stream, options) {
57
104
  return stream;
58
105
  }
59
106
  const originalIteratorFn = stream[Symbol.asyncIterator];
60
- if (originalIteratorFn.__braintrust_patched) {
107
+ if ("__braintrust_patched" in originalIteratorFn && originalIteratorFn["__braintrust_patched"]) {
61
108
  return stream;
62
109
  }
63
110
  try {
@@ -98,7 +145,10 @@ function patchStreamIfNeeded(stream, options) {
98
145
  completed = true;
99
146
  if (options.onError) {
100
147
  try {
101
- options.onError(error, chunks);
148
+ options.onError(
149
+ error instanceof Error ? error : new Error(String(error)),
150
+ chunks
151
+ );
102
152
  } catch (handlerError) {
103
153
  console.error("Error in stream onError handler:", handlerError);
104
154
  }
@@ -126,7 +176,8 @@ function patchStreamIfNeeded(stream, options) {
126
176
  iterator.throw = async function(...args) {
127
177
  if (!completed) {
128
178
  completed = true;
129
- const error = args[0];
179
+ const rawError = args[0];
180
+ const error = rawError instanceof Error ? rawError : new Error(String(rawError));
130
181
  if (options.onError) {
131
182
  try {
132
183
  options.onError(error, chunks);
@@ -140,7 +191,9 @@ function patchStreamIfNeeded(stream, options) {
140
191
  }
141
192
  return iterator;
142
193
  };
143
- patchedIteratorFn.__braintrust_patched = true;
194
+ Object.defineProperty(patchedIteratorFn, "__braintrust_patched", {
195
+ value: true
196
+ });
144
197
  stream[Symbol.asyncIterator] = patchedIteratorFn;
145
198
  return stream;
146
199
  } catch (error) {
@@ -152,6 +205,111 @@ function patchStreamIfNeeded(stream, options) {
152
205
  // src/logger.ts
153
206
  var import_uuid2 = require("uuid");
154
207
 
208
+ // src/debug-logger.ts
209
+ var PREFIX = "[braintrust]";
210
+ var DEBUG_LOG_LEVEL_SYMBOL = Symbol.for("braintrust-debug-log-level");
211
+ var LOG_LEVEL_PRIORITY = {
212
+ error: 0,
213
+ warn: 1,
214
+ info: 2,
215
+ debug: 3
216
+ };
217
+ var hasWarnedAboutInvalidEnvValue = false;
218
+ var debugLogStateResolver = void 0;
219
+ function warnInvalidEnvValue(value) {
220
+ if (hasWarnedAboutInvalidEnvValue) {
221
+ return;
222
+ }
223
+ hasWarnedAboutInvalidEnvValue = true;
224
+ console.warn(
225
+ PREFIX,
226
+ `Invalid BRAINTRUST_DEBUG_LOG_LEVEL value "${value}". Expected "error", "warn", "info", or "debug".`
227
+ );
228
+ }
229
+ function normalizeDebugLogLevelOption(option) {
230
+ if (option === false) {
231
+ return void 0;
232
+ }
233
+ if (option === "error" || option === "warn" || option === "info" || option === "debug") {
234
+ return option;
235
+ }
236
+ throw new Error(
237
+ `Invalid debugLogLevel value "${option}". Expected false, "error", "warn", "info", or "debug".`
238
+ );
239
+ }
240
+ function parseDebugLogLevelEnv(value) {
241
+ if (!value) {
242
+ return void 0;
243
+ }
244
+ if (value === "error" || value === "warn" || value === "info" || value === "debug") {
245
+ return value;
246
+ }
247
+ warnInvalidEnvValue(value);
248
+ return void 0;
249
+ }
250
+ function getEnvDebugLogLevel() {
251
+ return parseDebugLogLevelEnv(isomorph_default.getEnv("BRAINTRUST_DEBUG_LOG_LEVEL"));
252
+ }
253
+ function setGlobalDebugLogLevel(level) {
254
+ globalThis[DEBUG_LOG_LEVEL_SYMBOL] = level;
255
+ }
256
+ function setDebugLogStateResolver(resolver) {
257
+ debugLogStateResolver = resolver;
258
+ }
259
+ function resolveDebugLogLevel(state) {
260
+ const stateLevel = state?.getDebugLogLevel?.();
261
+ const hasStateOverride = state?.hasDebugLogLevelOverride?.() ?? false;
262
+ if (hasStateOverride) {
263
+ return stateLevel;
264
+ }
265
+ const globalLevel = (
266
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
267
+ globalThis[DEBUG_LOG_LEVEL_SYMBOL]
268
+ );
269
+ if (globalLevel !== void 0) {
270
+ return globalLevel === false ? void 0 : globalLevel;
271
+ }
272
+ return getEnvDebugLogLevel();
273
+ }
274
+ function emit(method, state, args) {
275
+ const level = resolveDebugLogLevel(state);
276
+ if (!level || LOG_LEVEL_PRIORITY[method] > LOG_LEVEL_PRIORITY[level]) {
277
+ return;
278
+ }
279
+ if (method === "info") {
280
+ console.log(PREFIX, ...args);
281
+ } else if (method === "debug") {
282
+ console.debug(PREFIX, ...args);
283
+ } else if (method === "warn") {
284
+ console.warn(PREFIX, ...args);
285
+ } else {
286
+ console.error(PREFIX, ...args);
287
+ }
288
+ }
289
+ function createDebugLogger(state) {
290
+ const resolveState = () => state ?? debugLogStateResolver?.();
291
+ return {
292
+ info(...args) {
293
+ emit("info", resolveState(), args);
294
+ },
295
+ debug(...args) {
296
+ emit("debug", resolveState(), args);
297
+ },
298
+ warn(...args) {
299
+ emit("warn", resolveState(), args);
300
+ },
301
+ error(...args) {
302
+ emit("error", resolveState(), args);
303
+ }
304
+ };
305
+ }
306
+ var debugLogger = {
307
+ ...createDebugLogger(),
308
+ forState(state) {
309
+ return createDebugLogger(state);
310
+ }
311
+ };
312
+
155
313
  // src/queue.ts
156
314
  var DEFAULT_QUEUE_SIZE = 15e3;
157
315
  var Queue = class {
@@ -160,7 +318,7 @@ var Queue = class {
160
318
  enforceSizeLimit = false;
161
319
  constructor(maxSize) {
162
320
  if (maxSize < 1) {
163
- console.warn(
321
+ debugLogger.warn(
164
322
  `maxSize ${maxSize} is <1, using default ${DEFAULT_QUEUE_SIZE}`
165
323
  );
166
324
  maxSize = DEFAULT_QUEUE_SIZE;
@@ -3500,34 +3658,6 @@ function devNullWritableStream() {
3500
3658
  });
3501
3659
  }
3502
3660
 
3503
- // src/isomorph.ts
3504
- var DefaultAsyncLocalStorage = class {
3505
- constructor() {
3506
- }
3507
- enterWith(_) {
3508
- }
3509
- run(_, callback) {
3510
- return callback();
3511
- }
3512
- getStore() {
3513
- return void 0;
3514
- }
3515
- };
3516
- var iso = {
3517
- buildType: "unknown",
3518
- // Will be set by configureBrowser() or configureNode()
3519
- getRepoInfo: async (_settings) => void 0,
3520
- getPastNAncestors: async () => [],
3521
- getEnv: (_name) => void 0,
3522
- getCallerLocation: () => void 0,
3523
- newAsyncLocalStorage: () => new DefaultAsyncLocalStorage(),
3524
- processOn: (_0, _1) => {
3525
- },
3526
- basename: (filepath) => filepath.split(/[\\/]/).pop() || filepath,
3527
- writeln: (text) => console.log(text)
3528
- };
3529
- var isomorph_default = iso;
3530
-
3531
3661
  // src/prompt-cache/disk-cache.ts
3532
3662
  function canUseDiskCache() {
3533
3663
  return !!(isomorph_default.hash && isomorph_default.gunzip && isomorph_default.gzip && isomorph_default.stat && isomorph_default.readFile && isomorph_default.writeFile && isomorph_default.utimes && isomorph_default.readdir && isomorph_default.mkdir && isomorph_default.unlink && isomorph_default.homedir);
@@ -4231,7 +4361,10 @@ var loginSchema = import_v38.z.strictObject({
4231
4361
  proxyUrl: import_v38.z.string(),
4232
4362
  loginToken: import_v38.z.string(),
4233
4363
  orgId: import_v38.z.string().nullish(),
4234
- gitMetadataSettings: GitMetadataSettings.nullish()
4364
+ gitMetadataSettings: GitMetadataSettings.nullish(),
4365
+ debugLogLevel: import_v38.z.enum(["error", "warn", "info", "debug"]).optional(),
4366
+ // Distinguishes explicit false from unset so env fallback stays disabled after deserialization.
4367
+ debugLogLevelDisabled: import_v38.z.boolean().optional()
4235
4368
  });
4236
4369
  var stateNonce = 0;
4237
4370
  var BraintrustState = class _BraintrustState {
@@ -4252,6 +4385,16 @@ var BraintrustState = class _BraintrustState {
4252
4385
  this._bgLogger = new SyncLazyValue(
4253
4386
  () => new HTTPBackgroundLogger(new LazyValue(defaultGetLogConn), loginParams)
4254
4387
  );
4388
+ if (loginParams.debugLogLevel !== void 0) {
4389
+ this.debugLogLevelConfigured = true;
4390
+ this.debugLogLevel = normalizeDebugLogLevelOption(
4391
+ loginParams.debugLogLevel
4392
+ );
4393
+ setGlobalDebugLogLevel(this.debugLogLevel ?? false);
4394
+ } else {
4395
+ this.debugLogLevel = getEnvDebugLogLevel();
4396
+ setGlobalDebugLogLevel(void 0);
4397
+ }
4255
4398
  this.resetLoginInfo();
4256
4399
  const memoryCache = new LRUCache({
4257
4400
  max: Number(isomorph_default.getEnv("BRAINTRUST_PROMPT_CACHE_MEMORY_MAX")) ?? 1 << 10
@@ -4296,6 +4439,8 @@ var BraintrustState = class _BraintrustState {
4296
4439
  proxyUrl = null;
4297
4440
  loggedIn = false;
4298
4441
  gitMetadataSettings;
4442
+ debugLogLevel;
4443
+ debugLogLevelConfigured = false;
4299
4444
  fetch = globalThis.fetch;
4300
4445
  _appConn = null;
4301
4446
  _apiConn = null;
@@ -4361,6 +4506,11 @@ var BraintrustState = class _BraintrustState {
4361
4506
  this.proxyUrl = other.proxyUrl;
4362
4507
  this.loggedIn = other.loggedIn;
4363
4508
  this.gitMetadataSettings = other.gitMetadataSettings;
4509
+ this.debugLogLevel = other.debugLogLevel;
4510
+ this.debugLogLevelConfigured = other.debugLogLevelConfigured;
4511
+ setGlobalDebugLogLevel(
4512
+ this.debugLogLevelConfigured ? this.debugLogLevel ?? false : void 0
4513
+ );
4364
4514
  this._appConn = other._appConn;
4365
4515
  this._apiConn = other._apiConn;
4366
4516
  this.loginReplaceApiConn(this.apiConn());
@@ -4385,7 +4535,9 @@ var BraintrustState = class _BraintrustState {
4385
4535
  orgName: this.orgName,
4386
4536
  apiUrl: this.apiUrl,
4387
4537
  proxyUrl: this.proxyUrl,
4388
- gitMetadataSettings: this.gitMetadataSettings
4538
+ gitMetadataSettings: this.gitMetadataSettings,
4539
+ ...this.debugLogLevel ? { debugLogLevel: this.debugLogLevel } : {},
4540
+ ...this.debugLogLevelConfigured && !this.debugLogLevel ? { debugLogLevelDisabled: true } : {}
4389
4541
  };
4390
4542
  }
4391
4543
  static deserialize(serialized, opts) {
@@ -4412,6 +4564,10 @@ var BraintrustState = class _BraintrustState {
4412
4564
  state.proxyConn().set_token(state.loginToken);
4413
4565
  }
4414
4566
  state.loggedIn = true;
4567
+ state.debugLogLevelConfigured = "debugLogLevel" in serializedParsed.data || !!serializedParsed.data.debugLogLevelDisabled;
4568
+ setGlobalDebugLogLevel(
4569
+ state.debugLogLevelConfigured ? state.debugLogLevel ?? false : void 0
4570
+ );
4415
4571
  state.loginReplaceApiConn(state.apiConn());
4416
4572
  return state;
4417
4573
  }
@@ -4424,7 +4580,22 @@ var BraintrustState = class _BraintrustState {
4424
4580
  setMaskingFunction(maskingFunction) {
4425
4581
  this.bgLogger().setMaskingFunction(maskingFunction);
4426
4582
  }
4583
+ setDebugLogLevel(option) {
4584
+ if (option === void 0) {
4585
+ return;
4586
+ }
4587
+ this.debugLogLevelConfigured = true;
4588
+ this.debugLogLevel = normalizeDebugLogLevelOption(option);
4589
+ setGlobalDebugLogLevel(this.debugLogLevel ?? false);
4590
+ }
4591
+ getDebugLogLevel() {
4592
+ return this.debugLogLevel;
4593
+ }
4594
+ hasDebugLogLevelOverride() {
4595
+ return this.debugLogLevelConfigured;
4596
+ }
4427
4597
  async login(loginParams) {
4598
+ this.setDebugLogLevel(loginParams.debugLogLevel);
4428
4599
  if (this.apiUrl && !loginParams.forceLogin) {
4429
4600
  return;
4430
4601
  }
@@ -4522,6 +4693,7 @@ var BraintrustState = class _BraintrustState {
4522
4693
  };
4523
4694
  var _globalState;
4524
4695
  var _internalGetGlobalState = () => _globalState;
4696
+ setDebugLogStateResolver(() => _internalGetGlobalState());
4525
4697
  var FailedHTTPResponse = class extends Error {
4526
4698
  status;
4527
4699
  text;
@@ -4636,7 +4808,7 @@ var HTTPConnection = class _HTTPConnection {
4636
4808
  return await resp.json();
4637
4809
  } catch (e) {
4638
4810
  if (i < tries - 1) {
4639
- console.log(
4811
+ debugLogger.debug(
4640
4812
  `Retrying API request ${object_type} ${JSON.stringify(args)} ${e.status} ${e.text}`
4641
4813
  );
4642
4814
  continue;
@@ -4848,7 +5020,7 @@ with a Blob/ArrayBuffer, or run the program on Node.js.`
4848
5020
  try {
4849
5021
  statSync(data);
4850
5022
  } catch (e) {
4851
- console.warn(`Failed to read file: ${e}`);
5023
+ debugLogger.warn(`Failed to read file: ${e}`);
4852
5024
  }
4853
5025
  }
4854
5026
  };
@@ -5560,7 +5732,7 @@ var HTTPBackgroundLogger = class _HTTPBackgroundLogger {
5560
5732
  this.queueDropLoggingPeriod = queueDropLoggingPeriodEnv;
5561
5733
  }
5562
5734
  if (isomorph_default.getEnv("BRAINTRUST_LOG_FLUSH_CHUNK_SIZE")) {
5563
- console.warn(
5735
+ debugLogger.warn(
5564
5736
  "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."
5565
5737
  );
5566
5738
  }
@@ -5622,7 +5794,10 @@ var HTTPBackgroundLogger = class _HTTPBackgroundLogger {
5622
5794
  const versionInfo = await conn.get_json("version");
5623
5795
  serverLimit = import_v38.z.object({ logs3_payload_max_bytes: import_v38.z.number().nullish() }).parse(versionInfo).logs3_payload_max_bytes ?? null;
5624
5796
  } catch (e) {
5625
- console.warn("Failed to fetch version info for payload limit:", e);
5797
+ debugLogger.warn(
5798
+ "Failed to fetch version info for payload limit:",
5799
+ e
5800
+ );
5626
5801
  }
5627
5802
  const validServerLimit = serverLimit !== null && serverLimit > 0 ? serverLimit : null;
5628
5803
  const canUseOverflow = validServerLimit !== null;
@@ -5766,16 +5941,16 @@ var HTTPBackgroundLogger = class _HTTPBackgroundLogger {
5766
5941
  if (isRetrying) {
5767
5942
  errmsg += ". Retrying";
5768
5943
  }
5769
- console.warn(errmsg);
5944
+ debugLogger.warn(errmsg);
5770
5945
  if (!isRetrying) {
5771
- console.warn(
5946
+ debugLogger.warn(
5772
5947
  `Failed to construct log records to flush after ${this.numTries} attempts. Dropping batch`
5773
5948
  );
5774
5949
  throw e;
5775
5950
  } else {
5776
- console.warn(e);
5951
+ debugLogger.warn(e);
5777
5952
  const sleepTimeS = BACKGROUND_LOGGER_BASE_SLEEP_TIME_S * 2 ** i;
5778
- console.info(`Sleeping for ${sleepTimeS}s`);
5953
+ debugLogger.info(`Sleeping for ${sleepTimeS}s`);
5779
5954
  await new Promise(
5780
5955
  (resolve) => setTimeout(resolve, sleepTimeS * 1e3)
5781
5956
  );
@@ -5876,15 +6051,15 @@ Error: ${errorText}`;
5876
6051
  this.logFailedPayloadsDir();
5877
6052
  }
5878
6053
  if (!isRetrying) {
5879
- console.warn(
6054
+ debugLogger.warn(
5880
6055
  `log request failed after ${this.numTries} retries. Dropping batch`
5881
6056
  );
5882
6057
  throw new Error(errMsg);
5883
6058
  } else {
5884
- console.warn(errMsg);
6059
+ debugLogger.warn(errMsg);
5885
6060
  if (isRetrying) {
5886
6061
  const sleepTimeS = BACKGROUND_LOGGER_BASE_SLEEP_TIME_S * 2 ** i;
5887
- console.info(`Sleeping for ${sleepTimeS}s`);
6062
+ debugLogger.info(`Sleeping for ${sleepTimeS}s`);
5888
6063
  await new Promise(
5889
6064
  (resolve) => setTimeout(resolve, sleepTimeS * 1e3)
5890
6065
  );
@@ -5899,7 +6074,7 @@ Error: ${errorText}`;
5899
6074
  this.queueDropLoggingState.numDropped += numItems;
5900
6075
  const timeNow = getCurrentUnixTimestamp();
5901
6076
  if (timeNow - this.queueDropLoggingState.lastLoggedTimestamp > this.queueDropLoggingPeriod) {
5902
- console.warn(
6077
+ debugLogger.warn(
5903
6078
  `Dropped ${this.queueDropLoggingState.numDropped} elements due to full queue`
5904
6079
  );
5905
6080
  if (this.failedPublishPayloadsDir) {
@@ -5931,7 +6106,7 @@ Error: ${errorText}`;
5931
6106
  await _HTTPBackgroundLogger.writePayloadToDir({ payloadDir, payload });
5932
6107
  }
5933
6108
  } catch (e) {
5934
- console.error(e);
6109
+ debugLogger.error(e);
5935
6110
  }
5936
6111
  }
5937
6112
  static async writePayloadToDir({
@@ -5939,7 +6114,7 @@ Error: ${errorText}`;
5939
6114
  payload
5940
6115
  }) {
5941
6116
  if (!(isomorph_default.pathJoin && isomorph_default.mkdir && isomorph_default.writeFile)) {
5942
- console.warn(
6117
+ debugLogger.warn(
5943
6118
  "Cannot dump payloads: filesystem-operations not supported on this platform"
5944
6119
  );
5945
6120
  return;
@@ -5952,7 +6127,7 @@ Error: ${errorText}`;
5952
6127
  await isomorph_default.mkdir(payloadDir, { recursive: true });
5953
6128
  await isomorph_default.writeFile(payloadFile, payload);
5954
6129
  } catch (e) {
5955
- console.error(
6130
+ debugLogger.error(
5956
6131
  `Failed to write failed payload to output file ${payloadFile}:
5957
6132
  `,
5958
6133
  e
@@ -5983,7 +6158,9 @@ Error: ${errorText}`;
5983
6158
  }
5984
6159
  }
5985
6160
  logFailedPayloadsDir() {
5986
- console.warn(`Logging failed payloads to ${this.failedPublishPayloadsDir}`);
6161
+ debugLogger.warn(
6162
+ `Logging failed payloads to ${this.failedPublishPayloadsDir}`
6163
+ );
5987
6164
  }
5988
6165
  // Should only be called by BraintrustState.
5989
6166
  internalReplaceApiConn(apiConn) {
@@ -6394,7 +6571,14 @@ var ObjectFetcher = class {
6394
6571
  async *fetchRecordsFromApi(batchSize) {
6395
6572
  const state = await this.getState();
6396
6573
  const objectId = await this.id;
6397
- const limit = batchSize ?? DEFAULT_FETCH_BATCH_SIZE;
6574
+ const batchLimit = batchSize ?? DEFAULT_FETCH_BATCH_SIZE;
6575
+ const internalLimit = this._internal_btql?.limit;
6576
+ const limit = batchSize !== void 0 ? batchSize : internalLimit ?? batchLimit;
6577
+ const internalBtqlWithoutReservedQueryKeys = Object.fromEntries(
6578
+ Object.entries(this._internal_btql ?? {}).filter(
6579
+ ([key]) => key !== "cursor" && key !== "limit" && key !== "select" && key !== "from"
6580
+ )
6581
+ );
6398
6582
  let cursor = void 0;
6399
6583
  let iterations = 0;
6400
6584
  while (true) {
@@ -6402,7 +6586,6 @@ var ObjectFetcher = class {
6402
6586
  `btql`,
6403
6587
  {
6404
6588
  query: {
6405
- ...this._internal_btql,
6406
6589
  select: [
6407
6590
  {
6408
6591
  op: "star"
@@ -6422,7 +6605,8 @@ var ObjectFetcher = class {
6422
6605
  ]
6423
6606
  },
6424
6607
  cursor,
6425
- limit
6608
+ limit,
6609
+ ...internalBtqlWithoutReservedQueryKeys
6426
6610
  },
6427
6611
  use_columnstore: false,
6428
6612
  brainstore_realtime: true,
@@ -6695,7 +6879,7 @@ var Experiment2 = class extends ObjectFetcher {
6695
6879
  scores = results["scores"];
6696
6880
  metrics = results["metrics"];
6697
6881
  } catch (e) {
6698
- console.warn(
6882
+ debugLogger.forState(state).warn(
6699
6883
  `Failed to fetch experiment scores and metrics: ${e}
6700
6884
 
6701
6885
  View complete results in Braintrust or run experiment.summarize() again.`
@@ -6772,7 +6956,7 @@ View complete results in Braintrust or run experiment.summarize() again.`
6772
6956
  * @deprecated This function is deprecated. You can simply remove it from your code.
6773
6957
  */
6774
6958
  async close() {
6775
- console.warn(
6959
+ debugLogger.forState(this.state).warn(
6776
6960
  "close is deprecated and will be removed in a future version of braintrust. It is now a no-op and can be removed"
6777
6961
  );
6778
6962
  return this.id;
@@ -6929,8 +7113,8 @@ var SpanImpl = class _SpanImpl {
6929
7113
  ...serializableInternalData,
6930
7114
  [IS_MERGE_FIELD]: this.isMerge
6931
7115
  });
6932
- if (partialRecord.metrics?.end) {
6933
- this.loggedEndTime = partialRecord.metrics?.end;
7116
+ if (typeof partialRecord.metrics?.end === "number") {
7117
+ this.loggedEndTime = partialRecord.metrics.end;
6934
7118
  }
6935
7119
  if (this.parentObjectType === 1 /* EXPERIMENT */) {
6936
7120
  const cachedSpan = {
@@ -7165,7 +7349,7 @@ var Dataset2 = class extends ObjectFetcher {
7165
7349
  constructor(state, lazyMetadata, pinnedVersion, legacy, _internal_btql) {
7166
7350
  const isLegacyDataset = legacy ?? DEFAULT_IS_LEGACY_DATASET;
7167
7351
  if (isLegacyDataset) {
7168
- console.warn(
7352
+ debugLogger.forState(state).warn(
7169
7353
  `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.`
7170
7354
  );
7171
7355
  }
@@ -7396,7 +7580,7 @@ var Dataset2 = class extends ObjectFetcher {
7396
7580
  * @deprecated This function is deprecated. You can simply remove it from your code.
7397
7581
  */
7398
7582
  async close() {
7399
- console.warn(
7583
+ debugLogger.forState(this.state).warn(
7400
7584
  "close is deprecated and will be removed in a future version of braintrust. It is now a no-op and can be removed"
7401
7585
  );
7402
7586
  return this.id;
@@ -7407,6 +7591,49 @@ var Dataset2 = class extends ObjectFetcher {
7407
7591
  };
7408
7592
  var TEST_API_KEY = "___TEST_API_KEY__THIS_IS_NOT_REAL___";
7409
7593
 
7594
+ // src/instrumentation/core/channel-tracing-utils.ts
7595
+ function hasChannelSpanInfo(value) {
7596
+ return isObject(value) && isObject(value.span_info);
7597
+ }
7598
+ function getChannelSpanInfo(event) {
7599
+ if (isObject(event.span_info)) {
7600
+ return event.span_info;
7601
+ }
7602
+ const firstArg = event.arguments?.[0];
7603
+ if (hasChannelSpanInfo(firstArg)) {
7604
+ return firstArg.span_info;
7605
+ }
7606
+ return void 0;
7607
+ }
7608
+ function buildStartSpanArgs(config, event) {
7609
+ const spanInfo = getChannelSpanInfo(event);
7610
+ const spanAttributes = {
7611
+ type: config.type
7612
+ };
7613
+ if (isObject(spanInfo?.spanAttributes)) {
7614
+ mergeDicts(spanAttributes, spanInfo.spanAttributes);
7615
+ }
7616
+ return {
7617
+ name: typeof spanInfo?.name === "string" && spanInfo.name ? spanInfo.name : config.name,
7618
+ spanAttributes,
7619
+ spanInfoMetadata: isObject(spanInfo?.metadata) ? spanInfo.metadata : void 0
7620
+ };
7621
+ }
7622
+ function mergeInputMetadata(metadata, spanInfoMetadata) {
7623
+ if (!spanInfoMetadata) {
7624
+ return isObject(metadata) ? (
7625
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
7626
+ metadata
7627
+ ) : void 0;
7628
+ }
7629
+ const mergedMetadata = {};
7630
+ mergeDicts(mergedMetadata, spanInfoMetadata);
7631
+ if (isObject(metadata)) {
7632
+ mergeDicts(mergedMetadata, metadata);
7633
+ }
7634
+ return mergedMetadata;
7635
+ }
7636
+
7410
7637
  // src/instrumentation/core/plugin.ts
7411
7638
  var BasePlugin = class {
7412
7639
  enabled = false;
@@ -7438,23 +7665,25 @@ var BasePlugin = class {
7438
7665
  * @param handlers - Event handlers
7439
7666
  */
7440
7667
  subscribe(channelName, handlers) {
7441
- const channel = (0, import_dc_browser.tracingChannel)(channelName);
7442
- channel.subscribe(handlers);
7668
+ const channel2 = isomorph_default.newTracingChannel(channelName);
7669
+ channel2.subscribe(handlers);
7443
7670
  }
7444
7671
  /**
7445
7672
  * Subscribe to a channel for async methods (non-streaming).
7446
7673
  * Creates a span and logs input/output/metrics.
7447
7674
  */
7448
7675
  subscribeToChannel(channelName, config) {
7449
- const channel = (0, import_dc_browser.tracingChannel)(channelName);
7676
+ const channel2 = isomorph_default.newTracingChannel(channelName);
7450
7677
  const spans = /* @__PURE__ */ new WeakMap();
7451
7678
  const handlers = {
7452
7679
  start: (event) => {
7680
+ const { name, spanAttributes, spanInfoMetadata } = buildStartSpanArgs(
7681
+ config,
7682
+ event
7683
+ );
7453
7684
  const span = startSpan({
7454
- name: config.name,
7455
- spanAttributes: {
7456
- type: config.type
7457
- }
7685
+ name,
7686
+ spanAttributes
7458
7687
  });
7459
7688
  const startTime = getCurrentUnixTimestamp();
7460
7689
  spans.set(event, { span, startTime });
@@ -7462,7 +7691,7 @@ var BasePlugin = class {
7462
7691
  const { input, metadata } = config.extractInput(event.arguments);
7463
7692
  span.log({
7464
7693
  input,
7465
- metadata
7694
+ metadata: mergeInputMetadata(metadata, spanInfoMetadata)
7466
7695
  });
7467
7696
  } catch (error) {
7468
7697
  console.error(`Error extracting input for ${channelName}:`, error);
@@ -7475,10 +7704,12 @@ var BasePlugin = class {
7475
7704
  }
7476
7705
  const { span, startTime } = spanData;
7477
7706
  try {
7478
- const output = config.extractOutput(event.result);
7479
- const metrics = config.extractMetrics(event.result, startTime);
7707
+ const output = config.extractOutput(event.result, event);
7708
+ const metrics = config.extractMetrics(event.result, startTime, event);
7709
+ const metadata = config.extractMetadata?.(event.result, event);
7480
7710
  span.log({
7481
7711
  output,
7712
+ ...metadata !== void 0 ? { metadata } : {},
7482
7713
  metrics
7483
7714
  });
7484
7715
  } catch (error) {
@@ -7501,9 +7732,9 @@ var BasePlugin = class {
7501
7732
  spans.delete(event);
7502
7733
  }
7503
7734
  };
7504
- channel.subscribe(handlers);
7735
+ channel2.subscribe(handlers);
7505
7736
  this.unsubscribers.push(() => {
7506
- channel.unsubscribe(handlers);
7737
+ channel2.unsubscribe(handlers);
7507
7738
  });
7508
7739
  }
7509
7740
  /**
@@ -7511,15 +7742,17 @@ var BasePlugin = class {
7511
7742
  * Handles both streaming and non-streaming responses.
7512
7743
  */
7513
7744
  subscribeToStreamingChannel(channelName, config) {
7514
- const channel = (0, import_dc_browser.tracingChannel)(channelName);
7745
+ const channel2 = isomorph_default.newTracingChannel(channelName);
7515
7746
  const spans = /* @__PURE__ */ new WeakMap();
7516
7747
  const handlers = {
7517
7748
  start: (event) => {
7749
+ const { name, spanAttributes, spanInfoMetadata } = buildStartSpanArgs(
7750
+ config,
7751
+ event
7752
+ );
7518
7753
  const span = startSpan({
7519
- name: config.name,
7520
- spanAttributes: {
7521
- type: config.type
7522
- }
7754
+ name,
7755
+ spanAttributes
7523
7756
  });
7524
7757
  const startTime = getCurrentUnixTimestamp();
7525
7758
  spans.set(event, { span, startTime });
@@ -7527,7 +7760,7 @@ var BasePlugin = class {
7527
7760
  const { input, metadata } = config.extractInput(event.arguments);
7528
7761
  span.log({
7529
7762
  input,
7530
- metadata
7763
+ metadata: mergeInputMetadata(metadata, spanInfoMetadata)
7531
7764
  });
7532
7765
  } catch (error) {
7533
7766
  console.error(`Error extracting input for ${channelName}:`, error);
@@ -7540,24 +7773,39 @@ var BasePlugin = class {
7540
7773
  }
7541
7774
  const { span, startTime } = spanData;
7542
7775
  if (isAsyncIterable(event.result)) {
7776
+ let firstChunkTime;
7543
7777
  patchStreamIfNeeded(event.result, {
7778
+ onChunk: () => {
7779
+ if (firstChunkTime === void 0) {
7780
+ firstChunkTime = getCurrentUnixTimestamp();
7781
+ }
7782
+ },
7544
7783
  onComplete: (chunks) => {
7545
7784
  try {
7546
7785
  let output;
7547
7786
  let metrics;
7787
+ let metadata;
7548
7788
  if (config.aggregateChunks) {
7549
- const aggregated = config.aggregateChunks(chunks);
7789
+ const aggregated = config.aggregateChunks(
7790
+ chunks,
7791
+ event.result,
7792
+ event
7793
+ );
7550
7794
  output = aggregated.output;
7551
7795
  metrics = aggregated.metrics;
7796
+ metadata = aggregated.metadata;
7552
7797
  } else {
7553
- output = config.extractOutput(chunks);
7554
- metrics = config.extractMetrics(chunks, startTime);
7798
+ output = config.extractOutput(chunks, event);
7799
+ metrics = config.extractMetrics(chunks, startTime, event);
7555
7800
  }
7556
- if (!metrics.time_to_first_token && chunks.length > 0) {
7801
+ if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
7802
+ metrics.time_to_first_token = firstChunkTime - startTime;
7803
+ } else if (metrics.time_to_first_token === void 0 && chunks.length > 0) {
7557
7804
  metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
7558
7805
  }
7559
7806
  span.log({
7560
7807
  output,
7808
+ ...metadata !== void 0 ? { metadata } : {},
7561
7809
  metrics
7562
7810
  });
7563
7811
  } catch (error) {
@@ -7578,10 +7826,16 @@ var BasePlugin = class {
7578
7826
  });
7579
7827
  } else {
7580
7828
  try {
7581
- const output = config.extractOutput(event.result);
7582
- const metrics = config.extractMetrics(event.result, startTime);
7829
+ const output = config.extractOutput(event.result, event);
7830
+ const metadata = config.extractMetadata ? config.extractMetadata(event.result, event) : void 0;
7831
+ const metrics = config.extractMetrics(
7832
+ event.result,
7833
+ startTime,
7834
+ event
7835
+ );
7583
7836
  span.log({
7584
7837
  output,
7838
+ ...metadata !== void 0 ? { metadata } : {},
7585
7839
  metrics
7586
7840
  });
7587
7841
  } catch (error) {
@@ -7605,9 +7859,9 @@ var BasePlugin = class {
7605
7859
  spans.delete(event);
7606
7860
  }
7607
7861
  };
7608
- channel.subscribe(handlers);
7862
+ channel2.subscribe(handlers);
7609
7863
  this.unsubscribers.push(() => {
7610
- channel.unsubscribe(handlers);
7864
+ channel2.unsubscribe(handlers);
7611
7865
  });
7612
7866
  }
7613
7867
  /**
@@ -7615,15 +7869,17 @@ var BasePlugin = class {
7615
7869
  * Used for methods like beta.chat.completions.stream() and responses.stream().
7616
7870
  */
7617
7871
  subscribeToSyncStreamChannel(channelName, config) {
7618
- const channel = (0, import_dc_browser.tracingChannel)(channelName);
7872
+ const channel2 = isomorph_default.newTracingChannel(channelName);
7619
7873
  const spans = /* @__PURE__ */ new WeakMap();
7620
7874
  const handlers = {
7621
7875
  start: (event) => {
7876
+ const { name, spanAttributes, spanInfoMetadata } = buildStartSpanArgs(
7877
+ config,
7878
+ event
7879
+ );
7622
7880
  const span = startSpan({
7623
- name: config.name,
7624
- spanAttributes: {
7625
- type: config.type
7626
- }
7881
+ name,
7882
+ spanAttributes
7627
7883
  });
7628
7884
  const startTime = getCurrentUnixTimestamp();
7629
7885
  spans.set(event, { span, startTime });
@@ -7631,7 +7887,7 @@ var BasePlugin = class {
7631
7887
  const { input, metadata } = config.extractInput(event.arguments);
7632
7888
  span.log({
7633
7889
  input,
7634
- metadata
7890
+ metadata: mergeInputMetadata(metadata, spanInfoMetadata)
7635
7891
  });
7636
7892
  } catch (error) {
7637
7893
  console.error(`Error extracting input for ${channelName}:`, error);
@@ -7715,9 +7971,9 @@ var BasePlugin = class {
7715
7971
  spans.delete(event);
7716
7972
  }
7717
7973
  };
7718
- channel.subscribe(handlers);
7974
+ channel2.subscribe(handlers);
7719
7975
  this.unsubscribers.push(() => {
7720
- channel.unsubscribe(handlers);
7976
+ channel2.unsubscribe(handlers);
7721
7977
  });
7722
7978
  }
7723
7979
  };
@@ -7740,149 +7996,635 @@ function isValidChannelName(channelName) {
7740
7996
  return /^braintrust:[^:]+:.+$/.test(channelName);
7741
7997
  }
7742
7998
 
7743
- // src/wrappers/attachment-utils.ts
7744
- function getExtensionFromMediaType(mediaType) {
7745
- const extensionMap = {
7746
- "image/png": "png",
7747
- "image/jpeg": "jpg",
7748
- "image/gif": "gif",
7749
- "image/webp": "webp",
7750
- "image/svg+xml": "svg",
7751
- "audio/mpeg": "mp3",
7752
- "audio/wav": "wav",
7753
- "audio/ogg": "ogg",
7754
- "video/mp4": "mp4",
7755
- "video/webm": "webm",
7756
- "application/pdf": "pdf",
7757
- "application/json": "json",
7758
- "text/plain": "txt",
7759
- "text/html": "html",
7760
- "text/csv": "csv"
7761
- };
7762
- return extensionMap[mediaType] || "bin";
7999
+ // src/instrumentation/core/channel-tracing.ts
8000
+ function isSyncStreamLike(value) {
8001
+ return !!value && typeof value === "object" && typeof value.on === "function";
7763
8002
  }
7764
- function convertDataToBlob(data, mediaType) {
8003
+ function hasChoices(value) {
8004
+ return !!value && typeof value === "object" && "choices" in value;
8005
+ }
8006
+ function normalizeMetadata(metadata) {
8007
+ return isObject(metadata) ? metadata : void 0;
8008
+ }
8009
+ function startSpanForEvent(config, event, channelName) {
8010
+ const { name, spanAttributes, spanInfoMetadata } = buildStartSpanArgs(
8011
+ config,
8012
+ event
8013
+ );
8014
+ const span = startSpan({
8015
+ name,
8016
+ spanAttributes
8017
+ });
8018
+ const startTime = getCurrentUnixTimestamp();
7765
8019
  try {
7766
- if (typeof data === "string") {
7767
- if (data.startsWith("data:")) {
7768
- const base64Match = data.match(/^data:[^;]+;base64,(.+)$/);
7769
- if (base64Match) {
7770
- const base64 = base64Match[1];
7771
- const binaryString = atob(base64);
7772
- const bytes = new Uint8Array(binaryString.length);
7773
- for (let i = 0; i < binaryString.length; i++) {
7774
- bytes[i] = binaryString.charCodeAt(i);
7775
- }
7776
- return new Blob([bytes], { type: mediaType });
7777
- }
7778
- } else if (data.startsWith("http://") || data.startsWith("https://")) {
7779
- return null;
7780
- } else {
7781
- const binaryString = atob(data);
7782
- const bytes = new Uint8Array(binaryString.length);
7783
- for (let i = 0; i < binaryString.length; i++) {
7784
- bytes[i] = binaryString.charCodeAt(i);
7785
- }
7786
- return new Blob([bytes], { type: mediaType });
7787
- }
7788
- } else if (data instanceof Uint8Array) {
7789
- return new Blob([data], { type: mediaType });
7790
- } else if (data instanceof ArrayBuffer) {
7791
- return new Blob([data], { type: mediaType });
7792
- } else if (typeof Buffer !== "undefined" && data instanceof Buffer) {
7793
- return new Blob([data], { type: mediaType });
7794
- }
7795
- } catch {
7796
- return null;
8020
+ const { input, metadata } = config.extractInput(event.arguments);
8021
+ span.log({
8022
+ input,
8023
+ metadata: mergeInputMetadata(metadata, spanInfoMetadata)
8024
+ });
8025
+ } catch (error) {
8026
+ console.error(`Error extracting input for ${channelName}:`, error);
7797
8027
  }
7798
- return null;
8028
+ return { span, startTime };
7799
8029
  }
7800
- function processInputAttachments(input) {
7801
- if (!input) {
7802
- return input;
8030
+ function logErrorAndEnd(states, event) {
8031
+ const spanData = states.get(event);
8032
+ if (!spanData) {
8033
+ return;
7803
8034
  }
7804
- let attachmentIndex = 0;
7805
- const processContentPart = (part) => {
7806
- if (!part || typeof part !== "object") {
7807
- return part;
7808
- }
7809
- if (part.type === "image" && part.image) {
7810
- let mediaType = "image/png";
7811
- if (typeof part.image === "string" && part.image.startsWith("data:")) {
7812
- const mediaTypeMatch = part.image.match(/^data:([^;]+);/);
7813
- if (mediaTypeMatch) {
7814
- mediaType = mediaTypeMatch[1];
7815
- }
7816
- } else if (part.mediaType) {
7817
- mediaType = part.mediaType;
7818
- }
7819
- const blob = convertDataToBlob(part.image, mediaType);
7820
- if (blob) {
7821
- const filename = `input_image_${attachmentIndex}.${getExtensionFromMediaType(mediaType)}`;
7822
- attachmentIndex++;
7823
- const attachment = new Attachment({
7824
- data: blob,
7825
- filename,
7826
- contentType: mediaType
7827
- });
7828
- return {
7829
- ...part,
7830
- image: attachment
7831
- };
8035
+ spanData.span.log({
8036
+ error: event.error.message
8037
+ });
8038
+ spanData.span.end();
8039
+ states.delete(event);
8040
+ }
8041
+ function traceAsyncChannel(channel2, config) {
8042
+ const tracingChannel = channel2.tracingChannel();
8043
+ const states = /* @__PURE__ */ new WeakMap();
8044
+ const channelName = channel2.channelName;
8045
+ const handlers = {
8046
+ start: (event) => {
8047
+ states.set(
8048
+ event,
8049
+ startSpanForEvent(
8050
+ config,
8051
+ event,
8052
+ channelName
8053
+ )
8054
+ );
8055
+ },
8056
+ asyncEnd: (event) => {
8057
+ const spanData = states.get(event);
8058
+ if (!spanData) {
8059
+ return;
7832
8060
  }
7833
- }
7834
- if (part.type === "file" && part.data) {
7835
- const mediaType = part.mediaType || "application/octet-stream";
7836
- const blob = convertDataToBlob(part.data, mediaType);
7837
- if (blob) {
7838
- const filename = part.filename || `input_file_${attachmentIndex}.${getExtensionFromMediaType(mediaType)}`;
7839
- attachmentIndex++;
7840
- const attachment = new Attachment({
7841
- data: blob,
7842
- filename,
7843
- contentType: mediaType
8061
+ const asyncEndEvent = event;
8062
+ const { span, startTime } = spanData;
8063
+ try {
8064
+ const output = config.extractOutput(
8065
+ asyncEndEvent.result,
8066
+ asyncEndEvent
8067
+ );
8068
+ const metrics = config.extractMetrics(
8069
+ asyncEndEvent.result,
8070
+ startTime,
8071
+ asyncEndEvent
8072
+ );
8073
+ const metadata = config.extractMetadata?.(
8074
+ asyncEndEvent.result,
8075
+ asyncEndEvent
8076
+ );
8077
+ span.log({
8078
+ output,
8079
+ ...normalizeMetadata(metadata) !== void 0 ? { metadata: normalizeMetadata(metadata) } : {},
8080
+ metrics
7844
8081
  });
7845
- return {
7846
- ...part,
7847
- data: attachment
7848
- };
8082
+ } catch (error) {
8083
+ console.error(`Error extracting output for ${channelName}:`, error);
8084
+ } finally {
8085
+ span.end();
8086
+ states.delete(event);
7849
8087
  }
8088
+ },
8089
+ error: (event) => {
8090
+ logErrorAndEnd(states, event);
7850
8091
  }
7851
- return part;
7852
8092
  };
7853
- const processMessage = (message) => {
7854
- if (!message || typeof message !== "object") {
7855
- return message;
7856
- }
7857
- if (Array.isArray(message.content)) {
7858
- return {
7859
- ...message,
7860
- content: message.content.map(processContentPart)
7861
- };
7862
- }
7863
- return message;
8093
+ tracingChannel.subscribe(handlers);
8094
+ return () => {
8095
+ tracingChannel.unsubscribe(handlers);
7864
8096
  };
7865
- if (Array.isArray(input)) {
7866
- return input.map(processMessage);
7867
- } else if (typeof input === "object" && input.content) {
7868
- return processMessage(input);
7869
- }
7870
- return input;
7871
8097
  }
7872
-
7873
- // src/instrumentation/plugins/openai-plugin.ts
7874
- var OpenAIPlugin = class extends BasePlugin {
7875
- constructor() {
8098
+ function traceStreamingChannel(channel2, config) {
8099
+ const tracingChannel = channel2.tracingChannel();
8100
+ const states = /* @__PURE__ */ new WeakMap();
8101
+ const channelName = channel2.channelName;
8102
+ const handlers = {
8103
+ start: (event) => {
8104
+ states.set(
8105
+ event,
8106
+ startSpanForEvent(
8107
+ config,
8108
+ event,
8109
+ channelName
8110
+ )
8111
+ );
8112
+ },
8113
+ asyncEnd: (event) => {
8114
+ const spanData = states.get(event);
8115
+ if (!spanData) {
8116
+ return;
8117
+ }
8118
+ const asyncEndEvent = event;
8119
+ const { span, startTime } = spanData;
8120
+ if (isAsyncIterable(asyncEndEvent.result)) {
8121
+ let firstChunkTime;
8122
+ patchStreamIfNeeded(asyncEndEvent.result, {
8123
+ onChunk: () => {
8124
+ if (firstChunkTime === void 0) {
8125
+ firstChunkTime = getCurrentUnixTimestamp();
8126
+ }
8127
+ },
8128
+ onComplete: (chunks) => {
8129
+ try {
8130
+ let output;
8131
+ let metrics;
8132
+ let metadata;
8133
+ if (config.aggregateChunks) {
8134
+ const aggregated = config.aggregateChunks(
8135
+ chunks,
8136
+ asyncEndEvent.result,
8137
+ asyncEndEvent,
8138
+ startTime
8139
+ );
8140
+ output = aggregated.output;
8141
+ metrics = aggregated.metrics;
8142
+ metadata = aggregated.metadata;
8143
+ } else {
8144
+ output = config.extractOutput(
8145
+ chunks,
8146
+ asyncEndEvent
8147
+ );
8148
+ metrics = config.extractMetrics(
8149
+ chunks,
8150
+ startTime,
8151
+ asyncEndEvent
8152
+ );
8153
+ }
8154
+ if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
8155
+ metrics.time_to_first_token = firstChunkTime - startTime;
8156
+ } else if (metrics.time_to_first_token === void 0 && chunks.length > 0) {
8157
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8158
+ }
8159
+ span.log({
8160
+ output,
8161
+ ...metadata !== void 0 ? { metadata } : {},
8162
+ metrics
8163
+ });
8164
+ } catch (error) {
8165
+ console.error(
8166
+ `Error extracting output for ${channelName}:`,
8167
+ error
8168
+ );
8169
+ } finally {
8170
+ span.end();
8171
+ states.delete(event);
8172
+ }
8173
+ },
8174
+ onError: (error) => {
8175
+ span.log({
8176
+ error: error.message
8177
+ });
8178
+ span.end();
8179
+ states.delete(event);
8180
+ }
8181
+ });
8182
+ return;
8183
+ }
8184
+ try {
8185
+ const output = config.extractOutput(
8186
+ asyncEndEvent.result,
8187
+ asyncEndEvent
8188
+ );
8189
+ const metrics = config.extractMetrics(
8190
+ asyncEndEvent.result,
8191
+ startTime,
8192
+ asyncEndEvent
8193
+ );
8194
+ const metadata = config.extractMetadata?.(
8195
+ asyncEndEvent.result,
8196
+ asyncEndEvent
8197
+ );
8198
+ span.log({
8199
+ output,
8200
+ ...normalizeMetadata(metadata) !== void 0 ? { metadata: normalizeMetadata(metadata) } : {},
8201
+ metrics
8202
+ });
8203
+ } catch (error) {
8204
+ console.error(`Error extracting output for ${channelName}:`, error);
8205
+ } finally {
8206
+ span.end();
8207
+ states.delete(event);
8208
+ }
8209
+ },
8210
+ error: (event) => {
8211
+ logErrorAndEnd(states, event);
8212
+ }
8213
+ };
8214
+ tracingChannel.subscribe(handlers);
8215
+ return () => {
8216
+ tracingChannel.unsubscribe(handlers);
8217
+ };
8218
+ }
8219
+ function traceSyncStreamChannel(channel2, config) {
8220
+ const tracingChannel = channel2.tracingChannel();
8221
+ const states = /* @__PURE__ */ new WeakMap();
8222
+ const channelName = channel2.channelName;
8223
+ const handlers = {
8224
+ start: (event) => {
8225
+ states.set(
8226
+ event,
8227
+ startSpanForEvent(
8228
+ config,
8229
+ event,
8230
+ channelName
8231
+ )
8232
+ );
8233
+ },
8234
+ end: (event) => {
8235
+ const spanData = states.get(event);
8236
+ if (!spanData) {
8237
+ return;
8238
+ }
8239
+ const { span, startTime } = spanData;
8240
+ const resultEvent = event;
8241
+ const stream = resultEvent.result;
8242
+ if (!isSyncStreamLike(stream)) {
8243
+ span.end();
8244
+ states.delete(event);
8245
+ return;
8246
+ }
8247
+ let first = true;
8248
+ stream.on("chunk", () => {
8249
+ if (first) {
8250
+ span.log({
8251
+ metrics: {
8252
+ time_to_first_token: getCurrentUnixTimestamp() - startTime
8253
+ }
8254
+ });
8255
+ first = false;
8256
+ }
8257
+ });
8258
+ stream.on("chatCompletion", (completion) => {
8259
+ try {
8260
+ if (hasChoices(completion)) {
8261
+ span.log({
8262
+ output: completion.choices
8263
+ });
8264
+ }
8265
+ } catch (error) {
8266
+ console.error(
8267
+ `Error extracting chatCompletion for ${channelName}:`,
8268
+ error
8269
+ );
8270
+ }
8271
+ });
8272
+ stream.on("event", (streamEvent) => {
8273
+ if (!config.extractFromEvent) {
8274
+ return;
8275
+ }
8276
+ try {
8277
+ if (first) {
8278
+ span.log({
8279
+ metrics: {
8280
+ time_to_first_token: getCurrentUnixTimestamp() - startTime
8281
+ }
8282
+ });
8283
+ first = false;
8284
+ }
8285
+ const extracted = config.extractFromEvent(streamEvent);
8286
+ if (extracted && Object.keys(extracted).length > 0) {
8287
+ span.log(extracted);
8288
+ }
8289
+ } catch (error) {
8290
+ console.error(`Error extracting event for ${channelName}:`, error);
8291
+ }
8292
+ });
8293
+ stream.on("end", () => {
8294
+ span.end();
8295
+ states.delete(event);
8296
+ });
8297
+ stream.on("error", (error) => {
8298
+ span.log({
8299
+ error: error.message
8300
+ });
8301
+ span.end();
8302
+ states.delete(event);
8303
+ });
8304
+ },
8305
+ error: (event) => {
8306
+ logErrorAndEnd(states, event);
8307
+ }
8308
+ };
8309
+ tracingChannel.subscribe(handlers);
8310
+ return () => {
8311
+ tracingChannel.unsubscribe(handlers);
8312
+ };
8313
+ }
8314
+ function unsubscribeAll(unsubscribers) {
8315
+ for (const unsubscribe of unsubscribers) {
8316
+ unsubscribe();
8317
+ }
8318
+ return [];
8319
+ }
8320
+
8321
+ // src/wrappers/attachment-utils.ts
8322
+ function getExtensionFromMediaType(mediaType) {
8323
+ const extensionMap = {
8324
+ "image/png": "png",
8325
+ "image/jpeg": "jpg",
8326
+ "image/gif": "gif",
8327
+ "image/webp": "webp",
8328
+ "image/svg+xml": "svg",
8329
+ "audio/mpeg": "mp3",
8330
+ "audio/wav": "wav",
8331
+ "audio/ogg": "ogg",
8332
+ "video/mp4": "mp4",
8333
+ "video/webm": "webm",
8334
+ "application/pdf": "pdf",
8335
+ "application/json": "json",
8336
+ "text/plain": "txt",
8337
+ "text/html": "html",
8338
+ "text/csv": "csv"
8339
+ };
8340
+ return extensionMap[mediaType] || "bin";
8341
+ }
8342
+ function convertDataToBlob(data, mediaType) {
8343
+ try {
8344
+ if (typeof data === "string") {
8345
+ if (data.startsWith("data:")) {
8346
+ const base64Match = data.match(/^data:[^;]+;base64,(.+)$/);
8347
+ if (base64Match) {
8348
+ const base64 = base64Match[1];
8349
+ const binaryString = atob(base64);
8350
+ const bytes = new Uint8Array(binaryString.length);
8351
+ for (let i = 0; i < binaryString.length; i++) {
8352
+ bytes[i] = binaryString.charCodeAt(i);
8353
+ }
8354
+ return new Blob([bytes], { type: mediaType });
8355
+ }
8356
+ } else if (data.startsWith("http://") || data.startsWith("https://")) {
8357
+ return null;
8358
+ } else {
8359
+ const binaryString = atob(data);
8360
+ const bytes = new Uint8Array(binaryString.length);
8361
+ for (let i = 0; i < binaryString.length; i++) {
8362
+ bytes[i] = binaryString.charCodeAt(i);
8363
+ }
8364
+ return new Blob([bytes], { type: mediaType });
8365
+ }
8366
+ } else if (data instanceof Uint8Array) {
8367
+ return new Blob([data], { type: mediaType });
8368
+ } else if (data instanceof ArrayBuffer) {
8369
+ return new Blob([data], { type: mediaType });
8370
+ } else if (typeof Buffer !== "undefined" && data instanceof Buffer) {
8371
+ return new Blob([data], { type: mediaType });
8372
+ }
8373
+ } catch {
8374
+ return null;
8375
+ }
8376
+ return null;
8377
+ }
8378
+ function processInputAttachments(input) {
8379
+ if (!input) {
8380
+ return input;
8381
+ }
8382
+ let attachmentIndex = 0;
8383
+ const inferMediaTypeFromDataUrl = (value, fallback) => {
8384
+ const mediaTypeMatch = value.match(/^data:([^;]+);/);
8385
+ return mediaTypeMatch?.[1] || fallback;
8386
+ };
8387
+ const toAttachment = (value, mediaType, filename) => {
8388
+ const blob = convertDataToBlob(value, mediaType);
8389
+ if (!blob) {
8390
+ return null;
8391
+ }
8392
+ return new Attachment({
8393
+ data: blob,
8394
+ filename,
8395
+ contentType: mediaType
8396
+ });
8397
+ };
8398
+ const processNode = (node) => {
8399
+ if (Array.isArray(node)) {
8400
+ return node.map(processNode);
8401
+ }
8402
+ if (!node || typeof node !== "object") {
8403
+ return node;
8404
+ }
8405
+ 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:")) {
8406
+ const mediaType = inferMediaTypeFromDataUrl(
8407
+ node.image_url.url,
8408
+ "image/png"
8409
+ );
8410
+ const filename = `image.${getExtensionFromMediaType(mediaType)}`;
8411
+ const attachment = toAttachment(node.image_url.url, mediaType, filename);
8412
+ if (attachment) {
8413
+ return {
8414
+ ...node,
8415
+ image_url: {
8416
+ ...node.image_url,
8417
+ url: attachment
8418
+ }
8419
+ };
8420
+ }
8421
+ }
8422
+ if (node.type === "file" && node.file && typeof node.file === "object" && typeof node.file.file_data === "string" && node.file.file_data.startsWith("data:")) {
8423
+ const mediaType = inferMediaTypeFromDataUrl(
8424
+ node.file.file_data,
8425
+ "application/octet-stream"
8426
+ );
8427
+ const filename = typeof node.file.filename === "string" && node.file.filename ? node.file.filename : `document.${getExtensionFromMediaType(mediaType)}`;
8428
+ const attachment = toAttachment(node.file.file_data, mediaType, filename);
8429
+ if (attachment) {
8430
+ return {
8431
+ ...node,
8432
+ file: {
8433
+ ...node.file,
8434
+ file_data: attachment
8435
+ }
8436
+ };
8437
+ }
8438
+ }
8439
+ if (node.type === "image" && node.image) {
8440
+ let mediaType = "image/png";
8441
+ if (typeof node.image === "string" && node.image.startsWith("data:")) {
8442
+ mediaType = inferMediaTypeFromDataUrl(node.image, mediaType);
8443
+ } else if (node.mediaType) {
8444
+ mediaType = node.mediaType;
8445
+ }
8446
+ const filename = `input_image_${attachmentIndex}.${getExtensionFromMediaType(mediaType)}`;
8447
+ const attachment = toAttachment(node.image, mediaType, filename);
8448
+ if (attachment) {
8449
+ attachmentIndex++;
8450
+ return {
8451
+ ...node,
8452
+ image: attachment
8453
+ };
8454
+ }
8455
+ }
8456
+ if (node.type === "file" && node.data) {
8457
+ const mediaType = node.mediaType || "application/octet-stream";
8458
+ const filename = node.filename || `input_file_${attachmentIndex}.${getExtensionFromMediaType(mediaType)}`;
8459
+ const attachment = toAttachment(node.data, mediaType, filename);
8460
+ if (attachment) {
8461
+ attachmentIndex++;
8462
+ return {
8463
+ ...node,
8464
+ data: attachment
8465
+ };
8466
+ }
8467
+ }
8468
+ const processed = {};
8469
+ for (const [key, value] of Object.entries(node)) {
8470
+ processed[key] = processNode(value);
8471
+ }
8472
+ return processed;
8473
+ };
8474
+ if (Array.isArray(input)) {
8475
+ return input.map(processNode);
8476
+ }
8477
+ return processNode(input);
8478
+ }
8479
+
8480
+ // src/instrumentation/core/channel-definitions.ts
8481
+ function channel(spec) {
8482
+ return spec;
8483
+ }
8484
+ function defineChannels(pkg, channels) {
8485
+ return Object.fromEntries(
8486
+ Object.entries(channels).map(([key, spec]) => {
8487
+ const fullChannelName = `orchestrion:${pkg}:${spec.channelName}`;
8488
+ if (spec.kind === "async") {
8489
+ const asyncSpec = spec;
8490
+ const tracingChannel2 = () => isomorph_default.newTracingChannel(
8491
+ fullChannelName
8492
+ );
8493
+ return [
8494
+ key,
8495
+ {
8496
+ ...asyncSpec,
8497
+ tracingChannel: tracingChannel2,
8498
+ tracePromise: (fn, context) => tracingChannel2().tracePromise(
8499
+ fn,
8500
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
8501
+ context
8502
+ )
8503
+ }
8504
+ ];
8505
+ }
8506
+ const syncSpec = spec;
8507
+ const tracingChannel = () => isomorph_default.newTracingChannel(
8508
+ fullChannelName
8509
+ );
8510
+ return [
8511
+ key,
8512
+ {
8513
+ ...syncSpec,
8514
+ tracingChannel,
8515
+ traceSync: (fn, context) => tracingChannel().traceSync(
8516
+ fn,
8517
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
8518
+ context
8519
+ )
8520
+ }
8521
+ ];
8522
+ })
8523
+ );
8524
+ }
8525
+
8526
+ // src/instrumentation/plugins/openai-channels.ts
8527
+ var openAIChannels = defineChannels("openai", {
8528
+ chatCompletionsCreate: channel({
8529
+ channelName: "chat.completions.create",
8530
+ kind: "async"
8531
+ }),
8532
+ embeddingsCreate: channel({
8533
+ channelName: "embeddings.create",
8534
+ kind: "async"
8535
+ }),
8536
+ betaChatCompletionsParse: channel({
8537
+ channelName: "beta.chat.completions.parse",
8538
+ kind: "async"
8539
+ }),
8540
+ betaChatCompletionsStream: channel({
8541
+ channelName: "beta.chat.completions.stream",
8542
+ kind: "sync-stream"
8543
+ }),
8544
+ moderationsCreate: channel({
8545
+ channelName: "moderations.create",
8546
+ kind: "async"
8547
+ }),
8548
+ responsesCreate: channel({
8549
+ channelName: "responses.create",
8550
+ kind: "async"
8551
+ }),
8552
+ responsesStream: channel({
8553
+ channelName: "responses.stream",
8554
+ kind: "sync-stream"
8555
+ }),
8556
+ responsesParse: channel({
8557
+ channelName: "responses.parse",
8558
+ kind: "async"
8559
+ })
8560
+ });
8561
+
8562
+ // src/openai-utils.ts
8563
+ var BRAINTRUST_CACHED_STREAM_METRIC = "__braintrust_cached_metric";
8564
+ var LEGACY_CACHED_HEADER = "x-cached";
8565
+ var X_CACHED_HEADER = "x-bt-cached";
8566
+ var TOKEN_NAME_MAP = {
8567
+ input_tokens: "prompt_tokens",
8568
+ output_tokens: "completion_tokens",
8569
+ total_tokens: "tokens"
8570
+ };
8571
+ var TOKEN_PREFIX_MAP = {
8572
+ input: "prompt",
8573
+ output: "completion"
8574
+ };
8575
+ function parseMetricsFromUsage(usage) {
8576
+ if (!usage) {
8577
+ return {};
8578
+ }
8579
+ const metrics = {};
8580
+ for (const [oaiName, value] of Object.entries(usage)) {
8581
+ if (typeof value === "number") {
8582
+ const metricName = TOKEN_NAME_MAP[oaiName] || oaiName;
8583
+ metrics[metricName] = value;
8584
+ continue;
8585
+ }
8586
+ if (!oaiName.endsWith("_tokens_details") || !isObject(value)) {
8587
+ continue;
8588
+ }
8589
+ const rawPrefix = oaiName.slice(0, -"_tokens_details".length);
8590
+ const prefix = TOKEN_PREFIX_MAP[rawPrefix] || rawPrefix;
8591
+ for (const [key, nestedValue] of Object.entries(value)) {
8592
+ if (typeof nestedValue !== "number") {
8593
+ continue;
8594
+ }
8595
+ metrics[`${prefix}_${key}`] = nestedValue;
8596
+ }
8597
+ }
8598
+ return metrics;
8599
+ }
8600
+ function parseCachedHeader(value) {
8601
+ if (!value) {
8602
+ return void 0;
8603
+ }
8604
+ return ["true", "hit"].includes(value.toLowerCase()) ? 1 : 0;
8605
+ }
8606
+ function getCachedMetricFromHeaders(headers) {
8607
+ if (!headers || typeof headers.get !== "function") {
8608
+ return void 0;
8609
+ }
8610
+ const cachedHeader = headers.get(X_CACHED_HEADER);
8611
+ if (cachedHeader) {
8612
+ return parseCachedHeader(cachedHeader);
8613
+ }
8614
+ return parseCachedHeader(headers.get(LEGACY_CACHED_HEADER));
8615
+ }
8616
+
8617
+ // src/instrumentation/plugins/openai-plugin.ts
8618
+ var OpenAIPlugin = class extends BasePlugin {
8619
+ constructor() {
7876
8620
  super();
7877
8621
  }
7878
8622
  onEnable() {
7879
- this.subscribeToStreamingChannel(
7880
- "orchestrion:openai:chat.completions.create",
7881
- {
8623
+ this.unsubscribers.push(
8624
+ traceStreamingChannel(openAIChannels.chatCompletionsCreate, {
7882
8625
  name: "Chat Completion",
7883
8626
  type: "llm" /* LLM */,
7884
- extractInput: (args) => {
7885
- const params = args[0] || {};
8627
+ extractInput: ([params]) => {
7886
8628
  const { messages, ...metadata } = params;
7887
8629
  return {
7888
8630
  input: processInputAttachments(messages),
@@ -7892,41 +8634,49 @@ var OpenAIPlugin = class extends BasePlugin {
7892
8634
  extractOutput: (result) => {
7893
8635
  return result?.choices;
7894
8636
  },
7895
- extractMetrics: (result, startTime) => {
7896
- const metrics = parseMetricsFromUsage(result?.usage);
8637
+ extractMetrics: (result, startTime, endEvent) => {
8638
+ const metrics = withCachedMetric(
8639
+ parseMetricsFromUsage(result?.usage),
8640
+ result,
8641
+ endEvent
8642
+ );
7897
8643
  if (startTime) {
7898
8644
  metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
7899
8645
  }
7900
8646
  return metrics;
7901
8647
  },
7902
8648
  aggregateChunks: aggregateChatCompletionChunks
7903
- }
8649
+ })
7904
8650
  );
7905
- this.subscribeToChannel("orchestrion:openai:embeddings.create", {
7906
- name: "Embedding",
7907
- type: "llm" /* LLM */,
7908
- extractInput: (args) => {
7909
- const params = args[0] || {};
7910
- const { input, ...metadata } = params;
7911
- return {
7912
- input,
7913
- metadata: { ...metadata, provider: "openai" }
7914
- };
7915
- },
7916
- extractOutput: (result) => {
7917
- return result?.data?.map((d) => d.embedding);
7918
- },
7919
- extractMetrics: (result) => {
7920
- return parseMetricsFromUsage(result?.usage);
7921
- }
7922
- });
7923
- this.subscribeToStreamingChannel(
7924
- "orchestrion:openai:beta.chat.completions.parse",
7925
- {
8651
+ this.unsubscribers.push(
8652
+ traceAsyncChannel(openAIChannels.embeddingsCreate, {
8653
+ name: "Embedding",
8654
+ type: "llm" /* LLM */,
8655
+ extractInput: ([params]) => {
8656
+ const { input, ...metadata } = params;
8657
+ return {
8658
+ input,
8659
+ metadata: { ...metadata, provider: "openai" }
8660
+ };
8661
+ },
8662
+ extractOutput: (result) => {
8663
+ const embedding = result?.data?.[0]?.embedding;
8664
+ return Array.isArray(embedding) ? { embedding_length: embedding.length } : void 0;
8665
+ },
8666
+ extractMetrics: (result, _startTime, endEvent) => {
8667
+ return withCachedMetric(
8668
+ parseMetricsFromUsage(result?.usage),
8669
+ result,
8670
+ endEvent
8671
+ );
8672
+ }
8673
+ })
8674
+ );
8675
+ this.unsubscribers.push(
8676
+ traceStreamingChannel(openAIChannels.betaChatCompletionsParse, {
7926
8677
  name: "Chat Completion",
7927
8678
  type: "llm" /* LLM */,
7928
- extractInput: (args) => {
7929
- const params = args[0] || {};
8679
+ extractInput: ([params]) => {
7930
8680
  const { messages, ...metadata } = params;
7931
8681
  return {
7932
8682
  input: processInputAttachments(messages),
@@ -7936,164 +8686,196 @@ var OpenAIPlugin = class extends BasePlugin {
7936
8686
  extractOutput: (result) => {
7937
8687
  return result?.choices;
7938
8688
  },
7939
- extractMetrics: (result, startTime) => {
7940
- const metrics = parseMetricsFromUsage(result?.usage);
8689
+ extractMetrics: (result, startTime, endEvent) => {
8690
+ const metrics = withCachedMetric(
8691
+ parseMetricsFromUsage(result?.usage),
8692
+ result,
8693
+ endEvent
8694
+ );
7941
8695
  if (startTime) {
7942
8696
  metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
7943
8697
  }
7944
8698
  return metrics;
7945
8699
  },
7946
8700
  aggregateChunks: aggregateChatCompletionChunks
7947
- }
8701
+ })
7948
8702
  );
7949
- this.subscribeToSyncStreamChannel(
7950
- "orchestrion:openai:beta.chat.completions.stream",
7951
- {
8703
+ this.unsubscribers.push(
8704
+ traceSyncStreamChannel(openAIChannels.betaChatCompletionsStream, {
7952
8705
  name: "Chat Completion",
7953
8706
  type: "llm" /* LLM */,
7954
- extractInput: (args) => {
7955
- const params = args[0] || {};
8707
+ extractInput: ([params]) => {
7956
8708
  const { messages, ...metadata } = params;
7957
8709
  return {
7958
8710
  input: processInputAttachments(messages),
7959
8711
  metadata: { ...metadata, provider: "openai" }
7960
8712
  };
7961
8713
  }
7962
- }
8714
+ })
7963
8715
  );
7964
- this.subscribeToChannel("orchestrion:openai:moderations.create", {
7965
- name: "Moderation",
7966
- type: "llm" /* LLM */,
7967
- extractInput: (args) => {
7968
- const params = args[0] || {};
7969
- const { input, ...metadata } = params;
7970
- return {
7971
- input,
7972
- metadata: { ...metadata, provider: "openai" }
7973
- };
7974
- },
7975
- extractOutput: (result) => {
7976
- return result?.results;
7977
- },
7978
- extractMetrics: () => {
7979
- return {};
7980
- }
7981
- });
7982
- this.subscribeToStreamingChannel("orchestrion:openai:responses.create", {
7983
- name: "openai.responses.create",
7984
- type: "llm" /* LLM */,
7985
- extractInput: (args) => {
7986
- const params = args[0] || {};
7987
- const { input, ...metadata } = params;
7988
- return {
7989
- input: processInputAttachments(input),
7990
- metadata: { ...metadata, provider: "openai" }
7991
- };
7992
- },
7993
- extractOutput: (result) => {
7994
- return processImagesInOutput(result?.output);
7995
- },
7996
- extractMetrics: (result, startTime) => {
7997
- const metrics = parseMetricsFromUsage(result?.usage);
7998
- if (startTime) {
7999
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8000
- }
8001
- return metrics;
8002
- }
8003
- });
8004
- this.subscribeToSyncStreamChannel("orchestrion:openai:responses.stream", {
8005
- name: "openai.responses.stream",
8006
- type: "llm" /* LLM */,
8007
- extractInput: (args) => {
8008
- const params = args[0] || {};
8009
- const { input, ...metadata } = params;
8010
- return {
8011
- input: processInputAttachments(input),
8012
- metadata: { ...metadata, provider: "openai" }
8013
- };
8014
- },
8015
- extractFromEvent: (event) => {
8016
- if (!event || !event.type || !event.response) {
8017
- return {};
8716
+ this.unsubscribers.push(
8717
+ traceAsyncChannel(openAIChannels.moderationsCreate, {
8718
+ name: "Moderation",
8719
+ type: "llm" /* LLM */,
8720
+ extractInput: ([params]) => {
8721
+ const { input, ...metadata } = params;
8722
+ return {
8723
+ input,
8724
+ metadata: { ...metadata, provider: "openai" }
8725
+ };
8726
+ },
8727
+ extractOutput: (result) => {
8728
+ return result?.results;
8729
+ },
8730
+ extractMetrics: (result, _startTime, endEvent) => {
8731
+ return withCachedMetric(
8732
+ parseMetricsFromUsage(result?.usage),
8733
+ result,
8734
+ endEvent
8735
+ );
8018
8736
  }
8019
- const response = event.response;
8020
- if (event.type === "response.completed") {
8737
+ })
8738
+ );
8739
+ this.unsubscribers.push(
8740
+ traceStreamingChannel(openAIChannels.responsesCreate, {
8741
+ name: "openai.responses.create",
8742
+ type: "llm" /* LLM */,
8743
+ extractInput: ([params]) => {
8744
+ const { input, ...metadata } = params;
8745
+ return {
8746
+ input: processInputAttachments(input),
8747
+ metadata: { ...metadata, provider: "openai" }
8748
+ };
8749
+ },
8750
+ extractOutput: (result) => {
8751
+ return processImagesInOutput(result?.output);
8752
+ },
8753
+ extractMetadata: (result) => {
8754
+ if (!result) {
8755
+ return void 0;
8756
+ }
8757
+ const { output: _output, usage: _usage, ...metadata } = result;
8758
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
8759
+ },
8760
+ extractMetrics: (result, startTime, endEvent) => {
8761
+ const metrics = withCachedMetric(
8762
+ parseMetricsFromUsage(result?.usage),
8763
+ result,
8764
+ endEvent
8765
+ );
8766
+ if (startTime) {
8767
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8768
+ }
8769
+ return metrics;
8770
+ },
8771
+ aggregateChunks: aggregateResponseStreamEvents
8772
+ })
8773
+ );
8774
+ this.unsubscribers.push(
8775
+ traceSyncStreamChannel(openAIChannels.responsesStream, {
8776
+ name: "openai.responses.create",
8777
+ type: "llm" /* LLM */,
8778
+ extractInput: ([params]) => {
8779
+ const { input, ...metadata } = params;
8780
+ return {
8781
+ input: processInputAttachments(input),
8782
+ metadata: { ...metadata, provider: "openai" }
8783
+ };
8784
+ },
8785
+ extractFromEvent: (event) => {
8786
+ if (event.type !== "response.completed" || !event.response) {
8787
+ return {};
8788
+ }
8789
+ const response = event.response;
8021
8790
  const data = {};
8022
- if (response?.output !== void 0) {
8791
+ if (response.output !== void 0) {
8023
8792
  data.output = processImagesInOutput(response.output);
8024
8793
  }
8025
- if (response) {
8026
- const { usage: _usage, output: _output, ...metadata } = response;
8027
- if (Object.keys(metadata).length > 0) {
8028
- data.metadata = metadata;
8029
- }
8794
+ const { usage: _usage, output: _output, ...metadata } = response;
8795
+ if (Object.keys(metadata).length > 0) {
8796
+ data.metadata = metadata;
8030
8797
  }
8031
- data.metrics = parseMetricsFromUsage(response?.usage);
8798
+ data.metrics = parseMetricsFromUsage(response.usage);
8032
8799
  return data;
8033
8800
  }
8034
- return {};
8035
- }
8036
- });
8037
- this.subscribeToStreamingChannel("orchestrion:openai:responses.parse", {
8038
- name: "openai.responses.parse",
8039
- type: "llm" /* LLM */,
8040
- extractInput: (args) => {
8041
- const params = args[0] || {};
8042
- const { input, ...metadata } = params;
8043
- return {
8044
- input: processInputAttachments(input),
8045
- metadata: { ...metadata, provider: "openai" }
8046
- };
8047
- },
8048
- extractOutput: (result) => {
8049
- return processImagesInOutput(result?.output);
8050
- },
8051
- extractMetrics: (result, startTime) => {
8052
- const metrics = parseMetricsFromUsage(result?.usage);
8053
- if (startTime) {
8054
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8055
- }
8056
- return metrics;
8057
- }
8058
- });
8801
+ })
8802
+ );
8803
+ this.unsubscribers.push(
8804
+ traceStreamingChannel(openAIChannels.responsesParse, {
8805
+ name: "openai.responses.parse",
8806
+ type: "llm" /* LLM */,
8807
+ extractInput: ([params]) => {
8808
+ const { input, ...metadata } = params;
8809
+ return {
8810
+ input: processInputAttachments(input),
8811
+ metadata: { ...metadata, provider: "openai" }
8812
+ };
8813
+ },
8814
+ extractOutput: (result) => {
8815
+ return processImagesInOutput(result?.output);
8816
+ },
8817
+ extractMetadata: (result) => {
8818
+ if (!result) {
8819
+ return void 0;
8820
+ }
8821
+ const { output: _output, usage: _usage, ...metadata } = result;
8822
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
8823
+ },
8824
+ extractMetrics: (result, startTime, endEvent) => {
8825
+ const metrics = withCachedMetric(
8826
+ parseMetricsFromUsage(result?.usage),
8827
+ result,
8828
+ endEvent
8829
+ );
8830
+ if (startTime) {
8831
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8832
+ }
8833
+ return metrics;
8834
+ },
8835
+ aggregateChunks: aggregateResponseStreamEvents
8836
+ })
8837
+ );
8059
8838
  }
8060
8839
  onDisable() {
8840
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
8061
8841
  }
8062
8842
  };
8063
- var TOKEN_NAME_MAP = {
8064
- input_tokens: "prompt_tokens",
8065
- output_tokens: "completion_tokens",
8066
- total_tokens: "tokens"
8067
- };
8068
- var TOKEN_PREFIX_MAP = {
8069
- input: "prompt",
8070
- output: "completion"
8071
- };
8072
- function parseMetricsFromUsage(usage) {
8073
- if (!usage) {
8074
- return {};
8843
+ function getCachedMetricFromEndEvent(endEvent) {
8844
+ if (!isObject(endEvent)) {
8845
+ return void 0;
8075
8846
  }
8076
- const metrics = {};
8077
- for (const [oai_name, value] of Object.entries(usage)) {
8078
- if (typeof value === "number") {
8079
- const metricName = TOKEN_NAME_MAP[oai_name] || oai_name;
8080
- metrics[metricName] = value;
8081
- } else if (oai_name.endsWith("_tokens_details")) {
8082
- if (!isObject(value)) {
8083
- continue;
8084
- }
8085
- const rawPrefix = oai_name.slice(0, -"_tokens_details".length);
8086
- const prefix = TOKEN_PREFIX_MAP[rawPrefix] || rawPrefix;
8087
- for (const [key, n] of Object.entries(value)) {
8088
- if (typeof n !== "number") {
8089
- continue;
8090
- }
8091
- const metricName = `${prefix}_${key}`;
8092
- metrics[metricName] = n;
8093
- }
8094
- }
8847
+ const response = endEvent.response;
8848
+ if (!isObject(response)) {
8849
+ return void 0;
8095
8850
  }
8096
- return metrics;
8851
+ const headers = response.headers;
8852
+ if (!headers || typeof headers.get !== "function") {
8853
+ return void 0;
8854
+ }
8855
+ return getCachedMetricFromHeaders(headers);
8856
+ }
8857
+ function withCachedMetric(metrics, result, endEvent) {
8858
+ if (metrics.cached !== void 0) {
8859
+ return metrics;
8860
+ }
8861
+ const cachedFromEvent = getCachedMetricFromEndEvent(endEvent);
8862
+ if (cachedFromEvent !== void 0) {
8863
+ return {
8864
+ ...metrics,
8865
+ cached: cachedFromEvent
8866
+ };
8867
+ }
8868
+ if (!isObject(result)) {
8869
+ return metrics;
8870
+ }
8871
+ const cached = result[BRAINTRUST_CACHED_STREAM_METRIC];
8872
+ if (typeof cached !== "number") {
8873
+ return metrics;
8874
+ }
8875
+ return {
8876
+ ...metrics,
8877
+ cached
8878
+ };
8097
8879
  }
8098
8880
  function processImagesInOutput(output) {
8099
8881
  if (Array.isArray(output)) {
@@ -8124,7 +8906,7 @@ function processImagesInOutput(output) {
8124
8906
  }
8125
8907
  return output;
8126
8908
  }
8127
- function aggregateChatCompletionChunks(chunks) {
8909
+ function aggregateChatCompletionChunks(chunks, streamResult, endEvent) {
8128
8910
  let role = void 0;
8129
8911
  let content = void 0;
8130
8912
  let tool_calls = void 0;
@@ -8166,6 +8948,7 @@ function aggregateChatCompletionChunks(chunks) {
8166
8948
  }
8167
8949
  }
8168
8950
  }
8951
+ metrics = withCachedMetric(metrics, streamResult, endEvent);
8169
8952
  return {
8170
8953
  metrics,
8171
8954
  output: [
@@ -8182,9 +8965,33 @@ function aggregateChatCompletionChunks(chunks) {
8182
8965
  ]
8183
8966
  };
8184
8967
  }
8185
-
8186
- // src/instrumentation/plugins/anthropic-plugin.ts
8187
- var import_dc_browser2 = require("dc-browser");
8968
+ function aggregateResponseStreamEvents(chunks, _streamResult, endEvent) {
8969
+ let output = void 0;
8970
+ let metrics = {};
8971
+ let metadata = void 0;
8972
+ for (const chunk of chunks) {
8973
+ if (!chunk || !chunk.type || !chunk.response) {
8974
+ continue;
8975
+ }
8976
+ if (chunk.type !== "response.completed") {
8977
+ continue;
8978
+ }
8979
+ const response = chunk.response;
8980
+ if (response?.output !== void 0) {
8981
+ output = processImagesInOutput(response.output);
8982
+ }
8983
+ const { usage: _usage, output: _output, ...rest } = response || {};
8984
+ if (Object.keys(rest).length > 0) {
8985
+ metadata = rest;
8986
+ }
8987
+ metrics = parseMetricsFromUsage(response?.usage);
8988
+ }
8989
+ return {
8990
+ output,
8991
+ metrics: withCachedMetric(metrics, void 0, endEvent),
8992
+ ...metadata !== void 0 ? { metadata } : {}
8993
+ };
8994
+ }
8188
8995
 
8189
8996
  // src/wrappers/anthropic-tokens-util.ts
8190
8997
  function finalizeAnthropicTokens(metrics) {
@@ -8205,216 +9012,76 @@ function extractAnthropicCacheTokens(cacheReadTokens = 0, cacheCreationTokens =
8205
9012
  }
8206
9013
  return cacheTokens;
8207
9014
  }
8208
-
8209
- // src/instrumentation/plugins/anthropic-plugin.ts
8210
- var AnthropicPlugin = class extends BasePlugin {
8211
- unsubscribers = [];
8212
- onEnable() {
8213
- this.subscribeToAnthropicChannels();
8214
- }
8215
- onDisable() {
8216
- for (const unsubscribe of this.unsubscribers) {
8217
- unsubscribe();
8218
- }
8219
- this.unsubscribers = [];
8220
- }
8221
- subscribeToAnthropicChannels() {
8222
- this.subscribeToStreamingChannel("orchestrion:anthropic:messages.create", {
8223
- name: "anthropic.messages.create",
8224
- type: "llm" /* LLM */,
8225
- extractInput: (args) => {
8226
- const params = args[0] || {};
8227
- const input = coalesceInput(params.messages || [], params.system);
8228
- const metadata = filterFrom(params, ["messages", "system"]);
8229
- return {
8230
- input: processAttachmentsInInput(input),
8231
- metadata: { ...metadata, provider: "anthropic" }
8232
- };
8233
- },
8234
- extractOutput: (result) => {
8235
- return result ? { role: result.role, content: result.content } : null;
8236
- },
8237
- extractMetrics: (result, startTime) => {
8238
- const metrics = parseMetricsFromUsage2(result?.usage);
8239
- if (startTime) {
8240
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8241
- }
8242
- const finalized = finalizeAnthropicTokens(metrics);
8243
- return Object.fromEntries(
8244
- Object.entries(finalized).filter(([, v]) => v !== void 0)
8245
- );
8246
- },
8247
- extractMetadata: (result) => {
8248
- const metadata = {};
8249
- const metas = ["stop_reason", "stop_sequence"];
8250
- for (const m of metas) {
8251
- if (result?.[m] !== void 0) {
8252
- metadata[m] = result[m];
8253
- }
8254
- }
8255
- return metadata;
8256
- },
8257
- aggregateChunks: aggregateAnthropicStreamChunks,
8258
- isStreaming: (args) => {
8259
- return args[0]?.stream === true;
8260
- }
8261
- });
8262
- this.subscribeToStreamingChannel(
8263
- "orchestrion:anthropic:beta.messages.create",
8264
- {
8265
- name: "anthropic.beta.messages.create",
8266
- type: "llm" /* LLM */,
8267
- extractInput: (args) => {
8268
- const params = args[0] || {};
8269
- const input = coalesceInput(params.messages || [], params.system);
8270
- const metadata = filterFrom(params, ["messages", "system"]);
8271
- return {
8272
- input: processAttachmentsInInput(input),
8273
- metadata: { ...metadata, provider: "anthropic" }
8274
- };
8275
- },
8276
- extractOutput: (result) => {
8277
- return result ? { role: result.role, content: result.content } : null;
8278
- },
8279
- extractMetrics: (result, startTime) => {
8280
- const metrics = parseMetricsFromUsage2(result?.usage);
8281
- if (startTime) {
8282
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8283
- }
8284
- const finalized = finalizeAnthropicTokens(metrics);
8285
- return Object.fromEntries(
8286
- Object.entries(finalized).filter(([, v]) => v !== void 0)
8287
- );
8288
- },
8289
- extractMetadata: (result) => {
8290
- const metadata = {};
8291
- const metas = ["stop_reason", "stop_sequence"];
8292
- for (const m of metas) {
8293
- if (result?.[m] !== void 0) {
8294
- metadata[m] = result[m];
8295
- }
8296
- }
8297
- return metadata;
8298
- },
8299
- aggregateChunks: aggregateAnthropicStreamChunks,
8300
- isStreaming: (args) => {
8301
- return args[0]?.stream === true;
8302
- }
8303
- }
8304
- );
8305
- }
8306
- /**
8307
- * Subscribe to a channel for async methods that may return streams.
8308
- * Handles both streaming and non-streaming responses based on the stream parameter.
8309
- */
8310
- subscribeToStreamingChannel(channelName, config) {
8311
- const channel = (0, import_dc_browser2.tracingChannel)(channelName);
8312
- const spans = /* @__PURE__ */ new WeakMap();
8313
- const handlers = {
8314
- start: (event) => {
8315
- const span = startSpan({
8316
- name: config.name,
8317
- spanAttributes: {
8318
- type: config.type
8319
- }
8320
- });
8321
- const startTime = getCurrentUnixTimestamp();
8322
- spans.set(event, { span, startTime });
8323
- try {
8324
- const { input, metadata } = config.extractInput(event.arguments);
8325
- span.log({
8326
- input,
8327
- metadata
8328
- });
8329
- } catch (error) {
8330
- console.error(`Error extracting input for ${channelName}:`, error);
8331
- }
8332
- },
8333
- asyncEnd: (event) => {
8334
- const spanData = spans.get(event);
8335
- if (!spanData) {
8336
- return;
8337
- }
8338
- const { span, startTime } = spanData;
8339
- const isStreaming = config.isStreaming ? config.isStreaming(event.arguments) : isAsyncIterable(event.result);
8340
- if (isStreaming && isAsyncIterable(event.result)) {
8341
- patchStreamIfNeeded(event.result, {
8342
- onComplete: (chunks) => {
8343
- try {
8344
- let output;
8345
- let metrics;
8346
- let metadata = {};
8347
- if (config.aggregateChunks) {
8348
- const aggregated = config.aggregateChunks(chunks);
8349
- output = aggregated.output;
8350
- metrics = aggregated.metrics;
8351
- metadata = aggregated.metadata || {};
8352
- } else {
8353
- output = config.extractOutput(chunks);
8354
- metrics = config.extractMetrics(chunks, startTime);
8355
- if (config.extractMetadata) {
8356
- metadata = config.extractMetadata(chunks);
8357
- }
8358
- }
8359
- if (!metrics.time_to_first_token && chunks.length > 0) {
8360
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8361
- }
8362
- span.log({
8363
- output,
8364
- metrics,
8365
- metadata
8366
- });
8367
- } catch (error) {
8368
- console.error(
8369
- `Error extracting output for ${channelName}:`,
8370
- error
8371
- );
8372
- } finally {
8373
- span.end();
8374
- }
8375
- },
8376
- onError: (error) => {
8377
- span.log({
8378
- error: error.message
8379
- });
8380
- span.end();
8381
- }
8382
- });
8383
- } else {
8384
- try {
8385
- const output = config.extractOutput(event.result);
8386
- const metrics = config.extractMetrics(event.result, startTime);
8387
- const metadata = config.extractMetadata ? config.extractMetadata(event.result) : {};
8388
- span.log({
8389
- output,
8390
- metrics,
8391
- metadata
8392
- });
8393
- } catch (error) {
8394
- console.error(`Error extracting output for ${channelName}:`, error);
8395
- } finally {
8396
- span.end();
8397
- spans.delete(event);
8398
- }
9015
+
9016
+ // src/instrumentation/plugins/anthropic-channels.ts
9017
+ var anthropicChannels = defineChannels("@anthropic-ai/sdk", {
9018
+ messagesCreate: channel({
9019
+ channelName: "messages.create",
9020
+ kind: "async"
9021
+ }),
9022
+ betaMessagesCreate: channel({
9023
+ channelName: "beta.messages.create",
9024
+ kind: "async"
9025
+ })
9026
+ });
9027
+
9028
+ // src/instrumentation/plugins/anthropic-plugin.ts
9029
+ var AnthropicPlugin = class extends BasePlugin {
9030
+ onEnable() {
9031
+ this.subscribeToAnthropicChannels();
9032
+ }
9033
+ onDisable() {
9034
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
9035
+ }
9036
+ subscribeToAnthropicChannels() {
9037
+ const anthropicConfig = {
9038
+ name: "anthropic.messages.create",
9039
+ type: "llm" /* LLM */,
9040
+ extractInput: (args) => {
9041
+ const params = args[0] || {};
9042
+ const input = coalesceInput(params.messages || [], params.system);
9043
+ const metadata = filterFrom(params, ["messages", "system"]);
9044
+ return {
9045
+ input: processAttachmentsInInput(input),
9046
+ metadata: { ...metadata, provider: "anthropic" }
9047
+ };
9048
+ },
9049
+ extractOutput: (message) => {
9050
+ return message ? { role: message.role, content: message.content } : null;
9051
+ },
9052
+ extractMetrics: (message, startTime) => {
9053
+ const metrics = parseMetricsFromUsage2(message?.usage);
9054
+ if (startTime) {
9055
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8399
9056
  }
9057
+ const finalized = finalizeAnthropicTokens(metrics);
9058
+ return Object.fromEntries(
9059
+ Object.entries(finalized).filter(
9060
+ (entry) => entry[1] !== void 0
9061
+ )
9062
+ );
8400
9063
  },
8401
- error: (event) => {
8402
- const spanData = spans.get(event);
8403
- if (!spanData) {
8404
- return;
9064
+ extractMetadata: (message) => {
9065
+ const metadata = {};
9066
+ const metas = ["stop_reason", "stop_sequence"];
9067
+ for (const m of metas) {
9068
+ if (message?.[m] !== void 0) {
9069
+ metadata[m] = message[m];
9070
+ }
8405
9071
  }
8406
- const { span } = spanData;
8407
- span.log({
8408
- error: event.error.message
8409
- });
8410
- span.end();
8411
- spans.delete(event);
8412
- }
9072
+ return metadata;
9073
+ },
9074
+ aggregateChunks: (chunks) => aggregateAnthropicStreamChunks(chunks)
8413
9075
  };
8414
- channel.subscribe(handlers);
8415
- this.unsubscribers.push(() => {
8416
- channel.unsubscribe(handlers);
8417
- });
9076
+ this.unsubscribers.push(
9077
+ traceStreamingChannel(anthropicChannels.messagesCreate, anthropicConfig)
9078
+ );
9079
+ this.unsubscribers.push(
9080
+ traceStreamingChannel(anthropicChannels.betaMessagesCreate, {
9081
+ ...anthropicConfig,
9082
+ name: "anthropic.beta.messages.create"
9083
+ })
9084
+ );
8418
9085
  }
8419
9086
  };
8420
9087
  function parseMetricsFromUsage2(usage) {
@@ -8438,29 +9105,29 @@ function aggregateAnthropicStreamChunks(chunks) {
8438
9105
  const deltas = [];
8439
9106
  let metrics = {};
8440
9107
  let metadata = {};
8441
- for (const chunk of chunks) {
8442
- switch (chunk?.type) {
9108
+ for (const event of chunks) {
9109
+ switch (event?.type) {
8443
9110
  case "message_start":
8444
- if (chunk.message?.usage) {
8445
- const initialMetrics = parseMetricsFromUsage2(chunk.message.usage);
9111
+ if (event.message?.usage) {
9112
+ const initialMetrics = parseMetricsFromUsage2(event.message.usage);
8446
9113
  metrics = { ...metrics, ...initialMetrics };
8447
9114
  }
8448
9115
  break;
8449
9116
  case "content_block_delta":
8450
- if (chunk.delta?.type === "text_delta") {
8451
- const text = chunk.delta?.text;
9117
+ if (event.delta?.type === "text_delta") {
9118
+ const text = event.delta.text;
8452
9119
  if (text) {
8453
9120
  deltas.push(text);
8454
9121
  }
8455
9122
  }
8456
9123
  break;
8457
9124
  case "message_delta":
8458
- if (chunk.usage) {
8459
- const finalMetrics = parseMetricsFromUsage2(chunk.usage);
9125
+ if (event.usage) {
9126
+ const finalMetrics = parseMetricsFromUsage2(event.usage);
8460
9127
  metrics = { ...metrics, ...finalMetrics };
8461
9128
  }
8462
- if (chunk.delta) {
8463
- metadata = { ...metadata, ...chunk.delta };
9129
+ if (event.delta) {
9130
+ metadata = { ...metadata, ...event.delta };
8464
9131
  }
8465
9132
  break;
8466
9133
  }
@@ -8468,7 +9135,9 @@ function aggregateAnthropicStreamChunks(chunks) {
8468
9135
  const output = deltas.join("");
8469
9136
  const finalized = finalizeAnthropicTokens(metrics);
8470
9137
  const filteredMetrics = Object.fromEntries(
8471
- Object.entries(finalized).filter(([, v]) => v !== void 0)
9138
+ Object.entries(finalized).filter(
9139
+ (entry) => entry[1] !== void 0
9140
+ )
8472
9141
  );
8473
9142
  return {
8474
9143
  output,
@@ -8476,6 +9145,9 @@ function aggregateAnthropicStreamChunks(chunks) {
8476
9145
  metadata
8477
9146
  };
8478
9147
  }
9148
+ function isAnthropicBase64ContentBlock(input) {
9149
+ return (input.type === "image" || input.type === "document") && isObject(input.source) && input.source.type === "base64";
9150
+ }
8479
9151
  function convertBase64ToAttachment(source, contentType) {
8480
9152
  const mediaType = typeof source.media_type === "string" ? source.media_type : "image/png";
8481
9153
  const base64Data = source.data;
@@ -8499,14 +9171,14 @@ function convertBase64ToAttachment(source, contentType) {
8499
9171
  data: attachment
8500
9172
  };
8501
9173
  }
8502
- return source;
9174
+ return { ...source };
8503
9175
  }
8504
9176
  function processAttachmentsInInput(input) {
8505
9177
  if (Array.isArray(input)) {
8506
9178
  return input.map(processAttachmentsInInput);
8507
9179
  }
8508
9180
  if (isObject(input)) {
8509
- if ((input.type === "image" || input.type === "document") && isObject(input.source) && input.source.type === "base64") {
9181
+ if (isAnthropicBase64ContentBlock(input)) {
8510
9182
  return {
8511
9183
  ...input,
8512
9184
  source: convertBase64ToAttachment(input.source, input.type)
@@ -8537,8 +9209,35 @@ function filterFrom(obj, fieldsToRemove) {
8537
9209
  return result;
8538
9210
  }
8539
9211
 
9212
+ // src/instrumentation/plugins/ai-sdk-channels.ts
9213
+ var aiSDKChannels = defineChannels("ai", {
9214
+ generateText: channel({
9215
+ channelName: "generateText",
9216
+ kind: "async"
9217
+ }),
9218
+ streamText: channel({
9219
+ channelName: "streamText",
9220
+ kind: "async"
9221
+ }),
9222
+ generateObject: channel({
9223
+ channelName: "generateObject",
9224
+ kind: "async"
9225
+ }),
9226
+ streamObject: channel({
9227
+ channelName: "streamObject",
9228
+ kind: "async"
9229
+ }),
9230
+ agentGenerate: channel({
9231
+ channelName: "Agent.generate",
9232
+ kind: "async"
9233
+ }),
9234
+ agentStream: channel({
9235
+ channelName: "Agent.stream",
9236
+ kind: "async"
9237
+ })
9238
+ });
9239
+
8540
9240
  // src/instrumentation/plugins/ai-sdk-plugin.ts
8541
- var import_dc_browser3 = require("dc-browser");
8542
9241
  var DEFAULT_DENY_OUTPUT_PATHS = [
8543
9242
  // v3
8544
9243
  "roundtrips[].request.body",
@@ -8554,7 +9253,6 @@ var DEFAULT_DENY_OUTPUT_PATHS = [
8554
9253
  "steps[].response.headers"
8555
9254
  ];
8556
9255
  var AISDKPlugin = class extends BasePlugin {
8557
- unsubscribers = [];
8558
9256
  config;
8559
9257
  constructor(config = {}) {
8560
9258
  super();
@@ -8564,249 +9262,148 @@ var AISDKPlugin = class extends BasePlugin {
8564
9262
  this.subscribeToAISDK();
8565
9263
  }
8566
9264
  onDisable() {
8567
- for (const unsubscribe of this.unsubscribers) {
8568
- unsubscribe();
8569
- }
8570
- this.unsubscribers = [];
9265
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
8571
9266
  }
8572
9267
  subscribeToAISDK() {
8573
9268
  const denyOutputPaths = this.config.denyOutputPaths || DEFAULT_DENY_OUTPUT_PATHS;
8574
- this.subscribeToStreamingChannel("orchestrion:ai-sdk:generateText", {
8575
- name: "generateText",
8576
- type: "llm" /* LLM */,
8577
- extractInput: (args) => {
8578
- const params = args[0] || {};
8579
- return {
8580
- input: processAISDKInput(params),
8581
- metadata: extractMetadataFromParams(params)
8582
- };
8583
- },
8584
- extractOutput: (result) => {
8585
- return processAISDKOutput(result, denyOutputPaths);
8586
- },
8587
- extractMetrics: (result, startTime) => {
8588
- const metrics = extractTokenMetrics(result);
8589
- if (startTime) {
8590
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8591
- }
8592
- return metrics;
8593
- },
8594
- aggregateChunks: aggregateAISDKChunks
8595
- });
8596
- this.subscribeToStreamingChannel("orchestrion:ai-sdk:streamText", {
8597
- name: "streamText",
8598
- type: "llm" /* LLM */,
8599
- extractInput: (args) => {
8600
- const params = args[0] || {};
8601
- return {
8602
- input: processAISDKInput(params),
8603
- metadata: extractMetadataFromParams(params)
8604
- };
8605
- },
8606
- extractOutput: (result) => {
8607
- return processAISDKOutput(result, denyOutputPaths);
8608
- },
8609
- extractMetrics: (result, startTime) => {
8610
- const metrics = extractTokenMetrics(result);
8611
- if (startTime) {
8612
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8613
- }
8614
- return metrics;
8615
- },
8616
- aggregateChunks: aggregateAISDKChunks
8617
- });
8618
- this.subscribeToStreamingChannel("orchestrion:ai-sdk:generateObject", {
8619
- name: "generateObject",
8620
- type: "llm" /* LLM */,
8621
- extractInput: (args) => {
8622
- const params = args[0] || {};
8623
- return {
8624
- input: processAISDKInput(params),
8625
- metadata: extractMetadataFromParams(params)
8626
- };
8627
- },
8628
- extractOutput: (result) => {
8629
- return processAISDKOutput(result, denyOutputPaths);
8630
- },
8631
- extractMetrics: (result, startTime) => {
8632
- const metrics = extractTokenMetrics(result);
8633
- if (startTime) {
8634
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8635
- }
8636
- return metrics;
8637
- },
8638
- aggregateChunks: aggregateAISDKChunks
8639
- });
8640
- this.subscribeToStreamingChannel("orchestrion:ai-sdk:streamObject", {
8641
- name: "streamObject",
8642
- type: "llm" /* LLM */,
8643
- extractInput: (args) => {
8644
- const params = args[0] || {};
8645
- return {
8646
- input: processAISDKInput(params),
8647
- metadata: extractMetadataFromParams(params)
8648
- };
8649
- },
8650
- extractOutput: (result) => {
8651
- return processAISDKOutput(result, denyOutputPaths);
8652
- },
8653
- extractMetrics: (result, startTime) => {
8654
- const metrics = extractTokenMetrics(result);
8655
- if (startTime) {
8656
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8657
- }
8658
- return metrics;
8659
- },
8660
- aggregateChunks: aggregateAISDKChunks
8661
- });
8662
- this.subscribeToStreamingChannel("orchestrion:ai-sdk:Agent.generate", {
8663
- name: "Agent.generate",
8664
- type: "llm" /* LLM */,
8665
- extractInput: (args) => {
8666
- const params = args[0] || {};
8667
- return {
8668
- input: processAISDKInput(params),
8669
- metadata: extractMetadataFromParams(params)
8670
- };
8671
- },
8672
- extractOutput: (result) => {
8673
- return processAISDKOutput(result, denyOutputPaths);
8674
- },
8675
- extractMetrics: (result, startTime) => {
8676
- const metrics = extractTokenMetrics(result);
8677
- if (startTime) {
8678
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8679
- }
8680
- return metrics;
8681
- },
8682
- aggregateChunks: aggregateAISDKChunks
8683
- });
8684
- this.subscribeToStreamingChannel("orchestrion:ai-sdk:Agent.stream", {
8685
- name: "Agent.stream",
8686
- type: "llm" /* LLM */,
8687
- extractInput: (args) => {
8688
- const params = args[0] || {};
8689
- return {
8690
- input: processAISDKInput(params),
8691
- metadata: extractMetadataFromParams(params)
8692
- };
8693
- },
8694
- extractOutput: (result) => {
8695
- return processAISDKOutput(result, denyOutputPaths);
8696
- },
8697
- extractMetrics: (result, startTime) => {
8698
- const metrics = extractTokenMetrics(result);
8699
- if (startTime) {
8700
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8701
- }
8702
- return metrics;
8703
- },
8704
- aggregateChunks: aggregateAISDKChunks
8705
- });
8706
- }
8707
- /**
8708
- * Subscribe to a channel for async methods that may return streams.
8709
- * Handles both streaming and non-streaming responses.
8710
- */
8711
- subscribeToStreamingChannel(channelName, config) {
8712
- const channel = (0, import_dc_browser3.tracingChannel)(channelName);
8713
- const spans = /* @__PURE__ */ new WeakMap();
8714
- const handlers = {
8715
- start: (event) => {
8716
- const span = startSpan({
8717
- name: config.name,
8718
- spanAttributes: {
8719
- type: config.type
9269
+ this.unsubscribers.push(
9270
+ traceStreamingChannel(aiSDKChannels.generateText, {
9271
+ name: "generateText",
9272
+ type: "llm" /* LLM */,
9273
+ extractInput: ([params]) => {
9274
+ return {
9275
+ input: processAISDKInput(params),
9276
+ metadata: extractMetadataFromParams(params)
9277
+ };
9278
+ },
9279
+ extractOutput: (result) => {
9280
+ return processAISDKOutput(result, denyOutputPaths);
9281
+ },
9282
+ extractMetrics: (result, startTime) => {
9283
+ const metrics = extractTokenMetrics(result);
9284
+ if (startTime) {
9285
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8720
9286
  }
8721
- });
8722
- const startTime = getCurrentUnixTimestamp();
8723
- spans.set(event, { span, startTime });
8724
- try {
8725
- const { input, metadata } = config.extractInput(event.arguments);
8726
- span.log({
8727
- input,
8728
- metadata
8729
- });
8730
- } catch (error) {
8731
- console.error(`Error extracting input for ${channelName}:`, error);
8732
- }
8733
- },
8734
- asyncEnd: (event) => {
8735
- const spanData = spans.get(event);
8736
- if (!spanData) {
8737
- return;
8738
- }
8739
- const { span, startTime } = spanData;
8740
- if (isAsyncIterable(event.result)) {
8741
- patchStreamIfNeeded(event.result, {
8742
- onComplete: (chunks) => {
8743
- try {
8744
- let output;
8745
- let metrics;
8746
- if (config.aggregateChunks) {
8747
- const aggregated = config.aggregateChunks(chunks);
8748
- output = aggregated.output;
8749
- metrics = aggregated.metrics;
8750
- } else {
8751
- output = config.extractOutput(chunks);
8752
- metrics = config.extractMetrics(chunks, startTime);
8753
- }
8754
- if (!metrics.time_to_first_token && chunks.length > 0) {
8755
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8756
- }
8757
- span.log({
8758
- output,
8759
- metrics
8760
- });
8761
- } catch (error) {
8762
- console.error(
8763
- `Error extracting output for ${channelName}:`,
8764
- error
8765
- );
8766
- } finally {
8767
- span.end();
8768
- }
8769
- },
8770
- onError: (error) => {
8771
- span.log({
8772
- error: error.message
8773
- });
8774
- span.end();
8775
- }
8776
- });
8777
- } else {
8778
- try {
8779
- const output = config.extractOutput(event.result);
8780
- const metrics = config.extractMetrics(event.result, startTime);
8781
- span.log({
8782
- output,
8783
- metrics
8784
- });
8785
- } catch (error) {
8786
- console.error(`Error extracting output for ${channelName}:`, error);
8787
- } finally {
8788
- span.end();
8789
- spans.delete(event);
9287
+ return metrics;
9288
+ },
9289
+ aggregateChunks: aggregateAISDKChunks
9290
+ })
9291
+ );
9292
+ this.unsubscribers.push(
9293
+ traceStreamingChannel(aiSDKChannels.streamText, {
9294
+ name: "streamText",
9295
+ type: "llm" /* LLM */,
9296
+ extractInput: ([params]) => {
9297
+ return {
9298
+ input: processAISDKInput(params),
9299
+ metadata: extractMetadataFromParams(params)
9300
+ };
9301
+ },
9302
+ extractOutput: (result) => {
9303
+ return processAISDKOutput(result, denyOutputPaths);
9304
+ },
9305
+ extractMetrics: (result, startTime) => {
9306
+ const metrics = extractTokenMetrics(result);
9307
+ if (startTime) {
9308
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9309
+ }
9310
+ return metrics;
9311
+ },
9312
+ aggregateChunks: aggregateAISDKChunks
9313
+ })
9314
+ );
9315
+ this.unsubscribers.push(
9316
+ traceStreamingChannel(aiSDKChannels.generateObject, {
9317
+ name: "generateObject",
9318
+ type: "llm" /* LLM */,
9319
+ extractInput: ([params]) => {
9320
+ return {
9321
+ input: processAISDKInput(params),
9322
+ metadata: extractMetadataFromParams(params)
9323
+ };
9324
+ },
9325
+ extractOutput: (result) => {
9326
+ return processAISDKOutput(result, denyOutputPaths);
9327
+ },
9328
+ extractMetrics: (result, startTime) => {
9329
+ const metrics = extractTokenMetrics(result);
9330
+ if (startTime) {
9331
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9332
+ }
9333
+ return metrics;
9334
+ },
9335
+ aggregateChunks: aggregateAISDKChunks
9336
+ })
9337
+ );
9338
+ this.unsubscribers.push(
9339
+ traceStreamingChannel(aiSDKChannels.streamObject, {
9340
+ name: "streamObject",
9341
+ type: "llm" /* LLM */,
9342
+ extractInput: ([params]) => {
9343
+ return {
9344
+ input: processAISDKInput(params),
9345
+ metadata: extractMetadataFromParams(params)
9346
+ };
9347
+ },
9348
+ extractOutput: (result) => {
9349
+ return processAISDKOutput(result, denyOutputPaths);
9350
+ },
9351
+ extractMetrics: (result, startTime) => {
9352
+ const metrics = extractTokenMetrics(result);
9353
+ if (startTime) {
9354
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8790
9355
  }
8791
- }
8792
- },
8793
- error: (event) => {
8794
- const spanData = spans.get(event);
8795
- if (!spanData) {
8796
- return;
8797
- }
8798
- const { span } = spanData;
8799
- span.log({
8800
- error: event.error.message
8801
- });
8802
- span.end();
8803
- spans.delete(event);
8804
- }
8805
- };
8806
- channel.subscribe(handlers);
8807
- this.unsubscribers.push(() => {
8808
- channel.unsubscribe(handlers);
8809
- });
9356
+ return metrics;
9357
+ },
9358
+ aggregateChunks: aggregateAISDKChunks
9359
+ })
9360
+ );
9361
+ this.unsubscribers.push(
9362
+ traceStreamingChannel(aiSDKChannels.agentGenerate, {
9363
+ name: "Agent.generate",
9364
+ type: "llm" /* LLM */,
9365
+ extractInput: ([params]) => {
9366
+ return {
9367
+ input: processAISDKInput(params),
9368
+ metadata: extractMetadataFromParams(params)
9369
+ };
9370
+ },
9371
+ extractOutput: (result) => {
9372
+ return processAISDKOutput(result, denyOutputPaths);
9373
+ },
9374
+ extractMetrics: (result, startTime) => {
9375
+ const metrics = extractTokenMetrics(result);
9376
+ if (startTime) {
9377
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9378
+ }
9379
+ return metrics;
9380
+ },
9381
+ aggregateChunks: aggregateAISDKChunks
9382
+ })
9383
+ );
9384
+ this.unsubscribers.push(
9385
+ traceStreamingChannel(aiSDKChannels.agentStream, {
9386
+ name: "Agent.stream",
9387
+ type: "llm" /* LLM */,
9388
+ extractInput: ([params]) => {
9389
+ return {
9390
+ input: processAISDKInput(params),
9391
+ metadata: extractMetadataFromParams(params)
9392
+ };
9393
+ },
9394
+ extractOutput: (result) => {
9395
+ return processAISDKOutput(result, denyOutputPaths);
9396
+ },
9397
+ extractMetrics: (result, startTime) => {
9398
+ const metrics = extractTokenMetrics(result);
9399
+ if (startTime) {
9400
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9401
+ }
9402
+ return metrics;
9403
+ },
9404
+ aggregateChunks: aggregateAISDKChunks
9405
+ })
9406
+ );
8810
9407
  }
8811
9408
  };
8812
9409
  function processAISDKInput(params) {
@@ -9048,7 +9645,10 @@ function omitAtPath(obj, keys) {
9048
9645
  if (Array.isArray(obj)) {
9049
9646
  obj.forEach((item) => {
9050
9647
  if (remainingKeys.length > 0) {
9051
- omitAtPath(item, remainingKeys);
9648
+ omitAtPath(
9649
+ item,
9650
+ remainingKeys
9651
+ );
9052
9652
  }
9053
9653
  });
9054
9654
  }
@@ -9058,7 +9658,10 @@ function omitAtPath(obj, keys) {
9058
9658
  }
9059
9659
  } else {
9060
9660
  if (obj && typeof obj === "object" && firstKey in obj) {
9061
- omitAtPath(obj[firstKey], remainingKeys);
9661
+ omitAtPath(
9662
+ obj[firstKey],
9663
+ remainingKeys
9664
+ );
9062
9665
  }
9063
9666
  }
9064
9667
  }
@@ -9071,8 +9674,18 @@ function omit(obj, paths) {
9071
9674
  return result;
9072
9675
  }
9073
9676
 
9677
+ // src/instrumentation/plugins/claude-agent-sdk-channels.ts
9678
+ var claudeAgentSDKChannels = defineChannels(
9679
+ "@anthropic-ai/claude-agent-sdk",
9680
+ {
9681
+ query: channel({
9682
+ channelName: "query",
9683
+ kind: "async"
9684
+ })
9685
+ }
9686
+ );
9687
+
9074
9688
  // src/instrumentation/plugins/claude-agent-sdk-plugin.ts
9075
- var import_dc_browser4 = require("dc-browser");
9076
9689
  function filterSerializableOptions(options) {
9077
9690
  const allowedKeys = [
9078
9691
  "model",
@@ -9156,7 +9769,9 @@ async function createLLMSpanForMessages(messages, prompt, conversationHistory, o
9156
9769
  const input = buildLLMInput(prompt, conversationHistory);
9157
9770
  const outputs = messages.map(
9158
9771
  (m) => m.message?.content && m.message?.role ? { content: m.message.content, role: m.message.role } : void 0
9159
- ).filter((c) => c !== void 0);
9772
+ ).filter(
9773
+ (c) => c !== void 0
9774
+ );
9160
9775
  const span = startSpan({
9161
9776
  name: "anthropic.messages.create",
9162
9777
  spanAttributes: {
@@ -9175,7 +9790,6 @@ async function createLLMSpanForMessages(messages, prompt, conversationHistory, o
9175
9790
  return lastMessage.message?.content && lastMessage.message?.role ? { content: lastMessage.message.content, role: lastMessage.message.role } : void 0;
9176
9791
  }
9177
9792
  var ClaudeAgentSDKPlugin = class extends BasePlugin {
9178
- unsubscribers = [];
9179
9793
  onEnable() {
9180
9794
  this.subscribeToQuery();
9181
9795
  }
@@ -9191,12 +9805,13 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
9191
9805
  * and individual LLM calls.
9192
9806
  */
9193
9807
  subscribeToQuery() {
9194
- const channel = (0, import_dc_browser4.tracingChannel)("orchestrion:claude-agent-sdk:query");
9808
+ const channel2 = claudeAgentSDKChannels.query.tracingChannel();
9195
9809
  const spans = /* @__PURE__ */ new WeakMap();
9196
9810
  const handlers = {
9197
9811
  start: (event) => {
9198
- const params = event.arguments[0] ?? {};
9199
- const { prompt, options = {} } = params;
9812
+ const params = event.arguments[0];
9813
+ const prompt = params?.prompt;
9814
+ const options = params?.options ?? {};
9200
9815
  const span = startSpan({
9201
9816
  name: "Claude Agent",
9202
9817
  spanAttributes: {
@@ -9208,7 +9823,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
9208
9823
  span.log({
9209
9824
  input: typeof prompt === "string" ? prompt : {
9210
9825
  type: "streaming",
9211
- description: "AsyncIterable<SDKMessage>"
9826
+ description: "AsyncIterable<ClaudeAgentSDKMessage>"
9212
9827
  },
9213
9828
  metadata: filterSerializableOptions(options)
9214
9829
  });
@@ -9230,12 +9845,19 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
9230
9845
  if (!spanData) {
9231
9846
  return;
9232
9847
  }
9233
- if (isAsyncIterable(event.result)) {
9234
- patchStreamIfNeeded(event.result, {
9848
+ const eventResult = event.result;
9849
+ if (eventResult === void 0) {
9850
+ spanData.span.end();
9851
+ spans.delete(event);
9852
+ return;
9853
+ }
9854
+ if (isAsyncIterable(eventResult)) {
9855
+ patchStreamIfNeeded(eventResult, {
9235
9856
  onChunk: async (message) => {
9236
9857
  const currentTime = getCurrentUnixTimestamp();
9237
9858
  const params = event.arguments[0];
9238
- const { prompt, options = {} } = params;
9859
+ const prompt = params?.prompt;
9860
+ const options = params?.options ?? {};
9239
9861
  const messageId = message.message?.id;
9240
9862
  if (messageId && messageId !== spanData.currentMessageId) {
9241
9863
  if (spanData.currentMessages.length > 0) {
@@ -9294,7 +9916,8 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
9294
9916
  onComplete: async () => {
9295
9917
  try {
9296
9918
  const params = event.arguments[0];
9297
- const { prompt, options = {} } = params;
9919
+ const prompt = params?.prompt;
9920
+ const options = params?.options ?? {};
9298
9921
  if (spanData.currentMessages.length > 0) {
9299
9922
  const finalMessage = await createLLMSpanForMessages(
9300
9923
  spanData.currentMessages,
@@ -9332,7 +9955,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
9332
9955
  } else {
9333
9956
  try {
9334
9957
  spanData.span.log({
9335
- output: event.result
9958
+ output: eventResult
9336
9959
  });
9337
9960
  } catch (error) {
9338
9961
  console.error(
@@ -9347,7 +9970,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
9347
9970
  },
9348
9971
  error: (event) => {
9349
9972
  const spanData = spans.get(event);
9350
- if (!spanData) {
9973
+ if (!spanData || !event.error) {
9351
9974
  return;
9352
9975
  }
9353
9976
  const { span } = spanData;
@@ -9358,53 +9981,39 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
9358
9981
  spans.delete(event);
9359
9982
  }
9360
9983
  };
9361
- channel.subscribe(handlers);
9984
+ channel2.subscribe(handlers);
9362
9985
  this.unsubscribers.push(() => {
9363
- channel.unsubscribe(handlers);
9986
+ channel2.unsubscribe(handlers);
9364
9987
  });
9365
9988
  }
9366
9989
  };
9367
9990
 
9991
+ // src/instrumentation/plugins/google-genai-channels.ts
9992
+ var googleGenAIChannels = defineChannels("@google/genai", {
9993
+ generateContent: channel({
9994
+ channelName: "models.generateContent",
9995
+ kind: "async"
9996
+ }),
9997
+ generateContentStream: channel({
9998
+ channelName: "models.generateContentStream",
9999
+ kind: "async"
10000
+ })
10001
+ });
10002
+
9368
10003
  // src/instrumentation/plugins/google-genai-plugin.ts
9369
- var import_dc_browser5 = require("dc-browser");
9370
10004
  var GoogleGenAIPlugin = class extends BasePlugin {
9371
- unsubscribers = [];
9372
10005
  onEnable() {
9373
10006
  this.subscribeToGoogleGenAIChannels();
9374
10007
  }
9375
10008
  onDisable() {
9376
- for (const unsubscribe of this.unsubscribers) {
9377
- unsubscribe();
9378
- }
9379
- this.unsubscribers = [];
10009
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
9380
10010
  }
9381
10011
  subscribeToGoogleGenAIChannels() {
9382
- this.subscribeToChannel("orchestrion:google-genai:models.generateContent", {
9383
- name: "google-genai.generateContent",
9384
- type: "llm" /* LLM */,
9385
- extractInput: (args) => {
9386
- const params = args[0] || {};
9387
- const input = serializeInput(params);
9388
- const metadata = extractMetadata(params);
9389
- return {
9390
- input,
9391
- metadata: { ...metadata, provider: "google-genai" }
9392
- };
9393
- },
9394
- extractOutput: (result) => {
9395
- return result;
9396
- },
9397
- extractMetrics: (result, startTime) => {
9398
- return extractGenerateContentMetrics(result, startTime);
9399
- }
9400
- });
9401
- this.subscribeToGoogleStreamingChannel(
9402
- "orchestrion:google-genai:models.generateContentStream",
9403
- {
9404
- name: "google-genai.generateContentStream",
10012
+ this.unsubscribers.push(
10013
+ traceAsyncChannel(googleGenAIChannels.generateContent, {
10014
+ name: "google-genai.generateContent",
9405
10015
  type: "llm" /* LLM */,
9406
- extractInput: (args) => {
9407
- const params = args[0] || {};
10016
+ extractInput: ([params]) => {
9408
10017
  const input = serializeInput(params);
9409
10018
  const metadata = extractMetadata(params);
9410
10019
  return {
@@ -9412,150 +10021,37 @@ var GoogleGenAIPlugin = class extends BasePlugin {
9412
10021
  metadata: { ...metadata, provider: "google-genai" }
9413
10022
  };
9414
10023
  },
9415
- aggregateChunks: aggregateGenerateContentChunks
9416
- }
9417
- );
9418
- }
9419
- subscribeToChannel(channelName, config) {
9420
- const channel = (0, import_dc_browser5.tracingChannel)(channelName);
9421
- const spans = /* @__PURE__ */ new WeakMap();
9422
- const handlers = {
9423
- start: (event) => {
9424
- const span = startSpan({
9425
- name: config.name,
9426
- spanAttributes: {
9427
- type: config.type
9428
- }
9429
- });
9430
- const startTime = getCurrentUnixTimestamp();
9431
- spans.set(event, { span, startTime });
9432
- try {
9433
- const { input, metadata } = config.extractInput(event.arguments);
9434
- span.log({
9435
- input,
9436
- metadata
9437
- });
9438
- } catch (error) {
9439
- console.error(`Error extracting input for ${channelName}:`, error);
9440
- }
9441
- },
9442
- asyncEnd: (event) => {
9443
- const spanData = spans.get(event);
9444
- if (!spanData) {
9445
- return;
9446
- }
9447
- const { span, startTime } = spanData;
9448
- try {
9449
- const output = config.extractOutput(event.result);
9450
- const metrics = config.extractMetrics(event.result, startTime);
9451
- span.log({
9452
- output,
9453
- metrics
9454
- });
9455
- } catch (error) {
9456
- console.error(`Error extracting output for ${channelName}:`, error);
9457
- } finally {
9458
- span.end();
9459
- spans.delete(event);
9460
- }
9461
- },
9462
- error: (event) => {
9463
- const spanData = spans.get(event);
9464
- if (!spanData) {
9465
- return;
10024
+ extractOutput: (result) => {
10025
+ return result;
10026
+ },
10027
+ extractMetrics: (result, startTime) => {
10028
+ return extractGenerateContentMetrics(result, startTime);
9466
10029
  }
9467
- const { span } = spanData;
9468
- span.log({
9469
- error: event.error.message
9470
- });
9471
- span.end();
9472
- spans.delete(event);
9473
- }
9474
- };
9475
- channel.subscribe(handlers);
9476
- this.unsubscribers.push(() => {
9477
- channel.unsubscribe(handlers);
9478
- });
9479
- }
9480
- subscribeToGoogleStreamingChannel(channelName, config) {
9481
- const channel = (0, import_dc_browser5.tracingChannel)(channelName);
9482
- const spans = /* @__PURE__ */ new WeakMap();
9483
- const handlers = {
9484
- start: (event) => {
9485
- const span = startSpan({
9486
- name: config.name,
9487
- spanAttributes: {
9488
- type: config.type
9489
- }
9490
- });
9491
- const startTime = getCurrentUnixTimestamp();
9492
- spans.set(event, { span, startTime });
9493
- try {
9494
- const { input, metadata } = config.extractInput(event.arguments);
9495
- span.log({
10030
+ })
10031
+ );
10032
+ this.unsubscribers.push(
10033
+ traceStreamingChannel(googleGenAIChannels.generateContentStream, {
10034
+ name: "google-genai.generateContentStream",
10035
+ type: "llm" /* LLM */,
10036
+ extractInput: ([params]) => {
10037
+ const input = serializeInput(params);
10038
+ const metadata = extractMetadata(params);
10039
+ return {
9496
10040
  input,
9497
- metadata
9498
- });
9499
- } catch (error) {
9500
- console.error(`Error extracting input for ${channelName}:`, error);
9501
- }
9502
- },
9503
- asyncEnd: (event) => {
9504
- const spanData = spans.get(event);
9505
- if (!spanData) {
9506
- return;
9507
- }
9508
- const { span, startTime } = spanData;
9509
- if (isAsyncIterable(event.result)) {
9510
- patchStreamIfNeeded(event.result, {
9511
- onComplete: (chunks) => {
9512
- try {
9513
- const { output, metrics } = config.aggregateChunks(
9514
- chunks,
9515
- startTime
9516
- );
9517
- span.log({
9518
- output,
9519
- metrics
9520
- });
9521
- } catch (error) {
9522
- console.error(
9523
- `Error extracting output for ${channelName}:`,
9524
- error
9525
- );
9526
- } finally {
9527
- span.end();
9528
- }
9529
- },
9530
- onError: (error) => {
9531
- span.log({
9532
- error: error.message
9533
- });
9534
- span.end();
9535
- }
9536
- });
9537
- } else {
9538
- span.end();
9539
- spans.delete(event);
9540
- }
9541
- },
9542
- error: (event) => {
9543
- const spanData = spans.get(event);
9544
- if (!spanData) {
9545
- return;
10041
+ metadata: { ...metadata, provider: "google-genai" }
10042
+ };
10043
+ },
10044
+ extractOutput: (result) => {
10045
+ return result;
10046
+ },
10047
+ extractMetrics: () => {
10048
+ return {};
10049
+ },
10050
+ aggregateChunks: (chunks, _result, _endEvent, startTime) => {
10051
+ return aggregateGenerateContentChunks(chunks, startTime);
9546
10052
  }
9547
- const { span } = spanData;
9548
- span.log({
9549
- error: event.error.message
9550
- });
9551
- span.end();
9552
- spans.delete(event);
9553
- }
9554
- };
9555
- channel.subscribe(handlers);
9556
- this.unsubscribers.push(() => {
9557
- channel.unsubscribe(handlers);
9558
- });
10053
+ })
10054
+ );
9559
10055
  }
9560
10056
  };
9561
10057
  function serializeInput(params) {
@@ -9611,8 +10107,12 @@ function serializePart(part) {
9611
10107
  const buffer = typeof data === "string" ? typeof Buffer !== "undefined" ? Buffer.from(data, "base64") : new Uint8Array(
9612
10108
  atob(data).split("").map((c) => c.charCodeAt(0))
9613
10109
  ) : typeof Buffer !== "undefined" ? Buffer.from(data) : new Uint8Array(data);
10110
+ const arrayBuffer = buffer instanceof Uint8Array ? buffer.buffer.slice(
10111
+ buffer.byteOffset,
10112
+ buffer.byteOffset + buffer.byteLength
10113
+ ) : buffer;
9614
10114
  const attachment = new Attachment({
9615
- data: buffer,
10115
+ data: arrayBuffer,
9616
10116
  filename,
9617
10117
  contentType: mimeType || "application/octet-stream"
9618
10118
  });
@@ -9661,33 +10161,36 @@ function extractGenerateContentMetrics(response, startTime) {
9661
10161
  const end = getCurrentUnixTimestamp();
9662
10162
  metrics.duration = end - startTime;
9663
10163
  }
9664
- if (response.usageMetadata) {
9665
- const usage = response.usageMetadata;
9666
- if (usage.promptTokenCount !== void 0) {
9667
- metrics.prompt_tokens = usage.promptTokenCount;
9668
- }
9669
- if (usage.candidatesTokenCount !== void 0) {
9670
- metrics.completion_tokens = usage.candidatesTokenCount;
9671
- }
9672
- if (usage.totalTokenCount !== void 0) {
9673
- metrics.tokens = usage.totalTokenCount;
9674
- }
9675
- if (usage.cachedContentTokenCount !== void 0) {
9676
- metrics.prompt_cached_tokens = usage.cachedContentTokenCount;
9677
- }
9678
- if (usage.thoughtsTokenCount !== void 0) {
9679
- metrics.completion_reasoning_tokens = usage.thoughtsTokenCount;
9680
- }
10164
+ if (response?.usageMetadata) {
10165
+ populateUsageMetrics(metrics, response.usageMetadata);
9681
10166
  }
9682
10167
  return metrics;
9683
10168
  }
10169
+ function populateUsageMetrics(metrics, usage) {
10170
+ if (usage.promptTokenCount !== void 0) {
10171
+ metrics.prompt_tokens = usage.promptTokenCount;
10172
+ }
10173
+ if (usage.candidatesTokenCount !== void 0) {
10174
+ metrics.completion_tokens = usage.candidatesTokenCount;
10175
+ }
10176
+ if (usage.totalTokenCount !== void 0) {
10177
+ metrics.tokens = usage.totalTokenCount;
10178
+ }
10179
+ if (usage.cachedContentTokenCount !== void 0) {
10180
+ metrics.prompt_cached_tokens = usage.cachedContentTokenCount;
10181
+ }
10182
+ if (usage.thoughtsTokenCount !== void 0) {
10183
+ metrics.completion_reasoning_tokens = usage.thoughtsTokenCount;
10184
+ }
10185
+ }
9684
10186
  function aggregateGenerateContentChunks(chunks, startTime) {
9685
- const end = getCurrentUnixTimestamp();
9686
- const metrics = {
9687
- duration: end - startTime
9688
- };
10187
+ const metrics = {};
10188
+ if (startTime !== void 0) {
10189
+ const end = getCurrentUnixTimestamp();
10190
+ metrics.duration = end - startTime;
10191
+ }
9689
10192
  let firstTokenTime = null;
9690
- if (chunks.length > 0 && firstTokenTime === null) {
10193
+ if (chunks.length > 0 && firstTokenTime === null && startTime !== void 0) {
9691
10194
  firstTokenTime = getCurrentUnixTimestamp();
9692
10195
  metrics.time_to_first_token = firstTokenTime - startTime;
9693
10196
  }
@@ -9758,21 +10261,7 @@ function aggregateGenerateContentChunks(chunks, startTime) {
9758
10261
  }
9759
10262
  if (usageMetadata) {
9760
10263
  output.usageMetadata = usageMetadata;
9761
- if (usageMetadata.promptTokenCount !== void 0) {
9762
- metrics.prompt_tokens = usageMetadata.promptTokenCount;
9763
- }
9764
- if (usageMetadata.candidatesTokenCount !== void 0) {
9765
- metrics.completion_tokens = usageMetadata.candidatesTokenCount;
9766
- }
9767
- if (usageMetadata.totalTokenCount !== void 0) {
9768
- metrics.tokens = usageMetadata.totalTokenCount;
9769
- }
9770
- if (usageMetadata.cachedContentTokenCount !== void 0) {
9771
- metrics.prompt_cached_tokens = usageMetadata.cachedContentTokenCount;
9772
- }
9773
- if (usageMetadata.thoughtsTokenCount !== void 0) {
9774
- metrics.completion_reasoning_tokens = usageMetadata.thoughtsTokenCount;
9775
- }
10264
+ populateUsageMetrics(metrics, usageMetadata);
9776
10265
  }
9777
10266
  if (text) {
9778
10267
  output.text = text;
@@ -9784,7 +10273,8 @@ function tryToDict(obj) {
9784
10273
  return null;
9785
10274
  }
9786
10275
  if (typeof obj === "object") {
9787
- if (typeof obj.toJSON === "function") {
10276
+ if ("toJSON" in obj && // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
10277
+ typeof obj.toJSON === "function") {
9788
10278
  return obj.toJSON();
9789
10279
  }
9790
10280
  return obj;