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
@@ -10,11 +10,13 @@ import {
10
10
  cloudflareThinkChannels,
11
11
  cohereChannels,
12
12
  cursorSDKChannels,
13
+ debugLogger,
13
14
  detectSpanOriginEnvironment,
14
15
  flueChannels,
15
16
  genkitChannels,
16
17
  genkitCoreChannels,
17
18
  getDefaultInstrumentationIntegrations,
19
+ getEnvDebugLogLevel,
18
20
  getHuggingFaceTransformersPipelineInfo,
19
21
  getSpanInstrumentationName,
20
22
  gitHubCopilotChannels,
@@ -32,6 +34,7 @@ import {
32
34
  langSmithChannels,
33
35
  mergeSpanOriginContext,
34
36
  mistralChannels,
37
+ normalizeDebugLogLevelOption,
35
38
  ollamaChannels,
36
39
  openAIAgentsCoreChannels,
37
40
  openAIChannels,
@@ -41,130 +44,18 @@ import {
41
44
  piCodingAgentChannels,
42
45
  readDisabledInstrumentationEnvConfig,
43
46
  registerHuggingFaceTransformersPipeline,
44
- setGlobalHookErrorReporter,
47
+ resetDebugLoggerForTests,
48
+ setDebugLogStateResolver,
49
+ setGlobalDebugLogLevel,
45
50
  smithyClientChannels,
46
51
  smithyCoreChannels,
47
52
  strandsAgentSDKChannels,
48
53
  voyageAIChannels,
49
54
  withSpanInstrumentationName
50
- } from "./chunk-ZHUHZWFY.mjs";
55
+ } from "./chunk-N3JKQ3D3.mjs";
51
56
 
52
57
  // src/id-gen.ts
53
58
  import { v4 as uuidv4 } from "uuid";
54
-
55
- // src/debug-logger.ts
56
- var PREFIX = "[braintrust]";
57
- var DEBUG_LOG_LEVEL_SYMBOL = /* @__PURE__ */ Symbol.for("braintrust-debug-log-level");
58
- var LOG_LEVEL_PRIORITY = {
59
- error: 0,
60
- warn: 1,
61
- info: 2,
62
- debug: 3
63
- };
64
- var hasWarnedAboutInvalidEnvValue = false;
65
- var debugLogStateResolver = void 0;
66
- function warnInvalidEnvValue(value) {
67
- if (hasWarnedAboutInvalidEnvValue) {
68
- return;
69
- }
70
- hasWarnedAboutInvalidEnvValue = true;
71
- console.warn(
72
- PREFIX,
73
- `Invalid BRAINTRUST_DEBUG_LOG_LEVEL value "${value}". Expected "error", "warn", "info", or "debug".`
74
- );
75
- }
76
- function normalizeDebugLogLevelOption(option) {
77
- if (option === false) {
78
- return void 0;
79
- }
80
- if (option === "error" || option === "warn" || option === "info" || option === "debug") {
81
- return option;
82
- }
83
- throw new Error(
84
- `Invalid debugLogLevel value "${option}". Expected false, "error", "warn", "info", or "debug".`
85
- );
86
- }
87
- function parseDebugLogLevelEnv(value) {
88
- if (!value) {
89
- return void 0;
90
- }
91
- if (value === "error" || value === "warn" || value === "info" || value === "debug") {
92
- return value;
93
- }
94
- warnInvalidEnvValue(value);
95
- return void 0;
96
- }
97
- function getEnvDebugLogLevel() {
98
- return parseDebugLogLevelEnv(isomorph_default.getEnv("BRAINTRUST_DEBUG_LOG_LEVEL"));
99
- }
100
- function setGlobalDebugLogLevel(level) {
101
- globalThis[DEBUG_LOG_LEVEL_SYMBOL] = level;
102
- }
103
- function resetDebugLoggerForTests() {
104
- hasWarnedAboutInvalidEnvValue = false;
105
- setGlobalDebugLogLevel(void 0);
106
- }
107
- function setDebugLogStateResolver(resolver) {
108
- debugLogStateResolver = resolver;
109
- }
110
- function resolveDebugLogLevel(state) {
111
- const stateLevel = state?.getDebugLogLevel?.();
112
- const hasStateOverride = state?.hasDebugLogLevelOverride?.() ?? false;
113
- if (hasStateOverride) {
114
- return stateLevel;
115
- }
116
- const globalLevel = (
117
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
118
- globalThis[DEBUG_LOG_LEVEL_SYMBOL]
119
- );
120
- if (globalLevel !== void 0) {
121
- return globalLevel === false ? void 0 : globalLevel;
122
- }
123
- return getEnvDebugLogLevel();
124
- }
125
- function emit(method, state, args) {
126
- const level = resolveDebugLogLevel(state);
127
- if (!level || LOG_LEVEL_PRIORITY[method] > LOG_LEVEL_PRIORITY[level]) {
128
- return;
129
- }
130
- if (method === "info") {
131
- console.log(PREFIX, ...args);
132
- } else if (method === "debug") {
133
- console.debug(PREFIX, ...args);
134
- } else if (method === "warn") {
135
- console.warn(PREFIX, ...args);
136
- } else {
137
- console.error(PREFIX, ...args);
138
- }
139
- }
140
- function createDebugLogger(state) {
141
- const resolveState = () => state ?? debugLogStateResolver?.();
142
- return {
143
- info(...args) {
144
- emit("info", resolveState(), args);
145
- },
146
- debug(...args) {
147
- emit("debug", resolveState(), args);
148
- },
149
- warn(...args) {
150
- emit("warn", resolveState(), args);
151
- },
152
- error(...args) {
153
- emit("error", resolveState(), args);
154
- }
155
- };
156
- }
157
- var debugLogger = {
158
- ...createDebugLogger(),
159
- forState(state) {
160
- return createDebugLogger(state);
161
- }
162
- };
163
- setGlobalHookErrorReporter((error) => {
164
- debugLogger.error("Global instrumentation hook error:", error);
165
- });
166
-
167
- // src/id-gen.ts
168
59
  var IDGenerator = class {
169
60
  };
170
61
  var UUIDGenerator = class extends IDGenerator {
@@ -1782,7 +1673,9 @@ var AclObjectType = z6.union([
1782
1673
  "project_log",
1783
1674
  "org_project",
1784
1675
  "org_audit_logs",
1785
- "project_group"
1676
+ "project_group",
1677
+ "ai_secret",
1678
+ "org_ai_secret"
1786
1679
  ]),
1787
1680
  z6.null()
1788
1681
  ]);
@@ -2615,7 +2508,7 @@ var PromptParserNullish = z6.union([
2615
2508
  }),
2616
2509
  z6.null()
2617
2510
  ]);
