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
@@ -3818,6 +3818,7 @@ var ProjectSettings = z6.union([
3818
3818
  ]),
3819
3819
  disable_realtime_queries: z6.union([z6.boolean(), z6.null()]),
3820
3820
  monitor_charts_use_metrics_start: z6.union([z6.boolean(), z6.null()]),
3821
+ blind_reviews: z6.union([z6.boolean(), z6.null()]),
3821
3822
  default_preprocessor: NullableSavedFunctionId
3822
3823
  }).partial(),
3823
3824
  z6.null()
@@ -3879,6 +3880,7 @@ var WindowedAutomationConfig = z6.object({
3879
3880
  auto_approve_tools: z6.array(z6.string().min(1)).optional().default([]),
3880
3881
  harness: z6.enum(["native", "codex", "claude-code"]).optional(),
3881
3882
  model: z6.string().min(1).optional(),
3883
+ endpoint_name: z6.string().min(1).optional(),
3882
3884
  reasoning_effort: z6.enum(["none", "minimal", "low", "medium", "high", "xhigh", "max"]).optional()
3883
3885
  }).optional(),
3884
3886
  actions: z6.array(
@@ -5672,7 +5674,7 @@ var INSTRUMENTATION_NAMES = {
5672
5674
  var INTERNAL_SPAN_INSTRUMENTATION_NAME = /* @__PURE__ */ Symbol.for(
5673
5675
  "braintrust.spanInstrumentationName"
5674
5676
  );
5675
- var SDK_VERSION = true ? "3.29.0" : "0.0.0";
5677
+ var SDK_VERSION = true ? "3.31.0" : "0.0.0";
5676
5678
  function withSpanInstrumentationName(args, instrumentationName) {
5677
5679
  return {
5678
5680
  ...args,
@@ -9919,6 +9921,15 @@ function _internalStartSpanWithInitialMerge(args) {
9919
9921
  [INITIAL_SPAN_WRITE_AS_MERGE]: true
9920
9922
  }).span;
9921
9923
  }
9924
+ function _internalStartSpanWithInitialMergeAndParentSpanIds(args) {
9925
+ return startSpanAndIsLogger(
9926
+ {
9927
+ ...args,
9928
+ [INITIAL_SPAN_WRITE_AS_MERGE]: true
9929
+ },
9930
+ { useParentSpanIdsForObjectParent: true }
9931
+ ).span;
9932
+ }
9922
9933
  function _internalStartSpanWithContext(args, context) {
9923
9934
  return startSpanAndIsLogger({
9924
9935
  ...args,
@@ -9932,7 +9943,7 @@ async function flush(options) {
9932
9943
  function setFetch(fetch2) {
9933
9944
  _internalGetGlobalState().setFetch(fetch2);
9934
9945
  }
9935
- function startSpanAndIsLogger(args) {
9946
+ function startSpanAndIsLogger(args, internalOptions) {
9936
9947
  const state = args?.state ?? _globalState;
9937
9948
  const { parentObject, propagatedState } = getSpanParentObjectAndPropagatedState({
9938
9949
  asyncFlush: args?.asyncFlush,
@@ -9946,7 +9957,7 @@ function startSpanAndIsLogger(args) {
9946
9957
  ) ? {
9947
9958
  spanId: parentObject.data.span_id,
9948
9959
  rootSpanId: parentObject.data.root_span_id
9949
- } : void 0;
9960
+ } : internalOptions?.useParentSpanIdsForObjectParent ? args?.parentSpanIds : void 0;
9950
9961
  const { parent: _ignoredParent, ...spanArgs } = args ?? {};
9951
9962
  const span = new SpanImpl({
9952
9963
  state,
@@ -13424,6 +13435,131 @@ function unsubscribeAll(unsubscribers) {
13424
13435
  return [];
13425
13436
  }
13426
13437
 
13438
+ // src/instrumentation/core/channel-definitions.ts
13439
+ function channel(spec) {
13440
+ return spec;
13441
+ }
13442
+ function defineChannels(pkg, channels, options) {
13443
+ const { instrumentationName } = options;
13444
+ return Object.fromEntries(
13445
+ Object.entries(channels).map(([key, spec]) => {
13446
+ const fullChannelName = `orchestrion:${pkg}:${spec.channelName}`;
13447
+ if (spec.kind === "async") {
13448
+ const asyncSpec = spec;
13449
+ const tracingChannel2 = () => isomorph_default.newTracingChannel(
13450
+ fullChannelName
13451
+ );
13452
+ const intercept2 = (interceptor) => {
13453
+ const hook = tracingChannel2();
13454
+ return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
13455
+ };
13456
+ };
13457
+ return [
13458
+ key,
13459
+ {
13460
+ ...asyncSpec,
13461
+ instrumentationName,
13462
+ intercept: intercept2,
13463
+ invoke: (target, thisArg, args, additional) => {
13464
+ const hook = tracingChannel2();
13465
+ return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
13466
+ },
13467
+ tracingChannel: tracingChannel2,
13468
+ tracePromise: (fn, context) => tracingChannel2().tracePromise(
13469
+ fn,
13470
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
13471
+ context
13472
+ )
13473
+ }
13474
+ ];
13475
+ }
13476
+ const syncSpec = spec;
13477
+ const tracingChannel = () => isomorph_default.newTracingChannel(
13478
+ fullChannelName
13479
+ );
13480
+ const intercept = (interceptor) => {
13481
+ const hook = tracingChannel();
13482
+ return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
13483
+ };
13484
+ };
13485
+ return [
13486
+ key,
13487
+ {
13488
+ ...syncSpec,
13489
+ instrumentationName,
13490
+ intercept,
13491
+ invoke: (target, thisArg, args, additional) => {
13492
+ const hook = tracingChannel();
13493
+ return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
13494
+ },
13495
+ tracingChannel,
13496
+ traceSync: (fn, context) => tracingChannel().traceSync(
13497
+ fn,
13498
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
13499
+ context
13500
+ )
13501
+ }
13502
+ ];
13503
+ })
13504
+ );
13505
+ }
13506
+
13507
+ // src/instrumentation/plugins/openai-channels.ts
13508
+ var openAIChannels = defineChannels(
13509
+ "openai",
13510
+ {
13511
+ filesCreateTraced: channel({
13512
+ channelName: "files.create-traced",
13513
+ kind: "async"
13514
+ }),
13515
+ batchesRetrieveTraced: channel({
13516
+ channelName: "batches.retrieve-traced",
13517
+ kind: "async"
13518
+ }),
13519
+ batchesCompleteTrace: channel({
13520
+ channelName: "batches.complete-trace",
13521
+ kind: "async"
13522
+ }),
13523
+ chatCompletionsCreate: channel({
13524
+ channelName: "chat.completions.create",
13525
+ kind: "async"
13526
+ }),
13527
+ embeddingsCreate: channel({
13528
+ channelName: "embeddings.create",
13529
+ kind: "async"
13530
+ }),
13531
+ betaChatCompletionsParse: channel({
13532
+ channelName: "beta.chat.completions.parse",
13533
+ kind: "async"
13534
+ }),
13535
+ betaChatCompletionsStream: channel({
13536
+ channelName: "beta.chat.completions.stream",
13537
+ kind: "sync-stream"
13538
+ }),
13539
+ moderationsCreate: channel({
13540
+ channelName: "moderations.create",
13541
+ kind: "async"
13542
+ }),
13543
+ responsesCreate: channel({
13544
+ channelName: "responses.create",
13545
+ kind: "async"
13546
+ }),
13547
+ responsesStream: channel({
13548
+ channelName: "responses.stream",
13549
+ kind: "sync-stream"
13550
+ }),
13551
+ responsesParse: channel({
13552
+ channelName: "responses.parse",
13553
+ kind: "async"
13554
+ }),
13555
+ responsesCompact: channel({
13556
+ channelName: "responses.compact",
13557
+ kind: "async"
13558
+ })
13559
+ },
13560
+ { instrumentationName: INSTRUMENTATION_NAMES.OPENAI }
13561
+ );
13562
+
13427
13563
  // src/wrappers/attachment-utils.ts
13428
13564
  function getExtensionFromMediaType(mediaType) {
13429
13565
  const extensionMap = {
@@ -13599,118 +13735,91 @@ function processInputAttachments(input) {
13599
13735
  return processNode(input);
13600
13736
  }
13601
13737
 
13602
- // src/instrumentation/core/channel-definitions.ts
13603
- function channel(spec) {
13604
- return spec;
13605
- }
13606
- function defineChannels(pkg, channels, options) {
13607
- const { instrumentationName } = options;
13608
- return Object.fromEntries(
13609
- Object.entries(channels).map(([key, spec]) => {
13610
- const fullChannelName = `orchestrion:${pkg}:${spec.channelName}`;
13611
- if (spec.kind === "async") {
13612
- const asyncSpec = spec;
13613
- const tracingChannel2 = () => isomorph_default.newTracingChannel(
13614
- fullChannelName
13615
- );
13616
- const intercept2 = (interceptor) => {
13617
- const hook = tracingChannel2();
13618
- return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
13619
- };
13620
- };
13621
- return [
13622
- key,
13623
- {
13624
- ...asyncSpec,
13625
- instrumentationName,
13626
- intercept: intercept2,
13627
- invoke: (target, thisArg, args, additional) => {
13628
- const hook = tracingChannel2();
13629
- return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
13630
- },
13631
- tracingChannel: tracingChannel2,
13632
- tracePromise: (fn, context) => tracingChannel2().tracePromise(
13633
- fn,
13634
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
13635
- context
13636
- )
13637
- }
13638
- ];
13738
+ // src/instrumentation/plugins/openai-span-data.ts
13739
+ var OPENAI_METADATA_KEYS = [
13740
+ "model",
13741
+ "temperature",
13742
+ "top_p",
13743
+ "max_tokens",
13744
+ "frequency_penalty",
13745
+ "presence_penalty",
13746
+ "stop",
13747
+ "response_format",
13748
+ "tools",
13749
+ "tool_choice",
13750
+ "parallel_tool_calls",
13751
+ "max_tool_calls"
13752
+ ];
13753
+ function batchMetadata(params) {
13754
+ const metadata = { provider: "openai" };
13755
+ for (const key of OPENAI_METADATA_KEYS) {
13756
+ try {
13757
+ const value = Reflect.get(params, key);
13758
+ if (value !== void 0) {
13759
+ metadata[key] = value;
13639
13760
  }
13640
- const syncSpec = spec;
13641
- const tracingChannel = () => isomorph_default.newTracingChannel(
13642
- fullChannelName
13643
- );
13644
- const intercept = (interceptor) => {
13645
- const hook = tracingChannel();
13646
- return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
13647
- };
13648
- };
13649
- return [
13650
- key,
13651
- {
13652
- ...syncSpec,
13653
- instrumentationName,
13654
- intercept,
13655
- invoke: (target, thisArg, args, additional) => {
13656
- const hook = tracingChannel();
13657
- return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
13658
- },
13659
- tracingChannel,
13660
- traceSync: (fn, context) => tracingChannel().traceSync(
13661
- fn,
13662
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
13663
- context
13664
- )
13665
- }
13666
- ];
13667
- })
13668
- );
13761
+ } catch {
13762
+ }
13763
+ }
13764
+ return metadata;
13765
+ }
13766
+ function extractOpenAIBatchInput(endpoint, params) {
13767
+ const input = endpoint === "/v1/chat/completions" ? params.messages : params.input;
13768
+ return {
13769
+ input: processInputAttachments(input),
13770
+ metadata: batchMetadata(params)
13771
+ };
13772
+ }
13773
+ function extractOpenAIChatInput(params) {
13774
+ const { messages, ...metadata } = params;
13775
+ return {
13776
+ input: processInputAttachments(messages),
13777
+ metadata: { ...metadata, provider: "openai" }
13778
+ };
13779
+ }
13780
+ function extractOpenAIResponsesInput(params) {
13781
+ const { input, ...metadata } = params;
13782
+ return {
13783
+ input: processInputAttachments(input),
13784
+ metadata: { ...metadata, provider: "openai" }
13785
+ };
13786
+ }
13787
+ function extractOpenAIResponsesMetadata(result) {
13788
+ if (!result) {
13789
+ return void 0;
13790
+ }
13791
+ const { output: _output, usage: _usage, ...metadata } = result;
13792
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
13793
+ }
13794
+ function processImagesInOutput(output) {
13795
+ if (Array.isArray(output)) {
13796
+ return output.map(processImagesInOutput);
13797
+ }
13798
+ if (isObject(output) && output.type === "image_generation_call" && typeof output.result === "string" && output.result) {
13799
+ const fileExtension = output.output_format || "png";
13800
+ const contentType = `image/${fileExtension}`;
13801
+ const baseFilename = typeof output.revised_prompt === "string" ? output.revised_prompt.slice(0, 50).replace(/[^a-zA-Z0-9]/g, "_") : "generated_image";
13802
+ let binaryString;
13803
+ try {
13804
+ binaryString = atob(output.result);
13805
+ } catch {
13806
+ return output;
13807
+ }
13808
+ const bytes = new Uint8Array(binaryString.length);
13809
+ for (let i = 0; i < binaryString.length; i++) {
13810
+ bytes[i] = binaryString.charCodeAt(i);
13811
+ }
13812
+ return {
13813
+ ...output,
13814
+ result: new Attachment({
13815
+ data: new Blob([bytes], { type: contentType }),
13816
+ filename: `${baseFilename}.${fileExtension}`,
13817
+ contentType
13818
+ })
13819
+ };
13820
+ }
13821
+ return output;
13669
13822
  }
13670
-
13671
- // src/instrumentation/plugins/openai-channels.ts
13672
- var openAIChannels = defineChannels(
13673
- "openai",
13674
- {
13675
- chatCompletionsCreate: channel({
13676
- channelName: "chat.completions.create",
13677
- kind: "async"
13678
- }),
13679
- embeddingsCreate: channel({
13680
- channelName: "embeddings.create",
13681
- kind: "async"
13682
- }),
13683
- betaChatCompletionsParse: channel({
13684
- channelName: "beta.chat.completions.parse",
13685
- kind: "async"
13686
- }),
13687
- betaChatCompletionsStream: channel({
13688
- channelName: "beta.chat.completions.stream",
13689
- kind: "sync-stream"
13690
- }),
13691
- moderationsCreate: channel({
13692
- channelName: "moderations.create",
13693
- kind: "async"
13694
- }),
13695
- responsesCreate: channel({
13696
- channelName: "responses.create",
13697
- kind: "async"
13698
- }),
13699
- responsesStream: channel({
13700
- channelName: "responses.stream",
13701
- kind: "sync-stream"
13702
- }),
13703
- responsesParse: channel({
13704
- channelName: "responses.parse",
13705
- kind: "async"
13706
- }),
13707
- responsesCompact: channel({
13708
- channelName: "responses.compact",
13709
- kind: "async"
13710
- })
13711
- },
13712
- { instrumentationName: INSTRUMENTATION_NAMES.OPENAI }
13713
- );
13714
13823
 
13715
13824
  // src/openai-utils.ts
13716
13825
  var BRAINTRUST_CACHED_STREAM_METRIC = "__braintrust_cached_metric";
@@ -13767,23 +13876,573 @@ function getCachedMetricFromHeaders(headers) {
13767
13876
  return parseCachedHeader(headers.get(LEGACY_CACHED_HEADER));
13768
13877
  }
13769
13878
 
13879
+ // src/instrumentation/plugins/openai-batch-instrumentation.ts
13880
+ var SUPPORTED_ENDPOINTS = /* @__PURE__ */ new Set(["/v1/chat/completions", "/v1/responses"]);
13881
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
13882
+ "completed",
13883
+ "failed",
13884
+ "expired",
13885
+ "cancelled"
13886
+ ]);
13887
+ var pendingBatchTraces = /* @__PURE__ */ new Map();
13888
+ function read(value, key) {
13889
+ if (!isObject(value)) {
13890
+ return void 0;
13891
+ }
13892
+ try {
13893
+ return Reflect.get(value, key);
13894
+ } catch {
13895
+ return void 0;
13896
+ }
13897
+ }
13898
+ function isBatchRecordIterable(value) {
13899
+ if (value === null || typeof value !== "object" && typeof value !== "function") {
13900
+ return false;
13901
+ }
13902
+ return typeof read(value, Symbol.iterator) === "function" || typeof read(value, Symbol.asyncIterator) === "function";
13903
+ }
13904
+ function validCustomId(value) {
13905
+ return typeof value === "string" && value.length > 0 && value.length <= 64;
13906
+ }
13907
+ function logBatchInstrumentationError(context, error) {
13908
+ debugLogger.debug(`OpenAI Batch instrumentation ${context}:`, error);
13909
+ }
13910
+ async function exportParent(parent) {
13911
+ if ("toStr" in parent && typeof parent.toStr === "function") {
13912
+ return parent.toStr();
13913
+ }
13914
+ if ("export" in parent && typeof parent.export === "function") {
13915
+ return await parent.export();
13916
+ }
13917
+ return void 0;
13918
+ }
13919
+ async function deterministicDigest(namespace, ...parts) {
13920
+ const encoded = new TextEncoder().encode(
13921
+ [namespace, ...parts].map((part) => `${part.length}:${part}`).join("\0")
13922
+ );
13923
+ return new Uint8Array(
13924
+ await globalThis.crypto.subtle.digest("SHA-256", encoded)
13925
+ );
13926
+ }
13927
+ function digestHex(bytes, length) {
13928
+ return Array.from(bytes.slice(0, length)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
13929
+ }
13930
+ function digestUuid(bytes) {
13931
+ const hex = digestHex(bytes, 16);
13932
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
13933
+ }
13934
+ async function batchSpanIds(inputFileId) {
13935
+ const [row, span, root] = await Promise.all([
13936
+ deterministicDigest("openai:batch:row", inputFileId),
13937
+ deterministicDigest("openai:batch:span", inputFileId),
13938
+ deterministicDigest("openai:batch:root", inputFileId)
13939
+ ]);
13940
+ return {
13941
+ rowId: digestUuid(row),
13942
+ spanId: digestHex(span, 8),
13943
+ rootSpanId: digestHex(root, 16)
13944
+ };
13945
+ }
13946
+ async function childSpanIds(inputFileId, customId) {
13947
+ const [row, span] = await Promise.all([
13948
+ deterministicDigest("openai:batch:child:row", inputFileId, customId),
13949
+ deterministicDigest("openai:batch:child:span", inputFileId, customId)
13950
+ ]);
13951
+ return { rowId: digestUuid(row), spanId: digestHex(span, 8) };
13952
+ }
13953
+ async function startBatchSpan(context) {
13954
+ const ids = await batchSpanIds(context.inputFileId);
13955
+ const parent = SpanComponentsV4.fromStr(context.parent);
13956
+ const hasParentSpan = Boolean(
13957
+ parent.data.row_id && parent.data.span_id && parent.data.root_span_id
13958
+ );
13959
+ return withCurrent(
13960
+ NOOP_SPAN,
13961
+ () => _internalStartSpanWithInitialMergeAndParentSpanIds(
13962
+ withSpanInstrumentationName(
13963
+ {
13964
+ name: "openai.batch",
13965
+ type: "task" /* TASK */,
13966
+ parent: context.parent,
13967
+ ...!hasParentSpan ? {
13968
+ parentSpanIds: {
13969
+ parentSpanIds: [],
13970
+ rootSpanId: ids.rootSpanId
13971
+ }
13972
+ } : {},
13973
+ spanId: ids.spanId,
13974
+ startTime: context.taskStartTime,
13975
+ event: {
13976
+ id: ids.rowId,
13977
+ metadata: {
13978
+ endpoint: context.endpoint,
13979
+ input_file_id: context.inputFileId,
13980
+ provider: "openai"
13981
+ }
13982
+ }
13983
+ },
13984
+ INSTRUMENTATION_NAMES.OPENAI
13985
+ )
13986
+ )
13987
+ );
13988
+ }
13989
+ async function startBatchChild(context, taskParent, input) {
13990
+ const ids = await childSpanIds(context.inputFileId, input.customId);
13991
+ return withCurrent(
13992
+ NOOP_SPAN,
13993
+ () => _internalStartSpanWithInitialMerge(
13994
+ withSpanInstrumentationName(
13995
+ {
13996
+ name: context.endpoint === "/v1/chat/completions" ? "Chat Completion" : "openai.responses.create",
13997
+ type: "llm" /* LLM */,
13998
+ parent: taskParent,
13999
+ spanId: ids.spanId,
14000
+ startTime: context.childStartTime,
14001
+ event: {
14002
+ id: ids.rowId,
14003
+ ...input.spanData?.input !== void 0 ? { input: input.spanData.input } : {},
14004
+ metadata: {
14005
+ ...input.spanData?.metadata,
14006
+ custom_id: input.customId,
14007
+ provider: "openai"
14008
+ }
14009
+ }
14010
+ },
14011
+ INSTRUMENTATION_NAMES.OPENAI
14012
+ )
14013
+ )
14014
+ );
14015
+ }
14016
+ async function* jsonlRecords(file, onIssue = () => {
14017
+ }) {
14018
+ const resolvedFile = await file;
14019
+ if (typeof resolvedFile === "string") {
14020
+ for (const line of resolvedFile.split("\n")) {
14021
+ if (!line.trim()) {
14022
+ continue;
14023
+ }
14024
+ try {
14025
+ yield JSON.parse(line.replace(/\r$/, ""));
14026
+ } catch (error) {
14027
+ logBatchInstrumentationError("skipped malformed JSONL", error);
14028
+ onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
14029
+ }
14030
+ }
14031
+ return;
14032
+ }
14033
+ const body = read(resolvedFile, "body");
14034
+ const getReader = read(body, "getReader");
14035
+ if (isObject(body) && typeof getReader === "function") {
14036
+ let reader;
14037
+ try {
14038
+ reader = Reflect.apply(getReader, body, []);
14039
+ const decoder2 = new TextDecoder();
14040
+ let pending = "";
14041
+ while (true) {
14042
+ const readChunk = read(reader, "read");
14043
+ if (typeof readChunk !== "function") {
14044
+ throw new Error("Response body stream has no read method");
14045
+ }
14046
+ const chunk = await Reflect.apply(readChunk, reader, []);
14047
+ if (!isObject(chunk)) {
14048
+ throw new Error("Response body stream returned an invalid chunk");
14049
+ }
14050
+ if (chunk.done === true) {
14051
+ pending += decoder2.decode();
14052
+ break;
14053
+ }
14054
+ if (!(chunk.value instanceof Uint8Array)) {
14055
+ throw new Error("Response body stream returned a non-byte chunk");
14056
+ }
14057
+ pending += decoder2.decode(chunk.value, { stream: true });
14058
+ let newline = pending.indexOf("\n");
14059
+ while (newline !== -1) {
14060
+ const line = pending.slice(0, newline).replace(/\r$/, "");
14061
+ pending = pending.slice(newline + 1);
14062
+ if (line.trim()) {
14063
+ try {
14064
+ yield JSON.parse(line);
14065
+ } catch (error) {
14066
+ logBatchInstrumentationError("skipped malformed JSONL", error);
14067
+ onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
14068
+ }
14069
+ }
14070
+ newline = pending.indexOf("\n");
14071
+ }
14072
+ }
14073
+ if (pending.trim()) {
14074
+ try {
14075
+ yield JSON.parse(pending.replace(/\r$/, ""));
14076
+ } catch (error) {
14077
+ logBatchInstrumentationError("skipped malformed JSONL", error);
14078
+ onIssue(new Error("OpenAI Batch file contains malformed JSONL"));
14079
+ }
14080
+ }
14081
+ } catch (error) {
14082
+ logBatchInstrumentationError("could not read JSONL response body", error);
14083
+ onIssue(new Error("OpenAI Batch response body could not be read"));
14084
+ } finally {
14085
+ const releaseLock = read(reader, "releaseLock");
14086
+ if (typeof releaseLock === "function") {
14087
+ try {
14088
+ Reflect.apply(releaseLock, reader, []);
14089
+ } catch (error) {
14090
+ logBatchInstrumentationError(
14091
+ "could not release stream reader",
14092
+ error
14093
+ );
14094
+ }
14095
+ }
14096
+ }
14097
+ return;
14098
+ }
14099
+ if (isBatchRecordIterable(resolvedFile)) {
14100
+ for await (const record of resolvedFile) {
14101
+ yield record;
14102
+ }
14103
+ return;
14104
+ }
14105
+ logBatchInstrumentationError("skipped invalid JSONL source", resolvedFile);
14106
+ onIssue(new Error("OpenAI Batch file source is invalid"));
14107
+ }
14108
+ async function readBatchInputs(file) {
14109
+ const inputs = /* @__PURE__ */ new Map();
14110
+ const issues = [];
14111
+ let endpoint;
14112
+ try {
14113
+ for await (const value of jsonlRecords(
14114
+ file,
14115
+ (issue) => issues.push(issue)
14116
+ )) {
14117
+ const customId = read(value, "custom_id");
14118
+ const url = read(value, "url");
14119
+ const body = read(value, "body");
14120
+ if (!validCustomId(customId) || inputs.has(customId) || read(value, "method") !== "POST" || typeof url !== "string" || !SUPPORTED_ENDPOINTS.has(url) || endpoint !== void 0 && endpoint !== url || !isObject(body)) {
14121
+ issues.push(new Error("OpenAI Batch input contains an invalid record"));
14122
+ continue;
14123
+ }
14124
+ endpoint = url;
14125
+ let spanData;
14126
+ try {
14127
+ spanData = extractOpenAIBatchInput(url, body);
14128
+ } catch (error) {
14129
+ logBatchInstrumentationError("could not extract batch input", error);
14130
+ }
14131
+ inputs.set(customId, { customId, spanData });
14132
+ }
14133
+ } catch (error) {
14134
+ logBatchInstrumentationError("could not process input file", error);
14135
+ issues.push(new Error("OpenAI Batch input file could not be processed"));
14136
+ }
14137
+ if (!endpoint || inputs.size === 0) {
14138
+ issues.push(new Error("OpenAI Batch input contains no supported records"));
14139
+ }
14140
+ return { endpoint, inputs, issues };
14141
+ }
14142
+ async function writePendingSpans(trace, endTime) {
14143
+ const task = await startBatchSpan(trace.context);
14144
+ const taskParent = await task.export();
14145
+ for (const input of trace.inputs.values()) {
14146
+ try {
14147
+ const child = await startBatchChild(trace.context, taskParent, input);
14148
+ if (endTime !== void 0) {
14149
+ child.end({ endTime });
14150
+ }
14151
+ } catch (error) {
14152
+ logBatchInstrumentationError("could not write batch request span", error);
14153
+ }
14154
+ }
14155
+ if (endTime !== void 0) {
14156
+ if (trace.status && trace.status !== "completed") {
14157
+ task.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
14158
+ }
14159
+ task.end({ endTime });
14160
+ }
14161
+ }
14162
+ var interceptOpenAIFilesCreateTraced = async (target, thisArg, args) => {
14163
+ const startTime = getCurrentUnixTimestamp();
14164
+ const inputPromise = readBatchInputs(args[0].inputFileContent);
14165
+ const parentPromise = exportParent(args[0].parent);
14166
+ const file = await Reflect.apply(target, thisArg, args);
14167
+ try {
14168
+ const inputFileId = read(file, "id");
14169
+ const [inputData, exportedParent2] = await Promise.all([
14170
+ inputPromise,
14171
+ parentPromise
14172
+ ]);
14173
+ if (typeof inputFileId !== "string" || !exportedParent2 || !inputData.endpoint || inputData.issues.length > 0) {
14174
+ if (inputData.issues[0]) {
14175
+ logBatchInstrumentationError(
14176
+ "skipped invalid input file",
14177
+ inputData.issues[0]
14178
+ );
14179
+ }
14180
+ return file;
14181
+ }
14182
+ const trace = {
14183
+ context: {
14184
+ inputFileId,
14185
+ endpoint: inputData.endpoint,
14186
+ parent: exportedParent2,
14187
+ taskStartTime: startTime,
14188
+ childStartTime: startTime
14189
+ },
14190
+ inputs: inputData.inputs
14191
+ };
14192
+ pendingBatchTraces.set(inputFileId, trace);
14193
+ await writePendingSpans(trace);
14194
+ } catch (error) {
14195
+ logBatchInstrumentationError("could not start batch spans", error);
14196
+ }
14197
+ return file;
14198
+ };
14199
+ function validTimestamp(value) {
14200
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
14201
+ }
14202
+ function terminalEndTime(batch, startTime) {
14203
+ let timestamp;
14204
+ switch (batch.status) {
14205
+ case "completed":
14206
+ timestamp = batch.completed_at;
14207
+ break;
14208
+ case "failed":
14209
+ timestamp = batch.failed_at;
14210
+ break;
14211
+ case "expired":
14212
+ timestamp = batch.expired_at;
14213
+ break;
14214
+ default:
14215
+ timestamp = batch.cancelled_at;
14216
+ }
14217
+ return validTimestamp(timestamp) ? Math.max(timestamp, startTime) : Math.max(getCurrentUnixTimestamp(), startTime);
14218
+ }
14219
+ async function updateBatchTimestamps(batch) {
14220
+ const trace = pendingBatchTraces.get(batch.input_file_id);
14221
+ if (!trace || trace.context.endpoint !== batch.endpoint) {
14222
+ return;
14223
+ }
14224
+ if (validTimestamp(batch.created_at)) {
14225
+ trace.context.taskStartTime = batch.created_at;
14226
+ }
14227
+ trace.context.childStartTime = validTimestamp(batch.in_progress_at) ? Math.max(batch.in_progress_at, trace.context.taskStartTime) : trace.context.taskStartTime;
14228
+ trace.status = batch.status;
14229
+ if (TERMINAL_STATUSES.has(batch.status)) {
14230
+ trace.endTime = terminalEndTime(batch, trace.context.childStartTime);
14231
+ }
14232
+ await writePendingSpans(trace, trace.endTime);
14233
+ }
14234
+ var interceptOpenAIBatchesRetrieveTraced = (target, thisArg, args) => Promise.resolve(Reflect.apply(target, thisArg, args)).then(async (batch) => {
14235
+ try {
14236
+ await updateBatchTimestamps(batch);
14237
+ } catch (error) {
14238
+ logBatchInstrumentationError("could not update batch timestamps", error);
14239
+ }
14240
+ return batch;
14241
+ });
14242
+ function errorFromResult(result) {
14243
+ const error = read(result.value, "error");
14244
+ if (isObject(error)) {
14245
+ const message = read(error, "message");
14246
+ return new Error(
14247
+ typeof message === "string" ? message : "OpenAI Batch request failed"
14248
+ );
14249
+ }
14250
+ const response = read(result.value, "response");
14251
+ const statusCode = read(response, "status_code");
14252
+ if (result.source === "error" || typeof statusCode === "number" && (statusCode < 200 || statusCode >= 300)) {
14253
+ const message = read(read(read(response, "body"), "error"), "message");
14254
+ return new Error(
14255
+ typeof message === "string" ? message : "OpenAI Batch request failed"
14256
+ );
14257
+ }
14258
+ return void 0;
14259
+ }
14260
+ async function completeBatchResult(context, taskParent, endTime, input, result) {
14261
+ const child = await startBatchChild(context, taskParent, input);
14262
+ try {
14263
+ const resultError = errorFromResult(result);
14264
+ const responseBody = read(read(result.value, "response"), "body");
14265
+ if (resultError) {
14266
+ child.log({ error: resultError });
14267
+ } else if (isObject(responseBody)) {
14268
+ const model = read(responseBody, "model");
14269
+ child.log({
14270
+ output: context.endpoint === "/v1/chat/completions" ? read(responseBody, "choices") : processImagesInOutput(read(responseBody, "output")),
14271
+ ...typeof model === "string" ? { metadata: { model } } : {},
14272
+ metrics: parseMetricsFromUsage(read(responseBody, "usage"))
14273
+ });
14274
+ } else {
14275
+ child.log({ error: new Error("OpenAI Batch response body is missing") });
14276
+ }
14277
+ } catch (error) {
14278
+ child.log({ error });
14279
+ } finally {
14280
+ child.end({ endTime });
14281
+ }
14282
+ }
14283
+ async function completeResultFile({
14284
+ context,
14285
+ endTime,
14286
+ file,
14287
+ inputs,
14288
+ issues,
14289
+ seen,
14290
+ source,
14291
+ taskParent
14292
+ }) {
14293
+ if (file === void 0) {
14294
+ return;
14295
+ }
14296
+ for await (const value of jsonlRecords(file, (issue) => issues.push(issue))) {
14297
+ const customId = read(value, "custom_id");
14298
+ if (!validCustomId(customId) || !isObject(value)) {
14299
+ issues.push(
14300
+ new Error("OpenAI Batch result is missing a valid custom_id")
14301
+ );
14302
+ continue;
14303
+ }
14304
+ if (seen.has(customId)) {
14305
+ issues.push(new Error("OpenAI Batch result contains a duplicate"));
14306
+ continue;
14307
+ }
14308
+ const input = inputs.get(customId);
14309
+ if (!input) {
14310
+ issues.push(
14311
+ new Error("OpenAI Batch result does not match an input record")
14312
+ );
14313
+ continue;
14314
+ }
14315
+ seen.add(customId);
14316
+ await completeBatchResult(context, taskParent, endTime, input, {
14317
+ value,
14318
+ source
14319
+ });
14320
+ }
14321
+ }
14322
+ function sameInputs(first, second) {
14323
+ return first.size === second.size && [...first.keys()].every((customId) => second.has(customId));
14324
+ }
14325
+ async function contextForCompletion(inputFileId, inputData) {
14326
+ if (!inputData.endpoint || inputData.issues.length > 0) {
14327
+ return void 0;
14328
+ }
14329
+ const existing = pendingBatchTraces.get(inputFileId);
14330
+ if (existing) {
14331
+ if (existing.context.endpoint !== inputData.endpoint || !sameInputs(existing.inputs, inputData.inputs)) {
14332
+ return void 0;
14333
+ }
14334
+ return { ...existing, inputs: inputData.inputs };
14335
+ }
14336
+ const parent = await exportParent(getSpanParentObject());
14337
+ if (!parent) {
14338
+ return void 0;
14339
+ }
14340
+ const startTime = getCurrentUnixTimestamp();
14341
+ return {
14342
+ context: {
14343
+ inputFileId,
14344
+ endpoint: inputData.endpoint,
14345
+ parent,
14346
+ taskStartTime: startTime,
14347
+ childStartTime: startTime
14348
+ },
14349
+ inputs: inputData.inputs
14350
+ };
14351
+ }
14352
+ async function completeBatch(args) {
14353
+ const inputData = await readBatchInputs(args.inputFileContent);
14354
+ const trace = await contextForCompletion(args.inputFileId, inputData);
14355
+ if (!trace) {
14356
+ logBatchInstrumentationError(
14357
+ "left batch spans pending",
14358
+ inputData.issues[0] ?? new Error("OpenAI Batch input does not match")
14359
+ );
14360
+ return;
14361
+ }
14362
+ const endTime = trace.endTime ?? getCurrentUnixTimestamp();
14363
+ const task = await startBatchSpan(trace.context);
14364
+ const taskParent = await task.export();
14365
+ const issues = [];
14366
+ const seen = /* @__PURE__ */ new Set();
14367
+ await Promise.all([
14368
+ completeResultFile({
14369
+ context: trace.context,
14370
+ endTime,
14371
+ file: args.outputFileContent,
14372
+ inputs: trace.inputs,
14373
+ issues,
14374
+ seen,
14375
+ source: "output",
14376
+ taskParent
14377
+ }),
14378
+ completeResultFile({
14379
+ context: trace.context,
14380
+ endTime,
14381
+ file: args.errorFileContent,
14382
+ inputs: trace.inputs,
14383
+ issues,
14384
+ seen,
14385
+ source: "error",
14386
+ taskParent
14387
+ })
14388
+ ]);
14389
+ if (issues.length > 0) {
14390
+ logBatchInstrumentationError("left batch spans pending", issues[0]);
14391
+ return;
14392
+ }
14393
+ const missing = [...trace.inputs.values()].filter(
14394
+ ({ customId }) => !seen.has(customId)
14395
+ );
14396
+ if (!trace.status || trace.status === "completed") {
14397
+ if (missing.length > 0) {
14398
+ logBatchInstrumentationError(
14399
+ "left batch spans pending",
14400
+ new Error("OpenAI Batch result files are incomplete")
14401
+ );
14402
+ return;
14403
+ }
14404
+ } else {
14405
+ for (const input of missing) {
14406
+ const child = await startBatchChild(trace.context, taskParent, input);
14407
+ child.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
14408
+ child.end({ endTime });
14409
+ }
14410
+ task.log({ error: new Error(`OpenAI Batch ${trace.status}`) });
14411
+ }
14412
+ task.end({ endTime });
14413
+ pendingBatchTraces.delete(args.inputFileId);
14414
+ }
14415
+ var interceptOpenAIBatchTraceComplete = (target, thisArg, args) => Promise.resolve(Reflect.apply(target, thisArg, args)).then(async (result) => {
14416
+ try {
14417
+ await completeBatch(args[0]);
14418
+ } catch (error) {
14419
+ logBatchInstrumentationError("could not complete batch", error);
14420
+ }
14421
+ return result;
14422
+ });
14423
+
13770
14424
  // src/instrumentation/plugins/openai-plugin.ts
13771
14425
  var OpenAIPlugin = class extends BasePlugin {
13772
14426
  constructor() {
13773
14427
  super();
13774
14428
  }
13775
14429
  onEnable() {
14430
+ this.unsubscribers.push(
14431
+ openAIChannels.filesCreateTraced.intercept(
14432
+ interceptOpenAIFilesCreateTraced
14433
+ ),
14434
+ openAIChannels.batchesRetrieveTraced.intercept(
14435
+ interceptOpenAIBatchesRetrieveTraced
14436
+ ),
14437
+ openAIChannels.batchesCompleteTrace.intercept(
14438
+ interceptOpenAIBatchTraceComplete
14439
+ )
14440
+ );
13776
14441
  this.unsubscribers.push(
13777
14442
  traceStreamingChannel(openAIChannels.chatCompletionsCreate, {
13778
14443
  name: "Chat Completion",
13779
14444
  type: "llm" /* LLM */,
13780
- extractInput: ([params]) => {
13781
- const { messages, ...metadata } = params;
13782
- return {
13783
- input: processInputAttachments(messages),
13784
- metadata: { ...metadata, provider: "openai" }
13785
- };
13786
- },
14445
+ extractInput: ([params]) => extractOpenAIChatInput(params),
13787
14446
  extractOutput: (result) => {
13788
14447
  return result?.choices;
13789
14448
  },
@@ -13829,13 +14488,7 @@ var OpenAIPlugin = class extends BasePlugin {
13829
14488
  traceStreamingChannel(openAIChannels.betaChatCompletionsParse, {
13830
14489
  name: "Chat Completion",
13831
14490
  type: "llm" /* LLM */,
13832
- extractInput: ([params]) => {
13833
- const { messages, ...metadata } = params;
13834
- return {
13835
- input: processInputAttachments(messages),
13836
- metadata: { ...metadata, provider: "openai" }
13837
- };
13838
- },
14491
+ extractInput: ([params]) => extractOpenAIChatInput(params),
13839
14492
  extractOutput: (result) => {
13840
14493
  return result?.choices;
13841
14494
  },
@@ -13857,13 +14510,7 @@ var OpenAIPlugin = class extends BasePlugin {
13857
14510
  traceSyncStreamChannel(openAIChannels.betaChatCompletionsStream, {
13858
14511
  name: "Chat Completion",
13859
14512
  type: "llm" /* LLM */,
13860
- extractInput: ([params]) => {
13861
- const { messages, ...metadata } = params;
13862
- return {
13863
- input: processInputAttachments(messages),
13864
- metadata: { ...metadata, provider: "openai" }
13865
- };
13866
- }
14513
+ extractInput: ([params]) => extractOpenAIChatInput(params)
13867
14514
  })
13868
14515
  );
13869
14516
  this.unsubscribers.push(
@@ -13893,23 +14540,11 @@ var OpenAIPlugin = class extends BasePlugin {
13893
14540
  traceStreamingChannel(openAIChannels.responsesCreate, {
13894
14541
  name: "openai.responses.create",
13895
14542
  type: "llm" /* LLM */,
13896
- extractInput: ([params]) => {
13897
- const { input, ...metadata } = params;
13898
- return {
13899
- input: processInputAttachments(input),
13900
- metadata: { ...metadata, provider: "openai" }
13901
- };
13902
- },
14543
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
13903
14544
  extractOutput: (result) => {
13904
14545
  return processImagesInOutput(result?.output);
13905
14546
  },
13906
- extractMetadata: (result) => {
13907
- if (!result) {
13908
- return void 0;
13909
- }
13910
- const { output: _output, usage: _usage, ...metadata } = result;
13911
- return Object.keys(metadata).length > 0 ? metadata : void 0;
13912
- },
14547
+ extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
13913
14548
  extractMetrics: (result, startTime, endEvent) => {
13914
14549
  const metrics = withCachedMetric(
13915
14550
  parseMetricsFromUsage(result?.usage),
@@ -13928,13 +14563,7 @@ var OpenAIPlugin = class extends BasePlugin {
13928
14563
  traceSyncStreamChannel(openAIChannels.responsesStream, {
13929
14564
  name: "openai.responses.create",
13930
14565
  type: "llm" /* LLM */,
13931
- extractInput: ([params]) => {
13932
- const { input, ...metadata } = params;
13933
- return {
13934
- input: processInputAttachments(input),
13935
- metadata: { ...metadata, provider: "openai" }
13936
- };
13937
- },
14566
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
13938
14567
  extractFromEvent: (event) => {
13939
14568
  if (event.type !== "response.completed" || !event.response) {
13940
14569
  return {};
@@ -13957,23 +14586,11 @@ var OpenAIPlugin = class extends BasePlugin {
13957
14586
  traceStreamingChannel(openAIChannels.responsesParse, {
13958
14587
  name: "openai.responses.parse",
13959
14588
  type: "llm" /* LLM */,
13960
- extractInput: ([params]) => {
13961
- const { input, ...metadata } = params;
13962
- return {
13963
- input: processInputAttachments(input),
13964
- metadata: { ...metadata, provider: "openai" }
13965
- };
13966
- },
14589
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
13967
14590
  extractOutput: (result) => {
13968
14591
  return processImagesInOutput(result?.output);
13969
14592
  },
13970
- extractMetadata: (result) => {
13971
- if (!result) {
13972
- return void 0;
13973
- }
13974
- const { output: _output, usage: _usage, ...metadata } = result;
13975
- return Object.keys(metadata).length > 0 ? metadata : void 0;
13976
- },
14593
+ extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
13977
14594
  extractMetrics: (result, startTime, endEvent) => {
13978
14595
  const metrics = withCachedMetric(
13979
14596
  parseMetricsFromUsage(result?.usage),
@@ -13992,23 +14609,11 @@ var OpenAIPlugin = class extends BasePlugin {
13992
14609
  traceAsyncChannel(openAIChannels.responsesCompact, {
13993
14610
  name: "openai.responses.compact",
13994
14611
  type: "llm" /* LLM */,
13995
- extractInput: ([params]) => {
13996
- const { input, ...metadata } = params;
13997
- return {
13998
- input: processInputAttachments(input),
13999
- metadata: { ...metadata, provider: "openai" }
14000
- };
14001
- },
14612
+ extractInput: ([params]) => extractOpenAIResponsesInput(params),
14002
14613
  extractOutput: (result) => {
14003
14614
  return processImagesInOutput(result?.output);
14004
14615
  },
14005
- extractMetadata: (result) => {
14006
- if (!result) {
14007
- return void 0;
14008
- }
14009
- const { output: _output, usage: _usage, ...metadata } = result;
14010
- return Object.keys(metadata).length > 0 ? metadata : void 0;
14011
- },
14616
+ extractMetadata: (result) => extractOpenAIResponsesMetadata(result),
14012
14617
  extractMetrics: (result, startTime, endEvent) => {
14013
14618
  const metrics = withCachedMetric(
14014
14619
  parseMetricsFromUsage(result?.usage),
@@ -14064,35 +14669,6 @@ function withCachedMetric(metrics, result, endEvent) {
14064
14669
  cached
14065
14670
  };
14066
14671
  }
14067
- function processImagesInOutput(output) {
14068
- if (Array.isArray(output)) {
14069
- return output.map(processImagesInOutput);
14070
- }
14071
- if (isObject(output)) {
14072
- if (output.type === "image_generation_call" && output.result && typeof output.result === "string") {
14073
- const fileExtension = output.output_format || "png";
14074
- const contentType = `image/${fileExtension}`;
14075
- const baseFilename = output.revised_prompt && typeof output.revised_prompt === "string" ? output.revised_prompt.slice(0, 50).replace(/[^a-zA-Z0-9]/g, "_") : "generated_image";
14076
- const filename = `${baseFilename}.${fileExtension}`;
14077
- const binaryString = atob(output.result);
14078
- const bytes = new Uint8Array(binaryString.length);
14079
- for (let i = 0; i < binaryString.length; i++) {
14080
- bytes[i] = binaryString.charCodeAt(i);
14081
- }
14082
- const blob = new Blob([bytes], { type: contentType });
14083
- const attachment = new Attachment({
14084
- data: blob,
14085
- filename,
14086
- contentType
14087
- });
14088
- return {
14089
- ...output,
14090
- result: attachment
14091
- };
14092
- }
14093
- }
14094
- return output;
14095
- }
14096
14672
  function mergeLogprobTokens(existing, incoming) {
14097
14673
  if (incoming === void 0) {
14098
14674
  return existing;
@@ -19249,6 +19825,12 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiS
19249
19825
  if (!activeEntry) {
19250
19826
  return result;
19251
19827
  }
19828
+ const executionOptions = args[1];
19829
+ const toolCallId = isObject(executionOptions) ? executionOptions.toolCallId : void 0;
19830
+ const toolInput = {
19831
+ input: serializeToolExecutionInput(args),
19832
+ ...typeof toolCallId === "string" ? { metadata: { toolCallId } } : {}
19833
+ };
19252
19834
  if (isAsyncGenerator(result)) {
19253
19835
  return (async function* () {
19254
19836
  const span = activeEntry.parentSpan.startSpan(
@@ -19262,7 +19844,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiS
19262
19844
  INSTRUMENTATION_NAMES.AI_SDK
19263
19845
  )
19264
19846
  );
19265
- span.log({ input: serializeToolExecutionInput(args) });
19847
+ span.log(toolInput);
19266
19848
  try {
19267
19849
  let lastValue;
19268
19850
  for await (const value of result) {
@@ -19280,7 +19862,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiS
19280
19862
  }
19281
19863
  return activeEntry.parentSpan.traced(
19282
19864
  async (span) => {
19283
- span.log({ input: serializeToolExecutionInput(args) });
19865
+ span.log(toolInput);
19284
19866
  const awaitedResult = await result;
19285
19867
  span.log({ output: awaitedResult });
19286
19868
  return awaitedResult;
@@ -33402,14 +33984,17 @@ function getMetricsFromResponse(response) {
33402
33984
  if (!isRecord(usageMetadata)) {
33403
33985
  continue;
33404
33986
  }
33405
- const inputTokenDetails = usageMetadata.input_token_details;
33987
+ const inputTokenDetails = isRecord(usageMetadata.input_token_details) ? usageMetadata.input_token_details : {};
33406
33988
  const outputTokenDetails = usageMetadata.output_token_details;
33407
33989
  return normalizeTokenMetrics({
33408
33990
  total_tokens: usageMetadata.total_tokens,
33409
33991
  prompt_tokens: usageMetadata.input_tokens,
33410
33992
  completion_tokens: usageMetadata.output_tokens,
33411
- prompt_cache_creation_tokens: isRecord(inputTokenDetails) ? inputTokenDetails.cache_creation : void 0,
33412
- prompt_cached_tokens: isRecord(inputTokenDetails) ? inputTokenDetails.cache_read : void 0,
33993
+ // Prefer TTL-specific cache writes over the aggregate when available.
33994
+ prompt_cache_creation_tokens: inputTokenDetails.ephemeral_5m_input_tokens == null && inputTokenDetails.ephemeral_1h_input_tokens == null ? inputTokenDetails.cache_creation : void 0,
33995
+ prompt_cache_creation_5m_tokens: inputTokenDetails.ephemeral_5m_input_tokens,
33996
+ prompt_cache_creation_1h_tokens: inputTokenDetails.ephemeral_1h_input_tokens,
33997
+ prompt_cached_tokens: inputTokenDetails.cache_read,
33413
33998
  completion_reasoning_tokens: isRecord(outputTokenDetails) ? outputTokenDetails.reasoning : void 0
33414
33999
  });
33415
34000
  }
@@ -34125,7 +34710,7 @@ function extractSession(event) {
34125
34710
  return isObject(candidate) && typeof candidate.prompt === "function" ? candidate : void 0;
34126
34711
  }
34127
34712
  function isPiAgent(value) {
34128
- return isObject(value) && typeof value.streamFn === "function" && typeof value.subscribe === "function";
34713
+ return isObject(value) && (typeof value.streamFunction === "function" || typeof value.streamFn === "function") && typeof value.subscribe === "function";
34129
34714
  }
34130
34715
  function promptContextStore() {
34131
34716
  piPromptContextStore ??= isomorph_default.newAsyncLocalStorage();
@@ -34135,17 +34720,21 @@ function currentPiPromptState() {
34135
34720
  return promptContextStore().getStore();
34136
34721
  }
34137
34722
  function installPiAgentInstrumentation(agent) {
34723
+ const property = typeof agent.streamFunction === "function" ? "streamFunction" : "streamFn";
34724
+ const streamFunction = agent[property];
34138
34725
  const existing = piAgentPatchStates.get(agent);
34139
- if (!existing || agent.streamFn !== existing.wrappedStreamFn) {
34726
+ if (streamFunction && (!existing || existing.property !== property || streamFunction !== existing.wrappedStreamFunction)) {
34140
34727
  const patchState = {
34141
- originalStreamFn: agent.streamFn,
34142
- wrappedStreamFn: agent.streamFn
34728
+ originalStreamFunction: streamFunction,
34729
+ property,
34730
+ wrappedStreamFunction: streamFunction
34143
34731
  };
34144
- patchState.wrappedStreamFn = makeInstrumentedStreamFn(
34732
+ patchState.wrappedStreamFunction = makeInstrumentedStreamFunction(
34145
34733
  agent,
34146
- patchState.originalStreamFn
34734
+ patchState.originalStreamFunction,
34735
+ property
34147
34736
  );
34148
- agent.streamFn = patchState.wrappedStreamFn;
34737
+ agent[property] = patchState.wrappedStreamFunction;
34149
34738
  piAgentPatchStates.set(agent, patchState);
34150
34739
  }
34151
34740
  if (piAgentEventSubscriptions.has(agent)) {
@@ -34170,15 +34759,21 @@ function installPiAgentInstrumentation(agent) {
34170
34759
  logInstrumentationError4("Pi Coding Agent event subscription", error);
34171
34760
  }
34172
34761
  }
34173
- function makeInstrumentedStreamFn(agent, originalStreamFn) {
34174
- return async function instrumentedPiStreamFn(model, context, options) {
34175
- const invokeOriginal = () => Reflect.apply(originalStreamFn, this, [model, context, options]);
34762
+ function makeInstrumentedStreamFunction(agent, originalStreamFunction, property) {
34763
+ return async function instrumentedPiStreamFunction(model, context, options) {
34764
+ const invokeOriginal = () => Reflect.apply(originalStreamFunction, this, [model, context, options]);
34176
34765
  const state = currentPiPromptState();
34177
34766
  if (!state || state.agent !== agent || state.finalized) {
34178
34767
  return invokeOriginal();
34179
34768
  }
34180
34769
  wrapPiToolExecutors(context.tools);
34181
- const llmState = await startPiLlmSpan(state, model, context, options);
34770
+ const llmState = await startPiLlmSpan(
34771
+ state,
34772
+ model,
34773
+ context,
34774
+ property,
34775
+ options
34776
+ );
34182
34777
  try {
34183
34778
  const stream = await runWithAutoInstrumentationSuppressed(invokeOriginal);
34184
34779
  return patchAssistantMessageStream(stream, state, llmState);
@@ -34215,12 +34810,12 @@ function wrapPiToolExecutors(tools) {
34215
34810
  }
34216
34811
  }
34217
34812
  }
34218
- async function startPiLlmSpan(state, model, context, options) {
34813
+ async function startPiLlmSpan(state, model, context, property, options) {
34219
34814
  const metadata = {
34220
34815
  ...extractModelMetadata2(model),
34221
34816
  ...extractStreamOptionsMetadata(options),
34222
34817
  ...extractToolMetadata(context.tools),
34223
- "pi_coding_agent.operation": "agent.streamFn"
34818
+ "pi_coding_agent.operation": `agent.${property}`
34224
34819
  };
34225
34820
  const span = startSpan(
34226
34821
  withSpanInstrumentationName(
@@ -37236,6 +37831,7 @@ __export(exports_exports, {
37236
37831
  braintrustStreamChunkSchema: () => braintrustStreamChunkSchema,
37237
37832
  buildLocalSummary: () => buildLocalSummary,
37238
37833
  collectAnthropicSession: () => collectAnthropicSession,
37834
+ completeOpenAIBatchTrace: () => completeOpenAIBatchTrace,
37239
37835
  configureInstrumentation: () => configureInstrumentation,
37240
37836
  constructLogs3OverflowRequest: () => constructLogs3OverflowRequest,
37241
37837
  createFinalValuePassThroughStream: () => createFinalValuePassThroughStream,
@@ -37274,6 +37870,8 @@ __export(exports_exports, {
37274
37870
  loginToState: () => loginToState,
37275
37871
  logs3OverflowUploadSchema: () => logs3OverflowUploadSchema,
37276
37872
  newId: () => newId,
37873
+ openaiBatchesRetrieveTraced: () => openaiBatchesRetrieveTraced,
37874
+ openaiFilesCreateTraced: () => openaiFilesCreateTraced,
37277
37875
  parseCachedHeader: () => parseCachedHeader,
37278
37876
  parseTemplateFormat: () => parseTemplateFormat,
37279
37877
  permalink: () => permalink,
@@ -37438,6 +38036,260 @@ async function registerSandbox(options) {
37438
38036
  };
37439
38037
  }
37440
38038
 
38039
+ // src/wrappers/openai-promise-utils.ts
38040
+ function splitSpanInfo(allParams) {
38041
+ const { span_info, ...params } = allParams;
38042
+ return {
38043
+ params,
38044
+ span_info
38045
+ };
38046
+ }
38047
+ function createChannelContext(_channel, params, span_info) {
38048
+ return {
38049
+ arguments: (
38050
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
38051
+ [params]
38052
+ ),
38053
+ span_info
38054
+ };
38055
+ }
38056
+ async function tracePromiseWithResponse(channel2, traceContext, apiPromise) {
38057
+ let enhancedResponse;
38058
+ const tracePromise = (
38059
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
38060
+ channel2.tracePromise
38061
+ );
38062
+ const data = await tracePromise(async () => {
38063
+ enhancedResponse = await apiPromise.withResponse();
38064
+ traceContext.response = enhancedResponse.response;
38065
+ return enhancedResponse.data;
38066
+ }, traceContext);
38067
+ if (!enhancedResponse) {
38068
+ throw new Error("Expected withResponse() to provide response");
38069
+ }
38070
+ return {
38071
+ data,
38072
+ response: enhancedResponse.response,
38073
+ request_id: enhancedResponse.request_id
38074
+ };
38075
+ }
38076
+ async function tracePromiseAsResponse(channel2, traceContext, apiPromise) {
38077
+ const tracePromise = (
38078
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
38079
+ channel2.tracePromise
38080
+ );
38081
+ let response;
38082
+ await tracePromise(async () => {
38083
+ response = await apiPromise.asResponse();
38084
+ traceContext.response = response;
38085
+ return void 0;
38086
+ }, traceContext);
38087
+ if (!response) {
38088
+ throw new Error("Expected asResponse() to provide response");
38089
+ }
38090
+ return response;
38091
+ }
38092
+ function createLazyAPIPromise(ensureExecuted, ensureResponse, getAPIPromise) {
38093
+ let firstConsumption;
38094
+ let enhancedResponsePromise;
38095
+ let dataPromise;
38096
+ let responsePromise;
38097
+ const withResponse = () => {
38098
+ firstConsumption ??= "data";
38099
+ enhancedResponsePromise ??= firstConsumption === "data" ? ensureExecuted() : getAPIPromise().withResponse();
38100
+ return enhancedResponsePromise;
38101
+ };
38102
+ const asResponse = () => {
38103
+ firstConsumption ??= "response";
38104
+ responsePromise ??= firstConsumption === "response" ? ensureResponse() : getAPIPromise().asResponse();
38105
+ return responsePromise;
38106
+ };
38107
+ return new Proxy({}, {
38108
+ get(target, prop, receiver) {
38109
+ if (prop === "withResponse") {
38110
+ return withResponse;
38111
+ }
38112
+ if (prop === "asResponse") {
38113
+ return asResponse;
38114
+ }
38115
+ if (prop === "then" || prop === "catch" || prop === "finally" || prop in Promise.prototype) {
38116
+ dataPromise ??= withResponse().then((result) => result.data);
38117
+ const value = Reflect.get(dataPromise, prop, receiver);
38118
+ return typeof value === "function" ? value.bind(dataPromise) : value;
38119
+ }
38120
+ return Reflect.get(target, prop, receiver);
38121
+ }
38122
+ });
38123
+ }
38124
+
38125
+ // src/openai-batch.ts
38126
+ function read2(value, key) {
38127
+ if (value === null || typeof value !== "object" && typeof value !== "function") {
38128
+ return void 0;
38129
+ }
38130
+ try {
38131
+ return Reflect.get(value, key);
38132
+ } catch {
38133
+ return void 0;
38134
+ }
38135
+ }
38136
+ function isAsyncIterable5(value) {
38137
+ return typeof read2(value, Symbol.asyncIterator) === "function";
38138
+ }
38139
+ function teeAsyncIterable(source) {
38140
+ const iterator = source[Symbol.asyncIterator]();
38141
+ const stream = new ReadableStream({
38142
+ async pull(controller) {
38143
+ try {
38144
+ const result = await iterator.next();
38145
+ if (result.done) {
38146
+ controller.close();
38147
+ return;
38148
+ }
38149
+ if (!(result.value instanceof Uint8Array)) {
38150
+ throw new TypeError(
38151
+ "OpenAI upload streams must yield Uint8Array chunks"
38152
+ );
38153
+ }
38154
+ controller.enqueue(result.value);
38155
+ } catch (error) {
38156
+ controller.error(error);
38157
+ }
38158
+ },
38159
+ async cancel(reason) {
38160
+ await iterator.return?.(reason);
38161
+ }
38162
+ });
38163
+ const [uploadStream, trace] = stream.tee();
38164
+ const upload = {
38165
+ async *[Symbol.asyncIterator]() {
38166
+ const reader = uploadStream.getReader();
38167
+ try {
38168
+ while (true) {
38169
+ const result = await reader.read();
38170
+ if (result.done) {
38171
+ return;
38172
+ }
38173
+ yield result.value;
38174
+ }
38175
+ } finally {
38176
+ reader.releaseLock();
38177
+ }
38178
+ }
38179
+ };
38180
+ const path = read2(source, "path");
38181
+ if (typeof path === "string") {
38182
+ Object.defineProperty(upload, "path", { value: path });
38183
+ }
38184
+ return { upload, trace: new Response(trace) };
38185
+ }
38186
+ function teeUpload(upload) {
38187
+ if (upload instanceof Blob) {
38188
+ return { upload, trace: new Response(upload) };
38189
+ }
38190
+ if (upload instanceof Response) {
38191
+ return { upload, trace: upload.clone() };
38192
+ }
38193
+ if (isAsyncIterable5(upload)) {
38194
+ return teeAsyncIterable(upload);
38195
+ }
38196
+ return void 0;
38197
+ }
38198
+ function openaiFilesCreateTraced(files) {
38199
+ return (params, options) => {
38200
+ const parent = getSpanParentObject();
38201
+ const purpose = read2(params, "purpose");
38202
+ const file = read2(params, "file");
38203
+ if (purpose !== "batch") {
38204
+ return files.create(params, options);
38205
+ }
38206
+ let branches;
38207
+ try {
38208
+ branches = teeUpload(file);
38209
+ } catch {
38210
+ return files.create(params, options);
38211
+ }
38212
+ if (!branches) {
38213
+ return files.create(params, options);
38214
+ }
38215
+ const tracedParams = {
38216
+ ...Object(params),
38217
+ file: branches.upload
38218
+ };
38219
+ const apiPromise = files.create(tracedParams, options);
38220
+ let enhancedResponse;
38221
+ const tracedResponse = openAIChannels.filesCreateTraced.invoke(
38222
+ async () => {
38223
+ enhancedResponse = await apiPromise.withResponse();
38224
+ return enhancedResponse.data;
38225
+ },
38226
+ files,
38227
+ [{ params, inputFileContent: branches.trace, parent }],
38228
+ {}
38229
+ ).then((data) => {
38230
+ if (!enhancedResponse) {
38231
+ throw new Error(
38232
+ "Expected OpenAI withResponse() to provide a response"
38233
+ );
38234
+ }
38235
+ return { ...enhancedResponse, data };
38236
+ });
38237
+ return createLazyAPIPromise(
38238
+ () => tracedResponse,
38239
+ () => tracedResponse.then(({ response }) => response),
38240
+ () => apiPromise
38241
+ );
38242
+ };
38243
+ }
38244
+ function openaiBatchesRetrieveTraced(batches) {
38245
+ return (batchId, options) => {
38246
+ const apiPromise = batches.retrieve(batchId, options);
38247
+ let enhancedResponse;
38248
+ const tracedResponse = openAIChannels.batchesRetrieveTraced.invoke(
38249
+ async () => {
38250
+ enhancedResponse = await apiPromise.withResponse();
38251
+ return enhancedResponse.data;
38252
+ },
38253
+ batches,
38254
+ [{ batchId }],
38255
+ {}
38256
+ ).then((data) => {
38257
+ if (!enhancedResponse) {
38258
+ throw new Error(
38259
+ "Expected OpenAI withResponse() to provide a response"
38260
+ );
38261
+ }
38262
+ return { ...enhancedResponse, data };
38263
+ });
38264
+ return createLazyAPIPromise(
38265
+ () => tracedResponse,
38266
+ () => tracedResponse.then(({ response }) => response),
38267
+ () => apiPromise
38268
+ );
38269
+ };
38270
+ }
38271
+ async function completeOpenAIBatchTrace(args) {
38272
+ const inputFileContent = Promise.resolve(args.inputFileContent);
38273
+ const outputFileContent = args.outputFileContent === void 0 ? void 0 : Promise.resolve(args.outputFileContent);
38274
+ const errorFileContent = args.errorFileContent === void 0 ? void 0 : Promise.resolve(args.errorFileContent);
38275
+ void inputFileContent.catch(() => void 0);
38276
+ void outputFileContent?.catch(() => void 0);
38277
+ void errorFileContent?.catch(() => void 0);
38278
+ await openAIChannels.batchesCompleteTrace.invoke(
38279
+ async () => void 0,
38280
+ void 0,
38281
+ [
38282
+ {
38283
+ ...args,
38284
+ inputFileContent,
38285
+ outputFileContent,
38286
+ errorFileContent
38287
+ }
38288
+ ],
38289
+ {}
38290
+ );
38291
+ }
38292
+
37441
38293
  // src/functions/invoke.ts
37442
38294
  async function invoke(args) {
37443
38295
  const {
@@ -37540,58 +38392,6 @@ function initFunction({
37540
38392
  return f;
37541
38393
  }
37542
38394
 
37543
- // src/wrappers/openai-promise-utils.ts
37544
- function splitSpanInfo(allParams) {
37545
- const { span_info, ...params } = allParams;
37546
- return {
37547
- params,
37548
- span_info
37549
- };
37550
- }
37551
- function createChannelContext(_channel, params, span_info) {
37552
- return {
37553
- arguments: (
37554
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
37555
- [params]
37556
- ),
37557
- span_info
37558
- };
37559
- }
37560
- async function tracePromiseWithResponse(channel2, traceContext, apiPromise) {
37561
- let enhancedResponse;
37562
- const tracePromise = (
37563
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
37564
- channel2.tracePromise
37565
- );
37566
- const data = await tracePromise(async () => {
37567
- enhancedResponse = await apiPromise.withResponse();
37568
- traceContext.response = enhancedResponse.response;
37569
- return enhancedResponse.data;
37570
- }, traceContext);
37571
- if (!enhancedResponse) {
37572
- throw new Error("Expected withResponse() to provide response");
37573
- }
37574
- return { data, response: enhancedResponse.response };
37575
- }
37576
- function createLazyAPIPromise(ensureExecuted) {
37577
- let dataPromise = null;
37578
- return new Proxy({}, {
37579
- get(target, prop, receiver) {
37580
- if (prop === "withResponse") {
37581
- return () => ensureExecuted();
37582
- }
37583
- if (prop === "then" || prop === "catch" || prop === "finally" || prop in Promise.prototype) {
37584
- if (!dataPromise) {
37585
- dataPromise = ensureExecuted().then((result) => result.data);
37586
- }
37587
- const value = Reflect.get(dataPromise, prop, receiver);
37588
- return typeof value === "function" ? value.bind(dataPromise) : value;
37589
- }
37590
- return Reflect.get(target, prop, receiver);
37591
- }
37592
- });
37593
- }
37594
-
37595
38395
  // src/wrappers/oai_responses.ts
37596
38396
  function responsesProxy(openai) {
37597
38397
  if (!openai.responses) {
@@ -37628,17 +38428,33 @@ function wrapResponsesAsync(target, channel2) {
37628
38428
  return (allParams, options) => {
37629
38429
  const { span_info, params } = splitSpanInfo(allParams);
37630
38430
  let executionPromise = null;
38431
+ let apiPromise = null;
38432
+ const getAPIPromise = () => {
38433
+ apiPromise ??= target(params, options);
38434
+ return apiPromise;
38435
+ };
37631
38436
  const ensureExecuted = () => {
37632
38437
  if (!executionPromise) {
37633
38438
  executionPromise = (async () => {
37634
38439
  const traceContext = createChannelContext(channel2, params, span_info);
37635
- const apiPromise = target(params, options);
37636
- return tracePromiseWithResponse(channel2, traceContext, apiPromise);
38440
+ return tracePromiseWithResponse(
38441
+ channel2,
38442
+ traceContext,
38443
+ getAPIPromise()
38444
+ );
37637
38445
  })();
37638
38446
  }
37639
38447
  return executionPromise;
37640
38448
  };
37641
- return createLazyAPIPromise(ensureExecuted);
38449
+ return createLazyAPIPromise(
38450
+ ensureExecuted,
38451
+ () => tracePromiseAsResponse(
38452
+ channel2,
38453
+ createChannelContext(channel2, params, span_info),
38454
+ getAPIPromise()
38455
+ ),
38456
+ getAPIPromise
38457
+ );
37642
38458
  };
37643
38459
  }
37644
38460
  function wrapResponsesSyncStream(target, channel2) {
@@ -37784,6 +38600,11 @@ function wrapChatCompletion(completion) {
37784
38600
  allParams
37785
38601
  );
37786
38602
  let executionPromise = null;
38603
+ let apiPromise = null;
38604
+ const getAPIPromise = () => {
38605
+ apiPromise ??= completion(params, options);
38606
+ return apiPromise;
38607
+ };
37787
38608
  const ensureExecuted = () => {
37788
38609
  if (!executionPromise) {
37789
38610
  executionPromise = (async () => {
@@ -37793,32 +38614,44 @@ function wrapChatCompletion(completion) {
37793
38614
  span_info
37794
38615
  );
37795
38616
  if (params.stream) {
37796
- const completionPromise = completion(
37797
- params,
37798
- options
38617
+ const completionPromise = (
38618
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
38619
+ getAPIPromise()
37799
38620
  );
37800
- const { data: data2, response: response2 } = await tracePromiseWithResponse(
38621
+ const { data: data2, response: response2, request_id: request_id2 } = await tracePromiseWithResponse(
37801
38622
  openAIChannels.chatCompletionsCreate,
37802
38623
  traceContext,
37803
38624
  completionPromise
37804
38625
  );
37805
- return { data: data2, response: response2 };
38626
+ return { data: data2, response: response2, request_id: request_id2 };
37806
38627
  }
37807
- const completionResponse = completion(
37808
- params,
37809
- options
38628
+ const completionResponse = (
38629
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
38630
+ getAPIPromise()
37810
38631
  );
37811
- const { data, response } = await tracePromiseWithResponse(
38632
+ const { data, response, request_id } = await tracePromiseWithResponse(
37812
38633
  openAIChannels.chatCompletionsCreate,
37813
38634
  traceContext,
37814
38635
  completionResponse
37815
38636
  );
37816
- return { data, response };
38637
+ return { data, response, request_id };
37817
38638
  })();
37818
38639
  }
37819
38640
  return executionPromise;
37820
38641
  };
37821
- return createLazyAPIPromise(ensureExecuted);
38642
+ return createLazyAPIPromise(
38643
+ ensureExecuted,
38644
+ () => tracePromiseAsResponse(
38645
+ openAIChannels.chatCompletionsCreate,
38646
+ createChannelContext(
38647
+ openAIChannels.chatCompletionsCreate,
38648
+ params,
38649
+ span_info
38650
+ ),
38651
+ getAPIPromise()
38652
+ ),
38653
+ getAPIPromise
38654
+ );
37822
38655
  };
37823
38656
  }
37824
38657
  function createEndpointProxy(target, wrapperFn) {
@@ -37836,6 +38669,11 @@ function wrapApiCreateWithChannel(create, channel2) {
37836
38669
  return (allParams, options) => {
37837
38670
  const { span_info, params } = splitSpanInfo(allParams);
37838
38671
  let executionPromise = null;
38672
+ let apiPromise = null;
38673
+ const getAPIPromise = () => {
38674
+ apiPromise ??= create(params, options);
38675
+ return apiPromise;
38676
+ };
37839
38677
  const ensureExecuted = () => {
37840
38678
  if (!executionPromise) {
37841
38679
  executionPromise = (async () => {
@@ -37843,13 +38681,21 @@ function wrapApiCreateWithChannel(create, channel2) {
37843
38681
  return tracePromiseWithResponse(
37844
38682
  channel2,
37845
38683
  traceContext,
37846
- create(params, options)
38684
+ getAPIPromise()
37847
38685
  );
37848
38686
  })();
37849
38687
  }
37850
38688
  return executionPromise;
37851
38689
  };
37852
- return createLazyAPIPromise(ensureExecuted);
38690
+ return createLazyAPIPromise(
38691
+ ensureExecuted,
38692
+ () => tracePromiseAsResponse(
38693
+ channel2,
38694
+ createChannelContext(channel2, params, span_info),
38695
+ getAPIPromise()
38696
+ ),
38697
+ getAPIPromise
38698
+ );
37853
38699
  };
37854
38700
  }
37855
38701
  var wrapEmbeddings = (create) => wrapApiCreateWithChannel(create, openAIChannels.embeddingsCreate);
@@ -40544,7 +41390,15 @@ var MAX_EVE_PROVIDER_CACHE_ENTRIES = 1e4;
40544
41390
  function createEveInstrumentationProvider(options = {}) {
40545
41391
  const bridge = new EveProviderBridge(options.metadata);
40546
41392
  return {
41393
+ // Eve versions before tracePolicy use capture to decide whether provider
41394
+ // events include content. Newer versions prefer tracePolicy when both are
41395
+ // present, so keep the deprecated field for backwards compatibility.
40547
41396
  capture: "content",
41397
+ tracePolicy: () => ({
41398
+ emit: true,
41399
+ recordInputs: true,
41400
+ recordOutputs: true
41401
+ }),
40548
41402
  events: {
40549
41403
  "action.completed": (event, context) => bridge.handleActionTerminal(event, context),
40550
41404
  "action.failed": (event, context) => bridge.handleActionTerminal(event, context),
@@ -44152,8 +45006,8 @@ function normalizeScores(result) {
44152
45006
  (s) => s !== null && s !== void 0 && typeof s === "object" && isScore(s)
44153
45007
  );
44154
45008
  }
44155
- if (typeof result === "object" && result !== null && isScore(result)) {
44156
- return [result];
45009
+ if (typeof result === "object" && result !== null && "score" in result) {
45010
+ return [{ ...result, name: result.name ?? "score" }];
44157
45011
  }
44158
45012
  return [];
44159
45013
  }
@@ -44973,7 +45827,7 @@ function isAsync(fn) {
44973
45827
  function isAsyncGenerator2(fn) {
44974
45828
  return fn[Symbol.toStringTag] === "AsyncGenerator";
44975
45829
  }
44976
- function isAsyncIterable5(obj) {
45830
+ function isAsyncIterable6(obj) {
44977
45831
  return typeof obj[Symbol.asyncIterator] === "function";
44978
45832
  }
44979
45833
  function wrapAsync(asyncFn) {
@@ -45145,7 +45999,7 @@ var eachOfLimit$2 = (limit) => {
45145
45999
  if (isAsyncGenerator2(obj)) {
45146
46000
  return asyncEachOfLimit(obj, limit, iteratee, callback);
45147
46001
  }
45148
- if (isAsyncIterable5(obj)) {
46002
+ if (isAsyncIterable6(obj)) {
45149
46003
  return asyncEachOfLimit(obj[Symbol.asyncIterator](), limit, iteratee, callback);
45150
46004
  }
45151
46005
  var nextElem = createIterator(obj);
@@ -46500,7 +47354,7 @@ function callEvaluatorData(data) {
46500
47354
  baseExperiment
46501
47355
  };
46502
47356
  }
46503
- function isAsyncIterable6(value) {
47357
+ function isAsyncIterable7(value) {
46504
47358
  return typeof value === "object" && value !== null && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
46505
47359
  }
46506
47360
  function isIterable(value) {
@@ -46535,7 +47389,7 @@ async function _internalResolveEvaluatorData(evaluator, experiment) {
46535
47389
  }).asDataset();
46536
47390
  }
46537
47391
  const resolvedDataResult = dataResult instanceof Promise ? await dataResult : dataResult;
46538
- if (isAsyncIterable6(resolvedDataResult)) {
47392
+ if (isAsyncIterable7(resolvedDataResult)) {
46539
47393
  return resolvedDataResult;
46540
47394
  }
46541
47395
  if (Array.isArray(resolvedDataResult) || isIterable(resolvedDataResult)) {
@@ -46736,19 +47590,29 @@ function buildSpanScores(results) {
46736
47590
  function _internalPrepareEvaluatorScore(scoreValue, name) {
46737
47591
  if (scoreValue === null) return { results: null };
46738
47592
  if (Array.isArray(scoreValue)) {
47593
+ const names = /* @__PURE__ */ new Set();
46739
47594
  for (const score of scoreValue) {
46740
47595
  if (!(typeof score === "object" && !isEmpty2(score))) {
46741
47596
  throw new Error(
46742
47597
  `When returning an array of scores, each score must be a non-empty object. Got: ${JSON.stringify(score)}`
46743
47598
  );
46744
47599
  }
47600
+ if (typeof score.name !== "string") {
47601
+ throw new Error(
47602
+ `When returning an array of scores, each score must have a name. Got: ${JSON.stringify(score)}`
47603
+ );
47604
+ }
47605
+ if (names.has(score.name)) {
47606
+ throw new Error(`Duplicate score name '${score.name}' in score array`);
47607
+ }
47608
+ names.add(score.name);
46745
47609
  }
46746
47610
  }
46747
47611
  let results;
46748
47612
  if (Array.isArray(scoreValue)) {
46749
47613
  results = scoreValue;
46750
47614
  } else if (typeof scoreValue === "object" && !isEmpty2(scoreValue)) {
46751
- results = [scoreValue];
47615
+ results = [{ ...scoreValue, name: scoreValue.name ?? name }];
46752
47616
  } else {
46753
47617
  results = [{ name, score: scoreValue }];
46754
47618
  }
@@ -49583,6 +50447,7 @@ export {
49583
50447
  braintrustStreamChunkSchema,
49584
50448
  buildLocalSummary,
49585
50449
  collectAnthropicSession,
50450
+ completeOpenAIBatchTrace,
49586
50451
  configureInstrumentation,
49587
50452
  constructLogs3OverflowRequest,
49588
50453
  createFinalValuePassThroughStream,
@@ -49622,6 +50487,8 @@ export {
49622
50487
  loginToState,
49623
50488
  logs3OverflowUploadSchema,
49624
50489
  newId,
50490
+ openaiBatchesRetrieveTraced,
50491
+ openaiFilesCreateTraced,
49625
50492
  parseCachedHeader,
49626
50493
  parseTemplateFormat,
49627
50494
  permalink,