@yourgpt/llm-sdk 2.6.0-beta.1 → 2.6.0-beta.3

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 (61) hide show
  1. package/README.md +146 -0
  2. package/dist/adapters/index.d.mts +5 -5
  3. package/dist/adapters/index.d.ts +5 -5
  4. package/dist/adapters/index.js +437 -39
  5. package/dist/adapters/index.mjs +437 -39
  6. package/dist/audio-BBSwnjo8.d.mts +92 -0
  7. package/dist/audio-BBSwnjo8.d.ts +92 -0
  8. package/dist/{base-CkeT7u-O.d.mts → base-BICaQaRS.d.mts} +7 -3
  9. package/dist/{base-BdTpUAWI.d.ts → base-CpRPB8u8.d.ts} +7 -3
  10. package/dist/fallback/index.d.mts +4 -4
  11. package/dist/fallback/index.d.ts +4 -4
  12. package/dist/index.d.mts +20 -36
  13. package/dist/index.d.ts +20 -36
  14. package/dist/index.js +327 -69
  15. package/dist/index.mjs +325 -70
  16. package/dist/providers/anthropic/index.d.mts +3 -3
  17. package/dist/providers/anthropic/index.d.ts +3 -3
  18. package/dist/providers/anthropic/index.js +116 -7
  19. package/dist/providers/anthropic/index.mjs +116 -7
  20. package/dist/providers/azure/index.d.mts +3 -3
  21. package/dist/providers/azure/index.d.ts +3 -3
  22. package/dist/providers/azure/index.js +101 -4
  23. package/dist/providers/azure/index.mjs +101 -4
  24. package/dist/providers/fireworks/index.d.mts +3 -3
  25. package/dist/providers/fireworks/index.d.ts +3 -3
  26. package/dist/providers/fireworks/index.js +393 -31
  27. package/dist/providers/fireworks/index.mjs +393 -31
  28. package/dist/providers/google/index.d.mts +3 -3
  29. package/dist/providers/google/index.d.ts +3 -3
  30. package/dist/providers/google/index.js +393 -31
  31. package/dist/providers/google/index.mjs +393 -31
  32. package/dist/providers/ollama/index.d.mts +4 -4
  33. package/dist/providers/ollama/index.d.ts +4 -4
  34. package/dist/providers/ollama/index.js +87 -2
  35. package/dist/providers/ollama/index.mjs +87 -2
  36. package/dist/providers/openai/index.d.mts +25 -6
  37. package/dist/providers/openai/index.d.ts +25 -6
  38. package/dist/providers/openai/index.js +822 -40
  39. package/dist/providers/openai/index.mjs +821 -41
  40. package/dist/providers/openrouter/index.d.mts +3 -3
  41. package/dist/providers/openrouter/index.d.ts +3 -3
  42. package/dist/providers/openrouter/index.js +393 -31
  43. package/dist/providers/openrouter/index.mjs +393 -31
  44. package/dist/providers/togetherai/index.d.mts +3 -3
  45. package/dist/providers/togetherai/index.d.ts +3 -3
  46. package/dist/providers/togetherai/index.js +393 -31
  47. package/dist/providers/togetherai/index.mjs +393 -31
  48. package/dist/providers/xai/index.d.mts +3 -3
  49. package/dist/providers/xai/index.d.ts +3 -3
  50. package/dist/providers/xai/index.js +393 -31
  51. package/dist/providers/xai/index.mjs +393 -31
  52. package/dist/{types-BWRYIvl_.d.mts → types-C3RgRbEC.d.mts} +1 -1
  53. package/dist/{types-CpXcT-tY.d.ts → types-DCFqkJAy.d.ts} +1 -1
  54. package/dist/{types-NOswlhm9.d.mts → types-DHQEMFHA.d.mts} +3 -3
  55. package/dist/{types-BW3csYo5.d.mts → types-Dg1Bcg1k.d.mts} +106 -3
  56. package/dist/{types-BW3csYo5.d.ts → types-Dg1Bcg1k.d.ts} +106 -3
  57. package/dist/{types-DA54bSmX.d.ts → types-Dikj3En4.d.ts} +3 -3
  58. package/dist/yourgpt/index.d.mts +1 -1
  59. package/dist/yourgpt/index.d.ts +1 -1
  60. package/docs/multimodal-and-audio.md +170 -0
  61. package/package.json +3 -2
