braintrust 3.29.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 (49) hide show
  1. package/dev/dist/index.js +1465 -881
  2. package/dev/dist/index.mjs +846 -262
  3. package/dist/apply-auto-instrumentation.js +217 -217
  4. package/dist/apply-auto-instrumentation.mjs +2 -2
  5. package/dist/auto-instrumentations/bundler/esbuild.cjs +13 -1
  6. package/dist/auto-instrumentations/bundler/esbuild.mjs +2 -2
  7. package/dist/auto-instrumentations/bundler/next.cjs +13 -1
  8. package/dist/auto-instrumentations/bundler/next.mjs +3 -3
  9. package/dist/auto-instrumentations/bundler/rollup.cjs +13 -1
  10. package/dist/auto-instrumentations/bundler/rollup.mjs +2 -2
  11. package/dist/auto-instrumentations/bundler/vite.cjs +13 -1
  12. package/dist/auto-instrumentations/bundler/vite.mjs +2 -2
  13. package/dist/auto-instrumentations/bundler/webpack-loader.cjs +13 -1
  14. package/dist/auto-instrumentations/bundler/webpack.cjs +13 -1
  15. package/dist/auto-instrumentations/bundler/webpack.mjs +3 -3
  16. package/dist/auto-instrumentations/{chunk-DKTGDNA7.mjs → chunk-LW4HDHXT.mjs} +1 -1
  17. package/dist/auto-instrumentations/{chunk-OMCZ3MV2.mjs → chunk-OXINGIEZ.mjs} +1 -1
  18. package/dist/auto-instrumentations/{chunk-AOIYCVEL.mjs → chunk-U64OYU4Q.mjs} +13 -1
  19. package/dist/auto-instrumentations/hook.mjs +16 -4
  20. package/dist/auto-instrumentations/index.cjs +13 -1
  21. package/dist/auto-instrumentations/index.mjs +1 -1
  22. package/dist/browser.d.mts +133 -2
  23. package/dist/browser.d.ts +133 -2
  24. package/dist/browser.js +1143 -297
  25. package/dist/browser.mjs +1143 -297
  26. package/dist/{chunk-6Z5S7VOU.js → chunk-2XDW3UCG.js} +13 -1
  27. package/dist/{chunk-M6XPNJC4.mjs → chunk-BU6SM54N.mjs} +687 -115
  28. package/dist/{chunk-7FA6VP2S.mjs → chunk-N3JKQ3D3.mjs} +13 -1
  29. package/dist/{chunk-XLLGRXGR.js → chunk-TWCDSYJN.js} +1803 -1231
  30. package/dist/cli.js +819 -235
  31. package/dist/edge-light.d.mts +1 -1
  32. package/dist/edge-light.d.ts +1 -1
  33. package/dist/edge-light.js +1143 -297
  34. package/dist/edge-light.mjs +1143 -297
  35. package/dist/index.d.mts +133 -2
  36. package/dist/index.d.ts +133 -2
  37. package/dist/index.js +902 -640
  38. package/dist/index.mjs +336 -74
  39. package/dist/instrumentation/index.d.mts +15 -0
  40. package/dist/instrumentation/index.d.ts +15 -0
  41. package/dist/instrumentation/index.js +857 -262
  42. package/dist/instrumentation/index.mjs +857 -262
  43. package/dist/vitest-evals-reporter.js +16 -16
  44. package/dist/vitest-evals-reporter.mjs +2 -2
  45. package/dist/workerd.d.mts +1 -1
  46. package/dist/workerd.d.ts +1 -1
  47. package/dist/workerd.js +1143 -297
  48. package/dist/workerd.mjs +1143 -297
  49. 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.30.0",
1240
1240
  description: "SDK for integrating Braintrust",