2618
- var PreprocessorSavedFunctionId = z6.union([
2511
+ var PreprocessorId = z6.union([
2619
2512
  z6.object({
2620
2513
  type: z6.literal("function"),
2621
2514
  id: z6.string(),
@@ -2626,6 +2519,7 @@ var PreprocessorSavedFunctionId = z6.union([
2626
2519
  name: z6.string(),
2627
2520
  function_type: z6.literal("preprocessor").optional().default("preprocessor")
2628
2521
  }),
2522
+ z6.object({ type: z6.literal("inline"), code: z6.string().min(1) }),
2629
2523
  z6.null()
2630
2524
  ]);
2631
2525
  var PromptDataNullish = z6.union([
@@ -2633,7 +2527,7 @@ var PromptDataNullish = z6.union([
2633
2527
  prompt: PromptBlockDataNullish,
2634
2528
  options: PromptOptionsNullish,
2635
2529
  parser: PromptParserNullish,
2636
- preprocessor: PreprocessorSavedFunctionId,
2530
+ preprocessor: PreprocessorId,
2637
2531
  tool_functions: z6.union([z6.array(SavedFunctionId), z6.null()]),
2638
2532
  template_format: z6.union([
2639
2533
  z6.enum(["mustache", "nunjucks", "none"]),
@@ -2835,7 +2729,7 @@ var PromptData = z6.object({
2835
2729
  prompt: PromptBlockDataNullish,
2836
2730
  options: PromptOptionsNullish,
2837
2731
  parser: PromptParserNullish,
2838
- preprocessor: PreprocessorSavedFunctionId,
2732
+ preprocessor: PreprocessorId,
2839
2733
  tool_functions: z6.union([z6.array(SavedFunctionId), z6.null()]),
2840
2734
  template_format: z6.union([
2841
2735
  z6.enum(["mustache", "nunjucks", "none"]),
@@ -3779,6 +3673,7 @@ var View = z6.object({
3779
3673
  ]),
3780
3674
  name: z6.string(),
3781
3675
  description: z6.union([z6.string(), z6.null()]).optional(),
3676
+ starred: z6.boolean().optional(),
3782
3677
  created: z6.union([z6.string(), z6.null()]).optional(),
3783
3678
  updated_at: z6.union([z6.string(), z6.null()]).optional(),
3784
3679
  view_data: ViewData.optional(),
@@ -4539,44 +4434,72 @@ function createCacheLayers({
4539
4434
  }
4540
4435
 
4541
4436
  // src/prompt-cache/prompt-cache.ts
4542
- function createCacheKey(key) {
4437
+ function createCacheKey(key, namespace) {
4438
+ let cacheKey;
4543
4439
  if (key.id) {
4544
- return `id:${key.id}`;
4545
- }
4546
- const prefix = key.projectId ?? key.projectName;
4547
- if (!prefix) {
4548
- throw new Error("Either projectId or projectName must be provided");
4549
- }
4550
- if (!key.slug) {
4551
- throw new Error("Slug must be provided when not using ID");
4440
+ cacheKey = `id:${key.id}`;
4441
+ } else {
4442
+ const prefix = key.projectId ?? key.projectName;
4443
+ if (!prefix) {
4444
+ throw new Error("Either projectId or projectName must be provided");
4445
+ }
4446
+ if (!key.slug) {
4447
+ throw new Error("Slug must be provided when not using ID");
4448
+ }
4449
+ cacheKey = `${prefix}:${key.slug}:${key.version ?? "latest"}`;
4552
4450
  }
4553
- return `${prefix}:${key.slug}:${key.version ?? "latest"}`;
4451
+ return namespace === void 0 ? cacheKey : `${namespace.length}:${namespace}:${cacheKey}`;
4554
4452
  }
4555
- var PromptCache = class {
4453
+ var PromptCache = class _PromptCache {
4556
4454
  memoryCache;
4557
4455
  diskCache;
4456
+ namespace;
4457
+ expectedResolvedOrgIdentity;
4558
4458
  constructor(options) {
4559
4459
  this.memoryCache = options.memoryCache;
4560
4460
  this.diskCache = options.diskCache;
4461
+ this.namespace = options.namespace;
4462
+ this.expectedResolvedOrgIdentity = options.expectedResolvedOrgIdentity;
4463
+ }
4464
+ /**
4465
+ * Returns a cache view that shares the same storage layers but isolates all
4466
+ * entries under the provided namespace.
4467
+ */
4468
+ withNamespace(namespace, expectedResolvedOrgIdentity) {
4469
+ return new _PromptCache({
4470
+ memoryCache: this.memoryCache,
4471
+ diskCache: this.diskCache,
4472
+ namespace,
4473
+ expectedResolvedOrgIdentity
4474
+ });
4561
4475
  }
4562
4476
  /**
4563
4477
  * Retrieves a prompt from the cache.
4564
4478
  * First checks the in-memory LRU cache, then falls back to checking the disk cache if available.
4565
4479
  */
4566
4480
  async get(key) {
4567
- const cacheKey = createCacheKey(key);
4481
+ const cacheKey = createCacheKey(key, this.namespace);
4568
4482
  if (this.memoryCache) {
4569
- const memoryPrompt = this.memoryCache.get(cacheKey);
4570
- if (memoryPrompt !== void 0) {
4571
- return memoryPrompt;
4483
+ const memoryEntry = this.memoryCache.get(cacheKey);
4484
+ if (memoryEntry !== void 0 && (this.expectedResolvedOrgIdentity === void 0 || memoryEntry.resolvedOrgIdentity === this.expectedResolvedOrgIdentity)) {
4485
+ return memoryEntry.value;
4572
4486
  }
4573
4487
  }
4574
4488
  if (this.diskCache) {
4575
- const diskPrompt = await this.diskCache.get(cacheKey);
4576
- if (!diskPrompt) {
4489
+ const diskEntry = await this.diskCache.get(cacheKey);
4490
+ if (!diskEntry || this.expectedResolvedOrgIdentity !== void 0 && diskEntry.resolvedOrgIdentity !== this.expectedResolvedOrgIdentity) {
4577
4491
  return void 0;
4578
4492
  }
4579
- this.memoryCache?.set(cacheKey, diskPrompt);
4493
+ const serializedPrompt = diskEntry.value;
4494
+ const diskPrompt = new Prompt2(
4495
+ serializedPrompt.metadata,
4496
+ serializedPrompt.defaults,
4497
+ serializedPrompt.noTrace
4498
+ );
4499
+ this.memoryCache?.set(cacheKey, {
4500
+ value: diskPrompt,
4501
+ resolvedOrgIdentity: diskEntry.resolvedOrgIdentity
4502
+ });
4580
4503
  return diskPrompt;
4581
4504
  }
4582
4505
  return void 0;
@@ -4590,58 +4513,91 @@ var PromptCache = class {
4590
4513
  * @throws If there is an error writing to the disk cache.
4591
4514
  */
4592
4515
  async set(key, value) {
4593
- const cacheKey = createCacheKey(key);
4594
- this.memoryCache?.set(cacheKey, value);
4516
+ const cacheKey = createCacheKey(key, this.namespace);
4517
+ const memoryEntry = {
4518
+ value,
4519
+ resolvedOrgIdentity: this.expectedResolvedOrgIdentity
4520
+ };
4521
+ this.memoryCache?.set(cacheKey, memoryEntry);
4595
4522
  if (this.diskCache) {
4596
- await this.diskCache.set(cacheKey, value);
4523
+ await this.diskCache.set(cacheKey, {
4524
+ value: value._internalSerializeForCache(),
4525
+ resolvedOrgIdentity: this.expectedResolvedOrgIdentity
4526
+ });
4597
4527
  }
4598
4528
  }
4599
4529
  };
4600
4530
 
4601
4531
  // src/prompt-cache/parameters-cache.ts
4602
- function createCacheKey2(key) {
4532
+ function createCacheKey2(key, namespace) {
4533
+ let cacheKey;
4603
4534
  if (key.id) {
4604
- return `parameters:id:${key.id}`;
4605
- }
4606
- const prefix = key.projectId ?? key.projectName;
4607
- if (!prefix) {
4608
- throw new Error("Either projectId or projectName must be provided");
4609
- }
4610
- if (!key.slug) {
4611
- throw new Error("Slug must be provided when not using ID");
4535
+ cacheKey = `parameters:id:${key.id}`;
4536
+ } else {
4537
+ const prefix = key.projectId ?? key.projectName;
4538
+ if (!prefix) {
4539
+ throw new Error("Either projectId or projectName must be provided");
4540
+ }
4541
+ if (!key.slug) {
4542
+ throw new Error("Slug must be provided when not using ID");
4543
+ }
4544
+ cacheKey = `parameters:${prefix}:${key.slug}:${key.version ?? "latest"}`;
4612
4545
  }
4613
- return `parameters:${prefix}:${key.slug}:${key.version ?? "latest"}`;
4546
+ return namespace === void 0 ? cacheKey : `${namespace.length}:${namespace}:${cacheKey}`;
4614
4547
  }
4615
- var ParametersCache = class {
4548
+ var ParametersCache = class _ParametersCache {
4616
4549
  memoryCache;
4617
4550
  diskCache;
4551
+ namespace;
4552
+ expectedResolvedOrgIdentity;
4618
4553
  constructor(options) {
4619
4554
  this.memoryCache = options.memoryCache;
4620
4555
  this.diskCache = options.diskCache;
4556
+ this.namespace = options.namespace;
4557
+ this.expectedResolvedOrgIdentity = options.expectedResolvedOrgIdentity;
4558
+ }
4559
+ withNamespace(namespace, expectedResolvedOrgIdentity) {
4560
+ return new _ParametersCache({
4561
+ memoryCache: this.memoryCache,
4562
+ diskCache: this.diskCache,
4563
+ namespace,
4564
+ expectedResolvedOrgIdentity
4565
+ });
4621
4566
  }
4622
4567
  async get(key) {
4623
- const cacheKey = createCacheKey2(key);
4568
+ const cacheKey = createCacheKey2(key, this.namespace);
4624
4569
  if (this.memoryCache) {
4625
- const memoryParams = this.memoryCache.get(cacheKey);
4626
- if (memoryParams !== void 0) {
4627
- return memoryParams;
4570
+ const memoryEntry = this.memoryCache.get(cacheKey);
4571
+ if (memoryEntry !== void 0 && (this.expectedResolvedOrgIdentity === void 0 || memoryEntry.resolvedOrgIdentity === this.expectedResolvedOrgIdentity)) {
4572
+ return memoryEntry.value;
4628
4573
  }
4629
4574
  }
4630
4575
  if (this.diskCache) {
4631
- const diskParams = await this.diskCache.get(cacheKey);
4632
- if (!diskParams) {
4576
+ const diskEntry = await this.diskCache.get(cacheKey);
4577
+ if (!diskEntry || this.expectedResolvedOrgIdentity !== void 0 && diskEntry.resolvedOrgIdentity !== this.expectedResolvedOrgIdentity) {
4633
4578
  return void 0;
4634
4579
  }
4635
- this.memoryCache?.set(cacheKey, diskParams);
4636
- return diskParams;
4580
+ const diskParameters = new RemoteEvalParameters(diskEntry.value.metadata);
4581
+ this.memoryCache?.set(cacheKey, {
4582
+ value: diskParameters,
4583
+ resolvedOrgIdentity: diskEntry.resolvedOrgIdentity
4584
+ });
4585
+ return diskParameters;
4637
4586
  }
4638
4587
  return void 0;
4639
4588
  }
4640
4589
  async set(key, value) {
4641
- const cacheKey = createCacheKey2(key);
4642
- this.memoryCache?.set(cacheKey, value);
4590
+ const cacheKey = createCacheKey2(key, this.namespace);
4591
+ const memoryEntry = {
4592
+ value,
4593
+ resolvedOrgIdentity: this.expectedResolvedOrgIdentity
4594
+ };
4595
+ this.memoryCache?.set(cacheKey, memoryEntry);
4643
4596
  if (this.diskCache) {
4644
- await this.diskCache.set(cacheKey, value);
4597
+ await this.diskCache.set(cacheKey, {
4598
+ value: value._internalSerializeForCache(),
4599
+ resolvedOrgIdentity: this.expectedResolvedOrgIdentity
4600
+ });
4645
4601
  }
4646
4602
  }
4647
4603
  };
@@ -4946,6 +4902,16 @@ var datasetSnapshotRegisterResponseSchema = z8.object({
4946
4902
  dataset_snapshot: DatasetSnapshot,
4947
4903
  found_existing: z8.boolean().optional()
4948
4904
  });
4905
+ var datasetObjectInfoSchema = z8.object({
4906
+ object_id: z8.string(),
4907
+ object_name: z8.string(),
4908
+ parent_cols: z8.object({
4909
+ project: z8.object({
4910
+ id: z8.string(),
4911
+ name: z8.string()
4912
+ })
4913
+ })
4914
+ });
4949
4915
  var datasetRestorePreviewResultSchema = z8.object({
4950
4916
  rows_to_restore: z8.number(),
4951
4917
  rows_to_delete: z8.number()
@@ -5159,12 +5125,53 @@ var loginSchema = z8.strictObject({
5159
5125
  });
5160
5126
  var stateNonce = 0;
5161
5127
  var V1_PROXY_SUFFIX = "/v1/proxy";
5128
+ var LOADER_LOGIN_CACHE_MAX = 16;
5162
5129
  function normalizeProxyConnUrl(proxyUrl) {
5163
5130
  return proxyUrl.endsWith(V1_PROXY_SUFFIX) ? proxyUrl.slice(0, proxyUrl.length - V1_PROXY_SUFFIX.length) : proxyUrl;
5164
5131
  }
5165
5132
  var BraintrustState = class _BraintrustState {
5133
+ id;
5134
+ currentExperiment;
5135
+ // Note: the value of IsAsyncFlush doesn't really matter here, since we
5136
+ // (safely) dynamically cast it whenever retrieving the logger.
5137
+ currentLogger;
5138
+ currentParent;
5139
+ currentSpan;
5140
+ // Any time we re-log in, we directly update the apiConn inside the logger.
5141
+ // This is preferable to replacing the whole logger, which would create the
5142
+ // possibility of multiple loggers floating around, which may not log in a
5143
+ // deterministic order.
5144
+ _bgLogger;
5145
+ _overrideBgLogger = null;
5146
+ appUrl = null;
5147
+ appPublicUrl = null;
5148
+ loginToken = null;
5149
+ orgId = null;
5150
+ orgName = null;
5151
+ apiUrl = null;
5152
+ proxyUrl = null;
5153
+ loggedIn = false;
5154
+ gitMetadataSettings;
5155
+ debugLogLevel;
5156
+ debugLogLevelConfigured = false;
5157
+ fetch = globalThis.fetch;
5158
+ _appConn = null;
5159
+ _apiConn = null;
5160
+ _proxyConn = null;
5161
+ promptCache;
5162
+ parametersCache;
5163
+ spanCache;
5164
+ _idGenerator = null;
5165
+ _contextManager = null;
5166
+ _otelFlushCallback = null;
5167
+ spanOriginEnvironment;
5168
+ traceContextSigningSecret;
5169
+ loaderLoginCache = /* @__PURE__ */ new WeakMap();
5170
+ loginParams;
5171
+ activeLoginOrgNameSelector;
5166
5172
  constructor(loginParams) {
5167
- this.loginParams = loginParams;
5173
+ this.loginParams = { ...loginParams };
5174
+ this.activeLoginOrgNameSelector = loginParams.orgName ?? isomorph_default.getEnv("BRAINTRUST_ORG_NAME");
5168
5175
  this.id = `${(/* @__PURE__ */ new Date()).toLocaleString()}-${stateNonce++}`;
5169
5176
  this.currentExperiment = void 0;
5170
5177
  this.currentLogger = void 0;
@@ -5201,12 +5208,14 @@ var BraintrustState = class _BraintrustState {
5201
5208
  const {
5202
5209
  memoryCache: parametersMemoryCache,
5203
5210
  diskCache: parametersDiskCache
5204
- } = createCacheLayers({
5205
- memoryMaxEnvVar: "BRAINTRUST_PARAMETERS_CACHE_MEMORY_MAX",
5206
- diskCacheDirEnvVar: "BRAINTRUST_PARAMETERS_CACHE_DIR",
5207
- diskMaxEnvVar: "BRAINTRUST_PARAMETERS_CACHE_DISK_MAX",
5208
- getDefaultDiskCacheDir: () => `${isomorph_default.getEnv("HOME") ?? isomorph_default.homedir()}/.braintrust/parameters_cache`
5209
- });
5211
+ } = createCacheLayers(
5212
+ {
5213
+ memoryMaxEnvVar: "BRAINTRUST_PARAMETERS_CACHE_MEMORY_MAX",
5214
+ diskCacheDirEnvVar: "BRAINTRUST_PARAMETERS_CACHE_DIR",
5215
+ diskMaxEnvVar: "BRAINTRUST_PARAMETERS_CACHE_DISK_MAX",
5216
+ getDefaultDiskCacheDir: () => `${isomorph_default.getEnv("HOME") ?? isomorph_default.homedir()}/.braintrust/parameters_cache`
5217
+ }
5218
+ );
5210
5219
  this.parametersCache = new ParametersCache({
5211
5220
  memoryCache: parametersMemoryCache,
5212
5221
  diskCache: parametersDiskCache
@@ -5215,43 +5224,6 @@ var BraintrustState = class _BraintrustState {
5215
5224
  this.spanOriginEnvironment = detectSpanOriginEnvironment();
5216
5225
  this._internalSetTraceContextSigningSecret(loginParams.apiKey);
5217
5226
  }
5218
- loginParams;
5219
- id;
5220
- currentExperiment;
5221
- // Note: the value of IsAsyncFlush doesn't really matter here, since we
5222
- // (safely) dynamically cast it whenever retrieving the logger.
5223
- currentLogger;
5224
- currentParent;
5225
- currentSpan;
5226
- // Any time we re-log in, we directly update the apiConn inside the logger.
5227
- // This is preferable to replacing the whole logger, which would create the
5228
- // possibility of multiple loggers floating around, which may not log in a
5229
- // deterministic order.
5230
- _bgLogger;
5231
- _overrideBgLogger = null;
5232
- appUrl = null;
5233
- appPublicUrl = null;
5234
- loginToken = null;
5235
- orgId = null;
5236
- orgName = null;
5237
- apiUrl = null;
5238
- proxyUrl = null;
5239
- loggedIn = false;
5240
- gitMetadataSettings;
5241
- debugLogLevel;
5242
- debugLogLevelConfigured = false;
5243
- fetch = globalThis.fetch;
5244
- _appConn = null;
5245
- _apiConn = null;
5246
- _proxyConn = null;
5247
- promptCache;
5248
- parametersCache;
5249
- spanCache;
5250
- _idGenerator = null;
5251
- _contextManager = null;
5252
- _otelFlushCallback = null;
5253
- spanOriginEnvironment;
5254
- traceContextSigningSecret;
5255
5227
  /** @internal */
5256
5228
  _internalSetTraceContextSigningSecret(secret) {
5257
5229
  const normalizedSecret = secret?.trim();
@@ -5276,6 +5248,101 @@ var BraintrustState = class _BraintrustState {
5276
5248
  this._appConn = null;
5277
5249
  this._apiConn = null;
5278
5250
  this._proxyConn = null;
5251
+ this.loaderLoginCache = /* @__PURE__ */ new WeakMap();
5252
+ }
5253
+ /** @internal */
5254
+ async _internalResolveLoaderLoginOptions({
5255
+ apiKey,
5256
+ appUrl,
5257
+ orgName,
5258
+ fetch: fetch2,
5259
+ forceLogin
5260
+ }) {
5261
+ const resolvedAppUrl = appUrl ?? (this.loggedIn ? this.appUrl ?? void 0 : void 0) ?? this.loginParams.appUrl ?? isomorph_default.getEnv("BRAINTRUST_APP_URL") ?? "https://www.braintrust.dev";
5262
+ const resolvedApiKey = apiKey ?? (this.loggedIn ? this.loginToken ?? void 0 : void 0) ?? this.loginParams.apiKey ?? await isomorph_default.getBraintrustApiKey();
5263
+ if (!resolvedApiKey) {
5264
+ throw new Error(
5265
+ "Please specify an api key (e.g. by setting BRAINTRUST_API_KEY)."
5266
+ );
5267
+ }
5268
+ const normalizedApiKey = HTTPConnection.sanitize_token(resolvedApiKey);
5269
+ const usesActiveCredential = this.loggedIn && normalizedApiKey === this.loginToken;
5270
+ const requestedOrgName = orgName ?? (usesActiveCredential ? this.activeLoginOrgNameSelector : void 0) ?? this.loginParams.orgName ?? isomorph_default.getEnv("BRAINTRUST_ORG_NAME");
5271
+ const resolvedOrgName = orgName ?? (usesActiveCredential ? this.orgName ?? void 0 : void 0) ?? requestedOrgName;
5272
+ const resolvedFetch = fetch2 ?? (this.loggedIn ? this.fetch : void 0) ?? this.loginParams.fetch ?? globalThis.fetch;
5273
+ const credentialCacheNamespace = JSON.stringify([
5274
+ "loader-credential",
5275
+ resolvedAppUrl,
5276
+ requestedOrgName,
5277
+ normalizedApiKey
5278
+ ]);
5279
+ return {
5280
+ apiKey: normalizedApiKey,
5281
+ appUrl: resolvedAppUrl,
5282
+ orgName: resolvedOrgName,
5283
+ fetch: resolvedFetch,
5284
+ forceLogin,
5285
+ credentialCacheNamespace,
5286
+ existingState: !forceLogin && usesActiveCredential && resolvedAppUrl === this.appUrl && resolvedOrgName === this.orgName && resolvedFetch === this.fetch ? this : void 0
5287
+ };
5288
+ }
5289
+ /** @internal */
5290
+ _internalGetLoaderCacheViews(loginOptions, requestState) {
5291
+ const expectedResolvedOrgIdentity = requestState?.orgId && requestState.appUrl ? JSON.stringify([
5292
+ "loader-org",
5293
+ requestState.appUrl,
5294
+ requestState.orgId
5295
+ ]) : void 0;
5296
+ return {
5297
+ promptCache: this.promptCache.withNamespace(
5298
+ loginOptions.credentialCacheNamespace,
5299
+ expectedResolvedOrgIdentity
5300
+ ),
5301
+ parametersCache: this.parametersCache.withNamespace(
5302
+ loginOptions.credentialCacheNamespace,
5303
+ expectedResolvedOrgIdentity
5304
+ )
5305
+ };
5306
+ }
5307
+ /** @internal */
5308
+ async _internalGetLoaderState({
5309
+ apiKey,
5310
+ appUrl,
5311
+ orgName,
5312
+ fetch: fetch2,
5313
+ forceLogin,
5314
+ existingState
5315
+ }) {
5316
+ if (existingState) {
5317
+ return existingState;
5318
+ }
5319
+ let cache = this.loaderLoginCache.get(fetch2);
5320
+ if (!cache) {
5321
+ cache = new LRUCache({ max: LOADER_LOGIN_CACHE_MAX });
5322
+ this.loaderLoginCache.set(fetch2, cache);
5323
+ }
5324
+ const cacheKey = JSON.stringify([appUrl, orgName, apiKey]);
5325
+ if (!forceLogin) {
5326
+ const cachedState = cache.get(cacheKey);
5327
+ if (cachedState) {
5328
+ return cachedState;
5329
+ }
5330
+ }
5331
+ const statePromise = loginToLoaderRequestState({
5332
+ orgName,
5333
+ apiKey,
5334
+ appUrl,
5335
+ fetch: fetch2
5336
+ });
5337
+ cache.set(cacheKey, statePromise);
5338
+ try {
5339
+ return await statePromise;
5340
+ } catch (error) {
5341
+ if (cache.get(cacheKey) === statePromise) {
5342
+ cache.delete(cacheKey);
5343
+ }
5344
+ throw error;
5345
+ }
5279
5346
  }
5280
5347
  resetIdGenState() {
5281
5348
  this._idGenerator = null;
@@ -5324,6 +5391,8 @@ var BraintrustState = class _BraintrustState {
5324
5391
  this.debugLogLevel = other.debugLogLevel;
5325
5392
  this.debugLogLevelConfigured = other.debugLogLevelConfigured;
5326
5393
  this.traceContextSigningSecret = other.traceContextSigningSecret;
5394
+ this.fetch = other.fetch;
5395
+ this.activeLoginOrgNameSelector = other.activeLoginOrgNameSelector;
5327
5396
  setGlobalDebugLogLevel(
5328
5397
  this.debugLogLevelConfigured ? this.debugLogLevel ?? false : void 0
5329
5398
  );
@@ -5563,36 +5632,85 @@ var FailedHTTPResponse = class extends Error {
5563
5632
  status;
5564
5633
  text;
5565
5634
  data;
5566
- constructor(status, text, data) {
5635
+ cause;
5636
+ constructor(status, text, data, cause) {
5567
5637
  super(`${status}: ${text} (${data})`);
5568
5638
  this.status = status;
5569
5639
  this.text = text;
5570
5640
  this.data = data;
5641
+ this.cause = cause;
5642
+ }
5643
+ };
5644
+ var HTTPTransportError = class extends Error {
5645
+ cause;
5646
+ constructor(cause) {
5647
+ super(cause instanceof Error ? cause.message : String(cause));
5648
+ this.name = "HTTPTransportError";
5649
+ this.cause = cause;
5571
5650
  }
5572
5651
  };
5652
+ var httpTransportErrorCauses = /* @__PURE__ */ new WeakSet();
5653
+ function recordHTTPTransportError(error) {
5654
+ if (typeof error === "object" && error !== null || typeof error === "function") {
5655
+ httpTransportErrorCauses.add(error);
5656
+ }
5657
+ }
5658
+ function rethrowHTTPTransportError(error, classifyTransportErrors) {
5659
+ if (classifyTransportErrors) {
5660
+ throw new HTTPTransportError(error);
5661
+ }
5662
+ recordHTTPTransportError(error);
5663
+ throw error;
5664
+ }
5665
+ function isLoaderCacheFallbackError(error) {
5666
+ if (error instanceof FailedHTTPResponse) {
5667
+ return error.status === 408 || error.status === 429 || error.status >= 500;
5668
+ }
5669
+ if (error instanceof HTTPTransportError) {
5670
+ return true;
5671
+ }
5672
+ return (typeof error === "object" && error !== null || typeof error === "function") && httpTransportErrorCauses.has(error);
5673
+ }
5674
+ async function readJSONResponse(response, classifyTransportErrors = false) {
5675
+ let data;
5676
+ try {
5677
+ data = await response.text();
5678
+ } catch (error) {
5679
+ rethrowHTTPTransportError(error, classifyTransportErrors);
5680
+ }
5681
+ return JSON.parse(data);
5682
+ }
5573
5683
  async function checkResponse(resp) {
5574
5684
  if (resp.ok) {
5575
5685
  return resp;
5576
- } else {
5686
+ }
5687
+ let data;
5688
+ try {
5689
+ data = await resp.text();
5690
+ } catch (error) {
5577
5691
  throw new FailedHTTPResponse(
5578
5692
  resp.status,
5579
5693
  resp.statusText,
5580
- await resp.text()
5694
+ "Unable to read response body",
5695
+ error
5581
5696
  );
5582
5697
  }
5698
+ throw new FailedHTTPResponse(resp.status, resp.statusText, data);
5583
5699
  }
5584
5700
  var HTTPConnection = class _HTTPConnection {
5585
- base_url;
5586
- token;
5587
- headers;
5588
- fetch;
5589
- constructor(base_url, fetch2) {
5701
+ constructor(base_url, fetch2, classifyTransportErrors = false) {
5702
+ this.classifyTransportErrors = classifyTransportErrors;
5590
5703
  this.base_url = base_url;
5591
5704
  this.token = null;
5592
5705
  this.headers = {};
5593
5706
  this._reset();
5594
5707
  this.fetch = fetch2;
5595
5708
  }
5709
+ classifyTransportErrors;
5710
+ base_url;
5711
+ token;
5712
+ headers;
5713
+ fetch;
5596
5714
  setFetch(fetch2) {
5597
5715
  this.fetch = fetch2;
5598
5716
  }
@@ -5632,9 +5750,9 @@ var HTTPConnection = class _HTTPConnection {
5632
5750
  ).toString();
5633
5751
  const this_fetch = this.fetch;
5634
5752
  const this_headers = this.headers;
5635
- return await checkResponse(
5636
- // Using toString() here makes it work with isomorphic fetch
5637
- await this_fetch(url.toString(), {
5753
+ let response;
5754
+ try {
5755
+ response = await this_fetch(url.toString(), {
5638
5756
  headers: {
5639
5757
  Accept: "application/json",
5640
5758
  ...this_headers,
@@ -5642,8 +5760,14 @@ var HTTPConnection = class _HTTPConnection {
5642
5760
  },
5643
5761
  keepalive: true,
5644
5762
  ...rest
5645
- })
5646
- );
5763
+ });
5764
+ } catch (error) {
5765
+ if (config?.signal?.aborted) {
5766
+ throw getAbortReason(config.signal);
5767
+ }
5768
+ rethrowHTTPTransportError(error, this.classifyTransportErrors);
5769
+ }
5770
+ return await checkResponse(response);
5647
5771
  }
5648
5772
  async post(path3, params, config, retries = 0) {
5649
5773
  const { headers, ...rest } = config || {};
@@ -5653,8 +5777,9 @@ var HTTPConnection = class _HTTPConnection {
5653
5777
  const tries = retries + 1;
5654
5778
  for (let i = 0; i < tries; i++) {
5655
5779
  try {
5656
- return await checkResponse(
5657
- await this_fetch(_urljoin(this_base_url, path3), {
5780
+ let response;
5781
+ try {
5782
+ response = await this_fetch(_urljoin(this_base_url, path3), {
5658
5783
  method: "POST",
5659
5784
  headers: {
5660
5785
  Accept: "application/json",
@@ -5665,8 +5790,14 @@ var HTTPConnection = class _HTTPConnection {
5665
5790
  body: typeof params === "string" ? params : params ? JSON.stringify(params) : void 0,
5666
5791
  keepalive: true,
5667
5792
  ...rest
5668
- })
5669
- );
5793
+ });
5794
+ } catch (error) {
5795
+ if (config?.signal?.aborted) {
5796
+ throw getAbortReason(config.signal);
5797
+ }
5798
+ rethrowHTTPTransportError(error, this.classifyTransportErrors);
5799
+ }
5800
+ return await checkResponse(response);
5670
5801
  } catch (error) {
5671
5802
  if (config?.signal?.aborted) {
5672
5803
  throw getAbortReason(config.signal);
@@ -5691,7 +5822,7 @@ var HTTPConnection = class _HTTPConnection {
5691
5822
  for (let i = 0; i < tries; i++) {
5692
5823
  try {
5693
5824
  const resp = await this.get(`${object_type}`, args);
5694
- return await resp.json();
5825
+ return await readJSONResponse(resp, this.classifyTransportErrors);
5695
5826
  } catch (e) {
5696
5827
  if (i < tries - 1) {
5697
5828
  debugLogger.debug(
@@ -5714,7 +5845,7 @@ var HTTPConnection = class _HTTPConnection {
5714
5845
  const resp = await this.post(`${object_type}`, args, {
5715
5846
  headers: { "Content-Type": "application/json" }
5716
5847
  });
5717
- return await resp.json();
5848
+ return await readJSONResponse(resp, this.classifyTransportErrors);
5718
5849
  }
5719
5850
  // Custom inspect for Node.js console.log
5720
5851
  [/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() {
@@ -7762,6 +7893,7 @@ function initDataset(projectOrOptions, optionalOptions) {
7762
7893
  const {
7763
7894
  project,
7764
7895
  dataset,
7896
+ datasetId,
7765
7897
  description,
7766
7898
  version,
7767
7899
  snapshotName,
@@ -7777,6 +7909,11 @@ function initDataset(projectOrOptions, optionalOptions) {
7777
7909
  state: stateArg,
7778
7910
  _internal_btql
7779
7911
  } = options;
7912
+ if (datasetId !== void 0 && (description !== void 0 || metadata !== void 0)) {
7913
+ throw new Error(
7914
+ "Cannot specify description or metadata when datasetId is provided"
7915
+ );
7916
+ }
7780
7917
  const selection = normalizeDatasetSelection({
7781
7918
  version,
7782
7919
  environment,
@@ -7797,6 +7934,35 @@ function initDataset(projectOrOptions, optionalOptions) {
7797
7934
  fetch: fetch2,
7798
7935
  forceLogin
7799
7936
  });
7937
+ if (datasetId !== void 0) {
7938
+ const objectInfo = datasetObjectInfoSchema.array().parse(
7939
+ await state.appConn().post_json("api/self/get_object_info", {
7940
+ object_type: "dataset",
7941
+ object_ids: [datasetId]
7942
+ })
7943
+ );
7944
+ if (objectInfo.length === 0) {
7945
+ throw new Error(`Dataset with ID ${datasetId} not found`);
7946
+ }
7947
+ if (objectInfo.length !== 1) {
7948
+ throw new Error(
7949
+ `Expected exactly one dataset with ID ${datasetId}, but found ${objectInfo.length}`
7950
+ );
7951
+ }
7952
+ const datasetInfo = objectInfo[0];
7953
+ return {
7954
+ project: {
7955
+ id: datasetInfo.parent_cols.project.id,
7956
+ name: datasetInfo.parent_cols.project.name,
7957
+ fullInfo: datasetInfo.parent_cols.project
7958
+ },
7959
+ dataset: {
7960
+ id: datasetInfo.object_id,
7961
+ name: datasetInfo.object_name,
7962
+ fullInfo: datasetInfo
7963
+ }
7964
+ };
7965
+ }
7800
7966
  const args = {
7801
7967
  org_id: state.orgId,
7802
7968
  project_name: project,
@@ -7950,6 +8116,24 @@ function initLogger(options = {}) {
7950
8116
  }
7951
8117
  return ret;
7952
8118
  }
8119
+ async function runCredentialScopedLoaderRequest(state, loginOptions, request) {
8120
+ const resolvedLoginOptions = await state._internalResolveLoaderLoginOptions(loginOptions);
8121
+ let cacheViews = state._internalGetLoaderCacheViews(resolvedLoginOptions);
8122
+ try {
8123
+ const requestState = await state._internalGetLoaderState(resolvedLoginOptions);
8124
+ cacheViews = state._internalGetLoaderCacheViews(
8125
+ resolvedLoginOptions,
8126
+ requestState
8127
+ );
8128
+ return {
8129
+ ok: true,
8130
+ response: await request(requestState),
8131
+ ...cacheViews
8132
+ };
8133
+ } catch (error) {
8134
+ return { ok: false, error, ...cacheViews };
8135
+ }
8136
+ }
7953
8137
  async function loadPrompt({
7954
8138
  projectName,
7955
8139
  projectId,
@@ -7974,43 +8158,47 @@ async function loadPrompt({
7974
8158
  throw new Error("Must specify slug");
7975
8159
  }
7976
8160
  const state = stateArg ?? _globalState;
7977
- let response;
7978
- try {
7979
- await state.login({
7980
- orgName,
7981
- apiKey,
7982
- appUrl,
7983
- fetch: fetch2,
7984
- forceLogin
7985
- });
7986
- if (id) {
7987
- response = await state.apiConn().get_json(`v1/prompt/${id}`, versionOrEnvironment);
7988
- if (response) {
7989
- response = { objects: [response] };
8161
+ const result = await runCredentialScopedLoaderRequest(
8162
+ state,
8163
+ { orgName, apiKey, appUrl, fetch: fetch2, forceLogin },
8164
+ async (requestState) => {
8165
+ let response2;
8166
+ if (id) {
8167
+ response2 = await requestState.apiConn().get_json(`v1/prompt/${id}`, versionOrEnvironment);
8168
+ if (response2) {
8169
+ response2 = { objects: [response2] };
8170
+ }
8171
+ } else {
8172
+ response2 = await requestState.apiConn().get_json("v1/prompt", {
8173
+ project_name: projectName,
8174
+ project_id: projectId,
8175
+ slug,
8176
+ ...versionOrEnvironment
8177
+ });
7990
8178
  }
7991
- } else {
7992
- response = await state.apiConn().get_json("v1/prompt", {
7993
- project_name: projectName,
7994
- project_id: projectId,
7995
- slug,
7996
- ...versionOrEnvironment
7997
- });
8179
+ return response2;
8180
+ }
8181
+ );
8182
+ const { promptCache } = result;
8183
+ if (!result.ok) {
8184
+ const e = result.error;
8185
+ if (!isLoaderCacheFallbackError(e)) {
8186
+ throw e;
7998
8187
  }
7999
- } catch (e) {
8000
8188
  if (version || environment) {
8001
8189
  throw new Error(`Prompt not found with specified parameters: ${e}`);
8002
8190
  }
8003
8191
  debugLogger.forState(state).warn("Failed to load prompt, attempting to fall back to cache:", e);
8004
8192
  let prompt2;
8005
8193
  if (id) {
8006
- prompt2 = await state.promptCache.get({ id });
8194
+ prompt2 = await promptCache.get({ id });
8007
8195
  if (!prompt2) {
8008
8196
  throw new Error(
8009
8197
  `Prompt with id ${id} not found (not found on server or in local cache): ${e}`
8010
8198
  );
8011
8199
  }
8012
8200
  } else {
8013
- prompt2 = await state.promptCache.get({
8201
+ prompt2 = await promptCache.get({
8014
8202
  slug,
8015
8203
  projectId,
8016
8204
  projectName,
@@ -8026,6 +8214,7 @@ async function loadPrompt({
8026
8214
  }
8027
8215
  return prompt2;
8028
8216
  }
8217
+ const { response } = result;
8029
8218
  if (!("objects" in response) || response.objects.length === 0) {
8030
8219
  if (id) {
8031
8220
  throw new Error(`Prompt with id ${id} not found.`);
@@ -8049,9 +8238,9 @@ async function loadPrompt({
8049
8238
  const prompt = new Prompt2(metadata, defaults || {}, noTrace);
8050
8239
  try {
8051
8240
  if (id) {
8052
- await state.promptCache.set({ id }, prompt);
8241
+ await promptCache.set({ id }, prompt);
8053
8242
  } else if (slug) {
8054
- await state.promptCache.set(
8243
+ await promptCache.set(
8055
8244
  { slug, projectId, projectName, version: version ?? "latest" },
8056
8245
  prompt
8057
8246
  );
@@ -8083,46 +8272,50 @@ async function loadParameters({
8083
8272
  throw new Error("Must specify slug");
8084
8273
  }
8085
8274
  const state = stateArg ?? _globalState;
8086
- let response;
8087
- try {
8088
- await state.login({
8089
- orgName,
8090
- apiKey,
8091
- appUrl,
8092
- fetch: fetch2,
8093
- forceLogin
8094
- });
8095
- if (id) {
8096
- response = await state.apiConn().get_json(`v1/function/${id}`, {
8097
- ...versionOrEnvironment
8098
- });
8099
- if (response) {
8100
- response = { objects: [response] };
8275
+ const result = await runCredentialScopedLoaderRequest(
8276
+ state,
8277
+ { orgName, apiKey, appUrl, fetch: fetch2, forceLogin },
8278
+ async (requestState) => {
8279
+ let response2;
8280
+ if (id) {
8281
+ response2 = await requestState.apiConn().get_json(`v1/function/${id}`, {
8282
+ ...versionOrEnvironment
8283
+ });
8284
+ if (response2) {
8285
+ response2 = { objects: [response2] };
8286
+ }
8287
+ } else {
8288
+ response2 = await requestState.apiConn().get_json("v1/function", {
8289
+ project_name: projectName,
8290
+ project_id: projectId,
8291
+ slug,
8292
+ function_type: "parameters",
8293
+ ...versionOrEnvironment
8294
+ });
8101
8295
  }
8102
- } else {
8103
- response = await state.apiConn().get_json("v1/function", {
8104
- project_name: projectName,
8105
- project_id: projectId,
8106
- slug,
8107
- function_type: "parameters",
8108
- ...versionOrEnvironment
8109
- });
8296
+ return response2;
8297
+ }
8298
+ );
8299
+ const { parametersCache } = result;
8300
+ if (!result.ok) {
8301
+ const e = result.error;
8302
+ if (!isLoaderCacheFallbackError(e)) {
8303
+ throw e;
8110
8304
  }
8111
- } catch (e) {
8112
8305
  if (version || environment) {
8113
8306
  throw new Error(`Parameters not found with specified parameters: ${e}`);
8114
8307
  }
8115
8308
  debugLogger.forState(state).warn("Failed to load parameters, attempting to fall back to cache:", e);
8116
8309
  let parameters2;
8117
8310
  if (id) {
8118
- parameters2 = await state.parametersCache.get({ id });
8311
+ parameters2 = await parametersCache.get({ id });
8119
8312
  if (!parameters2) {
8120
8313
  throw new Error(
8121
8314
  `Parameters with id ${id} not found (not found on server or in local cache): ${e}`
8122
8315
  );
8123
8316
  }
8124
8317
  } else {
8125
- parameters2 = await state.parametersCache.get({
8318
+ parameters2 = await parametersCache.get({
8126
8319
  slug,
8127
8320
  projectId,
8128
8321
  projectName,
@@ -8138,6 +8331,7 @@ async function loadParameters({
8138
8331
  }
8139
8332
  return parameters2;
8140
8333
  }
8334
+ const { response } = result;
8141
8335
  if (!("objects" in response) || response.objects.length === 0) {
8142
8336
  if (id) {
8143
8337
  throw new Error(`Parameters with id ${id} not found.`);
@@ -8161,9 +8355,9 @@ async function loadParameters({
8161
8355
  const parameters = new RemoteEvalParameters(metadata);
8162
8356
  try {
8163
8357
  if (id) {
8164
- await state.parametersCache.set({ id }, parameters);
8358
+ await parametersCache.set({ id }, parameters);
8165
8359
  } else if (slug) {
8166
- await state.parametersCache.set(
8360
+ await parametersCache.set(
8167
8361
  { slug, projectId, projectName, version: version ?? "latest" },
8168
8362
  parameters
8169
8363
  );
@@ -8204,6 +8398,52 @@ async function login(options = {}) {
8204
8398
  await state.login(options);
8205
8399
  return state;
8206
8400
  }
8401
+ async function loginToLoaderRequestState({
8402
+ appUrl,
8403
+ apiKey,
8404
+ orgName,
8405
+ fetch: fetch2
8406
+ }) {
8407
+ let orgId;
8408
+ let apiUrl;
8409
+ if (apiKey === TEST_API_KEY) {
8410
+ orgId = "test-org-id";
8411
+ apiUrl = "https://braintrust.dev/fake-api-url";
8412
+ } else {
8413
+ let loginResponse;
8414
+ try {
8415
+ loginResponse = await fetch2(_urljoin(appUrl, `/api/apikey/login`), {
8416
+ method: "POST",
8417
+ headers: {
8418
+ "Content-Type": "application/json",
8419
+ Authorization: `Bearer ${apiKey}`
8420
+ }
8421
+ });
8422
+ } catch (error) {
8423
+ throw new HTTPTransportError(error);
8424
+ }
8425
+ const info = await readJSONResponse(
8426
+ await checkResponse(loginResponse),
8427
+ true
8428
+ );
8429
+ const org = selectLoginOrg(info.org_info, orgName);
8430
+ orgId = org.id;
8431
+ apiUrl = isomorph_default.getEnv("BRAINTRUST_API_URL") ?? org.api_url;
8432
+ if (!apiUrl) {
8433
+ throw new Error(
8434
+ 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."
8435
+ );
8436
+ }
8437
+ }
8438
+ const apiConnection = new HTTPConnection(apiUrl, fetch2, true);
8439
+ apiConnection.set_token(apiKey);
8440
+ apiConnection.make_long_lived();
8441
+ return {
8442
+ appUrl,
8443
+ orgId,
8444
+ apiConn: () => apiConnection
8445
+ };
8446
+ }
8207
8447
  async function loginToState(options = {}) {
8208
8448
  const {
8209
8449
  appUrl = isomorph_default.getEnv("BRAINTRUST_APP_URL") || "https://www.braintrust.dev",
@@ -8235,16 +8475,18 @@ async function loginToState(options = {}) {
8235
8475
  _saveOrgInfo(state, testOrgInfo, testOrgInfo[0].name);
8236
8476
  return state;
8237
8477
  } else {
8238
- const resp = await checkResponse(
8239
- await fetch2(_urljoin(state.appUrl, `/api/apikey/login`), {
8478
+ const loginResponse = await fetch2(
8479
+ _urljoin(state.appUrl, `/api/apikey/login`),
8480
+ {
8240
8481
  method: "POST",
8241
8482
  headers: {
8242
8483
  "Content-Type": "application/json",
8243
8484
  Authorization: `Bearer ${apiKey}`
8244
8485
  }
8245
- })
8486
+ }
8246
8487
  );
8247
- const info = await resp.json();
8488
+ const resp = await checkResponse(loginResponse);
8489
+ const info = await readJSONResponse(resp);
8248
8490
  _saveOrgInfo(state, info.org_info, orgName);
8249
8491
  if (!state.apiUrl) {
8250
8492
  if (orgName) {
@@ -8747,11 +8989,11 @@ function wrapTraced(fn, args) {
8747
8989
  }
8748
8990
  if (args?.asyncFlush) {
8749
8991
  return ((...fnArgs) => traced((span) => {
8750
- if (!hasExplicitInput) {
8992
+ if (!args?.noTraceIO && !hasExplicitInput) {
8751
8993
  span.log({ input: fnArgs });
8752
8994
  }
8753
8995
  const output = fn(...fnArgs);
8754
- if (!hasExplicitOutput) {
8996
+ if (!args?.noTraceIO && !hasExplicitOutput) {
8755
8997
  if (output instanceof Promise) {
8756
8998
  return (async () => {
8757
8999
  const result = await output;
@@ -8766,12 +9008,12 @@ function wrapTraced(fn, args) {
8766
9008
  }, spanArgs));
8767
9009
  } else {
8768
9010
  return ((...fnArgs) => traced(async (span) => {
8769
- if (!hasExplicitInput) {
9011
+ if (!args?.noTraceIO && !hasExplicitInput) {
8770
9012
  span.log({ input: fnArgs });
8771
9013
  }
8772
9014
  const outputResult = fn(...fnArgs);
8773
9015
  const output = await outputResult;
8774
- if (!hasExplicitOutput) {
9016
+ if (!args?.noTraceIO && !hasExplicitOutput) {
8775
9017
  span.log({ output });
8776
9018
  }
8777
9019
  return output;
@@ -8788,6 +9030,15 @@ function _internalStartSpanWithInitialMerge(args) {
8788
9030
  [INITIAL_SPAN_WRITE_AS_MERGE]: true
8789
9031
  }).span;
8790
9032
  }
9033
+ function _internalStartSpanWithInitialMergeAndParentSpanIds(args) {
9034
+ return startSpanAndIsLogger(
9035
+ {
9036
+ ...args,
9037
+ [INITIAL_SPAN_WRITE_AS_MERGE]: true
9038
+ },
9039
+ { useParentSpanIdsForObjectParent: true }
9040
+ ).span;
9041
+ }
8791
9042
  function _internalStartSpanWithContext(args, context) {
8792
9043
  return startSpanAndIsLogger({
8793
9044
  ...args,
@@ -8801,7 +9052,7 @@ async function flush(options) {
8801
9052
  function setFetch(fetch2) {
8802
9053
  _internalGetGlobalState().setFetch(fetch2);
8803
9054
  }
8804
- function startSpanAndIsLogger(args) {
9055
+ function startSpanAndIsLogger(args, internalOptions) {
8805
9056
  const state = args?.state ?? _globalState;
8806
9057
  const { parentObject, propagatedState } = getSpanParentObjectAndPropagatedState({
8807
9058
  asyncFlush: args?.asyncFlush,
@@ -8815,7 +9066,7 @@ function startSpanAndIsLogger(args) {
8815
9066
  ) ? {
8816
9067
  spanId: parentObject.data.span_id,
8817
9068
  rootSpanId: parentObject.data.root_span_id
8818
- } : void 0;
9069
+ } : internalOptions?.useParentSpanIdsForObjectParent ? args?.parentSpanIds : void 0;
8819
9070
  const { parent: _ignoredParent, ...spanArgs } = args ?? {};
8820
9071
  const span = new SpanImpl({
8821
9072
  state,
@@ -8897,27 +9148,28 @@ async function* asyncGeneratorWithCurrent(span, gen, state = void 0) {
8897
9148
  function withParent(parent, callback, state = void 0) {
8898
9149
  return (state ?? _globalState).currentParent.run(parent, () => callback());
8899
9150
  }
8900
- function _saveOrgInfo(state, org_info, org_name) {
8901
- if (org_info.length === 0) {
9151
+ function _saveOrgInfo(state, orgInfo, orgName) {
9152
+ const org = selectLoginOrg(orgInfo, orgName);
9153
+ state.orgId = org.id;
9154
+ state.orgName = org.name;
9155
+ state.apiUrl = isomorph_default.getEnv("BRAINTRUST_API_URL") ?? org.api_url;
9156
+ state.proxyUrl = isomorph_default.getEnv("BRAINTRUST_PROXY_URL") ?? org.proxy_url;
9157
+ state.gitMetadataSettings = org.git_metadata || void 0;
9158
+ }
9159
+ function selectLoginOrg(orgInfo, orgName) {
9160
+ if (orgInfo.length === 0) {
8902
9161
  throw new LoginInvalidOrgError(
8903
9162
  "This user is not part of any organizations."
8904
9163
  );
8905
9164
  }
8906
- for (const org of org_info) {
8907
- if (org_name === void 0 || org.name === org_name) {
8908
- state.orgId = org.id;
8909
- state.orgName = org.name;
8910
- state.apiUrl = isomorph_default.getEnv("BRAINTRUST_API_URL") ?? org.api_url;
8911
- state.proxyUrl = isomorph_default.getEnv("BRAINTRUST_PROXY_URL") ?? org.proxy_url;
8912
- state.gitMetadataSettings = org.git_metadata || void 0;
8913
- break;
9165
+ for (const org of orgInfo) {
9166
+ if (orgName === void 0 || org.name === orgName) {
9167
+ return org;
8914
9168
  }
8915
9169
  }
8916
- if (state.orgId === void 0) {
8917
- throw new LoginInvalidOrgError(
8918
- `Organization ${org_name} not found. Must be one of ${org_info.map((x) => x.name).join(", ")}`
8919
- );
8920
- }
9170
+ throw new LoginInvalidOrgError(
9171
+ `Organization ${orgName} not found. Must be one of ${orgInfo.map((org) => org.name).join(", ")}`
9172
+ );
8921
9173
  }
8922
9174
  function validateTags(tags) {
8923
9175
  const seen = /* @__PURE__ */ new Set();
@@ -10823,6 +11075,14 @@ var Prompt2 = class _Prompt {
10823
11075
  static isPrompt(data) {
10824
11076
  return typeof data === "object" && data !== null && "__braintrust_prompt_marker" in data;
10825
11077
  }
11078
+ /** @internal */
11079
+ _internalSerializeForCache() {
11080
+ return {
11081
+ metadata: this.metadata,
11082
+ defaults: this.defaults,
11083
+ noTrace: this.noTrace
11084
+ };
11085
+ }
10826
11086
  static fromPromptData(name, promptData) {
10827
11087
  return new _Prompt(
10828
11088
  {
@@ -10863,6 +11123,10 @@ var RemoteEvalParameters = class {
10863
11123
  get data() {
10864
11124
  return this.metadata.function_data.data ?? {};
10865
11125
  }
11126
+ /** @internal */
11127
+ _internalSerializeForCache() {
11128
+ return { metadata: this.metadata };
11129
+ }
10866
11130
  validate(data) {
10867
11131
  if (typeof data !== "object" || data === null) {
10868
11132
  return false;
@@ -11723,56 +11987,14 @@ function suppressionStore() {
11723
11987
  autoInstrumentationSuppressionStore ??= isomorph_default.newAsyncLocalStorage();
11724
11988
  return autoInstrumentationSuppressionStore;
11725
11989
  }
11726
- function currentFrames() {
11727
- return suppressionStore().getStore()?.frames ?? [];
11728
- }
11729
11990
  function isAutoInstrumentationSuppressed() {
11730
- const frames = currentFrames();
11731
- return frames[frames.length - 1]?.mode === "suppress";
11991
+ return suppressionStore().getStore() === true;
11732
11992
  }
11733
11993
  function runWithAutoInstrumentationSuppressed(callback) {
11734
- const frame = {
11735
- id: /* @__PURE__ */ Symbol("braintrust.auto-instrumentation-suppress"),
11736
- mode: "suppress"
11737
- };
11738
- return suppressionStore().run(
11739
- { frames: [...currentFrames(), frame] },
11740
- callback
11741
- );
11994
+ return suppressionStore().run(true, callback);
11742
11995
  }
11743
- function bindAutoInstrumentationSuppressionToStart(tracingChannel) {
11744
- const startChannel = tracingChannel.start;
11745
- if (!startChannel) {
11746
- return void 0;
11747
- }
11748
- const store = suppressionStore();
11749
- startChannel.bindStore(store, () => ({
11750
- frames: [
11751
- ...currentFrames(),
11752
- {
11753
- id: /* @__PURE__ */ Symbol("braintrust.auto-instrumentation-suppress"),
11754
- mode: "suppress"
11755
- }
11756
- ]
11757
- }));
11758
- return () => {
11759
- startChannel.unbindStore(store);
11760
- };
11761
- }
11762
- function enterAutoInstrumentationAllowed() {
11763
- const frame = {
11764
- id: /* @__PURE__ */ Symbol("braintrust.auto-instrumentation-allow"),
11765
- mode: "allow"
11766
- };
11767
- suppressionStore().enterWith({
11768
- frames: [...currentFrames(), frame]
11769
- });
11770
- return () => {
11771
- const frames = currentFrames().filter(
11772
- (candidate) => candidate.id !== frame.id
11773
- );
11774
- suppressionStore().enterWith(frames.length > 0 ? { frames } : void 0);
11775
- };
11996
+ function runWithAutoInstrumentationAllowed(callback) {
11997
+ return suppressionStore().run(void 0, callback);
11776
11998
  }
11777
11999
 
11778
12000
  // src/instrumentation/core/channel-tracing.ts
@@ -13025,7 +13247,7 @@ var AISDKPlugin = class extends BasePlugin {
13025
13247
  }
13026
13248
  subscribeToAISDK() {
13027
13249
  const denyOutputPaths = this.config.denyOutputPaths || DEFAULT_DENY_OUTPUT_PATHS;
13028
- this.unsubscribers.push(subscribeToAISDKV7TelemetryDispatcher());
13250
+ this.unsubscribers.push(interceptAISDKV7TelemetryDispatcher());
13029
13251
  this.unsubscribers.push(subscribeToHarnessAgentCreateSession());
13030
13252
  this.unsubscribers.push(
13031
13253
  subscribeToHarnessContinuation(
@@ -13053,6 +13275,18 @@ var AISDKPlugin = class extends BasePlugin {
13053
13275
  aggregateChunks: aggregateAISDKChunks
13054
13276
  })
13055
13277
  );
13278
+ this.unsubscribers.push(
13279
+ traceAsyncChannel(aiSDKChannels.generateImage, {
13280
+ name: "generateImage",
13281
+ type: "llm" /* LLM */,
13282
+ extractInput: ([params], event) => prepareAISDKGenerateImageInput(params, event.self),
13283
+ extractOutput: (result, endEvent) => processAISDKGenerateImageOutput(
13284
+ result,
13285
+ resolveDenyOutputPaths(endEvent, denyOutputPaths)
13286
+ ),
13287
+ extractMetrics: (result) => extractTokenMetrics(result)
13288
+ })
13289
+ );
13056
13290
  this.unsubscribers.push(
13057
13291
  traceStreamingChannel(aiSDKChannels.streamText, {
13058
13292
  name: "streamText",
@@ -13542,26 +13776,29 @@ function subscribeToHarnessContinuation(continuationChannel, defaultDenyOutputPa
13542
13776
  channel.unsubscribe(handlers);
13543
13777
  };
13544
13778
  }
13545
- function subscribeToAISDKV7TelemetryDispatcher() {
13546
- const channel = aiSDKChannels.v7CreateTelemetryDispatcher.tracingChannel();
13779
+ function interceptAISDKV7TelemetryDispatcher() {
13547
13780
  const telemetry = braintrustAISDKTelemetry();
13548
- const handlers = {
13549
- end: (event) => {
13550
- const telemetryOptions = event.arguments?.[0]?.telemetry;
13551
- if (telemetryOptions?.isEnabled === false) {
13552
- return;
13781
+ return aiSDKChannels.v7CreateTelemetryDispatcher.intercept(
13782
+ (target, thisArg, args) => {
13783
+ const dispatcher = Reflect.apply(target, thisArg, args);
13784
+ const telemetryOptions = args[0]?.telemetry;
13785
+ if (telemetryOptions?.isEnabled !== false) {
13786
+ try {
13787
+ patchAISDKV7TelemetryDispatcher(
13788
+ dispatcher,
13789
+ telemetry,
13790
+ telemetryOptions
13791
+ );
13792
+ } catch (error) {
13793
+ debugLogger.error(
13794
+ "Error instrumenting AI SDK v7 telemetry dispatcher:",
13795
+ error
13796
+ );
13797
+ }
13553
13798
  }
13554
- patchAISDKV7TelemetryDispatcher(
13555
- event.result,
13556
- telemetry,
13557
- telemetryOptions
13558
- );
13799
+ return dispatcher;
13559
13800
  }
13560
- };
13561
- channel.subscribe(handlers);
13562
- return () => {
13563
- channel.unsubscribe(handlers);
13564
- };
13801
+ );
13565
13802
  }
13566
13803
  function patchAISDKV7TelemetryDispatcher(dispatcher, telemetry, telemetryOptions) {
13567
13804
  if (!isObject(dispatcher)) {
@@ -13886,16 +14123,10 @@ var convertImageToAttachment = (image, explicitMimeType) => {
13886
14123
  }
13887
14124
  }
13888
14125
  if (explicitMimeType) {
13889
- if (image instanceof Uint8Array) {
14126
+ const blob = convertDataToBlob(image, explicitMimeType);
14127
+ if (blob) {
13890
14128
  return new Attachment({
13891
- data: new Blob([image], { type: explicitMimeType }),
13892
- filename: `image.${getExtensionFromMediaType(explicitMimeType)}`,
13893
- contentType: explicitMimeType
13894
- });
13895
- }
13896
- if (typeof Buffer !== "undefined" && Buffer.isBuffer(image)) {
13897
- return new Attachment({
13898
- data: new Blob([image], { type: explicitMimeType }),
14129
+ data: blob,
13899
14130
  filename: `image.${getExtensionFromMediaType(explicitMimeType)}`,
13900
14131
  contentType: explicitMimeType
13901
14132
  });
@@ -13949,6 +14180,25 @@ var convertDataToAttachment = (data, mimeType, filename) => {
13949
14180
  function processAISDKCallInput(params) {
13950
14181
  return processInputAttachmentsSync(params);
13951
14182
  }
14183
+ function processAISDKGenerateImageInput(params) {
14184
+ const prompt = params.prompt;
14185
+ if (!isObject(prompt) || Array.isArray(prompt)) {
14186
+ return processAISDKCallInput(params);
14187
+ }
14188
+ const processedPrompt = { ...prompt };
14189
+ if (Array.isArray(prompt.images)) {
14190
+ processedPrompt.images = prompt.images.map(
14191
+ (image) => convertImageToAttachment(image, "image/png") ?? image
14192
+ );
14193
+ }
14194
+ if (prompt.mask !== void 0) {
14195
+ processedPrompt.mask = convertImageToAttachment(prompt.mask, "image/png") ?? prompt.mask;
14196
+ }
14197
+ return processAISDKCallInput({
14198
+ ...params,
14199
+ prompt: processedPrompt
14200
+ });
14201
+ }
13952
14202
  function processAISDKWorkflowAgentCallInput(params) {
13953
14203
  const processed = processAISDKCallInput(params);
13954
14204
  return {
@@ -14087,6 +14337,12 @@ function prepareAISDKEmbedInput(params, self) {
14087
14337
  metadata: extractMetadataFromEmbedParams(params, self)
14088
14338
  };
14089
14339
  }
14340
+ function prepareAISDKGenerateImageInput(params, self) {
14341
+ return {
14342
+ input: processAISDKGenerateImageInput(params).input,
14343
+ metadata: extractMetadataFromCallParams(params, self)
14344
+ };
14345
+ }
14090
14346
  function prepareAISDKRerankInput(params, self) {
14091
14347
  const { documents, query } = params;
14092
14348
  return {
@@ -15462,6 +15718,57 @@ function processAISDKOutput(output, denyOutputPaths) {
15462
15718
  }
15463
15719
  return normalizeAISDKLoggedOutput(sanitized);
15464
15720
  }
15721
+ function processAISDKGenerateImageOutput(output, denyOutputPaths) {
15722
+ if (!output || typeof output !== "object") {
15723
+ return output;
15724
+ }
15725
+ const summarized = {};
15726
+ for (const field of [
15727
+ "usage",
15728
+ "warnings",
15729
+ "providerMetadata",
15730
+ "experimental_providerMetadata",
15731
+ "responses"
15732
+ ]) {
15733
+ const value = safeSerializableFieldRead(output, field);
15734
+ if (value !== void 0 && isSerializableOutputValue(value)) {
15735
+ summarized[field] = value;
15736
+ }
15737
+ }
15738
+ const images = safeSerializableFieldRead(output, "images");
15739
+ const image = safeSerializableFieldRead(output, "image");
15740
+ const generatedFiles = Array.isArray(images) && images.length > 0 ? images : image !== void 0 ? [image] : [];
15741
+ const loggedOutput = normalizeAISDKLoggedOutput(
15742
+ omit(summarized, denyOutputPaths)
15743
+ );
15744
+ if (generatedFiles.length > 0) {
15745
+ loggedOutput.images = generatedFiles.map(
15746
+ (file, index) => convertAISDKGeneratedFileToAttachment(file, index)
15747
+ );
15748
+ }
15749
+ return loggedOutput;
15750
+ }
15751
+ function convertAISDKGeneratedFileToAttachment(file, index) {
15752
+ if (!file || typeof file !== "object") {
15753
+ return file;
15754
+ }
15755
+ const generatedFile = file;
15756
+ const generatedMediaType = safeSerializableFieldRead(
15757
+ generatedFile,
15758
+ "mediaType"
15759
+ );
15760
+ const mediaType = typeof generatedMediaType === "string" ? generatedMediaType : "application/octet-stream";
15761
+ const data = safeSerializableFieldRead(generatedFile, "base64") ?? safeSerializableFieldRead(generatedFile, "uint8Array");
15762
+ const blob = convertDataToBlob(data, mediaType);
15763
+ if (blob) {
15764
+ return new Attachment({
15765
+ data: blob,
15766
+ filename: `generated_image_${index}.${getExtensionFromMediaType(mediaType)}`,
15767
+ contentType: mediaType
15768
+ });
15769
+ }
15770
+ return file;
15771
+ }
15465
15772
  function processAISDKEmbeddingOutput(output, denyOutputPaths) {
15466
15773
  if (!output || typeof output !== "object") {
15467
15774
  return output;
@@ -15950,7 +16257,6 @@ function omit(obj, paths, deletePaths = []) {
15950
16257
  function braintrustAISDKTelemetry() {
15951
16258
  const operations = /* @__PURE__ */ new Map();
15952
16259
  const operationKeysByCallId = /* @__PURE__ */ new Map();
15953
- const workflowOperationKeyStore = isomorph_default.newAsyncLocalStorage();
15954
16260
  const modelSpans = /* @__PURE__ */ new Map();
15955
16261
  const objectSpans = /* @__PURE__ */ new Map();
15956
16262
  const embedSpans = /* @__PURE__ */ new Map();
@@ -15993,9 +16299,6 @@ function braintrustAISDKTelemetry() {
15993
16299
  return;
15994
16300
  }
15995
16301
  operations.delete(operationKey);
15996
- if (workflowOperationKeyStore.getStore() === operationKey) {
15997
- workflowOperationKeyStore.enterWith(void 0);
15998
- }
15999
16302
  const keys = operationKeysByCallId.get(state.callId);
16000
16303
  if (!keys) {
16001
16304
  return;
@@ -16043,14 +16346,7 @@ function braintrustAISDKTelemetry() {
16043
16346
  return key;
16044
16347
  }
16045
16348
  }
16046
- const workflowOperationKey = workflowOperationKeyStore.getStore();
16047
- if (workflowOperationKey && keys.includes(workflowOperationKey)) {
16048
- return workflowOperationKey;
16049
- }
16050
- if (callId === "workflow-agent") {
16051
- return void 0;
16052
- }
16053
- return mode === "finish" ? keys[0] : keys[keys.length - 1];
16349
+ return callId === "workflow-agent" || mode === "active" ? keys[keys.length - 1] : keys[0];
16054
16350
  };
16055
16351
  const operationKeyFromEvent = (event, mode = "active") => {
16056
16352
  const explicit = explicitOperationKey(event);
@@ -16064,17 +16360,13 @@ function braintrustAISDKTelemetry() {
16064
16360
  if (operationKey) {
16065
16361
  return operationKey;
16066
16362
  }
16067
- const workflowOperationKey2 = workflowOperationKeyStore.getStore();
16068
- if (workflowOperationKey2 && operations.has(workflowOperationKey2)) {
16069
- return workflowOperationKey2;
16363
+ const workflowAgentKeys2 = operationKeysByCallId.get("workflow-agent");
16364
+ if (workflowAgentKeys2?.length) {
16365
+ return workflowAgentKeys2[workflowAgentKeys2.length - 1];
16070
16366
  }
16071
16367
  return callId === "workflow-agent" ? void 0 : callId;
16072
16368
  }
16073
16369
  }
16074
- const workflowOperationKey = workflowOperationKeyStore.getStore();
16075
- if (workflowOperationKey && operations.has(workflowOperationKey)) {
16076
- return workflowOperationKey;
16077
- }
16078
16370
  const wrapperSpan = currentWorkflowAgentWrapperSpan();
16079
16371
  if (wrapperSpan?.spanId) {
16080
16372
  for (const [operationKey, state] of operations) {
@@ -16084,8 +16376,8 @@ function braintrustAISDKTelemetry() {
16084
16376
  }
16085
16377
  }
16086
16378
  const workflowAgentKeys = operationKeysByCallId.get("workflow-agent");
16087
- if (workflowAgentKeys?.length === 1) {
16088
- return workflowAgentKeys[0];
16379
+ if (workflowAgentKeys?.length) {
16380
+ return workflowAgentKeys[workflowAgentKeys.length - 1];
16089
16381
  }
16090
16382
  if (operations.size === 1) {
16091
16383
  return operations.keys().next().value;
@@ -16288,9 +16580,6 @@ function braintrustAISDKTelemetry() {
16288
16580
  if (!ownsSpan) {
16289
16581
  return;
16290
16582
  }
16291
- if (workflowAgent) {
16292
- workflowOperationKeyStore.enterWith(operationKey);
16293
- }
16294
16583
  let metadata = metadataFromEvent(event);
16295
16584
  const logPayload = { metadata };
16296
16585
  const workflowAgentCallInput = workflowAgent ? operationInput(event, operationName) : void 0;
@@ -18284,12 +18573,14 @@ function getMetricsFromResponse(response) {
18284
18573
  continue;
18285
18574
  }
18286
18575
  const inputTokenDetails = usageMetadata.input_token_details;
18576
+ const outputTokenDetails = usageMetadata.output_token_details;
18287
18577
  return normalizeTokenMetrics({
18288
18578
  total_tokens: usageMetadata.total_tokens,
18289
18579
  prompt_tokens: usageMetadata.input_tokens,
18290
18580
  completion_tokens: usageMetadata.output_tokens,
18291
18581
  prompt_cache_creation_tokens: isRecord(inputTokenDetails) ? inputTokenDetails.cache_creation : void 0,
18292
- prompt_cached_tokens: isRecord(inputTokenDetails) ? inputTokenDetails.cache_read : void 0
18582
+ prompt_cached_tokens: isRecord(inputTokenDetails) ? inputTokenDetails.cache_read : void 0,
18583
+ completion_reasoning_tokens: isRecord(outputTokenDetails) ? outputTokenDetails.reasoning : void 0
18293
18584
  });
18294
18585
  }
18295
18586
  const llmOutput = response.llmOutput || {};
@@ -18311,23 +18602,659 @@ function isRecord(value) {
18311
18602
  return typeof value === "object" && value !== null && !Array.isArray(value);
18312
18603
  }
18313
18604
 
18605
+ // src/instrumentation/plugins/openai-span-data.ts
18606
+ var OPENAI_METADATA_KEYS = [
18607
+ "model",
18608
+ "temperature",
18609
+ "top_p",
18610
+ "max_tokens",
18611
+ "frequency_penalty",
18612
+ "presence_penalty",
18613
+ "stop",
18614
+ "response_format",
18615
+ "tools",
18616
+ "tool_choice",
18617
+ "parallel_tool_calls",
18618
+ "max_tool_calls"
18619
+ ];
18620
+ function batchMetadata(params) {
18621
+ const metadata = { provider: "openai" };
18622
+ for (const key of OPENAI_METADATA_KEYS) {
18623
+ try {
18624
+ const value = Reflect.get(params, key);
18625
+ if (value !== void 0) {
18626
+ metadata[key] = value;
18627
+ }
18628
+ } catch {
18629
+ }
18630
+ }
18631
+ return metadata;
18632
+ }
18633
+ function extractOpenAIBatchInput(endpoint, params) {
18634
+ const input = endpoint === "/v1/chat/completions" ? params.messages : params.input;
18635
+ return {
18636
+ input: processInputAttachments(input),
18637
+ metadata: batchMetadata(params)
18638
+ };
18639
+ }
18640
+ function extractOpenAIChatInput(params) {
18641
+ const { messages, ...metadata } = params;
18642
+ return {
18643
+ input: processInputAttachments(messages),
18644
+ metadata: { ...metadata, provider: "openai" }
18645
+ };
18646
+ }
18647
+ function extractOpenAIResponsesInput(params) {
18648
+ const { input, ...metadata } = params;
18649
+ return {
18650
+ input: processInputAttachments(input),
18651
+ metadata: { ...metadata, provider: "openai" }
18652
+ };
18653
+ }
18654
+ function extractOpenAIResponsesMetadata(result) {
18655
+ if (!result) {
18656
+ return void 0;
18657
+ }
18658
+ const { output: _output, usage: _usage, ...metadata } = result;
18659
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
18660
+ }
18661
+ function processImagesInOutput(output) {
18662
+ if (Array.isArray(output)) {
18663
+ return output.map(processImagesInOutput);
18664
+ }
18665
+ if (isObject(output) && output.type === "image_generation_call" && typeof output.result === "string" && output.result) {
18666
+ const fileExtension = output.output_format || "png";
18667
+ const contentType = `image/${fileExtension}`;
18668
+ const baseFilename = typeof output.revised_prompt === "string" ? output.revised_prompt.slice(0, 50).replace(/[^a-zA-Z0-9]/g, "_") : "generated_image";
18669
+ let binaryString;
18670
+ try {
18671
+ binaryString = atob(output.result);
18672
+ } catch {
18673
+ return output;
18674
+ }
18675
+ const bytes = new Uint8Array(binaryString.length);
18676
+ for (let i = 0; i < binaryString.length; i++) {
18677
+ bytes[i] = binaryString.charCodeAt(i);
18678
+ }
18679
+ return {
18680
+ ...output,
18681
+ result: new Attachment({
18682
+ data: new Blob([bytes], { type: contentType }),
18683
+ filename: `${baseFilename}.${fileExtension}`,
18684
+ contentType
18685
+ })
18686
+ };
18687
+ }
18688
+ return output;
18689
+ }
18690
+
18691
+ // src/instrumentation/plugins/openai-batch-instrumentation.ts
18692
+ var SUPPORTED_ENDPOINTS = /* @__PURE__ */ new Set(["/v1/chat/completions", "/v1/responses"]);
18693
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
18694
+ "completed",
18695
+ "failed",
18696
+ "expired",
18697
+ "cancelled"
18698
+ ]);
18699
+ var pendingBatchTraces = /* @__PURE__ */ new Map();
18700
+ function read(value, key) {
18701
+ if (!isObject(value)) {
18702
+ return void 0;
18703
+ }
18704
+ try {
18705
+ return Reflect.get(value, key);
18706
+ } catch {
18707
+ return void 0;
18708
+ }
18709
+ }
18710
+ function isBatchRecordIterable(value) {
18711
+ if (value === null || typeof value !== "object" && typeof value !== "function") {
18712
+ return false;
18713
+ }
18714
+ return typeof read(value, Symbol.iterator) === "function" || typeof read(value, Symbol.asyncIterator) === "function";
18715
+ }
18716
+ function validCustomId(value) {
18717
+ return typeof value === "string" && value.length > 0 && value.length <= 64;
18718
+ }
18719
+ function logBatchInstrumentationError(context, error) {
18720
+ debugLogger.debug(`OpenAI Batch instrumentation ${context}:`, error);
18721
+ }
18722
+ async function exportParent(parent) {
18723
+ if ("toStr" in parent && typeof parent.toStr === "function") {
18724
+ return parent.toStr();
18725
+ }
18726
+ if ("export" in parent && typeof parent.export === "function") {
18727
+ return await parent.export();
18728
+ }
18729
+ return void 0;
18730
+ }
18731
+ async function deterministicDigest(namespace, ...parts) {
18732
+ const encoded = new TextEncoder().encode(
18733
+ [namespace, ...parts].map((part) => `${part.length}:${part}`).join("\0")
18734
+ );
18735
+ return new Uint8Array(
18736
+ await globalThis.crypto.subtle.digest("SHA-256", encoded)
18737
+ );
18738
+ }
18739
+ function digestHex(bytes, length) {
18740
+ return Array.from(bytes.slice(0, length)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
18741
+ }
18742
+ function digestUuid(bytes) {
18743
+ const hex = digestHex(bytes, 16);
18744
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
18745
+ }
18746
+ async function batchSpanIds(inputFileId) {
18747
+ const [row, span, root] = await Promise.all([
18748
+ deterministicDigest("openai:batch:row", inputFileId),
18749
+ deterministicDigest("openai:batch:span", inputFileId),
18750
+ deterministicDigest("openai:batch:root", inputFileId)
18751
+ ]);
18752
+ return {
18753
+ rowId: digestUuid(row),
18754
+ spanId: digestHex(span, 8),
18755
+ rootSpanId: digestHex(root, 16)
18756
+ };
18757
+ }
18758
+ async function childSpanIds(inputFileId, customId) {
18759
+ const [row, span] = await Promise.all([
18760
+ deterministicDigest("openai:batch:child:row", inputFileId, customId),
18761
+ deterministicDigest("openai:batch:child:span", inputFileId, customId)
18762
+ ]);
18763
+ return { rowId: digestUuid(row), spanId: digestHex(span, 8) };
18764
+ }
18765
+ async function startBatchSpan(context) {
18766
+ const ids = await batchSpanIds(context.inputFileId);
18767
+ const parent = SpanComponentsV4.fromStr(context.parent);
18768
+ const hasParentSpan = Boolean(
18769
+ parent.data.row_id && parent.data.span_id && parent.data.root_span_id
18770
+ );
18771
+ return withCurrent(
18772
+ NOOP_SPAN,
18773
+ () => _internalStartSpanWithInitialMergeAndParentSpanIds(
18774
+ withSpanInstrumentationName(
18775
+ {
18776
+ name: "openai.batch",
18777
+ type: "task" /* TASK */,
18778
+ parent: context.parent,
18779
+ ...!hasParentSpan ? {
18780
+ parentSpanIds: {
18781
+ parentSpanIds: [],
18782
+ rootSpanId: ids.rootSpanId
18783
+ }
18784
+ } : {},
18785
+ spanId: ids.spanId,
18786
+ startTime: context.taskStartTime,
18787
+ event: {
18788
+ id: ids.rowId,
18789
+ metadata: {
18790
+ endpoint: context.endpoint,
18791
+ input_file_id: context.inputFileId,
18792
+ provider: "openai"
18793
+ }
18794
+ }
18795
+ },
18796
+ INSTRUMENTATION_NAMES.OPENAI
18797
+ )
18798
+ )
18799
+ );
18800
+ }
18801
+ async function startBatchChild(context, taskParent, input) {
18802
+ const ids = await childSpanIds(context.inputFileId, input.customId);
18803
+ return withCurrent(
18804
+ NOOP_SPAN,
18805
+ () => _internalStartSpanWithInitialMerge(
18806
+ withSpanInstrumentationName(
18807
+ {
18808
+ name: context.endpoint === "/v1/chat/completions" ? "Chat Completion" : "openai.responses.create",
18809
+ type: "llm" /* LLM */,
18810
+ parent: taskParent,
18811
+ spanId: ids.spanId,
18812
+ startTime: context.childStartTime,
18813
+ event: {
18814
+ id: ids.rowId,
18815
+ ...input.spanData?.input !== void 0 ? { input: input.spanData.input } : {},
18816
+ metadata: {
18817
+ ...input.spanData?.metadata,
18818
+ custom_id: input.customId,
18819
+ provider: "openai"
18820
+ }
18821
+ }
18822
+ },
18823
+ INSTRUMENTATION_NAMES.OPENAI
18824
+ )
18825
+ )
18826
+ );
18827
+ }
18828
+ async function* jsonlRecords(file, onIssue = () => {
18829
+ }) {
18830
+ const resolvedFile = await file;
18831
+ if (typeof resolvedFile === "string") {
18832
+ for (const line of resolvedFile.split("\n")) {
18833
+ if (!line.trim()) {
18834
+ continue;
18835
+ }
18836
+ try {
18837
+ yield JSON.parse(line.replace(/\r$/, ""));
18838
+ } catch (error) {
18839
+ logBatchInstrumentationError("skipped malformed JSONL", error);
18840
+ onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
18841
+ }
18842
+ }
18843
+ return;
18844
+ }
18845
+ const body = read(resolvedFile, "body");
18846
+ const getReader = read(body, "getReader");
18847
+ if (isObject(body) && typeof getReader === "function") {
18848
+ let reader;
18849
+ try {
18850
+ reader = Reflect.apply(getReader, body, []);
18851
+ const decoder = new TextDecoder();
18852
+ let pending = "";
18853
+ while (true) {
18854
+ const readChunk = read(reader, "read");
18855
+ if (typeof readChunk !== "function") {
18856
+ throw new Error("Response body stream has no read method");
18857
+ }
18858
+ const chunk = await Reflect.apply(readChunk, reader, []);
18859
+ if (!isObject(chunk)) {
18860
+ throw new Error("Response body stream returned an invalid chunk");
18861
+ }
18862
+ if (chunk.done === true) {
18863
+ pending += decoder.decode();
18864
+ break;
18865
+ }
18866
+ if (!(chunk.value instanceof Uint8Array)) {
18867
+ throw new Error("Response body stream returned a non-byte chunk");
18868
+ }
18869
+ pending += decoder.decode(chunk.value, { stream: true });
18870
+ let newline = pending.indexOf("\n");
18871
+ while (newline !== -1) {
18872
+ const line = pending.slice(0, newline).replace(/\r$/, "");
18873
+ pending = pending.slice(newline + 1);
18874
+ if (line.trim()) {
18875
+ try {
18876
+ yield JSON.parse(line);
18877
+ } catch (error) {
18878
+ logBatchInstrumentationError("skipped malformed JSONL", error);
18879
+ onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
18880
+ }
18881
+ }
18882
+ newline = pending.indexOf("\n");
18883
+ }
18884
+ }
18885
+ if (pending.trim()) {
18886
+ try {
18887
+ yield JSON.parse(pending.replace(/\r$/, ""));
18888
+ } catch (error) {
18889
+ logBatchInstrumentationError("skipped malformed JSONL", error);
18890
+ onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
18891
+ }
18892
+ }
18893
+ } catch (error) {
18894
+ logBatchInstrumentationError("could not read JSONL response body", error);
18895
+ onIssue(new Error("OpenAI Batch response body could not be read"));
18896
+ } finally {
18897
+ const releaseLock = read(reader, "releaseLock");
18898
+ if (typeof releaseLock === "function") {
18899
+ try {
18900
+ Reflect.apply(releaseLock, reader, []);
18901
+ } catch (error) {
18902
+ logBatchInstrumentationError(
18903
+ "could not release stream reader",
18904
+ error
18905
+ );
18906
+ }
18907
+ }
18908
+ }
18909
+ return;
18910
+ }
18911
+ if (isBatchRecordIterable(resolvedFile)) {
18912
+ for await (const record of resolvedFile) {
18913
+ yield record;
18914
+ }
18915
+ return;
18916
+ }
18917
+ logBatchInstrumentationError("skipped invalid JSONL source", resolvedFile);
18918
+ onIssue(new Error("OpenAI Batch file source is invalid"));
18919
+ }
18920
+ async function readBatchInputs(file) {
18921
+ const inputs = /* @__PURE__ */ new Map();
18922
+ const issues = [];
18923
+ let endpoint;
18924
+ try {
18925
+ for await (const value of jsonlRecords(
18926
+ file,
18927
+ (issue) => issues.push(issue)
18928
+ )) {
18929
+ const customId = read(value, "custom_id");
18930
+ const url = read(value, "url");
18931
+ const body = read(value, "body");
18932
+ if (!validCustomId(customId) || inputs.has(customId) || read(value, "method") !== "POST" || typeof url !== "string" || !SUPPORTED_ENDPOINTS.has(url) || endpoint !== void 0 && endpoint !== url || !isObject(body)) {
18933
+ issues.push(new Error("OpenAI Batch input contains an invalid record"));
18934
+ continue;
18935
+ }
18936
+ endpoint = url;
18937
+ let spanData;
18938
+ try {
18939
+ spanData = extractOpenAIBatchInput(url, body);
18940
+ } catch (error) {
18941
+ logBatchInstrumentationError("could not extract batch input", error);
18942
+ }
18943
+ inputs.set(customId, { customId, spanData });
18944
+ }
18945
+ } catch (error) {
18946
+ logBatchInstrumentationError("could not process input file", error);
18947
+ issues.push(new Error("OpenAI Batch input file could not be processed"));
18948
+ }
18949
+ if (!endpoint || inputs.size === 0) {
18950
+ issues.push(new Error("OpenAI Batch input contains no supported records"));
18951
+ }
18952
+ return { endpoint, inputs, issues };
18953
+ }
18954
+ async function writePendingSpans(trace, endTime) {
18955
+ const task = await startBatchSpan(trace.context);
18956
+ const taskParent = await task.export();
18957
+ for (const input of trace.inputs.values()) {
18958
+ try {
18959
+ const child = await startBatchChild(trace.context, taskParent, input);
18960
+ if (endTime !== void 0) {
18961
+ child.end({ endTime });
18962
+ }
18963
+ } catch (error) {
18964
+ logBatchInstrumentationError("could not write batch request span", error);
18965
+ }
18966
+ }
18967
+ if (endTime !== void 0) {
18968
+ if (trace.status && trace.status !== "completed") {
18969
+ task.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
18970
+ }
18971
+ task.end({ endTime });
18972
+ }
18973
+ }
18974
+ var interceptOpenAIFilesCreateTraced = async (target, thisArg, args) => {
18975
+ const startTime = getCurrentUnixTimestamp();
18976
+ const inputPromise = readBatchInputs(args[0].inputFileContent);
18977
+ const parentPromise = exportParent(args[0].parent);
18978
+ const file = await Reflect.apply(target, thisArg, args);
18979
+ try {
18980
+ const inputFileId = read(file, "id");
18981
+ const [inputData, exportedParent2] = await Promise.all([
18982
+ inputPromise,
18983
+ parentPromise
18984
+ ]);
18985
+ if (typeof inputFileId !== "string" || !exportedParent2 || !inputData.endpoint || inputData.issues.length > 0) {
18986
+ if (inputData.issues[0]) {
18987
+ logBatchInstrumentationError(
18988
+ "skipped invalid input file",
18989
+ inputData.issues[0]
18990
+ );
18991
+ }
18992
+ return file;
18993
+ }
18994
+ const trace = {
18995
+ context: {
18996
+ inputFileId,
18997
+ endpoint: inputData.endpoint,
18998
+ parent: exportedParent2,
18999
+ taskStartTime: startTime,
19000
+ childStartTime: startTime
19001
+ },
19002
+ inputs: inputData.inputs
19003
+ };
19004
+ pendingBatchTraces.set(inputFileId, trace);
19005
+ await writePendingSpans(trace);
19006
+ } catch (error) {
19007
+ logBatchInstrumentationError("could not start batch spans", error);
19008
+ }
19009
+ return file;
19010
+ };
19011
+ function validTimestamp(value) {
19012
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
19013
+ }
19014
+ function terminalEndTime(batch, startTime) {
19015
+ let timestamp;
19016
+ switch (batch.status) {
19017
+ case "completed":
19018
+ timestamp = batch.completed_at;
19019
+ break;
19020
+ case "failed":
19021
+ timestamp = batch.failed_at;
19022
+ break;
19023
+ case "expired":
19024
+ timestamp = batch.expired_at;
19025
+ break;
19026
+ default:
19027
+ timestamp = batch.cancelled_at;
19028
+ }
19029
+ return validTimestamp(timestamp) ? Math.max(timestamp, startTime) : Math.max(getCurrentUnixTimestamp(), startTime);
19030
+ }
19031
+ async function updateBatchTimestamps(batch) {
19032
+ const trace = pendingBatchTraces.get(batch.input_file_id);
19033
+ if (!trace || trace.context.endpoint !== batch.endpoint) {
19034
+ return;
19035
+ }
19036
+ if (validTimestamp(batch.created_at)) {
19037
+ trace.context.taskStartTime = batch.created_at;
19038
+ }
19039
+ trace.context.childStartTime = validTimestamp(batch.in_progress_at) ? Math.max(batch.in_progress_at, trace.context.taskStartTime) : trace.context.taskStartTime;
19040
+ trace.status = batch.status;
19041
+ if (TERMINAL_STATUSES.has(batch.status)) {
19042
+ trace.endTime = terminalEndTime(batch, trace.context.childStartTime);
19043
+ }
19044
+ await writePendingSpans(trace, trace.endTime);
19045
+ }
19046
+ var interceptOpenAIBatchesRetrieveTraced = (target, thisArg, args) => Promise.resolve(Reflect.apply(target, thisArg, args)).then(async (batch) => {
19047
+ try {
19048
+ await updateBatchTimestamps(batch);
19049
+ } catch (error) {
19050
+ logBatchInstrumentationError("could not update batch timestamps", error);
19051
+ }
19052
+ return batch;
19053
+ });
19054
+ function errorFromResult(result) {
19055
+ const error = read(result.value, "error");
19056
+ if (isObject(error)) {
19057
+ const message = read(error, "message");
19058
+ return new Error(
19059
+ typeof message === "string" ? message : "OpenAI Batch request failed"
19060
+ );
19061
+ }
19062
+ const response = read(result.value, "response");
19063
+ const statusCode = read(response, "status_code");
19064
+ if (result.source === "error" || typeof statusCode === "number" && (statusCode < 200 || statusCode >= 300)) {
19065
+ const message = read(read(read(response, "body"), "error"), "message");
19066
+ return new Error(
19067
+ typeof message === "string" ? message : "OpenAI Batch request failed"
19068
+ );
19069
+ }
19070
+ return void 0;
19071
+ }
19072
+ async function completeBatchResult(context, taskParent, endTime, input, result) {
19073
+ const child = await startBatchChild(context, taskParent, input);
19074
+ try {
19075
+ const resultError = errorFromResult(result);
19076
+ const responseBody = read(read(result.value, "response"), "body");
19077
+ if (resultError) {
19078
+ child.log({ error: resultError });
19079
+ } else if (isObject(responseBody)) {
19080
+ const model = read(responseBody, "model");
19081
+ child.log({
19082
+ output: context.endpoint === "/v1/chat/completions" ? read(responseBody, "choices") : processImagesInOutput(read(responseBody, "output")),
19083
+ ...typeof model === "string" ? { metadata: { model } } : {},
19084
+ metrics: parseMetricsFromUsage(read(responseBody, "usage"))
19085
+ });
19086
+ } else {
19087
+ child.log({ error: new Error("OpenAI Batch response body is missing") });
19088
+ }
19089
+ } catch (error) {
19090
+ child.log({ error });
19091
+ } finally {
19092
+ child.end({ endTime });
19093
+ }
19094
+ }
19095
+ async function completeResultFile({
19096
+ context,
19097
+ endTime,
19098
+ file,
19099
+ inputs,
19100
+ issues,
19101
+ seen,
19102
+ source,
19103
+ taskParent
19104
+ }) {
19105
+ if (file === void 0) {
19106
+ return;
19107
+ }
19108
+ for await (const value of jsonlRecords(file, (issue) => issues.push(issue))) {
19109
+ const customId = read(value, "custom_id");
19110
+ if (!validCustomId(customId) || !isObject(value)) {
19111
+ issues.push(
19112
+ new Error("OpenAI Batch result is missing a valid custom_id")
19113
+ );
19114
+ continue;
19115
+ }
19116
+ if (seen.has(customId)) {
19117
+ issues.push(new Error("OpenAI Batch result contains a duplicate"));
19118
+ continue;
19119
+ }
19120
+ const input = inputs.get(customId);
19121
+ if (!input) {
19122
+ issues.push(
19123
+ new Error("OpenAI Batch result does not match an input record")
19124
+ );
19125
+ continue;
19126
+ }
19127
+ seen.add(customId);
19128
+ await completeBatchResult(context, taskParent, endTime, input, {
19129
+ value,
19130
+ source
19131
+ });
19132
+ }
19133
+ }
19134
+ function sameInputs(first, second) {
19135
+ return first.size === second.size && [...first.keys()].every((customId) => second.has(customId));
19136
+ }
19137
+ async function contextForCompletion(inputFileId, inputData) {
19138
+ if (!inputData.endpoint || inputData.issues.length > 0) {
19139
+ return void 0;
19140
+ }
19141
+ const existing = pendingBatchTraces.get(inputFileId);
19142
+ if (existing) {
19143
+ if (existing.context.endpoint !== inputData.endpoint || !sameInputs(existing.inputs, inputData.inputs)) {
19144
+ return void 0;
19145
+ }
19146
+ return { ...existing, inputs: inputData.inputs };
19147
+ }
19148
+ const parent = await exportParent(getSpanParentObject());
19149
+ if (!parent) {
19150
+ return void 0;
19151
+ }
19152
+ const startTime = getCurrentUnixTimestamp();
19153
+ return {
19154
+ context: {
19155
+ inputFileId,
19156
+ endpoint: inputData.endpoint,
19157
+ parent,
19158
+ taskStartTime: startTime,
19159
+ childStartTime: startTime
19160
+ },
19161
+ inputs: inputData.inputs
19162
+ };
19163
+ }
19164
+ async function completeBatch(args) {
19165
+ const inputData = await readBatchInputs(args.inputFileContent);
19166
+ const trace = await contextForCompletion(args.inputFileId, inputData);
19167
+ if (!trace) {
19168
+ logBatchInstrumentationError(
19169
+ "left batch spans pending",
19170
+ inputData.issues[0] ?? new Error("OpenAI Batch input does not match")
19171
+ );
19172
+ return;
19173
+ }
19174
+ const endTime = trace.endTime ?? getCurrentUnixTimestamp();
19175
+ const task = await startBatchSpan(trace.context);
19176
+ const taskParent = await task.export();
19177
+ const issues = [];
19178
+ const seen = /* @__PURE__ */ new Set();
19179
+ await Promise.all([
19180
+ completeResultFile({
19181
+ context: trace.context,
19182
+ endTime,
19183
+ file: args.outputFileContent,
19184
+ inputs: trace.inputs,
19185
+ issues,
19186
+ seen,
19187
+ source: "output",
19188
+ taskParent
19189
+ }),
19190
+ completeResultFile({
19191
+ context: trace.context,
19192
+ endTime,
19193
+ file: args.errorFileContent,
19194
+ inputs: trace.inputs,
19195
+ issues,
19196
+ seen,
19197
+ source: "error",
19198
+ taskParent
19199
+ })
19200
+ ]);
19201
+ if (issues.length > 0) {
19202
+ logBatchInstrumentationError("left batch spans pending", issues[0]);
19203
+ return;
19204
+ }
19205
+ const missing = [...trace.inputs.values()].filter(
19206
+ ({ customId }) => !seen.has(customId)
19207
+ );
19208
+ if (!trace.status || trace.status === "completed") {
19209
+ if (missing.length > 0) {
19210
+ logBatchInstrumentationError(
19211
+ "left batch spans pending",
19212
+ new Error("OpenAI Batch result files are incomplete")
19213
+ );
19214
+ return;
19215
+ }
19216
+ } else {
19217
+ for (const input of missing) {
19218
+ const child = await startBatchChild(trace.context, taskParent, input);
19219
+ child.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
19220
+ child.end({ endTime });
19221
+ }
19222
+ task.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
19223
+ }
19224
+ task.end({ endTime });
19225
+ pendingBatchTraces.delete(args.inputFileId);
19226
+ }
19227
+ var interceptOpenAIBatchTraceComplete = (target, thisArg, args) => Promise.resolve(Reflect.apply(target, thisArg, args)).then(async (result) => {
19228
+ try {
19229
+ await completeBatch(args[0]);
19230
+ } catch (error) {
19231
+ logBatchInstrumentationError("could not complete batch", error);
19232
+ }
19233
+ return result;
19234
+ });
19235
+
18314
19236
  // src/instrumentation/plugins/openai-plugin.ts
18315
19237
  var OpenAIPlugin = class extends BasePlugin {
18316
19238
  constructor() {
18317
19239
  super();
18318
19240
  }
18319
19241
  onEnable() {
19242
+ this.unsubscribers.push(
19243
+ openAIChannels.filesCreateTraced.intercept(
19244
+ interceptOpenAIFilesCreateTraced
19245
+ ),
19246
+ openAIChannels.batchesRetrieveTraced.intercept(
19247
+ interceptOpenAIBatchesRetrieveTraced
19248
+ ),
19249
+ openAIChannels.batchesCompleteTrace.intercept(
19250
+ interceptOpenAIBatchTraceComplete
19251
+ )
19252
+ );
18320
19253
  this.unsubscribers.push(
18321
19254
  traceStreamingChannel(openAIChannels.chatCompletionsCreate, {
18322
19255
  name: "Chat Completion",
18323
19256
  type: "llm" /* LLM */,
18324
- extractInput: ([params]) => {
18325
- const { messages, ...metadata } = params;
18326
- return {
18327
- input: processInputAttachments(messages),
18328
- metadata: { ...metadata, provider: "openai" }
18329
- };
18330
- },
19257
+ extractInput: ([params]) => extractOpenAIChatInput(params),
18331
19258
  extractOutput: (result) => {
18332
19259
  return result?.choices;
18333
19260
  },
@@ -18373,13 +19300,7 @@ var OpenAIPlugin = class extends BasePlugin {
18373
19300
  traceStreamingChannel(openAIChannels.betaChatCompletionsParse, {
18374
19301
  name: "Chat Completion",
18375
19302
  type: "llm" /* LLM */,
18376
- extractInput: ([params]) => {
18377
- const { messages, ...metadata } = params;
18378
- return {
18379
- input: processInputAttachments(messages),
18380
- metadata: { ...metadata, provider: "openai" }
18381
- };
18382
- },
19303
+ extractInput: ([params]) => extractOpenAIChatInput(params),
18383
19304
  extractOutput: (result) => {
18384
19305
  return result?.choices;
18385
19306
  },
@@ -18401,13 +19322,7 @@ var OpenAIPlugin = class extends BasePlugin {
18401
19322
  traceSyncStreamChannel(openAIChannels.betaChatCompletionsStream, {
18402
19323
  name: "Chat Completion",
18403
19324
  type: "llm" /* LLM */,
18404
- extractInput: ([params]) => {
18405
- const { messages, ...metadata } = params;
18406
- return {
18407
- input: processInputAttachments(messages),
18408
- metadata: { ...metadata, provider: "openai" }
18409
- };
18410
- }
19325
+ extractInput: ([params]) => extractOpenAIChatInput(params)
18411
19326
  })
18412
19327
  );
18413
19328
  this.unsubscribers.push(
@@ -18437,23 +19352,11 @@ var OpenAIPlugin = class extends BasePlugin {
18437
19352
  traceStreamingChannel(openAIChannels.responsesCreate, {
18438
19353
  name: "openai.responses.create",
18439
19354
  type: "llm" /* LLM */,
18440
- extractInput: ([params]) => {
18441
- const { input, ...metadata } = params;
18442
- return {
18443
- input: processInputAttachments(input),
18444
- metadata: { ...metadata, provider: "openai" }
18445
- };
18446
- },
19355
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
18447
19356
  extractOutput: (result) => {
18448
19357
  return processImagesInOutput(result?.output);
18449
19358
  },
18450
- extractMetadata: (result) => {
18451
- if (!result) {
18452
- return void 0;
18453
- }
18454
- const { output: _output, usage: _usage, ...metadata } = result;
18455
- return Object.keys(metadata).length > 0 ? metadata : void 0;
18456
- },
19359
+ extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
18457
19360
  extractMetrics: (result, startTime, endEvent) => {
18458
19361
  const metrics = withCachedMetric(
18459
19362
  parseMetricsFromUsage(result?.usage),
@@ -18472,13 +19375,7 @@ var OpenAIPlugin = class extends BasePlugin {
18472
19375
  traceSyncStreamChannel(openAIChannels.responsesStream, {
18473
19376
  name: "openai.responses.create",
18474
19377
  type: "llm" /* LLM */,
18475
- extractInput: ([params]) => {
18476
- const { input, ...metadata } = params;
18477
- return {
18478
- input: processInputAttachments(input),
18479
- metadata: { ...metadata, provider: "openai" }
18480
- };
18481
- },
19378
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
18482
19379
  extractFromEvent: (event) => {
18483
19380
  if (event.type !== "response.completed" || !event.response) {
18484
19381
  return {};
@@ -18501,23 +19398,11 @@ var OpenAIPlugin = class extends BasePlugin {
18501
19398
  traceStreamingChannel(openAIChannels.responsesParse, {
18502
19399
  name: "openai.responses.parse",
18503
19400
  type: "llm" /* LLM */,
18504
- extractInput: ([params]) => {
18505
- const { input, ...metadata } = params;
18506
- return {
18507
- input: processInputAttachments(input),
18508
- metadata: { ...metadata, provider: "openai" }
18509
- };
18510
- },
19401
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
18511
19402
  extractOutput: (result) => {
18512
19403
  return processImagesInOutput(result?.output);
18513
19404
  },
18514
- extractMetadata: (result) => {
18515
- if (!result) {
18516
- return void 0;
18517
- }
18518
- const { output: _output, usage: _usage, ...metadata } = result;
18519
- return Object.keys(metadata).length > 0 ? metadata : void 0;
18520
- },
19405
+ extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
18521
19406
  extractMetrics: (result, startTime, endEvent) => {
18522
19407
  const metrics = withCachedMetric(
18523
19408
  parseMetricsFromUsage(result?.usage),
@@ -18536,23 +19421,11 @@ var OpenAIPlugin = class extends BasePlugin {
18536
19421
  traceAsyncChannel(openAIChannels.responsesCompact, {
18537
19422
  name: "openai.responses.compact",
18538
19423
  type: "llm" /* LLM */,
18539
- extractInput: ([params]) => {
18540
- const { input, ...metadata } = params;
18541
- return {
18542
- input: processInputAttachments(input),
18543
- metadata: { ...metadata, provider: "openai" }
18544
- };
18545
- },
19424
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
18546
19425
  extractOutput: (result) => {
18547
19426
  return processImagesInOutput(result?.output);
18548
19427
  },
18549
- extractMetadata: (result) => {
18550
- if (!result) {
18551
- return void 0;
18552
- }
18553
- const { output: _output, usage: _usage, ...metadata } = result;
18554
- return Object.keys(metadata).length > 0 ? metadata : void 0;
18555
- },
19428
+ extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
18556
19429
  extractMetrics: (result, startTime, endEvent) => {
18557
19430
  const metrics = withCachedMetric(
18558
19431
  parseMetricsFromUsage(result?.usage),
@@ -18608,35 +19481,6 @@ function withCachedMetric(metrics, result, endEvent) {
18608
19481
  cached
18609
19482
  };
18610
19483
  }
18611
- function processImagesInOutput(output) {
18612
- if (Array.isArray(output)) {
18613
- return output.map(processImagesInOutput);
18614
- }
18615
- if (isObject(output)) {
18616
- if (output.type === "image_generation_call" && output.result && typeof output.result === "string") {
18617
- const fileExtension = output.output_format || "png";
18618
- const contentType = `image/${fileExtension}`;
18619
- const baseFilename = output.revised_prompt && typeof output.revised_prompt === "string" ? output.revised_prompt.slice(0, 50).replace(/[^a-zA-Z0-9]/g, "_") : "generated_image";
18620
- const filename = `${baseFilename}.${fileExtension}`;
18621
- const binaryString = atob(output.result);
18622
- const bytes = new Uint8Array(binaryString.length);
18623
- for (let i = 0; i < binaryString.length; i++) {
18624
- bytes[i] = binaryString.charCodeAt(i);
18625
- }
18626
- const blob = new Blob([bytes], { type: contentType });
18627
- const attachment = new Attachment({
18628
- data: blob,
18629
- filename,
18630
- contentType
18631
- });
18632
- return {
18633
- ...output,
18634
- result: attachment
18635
- };
18636
- }
18637
- }
18638
- return output;
18639
- }
18640
19484
  function mergeLogprobTokens(existing, incoming) {
18641
19485
  if (incoming === void 0) {
18642
19486
  return existing;
@@ -18667,13 +19511,33 @@ function aggregateChatLogprobs(existing, incoming) {
18667
19511
  }
18668
19512
  return aggregated;
18669
19513
  }
19514
+ function createAggregatedChatChoice(index) {
19515
+ return {
19516
+ index,
19517
+ role: void 0,
19518
+ content: void 0,
19519
+ refusal: void 0,
19520
+ toolCallsByIndex: /* @__PURE__ */ new Map(),
19521
+ logprobs: void 0,
19522
+ finish_reason: void 0
19523
+ };
19524
+ }
19525
+ function toChatChoice(choice) {
19526
+ const toolCalls = Array.from(choice.toolCallsByIndex.entries()).sort(([left], [right]) => left - right).map(([, toolCall]) => toolCall);
19527
+ return {
19528
+ index: choice.index,
19529
+ message: {
19530
+ role: choice.role,
19531
+ content: choice.content,
19532
+ ...choice.refusal !== void 0 ? { refusal: choice.refusal } : {},
19533
+ tool_calls: toolCalls.length > 0 ? toolCalls : void 0
19534
+ },
19535
+ logprobs: choice.logprobs ?? null,
19536
+ finish_reason: choice.finish_reason
19537
+ };
19538
+ }
18670
19539
  function aggregateChatCompletionChunks(chunks, streamResult, endEvent) {
18671
- let role = void 0;
18672
- let content = void 0;
18673
- let refusal = void 0;
18674
- let tool_calls = void 0;
18675
- let logprobs = void 0;
18676
- let finish_reason = void 0;
19540
+ const choicesByIndex = /* @__PURE__ */ new Map();
18677
19541
  let metrics = {};
18678
19542
  for (const chunk of chunks) {
18679
19543
  if (chunk.usage) {
@@ -18682,62 +19546,75 @@ function aggregateChatCompletionChunks(chunks, streamResult, endEvent) {
18682
19546
  ...parseMetricsFromUsage(chunk.usage)
18683
19547
  };
18684
19548
  }
18685
- const choice = chunk.choices?.[0];
18686
- if (!choice) {
19549
+ const choices = chunk.choices;
19550
+ if (!choices?.length) {
18687
19551
  continue;
18688
19552
  }
18689
- if (choice.finish_reason) {
18690
- finish_reason = choice.finish_reason;
18691
- }
18692
- logprobs = aggregateChatLogprobs(logprobs, choice.logprobs);
18693
- const delta = choice.delta;
18694
- if (!delta) {
18695
- continue;
18696
- }
18697
- if (delta.finish_reason) {
18698
- finish_reason = delta.finish_reason;
18699
- }
18700
- if (!role && delta.role) {
18701
- role = delta.role;
18702
- }
18703
- if (delta.content) {
18704
- content = (content || "") + delta.content;
18705
- }
18706
- if (delta.refusal) {
18707
- refusal = (refusal || "") + delta.refusal;
18708
- }
18709
- if (delta.tool_calls) {
18710
- const toolDelta = delta.tool_calls[0];
18711
- if (!tool_calls || toolDelta.id && tool_calls[tool_calls.length - 1].id !== toolDelta.id) {
18712
- tool_calls = [
18713
- ...tool_calls || [],
18714
- {
18715
- id: toolDelta.id,
18716
- type: toolDelta.type,
18717
- function: toolDelta.function
19553
+ for (const choice of choices) {
19554
+ const choiceIndex = choice.index;
19555
+ let aggregatedChoice = choicesByIndex.get(choiceIndex);
19556
+ if (!aggregatedChoice) {
19557
+ aggregatedChoice = createAggregatedChatChoice(choiceIndex);
19558
+ choicesByIndex.set(choiceIndex, aggregatedChoice);
19559
+ }
19560
+ if (choice.finish_reason) {
19561
+ aggregatedChoice.finish_reason = choice.finish_reason;
19562
+ }
19563
+ aggregatedChoice.logprobs = aggregateChatLogprobs(
19564
+ aggregatedChoice.logprobs,
19565
+ choice.logprobs
19566
+ );
19567
+ const delta = choice.delta;
19568
+ if (!delta) {
19569
+ continue;
19570
+ }
19571
+ if (delta.finish_reason) {
19572
+ aggregatedChoice.finish_reason = delta.finish_reason;
19573
+ }
19574
+ if (!aggregatedChoice.role && delta.role) {
19575
+ aggregatedChoice.role = delta.role;
19576
+ }
19577
+ if (delta.content) {
19578
+ aggregatedChoice.content = (aggregatedChoice.content || "") + delta.content;
19579
+ }
19580
+ if (delta.refusal) {
19581
+ aggregatedChoice.refusal = (aggregatedChoice.refusal || "") + delta.refusal;
19582
+ }
19583
+ if (delta.tool_calls) {
19584
+ for (const toolDelta of delta.tool_calls) {
19585
+ let aggregatedToolCall = aggregatedChoice.toolCallsByIndex.get(
19586
+ toolDelta.index
19587
+ );
19588
+ if (!aggregatedToolCall) {
19589
+ aggregatedToolCall = {
19590
+ function: { arguments: "" }
19591
+ };
19592
+ aggregatedChoice.toolCallsByIndex.set(
19593
+ toolDelta.index,
19594
+ aggregatedToolCall
19595
+ );
18718
19596
  }
18719
- ];
18720
- } else {
18721
- tool_calls[tool_calls.length - 1].function.arguments += toolDelta.function.arguments;
19597
+ if (toolDelta.id !== void 0) {
19598
+ aggregatedToolCall.id = toolDelta.id;
19599
+ }
19600
+ if (toolDelta.type !== void 0) {
19601
+ aggregatedToolCall.type = toolDelta.type;
19602
+ }
19603
+ if (toolDelta.function?.name !== void 0) {
19604
+ aggregatedToolCall.function.name = toolDelta.function.name;
19605
+ }
19606
+ if (toolDelta.function?.arguments !== void 0) {
19607
+ aggregatedToolCall.function.arguments += toolDelta.function.arguments;
19608
+ }
19609
+ }
18722
19610
  }
18723
19611
  }
18724
19612
  }
18725
19613
  metrics = withCachedMetric(metrics, streamResult, endEvent);
19614
+ const output = Array.from(choicesByIndex.values()).sort((left, right) => left.index - right.index).map(toChatChoice);
18726
19615
  return {
18727
19616
  metrics,
18728
- output: [
18729
- {
18730
- index: 0,
18731
- message: {
18732
- role,
18733
- content,
18734
- ...refusal !== void 0 ? { refusal } : {},
18735
- tool_calls
18736
- },
18737
- logprobs: logprobs ?? null,
18738
- finish_reason
18739
- }
18740
- ]
19617
+ output: output.length > 0 ? output : [toChatChoice(createAggregatedChatChoice(0))]
18741
19618
  };
18742
19619
  }
18743
19620
  function aggregateResponseStreamEvents(chunks, _streamResult, endEvent) {
@@ -20010,6 +20887,12 @@ function parseMetricsFromUsage2(usage) {
20010
20887
  }
20011
20888
  }
20012
20889
  }
20890
+ if (isObject(usage.output_tokens_details)) {
20891
+ const thinkingTokens = usage.output_tokens_details.thinking_tokens;
20892
+ if (typeof thinkingTokens === "number") {
20893
+ metrics.completion_reasoning_tokens = thinkingTokens;
20894
+ }
20895
+ }
20013
20896
  if (isObject(usage.server_tool_use)) {
20014
20897
  for (const [name, value] of Object.entries(usage.server_tool_use)) {
20015
20898
  if (typeof value === "number") {
@@ -20575,118 +21458,22 @@ function coalesceInput(messages, system) {
20575
21458
  var CLAUDE_AGENT_SDK_SKIP_LOCAL_TOOL_HOOKS_OPTION = "__braintrust_skip_local_tool_hooks";
20576
21459
 
20577
21460
  // src/instrumentation/plugins/claude-agent-sdk-local-tool-context.ts
20578
- var LOCAL_TOOL_CONTEXT_ASYNC_ITERATOR_PATCHED = /* @__PURE__ */ Symbol.for(
20579
- "braintrust.claude_agent_sdk.local_tool_context_async_iterator_patched"
20580
- );
20581
- function createLocalToolContextStore() {
20582
- const maybeIsoWithAsyncLocalStorage = isomorph_default;
20583
- if (typeof maybeIsoWithAsyncLocalStorage.newAsyncLocalStorage === "function") {
20584
- return maybeIsoWithAsyncLocalStorage.newAsyncLocalStorage();
20585
- }
20586
- let currentStore;
20587
- return {
20588
- enterWith(store) {
20589
- currentStore = store;
20590
- },
20591
- getStore() {
20592
- return currentStore;
20593
- },
20594
- run(store, callback) {
20595
- const previousStore = currentStore;
20596
- currentStore = store;
20597
- try {
20598
- return callback();
20599
- } finally {
20600
- currentStore = previousStore;
20601
- }
20602
- }
20603
- };
20604
- }
20605
- var localToolContextStore = createLocalToolContextStore();
20606
- var fallbackLocalToolParentResolver;
20607
- function createClaudeLocalToolContext() {
20608
- return {};
20609
- }
20610
- function runWithClaudeLocalToolContext(callback, context) {
20611
- return localToolContextStore.run(
20612
- context ?? createClaudeLocalToolContext(),
20613
- callback
20614
- );
21461
+ var localToolContextStore = isomorph_default.newAsyncLocalStorage();
21462
+ var localToolParentResolversByToolUseId = /* @__PURE__ */ new Map();
21463
+ function runWithClaudeLocalToolContext(callback, resolver) {
21464
+ return localToolContextStore.run(resolver, callback);
20615
21465
  }
20616
- function ensureClaudeLocalToolContext() {
20617
- const existing = localToolContextStore.getStore();
20618
- if (existing) {
20619
- return existing;
20620
- }
20621
- const created = {};
20622
- localToolContextStore.enterWith(created);
20623
- return created;
21466
+ function registerClaudeLocalToolParentResolver(toolUseId, resolver) {
21467
+ localToolParentResolversByToolUseId.set(toolUseId, resolver);
20624
21468
  }
20625
- function setClaudeLocalToolParentResolver(resolver) {
20626
- fallbackLocalToolParentResolver = resolver;
20627
- const context = ensureClaudeLocalToolContext();
20628
- if (!context) {
20629
- return;
20630
- }
20631
- context.resolveLocalToolParent = resolver;
20632
- }
20633
- function getClaudeLocalToolParentResolver() {
20634
- return localToolContextStore.getStore()?.resolveLocalToolParent ?? fallbackLocalToolParentResolver;
20635
- }
20636
- function isAsyncIterable3(value) {
20637
- return value !== null && typeof value === "object" && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
20638
- }
20639
- function bindClaudeLocalToolContextToAsyncIterable(result, localToolContext) {
20640
- if (!isAsyncIterable3(result) || Object.isFrozen(result) || Object.isSealed(result)) {
20641
- return result;
20642
- }
20643
- const stream = result;
20644
- const originalAsyncIterator = stream[Symbol.asyncIterator];
20645
- if (originalAsyncIterator[LOCAL_TOOL_CONTEXT_ASYNC_ITERATOR_PATCHED]) {
20646
- return result;
21469
+ function getClaudeLocalToolParentResolver(toolUseId) {
21470
+ const currentResolver = localToolContextStore.getStore();
21471
+ if (!toolUseId) {
21472
+ return currentResolver;
20647
21473
  }
20648
- const patchedAsyncIterator = function() {
20649
- return runWithClaudeLocalToolContext(() => {
20650
- const iterator = Reflect.apply(originalAsyncIterator, this, []);
20651
- if (!iterator || typeof iterator !== "object") {
20652
- return iterator;
20653
- }
20654
- const patchMethod = (methodName) => {
20655
- const originalMethod = Reflect.get(iterator, methodName);
20656
- if (typeof originalMethod !== "function") {
20657
- return;
20658
- }
20659
- Reflect.set(
20660
- iterator,
20661
- methodName,
20662
- (...args) => runWithClaudeLocalToolContext(
20663
- () => Reflect.apply(
20664
- originalMethod,
20665
- iterator,
20666
- args
20667
- ),
20668
- localToolContext
20669
- )
20670
- );
20671
- };
20672
- patchMethod("next");
20673
- patchMethod("return");
20674
- patchMethod("throw");
20675
- return iterator;
20676
- }, localToolContext);
20677
- };
20678
- Object.defineProperty(
20679
- patchedAsyncIterator,
20680
- LOCAL_TOOL_CONTEXT_ASYNC_ITERATOR_PATCHED,
20681
- {
20682
- configurable: false,
20683
- enumerable: false,
20684
- value: true,
20685
- writable: false
20686
- }
20687
- );
20688
- Reflect.set(stream, Symbol.asyncIterator, patchedAsyncIterator);
20689
- return result;
21474
+ const registeredResolver = localToolParentResolversByToolUseId.get(toolUseId);
21475
+ localToolParentResolversByToolUseId.delete(toolUseId);
21476
+ return currentResolver ?? registeredResolver;
20690
21477
  }
20691
21478
 
20692
21479
  // src/instrumentation/plugins/claude-agent-sdk-local-tool-spans.ts
@@ -20712,7 +21499,7 @@ function wrapLocalClaudeToolHandler(handler, getMetadata) {
20712
21499
  const metadata = getMetadata();
20713
21500
  const rawToolName = metadata.serverName ? `mcp__${metadata.serverName}__${metadata.toolName}` : metadata.toolName;
20714
21501
  const toolUseId = getToolUseIdFromExtra(handlerArgs[1]);
20715
- const localToolParentResolver = getClaudeLocalToolParentResolver();
21502
+ const localToolParentResolver = getClaudeLocalToolParentResolver(toolUseId);
20716
21503
  const spanName = metadata.serverName ? `tool: ${metadata.serverName}/${metadata.toolName}` : `tool: ${metadata.toolName}`;
20717
21504
  const runWithResolvedParent = async () => {
20718
21505
  const parent = toolUseId && localToolParentResolver ? await localToolParentResolver(toolUseId).catch(() => void 0) : void 0;
@@ -21239,6 +22026,7 @@ function createToolTracingHooks(resolveParentSpan, taskIdToToolUseId, toolUseToP
21239
22026
  }
21240
22027
  }
21241
22028
  if (skipLocalToolHooks && (isLocalToolUse(input.tool_name, mcpServers) || localToolHookNames.has(input.tool_name))) {
22029
+ registerClaudeLocalToolParentResolver(toolUseID, resolveParentSpan);
21242
22030
  return {};
21243
22031
  }
21244
22032
  const parsed = parseToolName(input.tool_name);
@@ -21949,7 +22737,7 @@ async function finalizeQuerySpan(state) {
21949
22737
  }
21950
22738
  var ClaudeAgentSDKPlugin = class extends BasePlugin {
21951
22739
  onEnable() {
21952
- this.subscribeToQuery();
22740
+ this.interceptQuery();
21953
22741
  }
21954
22742
  onDisable() {
21955
22743
  for (const unsubscribe of this.unsubscribers) {
@@ -21957,211 +22745,218 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
21957
22745
  }
21958
22746
  this.unsubscribers = [];
21959
22747
  }
21960
- subscribeToQuery() {
21961
- const channel = claudeAgentSDKChannels.query.tracingChannel();
21962
- const spans = /* @__PURE__ */ new WeakMap();
21963
- const handlers = {
21964
- start: (event) => {
21965
- const params = event.arguments[0] ?? {};
21966
- const originalPrompt = params.prompt;
21967
- const options = params.options ?? {};
21968
- const promptIsAsyncIterable = isAsyncIterable(originalPrompt);
21969
- let promptStarted = false;
21970
- let capturedPromptMessages;
21971
- let resolvePromptDone;
21972
- const promptDone = new Promise((resolve) => {
21973
- resolvePromptDone = resolve;
21974
- });
21975
- if (promptIsAsyncIterable) {
21976
- capturedPromptMessages = [];
21977
- const promptStream = originalPrompt;
21978
- params.prompt = (async function* () {
21979
- promptStarted = true;
21980
- try {
21981
- for await (const message of promptStream) {
21982
- capturedPromptMessages.push(message);
21983
- yield message;
21984
- }
21985
- } finally {
21986
- resolvePromptDone?.();
22748
+ interceptQuery() {
22749
+ const startQuery = (params) => {
22750
+ const originalPrompt = params.prompt;
22751
+ const options = params.options ?? {};
22752
+ const promptIsAsyncIterable = isAsyncIterable(originalPrompt);
22753
+ let promptStarted = false;
22754
+ let capturedPromptMessages;
22755
+ let resolvePromptDone;
22756
+ const promptDone = new Promise((resolve) => {
22757
+ resolvePromptDone = resolve;
22758
+ });
22759
+ if (promptIsAsyncIterable) {
22760
+ capturedPromptMessages = [];
22761
+ const promptStream = originalPrompt;
22762
+ params.prompt = (async function* () {
22763
+ promptStarted = true;
22764
+ try {
22765
+ for await (const message of promptStream) {
22766
+ capturedPromptMessages.push(message);
22767
+ yield message;
21987
22768
  }
21988
- })();
21989
- }
21990
- const span = startSpan(
21991
- withSpanInstrumentationName(
21992
- {
21993
- name: "Claude Agent",
21994
- spanAttributes: {
21995
- type: "task" /* TASK */
21996
- }
21997
- },
21998
- INSTRUMENTATION_NAMES.CLAUDE_AGENT_SDK
21999
- )
22000
- );
22001
- const startTime = getCurrentUnixTimestamp();
22002
- try {
22003
- span.log({
22004
- input: typeof originalPrompt === "string" ? originalPrompt : promptIsAsyncIterable ? void 0 : originalPrompt !== void 0 ? String(originalPrompt) : void 0,
22005
- metadata: filterSerializableOptions(options)
22006
- });
22007
- } catch (error) {
22008
- console.error("Error extracting input for Claude Agent SDK:", error);
22009
- }
22010
- const activeToolSpans = /* @__PURE__ */ new Map();
22011
- const activeLlmSpansByParentToolUse = /* @__PURE__ */ new Map();
22012
- const conversationHistoryByParentKey = /* @__PURE__ */ new Map();
22013
- const subAgentSpans = /* @__PURE__ */ new Map();
22014
- const endedSubAgentSpans = /* @__PURE__ */ new Set();
22015
- const toolUseToParent = /* @__PURE__ */ new Map();
22016
- const latestLlmParentBySubAgentToolUse = /* @__PURE__ */ new Map();
22017
- const latestRootLlmParentRef = {
22018
- value: void 0
22019
- };
22020
- const subAgentDetailsByToolUseId = /* @__PURE__ */ new Map();
22021
- const taskIdToToolUseId = /* @__PURE__ */ new Map();
22022
- const promptMessagesByParentKey = /* @__PURE__ */ new Map();
22023
- const promptSourcePriorityByParentKey = /* @__PURE__ */ new Map();
22024
- const localToolContext = createClaudeLocalToolContext();
22025
- const { hasLocalToolHandlers, localToolHookNames } = prepareLocalToolHandlersInMcpServers(options.mcpServers);
22026
- const skipLocalToolHooks = options[CLAUDE_AGENT_SDK_SKIP_LOCAL_TOOL_HOOKS_OPTION] === true || hasLocalToolHandlers;
22027
- const resolveToolUseParentSpan = async (toolUseID, context) => {
22028
- const trackedParentToolUseId = toolUseToParent.get(toolUseID);
22029
- const parentToolUseId = trackedParentToolUseId ?? (context?.agentId ? taskIdToToolUseId.get(context.agentId) ?? null : null);
22030
- const parentKey = llmParentKey(parentToolUseId);
22031
- const activeLlmSpan = activeLlmSpansByParentToolUse.get(parentKey);
22032
- const latestLlmParent = parentToolUseId ? latestLlmParentBySubAgentToolUse.get(parentToolUseId) : latestRootLlmParentRef.value;
22033
- if (!activeLlmSpan && !latestLlmParent) {
22034
- await ensureActiveLlmSpanForParentToolUse(
22035
- span,
22036
- activeLlmSpansByParentToolUse,
22037
- subAgentDetailsByToolUseId,
22038
- activeToolSpans,
22039
- subAgentSpans,
22040
- parentToolUseId,
22041
- getCurrentUnixTimestamp()
22042
- );
22043
- }
22044
- if (parentToolUseId) {
22045
- const subAgentSpan = await ensureSubAgentSpan(
22046
- subAgentDetailsByToolUseId,
22047
- span,
22048
- activeToolSpans,
22049
- subAgentSpans,
22050
- parentToolUseId
22051
- );
22052
- return subAgentSpan.export();
22769
+ } finally {
22770
+ resolvePromptDone?.();
22053
22771
  }
22054
- return span.export();
22055
- };
22056
- localToolContext.resolveLocalToolParent = resolveToolUseParentSpan;
22057
- setClaudeLocalToolParentResolver(resolveToolUseParentSpan);
22058
- const optionsWithHooks = injectTracingHooks(
22059
- options,
22060
- resolveToolUseParentSpan,
22061
- taskIdToToolUseId,
22062
- toolUseToParent,
22063
- activeToolSpans,
22064
- localToolHookNames,
22065
- skipLocalToolHooks,
22066
- subAgentDetailsByToolUseId,
22067
- subAgentSpans,
22068
- endedSubAgentSpans
22069
- );
22070
- params.options = optionsWithHooks;
22071
- event.arguments[0] = params;
22072
- spans.set(event, {
22073
- activeLlmSpansByParentToolUse,
22074
- activePartialMessageIdByParentKey: /* @__PURE__ */ new Map(),
22075
- activeToolSpans,
22076
- conversationHistoryByParentKey,
22077
- capturedPromptMessages,
22078
- currentMessageId: void 0,
22079
- currentMessageStartTime: startTime,
22080
- currentMessages: [],
22081
- endedSubAgentSpans,
22082
- finalOutputUsageMessageIds: /* @__PURE__ */ new Set(),
22083
- finalResults: [],
22084
- options: optionsWithHooks,
22085
- originalPrompt,
22086
- processing: Promise.resolve(),
22087
- promptDone,
22088
- promptMessagesByParentKey,
22089
- promptStarted: () => promptStarted,
22090
- promptSourcePriorityByParentKey,
22091
- span,
22092
- subAgentDetailsByToolUseId,
22093
- subAgentSpans,
22094
- taskIdToToolUseId,
22095
- latestLlmParentBySubAgentToolUse,
22096
- latestRootLlmParentRef,
22097
- toolUseToParent,
22098
- usageByMessageId: /* @__PURE__ */ new Map(),
22099
- localToolContext
22772
+ })();
22773
+ }
22774
+ const span = startSpan(
22775
+ withSpanInstrumentationName(
22776
+ {
22777
+ name: "Claude Agent",
22778
+ spanAttributes: {
22779
+ type: "task" /* TASK */
22780
+ }
22781
+ },
22782
+ INSTRUMENTATION_NAMES.CLAUDE_AGENT_SDK
22783
+ )
22784
+ );
22785
+ const startTime = getCurrentUnixTimestamp();
22786
+ try {
22787
+ span.log({
22788
+ input: typeof originalPrompt === "string" ? originalPrompt : promptIsAsyncIterable ? void 0 : originalPrompt !== void 0 ? String(originalPrompt) : void 0,
22789
+ metadata: filterSerializableOptions(options)
22100
22790
  });
22101
- },
22102
- end: (event) => {
22103
- const state = spans.get(event);
22104
- if (!state) {
22105
- return;
22106
- }
22107
- const eventResult = bindClaudeLocalToolContextToAsyncIterable(
22108
- event.result,
22109
- state.localToolContext
22110
- );
22111
- if (eventResult === void 0) {
22112
- state.span.end();
22113
- spans.delete(event);
22114
- return;
22115
- }
22116
- if (isAsyncIterable(eventResult)) {
22117
- patchStreamIfNeeded(eventResult, {
22118
- onChunk: (message) => {
22119
- maybeTrackToolUseContext(state, message);
22120
- state.processing = state.processing.then(() => handleStreamMessage(state, message)).catch((error) => {
22121
- console.error(
22122
- "Error processing Claude Agent SDK stream chunk:",
22123
- error
22124
- );
22125
- });
22126
- },
22127
- onComplete: () => state.processing.then(() => finalizeQuerySpan(state)).finally(() => {
22128
- spans.delete(event);
22129
- }),
22130
- onError: (error) => state.processing.then(() => {
22131
- state.span.log({
22132
- error: error.message
22133
- });
22134
- }).then(() => finalizeQuerySpan(state)).finally(() => {
22135
- spans.delete(event);
22136
- })
22137
- });
22138
- return;
22139
- }
22140
- try {
22141
- state.span.log({ output: eventResult });
22142
- } catch (error) {
22143
- console.error("Error extracting output for Claude Agent SDK:", error);
22144
- } finally {
22145
- state.span.end();
22146
- spans.delete(event);
22791
+ } catch (error) {
22792
+ console.error("Error extracting input for Claude Agent SDK:", error);
22793
+ }
22794
+ const activeToolSpans = /* @__PURE__ */ new Map();
22795
+ const activeLlmSpansByParentToolUse = /* @__PURE__ */ new Map();
22796
+ const conversationHistoryByParentKey = /* @__PURE__ */ new Map();
22797
+ const subAgentSpans = /* @__PURE__ */ new Map();
22798
+ const endedSubAgentSpans = /* @__PURE__ */ new Set();
22799
+ const toolUseToParent = /* @__PURE__ */ new Map();
22800
+ const latestLlmParentBySubAgentToolUse = /* @__PURE__ */ new Map();
22801
+ const latestRootLlmParentRef = {
22802
+ value: void 0
22803
+ };
22804
+ const subAgentDetailsByToolUseId = /* @__PURE__ */ new Map();
22805
+ const taskIdToToolUseId = /* @__PURE__ */ new Map();
22806
+ const promptMessagesByParentKey = /* @__PURE__ */ new Map();
22807
+ const promptSourcePriorityByParentKey = /* @__PURE__ */ new Map();
22808
+ const { hasLocalToolHandlers, localToolHookNames } = prepareLocalToolHandlersInMcpServers(options.mcpServers);
22809
+ const skipLocalToolHooks = options[CLAUDE_AGENT_SDK_SKIP_LOCAL_TOOL_HOOKS_OPTION] === true || hasLocalToolHandlers;
22810
+ const resolveToolUseParentSpan = async (toolUseID, context) => {
22811
+ const trackedParentToolUseId = toolUseToParent.get(toolUseID);
22812
+ const parentToolUseId = trackedParentToolUseId ?? (context?.agentId ? taskIdToToolUseId.get(context.agentId) ?? null : null);
22813
+ const parentKey = llmParentKey(parentToolUseId);
22814
+ const activeLlmSpan = activeLlmSpansByParentToolUse.get(parentKey);
22815
+ const latestLlmParent = parentToolUseId ? latestLlmParentBySubAgentToolUse.get(parentToolUseId) : latestRootLlmParentRef.value;
22816
+ if (!activeLlmSpan && !latestLlmParent) {
22817
+ await ensureActiveLlmSpanForParentToolUse(
22818
+ span,
22819
+ activeLlmSpansByParentToolUse,
22820
+ subAgentDetailsByToolUseId,
22821
+ activeToolSpans,
22822
+ subAgentSpans,
22823
+ parentToolUseId,
22824
+ getCurrentUnixTimestamp()
22825
+ );
22147
22826
  }
22148
- },
22149
- error: (event) => {
22150
- const state = spans.get(event);
22151
- if (!state || !event.error) {
22152
- return;
22827
+ if (parentToolUseId) {
22828
+ const subAgentSpan = await ensureSubAgentSpan(
22829
+ subAgentDetailsByToolUseId,
22830
+ span,
22831
+ activeToolSpans,
22832
+ subAgentSpans,
22833
+ parentToolUseId
22834
+ );
22835
+ return subAgentSpan.export();
22153
22836
  }
22154
- state.span.log({
22155
- error: event.error.message
22837
+ return span.export();
22838
+ };
22839
+ const optionsWithHooks = injectTracingHooks(
22840
+ options,
22841
+ resolveToolUseParentSpan,
22842
+ taskIdToToolUseId,
22843
+ toolUseToParent,
22844
+ activeToolSpans,
22845
+ localToolHookNames,
22846
+ skipLocalToolHooks,
22847
+ subAgentDetailsByToolUseId,
22848
+ subAgentSpans,
22849
+ endedSubAgentSpans
22850
+ );
22851
+ params.options = optionsWithHooks;
22852
+ return {
22853
+ activeLlmSpansByParentToolUse,
22854
+ activePartialMessageIdByParentKey: /* @__PURE__ */ new Map(),
22855
+ activeToolSpans,
22856
+ conversationHistoryByParentKey,
22857
+ capturedPromptMessages,
22858
+ currentMessageId: void 0,
22859
+ currentMessageStartTime: startTime,
22860
+ currentMessages: [],
22861
+ endedSubAgentSpans,
22862
+ finalOutputUsageMessageIds: /* @__PURE__ */ new Set(),
22863
+ finalResults: [],
22864
+ options: optionsWithHooks,
22865
+ originalPrompt,
22866
+ processing: Promise.resolve(),
22867
+ promptDone,
22868
+ promptMessagesByParentKey,
22869
+ promptStarted: () => promptStarted,
22870
+ promptSourcePriorityByParentKey,
22871
+ span,
22872
+ subAgentDetailsByToolUseId,
22873
+ subAgentSpans,
22874
+ taskIdToToolUseId,
22875
+ latestLlmParentBySubAgentToolUse,
22876
+ latestRootLlmParentRef,
22877
+ toolUseToParent,
22878
+ usageByMessageId: /* @__PURE__ */ new Map(),
22879
+ localToolParentResolver: resolveToolUseParentSpan
22880
+ };
22881
+ };
22882
+ const finishQuery = (state, result) => {
22883
+ if (isAsyncIterable(result)) {
22884
+ patchStreamIfNeeded(result, {
22885
+ aroundNext: (callback) => runWithClaudeLocalToolContext(
22886
+ callback,
22887
+ state.localToolParentResolver
22888
+ ),
22889
+ onChunk: (message) => {
22890
+ maybeTrackToolUseContext(state, message);
22891
+ state.processing = state.processing.then(() => handleStreamMessage(state, message)).catch((error) => {
22892
+ console.error(
22893
+ "Error processing Claude Agent SDK stream chunk:",
22894
+ error
22895
+ );
22896
+ });
22897
+ },
22898
+ onComplete: () => state.processing.then(() => finalizeQuerySpan(state)),
22899
+ onError: (error) => state.processing.then(() => {
22900
+ state.span.log({ error: error.message });
22901
+ }).then(() => finalizeQuerySpan(state))
22156
22902
  });
22903
+ return;
22904
+ }
22905
+ try {
22906
+ state.span.log({ output: result });
22907
+ } catch (error) {
22908
+ console.error("Error extracting output for Claude Agent SDK:", error);
22909
+ } finally {
22157
22910
  state.span.end();
22158
- spans.delete(event);
22159
22911
  }
22160
22912
  };
22161
- channel.subscribe(handlers);
22162
- this.unsubscribers.push(() => {
22163
- channel.unsubscribe(handlers);
22164
- });
22913
+ this.unsubscribers.push(
22914
+ claudeAgentSDKChannels.query.intercept((target, thisArg, args) => {
22915
+ let state;
22916
+ try {
22917
+ args[0] ??= {};
22918
+ state = startQuery(args[0]);
22919
+ } catch (error) {
22920
+ debugLogger.error(
22921
+ "Error starting Claude Agent SDK instrumentation:",
22922
+ error
22923
+ );
22924
+ }
22925
+ const invokeTarget = () => Reflect.apply(target, thisArg, args);
22926
+ try {
22927
+ const result = state ? runWithClaudeLocalToolContext(
22928
+ invokeTarget,
22929
+ state.localToolParentResolver
22930
+ ) : invokeTarget();
22931
+ if (state) {
22932
+ try {
22933
+ finishQuery(state, result);
22934
+ } catch (error) {
22935
+ debugLogger.error(
22936
+ "Error finalizing Claude Agent SDK instrumentation:",
22937
+ error
22938
+ );
22939
+ }
22940
+ }
22941
+ return result;
22942
+ } catch (error) {
22943
+ if (state) {
22944
+ try {
22945
+ state.span.log({
22946
+ error: error instanceof Error ? error.message : String(error)
22947
+ });
22948
+ state.span.end();
22949
+ } catch (instrumentationError) {
22950
+ debugLogger.error(
22951
+ "Error handling Claude Agent SDK instrumentation failure:",
22952
+ instrumentationError
22953
+ );
22954
+ }
22955
+ }
22956
+ throw error;
22957
+ }
22958
+ })
22959
+ );
22165
22960
  }
22166
22961
  };
22167
22962
 
@@ -26103,7 +26898,7 @@ function patchOpenRouterCallModelResult(args) {
26103
26898
  span,
26104
26899
  () => originalMethod.apply(resultLike, args2)
26105
26900
  );
26106
- if (!isAsyncIterable4(stream)) {
26901
+ if (!isAsyncIterable3(stream)) {
26107
26902
  return stream;
26108
26903
  }
26109
26904
  return wrapAsyncIterableWithSpan({
@@ -26298,7 +27093,7 @@ function wrapAsyncIterableWithSpan(args) {
26298
27093
  }
26299
27094
  };
26300
27095
  }
26301
- function isAsyncIterable4(value) {
27096
+ function isAsyncIterable3(value) {
26302
27097
  return !!value && (typeof value === "object" || typeof value === "function") && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
26303
27098
  }
26304
27099
  function normalizeError(error) {
@@ -27140,7 +27935,7 @@ function patchOpenRouterCallModelResult2(args) {
27140
27935
  span,
27141
27936
  () => originalMethod.apply(resultLike, args2)
27142
27937
  );
27143
- if (!isAsyncIterable5(stream)) {
27938
+ if (!isAsyncIterable4(stream)) {
27144
27939
  return stream;
27145
27940
  }
27146
27941
  return wrapAsyncIterableWithSpan2({
@@ -27335,7 +28130,7 @@ function wrapAsyncIterableWithSpan2(args) {
27335
28130
  }
27336
28131
  };
27337
28132
  }
27338
- function isAsyncIterable5(value) {
28133
+ function isAsyncIterable4(value) {
27339
28134
  return !!value && (typeof value === "object" || typeof value === "function") && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
27340
28135
  }
27341
28136
  function normalizeError2(error) {
@@ -32081,6 +32876,9 @@ function sanitizeLoggedValue(value, seen = /* @__PURE__ */ new WeakSet(), depth
32081
32876
  // src/instrumentation/plugins/pi-coding-agent-plugin.ts
32082
32877
  var piAgentPatchStates = /* @__PURE__ */ new WeakMap();
32083
32878
  var piAgentEventSubscriptions = /* @__PURE__ */ new WeakSet();
32879
+ var PI_TOOL_EXECUTE_WRAPPED = /* @__PURE__ */ Symbol.for(
32880
+ "braintrust.pi_coding_agent.tool_execute_wrapped"
32881
+ );
32084
32882
  var piPromptContextStore;
32085
32883
  var PiCodingAgentPlugin = class extends BasePlugin {
32086
32884
  activePromptStates = /* @__PURE__ */ new Set();
@@ -32161,6 +32959,7 @@ function startPiPromptRun(event, onFinalize) {
32161
32959
  return void 0;
32162
32960
  }
32163
32961
  installPiAgentInstrumentation(agent);
32962
+ wrapPiToolExecutors(agent.state?.tools);
32164
32963
  const metadata = {
32165
32964
  ...extractSessionMetadata(session),
32166
32965
  ...extractPromptOptionsMetadata(event.arguments[1]),
@@ -32205,7 +33004,7 @@ function extractSession(event) {
32205
33004
  return isObject(candidate) && typeof candidate.prompt === "function" ? candidate : void 0;
32206
33005
  }
32207
33006
  function isPiAgent(value) {
32208
- return isObject(value) && typeof value.streamFn === "function" && typeof value.subscribe === "function";
33007
+ return isObject(value) && (typeof value.streamFunction === "function" || typeof value.streamFn === "function") && typeof value.subscribe === "function";
32209
33008
  }
32210
33009
  function promptContextStore() {
32211
33010
  piPromptContextStore ??= isomorph_default.newAsyncLocalStorage();
@@ -32215,17 +33014,21 @@ function currentPiPromptState() {
32215
33014
  return promptContextStore().getStore();
32216
33015
  }
32217
33016
  function installPiAgentInstrumentation(agent) {
33017
+ const property = typeof agent.streamFunction === "function" ? "streamFunction" : "streamFn";
33018
+ const streamFunction = agent[property];
32218
33019
  const existing = piAgentPatchStates.get(agent);
32219
- if (!existing || agent.streamFn !== existing.wrappedStreamFn) {
33020
+ if (streamFunction && (!existing || existing.property !== property || streamFunction !== existing.wrappedStreamFunction)) {
32220
33021
  const patchState = {
32221
- originalStreamFn: agent.streamFn,
32222
- wrappedStreamFn: agent.streamFn
33022
+ originalStreamFunction: streamFunction,
33023
+ property,
33024
+ wrappedStreamFunction: streamFunction
32223
33025
  };
32224
- patchState.wrappedStreamFn = makeInstrumentedStreamFn(
33026
+ patchState.wrappedStreamFunction = makeInstrumentedStreamFunction(
32225
33027
  agent,
32226
- patchState.originalStreamFn
33028
+ patchState.originalStreamFunction,
33029
+ property
32227
33030
  );
32228
- agent.streamFn = patchState.wrappedStreamFn;
33031
+ agent[property] = patchState.wrappedStreamFunction;
32229
33032
  piAgentPatchStates.set(agent, patchState);
32230
33033
  }
32231
33034
  if (piAgentEventSubscriptions.has(agent)) {
@@ -32250,14 +33053,21 @@ function installPiAgentInstrumentation(agent) {
32250
33053
  logInstrumentationError4("Pi Coding Agent event subscription", error);
32251
33054
  }
32252
33055
  }
32253
- function makeInstrumentedStreamFn(agent, originalStreamFn) {
32254
- return async function instrumentedPiStreamFn(model, context, options) {
32255
- const invokeOriginal = () => Reflect.apply(originalStreamFn, this, [model, context, options]);
33056
+ function makeInstrumentedStreamFunction(agent, originalStreamFunction, property) {
33057
+ return async function instrumentedPiStreamFunction(model, context, options) {
33058
+ const invokeOriginal = () => Reflect.apply(originalStreamFunction, this, [model, context, options]);
32256
33059
  const state = currentPiPromptState();
32257
33060
  if (!state || state.agent !== agent || state.finalized) {
32258
33061
  return invokeOriginal();
32259
33062
  }
32260
- const llmState = await startPiLlmSpan(state, model, context, options);
33063
+ wrapPiToolExecutors(context.tools);
33064
+ const llmState = await startPiLlmSpan(
33065
+ state,
33066
+ model,
33067
+ context,
33068
+ property,
33069
+ options
33070
+ );
32261
33071
  try {
32262
33072
  const stream = await runWithAutoInstrumentationSuppressed(invokeOriginal);
32263
33073
  return patchAssistantMessageStream(stream, state, llmState);
@@ -32267,12 +33077,39 @@ function makeInstrumentedStreamFn(agent, originalStreamFn) {
32267
33077
  }
32268
33078
  };
32269
33079
  }
32270
- async function startPiLlmSpan(state, model, context, options) {
33080
+ function wrapPiToolExecutors(tools) {
33081
+ if (!tools) {
33082
+ return;
33083
+ }
33084
+ for (const tool of tools) {
33085
+ try {
33086
+ const execute = tool.execute;
33087
+ if (typeof execute !== "function" || execute[PI_TOOL_EXECUTE_WRAPPED]) {
33088
+ continue;
33089
+ }
33090
+ const wrappedExecute = function(...args) {
33091
+ return runWithAutoInstrumentationAllowed(
33092
+ () => Reflect.apply(execute, this, args)
33093
+ );
33094
+ };
33095
+ Object.defineProperty(wrappedExecute, PI_TOOL_EXECUTE_WRAPPED, {
33096
+ configurable: false,
33097
+ enumerable: false,
33098
+ value: true,
33099
+ writable: false
33100
+ });
33101
+ tool.execute = wrappedExecute;
33102
+ } catch (error) {
33103
+ logInstrumentationError4("Pi Coding Agent tool wrapping", error);
33104
+ }
33105
+ }
33106
+ }
33107
+ async function startPiLlmSpan(state, model, context, property, options) {
32271
33108
  const metadata = {
32272
33109
  ...extractModelMetadata2(model),
32273
33110
  ...extractStreamOptionsMetadata(options),
32274
33111
  ...extractToolMetadata(context.tools),
32275
- "pi_coding_agent.operation": "agent.streamFn"
33112
+ "pi_coding_agent.operation": `agent.${property}`
32276
33113
  };
32277
33114
  const span = startSpan(
32278
33115
  withSpanInstrumentationName(
@@ -32438,35 +33275,26 @@ async function startPiToolSpan(state, event) {
32438
33275
  if (!event.toolCallId || state.activeToolSpans.has(event.toolCallId)) {
32439
33276
  return;
32440
33277
  }
32441
- const restoreAutoInstrumentation = enterAutoInstrumentationAllowed();
32442
33278
  const metadata = {
32443
33279
  "gen_ai.tool.call.id": event.toolCallId,
32444
33280
  "gen_ai.tool.name": event.toolName,
32445
33281
  "pi_coding_agent.tool.name": event.toolName
32446
33282
  };
32447
- try {
32448
- const span = startSpan(
32449
- withSpanInstrumentationName(
32450
- {
32451
- event: {
32452
- input: event.args,
32453
- metadata
32454
- },
32455
- name: event.toolName || "tool",
32456
- parent: await state.span.export(),
32457
- spanAttributes: { type: "tool" /* TOOL */ }
33283
+ const span = startSpan(
33284
+ withSpanInstrumentationName(
33285
+ {
33286
+ event: {
33287
+ input: event.args,
33288
+ metadata
32458
33289
  },
32459
- INSTRUMENTATION_NAMES.PI_CODING_AGENT
32460
- )
32461
- );
32462
- state.activeToolSpans.set(event.toolCallId, {
32463
- restoreAutoInstrumentation,
32464
- span
32465
- });
32466
- } catch (error) {
32467
- restoreAutoInstrumentation();
32468
- throw error;
32469
- }
33290
+ name: event.toolName || "tool",
33291
+ parent: await state.span.export(),
33292
+ spanAttributes: { type: "tool" /* TOOL */ }
33293
+ },
33294
+ INSTRUMENTATION_NAMES.PI_CODING_AGENT
33295
+ )
33296
+ );
33297
+ state.activeToolSpans.set(event.toolCallId, { span });
32470
33298
  }
32471
33299
  function finishPiToolSpan(state, event) {
32472
33300
  const toolState = state.activeToolSpans.get(event.toolCallId);
@@ -32487,11 +33315,7 @@ function finishPiToolSpan(state, event) {
32487
33315
  output: event.result
32488
33316
  });
32489
33317
  } finally {
32490
- try {
32491
- toolState.span.end();
32492
- } finally {
32493
- toolState.restoreAutoInstrumentation?.();
32494
- }
33318
+ toolState.span.end();
32495
33319
  }
32496
33320
  }
32497
33321
  function finishPiPromptRun(state, error) {
@@ -32562,14 +33386,10 @@ function finishPiLlmSpan(promptState, llmState, message, error) {
32562
33386
  }
32563
33387
  function finishOpenToolSpans(state, error) {
32564
33388
  for (const [, toolState] of state.activeToolSpans) {
32565
- try {
32566
- safeLog4(toolState.span, {
32567
- error: error ? toLoggedError(error) : "Pi tool did not complete"
32568
- });
32569
- toolState.span.end();
32570
- } finally {
32571
- toolState.restoreAutoInstrumentation?.();
32572
- }
33389
+ safeLog4(toolState.span, {
33390
+ error: error ? toLoggedError(error) : "Pi tool did not complete"
33391
+ });
33392
+ toolState.span.end();
32573
33393
  }
32574
33394
  state.activeToolSpans.clear();
32575
33395
  }
@@ -32868,12 +33688,12 @@ var MAX_STRANDS_STRING_ATTACHMENT_CACHE_ENTRIES = 32;
32868
33688
  var StrandsAgentSDKPlugin = class extends BasePlugin {
32869
33689
  activeChildParents = /* @__PURE__ */ new WeakMap();
32870
33690
  onEnable() {
32871
- this.subscribeToAgentStream();
32872
- this.subscribeToMultiAgentStream(
33691
+ this.interceptAgentStream();
33692
+ this.interceptMultiAgentStream(
32873
33693
  strandsAgentSDKChannels.graphStream,
32874
33694
  "Graph.stream"
32875
33695
  );
32876
- this.subscribeToMultiAgentStream(
33696
+ this.interceptMultiAgentStream(
32877
33697
  strandsAgentSDKChannels.swarmStream,
32878
33698
  "Swarm.stream"
32879
33699
  );
@@ -32884,122 +33704,92 @@ var StrandsAgentSDKPlugin = class extends BasePlugin {
32884
33704
  }
32885
33705
  this.unsubscribers = [];
32886
33706
  }
32887
- subscribeToAgentStream() {
32888
- const channel = strandsAgentSDKChannels.agentStream.tracingChannel();
32889
- const states = /* @__PURE__ */ new WeakMap();
32890
- const unbindAutoInstrumentationSuppression = bindAutoInstrumentationSuppressionToStart(channel);
32891
- const handlers = {
32892
- start: (event) => {
32893
- const state = startAgentStream(event, this.activeChildParents);
32894
- if (state) {
32895
- states.set(event, state);
32896
- }
32897
- },
32898
- end: (event) => {
32899
- const state = states.get(event);
32900
- if (!state) {
32901
- return;
32902
- }
32903
- const result = event.result;
32904
- if (isAsyncIterable(result)) {
32905
- patchStreamIfNeeded(result, {
32906
- aroundNext: (callback) => runWithAutoInstrumentationSuppressed(callback),
32907
- onChunk: (chunk) => handleAgentStreamEvent(state, chunk),
32908
- onComplete: () => {
32909
- finalizeAgentStream(state);
32910
- states.delete(event);
32911
- },
32912
- onError: (error) => {
32913
- finalizeAgentStream(state, error);
32914
- states.delete(event);
32915
- }
32916
- });
32917
- return;
32918
- }
32919
- finalizeAgentStream(state, void 0, result);
32920
- states.delete(event);
32921
- },
32922
- error: (event) => {
32923
- const state = states.get(event);
32924
- if (!state || !event.error) {
32925
- return;
32926
- }
32927
- finalizeAgentStream(state, event.error);
32928
- states.delete(event);
32929
- }
32930
- };
32931
- channel.subscribe(handlers);
32932
- this.unsubscribers.push(() => {
32933
- unbindAutoInstrumentationSuppression?.();
32934
- channel.unsubscribe(handlers);
32935
- });
33707
+ interceptAgentStream() {
33708
+ this.unsubscribers.push(
33709
+ strandsAgentSDKChannels.agentStream.intercept(
33710
+ (target, thisArg, args, additional) => instrumentStrandsStreamInvocation({
33711
+ finalize: finalizeAgentStream,
33712
+ handleChunk: handleAgentStreamEvent,
33713
+ invoke: () => Reflect.apply(target, thisArg, args),
33714
+ name: "Strands Agent SDK",
33715
+ start: () => startAgentStream(
33716
+ args[0],
33717
+ extractAgent(additional.agent, thisArg),
33718
+ this.activeChildParents
33719
+ )
33720
+ })
33721
+ )
33722
+ );
32936
33723
  }
32937
- subscribeToMultiAgentStream(channel, operation) {
32938
- const tracingChannel = channel.tracingChannel();
32939
- const states = /* @__PURE__ */ new WeakMap();
32940
- const unbindAutoInstrumentationSuppression = bindAutoInstrumentationSuppressionToStart(tracingChannel);
32941
- const handlers = {
32942
- start: (event) => {
32943
- const state = startMultiAgentStream(
32944
- event,
32945
- operation,
32946
- this.activeChildParents
32947
- );
32948
- if (state) {
32949
- states.set(event, state);
32950
- }
32951
- },
32952
- end: (event) => {
32953
- const state = states.get(event);
32954
- if (!state) {
32955
- return;
32956
- }
32957
- const result = event.result;
32958
- if (isAsyncIterable(result)) {
32959
- patchStreamIfNeeded(result, {
32960
- aroundNext: (callback) => runWithAutoInstrumentationSuppressed(callback),
32961
- onChunk: (chunk) => handleMultiAgentStreamEvent(
32962
- state,
32963
- chunk,
32964
- this.activeChildParents
32965
- ),
32966
- onComplete: () => {
32967
- finalizeMultiAgentStream(state, this.activeChildParents);
32968
- states.delete(event);
32969
- },
32970
- onError: (error) => {
32971
- finalizeMultiAgentStream(state, this.activeChildParents, error);
32972
- states.delete(event);
32973
- }
32974
- });
32975
- return;
32976
- }
32977
- finalizeMultiAgentStream(
32978
- state,
32979
- this.activeChildParents,
32980
- void 0,
32981
- result
33724
+ interceptMultiAgentStream(channel, operation) {
33725
+ this.unsubscribers.push(
33726
+ channel.intercept(
33727
+ (target, thisArg, args, additional) => instrumentStrandsStreamInvocation({
33728
+ finalize: (state, error, output) => finalizeMultiAgentStream(
33729
+ state,
33730
+ this.activeChildParents,
33731
+ error,
33732
+ output
33733
+ ),
33734
+ handleChunk: (state, chunk) => handleMultiAgentStreamEvent(state, chunk, this.activeChildParents),
33735
+ invoke: () => Reflect.apply(target, thisArg, args),
33736
+ name: "Strands multi-agent",
33737
+ start: () => startMultiAgentStream(
33738
+ args[0],
33739
+ extractOrchestrator(additional.orchestrator, thisArg),
33740
+ operation,
33741
+ this.activeChildParents
33742
+ )
33743
+ })
33744
+ )
33745
+ );
33746
+ }
33747
+ };
33748
+ function instrumentStrandsStreamInvocation(options) {
33749
+ let state;
33750
+ try {
33751
+ state = options.start();
33752
+ } catch (error) {
33753
+ debugLogger.error(`Error starting ${options.name} instrumentation:`, error);
33754
+ }
33755
+ let result;
33756
+ try {
33757
+ result = runWithAutoInstrumentationSuppressed(options.invoke);
33758
+ } catch (error) {
33759
+ if (state) {
33760
+ try {
33761
+ options.finalize(state, error);
33762
+ } catch (instrumentationError) {
33763
+ debugLogger.error(
33764
+ `Error handling ${options.name} instrumentation failure:`,
33765
+ instrumentationError
32982
33766
  );
32983
- states.delete(event);
32984
- },
32985
- error: (event) => {
32986
- const state = states.get(event);
32987
- if (!state || !event.error) {
32988
- return;
32989
- }
32990
- finalizeMultiAgentStream(state, this.activeChildParents, event.error);
32991
- states.delete(event);
32992
33767
  }
32993
- };
32994
- tracingChannel.subscribe(handlers);
32995
- this.unsubscribers.push(() => {
32996
- unbindAutoInstrumentationSuppression?.();
32997
- tracingChannel.unsubscribe(handlers);
32998
- });
33768
+ }
33769
+ throw error;
32999
33770
  }
33000
- };
33001
- function startAgentStream(event, activeChildParents) {
33002
- const agent = extractAgent(event);
33771
+ if (state) {
33772
+ try {
33773
+ if (isAsyncIterable(result)) {
33774
+ patchStreamIfNeeded(result, {
33775
+ aroundNext: (callback) => runWithAutoInstrumentationSuppressed(callback),
33776
+ onChunk: (chunk) => options.handleChunk(state, chunk),
33777
+ onComplete: () => options.finalize(state),
33778
+ onError: (error) => options.finalize(state, error)
33779
+ });
33780
+ } else {
33781
+ options.finalize(state, void 0, result);
33782
+ }
33783
+ } catch (error) {
33784
+ debugLogger.error(
33785
+ `Error finalizing ${options.name} instrumentation:`,
33786
+ error
33787
+ );
33788
+ }
33789
+ }
33790
+ return result;
33791
+ }
33792
+ function startAgentStream(input, agent, activeChildParents) {
33003
33793
  const model = agent?.model;
33004
33794
  const metadata = {
33005
33795
  ...extractAgentMetadata2(agent),
@@ -33009,17 +33799,14 @@ function startAgentStream(event, activeChildParents) {
33009
33799
  };
33010
33800
  const parentSpan = agent ? getOnlyChildParent(activeChildParents, agent) : void 0;
33011
33801
  const attachmentCache = createStrandsAttachmentCache();
33012
- const input = processStrandsInputAttachments(
33013
- event.arguments[0],
33014
- attachmentCache
33015
- );
33802
+ const processedInput = processStrandsInputAttachments(input, attachmentCache);
33016
33803
  const span = parentSpan ? withCurrent(
33017
33804
  parentSpan,
33018
33805
  () => startSpan(
33019
33806
  withSpanInstrumentationName(
33020
33807
  {
33021
33808
  event: {
33022
- input,
33809
+ input: processedInput,
33023
33810
  metadata
33024
33811
  },
33025
33812
  name: formatAgentSpanName(agent),
@@ -33032,7 +33819,7 @@ function startAgentStream(event, activeChildParents) {
33032
33819
  withSpanInstrumentationName(
33033
33820
  {
33034
33821
  event: {
33035
- input,
33822
+ input: processedInput,
33036
33823
  metadata
33037
33824
  },
33038
33825
  name: formatAgentSpanName(agent),
@@ -33050,22 +33837,21 @@ function startAgentStream(event, activeChildParents) {
33050
33837
  startTime: getCurrentUnixTimestamp()
33051
33838
  };
33052
33839
  }
33053
- function startMultiAgentStream(event, operation, activeChildParents) {
33054
- const orchestrator = extractOrchestrator(event);
33840
+ function startMultiAgentStream(input, orchestrator, operation, activeChildParents) {
33055
33841
  const metadata = {
33056
33842
  "strands.operation": operation,
33057
33843
  provider: "strands",
33058
33844
  ...orchestrator?.id ? { "strands.orchestrator.id": orchestrator.id } : {}
33059
33845
  };
33060
33846
  const parentSpan = orchestrator ? getOnlyChildParent(activeChildParents, orchestrator) : void 0;
33061
- const input = processStrandsInputAttachments(event.arguments[0]);
33847
+ const processedInput = processStrandsInputAttachments(input);
33062
33848
  const span = parentSpan ? withCurrent(
33063
33849
  parentSpan,
33064
33850
  () => startSpan(
33065
33851
  withSpanInstrumentationName(
33066
33852
  {
33067
33853
  event: {
33068
- input,
33854
+ input: processedInput,
33069
33855
  metadata
33070
33856
  },
33071
33857
  name: operation === "Graph.stream" ? "Strands Graph" : "Strands Swarm",
@@ -33078,7 +33864,7 @@ function startMultiAgentStream(event, operation, activeChildParents) {
33078
33864
  withSpanInstrumentationName(
33079
33865
  {
33080
33866
  event: {
33081
- input,
33867
+ input: processedInput,
33082
33868
  metadata
33083
33869
  },
33084
33870
  name: operation === "Graph.stream" ? "Strands Graph" : "Strands Swarm",
@@ -33449,12 +34235,12 @@ function finalizeMultiAgentStream(state, activeChildParents, error, output) {
33449
34235
  });
33450
34236
  state.span.end();
33451
34237
  }
33452
- function extractAgent(event) {
33453
- const candidate = event.agent ?? event.self;
34238
+ function extractAgent(agent, self) {
34239
+ const candidate = agent ?? self;
33454
34240
  return isObject(candidate) && typeof candidate.stream === "function" ? candidate : void 0;
33455
34241
  }
33456
- function extractOrchestrator(event) {
33457
- const candidate = event.orchestrator ?? event.self;
34242
+ function extractOrchestrator(orchestrator, self) {
34243
+ const candidate = orchestrator ?? self;
33458
34244
  return isObject(candidate) && typeof candidate.stream === "function" ? candidate : void 0;
33459
34245
  }
33460
34246
  function extractAgentMetadata2(agent) {
@@ -35703,7 +36489,6 @@ async function summarizeAndFlush(experiment, options) {
35703
36489
  }
35704
36490
 
35705
36491
  export {
35706
- debugLogger,
35707
36492
  IDGenerator,
35708
36493
  UUIDGenerator,
35709
36494
  OTELIDGenerator,