braintrust 3.3.0-rc.45 → 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 +558 -47
  23. package/dist/browser.d.ts +558 -47
  24. package/dist/browser.js +2432 -2174
  25. package/dist/browser.mjs +2432 -2174
  26. package/dist/cli.js +1732 -1105
  27. package/dist/edge-light.d.mts +1 -1
  28. package/dist/edge-light.d.ts +1 -1
  29. package/dist/edge-light.js +2342 -2086
  30. package/dist/edge-light.mjs +2342 -2086
  31. package/dist/index.d.mts +558 -47
  32. package/dist/index.d.ts +558 -47
  33. package/dist/index.js +2655 -2399
  34. package/dist/index.mjs +2433 -2177
  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 +2342 -2086
  42. package/dist/workerd.mjs +2342 -2086
  43. package/package.json +6 -3
  44. package/dist/auto-instrumentations/chunk-KVX7OFPD.mjs +0 -288
@@ -1,5 +1,52 @@
1
- // src/instrumentation/core/plugin.ts
2
- import { tracingChannel } from "dc-browser";
1
+ // src/isomorph.ts
2
+ var DefaultAsyncLocalStorage = class {
3
+ constructor() {
4
+ }
5
+ enterWith(_) {
6
+ }
7
+ run(_, callback) {
8
+ return callback();
9
+ }
10
+ getStore() {
11
+ return void 0;
12
+ }
13
+ };
14
+ var DefaultTracingChannel = class {
15
+ hasSubscribers = false;
16
+ subscribe(_handlers) {
17
+ }
18
+ unsubscribe(_handlers) {
19
+ return false;
20
+ }
21
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
+ traceSync(fn, _message, thisArg, ...args) {
23
+ return fn.apply(thisArg, args);
24
+ }
25
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
+ tracePromise(fn, _message, thisArg, ...args) {
27
+ return Promise.resolve(fn.apply(thisArg, args));
28
+ }
29
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
+ traceCallback(fn, _position, _message, thisArg, ...args) {
31
+ return fn.apply(thisArg, args);
32
+ }
33
+ };
34
+ var iso = {
35
+ buildType: "unknown",
36
+ // Will be set by configureBrowser() or configureNode()
37
+ getRepoInfo: async (_settings) => void 0,
38
+ getPastNAncestors: async () => [],
39
+ getEnv: (_name) => void 0,
40
+ getCallerLocation: () => void 0,
41
+ newAsyncLocalStorage: () => new DefaultAsyncLocalStorage(),
42
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
43
+ newTracingChannel: (_nameOrChannels) => new DefaultTracingChannel(),
44
+ processOn: (_0, _1) => {
45
+ },
46
+ basename: (filepath) => filepath.split(/[\\/]/).pop() || filepath,
47
+ writeln: (text) => console.log(text)
48
+ };
49
+ var isomorph_default = iso;
3
50
 
4
51
  // src/instrumentation/core/stream-patcher.ts