@@ -1,5 +1,108 @@
1
1
  'use strict';
2
2
 
3
+ // src/core/tool-output.ts
4
+ var imageTypes = /* @__PURE__ */ new Set([
5
+ "image/png",
6
+ "image/jpeg",
7
+ "image/webp",
8
+ "image/gif"
9
+ ]);
10
+ function normalizeToolOutput(value) {
11
+ const output = value;
12
+ if (output?.type !== "content" || !Array.isArray(output.content) || !output.content.length) {
13
+ throw new Error("Tool model output must contain a nonempty content array");
14
+ }
15
+ return {
16
+ type: "content",
17
+ content: output.content.map((part) => {
18
+ if (part?.type === "text" && typeof part.text === "string")
19
+ return { type: "text", text: part.text };
20
+ if (part?.type !== "image")
21
+ throw new Error("Unsupported tool output content type");
22
+ const sources = ["data", "url", "fileId"].filter((key) => key in part);
23
+ if (sources.length !== 1)
24
+ throw new Error(
25
+ "Tool image requires exactly one of data, url, or fileId"
26
+ );
27
+ if ("fileId" in part) {
28
+ if (part.provider !== "openai" || typeof part.fileId !== "string" || !part.fileId.trim())
29
+ throw new Error(
30
+ "Tool image file references must name the openai provider and fileId"
31
+ );
32
+ return { type: "image", fileId: part.fileId, provider: "openai" };
33
+ }
34
+ if ("url" in part) {
35
+ let url;
36
+ try {
37
+ url = new URL(part.url);
38
+ } catch {
39
+ throw new Error("Invalid tool image URL");
40
+ }
41
+ if (!["https:", "http:"].includes(url.protocol) || url.username || url.password)
42
+ throw new Error(
43
+ "Tool image URL must use HTTP(S) without credentials"
44
+ );
45
+ return { type: "image", url: url.href };
46
+ }
47
+ if (!("data" in part) || !imageTypes.has(part.mediaType))
48
+ throw new Error("Unsupported tool image media type");
49
+ let data = part.data instanceof Uint8Array ? Buffer.from(part.data).toString("base64") : part.data;
50
+ if (typeof data !== "string")
51
+ throw new Error("Tool image data must be base64 or bytes");
52
+ if (data.startsWith("data:")) {
53
+ const match = /^data:([^;,]+);base64,(.+)$/.exec(data);
54
+ if (!match || match[1] !== part.mediaType)
55
+ throw new Error(
56
+ "Tool image data URL media type does not match mediaType"
57
+ );
58
+ data = match[2];
59
+ }
60
+ if (!data.length || data.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(data))
61
+ throw new Error("Invalid tool image base64 data");
62
+ if (Buffer.byteLength(data, "base64") > 20 * 1024 * 1024)
63
+ throw new Error("Tool image exceeds 20 MiB");
64
+ if (Buffer.from(data, "base64").toString("base64") !== data)
65
+ throw new Error("Invalid tool image base64 data");
66
+ return { type: "image", data, mediaType: part.mediaType };
67
+ })
68
+ };
69
+ }
70
+ function toolOutputForProvider(value, provider) {
71
+ const output = normalizeToolOutput(value);
72
+ return output.content.map((part) => {
73
+ if (part.type === "text")
74
+ return {
75
+ type: provider === "responses" ? "input_text" : "text",
76
+ text: part.text
77
+ };
78
+ if (provider === "unsupported")
79
+ throw new Error(
80
+ "Image tool results require OpenAI Responses or Anthropic; this endpoint is not supported"
81
+ );
82
+ if ("fileId" in part) {
83
+ if (provider !== "responses")
84
+ throw new Error(
85
+ "OpenAI image file references cannot be sent to another provider"
86
+ );
87
+ return { type: "input_image", file_id: part.fileId, detail: "auto" };
88
+ }
89
+ if (provider === "responses")
90
+ return {
91
+ type: "input_image",
92
+ image_url: "url" in part ? part.url : `data:${part.mediaType};base64,${part.data}`,
93
+ detail: "auto"
94
+ };
95
+ return {
96
+ type: "image",
97
+ source: "url" in part ? { type: "url", url: part.url } : { type: "base64", media_type: part.mediaType, data: part.data }
98
+ };
99
+ });
100
+ }
101
+ function textToolOutput(value, fallback) {
102
+ if (value === void 0) return fallback;
103
+ return toolOutputForProvider(value, "unsupported").map((part) => part.text).join("\n");
104
+ }
105
+
3
106
  // src/core/openai-parameters.ts
