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
@@ -3963,6 +3963,7 @@ var ProjectSettings = import_v36.z.union([
3963
3963
  ]),
3964
3964
  disable_realtime_queries: import_v36.z.union([import_v36.z.boolean(), import_v36.z.null()]),
3965
3965
  monitor_charts_use_metrics_start: import_v36.z.union([import_v36.z.boolean(), import_v36.z.null()]),
3966
+ blind_reviews: import_v36.z.union([import_v36.z.boolean(), import_v36.z.null()]),
3966
3967
  default_preprocessor: NullableSavedFunctionId
3967
3968
  }).partial(),
3968
3969
  import_v36.z.null()
@@ -4024,6 +4025,7 @@ var WindowedAutomationConfig = import_v36.z.object({
4024
4025
  auto_approve_tools: import_v36.z.array(import_v36.z.string().min(1)).optional().default([]),
4025
4026
  harness: import_v36.z.enum(["native", "codex", "claude-code"]).optional(),
4026
4027
  model: import_v36.z.string().min(1).optional(),
4028
+ endpoint_name: import_v36.z.string().min(1).optional(),
4027
4029
  reasoning_effort: import_v36.z.enum(["none", "minimal", "low", "medium", "high", "xhigh", "max"]).optional()
4028
4030
  }).optional(),
