braintrust 3.28.0 → 3.30.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 (54) hide show
  1. package/README.md +1 -1
  2. package/dev/dist/index.d.mts +356 -7
  3. package/dev/dist/index.d.ts +356 -7
  4. package/dev/dist/index.js +2679 -1828
  5. package/dev/dist/index.mjs +1862 -1011
  6. package/dist/apply-auto-instrumentation.js +298 -265
  7. package/dist/apply-auto-instrumentation.mjs +85 -52
  8. package/dist/auto-instrumentations/bundler/esbuild.cjs +56 -6
  9. package/dist/auto-instrumentations/bundler/esbuild.mjs +2 -2
  10. package/dist/auto-instrumentations/bundler/next.cjs +56 -6
  11. package/dist/auto-instrumentations/bundler/next.mjs +3 -3
  12. package/dist/auto-instrumentations/bundler/rollup.cjs +56 -6
  13. package/dist/auto-instrumentations/bundler/rollup.mjs +2 -2
  14. package/dist/auto-instrumentations/bundler/vite.cjs +56 -6
  15. package/dist/auto-instrumentations/bundler/vite.mjs +2 -2
  16. package/dist/auto-instrumentations/bundler/webpack-loader.cjs +56 -6
  17. package/dist/auto-instrumentations/bundler/webpack.cjs +56 -6
  18. package/dist/auto-instrumentations/bundler/webpack.mjs +3 -3
  19. package/dist/auto-instrumentations/{chunk-26PKVUKB.mjs → chunk-LW4HDHXT.mjs} +12 -4
  20. package/dist/auto-instrumentations/{chunk-HD35AM3M.mjs → chunk-OXINGIEZ.mjs} +1 -1
  21. package/dist/auto-instrumentations/{chunk-NP7V4XB2.mjs → chunk-U64OYU4Q.mjs} +45 -3
  22. package/dist/auto-instrumentations/hook.mjs +967 -136
  23. package/dist/auto-instrumentations/index.cjs +45 -3
  24. package/dist/auto-instrumentations/index.mjs +1 -1
  25. package/dist/browser.d.mts +893 -24
  26. package/dist/browser.d.ts +893 -24
  27. package/dist/browser.js +2795 -1136
  28. package/dist/browser.mjs +2795 -1136
  29. package/dist/{chunk-BBE7SNRV.js → chunk-2XDW3UCG.js} +155 -23
  30. package/dist/{chunk-UPFNQCGB.mjs → chunk-BU6SM54N.mjs} +1830 -1045
  31. package/dist/{chunk-ZHUHZWFY.mjs → chunk-N3JKQ3D3.mjs} +152 -20
  32. package/dist/{chunk-OBBWQW6K.js → chunk-TWCDSYJN.js} +2890 -2105
  33. package/dist/cli.js +5976 -1348
  34. package/dist/edge-light.d.mts +1 -1
  35. package/dist/edge-light.d.ts +1 -1
  36. package/dist/edge-light.js +2795 -1136
  37. package/dist/edge-light.mjs +2795 -1136
  38. package/dist/index.d.mts +893 -24
  39. package/dist/index.d.ts +893 -24
  40. package/dist/index.js +1448 -699
  41. package/dist/index.mjs +848 -99
  42. package/dist/instrumentation/index.d.mts +563 -9
  43. package/dist/instrumentation/index.d.ts +563 -9
  44. package/dist/instrumentation/index.js +2794 -989
  45. package/dist/instrumentation/index.mjs +2794 -989
  46. package/dist/vitest-evals-reporter.js +16 -16
  47. package/dist/vitest-evals-reporter.mjs +2 -2
  48. package/dist/workerd.d.mts +1 -1
  49. package/dist/workerd.d.ts +1 -1
  50. package/dist/workerd.js +2795 -1136
  51. package/dist/workerd.mjs +2795 -1136
  52. package/package.json +1 -1
  53. package/util/dist/index.d.mts +42 -0
  54. package/util/dist/index.d.ts +42 -0
@@ -664,8 +664,6 @@ function newGlobalTracingChannel(nameOrChannels) {
664
664
  var DefaultAsyncLocalStorage = class {
665
665
  constructor() {
666
666
  }
667
- enterWith(_) {
668
- }
669
667
  run(_, callback) {
670
668
  return callback();
671
669
  }
@@ -2777,7 +2775,9 @@ var AclObjectType = z6.union([
2777
2775
  "project_log",
2778
2776
  "org_project",
2779
2777
  "org_audit_logs",
2780
- "project_group"
2778
+ "project_group",
2779
+ "ai_secret",
2780
+ "org_ai_secret"
2781
2781
  ]),
2782
2782
  z6.null()
2783
2783
  ]);
@@ -3610,7 +3610,7 @@ var PromptParserNullish = z6.union([
3610
3610
  }),
3611
3611
  z6.null()
3612
3612
  ]);
