braintrust 3.29.0 → 3.31.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 (51) hide show
  1. package/dev/dist/index.d.mts +4 -1
  2. package/dev/dist/index.d.ts +4 -1
  3. package/dev/dist/index.js +1492 -887
  4. package/dev/dist/index.mjs +836 -231
  5. package/dist/apply-auto-instrumentation.js +217 -217
  6. package/dist/apply-auto-instrumentation.mjs +2 -2
  7. package/dist/auto-instrumentations/bundler/esbuild.cjs +13 -1
  8. package/dist/auto-instrumentations/bundler/esbuild.mjs +2 -2
  9. package/dist/auto-instrumentations/bundler/next.cjs +22 -4
  10. package/dist/auto-instrumentations/bundler/next.mjs +12 -6
  11. package/dist/auto-instrumentations/bundler/rollup.cjs +13 -1
  12. package/dist/auto-instrumentations/bundler/rollup.mjs +2 -2
  13. package/dist/auto-instrumentations/bundler/vite.cjs +13 -1
  14. package/dist/auto-instrumentations/bundler/vite.mjs +2 -2
  15. package/dist/auto-instrumentations/bundler/webpack-loader.cjs +13 -1
  16. package/dist/auto-instrumentations/bundler/webpack.cjs +13 -1
  17. package/dist/auto-instrumentations/bundler/webpack.mjs +3 -3
  18. package/dist/auto-instrumentations/{chunk-DKTGDNA7.mjs → chunk-LW4HDHXT.mjs} +1 -1
  19. package/dist/auto-instrumentations/{chunk-OMCZ3MV2.mjs → chunk-OXINGIEZ.mjs} +1 -1
  20. package/dist/auto-instrumentations/{chunk-AOIYCVEL.mjs → chunk-U64OYU4Q.mjs} +13 -1
  21. package/dist/auto-instrumentations/hook.mjs +18 -4
  22. package/dist/auto-instrumentations/index.cjs +13 -1
  23. package/dist/auto-instrumentations/index.mjs +1 -1
  24. package/dist/browser.d.mts +614 -480
  25. package/dist/browser.d.ts +614 -480
  26. package/dist/browser.js +1172 -305
  27. package/dist/browser.mjs +1172 -305
  28. package/dist/{chunk-6Z5S7VOU.js → chunk-4AQGZS6X.js} +13 -1
  29. package/dist/{chunk-M6XPNJC4.mjs → chunk-AJT4FR25.mjs} +703 -120
  30. package/dist/{chunk-7FA6VP2S.mjs → chunk-CE3BLB2L.mjs} +13 -1
  31. package/dist/{chunk-XLLGRXGR.js → chunk-V32LW47Z.js} +1819 -1236
  32. package/dist/cli.js +846 -241
  33. package/dist/edge-light.d.mts +1 -1
  34. package/dist/edge-light.d.ts +1 -1
  35. package/dist/edge-light.js +1172 -305
  36. package/dist/edge-light.mjs +1172 -305
  37. package/dist/index.d.mts +614 -480
  38. package/dist/index.d.ts +614 -480
  39. package/dist/index.js +915 -643
  40. package/dist/index.mjs +349 -77
  41. package/dist/instrumentation/index.d.mts +15 -0
  42. package/dist/instrumentation/index.d.ts +15 -0
  43. package/dist/instrumentation/index.js +836 -230
  44. package/dist/instrumentation/index.mjs +836 -230
  45. package/dist/vitest-evals-reporter.js +16 -16
  46. package/dist/vitest-evals-reporter.mjs +2 -2
  47. package/dist/workerd.d.mts +1 -1
  48. package/dist/workerd.d.ts +1 -1
  49. package/dist/workerd.js +1172 -305
  50. package/dist/workerd.mjs +1172 -305
  51. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -41,7 +41,7 @@ var require_async = __commonJS({
41
41
  "use strict";
42
42
  Object.defineProperty(exports2, "__esModule", { value: true });
43
43
  exports2.read = void 0;
44
- function read(path9, settings, callback) {
44
+ function read2(path9, settings, callback) {
45
45
  settings.fs.lstat(path9, (lstatError, lstat) => {
46
46
  if (lstatError !== null) {
47
47
  callFailureCallback(callback, lstatError);
@@ -67,7 +67,7 @@ var require_async = __commonJS({
67
67
  });
68
68
  });
69
69
  }
70
- exports2.read = read;
70
+ exports2.read = read2;
71
71
  function callFailureCallback(callback, error2) {
72
72
  callback(error2);
73
73
  }
@@ -83,7 +83,7 @@ var require_sync = __commonJS({
83
83
  "use strict";
84
84
  Object.defineProperty(exports2, "__esModule", { value: true });
85
85
  exports2.read = void 0;
86
- function read(path9, settings) {
86
+ function read2(path9, settings) {
87
87
  const lstat = settings.fs.lstatSync(path9);
88
88
  if (!lstat.isSymbolicLink() || !settings.followSymbolicLink) {
89
89
  return lstat;
@@ -101,7 +101,7 @@ var require_sync = __commonJS({
101
101
  throw error2;
102
102
  }
103
103
  }
104
- exports2.read = read;
104
+ exports2.read = read2;
105
105
  }
106
106
  });
107
107
 
@@ -327,14 +327,14 @@ var require_async2 = __commonJS({
327
327
  var constants_1 = require_constants();
328
328
  var utils = require_utils();
329
329
  var common = require_common();
330
- function read(directory, settings, callback) {
330
+ function read2(directory, settings, callback) {
331
331
  if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) {
332
332
  readdirWithFileTypes(directory, settings, callback);
333
333
  return;
334
334
  }
335
335
  readdir2(directory, settings, callback);
336
336
  }
337
- exports2.read = read;
337
+ exports2.read = read2;
338
338
  function readdirWithFileTypes(directory, settings, callback) {
339
339
  settings.fs.readdir(directory, { withFileTypes: true }, (readdirError, dirents) => {
340
340
  if (readdirError !== null) {
@@ -436,13 +436,13 @@ var require_sync2 = __commonJS({
436
436
  var constants_1 = require_constants();
437
437
  var utils = require_utils();
438
438
  var common = require_common();
439
- function read(directory, settings) {
439
+ function read2(directory, settings) {
440
440
  if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) {
441
441
  return readdirWithFileTypes(directory, settings);
442
442
  }
443
443
  return readdir2(directory, settings);
444
444
  }
445
- exports2.read = read;
445
+ exports2.read = read2;
446
446
  function readdirWithFileTypes(directory, settings) {
447
447
  const dirents = settings.fs.readdirSync(directory, { withFileTypes: true });
448
448
  return dirents.map((dirent) => {
@@ -1236,7 +1236,7 @@ var require_package = __commonJS({
1236
1236
  "package.json"(exports2, module2) {
1237
1237
  module2.exports = {
1238
1238
  name: "braintrust",
1239
- version: "3.29.0",
1239
+ version: "3.31.0",
1240
1240
  description: "SDK for integrating Braintrust",
1241
1241
  repository: {
1242
1242
  type: "git",
@@ -5356,6 +5356,7 @@ var ProjectSettings = import_v36.z.union([
5356
5356
  ]),
5357
5357
  disable_realtime_queries: import_v36.z.union([import_v36.z.boolean(), import_v36.z.null()]),
5358
5358
  monitor_charts_use_metrics_start: import_v36.z.union([import_v36.z.boolean(), import_v36.z.null()]),
5359
+ blind_reviews: import_v36.z.union([import_v36.z.boolean(), import_v36.z.null()]),
5359
5360
  default_preprocessor: NullableSavedFunctionId
5360
5361
  }).partial(),
5361
5362
  import_v36.z.null()
@@ -5417,6 +5418,7 @@ var WindowedAutomationConfig = import_v36.z.object({
5417
5418
  auto_approve_tools: import_v36.z.array(import_v36.z.string().min(1)).optional().default([]),
5418
5419
  harness: import_v36.z.enum(["native", "codex", "claude-code"]).optional(),
5419
5420
  model: import_v36.z.string().min(1).optional(),
5421
+ endpoint_name: import_v36.z.string().min(1).optional(),
5420
5422
  reasoning_effort: import_v36.z.enum(["none", "minimal", "low", "medium", "high", "xhigh", "max"]).optional()
5421
5423
  }).optional(),
5422
5424
  actions: import_v36.z.array(
@@ -7208,7 +7210,7 @@ var INSTRUMENTATION_NAMES = {
7208
7210
  var INTERNAL_SPAN_INSTRUMENTATION_NAME = /* @__PURE__ */ Symbol.for(
7209
7211
  "braintrust.spanInstrumentationName"
7210
7212
  );
7211
- var SDK_VERSION = true ? "3.29.0" : "0.0.0";
7213
+ var SDK_VERSION = true ? "3.31.0" : "0.0.0";
7212
7214
  function withSpanInstrumentationName(args, instrumentationName) {
7213
7215
  return {
7214
7216
  ...args,
@@ -10723,6 +10725,15 @@ function _internalStartSpanWithInitialMerge(args) {
10723
10725
  [INITIAL_SPAN_WRITE_AS_MERGE]: true
10724
10726
  }).span;
10725
10727
  }
10728
+ function _internalStartSpanWithInitialMergeAndParentSpanIds(args) {
10729
+ return startSpanAndIsLogger(
10730
+ {
10731
+ ...args,
10732
+ [INITIAL_SPAN_WRITE_AS_MERGE]: true
10733
+ },
10734
+ { useParentSpanIdsForObjectParent: true }
10735
+ ).span;
10736
+ }
10726
10737
  function _internalStartSpanWithContext(args, context2) {
10727
10738
  return startSpanAndIsLogger({
10728
10739
  ...args,
@@ -10733,7 +10744,7 @@ async function flush(options) {
10733
10744
  const state = options?.state ?? _globalState;
10734
10745
  return await state.bgLogger().flush();
10735
10746
  }
10736
- function startSpanAndIsLogger(args) {
10747
+ function startSpanAndIsLogger(args, internalOptions) {
10737
10748
  const state = args?.state ?? _globalState;
10738
10749
  const { parentObject, propagatedState } = getSpanParentObjectAndPropagatedState({
10739
10750
  asyncFlush: args?.asyncFlush,
@@ -10747,7 +10758,7 @@ function startSpanAndIsLogger(args) {
10747
10758
  ) ? {
10748
10759
  spanId: parentObject.data.span_id,
10749
10760
  rootSpanId: parentObject.data.root_span_id
10750
- } : void 0;
10761
+ } : internalOptions?.useParentSpanIdsForObjectParent ? args?.parentSpanIds : void 0;
10751
10762
  const { parent: _ignoredParent, ...spanArgs } = args ?? {};
10752
10763
  const span = new SpanImpl({
10753
10764
  state,
@@ -14760,19 +14771,29 @@ function buildSpanScores(results) {
14760
14771
  function _internalPrepareEvaluatorScore(scoreValue, name) {
14761
14772
  if (scoreValue === null) return { results: null };
14762
14773
  if (Array.isArray(scoreValue)) {
14774
+ const names = /* @__PURE__ */ new Set();
14763
14775
  for (const score of scoreValue) {
14764
14776
  if (!(typeof score === "object" && !isEmpty2(score))) {
14765
14777
  throw new Error(
14766
14778
  `When returning an array of scores, each score must be a non-empty object. Got: ${JSON.stringify(score)}`
14767
14779
  );
14768
14780
  }
14781
+ if (typeof score.name !== "string") {
14782
+ throw new Error(
14783
+ `When returning an array of scores, each score must have a name. Got: ${JSON.stringify(score)}`
14784
+ );
14785
+ }
14786
+ if (names.has(score.name)) {
14787
+ throw new Error(`Duplicate score name '${score.name}' in score array`);
14788
+ }
14789
+ names.add(score.name);
14769
14790
  }
14770
14791
  }
14771
14792
  let results;
14772
14793
  if (Array.isArray(scoreValue)) {
14773
14794
  results = scoreValue;
14774
14795
  } else if (typeof scoreValue === "object" && !isEmpty2(scoreValue)) {
14775
- results = [scoreValue];
14796
+ results = [{ ...scoreValue, name: scoreValue.name ?? name }];
14776
14797
  } else {
14777
14798
  results = [{ name, score: scoreValue }];
14778
14799
  }
@@ -17176,6 +17197,131 @@ function unsubscribeAll(unsubscribers) {
17176
17197
  return [];
17177
17198
  }
17178
17199
 
17200
+ // src/instrumentation/core/channel-definitions.ts
17201
+ function channel(spec) {
17202
+ return spec;
17203
+ }
17204
+ function defineChannels(pkg, channels, options) {
17205
+ const { instrumentationName } = options;
17206
+ return Object.fromEntries(
17207
+ Object.entries(channels).map(([key, spec]) => {
17208
+ const fullChannelName = `orchestrion:${pkg}:${spec.channelName}`;
17209
+ if (spec.kind === "async") {
17210
+ const asyncSpec = spec;
17211
+ const tracingChannel2 = () => isomorph_default.newTracingChannel(
17212
+ fullChannelName
17213
+ );
17214
+ const intercept2 = (interceptor) => {
17215
+ const hook = tracingChannel2();
17216
+ return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
17217
+ };
17218
+ };
17219
+ return [
17220
+ key,
17221
+ {
17222
+ ...asyncSpec,
17223
+ instrumentationName,
17224
+ intercept: intercept2,
17225
+ invoke: (target, thisArg, args, additional) => {
17226
+ const hook = tracingChannel2();
17227
+ return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
17228
+ },
17229
+ tracingChannel: tracingChannel2,
17230
+ tracePromise: (fn, context2) => tracingChannel2().tracePromise(
17231
+ fn,
17232
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
17233
+ context2
17234
+ )
17235
+ }
17236
+ ];
17237
+ }
17238
+ const syncSpec = spec;
17239
+ const tracingChannel = () => isomorph_default.newTracingChannel(
17240
+ fullChannelName
17241
+ );
17242
+ const intercept = (interceptor) => {
17243
+ const hook = tracingChannel();
17244
+ return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
17245
+ };
17246
+ };
17247
+ return [
17248
+ key,
17249
+ {
17250
+ ...syncSpec,
17251
+ instrumentationName,
17252
+ intercept,
17253
+ invoke: (target, thisArg, args, additional) => {
17254
+ const hook = tracingChannel();
17255
+ return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
17256
+ },
17257
+ tracingChannel,
17258
+ traceSync: (fn, context2) => tracingChannel().traceSync(
17259
+ fn,
17260
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
17261
+ context2
17262
+ )
17263
+ }
17264
+ ];
17265
+ })
17266
+ );
17267
+ }
17268
+
17269
+ // src/instrumentation/plugins/openai-channels.ts
17270
+ var openAIChannels = defineChannels(
17271
+ "openai",
17272
+ {
17273
+ filesCreateTraced: channel({
17274
+ channelName: "files.create-traced",
17275
+ kind: "async"
17276
+ }),
17277
+ batchesRetrieveTraced: channel({
17278
+ channelName: "batches.retrieve-traced",
17279
+ kind: "async"
17280
+ }),
17281
+ batchesCompleteTrace: channel({
17282
+ channelName: "batches.complete-trace",
17283
+ kind: "async"
17284
+ }),
17285
+ chatCompletionsCreate: channel({
17286
+ channelName: "chat.completions.create",
17287
+ kind: "async"
17288
+ }),
17289
+ embeddingsCreate: channel({
17290
+ channelName: "embeddings.create",
17291
+ kind: "async"
17292
+ }),
17293
+ betaChatCompletionsParse: channel({
17294
+ channelName: "beta.chat.completions.parse",
17295
+ kind: "async"
17296
+ }),
17297
+ betaChatCompletionsStream: channel({
17298
+ channelName: "beta.chat.completions.stream",
17299
+ kind: "sync-stream"
17300
+ }),
17301
+ moderationsCreate: channel({
17302
+ channelName: "moderations.create",
17303
+ kind: "async"
17304
+ }),
17305
+ responsesCreate: channel({
17306
+ channelName: "responses.create",
17307
+ kind: "async"
17308
+ }),
17309
+ responsesStream: channel({
17310
+ channelName: "responses.stream",
17311
+ kind: "sync-stream"
17312
+ }),
17313
+ responsesParse: channel({
17314
+ channelName: "responses.parse",
17315
+ kind: "async"
17316
+ }),
17317
+ responsesCompact: channel({
17318
+ channelName: "responses.compact",
17319
+ kind: "async"
17320
+ })
17321
+ },
17322
+ { instrumentationName: INSTRUMENTATION_NAMES.OPENAI }
17323
+ );
17324
+
17179
17325
  // src/wrappers/attachment-utils.ts
17180
17326
  function getExtensionFromMediaType(mediaType) {
17181
17327
  const extensionMap = {
@@ -17351,118 +17497,91 @@ function processInputAttachments(input) {
17351
17497
  return processNode(input);
17352
17498
  }
17353
17499
 
17354
- // src/instrumentation/core/channel-definitions.ts
17355
- function channel(spec) {
17356
- return spec;
17357
- }
17358
- function defineChannels(pkg, channels, options) {
17359
- const { instrumentationName } = options;
17360
- return Object.fromEntries(
17361
- Object.entries(channels).map(([key, spec]) => {
17362
- const fullChannelName = `orchestrion:${pkg}:${spec.channelName}`;
17363
- if (spec.kind === "async") {
17364
- const asyncSpec = spec;
17365
- const tracingChannel2 = () => isomorph_default.newTracingChannel(
17366
- fullChannelName
17367
- );
17368
- const intercept2 = (interceptor) => {
17369
- const hook = tracingChannel2();
17370
- return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
17371
- };
17372
- };
17373
- return [
17374
- key,
17375
- {
17376
- ...asyncSpec,
17377
- instrumentationName,
17378
- intercept: intercept2,
17379
- invoke: (target, thisArg, args, additional) => {
17380
- const hook = tracingChannel2();
17381
- return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
17382
- },
17383
- tracingChannel: tracingChannel2,
17384
- tracePromise: (fn, context2) => tracingChannel2().tracePromise(
17385
- fn,
17386
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
17387
- context2
17388
- )
17389
- }
17390
- ];
17500
+ // src/instrumentation/plugins/openai-span-data.ts
17501
+ var OPENAI_METADATA_KEYS = [
17502
+ "model",
17503
+ "temperature",
17504
+ "top_p",
17505
+ "max_tokens",
17506
+ "frequency_penalty",
17507
+ "presence_penalty",
17508
+ "stop",
17509
+ "response_format",
17510
+ "tools",
17511
+ "tool_choice",
17512
+ "parallel_tool_calls",
17513
+ "max_tool_calls"
17514
+ ];
17515
+ function batchMetadata(params) {
17516
+ const metadata = { provider: "openai" };
17517
+ for (const key of OPENAI_METADATA_KEYS) {
17518
+ try {
17519
+ const value = Reflect.get(params, key);
17520
+ if (value !== void 0) {
17521
+ metadata[key] = value;
17391
17522
  }
17392
- const syncSpec = spec;
17393
- const tracingChannel = () => isomorph_default.newTracingChannel(
17394
- fullChannelName
17395
- );
17396
- const intercept = (interceptor) => {
17397
- const hook = tracingChannel();
17398
- return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
17399
- };
17400
- };
17401
- return [
17402
- key,
17403
- {
17404
- ...syncSpec,
17405
- instrumentationName,
17406
- intercept,
17407
- invoke: (target, thisArg, args, additional) => {
17408
- const hook = tracingChannel();
17409
- return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
17410
- },
17411
- tracingChannel,
17412
- traceSync: (fn, context2) => tracingChannel().traceSync(
17413
- fn,
17414
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
17415
- context2
17416
- )
17417
- }
17418
- ];
17419
- })
17420
- );
17523
+ } catch {
17524
+ }
17525
+ }
17526
+ return metadata;
17527
+ }
17528
+ function extractOpenAIBatchInput(endpoint, params) {
17529
+ const input = endpoint === "/v1/chat/completions" ? params.messages : params.input;
17530
+ return {
17531
+ input: processInputAttachments(input),
17532
+ metadata: batchMetadata(params)
17533
+ };
17534
+ }
17535
+ function extractOpenAIChatInput(params) {
17536
+ const { messages, ...metadata } = params;
17537
+ return {
17538
+ input: processInputAttachments(messages),
17539
+ metadata: { ...metadata, provider: "openai" }
17540
+ };
17541
+ }
17542
+ function extractOpenAIResponsesInput(params) {
17543
+ const { input, ...metadata } = params;
17544
+ return {
17545
+ input: processInputAttachments(input),
17546
+ metadata: { ...metadata, provider: "openai" }
17547
+ };
17548
+ }
17549
+ function extractOpenAIResponsesMetadata(result) {
17550
+ if (!result) {
17551
+ return void 0;
17552
+ }
17553
+ const { output: _output, usage: _usage, ...metadata } = result;
17554
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
17555
+ }
17556
+ function processImagesInOutput(output) {
17557
+ if (Array.isArray(output)) {
17558
+ return output.map(processImagesInOutput);
17559
+ }
17560
+ if (isObject(output) && output.type === "image_generation_call" && typeof output.result === "string" && output.result) {
17561
+ const fileExtension = output.output_format || "png";
17562
+ const contentType = `image/${fileExtension}`;
17563
+ const baseFilename = typeof output.revised_prompt === "string" ? output.revised_prompt.slice(0, 50).replace(/[^a-zA-Z0-9]/g, "_") : "generated_image";
17564
+ let binaryString;
17565
+ try {
17566
+ binaryString = atob(output.result);
17567
+ } catch {
17568
+ return output;
17569
+ }
17570
+ const bytes = new Uint8Array(binaryString.length);
17571
+ for (let i = 0; i < binaryString.length; i++) {
17572
+ bytes[i] = binaryString.charCodeAt(i);
17573
+ }
17574
+ return {
17575
+ ...output,
17576
+ result: new Attachment({
17577
+ data: new Blob([bytes], { type: contentType }),
17578
+ filename: `${baseFilename}.${fileExtension}`,
17579
+ contentType
17580
+ })
17581
+ };
17582
+ }
17583
+ return output;
17421
17584
  }
17422
-
17423
- // src/instrumentation/plugins/openai-channels.ts
17424
- var openAIChannels = defineChannels(
17425
- "openai",
17426
- {
17427
- chatCompletionsCreate: channel({
17428
- channelName: "chat.completions.create",
17429
- kind: "async"
17430
- }),
17431
- embeddingsCreate: channel({
17432
- channelName: "embeddings.create",
17433
- kind: "async"
17434
- }),
17435
- betaChatCompletionsParse: channel({
17436
- channelName: "beta.chat.completions.parse",
17437
- kind: "async"
17438
- }),
17439
- betaChatCompletionsStream: channel({
17440
- channelName: "beta.chat.completions.stream",
17441
- kind: "sync-stream"
17442
- }),
17443
- moderationsCreate: channel({
17444
- channelName: "moderations.create",
17445
- kind: "async"
17446
- }),
17447
- responsesCreate: channel({
17448
- channelName: "responses.create",
17449
- kind: "async"
17450
- }),
17451
- responsesStream: channel({
17452
- channelName: "responses.stream",
17453
- kind: "sync-stream"
17454
- }),
17455
- responsesParse: channel({
17456
- channelName: "responses.parse",
17457
- kind: "async"
17458
- }),
17459
- responsesCompact: channel({
17460
- channelName: "responses.compact",
17461
- kind: "async"
17462
- })
17463
- },
17464
- { instrumentationName: INSTRUMENTATION_NAMES.OPENAI }
17465
- );
17466
17585
 
17467
17586
  // src/openai-utils.ts
17468
17587
  var BRAINTRUST_CACHED_STREAM_METRIC = "__braintrust_cached_metric";
@@ -17519,23 +17638,573 @@ function getCachedMetricFromHeaders(headers) {
17519
17638
  return parseCachedHeader(headers.get(LEGACY_CACHED_HEADER));
17520
17639
  }
17521
17640
 
17641
+ // src/instrumentation/plugins/openai-batch-instrumentation.ts
17642
+ var SUPPORTED_ENDPOINTS = /* @__PURE__ */ new Set(["/v1/chat/completions", "/v1/responses"]);
17643
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
17644
+ "completed",
17645
+ "failed",
17646
+ "expired",
17647
+ "cancelled"
17648
+ ]);
17649
+ var pendingBatchTraces = /* @__PURE__ */ new Map();
17650
+ function read(value, key) {
17651
+ if (!isObject(value)) {
17652
+ return void 0;
17653
+ }
17654
+ try {
17655
+ return Reflect.get(value, key);
17656
+ } catch {
17657
+ return void 0;
17658
+ }
17659
+ }
17660
+ function isBatchRecordIterable(value) {
17661
+ if (value === null || typeof value !== "object" && typeof value !== "function") {
17662
+ return false;
17663
+ }
17664
+ return typeof read(value, Symbol.iterator) === "function" || typeof read(value, Symbol.asyncIterator) === "function";
17665
+ }
17666
+ function validCustomId(value) {
17667
+ return typeof value === "string" && value.length > 0 && value.length <= 64;
17668
+ }
17669
+ function logBatchInstrumentationError(context2, error2) {
17670
+ debugLogger.debug(`OpenAI Batch instrumentation ${context2}:`, error2);
17671
+ }
17672
+ async function exportParent(parent) {
17673
+ if ("toStr" in parent && typeof parent.toStr === "function") {
17674
+ return parent.toStr();
17675
+ }
17676
+ if ("export" in parent && typeof parent.export === "function") {
17677
+ return await parent.export();
17678
+ }
17679
+ return void 0;
17680
+ }
17681
+ async function deterministicDigest(namespace, ...parts) {
17682
+ const encoded = new TextEncoder().encode(
17683
+ [namespace, ...parts].map((part) => `${part.length}:${part}`).join("\0")
17684
+ );
17685
+ return new Uint8Array(
17686
+ await globalThis.crypto.subtle.digest("SHA-256", encoded)
17687
+ );
17688
+ }
17689
+ function digestHex(bytes, length) {
17690
+ return Array.from(bytes.slice(0, length)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
17691
+ }
17692
+ function digestUuid(bytes) {
17693
+ const hex = digestHex(bytes, 16);
17694
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
17695
+ }
17696
+ async function batchSpanIds(inputFileId) {
17697
+ const [row, span, root] = await Promise.all([
17698
+ deterministicDigest("openai:batch:row", inputFileId),
17699
+ deterministicDigest("openai:batch:span", inputFileId),
17700
+ deterministicDigest("openai:batch:root", inputFileId)
17701
+ ]);
17702
+ return {
17703
+ rowId: digestUuid(row),
17704
+ spanId: digestHex(span, 8),
17705
+ rootSpanId: digestHex(root, 16)
17706
+ };
17707
+ }
17708
+ async function childSpanIds(inputFileId, customId) {
17709
+ const [row, span] = await Promise.all([
17710
+ deterministicDigest("openai:batch:child:row", inputFileId, customId),
17711
+ deterministicDigest("openai:batch:child:span", inputFileId, customId)
17712
+ ]);
17713
+ return { rowId: digestUuid(row), spanId: digestHex(span, 8) };
17714
+ }
17715
+ async function startBatchSpan(context2) {
17716
+ const ids = await batchSpanIds(context2.inputFileId);
17717
+ const parent = SpanComponentsV4.fromStr(context2.parent);
17718
+ const hasParentSpan = Boolean(
17719
+ parent.data.row_id && parent.data.span_id && parent.data.root_span_id
17720
+ );
17721
+ return withCurrent(
17722
+ NOOP_SPAN,
17723
+ () => _internalStartSpanWithInitialMergeAndParentSpanIds(
17724
+ withSpanInstrumentationName(
17725
+ {
17726
+ name: "openai.batch",
17727
+ type: "task" /* TASK */,
17728
+ parent: context2.parent,
17729
+ ...!hasParentSpan ? {
17730
+ parentSpanIds: {
17731
+ parentSpanIds: [],
17732
+ rootSpanId: ids.rootSpanId
17733
+ }
17734
+ } : {},
17735
+ spanId: ids.spanId,
17736
+ startTime: context2.taskStartTime,
17737
+ event: {
17738
+ id: ids.rowId,
17739
+ metadata: {
17740
+ endpoint: context2.endpoint,
17741
+ input_file_id: context2.inputFileId,
17742
+ provider: "openai"
17743
+ }
17744
+ }
17745
+ },
17746
+ INSTRUMENTATION_NAMES.OPENAI
17747
+ )
17748
+ )
17749
+ );
17750
+ }
17751
+ async function startBatchChild(context2, taskParent, input) {
17752
+ const ids = await childSpanIds(context2.inputFileId, input.customId);
17753
+ return withCurrent(
17754
+ NOOP_SPAN,
17755
+ () => _internalStartSpanWithInitialMerge(
17756
+ withSpanInstrumentationName(
17757
+ {
17758
+ name: context2.endpoint === "/v1/chat/completions" ? "Chat Completion" : "openai.responses.create",
17759
+ type: "llm" /* LLM */,
17760
+ parent: taskParent,
17761
+ spanId: ids.spanId,
17762
+ startTime: context2.childStartTime,
17763
+ event: {
17764
+ id: ids.rowId,
17765
+ ...input.spanData?.input !== void 0 ? { input: input.spanData.input } : {},
17766
+ metadata: {
17767
+ ...input.spanData?.metadata,
17768
+ custom_id: input.customId,
17769
+ provider: "openai"
17770
+ }
17771
+ }
17772
+ },
17773
+ INSTRUMENTATION_NAMES.OPENAI
17774
+ )
17775
+ )
17776
+ );
17777
+ }
17778
+ async function* jsonlRecords(file, onIssue = () => {
17779
+ }) {
17780
+ const resolvedFile = await file;
17781
+ if (typeof resolvedFile === "string") {
17782
+ for (const line of resolvedFile.split("\n")) {
17783
+ if (!line.trim()) {
17784
+ continue;
17785
+ }
17786
+ try {
17787
+ yield JSON.parse(line.replace(/\r$/, ""));
17788
+ } catch (error2) {
17789
+ logBatchInstrumentationError("skipped malformed JSONL", error2);
17790
+ onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
17791
+ }
17792
+ }
17793
+ return;
17794
+ }
17795
+ const body = read(resolvedFile, "body");
17796
+ const getReader = read(body, "getReader");
17797
+ if (isObject(body) && typeof getReader === "function") {
17798
+ let reader;
17799
+ try {
17800
+ reader = Reflect.apply(getReader, body, []);
17801
+ const decoder = new TextDecoder();
17802
+ let pending = "";
17803
+ while (true) {
17804
+ const readChunk = read(reader, "read");
17805
+ if (typeof readChunk !== "function") {
17806
+ throw new Error("Response body stream has no read method");
17807
+ }
17808
+ const chunk = await Reflect.apply(readChunk, reader, []);
17809
+ if (!isObject(chunk)) {
17810
+ throw new Error("Response body stream returned an invalid chunk");
17811
+ }
17812
+ if (chunk.done === true) {
17813
+ pending += decoder.decode();
17814
+ break;
17815
+ }
17816
+ if (!(chunk.value instanceof Uint8Array)) {
17817
+ throw new Error("Response body stream returned a non-byte chunk");
17818
+ }
17819
+ pending += decoder.decode(chunk.value, { stream: true });
17820
+ let newline = pending.indexOf("\n");
17821
+ while (newline !== -1) {
17822
+ const line = pending.slice(0, newline).replace(/\r$/, "");
17823
+ pending = pending.slice(newline + 1);
17824
+ if (line.trim()) {
17825
+ try {
17826
+ yield JSON.parse(line);
17827
+ } catch (error2) {
17828
+ logBatchInstrumentationError("skipped malformed JSONL", error2);
17829
+ onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
17830
+ }
17831
+ }
17832
+ newline = pending.indexOf("\n");
17833
+ }
17834
+ }
17835
+ if (pending.trim()) {
17836
+ try {
17837
+ yield JSON.parse(pending.replace(/\r$/, ""));
17838
+ } catch (error2) {
17839
+ logBatchInstrumentationError("skipped malformed JSONL", error2);
17840
+ onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
17841
+ }
17842
+ }
17843
+ } catch (error2) {
17844
+ logBatchInstrumentationError("could not read JSONL response body", error2);
17845
+ onIssue(new Error("OpenAI Batch response body could not be read"));
17846
+ } finally {
17847
+ const releaseLock = read(reader, "releaseLock");
17848
+ if (typeof releaseLock === "function") {
17849
+ try {
17850
+ Reflect.apply(releaseLock, reader, []);
17851
+ } catch (error2) {
17852
+ logBatchInstrumentationError(
17853
+ "could not release stream reader",
17854
+ error2
17855
+ );
17856
+ }
17857
+ }
17858
+ }
17859
+ return;
17860
+ }
17861
+ if (isBatchRecordIterable(resolvedFile)) {
17862
+ for await (const record of resolvedFile) {
17863
+ yield record;
17864
+ }
17865
+ return;
17866
+ }
17867
+ logBatchInstrumentationError("skipped invalid JSONL source", resolvedFile);
17868
+ onIssue(new Error("OpenAI Batch file source is invalid"));
17869
+ }
17870
+ async function readBatchInputs(file) {
17871
+ const inputs = /* @__PURE__ */ new Map();
17872
+ const issues = [];
17873
+ let endpoint;
17874
+ try {
17875
+ for await (const value of jsonlRecords(
17876
+ file,
17877
+ (issue) => issues.push(issue)
17878
+ )) {
17879
+ const customId = read(value, "custom_id");
17880
+ const url = read(value, "url");
17881
+ const body = read(value, "body");
17882
+ if (!validCustomId(customId) || inputs.has(customId) || read(value, "method") !== "POST" || typeof url !== "string" || !SUPPORTED_ENDPOINTS.has(url) || endpoint !== void 0 && endpoint !== url || !isObject(body)) {
17883
+ issues.push(new Error("OpenAI Batch input contains an invalid record"));
17884
+ continue;
17885
+ }
17886
+ endpoint = url;
17887
+ let spanData;
17888
+ try {
17889
+ spanData = extractOpenAIBatchInput(url, body);
17890
+ } catch (error2) {
17891
+ logBatchInstrumentationError("could not extract batch input", error2);
17892
+ }
17893
+ inputs.set(customId, { customId, spanData });
17894
+ }
17895
+ } catch (error2) {
17896
+ logBatchInstrumentationError("could not process input file", error2);
17897
+ issues.push(new Error("OpenAI Batch input file could not be processed"));
17898
+ }
17899
+ if (!endpoint || inputs.size === 0) {
17900
+ issues.push(new Error("OpenAI Batch input contains no supported records"));
17901
+ }
17902
+ return { endpoint, inputs, issues };
17903
+ }
17904
+ async function writePendingSpans(trace, endTime) {
17905
+ const task = await startBatchSpan(trace.context);
17906
+ const taskParent = await task.export();
17907
+ for (const input of trace.inputs.values()) {
17908
+ try {
17909
+ const child = await startBatchChild(trace.context, taskParent, input);
17910
+ if (endTime !== void 0) {
17911
+ child.end({ endTime });
17912
+ }
17913
+ } catch (error2) {
17914
+ logBatchInstrumentationError("could not write batch request span", error2);
17915
+ }
17916
+ }
17917
+ if (endTime !== void 0) {
17918
+ if (trace.status && trace.status !== "completed") {
17919
+ task.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
17920
+ }
17921
+ task.end({ endTime });
17922
+ }
17923
+ }
17924
+ var interceptOpenAIFilesCreateTraced = async (target, thisArg, args) => {
17925
+ const startTime = getCurrentUnixTimestamp();
17926
+ const inputPromise = readBatchInputs(args[0].inputFileContent);
17927
+ const parentPromise = exportParent(args[0].parent);
17928
+ const file = await Reflect.apply(target, thisArg, args);
17929
+ try {
17930
+ const inputFileId = read(file, "id");
17931
+ const [inputData, exportedParent2] = await Promise.all([
17932
+ inputPromise,
17933
+ parentPromise
17934
+ ]);
17935
+ if (typeof inputFileId !== "string" || !exportedParent2 || !inputData.endpoint || inputData.issues.length > 0) {
17936
+ if (inputData.issues[0]) {
17937
+ logBatchInstrumentationError(
17938
+ "skipped invalid input file",
17939
+ inputData.issues[0]
17940
+ );
17941
+ }
17942
+ return file;
17943
+ }
17944
+ const trace = {
17945
+ context: {
17946
+ inputFileId,
17947
+ endpoint: inputData.endpoint,
17948
+ parent: exportedParent2,
17949
+ taskStartTime: startTime,
17950
+ childStartTime: startTime
17951
+ },
17952
+ inputs: inputData.inputs
17953
+ };
17954
+ pendingBatchTraces.set(inputFileId, trace);
17955
+ await writePendingSpans(trace);
17956
+ } catch (error2) {
17957
+ logBatchInstrumentationError("could not start batch spans", error2);
17958
+ }
17959
+ return file;
17960
+ };
17961
+ function validTimestamp(value) {
17962
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
17963
+ }
17964
+ function terminalEndTime(batch, startTime) {
17965
+ let timestamp;
17966
+ switch (batch.status) {
17967
+ case "completed":
17968
+ timestamp = batch.completed_at;
17969
+ break;
17970
+ case "failed":
17971
+ timestamp = batch.failed_at;
17972
+ break;
17973
+ case "expired":
17974
+ timestamp = batch.expired_at;
17975
+ break;
17976
+ default:
17977
+ timestamp = batch.cancelled_at;
17978
+ }
17979
+ return validTimestamp(timestamp) ? Math.max(timestamp, startTime) : Math.max(getCurrentUnixTimestamp(), startTime);
17980
+ }
17981
+ async function updateBatchTimestamps(batch) {
17982
+ const trace = pendingBatchTraces.get(batch.input_file_id);
17983
+ if (!trace || trace.context.endpoint !== batch.endpoint) {
17984
+ return;
17985
+ }
17986
+ if (validTimestamp(batch.created_at)) {
17987
+ trace.context.taskStartTime = batch.created_at;
17988
+ }
17989
+ trace.context.childStartTime = validTimestamp(batch.in_progress_at) ? Math.max(batch.in_progress_at, trace.context.taskStartTime) : trace.context.taskStartTime;
17990
+ trace.status = batch.status;
17991
+ if (TERMINAL_STATUSES.has(batch.status)) {
17992
+ trace.endTime = terminalEndTime(batch, trace.context.childStartTime);
17993
+ }
17994
+ await writePendingSpans(trace, trace.endTime);
17995
+ }
17996
+ var interceptOpenAIBatchesRetrieveTraced = (target, thisArg, args) => Promise.resolve(Reflect.apply(target, thisArg, args)).then(async (batch) => {
17997
+ try {
17998
+ await updateBatchTimestamps(batch);
17999
+ } catch (error2) {
18000
+ logBatchInstrumentationError("could not update batch timestamps", error2);
18001
+ }
18002
+ return batch;
18003
+ });
18004
+ function errorFromResult(result) {
18005
+ const error2 = read(result.value, "error");
18006
+ if (isObject(error2)) {
18007
+ const message = read(error2, "message");
18008
+ return new Error(
18009
+ typeof message === "string" ? message : "OpenAI Batch request failed"
18010
+ );
18011
+ }
18012
+ const response = read(result.value, "response");
18013
+ const statusCode = read(response, "status_code");
18014
+ if (result.source === "error" || typeof statusCode === "number" && (statusCode < 200 || statusCode >= 300)) {
18015
+ const message = read(read(read(response, "body"), "error"), "message");
18016
+ return new Error(
18017
+ typeof message === "string" ? message : "OpenAI Batch request failed"
18018
+ );
18019
+ }
18020
+ return void 0;
18021
+ }
18022
+ async function completeBatchResult(context2, taskParent, endTime, input, result) {
18023
+ const child = await startBatchChild(context2, taskParent, input);
18024
+ try {
18025
+ const resultError = errorFromResult(result);
18026
+ const responseBody = read(read(result.value, "response"), "body");
18027
+ if (resultError) {
18028
+ child.log({ error: resultError });
18029
+ } else if (isObject(responseBody)) {
18030
+ const model = read(responseBody, "model");
18031
+ child.log({
18032
+ output: context2.endpoint === "/v1/chat/completions" ? read(responseBody, "choices") : processImagesInOutput(read(responseBody, "output")),
18033
+ ...typeof model === "string" ? { metadata: { model } } : {},
18034
+ metrics: parseMetricsFromUsage(read(responseBody, "usage"))
18035
+ });
18036
+ } else {
18037
+ child.log({ error: new Error("OpenAI Batch response body is missing") });
18038
+ }
18039
+ } catch (error2) {
18040
+ child.log({ error: error2 });
18041
+ } finally {
18042
+ child.end({ endTime });
18043
+ }
18044
+ }
18045
+ async function completeResultFile({
18046
+ context: context2,
18047
+ endTime,
18048
+ file,
18049
+ inputs,
18050
+ issues,
18051
+ seen,
18052
+ source,
18053
+ taskParent
18054
+ }) {
18055
+ if (file === void 0) {
18056
+ return;
18057
+ }
18058
+ for await (const value of jsonlRecords(file, (issue) => issues.push(issue))) {
18059
+ const customId = read(value, "custom_id");
18060
+ if (!validCustomId(customId) || !isObject(value)) {
18061
+ issues.push(
18062
+ new Error("OpenAI Batch result is missing a valid custom_id")
18063
+ );
18064
+ continue;
18065
+ }
18066
+ if (seen.has(customId)) {
18067
+ issues.push(new Error("OpenAI Batch result contains a duplicate"));
18068
+ continue;
18069
+ }
18070
+ const input = inputs.get(customId);
18071
+ if (!input) {
18072
+ issues.push(
18073
+ new Error("OpenAI Batch result does not match an input record")
18074
+ );
18075
+ continue;
18076
+ }
18077
+ seen.add(customId);
18078
+ await completeBatchResult(context2, taskParent, endTime, input, {
18079
+ value,
18080
+ source
18081
+ });
18082
+ }
18083
+ }
18084
+ function sameInputs(first, second) {
18085
+ return first.size === second.size && [...first.keys()].every((customId) => second.has(customId));
18086
+ }
18087
+ async function contextForCompletion(inputFileId, inputData) {
18088
+ if (!inputData.endpoint || inputData.issues.length > 0) {
18089
+ return void 0;
18090
+ }
18091
+ const existing = pendingBatchTraces.get(inputFileId);
18092
+ if (existing) {
18093
+ if (existing.context.endpoint !== inputData.endpoint || !sameInputs(existing.inputs, inputData.inputs)) {
18094
+ return void 0;
18095
+ }
18096
+ return { ...existing, inputs: inputData.inputs };
18097
+ }
18098
+ const parent = await exportParent(getSpanParentObject());
18099
+ if (!parent) {
18100
+ return void 0;
18101
+ }
18102
+ const startTime = getCurrentUnixTimestamp();
18103
+ return {
18104
+ context: {
18105
+ inputFileId,
18106
+ endpoint: inputData.endpoint,
18107
+ parent,
18108
+ taskStartTime: startTime,
18109
+ childStartTime: startTime
18110
+ },
18111
+ inputs: inputData.inputs
18112
+ };
18113
+ }
18114
+ async function completeBatch(args) {
18115
+ const inputData = await readBatchInputs(args.inputFileContent);
18116
+ const trace = await contextForCompletion(args.inputFileId, inputData);
18117
+ if (!trace) {
18118
+ logBatchInstrumentationError(
18119
+ "left batch spans pending",
18120
+ inputData.issues[0] ?? new Error("OpenAI Batch input does not match")
18121
+ );
18122
+ return;
18123
+ }
18124
+ const endTime = trace.endTime ?? getCurrentUnixTimestamp();
18125
+ const task = await startBatchSpan(trace.context);
18126
+ const taskParent = await task.export();
18127
+ const issues = [];
18128
+ const seen = /* @__PURE__ */ new Set();
18129
+ await Promise.all([
18130
+ completeResultFile({
18131
+ context: trace.context,
18132
+ endTime,
18133
+ file: args.outputFileContent,
18134
+ inputs: trace.inputs,
18135
+ issues,
18136
+ seen,
18137
+ source: "output",
18138
+ taskParent
18139
+ }),
18140
+ completeResultFile({
18141
+ context: trace.context,
18142
+ endTime,
18143
+ file: args.errorFileContent,
18144
+ inputs: trace.inputs,
18145
+ issues,
18146
+ seen,
18147
+ source: "error",
18148
+ taskParent
18149
+ })
18150
+ ]);
18151
+ if (issues.length > 0) {
18152
+ logBatchInstrumentationError("left batch spans pending", issues[0]);
18153
+ return;
18154
+ }
18155
+ const missing = [...trace.inputs.values()].filter(
18156
+ ({ customId }) => !seen.has(customId)
18157
+ );
18158
+ if (!trace.status || trace.status === "completed") {
18159
+ if (missing.length > 0) {
18160
+ logBatchInstrumentationError(
18161
+ "left batch spans pending",
18162
+ new Error("OpenAI Batch result files are incomplete")
18163
+ );
18164
+ return;
18165
+ }
18166
+ } else {
18167
+ for (const input of missing) {
18168
+ const child = await startBatchChild(trace.context, taskParent, input);
18169
+ child.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
18170
+ child.end({ endTime });
18171
+ }
18172
+ task.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
18173
+ }
18174
+ task.end({ endTime });
18175
+ pendingBatchTraces.delete(args.inputFileId);
18176
+ }
18177
+ var interceptOpenAIBatchTraceComplete = (target, thisArg, args) => Promise.resolve(Reflect.apply(target, thisArg, args)).then(async (result) => {
18178
+ try {
18179
+ await completeBatch(args[0]);
18180
+ } catch (error2) {
18181
+ logBatchInstrumentationError("could not complete batch", error2);
18182
+ }
18183
+ return result;
18184
+ });
18185
+
17522
18186
  // src/instrumentation/plugins/openai-plugin.ts
17523
18187
  var OpenAIPlugin = class extends BasePlugin {
17524
18188
  constructor() {
17525
18189
  super();
17526
18190
  }
17527
18191
  onEnable() {
18192
+ this.unsubscribers.push(
18193
+ openAIChannels.filesCreateTraced.intercept(
18194
+ interceptOpenAIFilesCreateTraced
18195
+ ),
18196
+ openAIChannels.batchesRetrieveTraced.intercept(
18197
+ interceptOpenAIBatchesRetrieveTraced
18198
+ ),
18199
+ openAIChannels.batchesCompleteTrace.intercept(
18200
+ interceptOpenAIBatchTraceComplete
18201
+ )
18202
+ );
17528
18203
  this.unsubscribers.push(
17529
18204
  traceStreamingChannel(openAIChannels.chatCompletionsCreate, {
17530
18205
  name: "Chat Completion",
17531
18206
  type: "llm" /* LLM */,
17532
- extractInput: ([params]) => {
17533
- const { messages, ...metadata } = params;
17534
- return {
17535
- input: processInputAttachments(messages),
17536
- metadata: { ...metadata, provider: "openai" }
17537
- };
17538
- },
18207
+ extractInput: ([params]) => extractOpenAIChatInput(params),
17539
18208
  extractOutput: (result) => {
17540
18209
  return result?.choices;
17541
18210
  },
@@ -17581,13 +18250,7 @@ var OpenAIPlugin = class extends BasePlugin {
17581
18250
  traceStreamingChannel(openAIChannels.betaChatCompletionsParse, {
17582
18251
  name: "Chat Completion",
17583
18252
  type: "llm" /* LLM */,
17584
- extractInput: ([params]) => {
17585
- const { messages, ...metadata } = params;
17586
- return {
17587
- input: processInputAttachments(messages),
17588
- metadata: { ...metadata, provider: "openai" }
17589
- };
17590
- },
18253
+ extractInput: ([params]) => extractOpenAIChatInput(params),
17591
18254
  extractOutput: (result) => {
17592
18255
  return result?.choices;
17593
18256
  },
@@ -17609,13 +18272,7 @@ var OpenAIPlugin = class extends BasePlugin {
17609
18272
  traceSyncStreamChannel(openAIChannels.betaChatCompletionsStream, {
17610
18273
  name: "Chat Completion",
17611
18274
  type: "llm" /* LLM */,
17612
- extractInput: ([params]) => {
17613
- const { messages, ...metadata } = params;
17614
- return {
17615
- input: processInputAttachments(messages),
17616
- metadata: { ...metadata, provider: "openai" }
17617
- };
17618
- }
18275
+ extractInput: ([params]) => extractOpenAIChatInput(params)
17619
18276
  })
17620
18277
  );
17621
18278
  this.unsubscribers.push(
@@ -17645,23 +18302,11 @@ var OpenAIPlugin = class extends BasePlugin {
17645
18302
  traceStreamingChannel(openAIChannels.responsesCreate, {
17646
18303
  name: "openai.responses.create",
17647
18304
  type: "llm" /* LLM */,
17648
- extractInput: ([params]) => {
17649
- const { input, ...metadata } = params;
17650
- return {
17651
- input: processInputAttachments(input),
17652
- metadata: { ...metadata, provider: "openai" }
17653
- };
17654
- },
18305
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
17655
18306
  extractOutput: (result) => {
17656
18307
  return processImagesInOutput(result?.output);
17657
18308
  },
17658
- extractMetadata: (result) => {
17659
- if (!result) {
17660
- return void 0;
17661
- }
17662
- const { output: _output, usage: _usage, ...metadata } = result;
17663
- return Object.keys(metadata).length > 0 ? metadata : void 0;
17664
- },
18309
+ extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
17665
18310
  extractMetrics: (result, startTime, endEvent) => {
17666
18311
  const metrics = withCachedMetric(
17667
18312
  parseMetricsFromUsage(result?.usage),
@@ -17680,13 +18325,7 @@ var OpenAIPlugin = class extends BasePlugin {
17680
18325
  traceSyncStreamChannel(openAIChannels.responsesStream, {
17681
18326
  name: "openai.responses.create",
17682
18327
  type: "llm" /* LLM */,
17683
- extractInput: ([params]) => {
17684
- const { input, ...metadata } = params;
17685
- return {
17686
- input: processInputAttachments(input),
17687
- metadata: { ...metadata, provider: "openai" }
17688
- };
17689
- },
18328
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
17690
18329
  extractFromEvent: (event) => {
17691
18330
  if (event.type !== "response.completed" || !event.response) {
17692
18331
  return {};
@@ -17709,23 +18348,11 @@ var OpenAIPlugin = class extends BasePlugin {
17709
18348
  traceStreamingChannel(openAIChannels.responsesParse, {
17710
18349
  name: "openai.responses.parse",
17711
18350
  type: "llm" /* LLM */,
17712
- extractInput: ([params]) => {
17713
- const { input, ...metadata } = params;
17714
- return {
17715
- input: processInputAttachments(input),
17716
- metadata: { ...metadata, provider: "openai" }
17717
- };
17718
- },
18351
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
17719
18352
  extractOutput: (result) => {
17720
18353
  return processImagesInOutput(result?.output);
17721
18354
  },
17722
- extractMetadata: (result) => {
17723
- if (!result) {
17724
- return void 0;
17725
- }
17726
- const { output: _output, usage: _usage, ...metadata } = result;
17727
- return Object.keys(metadata).length > 0 ? metadata : void 0;
17728
- },
18355
+ extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
17729
18356
  extractMetrics: (result, startTime, endEvent) => {
17730
18357
  const metrics = withCachedMetric(
17731
18358
  parseMetricsFromUsage(result?.usage),
@@ -17744,23 +18371,11 @@ var OpenAIPlugin = class extends BasePlugin {
17744
18371
  traceAsyncChannel(openAIChannels.responsesCompact, {
17745
18372
  name: "openai.responses.compact",
17746
18373
  type: "llm" /* LLM */,
17747
- extractInput: ([params]) => {
17748
- const { input, ...metadata } = params;
17749
- return {
17750
- input: processInputAttachments(input),
17751
- metadata: { ...metadata, provider: "openai" }
17752
- };
17753
- },
18374
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
17754
18375
  extractOutput: (result) => {
17755
18376
  return processImagesInOutput(result?.output);
17756
18377
  },
17757
- extractMetadata: (result) => {
17758
- if (!result) {
17759
- return void 0;
17760
- }
17761
- const { output: _output, usage: _usage, ...metadata } = result;
17762
- return Object.keys(metadata).length > 0 ? metadata : void 0;
17763
- },
18378
+ extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
17764
18379
  extractMetrics: (result, startTime, endEvent) => {
17765
18380
  const metrics = withCachedMetric(
17766
18381
  parseMetricsFromUsage(result?.usage),
@@ -17816,35 +18431,6 @@ function withCachedMetric(metrics, result, endEvent) {
17816
18431
  cached
17817
18432
  };
17818
18433
  }
17819
- function processImagesInOutput(output) {
17820
- if (Array.isArray(output)) {
17821
- return output.map(processImagesInOutput);
17822
- }
17823
- if (isObject(output)) {
17824
- if (output.type === "image_generation_call" && output.result && typeof output.result === "string") {
17825
- const fileExtension = output.output_format || "png";
17826
- const contentType = `image/${fileExtension}`;
17827
- const baseFilename = output.revised_prompt && typeof output.revised_prompt === "string" ? output.revised_prompt.slice(0, 50).replace(/[^a-zA-Z0-9]/g, "_") : "generated_image";
17828
- const filename = `${baseFilename}.${fileExtension}`;
17829
- const binaryString = atob(output.result);
17830
- const bytes = new Uint8Array(binaryString.length);
17831
- for (let i = 0; i < binaryString.length; i++) {
17832
- bytes[i] = binaryString.charCodeAt(i);
17833
- }
17834
- const blob = new Blob([bytes], { type: contentType });
17835
- const attachment = new Attachment({
17836
- data: blob,
17837
- filename,
17838
- contentType
17839
- });
17840
- return {
17841
- ...output,
17842
- result: attachment
17843
- };
17844
- }
17845
- }
17846
- return output;
17847
- }
17848
18434
  function mergeLogprobTokens(existing, incoming) {
17849
18435
  if (incoming === void 0) {
17850
18436
  return existing;
@@ -22988,6 +23574,12 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiS
22988
23574
  if (!activeEntry) {
22989
23575
  return result;
22990
23576
  }
23577
+ const executionOptions = args[1];
23578
+ const toolCallId = isObject(executionOptions) ? executionOptions.toolCallId : void 0;
23579
+ const toolInput = {
23580
+ input: serializeToolExecutionInput(args),
23581
+ ...typeof toolCallId === "string" ? { metadata: { toolCallId } } : {}
23582
+ };
22991
23583
  if (isAsyncGenerator2(result)) {
22992
23584
  return (async function* () {
22993
23585
  const span = activeEntry.parentSpan.startSpan(
@@ -23001,7 +23593,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiS
23001
23593
  INSTRUMENTATION_NAMES.AI_SDK
23002
23594
  )
23003
23595
  );
23004
- span.log({ input: serializeToolExecutionInput(args) });
23596
+ span.log(toolInput);
23005
23597
  try {
23006
23598
  let lastValue;
23007
23599
  for await (const value of result) {
@@ -23019,7 +23611,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiS
23019
23611
  }
23020
23612
  return activeEntry.parentSpan.traced(
23021
23613
  async (span) => {
23022
- span.log({ input: serializeToolExecutionInput(args) });
23614
+ span.log(toolInput);
23023
23615
  const awaitedResult = await result;
23024
23616
  span.log({ output: awaitedResult });
23025
23617
  return awaitedResult;
@@ -37141,14 +37733,17 @@ function getMetricsFromResponse(response) {
37141
37733
  if (!isRecord(usageMetadata)) {
37142
37734
  continue;
37143
37735
  }
37144
- const inputTokenDetails = usageMetadata.input_token_details;
37736
+ const inputTokenDetails = isRecord(usageMetadata.input_token_details) ? usageMetadata.input_token_details : {};
37145
37737
  const outputTokenDetails = usageMetadata.output_token_details;
37146
37738
  return normalizeTokenMetrics({
37147
37739
  total_tokens: usageMetadata.total_tokens,
37148
37740
  prompt_tokens: usageMetadata.input_tokens,
37149
37741
  completion_tokens: usageMetadata.output_tokens,
37150
- prompt_cache_creation_tokens: isRecord(inputTokenDetails) ? inputTokenDetails.cache_creation : void 0,
37151
- prompt_cached_tokens: isRecord(inputTokenDetails) ? inputTokenDetails.cache_read : void 0,
37742
+ // Prefer TTL-specific cache writes over the aggregate when available.
37743
+ prompt_cache_creation_tokens: inputTokenDetails.ephemeral_5m_input_tokens == null && inputTokenDetails.ephemeral_1h_input_tokens == null ? inputTokenDetails.cache_creation : void 0,
37744
+ prompt_cache_creation_5m_tokens: inputTokenDetails.ephemeral_5m_input_tokens,
37745
+ prompt_cache_creation_1h_tokens: inputTokenDetails.ephemeral_1h_input_tokens,
37746
+ prompt_cached_tokens: inputTokenDetails.cache_read,
37152
37747
  completion_reasoning_tokens: isRecord(outputTokenDetails) ? outputTokenDetails.reasoning : void 0
37153
37748
  });
37154
37749
  }
@@ -37864,7 +38459,7 @@ function extractSession(event) {
37864
38459
  return isObject(candidate) && typeof candidate.prompt === "function" ? candidate : void 0;
37865
38460
  }
37866
38461
  function isPiAgent(value) {
37867
- return isObject(value) && typeof value.streamFn === "function" && typeof value.subscribe === "function";
38462
+ return isObject(value) && (typeof value.streamFunction === "function" || typeof value.streamFn === "function") && typeof value.subscribe === "function";
37868
38463
  }
37869
38464
  function promptContextStore() {
37870
38465
  piPromptContextStore ??= isomorph_default.newAsyncLocalStorage();
@@ -37874,17 +38469,21 @@ function currentPiPromptState() {
37874
38469
  return promptContextStore().getStore();
37875
38470
  }
37876
38471
  function installPiAgentInstrumentation(agent) {
38472
+ const property = typeof agent.streamFunction === "function" ? "streamFunction" : "streamFn";
38473
+ const streamFunction = agent[property];
37877
38474
  const existing = piAgentPatchStates.get(agent);
37878
- if (!existing || agent.streamFn !== existing.wrappedStreamFn) {
38475
+ if (streamFunction && (!existing || existing.property !== property || streamFunction !== existing.wrappedStreamFunction)) {
37879
38476
  const patchState = {
37880
- originalStreamFn: agent.streamFn,
37881
- wrappedStreamFn: agent.streamFn
38477
+ originalStreamFunction: streamFunction,
38478
+ property,
38479
+ wrappedStreamFunction: streamFunction
37882
38480
  };
37883
- patchState.wrappedStreamFn = makeInstrumentedStreamFn(
38481
+ patchState.wrappedStreamFunction = makeInstrumentedStreamFunction(
37884
38482
  agent,
37885
- patchState.originalStreamFn
38483
+ patchState.originalStreamFunction,
38484
+ property
37886
38485
  );
37887
- agent.streamFn = patchState.wrappedStreamFn;
38486
+ agent[property] = patchState.wrappedStreamFunction;
37888
38487
  piAgentPatchStates.set(agent, patchState);
37889
38488
  }
37890
38489
  if (piAgentEventSubscriptions.has(agent)) {
@@ -37909,15 +38508,21 @@ function installPiAgentInstrumentation(agent) {
37909
38508
  logInstrumentationError4("Pi Coding Agent event subscription", error2);
37910
38509
  }
37911
38510
  }
37912
- function makeInstrumentedStreamFn(agent, originalStreamFn) {
37913
- return async function instrumentedPiStreamFn(model, context2, options) {
37914
- const invokeOriginal = () => Reflect.apply(originalStreamFn, this, [model, context2, options]);
38511
+ function makeInstrumentedStreamFunction(agent, originalStreamFunction, property) {
38512
+ return async function instrumentedPiStreamFunction(model, context2, options) {
38513
+ const invokeOriginal = () => Reflect.apply(originalStreamFunction, this, [model, context2, options]);
37915
38514
  const state = currentPiPromptState();
37916
38515
  if (!state || state.agent !== agent || state.finalized) {
37917
38516
  return invokeOriginal();
37918
38517
  }
37919
38518
  wrapPiToolExecutors(context2.tools);
37920
- const llmState = await startPiLlmSpan(state, model, context2, options);
38519
+ const llmState = await startPiLlmSpan(
38520
+ state,
38521
+ model,
38522
+ context2,
38523
+ property,
38524
+ options
38525
+ );
37921
38526
  try {
37922
38527
  const stream = await runWithAutoInstrumentationSuppressed(invokeOriginal);
37923
38528
  return patchAssistantMessageStream(stream, state, llmState);
@@ -37954,12 +38559,12 @@ function wrapPiToolExecutors(tools) {
37954
38559
  }
37955
38560
  }
37956
38561
  }
37957
- async function startPiLlmSpan(state, model, context2, options) {
38562
+ async function startPiLlmSpan(state, model, context2, property, options) {
37958
38563
  const metadata = {
37959
38564
  ...extractModelMetadata2(model),
37960
38565
  ...extractStreamOptionsMetadata(options),
37961
38566
  ...extractToolMetadata(context2.tools),
37962
- "pi_coding_agent.operation": "agent.streamFn"
38567
+ "pi_coding_agent.operation": `agent.${property}`
37963
38568
  };
37964
38569
  const span = startSpan(
37965
38570
  withSpanInstrumentationName(
@@ -44056,7 +44661,7 @@ var openRouterAgentConfigs = [
44056
44661
  ];
44057
44662
 
44058
44663
  // src/auto-instrumentations/configs/pi-coding-agent.ts
44059
- var piCodingAgentVersionRange = ">=0.79.0 <0.82.0";
44664
+ var piCodingAgentVersionRange = ">=0.79.0 <1.0.0";
44060
44665
  var piCodingAgentConfigs = [
44061
44666
  {
44062
44667
  channelName: piCodingAgentChannels.prompt.channelName,