4
107
  function capabilities(model) {
5
108
  const gpt = /^gpt-(\d+)(?:\.(\d+))?(?:-(.+))?$/i.exec(model);
@@ -241,7 +344,7 @@ function messageToOpenAIContent(message) {
241
344
  }
242
345
  return blocks;
243
346
  }
244
- function formatMessagesForOpenAI(messages, systemPrompt) {
347
+ function formatMessagesForOpenAI(messages, systemPrompt, preserveProviderMetadata = false) {
245
348
  const formatted = [];
246
349
  if (systemPrompt) {
247
350
  formatted.push({ role: "system", content: systemPrompt });
@@ -259,7 +362,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
259
362
  const assistantMsg = {
260
363
  role: "assistant",
261
364
  // Gemini/xAI (OpenAI-compatible) reject content: "" on assistant messages with tool_calls
262
- content: hasToolCalls ? msg.content || null : msg.content
365
+ content: hasToolCalls ? msg.content || null : msg.content,
366
+ ...preserveProviderMetadata && msg.providerMetadata ? { providerMetadata: msg.providerMetadata } : {}
263
367
  };
264
368
  if (hasToolCalls) {
265
369
  assistantMsg.tool_calls = msg.tool_calls;
@@ -268,7 +372,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
268
372
  } else if (msg.role === "tool" && msg.tool_call_id) {
269
373
  formatted.push({
270
374
  role: "tool",
271
- content: msg.content ?? "",
375
+ content: preserveProviderMetadata ? msg.content ?? "" : textToolOutput(msg.modelOutput, msg.content ?? ""),
376
+ ...preserveProviderMetadata && msg.modelOutput ? { modelOutput: msg.modelOutput } : {},
272
377
  tool_call_id: msg.tool_call_id
273
378
  });
274
379
  }
@@ -555,7 +660,7 @@ function formatMessagesForGoogle(messages) {
555
660
  return {
556
661
  role: "tool",
557
662
  tool_call_id: msg.toolCallId,
558
- content: msg.content
663
+ content: textToolOutput(msg.modelOutput, msg.content)
559
664
  };
560
665
  default:
561
666
  return msg;
@@ -563,7 +668,120 @@ function formatMessagesForGoogle(messages) {
563
668
  });
564
669
  }
565
670
 
671
+ // src/core/openai-responses.ts
672
+ function resolveOpenAITransport(defaults, overrides) {
673
+ return {
674
+ api: overrides?.api ?? defaults.api ?? "auto",
675
+ responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
676
+ };
677
+ }
678
+ function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
679
+ const api = transport.api ?? "auto";
680
+ if (!["auto", "chat", "responses"].includes(api)) {
681
+ throw new Error(`Unknown OpenAI API selection: ${api}`);
682
+ }
683
+ if (api === "chat") {
684
+ if (transport.responses !== void 0 || nativeToolSearch) {
685
+ throw new Error(
686
+ "Responses options and native tool search require the Responses API"
687
+ );
688
+ }
689
+ return false;
690
+ }
691
+ let officialEndpoint = !baseURL;
692
+ try {
693
+ officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
694
+ } catch {
695
+ }
696
+ return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
697
+ }
698
+ function responseMetadata(response) {
699
+ const output = Array.isArray(response?.output) ? response.output : [];
700
+ return {
701
+ openai: {
702
+ response: {
703
+ id: response?.id,
704
+ model: response?.model,
705
+ status: response?.status,
706
+ output,
707
+ usage: response?.usage,
708
+ store: response?.store,
709
+ refusals: output.flatMap(
710
+ (item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
711
+ (part) => part?.type === "refusal" && typeof part.refusal === "string"
712
+ ).map((part) => part.refusal) : []
713
+ )
714
+ }
715
+ }
716
+ };
717
+ }
718
+ function responseReplayMetadata(metadata) {
719
+ const response = metadata?.openai?.response;
720
+ if (!response) return metadata;
721
+ const output = response.output.map((item) => {
722
+ const fields = ["type", "id", "status"];
723
+ switch (item.type) {
724
+ case "reasoning":
725
+ fields.push("encrypted_content");
726
+ break;
727
+ case "message":
728
+ fields.push("role", "phase");
729
+ break;
730
+ case "function_call":
731
+ fields.push("call_id", "name", "namespace", "arguments");
732
+ break;
733
+ case "tool_search_call":
734
+ fields.push("call_id", "execution", "arguments");
735
+ break;
736
+ case "tool_search_output":
737
+ fields.push("call_id", "tools");
738
+ break;
739
+ case "web_search_call":
740
+ fields.push("action");
741
+ break;
742
+ default:
743
+ return stripItemDiagnostics(item);
744
+ }
745
+ const replay = Object.fromEntries(
746
+ fields.filter((key) => item[key] !== void 0).map((key) => [key, item[key]])
747
+ );
748
+ if (item.type === "reasoning") replay.summary = [];
749
+ if (item.type === "message")
750
+ replay.content = Array.isArray(item.content) ? item.content.flatMap(
751
+ (part) => part?.type === "output_text" && typeof part.text === "string" ? [{ type: "output_text", text: part.text, annotations: [] }] : part?.type === "refusal" && typeof part.refusal === "string" ? [{ type: "refusal", refusal: part.refusal }] : []
752
+ ) : [];
753
+ return stripItemDiagnostics(replay);
754
+ });
755
+ return { openai: { response: { output, refusals: [] } } };
756
+ }
757
+ function stripItemDiagnostics(item) {
758
+ const {
759
+ usage: _usage,
760
+ logprobs: _logprobs,
761
+ metadata: _metadata,
762
+ ...state
763
+ } = item;
764
+ return state;
765
+ }
766
+ function getResponseReplay(metadata) {
767
+ const response = metadata?.openai?.response;
768
+ if (response === void 0) return void 0;
769
+ if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
770
+ throw new Error(
771
+ "Invalid OpenAI Responses replay metadata: output must contain typed items"
772
+ );
773
+ }
774
+ return responseReplayMetadata(metadata)?.openai?.response?.output;
775
+ }
776
+
566
777
  // src/core/provider-error.ts
