braintrust 3.29.0 → 3.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dev/dist/index.d.mts +4 -1
  2. package/dev/dist/index.d.ts +4 -1
  3. package/dev/dist/index.js +1492 -887
  4. package/dev/dist/index.mjs +836 -231
  5. package/dist/apply-auto-instrumentation.js +217 -217
  6. package/dist/apply-auto-instrumentation.mjs +2 -2
  7. package/dist/auto-instrumentations/bundler/esbuild.cjs +13 -1
  8. package/dist/auto-instrumentations/bundler/esbuild.mjs +2 -2
  9. package/dist/auto-instrumentations/bundler/next.cjs +22 -4
  10. package/dist/auto-instrumentations/bundler/next.mjs +12 -6
  11. package/dist/auto-instrumentations/bundler/rollup.cjs +13 -1
  12. package/dist/auto-instrumentations/bundler/rollup.mjs +2 -2
  13. package/dist/auto-instrumentations/bundler/vite.cjs +13 -1
  14. package/dist/auto-instrumentations/bundler/vite.mjs +2 -2
  15. package/dist/auto-instrumentations/bundler/webpack-loader.cjs +13 -1
  16. package/dist/auto-instrumentations/bundler/webpack.cjs +13 -1
  17. package/dist/auto-instrumentations/bundler/webpack.mjs +3 -3
  18. package/dist/auto-instrumentations/{chunk-DKTGDNA7.mjs → chunk-LW4HDHXT.mjs} +1 -1
  19. package/dist/auto-instrumentations/{chunk-OMCZ3MV2.mjs → chunk-OXINGIEZ.mjs} +1 -1
  20. package/dist/auto-instrumentations/{chunk-AOIYCVEL.mjs → chunk-U64OYU4Q.mjs} +13 -1
  21. package/dist/auto-instrumentations/hook.mjs +18 -4
  22. package/dist/auto-instrumentations/index.cjs +13 -1
  23. package/dist/auto-instrumentations/index.mjs +1 -1
  24. package/dist/browser.d.mts +614 -480
  25. package/dist/browser.d.ts +614 -480
  26. package/dist/browser.js +1172 -305
  27. package/dist/browser.mjs +1172 -305
  28. package/dist/{chunk-6Z5S7VOU.js → chunk-4AQGZS6X.js} +13 -1
  29. package/dist/{chunk-M6XPNJC4.mjs → chunk-AJT4FR25.mjs} +703 -120
  30. package/dist/{chunk-7FA6VP2S.mjs → chunk-CE3BLB2L.mjs} +13 -1
  31. package/dist/{chunk-XLLGRXGR.js → chunk-V32LW47Z.js} +1819 -1236
  32. package/dist/cli.js +846 -241
  33. package/dist/edge-light.d.mts +1 -1
  34. package/dist/edge-light.d.ts +1 -1
  35. package/dist/edge-light.js +1172 -305
  36. package/dist/edge-light.mjs +1172 -305
  37. package/dist/index.d.mts +614 -480
  38. package/dist/index.d.ts +614 -480
  39. package/dist/index.js +915 -643
  40. package/dist/index.mjs +349 -77
  41. package/dist/instrumentation/index.d.mts +15 -0
  42. package/dist/instrumentation/index.d.ts +15 -0
  43. package/dist/instrumentation/index.js +836 -230
  44. package/dist/instrumentation/index.mjs +836 -230
  45. package/dist/vitest-evals-reporter.js +16 -16
  46. package/dist/vitest-evals-reporter.mjs +2 -2
  47. package/dist/workerd.d.mts +1 -1
  48. package/dist/workerd.d.ts +1 -1
  49. package/dist/workerd.js +1172 -305
  50. package/dist/workerd.mjs +1172 -305
  51. package/package.json +1 -1