5
52
  function isAsyncIterable(value) {
@@ -16,7 +63,7 @@ function patchStreamIfNeeded(stream, options) {
16
63
  return stream;
17
64
  }
18
65
  const originalIteratorFn = stream[Symbol.asyncIterator];
19
- if (originalIteratorFn.__braintrust_patched) {
66
+ if ("__braintrust_patched" in originalIteratorFn && originalIteratorFn["__braintrust_patched"]) {
20
67
  return stream;
21
68
  }
22
69
  try {
@@ -57,7 +104,10 @@ function patchStreamIfNeeded(stream, options) {
57
104
  completed = true;
58
105
  if (options.onError) {
59
106
  try {
60
- options.onError(error, chunks);
107
+ options.onError(
108
+ error instanceof Error ? error : new Error(String(error)),
109
+ chunks
110
+ );
61
111
  } catch (handlerError) {
62
112
  console.error("Error in stream onError handler:", handlerError);
63
113
  }
@@ -85,7 +135,8 @@ function patchStreamIfNeeded(stream, options) {
85
135
  iterator.throw = async function(...args) {
86
136
  if (!completed) {
87
137
  completed = true;
88
- const error = args[0];
138
+ const rawError = args[0];
139
+ const error = rawError instanceof Error ? rawError : new Error(String(rawError));
89
140
  if (options.onError) {
90
141
  try {
91
142
  options.onError(error, chunks);
@@ -99,7 +150,9 @@ function patchStreamIfNeeded(stream, options) {
99
150
  }
100
151
  return iterator;
101
152
  };
102
- patchedIteratorFn.__braintrust_patched = true;
153
+ Object.defineProperty(patchedIteratorFn, "__braintrust_patched", {
154
+ value: true
155
+ });
103
156
  stream[Symbol.asyncIterator] = patchedIteratorFn;
104
157
  return stream;
105
158
  } catch (error) {
@@ -111,6 +164,111 @@ function patchStreamIfNeeded(stream, options) {
111
164
  // src/logger.ts
112
165
  import { v4 as uuidv42 } from "uuid";
113
166
 
167
+ // src/debug-logger.ts
168
+ var PREFIX = "[braintrust]";
169
+ var DEBUG_LOG_LEVEL_SYMBOL = Symbol.for("braintrust-debug-log-level");
170
+ var LOG_LEVEL_PRIORITY = {
171
+ error: 0,
172
+ warn: 1,
173
+ info: 2,
174
+ debug: 3
175
+ };
176
+ var hasWarnedAboutInvalidEnvValue = false;
177
+ var debugLogStateResolver = void 0;
178
+ function warnInvalidEnvValue(value) {
179
+ if (hasWarnedAboutInvalidEnvValue) {
180
+ return;
181
+ }
182
+ hasWarnedAboutInvalidEnvValue = true;
183
+ console.warn(
184
+ PREFIX,
185
+ `Invalid BRAINTRUST_DEBUG_LOG_LEVEL value "${value}". Expected "error", "warn", "info", or "debug".`
186
+ );
187
+ }
188
+ function normalizeDebugLogLevelOption(option) {
189
+ if (option === false) {
190
+ return void 0;
191
+ }
192
+ if (option === "error" || option === "warn" || option === "info" || option === "debug") {
193
+ return option;
194
+ }
195
+ throw new Error(
196
+ `Invalid debugLogLevel value "${option}". Expected false, "error", "warn", "info", or "debug".`
197
+ );
198
+ }
199
+ function parseDebugLogLevelEnv(value) {
200
+ if (!value) {
201
+ return void 0;
202
+ }
203
+ if (value === "error" || value === "warn" || value === "info" || value === "debug") {
204
+ return value;
205
+ }
206
+ warnInvalidEnvValue(value);
207
+ return void 0;
208
+ }
209
+ function getEnvDebugLogLevel() {
210
+ return parseDebugLogLevelEnv(isomorph_default.getEnv("BRAINTRUST_DEBUG_LOG_LEVEL"));
211
+ }
212
+ function setGlobalDebugLogLevel(level) {
213
+ globalThis[DEBUG_LOG_LEVEL_SYMBOL] = level;
214
+ }
215
+ function setDebugLogStateResolver(resolver) {
216
+ debugLogStateResolver = resolver;
217
+ }
218
+ function resolveDebugLogLevel(state) {
219
+ const stateLevel = state?.getDebugLogLevel?.();
220
+ const hasStateOverride = state?.hasDebugLogLevelOverride?.() ?? false;
221
+ if (hasStateOverride) {
222
+ return stateLevel;
223
+ }
224
+ const globalLevel = (
225
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
226
+ globalThis[DEBUG_LOG_LEVEL_SYMBOL]
227
+ );
228
+ if (globalLevel !== void 0) {
229
+ return globalLevel === false ? void 0 : globalLevel;
230
+ }
231
+ return getEnvDebugLogLevel();
232
+ }
233
+ function emit(method, state, args) {
234
+ const level = resolveDebugLogLevel(state);
235
+ if (!level || LOG_LEVEL_PRIORITY[method] > LOG_LEVEL_PRIORITY[level]) {
236
+ return;
237
+ }
238
+ if (method === "info") {
239
+ console.log(PREFIX, ...args);
240
+ } else if (method === "debug") {
241
+ console.debug(PREFIX, ...args);
242
+ } else if (method === "warn") {
243
+ console.warn(PREFIX, ...args);
244
+ } else {
245
+ console.error(PREFIX, ...args);
246
+ }
247
+ }
248
+ function createDebugLogger(state) {
249
+ const resolveState = () => state ?? debugLogStateResolver?.();
250
+ return {
251
+ info(...args) {
252
+ emit("info", resolveState(), args);
253
+ },
254
+ debug(...args) {
255
+ emit("debug", resolveState(), args);
256
+ },
257
+ warn(...args) {
258
+ emit("warn", resolveState(), args);
259
+ },
260
+ error(...args) {
261
+ emit("error", resolveState(), args);
262
+ }
263
+ };
264
+ }
265
+ var debugLogger = {
266
+ ...createDebugLogger(),
267
+ forState(state) {
268
+ return createDebugLogger(state);
269
+ }
270
+ };
271
+
114
272
  // src/queue.ts
115
273
  var DEFAULT_QUEUE_SIZE = 15e3;
116
274
  var Queue = class {
@@ -119,7 +277,7 @@ var Queue = class {
119
277
  enforceSizeLimit = false;
120
278
  constructor(maxSize) {
121
279
  if (maxSize < 1) {
122
- console.warn(
280
+ debugLogger.warn(
123
281
  `maxSize ${maxSize} is <1, using default ${DEFAULT_QUEUE_SIZE}`
124
282
  );
125
283
  maxSize = DEFAULT_QUEUE_SIZE;
@@ -3461,34 +3619,6 @@ function devNullWritableStream() {
3461
3619
  });
3462
3620
  }
3463
3621
 
3464
- // src/isomorph.ts
3465
- var DefaultAsyncLocalStorage = class {
3466
- constructor() {
3467
- }
3468
- enterWith(_) {
3469
- }
3470
- run(_, callback) {
3471
- return callback();
3472
- }
3473
- getStore() {
3474
- return void 0;
3475
- }
3476
- };
3477
- var iso = {
3478
- buildType: "unknown",
3479
- // Will be set by configureBrowser() or configureNode()
3480
- getRepoInfo: async (_settings) => void 0,
3481
- getPastNAncestors: async () => [],
3482
- getEnv: (_name) => void 0,
3483
- getCallerLocation: () => void 0,
3484
- newAsyncLocalStorage: () => new DefaultAsyncLocalStorage(),
3485
- processOn: (_0, _1) => {
3486
- },
3487
- basename: (filepath) => filepath.split(/[\\/]/).pop() || filepath,
3488
- writeln: (text) => console.log(text)
3489
- };
3490
- var isomorph_default = iso;
3491
-
3492
3622
  // src/prompt-cache/disk-cache.ts
3493
3623
  function canUseDiskCache() {
3494
3624
  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);
@@ -4192,7 +4322,10 @@ var loginSchema = z8.strictObject({
4192
4322
  proxyUrl: z8.string(),
4193
4323
  loginToken: z8.string(),
4194
4324
  orgId: z8.string().nullish(),
4195
- gitMetadataSettings: GitMetadataSettings.nullish()
4325
+ gitMetadataSettings: GitMetadataSettings.nullish(),
4326
+ debugLogLevel: z8.enum(["error", "warn", "info", "debug"]).optional(),
4327
+ // Distinguishes explicit false from unset so env fallback stays disabled after deserialization.
4328
+ debugLogLevelDisabled: z8.boolean().optional()
4196
4329
  });
4197
4330
  var stateNonce = 0;
4198
4331
  var BraintrustState = class _BraintrustState {
@@ -4213,6 +4346,16 @@ var BraintrustState = class _BraintrustState {
4213
4346
  this._bgLogger = new SyncLazyValue(
4214
4347
  () => new HTTPBackgroundLogger(new LazyValue(defaultGetLogConn), loginParams)
4215
4348
  );
4349
+ if (loginParams.debugLogLevel !== void 0) {
4350
+ this.debugLogLevelConfigured = true;
4351
+ this.debugLogLevel = normalizeDebugLogLevelOption(
4352
+ loginParams.debugLogLevel
4353
+ );
4354
+ setGlobalDebugLogLevel(this.debugLogLevel ?? false);
4355
+ } else {
4356
+ this.debugLogLevel = getEnvDebugLogLevel();
4357
+ setGlobalDebugLogLevel(void 0);
4358
+ }
4216
4359
  this.resetLoginInfo();
4217
4360
  const memoryCache = new LRUCache({
4218
4361
  max: Number(isomorph_default.getEnv("BRAINTRUST_PROMPT_CACHE_MEMORY_MAX")) ?? 1 << 10
@@ -4257,6 +4400,8 @@ var BraintrustState = class _BraintrustState {
4257
4400
  proxyUrl = null;
4258
4401
  loggedIn = false;
4259
4402
  gitMetadataSettings;
4403
+ debugLogLevel;
4404
+ debugLogLevelConfigured = false;
4260
4405
  fetch = globalThis.fetch;
4261
4406
  _appConn = null;
4262
4407
  _apiConn = null;
@@ -4322,6 +4467,11 @@ var BraintrustState = class _BraintrustState {
4322
4467
  this.proxyUrl = other.proxyUrl;
4323
4468
  this.loggedIn = other.loggedIn;
4324
4469
  this.gitMetadataSettings = other.gitMetadataSettings;
4470
+ this.debugLogLevel = other.debugLogLevel;
4471
+ this.debugLogLevelConfigured = other.debugLogLevelConfigured;
4472
+ setGlobalDebugLogLevel(
4473
+ this.debugLogLevelConfigured ? this.debugLogLevel ?? false : void 0
4474
+ );
4325
4475
  this._appConn = other._appConn;
4326
4476
  this._apiConn = other._apiConn;
4327
4477
  this.loginReplaceApiConn(this.apiConn());
@@ -4346,7 +4496,9 @@ var BraintrustState = class _BraintrustState {
4346
4496
  orgName: this.orgName,
4347
4497
  apiUrl: this.apiUrl,
4348
4498
  proxyUrl: this.proxyUrl,
4349
- gitMetadataSettings: this.gitMetadataSettings
4499
+ gitMetadataSettings: this.gitMetadataSettings,
4500
+ ...this.debugLogLevel ? { debugLogLevel: this.debugLogLevel } : {},
4501
+ ...this.debugLogLevelConfigured && !this.debugLogLevel ? { debugLogLevelDisabled: true } : {}
4350
4502
  };
4351
4503
  }
4352
4504
  static deserialize(serialized, opts) {
@@ -4373,6 +4525,10 @@ var BraintrustState = class _BraintrustState {
4373
4525
  state.proxyConn().set_token(state.loginToken);
4374
4526
  }
4375
4527
  state.loggedIn = true;
4528
+ state.debugLogLevelConfigured = "debugLogLevel" in serializedParsed.data || !!serializedParsed.data.debugLogLevelDisabled;
4529
+ setGlobalDebugLogLevel(
4530
+ state.debugLogLevelConfigured ? state.debugLogLevel ?? false : void 0
4531
+ );
4376
4532
  state.loginReplaceApiConn(state.apiConn());
4377
4533
  return state;
4378
4534
  }
@@ -4385,7 +4541,22 @@ var BraintrustState = class _BraintrustState {
4385
4541
  setMaskingFunction(maskingFunction) {
4386
4542
  this.bgLogger().setMaskingFunction(maskingFunction);
4387
4543
  }
4544
+ setDebugLogLevel(option) {
4545
+ if (option === void 0) {
4546
+ return;
4547
+ }
4548
+ this.debugLogLevelConfigured = true;
4549
+ this.debugLogLevel = normalizeDebugLogLevelOption(option);
4550
+ setGlobalDebugLogLevel(this.debugLogLevel ?? false);
4551
+ }
4552
+ getDebugLogLevel() {
4553
+ return this.debugLogLevel;
4554
+ }
4555
+ hasDebugLogLevelOverride() {
4556
+ return this.debugLogLevelConfigured;
4557
+ }
4388
4558
  async login(loginParams) {
4559
+ this.setDebugLogLevel(loginParams.debugLogLevel);
4389
4560
  if (this.apiUrl && !loginParams.forceLogin) {
4390
4561
  return;
4391
4562
  }
@@ -4483,6 +4654,7 @@ var BraintrustState = class _BraintrustState {
4483
4654
  };
4484
4655
  var _globalState;
4485
4656
  var _internalGetGlobalState = () => _globalState;
4657
+ setDebugLogStateResolver(() => _internalGetGlobalState());
4486
4658
  var FailedHTTPResponse = class extends Error {
4487
4659
  status;
4488
4660
  text;
@@ -4597,7 +4769,7 @@ var HTTPConnection = class _HTTPConnection {
4597
4769
  return await resp.json();
4598
4770
  } catch (e) {
4599
4771
  if (i < tries - 1) {
4600
- console.log(
4772
+ debugLogger.debug(
4601
4773
  `Retrying API request ${object_type} ${JSON.stringify(args)} ${e.status} ${e.text}`
4602
4774
  );
4603
4775
  continue;
@@ -4809,7 +4981,7 @@ with a Blob/ArrayBuffer, or run the program on Node.js.`
4809
4981
  try {
4810
4982
  statSync(data);
4811
4983
  } catch (e) {
4812
- console.warn(`Failed to read file: ${e}`);
4984
+ debugLogger.warn(`Failed to read file: ${e}`);
4813
4985
  }
4814
4986
  }
4815
4987
  };
@@ -5521,7 +5693,7 @@ var HTTPBackgroundLogger = class _HTTPBackgroundLogger {
5521
5693
  this.queueDropLoggingPeriod = queueDropLoggingPeriodEnv;
5522
5694
  }
5523
5695
  if (isomorph_default.getEnv("BRAINTRUST_LOG_FLUSH_CHUNK_SIZE")) {
5524
- console.warn(
5696
+ debugLogger.warn(
5525
5697
  "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."
5526
5698
  );
5527
5699
  }
@@ -5583,7 +5755,10 @@ var HTTPBackgroundLogger = class _HTTPBackgroundLogger {
5583
5755
  const versionInfo = await conn.get_json("version");
5584
5756
  serverLimit = z8.object({ logs3_payload_max_bytes: z8.number().nullish() }).parse(versionInfo).logs3_payload_max_bytes ?? null;
5585
5757
  } catch (e) {
5586
- console.warn("Failed to fetch version info for payload limit:", e);
5758
+ debugLogger.warn(
5759
+ "Failed to fetch version info for payload limit:",
5760
+ e
5761
+ );
5587
5762
  }
5588
5763
  const validServerLimit = serverLimit !== null && serverLimit > 0 ? serverLimit : null;
5589
5764
  const canUseOverflow = validServerLimit !== null;
@@ -5727,16 +5902,16 @@ var HTTPBackgroundLogger = class _HTTPBackgroundLogger {
5727
5902
  if (isRetrying) {
5728
5903
  errmsg += ". Retrying";
5729
5904
  }
5730
- console.warn(errmsg);
5905
+ debugLogger.warn(errmsg);
5731
5906
  if (!isRetrying) {
5732
- console.warn(
5907
+ debugLogger.warn(
5733
5908
  `Failed to construct log records to flush after ${this.numTries} attempts. Dropping batch`
5734
5909
  );
5735
5910
  throw e;
5736
5911
  } else {
5737
- console.warn(e);
5912
+ debugLogger.warn(e);
5738
5913
  const sleepTimeS = BACKGROUND_LOGGER_BASE_SLEEP_TIME_S * 2 ** i;
5739
- console.info(`Sleeping for ${sleepTimeS}s`);
5914
+ debugLogger.info(`Sleeping for ${sleepTimeS}s`);
5740
5915
  await new Promise(
5741
5916
  (resolve) => setTimeout(resolve, sleepTimeS * 1e3)
5742
5917
  );
@@ -5837,15 +6012,15 @@ Error: ${errorText}`;
5837
6012
  this.logFailedPayloadsDir();
5838
6013
  }
5839
6014
  if (!isRetrying) {
5840
- console.warn(
6015
+ debugLogger.warn(
5841
6016
  `log request failed after ${this.numTries} retries. Dropping batch`
5842
6017
  );
5843
6018
  throw new Error(errMsg);
5844
6019
  } else {
5845
- console.warn(errMsg);
6020
+ debugLogger.warn(errMsg);
5846
6021
  if (isRetrying) {
5847
6022
  const sleepTimeS = BACKGROUND_LOGGER_BASE_SLEEP_TIME_S * 2 ** i;
5848
- console.info(`Sleeping for ${sleepTimeS}s`);
6023
+ debugLogger.info(`Sleeping for ${sleepTimeS}s`);
5849
6024
  await new Promise(
5850
6025
  (resolve) => setTimeout(resolve, sleepTimeS * 1e3)
5851
6026
  );
@@ -5860,7 +6035,7 @@ Error: ${errorText}`;
5860
6035
  this.queueDropLoggingState.numDropped += numItems;
5861
6036
  const timeNow = getCurrentUnixTimestamp();
5862
6037
  if (timeNow - this.queueDropLoggingState.lastLoggedTimestamp > this.queueDropLoggingPeriod) {
5863
- console.warn(
6038
+ debugLogger.warn(
5864
6039
  `Dropped ${this.queueDropLoggingState.numDropped} elements due to full queue`
5865
6040
  );
5866
6041
  if (this.failedPublishPayloadsDir) {
@@ -5892,7 +6067,7 @@ Error: ${errorText}`;
5892
6067
  await _HTTPBackgroundLogger.writePayloadToDir({ payloadDir, payload });
5893
6068
  }
5894
6069
  } catch (e) {
5895
- console.error(e);
6070
+ debugLogger.error(e);
5896
6071
  }
5897
6072
  }
5898
6073
  static async writePayloadToDir({
@@ -5900,7 +6075,7 @@ Error: ${errorText}`;
5900
6075
  payload
5901
6076
  }) {
5902
6077
  if (!(isomorph_default.pathJoin && isomorph_default.mkdir && isomorph_default.writeFile)) {
5903
- console.warn(
6078
+ debugLogger.warn(
5904
6079
  "Cannot dump payloads: filesystem-operations not supported on this platform"
5905
6080
  );
5906
6081
  return;
@@ -5913,7 +6088,7 @@ Error: ${errorText}`;
5913
6088
  await isomorph_default.mkdir(payloadDir, { recursive: true });
5914
6089
  await isomorph_default.writeFile(payloadFile, payload);
5915
6090
  } catch (e) {
5916
- console.error(
6091
+ debugLogger.error(
5917
6092
  `Failed to write failed payload to output file ${payloadFile}:
5918
6093
  `,
5919
6094
  e
@@ -5944,7 +6119,9 @@ Error: ${errorText}`;
5944
6119
  }
5945
6120
  }
5946
6121
  logFailedPayloadsDir() {
5947
- console.warn(`Logging failed payloads to ${this.failedPublishPayloadsDir}`);
6122
+ debugLogger.warn(
6123
+ `Logging failed payloads to ${this.failedPublishPayloadsDir}`
6124
+ );
5948
6125
  }
5949
6126
  // Should only be called by BraintrustState.
5950
6127
  internalReplaceApiConn(apiConn) {
@@ -6355,7 +6532,14 @@ var ObjectFetcher = class {
6355
6532
  async *fetchRecordsFromApi(batchSize) {
6356
6533
  const state = await this.getState();
6357
6534
  const objectId = await this.id;
6358
- const limit = batchSize ?? DEFAULT_FETCH_BATCH_SIZE;
6535
+ const batchLimit = batchSize ?? DEFAULT_FETCH_BATCH_SIZE;
6536
+ const internalLimit = this._internal_btql?.limit;
6537
+ const limit = batchSize !== void 0 ? batchSize : internalLimit ?? batchLimit;
6538
+ const internalBtqlWithoutReservedQueryKeys = Object.fromEntries(
6539
+ Object.entries(this._internal_btql ?? {}).filter(
6540
+ ([key]) => key !== "cursor" && key !== "limit" && key !== "select" && key !== "from"
6541
+ )
6542
+ );
6359
6543
  let cursor = void 0;
6360
6544
  let iterations = 0;
6361
6545
  while (true) {
@@ -6363,7 +6547,6 @@ var ObjectFetcher = class {
6363
6547
  `btql`,
6364
6548
  {
6365
6549
  query: {
6366
- ...this._internal_btql,
6367
6550
  select: [
6368
6551
  {
6369
6552
  op: "star"
@@ -6383,7 +6566,8 @@ var ObjectFetcher = class {
6383
6566
  ]
6384
6567
  },
6385
6568
  cursor,
6386
- limit
6569
+ limit,
6570
+ ...internalBtqlWithoutReservedQueryKeys
6387
6571
  },
6388
6572
  use_columnstore: false,
6389
6573
  brainstore_realtime: true,
@@ -6656,7 +6840,7 @@ var Experiment2 = class extends ObjectFetcher {
6656
6840
  scores = results["scores"];
6657
6841
  metrics = results["metrics"];
6658
6842
  } catch (e) {
6659
- console.warn(
6843
+ debugLogger.forState(state).warn(
6660
6844
  `Failed to fetch experiment scores and metrics: ${e}
6661
6845
 
6662
6846
  View complete results in Braintrust or run experiment.summarize() again.`
@@ -6733,7 +6917,7 @@ View complete results in Braintrust or run experiment.summarize() again.`
6733
6917
  * @deprecated This function is deprecated. You can simply remove it from your code.
6734
6918
  */
6735
6919
  async close() {
6736
- console.warn(
6920
+ debugLogger.forState(this.state).warn(
6737
6921
  "close is deprecated and will be removed in a future version of braintrust. It is now a no-op and can be removed"
6738
6922
  );
6739
6923
  return this.id;
@@ -6890,8 +7074,8 @@ var SpanImpl = class _SpanImpl {
6890
7074
  ...serializableInternalData,
6891
7075
  [IS_MERGE_FIELD]: this.isMerge
6892
7076
  });
6893
- if (partialRecord.metrics?.end) {
6894
- this.loggedEndTime = partialRecord.metrics?.end;
7077
+ if (typeof partialRecord.metrics?.end === "number") {
7078
+ this.loggedEndTime = partialRecord.metrics.end;
6895
7079
  }
6896
7080
  if (this.parentObjectType === 1 /* EXPERIMENT */) {
6897
7081
  const cachedSpan = {
@@ -7126,7 +7310,7 @@ var Dataset2 = class extends ObjectFetcher {
7126
7310
  constructor(state, lazyMetadata, pinnedVersion, legacy, _internal_btql) {
7127
7311
  const isLegacyDataset = legacy ?? DEFAULT_IS_LEGACY_DATASET;
7128
7312
  if (isLegacyDataset) {
7129
- console.warn(
7313
+ debugLogger.forState(state).warn(
7130
7314
  `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.`
7131
7315
  );
7132
7316
  }
@@ -7357,7 +7541,7 @@ var Dataset2 = class extends ObjectFetcher {
7357
7541
  * @deprecated This function is deprecated. You can simply remove it from your code.
7358
7542
  */
7359
7543
  async close() {
7360
- console.warn(
7544
+ debugLogger.forState(this.state).warn(
7361
7545
  "close is deprecated and will be removed in a future version of braintrust. It is now a no-op and can be removed"
7362
7546
  );
7363
7547
  return this.id;
@@ -7368,6 +7552,49 @@ var Dataset2 = class extends ObjectFetcher {
7368
7552
  };
7369
7553
  var TEST_API_KEY = "___TEST_API_KEY__THIS_IS_NOT_REAL___";
7370
7554
 
7555
+ // src/instrumentation/core/channel-tracing-utils.ts
7556
+ function hasChannelSpanInfo(value) {
7557
+ return isObject(value) && isObject(value.span_info);
7558
+ }
7559
+ function getChannelSpanInfo(event) {
7560
+ if (isObject(event.span_info)) {
7561
+ return event.span_info;
7562
+ }
7563
+ const firstArg = event.arguments?.[0];
7564
+ if (hasChannelSpanInfo(firstArg)) {
7565
+ return firstArg.span_info;
7566
+ }
7567
+ return void 0;
7568
+ }
7569
+ function buildStartSpanArgs(config, event) {
7570
+ const spanInfo = getChannelSpanInfo(event);
7571
+ const spanAttributes = {
7572
+ type: config.type
7573
+ };
7574
+ if (isObject(spanInfo?.spanAttributes)) {
7575
+ mergeDicts(spanAttributes, spanInfo.spanAttributes);
7576
+ }
7577
+ return {
7578
+ name: typeof spanInfo?.name === "string" && spanInfo.name ? spanInfo.name : config.name,
7579
+ spanAttributes,
7580
+ spanInfoMetadata: isObject(spanInfo?.metadata) ? spanInfo.metadata : void 0
7581
+ };
7582
+ }
7583
+ function mergeInputMetadata(metadata, spanInfoMetadata) {
7584
+ if (!spanInfoMetadata) {
7585
+ return isObject(metadata) ? (
7586
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
7587
+ metadata
7588
+ ) : void 0;
7589
+ }
7590
+ const mergedMetadata = {};
7591
+ mergeDicts(mergedMetadata, spanInfoMetadata);
7592
+ if (isObject(metadata)) {
7593
+ mergeDicts(mergedMetadata, metadata);
7594
+ }
7595
+ return mergedMetadata;
7596
+ }
7597
+
7371
7598
  // src/instrumentation/core/plugin.ts
7372
7599
  var BasePlugin = class {
7373
7600
  enabled = false;
@@ -7399,23 +7626,25 @@ var BasePlugin = class {
7399
7626
  * @param handlers - Event handlers
7400
7627
  */
7401
7628
  subscribe(channelName, handlers) {
7402
- const channel = tracingChannel(channelName);
7403
- channel.subscribe(handlers);
7629
+ const channel2 = isomorph_default.newTracingChannel(channelName);
7630
+ channel2.subscribe(handlers);
7404
7631
  }
7405
7632
  /**
7406
7633
  * Subscribe to a channel for async methods (non-streaming).
7407
7634
  * Creates a span and logs input/output/metrics.
7408
7635
  */
7409
7636
  subscribeToChannel(channelName, config) {
7410
- const channel = tracingChannel(channelName);
7637
+ const channel2 = isomorph_default.newTracingChannel(channelName);
7411
7638
  const spans = /* @__PURE__ */ new WeakMap();
7412
7639
  const handlers = {
7413
7640
  start: (event) => {
7641
+ const { name, spanAttributes, spanInfoMetadata } = buildStartSpanArgs(
7642
+ config,
7643
+ event
7644
+ );
7414
7645
  const span = startSpan({
7415
- name: config.name,
7416
- spanAttributes: {
7417
- type: config.type
7418
- }
7646
+ name,
7647
+ spanAttributes
7419
7648
  });
7420
7649
  const startTime = getCurrentUnixTimestamp();
7421
7650
  spans.set(event, { span, startTime });
@@ -7423,7 +7652,7 @@ var BasePlugin = class {
7423
7652
  const { input, metadata } = config.extractInput(event.arguments);
7424
7653
  span.log({
7425
7654
  input,
7426
- metadata
7655
+ metadata: mergeInputMetadata(metadata, spanInfoMetadata)
7427
7656
  });
7428
7657
  } catch (error) {
7429
7658
  console.error(`Error extracting input for ${channelName}:`, error);
@@ -7436,10 +7665,12 @@ var BasePlugin = class {
7436
7665
  }
7437
7666
  const { span, startTime } = spanData;
7438
7667
  try {
7439
- const output = config.extractOutput(event.result);
7440
- const metrics = config.extractMetrics(event.result, startTime);
7668
+ const output = config.extractOutput(event.result, event);
7669
+ const metrics = config.extractMetrics(event.result, startTime, event);
7670
+ const metadata = config.extractMetadata?.(event.result, event);
7441
7671
  span.log({
7442
7672
  output,
7673
+ ...metadata !== void 0 ? { metadata } : {},
7443
7674
  metrics
7444
7675
  });
7445
7676
  } catch (error) {
@@ -7462,9 +7693,9 @@ var BasePlugin = class {
7462
7693
  spans.delete(event);
7463
7694
  }
7464
7695
  };
7465
- channel.subscribe(handlers);
7696
+ channel2.subscribe(handlers);
7466
7697
  this.unsubscribers.push(() => {
7467
- channel.unsubscribe(handlers);
7698
+ channel2.unsubscribe(handlers);
7468
7699
  });
7469
7700
  }
7470
7701
  /**
@@ -7472,15 +7703,17 @@ var BasePlugin = class {
7472
7703
  * Handles both streaming and non-streaming responses.
7473
7704
  */
7474
7705
  subscribeToStreamingChannel(channelName, config) {
7475
- const channel = tracingChannel(channelName);
7706
+ const channel2 = isomorph_default.newTracingChannel(channelName);
7476
7707
  const spans = /* @__PURE__ */ new WeakMap();
7477
7708
  const handlers = {
7478
7709
  start: (event) => {
7710
+ const { name, spanAttributes, spanInfoMetadata } = buildStartSpanArgs(
7711
+ config,
7712
+ event
7713
+ );
7479
7714
  const span = startSpan({
7480
- name: config.name,
7481
- spanAttributes: {
7482
- type: config.type
7483
- }
7715
+ name,
7716
+ spanAttributes
7484
7717
  });
7485
7718
  const startTime = getCurrentUnixTimestamp();
7486
7719
  spans.set(event, { span, startTime });
@@ -7488,7 +7721,7 @@ var BasePlugin = class {
7488
7721
  const { input, metadata } = config.extractInput(event.arguments);
7489
7722
  span.log({
7490
7723
  input,
7491
- metadata
7724
+ metadata: mergeInputMetadata(metadata, spanInfoMetadata)
7492
7725
  });
7493
7726
  } catch (error) {
7494
7727
  console.error(`Error extracting input for ${channelName}:`, error);
@@ -7501,24 +7734,39 @@ var BasePlugin = class {
7501
7734
  }
7502
7735
  const { span, startTime } = spanData;
7503
7736
  if (isAsyncIterable(event.result)) {
7737
+ let firstChunkTime;
7504
7738
  patchStreamIfNeeded(event.result, {
7739
+ onChunk: () => {
7740
+ if (firstChunkTime === void 0) {
7741
+ firstChunkTime = getCurrentUnixTimestamp();
7742
+ }
7743
+ },
7505
7744
  onComplete: (chunks) => {
7506
7745
  try {
7507
7746
  let output;
7508
7747
  let metrics;
7748
+ let metadata;
7509
7749
  if (config.aggregateChunks) {
7510
- const aggregated = config.aggregateChunks(chunks);
7750
+ const aggregated = config.aggregateChunks(
7751
+ chunks,
7752
+ event.result,
7753
+ event
7754
+ );
7511
7755
  output = aggregated.output;
7512
7756
  metrics = aggregated.metrics;
7757
+ metadata = aggregated.metadata;
7513
7758
  } else {
7514
- output = config.extractOutput(chunks);
7515
- metrics = config.extractMetrics(chunks, startTime);
7759
+ output = config.extractOutput(chunks, event);
7760
+ metrics = config.extractMetrics(chunks, startTime, event);
7516
7761
  }
7517
- if (!metrics.time_to_first_token && chunks.length > 0) {
7762
+ if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
7763
+ metrics.time_to_first_token = firstChunkTime - startTime;
7764
+ } else if (metrics.time_to_first_token === void 0 && chunks.length > 0) {
7518
7765
  metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
7519
7766
  }
7520
7767
  span.log({
7521
7768
  output,
7769
+ ...metadata !== void 0 ? { metadata } : {},
7522
7770
  metrics
7523
7771
  });
7524
7772
  } catch (error) {
@@ -7539,10 +7787,16 @@ var BasePlugin = class {
7539
7787
  });
7540
7788
  } else {
7541
7789
  try {
7542
- const output = config.extractOutput(event.result);
7543
- const metrics = config.extractMetrics(event.result, startTime);
7790
+ const output = config.extractOutput(event.result, event);
7791
+ const metadata = config.extractMetadata ? config.extractMetadata(event.result, event) : void 0;
7792
+ const metrics = config.extractMetrics(
7793
+ event.result,
7794
+ startTime,
7795
+ event
7796
+ );
7544
7797
  span.log({
7545
7798
  output,
7799
+ ...metadata !== void 0 ? { metadata } : {},
7546
7800
  metrics
7547
7801
  });
7548
7802
  } catch (error) {
@@ -7566,9 +7820,9 @@ var BasePlugin = class {
7566
7820
  spans.delete(event);
7567
7821
  }
7568
7822
  };
7569
- channel.subscribe(handlers);
7823
+ channel2.subscribe(handlers);
7570
7824
  this.unsubscribers.push(() => {
7571
- channel.unsubscribe(handlers);
7825
+ channel2.unsubscribe(handlers);
7572
7826
  });
7573
7827
  }
7574
7828
  /**
@@ -7576,15 +7830,17 @@ var BasePlugin = class {
7576
7830
  * Used for methods like beta.chat.completions.stream() and responses.stream().
7577
7831
  */
7578
7832
  subscribeToSyncStreamChannel(channelName, config) {
7579
- const channel = tracingChannel(channelName);
7833
+ const channel2 = isomorph_default.newTracingChannel(channelName);
7580
7834
  const spans = /* @__PURE__ */ new WeakMap();
7581
7835
  const handlers = {
7582
7836
  start: (event) => {
7837
+ const { name, spanAttributes, spanInfoMetadata } = buildStartSpanArgs(
7838
+ config,
7839
+ event
7840
+ );
7583
7841
  const span = startSpan({
7584
- name: config.name,
7585
- spanAttributes: {
7586
- type: config.type
7587
- }
7842
+ name,
7843
+ spanAttributes
7588
7844
  });
7589
7845
  const startTime = getCurrentUnixTimestamp();
7590
7846
  spans.set(event, { span, startTime });
@@ -7592,7 +7848,7 @@ var BasePlugin = class {
7592
7848
  const { input, metadata } = config.extractInput(event.arguments);
7593
7849
  span.log({
7594
7850
  input,
7595
- metadata
7851
+ metadata: mergeInputMetadata(metadata, spanInfoMetadata)
7596
7852
  });
7597
7853
  } catch (error) {
7598
7854
  console.error(`Error extracting input for ${channelName}:`, error);
@@ -7676,9 +7932,9 @@ var BasePlugin = class {
7676
7932
  spans.delete(event);
7677
7933
  }
7678
7934
  };
7679
- channel.subscribe(handlers);
7935
+ channel2.subscribe(handlers);
7680
7936
  this.unsubscribers.push(() => {
7681
- channel.unsubscribe(handlers);
7937
+ channel2.unsubscribe(handlers);
7682
7938
  });
7683
7939
  }
7684
7940
  };
@@ -7701,149 +7957,635 @@ function isValidChannelName(channelName) {
7701
7957
  return /^braintrust:[^:]+:.+$/.test(channelName);
7702
7958
  }
7703
7959
 
7704
- // src/wrappers/attachment-utils.ts
7705
- function getExtensionFromMediaType(mediaType) {
7706
- const extensionMap = {
7707
- "image/png": "png",
7708
- "image/jpeg": "jpg",
7709
- "image/gif": "gif",
7710
- "image/webp": "webp",
7711
- "image/svg+xml": "svg",
7712
- "audio/mpeg": "mp3",
7713
- "audio/wav": "wav",
7714
- "audio/ogg": "ogg",
7715
- "video/mp4": "mp4",
7716
- "video/webm": "webm",
7717
- "application/pdf": "pdf",
7718
- "application/json": "json",
7719
- "text/plain": "txt",
7720
- "text/html": "html",
7721
- "text/csv": "csv"
7722
- };
7723
- return extensionMap[mediaType] || "bin";
7960
+ // src/instrumentation/core/channel-tracing.ts
7961
+ function isSyncStreamLike(value) {
7962
+ return !!value && typeof value === "object" && typeof value.on === "function";
7724
7963
  }
7725
- function convertDataToBlob(data, mediaType) {
7964
+ function hasChoices(value) {
7965
+ return !!value && typeof value === "object" && "choices" in value;
7966
+ }
7967
+ function normalizeMetadata(metadata) {
7968
+ return isObject(metadata) ? metadata : void 0;
7969
+ }
7970
+ function startSpanForEvent(config, event, channelName) {
7971
+ const { name, spanAttributes, spanInfoMetadata } = buildStartSpanArgs(
7972
+ config,
7973
+ event
7974
+ );
7975
+ const span = startSpan({
7976
+ name,
7977
+ spanAttributes
7978
+ });
7979
+ const startTime = getCurrentUnixTimestamp();
7726
7980
  try {
7727
- if (typeof data === "string") {
7728
- if (data.startsWith("data:")) {
7729
- const base64Match = data.match(/^data:[^;]+;base64,(.+)$/);
7730
- if (base64Match) {
7731
- const base64 = base64Match[1];
7732
- const binaryString = atob(base64);
7733
- const bytes = new Uint8Array(binaryString.length);
7734
- for (let i = 0; i < binaryString.length; i++) {
7735
- bytes[i] = binaryString.charCodeAt(i);
7736
- }
7737
- return new Blob([bytes], { type: mediaType });
7738
- }
7739
- } else if (data.startsWith("http://") || data.startsWith("https://")) {
7740
- return null;
7741
- } else {
7742
- const binaryString = atob(data);
7743
- const bytes = new Uint8Array(binaryString.length);
7744
- for (let i = 0; i < binaryString.length; i++) {
7745
- bytes[i] = binaryString.charCodeAt(i);
7746
- }
7747
- return new Blob([bytes], { type: mediaType });
7748
- }
7749
- } else if (data instanceof Uint8Array) {
7750
- return new Blob([data], { type: mediaType });
7751
- } else if (data instanceof ArrayBuffer) {
7752
- return new Blob([data], { type: mediaType });
7753
- } else if (typeof Buffer !== "undefined" && data instanceof Buffer) {
7754
- return new Blob([data], { type: mediaType });
7755
- }
7756
- } catch {
7757
- return null;
7981
+ const { input, metadata } = config.extractInput(event.arguments);
7982
+ span.log({
7983
+ input,
7984
+ metadata: mergeInputMetadata(metadata, spanInfoMetadata)
7985
+ });
7986
+ } catch (error) {
7987
+ console.error(`Error extracting input for ${channelName}:`, error);
7758
7988
  }
7759
- return null;
7989
+ return { span, startTime };
7760
7990
  }
7761
- function processInputAttachments(input) {
7762
- if (!input) {
7763
- return input;
7991
+ function logErrorAndEnd(states, event) {
7992
+ const spanData = states.get(event);
7993
+ if (!spanData) {
7994
+ return;
7764
7995
  }
7765
- let attachmentIndex = 0;
7766
- const processContentPart = (part) => {
7767
- if (!part || typeof part !== "object") {
7768
- return part;
7769
- }
7770
- if (part.type === "image" && part.image) {
7771
- let mediaType = "image/png";
7772
- if (typeof part.image === "string" && part.image.startsWith("data:")) {
7773
- const mediaTypeMatch = part.image.match(/^data:([^;]+);/);
7774
- if (mediaTypeMatch) {
7775
- mediaType = mediaTypeMatch[1];
7776
- }
7777
- } else if (part.mediaType) {
7778
- mediaType = part.mediaType;
7779
- }
7780
- const blob = convertDataToBlob(part.image, mediaType);
7781
- if (blob) {
7782
- const filename = `input_image_${attachmentIndex}.${getExtensionFromMediaType(mediaType)}`;
7783
- attachmentIndex++;
7784
- const attachment = new Attachment({
7785
- data: blob,
7786
- filename,
7787
- contentType: mediaType
7788
- });
7789
- return {
7790
- ...part,
7791
- image: attachment
7792
- };
7996
+ spanData.span.log({
7997
+ error: event.error.message
7998
+ });
7999
+ spanData.span.end();
8000
+ states.delete(event);
8001
+ }
8002
+ function traceAsyncChannel(channel2, config) {
8003
+ const tracingChannel = channel2.tracingChannel();
8004
+ const states = /* @__PURE__ */ new WeakMap();
8005
+ const channelName = channel2.channelName;
8006
+ const handlers = {
8007
+ start: (event) => {
8008
+ states.set(
8009
+ event,
8010
+ startSpanForEvent(
8011
+ config,
8012
+ event,
8013
+ channelName
8014
+ )
8015
+ );
8016
+ },
8017
+ asyncEnd: (event) => {
8018
+ const spanData = states.get(event);
8019
+ if (!spanData) {
8020
+ return;
7793
8021
  }
7794
- }
7795
- if (part.type === "file" && part.data) {
7796
- const mediaType = part.mediaType || "application/octet-stream";
7797
- const blob = convertDataToBlob(part.data, mediaType);
7798
- if (blob) {
7799
- const filename = part.filename || `input_file_${attachmentIndex}.${getExtensionFromMediaType(mediaType)}`;
7800
- attachmentIndex++;
7801
- const attachment = new Attachment({
7802
- data: blob,
7803
- filename,
7804
- contentType: mediaType
8022
+ const asyncEndEvent = event;
8023
+ const { span, startTime } = spanData;
8024
+ try {
8025
+ const output = config.extractOutput(
8026
+ asyncEndEvent.result,
8027
+ asyncEndEvent
8028
+ );
8029
+ const metrics = config.extractMetrics(
8030
+ asyncEndEvent.result,
8031
+ startTime,
8032
+ asyncEndEvent
8033
+ );
8034
+ const metadata = config.extractMetadata?.(
8035
+ asyncEndEvent.result,
8036
+ asyncEndEvent
8037
+ );
8038
+ span.log({
8039
+ output,
8040
+ ...normalizeMetadata(metadata) !== void 0 ? { metadata: normalizeMetadata(metadata) } : {},
8041
+ metrics
7805
8042
  });
7806
- return {
7807
- ...part,
7808
- data: attachment
7809
- };
8043
+ } catch (error) {
8044
+ console.error(`Error extracting output for ${channelName}:`, error);
8045
+ } finally {
8046
+ span.end();
8047
+ states.delete(event);
7810
8048
  }
8049
+ },
8050
+ error: (event) => {
8051
+ logErrorAndEnd(states, event);
7811
8052
  }
7812
- return part;
7813
8053
  };
7814
- const processMessage = (message) => {
7815
- if (!message || typeof message !== "object") {
7816
- return message;
7817
- }
7818
- if (Array.isArray(message.content)) {
7819
- return {
7820
- ...message,
7821
- content: message.content.map(processContentPart)
7822
- };
7823
- }
7824
- return message;
8054
+ tracingChannel.subscribe(handlers);
8055
+ return () => {
8056
+ tracingChannel.unsubscribe(handlers);
7825
8057
  };
7826
- if (Array.isArray(input)) {
7827
- return input.map(processMessage);
7828
- } else if (typeof input === "object" && input.content) {
7829
- return processMessage(input);
7830
- }
7831
- return input;
7832
8058
  }
7833
-
7834
- // src/instrumentation/plugins/openai-plugin.ts
7835
- var OpenAIPlugin = class extends BasePlugin {
7836
- constructor() {
8059
+ function traceStreamingChannel(channel2, config) {
8060
+ const tracingChannel = channel2.tracingChannel();
8061
+ const states = /* @__PURE__ */ new WeakMap();
8062
+ const channelName = channel2.channelName;
8063
+ const handlers = {
8064
+ start: (event) => {
8065
+ states.set(
8066
+ event,
8067
+ startSpanForEvent(
8068
+ config,
8069
+ event,
8070
+ channelName
8071
+ )
8072
+ );
8073
+ },
8074
+ asyncEnd: (event) => {
8075
+ const spanData = states.get(event);
8076
+ if (!spanData) {
8077
+ return;
8078
+ }
8079
+ const asyncEndEvent = event;
8080
+ const { span, startTime } = spanData;
8081
+ if (isAsyncIterable(asyncEndEvent.result)) {
8082
+ let firstChunkTime;
8083
+ patchStreamIfNeeded(asyncEndEvent.result, {
8084
+ onChunk: () => {
8085
+ if (firstChunkTime === void 0) {
8086
+ firstChunkTime = getCurrentUnixTimestamp();
8087
+ }
8088
+ },
8089
+ onComplete: (chunks) => {
8090
+ try {
8091
+ let output;
8092
+ let metrics;
8093
+ let metadata;
8094
+ if (config.aggregateChunks) {
8095
+ const aggregated = config.aggregateChunks(
8096
+ chunks,
8097
+ asyncEndEvent.result,
8098
+ asyncEndEvent,
8099
+ startTime
8100
+ );
8101
+ output = aggregated.output;
8102
+ metrics = aggregated.metrics;
8103
+ metadata = aggregated.metadata;
8104
+ } else {
8105
+ output = config.extractOutput(
8106
+ chunks,
8107
+ asyncEndEvent
8108
+ );
8109
+ metrics = config.extractMetrics(
8110
+ chunks,
8111
+ startTime,
8112
+ asyncEndEvent
8113
+ );
8114
+ }
8115
+ if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
8116
+ metrics.time_to_first_token = firstChunkTime - startTime;
8117
+ } else if (metrics.time_to_first_token === void 0 && chunks.length > 0) {
8118
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8119
+ }
8120
+ span.log({
8121
+ output,
8122
+ ...metadata !== void 0 ? { metadata } : {},
8123
+ metrics
8124
+ });
8125
+ } catch (error) {
8126
+ console.error(
8127
+ `Error extracting output for ${channelName}:`,
8128
+ error
8129
+ );
8130
+ } finally {
8131
+ span.end();
8132
+ states.delete(event);
8133
+ }
8134
+ },
8135
+ onError: (error) => {
8136
+ span.log({
8137
+ error: error.message
8138
+ });
8139
+ span.end();
8140
+ states.delete(event);
8141
+ }
8142
+ });
8143
+ return;
8144
+ }
8145
+ try {
8146
+ const output = config.extractOutput(
8147
+ asyncEndEvent.result,
8148
+ asyncEndEvent
8149
+ );
8150
+ const metrics = config.extractMetrics(
8151
+ asyncEndEvent.result,
8152
+ startTime,
8153
+ asyncEndEvent
8154
+ );
8155
+ const metadata = config.extractMetadata?.(
8156
+ asyncEndEvent.result,
8157
+ asyncEndEvent
8158
+ );
8159
+ span.log({
8160
+ output,
8161
+ ...normalizeMetadata(metadata) !== void 0 ? { metadata: normalizeMetadata(metadata) } : {},
8162
+ metrics
8163
+ });
8164
+ } catch (error) {
8165
+ console.error(`Error extracting output for ${channelName}:`, error);
8166
+ } finally {
8167
+ span.end();
8168
+ states.delete(event);
8169
+ }
8170
+ },
8171
+ error: (event) => {
8172
+ logErrorAndEnd(states, event);
8173
+ }
8174
+ };
8175
+ tracingChannel.subscribe(handlers);
8176
+ return () => {
8177
+ tracingChannel.unsubscribe(handlers);
8178
+ };
8179
+ }
8180
+ function traceSyncStreamChannel(channel2, config) {
8181
+ const tracingChannel = channel2.tracingChannel();
8182
+ const states = /* @__PURE__ */ new WeakMap();
8183
+ const channelName = channel2.channelName;
8184
+ const handlers = {
8185
+ start: (event) => {
8186
+ states.set(
8187
+ event,
8188
+ startSpanForEvent(
8189
+ config,
8190
+ event,
8191
+ channelName
8192
+ )
8193
+ );
8194
+ },
8195
+ end: (event) => {
8196
+ const spanData = states.get(event);
8197
+ if (!spanData) {
8198
+ return;
8199
+ }
8200
+ const { span, startTime } = spanData;
8201
+ const resultEvent = event;
8202
+ const stream = resultEvent.result;
8203
+ if (!isSyncStreamLike(stream)) {
8204
+ span.end();
8205
+ states.delete(event);
8206
+ return;
8207
+ }
8208
+ let first = true;
8209
+ stream.on("chunk", () => {
8210
+ if (first) {
8211
+ span.log({
8212
+ metrics: {
8213
+ time_to_first_token: getCurrentUnixTimestamp() - startTime
8214
+ }
8215
+ });
8216
+ first = false;
8217
+ }
8218
+ });
8219
+ stream.on("chatCompletion", (completion) => {
8220
+ try {
8221
+ if (hasChoices(completion)) {
8222
+ span.log({
8223
+ output: completion.choices
8224
+ });
8225
+ }
8226
+ } catch (error) {
8227
+ console.error(
8228
+ `Error extracting chatCompletion for ${channelName}:`,
8229
+ error
8230
+ );
8231
+ }
8232
+ });
8233
+ stream.on("event", (streamEvent) => {
8234
+ if (!config.extractFromEvent) {
8235
+ return;
8236
+ }
8237
+ try {
8238
+ if (first) {
8239
+ span.log({
8240
+ metrics: {
8241
+ time_to_first_token: getCurrentUnixTimestamp() - startTime
8242
+ }
8243
+ });
8244
+ first = false;
8245
+ }
8246
+ const extracted = config.extractFromEvent(streamEvent);
8247
+ if (extracted && Object.keys(extracted).length > 0) {
8248
+ span.log(extracted);
8249
+ }
8250
+ } catch (error) {
8251
+ console.error(`Error extracting event for ${channelName}:`, error);
8252
+ }
8253
+ });
8254
+ stream.on("end", () => {
8255
+ span.end();
8256
+ states.delete(event);
8257
+ });
8258
+ stream.on("error", (error) => {
8259
+ span.log({
8260
+ error: error.message
8261
+ });
8262
+ span.end();
8263
+ states.delete(event);
8264
+ });
8265
+ },
8266
+ error: (event) => {
8267
+ logErrorAndEnd(states, event);
8268
+ }
8269
+ };
8270
+ tracingChannel.subscribe(handlers);
8271
+ return () => {
8272
+ tracingChannel.unsubscribe(handlers);
8273
+ };
8274
+ }
8275
+ function unsubscribeAll(unsubscribers) {
8276
+ for (const unsubscribe of unsubscribers) {
8277
+ unsubscribe();
8278
+ }
8279
+ return [];
8280
+ }
8281
+
8282
+ // src/wrappers/attachment-utils.ts
8283
+ function getExtensionFromMediaType(mediaType) {
8284
+ const extensionMap = {
8285
+ "image/png": "png",
8286
+ "image/jpeg": "jpg",
8287
+ "image/gif": "gif",
8288
+ "image/webp": "webp",
8289
+ "image/svg+xml": "svg",
8290
+ "audio/mpeg": "mp3",
8291
+ "audio/wav": "wav",
8292
+ "audio/ogg": "ogg",
8293
+ "video/mp4": "mp4",
8294
+ "video/webm": "webm",
8295
+ "application/pdf": "pdf",
8296
+ "application/json": "json",
8297
+ "text/plain": "txt",
8298
+ "text/html": "html",
8299
+ "text/csv": "csv"
8300
+ };
8301
+ return extensionMap[mediaType] || "bin";
8302
+ }
8303
+ function convertDataToBlob(data, mediaType) {
8304
+ try {
8305
+ if (typeof data === "string") {
8306
+ if (data.startsWith("data:")) {
8307
+ const base64Match = data.match(/^data:[^;]+;base64,(.+)$/);
8308
+ if (base64Match) {
8309
+ const base64 = base64Match[1];
8310
+ const binaryString = atob(base64);
8311
+ const bytes = new Uint8Array(binaryString.length);
8312
+ for (let i = 0; i < binaryString.length; i++) {
8313
+ bytes[i] = binaryString.charCodeAt(i);
8314
+ }
8315
+ return new Blob([bytes], { type: mediaType });
8316
+ }
8317
+ } else if (data.startsWith("http://") || data.startsWith("https://")) {
8318
+ return null;
8319
+ } else {
8320
+ const binaryString = atob(data);
8321
+ const bytes = new Uint8Array(binaryString.length);
8322
+ for (let i = 0; i < binaryString.length; i++) {
8323
+ bytes[i] = binaryString.charCodeAt(i);
8324
+ }
8325
+ return new Blob([bytes], { type: mediaType });
8326
+ }
8327
+ } else if (data instanceof Uint8Array) {
8328
+ return new Blob([data], { type: mediaType });
8329
+ } else if (data instanceof ArrayBuffer) {
8330
+ return new Blob([data], { type: mediaType });
8331
+ } else if (typeof Buffer !== "undefined" && data instanceof Buffer) {
8332
+ return new Blob([data], { type: mediaType });
8333
+ }
8334
+ } catch {
8335
+ return null;
8336
+ }
8337
+ return null;
8338
+ }
8339
+ function processInputAttachments(input) {
8340
+ if (!input) {
8341
+ return input;
8342
+ }
8343
+ let attachmentIndex = 0;
8344
+ const inferMediaTypeFromDataUrl = (value, fallback) => {
8345
+ const mediaTypeMatch = value.match(/^data:([^;]+);/);
8346
+ return mediaTypeMatch?.[1] || fallback;
8347
+ };
8348
+ const toAttachment = (value, mediaType, filename) => {
8349
+ const blob = convertDataToBlob(value, mediaType);
8350
+ if (!blob) {
8351
+ return null;
8352
+ }
8353
+ return new Attachment({
8354
+ data: blob,
8355
+ filename,
8356
+ contentType: mediaType
8357
+ });
8358
+ };
8359
+ const processNode = (node) => {
8360
+ if (Array.isArray(node)) {
8361
+ return node.map(processNode);
8362
+ }
8363
+ if (!node || typeof node !== "object") {
8364
+ return node;
8365
+ }
8366
+ 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:")) {
8367
+ const mediaType = inferMediaTypeFromDataUrl(
8368
+ node.image_url.url,
8369
+ "image/png"
8370
+ );
8371
+ const filename = `image.${getExtensionFromMediaType(mediaType)}`;
8372
+ const attachment = toAttachment(node.image_url.url, mediaType, filename);
8373
+ if (attachment) {
8374
+ return {
8375
+ ...node,
8376
+ image_url: {
8377
+ ...node.image_url,
8378
+ url: attachment
8379
+ }
8380
+ };
8381
+ }
8382
+ }
8383
+ if (node.type === "file" && node.file && typeof node.file === "object" && typeof node.file.file_data === "string" && node.file.file_data.startsWith("data:")) {
8384
+ const mediaType = inferMediaTypeFromDataUrl(
8385
+ node.file.file_data,
8386
+ "application/octet-stream"
8387
+ );
8388
+ const filename = typeof node.file.filename === "string" && node.file.filename ? node.file.filename : `document.${getExtensionFromMediaType(mediaType)}`;
8389
+ const attachment = toAttachment(node.file.file_data, mediaType, filename);
8390
+ if (attachment) {
8391
+ return {
8392
+ ...node,
8393
+ file: {
8394
+ ...node.file,
8395
+ file_data: attachment
8396
+ }
8397
+ };
8398
+ }
8399
+ }
8400
+ if (node.type === "image" && node.image) {
8401
+ let mediaType = "image/png";
8402
+ if (typeof node.image === "string" && node.image.startsWith("data:")) {
8403
+ mediaType = inferMediaTypeFromDataUrl(node.image, mediaType);
8404
+ } else if (node.mediaType) {
8405
+ mediaType = node.mediaType;
8406
+ }
8407
+ const filename = `input_image_${attachmentIndex}.${getExtensionFromMediaType(mediaType)}`;
8408
+ const attachment = toAttachment(node.image, mediaType, filename);
8409
+ if (attachment) {
8410
+ attachmentIndex++;
8411
+ return {
8412
+ ...node,
8413
+ image: attachment
8414
+ };
8415
+ }
8416
+ }
8417
+ if (node.type === "file" && node.data) {
8418
+ const mediaType = node.mediaType || "application/octet-stream";
8419
+ const filename = node.filename || `input_file_${attachmentIndex}.${getExtensionFromMediaType(mediaType)}`;
8420
+ const attachment = toAttachment(node.data, mediaType, filename);
8421
+ if (attachment) {
8422
+ attachmentIndex++;
8423
+ return {
8424
+ ...node,
8425
+ data: attachment
8426
+ };
8427
+ }
8428
+ }
8429
+ const processed = {};
8430
+ for (const [key, value] of Object.entries(node)) {
8431
+ processed[key] = processNode(value);
8432
+ }
8433
+ return processed;
8434
+ };
8435
+ if (Array.isArray(input)) {
8436
+ return input.map(processNode);
8437
+ }
8438
+ return processNode(input);
8439
+ }
8440
+
8441
+ // src/instrumentation/core/channel-definitions.ts
8442
+ function channel(spec) {
8443
+ return spec;
8444
+ }
8445
+ function defineChannels(pkg, channels) {
8446
+ return Object.fromEntries(
8447
+ Object.entries(channels).map(([key, spec]) => {
8448
+ const fullChannelName = `orchestrion:${pkg}:${spec.channelName}`;
8449
+ if (spec.kind === "async") {
8450
+ const asyncSpec = spec;
8451
+ const tracingChannel2 = () => isomorph_default.newTracingChannel(
8452
+ fullChannelName
8453
+ );
8454
+ return [
8455
+ key,
8456
+ {
8457
+ ...asyncSpec,
8458
+ tracingChannel: tracingChannel2,
8459
+ tracePromise: (fn, context) => tracingChannel2().tracePromise(
8460
+ fn,
8461
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
8462
+ context
8463
+ )
8464
+ }
8465
+ ];
8466
+ }
8467
+ const syncSpec = spec;
8468
+ const tracingChannel = () => isomorph_default.newTracingChannel(
8469
+ fullChannelName
8470
+ );
8471
+ return [
8472
+ key,
8473
+ {
8474
+ ...syncSpec,
8475
+ tracingChannel,
8476
+ traceSync: (fn, context) => tracingChannel().traceSync(
8477
+ fn,
8478
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
8479
+ context
8480
+ )
8481
+ }
8482
+ ];
8483
+ })
8484
+ );
8485
+ }
8486
+
8487
+ // src/instrumentation/plugins/openai-channels.ts
8488
+ var openAIChannels = defineChannels("openai", {
8489
+ chatCompletionsCreate: channel({
8490
+ channelName: "chat.completions.create",
8491
+ kind: "async"
8492
+ }),
8493
+ embeddingsCreate: channel({
8494
+ channelName: "embeddings.create",
8495
+ kind: "async"
8496
+ }),
8497
+ betaChatCompletionsParse: channel({
8498
+ channelName: "beta.chat.completions.parse",
8499
+ kind: "async"
8500
+ }),
8501
+ betaChatCompletionsStream: channel({
8502
+ channelName: "beta.chat.completions.stream",
8503
+ kind: "sync-stream"
8504
+ }),
8505
+ moderationsCreate: channel({
8506
+ channelName: "moderations.create",
8507
+ kind: "async"
8508
+ }),
8509
+ responsesCreate: channel({
8510
+ channelName: "responses.create",
8511
+ kind: "async"
8512
+ }),
8513
+ responsesStream: channel({
8514
+ channelName: "responses.stream",
8515
+ kind: "sync-stream"
8516
+ }),
8517
+ responsesParse: channel({
8518
+ channelName: "responses.parse",
8519
+ kind: "async"
8520
+ })
8521
+ });
8522
+
8523
+ // src/openai-utils.ts
8524
+ var BRAINTRUST_CACHED_STREAM_METRIC = "__braintrust_cached_metric";
8525
+ var LEGACY_CACHED_HEADER = "x-cached";
8526
+ var X_CACHED_HEADER = "x-bt-cached";
8527
+ var TOKEN_NAME_MAP = {
8528
+ input_tokens: "prompt_tokens",
8529
+ output_tokens: "completion_tokens",
8530
+ total_tokens: "tokens"
8531
+ };
8532
+ var TOKEN_PREFIX_MAP = {
8533
+ input: "prompt",
8534
+ output: "completion"
8535
+ };
8536
+ function parseMetricsFromUsage(usage) {
8537
+ if (!usage) {
8538
+ return {};
8539
+ }
8540
+ const metrics = {};
8541
+ for (const [oaiName, value] of Object.entries(usage)) {
8542
+ if (typeof value === "number") {
8543
+ const metricName = TOKEN_NAME_MAP[oaiName] || oaiName;
8544
+ metrics[metricName] = value;
8545
+ continue;
8546
+ }
8547
+ if (!oaiName.endsWith("_tokens_details") || !isObject(value)) {
8548
+ continue;
8549
+ }
8550
+ const rawPrefix = oaiName.slice(0, -"_tokens_details".length);
8551
+ const prefix = TOKEN_PREFIX_MAP[rawPrefix] || rawPrefix;
8552
+ for (const [key, nestedValue] of Object.entries(value)) {
8553
+ if (typeof nestedValue !== "number") {
8554
+ continue;
8555
+ }
8556
+ metrics[`${prefix}_${key}`] = nestedValue;
8557
+ }
8558
+ }
8559
+ return metrics;
8560
+ }
8561
+ function parseCachedHeader(value) {
8562
+ if (!value) {
8563
+ return void 0;
8564
+ }
8565
+ return ["true", "hit"].includes(value.toLowerCase()) ? 1 : 0;
8566
+ }
8567
+ function getCachedMetricFromHeaders(headers) {
8568
+ if (!headers || typeof headers.get !== "function") {
8569
+ return void 0;
8570
+ }
8571
+ const cachedHeader = headers.get(X_CACHED_HEADER);
8572
+ if (cachedHeader) {
8573
+ return parseCachedHeader(cachedHeader);
8574
+ }
8575
+ return parseCachedHeader(headers.get(LEGACY_CACHED_HEADER));
8576
+ }
8577
+
8578
+ // src/instrumentation/plugins/openai-plugin.ts
8579
+ var OpenAIPlugin = class extends BasePlugin {
8580
+ constructor() {
7837
8581
  super();
7838
8582
  }
7839
8583
  onEnable() {
7840
- this.subscribeToStreamingChannel(
7841
- "orchestrion:openai:chat.completions.create",
7842
- {
8584
+ this.unsubscribers.push(
8585
+ traceStreamingChannel(openAIChannels.chatCompletionsCreate, {
7843
8586
  name: "Chat Completion",
7844
8587
  type: "llm" /* LLM */,
7845
- extractInput: (args) => {
7846
- const params = args[0] || {};
8588
+ extractInput: ([params]) => {
7847
8589
  const { messages, ...metadata } = params;
7848
8590
  return {
7849
8591
  input: processInputAttachments(messages),
@@ -7853,41 +8595,49 @@ var OpenAIPlugin = class extends BasePlugin {
7853
8595
  extractOutput: (result) => {
7854
8596
  return result?.choices;
7855
8597
  },
7856
- extractMetrics: (result, startTime) => {
7857
- const metrics = parseMetricsFromUsage(result?.usage);
8598
+ extractMetrics: (result, startTime, endEvent) => {
8599
+ const metrics = withCachedMetric(
8600
+ parseMetricsFromUsage(result?.usage),
8601
+ result,
8602
+ endEvent
8603
+ );
7858
8604
  if (startTime) {
7859
8605
  metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
7860
8606
  }
7861
8607
  return metrics;
7862
8608
  },
7863
8609
  aggregateChunks: aggregateChatCompletionChunks
7864
- }
8610
+ })
7865
8611
  );
7866
- this.subscribeToChannel("orchestrion:openai:embeddings.create", {
7867
- name: "Embedding",
7868
- type: "llm" /* LLM */,
7869
- extractInput: (args) => {
7870
- const params = args[0] || {};
7871
- const { input, ...metadata } = params;
7872
- return {
7873
- input,
7874
- metadata: { ...metadata, provider: "openai" }
7875
- };
7876
- },
7877
- extractOutput: (result) => {
7878
- return result?.data?.map((d) => d.embedding);
7879
- },
7880
- extractMetrics: (result) => {
7881
- return parseMetricsFromUsage(result?.usage);
7882
- }
7883
- });
7884
- this.subscribeToStreamingChannel(
7885
- "orchestrion:openai:beta.chat.completions.parse",
7886
- {
8612
+ this.unsubscribers.push(
8613
+ traceAsyncChannel(openAIChannels.embeddingsCreate, {
8614
+ name: "Embedding",
8615
+ type: "llm" /* LLM */,
8616
+ extractInput: ([params]) => {
8617
+ const { input, ...metadata } = params;
8618
+ return {
8619
+ input,
8620
+ metadata: { ...metadata, provider: "openai" }
8621
+ };
8622
+ },
8623
+ extractOutput: (result) => {
8624
+ const embedding = result?.data?.[0]?.embedding;
8625
+ return Array.isArray(embedding) ? { embedding_length: embedding.length } : void 0;
8626
+ },
8627
+ extractMetrics: (result, _startTime, endEvent) => {
8628
+ return withCachedMetric(
8629
+ parseMetricsFromUsage(result?.usage),
8630
+ result,
8631
+ endEvent
8632
+ );
8633
+ }
8634
+ })
8635
+ );
8636
+ this.unsubscribers.push(
8637
+ traceStreamingChannel(openAIChannels.betaChatCompletionsParse, {
7887
8638
  name: "Chat Completion",
7888
8639
  type: "llm" /* LLM */,
7889
- extractInput: (args) => {
7890
- const params = args[0] || {};
8640
+ extractInput: ([params]) => {
7891
8641
  const { messages, ...metadata } = params;
7892
8642
  return {
7893
8643
  input: processInputAttachments(messages),
@@ -7897,164 +8647,196 @@ var OpenAIPlugin = class extends BasePlugin {
7897
8647
  extractOutput: (result) => {
7898
8648
  return result?.choices;
7899
8649
  },
7900
- extractMetrics: (result, startTime) => {
7901
- const metrics = parseMetricsFromUsage(result?.usage);
8650
+ extractMetrics: (result, startTime, endEvent) => {
8651
+ const metrics = withCachedMetric(
8652
+ parseMetricsFromUsage(result?.usage),
8653
+ result,
8654
+ endEvent
8655
+ );
7902
8656
  if (startTime) {
7903
8657
  metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
7904
8658
  }
7905
8659
  return metrics;
7906
8660
  },
7907
8661
  aggregateChunks: aggregateChatCompletionChunks
7908
- }
8662
+ })
7909
8663
  );
7910
- this.subscribeToSyncStreamChannel(
7911
- "orchestrion:openai:beta.chat.completions.stream",
7912
- {
8664
+ this.unsubscribers.push(
8665
+ traceSyncStreamChannel(openAIChannels.betaChatCompletionsStream, {
7913
8666
  name: "Chat Completion",
7914
8667
  type: "llm" /* LLM */,
7915
- extractInput: (args) => {
7916
- const params = args[0] || {};
8668
+ extractInput: ([params]) => {
7917
8669
  const { messages, ...metadata } = params;
7918
8670
  return {
7919
8671
  input: processInputAttachments(messages),
7920
8672
  metadata: { ...metadata, provider: "openai" }
7921
8673
  };
7922
8674
  }
7923
- }
8675
+ })
7924
8676
  );
7925
- this.subscribeToChannel("orchestrion:openai:moderations.create", {
7926
- name: "Moderation",
7927
- type: "llm" /* LLM */,
7928
- extractInput: (args) => {
7929
- const params = args[0] || {};
7930
- const { input, ...metadata } = params;
7931
- return {
7932
- input,
7933
- metadata: { ...metadata, provider: "openai" }
7934
- };
7935
- },
7936
- extractOutput: (result) => {
7937
- return result?.results;
7938
- },
7939
- extractMetrics: () => {
7940
- return {};
7941
- }
7942
- });
7943
- this.subscribeToStreamingChannel("orchestrion:openai:responses.create", {
7944
- name: "openai.responses.create",
7945
- type: "llm" /* LLM */,
7946
- extractInput: (args) => {
7947
- const params = args[0] || {};
7948
- const { input, ...metadata } = params;
7949
- return {
7950
- input: processInputAttachments(input),
7951
- metadata: { ...metadata, provider: "openai" }
7952
- };
7953
- },
7954
- extractOutput: (result) => {
7955
- return processImagesInOutput(result?.output);
7956
- },
7957
- extractMetrics: (result, startTime) => {
7958
- const metrics = parseMetricsFromUsage(result?.usage);
7959
- if (startTime) {
7960
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
7961
- }
7962
- return metrics;
7963
- }
7964
- });
7965
- this.subscribeToSyncStreamChannel("orchestrion:openai:responses.stream", {
7966
- name: "openai.responses.stream",
7967
- type: "llm" /* LLM */,
7968
- extractInput: (args) => {
7969
- const params = args[0] || {};
7970
- const { input, ...metadata } = params;
7971
- return {
7972
- input: processInputAttachments(input),
7973
- metadata: { ...metadata, provider: "openai" }
7974
- };
7975
- },
7976
- extractFromEvent: (event) => {
7977
- if (!event || !event.type || !event.response) {
7978
- return {};
8677
+ this.unsubscribers.push(
8678
+ traceAsyncChannel(openAIChannels.moderationsCreate, {
8679
+ name: "Moderation",
8680
+ type: "llm" /* LLM */,
8681
+ extractInput: ([params]) => {
8682
+ const { input, ...metadata } = params;
8683
+ return {
8684
+ input,
8685
+ metadata: { ...metadata, provider: "openai" }
8686
+ };
8687
+ },
8688
+ extractOutput: (result) => {
8689
+ return result?.results;
8690
+ },
8691
+ extractMetrics: (result, _startTime, endEvent) => {
8692
+ return withCachedMetric(
8693
+ parseMetricsFromUsage(result?.usage),
8694
+ result,
8695
+ endEvent
8696
+ );
7979
8697
  }
7980
- const response = event.response;
7981
- if (event.type === "response.completed") {
8698
+ })
8699
+ );
8700
+ this.unsubscribers.push(
8701
+ traceStreamingChannel(openAIChannels.responsesCreate, {
8702
+ name: "openai.responses.create",
8703
+ type: "llm" /* LLM */,
8704
+ extractInput: ([params]) => {
8705
+ const { input, ...metadata } = params;
8706
+ return {
8707
+ input: processInputAttachments(input),
8708
+ metadata: { ...metadata, provider: "openai" }
8709
+ };
8710
+ },
8711
+ extractOutput: (result) => {
8712
+ return processImagesInOutput(result?.output);
8713
+ },
8714
+ extractMetadata: (result) => {
8715
+ if (!result) {
8716
+ return void 0;
8717
+ }
8718
+ const { output: _output, usage: _usage, ...metadata } = result;
8719
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
8720
+ },
8721
+ extractMetrics: (result, startTime, endEvent) => {
8722
+ const metrics = withCachedMetric(
8723
+ parseMetricsFromUsage(result?.usage),
8724
+ result,
8725
+ endEvent
8726
+ );
8727
+ if (startTime) {
8728
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8729
+ }
8730
+ return metrics;
8731
+ },
8732
+ aggregateChunks: aggregateResponseStreamEvents
8733
+ })
8734
+ );
8735
+ this.unsubscribers.push(
8736
+ traceSyncStreamChannel(openAIChannels.responsesStream, {
8737
+ name: "openai.responses.create",
8738
+ type: "llm" /* LLM */,
8739
+ extractInput: ([params]) => {
8740
+ const { input, ...metadata } = params;
8741
+ return {
8742
+ input: processInputAttachments(input),
8743
+ metadata: { ...metadata, provider: "openai" }
8744
+ };
8745
+ },
8746
+ extractFromEvent: (event) => {
8747
+ if (event.type !== "response.completed" || !event.response) {
8748
+ return {};
8749
+ }
8750
+ const response = event.response;
7982
8751
  const data = {};
7983
- if (response?.output !== void 0) {
8752
+ if (response.output !== void 0) {
7984
8753
  data.output = processImagesInOutput(response.output);
7985
8754
  }
7986
- if (response) {
7987
- const { usage: _usage, output: _output, ...metadata } = response;
7988
- if (Object.keys(metadata).length > 0) {
7989
- data.metadata = metadata;
7990
- }
8755
+ const { usage: _usage, output: _output, ...metadata } = response;
8756
+ if (Object.keys(metadata).length > 0) {
8757
+ data.metadata = metadata;
7991
8758
  }
7992
- data.metrics = parseMetricsFromUsage(response?.usage);
8759
+ data.metrics = parseMetricsFromUsage(response.usage);
7993
8760
  return data;
7994
8761
  }
7995
- return {};
7996
- }
7997
- });
7998
- this.subscribeToStreamingChannel("orchestrion:openai:responses.parse", {
7999
- name: "openai.responses.parse",
8000
- type: "llm" /* LLM */,
8001
- extractInput: (args) => {
8002
- const params = args[0] || {};
8003
- const { input, ...metadata } = params;
8004
- return {
8005
- input: processInputAttachments(input),
8006
- metadata: { ...metadata, provider: "openai" }
8007
- };
8008
- },
8009
- extractOutput: (result) => {
8010
- return processImagesInOutput(result?.output);
8011
- },
8012
- extractMetrics: (result, startTime) => {
8013
- const metrics = parseMetricsFromUsage(result?.usage);
8014
- if (startTime) {
8015
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8016
- }
8017
- return metrics;
8018
- }
8019
- });
8762
+ })
8763
+ );
8764
+ this.unsubscribers.push(
8765
+ traceStreamingChannel(openAIChannels.responsesParse, {
8766
+ name: "openai.responses.parse",
8767
+ type: "llm" /* LLM */,
8768
+ extractInput: ([params]) => {
8769
+ const { input, ...metadata } = params;
8770
+ return {
8771
+ input: processInputAttachments(input),
8772
+ metadata: { ...metadata, provider: "openai" }
8773
+ };
8774
+ },
8775
+ extractOutput: (result) => {
8776
+ return processImagesInOutput(result?.output);
8777
+ },
8778
+ extractMetadata: (result) => {
8779
+ if (!result) {
8780
+ return void 0;
8781
+ }
8782
+ const { output: _output, usage: _usage, ...metadata } = result;
8783
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
8784
+ },
8785
+ extractMetrics: (result, startTime, endEvent) => {
8786
+ const metrics = withCachedMetric(
8787
+ parseMetricsFromUsage(result?.usage),
8788
+ result,
8789
+ endEvent
8790
+ );
8791
+ if (startTime) {
8792
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8793
+ }
8794
+ return metrics;
8795
+ },
8796
+ aggregateChunks: aggregateResponseStreamEvents
8797
+ })
8798
+ );
8020
8799
  }
8021
8800
  onDisable() {
8801
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
8022
8802
  }
8023
8803
  };
8024
- var TOKEN_NAME_MAP = {
8025
- input_tokens: "prompt_tokens",
8026
- output_tokens: "completion_tokens",
8027
- total_tokens: "tokens"
8028
- };
8029
- var TOKEN_PREFIX_MAP = {
8030
- input: "prompt",
8031
- output: "completion"
8032
- };
8033
- function parseMetricsFromUsage(usage) {
8034
- if (!usage) {
8035
- return {};
8804
+ function getCachedMetricFromEndEvent(endEvent) {
8805
+ if (!isObject(endEvent)) {
8806
+ return void 0;
8036
8807
  }
8037
- const metrics = {};
8038
- for (const [oai_name, value] of Object.entries(usage)) {
8039
- if (typeof value === "number") {
8040
- const metricName = TOKEN_NAME_MAP[oai_name] || oai_name;
8041
- metrics[metricName] = value;
8042
- } else if (oai_name.endsWith("_tokens_details")) {
8043
- if (!isObject(value)) {
8044
- continue;
8045
- }
8046
- const rawPrefix = oai_name.slice(0, -"_tokens_details".length);
8047
- const prefix = TOKEN_PREFIX_MAP[rawPrefix] || rawPrefix;
8048
- for (const [key, n] of Object.entries(value)) {
8049
- if (typeof n !== "number") {
8050
- continue;
8051
- }
8052
- const metricName = `${prefix}_${key}`;
8053
- metrics[metricName] = n;
8054
- }
8055
- }
8808
+ const response = endEvent.response;
8809
+ if (!isObject(response)) {
8810
+ return void 0;
8056
8811
  }
8057
- return metrics;
8812
+ const headers = response.headers;
8813
+ if (!headers || typeof headers.get !== "function") {
8814
+ return void 0;
8815
+ }
8816
+ return getCachedMetricFromHeaders(headers);
8817
+ }
8818
+ function withCachedMetric(metrics, result, endEvent) {
8819
+ if (metrics.cached !== void 0) {
8820
+ return metrics;
8821
+ }
8822
+ const cachedFromEvent = getCachedMetricFromEndEvent(endEvent);
8823
+ if (cachedFromEvent !== void 0) {
8824
+ return {
8825
+ ...metrics,
8826
+ cached: cachedFromEvent
8827
+ };
8828
+ }
8829
+ if (!isObject(result)) {
8830
+ return metrics;
8831
+ }
8832
+ const cached = result[BRAINTRUST_CACHED_STREAM_METRIC];
8833
+ if (typeof cached !== "number") {
8834
+ return metrics;
8835
+ }
8836
+ return {
8837
+ ...metrics,
8838
+ cached
8839
+ };
8058
8840
  }
8059
8841
  function processImagesInOutput(output) {
8060
8842
  if (Array.isArray(output)) {
@@ -8085,7 +8867,7 @@ function processImagesInOutput(output) {
8085
8867
  }
8086
8868
  return output;
8087
8869
  }
8088
- function aggregateChatCompletionChunks(chunks) {
8870
+ function aggregateChatCompletionChunks(chunks, streamResult, endEvent) {
8089
8871
  let role = void 0;
8090
8872
  let content = void 0;
8091
8873
  let tool_calls = void 0;
@@ -8127,6 +8909,7 @@ function aggregateChatCompletionChunks(chunks) {
8127
8909
  }
8128
8910
  }
8129
8911
  }
8912
+ metrics = withCachedMetric(metrics, streamResult, endEvent);
8130
8913
  return {
8131
8914
  metrics,
8132
8915
  output: [
@@ -8143,9 +8926,33 @@ function aggregateChatCompletionChunks(chunks) {
8143
8926
  ]
8144
8927
  };
8145
8928
  }
8146
-
8147
- // src/instrumentation/plugins/anthropic-plugin.ts
8148
- import { tracingChannel as tracingChannel2 } from "dc-browser";
8929
+ function aggregateResponseStreamEvents(chunks, _streamResult, endEvent) {
8930
+ let output = void 0;
8931
+ let metrics = {};
8932
+ let metadata = void 0;
8933
+ for (const chunk of chunks) {
8934
+ if (!chunk || !chunk.type || !chunk.response) {
8935
+ continue;
8936
+ }
8937
+ if (chunk.type !== "response.completed") {
8938
+ continue;
8939
+ }
8940
+ const response = chunk.response;
8941
+ if (response?.output !== void 0) {
8942
+ output = processImagesInOutput(response.output);
8943
+ }
8944
+ const { usage: _usage, output: _output, ...rest } = response || {};
8945
+ if (Object.keys(rest).length > 0) {
8946
+ metadata = rest;
8947
+ }
8948
+ metrics = parseMetricsFromUsage(response?.usage);
8949
+ }
8950
+ return {
8951
+ output,
8952
+ metrics: withCachedMetric(metrics, void 0, endEvent),
8953
+ ...metadata !== void 0 ? { metadata } : {}
8954
+ };
8955
+ }
8149
8956
 
8150
8957
  // src/wrappers/anthropic-tokens-util.ts
8151
8958
  function finalizeAnthropicTokens(metrics) {
@@ -8166,216 +8973,76 @@ function extractAnthropicCacheTokens(cacheReadTokens = 0, cacheCreationTokens =
8166
8973
  }
8167
8974
  return cacheTokens;
8168
8975
  }
8169
-
8170
- // src/instrumentation/plugins/anthropic-plugin.ts
8171
- var AnthropicPlugin = class extends BasePlugin {
8172
- unsubscribers = [];
8173
- onEnable() {
8174
- this.subscribeToAnthropicChannels();
8175
- }
8176
- onDisable() {
8177
- for (const unsubscribe of this.unsubscribers) {
8178
- unsubscribe();
8179
- }
8180
- this.unsubscribers = [];
8181
- }
8182
- subscribeToAnthropicChannels() {
8183
- this.subscribeToStreamingChannel("orchestrion:anthropic:messages.create", {
8184
- name: "anthropic.messages.create",
8185
- type: "llm" /* LLM */,
8186
- extractInput: (args) => {
8187
- const params = args[0] || {};
8188
- const input = coalesceInput(params.messages || [], params.system);
8189
- const metadata = filterFrom(params, ["messages", "system"]);
8190
- return {
8191
- input: processAttachmentsInInput(input),
8192
- metadata: { ...metadata, provider: "anthropic" }
8193
- };
8194
- },
8195
- extractOutput: (result) => {
8196
- return result ? { role: result.role, content: result.content } : null;
8197
- },
8198
- extractMetrics: (result, startTime) => {
8199
- const metrics = parseMetricsFromUsage2(result?.usage);
8200
- if (startTime) {
8201
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8202
- }
8203
- const finalized = finalizeAnthropicTokens(metrics);
8204
- return Object.fromEntries(
8205
- Object.entries(finalized).filter(([, v]) => v !== void 0)
8206
- );
8207
- },
8208
- extractMetadata: (result) => {
8209
- const metadata = {};
8210
- const metas = ["stop_reason", "stop_sequence"];
8211
- for (const m of metas) {
8212
- if (result?.[m] !== void 0) {
8213
- metadata[m] = result[m];
8214
- }
8215
- }
8216
- return metadata;
8217
- },
8218
- aggregateChunks: aggregateAnthropicStreamChunks,
8219
- isStreaming: (args) => {
8220
- return args[0]?.stream === true;
8221
- }
8222
- });
8223
- this.subscribeToStreamingChannel(
8224
- "orchestrion:anthropic:beta.messages.create",
8225
- {
8226
- name: "anthropic.beta.messages.create",
8227
- type: "llm" /* LLM */,
8228
- extractInput: (args) => {
8229
- const params = args[0] || {};
8230
- const input = coalesceInput(params.messages || [], params.system);
8231
- const metadata = filterFrom(params, ["messages", "system"]);
8232
- return {
8233
- input: processAttachmentsInInput(input),
8234
- metadata: { ...metadata, provider: "anthropic" }
8235
- };
8236
- },
8237
- extractOutput: (result) => {
8238
- return result ? { role: result.role, content: result.content } : null;
8239
- },
8240
- extractMetrics: (result, startTime) => {
8241
- const metrics = parseMetricsFromUsage2(result?.usage);
8242
- if (startTime) {
8243
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8244
- }
8245
- const finalized = finalizeAnthropicTokens(metrics);
8246
- return Object.fromEntries(
8247
- Object.entries(finalized).filter(([, v]) => v !== void 0)
8248
- );
8249
- },
8250
- extractMetadata: (result) => {
8251
- const metadata = {};
8252
- const metas = ["stop_reason", "stop_sequence"];
8253
- for (const m of metas) {
8254
- if (result?.[m] !== void 0) {
8255
- metadata[m] = result[m];
8256
- }
8257
- }
8258
- return metadata;
8259
- },
8260
- aggregateChunks: aggregateAnthropicStreamChunks,
8261
- isStreaming: (args) => {
8262
- return args[0]?.stream === true;
8263
- }
8264
- }
8265
- );
8266
- }
8267
- /**
8268
- * Subscribe to a channel for async methods that may return streams.
8269
- * Handles both streaming and non-streaming responses based on the stream parameter.
8270
- */
8271
- subscribeToStreamingChannel(channelName, config) {
8272
- const channel = tracingChannel2(channelName);
8273
- const spans = /* @__PURE__ */ new WeakMap();
8274
- const handlers = {
8275
- start: (event) => {
8276
- const span = startSpan({
8277
- name: config.name,
8278
- spanAttributes: {
8279
- type: config.type
8280
- }
8281
- });
8282
- const startTime = getCurrentUnixTimestamp();
8283
- spans.set(event, { span, startTime });
8284
- try {
8285
- const { input, metadata } = config.extractInput(event.arguments);
8286
- span.log({
8287
- input,
8288
- metadata
8289
- });
8290
- } catch (error) {
8291
- console.error(`Error extracting input for ${channelName}:`, error);
8292
- }
8293
- },
8294
- asyncEnd: (event) => {
8295
- const spanData = spans.get(event);
8296
- if (!spanData) {
8297
- return;
8298
- }
8299
- const { span, startTime } = spanData;
8300
- const isStreaming = config.isStreaming ? config.isStreaming(event.arguments) : isAsyncIterable(event.result);
8301
- if (isStreaming && isAsyncIterable(event.result)) {
8302
- patchStreamIfNeeded(event.result, {
8303
- onComplete: (chunks) => {
8304
- try {
8305
- let output;
8306
- let metrics;
8307
- let metadata = {};
8308
- if (config.aggregateChunks) {
8309
- const aggregated = config.aggregateChunks(chunks);
8310
- output = aggregated.output;
8311
- metrics = aggregated.metrics;
8312
- metadata = aggregated.metadata || {};
8313
- } else {
8314
- output = config.extractOutput(chunks);
8315
- metrics = config.extractMetrics(chunks, startTime);
8316
- if (config.extractMetadata) {
8317
- metadata = config.extractMetadata(chunks);
8318
- }
8319
- }
8320
- if (!metrics.time_to_first_token && chunks.length > 0) {
8321
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8322
- }
8323
- span.log({
8324
- output,
8325
- metrics,
8326
- metadata
8327
- });
8328
- } catch (error) {
8329
- console.error(
8330
- `Error extracting output for ${channelName}:`,
8331
- error
8332
- );
8333
- } finally {
8334
- span.end();
8335
- }
8336
- },
8337
- onError: (error) => {
8338
- span.log({
8339
- error: error.message
8340
- });
8341
- span.end();
8342
- }
8343
- });
8344
- } else {
8345
- try {
8346
- const output = config.extractOutput(event.result);
8347
- const metrics = config.extractMetrics(event.result, startTime);
8348
- const metadata = config.extractMetadata ? config.extractMetadata(event.result) : {};
8349
- span.log({
8350
- output,
8351
- metrics,
8352
- metadata
8353
- });
8354
- } catch (error) {
8355
- console.error(`Error extracting output for ${channelName}:`, error);
8356
- } finally {
8357
- span.end();
8358
- spans.delete(event);
8359
- }
8976
+
8977
+ // src/instrumentation/plugins/anthropic-channels.ts
8978
+ var anthropicChannels = defineChannels("@anthropic-ai/sdk", {
8979
+ messagesCreate: channel({
8980
+ channelName: "messages.create",
8981
+ kind: "async"
8982
+ }),
8983
+ betaMessagesCreate: channel({
8984
+ channelName: "beta.messages.create",
8985
+ kind: "async"
8986
+ })
8987
+ });
8988
+
8989
+ // src/instrumentation/plugins/anthropic-plugin.ts
8990
+ var AnthropicPlugin = class extends BasePlugin {
8991
+ onEnable() {
8992
+ this.subscribeToAnthropicChannels();
8993
+ }
8994
+ onDisable() {
8995
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
8996
+ }
8997
+ subscribeToAnthropicChannels() {
8998
+ const anthropicConfig = {
8999
+ name: "anthropic.messages.create",
9000
+ type: "llm" /* LLM */,
9001
+ extractInput: (args) => {
9002
+ const params = args[0] || {};
9003
+ const input = coalesceInput(params.messages || [], params.system);
9004
+ const metadata = filterFrom(params, ["messages", "system"]);
9005
+ return {
9006
+ input: processAttachmentsInInput(input),
9007
+ metadata: { ...metadata, provider: "anthropic" }
9008
+ };
9009
+ },
9010
+ extractOutput: (message) => {
9011
+ return message ? { role: message.role, content: message.content } : null;
9012
+ },
9013
+ extractMetrics: (message, startTime) => {
9014
+ const metrics = parseMetricsFromUsage2(message?.usage);
9015
+ if (startTime) {
9016
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8360
9017
  }
9018
+ const finalized = finalizeAnthropicTokens(metrics);
9019
+ return Object.fromEntries(
9020
+ Object.entries(finalized).filter(
9021
+ (entry) => entry[1] !== void 0
9022
+ )
9023
+ );
8361
9024
  },
8362
- error: (event) => {
8363
- const spanData = spans.get(event);
8364
- if (!spanData) {
8365
- return;
9025
+ extractMetadata: (message) => {
9026
+ const metadata = {};
9027
+ const metas = ["stop_reason", "stop_sequence"];
9028
+ for (const m of metas) {
9029
+ if (message?.[m] !== void 0) {
9030
+ metadata[m] = message[m];
9031
+ }
8366
9032
  }
8367
- const { span } = spanData;
8368
- span.log({
8369
- error: event.error.message
8370
- });
8371
- span.end();
8372
- spans.delete(event);
8373
- }
9033
+ return metadata;
9034
+ },
9035
+ aggregateChunks: (chunks) => aggregateAnthropicStreamChunks(chunks)
8374
9036
  };
8375
- channel.subscribe(handlers);
8376
- this.unsubscribers.push(() => {
8377
- channel.unsubscribe(handlers);
8378
- });
9037
+ this.unsubscribers.push(
9038
+ traceStreamingChannel(anthropicChannels.messagesCreate, anthropicConfig)
9039
+ );
9040
+ this.unsubscribers.push(
9041
+ traceStreamingChannel(anthropicChannels.betaMessagesCreate, {
9042
+ ...anthropicConfig,
9043
+ name: "anthropic.beta.messages.create"
9044
+ })
9045
+ );
8379
9046
  }
8380
9047
  };
8381
9048
  function parseMetricsFromUsage2(usage) {
@@ -8399,29 +9066,29 @@ function aggregateAnthropicStreamChunks(chunks) {
8399
9066
  const deltas = [];
8400
9067
  let metrics = {};
8401
9068
  let metadata = {};
8402
- for (const chunk of chunks) {
8403
- switch (chunk?.type) {
9069
+ for (const event of chunks) {
9070
+ switch (event?.type) {
8404
9071
  case "message_start":
8405
- if (chunk.message?.usage) {
8406
- const initialMetrics = parseMetricsFromUsage2(chunk.message.usage);
9072
+ if (event.message?.usage) {
9073
+ const initialMetrics = parseMetricsFromUsage2(event.message.usage);
8407
9074
  metrics = { ...metrics, ...initialMetrics };
8408
9075
  }
8409
9076
  break;
8410
9077
  case "content_block_delta":
8411
- if (chunk.delta?.type === "text_delta") {
8412
- const text = chunk.delta?.text;
9078
+ if (event.delta?.type === "text_delta") {
9079
+ const text = event.delta.text;
8413
9080
  if (text) {
8414
9081
  deltas.push(text);
8415
9082
  }
8416
9083
  }
8417
9084
  break;
8418
9085
  case "message_delta":
8419
- if (chunk.usage) {
8420
- const finalMetrics = parseMetricsFromUsage2(chunk.usage);
9086
+ if (event.usage) {
9087
+ const finalMetrics = parseMetricsFromUsage2(event.usage);
8421
9088
  metrics = { ...metrics, ...finalMetrics };
8422
9089
  }
8423
- if (chunk.delta) {
8424
- metadata = { ...metadata, ...chunk.delta };
9090
+ if (event.delta) {
9091
+ metadata = { ...metadata, ...event.delta };
8425
9092
  }
8426
9093
  break;
8427
9094
  }
@@ -8429,7 +9096,9 @@ function aggregateAnthropicStreamChunks(chunks) {
8429
9096
  const output = deltas.join("");
8430
9097
  const finalized = finalizeAnthropicTokens(metrics);
8431
9098
  const filteredMetrics = Object.fromEntries(
8432
- Object.entries(finalized).filter(([, v]) => v !== void 0)
9099
+ Object.entries(finalized).filter(
9100
+ (entry) => entry[1] !== void 0
9101
+ )
8433
9102
  );
8434
9103
  return {
8435
9104
  output,
@@ -8437,6 +9106,9 @@ function aggregateAnthropicStreamChunks(chunks) {
8437
9106
  metadata
8438
9107
  };
8439
9108
  }
9109
+ function isAnthropicBase64ContentBlock(input) {
9110
+ return (input.type === "image" || input.type === "document") && isObject(input.source) && input.source.type === "base64";
9111
+ }
8440
9112
  function convertBase64ToAttachment(source, contentType) {
8441
9113
  const mediaType = typeof source.media_type === "string" ? source.media_type : "image/png";
8442
9114
  const base64Data = source.data;
@@ -8460,14 +9132,14 @@ function convertBase64ToAttachment(source, contentType) {
8460
9132
  data: attachment
8461
9133
  };
8462
9134
  }
8463
- return source;
9135
+ return { ...source };
8464
9136
  }
8465
9137
  function processAttachmentsInInput(input) {
8466
9138
  if (Array.isArray(input)) {
8467
9139
  return input.map(processAttachmentsInInput);
8468
9140
  }
8469
9141
  if (isObject(input)) {
8470
- if ((input.type === "image" || input.type === "document") && isObject(input.source) && input.source.type === "base64") {
9142
+ if (isAnthropicBase64ContentBlock(input)) {
8471
9143
  return {
8472
9144
  ...input,
8473
9145
  source: convertBase64ToAttachment(input.source, input.type)
@@ -8498,8 +9170,35 @@ function filterFrom(obj, fieldsToRemove) {
8498
9170
  return result;
8499
9171
  }
8500
9172
 
9173
+ // src/instrumentation/plugins/ai-sdk-channels.ts
9174
+ var aiSDKChannels = defineChannels("ai", {
9175
+ generateText: channel({
9176
+ channelName: "generateText",
9177
+ kind: "async"
9178
+ }),
9179
+ streamText: channel({
9180
+ channelName: "streamText",
9181
+ kind: "async"
9182
+ }),
9183
+ generateObject: channel({
9184
+ channelName: "generateObject",
9185
+ kind: "async"
9186
+ }),
9187
+ streamObject: channel({
9188
+ channelName: "streamObject",
9189
+ kind: "async"
9190
+ }),
9191
+ agentGenerate: channel({
9192
+ channelName: "Agent.generate",
9193
+ kind: "async"
9194
+ }),
9195
+ agentStream: channel({
9196
+ channelName: "Agent.stream",
9197
+ kind: "async"
9198
+ })
9199
+ });
9200
+
8501
9201
  // src/instrumentation/plugins/ai-sdk-plugin.ts
8502
- import { tracingChannel as tracingChannel3 } from "dc-browser";
8503
9202
  var DEFAULT_DENY_OUTPUT_PATHS = [
8504
9203
  // v3
8505
9204
  "roundtrips[].request.body",
@@ -8515,7 +9214,6 @@ var DEFAULT_DENY_OUTPUT_PATHS = [
8515
9214
  "steps[].response.headers"
8516
9215
  ];
8517
9216
  var AISDKPlugin = class extends BasePlugin {
8518
- unsubscribers = [];
8519
9217
  config;
8520
9218
  constructor(config = {}) {
8521
9219
  super();
@@ -8525,249 +9223,148 @@ var AISDKPlugin = class extends BasePlugin {
8525
9223
  this.subscribeToAISDK();
8526
9224
  }
8527
9225
  onDisable() {
8528
- for (const unsubscribe of this.unsubscribers) {
8529
- unsubscribe();
8530
- }
8531
- this.unsubscribers = [];
9226
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
8532
9227
  }
8533
9228
  subscribeToAISDK() {
8534
9229
  const denyOutputPaths = this.config.denyOutputPaths || DEFAULT_DENY_OUTPUT_PATHS;
8535
- this.subscribeToStreamingChannel("orchestrion:ai-sdk:generateText", {
8536
- name: "generateText",
8537
- type: "llm" /* LLM */,
8538
- extractInput: (args) => {
8539
- const params = args[0] || {};
8540
- return {
8541
- input: processAISDKInput(params),
8542
- metadata: extractMetadataFromParams(params)
8543
- };
8544
- },
8545
- extractOutput: (result) => {
8546
- return processAISDKOutput(result, denyOutputPaths);
8547
- },
8548
- extractMetrics: (result, startTime) => {
8549
- const metrics = extractTokenMetrics(result);
8550
- if (startTime) {
8551
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8552
- }
8553
- return metrics;
8554
- },
8555
- aggregateChunks: aggregateAISDKChunks
8556
- });
8557
- this.subscribeToStreamingChannel("orchestrion:ai-sdk:streamText", {
8558
- name: "streamText",
8559
- type: "llm" /* LLM */,
8560
- extractInput: (args) => {
8561
- const params = args[0] || {};
8562
- return {
8563
- input: processAISDKInput(params),
8564
- metadata: extractMetadataFromParams(params)
8565
- };
8566
- },
8567
- extractOutput: (result) => {
8568
- return processAISDKOutput(result, denyOutputPaths);
8569
- },
8570
- extractMetrics: (result, startTime) => {
8571
- const metrics = extractTokenMetrics(result);
8572
- if (startTime) {
8573
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8574
- }
8575
- return metrics;
8576
- },
8577
- aggregateChunks: aggregateAISDKChunks
8578
- });
8579
- this.subscribeToStreamingChannel("orchestrion:ai-sdk:generateObject", {
8580
- name: "generateObject",
8581
- type: "llm" /* LLM */,
8582
- extractInput: (args) => {
8583
- const params = args[0] || {};
8584
- return {
8585
- input: processAISDKInput(params),
8586
- metadata: extractMetadataFromParams(params)
8587
- };
8588
- },
8589
- extractOutput: (result) => {
8590
- return processAISDKOutput(result, denyOutputPaths);
8591
- },
8592
- extractMetrics: (result, startTime) => {
8593
- const metrics = extractTokenMetrics(result);
8594
- if (startTime) {
8595
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8596
- }
8597
- return metrics;
8598
- },
8599
- aggregateChunks: aggregateAISDKChunks
8600
- });
8601
- this.subscribeToStreamingChannel("orchestrion:ai-sdk:streamObject", {
8602
- name: "streamObject",
8603
- type: "llm" /* LLM */,
8604
- extractInput: (args) => {
8605
- const params = args[0] || {};
8606
- return {
8607
- input: processAISDKInput(params),
8608
- metadata: extractMetadataFromParams(params)
8609
- };
8610
- },
8611
- extractOutput: (result) => {
8612
- return processAISDKOutput(result, denyOutputPaths);
8613
- },
8614
- extractMetrics: (result, startTime) => {
8615
- const metrics = extractTokenMetrics(result);
8616
- if (startTime) {
8617
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8618
- }
8619
- return metrics;
8620
- },
8621
- aggregateChunks: aggregateAISDKChunks
8622
- });
8623
- this.subscribeToStreamingChannel("orchestrion:ai-sdk:Agent.generate", {
8624
- name: "Agent.generate",
8625
- type: "llm" /* LLM */,
8626
- extractInput: (args) => {
8627
- const params = args[0] || {};
8628
- return {
8629
- input: processAISDKInput(params),
8630
- metadata: extractMetadataFromParams(params)
8631
- };
8632
- },
8633
- extractOutput: (result) => {
8634
- return processAISDKOutput(result, denyOutputPaths);
8635
- },
8636
- extractMetrics: (result, startTime) => {
8637
- const metrics = extractTokenMetrics(result);
8638
- if (startTime) {
8639
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8640
- }
8641
- return metrics;
8642
- },
8643
- aggregateChunks: aggregateAISDKChunks
8644
- });
8645
- this.subscribeToStreamingChannel("orchestrion:ai-sdk:Agent.stream", {
8646
- name: "Agent.stream",
8647
- type: "llm" /* LLM */,
8648
- extractInput: (args) => {
8649
- const params = args[0] || {};
8650
- return {
8651
- input: processAISDKInput(params),
8652
- metadata: extractMetadataFromParams(params)
8653
- };
8654
- },
8655
- extractOutput: (result) => {
8656
- return processAISDKOutput(result, denyOutputPaths);
8657
- },
8658
- extractMetrics: (result, startTime) => {
8659
- const metrics = extractTokenMetrics(result);
8660
- if (startTime) {
8661
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8662
- }
8663
- return metrics;
8664
- },
8665
- aggregateChunks: aggregateAISDKChunks
8666
- });
8667
- }
8668
- /**
8669
- * Subscribe to a channel for async methods that may return streams.
8670
- * Handles both streaming and non-streaming responses.
8671
- */
8672
- subscribeToStreamingChannel(channelName, config) {
8673
- const channel = tracingChannel3(channelName);
8674
- const spans = /* @__PURE__ */ new WeakMap();
8675
- const handlers = {
8676
- start: (event) => {
8677
- const span = startSpan({
8678
- name: config.name,
8679
- spanAttributes: {
8680
- type: config.type
9230
+ this.unsubscribers.push(
9231
+ traceStreamingChannel(aiSDKChannels.generateText, {
9232
+ name: "generateText",
9233
+ type: "llm" /* LLM */,
9234
+ extractInput: ([params]) => {
9235
+ return {
9236
+ input: processAISDKInput(params),
9237
+ metadata: extractMetadataFromParams(params)
9238
+ };
9239
+ },
9240
+ extractOutput: (result) => {
9241
+ return processAISDKOutput(result, denyOutputPaths);
9242
+ },
9243
+ extractMetrics: (result, startTime) => {
9244
+ const metrics = extractTokenMetrics(result);
9245
+ if (startTime) {
9246
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8681
9247
  }
8682
- });
8683
- const startTime = getCurrentUnixTimestamp();
8684
- spans.set(event, { span, startTime });
8685
- try {
8686
- const { input, metadata } = config.extractInput(event.arguments);
8687
- span.log({
8688
- input,
8689
- metadata
8690
- });
8691
- } catch (error) {
8692
- console.error(`Error extracting input for ${channelName}:`, error);
8693
- }
8694
- },
8695
- asyncEnd: (event) => {
8696
- const spanData = spans.get(event);
8697
- if (!spanData) {
8698
- return;
8699
- }
8700
- const { span, startTime } = spanData;
8701
- if (isAsyncIterable(event.result)) {
8702
- patchStreamIfNeeded(event.result, {
8703
- onComplete: (chunks) => {
8704
- try {
8705
- let output;
8706
- let metrics;
8707
- if (config.aggregateChunks) {
8708
- const aggregated = config.aggregateChunks(chunks);
8709
- output = aggregated.output;
8710
- metrics = aggregated.metrics;
8711
- } else {
8712
- output = config.extractOutput(chunks);
8713
- metrics = config.extractMetrics(chunks, startTime);
8714
- }
8715
- if (!metrics.time_to_first_token && chunks.length > 0) {
8716
- metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8717
- }
8718
- span.log({
8719
- output,
8720
- metrics
8721
- });
8722
- } catch (error) {
8723
- console.error(
8724
- `Error extracting output for ${channelName}:`,
8725
- error
8726
- );
8727
- } finally {
8728
- span.end();
8729
- }
8730
- },
8731
- onError: (error) => {
8732
- span.log({
8733
- error: error.message
8734
- });
8735
- span.end();
8736
- }
8737
- });
8738
- } else {
8739
- try {
8740
- const output = config.extractOutput(event.result);
8741
- const metrics = config.extractMetrics(event.result, startTime);
8742
- span.log({
8743
- output,
8744
- metrics
8745
- });
8746
- } catch (error) {
8747
- console.error(`Error extracting output for ${channelName}:`, error);
8748
- } finally {
8749
- span.end();
8750
- spans.delete(event);
9248
+ return metrics;
9249
+ },
9250
+ aggregateChunks: aggregateAISDKChunks
9251
+ })
9252
+ );
9253
+ this.unsubscribers.push(
9254
+ traceStreamingChannel(aiSDKChannels.streamText, {
9255
+ name: "streamText",
9256
+ type: "llm" /* LLM */,
9257
+ extractInput: ([params]) => {
9258
+ return {
9259
+ input: processAISDKInput(params),
9260
+ metadata: extractMetadataFromParams(params)
9261
+ };
9262
+ },
9263
+ extractOutput: (result) => {
9264
+ return processAISDKOutput(result, denyOutputPaths);
9265
+ },
9266
+ extractMetrics: (result, startTime) => {
9267
+ const metrics = extractTokenMetrics(result);
9268
+ if (startTime) {
9269
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9270
+ }
9271
+ return metrics;
9272
+ },
9273
+ aggregateChunks: aggregateAISDKChunks
9274
+ })
9275
+ );
9276
+ this.unsubscribers.push(
9277
+ traceStreamingChannel(aiSDKChannels.generateObject, {
9278
+ name: "generateObject",
9279
+ type: "llm" /* LLM */,
9280
+ extractInput: ([params]) => {
9281
+ return {
9282
+ input: processAISDKInput(params),
9283
+ metadata: extractMetadataFromParams(params)
9284
+ };
9285
+ },
9286
+ extractOutput: (result) => {
9287
+ return processAISDKOutput(result, denyOutputPaths);
9288
+ },
9289
+ extractMetrics: (result, startTime) => {
9290
+ const metrics = extractTokenMetrics(result);
9291
+ if (startTime) {
9292
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9293
+ }
9294
+ return metrics;
9295
+ },
9296
+ aggregateChunks: aggregateAISDKChunks
9297
+ })
9298
+ );
9299
+ this.unsubscribers.push(
9300
+ traceStreamingChannel(aiSDKChannels.streamObject, {
9301
+ name: "streamObject",
9302
+ type: "llm" /* LLM */,
9303
+ extractInput: ([params]) => {
9304
+ return {
9305
+ input: processAISDKInput(params),
9306
+ metadata: extractMetadataFromParams(params)
9307
+ };
9308
+ },
9309
+ extractOutput: (result) => {
9310
+ return processAISDKOutput(result, denyOutputPaths);
9311
+ },
9312
+ extractMetrics: (result, startTime) => {
9313
+ const metrics = extractTokenMetrics(result);
9314
+ if (startTime) {
9315
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8751
9316
  }
8752
- }
8753
- },
8754
- error: (event) => {
8755
- const spanData = spans.get(event);
8756
- if (!spanData) {
8757
- return;
8758
- }
8759
- const { span } = spanData;
8760
- span.log({
8761
- error: event.error.message
8762
- });
8763
- span.end();
8764
- spans.delete(event);
8765
- }
8766
- };
8767
- channel.subscribe(handlers);
8768
- this.unsubscribers.push(() => {
8769
- channel.unsubscribe(handlers);
8770
- });
9317
+ return metrics;
9318
+ },
9319
+ aggregateChunks: aggregateAISDKChunks
9320
+ })
9321
+ );
9322
+ this.unsubscribers.push(
9323
+ traceStreamingChannel(aiSDKChannels.agentGenerate, {
9324
+ name: "Agent.generate",
9325
+ type: "llm" /* LLM */,
9326
+ extractInput: ([params]) => {
9327
+ return {
9328
+ input: processAISDKInput(params),
9329
+ metadata: extractMetadataFromParams(params)
9330
+ };
9331
+ },
9332
+ extractOutput: (result) => {
9333
+ return processAISDKOutput(result, denyOutputPaths);
9334
+ },
9335
+ extractMetrics: (result, startTime) => {
9336
+ const metrics = extractTokenMetrics(result);
9337
+ if (startTime) {
9338
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9339
+ }
9340
+ return metrics;
9341
+ },
9342
+ aggregateChunks: aggregateAISDKChunks
9343
+ })
9344
+ );
9345
+ this.unsubscribers.push(
9346
+ traceStreamingChannel(aiSDKChannels.agentStream, {
9347
+ name: "Agent.stream",
9348
+ type: "llm" /* LLM */,
9349
+ extractInput: ([params]) => {
9350
+ return {
9351
+ input: processAISDKInput(params),
9352
+ metadata: extractMetadataFromParams(params)
9353
+ };
9354
+ },
9355
+ extractOutput: (result) => {
9356
+ return processAISDKOutput(result, denyOutputPaths);
9357
+ },
9358
+ extractMetrics: (result, startTime) => {
9359
+ const metrics = extractTokenMetrics(result);
9360
+ if (startTime) {
9361
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9362
+ }
9363
+ return metrics;
9364
+ },
9365
+ aggregateChunks: aggregateAISDKChunks
9366
+ })
9367
+ );
8771
9368
  }
8772
9369
  };
8773
9370
  function processAISDKInput(params) {
@@ -9009,7 +9606,10 @@ function omitAtPath(obj, keys) {
9009
9606
  if (Array.isArray(obj)) {
9010
9607
  obj.forEach((item) => {
9011
9608
  if (remainingKeys.length > 0) {
9012
- omitAtPath(item, remainingKeys);
9609
+ omitAtPath(
9610
+ item,
9611
+ remainingKeys
9612
+ );
9013
9613
  }
9014
9614
  });
9015
9615
  }
@@ -9019,7 +9619,10 @@ function omitAtPath(obj, keys) {
9019
9619
  }
9020
9620
  } else {
9021
9621
  if (obj && typeof obj === "object" && firstKey in obj) {
9022
- omitAtPath(obj[firstKey], remainingKeys);
9622
+ omitAtPath(
9623
+ obj[firstKey],
9624
+ remainingKeys
9625
+ );
9023
9626
  }
9024
9627
  }
9025
9628
  }
@@ -9032,8 +9635,18 @@ function omit(obj, paths) {
9032
9635
  return result;
9033
9636
  }
9034
9637
 
9638
+ // src/instrumentation/plugins/claude-agent-sdk-channels.ts
9639
+ var claudeAgentSDKChannels = defineChannels(
9640
+ "@anthropic-ai/claude-agent-sdk",
9641
+ {
9642
+ query: channel({
9643
+ channelName: "query",
9644
+ kind: "async"
9645
+ })
9646
+ }
9647
+ );
9648
+
9035
9649
  // src/instrumentation/plugins/claude-agent-sdk-plugin.ts
9036
- import { tracingChannel as tracingChannel4 } from "dc-browser";
9037
9650
  function filterSerializableOptions(options) {
9038
9651
  const allowedKeys = [
9039
9652
  "model",
@@ -9117,7 +9730,9 @@ async function createLLMSpanForMessages(messages, prompt, conversationHistory, o
9117
9730
  const input = buildLLMInput(prompt, conversationHistory);
9118
9731
  const outputs = messages.map(
9119
9732
  (m) => m.message?.content && m.message?.role ? { content: m.message.content, role: m.message.role } : void 0
9120
- ).filter((c) => c !== void 0);
9733
+ ).filter(
9734
+ (c) => c !== void 0
9735
+ );
9121
9736
  const span = startSpan({
9122
9737
  name: "anthropic.messages.create",
9123
9738
  spanAttributes: {
@@ -9136,7 +9751,6 @@ async function createLLMSpanForMessages(messages, prompt, conversationHistory, o
9136
9751
  return lastMessage.message?.content && lastMessage.message?.role ? { content: lastMessage.message.content, role: lastMessage.message.role } : void 0;
9137
9752
  }
9138
9753
  var ClaudeAgentSDKPlugin = class extends BasePlugin {
9139
- unsubscribers = [];
9140
9754
  onEnable() {
9141
9755
  this.subscribeToQuery();
9142
9756
  }
@@ -9152,12 +9766,13 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
9152
9766
  * and individual LLM calls.
9153
9767
  */
9154
9768
  subscribeToQuery() {
9155
- const channel = tracingChannel4("orchestrion:claude-agent-sdk:query");
9769
+ const channel2 = claudeAgentSDKChannels.query.tracingChannel();
9156
9770
  const spans = /* @__PURE__ */ new WeakMap();
9157
9771
  const handlers = {
9158
9772
  start: (event) => {
9159
- const params = event.arguments[0] ?? {};
9160
- const { prompt, options = {} } = params;
9773
+ const params = event.arguments[0];
9774
+ const prompt = params?.prompt;
9775
+ const options = params?.options ?? {};
9161
9776
  const span = startSpan({
9162
9777
  name: "Claude Agent",
9163
9778
  spanAttributes: {
@@ -9169,7 +9784,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
9169
9784
  span.log({
9170
9785
  input: typeof prompt === "string" ? prompt : {
9171
9786
  type: "streaming",
9172
- description: "AsyncIterable<SDKMessage>"
9787
+ description: "AsyncIterable<ClaudeAgentSDKMessage>"
9173
9788
  },
9174
9789
  metadata: filterSerializableOptions(options)
9175
9790
  });
@@ -9191,12 +9806,19 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
9191
9806
  if (!spanData) {
9192
9807
  return;
9193
9808
  }
9194
- if (isAsyncIterable(event.result)) {
9195
- patchStreamIfNeeded(event.result, {
9809
+ const eventResult = event.result;
9810
+ if (eventResult === void 0) {
9811
+ spanData.span.end();
9812
+ spans.delete(event);
9813
+ return;
9814
+ }
9815
+ if (isAsyncIterable(eventResult)) {
9816
+ patchStreamIfNeeded(eventResult, {
9196
9817
  onChunk: async (message) => {
9197
9818
  const currentTime = getCurrentUnixTimestamp();
9198
9819
  const params = event.arguments[0];
9199
- const { prompt, options = {} } = params;
9820
+ const prompt = params?.prompt;
9821
+ const options = params?.options ?? {};
9200
9822
  const messageId = message.message?.id;
9201
9823
  if (messageId && messageId !== spanData.currentMessageId) {
9202
9824
  if (spanData.currentMessages.length > 0) {
@@ -9255,7 +9877,8 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
9255
9877
  onComplete: async () => {
9256
9878
  try {
9257
9879
  const params = event.arguments[0];
9258
- const { prompt, options = {} } = params;
9880
+ const prompt = params?.prompt;
9881
+ const options = params?.options ?? {};
9259
9882
  if (spanData.currentMessages.length > 0) {
9260
9883
  const finalMessage = await createLLMSpanForMessages(
9261
9884
  spanData.currentMessages,
@@ -9293,7 +9916,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
9293
9916
  } else {
9294
9917
  try {
9295
9918
  spanData.span.log({
9296
- output: event.result
9919
+ output: eventResult
9297
9920
  });
9298
9921
  } catch (error) {
9299
9922
  console.error(
@@ -9308,7 +9931,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
9308
9931
  },
9309
9932
  error: (event) => {
9310
9933
  const spanData = spans.get(event);
9311
- if (!spanData) {
9934
+ if (!spanData || !event.error) {
9312
9935
  return;
9313
9936
  }
9314
9937
  const { span } = spanData;
@@ -9319,53 +9942,39 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
9319
9942
  spans.delete(event);
9320
9943
  }
9321
9944
  };
9322
- channel.subscribe(handlers);
9945
+ channel2.subscribe(handlers);
9323
9946
  this.unsubscribers.push(() => {
9324
- channel.unsubscribe(handlers);
9947
+ channel2.unsubscribe(handlers);
9325
9948
  });
9326
9949
  }
9327
9950
  };
9328
9951
 
9952
+ // src/instrumentation/plugins/google-genai-channels.ts
9953
+ var googleGenAIChannels = defineChannels("@google/genai", {
9954
+ generateContent: channel({
9955
+ channelName: "models.generateContent",
9956
+ kind: "async"
9957
+ }),
9958
+ generateContentStream: channel({
9959
+ channelName: "models.generateContentStream",
9960
+ kind: "async"
9961
+ })
9962
+ });
9963
+
9329
9964
  // src/instrumentation/plugins/google-genai-plugin.ts
9330
- import { tracingChannel as tracingChannel5 } from "dc-browser";
9331
9965
  var GoogleGenAIPlugin = class extends BasePlugin {
9332
- unsubscribers = [];
9333
9966
  onEnable() {
9334
9967
  this.subscribeToGoogleGenAIChannels();
9335
9968
  }
9336
9969
  onDisable() {
9337
- for (const unsubscribe of this.unsubscribers) {
9338
- unsubscribe();
9339
- }
9340
- this.unsubscribers = [];
9970
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
9341
9971
  }
9342
9972
  subscribeToGoogleGenAIChannels() {
9343
- this.subscribeToChannel("orchestrion:google-genai:models.generateContent", {
9344
- name: "google-genai.generateContent",
9345
- type: "llm" /* LLM */,
9346
- extractInput: (args) => {
9347
- const params = args[0] || {};
9348
- const input = serializeInput(params);
9349
- const metadata = extractMetadata(params);
9350
- return {
9351
- input,
9352
- metadata: { ...metadata, provider: "google-genai" }
9353
- };
9354
- },
9355
- extractOutput: (result) => {
9356
- return result;
9357
- },
9358
- extractMetrics: (result, startTime) => {
9359
- return extractGenerateContentMetrics(result, startTime);
9360
- }
9361
- });
9362
- this.subscribeToGoogleStreamingChannel(
9363
- "orchestrion:google-genai:models.generateContentStream",
9364
- {
9365
- name: "google-genai.generateContentStream",
9973
+ this.unsubscribers.push(
9974
+ traceAsyncChannel(googleGenAIChannels.generateContent, {
9975
+ name: "google-genai.generateContent",
9366
9976
  type: "llm" /* LLM */,
9367
- extractInput: (args) => {
9368
- const params = args[0] || {};
9977
+ extractInput: ([params]) => {
9369
9978
  const input = serializeInput(params);
9370
9979
  const metadata = extractMetadata(params);
9371
9980
  return {
@@ -9373,150 +9982,37 @@ var GoogleGenAIPlugin = class extends BasePlugin {
9373
9982
  metadata: { ...metadata, provider: "google-genai" }
9374
9983
  };
9375
9984
  },
9376
- aggregateChunks: aggregateGenerateContentChunks
9377
- }
9378
- );
9379
- }
9380
- subscribeToChannel(channelName, config) {
9381
- const channel = tracingChannel5(channelName);
9382
- const spans = /* @__PURE__ */ new WeakMap();
9383
- const handlers = {
9384
- start: (event) => {
9385
- const span = startSpan({
9386
- name: config.name,
9387
- spanAttributes: {
9388
- type: config.type
9389
- }
9390
- });
9391
- const startTime = getCurrentUnixTimestamp();
9392
- spans.set(event, { span, startTime });
9393
- try {
9394
- const { input, metadata } = config.extractInput(event.arguments);
9395
- span.log({
9396
- input,
9397
- metadata
9398
- });
9399
- } catch (error) {
9400
- console.error(`Error extracting input for ${channelName}:`, error);
9401
- }
9402
- },
9403
- asyncEnd: (event) => {
9404
- const spanData = spans.get(event);
9405
- if (!spanData) {
9406
- return;
9407
- }
9408
- const { span, startTime } = spanData;
9409
- try {
9410
- const output = config.extractOutput(event.result);
9411
- const metrics = config.extractMetrics(event.result, startTime);
9412
- span.log({
9413
- output,
9414
- metrics
9415
- });
9416
- } catch (error) {
9417
- console.error(`Error extracting output for ${channelName}:`, error);
9418
- } finally {
9419
- span.end();
9420
- spans.delete(event);
9421
- }
9422
- },
9423
- error: (event) => {
9424
- const spanData = spans.get(event);
9425
- if (!spanData) {
9426
- return;
9985
+ extractOutput: (result) => {
9986
+ return result;
9987
+ },
9988
+ extractMetrics: (result, startTime) => {
9989
+ return extractGenerateContentMetrics(result, startTime);
9427
9990
  }
9428
- const { span } = spanData;
9429
- span.log({
9430
- error: event.error.message
9431
- });
9432
- span.end();
9433
- spans.delete(event);
9434
- }
9435
- };
9436
- channel.subscribe(handlers);
9437
- this.unsubscribers.push(() => {
9438
- channel.unsubscribe(handlers);
9439
- });
9440
- }
9441
- subscribeToGoogleStreamingChannel(channelName, config) {
9442
- const channel = tracingChannel5(channelName);
9443
- const spans = /* @__PURE__ */ new WeakMap();
9444
- const handlers = {
9445
- start: (event) => {
9446
- const span = startSpan({
9447
- name: config.name,
9448
- spanAttributes: {
9449
- type: config.type
9450
- }
9451
- });
9452
- const startTime = getCurrentUnixTimestamp();
9453
- spans.set(event, { span, startTime });
9454
- try {
9455
- const { input, metadata } = config.extractInput(event.arguments);
9456
- span.log({
9991
+ })
9992
+ );
9993
+ this.unsubscribers.push(
9994
+ traceStreamingChannel(googleGenAIChannels.generateContentStream, {
9995
+ name: "google-genai.generateContentStream",
9996
+ type: "llm" /* LLM */,
9997
+ extractInput: ([params]) => {
9998
+ const input = serializeInput(params);
9999
+ const metadata = extractMetadata(params);
10000
+ return {
9457
10001
  input,
9458
- metadata
9459
- });
9460
- } catch (error) {
9461
- console.error(`Error extracting input for ${channelName}:`, error);
9462
- }
9463
- },
9464
- asyncEnd: (event) => {
9465
- const spanData = spans.get(event);
9466
- if (!spanData) {
9467
- return;
9468
- }
9469
- const { span, startTime } = spanData;
9470
- if (isAsyncIterable(event.result)) {
9471
- patchStreamIfNeeded(event.result, {
9472
- onComplete: (chunks) => {
9473
- try {
9474
- const { output, metrics } = config.aggregateChunks(
9475
- chunks,
9476
- startTime
9477
- );
9478
- span.log({
9479
- output,
9480
- metrics
9481
- });
9482
- } catch (error) {
9483
- console.error(
9484
- `Error extracting output for ${channelName}:`,
9485
- error
9486
- );
9487
- } finally {
9488
- span.end();
9489
- }
9490
- },
9491
- onError: (error) => {
9492
- span.log({
9493
- error: error.message
9494
- });
9495
- span.end();
9496
- }
9497
- });
9498
- } else {
9499
- span.end();
9500
- spans.delete(event);
9501
- }
9502
- },
9503
- error: (event) => {
9504
- const spanData = spans.get(event);
9505
- if (!spanData) {
9506
- return;
10002
+ metadata: { ...metadata, provider: "google-genai" }
10003
+ };
10004
+ },
10005
+ extractOutput: (result) => {
10006
+ return result;
10007
+ },
10008
+ extractMetrics: () => {
10009
+ return {};
10010
+ },
10011
+ aggregateChunks: (chunks, _result, _endEvent, startTime) => {
10012
+ return aggregateGenerateContentChunks(chunks, startTime);
9507
10013
  }
9508
- const { span } = spanData;
9509
- span.log({
9510
- error: event.error.message
9511
- });
9512
- span.end();
9513
- spans.delete(event);
9514
- }
9515
- };
9516
- channel.subscribe(handlers);
9517
- this.unsubscribers.push(() => {
9518
- channel.unsubscribe(handlers);
9519
- });
10014
+ })
10015
+ );
9520
10016
  }
9521
10017
  };
9522
10018
  function serializeInput(params) {
@@ -9572,8 +10068,12 @@ function serializePart(part) {
9572
10068
  const buffer = typeof data === "string" ? typeof Buffer !== "undefined" ? Buffer.from(data, "base64") : new Uint8Array(
9573
10069
  atob(data).split("").map((c) => c.charCodeAt(0))
9574
10070
  ) : typeof Buffer !== "undefined" ? Buffer.from(data) : new Uint8Array(data);
10071
+ const arrayBuffer = buffer instanceof Uint8Array ? buffer.buffer.slice(
10072
+ buffer.byteOffset,
10073
+ buffer.byteOffset + buffer.byteLength
10074
+ ) : buffer;
9575
10075
  const attachment = new Attachment({
9576
- data: buffer,
10076
+ data: arrayBuffer,
9577
10077
  filename,
9578
10078
  contentType: mimeType || "application/octet-stream"
9579
10079
  });
@@ -9622,33 +10122,36 @@ function extractGenerateContentMetrics(response, startTime) {
9622
10122
  const end = getCurrentUnixTimestamp();
9623
10123
  metrics.duration = end - startTime;
9624
10124
  }
9625
- if (response.usageMetadata) {
9626
- const usage = response.usageMetadata;
9627
- if (usage.promptTokenCount !== void 0) {
9628
- metrics.prompt_tokens = usage.promptTokenCount;
9629
- }
9630
- if (usage.candidatesTokenCount !== void 0) {
9631
- metrics.completion_tokens = usage.candidatesTokenCount;
9632
- }
9633
- if (usage.totalTokenCount !== void 0) {
9634
- metrics.tokens = usage.totalTokenCount;
9635
- }
9636
- if (usage.cachedContentTokenCount !== void 0) {
9637
- metrics.prompt_cached_tokens = usage.cachedContentTokenCount;
9638
- }
9639
- if (usage.thoughtsTokenCount !== void 0) {
9640
- metrics.completion_reasoning_tokens = usage.thoughtsTokenCount;
9641
- }
10125
+ if (response?.usageMetadata) {
10126
+ populateUsageMetrics(metrics, response.usageMetadata);
9642
10127
  }
9643
10128
  return metrics;
9644
10129
  }
10130
+ function populateUsageMetrics(metrics, usage) {
10131
+ if (usage.promptTokenCount !== void 0) {
10132
+ metrics.prompt_tokens = usage.promptTokenCount;
10133
+ }
10134
+ if (usage.candidatesTokenCount !== void 0) {
10135
+ metrics.completion_tokens = usage.candidatesTokenCount;
10136
+ }
10137
+ if (usage.totalTokenCount !== void 0) {
10138
+ metrics.tokens = usage.totalTokenCount;
10139
+ }
10140
+ if (usage.cachedContentTokenCount !== void 0) {
10141
+ metrics.prompt_cached_tokens = usage.cachedContentTokenCount;
10142
+ }
10143
+ if (usage.thoughtsTokenCount !== void 0) {
10144
+ metrics.completion_reasoning_tokens = usage.thoughtsTokenCount;
10145
+ }
10146
+ }
9645
10147
  function aggregateGenerateContentChunks(chunks, startTime) {
9646
- const end = getCurrentUnixTimestamp();
9647
- const metrics = {
9648
- duration: end - startTime
9649
- };
10148
+ const metrics = {};
10149
+ if (startTime !== void 0) {
10150
+ const end = getCurrentUnixTimestamp();
10151
+ metrics.duration = end - startTime;
10152
+ }
9650
10153
  let firstTokenTime = null;
9651
- if (chunks.length > 0 && firstTokenTime === null) {
10154
+ if (chunks.length > 0 && firstTokenTime === null && startTime !== void 0) {
9652
10155
  firstTokenTime = getCurrentUnixTimestamp();
9653
10156
  metrics.time_to_first_token = firstTokenTime - startTime;
9654
10157
  }
@@ -9719,21 +10222,7 @@ function aggregateGenerateContentChunks(chunks, startTime) {
9719
10222
  }
9720
10223
  if (usageMetadata) {
9721
10224
  output.usageMetadata = usageMetadata;
9722
- if (usageMetadata.promptTokenCount !== void 0) {
9723
- metrics.prompt_tokens = usageMetadata.promptTokenCount;
9724
- }
9725
- if (usageMetadata.candidatesTokenCount !== void 0) {
9726
- metrics.completion_tokens = usageMetadata.candidatesTokenCount;
9727
- }
9728
- if (usageMetadata.totalTokenCount !== void 0) {
9729
- metrics.tokens = usageMetadata.totalTokenCount;
9730
- }
9731
- if (usageMetadata.cachedContentTokenCount !== void 0) {
9732
- metrics.prompt_cached_tokens = usageMetadata.cachedContentTokenCount;
9733
- }
9734
- if (usageMetadata.thoughtsTokenCount !== void 0) {
9735
- metrics.completion_reasoning_tokens = usageMetadata.thoughtsTokenCount;
9736
- }
10225
+ populateUsageMetrics(metrics, usageMetadata);
9737
10226
  }
9738
10227
  if (text) {
9739
10228
  output.text = text;
@@ -9745,7 +10234,8 @@ function tryToDict(obj) {
9745
10234
  return null;
9746
10235
  }
9747
10236
  if (typeof obj === "object") {
9748
- if (typeof obj.toJSON === "function") {
10237
+ if ("toJSON" in obj && // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
10238
+ typeof obj.toJSON === "function") {
9749
10239
  return obj.toJSON();
9750
10240
  }
9751
10241
  return obj;