778
+ function invalidProviderResponse(message) {
779
+ return Object.assign(new Error(message), {
780
+ name: "InvalidProviderResponseError",
781
+ status: 502,
782
+ code: "INVALID_PROVIDER_RESPONSE"
783
+ });
784
+ }
567
785
  function providerErrorEvent(error, code) {
568
786
  const details = error ?? {};
569
787
  const event = {
@@ -639,13 +857,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
639
857
  }));
640
858
  }
641
859
  shouldUseResponsesApi(request) {
642
- if (this.toolDefinitions(request).length === 0) return false;
643
- if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false)
644
- return true;
645
- return requiresOpenAIResponses(
860
+ return useOpenAIResponses(
646
861
  request.config?.model || this.model,
647
862
  this.config.baseUrl,
648
- this.parameterOptions(request)
863
+ this.parameterOptions(request),
864
+ resolveOpenAITransport(this.config, request.config?.openai),
865
+ this.toolDefinitions(request).length > 0,
866
+ !!(request.rawMessages ?? request.messages)?.some(
867
+ (message) => message.providerMetadata?.openai?.response
868
+ ),
869
+ request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
649
870
  );
650
871
  }
651
872
  /**
@@ -678,11 +899,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
678
899
  });
679
900
  }
680
901
  buildResponsesInput(request) {
681
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages) : formatMessagesForOpenAI(request.messages, void 0);
902
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
682
903
  const input = [];
683
904
  for (const message of sourceMessages) {
684
905
  if (message.role === "system") {
685
- if (!request.systemPrompt && message.content) {
906
+ if (message.content && message.content !== request.systemPrompt) {
686
907
  input.push({
687
908
  type: "message",
688
909
  role: "developer",
@@ -692,6 +913,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
692
913
  continue;
693
914
  }
694
915
  if (message.role === "assistant") {
916
+ const replay = getResponseReplay(message.providerMetadata);
917
+ if (replay) {
918
+ input.push(...replay);
919
+ continue;
920
+ }
695
921
  const content = this.toResponsesContent(message.content, "assistant");
696
922
  if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
697
923
  input.push({
@@ -715,7 +941,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
715
941
  input.push({
716
942
  type: "function_call_output",
717
943
  call_id: message.tool_call_id,
718
- output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
944
+ output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
719
945
  });
720
946
  continue;
721
947
  }
@@ -729,10 +955,12 @@ var OpenAIAdapter = class _OpenAIAdapter {
729
955
  return input;
730
956
  }
731
957
  /** Preserve Copilot screenshot attachments on every OpenAI transport. */