3613
- var PreprocessorSavedFunctionId = z6.union([
3613
+ var PreprocessorId = z6.union([
3614
3614
  z6.object({
3615
3615
  type: z6.literal("function"),
3616
3616
  id: z6.string(),
@@ -3621,6 +3621,7 @@ var PreprocessorSavedFunctionId = z6.union([
3621
3621
  name: z6.string(),
3622
3622
  function_type: z6.literal("preprocessor").optional().default("preprocessor")
3623
3623
  }),
3624
+ z6.object({ type: z6.literal("inline"), code: z6.string().min(1) }),
3624
3625
  z6.null()
3625
3626
  ]);
3626
3627
  var PromptDataNullish = z6.union([
@@ -3628,7 +3629,7 @@ var PromptDataNullish = z6.union([
3628
3629
  prompt: PromptBlockDataNullish,
3629
3630
  options: PromptOptionsNullish,
3630
3631
  parser: PromptParserNullish,
3631
- preprocessor: PreprocessorSavedFunctionId,
3632
+ preprocessor: PreprocessorId,
3632
3633
  tool_functions: z6.union([z6.array(SavedFunctionId), z6.null()]),
3633
3634
  template_format: z6.union([
3634
3635
  z6.enum(["mustache", "nunjucks", "none"]),
@@ -3830,7 +3831,7 @@ var PromptData = z6.object({
3830
3831
  prompt: PromptBlockDataNullish,
3831
3832
  options: PromptOptionsNullish,
3832
3833
  parser: PromptParserNullish,
3833
- preprocessor: PreprocessorSavedFunctionId,
3834
+ preprocessor: PreprocessorId,
3834
3835
  tool_functions: z6.union([z6.array(SavedFunctionId), z6.null()]),
3835
3836
  template_format: z6.union([
3836
3837
  z6.enum(["mustache", "nunjucks", "none"]),
@@ -4774,6 +4775,7 @@ var View = z6.object({
4774
4775
  ]),
4775
4776
  name: z6.string(),
4776
4777
  description: z6.union([z6.string(), z6.null()]).optional(),
4778
+ starred: z6.boolean().optional(),
4777
4779
  created: z6.union([z6.string(), z6.null()]).optional(),
4778
4780
  updated_at: z6.union([z6.string(), z6.null()]).optional(),
4779
4781
  view_data: ViewData.optional(),
@@ -5477,44 +5479,72 @@ function createCacheLayers({
5477
5479
  }
5478
5480
 
5479
5481
  // src/prompt-cache/prompt-cache.ts
5480
- function createCacheKey(key) {
5482
+ function createCacheKey(key, namespace) {
5483
+ let cacheKey;
5481
5484
  if (key.id) {
5482
- return `id:${key.id}`;
5483
- }
5484
- const prefix = key.projectId ?? key.projectName;
5485
- if (!prefix) {
5486
- throw new Error("Either projectId or projectName must be provided");
5487
- }
5488
- if (!key.slug) {
5489
- throw new Error("Slug must be provided when not using ID");
5485
+ cacheKey = `id:${key.id}`;
5486
+ } else {
5487
+ const prefix = key.projectId ?? key.projectName;
5488
+ if (!prefix) {
5489
+ throw new Error("Either projectId or projectName must be provided");
5490
+ }
5491
+ if (!key.slug) {
5492
+ throw new Error("Slug must be provided when not using ID");
5493
+ }
5494
+ cacheKey = `${prefix}:${key.slug}:${key.version ?? "latest"}`;
5490
5495
  }
5491
- return `${prefix}:${key.slug}:${key.version ?? "latest"}`;
5496
+ return namespace === void 0 ? cacheKey : `${namespace.length}:${namespace}:${cacheKey}`;
5492
5497
  }
5493
- var PromptCache = class {
5498
+ var PromptCache = class _PromptCache {
5494
5499
  memoryCache;
5495
5500
  diskCache;
5501
+ namespace;
5502
+ expectedResolvedOrgIdentity;
5496
5503
  constructor(options) {
5497
5504
  this.memoryCache = options.memoryCache;
5498
5505
  this.diskCache = options.diskCache;
5506
+ this.namespace = options.namespace;
5507
+ this.expectedResolvedOrgIdentity = options.expectedResolvedOrgIdentity;
5508
+ }
5509
+ /**
5510
+ * Returns a cache view that shares the same storage layers but isolates all
5511
+ * entries under the provided namespace.
5512
+ */
5513
+ withNamespace(namespace, expectedResolvedOrgIdentity) {
5514
+ return new _PromptCache({
5515
+ memoryCache: this.memoryCache,
5516
+ diskCache: this.diskCache,
5517
+ namespace,
5518
+ expectedResolvedOrgIdentity
5519
+ });
5499
5520
  }
5500
5521
  /**
5501
5522
  * Retrieves a prompt from the cache.
5502
5523
  * First checks the in-memory LRU cache, then falls back to checking the disk cache if available.
5503
5524
  */
5504
5525
  async get(key) {
5505
- const cacheKey = createCacheKey(key);
5526
+ const cacheKey = createCacheKey(key, this.namespace);
5506
5527
  if (this.memoryCache) {
5507
- const memoryPrompt = this.memoryCache.get(cacheKey);
5508
- if (memoryPrompt !== void 0) {
5509
- return memoryPrompt;
5528
+ const memoryEntry = this.memoryCache.get(cacheKey);
5529
+ if (memoryEntry !== void 0 && (this.expectedResolvedOrgIdentity === void 0 || memoryEntry.resolvedOrgIdentity === this.expectedResolvedOrgIdentity)) {
5530
+ return memoryEntry.value;
5510
5531
  }
5511
5532
  }
5512
5533
  if (this.diskCache) {
5513
- const diskPrompt = await this.diskCache.get(cacheKey);
5514
- if (!diskPrompt) {
5534
+ const diskEntry = await this.diskCache.get(cacheKey);
5535
+ if (!diskEntry || this.expectedResolvedOrgIdentity !== void 0 && diskEntry.resolvedOrgIdentity !== this.expectedResolvedOrgIdentity) {
5515
5536
  return void 0;
5516
5537
  }
5517
- this.memoryCache?.set(cacheKey, diskPrompt);
5538
+ const serializedPrompt = diskEntry.value;
5539
+ const diskPrompt = new Prompt2(
5540
+ serializedPrompt.metadata,
5541
+ serializedPrompt.defaults,
5542
+ serializedPrompt.noTrace
5543
+ );
5544
+ this.memoryCache?.set(cacheKey, {
5545
+ value: diskPrompt,
5546
+ resolvedOrgIdentity: diskEntry.resolvedOrgIdentity
5547
+ });
5518
5548
  return diskPrompt;
5519
5549
  }
5520
5550
  return void 0;
@@ -5528,58 +5558,91 @@ var PromptCache = class {
5528
5558
  * @throws If there is an error writing to the disk cache.
5529
5559
  */
5530
5560
  async set(key, value) {
5531
- const cacheKey = createCacheKey(key);
5532
- this.memoryCache?.set(cacheKey, value);
5561
+ const cacheKey = createCacheKey(key, this.namespace);
5562
+ const memoryEntry = {
5563
+ value,
5564
+ resolvedOrgIdentity: this.expectedResolvedOrgIdentity
5565
+ };
5566
+ this.memoryCache?.set(cacheKey, memoryEntry);
5533
5567
  if (this.diskCache) {
5534
- await this.diskCache.set(cacheKey, value);
5568
+ await this.diskCache.set(cacheKey, {
5569
+ value: value._internalSerializeForCache(),
5570
+ resolvedOrgIdentity: this.expectedResolvedOrgIdentity
5571
+ });
5535
5572
  }
5536
5573
  }
5537
5574
  };
5538
5575
 
5539
5576
  // src/prompt-cache/parameters-cache.ts
5540
- function createCacheKey2(key) {
5577
+ function createCacheKey2(key, namespace) {
5578
+ let cacheKey;
5541
5579
  if (key.id) {
5542
- return `parameters:id:${key.id}`;
5543
- }
5544
- const prefix = key.projectId ?? key.projectName;
5545
- if (!prefix) {
5546
- throw new Error("Either projectId or projectName must be provided");
5547
- }
5548
- if (!key.slug) {
5549
- throw new Error("Slug must be provided when not using ID");
5580
+ cacheKey = `parameters:id:${key.id}`;
5581
+ } else {
5582
+ const prefix = key.projectId ?? key.projectName;
5583
+ if (!prefix) {
5584
+ throw new Error("Either projectId or projectName must be provided");
5585
+ }
5586
+ if (!key.slug) {
5587
+ throw new Error("Slug must be provided when not using ID");
5588
+ }
5589
+ cacheKey = `parameters:${prefix}:${key.slug}:${key.version ?? "latest"}`;
5550
5590
  }
5551
- return `parameters:${prefix}:${key.slug}:${key.version ?? "latest"}`;
5591
+ return namespace === void 0 ? cacheKey : `${namespace.length}:${namespace}:${cacheKey}`;
5552
5592
  }
5553
- var ParametersCache = class {
5593
+ var ParametersCache = class _ParametersCache {
5554
5594
  memoryCache;
5555
5595
  diskCache;
5596
+ namespace;
5597
+ expectedResolvedOrgIdentity;
5556
5598
  constructor(options) {
5557
5599
  this.memoryCache = options.memoryCache;
5558
5600
  this.diskCache = options.diskCache;
5601
+ this.namespace = options.namespace;
5602
+ this.expectedResolvedOrgIdentity = options.expectedResolvedOrgIdentity;
5603
+ }
5604
+ withNamespace(namespace, expectedResolvedOrgIdentity) {
5605
+ return new _ParametersCache({
5606
+ memoryCache: this.memoryCache,
5607
+ diskCache: this.diskCache,
5608
+ namespace,
5609
+ expectedResolvedOrgIdentity
5610
+ });
5559
5611
  }
5560
5612
  async get(key) {
5561
- const cacheKey = createCacheKey2(key);
5613
+ const cacheKey = createCacheKey2(key, this.namespace);
5562
5614
  if (this.memoryCache) {
5563
- const memoryParams = this.memoryCache.get(cacheKey);
5564
- if (memoryParams !== void 0) {
5565
- return memoryParams;
5615
+ const memoryEntry = this.memoryCache.get(cacheKey);
5616
+ if (memoryEntry !== void 0 && (this.expectedResolvedOrgIdentity === void 0 || memoryEntry.resolvedOrgIdentity === this.expectedResolvedOrgIdentity)) {
5617
+ return memoryEntry.value;
5566
5618
  }
5567
5619
  }
5568
5620
  if (this.diskCache) {
5569
- const diskParams = await this.diskCache.get(cacheKey);
5570
- if (!diskParams) {
5621
+ const diskEntry = await this.diskCache.get(cacheKey);
5622
+ if (!diskEntry || this.expectedResolvedOrgIdentity !== void 0 && diskEntry.resolvedOrgIdentity !== this.expectedResolvedOrgIdentity) {
5571
5623
  return void 0;
5572
5624
  }
5573
- this.memoryCache?.set(cacheKey, diskParams);
5574
- return diskParams;
5625
+ const diskParameters = new RemoteEvalParameters(diskEntry.value.metadata);
5626
+ this.memoryCache?.set(cacheKey, {
5627
+ value: diskParameters,
5628
+ resolvedOrgIdentity: diskEntry.resolvedOrgIdentity
5629
+ });
5630
+ return diskParameters;
5575
5631
  }
5576
5632
  return void 0;
5577
5633
  }
5578
5634
  async set(key, value) {
5579
- const cacheKey = createCacheKey2(key);
5580
- this.memoryCache?.set(cacheKey, value);
5635
+ const cacheKey = createCacheKey2(key, this.namespace);
5636
+ const memoryEntry = {
5637
+ value,
5638
+ resolvedOrgIdentity: this.expectedResolvedOrgIdentity
5639
+ };
5640
+ this.memoryCache?.set(cacheKey, memoryEntry);
5581
5641
  if (this.diskCache) {
5582
- await this.diskCache.set(cacheKey, value);
5642
+ await this.diskCache.set(cacheKey, {
5643
+ value: value._internalSerializeForCache(),
5644
+ resolvedOrgIdentity: this.expectedResolvedOrgIdentity
5645
+ });
5583
5646
  }
5584
5647
  }
5585
5648
  };
@@ -5883,6 +5946,7 @@ var INSTRUMENTATION_NAMES = {
5883
5946
  CLOUDFLARE_THINK: "cloudflare-think",
5884
5947
  COHERE: "cohere",
5885
5948
  CURSOR_SDK: "cursor-sdk",
5949
+ DEEPSEEK_HARNESS: "deepseek-harness",
5886
5950
  EVE: "eve",
5887
5951
  FLUE: "flue",
5888
5952
  GENKIT: "genkit",
@@ -5908,7 +5972,7 @@ var INSTRUMENTATION_NAMES = {
5908
5972
  var INTERNAL_SPAN_INSTRUMENTATION_NAME = /* @__PURE__ */ Symbol.for(
5909
5973
  "braintrust.spanInstrumentationName"
5910
5974
  );
5911
- var SDK_VERSION = true ? "3.28.0" : "0.0.0";
5975
+ var SDK_VERSION = true ? "3.30.0" : "0.0.0";
5912
5976
  function withSpanInstrumentationName(args, instrumentationName) {
5913
5977
  return {
5914
5978
  ...args,
@@ -6027,6 +6091,16 @@ var datasetSnapshotRegisterResponseSchema = z8.object({
6027
6091
  dataset_snapshot: DatasetSnapshot,
6028
6092
  found_existing: z8.boolean().optional()
6029
6093
  });
6094
+ var datasetObjectInfoSchema = z8.object({
6095
+ object_id: z8.string(),
6096
+ object_name: z8.string(),
6097
+ parent_cols: z8.object({
6098
+ project: z8.object({
6099
+ id: z8.string(),
6100
+ name: z8.string()
6101
+ })
6102
+ })
6103
+ });
6030
6104
  var datasetRestorePreviewResultSchema = z8.object({
6031
6105
  rows_to_restore: z8.number(),
6032
6106
  rows_to_delete: z8.number()
@@ -6240,12 +6314,53 @@ var loginSchema = z8.strictObject({
6240
6314
  });
6241
6315
  var stateNonce = 0;
6242
6316
  var V1_PROXY_SUFFIX = "/v1/proxy";
6317
+ var LOADER_LOGIN_CACHE_MAX = 16;
6243
6318
  function normalizeProxyConnUrl(proxyUrl) {
6244
6319
  return proxyUrl.endsWith(V1_PROXY_SUFFIX) ? proxyUrl.slice(0, proxyUrl.length - V1_PROXY_SUFFIX.length) : proxyUrl;
6245
6320
  }
6246
6321
  var BraintrustState = class _BraintrustState {
6322
+ id;
6323
+ currentExperiment;
6324
+ // Note: the value of IsAsyncFlush doesn't really matter here, since we
6325
+ // (safely) dynamically cast it whenever retrieving the logger.
6326
+ currentLogger;
6327
+ currentParent;
6328
+ currentSpan;
6329
+ // Any time we re-log in, we directly update the apiConn inside the logger.
6330
+ // This is preferable to replacing the whole logger, which would create the
6331
+ // possibility of multiple loggers floating around, which may not log in a
6332
+ // deterministic order.
6333
+ _bgLogger;
6334
+ _overrideBgLogger = null;
6335
+ appUrl = null;
6336
+ appPublicUrl = null;
6337
+ loginToken = null;
6338
+ orgId = null;
6339
+ orgName = null;
6340
+ apiUrl = null;
6341
+ proxyUrl = null;
6342
+ loggedIn = false;
6343
+ gitMetadataSettings;
6344
+ debugLogLevel;
6345
+ debugLogLevelConfigured = false;
6346
+ fetch = globalThis.fetch;
6347
+ _appConn = null;
6348
+ _apiConn = null;
6349
+ _proxyConn = null;
6350
+ promptCache;
6351
+ parametersCache;
6352
+ spanCache;
6353
+ _idGenerator = null;
6354
+ _contextManager = null;
6355
+ _otelFlushCallback = null;
6356
+ spanOriginEnvironment;
6357
+ traceContextSigningSecret;
6358
+ loaderLoginCache = /* @__PURE__ */ new WeakMap();
6359
+ loginParams;
6360
+ activeLoginOrgNameSelector;
6247
6361
  constructor(loginParams) {
6248
- this.loginParams = loginParams;
6362
+ this.loginParams = { ...loginParams };
6363
+ this.activeLoginOrgNameSelector = loginParams.orgName ?? isomorph_default.getEnv("BRAINTRUST_ORG_NAME");
6249
6364
  this.id = `${(/* @__PURE__ */ new Date()).toLocaleString()}-${stateNonce++}`;
6250
6365
  this.currentExperiment = void 0;
6251
6366
  this.currentLogger = void 0;
@@ -6282,12 +6397,14 @@ var BraintrustState = class _BraintrustState {
6282
6397
  const {
6283
6398
  memoryCache: parametersMemoryCache,
6284
6399
  diskCache: parametersDiskCache
6285
- } = createCacheLayers({
6286
- memoryMaxEnvVar: "BRAINTRUST_PARAMETERS_CACHE_MEMORY_MAX",
6287
- diskCacheDirEnvVar: "BRAINTRUST_PARAMETERS_CACHE_DIR",
6288
- diskMaxEnvVar: "BRAINTRUST_PARAMETERS_CACHE_DISK_MAX",
6289
- getDefaultDiskCacheDir: () => `${isomorph_default.getEnv("HOME") ?? isomorph_default.homedir()}/.braintrust/parameters_cache`
6290
- });
6400
+ } = createCacheLayers(
6401
+ {
6402
+ memoryMaxEnvVar: "BRAINTRUST_PARAMETERS_CACHE_MEMORY_MAX",
6403
+ diskCacheDirEnvVar: "BRAINTRUST_PARAMETERS_CACHE_DIR",
6404
+ diskMaxEnvVar: "BRAINTRUST_PARAMETERS_CACHE_DISK_MAX",
6405
+ getDefaultDiskCacheDir: () => `${isomorph_default.getEnv("HOME") ?? isomorph_default.homedir()}/.braintrust/parameters_cache`
6406
+ }
6407
+ );
6291
6408
  this.parametersCache = new ParametersCache({
6292
6409
  memoryCache: parametersMemoryCache,
6293
6410
  diskCache: parametersDiskCache
@@ -6296,43 +6413,6 @@ var BraintrustState = class _BraintrustState {
6296
6413
  this.spanOriginEnvironment = detectSpanOriginEnvironment();
6297
6414
  this._internalSetTraceContextSigningSecret(loginParams.apiKey);
6298
6415
  }
6299
- loginParams;
6300
- id;
6301
- currentExperiment;
6302
- // Note: the value of IsAsyncFlush doesn't really matter here, since we
6303
- // (safely) dynamically cast it whenever retrieving the logger.
6304
- currentLogger;
6305
- currentParent;
6306
- currentSpan;
6307
- // Any time we re-log in, we directly update the apiConn inside the logger.
6308
- // This is preferable to replacing the whole logger, which would create the
6309
- // possibility of multiple loggers floating around, which may not log in a
6310
- // deterministic order.
6311
- _bgLogger;
6312
- _overrideBgLogger = null;
6313
- appUrl = null;
6314
- appPublicUrl = null;
6315
- loginToken = null;
6316
- orgId = null;
6317
- orgName = null;
6318
- apiUrl = null;
6319
- proxyUrl = null;
6320
- loggedIn = false;
6321
- gitMetadataSettings;
6322
- debugLogLevel;
6323
- debugLogLevelConfigured = false;
6324
- fetch = globalThis.fetch;
6325
- _appConn = null;
6326
- _apiConn = null;
6327
- _proxyConn = null;
6328
- promptCache;
6329
- parametersCache;
6330
- spanCache;
6331
- _idGenerator = null;
6332
- _contextManager = null;
6333
- _otelFlushCallback = null;
6334
- spanOriginEnvironment;
6335
- traceContextSigningSecret;
6336
6416
  /** @internal */
6337
6417
  _internalSetTraceContextSigningSecret(secret) {
6338
6418
  const normalizedSecret = secret?.trim();
@@ -6357,6 +6437,101 @@ var BraintrustState = class _BraintrustState {
6357
6437
  this._appConn = null;
6358
6438
  this._apiConn = null;
6359
6439
  this._proxyConn = null;
6440
+ this.loaderLoginCache = /* @__PURE__ */ new WeakMap();
6441
+ }
6442
+ /** @internal */
6443
+ async _internalResolveLoaderLoginOptions({
6444
+ apiKey,
6445
+ appUrl,
6446
+ orgName,
6447
+ fetch: fetch2,
6448
+ forceLogin
6449
+ }) {
6450
+ const resolvedAppUrl = appUrl ?? (this.loggedIn ? this.appUrl ?? void 0 : void 0) ?? this.loginParams.appUrl ?? isomorph_default.getEnv("BRAINTRUST_APP_URL") ?? "https://www.braintrust.dev";
6451
+ const resolvedApiKey = apiKey ?? (this.loggedIn ? this.loginToken ?? void 0 : void 0) ?? this.loginParams.apiKey ?? await isomorph_default.getBraintrustApiKey();
6452
+ if (!resolvedApiKey) {
6453
+ throw new Error(
6454
+ "Please specify an api key (e.g. by setting BRAINTRUST_API_KEY)."
6455
+ );
6456
+ }
6457
+ const normalizedApiKey = HTTPConnection.sanitize_token(resolvedApiKey);
6458
+ const usesActiveCredential = this.loggedIn && normalizedApiKey === this.loginToken;
6459
+ const requestedOrgName = orgName ?? (usesActiveCredential ? this.activeLoginOrgNameSelector : void 0) ?? this.loginParams.orgName ?? isomorph_default.getEnv("BRAINTRUST_ORG_NAME");
6460
+ const resolvedOrgName = orgName ?? (usesActiveCredential ? this.orgName ?? void 0 : void 0) ?? requestedOrgName;
6461
+ const resolvedFetch = fetch2 ?? (this.loggedIn ? this.fetch : void 0) ?? this.loginParams.fetch ?? globalThis.fetch;
6462
+ const credentialCacheNamespace = JSON.stringify([
6463
+ "loader-credential",
6464
+ resolvedAppUrl,
6465
+ requestedOrgName,
6466
+ normalizedApiKey
6467
+ ]);
6468
+ return {
6469
+ apiKey: normalizedApiKey,
6470
+ appUrl: resolvedAppUrl,
6471
+ orgName: resolvedOrgName,
6472
+ fetch: resolvedFetch,
6473
+ forceLogin,
6474
+ credentialCacheNamespace,
6475
+ existingState: !forceLogin && usesActiveCredential && resolvedAppUrl === this.appUrl && resolvedOrgName === this.orgName && resolvedFetch === this.fetch ? this : void 0
6476
+ };
6477
+ }
6478
+ /** @internal */
6479
+ _internalGetLoaderCacheViews(loginOptions, requestState) {
6480
+ const expectedResolvedOrgIdentity = requestState?.orgId && requestState.appUrl ? JSON.stringify([
6481
+ "loader-org",
6482
+ requestState.appUrl,
6483
+ requestState.orgId
6484
+ ]) : void 0;
6485
+ return {
6486
+ promptCache: this.promptCache.withNamespace(
6487
+ loginOptions.credentialCacheNamespace,
6488
+ expectedResolvedOrgIdentity
6489
+ ),
6490
+ parametersCache: this.parametersCache.withNamespace(
6491
+ loginOptions.credentialCacheNamespace,
6492
+ expectedResolvedOrgIdentity
6493
+ )
6494
+ };
6495
+ }
6496
+ /** @internal */
6497
+ async _internalGetLoaderState({
6498
+ apiKey,
6499
+ appUrl,
6500
+ orgName,
6501
+ fetch: fetch2,
6502
+ forceLogin,
6503
+ existingState
6504
+ }) {
6505
+ if (existingState) {
6506
+ return existingState;
6507
+ }
6508
+ let cache = this.loaderLoginCache.get(fetch2);
6509
+ if (!cache) {
6510
+ cache = new LRUCache({ max: LOADER_LOGIN_CACHE_MAX });
6511
+ this.loaderLoginCache.set(fetch2, cache);
6512
+ }
6513
+ const cacheKey = JSON.stringify([appUrl, orgName, apiKey]);
6514
+ if (!forceLogin) {
6515
+ const cachedState = cache.get(cacheKey);
6516
+ if (cachedState) {
6517
+ return cachedState;
6518
+ }
6519
+ }
6520
+ const statePromise = loginToLoaderRequestState({
6521
+ orgName,
6522
+ apiKey,
6523
+ appUrl,
6524
+ fetch: fetch2
6525
+ });
6526
+ cache.set(cacheKey, statePromise);
6527
+ try {
6528
+ return await statePromise;
6529
+ } catch (error) {
6530
+ if (cache.get(cacheKey) === statePromise) {
6531
+ cache.delete(cacheKey);
6532
+ }
6533
+ throw error;
6534
+ }
6360
6535
  }
6361
6536
  resetIdGenState() {
6362
6537
  this._idGenerator = null;
@@ -6405,6 +6580,8 @@ var BraintrustState = class _BraintrustState {
6405
6580
  this.debugLogLevel = other.debugLogLevel;
6406
6581
  this.debugLogLevelConfigured = other.debugLogLevelConfigured;
6407
6582
  this.traceContextSigningSecret = other.traceContextSigningSecret;
6583
+ this.fetch = other.fetch;
6584
+ this.activeLoginOrgNameSelector = other.activeLoginOrgNameSelector;
6408
6585
  setGlobalDebugLogLevel(
6409
6586
  this.debugLogLevelConfigured ? this.debugLogLevel ?? false : void 0
6410
6587
  );
@@ -6612,36 +6789,76 @@ var FailedHTTPResponse = class extends Error {
6612
6789
  status;
6613
6790
  text;
6614
6791
  data;
6615
- constructor(status, text, data) {
6792
+ cause;
6793
+ constructor(status, text, data, cause) {
6616
6794
  super(`${status}: ${text} (${data})`);
6617
6795
  this.status = status;
6618
6796
  this.text = text;
6619
6797
  this.data = data;
6798
+ this.cause = cause;
6799
+ }
6800
+ };
6801
+ var HTTPTransportError = class extends Error {
6802
+ cause;
6803
+ constructor(cause) {
6804
+ super(cause instanceof Error ? cause.message : String(cause));
6805
+ this.name = "HTTPTransportError";
6806
+ this.cause = cause;
6620
6807
  }
6621
6808
  };
6809
+ var httpTransportErrorCauses = /* @__PURE__ */ new WeakSet();
6810
+ function recordHTTPTransportError(error) {
6811
+ if (typeof error === "object" && error !== null || typeof error === "function") {
6812
+ httpTransportErrorCauses.add(error);
6813
+ }
6814
+ }
6815
+ function rethrowHTTPTransportError(error, classifyTransportErrors) {
6816
+ if (classifyTransportErrors) {
6817
+ throw new HTTPTransportError(error);
6818
+ }
6819
+ recordHTTPTransportError(error);
6820
+ throw error;
6821
+ }
6822
+ async function readJSONResponse(response, classifyTransportErrors = false) {
6823
+ let data;
6824
+ try {
6825
+ data = await response.text();
6826
+ } catch (error) {
6827
+ rethrowHTTPTransportError(error, classifyTransportErrors);
6828
+ }
6829
+ return JSON.parse(data);
6830
+ }
6622
6831
  async function checkResponse(resp) {
6623
6832
  if (resp.ok) {
6624
6833
  return resp;
6625
- } else {
6834
+ }
6835
+ let data;
6836
+ try {
6837
+ data = await resp.text();
6838
+ } catch (error) {
6626
6839
  throw new FailedHTTPResponse(
6627
6840
  resp.status,
6628
6841
  resp.statusText,
6629
- await resp.text()
6842
+ "Unable to read response body",
6843
+ error
6630
6844
  );
6631
6845
  }
6846
+ throw new FailedHTTPResponse(resp.status, resp.statusText, data);
6632
6847
  }
6633
6848
  var HTTPConnection = class _HTTPConnection {
6634
- base_url;
6635
- token;
6636
- headers;
6637
- fetch;
6638
- constructor(base_url, fetch2) {
6849
+ constructor(base_url, fetch2, classifyTransportErrors = false) {
6850
+ this.classifyTransportErrors = classifyTransportErrors;
6639
6851
  this.base_url = base_url;
6640
6852
  this.token = null;
6641
6853
  this.headers = {};
6642
6854
  this._reset();
6643
6855
  this.fetch = fetch2;
6644
6856
  }
6857
+ classifyTransportErrors;
6858
+ base_url;
6859
+ token;
6860
+ headers;
6861
+ fetch;
6645
6862
  setFetch(fetch2) {
6646
6863
  this.fetch = fetch2;
6647
6864
  }
@@ -6681,9 +6898,9 @@ var HTTPConnection = class _HTTPConnection {
6681
6898
  ).toString();
6682
6899
  const this_fetch = this.fetch;
6683
6900
  const this_headers = this.headers;
6684
- return await checkResponse(
6685
- // Using toString() here makes it work with isomorphic fetch
6686
- await this_fetch(url.toString(), {
6901
+ let response;
6902
+ try {
6903
+ response = await this_fetch(url.toString(), {
6687
6904
  headers: {
6688
6905
  Accept: "application/json",
6689
6906
  ...this_headers,
@@ -6691,8 +6908,14 @@ var HTTPConnection = class _HTTPConnection {
6691
6908
  },
6692
6909
  keepalive: true,
6693
6910
  ...rest
6694
- })
6695
- );
6911
+ });
6912
+ } catch (error) {
6913
+ if (config?.signal?.aborted) {
6914
+ throw getAbortReason(config.signal);
6915
+ }
6916
+ rethrowHTTPTransportError(error, this.classifyTransportErrors);
6917
+ }
6918
+ return await checkResponse(response);
6696
6919
  }
6697
6920
  async post(path3, params, config, retries = 0) {
6698
6921
  const { headers, ...rest } = config || {};
@@ -6702,8 +6925,9 @@ var HTTPConnection = class _HTTPConnection {
6702
6925
  const tries = retries + 1;
6703
6926
  for (let i = 0; i < tries; i++) {
6704
6927
  try {
6705
- return await checkResponse(
6706
- await this_fetch(_urljoin(this_base_url, path3), {
6928
+ let response;
6929
+ try {
6930
+ response = await this_fetch(_urljoin(this_base_url, path3), {
6707
6931
  method: "POST",
6708
6932
  headers: {
6709
6933
  Accept: "application/json",
@@ -6714,8 +6938,14 @@ var HTTPConnection = class _HTTPConnection {
6714
6938
  body: typeof params === "string" ? params : params ? JSON.stringify(params) : void 0,
6715
6939
  keepalive: true,
6716
6940
  ...rest
6717
- })
6718
- );
6941
+ });
6942
+ } catch (error) {
6943
+ if (config?.signal?.aborted) {
6944
+ throw getAbortReason(config.signal);
6945
+ }
6946
+ rethrowHTTPTransportError(error, this.classifyTransportErrors);
6947
+ }
6948
+ return await checkResponse(response);
6719
6949
  } catch (error) {
6720
6950
  if (config?.signal?.aborted) {
6721
6951
  throw getAbortReason(config.signal);
@@ -6740,7 +6970,7 @@ var HTTPConnection = class _HTTPConnection {
6740
6970
  for (let i = 0; i < tries; i++) {
6741
6971
  try {
6742
6972
  const resp = await this.get(`${object_type}`, args);
6743
- return await resp.json();
6973
+ return await readJSONResponse(resp, this.classifyTransportErrors);
6744
6974
  } catch (e) {
6745
6975
  if (i < tries - 1) {
6746
6976
  debugLogger.debug(
@@ -6763,7 +6993,7 @@ var HTTPConnection = class _HTTPConnection {
6763
6993
  const resp = await this.post(`${object_type}`, args, {
6764
6994
  headers: { "Content-Type": "application/json" }
6765
6995
  });
6766
- return await resp.json();
6996
+ return await readJSONResponse(resp, this.classifyTransportErrors);
6767
6997
  }
6768
6998
  // Custom inspect for Node.js console.log
6769
6999
  [/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() {
@@ -8583,6 +8813,7 @@ function initDataset(projectOrOptions, optionalOptions) {
8583
8813
  const {
8584
8814
  project,
8585
8815
  dataset,
8816
+ datasetId,
8586
8817
  description,
8587
8818
  version,
8588
8819
  snapshotName,
@@ -8598,6 +8829,11 @@ function initDataset(projectOrOptions, optionalOptions) {
8598
8829
  state: stateArg,
8599
8830
  _internal_btql
8600
8831
  } = options;
8832
+ if (datasetId !== void 0 && (description !== void 0 || metadata !== void 0)) {
8833
+ throw new Error(
8834
+ "Cannot specify description or metadata when datasetId is provided"
8835
+ );
8836
+ }
8601
8837
  const selection = normalizeDatasetSelection({
8602
8838
  version,
8603
8839
  environment,
@@ -8618,6 +8854,35 @@ function initDataset(projectOrOptions, optionalOptions) {
8618
8854
  fetch: fetch2,
8619
8855
  forceLogin
8620
8856
  });
8857
+ if (datasetId !== void 0) {
8858
+ const objectInfo = datasetObjectInfoSchema.array().parse(
8859
+ await state.appConn().post_json("api/self/get_object_info", {
8860
+ object_type: "dataset",
8861
+ object_ids: [datasetId]
8862
+ })
8863
+ );
8864
+ if (objectInfo.length === 0) {
8865
+ throw new Error(`Dataset with ID ${datasetId} not found`);
8866
+ }
8867
+ if (objectInfo.length !== 1) {
8868
+ throw new Error(
8869
+ `Expected exactly one dataset with ID ${datasetId}, but found ${objectInfo.length}`
8870
+ );
8871
+ }
8872
+ const datasetInfo = objectInfo[0];
8873
+ return {
8874
+ project: {
8875
+ id: datasetInfo.parent_cols.project.id,
8876
+ name: datasetInfo.parent_cols.project.name,
8877
+ fullInfo: datasetInfo.parent_cols.project
8878
+ },
8879
+ dataset: {
8880
+ id: datasetInfo.object_id,
8881
+ name: datasetInfo.object_name,
8882
+ fullInfo: datasetInfo
8883
+ }
8884
+ };
8885
+ }
8621
8886
  const args = {
8622
8887
  org_id: state.orgId,
8623
8888
  project_name: project,
@@ -8792,6 +9057,52 @@ async function login(options = {}) {
8792
9057
  await state.login(options);
8793
9058
  return state;
8794
9059
  }
9060
+ async function loginToLoaderRequestState({
9061
+ appUrl,
9062
+ apiKey,
9063
+ orgName,
9064
+ fetch: fetch2
9065
+ }) {
9066
+ let orgId;
9067
+ let apiUrl;
9068
+ if (apiKey === TEST_API_KEY) {
9069
+ orgId = "test-org-id";
9070
+ apiUrl = "https://braintrust.dev/fake-api-url";
9071
+ } else {
9072
+ let loginResponse;
9073
+ try {
9074
+ loginResponse = await fetch2(_urljoin(appUrl, `/api/apikey/login`), {
9075
+ method: "POST",
9076
+ headers: {
9077
+ "Content-Type": "application/json",
9078
+ Authorization: `Bearer ${apiKey}`
9079
+ }
9080
+ });
9081
+ } catch (error) {
9082
+ throw new HTTPTransportError(error);
9083
+ }
9084
+ const info = await readJSONResponse(
9085
+ await checkResponse(loginResponse),
9086
+ true
9087
+ );
9088
+ const org = selectLoginOrg(info.org_info, orgName);
9089
+ orgId = org.id;
9090
+ apiUrl = isomorph_default.getEnv("BRAINTRUST_API_URL") ?? org.api_url;
9091
+ if (!apiUrl) {
9092
+ throw new Error(
9093
+ orgName ? `Unable to log into organization '${orgName}'. Are you sure this credential is scoped to the organization?` : "Unable to log into any organization with the provided credential."
9094
+ );
9095
+ }
9096
+ }
9097
+ const apiConnection = new HTTPConnection(apiUrl, fetch2, true);
9098
+ apiConnection.set_token(apiKey);
9099
+ apiConnection.make_long_lived();
9100
+ return {
9101
+ appUrl,
9102
+ orgId,
9103
+ apiConn: () => apiConnection
9104
+ };
9105
+ }
8795
9106
  async function loginToState(options = {}) {
8796
9107
  const {
8797
9108
  appUrl = isomorph_default.getEnv("BRAINTRUST_APP_URL") || "https://www.braintrust.dev",
@@ -8823,16 +9134,18 @@ async function loginToState(options = {}) {
8823
9134
  _saveOrgInfo(state, testOrgInfo, testOrgInfo[0].name);
8824
9135
  return state;
8825
9136
  } else {
8826
- const resp = await checkResponse(
8827
- await fetch2(_urljoin(state.appUrl, `/api/apikey/login`), {
9137
+ const loginResponse = await fetch2(
9138
+ _urljoin(state.appUrl, `/api/apikey/login`),
9139
+ {
8828
9140
  method: "POST",
8829
9141
  headers: {
8830
9142
  "Content-Type": "application/json",
8831
9143
  Authorization: `Bearer ${apiKey}`
8832
9144
  }
8833
- })
9145
+ }
8834
9146
  );
8835
- const info = await resp.json();
9147
+ const resp = await checkResponse(loginResponse);
9148
+ const info = await readJSONResponse(resp);
8836
9149
  _saveOrgInfo(state, info.org_info, orgName);
8837
9150
  if (!state.apiUrl) {
8838
9151
  if (orgName) {
@@ -9174,6 +9487,15 @@ function _internalStartSpanWithInitialMerge(args) {
9174
9487
  [INITIAL_SPAN_WRITE_AS_MERGE]: true
9175
9488
  }).span;
9176
9489
  }
9490
+ function _internalStartSpanWithInitialMergeAndParentSpanIds(args) {
9491
+ return startSpanAndIsLogger(
9492
+ {
9493
+ ...args,
9494
+ [INITIAL_SPAN_WRITE_AS_MERGE]: true
9495
+ },
9496
+ { useParentSpanIdsForObjectParent: true }
9497
+ ).span;
9498
+ }
9177
9499
  function _internalStartSpanWithContext(args, context) {
9178
9500
  return startSpanAndIsLogger({
9179
9501
  ...args,
@@ -9184,7 +9506,7 @@ async function flush(options) {
9184
9506
  const state = options?.state ?? _globalState;
9185
9507
  return await state.bgLogger().flush();
9186
9508
  }
9187
- function startSpanAndIsLogger(args) {
9509
+ function startSpanAndIsLogger(args, internalOptions) {
9188
9510
  const state = args?.state ?? _globalState;
9189
9511
  const { parentObject, propagatedState } = getSpanParentObjectAndPropagatedState({
9190
9512
  asyncFlush: args?.asyncFlush,
@@ -9198,7 +9520,7 @@ function startSpanAndIsLogger(args) {
9198
9520
  ) ? {
9199
9521
  spanId: parentObject.data.span_id,
9200
9522
  rootSpanId: parentObject.data.root_span_id
9201
- } : void 0;
9523
+ } : internalOptions?.useParentSpanIdsForObjectParent ? args?.parentSpanIds : void 0;
9202
9524
  const { parent: _ignoredParent, ...spanArgs } = args ?? {};
9203
9525
  const span = new SpanImpl({
9204
9526
  state,
@@ -9234,27 +9556,28 @@ function withCurrent(span, callback, state = void 0) {
9234
9556
  function withParent(parent, callback, state = void 0) {
9235
9557
  return (state ?? _globalState).currentParent.run(parent, () => callback());
9236
9558
  }
9237
- function _saveOrgInfo(state, org_info, org_name) {
9238
- if (org_info.length === 0) {
9559
+ function _saveOrgInfo(state, orgInfo, orgName) {
9560
+ const org = selectLoginOrg(orgInfo, orgName);
9561
+ state.orgId = org.id;
9562
+ state.orgName = org.name;
9563
+ state.apiUrl = isomorph_default.getEnv("BRAINTRUST_API_URL") ?? org.api_url;
9564
+ state.proxyUrl = isomorph_default.getEnv("BRAINTRUST_PROXY_URL") ?? org.proxy_url;
9565
+ state.gitMetadataSettings = org.git_metadata || void 0;
9566
+ }
9567
+ function selectLoginOrg(orgInfo, orgName) {
9568
+ if (orgInfo.length === 0) {
9239
9569
  throw new LoginInvalidOrgError(
9240
9570
  "This user is not part of any organizations."
9241
9571
  );
9242
9572
  }
9243
- for (const org of org_info) {
9244
- if (org_name === void 0 || org.name === org_name) {
9245
- state.orgId = org.id;
9246
- state.orgName = org.name;
9247
- state.apiUrl = isomorph_default.getEnv("BRAINTRUST_API_URL") ?? org.api_url;
9248
- state.proxyUrl = isomorph_default.getEnv("BRAINTRUST_PROXY_URL") ?? org.proxy_url;
9249
- state.gitMetadataSettings = org.git_metadata || void 0;
9250
- break;
9573
+ for (const org of orgInfo) {
9574
+ if (orgName === void 0 || org.name === orgName) {
9575
+ return org;
9251
9576
  }
9252
9577
  }
9253
- if (state.orgId === void 0) {
9254
- throw new LoginInvalidOrgError(
9255
- `Organization ${org_name} not found. Must be one of ${org_info.map((x) => x.name).join(", ")}`
9256
- );
9257
- }
9578
+ throw new LoginInvalidOrgError(
9579
+ `Organization ${orgName} not found. Must be one of ${orgInfo.map((org) => org.name).join(", ")}`
9580
+ );
9258
9581
  }
9259
9582
  function validateTags(tags) {
9260
9583
  const seen = /* @__PURE__ */ new Set();
@@ -11157,6 +11480,14 @@ var Prompt2 = class _Prompt {
11157
11480
  static isPrompt(data) {
11158
11481
  return typeof data === "object" && data !== null && "__braintrust_prompt_marker" in data;
11159
11482
  }
11483
+ /** @internal */
11484
+ _internalSerializeForCache() {
11485
+ return {
11486
+ metadata: this.metadata,
11487
+ defaults: this.defaults,
11488
+ noTrace: this.noTrace
11489
+ };
11490
+ }
11160
11491
  static fromPromptData(name, promptData) {
11161
11492
  return new _Prompt(
11162
11493
  {
@@ -11197,6 +11528,10 @@ var RemoteEvalParameters = class {
11197
11528
  get data() {
11198
11529
  return this.metadata.function_data.data ?? {};
11199
11530
  }
11531
+ /** @internal */
11532
+ _internalSerializeForCache() {
11533
+ return { metadata: this.metadata };
11534
+ }
11200
11535
  validate(data) {
11201
11536
  if (typeof data !== "object" || data === null) {
11202
11537
  return false;
@@ -11888,56 +12223,14 @@ function suppressionStore() {
11888
12223
  autoInstrumentationSuppressionStore ??= isomorph_default.newAsyncLocalStorage();
11889
12224
  return autoInstrumentationSuppressionStore;
11890
12225
  }
11891
- function currentFrames() {
11892
- return suppressionStore().getStore()?.frames ?? [];
11893
- }
11894
12226
  function isAutoInstrumentationSuppressed() {
11895
- const frames = currentFrames();
11896
- return frames[frames.length - 1]?.mode === "suppress";
12227
+ return suppressionStore().getStore() === true;
11897
12228
  }
11898
12229
  function runWithAutoInstrumentationSuppressed(callback) {
11899
- const frame = {
11900
- id: /* @__PURE__ */ Symbol("braintrust.auto-instrumentation-suppress"),
11901
- mode: "suppress"
11902
- };
11903
- return suppressionStore().run(
11904
- { frames: [...currentFrames(), frame] },
11905
- callback
11906
- );
12230
+ return suppressionStore().run(true, callback);
11907
12231
  }
11908
- function bindAutoInstrumentationSuppressionToStart(tracingChannel) {
11909
- const startChannel = tracingChannel.start;
11910
- if (!startChannel) {
11911
- return void 0;
11912
- }
11913
- const store = suppressionStore();
11914
- startChannel.bindStore(store, () => ({
11915
- frames: [
11916
- ...currentFrames(),
11917
- {
11918
- id: /* @__PURE__ */ Symbol("braintrust.auto-instrumentation-suppress"),
11919
- mode: "suppress"
11920
- }
11921
- ]
11922
- }));
11923
- return () => {
11924
- startChannel.unbindStore(store);
11925
- };
11926
- }
11927
- function enterAutoInstrumentationAllowed() {
11928
- const frame = {
11929
- id: /* @__PURE__ */ Symbol("braintrust.auto-instrumentation-allow"),
11930
- mode: "allow"
11931
- };
11932
- suppressionStore().enterWith({
11933
- frames: [...currentFrames(), frame]
11934
- });
11935
- return () => {
11936
- const frames = currentFrames().filter(
11937
- (candidate) => candidate.id !== frame.id
11938
- );
11939
- suppressionStore().enterWith(frames.length > 0 ? { frames } : void 0);
11940
- };
12232
+ function runWithAutoInstrumentationAllowed(callback) {
12233
+ return suppressionStore().run(void 0, callback);
11941
12234
  }
11942
12235
 
11943
12236
  // src/instrumentation/core/channel-tracing.ts
@@ -12527,6 +12820,131 @@ function unsubscribeAll(unsubscribers) {
12527
12820
  return [];
12528
12821
  }
12529
12822
 
12823
+ // src/instrumentation/core/channel-definitions.ts
12824
+ function channel(spec) {
12825
+ return spec;
12826
+ }
12827
+ function defineChannels(pkg, channels, options) {
12828
+ const { instrumentationName } = options;
12829
+ return Object.fromEntries(
12830
+ Object.entries(channels).map(([key, spec]) => {
12831
+ const fullChannelName = `orchestrion:${pkg}:${spec.channelName}`;
12832
+ if (spec.kind === "async") {
12833
+ const asyncSpec = spec;
12834
+ const tracingChannel2 = () => isomorph_default.newTracingChannel(
12835
+ fullChannelName
12836
+ );
12837
+ const intercept2 = (interceptor) => {
12838
+ const hook = tracingChannel2();
12839
+ return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
12840
+ };
12841
+ };
12842
+ return [
12843
+ key,
12844
+ {
12845
+ ...asyncSpec,
12846
+ instrumentationName,
12847
+ intercept: intercept2,
12848
+ invoke: (target, thisArg, args, additional) => {
12849
+ const hook = tracingChannel2();
12850
+ return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
12851
+ },
12852
+ tracingChannel: tracingChannel2,
12853
+ tracePromise: (fn, context) => tracingChannel2().tracePromise(
12854
+ fn,
12855
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
12856
+ context
12857
+ )
12858
+ }
12859
+ ];
12860
+ }
12861
+ const syncSpec = spec;
12862
+ const tracingChannel = () => isomorph_default.newTracingChannel(
12863
+ fullChannelName
12864
+ );
12865
+ const intercept = (interceptor) => {
12866
+ const hook = tracingChannel();
12867
+ return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
12868
+ };
12869
+ };
12870
+ return [
12871
+ key,
12872
+ {
12873
+ ...syncSpec,
12874
+ instrumentationName,
12875
+ intercept,
12876
+ invoke: (target, thisArg, args, additional) => {
12877
+ const hook = tracingChannel();
12878
+ return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
12879
+ },
12880
+ tracingChannel,
12881
+ traceSync: (fn, context) => tracingChannel().traceSync(
12882
+ fn,
12883
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
12884
+ context
12885
+ )
12886
+ }
12887
+ ];
12888
+ })
12889
+ );
12890
+ }
12891
+
12892
+ // src/instrumentation/plugins/openai-channels.ts
12893
+ var openAIChannels = defineChannels(
12894
+ "openai",
12895
+ {
12896
+ filesCreateTraced: channel({
12897
+ channelName: "files.create-traced",
12898
+ kind: "async"
12899
+ }),
12900
+ batchesRetrieveTraced: channel({
12901
+ channelName: "batches.retrieve-traced",
12902
+ kind: "async"
12903
+ }),
12904
+ batchesCompleteTrace: channel({
12905
+ channelName: "batches.complete-trace",
12906
+ kind: "async"
12907
+ }),
12908
+ chatCompletionsCreate: channel({
12909
+ channelName: "chat.completions.create",
12910
+ kind: "async"
12911
+ }),
12912
+ embeddingsCreate: channel({
12913
+ channelName: "embeddings.create",
12914
+ kind: "async"
12915
+ }),
12916
+ betaChatCompletionsParse: channel({
12917
+ channelName: "beta.chat.completions.parse",
12918
+ kind: "async"
12919
+ }),
12920
+ betaChatCompletionsStream: channel({
12921
+ channelName: "beta.chat.completions.stream",
12922
+ kind: "sync-stream"
12923
+ }),
12924
+ moderationsCreate: channel({
12925
+ channelName: "moderations.create",
12926
+ kind: "async"
12927
+ }),
12928
+ responsesCreate: channel({
12929
+ channelName: "responses.create",
12930
+ kind: "async"
12931
+ }),
12932
+ responsesStream: channel({
12933
+ channelName: "responses.stream",
12934
+ kind: "sync-stream"
12935
+ }),
12936
+ responsesParse: channel({
12937
+ channelName: "responses.parse",
12938
+ kind: "async"
12939
+ }),
12940
+ responsesCompact: channel({
12941
+ channelName: "responses.compact",
12942
+ kind: "async"
12943
+ })
12944
+ },
12945
+ { instrumentationName: INSTRUMENTATION_NAMES.OPENAI }
12946
+ );
12947
+
12530
12948
  // src/wrappers/attachment-utils.ts
12531
12949
  function getExtensionFromMediaType(mediaType) {
12532
12950
  const extensionMap = {
@@ -12702,118 +13120,91 @@ function processInputAttachments(input) {
12702
13120
  return processNode(input);
12703
13121
  }
12704
13122
 
12705
- // src/instrumentation/core/channel-definitions.ts
12706
- function channel(spec) {
12707
- return spec;
12708
- }
12709
- function defineChannels(pkg, channels, options) {
12710
- const { instrumentationName } = options;
12711
- return Object.fromEntries(
12712
- Object.entries(channels).map(([key, spec]) => {
12713
- const fullChannelName = `orchestrion:${pkg}:${spec.channelName}`;
12714
- if (spec.kind === "async") {
12715
- const asyncSpec = spec;
12716
- const tracingChannel2 = () => isomorph_default.newTracingChannel(
12717
- fullChannelName
12718
- );
12719
- const intercept2 = (interceptor) => {
12720
- const hook = tracingChannel2();
12721
- return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
12722
- };
12723
- };
12724
- return [
12725
- key,
12726
- {
12727
- ...asyncSpec,
12728
- instrumentationName,
12729
- intercept: intercept2,
12730
- invoke: (target, thisArg, args, additional) => {
12731
- const hook = tracingChannel2();
12732
- return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
12733
- },
12734
- tracingChannel: tracingChannel2,
12735
- tracePromise: (fn, context) => tracingChannel2().tracePromise(
12736
- fn,
12737
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
12738
- context
12739
- )
12740
- }
12741
- ];
13123
+ // src/instrumentation/plugins/openai-span-data.ts
13124
+ var OPENAI_METADATA_KEYS = [
13125
+ "model",
13126
+ "temperature",
13127
+ "top_p",
13128
+ "max_tokens",
13129
+ "frequency_penalty",
13130
+ "presence_penalty",
13131
+ "stop",
13132
+ "response_format",
13133
+ "tools",
13134
+ "tool_choice",
13135
+ "parallel_tool_calls",
13136
+ "max_tool_calls"
13137
+ ];
13138
+ function batchMetadata(params) {
13139
+ const metadata = { provider: "openai" };
13140
+ for (const key of OPENAI_METADATA_KEYS) {
13141
+ try {
13142
+ const value = Reflect.get(params, key);
13143
+ if (value !== void 0) {
13144
+ metadata[key] = value;
12742
13145
  }
12743
- const syncSpec = spec;
12744
- const tracingChannel = () => isomorph_default.newTracingChannel(
12745
- fullChannelName
12746
- );
12747
- const intercept = (interceptor) => {
12748
- const hook = tracingChannel();
12749
- return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
12750
- };
12751
- };
12752
- return [
12753
- key,
12754
- {
12755
- ...syncSpec,
12756
- instrumentationName,
12757
- intercept,
12758
- invoke: (target, thisArg, args, additional) => {
12759
- const hook = tracingChannel();
12760
- return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
12761
- },
12762
- tracingChannel,
12763
- traceSync: (fn, context) => tracingChannel().traceSync(
12764
- fn,
12765
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
12766
- context
12767
- )
12768
- }
12769
- ];
12770
- })
12771
- );
13146
+ } catch {
13147
+ }
13148
+ }
13149
+ return metadata;
13150
+ }
13151
+ function extractOpenAIBatchInput(endpoint, params) {
13152
+ const input = endpoint === "/v1/chat/completions" ? params.messages : params.input;
13153
+ return {
13154
+ input: processInputAttachments(input),
13155
+ metadata: batchMetadata(params)
13156
+ };
13157
+ }
13158
+ function extractOpenAIChatInput(params) {
13159
+ const { messages, ...metadata } = params;
13160
+ return {
13161
+ input: processInputAttachments(messages),
13162
+ metadata: { ...metadata, provider: "openai" }
13163
+ };
13164
+ }
13165
+ function extractOpenAIResponsesInput(params) {
13166
+ const { input, ...metadata } = params;
13167
+ return {
13168
+ input: processInputAttachments(input),
13169
+ metadata: { ...metadata, provider: "openai" }
13170
+ };
13171
+ }
13172
+ function extractOpenAIResponsesMetadata(result) {
13173
+ if (!result) {
13174
+ return void 0;
13175
+ }
13176
+ const { output: _output, usage: _usage, ...metadata } = result;
13177
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
13178
+ }
13179
+ function processImagesInOutput(output) {
13180
+ if (Array.isArray(output)) {
13181
+ return output.map(processImagesInOutput);
13182
+ }
13183
+ if (isObject(output) && output.type === "image_generation_call" && typeof output.result === "string" && output.result) {
13184
+ const fileExtension = output.output_format || "png";
13185
+ const contentType = `image/${fileExtension}`;
13186
+ const baseFilename = typeof output.revised_prompt === "string" ? output.revised_prompt.slice(0, 50).replace(/[^a-zA-Z0-9]/g, "_") : "generated_image";
13187
+ let binaryString;
13188
+ try {
13189
+ binaryString = atob(output.result);
13190
+ } catch {
13191
+ return output;
13192
+ }
13193
+ const bytes = new Uint8Array(binaryString.length);
13194
+ for (let i = 0; i < binaryString.length; i++) {
13195
+ bytes[i] = binaryString.charCodeAt(i);
13196
+ }
13197
+ return {
13198
+ ...output,
13199
+ result: new Attachment({
13200
+ data: new Blob([bytes], { type: contentType }),
13201
+ filename: `${baseFilename}.${fileExtension}`,
13202
+ contentType
13203
+ })
13204
+ };
13205
+ }
13206
+ return output;
12772
13207
  }
12773
-
12774
- // src/instrumentation/plugins/openai-channels.ts
12775
- var openAIChannels = defineChannels(
12776
- "openai",
12777
- {
12778
- chatCompletionsCreate: channel({
12779
- channelName: "chat.completions.create",
12780
- kind: "async"
12781
- }),
12782
- embeddingsCreate: channel({
12783
- channelName: "embeddings.create",
12784
- kind: "async"
12785
- }),
12786
- betaChatCompletionsParse: channel({
12787
- channelName: "beta.chat.completions.parse",
12788
- kind: "async"
12789
- }),
12790
- betaChatCompletionsStream: channel({
12791
- channelName: "beta.chat.completions.stream",
12792
- kind: "sync-stream"
12793
- }),
12794
- moderationsCreate: channel({
12795
- channelName: "moderations.create",
12796
- kind: "async"
12797
- }),
12798
- responsesCreate: channel({
12799
- channelName: "responses.create",
12800
- kind: "async"
12801
- }),
12802
- responsesStream: channel({
12803
- channelName: "responses.stream",
12804
- kind: "sync-stream"
12805
- }),
12806
- responsesParse: channel({
12807
- channelName: "responses.parse",
12808
- kind: "async"
12809
- }),
12810
- responsesCompact: channel({
12811
- channelName: "responses.compact",
12812
- kind: "async"
12813
- })
12814
- },
12815
- { instrumentationName: INSTRUMENTATION_NAMES.OPENAI }
12816
- );
12817
13208
 
12818
13209
  // src/openai-utils.ts
12819
13210
  var BRAINTRUST_CACHED_STREAM_METRIC = "__braintrust_cached_metric";
@@ -12870,23 +13261,573 @@ function getCachedMetricFromHeaders(headers) {
12870
13261
  return parseCachedHeader(headers.get(LEGACY_CACHED_HEADER));
12871
13262
  }
12872
13263
 
13264
+ // src/instrumentation/plugins/openai-batch-instrumentation.ts
13265
+ var SUPPORTED_ENDPOINTS = /* @__PURE__ */ new Set(["/v1/chat/completions", "/v1/responses"]);
13266
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
13267
+ "completed",
13268
+ "failed",
13269
+ "expired",
13270
+ "cancelled"
13271
+ ]);
13272
+ var pendingBatchTraces = /* @__PURE__ */ new Map();
13273
+ function read(value, key) {
13274
+ if (!isObject(value)) {
13275
+ return void 0;
13276
+ }
13277
+ try {
13278
+ return Reflect.get(value, key);
13279
+ } catch {
13280
+ return void 0;
13281
+ }
13282
+ }
13283
+ function isBatchRecordIterable(value) {
13284
+ if (value === null || typeof value !== "object" && typeof value !== "function") {
13285
+ return false;
13286
+ }
13287
+ return typeof read(value, Symbol.iterator) === "function" || typeof read(value, Symbol.asyncIterator) === "function";
13288
+ }
13289
+ function validCustomId(value) {
13290
+ return typeof value === "string" && value.length > 0 && value.length <= 64;
13291
+ }
13292
+ function logBatchInstrumentationError(context, error) {
13293
+ debugLogger.debug(`OpenAI Batch instrumentation ${context}:`, error);
13294
+ }
13295
+ async function exportParent(parent) {
13296
+ if ("toStr" in parent && typeof parent.toStr === "function") {
13297
+ return parent.toStr();
13298
+ }
13299
+ if ("export" in parent && typeof parent.export === "function") {
13300
+ return await parent.export();
13301
+ }
13302
+ return void 0;
13303
+ }
13304
+ async function deterministicDigest(namespace, ...parts) {
13305
+ const encoded = new TextEncoder().encode(
13306
+ [namespace, ...parts].map((part) => `${part.length}:${part}`).join("\0")
13307
+ );
13308
+ return new Uint8Array(
13309
+ await globalThis.crypto.subtle.digest("SHA-256", encoded)
13310
+ );
13311
+ }
13312
+ function digestHex(bytes, length) {
13313
+ return Array.from(bytes.slice(0, length)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
13314
+ }
13315
+ function digestUuid(bytes) {
13316
+ const hex = digestHex(bytes, 16);
13317
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
13318
+ }
13319
+ async function batchSpanIds(inputFileId) {
13320
+ const [row, span, root] = await Promise.all([
13321
+ deterministicDigest("openai:batch:row", inputFileId),
13322
+ deterministicDigest("openai:batch:span", inputFileId),
13323
+ deterministicDigest("openai:batch:root", inputFileId)
13324
+ ]);
13325
+ return {
13326
+ rowId: digestUuid(row),
13327
+ spanId: digestHex(span, 8),
13328
+ rootSpanId: digestHex(root, 16)
13329
+ };
13330
+ }
13331
+ async function childSpanIds(inputFileId, customId) {
13332
+ const [row, span] = await Promise.all([
13333
+ deterministicDigest("openai:batch:child:row", inputFileId, customId),
13334
+ deterministicDigest("openai:batch:child:span", inputFileId, customId)
13335
+ ]);
13336
+ return { rowId: digestUuid(row), spanId: digestHex(span, 8) };
13337
+ }
13338
+ async function startBatchSpan(context) {
13339
+ const ids = await batchSpanIds(context.inputFileId);
13340
+ const parent = SpanComponentsV4.fromStr(context.parent);
13341
+ const hasParentSpan = Boolean(
13342
+ parent.data.row_id && parent.data.span_id && parent.data.root_span_id
13343
+ );
13344
+ return withCurrent(
13345
+ NOOP_SPAN,
13346
+ () => _internalStartSpanWithInitialMergeAndParentSpanIds(
13347
+ withSpanInstrumentationName(
13348
+ {
13349
+ name: "openai.batch",
13350
+ type: "task" /* TASK */,
13351
+ parent: context.parent,
13352
+ ...!hasParentSpan ? {
13353
+ parentSpanIds: {
13354
+ parentSpanIds: [],
13355
+ rootSpanId: ids.rootSpanId
13356
+ }
13357
+ } : {},
13358
+ spanId: ids.spanId,
13359
+ startTime: context.taskStartTime,
13360
+ event: {
13361
+ id: ids.rowId,
13362
+ metadata: {
13363
+ endpoint: context.endpoint,
13364
+ input_file_id: context.inputFileId,
13365
+ provider: "openai"
13366
+ }
13367
+ }
13368
+ },
13369
+ INSTRUMENTATION_NAMES.OPENAI
13370
+ )
13371
+ )
13372
+ );
13373
+ }
13374
+ async function startBatchChild(context, taskParent, input) {
13375
+ const ids = await childSpanIds(context.inputFileId, input.customId);
13376
+ return withCurrent(
13377
+ NOOP_SPAN,
13378
+ () => _internalStartSpanWithInitialMerge(
13379
+ withSpanInstrumentationName(
13380
+ {
13381
+ name: context.endpoint === "/v1/chat/completions" ? "Chat Completion" : "openai.responses.create",
13382
+ type: "llm" /* LLM */,
13383
+ parent: taskParent,
13384
+ spanId: ids.spanId,
13385
+ startTime: context.childStartTime,
13386
+ event: {
13387
+ id: ids.rowId,
13388
+ ...input.spanData?.input !== void 0 ? { input: input.spanData.input } : {},
13389
+ metadata: {
13390
+ ...input.spanData?.metadata,
13391
+ custom_id: input.customId,
13392
+ provider: "openai"
13393
+ }
13394
+ }
13395
+ },
13396
+ INSTRUMENTATION_NAMES.OPENAI
13397
+ )
13398
+ )
13399
+ );
13400
+ }
13401
+ async function* jsonlRecords(file, onIssue = () => {
13402
+ }) {
13403
+ const resolvedFile = await file;
13404
+ if (typeof resolvedFile === "string") {
13405
+ for (const line of resolvedFile.split("\n")) {
13406
+ if (!line.trim()) {
13407
+ continue;
13408
+ }
13409
+ try {
13410
+ yield JSON.parse(line.replace(/\r$/, ""));
13411
+ } catch (error) {
13412
+ logBatchInstrumentationError("skipped malformed JSONL", error);
13413
+ onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
13414
+ }
13415
+ }
13416
+ return;
13417
+ }
13418
+ const body = read(resolvedFile, "body");
13419
+ const getReader = read(body, "getReader");
13420
+ if (isObject(body) && typeof getReader === "function") {
13421
+ let reader;
13422
+ try {
13423
+ reader = Reflect.apply(getReader, body, []);
13424
+ const decoder = new TextDecoder();
13425
+ let pending = "";
13426
+ while (true) {
13427
+ const readChunk = read(reader, "read");
13428
+ if (typeof readChunk !== "function") {
13429
+ throw new Error("Response body stream has no read method");
13430
+ }
13431
+ const chunk = await Reflect.apply(readChunk, reader, []);
13432
+ if (!isObject(chunk)) {
13433
+ throw new Error("Response body stream returned an invalid chunk");
13434
+ }
13435
+ if (chunk.done === true) {
13436
+ pending += decoder.decode();
13437
+ break;
13438
+ }
13439
+ if (!(chunk.value instanceof Uint8Array)) {
13440
+ throw new Error("Response body stream returned a non-byte chunk");
13441
+ }
13442
+ pending += decoder.decode(chunk.value, { stream: true });
13443
+ let newline = pending.indexOf("\n");
13444
+ while (newline !== -1) {
13445
+ const line = pending.slice(0, newline).replace(/\r$/, "");
13446
+ pending = pending.slice(newline + 1);
13447
+ if (line.trim()) {
13448
+ try {
13449
+ yield JSON.parse(line);
13450
+ } catch (error) {
13451
+ logBatchInstrumentationError("skipped malformed JSONL", error);
13452
+ onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
13453
+ }
13454
+ }
13455
+ newline = pending.indexOf("\n");
13456
+ }
13457
+ }
13458
+ if (pending.trim()) {
13459
+ try {
13460
+ yield JSON.parse(pending.replace(/\r$/, ""));
13461
+ } catch (error) {
13462
+ logBatchInstrumentationError("skipped malformed JSONL", error);
13463
+ onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
13464
+ }
13465
+ }
13466
+ } catch (error) {
13467
+ logBatchInstrumentationError("could not read JSONL response body", error);
13468
+ onIssue(new Error("OpenAI Batch response body could not be read"));
13469
+ } finally {
13470
+ const releaseLock = read(reader, "releaseLock");
13471
+ if (typeof releaseLock === "function") {
13472
+ try {
13473
+ Reflect.apply(releaseLock, reader, []);
13474
+ } catch (error) {
13475
+ logBatchInstrumentationError(
13476
+ "could not release stream reader",
13477
+ error
13478
+ );
13479
+ }
13480
+ }
13481
+ }
13482
+ return;
13483
+ }
13484
+ if (isBatchRecordIterable(resolvedFile)) {
13485
+ for await (const record of resolvedFile) {
13486
+ yield record;
13487
+ }
13488
+ return;
13489
+ }
13490
+ logBatchInstrumentationError("skipped invalid JSONL source", resolvedFile);
13491
+ onIssue(new Error("OpenAI Batch file source is invalid"));
13492
+ }
13493
+ async function readBatchInputs(file) {
13494
+ const inputs = /* @__PURE__ */ new Map();
13495
+ const issues = [];
13496
+ let endpoint;
13497
+ try {
13498
+ for await (const value of jsonlRecords(
13499
+ file,
13500
+ (issue) => issues.push(issue)
13501
+ )) {
13502
+ const customId = read(value, "custom_id");
13503
+ const url = read(value, "url");
13504
+ const body = read(value, "body");
13505
+ if (!validCustomId(customId) || inputs.has(customId) || read(value, "method") !== "POST" || typeof url !== "string" || !SUPPORTED_ENDPOINTS.has(url) || endpoint !== void 0 && endpoint !== url || !isObject(body)) {
13506
+ issues.push(new Error("OpenAI Batch input contains an invalid record"));
13507
+ continue;
13508
+ }
13509
+ endpoint = url;
13510
+ let spanData;
13511
+ try {
13512
+ spanData = extractOpenAIBatchInput(url, body);
13513
+ } catch (error) {
13514
+ logBatchInstrumentationError("could not extract batch input", error);
13515
+ }
13516
+ inputs.set(customId, { customId, spanData });
13517
+ }
13518
+ } catch (error) {
13519
+ logBatchInstrumentationError("could not process input file", error);
13520
+ issues.push(new Error("OpenAI Batch input file could not be processed"));
13521
+ }
13522
+ if (!endpoint || inputs.size === 0) {
13523
+ issues.push(new Error("OpenAI Batch input contains no supported records"));
13524
+ }
13525
+ return { endpoint, inputs, issues };
13526
+ }
13527
+ async function writePendingSpans(trace, endTime) {
13528
+ const task = await startBatchSpan(trace.context);
13529
+ const taskParent = await task.export();
13530
+ for (const input of trace.inputs.values()) {
13531
+ try {
13532
+ const child = await startBatchChild(trace.context, taskParent, input);
13533
+ if (endTime !== void 0) {
13534
+ child.end({ endTime });
13535
+ }
13536
+ } catch (error) {
13537
+ logBatchInstrumentationError("could not write batch request span", error);
13538
+ }
13539
+ }
13540
+ if (endTime !== void 0) {
13541
+ if (trace.status && trace.status !== "completed") {
13542
+ task.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
13543
+ }
13544
+ task.end({ endTime });
13545
+ }
13546
+ }
13547
+ var interceptOpenAIFilesCreateTraced = async (target, thisArg, args) => {
13548
+ const startTime = getCurrentUnixTimestamp();
13549
+ const inputPromise = readBatchInputs(args[0].inputFileContent);
13550
+ const parentPromise = exportParent(args[0].parent);
13551
+ const file = await Reflect.apply(target, thisArg, args);
13552
+ try {
13553
+ const inputFileId = read(file, "id");
13554
+ const [inputData, exportedParent2] = await Promise.all([
13555
+ inputPromise,
13556
+ parentPromise
13557
+ ]);
13558
+ if (typeof inputFileId !== "string" || !exportedParent2 || !inputData.endpoint || inputData.issues.length > 0) {
13559
+ if (inputData.issues[0]) {
13560
+ logBatchInstrumentationError(
13561
+ "skipped invalid input file",
13562
+ inputData.issues[0]
13563
+ );
13564
+ }
13565
+ return file;
13566
+ }
13567
+ const trace = {
13568
+ context: {
13569
+ inputFileId,
13570
+ endpoint: inputData.endpoint,
13571
+ parent: exportedParent2,
13572
+ taskStartTime: startTime,
13573
+ childStartTime: startTime
13574
+ },
13575
+ inputs: inputData.inputs
13576
+ };
13577
+ pendingBatchTraces.set(inputFileId, trace);
13578
+ await writePendingSpans(trace);
13579
+ } catch (error) {
13580
+ logBatchInstrumentationError("could not start batch spans", error);
13581
+ }
13582
+ return file;
13583
+ };
13584
+ function validTimestamp(value) {
13585
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
13586
+ }
13587
+ function terminalEndTime(batch, startTime) {
13588
+ let timestamp;
13589
+ switch (batch.status) {
13590
+ case "completed":
13591
+ timestamp = batch.completed_at;
13592
+ break;
13593
+ case "failed":
13594
+ timestamp = batch.failed_at;
13595
+ break;
13596
+ case "expired":
13597
+ timestamp = batch.expired_at;
13598
+ break;
13599
+ default:
13600
+ timestamp = batch.cancelled_at;
13601
+ }
13602
+ return validTimestamp(timestamp) ? Math.max(timestamp, startTime) : Math.max(getCurrentUnixTimestamp(), startTime);
13603
+ }
13604
+ async function updateBatchTimestamps(batch) {
13605
+ const trace = pendingBatchTraces.get(batch.input_file_id);
13606
+ if (!trace || trace.context.endpoint !== batch.endpoint) {
13607
+ return;
13608
+ }
13609
+ if (validTimestamp(batch.created_at)) {
13610
+ trace.context.taskStartTime = batch.created_at;
13611
+ }
13612
+ trace.context.childStartTime = validTimestamp(batch.in_progress_at) ? Math.max(batch.in_progress_at, trace.context.taskStartTime) : trace.context.taskStartTime;
13613
+ trace.status = batch.status;
13614
+ if (TERMINAL_STATUSES.has(batch.status)) {
13615
+ trace.endTime = terminalEndTime(batch, trace.context.childStartTime);
13616
+ }
13617
+ await writePendingSpans(trace, trace.endTime);
13618
+ }
13619
+ var interceptOpenAIBatchesRetrieveTraced = (target, thisArg, args) => Promise.resolve(Reflect.apply(target, thisArg, args)).then(async (batch) => {
13620
+ try {
13621
+ await updateBatchTimestamps(batch);
13622
+ } catch (error) {
13623
+ logBatchInstrumentationError("could not update batch timestamps", error);
13624
+ }
13625
+ return batch;
13626
+ });
13627
+ function errorFromResult(result) {
13628
+ const error = read(result.value, "error");
13629
+ if (isObject(error)) {
13630
+ const message = read(error, "message");
13631
+ return new Error(
13632
+ typeof message === "string" ? message : "OpenAI Batch request failed"
13633
+ );
13634
+ }
13635
+ const response = read(result.value, "response");
13636
+ const statusCode = read(response, "status_code");
13637
+ if (result.source === "error" || typeof statusCode === "number" && (statusCode < 200 || statusCode >= 300)) {
13638
+ const message = read(read(read(response, "body"), "error"), "message");
13639
+ return new Error(
13640
+ typeof message === "string" ? message : "OpenAI Batch request failed"
13641
+ );
13642
+ }
13643
+ return void 0;
13644
+ }
13645
+ async function completeBatchResult(context, taskParent, endTime, input, result) {
13646
+ const child = await startBatchChild(context, taskParent, input);
13647
+ try {
13648
+ const resultError = errorFromResult(result);
13649
+ const responseBody = read(read(result.value, "response"), "body");
13650
+ if (resultError) {
13651
+ child.log({ error: resultError });
13652
+ } else if (isObject(responseBody)) {
13653
+ const model = read(responseBody, "model");
13654
+ child.log({
13655
+ output: context.endpoint === "/v1/chat/completions" ? read(responseBody, "choices") : processImagesInOutput(read(responseBody, "output")),
13656
+ ...typeof model === "string" ? { metadata: { model } } : {},
13657
+ metrics: parseMetricsFromUsage(read(responseBody, "usage"))
13658
+ });
13659
+ } else {
13660
+ child.log({ error: new Error("OpenAI Batch response body is missing") });
13661
+ }
13662
+ } catch (error) {
13663
+ child.log({ error });
13664
+ } finally {
13665
+ child.end({ endTime });
13666
+ }
13667
+ }
13668
+ async function completeResultFile({
13669
+ context,
13670
+ endTime,
13671
+ file,
13672
+ inputs,
13673
+ issues,
13674
+ seen,
13675
+ source,
13676
+ taskParent
13677
+ }) {
13678
+ if (file === void 0) {
13679
+ return;
13680
+ }
13681
+ for await (const value of jsonlRecords(file, (issue) => issues.push(issue))) {
13682
+ const customId = read(value, "custom_id");
13683
+ if (!validCustomId(customId) || !isObject(value)) {
13684
+ issues.push(
13685
+ new Error("OpenAI Batch result is missing a valid custom_id")
13686
+ );
13687
+ continue;
13688
+ }
13689
+ if (seen.has(customId)) {
13690
+ issues.push(new Error("OpenAI Batch result contains a duplicate"));
13691
+ continue;
13692
+ }
13693
+ const input = inputs.get(customId);
13694
+ if (!input) {
13695
+ issues.push(
13696
+ new Error("OpenAI Batch result does not match an input record")
13697
+ );
13698
+ continue;
13699
+ }
13700
+ seen.add(customId);
13701
+ await completeBatchResult(context, taskParent, endTime, input, {
13702
+ value,
13703
+ source
13704
+ });
13705
+ }
13706
+ }
13707
+ function sameInputs(first, second) {
13708
+ return first.size === second.size && [...first.keys()].every((customId) => second.has(customId));
13709
+ }
13710
+ async function contextForCompletion(inputFileId, inputData) {
13711
+ if (!inputData.endpoint || inputData.issues.length > 0) {
13712
+ return void 0;
13713
+ }
13714
+ const existing = pendingBatchTraces.get(inputFileId);
13715
+ if (existing) {
13716
+ if (existing.context.endpoint !== inputData.endpoint || !sameInputs(existing.inputs, inputData.inputs)) {
13717
+ return void 0;
13718
+ }
13719
+ return { ...existing, inputs: inputData.inputs };
13720
+ }
13721
+ const parent = await exportParent(getSpanParentObject());
13722
+ if (!parent) {
13723
+ return void 0;
13724
+ }
13725
+ const startTime = getCurrentUnixTimestamp();
13726
+ return {
13727
+ context: {
13728
+ inputFileId,
13729
+ endpoint: inputData.endpoint,
13730
+ parent,
13731
+ taskStartTime: startTime,
13732
+ childStartTime: startTime
13733
+ },
13734
+ inputs: inputData.inputs
13735
+ };
13736
+ }
13737
+ async function completeBatch(args) {
13738
+ const inputData = await readBatchInputs(args.inputFileContent);
13739
+ const trace = await contextForCompletion(args.inputFileId, inputData);
13740
+ if (!trace) {
13741
+ logBatchInstrumentationError(
13742
+ "left batch spans pending",
13743
+ inputData.issues[0] ?? new Error("OpenAI Batch input does not match")
13744
+ );
13745
+ return;
13746
+ }
13747
+ const endTime = trace.endTime ?? getCurrentUnixTimestamp();
13748
+ const task = await startBatchSpan(trace.context);
13749
+ const taskParent = await task.export();
13750
+ const issues = [];
13751
+ const seen = /* @__PURE__ */ new Set();
13752
+ await Promise.all([
13753
+ completeResultFile({
13754
+ context: trace.context,
13755
+ endTime,
13756
+ file: args.outputFileContent,
13757
+ inputs: trace.inputs,
13758
+ issues,
13759
+ seen,
13760
+ source: "output",
13761
+ taskParent
13762
+ }),
13763
+ completeResultFile({
13764
+ context: trace.context,
13765
+ endTime,
13766
+ file: args.errorFileContent,
13767
+ inputs: trace.inputs,
13768
+ issues,
13769
+ seen,
13770
+ source: "error",
13771
+ taskParent
13772
+ })
13773
+ ]);
13774
+ if (issues.length > 0) {
13775
+ logBatchInstrumentationError("left batch spans pending", issues[0]);
13776
+ return;
13777
+ }
13778
+ const missing = [...trace.inputs.values()].filter(
13779
+ ({ customId }) => !seen.has(customId)
13780
+ );
13781
+ if (!trace.status || trace.status === "completed") {
13782
+ if (missing.length > 0) {
13783
+ logBatchInstrumentationError(
13784
+ "left batch spans pending",
13785
+ new Error("OpenAI Batch result files are incomplete")
13786
+ );
13787
+ return;
13788
+ }
13789
+ } else {
13790
+ for (const input of missing) {
13791
+ const child = await startBatchChild(trace.context, taskParent, input);
13792
+ child.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
13793
+ child.end({ endTime });
13794
+ }
13795
+ task.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
13796
+ }
13797
+ task.end({ endTime });
13798
+ pendingBatchTraces.delete(args.inputFileId);
13799
+ }
13800
+ var interceptOpenAIBatchTraceComplete = (target, thisArg, args) => Promise.resolve(Reflect.apply(target, thisArg, args)).then(async (result) => {
13801
+ try {
13802
+ await completeBatch(args[0]);
13803
+ } catch (error) {
13804
+ logBatchInstrumentationError("could not complete batch", error);
13805
+ }
13806
+ return result;
13807
+ });
13808
+
12873
13809
  // src/instrumentation/plugins/openai-plugin.ts
12874
13810
  var OpenAIPlugin = class extends BasePlugin {
12875
13811
  constructor() {
12876
13812
  super();
12877
13813
  }
12878
13814
  onEnable() {
13815
+ this.unsubscribers.push(
13816
+ openAIChannels.filesCreateTraced.intercept(
13817
+ interceptOpenAIFilesCreateTraced
13818
+ ),
13819
+ openAIChannels.batchesRetrieveTraced.intercept(
13820
+ interceptOpenAIBatchesRetrieveTraced
13821
+ ),
13822
+ openAIChannels.batchesCompleteTrace.intercept(
13823
+ interceptOpenAIBatchTraceComplete
13824
+ )
13825
+ );
12879
13826
  this.unsubscribers.push(
12880
13827
  traceStreamingChannel(openAIChannels.chatCompletionsCreate, {
12881
13828
  name: "Chat Completion",
12882
13829
  type: "llm" /* LLM */,
12883
- extractInput: ([params]) => {
12884
- const { messages, ...metadata } = params;
12885
- return {
12886
- input: processInputAttachments(messages),
12887
- metadata: { ...metadata, provider: "openai" }
12888
- };
12889
- },
13830
+ extractInput: ([params]) => extractOpenAIChatInput(params),
12890
13831
  extractOutput: (result) => {
12891
13832
  return result?.choices;
12892
13833
  },
@@ -12932,13 +13873,7 @@ var OpenAIPlugin = class extends BasePlugin {
12932
13873
  traceStreamingChannel(openAIChannels.betaChatCompletionsParse, {
12933
13874
  name: "Chat Completion",
12934
13875
  type: "llm" /* LLM */,
12935
- extractInput: ([params]) => {
12936
- const { messages, ...metadata } = params;
12937
- return {
12938
- input: processInputAttachments(messages),
12939
- metadata: { ...metadata, provider: "openai" }
12940
- };
12941
- },
13876
+ extractInput: ([params]) => extractOpenAIChatInput(params),
12942
13877
  extractOutput: (result) => {
12943
13878
  return result?.choices;
12944
13879
  },
@@ -12960,13 +13895,7 @@ var OpenAIPlugin = class extends BasePlugin {
12960
13895
  traceSyncStreamChannel(openAIChannels.betaChatCompletionsStream, {
12961
13896
  name: "Chat Completion",
12962
13897
  type: "llm" /* LLM */,
12963
- extractInput: ([params]) => {
12964
- const { messages, ...metadata } = params;
12965
- return {
12966
- input: processInputAttachments(messages),
12967
- metadata: { ...metadata, provider: "openai" }
12968
- };
12969
- }
13898
+ extractInput: ([params]) => extractOpenAIChatInput(params)
12970
13899
  })
12971
13900
  );
12972
13901
  this.unsubscribers.push(
@@ -12996,23 +13925,11 @@ var OpenAIPlugin = class extends BasePlugin {
12996
13925
  traceStreamingChannel(openAIChannels.responsesCreate, {
12997
13926
  name: "openai.responses.create",
12998
13927
  type: "llm" /* LLM */,
12999
- extractInput: ([params]) => {
13000
- const { input, ...metadata } = params;
13001
- return {
13002
- input: processInputAttachments(input),
13003
- metadata: { ...metadata, provider: "openai" }
13004
- };
13005
- },
13928
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
13006
13929
  extractOutput: (result) => {
13007
13930
  return processImagesInOutput(result?.output);
13008
13931
  },
13009
- extractMetadata: (result) => {
13010
- if (!result) {
13011
- return void 0;
13012
- }
13013
- const { output: _output, usage: _usage, ...metadata } = result;
13014
- return Object.keys(metadata).length > 0 ? metadata : void 0;
13015
- },
13932
+ extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
13016
13933
  extractMetrics: (result, startTime, endEvent) => {
13017
13934
  const metrics = withCachedMetric(
13018
13935
  parseMetricsFromUsage(result?.usage),
@@ -13031,13 +13948,7 @@ var OpenAIPlugin = class extends BasePlugin {
13031
13948
  traceSyncStreamChannel(openAIChannels.responsesStream, {
13032
13949
  name: "openai.responses.create",
13033
13950
  type: "llm" /* LLM */,
13034
- extractInput: ([params]) => {
13035
- const { input, ...metadata } = params;
13036
- return {
13037
- input: processInputAttachments(input),
13038
- metadata: { ...metadata, provider: "openai" }
13039
- };
13040
- },
13951
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
13041
13952
  extractFromEvent: (event) => {
13042
13953
  if (event.type !== "response.completed" || !event.response) {
13043
13954
  return {};
@@ -13060,23 +13971,11 @@ var OpenAIPlugin = class extends BasePlugin {
13060
13971
  traceStreamingChannel(openAIChannels.responsesParse, {
13061
13972
  name: "openai.responses.parse",
13062
13973
  type: "llm" /* LLM */,
13063
- extractInput: ([params]) => {
13064
- const { input, ...metadata } = params;
13065
- return {
13066
- input: processInputAttachments(input),
13067
- metadata: { ...metadata, provider: "openai" }
13068
- };
13069
- },
13974
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
13070
13975
  extractOutput: (result) => {
13071
13976
  return processImagesInOutput(result?.output);
13072
13977
  },
13073
- extractMetadata: (result) => {
13074
- if (!result) {
13075
- return void 0;
13076
- }
13077
- const { output: _output, usage: _usage, ...metadata } = result;
13078
- return Object.keys(metadata).length > 0 ? metadata : void 0;
13079
- },
13978
+ extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
13080
13979
  extractMetrics: (result, startTime, endEvent) => {
13081
13980
  const metrics = withCachedMetric(
13082
13981
  parseMetricsFromUsage(result?.usage),
@@ -13095,23 +13994,11 @@ var OpenAIPlugin = class extends BasePlugin {
13095
13994
  traceAsyncChannel(openAIChannels.responsesCompact, {
13096
13995
  name: "openai.responses.compact",
13097
13996
  type: "llm" /* LLM */,
13098
- extractInput: ([params]) => {
13099
- const { input, ...metadata } = params;
13100
- return {
13101
- input: processInputAttachments(input),
13102
- metadata: { ...metadata, provider: "openai" }
13103
- };
13104
- },
13997
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
13105
13998
  extractOutput: (result) => {
13106
13999
  return processImagesInOutput(result?.output);
13107
14000
  },
13108
- extractMetadata: (result) => {
13109
- if (!result) {
13110
- return void 0;
13111
- }
13112
- const { output: _output, usage: _usage, ...metadata } = result;
13113
- return Object.keys(metadata).length > 0 ? metadata : void 0;
13114
- },
14001
+ extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
13115
14002
  extractMetrics: (result, startTime, endEvent) => {
13116
14003
  const metrics = withCachedMetric(
13117
14004
  parseMetricsFromUsage(result?.usage),
@@ -13167,35 +14054,6 @@ function withCachedMetric(metrics, result, endEvent) {
13167
14054
  cached
13168
14055
  };
13169
14056
  }
13170
- function processImagesInOutput(output) {
13171
- if (Array.isArray(output)) {
13172
- return output.map(processImagesInOutput);
13173
- }
13174
- if (isObject(output)) {
13175
- if (output.type === "image_generation_call" && output.result && typeof output.result === "string") {
13176
- const fileExtension = output.output_format || "png";
13177
- const contentType = `image/${fileExtension}`;
13178
- const baseFilename = output.revised_prompt && typeof output.revised_prompt === "string" ? output.revised_prompt.slice(0, 50).replace(/[^a-zA-Z0-9]/g, "_") : "generated_image";
13179
- const filename = `${baseFilename}.${fileExtension}`;
13180
- const binaryString = atob(output.result);
13181
- const bytes = new Uint8Array(binaryString.length);
13182
- for (let i = 0; i < binaryString.length; i++) {
13183
- bytes[i] = binaryString.charCodeAt(i);
13184
- }
13185
- const blob = new Blob([bytes], { type: contentType });
13186
- const attachment = new Attachment({
13187
- data: blob,
13188
- filename,
13189
- contentType
13190
- });
13191
- return {
13192
- ...output,
13193
- result: attachment
13194
- };
13195
- }
13196
- }
13197
- return output;
13198
- }
13199
14057
  function mergeLogprobTokens(existing, incoming) {
13200
14058
  if (incoming === void 0) {
13201
14059
  return existing;
@@ -13226,13 +14084,33 @@ function aggregateChatLogprobs(existing, incoming) {
13226
14084
  }
13227
14085
  return aggregated;
13228
14086
  }
14087
+ function createAggregatedChatChoice(index) {
14088
+ return {
14089
+ index,
14090
+ role: void 0,
14091
+ content: void 0,
14092
+ refusal: void 0,
14093
+ toolCallsByIndex: /* @__PURE__ */ new Map(),
14094
+ logprobs: void 0,
14095
+ finish_reason: void 0
14096
+ };
14097
+ }
14098
+ function toChatChoice(choice) {
14099
+ const toolCalls = Array.from(choice.toolCallsByIndex.entries()).sort(([left], [right]) => left - right).map(([, toolCall]) => toolCall);
14100
+ return {
14101
+ index: choice.index,
14102
+ message: {
14103
+ role: choice.role,
14104
+ content: choice.content,
14105
+ ...choice.refusal !== void 0 ? { refusal: choice.refusal } : {},
14106
+ tool_calls: toolCalls.length > 0 ? toolCalls : void 0
14107
+ },
14108
+ logprobs: choice.logprobs ?? null,
14109
+ finish_reason: choice.finish_reason
14110
+ };
14111
+ }
13229
14112
  function aggregateChatCompletionChunks(chunks, streamResult, endEvent) {
13230
- let role = void 0;
13231
- let content = void 0;
13232
- let refusal = void 0;
13233
- let tool_calls = void 0;
13234
- let logprobs = void 0;
13235
- let finish_reason = void 0;
14113
+ const choicesByIndex = /* @__PURE__ */ new Map();
13236
14114
  let metrics = {};
13237
14115
  for (const chunk of chunks) {
13238
14116
  if (chunk.usage) {
@@ -13241,62 +14119,75 @@ function aggregateChatCompletionChunks(chunks, streamResult, endEvent) {
13241
14119
  ...parseMetricsFromUsage(chunk.usage)
13242
14120
  };
13243
14121
  }
13244
- const choice = chunk.choices?.[0];
13245
- if (!choice) {
13246
- continue;
13247
- }
13248
- if (choice.finish_reason) {
13249
- finish_reason = choice.finish_reason;
13250
- }
13251
- logprobs = aggregateChatLogprobs(logprobs, choice.logprobs);
13252
- const delta = choice.delta;
13253
- if (!delta) {
14122
+ const choices = chunk.choices;
14123
+ if (!choices?.length) {
13254
14124
  continue;
13255
14125
  }
13256
- if (delta.finish_reason) {
13257
- finish_reason = delta.finish_reason;
13258
- }
13259
- if (!role && delta.role) {
13260
- role = delta.role;
13261
- }
13262
- if (delta.content) {
13263
- content = (content || "") + delta.content;
13264
- }
13265
- if (delta.refusal) {
13266
- refusal = (refusal || "") + delta.refusal;
13267
- }
13268
- if (delta.tool_calls) {
13269
- const toolDelta = delta.tool_calls[0];
13270
- if (!tool_calls || toolDelta.id && tool_calls[tool_calls.length - 1].id !== toolDelta.id) {
13271
- tool_calls = [
13272
- ...tool_calls || [],
13273
- {
13274
- id: toolDelta.id,
13275
- type: toolDelta.type,
13276
- function: toolDelta.function
14126
+ for (const choice of choices) {
14127
+ const choiceIndex = choice.index;
14128
+ let aggregatedChoice = choicesByIndex.get(choiceIndex);
14129
+ if (!aggregatedChoice) {
14130
+ aggregatedChoice = createAggregatedChatChoice(choiceIndex);
14131
+ choicesByIndex.set(choiceIndex, aggregatedChoice);
14132
+ }
14133
+ if (choice.finish_reason) {
14134
+ aggregatedChoice.finish_reason = choice.finish_reason;
14135
+ }
14136
+ aggregatedChoice.logprobs = aggregateChatLogprobs(
14137
+ aggregatedChoice.logprobs,
14138
+ choice.logprobs
14139
+ );
14140
+ const delta = choice.delta;
14141
+ if (!delta) {
14142
+ continue;
14143
+ }
14144
+ if (delta.finish_reason) {
14145
+ aggregatedChoice.finish_reason = delta.finish_reason;
14146
+ }
14147
+ if (!aggregatedChoice.role && delta.role) {
14148
+ aggregatedChoice.role = delta.role;
14149
+ }
14150
+ if (delta.content) {
14151
+ aggregatedChoice.content = (aggregatedChoice.content || "") + delta.content;
14152
+ }
14153
+ if (delta.refusal) {
14154
+ aggregatedChoice.refusal = (aggregatedChoice.refusal || "") + delta.refusal;
14155
+ }
14156
+ if (delta.tool_calls) {
14157
+ for (const toolDelta of delta.tool_calls) {
14158
+ let aggregatedToolCall = aggregatedChoice.toolCallsByIndex.get(
14159
+ toolDelta.index
14160
+ );
14161
+ if (!aggregatedToolCall) {
14162
+ aggregatedToolCall = {
14163
+ function: { arguments: "" }
14164
+ };
14165
+ aggregatedChoice.toolCallsByIndex.set(
14166
+ toolDelta.index,
14167
+ aggregatedToolCall
14168
+ );
13277
14169
  }
13278
- ];
13279
- } else {
13280
- tool_calls[tool_calls.length - 1].function.arguments += toolDelta.function.arguments;
14170
+ if (toolDelta.id !== void 0) {
14171
+ aggregatedToolCall.id = toolDelta.id;
14172
+ }
14173
+ if (toolDelta.type !== void 0) {
14174
+ aggregatedToolCall.type = toolDelta.type;
14175
+ }
14176
+ if (toolDelta.function?.name !== void 0) {
14177
+ aggregatedToolCall.function.name = toolDelta.function.name;
14178
+ }
14179
+ if (toolDelta.function?.arguments !== void 0) {
14180
+ aggregatedToolCall.function.arguments += toolDelta.function.arguments;
14181
+ }
14182
+ }
13281
14183
  }
13282
14184
  }
13283
14185
  }
13284
14186
  metrics = withCachedMetric(metrics, streamResult, endEvent);
14187
+ const output = Array.from(choicesByIndex.values()).sort((left, right) => left.index - right.index).map(toChatChoice);
13285
14188
  return {
13286
14189
  metrics,
13287
- output: [
13288
- {
13289
- index: 0,
13290
- message: {
13291
- role,
13292
- content,
13293
- ...refusal !== void 0 ? { refusal } : {},
13294
- tool_calls
13295
- },
13296
- logprobs: logprobs ?? null,
13297
- finish_reason
13298
- }
13299
- ]
14190
+ output: output.length > 0 ? output : [toChatChoice(createAggregatedChatChoice(0))]
13300
14191
  };
13301
14192
  }
13302
14193
  function aggregateResponseStreamEvents(chunks, _streamResult, endEvent) {
@@ -14619,6 +15510,12 @@ function parseMetricsFromUsage2(usage) {
14619
15510
  }
14620
15511
  }
14621
15512
  }
15513
+ if (isObject(usage.output_tokens_details)) {
15514
+ const thinkingTokens = usage.output_tokens_details.thinking_tokens;
15515
+ if (typeof thinkingTokens === "number") {
15516
+ metrics.completion_reasoning_tokens = thinkingTokens;
15517
+ }
15518
+ }
14622
15519
  if (isObject(usage.server_tool_use)) {
14623
15520
  for (const [name, value] of Object.entries(usage.server_tool_use)) {
14624
15521
  if (typeof value === "number") {
@@ -15575,7 +16472,6 @@ function endHarnessTurn(parent) {
15575
16472
  function braintrustAISDKTelemetry() {
15576
16473
  const operations = /* @__PURE__ */ new Map();
15577
16474
  const operationKeysByCallId = /* @__PURE__ */ new Map();
15578
- const workflowOperationKeyStore = isomorph_default.newAsyncLocalStorage();
15579
16475
  const modelSpans = /* @__PURE__ */ new Map();
15580
16476
  const objectSpans = /* @__PURE__ */ new Map();
15581
16477
  const embedSpans = /* @__PURE__ */ new Map();
@@ -15618,9 +16514,6 @@ function braintrustAISDKTelemetry() {
15618
16514
  return;
15619
16515
  }
15620
16516
  operations.delete(operationKey);
15621
- if (workflowOperationKeyStore.getStore() === operationKey) {
15622
- workflowOperationKeyStore.enterWith(void 0);
15623
- }
15624
16517
  const keys = operationKeysByCallId.get(state.callId);
15625
16518
  if (!keys) {
15626
16519
  return;
@@ -15668,14 +16561,7 @@ function braintrustAISDKTelemetry() {
15668
16561
  return key;
15669
16562
  }
15670
16563
  }
15671
- const workflowOperationKey = workflowOperationKeyStore.getStore();
15672
- if (workflowOperationKey && keys.includes(workflowOperationKey)) {
15673
- return workflowOperationKey;
15674
- }
15675
- if (callId === "workflow-agent") {
15676
- return void 0;
15677
- }
15678
- return mode === "finish" ? keys[0] : keys[keys.length - 1];
16564
+ return callId === "workflow-agent" || mode === "active" ? keys[keys.length - 1] : keys[0];
15679
16565
  };
15680
16566
  const operationKeyFromEvent = (event, mode = "active") => {
15681
16567
  const explicit = explicitOperationKey(event);
@@ -15689,17 +16575,13 @@ function braintrustAISDKTelemetry() {
15689
16575
  if (operationKey) {
15690
16576
  return operationKey;
15691
16577
  }
15692
- const workflowOperationKey2 = workflowOperationKeyStore.getStore();
15693
- if (workflowOperationKey2 && operations.has(workflowOperationKey2)) {
15694
- return workflowOperationKey2;
16578
+ const workflowAgentKeys2 = operationKeysByCallId.get("workflow-agent");
16579
+ if (workflowAgentKeys2?.length) {
16580
+ return workflowAgentKeys2[workflowAgentKeys2.length - 1];
15695
16581
  }
15696
16582
  return callId === "workflow-agent" ? void 0 : callId;
15697
16583
  }
15698
16584
  }
15699
- const workflowOperationKey = workflowOperationKeyStore.getStore();
15700
- if (workflowOperationKey && operations.has(workflowOperationKey)) {
15701
- return workflowOperationKey;
15702
- }
15703
16585
  const wrapperSpan = currentWorkflowAgentWrapperSpan();
15704
16586
  if (wrapperSpan?.spanId) {
15705
16587
  for (const [operationKey, state] of operations) {
@@ -15709,8 +16591,8 @@ function braintrustAISDKTelemetry() {
15709
16591
  }
15710
16592
  }
15711
16593
  const workflowAgentKeys = operationKeysByCallId.get("workflow-agent");
15712
- if (workflowAgentKeys?.length === 1) {
15713
- return workflowAgentKeys[0];
16594
+ if (workflowAgentKeys?.length) {
16595
+ return workflowAgentKeys[workflowAgentKeys.length - 1];
15714
16596
  }
15715
16597
  if (operations.size === 1) {
15716
16598
  return operations.keys().next().value;
@@ -15913,9 +16795,6 @@ function braintrustAISDKTelemetry() {
15913
16795
  if (!ownsSpan) {
15914
16796
  return;
15915
16797
  }
15916
- if (workflowAgent) {
15917
- workflowOperationKeyStore.enterWith(operationKey);
15918
- }
15919
16798
  let metadata = metadataFromEvent(event);
15920
16799
  const logPayload = { metadata };
15921
16800
  const workflowAgentCallInput = workflowAgent ? operationInput(event, operationName) : void 0;
@@ -16387,6 +17266,10 @@ var aiSDKChannels = defineChannels(
16387
17266
  channelName: "generateText",
16388
17267
  kind: "async"
16389
17268
  }),
17269
+ generateImage: channel({
17270
+ channelName: "generateImage",
17271
+ kind: "async"
17272
+ }),
16390
17273
  streamText: channel({
16391
17274
  channelName: "streamText",
16392
17275
  kind: "async"
@@ -16574,7 +17457,7 @@ var AISDKPlugin = class extends BasePlugin {
16574
17457
  }
16575
17458
  subscribeToAISDK() {
16576
17459
  const denyOutputPaths = this.config.denyOutputPaths || DEFAULT_DENY_OUTPUT_PATHS;
16577
- this.unsubscribers.push(subscribeToAISDKV7TelemetryDispatcher());
17460
+ this.unsubscribers.push(interceptAISDKV7TelemetryDispatcher());
16578
17461
  this.unsubscribers.push(subscribeToHarnessAgentCreateSession());
16579
17462
  this.unsubscribers.push(
16580
17463
  subscribeToHarnessContinuation(
@@ -16602,6 +17485,18 @@ var AISDKPlugin = class extends BasePlugin {
16602
17485
  aggregateChunks: aggregateAISDKChunks
16603
17486
  })
16604
17487
  );
17488
+ this.unsubscribers.push(
17489
+ traceAsyncChannel(aiSDKChannels.generateImage, {
17490
+ name: "generateImage",
17491
+ type: "llm" /* LLM */,
17492
+ extractInput: ([params], event) => prepareAISDKGenerateImageInput(params, event.self),
17493
+ extractOutput: (result, endEvent) => processAISDKGenerateImageOutput(
17494
+ result,
17495
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
17496
+ ),
17497
+ extractMetrics: (result) => extractTokenMetrics(result)
17498
+ })
17499
+ );
16605
17500
  this.unsubscribers.push(
16606
17501
  traceStreamingChannel(aiSDKChannels.streamText, {
16607
17502
  name: "streamText",
@@ -17091,26 +17986,29 @@ function subscribeToHarnessContinuation(continuationChannel, defaultDenyOutputPa
17091
17986
  channel2.unsubscribe(handlers);
17092
17987
  };
17093
17988
  }
17094
- function subscribeToAISDKV7TelemetryDispatcher() {
17095
- const channel2 = aiSDKChannels.v7CreateTelemetryDispatcher.tracingChannel();
17989
+ function interceptAISDKV7TelemetryDispatcher() {
17096
17990
  const telemetry = braintrustAISDKTelemetry();
17097
- const handlers = {
17098
- end: (event) => {
17099
- const telemetryOptions = event.arguments?.[0]?.telemetry;
17100
- if (telemetryOptions?.isEnabled === false) {
17101
- return;
17991
+ return aiSDKChannels.v7CreateTelemetryDispatcher.intercept(
17992
+ (target, thisArg, args) => {
17993
+ const dispatcher = Reflect.apply(target, thisArg, args);
17994
+ const telemetryOptions = args[0]?.telemetry;
17995
+ if (telemetryOptions?.isEnabled !== false) {
17996
+ try {
17997
+ patchAISDKV7TelemetryDispatcher(
17998
+ dispatcher,
17999
+ telemetry,
18000
+ telemetryOptions
18001
+ );
18002
+ } catch (error) {
18003
+ debugLogger.error(
18004
+ "Error instrumenting AI SDK v7 telemetry dispatcher:",
18005
+ error
18006
+ );
18007
+ }
17102
18008
  }
17103
- patchAISDKV7TelemetryDispatcher(
17104
- event.result,
17105
- telemetry,
17106
- telemetryOptions
17107
- );
18009
+ return dispatcher;
17108
18010
  }
17109
- };
17110
- channel2.subscribe(handlers);
17111
- return () => {
17112
- channel2.unsubscribe(handlers);
17113
- };
18011
+ );
17114
18012
  }
17115
18013
  function patchAISDKV7TelemetryDispatcher(dispatcher, telemetry, telemetryOptions) {
17116
18014
  if (!isObject(dispatcher)) {
@@ -17435,16 +18333,10 @@ var convertImageToAttachment = (image, explicitMimeType) => {
17435
18333
  }
17436
18334
  }
17437
18335
  if (explicitMimeType) {
17438
- if (image instanceof Uint8Array) {
17439
- return new Attachment({
17440
- data: new Blob([image], { type: explicitMimeType }),
17441
- filename: `image.${getExtensionFromMediaType(explicitMimeType)}`,
17442
- contentType: explicitMimeType
17443
- });
17444
- }
17445
- if (typeof Buffer !== "undefined" && Buffer.isBuffer(image)) {
18336
+ const blob = convertDataToBlob(image, explicitMimeType);
18337
+ if (blob) {
17446
18338
  return new Attachment({
17447
- data: new Blob([image], { type: explicitMimeType }),
18339
+ data: blob,
17448
18340
  filename: `image.${getExtensionFromMediaType(explicitMimeType)}`,
17449
18341
  contentType: explicitMimeType
17450
18342
  });
@@ -17498,6 +18390,25 @@ var convertDataToAttachment = (data, mimeType, filename) => {
17498
18390
  function processAISDKCallInput(params) {
17499
18391
  return processInputAttachmentsSync(params);
17500
18392
  }
18393
+ function processAISDKGenerateImageInput(params) {
18394
+ const prompt = params.prompt;
18395
+ if (!isObject(prompt) || Array.isArray(prompt)) {
18396
+ return processAISDKCallInput(params);
18397
+ }
18398
+ const processedPrompt = { ...prompt };
18399
+ if (Array.isArray(prompt.images)) {
18400
+ processedPrompt.images = prompt.images.map(
18401
+ (image) => convertImageToAttachment(image, "image/png") ?? image
18402
+ );
18403
+ }
18404
+ if (prompt.mask !== void 0) {
18405
+ processedPrompt.mask = convertImageToAttachment(prompt.mask, "image/png") ?? prompt.mask;
18406
+ }
18407
+ return processAISDKCallInput({
18408
+ ...params,
18409
+ prompt: processedPrompt
18410
+ });
18411
+ }
17501
18412
  function processAISDKWorkflowAgentCallInput(params) {
17502
18413
  const processed = processAISDKCallInput(params);
17503
18414
  return {
@@ -17636,6 +18547,12 @@ function prepareAISDKEmbedInput(params, self) {
17636
18547
  metadata: extractMetadataFromEmbedParams(params, self)
17637
18548
  };
17638
18549
  }
18550
+ function prepareAISDKGenerateImageInput(params, self) {
18551
+ return {
18552
+ input: processAISDKGenerateImageInput(params).input,
18553
+ metadata: extractMetadataFromCallParams(params, self)
18554
+ };
18555
+ }
17639
18556
  function prepareAISDKRerankInput(params, self) {
17640
18557
  const { documents, query } = params;
17641
18558
  return {
@@ -19011,6 +19928,57 @@ function processAISDKOutput(output, denyOutputPaths) {
19011
19928
  }
19012
19929
  return normalizeAISDKLoggedOutput(sanitized);
19013
19930
  }
19931
+ function processAISDKGenerateImageOutput(output, denyOutputPaths) {
19932
+ if (!output || typeof output !== "object") {
19933
+ return output;
19934
+ }
19935
+ const summarized = {};
19936
+ for (const field of [
19937
+ "usage",
19938
+ "warnings",
19939
+ "providerMetadata",
19940
+ "experimental_providerMetadata",
19941
+ "responses"
19942
+ ]) {
19943
+ const value = safeSerializableFieldRead(output, field);
19944
+ if (value !== void 0 && isSerializableOutputValue(value)) {
19945
+ summarized[field] = value;
19946
+ }
19947
+ }
19948
+ const images = safeSerializableFieldRead(output, "images");
19949
+ const image = safeSerializableFieldRead(output, "image");
19950
+ const generatedFiles = Array.isArray(images) && images.length > 0 ? images : image !== void 0 ? [image] : [];
19951
+ const loggedOutput = normalizeAISDKLoggedOutput(
19952
+ omit(summarized, denyOutputPaths)
19953
+ );
19954
+ if (generatedFiles.length > 0) {
19955
+ loggedOutput.images = generatedFiles.map(
19956
+ (file, index) => convertAISDKGeneratedFileToAttachment(file, index)
19957
+ );
19958
+ }
19959
+ return loggedOutput;
19960
+ }
19961
+ function convertAISDKGeneratedFileToAttachment(file, index) {
19962
+ if (!file || typeof file !== "object") {
19963
+ return file;
19964
+ }
19965
+ const generatedFile = file;
19966
+ const generatedMediaType = safeSerializableFieldRead(
19967
+ generatedFile,
19968
+ "mediaType"
19969
+ );
19970
+ const mediaType = typeof generatedMediaType === "string" ? generatedMediaType : "application/octet-stream";
19971
+ const data = safeSerializableFieldRead(generatedFile, "base64") ?? safeSerializableFieldRead(generatedFile, "uint8Array");
19972
+ const blob = convertDataToBlob(data, mediaType);
19973
+ if (blob) {
19974
+ return new Attachment({
19975
+ data: blob,
19976
+ filename: `generated_image_${index}.${getExtensionFromMediaType(mediaType)}`,
19977
+ contentType: mediaType
19978
+ });
19979
+ }
19980
+ return file;
19981
+ }
19014
19982
  function processAISDKEmbeddingOutput(output, denyOutputPaths) {
19015
19983
  if (!output || typeof output !== "object") {
19016
19984
  return output;
@@ -19511,118 +20479,22 @@ var claudeAgentSDKChannels = defineChannels(
19511
20479
  var CLAUDE_AGENT_SDK_SKIP_LOCAL_TOOL_HOOKS_OPTION = "__braintrust_skip_local_tool_hooks";
19512
20480
 
19513
20481
  // src/instrumentation/plugins/claude-agent-sdk-local-tool-context.ts
19514
- var LOCAL_TOOL_CONTEXT_ASYNC_ITERATOR_PATCHED = /* @__PURE__ */ Symbol.for(
19515
- "braintrust.claude_agent_sdk.local_tool_context_async_iterator_patched"
19516
- );
19517
- function createLocalToolContextStore() {
19518
- const maybeIsoWithAsyncLocalStorage = isomorph_default;
19519
- if (typeof maybeIsoWithAsyncLocalStorage.newAsyncLocalStorage === "function") {
19520
- return maybeIsoWithAsyncLocalStorage.newAsyncLocalStorage();
19521
- }
19522
- let currentStore;
19523
- return {
19524
- enterWith(store) {
19525
- currentStore = store;
19526
- },
19527
- getStore() {
19528
- return currentStore;
19529
- },
19530
- run(store, callback) {
19531
- const previousStore = currentStore;
19532
- currentStore = store;
19533
- try {
19534
- return callback();
19535
- } finally {
19536
- currentStore = previousStore;
19537
- }
19538
- }
19539
- };
19540
- }
19541
- var localToolContextStore = createLocalToolContextStore();
19542
- var fallbackLocalToolParentResolver;
19543
- function createClaudeLocalToolContext() {
19544
- return {};
19545
- }
19546
- function runWithClaudeLocalToolContext(callback, context) {
19547
- return localToolContextStore.run(
19548
- context ?? createClaudeLocalToolContext(),
19549
- callback
19550
- );
19551
- }
19552
- function ensureClaudeLocalToolContext() {
19553
- const existing = localToolContextStore.getStore();
19554
- if (existing) {
19555
- return existing;
19556
- }
19557
- const created = {};
19558
- localToolContextStore.enterWith(created);
19559
- return created;
19560
- }
19561
- function setClaudeLocalToolParentResolver(resolver) {
19562
- fallbackLocalToolParentResolver = resolver;
19563
- const context = ensureClaudeLocalToolContext();
19564
- if (!context) {
19565
- return;
19566
- }
19567
- context.resolveLocalToolParent = resolver;
19568
- }
19569
- function getClaudeLocalToolParentResolver() {
19570
- return localToolContextStore.getStore()?.resolveLocalToolParent ?? fallbackLocalToolParentResolver;
20482
+ var localToolContextStore = isomorph_default.newAsyncLocalStorage();
20483
+ var localToolParentResolversByToolUseId = /* @__PURE__ */ new Map();
20484
+ function runWithClaudeLocalToolContext(callback, resolver) {
20485
+ return localToolContextStore.run(resolver, callback);
19571
20486
  }
19572
- function isAsyncIterable3(value) {
19573
- return value !== null && typeof value === "object" && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
20487
+ function registerClaudeLocalToolParentResolver(toolUseId, resolver) {
20488
+ localToolParentResolversByToolUseId.set(toolUseId, resolver);
19574
20489
  }
19575
- function bindClaudeLocalToolContextToAsyncIterable(result, localToolContext) {
19576
- if (!isAsyncIterable3(result) || Object.isFrozen(result) || Object.isSealed(result)) {
19577
- return result;
19578
- }
19579
- const stream = result;
19580
- const originalAsyncIterator = stream[Symbol.asyncIterator];
19581
- if (originalAsyncIterator[LOCAL_TOOL_CONTEXT_ASYNC_ITERATOR_PATCHED]) {
19582
- return result;
20490
+ function getClaudeLocalToolParentResolver(toolUseId) {
20491
+ const currentResolver = localToolContextStore.getStore();
20492
+ if (!toolUseId) {
20493
+ return currentResolver;
19583
20494
  }
19584
- const patchedAsyncIterator = function() {
19585
- return runWithClaudeLocalToolContext(() => {
19586
- const iterator = Reflect.apply(originalAsyncIterator, this, []);
19587
- if (!iterator || typeof iterator !== "object") {
19588
- return iterator;
19589
- }
19590
- const patchMethod = (methodName) => {
19591
- const originalMethod = Reflect.get(iterator, methodName);
19592
- if (typeof originalMethod !== "function") {
19593
- return;
19594
- }
19595
- Reflect.set(
19596
- iterator,
19597
- methodName,
19598
- (...args) => runWithClaudeLocalToolContext(
19599
- () => Reflect.apply(
19600
- originalMethod,
19601
- iterator,
19602
- args
19603
- ),
19604
- localToolContext
19605
- )
19606
- );
19607
- };
19608
- patchMethod("next");
19609
- patchMethod("return");
19610
- patchMethod("throw");
19611
- return iterator;
19612
- }, localToolContext);
19613
- };
19614
- Object.defineProperty(
19615
- patchedAsyncIterator,
19616
- LOCAL_TOOL_CONTEXT_ASYNC_ITERATOR_PATCHED,
19617
- {
19618
- configurable: false,
19619
- enumerable: false,
19620
- value: true,
19621
- writable: false
19622
- }
19623
- );
19624
- Reflect.set(stream, Symbol.asyncIterator, patchedAsyncIterator);
19625
- return result;
20495
+ const registeredResolver = localToolParentResolversByToolUseId.get(toolUseId);
20496
+ localToolParentResolversByToolUseId.delete(toolUseId);
20497
+ return currentResolver ?? registeredResolver;
19626
20498
  }
19627
20499
 
19628
20500
  // src/instrumentation/plugins/claude-agent-sdk-local-tool-spans.ts
@@ -19648,7 +20520,7 @@ function wrapLocalClaudeToolHandler(handler, getMetadata) {
19648
20520
  const metadata = getMetadata();
19649
20521
  const rawToolName = metadata.serverName ? `mcp__${metadata.serverName}__${metadata.toolName}` : metadata.toolName;
19650
20522
  const toolUseId = getToolUseIdFromExtra(handlerArgs[1]);
19651
- const localToolParentResolver = getClaudeLocalToolParentResolver();
20523
+ const localToolParentResolver = getClaudeLocalToolParentResolver(toolUseId);
19652
20524
  const spanName = metadata.serverName ? `tool: ${metadata.serverName}/${metadata.toolName}` : `tool: ${metadata.toolName}`;
19653
20525
  const runWithResolvedParent = async () => {
19654
20526
  const parent = toolUseId && localToolParentResolver ? await localToolParentResolver(toolUseId).catch(() => void 0) : void 0;
@@ -20175,6 +21047,7 @@ function createToolTracingHooks(resolveParentSpan, taskIdToToolUseId, toolUseToP
20175
21047
  }
20176
21048
  }
20177
21049
  if (skipLocalToolHooks && (isLocalToolUse(input.tool_name, mcpServers) || localToolHookNames.has(input.tool_name))) {
21050
+ registerClaudeLocalToolParentResolver(toolUseID, resolveParentSpan);
20178
21051
  return {};
20179
21052
  }
20180
21053
  const parsed = parseToolName(input.tool_name);
@@ -20885,7 +21758,7 @@ async function finalizeQuerySpan(state) {
20885
21758
  }
20886
21759
  var ClaudeAgentSDKPlugin = class extends BasePlugin {
20887
21760
  onEnable() {
20888
- this.subscribeToQuery();
21761
+ this.interceptQuery();
20889
21762
  }
20890
21763
  onDisable() {
20891
21764
  for (const unsubscribe of this.unsubscribers) {
@@ -20893,211 +21766,218 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
20893
21766
  }
20894
21767
  this.unsubscribers = [];
20895
21768
  }
20896
- subscribeToQuery() {
20897
- const channel2 = claudeAgentSDKChannels.query.tracingChannel();
20898
- const spans = /* @__PURE__ */ new WeakMap();
20899
- const handlers = {
20900
- start: (event) => {
20901
- const params = event.arguments[0] ?? {};
20902
- const originalPrompt = params.prompt;
20903
- const options = params.options ?? {};
20904
- const promptIsAsyncIterable = isAsyncIterable(originalPrompt);
20905
- let promptStarted = false;
20906
- let capturedPromptMessages;
20907
- let resolvePromptDone;
20908
- const promptDone = new Promise((resolve) => {
20909
- resolvePromptDone = resolve;
20910
- });
20911
- if (promptIsAsyncIterable) {
20912
- capturedPromptMessages = [];
20913
- const promptStream = originalPrompt;
20914
- params.prompt = (async function* () {
20915
- promptStarted = true;
20916
- try {
20917
- for await (const message of promptStream) {
20918
- capturedPromptMessages.push(message);
20919
- yield message;
20920
- }
20921
- } finally {
20922
- resolvePromptDone?.();
21769
+ interceptQuery() {
21770
+ const startQuery = (params) => {
21771
+ const originalPrompt = params.prompt;
21772
+ const options = params.options ?? {};
21773
+ const promptIsAsyncIterable = isAsyncIterable(originalPrompt);
21774
+ let promptStarted = false;
21775
+ let capturedPromptMessages;
21776
+ let resolvePromptDone;
21777
+ const promptDone = new Promise((resolve) => {
21778
+ resolvePromptDone = resolve;
21779
+ });
21780
+ if (promptIsAsyncIterable) {
21781
+ capturedPromptMessages = [];
21782
+ const promptStream = originalPrompt;
21783
+ params.prompt = (async function* () {
21784
+ promptStarted = true;
21785
+ try {
21786
+ for await (const message of promptStream) {
21787
+ capturedPromptMessages.push(message);
21788
+ yield message;
20923
21789
  }
20924
- })();
20925
- }
20926
- const span = startSpan(
20927
- withSpanInstrumentationName(
20928
- {
20929
- name: "Claude Agent",
20930
- spanAttributes: {
20931
- type: "task" /* TASK */
20932
- }
20933
- },
20934
- INSTRUMENTATION_NAMES.CLAUDE_AGENT_SDK
20935
- )
20936
- );
20937
- const startTime = getCurrentUnixTimestamp();
20938
- try {
20939
- span.log({
20940
- input: typeof originalPrompt === "string" ? originalPrompt : promptIsAsyncIterable ? void 0 : originalPrompt !== void 0 ? String(originalPrompt) : void 0,
20941
- metadata: filterSerializableOptions(options)
20942
- });
20943
- } catch (error) {
20944
- console.error("Error extracting input for Claude Agent SDK:", error);
20945
- }
20946
- const activeToolSpans = /* @__PURE__ */ new Map();
20947
- const activeLlmSpansByParentToolUse = /* @__PURE__ */ new Map();
20948
- const conversationHistoryByParentKey = /* @__PURE__ */ new Map();
20949
- const subAgentSpans = /* @__PURE__ */ new Map();
20950
- const endedSubAgentSpans = /* @__PURE__ */ new Set();
20951
- const toolUseToParent = /* @__PURE__ */ new Map();
20952
- const latestLlmParentBySubAgentToolUse = /* @__PURE__ */ new Map();
20953
- const latestRootLlmParentRef = {
20954
- value: void 0
20955
- };
20956
- const subAgentDetailsByToolUseId = /* @__PURE__ */ new Map();
20957
- const taskIdToToolUseId = /* @__PURE__ */ new Map();
20958
- const promptMessagesByParentKey = /* @__PURE__ */ new Map();
20959
- const promptSourcePriorityByParentKey = /* @__PURE__ */ new Map();
20960
- const localToolContext = createClaudeLocalToolContext();
20961
- const { hasLocalToolHandlers, localToolHookNames } = prepareLocalToolHandlersInMcpServers(options.mcpServers);
20962
- const skipLocalToolHooks = options[CLAUDE_AGENT_SDK_SKIP_LOCAL_TOOL_HOOKS_OPTION] === true || hasLocalToolHandlers;
20963
- const resolveToolUseParentSpan = async (toolUseID, context) => {
20964
- const trackedParentToolUseId = toolUseToParent.get(toolUseID);
20965
- const parentToolUseId = trackedParentToolUseId ?? (context?.agentId ? taskIdToToolUseId.get(context.agentId) ?? null : null);
20966
- const parentKey = llmParentKey(parentToolUseId);
20967
- const activeLlmSpan = activeLlmSpansByParentToolUse.get(parentKey);
20968
- const latestLlmParent = parentToolUseId ? latestLlmParentBySubAgentToolUse.get(parentToolUseId) : latestRootLlmParentRef.value;
20969
- if (!activeLlmSpan && !latestLlmParent) {
20970
- await ensureActiveLlmSpanForParentToolUse(
20971
- span,
20972
- activeLlmSpansByParentToolUse,
20973
- subAgentDetailsByToolUseId,
20974
- activeToolSpans,
20975
- subAgentSpans,
20976
- parentToolUseId,
20977
- getCurrentUnixTimestamp()
20978
- );
20979
- }
20980
- if (parentToolUseId) {
20981
- const subAgentSpan = await ensureSubAgentSpan(
20982
- subAgentDetailsByToolUseId,
20983
- span,
20984
- activeToolSpans,
20985
- subAgentSpans,
20986
- parentToolUseId
20987
- );
20988
- return subAgentSpan.export();
21790
+ } finally {
21791
+ resolvePromptDone?.();
20989
21792
  }
20990
- return span.export();
20991
- };
20992
- localToolContext.resolveLocalToolParent = resolveToolUseParentSpan;
20993
- setClaudeLocalToolParentResolver(resolveToolUseParentSpan);
20994
- const optionsWithHooks = injectTracingHooks(
20995
- options,
20996
- resolveToolUseParentSpan,
20997
- taskIdToToolUseId,
20998
- toolUseToParent,
20999
- activeToolSpans,
21000
- localToolHookNames,
21001
- skipLocalToolHooks,
21002
- subAgentDetailsByToolUseId,
21003
- subAgentSpans,
21004
- endedSubAgentSpans
21005
- );
21006
- params.options = optionsWithHooks;
21007
- event.arguments[0] = params;
21008
- spans.set(event, {
21009
- activeLlmSpansByParentToolUse,
21010
- activePartialMessageIdByParentKey: /* @__PURE__ */ new Map(),
21011
- activeToolSpans,
21012
- conversationHistoryByParentKey,
21013
- capturedPromptMessages,
21014
- currentMessageId: void 0,
21015
- currentMessageStartTime: startTime,
21016
- currentMessages: [],
21017
- endedSubAgentSpans,
21018
- finalOutputUsageMessageIds: /* @__PURE__ */ new Set(),
21019
- finalResults: [],
21020
- options: optionsWithHooks,
21021
- originalPrompt,
21022
- processing: Promise.resolve(),
21023
- promptDone,
21024
- promptMessagesByParentKey,
21025
- promptStarted: () => promptStarted,
21026
- promptSourcePriorityByParentKey,
21027
- span,
21028
- subAgentDetailsByToolUseId,
21029
- subAgentSpans,
21030
- taskIdToToolUseId,
21031
- latestLlmParentBySubAgentToolUse,
21032
- latestRootLlmParentRef,
21033
- toolUseToParent,
21034
- usageByMessageId: /* @__PURE__ */ new Map(),
21035
- localToolContext
21793
+ })();
21794
+ }
21795
+ const span = startSpan(
21796
+ withSpanInstrumentationName(
21797
+ {
21798
+ name: "Claude Agent",
21799
+ spanAttributes: {
21800
+ type: "task" /* TASK */
21801
+ }
21802
+ },
21803
+ INSTRUMENTATION_NAMES.CLAUDE_AGENT_SDK
21804
+ )
21805
+ );
21806
+ const startTime = getCurrentUnixTimestamp();
21807
+ try {
21808
+ span.log({
21809
+ input: typeof originalPrompt === "string" ? originalPrompt : promptIsAsyncIterable ? void 0 : originalPrompt !== void 0 ? String(originalPrompt) : void 0,
21810
+ metadata: filterSerializableOptions(options)
21036
21811
  });
21037
- },
21038
- end: (event) => {
21039
- const state = spans.get(event);
21040
- if (!state) {
21041
- return;
21042
- }
21043
- const eventResult = bindClaudeLocalToolContextToAsyncIterable(
21044
- event.result,
21045
- state.localToolContext
21046
- );
21047
- if (eventResult === void 0) {
21048
- state.span.end();
21049
- spans.delete(event);
21050
- return;
21051
- }
21052
- if (isAsyncIterable(eventResult)) {
21053
- patchStreamIfNeeded(eventResult, {
21054
- onChunk: (message) => {
21055
- maybeTrackToolUseContext(state, message);
21056
- state.processing = state.processing.then(() => handleStreamMessage(state, message)).catch((error) => {
21057
- console.error(
21058
- "Error processing Claude Agent SDK stream chunk:",
21059
- error
21060
- );
21061
- });
21062
- },
21063
- onComplete: () => state.processing.then(() => finalizeQuerySpan(state)).finally(() => {
21064
- spans.delete(event);
21065
- }),
21066
- onError: (error) => state.processing.then(() => {
21067
- state.span.log({
21068
- error: error.message
21069
- });
21070
- }).then(() => finalizeQuerySpan(state)).finally(() => {
21071
- spans.delete(event);
21072
- })
21073
- });
21074
- return;
21075
- }
21076
- try {
21077
- state.span.log({ output: eventResult });
21078
- } catch (error) {
21079
- console.error("Error extracting output for Claude Agent SDK:", error);
21080
- } finally {
21081
- state.span.end();
21082
- spans.delete(event);
21812
+ } catch (error) {
21813
+ console.error("Error extracting input for Claude Agent SDK:", error);
21814
+ }
21815
+ const activeToolSpans = /* @__PURE__ */ new Map();
21816
+ const activeLlmSpansByParentToolUse = /* @__PURE__ */ new Map();
21817
+ const conversationHistoryByParentKey = /* @__PURE__ */ new Map();
21818
+ const subAgentSpans = /* @__PURE__ */ new Map();
21819
+ const endedSubAgentSpans = /* @__PURE__ */ new Set();
21820
+ const toolUseToParent = /* @__PURE__ */ new Map();
21821
+ const latestLlmParentBySubAgentToolUse = /* @__PURE__ */ new Map();
21822
+ const latestRootLlmParentRef = {
21823
+ value: void 0
21824
+ };
21825
+ const subAgentDetailsByToolUseId = /* @__PURE__ */ new Map();
21826
+ const taskIdToToolUseId = /* @__PURE__ */ new Map();
21827
+ const promptMessagesByParentKey = /* @__PURE__ */ new Map();
21828
+ const promptSourcePriorityByParentKey = /* @__PURE__ */ new Map();
21829
+ const { hasLocalToolHandlers, localToolHookNames } = prepareLocalToolHandlersInMcpServers(options.mcpServers);
21830
+ const skipLocalToolHooks = options[CLAUDE_AGENT_SDK_SKIP_LOCAL_TOOL_HOOKS_OPTION] === true || hasLocalToolHandlers;
21831
+ const resolveToolUseParentSpan = async (toolUseID, context) => {
21832
+ const trackedParentToolUseId = toolUseToParent.get(toolUseID);
21833
+ const parentToolUseId = trackedParentToolUseId ?? (context?.agentId ? taskIdToToolUseId.get(context.agentId) ?? null : null);
21834
+ const parentKey = llmParentKey(parentToolUseId);
21835
+ const activeLlmSpan = activeLlmSpansByParentToolUse.get(parentKey);
21836
+ const latestLlmParent = parentToolUseId ? latestLlmParentBySubAgentToolUse.get(parentToolUseId) : latestRootLlmParentRef.value;
21837
+ if (!activeLlmSpan && !latestLlmParent) {
21838
+ await ensureActiveLlmSpanForParentToolUse(
21839
+ span,
21840
+ activeLlmSpansByParentToolUse,
21841
+ subAgentDetailsByToolUseId,
21842
+ activeToolSpans,
21843
+ subAgentSpans,
21844
+ parentToolUseId,
21845
+ getCurrentUnixTimestamp()
21846
+ );
21083
21847
  }
21084
- },
21085
- error: (event) => {
21086
- const state = spans.get(event);
21087
- if (!state || !event.error) {
21088
- return;
21848
+ if (parentToolUseId) {
21849
+ const subAgentSpan = await ensureSubAgentSpan(
21850
+ subAgentDetailsByToolUseId,
21851
+ span,
21852
+ activeToolSpans,
21853
+ subAgentSpans,
21854
+ parentToolUseId
21855
+ );
21856
+ return subAgentSpan.export();
21089
21857
  }
21090
- state.span.log({
21091
- error: event.error.message
21858
+ return span.export();
21859
+ };
21860
+ const optionsWithHooks = injectTracingHooks(
21861
+ options,
21862
+ resolveToolUseParentSpan,
21863
+ taskIdToToolUseId,
21864
+ toolUseToParent,
21865
+ activeToolSpans,
21866
+ localToolHookNames,
21867
+ skipLocalToolHooks,
21868
+ subAgentDetailsByToolUseId,
21869
+ subAgentSpans,
21870
+ endedSubAgentSpans
21871
+ );
21872
+ params.options = optionsWithHooks;
21873
+ return {
21874
+ activeLlmSpansByParentToolUse,
21875
+ activePartialMessageIdByParentKey: /* @__PURE__ */ new Map(),
21876
+ activeToolSpans,
21877
+ conversationHistoryByParentKey,
21878
+ capturedPromptMessages,
21879
+ currentMessageId: void 0,
21880
+ currentMessageStartTime: startTime,
21881
+ currentMessages: [],
21882
+ endedSubAgentSpans,
21883
+ finalOutputUsageMessageIds: /* @__PURE__ */ new Set(),
21884
+ finalResults: [],
21885
+ options: optionsWithHooks,
21886
+ originalPrompt,
21887
+ processing: Promise.resolve(),
21888
+ promptDone,
21889
+ promptMessagesByParentKey,
21890
+ promptStarted: () => promptStarted,
21891
+ promptSourcePriorityByParentKey,
21892
+ span,
21893
+ subAgentDetailsByToolUseId,
21894
+ subAgentSpans,
21895
+ taskIdToToolUseId,
21896
+ latestLlmParentBySubAgentToolUse,
21897
+ latestRootLlmParentRef,
21898
+ toolUseToParent,
21899
+ usageByMessageId: /* @__PURE__ */ new Map(),
21900
+ localToolParentResolver: resolveToolUseParentSpan
21901
+ };
21902
+ };
21903
+ const finishQuery = (state, result) => {
21904
+ if (isAsyncIterable(result)) {
21905
+ patchStreamIfNeeded(result, {
21906
+ aroundNext: (callback) => runWithClaudeLocalToolContext(
21907
+ callback,
21908
+ state.localToolParentResolver
21909
+ ),
21910
+ onChunk: (message) => {
21911
+ maybeTrackToolUseContext(state, message);
21912
+ state.processing = state.processing.then(() => handleStreamMessage(state, message)).catch((error) => {
21913
+ console.error(
21914
+ "Error processing Claude Agent SDK stream chunk:",
21915
+ error
21916
+ );
21917
+ });
21918
+ },
21919
+ onComplete: () => state.processing.then(() => finalizeQuerySpan(state)),
21920
+ onError: (error) => state.processing.then(() => {
21921
+ state.span.log({ error: error.message });
21922
+ }).then(() => finalizeQuerySpan(state))
21092
21923
  });
21924
+ return;
21925
+ }
21926
+ try {
21927
+ state.span.log({ output: result });
21928
+ } catch (error) {
21929
+ console.error("Error extracting output for Claude Agent SDK:", error);
21930
+ } finally {
21093
21931
  state.span.end();
21094
- spans.delete(event);
21095
21932
  }
21096
21933
  };
21097
- channel2.subscribe(handlers);
21098
- this.unsubscribers.push(() => {
21099
- channel2.unsubscribe(handlers);
21100
- });
21934
+ this.unsubscribers.push(
21935
+ claudeAgentSDKChannels.query.intercept((target, thisArg, args) => {
21936
+ let state;
21937
+ try {
21938
+ args[0] ??= {};
21939
+ state = startQuery(args[0]);
21940
+ } catch (error) {
21941
+ debugLogger.error(
21942
+ "Error starting Claude Agent SDK instrumentation:",
21943
+ error
21944
+ );
21945
+ }
21946
+ const invokeTarget = () => Reflect.apply(target, thisArg, args);
21947
+ try {
21948
+ const result = state ? runWithClaudeLocalToolContext(
21949
+ invokeTarget,
21950
+ state.localToolParentResolver
21951
+ ) : invokeTarget();
21952
+ if (state) {
21953
+ try {
21954
+ finishQuery(state, result);
21955
+ } catch (error) {
21956
+ debugLogger.error(
21957
+ "Error finalizing Claude Agent SDK instrumentation:",
21958
+ error
21959
+ );
21960
+ }
21961
+ }
21962
+ return result;
21963
+ } catch (error) {
21964
+ if (state) {
21965
+ try {
21966
+ state.span.log({
21967
+ error: error instanceof Error ? error.message : String(error)
21968
+ });
21969
+ state.span.end();
21970
+ } catch (instrumentationError) {
21971
+ debugLogger.error(
21972
+ "Error handling Claude Agent SDK instrumentation failure:",
21973
+ instrumentationError
21974
+ );
21975
+ }
21976
+ }
21977
+ throw error;
21978
+ }
21979
+ })
21980
+ );
21101
21981
  }
21102
21982
  };
21103
21983
 
@@ -25211,7 +26091,7 @@ function patchOpenRouterCallModelResult(args) {
25211
26091
  span,
25212
26092
  () => originalMethod.apply(resultLike, args2)
25213
26093
  );
25214
- if (!isAsyncIterable4(stream)) {
26094
+ if (!isAsyncIterable3(stream)) {
25215
26095
  return stream;
25216
26096
  }
25217
26097
  return wrapAsyncIterableWithSpan({
@@ -25406,7 +26286,7 @@ function wrapAsyncIterableWithSpan(args) {
25406
26286
  }
25407
26287
  };
25408
26288
  }
25409
- function isAsyncIterable4(value) {
26289
+ function isAsyncIterable3(value) {
25410
26290
  return !!value && (typeof value === "object" || typeof value === "function") && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
25411
26291
  }
25412
26292
  function normalizeError(error) {
@@ -26284,7 +27164,7 @@ function patchOpenRouterCallModelResult2(args) {
26284
27164
  span,
26285
27165
  () => originalMethod.apply(resultLike, args2)
26286
27166
  );
26287
- if (!isAsyncIterable5(stream)) {
27167
+ if (!isAsyncIterable4(stream)) {
26288
27168
  return stream;
26289
27169
  }
26290
27170
  return wrapAsyncIterableWithSpan2({
@@ -26479,7 +27359,7 @@ function wrapAsyncIterableWithSpan2(args) {
26479
27359
  }
26480
27360
  };
26481
27361
  }
26482
- function isAsyncIterable5(value) {
27362
+ function isAsyncIterable4(value) {
26483
27363
  return !!value && (typeof value === "object" || typeof value === "function") && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
26484
27364
  }
26485
27365
  function normalizeError2(error) {
@@ -32471,12 +33351,14 @@ function getMetricsFromResponse(response) {
32471
33351
  continue;
32472
33352
  }
32473
33353
  const inputTokenDetails = usageMetadata.input_token_details;
33354
+ const outputTokenDetails = usageMetadata.output_token_details;
32474
33355
  return normalizeTokenMetrics({
32475
33356
  total_tokens: usageMetadata.total_tokens,
32476
33357
  prompt_tokens: usageMetadata.input_tokens,
32477
33358
  completion_tokens: usageMetadata.output_tokens,
32478
33359
  prompt_cache_creation_tokens: isRecord(inputTokenDetails) ? inputTokenDetails.cache_creation : void 0,
32479
- prompt_cached_tokens: isRecord(inputTokenDetails) ? inputTokenDetails.cache_read : void 0
33360
+ prompt_cached_tokens: isRecord(inputTokenDetails) ? inputTokenDetails.cache_read : void 0,
33361
+ completion_reasoning_tokens: isRecord(outputTokenDetails) ? outputTokenDetails.reasoning : void 0
32480
33362
  });
32481
33363
  }
32482
33364
  const llmOutput = response.llmOutput || {};
@@ -33063,6 +33945,9 @@ var piCodingAgentChannels = defineChannels(
33063
33945
  // src/instrumentation/plugins/pi-coding-agent-plugin.ts
33064
33946
  var piAgentPatchStates = /* @__PURE__ */ new WeakMap();
33065
33947
  var piAgentEventSubscriptions = /* @__PURE__ */ new WeakSet();
33948
+ var PI_TOOL_EXECUTE_WRAPPED = /* @__PURE__ */ Symbol.for(
33949
+ "braintrust.pi_coding_agent.tool_execute_wrapped"
33950
+ );
33066
33951
  var piPromptContextStore;
33067
33952
  var PiCodingAgentPlugin = class extends BasePlugin {
33068
33953
  activePromptStates = /* @__PURE__ */ new Set();
@@ -33143,6 +34028,7 @@ function startPiPromptRun(event, onFinalize) {
33143
34028
  return void 0;
33144
34029
  }
33145
34030
  installPiAgentInstrumentation(agent);
34031
+ wrapPiToolExecutors(agent.state?.tools);
33146
34032
  const metadata = {
33147
34033
  ...extractSessionMetadata(session),
33148
34034
  ...extractPromptOptionsMetadata(event.arguments[1]),
@@ -33187,7 +34073,7 @@ function extractSession(event) {
33187
34073
  return isObject(candidate) && typeof candidate.prompt === "function" ? candidate : void 0;
33188
34074
  }
33189
34075
  function isPiAgent(value) {
33190
- return isObject(value) && typeof value.streamFn === "function" && typeof value.subscribe === "function";
34076
+ return isObject(value) && (typeof value.streamFunction === "function" || typeof value.streamFn === "function") && typeof value.subscribe === "function";
33191
34077
  }
33192
34078
  function promptContextStore() {
33193
34079
  piPromptContextStore ??= isomorph_default.newAsyncLocalStorage();
@@ -33197,17 +34083,21 @@ function currentPiPromptState() {
33197
34083
  return promptContextStore().getStore();
33198
34084
  }
33199
34085
  function installPiAgentInstrumentation(agent) {
34086
+ const property = typeof agent.streamFunction === "function" ? "streamFunction" : "streamFn";
34087
+ const streamFunction = agent[property];
33200
34088
  const existing = piAgentPatchStates.get(agent);
33201
- if (!existing || agent.streamFn !== existing.wrappedStreamFn) {
34089
+ if (streamFunction && (!existing || existing.property !== property || streamFunction !== existing.wrappedStreamFunction)) {
33202
34090
  const patchState = {
33203
- originalStreamFn: agent.streamFn,
33204
- wrappedStreamFn: agent.streamFn
34091
+ originalStreamFunction: streamFunction,
34092
+ property,
34093
+ wrappedStreamFunction: streamFunction
33205
34094
  };
33206
- patchState.wrappedStreamFn = makeInstrumentedStreamFn(
34095
+ patchState.wrappedStreamFunction = makeInstrumentedStreamFunction(
33207
34096
  agent,
33208
- patchState.originalStreamFn
34097
+ patchState.originalStreamFunction,
34098
+ property
33209
34099
  );
33210
- agent.streamFn = patchState.wrappedStreamFn;
34100
+ agent[property] = patchState.wrappedStreamFunction;
33211
34101
  piAgentPatchStates.set(agent, patchState);
33212
34102
  }
33213
34103
  if (piAgentEventSubscriptions.has(agent)) {
@@ -33232,14 +34122,21 @@ function installPiAgentInstrumentation(agent) {
33232
34122
  logInstrumentationError4("Pi Coding Agent event subscription", error);
33233
34123
  }
33234
34124
  }
33235
- function makeInstrumentedStreamFn(agent, originalStreamFn) {
33236
- return async function instrumentedPiStreamFn(model, context, options) {
33237
- const invokeOriginal = () => Reflect.apply(originalStreamFn, this, [model, context, options]);
34125
+ function makeInstrumentedStreamFunction(agent, originalStreamFunction, property) {
34126
+ return async function instrumentedPiStreamFunction(model, context, options) {
34127
+ const invokeOriginal = () => Reflect.apply(originalStreamFunction, this, [model, context, options]);
33238
34128
  const state = currentPiPromptState();
33239
34129
  if (!state || state.agent !== agent || state.finalized) {
33240
34130
  return invokeOriginal();
33241
34131
  }
33242
- const llmState = await startPiLlmSpan(state, model, context, options);
34132
+ wrapPiToolExecutors(context.tools);
34133
+ const llmState = await startPiLlmSpan(
34134
+ state,
34135
+ model,
34136
+ context,
34137
+ property,
34138
+ options
34139
+ );
33243
34140
  try {
33244
34141
  const stream = await runWithAutoInstrumentationSuppressed(invokeOriginal);
33245
34142
  return patchAssistantMessageStream(stream, state, llmState);
@@ -33249,12 +34146,39 @@ function makeInstrumentedStreamFn(agent, originalStreamFn) {
33249
34146
  }
33250
34147
  };
33251
34148
  }
33252
- async function startPiLlmSpan(state, model, context, options) {
34149
+ function wrapPiToolExecutors(tools) {
34150
+ if (!tools) {
34151
+ return;
34152
+ }
34153
+ for (const tool of tools) {
34154
+ try {
34155
+ const execute = tool.execute;
34156
+ if (typeof execute !== "function" || execute[PI_TOOL_EXECUTE_WRAPPED]) {
34157
+ continue;
34158
+ }
34159
+ const wrappedExecute = function(...args) {
34160
+ return runWithAutoInstrumentationAllowed(
34161
+ () => Reflect.apply(execute, this, args)
34162
+ );
34163
+ };
34164
+ Object.defineProperty(wrappedExecute, PI_TOOL_EXECUTE_WRAPPED, {
34165
+ configurable: false,
34166
+ enumerable: false,
34167
+ value: true,
34168
+ writable: false
34169
+ });
34170
+ tool.execute = wrappedExecute;
34171
+ } catch (error) {
34172
+ logInstrumentationError4("Pi Coding Agent tool wrapping", error);
34173
+ }
34174
+ }
34175
+ }
34176
+ async function startPiLlmSpan(state, model, context, property, options) {
33253
34177
  const metadata = {
33254
34178
  ...extractModelMetadata2(model),
33255
34179
  ...extractStreamOptionsMetadata(options),
33256
34180
  ...extractToolMetadata(context.tools),
33257
- "pi_coding_agent.operation": "agent.streamFn"
34181
+ "pi_coding_agent.operation": `agent.${property}`
33258
34182
  };
33259
34183
  const span = startSpan(
33260
34184
  withSpanInstrumentationName(
@@ -33420,35 +34344,26 @@ async function startPiToolSpan(state, event) {
33420
34344
  if (!event.toolCallId || state.activeToolSpans.has(event.toolCallId)) {
33421
34345
  return;
33422
34346
  }
33423
- const restoreAutoInstrumentation = enterAutoInstrumentationAllowed();
33424
34347
  const metadata = {
33425
34348
  "gen_ai.tool.call.id": event.toolCallId,
33426
34349
  "gen_ai.tool.name": event.toolName,
33427
34350
  "pi_coding_agent.tool.name": event.toolName
33428
34351
  };
33429
- try {
33430
- const span = startSpan(
33431
- withSpanInstrumentationName(
33432
- {
33433
- event: {
33434
- input: event.args,
33435
- metadata
33436
- },
33437
- name: event.toolName || "tool",
33438
- parent: await state.span.export(),
33439
- spanAttributes: { type: "tool" /* TOOL */ }
34352
+ const span = startSpan(
34353
+ withSpanInstrumentationName(
34354
+ {
34355
+ event: {
34356
+ input: event.args,
34357
+ metadata
33440
34358
  },
33441
- INSTRUMENTATION_NAMES.PI_CODING_AGENT
33442
- )
33443
- );
33444
- state.activeToolSpans.set(event.toolCallId, {
33445
- restoreAutoInstrumentation,
33446
- span
33447
- });
33448
- } catch (error) {
33449
- restoreAutoInstrumentation();
33450
- throw error;
33451
- }
34359
+ name: event.toolName || "tool",
34360
+ parent: await state.span.export(),
34361
+ spanAttributes: { type: "tool" /* TOOL */ }
34362
+ },
34363
+ INSTRUMENTATION_NAMES.PI_CODING_AGENT
34364
+ )
34365
+ );
34366
+ state.activeToolSpans.set(event.toolCallId, { span });
33452
34367
  }
33453
34368
  function finishPiToolSpan(state, event) {
33454
34369
  const toolState = state.activeToolSpans.get(event.toolCallId);
@@ -33469,11 +34384,7 @@ function finishPiToolSpan(state, event) {
33469
34384
  output: event.result
33470
34385
  });
33471
34386
  } finally {
33472
- try {
33473
- toolState.span.end();
33474
- } finally {
33475
- toolState.restoreAutoInstrumentation?.();
33476
- }
34387
+ toolState.span.end();
33477
34388
  }
33478
34389
  }
33479
34390
  function finishPiPromptRun(state, error) {
@@ -33544,14 +34455,10 @@ function finishPiLlmSpan(promptState, llmState, message, error) {
33544
34455
  }
33545
34456
  function finishOpenToolSpans(state, error) {
33546
34457
  for (const [, toolState] of state.activeToolSpans) {
33547
- try {
33548
- safeLog4(toolState.span, {
33549
- error: error ? toLoggedError(error) : "Pi tool did not complete"
33550
- });
33551
- toolState.span.end();
33552
- } finally {
33553
- toolState.restoreAutoInstrumentation?.();
33554
- }
34458
+ safeLog4(toolState.span, {
34459
+ error: error ? toLoggedError(error) : "Pi tool did not complete"
34460
+ });
34461
+ toolState.span.end();
33555
34462
  }
33556
34463
  state.activeToolSpans.clear();
33557
34464
  }
@@ -33870,12 +34777,12 @@ var MAX_STRANDS_STRING_ATTACHMENT_CACHE_ENTRIES = 32;
33870
34777
  var StrandsAgentSDKPlugin = class extends BasePlugin {
33871
34778
  activeChildParents = /* @__PURE__ */ new WeakMap();
33872
34779
  onEnable() {
33873
- this.subscribeToAgentStream();
33874
- this.subscribeToMultiAgentStream(
34780
+ this.interceptAgentStream();
34781
+ this.interceptMultiAgentStream(
33875
34782
  strandsAgentSDKChannels.graphStream,
33876
34783
  "Graph.stream"
33877
34784
  );
33878
- this.subscribeToMultiAgentStream(
34785
+ this.interceptMultiAgentStream(
33879
34786
  strandsAgentSDKChannels.swarmStream,
33880
34787
  "Swarm.stream"
33881
34788
  );
@@ -33886,122 +34793,92 @@ var StrandsAgentSDKPlugin = class extends BasePlugin {
33886
34793
  }
33887
34794
  this.unsubscribers = [];
33888
34795
  }
33889
- subscribeToAgentStream() {
33890
- const channel2 = strandsAgentSDKChannels.agentStream.tracingChannel();
33891
- const states = /* @__PURE__ */ new WeakMap();
33892
- const unbindAutoInstrumentationSuppression = bindAutoInstrumentationSuppressionToStart(channel2);
33893
- const handlers = {
33894
- start: (event) => {
33895
- const state = startAgentStream(event, this.activeChildParents);
33896
- if (state) {
33897
- states.set(event, state);
33898
- }
33899
- },
33900
- end: (event) => {
33901
- const state = states.get(event);
33902
- if (!state) {
33903
- return;
33904
- }
33905
- const result = event.result;
33906
- if (isAsyncIterable(result)) {
33907
- patchStreamIfNeeded(result, {
33908
- aroundNext: (callback) => runWithAutoInstrumentationSuppressed(callback),
33909
- onChunk: (chunk) => handleAgentStreamEvent(state, chunk),
33910
- onComplete: () => {
33911
- finalizeAgentStream(state);
33912
- states.delete(event);
33913
- },
33914
- onError: (error) => {
33915
- finalizeAgentStream(state, error);
33916
- states.delete(event);
33917
- }
33918
- });
33919
- return;
33920
- }
33921
- finalizeAgentStream(state, void 0, result);
33922
- states.delete(event);
33923
- },
33924
- error: (event) => {
33925
- const state = states.get(event);
33926
- if (!state || !event.error) {
33927
- return;
33928
- }
33929
- finalizeAgentStream(state, event.error);
33930
- states.delete(event);
33931
- }
33932
- };
33933
- channel2.subscribe(handlers);
33934
- this.unsubscribers.push(() => {
33935
- unbindAutoInstrumentationSuppression?.();
33936
- channel2.unsubscribe(handlers);
33937
- });
34796
+ interceptAgentStream() {
34797
+ this.unsubscribers.push(
34798
+ strandsAgentSDKChannels.agentStream.intercept(
34799
+ (target, thisArg, args, additional) => instrumentStrandsStreamInvocation({
34800
+ finalize: finalizeAgentStream,
34801
+ handleChunk: handleAgentStreamEvent,
34802
+ invoke: () => Reflect.apply(target, thisArg, args),
34803
+ name: "Strands Agent SDK",
34804
+ start: () => startAgentStream(
34805
+ args[0],
34806
+ extractAgent(additional.agent, thisArg),
34807
+ this.activeChildParents
34808
+ )
34809
+ })
34810
+ )
34811
+ );
33938
34812
  }
33939
- subscribeToMultiAgentStream(channel2, operation) {
33940
- const tracingChannel = channel2.tracingChannel();
33941
- const states = /* @__PURE__ */ new WeakMap();
33942
- const unbindAutoInstrumentationSuppression = bindAutoInstrumentationSuppressionToStart(tracingChannel);
33943
- const handlers = {
33944
- start: (event) => {
33945
- const state = startMultiAgentStream(
33946
- event,
33947
- operation,
33948
- this.activeChildParents
33949
- );
33950
- if (state) {
33951
- states.set(event, state);
33952
- }
33953
- },
33954
- end: (event) => {
33955
- const state = states.get(event);
33956
- if (!state) {
33957
- return;
33958
- }
33959
- const result = event.result;
33960
- if (isAsyncIterable(result)) {
33961
- patchStreamIfNeeded(result, {
33962
- aroundNext: (callback) => runWithAutoInstrumentationSuppressed(callback),
33963
- onChunk: (chunk) => handleMultiAgentStreamEvent(
33964
- state,
33965
- chunk,
33966
- this.activeChildParents
33967
- ),
33968
- onComplete: () => {
33969
- finalizeMultiAgentStream(state, this.activeChildParents);
33970
- states.delete(event);
33971
- },
33972
- onError: (error) => {
33973
- finalizeMultiAgentStream(state, this.activeChildParents, error);
33974
- states.delete(event);
33975
- }
33976
- });
33977
- return;
33978
- }
33979
- finalizeMultiAgentStream(
33980
- state,
33981
- this.activeChildParents,
33982
- void 0,
33983
- result
34813
+ interceptMultiAgentStream(channel2, operation) {
34814
+ this.unsubscribers.push(
34815
+ channel2.intercept(
34816
+ (target, thisArg, args, additional) => instrumentStrandsStreamInvocation({
34817
+ finalize: (state, error, output) => finalizeMultiAgentStream(
34818
+ state,
34819
+ this.activeChildParents,
34820
+ error,
34821
+ output
34822
+ ),
34823
+ handleChunk: (state, chunk) => handleMultiAgentStreamEvent(state, chunk, this.activeChildParents),
34824
+ invoke: () => Reflect.apply(target, thisArg, args),
34825
+ name: "Strands multi-agent",
34826
+ start: () => startMultiAgentStream(
34827
+ args[0],
34828
+ extractOrchestrator(additional.orchestrator, thisArg),
34829
+ operation,
34830
+ this.activeChildParents
34831
+ )
34832
+ })
34833
+ )
34834
+ );
34835
+ }
34836
+ };
34837
+ function instrumentStrandsStreamInvocation(options) {
34838
+ let state;
34839
+ try {
34840
+ state = options.start();
34841
+ } catch (error) {
34842
+ debugLogger.error(`Error starting ${options.name} instrumentation:`, error);
34843
+ }
34844
+ let result;
34845
+ try {
34846
+ result = runWithAutoInstrumentationSuppressed(options.invoke);
34847
+ } catch (error) {
34848
+ if (state) {
34849
+ try {
34850
+ options.finalize(state, error);
34851
+ } catch (instrumentationError) {
34852
+ debugLogger.error(
34853
+ `Error handling ${options.name} instrumentation failure:`,
34854
+ instrumentationError
33984
34855
  );
33985
- states.delete(event);
33986
- },
33987
- error: (event) => {
33988
- const state = states.get(event);
33989
- if (!state || !event.error) {
33990
- return;
33991
- }
33992
- finalizeMultiAgentStream(state, this.activeChildParents, event.error);
33993
- states.delete(event);
33994
34856
  }
33995
- };
33996
- tracingChannel.subscribe(handlers);
33997
- this.unsubscribers.push(() => {
33998
- unbindAutoInstrumentationSuppression?.();
33999
- tracingChannel.unsubscribe(handlers);
34000
- });
34857
+ }
34858
+ throw error;
34001
34859
  }
34002
- };
34003
- function startAgentStream(event, activeChildParents) {
34004
- const agent = extractAgent(event);
34860
+ if (state) {
34861
+ try {
34862
+ if (isAsyncIterable(result)) {
34863
+ patchStreamIfNeeded(result, {
34864
+ aroundNext: (callback) => runWithAutoInstrumentationSuppressed(callback),
34865
+ onChunk: (chunk) => options.handleChunk(state, chunk),
34866
+ onComplete: () => options.finalize(state),
34867
+ onError: (error) => options.finalize(state, error)
34868
+ });
34869
+ } else {
34870
+ options.finalize(state, void 0, result);
34871
+ }
34872
+ } catch (error) {
34873
+ debugLogger.error(
34874
+ `Error finalizing ${options.name} instrumentation:`,
34875
+ error
34876
+ );
34877
+ }
34878
+ }
34879
+ return result;
34880
+ }
34881
+ function startAgentStream(input, agent, activeChildParents) {
34005
34882
  const model = agent?.model;
34006
34883
  const metadata = {
34007
34884
  ...extractAgentMetadata2(agent),
@@ -34011,17 +34888,14 @@ function startAgentStream(event, activeChildParents) {
34011
34888
  };
34012
34889
  const parentSpan = agent ? getOnlyChildParent(activeChildParents, agent) : void 0;
34013
34890
  const attachmentCache = createStrandsAttachmentCache();
34014
- const input = processStrandsInputAttachments(
34015
- event.arguments[0],
34016
- attachmentCache
34017
- );
34891
+ const processedInput = processStrandsInputAttachments(input, attachmentCache);
34018
34892
  const span = parentSpan ? withCurrent(
34019
34893
  parentSpan,
34020
34894
  () => startSpan(
34021
34895
  withSpanInstrumentationName(
34022
34896
  {
34023
34897
  event: {
34024
- input,
34898
+ input: processedInput,
34025
34899
  metadata
34026
34900
  },
34027
34901
  name: formatAgentSpanName(agent),
@@ -34034,7 +34908,7 @@ function startAgentStream(event, activeChildParents) {
34034
34908
  withSpanInstrumentationName(
34035
34909
  {
34036
34910
  event: {
34037
- input,
34911
+ input: processedInput,
34038
34912
  metadata
34039
34913
  },
34040
34914
  name: formatAgentSpanName(agent),
@@ -34052,22 +34926,21 @@ function startAgentStream(event, activeChildParents) {
34052
34926
  startTime: getCurrentUnixTimestamp()
34053
34927
  };
34054
34928
  }
34055
- function startMultiAgentStream(event, operation, activeChildParents) {
34056
- const orchestrator = extractOrchestrator(event);
34929
+ function startMultiAgentStream(input, orchestrator, operation, activeChildParents) {
34057
34930
  const metadata = {
34058
34931
  "strands.operation": operation,
34059
34932
  provider: "strands",
34060
34933
  ...orchestrator?.id ? { "strands.orchestrator.id": orchestrator.id } : {}
34061
34934
  };
34062
34935
  const parentSpan = orchestrator ? getOnlyChildParent(activeChildParents, orchestrator) : void 0;
34063
- const input = processStrandsInputAttachments(event.arguments[0]);
34936
+ const processedInput = processStrandsInputAttachments(input);
34064
34937
  const span = parentSpan ? withCurrent(
34065
34938
  parentSpan,
34066
34939
  () => startSpan(
34067
34940
  withSpanInstrumentationName(
34068
34941
  {
34069
34942
  event: {
34070
- input,
34943
+ input: processedInput,
34071
34944
  metadata
34072
34945
  },
34073
34946
  name: operation === "Graph.stream" ? "Strands Graph" : "Strands Swarm",
@@ -34080,7 +34953,7 @@ function startMultiAgentStream(event, operation, activeChildParents) {
34080
34953
  withSpanInstrumentationName(
34081
34954
  {
34082
34955
  event: {
34083
- input,
34956
+ input: processedInput,
34084
34957
  metadata
34085
34958
  },
34086
34959
  name: operation === "Graph.stream" ? "Strands Graph" : "Strands Swarm",
@@ -34451,12 +35324,12 @@ function finalizeMultiAgentStream(state, activeChildParents, error, output) {
34451
35324
  });
34452
35325
  state.span.end();
34453
35326
  }
34454
- function extractAgent(event) {
34455
- const candidate = event.agent ?? event.self;
35327
+ function extractAgent(agent, self) {
35328
+ const candidate = agent ?? self;
34456
35329
  return isObject(candidate) && typeof candidate.stream === "function" ? candidate : void 0;
34457
35330
  }
34458
- function extractOrchestrator(event) {
34459
- const candidate = event.orchestrator ?? event.self;
35331
+ function extractOrchestrator(orchestrator, self) {
35332
+ const candidate = orchestrator ?? self;
34460
35333
  return isObject(candidate) && typeof candidate.stream === "function" ? candidate : void 0;
34461
35334
  }
34462
35335
  function extractAgentMetadata2(agent) {
@@ -36664,7 +37537,7 @@ function isAsync(fn) {
36664
37537
  function isAsyncGenerator2(fn) {
36665
37538
  return fn[Symbol.toStringTag] === "AsyncGenerator";
36666
37539
  }
36667
- function isAsyncIterable6(obj) {
37540
+ function isAsyncIterable5(obj) {
36668
37541
  return typeof obj[Symbol.asyncIterator] === "function";
36669
37542
  }
36670
37543
  function wrapAsync(asyncFn) {
@@ -36836,7 +37709,7 @@ var eachOfLimit$2 = (limit) => {
36836
37709
  if (isAsyncGenerator2(obj)) {
36837
37710
  return asyncEachOfLimit(obj, limit, iteratee, callback);
36838
37711
  }
36839
- if (isAsyncIterable6(obj)) {
37712
+ if (isAsyncIterable5(obj)) {
36840
37713
  return asyncEachOfLimit(obj[Symbol.asyncIterator](), limit, iteratee, callback);
36841
37714
  }
36842
37715
  var nextElem = createIterator(obj);
@@ -38272,7 +39145,7 @@ function callEvaluatorData(data) {
38272
39145
  baseExperiment
38273
39146
  };
38274
39147
  }
38275
- function isAsyncIterable7(value) {
39148
+ function isAsyncIterable6(value) {
38276
39149
  return typeof value === "object" && value !== null && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
38277
39150
  }
38278
39151
  function isIterable(value) {
@@ -38307,7 +39180,7 @@ async function _internalResolveEvaluatorData(evaluator, experiment) {
38307
39180
  }).asDataset();
38308
39181
  }
38309
39182
  const resolvedDataResult = dataResult instanceof Promise ? await dataResult : dataResult;
38310
- if (isAsyncIterable7(resolvedDataResult)) {
39183
+ if (isAsyncIterable6(resolvedDataResult)) {
38311
39184
  return resolvedDataResult;
38312
39185
  }
38313
39186
  if (Array.isArray(resolvedDataResult) || isIterable(resolvedDataResult)) {
@@ -40207,14 +41080,9 @@ async function getDataset(state, data) {
40207
41080
  _internal_btql: data._internal_btql ?? void 0
40208
41081
  });
40209
41082
  } else if ("dataset_id" in data) {
40210
- const datasetInfo = await getDatasetById({
40211
- state,
40212
- datasetId: data.dataset_id
40213
- });
40214
41083
  return initDataset({
40215
41084
  state,
40216
- projectId: datasetInfo.projectId,
40217
- dataset: datasetInfo.dataset,
41085
+ datasetId: data.dataset_id,
40218
41086
  version: data.dataset_version ?? void 0,
40219
41087
  environment: data.dataset_environment ?? void 0,
40220
41088
  _internal_btql: data._internal_btql ?? void 0
@@ -40225,23 +41093,6 @@ async function getDataset(state, data) {
40225
41093
  return data.data;
40226
41094
  }
40227
41095
  }
40228
- var datasetFetchSchema = z14.object({
40229
- project_id: z14.string(),
40230
- name: z14.string()
40231
- });
40232
- async function getDatasetById({
40233
- state,
40234
- datasetId
40235
- }) {
40236
- const dataset = await state.appConn().post_json("api/dataset/get", {
40237
- id: datasetId
40238
- });
40239
- const parsed = z14.array(datasetFetchSchema).parse(dataset);
40240
- if (parsed.length === 0) {
40241
- throw new Error(`Dataset '${datasetId}' not found`);
40242
- }
40243
- return { projectId: parsed[0].project_id, dataset: parsed[0].name };
40244
- }
40245
41096
  function makeScorer(state, name, score, projectId) {
40246
41097
  const ret = async (input) => {
40247
41098
  const request = {