4029
4031
  actions: import_v36.z.array(
@@ -5815,7 +5817,7 @@ var INSTRUMENTATION_NAMES = {
5815
5817
  var INTERNAL_SPAN_INSTRUMENTATION_NAME = /* @__PURE__ */ Symbol.for(
5816
5818
  "braintrust.spanInstrumentationName"
5817
5819
  );
5818
- var SDK_VERSION = true ? "3.29.0" : "0.0.0";
5820
+ var SDK_VERSION = true ? "3.31.0" : "0.0.0";
5819
5821
  function withSpanInstrumentationName(args, instrumentationName) {
5820
5822
  return {
5821
5823
  ...args,
@@ -8531,6 +8533,9 @@ function getSpanParentObjectAndPropagatedState(options) {
8531
8533
  }
8532
8534
  return { parentObject: NOOP_SPAN, propagatedState: void 0 };
8533
8535
  }
8536
+ function getSpanParentObject(options) {
8537
+ return getSpanParentObjectAndPropagatedState(options).parentObject;
8538
+ }
8534
8539
  function currentBraintrustParent(state) {
8535
8540
  const resolvedState = state ?? _globalState;
8536
8541
  const experiment = currentExperiment({ state: resolvedState });
@@ -8777,6 +8782,15 @@ function _internalStartSpanWithInitialMerge(args) {
8777
8782
  [INITIAL_SPAN_WRITE_AS_MERGE]: true
8778
8783
  }).span;
8779
8784
  }
8785
+ function _internalStartSpanWithInitialMergeAndParentSpanIds(args) {
8786
+ return startSpanAndIsLogger(
8787
+ {
8788
+ ...args,
8789
+ [INITIAL_SPAN_WRITE_AS_MERGE]: true
8790
+ },
8791
+ { useParentSpanIdsForObjectParent: true }
8792
+ ).span;
8793
+ }
8780
8794
  function _internalStartSpanWithContext(args, context) {
8781
8795
  return startSpanAndIsLogger({
8782
8796
  ...args,
@@ -8787,7 +8801,7 @@ async function flush(options) {
8787
8801
  const state = options?.state ?? _globalState;
8788
8802
  return await state.bgLogger().flush();
8789
8803
  }
8790
- function startSpanAndIsLogger(args) {
8804
+ function startSpanAndIsLogger(args, internalOptions) {
8791
8805
  const state = args?.state ?? _globalState;
8792
8806
  const { parentObject, propagatedState } = getSpanParentObjectAndPropagatedState({
8793
8807
  asyncFlush: args?.asyncFlush,
@@ -8801,7 +8815,7 @@ function startSpanAndIsLogger(args) {
8801
8815
  ) ? {
8802
8816
  spanId: parentObject.data.span_id,
8803
8817
  rootSpanId: parentObject.data.root_span_id
8804
- } : void 0;
8818
+ } : internalOptions?.useParentSpanIdsForObjectParent ? args?.parentSpanIds : void 0;
8805
8819
  const { parent: _ignoredParent, ...spanArgs } = args ?? {};
8806
8820
  const span = new SpanImpl({
8807
8821
  state,
@@ -11831,6 +11845,131 @@ function unsubscribeAll(unsubscribers) {
11831
11845
  return [];
11832
11846
  }
11833
11847
 
11848
+ // src/instrumentation/core/channel-definitions.ts
11849
+ function channel(spec) {
11850
+ return spec;
11851
+ }
11852
+ function defineChannels(pkg, channels, options) {
11853
+ const { instrumentationName } = options;
11854
+ return Object.fromEntries(
11855
+ Object.entries(channels).map(([key, spec]) => {
11856
+ const fullChannelName = `orchestrion:${pkg}:${spec.channelName}`;
11857
+ if (spec.kind === "async") {
11858
+ const asyncSpec = spec;
11859
+ const tracingChannel2 = () => isomorph_default.newTracingChannel(
11860
+ fullChannelName
11861
+ );
11862
+ const intercept2 = (interceptor) => {
11863
+ const hook = tracingChannel2();
11864
+ return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
11865
+ };
11866
+ };
11867
+ return [
11868
+ key,
11869
+ {
11870
+ ...asyncSpec,
11871
+ instrumentationName,
11872
+ intercept: intercept2,
11873
+ invoke: (target, thisArg, args, additional) => {
11874
+ const hook = tracingChannel2();
11875
+ return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
11876
+ },
11877
+ tracingChannel: tracingChannel2,
11878
+ tracePromise: (fn, context) => tracingChannel2().tracePromise(
11879
+ fn,
11880
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
11881
+ context
11882
+ )
11883
+ }
11884
+ ];
11885
+ }
11886
+ const syncSpec = spec;
11887
+ const tracingChannel = () => isomorph_default.newTracingChannel(
11888
+ fullChannelName
11889
+ );
11890
+ const intercept = (interceptor) => {
11891
+ const hook = tracingChannel();
11892
+ return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
11893
+ };
11894
+ };
11895
+ return [
11896
+ key,
11897
+ {
11898
+ ...syncSpec,
11899
+ instrumentationName,
11900
+ intercept,
11901
+ invoke: (target, thisArg, args, additional) => {
11902
+ const hook = tracingChannel();
11903
+ return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
11904
+ },
11905
+ tracingChannel,
11906
+ traceSync: (fn, context) => tracingChannel().traceSync(
11907
+ fn,
11908
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
11909
+ context
11910
+ )
11911
+ }
11912
+ ];
11913
+ })
11914
+ );
11915
+ }
11916
+
11917
+ // src/instrumentation/plugins/openai-channels.ts
11918
+ var openAIChannels = defineChannels(
11919
+ "openai",
11920
+ {
11921
+ filesCreateTraced: channel({
11922
+ channelName: "files.create-traced",
11923
+ kind: "async"
11924
+ }),
11925
+ batchesRetrieveTraced: channel({
11926
+ channelName: "batches.retrieve-traced",
11927
+ kind: "async"
11928
+ }),
11929
+ batchesCompleteTrace: channel({
11930
+ channelName: "batches.complete-trace",
11931
+ kind: "async"
11932
+ }),
11933
+ chatCompletionsCreate: channel({
11934
+ channelName: "chat.completions.create",
11935
+ kind: "async"
11936
+ }),
11937
+ embeddingsCreate: channel({
11938
+ channelName: "embeddings.create",
11939
+ kind: "async"
11940
+ }),
11941
+ betaChatCompletionsParse: channel({
11942
+ channelName: "beta.chat.completions.parse",
11943
+ kind: "async"
11944
+ }),
11945
+ betaChatCompletionsStream: channel({
11946
+ channelName: "beta.chat.completions.stream",
11947
+ kind: "sync-stream"
11948
+ }),
11949
+ moderationsCreate: channel({
11950
+ channelName: "moderations.create",
11951
+ kind: "async"
11952
+ }),
11953
+ responsesCreate: channel({
11954
+ channelName: "responses.create",
11955
+ kind: "async"
11956
+ }),
11957
+ responsesStream: channel({
11958
+ channelName: "responses.stream",
11959
+ kind: "sync-stream"
11960
+ }),
11961
+ responsesParse: channel({
11962
+ channelName: "responses.parse",
11963
+ kind: "async"
11964
+ }),
11965
+ responsesCompact: channel({
11966
+ channelName: "responses.compact",
11967
+ kind: "async"
11968
+ })
11969
+ },
11970
+ { instrumentationName: INSTRUMENTATION_NAMES.OPENAI }
11971
+ );
11972
+
11834
11973
  // src/wrappers/attachment-utils.ts
11835
11974
  function getExtensionFromMediaType(mediaType) {
11836
11975
  const extensionMap = {
@@ -12006,118 +12145,91 @@ function processInputAttachments(input) {
12006
12145
  return processNode(input);
12007
12146
  }
12008
12147
 
12009
- // src/instrumentation/core/channel-definitions.ts
12010
- function channel(spec) {
12011
- return spec;
12012
- }
12013
- function defineChannels(pkg, channels, options) {
12014
- const { instrumentationName } = options;
12015
- return Object.fromEntries(
12016
- Object.entries(channels).map(([key, spec]) => {
12017
- const fullChannelName = `orchestrion:${pkg}:${spec.channelName}`;
12018
- if (spec.kind === "async") {
12019
- const asyncSpec = spec;
12020
- const tracingChannel2 = () => isomorph_default.newTracingChannel(
12021
- fullChannelName
12022
- );
12023
- const intercept2 = (interceptor) => {
12024
- const hook = tracingChannel2();
12025
- return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
12026
- };
12027
- };
12028
- return [
12029
- key,
12030
- {
12031
- ...asyncSpec,
12032
- instrumentationName,
12033
- intercept: intercept2,
12034
- invoke: (target, thisArg, args, additional) => {
12035
- const hook = tracingChannel2();
12036
- return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
12037
- },
12038
- tracingChannel: tracingChannel2,
12039
- tracePromise: (fn, context) => tracingChannel2().tracePromise(
12040
- fn,
12041
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
12042
- context
12043
- )
12044
- }
12045
- ];
12148
+ // src/instrumentation/plugins/openai-span-data.ts
12149
+ var OPENAI_METADATA_KEYS = [
12150
+ "model",
12151
+ "temperature",
12152
+ "top_p",
12153
+ "max_tokens",
12154
+ "frequency_penalty",
12155
+ "presence_penalty",
12156
+ "stop",
12157
+ "response_format",
12158
+ "tools",
12159
+ "tool_choice",
12160
+ "parallel_tool_calls",
12161
+ "max_tool_calls"
12162
+ ];
12163
+ function batchMetadata(params) {
12164
+ const metadata = { provider: "openai" };
12165
+ for (const key of OPENAI_METADATA_KEYS) {
12166
+ try {
12167
+ const value = Reflect.get(params, key);
12168
+ if (value !== void 0) {
12169
+ metadata[key] = value;
12046
12170
  }
12047
- const syncSpec = spec;
12048
- const tracingChannel = () => isomorph_default.newTracingChannel(
12049
- fullChannelName
12050
- );
12051
- const intercept = (interceptor) => {
12052
- const hook = tracingChannel();
12053
- return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
12054
- };
12055
- };
12056
- return [
12057
- key,
12058
- {
12059
- ...syncSpec,
12060
- instrumentationName,
12061
- intercept,
12062
- invoke: (target, thisArg, args, additional) => {
12063
- const hook = tracingChannel();
12064
- return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
12065
- },
12066
- tracingChannel,
12067
- traceSync: (fn, context) => tracingChannel().traceSync(
12068
- fn,
12069
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
12070
- context
12071
- )
12072
- }
12073
- ];
12074
- })
12075
- );
12171
+ } catch {
12172
+ }
12173
+ }
12174
+ return metadata;
12175
+ }
12176
+ function extractOpenAIBatchInput(endpoint, params) {
12177
+ const input = endpoint === "/v1/chat/completions" ? params.messages : params.input;
12178
+ return {
12179
+ input: processInputAttachments(input),
12180
+ metadata: batchMetadata(params)
12181
+ };
12182
+ }
12183
+ function extractOpenAIChatInput(params) {
12184
+ const { messages, ...metadata } = params;
12185
+ return {
12186
+ input: processInputAttachments(messages),
12187
+ metadata: { ...metadata, provider: "openai" }
12188
+ };
12189
+ }
12190
+ function extractOpenAIResponsesInput(params) {
12191
+ const { input, ...metadata } = params;
12192
+ return {
12193
+ input: processInputAttachments(input),
12194
+ metadata: { ...metadata, provider: "openai" }
12195
+ };
12196
+ }
12197
+ function extractOpenAIResponsesMetadata(result) {
12198
+ if (!result) {
12199
+ return void 0;
12200
+ }
12201
+ const { output: _output, usage: _usage, ...metadata } = result;
12202
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
12203
+ }
12204
+ function processImagesInOutput(output) {
12205
+ if (Array.isArray(output)) {
12206
+ return output.map(processImagesInOutput);
12207
+ }
12208
+ if (isObject(output) && output.type === "image_generation_call" && typeof output.result === "string" && output.result) {
12209
+ const fileExtension = output.output_format || "png";
12210
+ const contentType = `image/${fileExtension}`;
12211
+ const baseFilename = typeof output.revised_prompt === "string" ? output.revised_prompt.slice(0, 50).replace(/[^a-zA-Z0-9]/g, "_") : "generated_image";
12212
+ let binaryString;
12213
+ try {
12214
+ binaryString = atob(output.result);
12215
+ } catch {
12216
+ return output;
12217
+ }
12218
+ const bytes = new Uint8Array(binaryString.length);
12219
+ for (let i = 0; i < binaryString.length; i++) {
12220
+ bytes[i] = binaryString.charCodeAt(i);
12221
+ }
12222
+ return {
12223
+ ...output,
12224
+ result: new Attachment({
12225
+ data: new Blob([bytes], { type: contentType }),
12226
+ filename: `${baseFilename}.${fileExtension}`,
12227
+ contentType
12228
+ })
12229
+ };
12230
+ }
12231
+ return output;
12076
12232
  }
12077
-
12078
- // src/instrumentation/plugins/openai-channels.ts
12079
- var openAIChannels = defineChannels(
12080
- "openai",
12081
- {
12082
- chatCompletionsCreate: channel({
12083
- channelName: "chat.completions.create",
12084
- kind: "async"
12085
- }),
12086
- embeddingsCreate: channel({
12087
- channelName: "embeddings.create",
12088
- kind: "async"
12089
- }),
12090
- betaChatCompletionsParse: channel({
12091
- channelName: "beta.chat.completions.parse",
12092
- kind: "async"
12093
- }),
12094
- betaChatCompletionsStream: channel({
12095
- channelName: "beta.chat.completions.stream",
12096
- kind: "sync-stream"
12097
- }),
12098
- moderationsCreate: channel({
12099
- channelName: "moderations.create",
12100
- kind: "async"
12101
- }),
12102
- responsesCreate: channel({
12103
- channelName: "responses.create",
12104
- kind: "async"
12105
- }),
12106
- responsesStream: channel({
12107
- channelName: "responses.stream",
12108
- kind: "sync-stream"
12109
- }),
12110
- responsesParse: channel({
12111
- channelName: "responses.parse",
12112
- kind: "async"
12113
- }),
12114
- responsesCompact: channel({
12115
- channelName: "responses.compact",
12116
- kind: "async"
12117
- })
12118
- },
12119
- { instrumentationName: INSTRUMENTATION_NAMES.OPENAI }
12120
- );
12121
12233
 
12122
12234
  // src/openai-utils.ts
12123
12235
  var BRAINTRUST_CACHED_STREAM_METRIC = "__braintrust_cached_metric";
@@ -12174,23 +12286,573 @@ function getCachedMetricFromHeaders(headers) {
12174
12286
  return parseCachedHeader(headers.get(LEGACY_CACHED_HEADER));
12175
12287
  }
12176
12288
 
12289
+ // src/instrumentation/plugins/openai-batch-instrumentation.ts
12290
+ var SUPPORTED_ENDPOINTS = /* @__PURE__ */ new Set(["/v1/chat/completions", "/v1/responses"]);
12291
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
12292
+ "completed",
12293
+ "failed",
12294
+ "expired",
12295
+ "cancelled"
12296
+ ]);
12297
+ var pendingBatchTraces = /* @__PURE__ */ new Map();
12298
+ function read(value, key) {
12299
+ if (!isObject(value)) {
12300
+ return void 0;
12301
+ }
12302
+ try {
12303
+ return Reflect.get(value, key);
12304
+ } catch {
12305
+ return void 0;
12306
+ }
12307
+ }
12308
+ function isBatchRecordIterable(value) {
12309
+ if (value === null || typeof value !== "object" && typeof value !== "function") {
12310
+ return false;
12311
+ }
12312
+ return typeof read(value, Symbol.iterator) === "function" || typeof read(value, Symbol.asyncIterator) === "function";
12313
+ }
12314
+ function validCustomId(value) {
12315
+ return typeof value === "string" && value.length > 0 && value.length <= 64;
12316
+ }
12317
+ function logBatchInstrumentationError(context, error) {
12318
+ debugLogger.debug(`OpenAI Batch instrumentation ${context}:`, error);
12319
+ }
12320
+ async function exportParent(parent) {
12321
+ if ("toStr" in parent && typeof parent.toStr === "function") {
12322
+ return parent.toStr();
12323
+ }
12324
+ if ("export" in parent && typeof parent.export === "function") {
12325
+ return await parent.export();
12326
+ }
12327
+ return void 0;
12328
+ }
12329
+ async function deterministicDigest(namespace, ...parts) {
12330
+ const encoded = new TextEncoder().encode(
12331
+ [namespace, ...parts].map((part) => `${part.length}:${part}`).join("\0")
12332
+ );
12333
+ return new Uint8Array(
12334
+ await globalThis.crypto.subtle.digest("SHA-256", encoded)
12335
+ );
12336
+ }
12337
+ function digestHex(bytes, length) {
12338
+ return Array.from(bytes.slice(0, length)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
12339
+ }
12340
+ function digestUuid(bytes) {
12341
+ const hex = digestHex(bytes, 16);
12342
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
12343
+ }
12344
+ async function batchSpanIds(inputFileId) {
12345
+ const [row, span, root] = await Promise.all([
12346
+ deterministicDigest("openai:batch:row", inputFileId),
12347
+ deterministicDigest("openai:batch:span", inputFileId),
12348
+ deterministicDigest("openai:batch:root", inputFileId)
12349
+ ]);
12350
+ return {
12351
+ rowId: digestUuid(row),
12352
+ spanId: digestHex(span, 8),
12353
+ rootSpanId: digestHex(root, 16)
12354
+ };
12355
+ }
12356
+ async function childSpanIds(inputFileId, customId) {
12357
+ const [row, span] = await Promise.all([
12358
+ deterministicDigest("openai:batch:child:row", inputFileId, customId),
12359
+ deterministicDigest("openai:batch:child:span", inputFileId, customId)
12360
+ ]);
12361
+ return { rowId: digestUuid(row), spanId: digestHex(span, 8) };
12362
+ }
12363
+ async function startBatchSpan(context) {
12364
+ const ids = await batchSpanIds(context.inputFileId);
12365
+ const parent = SpanComponentsV4.fromStr(context.parent);
12366
+ const hasParentSpan = Boolean(
12367
+ parent.data.row_id && parent.data.span_id && parent.data.root_span_id
12368
+ );
12369
+ return withCurrent(
12370
+ NOOP_SPAN,
12371
+ () => _internalStartSpanWithInitialMergeAndParentSpanIds(
12372
+ withSpanInstrumentationName(
12373
+ {
12374
+ name: "openai.batch",
12375
+ type: "task" /* TASK */,
12376
+ parent: context.parent,
12377
+ ...!hasParentSpan ? {
12378
+ parentSpanIds: {
12379
+ parentSpanIds: [],
12380
+ rootSpanId: ids.rootSpanId
12381
+ }
12382
+ } : {},
12383
+ spanId: ids.spanId,
12384
+ startTime: context.taskStartTime,
12385
+ event: {
12386
+ id: ids.rowId,
12387
+ metadata: {
12388
+ endpoint: context.endpoint,
12389
+ input_file_id: context.inputFileId,
12390
+ provider: "openai"
12391
+ }
12392
+ }
12393
+ },
12394
+ INSTRUMENTATION_NAMES.OPENAI
12395
+ )
12396
+ )
12397
+ );
12398
+ }
12399
+ async function startBatchChild(context, taskParent, input) {
12400
+ const ids = await childSpanIds(context.inputFileId, input.customId);
12401
+ return withCurrent(
12402
+ NOOP_SPAN,
12403
+ () => _internalStartSpanWithInitialMerge(
12404
+ withSpanInstrumentationName(
12405
+ {
12406
+ name: context.endpoint === "/v1/chat/completions" ? "Chat Completion" : "openai.responses.create",
12407
+ type: "llm" /* LLM */,
12408
+ parent: taskParent,
12409
+ spanId: ids.spanId,
12410
+ startTime: context.childStartTime,
12411
+ event: {
12412
+ id: ids.rowId,
12413
+ ...input.spanData?.input !== void 0 ? { input: input.spanData.input } : {},
12414
+ metadata: {
12415
+ ...input.spanData?.metadata,
12416
+ custom_id: input.customId,
12417
+ provider: "openai"
12418
+ }
12419
+ }
12420
+ },
12421
+ INSTRUMENTATION_NAMES.OPENAI
12422
+ )
12423
+ )
12424
+ );
12425
+ }
12426
+ async function* jsonlRecords(file, onIssue = () => {
12427
+ }) {
12428
+ const resolvedFile = await file;
12429
+ if (typeof resolvedFile === "string") {
12430
+ for (const line of resolvedFile.split("\n")) {
12431
+ if (!line.trim()) {
12432
+ continue;
12433
+ }
12434
+ try {
12435
+ yield JSON.parse(line.replace(/\r$/, ""));
12436
+ } catch (error) {
12437
+ logBatchInstrumentationError("skipped malformed JSONL", error);
12438
+ onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
12439
+ }
12440
+ }
12441
+ return;
12442
+ }
12443
+ const body = read(resolvedFile, "body");
12444
+ const getReader = read(body, "getReader");
12445
+ if (isObject(body) && typeof getReader === "function") {
12446
+ let reader;
12447
+ try {
12448
+ reader = Reflect.apply(getReader, body, []);
12449
+ const decoder = new TextDecoder();
12450
+ let pending = "";
12451
+ while (true) {
12452
+ const readChunk = read(reader, "read");
12453
+ if (typeof readChunk !== "function") {
12454
+ throw new Error("Response body stream has no read method");
12455
+ }
12456
+ const chunk = await Reflect.apply(readChunk, reader, []);
12457
+ if (!isObject(chunk)) {
12458
+ throw new Error("Response body stream returned an invalid chunk");
12459
+ }
12460
+ if (chunk.done === true) {
12461
+ pending += decoder.decode();
12462
+ break;
12463
+ }
12464
+ if (!(chunk.value instanceof Uint8Array)) {
12465
+ throw new Error("Response body stream returned a non-byte chunk");
12466
+ }
12467
+ pending += decoder.decode(chunk.value, { stream: true });
12468
+ let newline = pending.indexOf("\n");
12469
+ while (newline !== -1) {
12470
+ const line = pending.slice(0, newline).replace(/\r$/, "");
12471
+ pending = pending.slice(newline + 1);
12472
+ if (line.trim()) {
12473
+ try {
12474
+ yield JSON.parse(line);
12475
+ } catch (error) {
12476
+ logBatchInstrumentationError("skipped malformed JSONL", error);
12477
+ onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
12478
+ }
12479
+ }
12480
+ newline = pending.indexOf("\n");
12481
+ }
12482
+ }
12483
+ if (pending.trim()) {
12484
+ try {
12485
+ yield JSON.parse(pending.replace(/\r$/, ""));
12486
+ } catch (error) {
12487
+ logBatchInstrumentationError("skipped malformed JSONL", error);
12488
+ onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
12489
+ }
12490
+ }
12491
+ } catch (error) {
12492
+ logBatchInstrumentationError("could not read JSONL response body", error);
12493
+ onIssue(new Error("OpenAI Batch response body could not be read"));
12494
+ } finally {
12495
+ const releaseLock = read(reader, "releaseLock");
12496
+ if (typeof releaseLock === "function") {
12497
+ try {
12498
+ Reflect.apply(releaseLock, reader, []);
12499
+ } catch (error) {
12500
+ logBatchInstrumentationError(
12501
+ "could not release stream reader",
12502
+ error
12503
+ );
12504
+ }
12505
+ }
12506
+ }
12507
+ return;
12508
+ }
12509
+ if (isBatchRecordIterable(resolvedFile)) {
12510
+ for await (const record of resolvedFile) {
12511
+ yield record;
12512
+ }
12513
+ return;
12514
+ }
12515
+ logBatchInstrumentationError("skipped invalid JSONL source", resolvedFile);
12516
+ onIssue(new Error("OpenAI Batch file source is invalid"));
12517
+ }
12518
+ async function readBatchInputs(file) {
12519
+ const inputs = /* @__PURE__ */ new Map();
12520
+ const issues = [];
12521
+ let endpoint;
12522
+ try {
12523
+ for await (const value of jsonlRecords(
12524
+ file,
12525
+ (issue) => issues.push(issue)
12526
+ )) {
12527
+ const customId = read(value, "custom_id");
12528
+ const url = read(value, "url");
12529
+ const body = read(value, "body");
12530
+ if (!validCustomId(customId) || inputs.has(customId) || read(value, "method") !== "POST" || typeof url !== "string" || !SUPPORTED_ENDPOINTS.has(url) || endpoint !== void 0 && endpoint !== url || !isObject(body)) {
12531
+ issues.push(new Error("OpenAI Batch input contains an invalid record"));
12532
+ continue;
12533
+ }
12534
+ endpoint = url;
12535
+ let spanData;
12536
+ try {
12537
+ spanData = extractOpenAIBatchInput(url, body);
12538
+ } catch (error) {
12539
+ logBatchInstrumentationError("could not extract batch input", error);
12540
+ }
12541
+ inputs.set(customId, { customId, spanData });
12542
+ }
12543
+ } catch (error) {
12544
+ logBatchInstrumentationError("could not process input file", error);
12545
+ issues.push(new Error("OpenAI Batch input file could not be processed"));
12546
+ }
12547
+ if (!endpoint || inputs.size === 0) {
12548
+ issues.push(new Error("OpenAI Batch input contains no supported records"));
12549
+ }
12550
+ return { endpoint, inputs, issues };
12551
+ }
12552
+ async function writePendingSpans(trace, endTime) {
12553
+ const task = await startBatchSpan(trace.context);
12554
+ const taskParent = await task.export();
12555
+ for (const input of trace.inputs.values()) {
12556
+ try {
12557
+ const child = await startBatchChild(trace.context, taskParent, input);
12558
+ if (endTime !== void 0) {
12559
+ child.end({ endTime });
12560
+ }
12561
+ } catch (error) {
12562
+ logBatchInstrumentationError("could not write batch request span", error);
12563
+ }
12564
+ }
12565
+ if (endTime !== void 0) {
12566
+ if (trace.status && trace.status !== "completed") {
12567
+ task.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
12568
+ }
12569
+ task.end({ endTime });
12570
+ }
12571
+ }
12572
+ var interceptOpenAIFilesCreateTraced = async (target, thisArg, args) => {
12573
+ const startTime = getCurrentUnixTimestamp();
12574
+ const inputPromise = readBatchInputs(args[0].inputFileContent);
12575
+ const parentPromise = exportParent(args[0].parent);
12576
+ const file = await Reflect.apply(target, thisArg, args);
12577
+ try {
12578
+ const inputFileId = read(file, "id");
12579
+ const [inputData, exportedParent2] = await Promise.all([
12580
+ inputPromise,
12581
+ parentPromise
12582
+ ]);
12583
+ if (typeof inputFileId !== "string" || !exportedParent2 || !inputData.endpoint || inputData.issues.length > 0) {
12584
+ if (inputData.issues[0]) {
12585
+ logBatchInstrumentationError(
12586
+ "skipped invalid input file",
12587
+ inputData.issues[0]
12588
+ );
12589
+ }
12590
+ return file;
12591
+ }
12592
+ const trace = {
12593
+ context: {
12594
+ inputFileId,
12595
+ endpoint: inputData.endpoint,
12596
+ parent: exportedParent2,
12597
+ taskStartTime: startTime,
12598
+ childStartTime: startTime
12599
+ },
12600
+ inputs: inputData.inputs
12601
+ };
12602
+ pendingBatchTraces.set(inputFileId, trace);
12603
+ await writePendingSpans(trace);
12604
+ } catch (error) {
12605
+ logBatchInstrumentationError("could not start batch spans", error);
12606
+ }
12607
+ return file;
12608
+ };
12609
+ function validTimestamp(value) {
12610
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
12611
+ }
12612
+ function terminalEndTime(batch, startTime) {
12613
+ let timestamp;
12614
+ switch (batch.status) {
12615
+ case "completed":
12616
+ timestamp = batch.completed_at;
12617
+ break;
12618
+ case "failed":
12619
+ timestamp = batch.failed_at;
12620
+ break;
12621
+ case "expired":
12622
+ timestamp = batch.expired_at;
12623
+ break;
12624
+ default:
12625
+ timestamp = batch.cancelled_at;
12626
+ }
12627
+ return validTimestamp(timestamp) ? Math.max(timestamp, startTime) : Math.max(getCurrentUnixTimestamp(), startTime);
12628
+ }
12629
+ async function updateBatchTimestamps(batch) {
12630
+ const trace = pendingBatchTraces.get(batch.input_file_id);
12631
+ if (!trace || trace.context.endpoint !== batch.endpoint) {
12632
+ return;
12633
+ }
12634
+ if (validTimestamp(batch.created_at)) {
12635
+ trace.context.taskStartTime = batch.created_at;
12636
+ }
12637
+ trace.context.childStartTime = validTimestamp(batch.in_progress_at) ? Math.max(batch.in_progress_at, trace.context.taskStartTime) : trace.context.taskStartTime;
12638
+ trace.status = batch.status;
12639
+ if (TERMINAL_STATUSES.has(batch.status)) {
12640
+ trace.endTime = terminalEndTime(batch, trace.context.childStartTime);
12641
+ }
12642
+ await writePendingSpans(trace, trace.endTime);
12643
+ }
12644
+ var interceptOpenAIBatchesRetrieveTraced = (target, thisArg, args) => Promise.resolve(Reflect.apply(target, thisArg, args)).then(async (batch) => {
12645
+ try {
12646
+ await updateBatchTimestamps(batch);
12647
+ } catch (error) {
12648
+ logBatchInstrumentationError("could not update batch timestamps", error);
12649
+ }
12650
+ return batch;
12651
+ });
12652
+ function errorFromResult(result) {
12653
+ const error = read(result.value, "error");
12654
+ if (isObject(error)) {
12655
+ const message = read(error, "message");
12656
+ return new Error(
12657
+ typeof message === "string" ? message : "OpenAI Batch request failed"
12658
+ );
12659
+ }
12660
+ const response = read(result.value, "response");
12661
+ const statusCode = read(response, "status_code");
12662
+ if (result.source === "error" || typeof statusCode === "number" && (statusCode < 200 || statusCode >= 300)) {
12663
+ const message = read(read(read(response, "body"), "error"), "message");
12664
+ return new Error(
12665
+ typeof message === "string" ? message : "OpenAI Batch request failed"
12666
+ );
12667
+ }
12668
+ return void 0;
12669
+ }
12670
+ async function completeBatchResult(context, taskParent, endTime, input, result) {
12671
+ const child = await startBatchChild(context, taskParent, input);
12672
+ try {
12673
+ const resultError = errorFromResult(result);
12674
+ const responseBody = read(read(result.value, "response"), "body");
12675
+ if (resultError) {
12676
+ child.log({ error: resultError });
12677
+ } else if (isObject(responseBody)) {
12678
+ const model = read(responseBody, "model");
12679
+ child.log({
12680
+ output: context.endpoint === "/v1/chat/completions" ? read(responseBody, "choices") : processImagesInOutput(read(responseBody, "output")),
12681
+ ...typeof model === "string" ? { metadata: { model } } : {},
12682
+ metrics: parseMetricsFromUsage(read(responseBody, "usage"))
12683
+ });
12684
+ } else {
12685
+ child.log({ error: new Error("OpenAI Batch response body is missing") });
12686
+ }
12687
+ } catch (error) {
12688
+ child.log({ error });
12689
+ } finally {
12690
+ child.end({ endTime });
12691
+ }
12692
+ }
12693
+ async function completeResultFile({
12694
+ context,
12695
+ endTime,
12696
+ file,
12697
+ inputs,
12698
+ issues,
12699
+ seen,
12700
+ source,
12701
+ taskParent
12702
+ }) {
12703
+ if (file === void 0) {
12704
+ return;
12705
+ }
12706
+ for await (const value of jsonlRecords(file, (issue) => issues.push(issue))) {
12707
+ const customId = read(value, "custom_id");
12708
+ if (!validCustomId(customId) || !isObject(value)) {
12709
+ issues.push(
12710
+ new Error("OpenAI Batch result is missing a valid custom_id")
12711
+ );
12712
+ continue;
12713
+ }
12714
+ if (seen.has(customId)) {
12715
+ issues.push(new Error("OpenAI Batch result contains a duplicate"));
12716
+ continue;
12717
+ }
12718
+ const input = inputs.get(customId);
12719
+ if (!input) {
12720
+ issues.push(
12721
+ new Error("OpenAI Batch result does not match an input record")
12722
+ );
12723
+ continue;
12724
+ }
12725
+ seen.add(customId);
12726
+ await completeBatchResult(context, taskParent, endTime, input, {
12727
+ value,
12728
+ source
12729
+ });
12730
+ }
12731
+ }
12732
+ function sameInputs(first, second) {
12733
+ return first.size === second.size && [...first.keys()].every((customId) => second.has(customId));
12734
+ }
12735
+ async function contextForCompletion(inputFileId, inputData) {
12736
+ if (!inputData.endpoint || inputData.issues.length > 0) {
12737
+ return void 0;
12738
+ }
12739
+ const existing = pendingBatchTraces.get(inputFileId);
12740
+ if (existing) {
12741
+ if (existing.context.endpoint !== inputData.endpoint || !sameInputs(existing.inputs, inputData.inputs)) {
12742
+ return void 0;
12743
+ }
12744
+ return { ...existing, inputs: inputData.inputs };
12745
+ }
12746
+ const parent = await exportParent(getSpanParentObject());
12747
+ if (!parent) {
12748
+ return void 0;
12749
+ }
12750
+ const startTime = getCurrentUnixTimestamp();
12751
+ return {
12752
+ context: {
12753
+ inputFileId,
12754
+ endpoint: inputData.endpoint,
12755
+ parent,
12756
+ taskStartTime: startTime,
12757
+ childStartTime: startTime
12758
+ },
12759
+ inputs: inputData.inputs
12760
+ };
12761
+ }
12762
+ async function completeBatch(args) {
12763
+ const inputData = await readBatchInputs(args.inputFileContent);
12764
+ const trace = await contextForCompletion(args.inputFileId, inputData);
12765
+ if (!trace) {
12766
+ logBatchInstrumentationError(
12767
+ "left batch spans pending",
12768
+ inputData.issues[0] ?? new Error("OpenAI Batch input does not match")
12769
+ );
12770
+ return;
12771
+ }
12772
+ const endTime = trace.endTime ?? getCurrentUnixTimestamp();
12773
+ const task = await startBatchSpan(trace.context);
12774
+ const taskParent = await task.export();
12775
+ const issues = [];
12776
+ const seen = /* @__PURE__ */ new Set();
12777
+ await Promise.all([
12778
+ completeResultFile({
12779
+ context: trace.context,
12780
+ endTime,
12781
+ file: args.outputFileContent,
12782
+ inputs: trace.inputs,
12783
+ issues,
12784
+ seen,
12785
+ source: "output",
12786
+ taskParent
12787
+ }),
12788
+ completeResultFile({
12789
+ context: trace.context,
12790
+ endTime,
12791
+ file: args.errorFileContent,
12792
+ inputs: trace.inputs,
12793
+ issues,
12794
+ seen,
12795
+ source: "error",
12796
+ taskParent
12797
+ })
12798
+ ]);
12799
+ if (issues.length > 0) {
12800
+ logBatchInstrumentationError("left batch spans pending", issues[0]);
12801
+ return;
12802
+ }
12803
+ const missing = [...trace.inputs.values()].filter(
12804
+ ({ customId }) => !seen.has(customId)
12805
+ );
12806
+ if (!trace.status || trace.status === "completed") {
12807
+ if (missing.length > 0) {
12808
+ logBatchInstrumentationError(
12809
+ "left batch spans pending",
12810
+ new Error("OpenAI Batch result files are incomplete")
12811
+ );
12812
+ return;
12813
+ }
12814
+ } else {
12815
+ for (const input of missing) {
12816
+ const child = await startBatchChild(trace.context, taskParent, input);
12817
+ child.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
12818
+ child.end({ endTime });
12819
+ }
12820
+ task.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
12821
+ }
12822
+ task.end({ endTime });
12823
+ pendingBatchTraces.delete(args.inputFileId);
12824
+ }
12825
+ var interceptOpenAIBatchTraceComplete = (target, thisArg, args) => Promise.resolve(Reflect.apply(target, thisArg, args)).then(async (result) => {
12826
+ try {
12827
+ await completeBatch(args[0]);
12828
+ } catch (error) {
12829
+ logBatchInstrumentationError("could not complete batch", error);
12830
+ }
12831
+ return result;
12832
+ });
12833
+
12177
12834
  // src/instrumentation/plugins/openai-plugin.ts
12178
12835
  var OpenAIPlugin = class extends BasePlugin {
12179
12836
  constructor() {
12180
12837
  super();
12181
12838
  }
12182
12839
  onEnable() {
12840
+ this.unsubscribers.push(
12841
+ openAIChannels.filesCreateTraced.intercept(
12842
+ interceptOpenAIFilesCreateTraced
12843
+ ),
12844
+ openAIChannels.batchesRetrieveTraced.intercept(
12845
+ interceptOpenAIBatchesRetrieveTraced
12846
+ ),
12847
+ openAIChannels.batchesCompleteTrace.intercept(
12848
+ interceptOpenAIBatchTraceComplete
12849
+ )
12850
+ );
12183
12851
  this.unsubscribers.push(
12184
12852
  traceStreamingChannel(openAIChannels.chatCompletionsCreate, {
12185
12853
  name: "Chat Completion",
12186
12854
  type: "llm" /* LLM */,
12187
- extractInput: ([params]) => {
12188
- const { messages, ...metadata } = params;
12189
- return {
12190
- input: processInputAttachments(messages),
12191
- metadata: { ...metadata, provider: "openai" }
12192
- };
12193
- },
12855
+ extractInput: ([params]) => extractOpenAIChatInput(params),
12194
12856
  extractOutput: (result) => {
12195
12857
  return result?.choices;
12196
12858
  },
@@ -12236,13 +12898,7 @@ var OpenAIPlugin = class extends BasePlugin {
12236
12898
  traceStreamingChannel(openAIChannels.betaChatCompletionsParse, {
12237
12899
  name: "Chat Completion",
12238
12900
  type: "llm" /* LLM */,
12239
- extractInput: ([params]) => {
12240
- const { messages, ...metadata } = params;
12241
- return {
12242
- input: processInputAttachments(messages),
12243
- metadata: { ...metadata, provider: "openai" }
12244
- };
12245
- },
12901
+ extractInput: ([params]) => extractOpenAIChatInput(params),
12246
12902
  extractOutput: (result) => {
12247
12903
  return result?.choices;
12248
12904
  },
@@ -12264,13 +12920,7 @@ var OpenAIPlugin = class extends BasePlugin {
12264
12920
  traceSyncStreamChannel(openAIChannels.betaChatCompletionsStream, {
12265
12921
  name: "Chat Completion",
12266
12922
  type: "llm" /* LLM */,
12267
- extractInput: ([params]) => {
12268
- const { messages, ...metadata } = params;
12269
- return {
12270
- input: processInputAttachments(messages),
12271
- metadata: { ...metadata, provider: "openai" }
12272
- };
12273
- }
12923
+ extractInput: ([params]) => extractOpenAIChatInput(params)
12274
12924
  })
12275
12925
  );
12276
12926
  this.unsubscribers.push(
@@ -12300,23 +12950,11 @@ var OpenAIPlugin = class extends BasePlugin {
12300
12950
  traceStreamingChannel(openAIChannels.responsesCreate, {
12301
12951
  name: "openai.responses.create",
12302
12952
  type: "llm" /* LLM */,
12303
- extractInput: ([params]) => {
12304
- const { input, ...metadata } = params;
12305
- return {
12306
- input: processInputAttachments(input),
12307
- metadata: { ...metadata, provider: "openai" }
12308
- };
12309
- },
12953
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
12310
12954
  extractOutput: (result) => {
12311
12955
  return processImagesInOutput(result?.output);
12312
12956
  },
12313
- extractMetadata: (result) => {
12314
- if (!result) {
12315
- return void 0;
12316
- }
12317
- const { output: _output, usage: _usage, ...metadata } = result;
12318
- return Object.keys(metadata).length > 0 ? metadata : void 0;
12319
- },
12957
+ extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
12320
12958
  extractMetrics: (result, startTime, endEvent) => {
12321
12959
  const metrics = withCachedMetric(
12322
12960
  parseMetricsFromUsage(result?.usage),
@@ -12335,13 +12973,7 @@ var OpenAIPlugin = class extends BasePlugin {
12335
12973
  traceSyncStreamChannel(openAIChannels.responsesStream, {
12336
12974
  name: "openai.responses.create",
12337
12975
  type: "llm" /* LLM */,
12338
- extractInput: ([params]) => {
12339
- const { input, ...metadata } = params;
12340
- return {
12341
- input: processInputAttachments(input),
12342
- metadata: { ...metadata, provider: "openai" }
12343
- };
12344
- },
12976
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
12345
12977
  extractFromEvent: (event) => {
12346
12978
  if (event.type !== "response.completed" || !event.response) {
12347
12979
  return {};
@@ -12364,23 +12996,11 @@ var OpenAIPlugin = class extends BasePlugin {
12364
12996
  traceStreamingChannel(openAIChannels.responsesParse, {
12365
12997
  name: "openai.responses.parse",
12366
12998
  type: "llm" /* LLM */,
12367
- extractInput: ([params]) => {
12368
- const { input, ...metadata } = params;
12369
- return {
12370
- input: processInputAttachments(input),
12371
- metadata: { ...metadata, provider: "openai" }
12372
- };
12373
- },
12999
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
12374
13000
  extractOutput: (result) => {
12375
13001
  return processImagesInOutput(result?.output);
12376
13002
  },
12377
- extractMetadata: (result) => {
12378
- if (!result) {
12379
- return void 0;
12380
- }
12381
- const { output: _output, usage: _usage, ...metadata } = result;
12382
- return Object.keys(metadata).length > 0 ? metadata : void 0;
12383
- },
13003
+ extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
12384
13004
  extractMetrics: (result, startTime, endEvent) => {
12385
13005
  const metrics = withCachedMetric(
12386
13006
  parseMetricsFromUsage(result?.usage),
@@ -12399,23 +13019,11 @@ var OpenAIPlugin = class extends BasePlugin {
12399
13019
  traceAsyncChannel(openAIChannels.responsesCompact, {
12400
13020
  name: "openai.responses.compact",
12401
13021
  type: "llm" /* LLM */,
12402
- extractInput: ([params]) => {
12403
- const { input, ...metadata } = params;
12404
- return {
12405
- input: processInputAttachments(input),
12406
- metadata: { ...metadata, provider: "openai" }
12407
- };
12408
- },
13022
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
12409
13023
  extractOutput: (result) => {
12410
13024
  return processImagesInOutput(result?.output);
12411
13025
  },
12412
- extractMetadata: (result) => {
12413
- if (!result) {
12414
- return void 0;
12415
- }
12416
- const { output: _output, usage: _usage, ...metadata } = result;
12417
- return Object.keys(metadata).length > 0 ? metadata : void 0;
12418
- },
13026
+ extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
12419
13027
  extractMetrics: (result, startTime, endEvent) => {
12420
13028
  const metrics = withCachedMetric(
12421
13029
  parseMetricsFromUsage(result?.usage),
@@ -12471,35 +13079,6 @@ function withCachedMetric(metrics, result, endEvent) {
12471
13079
  cached
12472
13080
  };
12473
13081
  }
12474
- function processImagesInOutput(output) {
12475
- if (Array.isArray(output)) {
12476
- return output.map(processImagesInOutput);
12477
- }
12478
- if (isObject(output)) {
12479
- if (output.type === "image_generation_call" && output.result && typeof output.result === "string") {
12480
- const fileExtension = output.output_format || "png";
12481
- const contentType = `image/${fileExtension}`;
12482
- const baseFilename = output.revised_prompt && typeof output.revised_prompt === "string" ? output.revised_prompt.slice(0, 50).replace(/[^a-zA-Z0-9]/g, "_") : "generated_image";
12483
- const filename = `${baseFilename}.${fileExtension}`;
12484
- const binaryString = atob(output.result);
12485
- const bytes = new Uint8Array(binaryString.length);
12486
- for (let i = 0; i < binaryString.length; i++) {
12487
- bytes[i] = binaryString.charCodeAt(i);
12488
- }
12489
- const blob = new Blob([bytes], { type: contentType });
12490
- const attachment = new Attachment({
12491
- data: blob,
12492
- filename,
12493
- contentType
12494
- });
12495
- return {
12496
- ...output,
12497
- result: attachment
12498
- };
12499
- }
12500
- }
12501
- return output;
12502
- }
12503
13082
  function mergeLogprobTokens(existing, incoming) {
12504
13083
  if (incoming === void 0) {
12505
13084
  return existing;
@@ -17643,6 +18222,12 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiS
17643
18222
  if (!activeEntry) {
17644
18223
  return result;
17645
18224
  }
18225
+ const executionOptions = args[1];
18226
+ const toolCallId = isObject(executionOptions) ? executionOptions.toolCallId : void 0;
18227
+ const toolInput = {
18228
+ input: serializeToolExecutionInput(args),
18229
+ ...typeof toolCallId === "string" ? { metadata: { toolCallId } } : {}
18230
+ };
17646
18231
  if (isAsyncGenerator(result)) {
17647
18232
  return (async function* () {
17648
18233
  const span = activeEntry.parentSpan.startSpan(
@@ -17656,7 +18241,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiS
17656
18241
  INSTRUMENTATION_NAMES.AI_SDK
17657
18242
  )
17658
18243
  );
17659
- span.log({ input: serializeToolExecutionInput(args) });
18244
+ span.log(toolInput);
17660
18245
  try {
17661
18246
  let lastValue;
17662
18247
  for await (const value of result) {
@@ -17674,7 +18259,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiS
17674
18259
  }
17675
18260
  return activeEntry.parentSpan.traced(
17676
18261
  async (span) => {
17677
- span.log({ input: serializeToolExecutionInput(args) });
18262
+ span.log(toolInput);
17678
18263
  const awaitedResult = await result;
17679
18264
  span.log({ output: awaitedResult });
17680
18265
  return awaitedResult;
@@ -31796,14 +32381,17 @@ function getMetricsFromResponse(response) {
31796
32381
  if (!isRecord(usageMetadata)) {
31797
32382
  continue;
31798
32383
  }
31799
- const inputTokenDetails = usageMetadata.input_token_details;
32384
+ const inputTokenDetails = isRecord(usageMetadata.input_token_details) ? usageMetadata.input_token_details : {};
31800
32385
  const outputTokenDetails = usageMetadata.output_token_details;
31801
32386
  return normalizeTokenMetrics({
31802
32387
  total_tokens: usageMetadata.total_tokens,
31803
32388
  prompt_tokens: usageMetadata.input_tokens,
31804
32389
  completion_tokens: usageMetadata.output_tokens,
31805
- prompt_cache_creation_tokens: isRecord(inputTokenDetails) ? inputTokenDetails.cache_creation : void 0,
31806
- prompt_cached_tokens: isRecord(inputTokenDetails) ? inputTokenDetails.cache_read : void 0,
32390
+ // Prefer TTL-specific cache writes over the aggregate when available.
32391
+ prompt_cache_creation_tokens: inputTokenDetails.ephemeral_5m_input_tokens == null && inputTokenDetails.ephemeral_1h_input_tokens == null ? inputTokenDetails.cache_creation : void 0,
32392
+ prompt_cache_creation_5m_tokens: inputTokenDetails.ephemeral_5m_input_tokens,
32393
+ prompt_cache_creation_1h_tokens: inputTokenDetails.ephemeral_1h_input_tokens,
32394
+ prompt_cached_tokens: inputTokenDetails.cache_read,
31807
32395
  completion_reasoning_tokens: isRecord(outputTokenDetails) ? outputTokenDetails.reasoning : void 0
31808
32396
  });
31809
32397
  }
@@ -32519,7 +33107,7 @@ function extractSession(event) {
32519
33107
  return isObject(candidate) && typeof candidate.prompt === "function" ? candidate : void 0;
32520
33108
  }
32521
33109
  function isPiAgent(value) {
32522
- return isObject(value) && typeof value.streamFn === "function" && typeof value.subscribe === "function";
33110
+ return isObject(value) && (typeof value.streamFunction === "function" || typeof value.streamFn === "function") && typeof value.subscribe === "function";
32523
33111
  }
32524
33112
  function promptContextStore() {
32525
33113
  piPromptContextStore ??= isomorph_default.newAsyncLocalStorage();
@@ -32529,17 +33117,21 @@ function currentPiPromptState() {
32529
33117
  return promptContextStore().getStore();
32530
33118
  }
32531
33119
  function installPiAgentInstrumentation(agent) {
33120
+ const property = typeof agent.streamFunction === "function" ? "streamFunction" : "streamFn";
33121
+ const streamFunction = agent[property];
32532
33122
  const existing = piAgentPatchStates.get(agent);
32533
- if (!existing || agent.streamFn !== existing.wrappedStreamFn) {
33123
+ if (streamFunction && (!existing || existing.property !== property || streamFunction !== existing.wrappedStreamFunction)) {
32534
33124
  const patchState = {
32535
- originalStreamFn: agent.streamFn,
32536
- wrappedStreamFn: agent.streamFn
33125
+ originalStreamFunction: streamFunction,
33126
+ property,
33127
+ wrappedStreamFunction: streamFunction
32537
33128
  };
32538
- patchState.wrappedStreamFn = makeInstrumentedStreamFn(
33129
+ patchState.wrappedStreamFunction = makeInstrumentedStreamFunction(
32539
33130
  agent,
32540
- patchState.originalStreamFn
33131
+ patchState.originalStreamFunction,
33132
+ property
32541
33133
  );
32542
- agent.streamFn = patchState.wrappedStreamFn;
33134
+ agent[property] = patchState.wrappedStreamFunction;
32543
33135
  piAgentPatchStates.set(agent, patchState);
32544
33136
  }
32545
33137
  if (piAgentEventSubscriptions.has(agent)) {
@@ -32564,15 +33156,21 @@ function installPiAgentInstrumentation(agent) {
32564
33156
  logInstrumentationError4("Pi Coding Agent event subscription", error);
32565
33157
  }
32566
33158
  }
32567
- function makeInstrumentedStreamFn(agent, originalStreamFn) {
32568
- return async function instrumentedPiStreamFn(model, context, options) {
32569
- const invokeOriginal = () => Reflect.apply(originalStreamFn, this, [model, context, options]);
33159
+ function makeInstrumentedStreamFunction(agent, originalStreamFunction, property) {
33160
+ return async function instrumentedPiStreamFunction(model, context, options) {
33161
+ const invokeOriginal = () => Reflect.apply(originalStreamFunction, this, [model, context, options]);
32570
33162
  const state = currentPiPromptState();
32571
33163
  if (!state || state.agent !== agent || state.finalized) {
32572
33164
  return invokeOriginal();
32573
33165
  }
32574
33166
  wrapPiToolExecutors(context.tools);
32575
- const llmState = await startPiLlmSpan(state, model, context, options);
33167
+ const llmState = await startPiLlmSpan(
33168
+ state,
33169
+ model,
33170
+ context,
33171
+ property,
33172
+ options
33173
+ );
32576
33174
  try {
32577
33175
  const stream = await runWithAutoInstrumentationSuppressed(invokeOriginal);
32578
33176
  return patchAssistantMessageStream(stream, state, llmState);
@@ -32609,12 +33207,12 @@ function wrapPiToolExecutors(tools) {
32609
33207
  }
32610
33208
  }
32611
33209
  }
32612
- async function startPiLlmSpan(state, model, context, options) {
33210
+ async function startPiLlmSpan(state, model, context, property, options) {
32613
33211
  const metadata = {
32614
33212
  ...extractModelMetadata2(model),
32615
33213
  ...extractStreamOptionsMetadata(options),
32616
33214
  ...extractToolMetadata(context.tools),
32617
- "pi_coding_agent.operation": "agent.streamFn"
33215
+ "pi_coding_agent.operation": `agent.${property}`
32618
33216
  };
32619
33217
  const span = startSpan(
32620
33218
  withSpanInstrumentationName(
@@ -36925,7 +37523,15 @@ var MAX_EVE_PROVIDER_CACHE_ENTRIES = 1e4;
36925
37523
  function createEveInstrumentationProvider(options = {}) {
36926
37524
  const bridge = new EveProviderBridge(options.metadata);
36927
37525
  return {
37526
+ // Eve versions before tracePolicy use capture to decide whether provider
37527
+ // events include content. Newer versions prefer tracePolicy when both are
37528
+ // present, so keep the deprecated field for backwards compatibility.
36928
37529
  capture: "content",
37530
+ tracePolicy: () => ({
37531
+ emit: true,
37532
+ recordInputs: true,
37533
+ recordOutputs: true
37534
+ }),
36929
37535
  events: {
36930
37536
  "action.completed": (event, context) => bridge.handleActionTerminal(event, context),
36931
37537
  "action.failed": (event, context) => bridge.handleActionTerminal(event, context),