732
- formatRawMessages(messages) {
958
+ formatRawMessages(messages, preserveProviderMetadata = false) {
733
959
  return messages.map((message) => {
734
- const { attachments, ...rest } = message;
735
- let content = message.content;
960
+ const { attachments, providerMetadata, modelOutput, ...rest } = message;
961
+ let content = message.role === "tool" && !preserveProviderMetadata && modelOutput !== void 0 ? textToolOutput(modelOutput, "") : message.content;
962
+ if (preserveProviderMetadata && modelOutput)
963
+ rest.modelOutput = modelOutput;
736
964
  if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
737
965
  content = null;
738
966
  }
@@ -745,7 +973,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
745
973
  content = [...parts, ...images];
746
974
  }
747
975
  }
748
- return { ...rest, content };
976
+ return {
977
+ ...rest,
978
+ content,
979
+ ...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
980
+ };
749
981
  });
750
982
  }
751
983
  /**
@@ -826,14 +1058,40 @@ var OpenAIAdapter = class _OpenAIAdapter {
826
1058
  ).map((part) => part.text) : []
827
1059
  ).join("") : "";
828
1060
  const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
829
- const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
830
- id: item.call_id ?? item.id ?? generateToolCallId(),
831
- name: item.name,
832
- args: JSON.parse(item.arguments ?? "{}")
833
- })) : [];
1061
+ const seenCallIds = /* @__PURE__ */ new Set();
1062
+ const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
1063
+ if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
1064
+ throw invalidProviderResponse(
1065
+ "Responses contains an incomplete function call"
1066
+ );
1067
+ }
1068
+ if (seenCallIds.has(item.call_id))
1069
+ throw invalidProviderResponse(
1070
+ "Responses contains duplicate function call IDs"
1071
+ );
1072
+ seenCallIds.add(item.call_id);
1073
+ let args;
1074
+ try {
1075
+ args = JSON.parse(item.arguments);
1076
+ } catch {
1077
+ throw invalidProviderResponse(
1078
+ "Responses function arguments must be valid JSON"
1079
+ );
1080
+ }
1081
+ if (!args || typeof args !== "object" || Array.isArray(args))
1082
+ throw invalidProviderResponse(
1083
+ "Responses function arguments must be a JSON object"
1084
+ );
1085
+ return {
1086
+ id: item.call_id,
1087
+ name: item.name,
1088
+ args
1089
+ };
1090
+ }) : [];
834
1091
  return {
835
1092
  content,
836
1093
  toolCalls,
1094
+ providerMetadata: responseMetadata(response),
837
1095
  usage: response?.usage ? {
838
1096
  promptTokens: response.usage.input_tokens ?? 0,
839
1097
  completionTokens: response.usage.output_tokens ?? 0,
@@ -844,6 +1102,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
844
1102
  }
845
1103
  buildResponsesPayload(request, stream) {
846
1104
  const openaiToolOptions = request.providerToolOptions?.openai;
1105
+ const options = resolveOpenAITransport(
1106
+ this.config,
1107
+ request.config?.openai
1108
+ ).responses;
1109
+ const parameters = buildOpenAIParameters(
1110
+ request.config?.model || this.model,
1111
+ request.config?.maxTokens ?? this.config.maxTokens,
1112
+ request.config?.temperature ?? this.config.temperature,
1113
+ this.parameterOptions(request),
1114
+ "responses"
1115
+ );
847
1116
  const responsesTextFormat = toOpenAIResponsesTextFormat(
848
1117
  request.config?.responseFormat
849
1118
  );
@@ -859,13 +1128,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
859
1128
  ),
860
1129
  tool_choice: openaiToolOptions?.toolChoice,
861
1130
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
862
- ...buildOpenAIParameters(
863
- modelIdForPayload,
864
- request.config?.maxTokens ?? this.config.maxTokens,
865
- request.config?.temperature ?? this.config.temperature,
866
- this.parameterOptions(request),
867
- "responses"
868
- ),
1131
+ ...parameters,
1132
+ ...options?.reasoningSummary ? {
1133
+ reasoning: {
1134
+ ...parameters.reasoning,
1135
+ summary: options.reasoningSummary
1136
+ }
1137
+ } : {},
1138
+ ...options?.store !== void 0 ? { store: options.store } : {},
1139
+ ...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
1140
+ ...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
1141
+ ...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
1142
+ ...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
1143
+ ...options?.store === false || options?.include ? {
1144
+ include: [
1145
+ .../* @__PURE__ */ new Set([
1146
+ ...options?.include ?? [],
1147
+ ...options?.store === false ? ["reasoning.encrypted_content"] : []
1148
+ ])
1149
+ ]
1150
+ } : {},
869
1151
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
870
1152
  stream
871
1153
  };
