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
@@ -3917,6 +3917,7 @@ var ProjectSettings = z6.union([
3917
3917
  ]),
3918
3918
  disable_realtime_queries: z6.union([z6.boolean(), z6.null()]),
3919
3919
  monitor_charts_use_metrics_start: z6.union([z6.boolean(), z6.null()]),
3920
+ blind_reviews: z6.union([z6.boolean(), z6.null()]),
3920
3921
  default_preprocessor: NullableSavedFunctionId
3921
3922
  }).partial(),
3922
3923
  z6.null()
@@ -3978,6 +3979,7 @@ var WindowedAutomationConfig = z6.object({
3978
3979
  auto_approve_tools: z6.array(z6.string().min(1)).optional().default([]),
3979
3980
  harness: z6.enum(["native", "codex", "claude-code"]).optional(),
3980
3981
  model: z6.string().min(1).optional(),
3982
+ endpoint_name: z6.string().min(1).optional(),
3981
3983
  reasoning_effort: z6.enum(["none", "minimal", "low", "medium", "high", "xhigh", "max"]).optional()
3982
3984
  }).optional(),
3983
3985
  actions: z6.array(
@@ -5771,7 +5773,7 @@ var INSTRUMENTATION_NAMES = {
5771
5773
  var INTERNAL_SPAN_INSTRUMENTATION_NAME = /* @__PURE__ */ Symbol.for(
5772
5774
  "braintrust.spanInstrumentationName"
5773
5775
  );
5774
- var SDK_VERSION = true ? "3.29.0" : "0.0.0";
5776
+ var SDK_VERSION = true ? "3.31.0" : "0.0.0";
5775
5777
  function withSpanInstrumentationName(args, instrumentationName) {
5776
5778
  return {
5777
5779
  ...args,
@@ -8487,6 +8489,9 @@ function getSpanParentObjectAndPropagatedState(options) {
8487
8489
  }
8488
8490
  return { parentObject: NOOP_SPAN, propagatedState: void 0 };
8489
8491
  }
8492
+ function getSpanParentObject(options) {
8493
+ return getSpanParentObjectAndPropagatedState(options).parentObject;
8494
+ }
8490
8495
  function currentBraintrustParent(state) {
8491
8496
  const resolvedState = state ?? _globalState;
8492
8497
  const experiment = currentExperiment({ state: resolvedState });
@@ -8733,6 +8738,15 @@ function _internalStartSpanWithInitialMerge(args) {
8733
8738
  [INITIAL_SPAN_WRITE_AS_MERGE]: true
8734
8739
  }).span;
8735
8740
  }
8741
+ function _internalStartSpanWithInitialMergeAndParentSpanIds(args) {
8742
+ return startSpanAndIsLogger(
8743
+ {
8744
+ ...args,
8745
+ [INITIAL_SPAN_WRITE_AS_MERGE]: true
8746
+ },
8747
+ { useParentSpanIdsForObjectParent: true }
8748
+ ).span;
8749
+ }
8736
8750
  function _internalStartSpanWithContext(args, context) {
8737
8751
  return startSpanAndIsLogger({
8738
8752
  ...args,
@@ -8743,7 +8757,7 @@ async function flush(options) {
8743
8757
  const state = options?.state ?? _globalState;
8744
8758
  return await state.bgLogger().flush();
8745
8759
  }
8746
- function startSpanAndIsLogger(args) {
8760
+ function startSpanAndIsLogger(args, internalOptions) {
8747
8761
  const state = args?.state ?? _globalState;
8748
8762
  const { parentObject, propagatedState } = getSpanParentObjectAndPropagatedState({
8749
8763
  asyncFlush: args?.asyncFlush,
@@ -8757,7 +8771,7 @@ function startSpanAndIsLogger(args) {
8757
8771
  ) ? {
8758
8772
  spanId: parentObject.data.span_id,
8759
8773
  rootSpanId: parentObject.data.root_span_id
8760
- } : void 0;
8774
+ } : internalOptions?.useParentSpanIdsForObjectParent ? args?.parentSpanIds : void 0;
8761
8775
  const { parent: _ignoredParent, ...spanArgs } = args ?? {};
8762
8776
  const span = new SpanImpl({
8763
8777
  state,
@@ -11787,6 +11801,131 @@ function unsubscribeAll(unsubscribers) {
11787
11801
  return [];
11788
11802
  }
11789
11803
 
11804
+ // src/instrumentation/core/channel-definitions.ts
11805
+ function channel(spec) {
11806
+ return spec;
11807
+ }
11808
+ function defineChannels(pkg, channels, options) {
11809
+ const { instrumentationName } = options;
11810
+ return Object.fromEntries(
11811
+ Object.entries(channels).map(([key, spec]) => {
11812
+ const fullChannelName = `orchestrion:${pkg}:${spec.channelName}`;
11813
+ if (spec.kind === "async") {
11814
+ const asyncSpec = spec;
11815
+ const tracingChannel2 = () => isomorph_default.newTracingChannel(
11816
+ fullChannelName
11817
+ );
11818
+ const intercept2 = (interceptor) => {
11819
+ const hook = tracingChannel2();
11820
+ return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
11821
+ };
11822
+ };
11823
+ return [
11824
+ key,
11825
+ {
11826
+ ...asyncSpec,
11827
+ instrumentationName,
11828
+ intercept: intercept2,
11829
+ invoke: (target, thisArg, args, additional) => {
11830
+ const hook = tracingChannel2();
11831
+ return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
11832
+ },
11833
+ tracingChannel: tracingChannel2,
11834
+ tracePromise: (fn, context) => tracingChannel2().tracePromise(
11835
+ fn,
11836
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
11837
+ context
11838
+ )
11839
+ }
11840
+ ];
11841
+ }
11842
+ const syncSpec = spec;
11843
+ const tracingChannel = () => isomorph_default.newTracingChannel(
11844
+ fullChannelName
11845
+ );
11846
+ const intercept = (interceptor) => {
11847
+ const hook = tracingChannel();
11848
+ return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
11849
+ };
11850
+ };
11851
+ return [
11852
+ key,
11853
+ {
11854
+ ...syncSpec,
11855
+ instrumentationName,
11856
+ intercept,
11857
+ invoke: (target, thisArg, args, additional) => {
11858
+ const hook = tracingChannel();
11859
+ return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
11860
+ },
11861
+ tracingChannel,
11862
+ traceSync: (fn, context) => tracingChannel().traceSync(
11863
+ fn,
11864
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
11865
+ context
11866
+ )
11867
+ }
11868
+ ];
11869
+ })
11870
+ );
11871
+ }
11872
+
11873
+ // src/instrumentation/plugins/openai-channels.ts
11874
+ var openAIChannels = defineChannels(
11875
+ "openai",
11876
+ {
11877
+ filesCreateTraced: channel({
11878
+ channelName: "files.create-traced",
11879
+ kind: "async"
11880
+ }),
11881
+ batchesRetrieveTraced: channel({
11882
+ channelName: "batches.retrieve-traced",
11883
+ kind: "async"
11884
+ }),
11885
+ batchesCompleteTrace: channel({
11886
+ channelName: "batches.complete-trace",
11887
+ kind: "async"
11888
+ }),
11889
+ chatCompletionsCreate: channel({
11890
+ channelName: "chat.completions.create",
11891
+ kind: "async"
11892
+ }),
11893
+ embeddingsCreate: channel({
11894
+ channelName: "embeddings.create",
11895
+ kind: "async"
11896
+ }),
11897
+ betaChatCompletionsParse: channel({
11898
+ channelName: "beta.chat.completions.parse",
11899
+ kind: "async"
11900
+ }),
11901
+ betaChatCompletionsStream: channel({
11902
+ channelName: "beta.chat.completions.stream",
11903
+ kind: "sync-stream"
11904
+ }),
11905
+ moderationsCreate: channel({
11906
+ channelName: "moderations.create",
11907
+ kind: "async"
11908
+ }),
11909
+ responsesCreate: channel({
11910
+ channelName: "responses.create",
11911
+ kind: "async"
11912
+ }),
11913
+ responsesStream: channel({
11914
+ channelName: "responses.stream",
11915
+ kind: "sync-stream"
11916
+ }),
11917
+ responsesParse: channel({
11918
+ channelName: "responses.parse",
11919
+ kind: "async"
11920
+ }),
11921
+ responsesCompact: channel({
11922
+ channelName: "responses.compact",
11923
+ kind: "async"
11924
+ })
11925
+ },
11926
+ { instrumentationName: INSTRUMENTATION_NAMES.OPENAI }
11927
+ );
11928
+
11790
11929
  // src/wrappers/attachment-utils.ts
11791
11930
  function getExtensionFromMediaType(mediaType) {
11792
11931
  const extensionMap = {
@@ -11962,118 +12101,91 @@ function processInputAttachments(input) {
11962
12101
  return processNode(input);
11963
12102
  }
11964
12103
 
11965
- // src/instrumentation/core/channel-definitions.ts
11966
- function channel(spec) {
11967
- return spec;
11968
- }
11969
- function defineChannels(pkg, channels, options) {
11970
- const { instrumentationName } = options;
11971
- return Object.fromEntries(
11972
- Object.entries(channels).map(([key, spec]) => {
11973
- const fullChannelName = `orchestrion:${pkg}:${spec.channelName}`;
11974
- if (spec.kind === "async") {
11975
- const asyncSpec = spec;
11976
- const tracingChannel2 = () => isomorph_default.newTracingChannel(
11977
- fullChannelName
11978
- );
11979
- const intercept2 = (interceptor) => {
11980
- const hook = tracingChannel2();
11981
- return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
11982
- };
11983
- };
11984
- return [
11985
- key,
11986
- {
11987
- ...asyncSpec,
11988
- instrumentationName,
11989
- intercept: intercept2,
11990
- invoke: (target, thisArg, args, additional) => {
11991
- const hook = tracingChannel2();
11992
- return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
11993
- },
11994
- tracingChannel: tracingChannel2,
11995
- tracePromise: (fn, context) => tracingChannel2().tracePromise(
11996
- fn,
11997
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
11998
- context
11999
- )
12000
- }
12001
- ];
12104
+ // src/instrumentation/plugins/openai-span-data.ts
12105
+ var OPENAI_METADATA_KEYS = [
12106
+ "model",
12107
+ "temperature",
12108
+ "top_p",
12109
+ "max_tokens",
12110
+ "frequency_penalty",
12111
+ "presence_penalty",
12112
+ "stop",
12113
+ "response_format",
12114
+ "tools",
12115
+ "tool_choice",
12116
+ "parallel_tool_calls",
12117
+ "max_tool_calls"
12118
+ ];
12119
+ function batchMetadata(params) {
12120
+ const metadata = { provider: "openai" };
12121
+ for (const key of OPENAI_METADATA_KEYS) {
12122
+ try {
12123
+ const value = Reflect.get(params, key);
12124
+ if (value !== void 0) {
12125
+ metadata[key] = value;
12002
12126
  }
12003
- const syncSpec = spec;
12004
- const tracingChannel = () => isomorph_default.newTracingChannel(
12005
- fullChannelName
12006
- );
12007
- const intercept = (interceptor) => {
12008
- const hook = tracingChannel();
12009
- return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
12010
- };
12011
- };
12012
- return [
12013
- key,
12014
- {
12015
- ...syncSpec,
12016
- instrumentationName,
12017
- intercept,
12018
- invoke: (target, thisArg, args, additional) => {
12019
- const hook = tracingChannel();
12020
- return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
12021
- },
12022
- tracingChannel,
12023
- traceSync: (fn, context) => tracingChannel().traceSync(
12024
- fn,
12025
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
12026
- context
12027
- )
12028
- }
12029
- ];
12030
- })
12031
- );
12127
+ } catch {
12128
+ }
12129
+ }
12130
+ return metadata;
12131
+ }
12132
+ function extractOpenAIBatchInput(endpoint, params) {
12133
+ const input = endpoint === "/v1/chat/completions" ? params.messages : params.input;
12134
+ return {
12135
+ input: processInputAttachments(input),
12136
+ metadata: batchMetadata(params)
12137
+ };
12138
+ }
12139
+ function extractOpenAIChatInput(params) {
12140
+ const { messages, ...metadata } = params;
12141
+ return {
12142
+ input: processInputAttachments(messages),
12143
+ metadata: { ...metadata, provider: "openai" }
12144
+ };
12145
+ }
12146
+ function extractOpenAIResponsesInput(params) {
12147
+ const { input, ...metadata } = params;
12148
+ return {
12149
+ input: processInputAttachments(input),
12150
+ metadata: { ...metadata, provider: "openai" }
12151
+ };
12152
+ }
12153
+ function extractOpenAIResponsesMetadata(result) {
12154
+ if (!result) {
12155
+ return void 0;
12156
+ }
12157
+ const { output: _output, usage: _usage, ...metadata } = result;
12158
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
12159
+ }
12160
+ function processImagesInOutput(output) {
12161
+ if (Array.isArray(output)) {
12162
+ return output.map(processImagesInOutput);
12163
+ }
12164
+ if (isObject(output) && output.type === "image_generation_call" && typeof output.result === "string" && output.result) {
12165
+ const fileExtension = output.output_format || "png";
12166
+ const contentType = `image/${fileExtension}`;
12167
+ const baseFilename = typeof output.revised_prompt === "string" ? output.revised_prompt.slice(0, 50).replace(/[^a-zA-Z0-9]/g, "_") : "generated_image";
12168
+ let binaryString;
12169
+ try {
12170
+ binaryString = atob(output.result);
12171
+ } catch {
12172
+ return output;
12173
+ }
12174
+ const bytes = new Uint8Array(binaryString.length);
12175
+ for (let i = 0; i < binaryString.length; i++) {
12176
+ bytes[i] = binaryString.charCodeAt(i);
12177
+ }
12178
+ return {
12179
+ ...output,
12180
+ result: new Attachment({
12181
+ data: new Blob([bytes], { type: contentType }),
12182
+ filename: `${baseFilename}.${fileExtension}`,
12183
+ contentType
12184
+ })
12185
+ };
12186
+ }
12187
+ return output;
12032
12188
  }
12033
-
12034
- // src/instrumentation/plugins/openai-channels.ts
12035
- var openAIChannels = defineChannels(
12036
- "openai",
12037
- {
12038
- chatCompletionsCreate: channel({
12039
- channelName: "chat.completions.create",
12040
- kind: "async"
12041
- }),
12042
- embeddingsCreate: channel({
12043
- channelName: "embeddings.create",
12044
- kind: "async"
12045
- }),
12046
- betaChatCompletionsParse: channel({
12047
- channelName: "beta.chat.completions.parse",
12048
- kind: "async"
12049
- }),
12050
- betaChatCompletionsStream: channel({
12051
- channelName: "beta.chat.completions.stream",
12052
- kind: "sync-stream"
12053
- }),
12054
- moderationsCreate: channel({
12055
- channelName: "moderations.create",
12056
- kind: "async"
12057
- }),
12058
- responsesCreate: channel({
12059
- channelName: "responses.create",
12060
- kind: "async"
12061
- }),
12062
- responsesStream: channel({
12063
- channelName: "responses.stream",
12064
- kind: "sync-stream"
12065
- }),
12066
- responsesParse: channel({
12067
- channelName: "responses.parse",
12068
- kind: "async"
12069
- }),
12070
- responsesCompact: channel({
12071
- channelName: "responses.compact",
12072
- kind: "async"
12073
- })
12074
- },
12075
- { instrumentationName: INSTRUMENTATION_NAMES.OPENAI }
12076
- );
12077
12189
 
12078
12190
  // src/openai-utils.ts
12079
12191
  var BRAINTRUST_CACHED_STREAM_METRIC = "__braintrust_cached_metric";
@@ -12130,23 +12242,573 @@ function getCachedMetricFromHeaders(headers) {
12130
12242
  return parseCachedHeader(headers.get(LEGACY_CACHED_HEADER));
12131
12243
  }
12132
12244
 
12245
+ // src/instrumentation/plugins/openai-batch-instrumentation.ts
12246
+ var SUPPORTED_ENDPOINTS = /* @__PURE__ */ new Set(["/v1/chat/completions", "/v1/responses"]);
12247
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
12248
+ "completed",
12249
+ "failed",
12250
+ "expired",
12251
+ "cancelled"
12252
+ ]);
12253
+ var pendingBatchTraces = /* @__PURE__ */ new Map();
12254
+ function read(value, key) {
12255
+ if (!isObject(value)) {
12256
+ return void 0;
12257
+ }
12258
+ try {
12259
+ return Reflect.get(value, key);
12260
+ } catch {
12261
+ return void 0;
12262
+ }
12263
+ }
12264
+ function isBatchRecordIterable(value) {
12265
+ if (value === null || typeof value !== "object" && typeof value !== "function") {
12266
+ return false;
12267
+ }
12268
+ return typeof read(value, Symbol.iterator) === "function" || typeof read(value, Symbol.asyncIterator) === "function";
12269
+ }
12270
+ function validCustomId(value) {
12271
+ return typeof value === "string" && value.length > 0 && value.length <= 64;
12272
+ }
12273
+ function logBatchInstrumentationError(context, error) {
12274
+ debugLogger.debug(`OpenAI Batch instrumentation ${context}:`, error);
12275
+ }
12276
+ async function exportParent(parent) {
12277
+ if ("toStr" in parent && typeof parent.toStr === "function") {
12278
+ return parent.toStr();
12279
+ }
12280
+ if ("export" in parent && typeof parent.export === "function") {
12281
+ return await parent.export();
12282
+ }
12283
+ return void 0;
12284
+ }
12285
+ async function deterministicDigest(namespace, ...parts) {
12286
+ const encoded = new TextEncoder().encode(
12287
+ [namespace, ...parts].map((part) => `${part.length}:${part}`).join("\0")
12288
+ );
12289
+ return new Uint8Array(
12290
+ await globalThis.crypto.subtle.digest("SHA-256", encoded)
12291
+ );
12292
+ }
12293
+ function digestHex(bytes, length) {
12294
+ return Array.from(bytes.slice(0, length)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
12295
+ }
12296
+ function digestUuid(bytes) {
12297
+ const hex = digestHex(bytes, 16);
12298
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
12299
+ }
12300
+ async function batchSpanIds(inputFileId) {
12301
+ const [row, span, root] = await Promise.all([
12302
+ deterministicDigest("openai:batch:row", inputFileId),
12303
+ deterministicDigest("openai:batch:span", inputFileId),
12304
+ deterministicDigest("openai:batch:root", inputFileId)
12305
+ ]);
12306
+ return {
12307
+ rowId: digestUuid(row),
12308
+ spanId: digestHex(span, 8),
12309
+ rootSpanId: digestHex(root, 16)
12310
+ };
12311
+ }
12312
+ async function childSpanIds(inputFileId, customId) {
12313
+ const [row, span] = await Promise.all([
12314
+ deterministicDigest("openai:batch:child:row", inputFileId, customId),
12315
+ deterministicDigest("openai:batch:child:span", inputFileId, customId)
12316
+ ]);
12317
+ return { rowId: digestUuid(row), spanId: digestHex(span, 8) };
12318
+ }
12319
+ async function startBatchSpan(context) {
12320
+ const ids = await batchSpanIds(context.inputFileId);
12321
+ const parent = SpanComponentsV4.fromStr(context.parent);
12322
+ const hasParentSpan = Boolean(
12323
+ parent.data.row_id && parent.data.span_id && parent.data.root_span_id
12324
+ );
12325
+ return withCurrent(
12326
+ NOOP_SPAN,
12327
+ () => _internalStartSpanWithInitialMergeAndParentSpanIds(
12328
+ withSpanInstrumentationName(
12329
+ {
12330
+ name: "openai.batch",
12331
+ type: "task" /* TASK */,
12332
+ parent: context.parent,
12333
+ ...!hasParentSpan ? {
12334
+ parentSpanIds: {
12335
+ parentSpanIds: [],
12336
+ rootSpanId: ids.rootSpanId
12337
+ }
12338
+ } : {},
12339
+ spanId: ids.spanId,
12340
+ startTime: context.taskStartTime,
12341
+ event: {
12342
+ id: ids.rowId,
12343
+ metadata: {
12344
+ endpoint: context.endpoint,
12345
+ input_file_id: context.inputFileId,
12346
+ provider: "openai"
12347
+ }
12348
+ }
12349
+ },
12350
+ INSTRUMENTATION_NAMES.OPENAI
12351
+ )
12352
+ )
12353
+ );
12354
+ }
12355
+ async function startBatchChild(context, taskParent, input) {
12356
+ const ids = await childSpanIds(context.inputFileId, input.customId);
12357
+ return withCurrent(
12358
+ NOOP_SPAN,
12359
+ () => _internalStartSpanWithInitialMerge(
12360
+ withSpanInstrumentationName(
12361
+ {
12362
+ name: context.endpoint === "/v1/chat/completions" ? "Chat Completion" : "openai.responses.create",
12363
+ type: "llm" /* LLM */,
12364
+ parent: taskParent,
12365
+ spanId: ids.spanId,
12366
+ startTime: context.childStartTime,
12367
+ event: {
12368
+ id: ids.rowId,
12369
+ ...input.spanData?.input !== void 0 ? { input: input.spanData.input } : {},
12370
+ metadata: {
12371
+ ...input.spanData?.metadata,
12372
+ custom_id: input.customId,
12373
+ provider: "openai"
12374
+ }
12375
+ }
12376
+ },
12377
+ INSTRUMENTATION_NAMES.OPENAI
12378
+ )
12379
+ )
12380
+ );
12381
+ }
12382
+ async function* jsonlRecords(file, onIssue = () => {
12383
+ }) {
12384
+ const resolvedFile = await file;
12385
+ if (typeof resolvedFile === "string") {
12386
+ for (const line of resolvedFile.split("\n")) {
12387
+ if (!line.trim()) {
12388
+ continue;
12389
+ }
12390
+ try {
12391
+ yield JSON.parse(line.replace(/\r$/, ""));
12392
+ } catch (error) {
12393
+ logBatchInstrumentationError("skipped malformed JSONL", error);
12394
+ onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
12395
+ }
12396
+ }
12397
+ return;
12398
+ }
12399
+ const body = read(resolvedFile, "body");
12400
+ const getReader = read(body, "getReader");
12401
+ if (isObject(body) && typeof getReader === "function") {
12402
+ let reader;
12403
+ try {
12404
+ reader = Reflect.apply(getReader, body, []);
12405
+ const decoder = new TextDecoder();
12406
+ let pending = "";
12407
+ while (true) {
12408
+ const readChunk = read(reader, "read");
12409
+ if (typeof readChunk !== "function") {
12410
+ throw new Error("Response body stream has no read method");
12411
+ }
12412
+ const chunk = await Reflect.apply(readChunk, reader, []);
12413
+ if (!isObject(chunk)) {
12414
+ throw new Error("Response body stream returned an invalid chunk");
12415
+ }
12416
+ if (chunk.done === true) {
12417
+ pending += decoder.decode();
12418
+ break;
12419
+ }
12420
+ if (!(chunk.value instanceof Uint8Array)) {
12421
+ throw new Error("Response body stream returned a non-byte chunk");
12422
+ }
12423
+ pending += decoder.decode(chunk.value, { stream: true });
12424
+ let newline = pending.indexOf("\n");
12425
+ while (newline !== -1) {
12426
+ const line = pending.slice(0, newline).replace(/\r$/, "");
12427
+ pending = pending.slice(newline + 1);
12428
+ if (line.trim()) {
12429
+ try {
12430
+ yield JSON.parse(line);
12431
+ } catch (error) {
12432
+ logBatchInstrumentationError("skipped malformed JSONL", error);
12433
+ onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
12434
+ }
12435
+ }
12436
+ newline = pending.indexOf("\n");
12437
+ }
12438
+ }
12439
+ if (pending.trim()) {
12440
+ try {
12441
+ yield JSON.parse(pending.replace(/\r$/, ""));
12442
+ } catch (error) {
12443
+ logBatchInstrumentationError("skipped malformed JSONL", error);
12444
+ onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
12445
+ }
12446
+ }
12447
+ } catch (error) {
12448
+ logBatchInstrumentationError("could not read JSONL response body", error);
12449
+ onIssue(new Error("OpenAI Batch response body could not be read"));
12450
+ } finally {
12451
+ const releaseLock = read(reader, "releaseLock");
12452
+ if (typeof releaseLock === "function") {
12453
+ try {
12454
+ Reflect.apply(releaseLock, reader, []);
12455
+ } catch (error) {
12456
+ logBatchInstrumentationError(
12457
+ "could not release stream reader",
12458
+ error
12459
+ );
12460
+ }
12461
+ }
12462
+ }
12463
+ return;
12464
+ }
12465
+ if (isBatchRecordIterable(resolvedFile)) {
12466
+ for await (const record of resolvedFile) {
12467
+ yield record;
12468
+ }
12469
+ return;
12470
+ }
12471
+ logBatchInstrumentationError("skipped invalid JSONL source", resolvedFile);
12472
+ onIssue(new Error("OpenAI Batch file source is invalid"));
12473
+ }
12474
+ async function readBatchInputs(file) {
12475
+ const inputs = /* @__PURE__ */ new Map();
12476
+ const issues = [];
12477
+ let endpoint;
12478
+ try {
12479
+ for await (const value of jsonlRecords(
12480
+ file,
12481
+ (issue) => issues.push(issue)
12482
+ )) {
12483
+ const customId = read(value, "custom_id");
12484
+ const url = read(value, "url");
12485
+ const body = read(value, "body");
12486
+ if (!validCustomId(customId) || inputs.has(customId) || read(value, "method") !== "POST" || typeof url !== "string" || !SUPPORTED_ENDPOINTS.has(url) || endpoint !== void 0 && endpoint !== url || !isObject(body)) {
12487
+ issues.push(new Error("OpenAI Batch input contains an invalid record"));
12488
+ continue;
12489
+ }
12490
+ endpoint = url;
12491
+ let spanData;
12492
+ try {
12493
+ spanData = extractOpenAIBatchInput(url, body);
12494
+ } catch (error) {
12495
+ logBatchInstrumentationError("could not extract batch input", error);
12496
+ }
12497
+ inputs.set(customId, { customId, spanData });
12498
+ }
12499
+ } catch (error) {
12500
+ logBatchInstrumentationError("could not process input file", error);
12501
+ issues.push(new Error("OpenAI Batch input file could not be processed"));
12502
+ }
12503
+ if (!endpoint || inputs.size === 0) {
12504
+ issues.push(new Error("OpenAI Batch input contains no supported records"));
12505
+ }
12506
+ return { endpoint, inputs, issues };
12507
+ }
12508
+ async function writePendingSpans(trace, endTime) {
12509
+ const task = await startBatchSpan(trace.context);
12510
+ const taskParent = await task.export();
12511
+ for (const input of trace.inputs.values()) {
12512
+ try {
12513
+ const child = await startBatchChild(trace.context, taskParent, input);
12514
+ if (endTime !== void 0) {
12515
+ child.end({ endTime });
12516
+ }
12517
+ } catch (error) {
12518
+ logBatchInstrumentationError("could not write batch request span", error);
12519
+ }
12520
+ }
12521
+ if (endTime !== void 0) {
12522
+ if (trace.status && trace.status !== "completed") {
12523
+ task.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
12524
+ }
12525
+ task.end({ endTime });
12526
+ }
12527
+ }
12528
+ var interceptOpenAIFilesCreateTraced = async (target, thisArg, args) => {
12529
+ const startTime = getCurrentUnixTimestamp();
12530
+ const inputPromise = readBatchInputs(args[0].inputFileContent);
12531
+ const parentPromise = exportParent(args[0].parent);
12532
+ const file = await Reflect.apply(target, thisArg, args);
12533
+ try {
12534
+ const inputFileId = read(file, "id");
12535
+ const [inputData, exportedParent2] = await Promise.all([
12536
+ inputPromise,
12537
+ parentPromise
12538
+ ]);
12539
+ if (typeof inputFileId !== "string" || !exportedParent2 || !inputData.endpoint || inputData.issues.length > 0) {
12540
+ if (inputData.issues[0]) {
12541
+ logBatchInstrumentationError(
12542
+ "skipped invalid input file",
12543
+ inputData.issues[0]
12544
+ );
12545
+ }
12546
+ return file;
12547
+ }
12548
+ const trace = {
12549
+ context: {
12550
+ inputFileId,
12551
+ endpoint: inputData.endpoint,
12552
+ parent: exportedParent2,
12553
+ taskStartTime: startTime,
12554
+ childStartTime: startTime
12555
+ },
12556
+ inputs: inputData.inputs
12557
+ };
12558
+ pendingBatchTraces.set(inputFileId, trace);
12559
+ await writePendingSpans(trace);
12560
+ } catch (error) {
12561
+ logBatchInstrumentationError("could not start batch spans", error);
12562
+ }
12563
+ return file;
12564
+ };
12565
+ function validTimestamp(value) {
12566
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
12567
+ }
12568
+ function terminalEndTime(batch, startTime) {
12569
+ let timestamp;
12570
+ switch (batch.status) {
12571
+ case "completed":
12572
+ timestamp = batch.completed_at;
12573
+ break;
12574
+ case "failed":
12575
+ timestamp = batch.failed_at;
12576
+ break;
12577
+ case "expired":
12578
+ timestamp = batch.expired_at;
12579
+ break;
12580
+ default:
12581
+ timestamp = batch.cancelled_at;
12582
+ }
12583
+ return validTimestamp(timestamp) ? Math.max(timestamp, startTime) : Math.max(getCurrentUnixTimestamp(), startTime);
12584
+ }
12585
+ async function updateBatchTimestamps(batch) {
12586
+ const trace = pendingBatchTraces.get(batch.input_file_id);
12587
+ if (!trace || trace.context.endpoint !== batch.endpoint) {
12588
+ return;
12589
+ }
12590
+ if (validTimestamp(batch.created_at)) {
12591
+ trace.context.taskStartTime = batch.created_at;
12592
+ }
12593
+ trace.context.childStartTime = validTimestamp(batch.in_progress_at) ? Math.max(batch.in_progress_at, trace.context.taskStartTime) : trace.context.taskStartTime;
12594
+ trace.status = batch.status;
12595
+ if (TERMINAL_STATUSES.has(batch.status)) {
12596
+ trace.endTime = terminalEndTime(batch, trace.context.childStartTime);
12597
+ }
12598
+ await writePendingSpans(trace, trace.endTime);
12599
+ }
12600
+ var interceptOpenAIBatchesRetrieveTraced = (target, thisArg, args) => Promise.resolve(Reflect.apply(target, thisArg, args)).then(async (batch) => {
12601
+ try {
12602
+ await updateBatchTimestamps(batch);
12603
+ } catch (error) {
12604
+ logBatchInstrumentationError("could not update batch timestamps", error);
12605
+ }
12606
+ return batch;
12607
+ });
12608
+ function errorFromResult(result) {
12609
+ const error = read(result.value, "error");
12610
+ if (isObject(error)) {
12611
+ const message = read(error, "message");
12612
+ return new Error(
12613
+ typeof message === "string" ? message : "OpenAI Batch request failed"
12614
+ );
12615
+ }
12616
+ const response = read(result.value, "response");
12617
+ const statusCode = read(response, "status_code");
12618
+ if (result.source === "error" || typeof statusCode === "number" && (statusCode < 200 || statusCode >= 300)) {
12619
+ const message = read(read(read(response, "body"), "error"), "message");
12620
+ return new Error(
12621
+ typeof message === "string" ? message : "OpenAI Batch request failed"
12622
+ );
12623
+ }
12624
+ return void 0;
12625
+ }
12626
+ async function completeBatchResult(context, taskParent, endTime, input, result) {
12627
+ const child = await startBatchChild(context, taskParent, input);
12628
+ try {
12629
+ const resultError = errorFromResult(result);
12630
+ const responseBody = read(read(result.value, "response"), "body");
12631
+ if (resultError) {
12632
+ child.log({ error: resultError });
12633
+ } else if (isObject(responseBody)) {
12634
+ const model = read(responseBody, "model");
12635
+ child.log({
12636
+ output: context.endpoint === "/v1/chat/completions" ? read(responseBody, "choices") : processImagesInOutput(read(responseBody, "output")),
12637
+ ...typeof model === "string" ? { metadata: { model } } : {},
12638
+ metrics: parseMetricsFromUsage(read(responseBody, "usage"))
12639
+ });
12640
+ } else {
12641
+ child.log({ error: new Error("OpenAI Batch response body is missing") });
12642
+ }
12643
+ } catch (error) {
12644
+ child.log({ error });
12645
+ } finally {
12646
+ child.end({ endTime });
12647
+ }
12648
+ }
12649
+ async function completeResultFile({
12650
+ context,
12651
+ endTime,
12652
+ file,
12653
+ inputs,
12654
+ issues,
12655
+ seen,
12656
+ source,
12657
+ taskParent
12658
+ }) {
12659
+ if (file === void 0) {
12660
+ return;
12661
+ }
12662
+ for await (const value of jsonlRecords(file, (issue) => issues.push(issue))) {
12663
+ const customId = read(value, "custom_id");
12664
+ if (!validCustomId(customId) || !isObject(value)) {
12665
+ issues.push(
12666
+ new Error("OpenAI Batch result is missing a valid custom_id")
12667
+ );
12668
+ continue;
12669
+ }
12670
+ if (seen.has(customId)) {
12671
+ issues.push(new Error("OpenAI Batch result contains a duplicate"));
12672
+ continue;
12673
+ }
12674
+ const input = inputs.get(customId);
12675
+ if (!input) {
12676
+ issues.push(
12677
+ new Error("OpenAI Batch result does not match an input record")
12678
+ );
12679
+ continue;
12680
+ }
12681
+ seen.add(customId);
12682
+ await completeBatchResult(context, taskParent, endTime, input, {
12683
+ value,
12684
+ source
12685
+ });
12686
+ }
12687
+ }
12688
+ function sameInputs(first, second) {
12689
+ return first.size === second.size && [...first.keys()].every((customId) => second.has(customId));
12690
+ }
12691
+ async function contextForCompletion(inputFileId, inputData) {
12692
+ if (!inputData.endpoint || inputData.issues.length > 0) {
12693
+ return void 0;
12694
+ }
12695
+ const existing = pendingBatchTraces.get(inputFileId);
12696
+ if (existing) {
12697
+ if (existing.context.endpoint !== inputData.endpoint || !sameInputs(existing.inputs, inputData.inputs)) {
12698
+ return void 0;
12699
+ }
12700
+ return { ...existing, inputs: inputData.inputs };
12701
+ }
12702
+ const parent = await exportParent(getSpanParentObject());
12703
+ if (!parent) {
12704
+ return void 0;
12705
+ }
12706
+ const startTime = getCurrentUnixTimestamp();
12707
+ return {
12708
+ context: {
12709
+ inputFileId,
12710
+ endpoint: inputData.endpoint,
12711
+ parent,
12712
+ taskStartTime: startTime,
12713
+ childStartTime: startTime
12714
+ },
12715
+ inputs: inputData.inputs
12716
+ };
12717
+ }
12718
+ async function completeBatch(args) {
12719
+ const inputData = await readBatchInputs(args.inputFileContent);
12720
+ const trace = await contextForCompletion(args.inputFileId, inputData);
12721
+ if (!trace) {
12722
+ logBatchInstrumentationError(
12723
+ "left batch spans pending",
12724
+ inputData.issues[0] ?? new Error("OpenAI Batch input does not match")
12725
+ );
12726
+ return;
12727
+ }
12728
+ const endTime = trace.endTime ?? getCurrentUnixTimestamp();
12729
+ const task = await startBatchSpan(trace.context);
12730
+ const taskParent = await task.export();
12731
+ const issues = [];
12732
+ const seen = /* @__PURE__ */ new Set();
12733
+ await Promise.all([
12734
+ completeResultFile({
12735
+ context: trace.context,
12736
+ endTime,
12737
+ file: args.outputFileContent,
12738
+ inputs: trace.inputs,
12739
+ issues,
12740
+ seen,
12741
+ source: "output",
12742
+ taskParent
12743
+ }),
12744
+ completeResultFile({
12745
+ context: trace.context,
12746
+ endTime,
12747
+ file: args.errorFileContent,
12748
+ inputs: trace.inputs,
12749
+ issues,
12750
+ seen,
12751
+ source: "error",
12752
+ taskParent
12753
+ })
12754
+ ]);
12755
+ if (issues.length > 0) {
12756
+ logBatchInstrumentationError("left batch spans pending", issues[0]);
12757
+ return;
12758
+ }
12759
+ const missing = [...trace.inputs.values()].filter(
12760
+ ({ customId }) => !seen.has(customId)
12761
+ );
12762
+ if (!trace.status || trace.status === "completed") {
12763
+ if (missing.length > 0) {
12764
+ logBatchInstrumentationError(
12765
+ "left batch spans pending",
12766
+ new Error("OpenAI Batch result files are incomplete")
12767
+ );
12768
+ return;
12769
+ }
12770
+ } else {
12771
+ for (const input of missing) {
12772
+ const child = await startBatchChild(trace.context, taskParent, input);
12773
+ child.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
12774
+ child.end({ endTime });
12775
+ }
12776
+ task.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
12777
+ }
12778
+ task.end({ endTime });
12779
+ pendingBatchTraces.delete(args.inputFileId);
12780
+ }
12781
+ var interceptOpenAIBatchTraceComplete = (target, thisArg, args) => Promise.resolve(Reflect.apply(target, thisArg, args)).then(async (result) => {
12782
+ try {
12783
+ await completeBatch(args[0]);
12784
+ } catch (error) {
12785
+ logBatchInstrumentationError("could not complete batch", error);
12786
+ }
12787
+ return result;
12788
+ });
12789
+
12133
12790
  // src/instrumentation/plugins/openai-plugin.ts
12134
12791
  var OpenAIPlugin = class extends BasePlugin {
12135
12792
  constructor() {
12136
12793
  super();
12137
12794
  }
12138
12795
  onEnable() {
12796
+ this.unsubscribers.push(
12797
+ openAIChannels.filesCreateTraced.intercept(
12798
+ interceptOpenAIFilesCreateTraced
12799
+ ),
12800
+ openAIChannels.batchesRetrieveTraced.intercept(
12801
+ interceptOpenAIBatchesRetrieveTraced
12802
+ ),
12803
+ openAIChannels.batchesCompleteTrace.intercept(
12804
+ interceptOpenAIBatchTraceComplete
12805
+ )
12806
+ );
12139
12807
  this.unsubscribers.push(
12140
12808
  traceStreamingChannel(openAIChannels.chatCompletionsCreate, {
12141
12809
  name: "Chat Completion",
12142
12810
  type: "llm" /* LLM */,
12143
- extractInput: ([params]) => {
12144
- const { messages, ...metadata } = params;
12145
- return {
12146
- input: processInputAttachments(messages),
12147
- metadata: { ...metadata, provider: "openai" }
12148
- };
12149
- },
12811
+ extractInput: ([params]) => extractOpenAIChatInput(params),
12150
12812
  extractOutput: (result) => {
12151
12813
  return result?.choices;
12152
12814
  },
@@ -12192,13 +12854,7 @@ var OpenAIPlugin = class extends BasePlugin {
12192
12854
  traceStreamingChannel(openAIChannels.betaChatCompletionsParse, {
12193
12855
  name: "Chat Completion",
12194
12856
  type: "llm" /* LLM */,
12195
- extractInput: ([params]) => {
12196
- const { messages, ...metadata } = params;
12197
- return {
12198
- input: processInputAttachments(messages),
12199
- metadata: { ...metadata, provider: "openai" }
12200
- };
12201
- },
12857
+ extractInput: ([params]) => extractOpenAIChatInput(params),
12202
12858
  extractOutput: (result) => {
12203
12859
  return result?.choices;
12204
12860
  },
@@ -12220,13 +12876,7 @@ var OpenAIPlugin = class extends BasePlugin {
12220
12876
  traceSyncStreamChannel(openAIChannels.betaChatCompletionsStream, {
12221
12877
  name: "Chat Completion",
12222
12878
  type: "llm" /* LLM */,
12223
- extractInput: ([params]) => {
12224
- const { messages, ...metadata } = params;
12225
- return {
12226
- input: processInputAttachments(messages),
12227
- metadata: { ...metadata, provider: "openai" }
12228
- };
12229
- }
12879
+ extractInput: ([params]) => extractOpenAIChatInput(params)
12230
12880
  })
12231
12881
  );
12232
12882
  this.unsubscribers.push(
@@ -12256,23 +12906,11 @@ var OpenAIPlugin = class extends BasePlugin {
12256
12906
  traceStreamingChannel(openAIChannels.responsesCreate, {
12257
12907
  name: "openai.responses.create",
12258
12908
  type: "llm" /* LLM */,
12259
- extractInput: ([params]) => {
12260
- const { input, ...metadata } = params;
12261
- return {
12262
- input: processInputAttachments(input),
12263
- metadata: { ...metadata, provider: "openai" }
12264
- };
12265
- },
12909
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
12266
12910
  extractOutput: (result) => {
12267
12911
  return processImagesInOutput(result?.output);
12268
12912
  },
12269
- extractMetadata: (result) => {
12270
- if (!result) {
12271
- return void 0;
12272
- }
12273
- const { output: _output, usage: _usage, ...metadata } = result;
12274
- return Object.keys(metadata).length > 0 ? metadata : void 0;
12275
- },
12913
+ extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
12276
12914
  extractMetrics: (result, startTime, endEvent) => {
12277
12915
  const metrics = withCachedMetric(
12278
12916
  parseMetricsFromUsage(result?.usage),
@@ -12291,13 +12929,7 @@ var OpenAIPlugin = class extends BasePlugin {
12291
12929
  traceSyncStreamChannel(openAIChannels.responsesStream, {
12292
12930
  name: "openai.responses.create",
12293
12931
  type: "llm" /* LLM */,
12294
- extractInput: ([params]) => {
12295
- const { input, ...metadata } = params;
12296
- return {
12297
- input: processInputAttachments(input),
12298
- metadata: { ...metadata, provider: "openai" }
12299
- };
12300
- },
12932
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
12301
12933
  extractFromEvent: (event) => {
12302
12934
  if (event.type !== "response.completed" || !event.response) {
12303
12935
  return {};
@@ -12320,23 +12952,11 @@ var OpenAIPlugin = class extends BasePlugin {
12320
12952
  traceStreamingChannel(openAIChannels.responsesParse, {
12321
12953
  name: "openai.responses.parse",
12322
12954
  type: "llm" /* LLM */,
12323
- extractInput: ([params]) => {
12324
- const { input, ...metadata } = params;
12325
- return {
12326
- input: processInputAttachments(input),
12327
- metadata: { ...metadata, provider: "openai" }
12328
- };
12329
- },
12955
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
12330
12956
  extractOutput: (result) => {
12331
12957
  return processImagesInOutput(result?.output);
12332
12958
  },
12333
- extractMetadata: (result) => {
12334
- if (!result) {
12335
- return void 0;
12336
- }
12337
- const { output: _output, usage: _usage, ...metadata } = result;
12338
- return Object.keys(metadata).length > 0 ? metadata : void 0;
12339
- },
12959
+ extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
12340
12960
  extractMetrics: (result, startTime, endEvent) => {
12341
12961
  const metrics = withCachedMetric(
12342
12962
  parseMetricsFromUsage(result?.usage),
@@ -12355,23 +12975,11 @@ var OpenAIPlugin = class extends BasePlugin {
12355
12975
  traceAsyncChannel(openAIChannels.responsesCompact, {
12356
12976
  name: "openai.responses.compact",
12357
12977
  type: "llm" /* LLM */,
12358
- extractInput: ([params]) => {
12359
- const { input, ...metadata } = params;
12360
- return {
12361
- input: processInputAttachments(input),
12362
- metadata: { ...metadata, provider: "openai" }
12363
- };
12364
- },
12978
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
12365
12979
  extractOutput: (result) => {
12366
12980
  return processImagesInOutput(result?.output);
12367
12981
  },
12368
- extractMetadata: (result) => {
12369
- if (!result) {
12370
- return void 0;
12371
- }
12372
- const { output: _output, usage: _usage, ...metadata } = result;
12373
- return Object.keys(metadata).length > 0 ? metadata : void 0;
12374
- },
12982
+ extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
12375
12983
  extractMetrics: (result, startTime, endEvent) => {
12376
12984
  const metrics = withCachedMetric(
12377
12985
  parseMetricsFromUsage(result?.usage),
@@ -12427,35 +13035,6 @@ function withCachedMetric(metrics, result, endEvent) {
12427
13035
  cached
12428
13036
  };
12429
13037
  }
12430
- function processImagesInOutput(output) {
12431
- if (Array.isArray(output)) {
12432
- return output.map(processImagesInOutput);
12433
- }
12434
- if (isObject(output)) {
12435
- if (output.type === "image_generation_call" && output.result && typeof output.result === "string") {
12436
- const fileExtension = output.output_format || "png";
12437
- const contentType = `image/${fileExtension}`;
12438
- const baseFilename = output.revised_prompt && typeof output.revised_prompt === "string" ? output.revised_prompt.slice(0, 50).replace(/[^a-zA-Z0-9]/g, "_") : "generated_image";
12439
- const filename = `${baseFilename}.${fileExtension}`;
12440
- const binaryString = atob(output.result);
12441
- const bytes = new Uint8Array(binaryString.length);
12442
- for (let i = 0; i < binaryString.length; i++) {
12443
- bytes[i] = binaryString.charCodeAt(i);
12444
- }
12445
- const blob = new Blob([bytes], { type: contentType });
12446
- const attachment = new Attachment({
12447
- data: blob,
12448
- filename,
12449
- contentType
12450
- });
12451
- return {
12452
- ...output,
12453
- result: attachment
12454
- };
12455
- }
12456
- }
12457
- return output;
12458
- }
12459
13038
  function mergeLogprobTokens(existing, incoming) {
12460
13039
  if (incoming === void 0) {
12461
13040
  return existing;
@@ -17599,6 +18178,12 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiS
17599
18178
  if (!activeEntry) {
17600
18179
  return result;
17601
18180
  }
18181
+ const executionOptions = args[1];
18182
+ const toolCallId = isObject(executionOptions) ? executionOptions.toolCallId : void 0;
18183
+ const toolInput = {
18184
+ input: serializeToolExecutionInput(args),
18185
+ ...typeof toolCallId === "string" ? { metadata: { toolCallId } } : {}
18186
+ };
17602
18187
  if (isAsyncGenerator(result)) {
17603
18188
  return (async function* () {
17604
18189
  const span = activeEntry.parentSpan.startSpan(
@@ -17612,7 +18197,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiS
17612
18197
  INSTRUMENTATION_NAMES.AI_SDK
17613
18198
  )
17614
18199
  );
17615
- span.log({ input: serializeToolExecutionInput(args) });
18200
+ span.log(toolInput);
17616
18201
  try {
17617
18202
  let lastValue;
17618
18203
  for await (const value of result) {
@@ -17630,7 +18215,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiS
17630
18215
  }
17631
18216
  return activeEntry.parentSpan.traced(
17632
18217
  async (span) => {
17633
- span.log({ input: serializeToolExecutionInput(args) });
18218
+ span.log(toolInput);
17634
18219
  const awaitedResult = await result;
17635
18220
  span.log({ output: awaitedResult });
17636
18221
  return awaitedResult;
@@ -31752,14 +32337,17 @@ function getMetricsFromResponse(response) {
31752
32337
  if (!isRecord(usageMetadata)) {
31753
32338
  continue;
31754
32339
  }
31755
- const inputTokenDetails = usageMetadata.input_token_details;
32340
+ const inputTokenDetails = isRecord(usageMetadata.input_token_details) ? usageMetadata.input_token_details : {};
31756
32341
  const outputTokenDetails = usageMetadata.output_token_details;
31757
32342
  return normalizeTokenMetrics({
31758
32343
  total_tokens: usageMetadata.total_tokens,
31759
32344
  prompt_tokens: usageMetadata.input_tokens,
31760
32345
  completion_tokens: usageMetadata.output_tokens,
31761
- prompt_cache_creation_tokens: isRecord(inputTokenDetails) ? inputTokenDetails.cache_creation : void 0,
31762
- prompt_cached_tokens: isRecord(inputTokenDetails) ? inputTokenDetails.cache_read : void 0,
32346
+ // Prefer TTL-specific cache writes over the aggregate when available.
32347
+ prompt_cache_creation_tokens: inputTokenDetails.ephemeral_5m_input_tokens == null && inputTokenDetails.ephemeral_1h_input_tokens == null ? inputTokenDetails.cache_creation : void 0,
32348
+ prompt_cache_creation_5m_tokens: inputTokenDetails.ephemeral_5m_input_tokens,
32349
+ prompt_cache_creation_1h_tokens: inputTokenDetails.ephemeral_1h_input_tokens,
32350
+ prompt_cached_tokens: inputTokenDetails.cache_read,
31763
32351
  completion_reasoning_tokens: isRecord(outputTokenDetails) ? outputTokenDetails.reasoning : void 0
31764
32352
  });
31765
32353
  }
@@ -32475,7 +33063,7 @@ function extractSession(event) {
32475
33063
  return isObject(candidate) && typeof candidate.prompt === "function" ? candidate : void 0;
32476
33064
  }
32477
33065
  function isPiAgent(value) {
32478
- return isObject(value) && typeof value.streamFn === "function" && typeof value.subscribe === "function";
33066
+ return isObject(value) && (typeof value.streamFunction === "function" || typeof value.streamFn === "function") && typeof value.subscribe === "function";
32479
33067
  }
32480
33068
  function promptContextStore() {
32481
33069
  piPromptContextStore ??= isomorph_default.newAsyncLocalStorage();
@@ -32485,17 +33073,21 @@ function currentPiPromptState() {
32485
33073
  return promptContextStore().getStore();
32486
33074
  }
32487
33075
  function installPiAgentInstrumentation(agent) {
33076
+ const property = typeof agent.streamFunction === "function" ? "streamFunction" : "streamFn";
33077
+ const streamFunction = agent[property];
32488
33078
  const existing = piAgentPatchStates.get(agent);
32489
- if (!existing || agent.streamFn !== existing.wrappedStreamFn) {
33079
+ if (streamFunction && (!existing || existing.property !== property || streamFunction !== existing.wrappedStreamFunction)) {
32490
33080
  const patchState = {
32491
- originalStreamFn: agent.streamFn,
32492
- wrappedStreamFn: agent.streamFn
33081
+ originalStreamFunction: streamFunction,
33082
+ property,
33083
+ wrappedStreamFunction: streamFunction
32493
33084
  };
32494
- patchState.wrappedStreamFn = makeInstrumentedStreamFn(
33085
+ patchState.wrappedStreamFunction = makeInstrumentedStreamFunction(
32495
33086
  agent,
32496
- patchState.originalStreamFn
33087
+ patchState.originalStreamFunction,
33088
+ property
32497
33089
  );
32498
- agent.streamFn = patchState.wrappedStreamFn;
33090
+ agent[property] = patchState.wrappedStreamFunction;
32499
33091
  piAgentPatchStates.set(agent, patchState);
32500
33092
  }
32501
33093
  if (piAgentEventSubscriptions.has(agent)) {
@@ -32520,15 +33112,21 @@ function installPiAgentInstrumentation(agent) {
32520
33112
  logInstrumentationError4("Pi Coding Agent event subscription", error);
32521
33113
  }
32522
33114
  }
32523
- function makeInstrumentedStreamFn(agent, originalStreamFn) {
32524
- return async function instrumentedPiStreamFn(model, context, options) {
32525
- const invokeOriginal = () => Reflect.apply(originalStreamFn, this, [model, context, options]);
33115
+ function makeInstrumentedStreamFunction(agent, originalStreamFunction, property) {
33116
+ return async function instrumentedPiStreamFunction(model, context, options) {
33117
+ const invokeOriginal = () => Reflect.apply(originalStreamFunction, this, [model, context, options]);
32526
33118
  const state = currentPiPromptState();
32527
33119
  if (!state || state.agent !== agent || state.finalized) {
32528
33120
  return invokeOriginal();
32529
33121
  }
32530
33122
  wrapPiToolExecutors(context.tools);
32531
- const llmState = await startPiLlmSpan(state, model, context, options);
33123
+ const llmState = await startPiLlmSpan(
33124
+ state,
33125
+ model,
33126
+ context,
33127
+ property,
33128
+ options
33129
+ );
32532
33130
  try {
32533
33131
  const stream = await runWithAutoInstrumentationSuppressed(invokeOriginal);
32534
33132
  return patchAssistantMessageStream(stream, state, llmState);
@@ -32565,12 +33163,12 @@ function wrapPiToolExecutors(tools) {
32565
33163
  }
32566
33164
  }
32567
33165
  }
32568
- async function startPiLlmSpan(state, model, context, options) {
33166
+ async function startPiLlmSpan(state, model, context, property, options) {
32569
33167
  const metadata = {
32570
33168
  ...extractModelMetadata2(model),
32571
33169
  ...extractStreamOptionsMetadata(options),
32572
33170
  ...extractToolMetadata(context.tools),
32573
- "pi_coding_agent.operation": "agent.streamFn"
33171
+ "pi_coding_agent.operation": `agent.${property}`
32574
33172
  };
32575
33173
  const span = startSpan(
32576
33174
  withSpanInstrumentationName(
@@ -36881,7 +37479,15 @@ var MAX_EVE_PROVIDER_CACHE_ENTRIES = 1e4;
36881
37479
  function createEveInstrumentationProvider(options = {}) {
36882
37480
  const bridge = new EveProviderBridge(options.metadata);
36883
37481
  return {
37482
+ // Eve versions before tracePolicy use capture to decide whether provider
37483
+ // events include content. Newer versions prefer tracePolicy when both are
37484
+ // present, so keep the deprecated field for backwards compatibility.
36884
37485
  capture: "content",
37486
+ tracePolicy: () => ({
37487
+ emit: true,
37488
+ recordInputs: true,
37489
+ recordOutputs: true
37490
+ }),
36885
37491
  events: {
36886
37492
  "action.completed": (event, context) => bridge.handleActionTerminal(event, context),
36887
37493
  "action.failed": (event, context) => bridge.handleActionTerminal(event, context),