1241
1241
  repository: {
1242
1242
  type: "git",
@@ -7208,7 +7208,7 @@ var INSTRUMENTATION_NAMES = {
7208
7208
  var INTERNAL_SPAN_INSTRUMENTATION_NAME = /* @__PURE__ */ Symbol.for(
7209
7209
  "braintrust.spanInstrumentationName"
7210
7210
  );
7211
- var SDK_VERSION = true ? "3.29.0" : "0.0.0";
7211
+ var SDK_VERSION = true ? "3.30.0" : "0.0.0";
7212
7212
  function withSpanInstrumentationName(args, instrumentationName) {
7213
7213
  return {
7214
7214
  ...args,
@@ -10723,6 +10723,15 @@ function _internalStartSpanWithInitialMerge(args) {
10723
10723
  [INITIAL_SPAN_WRITE_AS_MERGE]: true
10724
10724
  }).span;
10725
10725
  }
10726
+ function _internalStartSpanWithInitialMergeAndParentSpanIds(args) {
10727
+ return startSpanAndIsLogger(
10728
+ {
10729
+ ...args,
10730
+ [INITIAL_SPAN_WRITE_AS_MERGE]: true
10731
+ },
10732
+ { useParentSpanIdsForObjectParent: true }
10733
+ ).span;
10734
+ }
10726
10735
  function _internalStartSpanWithContext(args, context2) {
10727
10736
  return startSpanAndIsLogger({
10728
10737
  ...args,
@@ -10733,7 +10742,7 @@ async function flush(options) {
10733
10742
  const state = options?.state ?? _globalState;
10734
10743
  return await state.bgLogger().flush();
10735
10744
  }
10736
- function startSpanAndIsLogger(args) {
10745
+ function startSpanAndIsLogger(args, internalOptions) {
10737
10746
  const state = args?.state ?? _globalState;
10738
10747
  const { parentObject, propagatedState } = getSpanParentObjectAndPropagatedState({
10739
10748
  asyncFlush: args?.asyncFlush,
@@ -10747,7 +10756,7 @@ function startSpanAndIsLogger(args) {
10747
10756
  ) ? {
10748
10757
  spanId: parentObject.data.span_id,
10749
10758
  rootSpanId: parentObject.data.root_span_id
10750
- } : void 0;
10759
+ } : internalOptions?.useParentSpanIdsForObjectParent ? args?.parentSpanIds : void 0;
10751
10760
  const { parent: _ignoredParent, ...spanArgs } = args ?? {};
10752
10761
  const span = new SpanImpl({
10753
10762
  state,
@@ -17176,6 +17185,131 @@ function unsubscribeAll(unsubscribers) {
17176
17185
  return [];
17177
17186
  }
17178
17187
 
17188
+ // src/instrumentation/core/channel-definitions.ts
17189
+ function channel(spec) {
17190
+ return spec;
17191
+ }
17192
+ function defineChannels(pkg, channels, options) {
17193
+ const { instrumentationName } = options;
17194
+ return Object.fromEntries(
17195
+ Object.entries(channels).map(([key, spec]) => {
17196
+ const fullChannelName = `orchestrion:${pkg}:${spec.channelName}`;
17197
+ if (spec.kind === "async") {
17198
+ const asyncSpec = spec;
17199
+ const tracingChannel2 = () => isomorph_default.newTracingChannel(
17200
+ fullChannelName
17201
+ );
17202
+ const intercept2 = (interceptor) => {
17203
+ const hook = tracingChannel2();
17204
+ return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
17205
+ };
17206
+ };
17207
+ return [
17208
+ key,
17209
+ {
17210
+ ...asyncSpec,
17211
+ instrumentationName,
17212
+ intercept: intercept2,
17213
+ invoke: (target, thisArg, args, additional) => {
17214
+ const hook = tracingChannel2();
17215
+ return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
17216
+ },
17217
+ tracingChannel: tracingChannel2,
17218
+ tracePromise: (fn, context2) => tracingChannel2().tracePromise(
17219
+ fn,
17220
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
17221
+ context2
17222
+ )
17223
+ }
17224
+ ];
17225
+ }
17226
+ const syncSpec = spec;
17227
+ const tracingChannel = () => isomorph_default.newTracingChannel(
17228
+ fullChannelName
17229
+ );
17230
+ const intercept = (interceptor) => {
17231
+ const hook = tracingChannel();
17232
+ return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
17233
+ };
17234
+ };
17235
+ return [
17236
+ key,
17237
+ {
17238
+ ...syncSpec,
17239
+ instrumentationName,
17240
+ intercept,
17241
+ invoke: (target, thisArg, args, additional) => {
17242
+ const hook = tracingChannel();
17243
+ return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
17244
+ },
17245
+ tracingChannel,
17246
+ traceSync: (fn, context2) => tracingChannel().traceSync(
17247
+ fn,
17248
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
17249
+ context2
17250
+ )
17251
+ }
17252
+ ];
17253
+ })
17254
+ );
17255
+ }
17256
+
17257
+ // src/instrumentation/plugins/openai-channels.ts
17258
+ var openAIChannels = defineChannels(
17259
+ "openai",
17260
+ {
17261
+ filesCreateTraced: channel({
17262
+ channelName: "files.create-traced",
17263
+ kind: "async"
17264
+ }),
17265
+ batchesRetrieveTraced: channel({
17266
+ channelName: "batches.retrieve-traced",
17267
+ kind: "async"
17268
+ }),
17269
+ batchesCompleteTrace: channel({
17270
+ channelName: "batches.complete-trace",
17271
+ kind: "async"
17272
+ }),
17273
+ chatCompletionsCreate: channel({
17274
+ channelName: "chat.completions.create",
17275
+ kind: "async"
17276
+ }),
17277
+ embeddingsCreate: channel({
17278
+ channelName: "embeddings.create",
17279
+ kind: "async"
17280
+ }),
17281
+ betaChatCompletionsParse: channel({
17282
+ channelName: "beta.chat.completions.parse",
17283
+ kind: "async"
17284
+ }),
17285
+ betaChatCompletionsStream: channel({
17286
+ channelName: "beta.chat.completions.stream",
17287
+ kind: "sync-stream"
17288
+ }),
17289
+ moderationsCreate: channel({
17290
+ channelName: "moderations.create",
17291
+ kind: "async"
17292
+ }),
17293
+ responsesCreate: channel({
17294
+ channelName: "responses.create",
17295
+ kind: "async"
17296
+ }),
17297
+ responsesStream: channel({
17298
+ channelName: "responses.stream",
17299
+ kind: "sync-stream"
17300
+ }),
17301
+ responsesParse: channel({
17302
+ channelName: "responses.parse",
17303
+ kind: "async"
17304
+ }),
17305
+ responsesCompact: channel({
17306
+ channelName: "responses.compact",
17307
+ kind: "async"
17308
+ })
17309
+ },
17310
+ { instrumentationName: INSTRUMENTATION_NAMES.OPENAI }
17311
+ );
17312
+
17179
17313
  // src/wrappers/attachment-utils.ts
17180
17314
  function getExtensionFromMediaType(mediaType) {
17181
17315
  const extensionMap = {
@@ -17351,118 +17485,91 @@ function processInputAttachments(input) {
17351
17485
  return processNode(input);
17352
17486
  }
17353
17487
 
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
- ];
17488
+ // src/instrumentation/plugins/openai-span-data.ts
17489
+ var OPENAI_METADATA_KEYS = [
17490
+ "model",
17491
+ "temperature",
17492
+ "top_p",
17493
+ "max_tokens",
17494
+ "frequency_penalty",
17495
+ "presence_penalty",
17496
+ "stop",
17497
+ "response_format",
17498
+ "tools",
17499
+ "tool_choice",
17500
+ "parallel_tool_calls",
17501
+ "max_tool_calls"
17502
+ ];
17503
+ function batchMetadata(params) {
17504
+ const metadata = { provider: "openai" };
17505
+ for (const key of OPENAI_METADATA_KEYS) {
17506
+ try {
17507
+ const value = Reflect.get(params, key);
17508
+ if (value !== void 0) {
17509
+ metadata[key] = value;
17391
17510
  }
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
- );
17511
+ } catch {
17512
+ }
17513
+ }
17514
+ return metadata;
17515
+ }
17516
+ function extractOpenAIBatchInput(endpoint, params) {
17517
+ const input = endpoint === "/v1/chat/completions" ? params.messages : params.input;
17518
+ return {
17519
+ input: processInputAttachments(input),
17520
+ metadata: batchMetadata(params)
17521
+ };
17522
+ }
17523
+ function extractOpenAIChatInput(params) {
17524
+ const { messages, ...metadata } = params;
17525
+ return {
17526
+ input: processInputAttachments(messages),
17527
+ metadata: { ...metadata, provider: "openai" }
17528
+ };
17529
+ }
17530
+ function extractOpenAIResponsesInput(params) {
17531
+ const { input, ...metadata } = params;
17532
+ return {
17533
+ input: processInputAttachments(input),
17534
+ metadata: { ...metadata, provider: "openai" }
17535
+ };
17536
+ }
17537
+ function extractOpenAIResponsesMetadata(result) {
17538
+ if (!result) {
17539
+ return void 0;
17540
+ }
17541
+ const { output: _output, usage: _usage, ...metadata } = result;
17542
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
17543
+ }
17544
+ function processImagesInOutput(output) {
17545
+ if (Array.isArray(output)) {
17546
+ return output.map(processImagesInOutput);
17547
+ }
17548
+ if (isObject(output) && output.type === "image_generation_call" && typeof output.result === "string" && output.result) {
17549
+ const fileExtension = output.output_format || "png";
17550
+ const contentType = `image/${fileExtension}`;
17551
+ const baseFilename = typeof output.revised_prompt === "string" ? output.revised_prompt.slice(0, 50).replace(/[^a-zA-Z0-9]/g, "_") : "generated_image";
17552
+ let binaryString;
17553
+ try {
17554
+ binaryString = atob(output.result);
17555
+ } catch {
17556
+ return output;
17557
+ }
17558
+ const bytes = new Uint8Array(binaryString.length);
17559
+ for (let i = 0; i < binaryString.length; i++) {
17560
+ bytes[i] = binaryString.charCodeAt(i);
17561
+ }
17562
+ return {
17563
+ ...output,
17564
+ result: new Attachment({
17565
+ data: new Blob([bytes], { type: contentType }),
17566
+ filename: `${baseFilename}.${fileExtension}`,
17567
+ contentType
17568
+ })
17569
+ };
17570
+ }
17571
+ return output;
17421
17572
  }
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
17573
 
17467
17574
  // src/openai-utils.ts
17468
17575
  var BRAINTRUST_CACHED_STREAM_METRIC = "__braintrust_cached_metric";
@@ -17519,23 +17626,573 @@ function getCachedMetricFromHeaders(headers) {
17519
17626
  return parseCachedHeader(headers.get(LEGACY_CACHED_HEADER));
17520
17627
  }
17521
17628
 
17629
+ // src/instrumentation/plugins/openai-batch-instrumentation.ts
17630
+ var SUPPORTED_ENDPOINTS = /* @__PURE__ */ new Set(["/v1/chat/completions", "/v1/responses"]);
17631
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
17632
+ "completed",
17633
+ "failed",
17634
+ "expired",
17635
+ "cancelled"
17636
+ ]);
17637
+ var pendingBatchTraces = /* @__PURE__ */ new Map();
17638
+ function read(value, key) {
17639
+ if (!isObject(value)) {
17640
+ return void 0;
17641
+ }
17642
+ try {
17643
+ return Reflect.get(value, key);
17644
+ } catch {
17645
+ return void 0;
17646
+ }
17647
+ }
17648
+ function isBatchRecordIterable(value) {
17649
+ if (value === null || typeof value !== "object" && typeof value !== "function") {
17650
+ return false;
17651
+ }
17652
+ return typeof read(value, Symbol.iterator) === "function" || typeof read(value, Symbol.asyncIterator) === "function";
17653
+ }
17654
+ function validCustomId(value) {
17655
+ return typeof value === "string" && value.length > 0 && value.length <= 64;
17656
+ }
17657
+ function logBatchInstrumentationError(context2, error2) {
17658
+ debugLogger.debug(`OpenAI Batch instrumentation ${context2}:`, error2);
17659
+ }
17660
+ async function exportParent(parent) {
17661
+ if ("toStr" in parent && typeof parent.toStr === "function") {
17662
+ return parent.toStr();
17663
+ }
17664
+ if ("export" in parent && typeof parent.export === "function") {
17665
+ return await parent.export();
17666
+ }
17667
+ return void 0;
17668
+ }
17669
+ async function deterministicDigest(namespace, ...parts) {
17670
+ const encoded = new TextEncoder().encode(
17671
+ [namespace, ...parts].map((part) => `${part.length}:${part}`).join("\0")
17672
+ );
17673
+ return new Uint8Array(
17674
+ await globalThis.crypto.subtle.digest("SHA-256", encoded)
17675
+ );
17676
+ }
17677
+ function digestHex(bytes, length) {
17678
+ return Array.from(bytes.slice(0, length)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
17679
+ }
17680
+ function digestUuid(bytes) {
17681
+ const hex = digestHex(bytes, 16);
17682
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
17683
+ }
17684
+ async function batchSpanIds(inputFileId) {
17685
+ const [row, span, root] = await Promise.all([
17686
+ deterministicDigest("openai:batch:row", inputFileId),
17687
+ deterministicDigest("openai:batch:span", inputFileId),
17688
+ deterministicDigest("openai:batch:root", inputFileId)
17689
+ ]);
17690
+ return {
17691
+ rowId: digestUuid(row),
17692
+ spanId: digestHex(span, 8),
17693
+ rootSpanId: digestHex(root, 16)
17694
+ };
17695
+ }
17696
+ async function childSpanIds(inputFileId, customId) {
17697
+ const [row, span] = await Promise.all([
17698
+ deterministicDigest("openai:batch:child:row", inputFileId, customId),
17699
+ deterministicDigest("openai:batch:child:span", inputFileId, customId)
17700
+ ]);
17701
+ return { rowId: digestUuid(row), spanId: digestHex(span, 8) };
17702
+ }
17703
+ async function startBatchSpan(context2) {
17704
+ const ids = await batchSpanIds(context2.inputFileId);
17705
+ const parent = SpanComponentsV4.fromStr(context2.parent);
17706
+ const hasParentSpan = Boolean(
17707
+ parent.data.row_id && parent.data.span_id && parent.data.root_span_id
17708
+ );
17709
+ return withCurrent(
17710
+ NOOP_SPAN,
17711
+ () => _internalStartSpanWithInitialMergeAndParentSpanIds(
17712
+ withSpanInstrumentationName(
17713
+ {
17714
+ name: "openai.batch",
17715
+ type: "task" /* TASK */,
17716
+ parent: context2.parent,
17717
+ ...!hasParentSpan ? {
17718
+ parentSpanIds: {
17719
+ parentSpanIds: [],
17720
+ rootSpanId: ids.rootSpanId
17721
+ }
17722
+ } : {},
17723
+ spanId: ids.spanId,
17724
+ startTime: context2.taskStartTime,
17725
+ event: {
17726
+ id: ids.rowId,
17727
+ metadata: {
17728
+ endpoint: context2.endpoint,
17729
+ input_file_id: context2.inputFileId,
17730
+ provider: "openai"
17731
+ }
17732
+ }
17733
+ },
17734
+ INSTRUMENTATION_NAMES.OPENAI
17735
+ )
17736
+ )
17737
+ );
17738
+ }
17739
+ async function startBatchChild(context2, taskParent, input) {
17740
+ const ids = await childSpanIds(context2.inputFileId, input.customId);
17741
+ return withCurrent(
17742
+ NOOP_SPAN,
17743
+ () => _internalStartSpanWithInitialMerge(
17744
+ withSpanInstrumentationName(
17745
+ {
17746
+ name: context2.endpoint === "/v1/chat/completions" ? "Chat Completion" : "openai.responses.create",
17747
+ type: "llm" /* LLM */,
17748
+ parent: taskParent,
17749
+ spanId: ids.spanId,
17750
+ startTime: context2.childStartTime,
17751
+ event: {
17752
+ id: ids.rowId,
17753
+ ...input.spanData?.input !== void 0 ? { input: input.spanData.input } : {},
17754
+ metadata: {
17755
+ ...input.spanData?.metadata,
17756
+ custom_id: input.customId,
17757
+ provider: "openai"
17758
+ }
17759
+ }
17760
+ },
17761
+ INSTRUMENTATION_NAMES.OPENAI
17762
+ )
17763
+ )
17764
+ );
17765
+ }
17766
+ async function* jsonlRecords(file, onIssue = () => {
17767
+ }) {
17768
+ const resolvedFile = await file;
17769
+ if (typeof resolvedFile === "string") {
17770
+ for (const line of resolvedFile.split("\n")) {
17771
+ if (!line.trim()) {
17772
+ continue;
17773
+ }
17774
+ try {
17775
+ yield JSON.parse(line.replace(/\r$/, ""));
17776
+ } catch (error2) {
17777
+ logBatchInstrumentationError("skipped malformed JSONL", error2);
17778
+ onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
17779
+ }
17780
+ }
17781
+ return;
17782
+ }
17783
+ const body = read(resolvedFile, "body");
17784
+ const getReader = read(body, "getReader");
17785
+ if (isObject(body) && typeof getReader === "function") {
17786
+ let reader;
17787
+ try {
17788
+ reader = Reflect.apply(getReader, body, []);
17789
+ const decoder = new TextDecoder();
17790
+ let pending = "";
17791
+ while (true) {
17792
+ const readChunk = read(reader, "read");
17793
+ if (typeof readChunk !== "function") {
17794
+ throw new Error("Response body stream has no read method");
17795
+ }
17796
+ const chunk = await Reflect.apply(readChunk, reader, []);
17797
+ if (!isObject(chunk)) {
17798
+ throw new Error("Response body stream returned an invalid chunk");
17799
+ }
17800
+ if (chunk.done === true) {
17801
+ pending += decoder.decode();
17802
+ break;
17803
+ }
17804
+ if (!(chunk.value instanceof Uint8Array)) {
17805
+ throw new Error("Response body stream returned a non-byte chunk");
17806
+ }
17807
+ pending += decoder.decode(chunk.value, { stream: true });
17808
+ let newline = pending.indexOf("\n");
17809
+ while (newline !== -1) {
17810
+ const line = pending.slice(0, newline).replace(/\r$/, "");
17811
+ pending = pending.slice(newline + 1);
17812
+ if (line.trim()) {
17813
+ try {
17814
+ yield JSON.parse(line);
17815
+ } catch (error2) {
17816
+ logBatchInstrumentationError("skipped malformed JSONL", error2);
17817
+ onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
17818
+ }
17819
+ }
17820
+ newline = pending.indexOf("\n");
17821
+ }
17822
+ }
17823
+ if (pending.trim()) {
17824
+ try {
17825
+ yield JSON.parse(pending.replace(/\r$/, ""));
17826
+ } catch (error2) {
17827
+ logBatchInstrumentationError("skipped malformed JSONL", error2);
17828
+ onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
17829
+ }
17830
+ }
17831
+ } catch (error2) {
17832
+ logBatchInstrumentationError("could not read JSONL response body", error2);
17833
+ onIssue(new Error("OpenAI Batch response body could not be read"));
17834
+ } finally {
17835
+ const releaseLock = read(reader, "releaseLock");
17836
+ if (typeof releaseLock === "function") {
17837
+ try {
17838
+ Reflect.apply(releaseLock, reader, []);
17839
+ } catch (error2) {
17840
+ logBatchInstrumentationError(
17841
+ "could not release stream reader",
17842
+ error2
17843
+ );
17844
+ }
17845
+ }
17846
+ }
17847
+ return;
17848
+ }
17849
+ if (isBatchRecordIterable(resolvedFile)) {
17850
+ for await (const record of resolvedFile) {
17851
+ yield record;
17852
+ }
17853
+ return;
17854
+ }
17855
+ logBatchInstrumentationError("skipped invalid JSONL source", resolvedFile);
17856
+ onIssue(new Error("OpenAI Batch file source is invalid"));
17857
+ }
17858
+ async function readBatchInputs(file) {
17859
+ const inputs = /* @__PURE__ */ new Map();
17860
+ const issues = [];
17861
+ let endpoint;
17862
+ try {
17863
+ for await (const value of jsonlRecords(
17864
+ file,
17865
+ (issue) => issues.push(issue)
17866
+ )) {
17867
+ const customId = read(value, "custom_id");
17868
+ const url = read(value, "url");
17869
+ const body = read(value, "body");
17870
+ if (!validCustomId(customId) || inputs.has(customId) || read(value, "method") !== "POST" || typeof url !== "string" || !SUPPORTED_ENDPOINTS.has(url) || endpoint !== void 0 && endpoint !== url || !isObject(body)) {
17871
+ issues.push(new Error("OpenAI Batch input contains an invalid record"));
17872
+ continue;
17873
+ }
17874
+ endpoint = url;
17875
+ let spanData;
17876
+ try {
17877
+ spanData = extractOpenAIBatchInput(url, body);
17878
+ } catch (error2) {
17879
+ logBatchInstrumentationError("could not extract batch input", error2);
17880
+ }
17881
+ inputs.set(customId, { customId, spanData });
17882
+ }
17883
+ } catch (error2) {
17884
+ logBatchInstrumentationError("could not process input file", error2);
17885
+ issues.push(new Error("OpenAI Batch input file could not be processed"));
17886
+ }
17887
+ if (!endpoint || inputs.size === 0) {
17888
+ issues.push(new Error("OpenAI Batch input contains no supported records"));
17889
+ }
17890
+ return { endpoint, inputs, issues };
17891
+ }
17892
+ async function writePendingSpans(trace, endTime) {
17893
+ const task = await startBatchSpan(trace.context);
17894
+ const taskParent = await task.export();
17895
+ for (const input of trace.inputs.values()) {
17896
+ try {
17897
+ const child = await startBatchChild(trace.context, taskParent, input);
17898
+ if (endTime !== void 0) {
17899
+ child.end({ endTime });
17900
+ }
17901
+ } catch (error2) {
17902
+ logBatchInstrumentationError("could not write batch request span", error2);
17903
+ }
17904
+ }
17905
+ if (endTime !== void 0) {
17906
+ if (trace.status && trace.status !== "completed") {
17907
+ task.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
17908
+ }
17909
+ task.end({ endTime });
17910
+ }
17911
+ }
17912
+ var interceptOpenAIFilesCreateTraced = async (target, thisArg, args) => {
17913
+ const startTime = getCurrentUnixTimestamp();
17914
+ const inputPromise = readBatchInputs(args[0].inputFileContent);
17915
+ const parentPromise = exportParent(args[0].parent);
17916
+ const file = await Reflect.apply(target, thisArg, args);
17917
+ try {
17918
+ const inputFileId = read(file, "id");
17919
+ const [inputData, exportedParent2] = await Promise.all([
17920
+ inputPromise,
17921
+ parentPromise
17922
+ ]);
17923
+ if (typeof inputFileId !== "string" || !exportedParent2 || !inputData.endpoint || inputData.issues.length > 0) {
17924
+ if (inputData.issues[0]) {
17925
+ logBatchInstrumentationError(
17926
+ "skipped invalid input file",
17927
+ inputData.issues[0]
17928
+ );
17929
+ }
17930
+ return file;
17931
+ }
17932
+ const trace = {
17933
+ context: {
17934
+ inputFileId,
17935
+ endpoint: inputData.endpoint,
17936
+ parent: exportedParent2,
17937
+ taskStartTime: startTime,
17938
+ childStartTime: startTime
17939
+ },
17940
+ inputs: inputData.inputs
17941
+ };
17942
+ pendingBatchTraces.set(inputFileId, trace);
17943
+ await writePendingSpans(trace);
17944
+ } catch (error2) {
17945
+ logBatchInstrumentationError("could not start batch spans", error2);
17946
+ }
17947
+ return file;
17948
+ };
17949
+ function validTimestamp(value) {
17950
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
17951
+ }
17952
+ function terminalEndTime(batch, startTime) {
17953
+ let timestamp;
17954
+ switch (batch.status) {
17955
+ case "completed":
17956
+ timestamp = batch.completed_at;
17957
+ break;
17958
+ case "failed":
17959
+ timestamp = batch.failed_at;
17960
+ break;
17961
+ case "expired":
17962
+ timestamp = batch.expired_at;
17963
+ break;
17964
+ default:
17965
+ timestamp = batch.cancelled_at;
17966
+ }
17967
+ return validTimestamp(timestamp) ? Math.max(timestamp, startTime) : Math.max(getCurrentUnixTimestamp(), startTime);
17968
+ }
17969
+ async function updateBatchTimestamps(batch) {
17970
+ const trace = pendingBatchTraces.get(batch.input_file_id);
17971
+ if (!trace || trace.context.endpoint !== batch.endpoint) {
17972
+ return;
17973
+ }
17974
+ if (validTimestamp(batch.created_at)) {
17975
+ trace.context.taskStartTime = batch.created_at;
17976
+ }
17977
+ trace.context.childStartTime = validTimestamp(batch.in_progress_at) ? Math.max(batch.in_progress_at, trace.context.taskStartTime) : trace.context.taskStartTime;
17978
+ trace.status = batch.status;
17979
+ if (TERMINAL_STATUSES.has(batch.status)) {
17980
+ trace.endTime = terminalEndTime(batch, trace.context.childStartTime);
17981
+ }
17982
+ await writePendingSpans(trace, trace.endTime);
17983
+ }
17984
+ var interceptOpenAIBatchesRetrieveTraced = (target, thisArg, args) => Promise.resolve(Reflect.apply(target, thisArg, args)).then(async (batch) => {
17985
+ try {
17986
+ await updateBatchTimestamps(batch);
17987
+ } catch (error2) {
17988
+ logBatchInstrumentationError("could not update batch timestamps", error2);
17989
+ }
17990
+ return batch;
17991
+ });
17992
+ function errorFromResult(result) {
17993
+ const error2 = read(result.value, "error");
17994
+ if (isObject(error2)) {
17995
+ const message = read(error2, "message");
17996
+ return new Error(
17997
+ typeof message === "string" ? message : "OpenAI Batch request failed"
17998
+ );
17999
+ }
18000
+ const response = read(result.value, "response");
18001
+ const statusCode = read(response, "status_code");
18002
+ if (result.source === "error" || typeof statusCode === "number" && (statusCode < 200 || statusCode >= 300)) {
18003
+ const message = read(read(read(response, "body"), "error"), "message");
18004
+ return new Error(
18005
+ typeof message === "string" ? message : "OpenAI Batch request failed"
18006
+ );
18007
+ }
18008
+ return void 0;
18009
+ }
18010
+ async function completeBatchResult(context2, taskParent, endTime, input, result) {
18011
+ const child = await startBatchChild(context2, taskParent, input);
18012
+ try {
18013
+ const resultError = errorFromResult(result);
18014
+ const responseBody = read(read(result.value, "response"), "body");
18015
+ if (resultError) {
18016
+ child.log({ error: resultError });
18017
+ } else if (isObject(responseBody)) {
18018
+ const model = read(responseBody, "model");
18019
+ child.log({
18020
+ output: context2.endpoint === "/v1/chat/completions" ? read(responseBody, "choices") : processImagesInOutput(read(responseBody, "output")),
18021
+ ...typeof model === "string" ? { metadata: { model } } : {},
18022
+ metrics: parseMetricsFromUsage(read(responseBody, "usage"))
18023
+ });
18024
+ } else {
18025
+ child.log({ error: new Error("OpenAI Batch response body is missing") });
18026
+ }
18027
+ } catch (error2) {
18028
+ child.log({ error: error2 });
18029
+ } finally {
18030
+ child.end({ endTime });
18031
+ }
18032
+ }
18033
+ async function completeResultFile({
18034
+ context: context2,
18035
+ endTime,
18036
+ file,
18037
+ inputs,
18038
+ issues,
18039
+ seen,
18040
+ source,
18041
+ taskParent
18042
+ }) {
18043
+ if (file === void 0) {
18044
+ return;
18045
+ }
18046
+ for await (const value of jsonlRecords(file, (issue) => issues.push(issue))) {
18047
+ const customId = read(value, "custom_id");
18048
+ if (!validCustomId(customId) || !isObject(value)) {
18049
+ issues.push(
18050
+ new Error("OpenAI Batch result is missing a valid custom_id")
18051
+ );
18052
+ continue;
18053
+ }
18054
+ if (seen.has(customId)) {
18055
+ issues.push(new Error("OpenAI Batch result contains a duplicate"));
18056
+ continue;
18057
+ }
18058
+ const input = inputs.get(customId);
18059
+ if (!input) {
18060
+ issues.push(
18061
+ new Error("OpenAI Batch result does not match an input record")
18062
+ );
18063
+ continue;
18064
+ }
18065
+ seen.add(customId);
18066
+ await completeBatchResult(context2, taskParent, endTime, input, {
18067
+ value,
18068
+ source
18069
+ });
18070
+ }
18071
+ }
18072
+ function sameInputs(first, second) {
18073
+ return first.size === second.size && [...first.keys()].every((customId) => second.has(customId));
18074
+ }
18075
+ async function contextForCompletion(inputFileId, inputData) {
18076
+ if (!inputData.endpoint || inputData.issues.length > 0) {
18077
+ return void 0;
18078
+ }
18079
+ const existing = pendingBatchTraces.get(inputFileId);
18080
+ if (existing) {
18081
+ if (existing.context.endpoint !== inputData.endpoint || !sameInputs(existing.inputs, inputData.inputs)) {
18082
+ return void 0;
18083
+ }
18084
+ return { ...existing, inputs: inputData.inputs };
18085
+ }
18086
+ const parent = await exportParent(getSpanParentObject());
18087
+ if (!parent) {
18088
+ return void 0;
18089
+ }
18090
+ const startTime = getCurrentUnixTimestamp();
18091
+ return {
18092
+ context: {
18093
+ inputFileId,
18094
+ endpoint: inputData.endpoint,
18095
+ parent,
18096
+ taskStartTime: startTime,
18097
+ childStartTime: startTime
18098
+ },
18099
+ inputs: inputData.inputs
18100
+ };
18101
+ }
18102
+ async function completeBatch(args) {
18103
+ const inputData = await readBatchInputs(args.inputFileContent);
18104
+ const trace = await contextForCompletion(args.inputFileId, inputData);
18105
+ if (!trace) {
18106
+ logBatchInstrumentationError(
18107
+ "left batch spans pending",
18108
+ inputData.issues[0] ?? new Error("OpenAI Batch input does not match")
18109
+ );
18110
+ return;
18111
+ }
18112
+ const endTime = trace.endTime ?? getCurrentUnixTimestamp();
18113
+ const task = await startBatchSpan(trace.context);
18114
+ const taskParent = await task.export();
18115
+ const issues = [];
18116
+ const seen = /* @__PURE__ */ new Set();
18117
+ await Promise.all([
18118
+ completeResultFile({
18119
+ context: trace.context,
18120
+ endTime,
18121
+ file: args.outputFileContent,
18122
+ inputs: trace.inputs,
18123
+ issues,
18124
+ seen,
18125
+ source: "output",
18126
+ taskParent
18127
+ }),
18128
+ completeResultFile({
18129
+ context: trace.context,
18130
+ endTime,
18131
+ file: args.errorFileContent,
18132
+ inputs: trace.inputs,
18133
+ issues,
18134
+ seen,
18135
+ source: "error",
18136
+ taskParent
18137
+ })
18138
+ ]);
18139
+ if (issues.length > 0) {
18140
+ logBatchInstrumentationError("left batch spans pending", issues[0]);
18141
+ return;
18142
+ }
18143
+ const missing = [...trace.inputs.values()].filter(
18144
+ ({ customId }) => !seen.has(customId)
18145
+ );
18146
+ if (!trace.status || trace.status === "completed") {
18147
+ if (missing.length > 0) {
18148
+ logBatchInstrumentationError(
18149
+ "left batch spans pending",
18150
+ new Error("OpenAI Batch result files are incomplete")
18151
+ );
18152
+ return;
18153
+ }
18154
+ } else {
18155
+ for (const input of missing) {
18156
+ const child = await startBatchChild(trace.context, taskParent, input);
18157
+ child.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
18158
+ child.end({ endTime });
18159
+ }
18160
+ task.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
18161
+ }
18162
+ task.end({ endTime });
18163
+ pendingBatchTraces.delete(args.inputFileId);
18164
+ }
18165
+ var interceptOpenAIBatchTraceComplete = (target, thisArg, args) => Promise.resolve(Reflect.apply(target, thisArg, args)).then(async (result) => {
18166
+ try {
18167
+ await completeBatch(args[0]);
18168
+ } catch (error2) {
18169
+ logBatchInstrumentationError("could not complete batch", error2);
18170
+ }
18171
+ return result;
18172
+ });
18173
+
17522
18174
  // src/instrumentation/plugins/openai-plugin.ts
17523
18175
  var OpenAIPlugin = class extends BasePlugin {
17524
18176
  constructor() {
17525
18177
  super();
17526
18178
  }
17527
18179
  onEnable() {
18180
+ this.unsubscribers.push(
18181
+ openAIChannels.filesCreateTraced.intercept(
18182
+ interceptOpenAIFilesCreateTraced
18183
+ ),
18184
+ openAIChannels.batchesRetrieveTraced.intercept(
18185
+ interceptOpenAIBatchesRetrieveTraced
18186
+ ),
18187
+ openAIChannels.batchesCompleteTrace.intercept(
18188
+ interceptOpenAIBatchTraceComplete
18189
+ )
18190
+ );
17528
18191
  this.unsubscribers.push(
17529
18192
  traceStreamingChannel(openAIChannels.chatCompletionsCreate, {
17530
18193
  name: "Chat Completion",
17531
18194
  type: "llm" /* LLM */,
17532
- extractInput: ([params]) => {
17533
- const { messages, ...metadata } = params;
17534
- return {
17535
- input: processInputAttachments(messages),
17536
- metadata: { ...metadata, provider: "openai" }
17537
- };
17538
- },
18195
+ extractInput: ([params]) => extractOpenAIChatInput(params),
17539
18196
  extractOutput: (result) => {
17540
18197
  return result?.choices;
17541
18198
  },
@@ -17581,13 +18238,7 @@ var OpenAIPlugin = class extends BasePlugin {
17581
18238
  traceStreamingChannel(openAIChannels.betaChatCompletionsParse, {
17582
18239
  name: "Chat Completion",
17583
18240
  type: "llm" /* LLM */,
17584
- extractInput: ([params]) => {
17585
- const { messages, ...metadata } = params;
17586
- return {
17587
- input: processInputAttachments(messages),
17588
- metadata: { ...metadata, provider: "openai" }
17589
- };
17590
- },
18241
+ extractInput: ([params]) => extractOpenAIChatInput(params),
17591
18242
  extractOutput: (result) => {
17592
18243
  return result?.choices;
17593
18244
  },
@@ -17609,13 +18260,7 @@ var OpenAIPlugin = class extends BasePlugin {
17609
18260
  traceSyncStreamChannel(openAIChannels.betaChatCompletionsStream, {
17610
18261
  name: "Chat Completion",
17611
18262
  type: "llm" /* LLM */,
17612
- extractInput: ([params]) => {
17613
- const { messages, ...metadata } = params;
17614
- return {
17615
- input: processInputAttachments(messages),
17616
- metadata: { ...metadata, provider: "openai" }
17617
- };
17618
- }
18263
+ extractInput: ([params]) => extractOpenAIChatInput(params)
17619
18264
  })
17620
18265
  );
17621
18266
  this.unsubscribers.push(
@@ -17645,23 +18290,11 @@ var OpenAIPlugin = class extends BasePlugin {
17645
18290
  traceStreamingChannel(openAIChannels.responsesCreate, {
17646
18291
  name: "openai.responses.create",
17647
18292
  type: "llm" /* LLM */,
17648
- extractInput: ([params]) => {
17649
- const { input, ...metadata } = params;
17650
- return {
17651
- input: processInputAttachments(input),
17652
- metadata: { ...metadata, provider: "openai" }
17653
- };
17654
- },
18293
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
17655
18294
  extractOutput: (result) => {
17656
18295
  return processImagesInOutput(result?.output);
17657
18296
  },
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
- },
18297
+ extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
17665
18298
  extractMetrics: (result, startTime, endEvent) => {
17666
18299
  const metrics = withCachedMetric(
17667
18300
  parseMetricsFromUsage(result?.usage),
@@ -17680,13 +18313,7 @@ var OpenAIPlugin = class extends BasePlugin {
17680
18313
  traceSyncStreamChannel(openAIChannels.responsesStream, {
17681
18314
  name: "openai.responses.create",
17682
18315
  type: "llm" /* LLM */,
17683
- extractInput: ([params]) => {
17684
- const { input, ...metadata } = params;
17685
- return {
17686
- input: processInputAttachments(input),
17687
- metadata: { ...metadata, provider: "openai" }
17688
- };
17689
- },
18316
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
17690
18317
  extractFromEvent: (event) => {
17691
18318
  if (event.type !== "response.completed" || !event.response) {
17692
18319
  return {};
@@ -17709,23 +18336,11 @@ var OpenAIPlugin = class extends BasePlugin {
17709
18336
  traceStreamingChannel(openAIChannels.responsesParse, {
17710
18337
  name: "openai.responses.parse",
17711
18338
  type: "llm" /* LLM */,
17712
- extractInput: ([params]) => {
17713
- const { input, ...metadata } = params;
17714
- return {
17715
- input: processInputAttachments(input),
17716
- metadata: { ...metadata, provider: "openai" }
17717
- };
17718
- },
18339
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
17719
18340
  extractOutput: (result) => {
17720
18341
  return processImagesInOutput(result?.output);
17721
18342
  },
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
- },
18343
+ extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
17729
18344
  extractMetrics: (result, startTime, endEvent) => {
17730
18345
  const metrics = withCachedMetric(
17731
18346
  parseMetricsFromUsage(result?.usage),
@@ -17744,23 +18359,11 @@ var OpenAIPlugin = class extends BasePlugin {
17744
18359
  traceAsyncChannel(openAIChannels.responsesCompact, {
17745
18360
  name: "openai.responses.compact",
17746
18361
  type: "llm" /* LLM */,
17747
- extractInput: ([params]) => {
17748
- const { input, ...metadata } = params;
17749
- return {
17750
- input: processInputAttachments(input),
17751
- metadata: { ...metadata, provider: "openai" }
17752
- };
17753
- },
18362
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
17754
18363
  extractOutput: (result) => {
17755
18364
  return processImagesInOutput(result?.output);
17756
18365
  },
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
- },
18366
+ extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
17764
18367
  extractMetrics: (result, startTime, endEvent) => {
17765
18368
  const metrics = withCachedMetric(
17766
18369
  parseMetricsFromUsage(result?.usage),
@@ -17816,35 +18419,6 @@ function withCachedMetric(metrics, result, endEvent) {
17816
18419
  cached
17817
18420
  };
17818
18421
  }
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
18422
  function mergeLogprobTokens(existing, incoming) {
17849
18423
  if (incoming === void 0) {
17850
18424
  return existing;
@@ -37864,7 +38438,7 @@ function extractSession(event) {
37864
38438
  return isObject(candidate) && typeof candidate.prompt === "function" ? candidate : void 0;
37865
38439
  }
37866
38440
  function isPiAgent(value) {
37867
- return isObject(value) && typeof value.streamFn === "function" && typeof value.subscribe === "function";
38441
+ return isObject(value) && (typeof value.streamFunction === "function" || typeof value.streamFn === "function") && typeof value.subscribe === "function";
37868
38442
  }
37869
38443
  function promptContextStore() {
37870
38444
  piPromptContextStore ??= isomorph_default.newAsyncLocalStorage();
@@ -37874,17 +38448,21 @@ function currentPiPromptState() {
37874
38448
  return promptContextStore().getStore();
37875
38449
  }
37876
38450
  function installPiAgentInstrumentation(agent) {
38451
+ const property = typeof agent.streamFunction === "function" ? "streamFunction" : "streamFn";
38452
+ const streamFunction = agent[property];
37877
38453
  const existing = piAgentPatchStates.get(agent);
37878
- if (!existing || agent.streamFn !== existing.wrappedStreamFn) {
38454
+ if (streamFunction && (!existing || existing.property !== property || streamFunction !== existing.wrappedStreamFunction)) {
37879
38455
  const patchState = {
37880
- originalStreamFn: agent.streamFn,
37881
- wrappedStreamFn: agent.streamFn
38456
+ originalStreamFunction: streamFunction,
38457
+ property,
38458
+ wrappedStreamFunction: streamFunction
37882
38459
  };
37883
- patchState.wrappedStreamFn = makeInstrumentedStreamFn(
38460
+ patchState.wrappedStreamFunction = makeInstrumentedStreamFunction(
37884
38461
  agent,
37885
- patchState.originalStreamFn
38462
+ patchState.originalStreamFunction,
38463
+ property
37886
38464
  );
37887
- agent.streamFn = patchState.wrappedStreamFn;
38465
+ agent[property] = patchState.wrappedStreamFunction;
37888
38466
  piAgentPatchStates.set(agent, patchState);
37889
38467
  }
37890
38468
  if (piAgentEventSubscriptions.has(agent)) {
@@ -37909,15 +38487,21 @@ function installPiAgentInstrumentation(agent) {
37909
38487
  logInstrumentationError4("Pi Coding Agent event subscription", error2);
37910
38488
  }
37911
38489
  }
37912
- function makeInstrumentedStreamFn(agent, originalStreamFn) {
37913
- return async function instrumentedPiStreamFn(model, context2, options) {
37914
- const invokeOriginal = () => Reflect.apply(originalStreamFn, this, [model, context2, options]);
38490
+ function makeInstrumentedStreamFunction(agent, originalStreamFunction, property) {
38491
+ return async function instrumentedPiStreamFunction(model, context2, options) {
38492
+ const invokeOriginal = () => Reflect.apply(originalStreamFunction, this, [model, context2, options]);
37915
38493
  const state = currentPiPromptState();
37916
38494
  if (!state || state.agent !== agent || state.finalized) {
37917
38495
  return invokeOriginal();
37918
38496
  }
37919
38497
  wrapPiToolExecutors(context2.tools);
37920
- const llmState = await startPiLlmSpan(state, model, context2, options);
38498
+ const llmState = await startPiLlmSpan(
38499
+ state,
38500
+ model,
38501
+ context2,
38502
+ property,
38503
+ options
38504
+ );
37921
38505
  try {
37922
38506
  const stream = await runWithAutoInstrumentationSuppressed(invokeOriginal);
37923
38507
  return patchAssistantMessageStream(stream, state, llmState);
@@ -37954,12 +38538,12 @@ function wrapPiToolExecutors(tools) {
37954
38538
  }
37955
38539
  }
37956
38540
  }
37957
- async function startPiLlmSpan(state, model, context2, options) {
38541
+ async function startPiLlmSpan(state, model, context2, property, options) {
37958
38542
  const metadata = {
37959
38543
  ...extractModelMetadata2(model),
37960
38544
  ...extractStreamOptionsMetadata(options),
37961
38545
  ...extractToolMetadata(context2.tools),
37962
- "pi_coding_agent.operation": "agent.streamFn"
38546
+ "pi_coding_agent.operation": `agent.${property}`
37963
38547
  };
37964
38548
  const span = startSpan(
37965
38549
  withSpanInstrumentationName(
@@ -44056,7 +44640,7 @@ var openRouterAgentConfigs = [
44056
44640
  ];
44057
44641
 
44058
44642
  // src/auto-instrumentations/configs/pi-coding-agent.ts
44059
- var piCodingAgentVersionRange = ">=0.79.0 <0.82.0";
44643
+ var piCodingAgentVersionRange = ">=0.79.0 <1.0.0";
44060
44644
  var piCodingAgentConfigs = [
44061
44645
  {
44062
44646
  channelName: piCodingAgentChannels.prompt.channelName,