@@ -924,6 +1206,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
924
1206
  let usage;
925
1207
  const citations = [];
926
1208
  let completed = false;
1209
+ let terminalResponse;
1210
+ let responseIdentity = {};
1211
+ const outputItems = /* @__PURE__ */ new Map();
1212
+ let streamedText = "";
927
1213
  function* flushToolCalls() {
928
1214
  for (const call of toolCalls.values()) {
929
1215
  if (!call.name)
@@ -940,17 +1226,36 @@ var OpenAIAdapter = class _OpenAIAdapter {
940
1226
  for await (const event of stream) {
941
1227
  throwIfAborted(request.signal);
942
1228
  logProviderPayload("openai", "stream chunk", event, request.debug);
1229
+ if (completed)
1230
+ throw new Error("Responses stream emitted an event after completion");
943
1231
  const index = event?.output_index ?? 0;
944
1232
  switch (event?.type) {
1233
+ case "response.created":
1234
+ responseIdentity = {
1235
+ id: event.response?.id,
1236
+ model: event.response?.model
1237
+ };
1238
+ break;
1239
+ case "response.output_item.done":
1240
+ if (event.item) outputItems.set(index, event.item);
1241
+ if (event.item?.type === "function_call") {
1242
+ toolCalls.set(index, {
1243
+ id: event.item.call_id,
1244
+ name: event.item.name,
1245
+ arguments: event.item.arguments
1246
+ });
1247
+ }
1248
+ break;
945
1249
  case "response.output_text.delta":
946
1250
  if (event.delta) {
1251
+ streamedText += event.delta;
947
1252
  yield { type: "message:delta", content: event.delta };
948
1253
  }
949
1254
  break;
950
1255
  case "response.output_item.added":
951
1256
  if (event.item?.type === "function_call") {
952
1257
  toolCalls.set(index, {
953
- id: event.item.call_id ?? event.item.id ?? generateToolCallId(),
1258
+ id: event.item.call_id ?? "",
954
1259
  name: event.item.name ?? "",
955
1260
  arguments: event.item.arguments ?? ""
956
1261
  });
@@ -969,7 +1274,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
969
1274
  break;
970
1275
  }
971
1276
  case "response.completed": {
1277
+ if (event.response?.status && event.response.status !== "completed")
1278
+ throw new Error(`Responses stream ${event.response.status}`);
972
1279
  completed = true;
1280
+ terminalResponse = { ...responseIdentity, ...event.response };
973
1281
  const found = this.collectResponsesCitations(
974
1282
  event.response?.output,
975
1283
  citations.length
@@ -1006,12 +1314,66 @@ var OpenAIAdapter = class _OpenAIAdapter {
1006
1314
  throwIfAborted(request.signal);
1007
1315
  if (!completed)
1008
1316
  throw new Error("Responses stream ended without a completed response");
1317
+ const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
1318
+ if (output.some((item) => item.type === "function_call"))
1319
+ toolCalls.clear();
1320
+ for (const [index, item] of output.entries()) {
1321
+ if (item.type === "function_call") {
1322
+ toolCalls.set(index, {
1323
+ id: item.call_id,
1324
+ name: item.name,
1325
+ arguments: item.arguments
1326
+ });
1327
+ }
1328
+ }
1329
+ const uniqueCalls = /* @__PURE__ */ new Map();
1330
+ for (const call of toolCalls.values()) {
1331
+ if (!call.id || !call.name)
1332
+ throw invalidProviderResponse(
1333
+ "Responses stream contains an incomplete tool call"
1334
+ );
1335
+ uniqueCalls.set(call.id, call);
1336
+ }
1337
+ toolCalls.clear();
1338
+ [...uniqueCalls.values()].forEach(
1339
+ (call, index) => toolCalls.set(index, call)
1340
+ );
1341
+ if (!output.length && toolCalls.size) {
1342
+ for (const call of toolCalls.values())
1343
+ output.push({
1344
+ type: "function_call",
1345
+ call_id: call.id,
1346
+ name: call.name,
1347
+ arguments: call.arguments
1348
+ });
1349
+ }
1350
+ if (!output.length && streamedText)
1351
+ output.push({
1352
+ type: "message",
1353
+ role: "assistant",
1354
+ content: [{ type: "output_text", text: streamedText, annotations: [] }]
1355
+ });
1356
+ terminalResponse.output = output;
1357
+ const finalText = this.parseResponsesResult(terminalResponse).content;
1358
+ if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
1359
+ yield {
1360
+ type: "message:delta",
1361
+ content: finalText.slice(streamedText.length)
1362
+ };
1363
+ }
1009
1364
  yield* flushToolCalls();
1010
1365
  if (citations.length > 0) {
1011
1366
  yield { type: "citation", citations: deduplicateCitations(citations) };
1012
1367
  }
1013
- yield { type: "message:end" };
1014
- yield { type: "done", usage };
1368
+ yield {
1369
+ type: "message:end",
1370
+ providerMetadata: responseMetadata(terminalResponse)
1371
+ };
1372
+ yield {
1373
+ type: "done",
1374
+ usage,
1375
+ providerMetadata: responseMetadata(terminalResponse)
1376
+ };
1015
1377
  }
1016
1378
  async *stream(request) {
1017
1379
  throwIfAborted(request.signal);