package/dist/browser.js CHANGED
@@ -104,6 +104,7 @@ __export(browser_exports, {
104
104
  braintrustStreamChunkSchema: () => braintrustStreamChunkSchema,
105
105
  buildLocalSummary: () => buildLocalSummary,
106
106
  collectAnthropicSession: () => collectAnthropicSession,
107
+ completeOpenAIBatchTrace: () => completeOpenAIBatchTrace,
107
108
  configureInstrumentation: () => configureInstrumentation,
108
109
  constructLogs3OverflowRequest: () => constructLogs3OverflowRequest,
109
110
  createFinalValuePassThroughStream: () => createFinalValuePassThroughStream,
@@ -143,6 +144,8 @@ __export(browser_exports, {
143
144
  loginToState: () => loginToState,
144
145
  logs3OverflowUploadSchema: () => logs3OverflowUploadSchema,
145
146
  newId: () => newId,
147
+ openaiBatchesRetrieveTraced: () => openaiBatchesRetrieveTraced,
148
+ openaiFilesCreateTraced: () => openaiFilesCreateTraced,
146
149
  parseCachedHeader: () => parseCachedHeader,
147
150
  parseTemplateFormat: () => parseTemplateFormat,
148
151
  permalink: () => permalink,
@@ -4028,6 +4031,7 @@ var ProjectSettings = import_v36.z.union([
4028
4031
  ]),
4029
4032
  disable_realtime_queries: import_v36.z.union([import_v36.z.boolean(), import_v36.z.null()]),
4030
4033
  monitor_charts_use_metrics_start: import_v36.z.union([import_v36.z.boolean(), import_v36.z.null()]),
4034
+ blind_reviews: import_v36.z.union([import_v36.z.boolean(), import_v36.z.null()]),
4031
4035
  default_preprocessor: NullableSavedFunctionId
4032
4036
  }).partial(),
4033
4037
  import_v36.z.null()
@@ -4089,6 +4093,7 @@ var WindowedAutomationConfig = import_v36.z.object({
4089
4093
  auto_approve_tools: import_v36.z.array(import_v36.z.string().min(1)).optional().default([]),
4090
4094
  harness: import_v36.z.enum(["native", "codex", "claude-code"]).optional(),
4091
4095
  model: import_v36.z.string().min(1).optional(),
4096
+ endpoint_name: import_v36.z.string().min(1).optional(),
4092
4097
  reasoning_effort: import_v36.z.enum(["none", "minimal", "low", "medium", "high", "xhigh", "max"]).optional()
4093
4098
  }).optional(),
4094
4099
  actions: import_v36.z.array(
@@ -5880,7 +5885,7 @@ var INSTRUMENTATION_NAMES = {
5880
5885
  var INTERNAL_SPAN_INSTRUMENTATION_NAME = /* @__PURE__ */ Symbol.for(
5881
5886
  "braintrust.spanInstrumentationName"
5882
5887
  );
5883
- var SDK_VERSION = true ? "3.29.0" : "0.0.0";
5888
+ var SDK_VERSION = true ? "3.31.0" : "0.0.0";
5884
5889
  function withSpanInstrumentationName(args, instrumentationName) {
5885
5890
  return {
5886
5891
  ...args,
@@ -10127,6 +10132,15 @@ function _internalStartSpanWithInitialMerge(args) {
10127
10132
  [INITIAL_SPAN_WRITE_AS_MERGE]: true
10128
10133
  }).span;
10129
10134
  }
10135
+ function _internalStartSpanWithInitialMergeAndParentSpanIds(args) {
10136
+ return startSpanAndIsLogger(
10137
+ {
10138
+ ...args,
10139
+ [INITIAL_SPAN_WRITE_AS_MERGE]: true
10140
+ },
10141
+ { useParentSpanIdsForObjectParent: true }
10142
+ ).span;
10143
+ }
10130
10144
  function _internalStartSpanWithContext(args, context) {
10131
10145
  return startSpanAndIsLogger({
10132
10146
  ...args,
@@ -10140,7 +10154,7 @@ async function flush(options) {
10140
10154
  function setFetch(fetch2) {
10141
10155
  _internalGetGlobalState().setFetch(fetch2);
10142
10156
  }
10143
- function startSpanAndIsLogger(args) {
10157
+ function startSpanAndIsLogger(args, internalOptions) {
10144
10158
  const state = args?.state ?? _globalState;
10145
10159
  const { parentObject, propagatedState } = getSpanParentObjectAndPropagatedState({
10146
10160
  asyncFlush: args?.asyncFlush,
@@ -10154,7 +10168,7 @@ function startSpanAndIsLogger(args) {
10154
10168
  ) ? {
10155
10169
  spanId: parentObject.data.span_id,
10156
10170
  rootSpanId: parentObject.data.root_span_id
10157
- } : void 0;
10171
+ } : internalOptions?.useParentSpanIdsForObjectParent ? args?.parentSpanIds : void 0;
10158
10172
  const { parent: _ignoredParent, ...spanArgs } = args ?? {};
10159
10173
  const span = new SpanImpl({
10160
10174
  state,
@@ -13598,6 +13612,131 @@ function unsubscribeAll(unsubscribers) {
13598
13612
  return [];
13599
13613
  }
13600
13614
 
13615
+ // src/instrumentation/core/channel-definitions.ts
13616
+ function channel(spec) {
13617
+ return spec;
13618
+ }
13619
+ function defineChannels(pkg, channels, options) {
13620
+ const { instrumentationName } = options;
13621
+ return Object.fromEntries(
13622
+ Object.entries(channels).map(([key, spec]) => {
13623
+ const fullChannelName = `orchestrion:${pkg}:${spec.channelName}`;
13624
+ if (spec.kind === "async") {
13625
+ const asyncSpec = spec;
13626
+ const tracingChannel2 = () => isomorph_default.newTracingChannel(
13627
+ fullChannelName
13628
+ );
13629
+ const intercept2 = (interceptor) => {
13630
+ const hook = tracingChannel2();
13631
+ return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
13632
+ };
13633
+ };
13634
+ return [
13635
+ key,
13636
+ {
13637
+ ...asyncSpec,
13638
+ instrumentationName,
13639
+ intercept: intercept2,
13640
+ invoke: (target, thisArg, args, additional) => {
13641
+ const hook = tracingChannel2();
13642
+ return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
13643
+ },
13644
+ tracingChannel: tracingChannel2,
13645
+ tracePromise: (fn, context) => tracingChannel2().tracePromise(
13646
+ fn,
13647
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
13648
+ context
13649
+ )
13650
+ }
13651
+ ];
13652
+ }
13653
+ const syncSpec = spec;
13654
+ const tracingChannel = () => isomorph_default.newTracingChannel(
13655
+ fullChannelName
13656
+ );
13657
+ const intercept = (interceptor) => {
13658
+ const hook = tracingChannel();
13659
+ return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
13660
+ };
13661
+ };
13662
+ return [
13663
+ key,
13664
+ {
13665
+ ...syncSpec,
13666
+ instrumentationName,
13667
+ intercept,
13668
+ invoke: (target, thisArg, args, additional) => {
13669
+ const hook = tracingChannel();
13670
+ return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
13671
+ },
13672
+ tracingChannel,
13673
+ traceSync: (fn, context) => tracingChannel().traceSync(
13674
+ fn,
13675
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
13676
+ context
13677
+ )
13678
+ }
13679
+ ];
13680
+ })
13681
+ );
13682
+ }
13683
+
13684
+ // src/instrumentation/plugins/openai-channels.ts
13685
+ var openAIChannels = defineChannels(
13686
+ "openai",
13687
+ {
13688
+ filesCreateTraced: channel({
13689
+ channelName: "files.create-traced",
13690
+ kind: "async"
13691
+ }),
13692
+ batchesRetrieveTraced: channel({
13693
+ channelName: "batches.retrieve-traced",
13694
+ kind: "async"
13695
+ }),
13696
+ batchesCompleteTrace: channel({
13697
+ channelName: "batches.complete-trace",
13698
+ kind: "async"
13699
+ }),
13700
+ chatCompletionsCreate: channel({
13701
+ channelName: "chat.completions.create",
13702
+ kind: "async"
13703
+ }),
13704
+ embeddingsCreate: channel({
13705
+ channelName: "embeddings.create",
13706
+ kind: "async"
13707
+ }),
13708
+ betaChatCompletionsParse: channel({
13709
+ channelName: "beta.chat.completions.parse",
13710
+ kind: "async"
13711
+ }),
13712
+ betaChatCompletionsStream: channel({
13713
+ channelName: "beta.chat.completions.stream",
13714
+ kind: "sync-stream"
13715
+ }),
13716
+ moderationsCreate: channel({
13717
+ channelName: "moderations.create",
13718
+ kind: "async"
13719
+ }),
13720
+ responsesCreate: channel({
13721
+ channelName: "responses.create",
13722
+ kind: "async"
13723
+ }),
13724
+ responsesStream: channel({
13725
+ channelName: "responses.stream",
13726
+ kind: "sync-stream"
13727
+ }),
13728
+ responsesParse: channel({
13729
+ channelName: "responses.parse",
13730
+ kind: "async"
13731
+ }),
13732
+ responsesCompact: channel({
13733
+ channelName: "responses.compact",
13734
+ kind: "async"
13735
+ })
13736
+ },
13737
+ { instrumentationName: INSTRUMENTATION_NAMES.OPENAI }
13738
+ );
13739
+
13601
13740
  // src/wrappers/attachment-utils.ts
13602
13741
  function getExtensionFromMediaType(mediaType) {
13603
13742
  const extensionMap = {
@@ -13773,118 +13912,91 @@ function processInputAttachments(input) {
13773
13912
  return processNode(input);
13774
13913
  }
13775
13914
 
13776
- // src/instrumentation/core/channel-definitions.ts
13777
- function channel(spec) {
13778
- return spec;
13779
- }
13780
- function defineChannels(pkg, channels, options) {
13781
- const { instrumentationName } = options;
13782
- return Object.fromEntries(
13783
- Object.entries(channels).map(([key, spec]) => {
13784
- const fullChannelName = `orchestrion:${pkg}:${spec.channelName}`;
13785
- if (spec.kind === "async") {
13786
- const asyncSpec = spec;
13787
- const tracingChannel2 = () => isomorph_default.newTracingChannel(
13788
- fullChannelName
13789
- );
13790
- const intercept2 = (interceptor) => {
13791
- const hook = tracingChannel2();
13792
- return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
13793
- };
13794
- };
13795
- return [
13796
- key,
13797
- {
13798
- ...asyncSpec,
13799
- instrumentationName,
13800
- intercept: intercept2,
13801
- invoke: (target, thisArg, args, additional) => {
13802
- const hook = tracingChannel2();
13803
- return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
13804
- },
13805
- tracingChannel: tracingChannel2,
13806
- tracePromise: (fn, context) => tracingChannel2().tracePromise(
13807
- fn,
13808
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
13809
- context
13810
- )
13811
- }
13812
- ];
13915
+ // src/instrumentation/plugins/openai-span-data.ts
13916
+ var OPENAI_METADATA_KEYS = [
13917
+ "model",
13918
+ "temperature",
13919
+ "top_p",
13920
+ "max_tokens",
13921
+ "frequency_penalty",
13922
+ "presence_penalty",
13923
+ "stop",
13924
+ "response_format",
13925
+ "tools",
13926
+ "tool_choice",
13927
+ "parallel_tool_calls",
13928
+ "max_tool_calls"
13929
+ ];
13930
+ function batchMetadata(params) {
13931
+ const metadata = { provider: "openai" };
13932
+ for (const key of OPENAI_METADATA_KEYS) {
13933
+ try {
13934
+ const value = Reflect.get(params, key);
13935
+ if (value !== void 0) {
13936
+ metadata[key] = value;
13813
13937
  }
13814
- const syncSpec = spec;
13815
- const tracingChannel = () => isomorph_default.newTracingChannel(
13816
- fullChannelName
13817
- );
13818
- const intercept = (interceptor) => {
13819
- const hook = tracingChannel();
13820
- return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
13821
- };
13822
- };
13823
- return [
13824
- key,
13825
- {
13826
- ...syncSpec,
13827
- instrumentationName,
13828
- intercept,
13829
- invoke: (target, thisArg, args, additional) => {
13830
- const hook = tracingChannel();
13831
- return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
13832
- },
13833
- tracingChannel,
13834
- traceSync: (fn, context) => tracingChannel().traceSync(
13835
- fn,
13836
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
13837
- context
13838
- )
13839
- }
13840
- ];
13841
- })
13842
- );
13938
+ } catch {
13939
+ }
13940
+ }
13941
+ return metadata;
13942
+ }
13943
+ function extractOpenAIBatchInput(endpoint, params) {
13944
+ const input = endpoint === "/v1/chat/completions" ? params.messages : params.input;
13945
+ return {
13946
+ input: processInputAttachments(input),
13947
+ metadata: batchMetadata(params)
13948
+ };
13949
+ }
13950
+ function extractOpenAIChatInput(params) {
13951
+ const { messages, ...metadata } = params;
13952
+ return {
13953
+ input: processInputAttachments(messages),
13954
+ metadata: { ...metadata, provider: "openai" }
13955
+ };
13956
+ }
13957
+ function extractOpenAIResponsesInput(params) {
13958
+ const { input, ...metadata } = params;
13959
+ return {
13960
+ input: processInputAttachments(input),
13961
+ metadata: { ...metadata, provider: "openai" }
13962
+ };
13963
+ }
13964
+ function extractOpenAIResponsesMetadata(result) {
13965
+ if (!result) {
13966
+ return void 0;
13967
+ }
13968
+ const { output: _output, usage: _usage, ...metadata } = result;
13969
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
13970
+ }
13971
+ function processImagesInOutput(output) {
13972
+ if (Array.isArray(output)) {
13973
+ return output.map(processImagesInOutput);
13974
+ }
13975
+ if (isObject(output) && output.type === "image_generation_call" && typeof output.result === "string" && output.result) {
13976
+ const fileExtension = output.output_format || "png";
13977
+ const contentType = `image/${fileExtension}`;
13978
+ const baseFilename = typeof output.revised_prompt === "string" ? output.revised_prompt.slice(0, 50).replace(/[^a-zA-Z0-9]/g, "_") : "generated_image";
13979
+ let binaryString;
13980
+ try {
13981
+ binaryString = atob(output.result);
13982
+ } catch {
13983
+ return output;
13984
+ }
13985
+ const bytes = new Uint8Array(binaryString.length);
13986
+ for (let i = 0; i < binaryString.length; i++) {
13987
+ bytes[i] = binaryString.charCodeAt(i);
13988
+ }
13989
+ return {
13990
+ ...output,
13991
+ result: new Attachment({
13992
+ data: new Blob([bytes], { type: contentType }),
13993
+ filename: `${baseFilename}.${fileExtension}`,
13994
+ contentType
13995
+ })
13996
+ };
13997
+ }
13998
+ return output;
13843
13999
  }
13844
-
13845
- // src/instrumentation/plugins/openai-channels.ts
13846
- var openAIChannels = defineChannels(
13847
- "openai",
13848
- {
13849
- chatCompletionsCreate: channel({
13850
- channelName: "chat.completions.create",
13851
- kind: "async"
13852
- }),
13853
- embeddingsCreate: channel({
13854
- channelName: "embeddings.create",
13855
- kind: "async"
13856
- }),
13857
- betaChatCompletionsParse: channel({
13858
- channelName: "beta.chat.completions.parse",
13859
- kind: "async"
13860
- }),
13861
- betaChatCompletionsStream: channel({
13862
- channelName: "beta.chat.completions.stream",
13863
- kind: "sync-stream"
13864
- }),
13865
- moderationsCreate: channel({
13866
- channelName: "moderations.create",
13867
- kind: "async"
13868
- }),
13869
- responsesCreate: channel({
13870
- channelName: "responses.create",
13871
- kind: "async"
13872
- }),
13873
- responsesStream: channel({
13874
- channelName: "responses.stream",
13875
- kind: "sync-stream"
13876
- }),
13877
- responsesParse: channel({
13878
- channelName: "responses.parse",
13879
- kind: "async"
13880
- }),
13881
- responsesCompact: channel({
13882
- channelName: "responses.compact",
13883
- kind: "async"
13884
- })
13885
- },
13886
- { instrumentationName: INSTRUMENTATION_NAMES.OPENAI }
13887
- );
13888
14000
 
13889
14001
  // src/openai-utils.ts
13890
14002
  var BRAINTRUST_CACHED_STREAM_METRIC = "__braintrust_cached_metric";
@@ -13941,23 +14053,573 @@ function getCachedMetricFromHeaders(headers) {
13941
14053
  return parseCachedHeader(headers.get(LEGACY_CACHED_HEADER));
13942
14054
  }
13943
14055
 
14056
+ // src/instrumentation/plugins/openai-batch-instrumentation.ts
14057
+ var SUPPORTED_ENDPOINTS = /* @__PURE__ */ new Set(["/v1/chat/completions", "/v1/responses"]);
14058
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
14059
+ "completed",
14060
+ "failed",
14061
+ "expired",
14062
+ "cancelled"
14063
+ ]);
14064
+ var pendingBatchTraces = /* @__PURE__ */ new Map();
14065
+ function read(value, key) {
14066
+ if (!isObject(value)) {
14067
+ return void 0;
14068
+ }
14069
+ try {
14070
+ return Reflect.get(value, key);
14071
+ } catch {
14072
+ return void 0;
14073
+ }
14074
+ }
14075
+ function isBatchRecordIterable(value) {
14076
+ if (value === null || typeof value !== "object" && typeof value !== "function") {
14077
+ return false;
14078
+ }
14079
+ return typeof read(value, Symbol.iterator) === "function" || typeof read(value, Symbol.asyncIterator) === "function";
14080
+ }
14081
+ function validCustomId(value) {
14082
+ return typeof value === "string" && value.length > 0 && value.length <= 64;
14083
+ }
14084
+ function logBatchInstrumentationError(context, error) {
14085
+ debugLogger.debug(`OpenAI Batch instrumentation ${context}:`, error);
14086
+ }
14087
+ async function exportParent(parent) {
14088
+ if ("toStr" in parent && typeof parent.toStr === "function") {
14089
+ return parent.toStr();
14090
+ }
14091
+ if ("export" in parent && typeof parent.export === "function") {
14092
+ return await parent.export();
14093
+ }
14094
+ return void 0;
14095
+ }
14096
+ async function deterministicDigest(namespace, ...parts) {
14097
+ const encoded = new TextEncoder().encode(
14098
+ [namespace, ...parts].map((part) => `${part.length}:${part}`).join("\0")
14099
+ );
14100
+ return new Uint8Array(
14101
+ await globalThis.crypto.subtle.digest("SHA-256", encoded)
14102
+ );
14103
+ }
14104
+ function digestHex(bytes, length) {
14105
+ return Array.from(bytes.slice(0, length)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
14106
+ }
14107
+ function digestUuid(bytes) {
14108
+ const hex = digestHex(bytes, 16);
14109
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
14110
+ }
14111
+ async function batchSpanIds(inputFileId) {
14112
+ const [row, span, root] = await Promise.all([
14113
+ deterministicDigest("openai:batch:row", inputFileId),
14114
+ deterministicDigest("openai:batch:span", inputFileId),
14115
+ deterministicDigest("openai:batch:root", inputFileId)
14116
+ ]);
14117
+ return {
14118
+ rowId: digestUuid(row),
14119
+ spanId: digestHex(span, 8),
14120
+ rootSpanId: digestHex(root, 16)
14121
+ };
14122
+ }
14123
+ async function childSpanIds(inputFileId, customId) {
14124
+ const [row, span] = await Promise.all([
14125
+ deterministicDigest("openai:batch:child:row", inputFileId, customId),
14126
+ deterministicDigest("openai:batch:child:span", inputFileId, customId)
14127
+ ]);
14128
+ return { rowId: digestUuid(row), spanId: digestHex(span, 8) };
14129
+ }
14130
+ async function startBatchSpan(context) {
14131
+ const ids = await batchSpanIds(context.inputFileId);
14132
+ const parent = SpanComponentsV4.fromStr(context.parent);
14133
+ const hasParentSpan = Boolean(
14134
+ parent.data.row_id && parent.data.span_id && parent.data.root_span_id
14135
+ );
14136
+ return withCurrent(
14137
+ NOOP_SPAN,
14138
+ () => _internalStartSpanWithInitialMergeAndParentSpanIds(
14139
+ withSpanInstrumentationName(
14140
+ {
14141
+ name: "openai.batch",
14142
+ type: "task" /* TASK */,
14143
+ parent: context.parent,
14144
+ ...!hasParentSpan ? {
14145
+ parentSpanIds: {
14146
+ parentSpanIds: [],
14147
+ rootSpanId: ids.rootSpanId
14148
+ }
14149
+ } : {},
14150
+ spanId: ids.spanId,
14151
+ startTime: context.taskStartTime,
14152
+ event: {
14153
+ id: ids.rowId,
14154
+ metadata: {
14155
+ endpoint: context.endpoint,
14156
+ input_file_id: context.inputFileId,
14157
+ provider: "openai"
14158
+ }
14159
+ }
14160
+ },
14161
+ INSTRUMENTATION_NAMES.OPENAI
14162
+ )
14163
+ )
14164
+ );
14165
+ }
14166
+ async function startBatchChild(context, taskParent, input) {
14167
+ const ids = await childSpanIds(context.inputFileId, input.customId);
14168
+ return withCurrent(
14169
+ NOOP_SPAN,
14170
+ () => _internalStartSpanWithInitialMerge(
14171
+ withSpanInstrumentationName(
14172
+ {
14173
+ name: context.endpoint === "/v1/chat/completions" ? "Chat Completion" : "openai.responses.create",
14174
+ type: "llm" /* LLM */,
14175
+ parent: taskParent,
14176
+ spanId: ids.spanId,
14177
+ startTime: context.childStartTime,
14178
+ event: {
14179
+ id: ids.rowId,
14180
+ ...input.spanData?.input !== void 0 ? { input: input.spanData.input } : {},
14181
+ metadata: {
14182
+ ...input.spanData?.metadata,
14183
+ custom_id: input.customId,
14184
+ provider: "openai"
14185
+ }
14186
+ }
14187
+ },
14188
+ INSTRUMENTATION_NAMES.OPENAI
14189
+ )
14190
+ )
14191
+ );
14192
+ }
14193
+ async function* jsonlRecords(file, onIssue = () => {
14194
+ }) {
14195
+ const resolvedFile = await file;
14196
+ if (typeof resolvedFile === "string") {
14197
+ for (const line of resolvedFile.split("\n")) {
14198
+ if (!line.trim()) {
14199
+ continue;
14200
+ }
14201
+ try {
14202
+ yield JSON.parse(line.replace(/\r$/, ""));
14203
+ } catch (error) {
14204
+ logBatchInstrumentationError("skipped malformed JSONL", error);
14205
+ onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
14206
+ }
14207
+ }
14208
+ return;
14209
+ }
14210
+ const body = read(resolvedFile, "body");
14211
+ const getReader = read(body, "getReader");
14212
+ if (isObject(body) && typeof getReader === "function") {
14213
+ let reader;
14214
+ try {
14215
+ reader = Reflect.apply(getReader, body, []);
14216
+ const decoder2 = new TextDecoder();
14217
+ let pending = "";
14218
+ while (true) {
14219
+ const readChunk = read(reader, "read");
14220
+ if (typeof readChunk !== "function") {
14221
+ throw new Error("Response body stream has no read method");
14222
+ }
14223
+ const chunk = await Reflect.apply(readChunk, reader, []);
14224
+ if (!isObject(chunk)) {
14225
+ throw new Error("Response body stream returned an invalid chunk");
14226
+ }
14227
+ if (chunk.done === true) {
14228
+ pending += decoder2.decode();
14229
+ break;
14230
+ }
14231
+ if (!(chunk.value instanceof Uint8Array)) {
14232
+ throw new Error("Response body stream returned a non-byte chunk");
14233
+ }
14234
+ pending += decoder2.decode(chunk.value, { stream: true });
14235
+ let newline = pending.indexOf("\n");
14236
+ while (newline !== -1) {
14237
+ const line = pending.slice(0, newline).replace(/\r$/, "");
14238
+ pending = pending.slice(newline + 1);
14239
+ if (line.trim()) {
14240
+ try {
14241
+ yield JSON.parse(line);
14242
+ } catch (error) {
14243
+ logBatchInstrumentationError("skipped malformed JSONL", error);
14244
+ onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
14245
+ }
14246
+ }
14247
+ newline = pending.indexOf("\n");
14248
+ }
14249
+ }
14250
+ if (pending.trim()) {
14251
+ try {
14252
+ yield JSON.parse(pending.replace(/\r$/, ""));
14253
+ } catch (error) {
14254
+ logBatchInstrumentationError("skipped malformed JSONL", error);
14255
+ onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
14256
+ }
14257
+ }
14258
+ } catch (error) {
14259
+ logBatchInstrumentationError("could not read JSONL response body", error);
14260
+ onIssue(new Error("OpenAI Batch response body could not be read"));
14261
+ } finally {
14262
+ const releaseLock = read(reader, "releaseLock");
14263
+ if (typeof releaseLock === "function") {
14264
+ try {
14265
+ Reflect.apply(releaseLock, reader, []);
14266
+ } catch (error) {
14267
+ logBatchInstrumentationError(
14268
+ "could not release stream reader",
14269
+ error
14270
+ );
14271
+ }
14272
+ }
14273
+ }
14274
+ return;
14275
+ }
14276
+ if (isBatchRecordIterable(resolvedFile)) {
14277
+ for await (const record of resolvedFile) {
14278
+ yield record;
14279
+ }
14280
+ return;
14281
+ }
14282
+ logBatchInstrumentationError("skipped invalid JSONL source", resolvedFile);
14283
+ onIssue(new Error("OpenAI Batch file source is invalid"));
14284
+ }
14285
+ async function readBatchInputs(file) {
14286
+ const inputs = /* @__PURE__ */ new Map();
14287
+ const issues = [];
14288
+ let endpoint;
14289
+ try {
14290
+ for await (const value of jsonlRecords(
14291
+ file,
14292
+ (issue) => issues.push(issue)
14293
+ )) {
14294
+ const customId = read(value, "custom_id");
14295
+ const url = read(value, "url");
14296
+ const body = read(value, "body");
14297
+ if (!validCustomId(customId) || inputs.has(customId) || read(value, "method") !== "POST" || typeof url !== "string" || !SUPPORTED_ENDPOINTS.has(url) || endpoint !== void 0 && endpoint !== url || !isObject(body)) {
14298
+ issues.push(new Error("OpenAI Batch input contains an invalid record"));
14299
+ continue;
14300
+ }
14301
+ endpoint = url;
14302
+ let spanData;
14303
+ try {
14304
+ spanData = extractOpenAIBatchInput(url, body);
14305
+ } catch (error) {
14306
+ logBatchInstrumentationError("could not extract batch input", error);
14307
+ }
14308
+ inputs.set(customId, { customId, spanData });
14309
+ }
14310
+ } catch (error) {
14311
+ logBatchInstrumentationError("could not process input file", error);
14312
+ issues.push(new Error("OpenAI Batch input file could not be processed"));
14313
+ }
14314
+ if (!endpoint || inputs.size === 0) {
14315
+ issues.push(new Error("OpenAI Batch input contains no supported records"));
14316
+ }
14317
+ return { endpoint, inputs, issues };
14318
+ }
14319
+ async function writePendingSpans(trace, endTime) {
14320
+ const task = await startBatchSpan(trace.context);
14321
+ const taskParent = await task.export();
14322
+ for (const input of trace.inputs.values()) {
14323
+ try {
14324
+ const child = await startBatchChild(trace.context, taskParent, input);
14325
+ if (endTime !== void 0) {
14326
+ child.end({ endTime });
14327
+ }
14328
+ } catch (error) {
14329
+ logBatchInstrumentationError("could not write batch request span", error);
14330
+ }
14331
+ }
14332
+ if (endTime !== void 0) {
14333
+ if (trace.status && trace.status !== "completed") {
14334
+ task.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
14335
+ }
14336
+ task.end({ endTime });
14337
+ }
14338
+ }
14339
+ var interceptOpenAIFilesCreateTraced = async (target, thisArg, args) => {
14340
+ const startTime = getCurrentUnixTimestamp();
14341
+ const inputPromise = readBatchInputs(args[0].inputFileContent);
14342
+ const parentPromise = exportParent(args[0].parent);
14343
+ const file = await Reflect.apply(target, thisArg, args);
14344
+ try {
14345
+ const inputFileId = read(file, "id");
14346
+ const [inputData, exportedParent2] = await Promise.all([
14347
+ inputPromise,
14348
+ parentPromise
14349
+ ]);
14350
+ if (typeof inputFileId !== "string" || !exportedParent2 || !inputData.endpoint || inputData.issues.length > 0) {
14351
+ if (inputData.issues[0]) {
14352
+ logBatchInstrumentationError(
14353
+ "skipped invalid input file",
14354
+ inputData.issues[0]
14355
+ );
14356
+ }
14357
+ return file;
14358
+ }
14359
+ const trace = {
14360
+ context: {
14361
+ inputFileId,
14362
+ endpoint: inputData.endpoint,
14363
+ parent: exportedParent2,
14364
+ taskStartTime: startTime,
14365
+ childStartTime: startTime
14366
+ },
14367
+ inputs: inputData.inputs
14368
+ };
14369
+ pendingBatchTraces.set(inputFileId, trace);
14370
+ await writePendingSpans(trace);
14371
+ } catch (error) {
14372
+ logBatchInstrumentationError("could not start batch spans", error);
14373
+ }
14374
+ return file;
14375
+ };
14376
+ function validTimestamp(value) {
14377
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
14378
+ }
14379
+ function terminalEndTime(batch, startTime) {
14380
+ let timestamp;
14381
+ switch (batch.status) {
14382
+ case "completed":
14383
+ timestamp = batch.completed_at;
14384
+ break;
14385
+ case "failed":
14386
+ timestamp = batch.failed_at;
14387
+ break;
14388
+ case "expired":
14389
+ timestamp = batch.expired_at;
14390
+ break;
14391
+ default:
14392
+ timestamp = batch.cancelled_at;
14393
+ }
14394
+ return validTimestamp(timestamp) ? Math.max(timestamp, startTime) : Math.max(getCurrentUnixTimestamp(), startTime);
14395
+ }
14396
+ async function updateBatchTimestamps(batch) {
14397
+ const trace = pendingBatchTraces.get(batch.input_file_id);
14398
+ if (!trace || trace.context.endpoint !== batch.endpoint) {
14399
+ return;
14400
+ }
14401
+ if (validTimestamp(batch.created_at)) {
14402
+ trace.context.taskStartTime = batch.created_at;
14403
+ }
14404
+ trace.context.childStartTime = validTimestamp(batch.in_progress_at) ? Math.max(batch.in_progress_at, trace.context.taskStartTime) : trace.context.taskStartTime;
14405
+ trace.status = batch.status;
14406
+ if (TERMINAL_STATUSES.has(batch.status)) {
14407
+ trace.endTime = terminalEndTime(batch, trace.context.childStartTime);
14408
+ }
14409
+ await writePendingSpans(trace, trace.endTime);
14410
+ }
14411
+ var interceptOpenAIBatchesRetrieveTraced = (target, thisArg, args) => Promise.resolve(Reflect.apply(target, thisArg, args)).then(async (batch) => {
14412
+ try {
14413
+ await updateBatchTimestamps(batch);
14414
+ } catch (error) {
14415
+ logBatchInstrumentationError("could not update batch timestamps", error);
14416
+ }
14417
+ return batch;
14418
+ });
14419
+ function errorFromResult(result) {
14420
+ const error = read(result.value, "error");
14421
+ if (isObject(error)) {
14422
+ const message = read(error, "message");
14423
+ return new Error(
14424
+ typeof message === "string" ? message : "OpenAI Batch request failed"
14425
+ );
14426
+ }
14427
+ const response = read(result.value, "response");
14428
+ const statusCode = read(response, "status_code");
14429
+ if (result.source === "error" || typeof statusCode === "number" && (statusCode < 200 || statusCode >= 300)) {
14430
+ const message = read(read(read(response, "body"), "error"), "message");
14431
+ return new Error(
14432
+ typeof message === "string" ? message : "OpenAI Batch request failed"
14433
+ );
14434
+ }
14435
+ return void 0;
14436
+ }
14437
+ async function completeBatchResult(context, taskParent, endTime, input, result) {
14438
+ const child = await startBatchChild(context, taskParent, input);
14439
+ try {
14440
+ const resultError = errorFromResult(result);
14441
+ const responseBody = read(read(result.value, "response"), "body");
14442
+ if (resultError) {
14443
+ child.log({ error: resultError });
14444
+ } else if (isObject(responseBody)) {
14445
+ const model = read(responseBody, "model");
14446
+ child.log({
14447
+ output: context.endpoint === "/v1/chat/completions" ? read(responseBody, "choices") : processImagesInOutput(read(responseBody, "output")),
14448
+ ...typeof model === "string" ? { metadata: { model } } : {},
14449
+ metrics: parseMetricsFromUsage(read(responseBody, "usage"))
14450
+ });
14451
+ } else {
14452
+ child.log({ error: new Error("OpenAI Batch response body is missing") });
14453
+ }
14454
+ } catch (error) {
14455
+ child.log({ error });
14456
+ } finally {
14457
+ child.end({ endTime });
14458
+ }
14459
+ }
14460
+ async function completeResultFile({
14461
+ context,
14462
+ endTime,
14463
+ file,
14464
+ inputs,
14465
+ issues,
14466
+ seen,
14467
+ source,
14468
+ taskParent
14469
+ }) {
14470
+ if (file === void 0) {
14471
+ return;
14472
+ }
14473
+ for await (const value of jsonlRecords(file, (issue) => issues.push(issue))) {
14474
+ const customId = read(value, "custom_id");
14475
+ if (!validCustomId(customId) || !isObject(value)) {
14476
+ issues.push(
14477
+ new Error("OpenAI Batch result is missing a valid custom_id")
14478
+ );
14479
+ continue;
14480
+ }
14481
+ if (seen.has(customId)) {
14482
+ issues.push(new Error("OpenAI Batch result contains a duplicate"));
14483
+ continue;
14484
+ }
14485
+ const input = inputs.get(customId);
14486
+ if (!input) {
14487
+ issues.push(
14488
+ new Error("OpenAI Batch result does not match an input record")
14489
+ );
14490
+ continue;
14491
+ }
14492
+ seen.add(customId);
14493
+ await completeBatchResult(context, taskParent, endTime, input, {
14494
+ value,
14495
+ source
14496
+ });
14497
+ }
14498
+ }
14499
+ function sameInputs(first, second) {
14500
+ return first.size === second.size && [...first.keys()].every((customId) => second.has(customId));
14501
+ }
14502
+ async function contextForCompletion(inputFileId, inputData) {
14503
+ if (!inputData.endpoint || inputData.issues.length > 0) {
14504
+ return void 0;
14505
+ }
14506
+ const existing = pendingBatchTraces.get(inputFileId);
14507
+ if (existing) {
14508
+ if (existing.context.endpoint !== inputData.endpoint || !sameInputs(existing.inputs, inputData.inputs)) {
14509
+ return void 0;
14510
+ }
14511
+ return { ...existing, inputs: inputData.inputs };
14512
+ }
14513
+ const parent = await exportParent(getSpanParentObject());
14514
+ if (!parent) {
14515
+ return void 0;
14516
+ }
14517
+ const startTime = getCurrentUnixTimestamp();
14518
+ return {
14519
+ context: {
14520
+ inputFileId,
14521
+ endpoint: inputData.endpoint,
14522
+ parent,
14523
+ taskStartTime: startTime,
14524
+ childStartTime: startTime
14525
+ },
14526
+ inputs: inputData.inputs
14527
+ };
14528
+ }
14529
+ async function completeBatch(args) {
14530
+ const inputData = await readBatchInputs(args.inputFileContent);
14531
+ const trace = await contextForCompletion(args.inputFileId, inputData);
14532
+ if (!trace) {
14533
+ logBatchInstrumentationError(
14534
+ "left batch spans pending",
14535
+ inputData.issues[0] ?? new Error("OpenAI Batch input does not match")
14536
+ );
14537
+ return;
14538
+ }
14539
+ const endTime = trace.endTime ?? getCurrentUnixTimestamp();
14540
+ const task = await startBatchSpan(trace.context);
14541
+ const taskParent = await task.export();
14542
+ const issues = [];
14543
+ const seen = /* @__PURE__ */ new Set();
14544
+ await Promise.all([
14545
+ completeResultFile({
14546
+ context: trace.context,
14547
+ endTime,
14548
+ file: args.outputFileContent,
14549
+ inputs: trace.inputs,
14550
+ issues,
14551
+ seen,
14552
+ source: "output",
14553
+ taskParent
14554
+ }),
14555
+ completeResultFile({
14556
+ context: trace.context,
14557
+ endTime,
14558
+ file: args.errorFileContent,
14559
+ inputs: trace.inputs,
14560
+ issues,
14561
+ seen,
14562
+ source: "error",
14563
+ taskParent
14564
+ })
14565
+ ]);
14566
+ if (issues.length > 0) {
14567
+ logBatchInstrumentationError("left batch spans pending", issues[0]);
14568
+ return;
14569
+ }
14570
+ const missing = [...trace.inputs.values()].filter(
14571
+ ({ customId }) => !seen.has(customId)
14572
+ );
14573
+ if (!trace.status || trace.status === "completed") {
14574
+ if (missing.length > 0) {
14575
+ logBatchInstrumentationError(
14576
+ "left batch spans pending",
14577
+ new Error("OpenAI Batch result files are incomplete")
14578
+ );
14579
+ return;
14580
+ }
14581
+ } else {
14582
+ for (const input of missing) {
14583
+ const child = await startBatchChild(trace.context, taskParent, input);
14584
+ child.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
14585
+ child.end({ endTime });
14586
+ }
14587
+ task.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
14588
+ }
14589
+ task.end({ endTime });
14590
+ pendingBatchTraces.delete(args.inputFileId);
14591
+ }
14592
+ var interceptOpenAIBatchTraceComplete = (target, thisArg, args) => Promise.resolve(Reflect.apply(target, thisArg, args)).then(async (result) => {
14593
+ try {
14594
+ await completeBatch(args[0]);
14595
+ } catch (error) {
14596
+ logBatchInstrumentationError("could not complete batch", error);
14597
+ }
14598
+ return result;
14599
+ });
14600
+
13944
14601
  // src/instrumentation/plugins/openai-plugin.ts
13945
14602
  var OpenAIPlugin = class extends BasePlugin {
13946
14603
  constructor() {
13947
14604
  super();
13948
14605
  }
13949
14606
  onEnable() {
14607
+ this.unsubscribers.push(
14608
+ openAIChannels.filesCreateTraced.intercept(
14609
+ interceptOpenAIFilesCreateTraced
14610
+ ),
14611
+ openAIChannels.batchesRetrieveTraced.intercept(
14612
+ interceptOpenAIBatchesRetrieveTraced
14613
+ ),
14614
+ openAIChannels.batchesCompleteTrace.intercept(
14615
+ interceptOpenAIBatchTraceComplete
14616
+ )
14617
+ );
13950
14618
  this.unsubscribers.push(
13951
14619
  traceStreamingChannel(openAIChannels.chatCompletionsCreate, {
13952
14620
  name: "Chat Completion",
13953
14621
  type: "llm" /* LLM */,
13954
- extractInput: ([params]) => {
13955
- const { messages, ...metadata } = params;
13956
- return {
13957
- input: processInputAttachments(messages),
13958
- metadata: { ...metadata, provider: "openai" }
13959
- };
13960
- },
14622
+ extractInput: ([params]) => extractOpenAIChatInput(params),
13961
14623
  extractOutput: (result) => {
13962
14624
  return result?.choices;
13963
14625
  },
@@ -14003,13 +14665,7 @@ var OpenAIPlugin = class extends BasePlugin {
14003
14665
  traceStreamingChannel(openAIChannels.betaChatCompletionsParse, {
14004
14666
  name: "Chat Completion",
14005
14667
  type: "llm" /* LLM */,
14006
- extractInput: ([params]) => {
14007
- const { messages, ...metadata } = params;
14008
- return {
14009
- input: processInputAttachments(messages),
14010
- metadata: { ...metadata, provider: "openai" }
14011
- };
14012
- },
14668
+ extractInput: ([params]) => extractOpenAIChatInput(params),
14013
14669
  extractOutput: (result) => {
14014
14670
  return result?.choices;
14015
14671
  },
@@ -14031,13 +14687,7 @@ var OpenAIPlugin = class extends BasePlugin {
14031
14687
  traceSyncStreamChannel(openAIChannels.betaChatCompletionsStream, {
14032
14688
  name: "Chat Completion",
14033
14689
  type: "llm" /* LLM */,
14034
- extractInput: ([params]) => {
14035
- const { messages, ...metadata } = params;
14036
- return {
14037
- input: processInputAttachments(messages),
14038
- metadata: { ...metadata, provider: "openai" }
14039
- };
14040
- }
14690
+ extractInput: ([params]) => extractOpenAIChatInput(params)
14041
14691
  })
14042
14692
  );
14043
14693
  this.unsubscribers.push(
@@ -14067,23 +14717,11 @@ var OpenAIPlugin = class extends BasePlugin {
14067
14717
  traceStreamingChannel(openAIChannels.responsesCreate, {
14068
14718
  name: "openai.responses.create",
14069
14719
  type: "llm" /* LLM */,
14070
- extractInput: ([params]) => {
14071
- const { input, ...metadata } = params;
14072
- return {
14073
- input: processInputAttachments(input),
14074
- metadata: { ...metadata, provider: "openai" }
14075
- };
14076
- },
14720
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
14077
14721
  extractOutput: (result) => {
14078
14722
  return processImagesInOutput(result?.output);
14079
14723
  },
14080
- extractMetadata: (result) => {
14081
- if (!result) {
14082
- return void 0;
14083
- }
14084
- const { output: _output, usage: _usage, ...metadata } = result;
14085
- return Object.keys(metadata).length > 0 ? metadata : void 0;
14086
- },
14724
+ extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
14087
14725
  extractMetrics: (result, startTime, endEvent) => {
14088
14726
  const metrics = withCachedMetric(
14089
14727
  parseMetricsFromUsage(result?.usage),
@@ -14102,13 +14740,7 @@ var OpenAIPlugin = class extends BasePlugin {
14102
14740
  traceSyncStreamChannel(openAIChannels.responsesStream, {
14103
14741
  name: "openai.responses.create",
14104
14742
  type: "llm" /* LLM */,
14105
- extractInput: ([params]) => {
14106
- const { input, ...metadata } = params;
14107
- return {
14108
- input: processInputAttachments(input),
14109
- metadata: { ...metadata, provider: "openai" }
14110
- };
14111
- },
14743
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
14112
14744
  extractFromEvent: (event) => {
14113
14745
  if (event.type !== "response.completed" || !event.response) {
14114
14746
  return {};
@@ -14131,23 +14763,11 @@ var OpenAIPlugin = class extends BasePlugin {
14131
14763
  traceStreamingChannel(openAIChannels.responsesParse, {
14132
14764
  name: "openai.responses.parse",
14133
14765
  type: "llm" /* LLM */,
14134
- extractInput: ([params]) => {
14135
- const { input, ...metadata } = params;
14136
- return {
14137
- input: processInputAttachments(input),
14138
- metadata: { ...metadata, provider: "openai" }
14139
- };
14140
- },
14766
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
14141
14767
  extractOutput: (result) => {
14142
14768
  return processImagesInOutput(result?.output);
14143
14769
  },
14144
- extractMetadata: (result) => {
14145
- if (!result) {
14146
- return void 0;
14147
- }
14148
- const { output: _output, usage: _usage, ...metadata } = result;
14149
- return Object.keys(metadata).length > 0 ? metadata : void 0;
14150
- },
14770
+ extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
14151
14771
  extractMetrics: (result, startTime, endEvent) => {
14152
14772
  const metrics = withCachedMetric(
14153
14773
  parseMetricsFromUsage(result?.usage),
@@ -14166,23 +14786,11 @@ var OpenAIPlugin = class extends BasePlugin {
14166
14786
  traceAsyncChannel(openAIChannels.responsesCompact, {
14167
14787
  name: "openai.responses.compact",
14168
14788
  type: "llm" /* LLM */,
14169
- extractInput: ([params]) => {
14170
- const { input, ...metadata } = params;
14171
- return {
14172
- input: processInputAttachments(input),
14173
- metadata: { ...metadata, provider: "openai" }
14174
- };
14175
- },
14789
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
14176
14790
  extractOutput: (result) => {
14177
14791
  return processImagesInOutput(result?.output);
14178
14792
  },
14179
- extractMetadata: (result) => {
14180
- if (!result) {
14181
- return void 0;
14182
- }
14183
- const { output: _output, usage: _usage, ...metadata } = result;
14184
- return Object.keys(metadata).length > 0 ? metadata : void 0;
14185
- },
14793
+ extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
14186
14794
  extractMetrics: (result, startTime, endEvent) => {
14187
14795
  const metrics = withCachedMetric(
14188
14796
  parseMetricsFromUsage(result?.usage),
@@ -14238,35 +14846,6 @@ function withCachedMetric(metrics, result, endEvent) {
14238
14846
  cached
14239
14847
  };
14240
14848
  }
14241
- function processImagesInOutput(output) {
14242
- if (Array.isArray(output)) {
14243
- return output.map(processImagesInOutput);
14244
- }
14245
- if (isObject(output)) {
14246
- if (output.type === "image_generation_call" && output.result && typeof output.result === "string") {
14247
- const fileExtension = output.output_format || "png";
14248
- const contentType = `image/${fileExtension}`;
14249
- const baseFilename = output.revised_prompt && typeof output.revised_prompt === "string" ? output.revised_prompt.slice(0, 50).replace(/[^a-zA-Z0-9]/g, "_") : "generated_image";
14250
- const filename = `${baseFilename}.${fileExtension}`;
14251
- const binaryString = atob(output.result);
14252
- const bytes = new Uint8Array(binaryString.length);
14253
- for (let i = 0; i < binaryString.length; i++) {
14254
- bytes[i] = binaryString.charCodeAt(i);
14255
- }
14256
- const blob = new Blob([bytes], { type: contentType });
14257
- const attachment = new Attachment({
14258
- data: blob,
14259
- filename,
14260
- contentType
14261
- });
14262
- return {
14263
- ...output,
14264
- result: attachment
14265
- };
14266
- }
14267
- }
14268
- return output;
14269
- }
14270
14849
  function mergeLogprobTokens(existing, incoming) {
14271
14850
  if (incoming === void 0) {
14272
14851
  return existing;
@@ -19423,6 +20002,12 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiS
19423
20002
  if (!activeEntry) {
19424
20003
  return result;
19425
20004
  }
20005
+ const executionOptions = args[1];
20006
+ const toolCallId = isObject(executionOptions) ? executionOptions.toolCallId : void 0;
20007
+ const toolInput = {
20008
+ input: serializeToolExecutionInput(args),
20009
+ ...typeof toolCallId === "string" ? { metadata: { toolCallId } } : {}
20010
+ };
19426
20011
  if (isAsyncGenerator(result)) {
19427
20012
  return (async function* () {
19428
20013
  const span = activeEntry.parentSpan.startSpan(
@@ -19436,7 +20021,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiS
19436
20021
  INSTRUMENTATION_NAMES.AI_SDK
19437
20022
  )
19438
20023
  );
19439
- span.log({ input: serializeToolExecutionInput(args) });
20024
+ span.log(toolInput);
19440
20025
  try {
19441
20026
  let lastValue;
19442
20027
  for await (const value of result) {
@@ -19454,7 +20039,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiS
19454
20039
  }
19455
20040
  return activeEntry.parentSpan.traced(
19456
20041
  async (span) => {
19457
- span.log({ input: serializeToolExecutionInput(args) });
20042
+ span.log(toolInput);
19458
20043
  const awaitedResult = await result;
19459
20044
  span.log({ output: awaitedResult });
19460
20045
  return awaitedResult;
@@ -33576,14 +34161,17 @@ function getMetricsFromResponse(response) {
33576
34161
  if (!isRecord(usageMetadata)) {
33577
34162
  continue;
33578
34163
  }
33579
- const inputTokenDetails = usageMetadata.input_token_details;
34164
+ const inputTokenDetails = isRecord(usageMetadata.input_token_details) ? usageMetadata.input_token_details : {};
33580
34165
  const outputTokenDetails = usageMetadata.output_token_details;
33581
34166
  return normalizeTokenMetrics({
33582
34167
  total_tokens: usageMetadata.total_tokens,
33583
34168
  prompt_tokens: usageMetadata.input_tokens,
33584
34169
  completion_tokens: usageMetadata.output_tokens,
33585
- prompt_cache_creation_tokens: isRecord(inputTokenDetails) ? inputTokenDetails.cache_creation : void 0,
33586
- prompt_cached_tokens: isRecord(inputTokenDetails) ? inputTokenDetails.cache_read : void 0,
34170
+ // Prefer TTL-specific cache writes over the aggregate when available.
34171
+ prompt_cache_creation_tokens: inputTokenDetails.ephemeral_5m_input_tokens == null && inputTokenDetails.ephemeral_1h_input_tokens == null ? inputTokenDetails.cache_creation : void 0,
34172
+ prompt_cache_creation_5m_tokens: inputTokenDetails.ephemeral_5m_input_tokens,
34173
+ prompt_cache_creation_1h_tokens: inputTokenDetails.ephemeral_1h_input_tokens,
34174
+ prompt_cached_tokens: inputTokenDetails.cache_read,
33587
34175
  completion_reasoning_tokens: isRecord(outputTokenDetails) ? outputTokenDetails.reasoning : void 0
33588
34176
  });
33589
34177
  }
@@ -34299,7 +34887,7 @@ function extractSession(event) {
34299
34887
  return isObject(candidate) && typeof candidate.prompt === "function" ? candidate : void 0;
34300
34888
  }
34301
34889
  function isPiAgent(value) {
34302
- return isObject(value) && typeof value.streamFn === "function" && typeof value.subscribe === "function";
34890
+ return isObject(value) && (typeof value.streamFunction === "function" || typeof value.streamFn === "function") && typeof value.subscribe === "function";
34303
34891
  }
34304
34892
  function promptContextStore() {
34305
34893
  piPromptContextStore ??= isomorph_default.newAsyncLocalStorage();
@@ -34309,17 +34897,21 @@ function currentPiPromptState() {
34309
34897
  return promptContextStore().getStore();
34310
34898
  }
34311
34899
  function installPiAgentInstrumentation(agent) {
34900
+ const property = typeof agent.streamFunction === "function" ? "streamFunction" : "streamFn";
34901
+ const streamFunction = agent[property];
34312
34902
  const existing = piAgentPatchStates.get(agent);
34313
- if (!existing || agent.streamFn !== existing.wrappedStreamFn) {
34903
+ if (streamFunction && (!existing || existing.property !== property || streamFunction !== existing.wrappedStreamFunction)) {
34314
34904
  const patchState = {
34315
- originalStreamFn: agent.streamFn,
34316
- wrappedStreamFn: agent.streamFn
34905
+ originalStreamFunction: streamFunction,
34906
+ property,
34907
+ wrappedStreamFunction: streamFunction
34317
34908
  };
34318
- patchState.wrappedStreamFn = makeInstrumentedStreamFn(
34909
+ patchState.wrappedStreamFunction = makeInstrumentedStreamFunction(
34319
34910
  agent,
34320
- patchState.originalStreamFn
34911
+ patchState.originalStreamFunction,
34912
+ property
34321
34913
  );
34322
- agent.streamFn = patchState.wrappedStreamFn;
34914
+ agent[property] = patchState.wrappedStreamFunction;
34323
34915
  piAgentPatchStates.set(agent, patchState);
34324
34916
  }
34325
34917
  if (piAgentEventSubscriptions.has(agent)) {
@@ -34344,15 +34936,21 @@ function installPiAgentInstrumentation(agent) {
34344
34936
  logInstrumentationError4("Pi Coding Agent event subscription", error);
34345
34937
  }
34346
34938
  }
34347
- function makeInstrumentedStreamFn(agent, originalStreamFn) {
34348
- return async function instrumentedPiStreamFn(model, context, options) {
34349
- const invokeOriginal = () => Reflect.apply(originalStreamFn, this, [model, context, options]);
34939
+ function makeInstrumentedStreamFunction(agent, originalStreamFunction, property) {
34940
+ return async function instrumentedPiStreamFunction(model, context, options) {
34941
+ const invokeOriginal = () => Reflect.apply(originalStreamFunction, this, [model, context, options]);
34350
34942
  const state = currentPiPromptState();
34351
34943
  if (!state || state.agent !== agent || state.finalized) {
34352
34944
  return invokeOriginal();
34353
34945
  }
34354
34946
  wrapPiToolExecutors(context.tools);
34355
- const llmState = await startPiLlmSpan(state, model, context, options);
34947
+ const llmState = await startPiLlmSpan(
34948
+ state,
34949
+ model,
34950
+ context,
34951
+ property,
34952
+ options
34953
+ );
34356
34954
  try {
34357
34955
  const stream = await runWithAutoInstrumentationSuppressed(invokeOriginal);
34358
34956
  return patchAssistantMessageStream(stream, state, llmState);
@@ -34389,12 +34987,12 @@ function wrapPiToolExecutors(tools) {
34389
34987
  }
34390
34988
  }
34391
34989
  }
34392
- async function startPiLlmSpan(state, model, context, options) {
34990
+ async function startPiLlmSpan(state, model, context, property, options) {
34393
34991
  const metadata = {
34394
34992
  ...extractModelMetadata2(model),
34395
34993
  ...extractStreamOptionsMetadata(options),
34396
34994
  ...extractToolMetadata(context.tools),
34397
- "pi_coding_agent.operation": "agent.streamFn"
34995
+ "pi_coding_agent.operation": `agent.${property}`
34398
34996
  };
34399
34997
  const span = startSpan(
34400
34998
  withSpanInstrumentationName(
@@ -37412,6 +38010,7 @@ __export(exports_exports, {
37412
38010
  braintrustStreamChunkSchema: () => braintrustStreamChunkSchema,
37413
38011
  buildLocalSummary: () => buildLocalSummary,
37414
38012
  collectAnthropicSession: () => collectAnthropicSession,
38013
+ completeOpenAIBatchTrace: () => completeOpenAIBatchTrace,
37415
38014
  configureInstrumentation: () => configureInstrumentation,
37416
38015
  constructLogs3OverflowRequest: () => constructLogs3OverflowRequest,
37417
38016
  createFinalValuePassThroughStream: () => createFinalValuePassThroughStream,
@@ -37450,6 +38049,8 @@ __export(exports_exports, {
37450
38049
  loginToState: () => loginToState,
37451
38050
  logs3OverflowUploadSchema: () => logs3OverflowUploadSchema,
37452
38051
  newId: () => newId,
38052
+ openaiBatchesRetrieveTraced: () => openaiBatchesRetrieveTraced,
38053
+ openaiFilesCreateTraced: () => openaiFilesCreateTraced,
37453
38054
  parseCachedHeader: () => parseCachedHeader,
37454
38055
  parseTemplateFormat: () => parseTemplateFormat,
37455
38056
  permalink: () => permalink,
@@ -37614,6 +38215,260 @@ async function registerSandbox(options) {
37614
38215
  };
37615
38216
  }
37616
38217
 
38218
+ // src/wrappers/openai-promise-utils.ts
38219
+ function splitSpanInfo(allParams) {
38220
+ const { span_info, ...params } = allParams;
38221
+ return {
38222
+ params,
38223
+ span_info
38224
+ };
38225
+ }
38226
+ function createChannelContext(_channel, params, span_info) {
38227
+ return {
38228
+ arguments: (
38229
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
38230
+ [params]
38231
+ ),
38232
+ span_info
38233
+ };
38234
+ }
38235
+ async function tracePromiseWithResponse(channel2, traceContext, apiPromise) {
38236
+ let enhancedResponse;
38237
+ const tracePromise = (
38238
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
38239
+ channel2.tracePromise
38240
+ );
38241
+ const data = await tracePromise(async () => {
38242
+ enhancedResponse = await apiPromise.withResponse();
38243
+ traceContext.response = enhancedResponse.response;
38244
+ return enhancedResponse.data;
38245
+ }, traceContext);
38246
+ if (!enhancedResponse) {
38247
+ throw new Error("Expected withResponse() to provide response");
38248
+ }
38249
+ return {
38250
+ data,
38251
+ response: enhancedResponse.response,
38252
+ request_id: enhancedResponse.request_id
38253
+ };
38254
+ }
38255
+ async function tracePromiseAsResponse(channel2, traceContext, apiPromise) {
38256
+ const tracePromise = (
38257
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
38258
+ channel2.tracePromise
38259
+ );
38260
+ let response;
38261
+ await tracePromise(async () => {
38262
+ response = await apiPromise.asResponse();
38263
+ traceContext.response = response;
38264
+ return void 0;
38265
+ }, traceContext);
38266
+ if (!response) {
38267
+ throw new Error("Expected asResponse() to provide response");
38268
+ }
38269
+ return response;
38270
+ }
38271
+ function createLazyAPIPromise(ensureExecuted, ensureResponse, getAPIPromise) {
38272
+ let firstConsumption;
38273
+ let enhancedResponsePromise;
38274
+ let dataPromise;
38275
+ let responsePromise;
38276
+ const withResponse = () => {
38277
+ firstConsumption ??= "data";
38278
+ enhancedResponsePromise ??= firstConsumption === "data" ? ensureExecuted() : getAPIPromise().withResponse();
38279
+ return enhancedResponsePromise;
38280
+ };
38281
+ const asResponse = () => {
38282
+ firstConsumption ??= "response";
38283
+ responsePromise ??= firstConsumption === "response" ? ensureResponse() : getAPIPromise().asResponse();
38284
+ return responsePromise;
38285
+ };
38286
+ return new Proxy({}, {
38287
+ get(target, prop, receiver) {
38288
+ if (prop === "withResponse") {
38289
+ return withResponse;
38290
+ }
38291
+ if (prop === "asResponse") {
38292
+ return asResponse;
38293
+ }
38294
+ if (prop === "then" || prop === "catch" || prop === "finally" || prop in Promise.prototype) {
38295
+ dataPromise ??= withResponse().then((result) => result.data);
38296
+ const value = Reflect.get(dataPromise, prop, receiver);
38297
+ return typeof value === "function" ? value.bind(dataPromise) : value;
38298
+ }
38299
+ return Reflect.get(target, prop, receiver);
38300
+ }
38301
+ });
38302
+ }
38303
+
38304
+ // src/openai-batch.ts
38305
+ function read2(value, key) {
38306
+ if (value === null || typeof value !== "object" && typeof value !== "function") {
38307
+ return void 0;
38308
+ }
38309
+ try {
38310
+ return Reflect.get(value, key);
38311
+ } catch {
38312
+ return void 0;
38313
+ }
38314
+ }
38315
+ function isAsyncIterable5(value) {
38316
+ return typeof read2(value, Symbol.asyncIterator) === "function";
38317
+ }
38318
+ function teeAsyncIterable(source) {
38319
+ const iterator = source[Symbol.asyncIterator]();
38320
+ const stream = new ReadableStream({
38321
+ async pull(controller) {
38322
+ try {
38323
+ const result = await iterator.next();
38324
+ if (result.done) {
38325
+ controller.close();
38326
+ return;
38327
+ }
38328
+ if (!(result.value instanceof Uint8Array)) {
38329
+ throw new TypeError(
38330
+ "OpenAI upload streams must yield Uint8Array chunks"
38331
+ );
38332
+ }
38333
+ controller.enqueue(result.value);
38334
+ } catch (error) {
38335
+ controller.error(error);
38336
+ }
38337
+ },
38338
+ async cancel(reason) {
38339
+ await iterator.return?.(reason);
38340
+ }
38341
+ });
38342
+ const [uploadStream, trace] = stream.tee();
38343
+ const upload = {
38344
+ async *[Symbol.asyncIterator]() {
38345
+ const reader = uploadStream.getReader();
38346
+ try {
38347
+ while (true) {
38348
+ const result = await reader.read();
38349
+ if (result.done) {
38350
+ return;
38351
+ }
38352
+ yield result.value;
38353
+ }
38354
+ } finally {
38355
+ reader.releaseLock();
38356
+ }
38357
+ }
38358
+ };
38359
+ const path = read2(source, "path");
38360
+ if (typeof path === "string") {
38361
+ Object.defineProperty(upload, "path", { value: path });
38362
+ }
38363
+ return { upload, trace: new Response(trace) };
38364
+ }
38365
+ function teeUpload(upload) {
38366
+ if (upload instanceof Blob) {
38367
+ return { upload, trace: new Response(upload) };
38368
+ }
38369
+ if (upload instanceof Response) {
38370
+ return { upload, trace: upload.clone() };
38371
+ }
38372
+ if (isAsyncIterable5(upload)) {
38373
+ return teeAsyncIterable(upload);
38374
+ }
38375
+ return void 0;
38376
+ }
38377
+ function openaiFilesCreateTraced(files) {
38378
+ return (params, options) => {
38379
+ const parent = getSpanParentObject();
38380
+ const purpose = read2(params, "purpose");
38381
+ const file = read2(params, "file");
38382
+ if (purpose !== "batch") {
38383
+ return files.create(params, options);
38384
+ }
38385
+ let branches;
38386
+ try {
38387
+ branches = teeUpload(file);
38388
+ } catch {
38389
+ return files.create(params, options);
38390
+ }
38391
+ if (!branches) {
38392
+ return files.create(params, options);
38393
+ }
38394
+ const tracedParams = {
38395
+ ...Object(params),
38396
+ file: branches.upload
38397
+ };
38398
+ const apiPromise = files.create(tracedParams, options);
38399
+ let enhancedResponse;
38400
+ const tracedResponse = openAIChannels.filesCreateTraced.invoke(
38401
+ async () => {
38402
+ enhancedResponse = await apiPromise.withResponse();
38403
+ return enhancedResponse.data;
38404
+ },
38405
+ files,
38406
+ [{ params, inputFileContent: branches.trace, parent }],
38407
+ {}
38408
+ ).then((data) => {
38409
+ if (!enhancedResponse) {
38410
+ throw new Error(
38411
+ "Expected OpenAI withResponse() to provide a response"
38412
+ );
38413
+ }
38414
+ return { ...enhancedResponse, data };
38415
+ });
38416
+ return createLazyAPIPromise(
38417
+ () => tracedResponse,
38418
+ () => tracedResponse.then(({ response }) => response),
38419
+ () => apiPromise
38420
+ );
38421
+ };
38422
+ }
38423
+ function openaiBatchesRetrieveTraced(batches) {
38424
+ return (batchId, options) => {
38425
+ const apiPromise = batches.retrieve(batchId, options);
38426
+ let enhancedResponse;
38427
+ const tracedResponse = openAIChannels.batchesRetrieveTraced.invoke(
38428
+ async () => {
38429
+ enhancedResponse = await apiPromise.withResponse();
38430
+ return enhancedResponse.data;
38431
+ },
38432
+ batches,
38433
+ [{ batchId }],
38434
+ {}
38435
+ ).then((data) => {
38436
+ if (!enhancedResponse) {
38437
+ throw new Error(
38438
+ "Expected OpenAI withResponse() to provide a response"
38439
+ );
38440
+ }
38441
+ return { ...enhancedResponse, data };
38442
+ });
38443
+ return createLazyAPIPromise(
38444
+ () => tracedResponse,
38445
+ () => tracedResponse.then(({ response }) => response),
38446
+ () => apiPromise
38447
+ );
38448
+ };
38449
+ }
38450
+ async function completeOpenAIBatchTrace(args) {
38451
+ const inputFileContent = Promise.resolve(args.inputFileContent);
38452
+ const outputFileContent = args.outputFileContent === void 0 ? void 0 : Promise.resolve(args.outputFileContent);
38453
+ const errorFileContent = args.errorFileContent === void 0 ? void 0 : Promise.resolve(args.errorFileContent);
38454
+ void inputFileContent.catch(() => void 0);
38455
+ void outputFileContent?.catch(() => void 0);
38456
+ void errorFileContent?.catch(() => void 0);
38457
+ await openAIChannels.batchesCompleteTrace.invoke(
38458
+ async () => void 0,
38459
+ void 0,
38460
+ [
38461
+ {
38462
+ ...args,
38463
+ inputFileContent,
38464
+ outputFileContent,
38465
+ errorFileContent
38466
+ }
38467
+ ],
38468
+ {}
38469
+ );
38470
+ }
38471
+
37617
38472
  // src/functions/invoke.ts
37618
38473
  async function invoke(args) {
37619
38474
  const {
@@ -37716,58 +38571,6 @@ function initFunction({
37716
38571
  return f;
37717
38572
  }
37718
38573
 
37719
- // src/wrappers/openai-promise-utils.ts
37720
- function splitSpanInfo(allParams) {
37721
- const { span_info, ...params } = allParams;
37722
- return {
37723
- params,
37724
- span_info
37725
- };
37726
- }
37727
- function createChannelContext(_channel, params, span_info) {
37728
- return {
37729
- arguments: (
37730
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
37731
- [params]
37732
- ),
37733
- span_info
37734
- };
37735
- }
37736
- async function tracePromiseWithResponse(channel2, traceContext, apiPromise) {
37737
- let enhancedResponse;
37738
- const tracePromise = (
37739
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
37740
- channel2.tracePromise
37741
- );
37742
- const data = await tracePromise(async () => {
37743
- enhancedResponse = await apiPromise.withResponse();
37744
- traceContext.response = enhancedResponse.response;
37745
- return enhancedResponse.data;
37746
- }, traceContext);
37747
- if (!enhancedResponse) {
37748
- throw new Error("Expected withResponse() to provide response");
37749
- }
37750
- return { data, response: enhancedResponse.response };
37751
- }
37752
- function createLazyAPIPromise(ensureExecuted) {
37753
- let dataPromise = null;
37754
- return new Proxy({}, {
37755
- get(target, prop, receiver) {
37756
- if (prop === "withResponse") {
37757
- return () => ensureExecuted();
37758
- }
37759
- if (prop === "then" || prop === "catch" || prop === "finally" || prop in Promise.prototype) {
37760
- if (!dataPromise) {
37761
- dataPromise = ensureExecuted().then((result) => result.data);
37762
- }
37763
- const value = Reflect.get(dataPromise, prop, receiver);
37764
- return typeof value === "function" ? value.bind(dataPromise) : value;
37765
- }
37766
- return Reflect.get(target, prop, receiver);
37767
- }
37768
- });
37769
- }
37770
-
37771
38574
  // src/wrappers/oai_responses.ts
37772
38575
  function responsesProxy(openai) {
37773
38576
  if (!openai.responses) {
@@ -37804,17 +38607,33 @@ function wrapResponsesAsync(target, channel2) {
37804
38607
  return (allParams, options) => {
37805
38608
  const { span_info, params } = splitSpanInfo(allParams);
37806
38609
  let executionPromise = null;
38610
+ let apiPromise = null;
38611
+ const getAPIPromise = () => {
38612
+ apiPromise ??= target(params, options);
38613
+ return apiPromise;
38614
+ };
37807
38615
  const ensureExecuted = () => {
37808
38616
  if (!executionPromise) {
37809
38617
  executionPromise = (async () => {
37810
38618
  const traceContext = createChannelContext(channel2, params, span_info);
37811
- const apiPromise = target(params, options);
37812
- return tracePromiseWithResponse(channel2, traceContext, apiPromise);
38619
+ return tracePromiseWithResponse(
38620
+ channel2,
38621
+ traceContext,
38622
+ getAPIPromise()
38623
+ );
37813
38624
  })();
37814
38625
  }
37815
38626
  return executionPromise;
37816
38627
  };
37817
- return createLazyAPIPromise(ensureExecuted);
38628
+ return createLazyAPIPromise(
38629
+ ensureExecuted,
38630
+ () => tracePromiseAsResponse(
38631
+ channel2,
38632
+ createChannelContext(channel2, params, span_info),
38633
+ getAPIPromise()
38634
+ ),
38635
+ getAPIPromise
38636
+ );
37818
38637
  };
37819
38638
  }
37820
38639
  function wrapResponsesSyncStream(target, channel2) {
@@ -37960,6 +38779,11 @@ function wrapChatCompletion(completion) {
37960
38779
  allParams
37961
38780
  );
37962
38781
  let executionPromise = null;
38782
+ let apiPromise = null;
38783
+ const getAPIPromise = () => {
38784
+ apiPromise ??= completion(params, options);
38785
+ return apiPromise;
38786
+ };
37963
38787
  const ensureExecuted = () => {
37964
38788
  if (!executionPromise) {
37965
38789
  executionPromise = (async () => {
@@ -37969,32 +38793,44 @@ function wrapChatCompletion(completion) {
37969
38793
  span_info
37970
38794
  );
37971
38795
  if (params.stream) {
37972
- const completionPromise = completion(
37973
- params,
37974
- options
38796
+ const completionPromise = (
38797
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
38798
+ getAPIPromise()
37975
38799
  );
37976
- const { data: data2, response: response2 } = await tracePromiseWithResponse(
38800
+ const { data: data2, response: response2, request_id: request_id2 } = await tracePromiseWithResponse(
37977
38801
  openAIChannels.chatCompletionsCreate,
37978
38802
  traceContext,
37979
38803
  completionPromise
37980
38804
  );
37981
- return { data: data2, response: response2 };
38805
+ return { data: data2, response: response2, request_id: request_id2 };
37982
38806
  }
37983
- const completionResponse = completion(
37984
- params,
37985
- options
38807
+ const completionResponse = (
38808
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
38809
+ getAPIPromise()
37986
38810
  );
37987
- const { data, response } = await tracePromiseWithResponse(
38811
+ const { data, response, request_id } = await tracePromiseWithResponse(
37988
38812
  openAIChannels.chatCompletionsCreate,
37989
38813
  traceContext,
37990
38814
  completionResponse
37991
38815
  );
37992
- return { data, response };
38816
+ return { data, response, request_id };
37993
38817
  })();
37994
38818
  }
37995
38819
  return executionPromise;
37996
38820
  };
37997
- return createLazyAPIPromise(ensureExecuted);
38821
+ return createLazyAPIPromise(
38822
+ ensureExecuted,
38823
+ () => tracePromiseAsResponse(
38824
+ openAIChannels.chatCompletionsCreate,
38825
+ createChannelContext(
38826
+ openAIChannels.chatCompletionsCreate,
38827
+ params,
38828
+ span_info
38829
+ ),
38830
+ getAPIPromise()
38831
+ ),
38832
+ getAPIPromise
38833
+ );
37998
38834
  };
37999
38835
  }
38000
38836
  function createEndpointProxy(target, wrapperFn) {
@@ -38012,6 +38848,11 @@ function wrapApiCreateWithChannel(create, channel2) {
38012
38848
  return (allParams, options) => {
38013
38849
  const { span_info, params } = splitSpanInfo(allParams);
38014
38850
  let executionPromise = null;
38851
+ let apiPromise = null;
38852
+ const getAPIPromise = () => {
38853
+ apiPromise ??= create(params, options);
38854
+ return apiPromise;
38855
+ };
38015
38856
  const ensureExecuted = () => {
38016
38857
  if (!executionPromise) {
38017
38858
  executionPromise = (async () => {
@@ -38019,13 +38860,21 @@ function wrapApiCreateWithChannel(create, channel2) {
38019
38860
  return tracePromiseWithResponse(
38020
38861
  channel2,
38021
38862
  traceContext,
38022
- create(params, options)
38863
+ getAPIPromise()
38023
38864
  );
38024
38865
  })();
38025
38866
  }
38026
38867
  return executionPromise;
38027
38868
  };
38028
- return createLazyAPIPromise(ensureExecuted);
38869
+ return createLazyAPIPromise(
38870
+ ensureExecuted,
38871
+ () => tracePromiseAsResponse(
38872
+ channel2,
38873
+ createChannelContext(channel2, params, span_info),
38874
+ getAPIPromise()
38875
+ ),
38876
+ getAPIPromise
38877
+ );
38029
38878
  };
38030
38879
  }
38031
38880
  var wrapEmbeddings = (create) => wrapApiCreateWithChannel(create, openAIChannels.embeddingsCreate);
@@ -40720,7 +41569,15 @@ var MAX_EVE_PROVIDER_CACHE_ENTRIES = 1e4;
40720
41569
  function createEveInstrumentationProvider(options = {}) {
40721
41570
  const bridge = new EveProviderBridge(options.metadata);
40722
41571
  return {
41572
+ // Eve versions before tracePolicy use capture to decide whether provider
41573
+ // events include content. Newer versions prefer tracePolicy when both are
41574
+ // present, so keep the deprecated field for backwards compatibility.
40723
41575
  capture: "content",
41576
+ tracePolicy: () => ({
41577
+ emit: true,
41578
+ recordInputs: true,
41579
+ recordOutputs: true
41580
+ }),
40724
41581
  events: {
40725
41582
  "action.completed": (event, context) => bridge.handleActionTerminal(event, context),
40726
41583
  "action.failed": (event, context) => bridge.handleActionTerminal(event, context),
@@ -44328,8 +45185,8 @@ function normalizeScores(result) {
44328
45185
  (s) => s !== null && s !== void 0 && typeof s === "object" && isScore(s)
44329
45186
  );
44330
45187
  }
44331
- if (typeof result === "object" && result !== null && isScore(result)) {
44332
- return [result];
45188
+ if (typeof result === "object" && result !== null && "score" in result) {
45189
+ return [{ ...result, name: result.name ?? "score" }];
44333
45190
  }
44334
45191
  return [];
44335
45192
  }
@@ -45149,7 +46006,7 @@ function isAsync(fn) {
45149
46006
  function isAsyncGenerator2(fn) {
45150
46007
  return fn[Symbol.toStringTag] === "AsyncGenerator";
45151
46008
  }
45152
- function isAsyncIterable5(obj) {
46009
+ function isAsyncIterable6(obj) {
45153
46010
  return typeof obj[Symbol.asyncIterator] === "function";
45154
46011
  }
45155
46012
  function wrapAsync(asyncFn) {
@@ -45321,7 +46178,7 @@ var eachOfLimit$2 = (limit) => {
45321
46178
  if (isAsyncGenerator2(obj)) {
45322
46179
  return asyncEachOfLimit(obj, limit, iteratee, callback);
45323
46180
  }
45324
- if (isAsyncIterable5(obj)) {
46181
+ if (isAsyncIterable6(obj)) {
45325
46182
  return asyncEachOfLimit(obj[Symbol.asyncIterator](), limit, iteratee, callback);
45326
46183
  }
45327
46184
  var nextElem = createIterator(obj);
@@ -46676,7 +47533,7 @@ function callEvaluatorData(data) {
46676
47533
  baseExperiment
46677
47534
  };
46678
47535
  }
46679
- function isAsyncIterable6(value) {
47536
+ function isAsyncIterable7(value) {
46680
47537
  return typeof value === "object" && value !== null && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
46681
47538
  }
46682
47539
  function isIterable(value) {
@@ -46711,7 +47568,7 @@ async function _internalResolveEvaluatorData(evaluator, experiment) {
46711
47568
  }).asDataset();
46712
47569
  }
46713
47570
  const resolvedDataResult = dataResult instanceof Promise ? await dataResult : dataResult;
46714
- if (isAsyncIterable6(resolvedDataResult)) {
47571
+ if (isAsyncIterable7(resolvedDataResult)) {
46715
47572
  return resolvedDataResult;
46716
47573
  }
46717
47574
  if (Array.isArray(resolvedDataResult) || isIterable(resolvedDataResult)) {
@@ -46912,19 +47769,29 @@ function buildSpanScores(results) {
46912
47769
  function _internalPrepareEvaluatorScore(scoreValue, name) {
46913
47770
  if (scoreValue === null) return { results: null };
46914
47771
  if (Array.isArray(scoreValue)) {
47772
+ const names = /* @__PURE__ */ new Set();
46915
47773
  for (const score of scoreValue) {
46916
47774
  if (!(typeof score === "object" && !isEmpty2(score))) {
46917
47775
  throw new Error(
46918
47776
  `When returning an array of scores, each score must be a non-empty object. Got: ${JSON.stringify(score)}`
46919
47777
  );
46920
47778
  }
47779
+ if (typeof score.name !== "string") {
47780
+ throw new Error(
47781
+ `When returning an array of scores, each score must have a name. Got: ${JSON.stringify(score)}`
47782
+ );
47783
+ }
47784
+ if (names.has(score.name)) {
47785
+ throw new Error(`Duplicate score name '${score.name}' in score array`);
47786
+ }
47787
+ names.add(score.name);
46921
47788
  }
46922
47789
  }
46923
47790
  let results;
46924
47791
  if (Array.isArray(scoreValue)) {
46925
47792
  results = scoreValue;
46926
47793
  } else if (typeof scoreValue === "object" && !isEmpty2(scoreValue)) {
46927
- results = [scoreValue];
47794
+ results = [{ ...scoreValue, name: scoreValue.name ?? name }];
46928
47795
  } else {
46929
47796
  results = [{ name, score: scoreValue }];
46930
47797
  }