@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
  }
@@ -504,7 +609,7 @@ function formatMessagesForXAI(messages) {
504
609
  return {
505
610
  role: "tool",
506
611
  tool_call_id: msg.toolCallId,
507
- content: msg.content
612
+ content: textToolOutput(msg.modelOutput, msg.content)
508
613
  };
509
614
  default:
510
615
  return msg;
@@ -512,7 +617,120 @@ function formatMessagesForXAI(messages) {
512
617
  });
513
618
  }
514
619
 
620
+ // src/core/openai-responses.ts
621
+ function resolveOpenAITransport(defaults, overrides) {
622
+ return {
623
+ api: overrides?.api ?? defaults.api ?? "auto",
624
+ responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
625
+ };
626
+ }
627
+ function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
628
+ const api = transport.api ?? "auto";
629
+ if (!["auto", "chat", "responses"].includes(api)) {
630
+ throw new Error(`Unknown OpenAI API selection: ${api}`);
631
+ }
632
+ if (api === "chat") {
633
+ if (transport.responses !== void 0 || nativeToolSearch) {
634
+ throw new Error(
635
+ "Responses options and native tool search require the Responses API"
636
+ );
637
+ }
638
+ return false;
639
+ }
640
+ let officialEndpoint = !baseURL;
641
+ try {
642
+ officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
643
+ } catch {
644
+ }
645
+ return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
646
+ }
647
+ function responseMetadata(response) {
648
+ const output = Array.isArray(response?.output) ? response.output : [];
649
+ return {
650
+ openai: {
651
+ response: {
652
+ id: response?.id,
653
+ model: response?.model,
654
+ status: response?.status,
655
+ output,
656
+ usage: response?.usage,
657
+ store: response?.store,
658
+ refusals: output.flatMap(
659
+ (item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
660
+ (part) => part?.type === "refusal" && typeof part.refusal === "string"
661
+ ).map((part) => part.refusal) : []
662
+ )
663
+ }
664
+ }
665
+ };
666
+ }
667
+ function responseReplayMetadata(metadata) {
668
+ const response = metadata?.openai?.response;
669
+ if (!response) return metadata;
670
+ const output = response.output.map((item) => {
671
+ const fields = ["type", "id", "status"];
672
+ switch (item.type) {
673
+ case "reasoning":
674
+ fields.push("encrypted_content");
675
+ break;
676
+ case "message":
677
+ fields.push("role", "phase");
678
+ break;
679
+ case "function_call":
680
+ fields.push("call_id", "name", "namespace", "arguments");
681
+ break;
682
+ case "tool_search_call":
683
+ fields.push("call_id", "execution", "arguments");
684
+ break;
685
+ case "tool_search_output":
686
+ fields.push("call_id", "tools");
687
+ break;
688
+ case "web_search_call":
689
+ fields.push("action");
690
+ break;
691
+ default:
692
+ return stripItemDiagnostics(item);
693
+ }
694
+ const replay = Object.fromEntries(
695
+ fields.filter((key) => item[key] !== void 0).map((key) => [key, item[key]])
696
+ );
697
+ if (item.type === "reasoning") replay.summary = [];
698
+ if (item.type === "message")
699
+ replay.content = Array.isArray(item.content) ? item.content.flatMap(
700
+ (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 }] : []
701
+ ) : [];
702
+ return stripItemDiagnostics(replay);
703
+ });
704
+ return { openai: { response: { output, refusals: [] } } };
705
+ }
706
+ function stripItemDiagnostics(item) {
707
+ const {
708
+ usage: _usage,
709
+ logprobs: _logprobs,
710
+ metadata: _metadata,
711
+ ...state
712
+ } = item;
713
+ return state;
714
+ }
715
+ function getResponseReplay(metadata) {
716
+ const response = metadata?.openai?.response;
717
+ if (response === void 0) return void 0;
718
+ if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
719
+ throw new Error(
720
+ "Invalid OpenAI Responses replay metadata: output must contain typed items"
721
+ );
722
+ }
723
+ return responseReplayMetadata(metadata)?.openai?.response?.output;
724
+ }
725
+
515
726
  // src/core/provider-error.ts
727
+ function invalidProviderResponse(message) {
728
+ return Object.assign(new Error(message), {
729
+ name: "InvalidProviderResponseError",
730
+ status: 502,
731
+ code: "INVALID_PROVIDER_RESPONSE"
732
+ });
733
+ }
516
734
  function providerErrorEvent(error, code) {
517
735
  const details = error ?? {};
518
736
  const event = {
@@ -588,13 +806,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
588
806
  }));
589
807
  }
590
808
  shouldUseResponsesApi(request) {
591
- if (this.toolDefinitions(request).length === 0) return false;
592
- if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false)
593
- return true;
594
- return requiresOpenAIResponses(
809
+ return useOpenAIResponses(
595
810
  request.config?.model || this.model,
596
811
  this.config.baseUrl,
597
- this.parameterOptions(request)
812
+ this.parameterOptions(request),
813
+ resolveOpenAITransport(this.config, request.config?.openai),
814
+ this.toolDefinitions(request).length > 0,
815
+ !!(request.rawMessages ?? request.messages)?.some(
816
+ (message) => message.providerMetadata?.openai?.response
817
+ ),
818
+ request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
598
819
  );
599
820
  }
600
821
  /**
@@ -627,11 +848,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
627
848
  });
628
849
  }
629
850
  buildResponsesInput(request) {
630
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages) : formatMessagesForOpenAI(request.messages, void 0);
851
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
631
852
  const input = [];
632
853
  for (const message of sourceMessages) {
633
854
  if (message.role === "system") {
634
- if (!request.systemPrompt && message.content) {
855
+ if (message.content && message.content !== request.systemPrompt) {
635
856
  input.push({
636
857
  type: "message",
637
858
  role: "developer",
@@ -641,6 +862,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
641
862
  continue;
642
863
  }
643
864
  if (message.role === "assistant") {
865
+ const replay = getResponseReplay(message.providerMetadata);
866
+ if (replay) {
867
+ input.push(...replay);
868
+ continue;
869
+ }
644
870
  const content = this.toResponsesContent(message.content, "assistant");
645
871
  if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
646
872
  input.push({
@@ -664,7 +890,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
664
890
  input.push({
665
891
  type: "function_call_output",
666
892
  call_id: message.tool_call_id,
667
- output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
893
+ output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
668
894
  });
669
895
  continue;
670
896
  }
@@ -678,10 +904,12 @@ var OpenAIAdapter = class _OpenAIAdapter {
678
904
  return input;
679
905
  }
680
906
  /** Preserve Copilot screenshot attachments on every OpenAI transport. */
681
- formatRawMessages(messages) {
907
+ formatRawMessages(messages, preserveProviderMetadata = false) {
682
908
  return messages.map((message) => {
683
- const { attachments, ...rest } = message;
684
- let content = message.content;
909
+ const { attachments, providerMetadata, modelOutput, ...rest } = message;
910
+ let content = message.role === "tool" && !preserveProviderMetadata && modelOutput !== void 0 ? textToolOutput(modelOutput, "") : message.content;
911
+ if (preserveProviderMetadata && modelOutput)
912
+ rest.modelOutput = modelOutput;
685
913
  if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
686
914
  content = null;
687
915
  }
@@ -694,7 +922,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
694
922
  content = [...parts, ...images];
695
923
  }
696
924
  }
697
- return { ...rest, content };
925
+ return {
926
+ ...rest,
927
+ content,
928
+ ...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
929
+ };
698
930
  });
699
931
  }
700
932
  /**
@@ -775,14 +1007,40 @@ var OpenAIAdapter = class _OpenAIAdapter {
775
1007
  ).map((part) => part.text) : []
776
1008
  ).join("") : "";
777
1009
  const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
778
- const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
779
- id: item.call_id ?? item.id ?? generateToolCallId(),
780
- name: item.name,
781
- args: JSON.parse(item.arguments ?? "{}")
782
- })) : [];
1010
+ const seenCallIds = /* @__PURE__ */ new Set();
1011
+ const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
1012
+ if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
1013
+ throw invalidProviderResponse(
1014
+ "Responses contains an incomplete function call"
1015
+ );
1016
+ }
1017
+ if (seenCallIds.has(item.call_id))
1018
+ throw invalidProviderResponse(
1019
+ "Responses contains duplicate function call IDs"
1020
+ );
1021
+ seenCallIds.add(item.call_id);
1022
+ let args;
1023
+ try {
1024
+ args = JSON.parse(item.arguments);
1025
+ } catch {
1026
+ throw invalidProviderResponse(
1027
+ "Responses function arguments must be valid JSON"
1028
+ );
1029
+ }
1030
+ if (!args || typeof args !== "object" || Array.isArray(args))
1031
+ throw invalidProviderResponse(
1032
+ "Responses function arguments must be a JSON object"
1033
+ );
1034
+ return {
1035
+ id: item.call_id,
1036
+ name: item.name,
1037
+ args
1038
+ };
1039
+ }) : [];
783
1040
  return {
784
1041
  content,
785
1042
  toolCalls,
1043
+ providerMetadata: responseMetadata(response),
786
1044
  usage: response?.usage ? {
787
1045
  promptTokens: response.usage.input_tokens ?? 0,
788
1046
  completionTokens: response.usage.output_tokens ?? 0,
@@ -793,6 +1051,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
793
1051
  }
794
1052
  buildResponsesPayload(request, stream) {
795
1053
  const openaiToolOptions = request.providerToolOptions?.openai;
1054
+ const options = resolveOpenAITransport(
1055
+ this.config,
1056
+ request.config?.openai
1057
+ ).responses;
1058
+ const parameters = buildOpenAIParameters(
1059
+ request.config?.model || this.model,
1060
+ request.config?.maxTokens ?? this.config.maxTokens,
1061
+ request.config?.temperature ?? this.config.temperature,
1062
+ this.parameterOptions(request),
1063
+ "responses"
1064
+ );
796
1065
  const responsesTextFormat = toOpenAIResponsesTextFormat(
797
1066
  request.config?.responseFormat
798
1067
  );
@@ -808,13 +1077,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
808
1077
  ),
809
1078
  tool_choice: openaiToolOptions?.toolChoice,
810
1079
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
811
- ...buildOpenAIParameters(
812
- modelIdForPayload,
813
- request.config?.maxTokens ?? this.config.maxTokens,
814
- request.config?.temperature ?? this.config.temperature,
815
- this.parameterOptions(request),
816
- "responses"
817
- ),
1080
+ ...parameters,
1081
+ ...options?.reasoningSummary ? {
1082
+ reasoning: {
1083
+ ...parameters.reasoning,
1084
+ summary: options.reasoningSummary
1085
+ }
1086
+ } : {},
1087
+ ...options?.store !== void 0 ? { store: options.store } : {},
1088
+ ...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
1089
+ ...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
1090
+ ...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
1091
+ ...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
1092
+ ...options?.store === false || options?.include ? {
1093
+ include: [
1094
+ .../* @__PURE__ */ new Set([
1095
+ ...options?.include ?? [],
1096
+ ...options?.store === false ? ["reasoning.encrypted_content"] : []
1097
+ ])
1098
+ ]
1099
+ } : {},
818
1100
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
819
1101
  stream
820
1102
  };
@@ -873,6 +1155,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
873
1155
  let usage;
874
1156
  const citations = [];
875
1157
  let completed = false;
1158
+ let terminalResponse;
1159
+ let responseIdentity = {};
1160
+ const outputItems = /* @__PURE__ */ new Map();
1161
+ let streamedText = "";
876
1162
  function* flushToolCalls() {
877
1163
  for (const call of toolCalls.values()) {
878
1164
  if (!call.name)
@@ -889,17 +1175,36 @@ var OpenAIAdapter = class _OpenAIAdapter {
889
1175
  for await (const event of stream) {
890
1176
  throwIfAborted(request.signal);
891
1177
  logProviderPayload("openai", "stream chunk", event, request.debug);
1178
+ if (completed)
1179
+ throw new Error("Responses stream emitted an event after completion");
892
1180
  const index = event?.output_index ?? 0;
893
1181
  switch (event?.type) {
1182
+ case "response.created":
1183
+ responseIdentity = {
1184
+ id: event.response?.id,
1185
+ model: event.response?.model
1186
+ };
1187
+ break;
1188
+ case "response.output_item.done":
1189
+ if (event.item) outputItems.set(index, event.item);
1190
+ if (event.item?.type === "function_call") {
1191
+ toolCalls.set(index, {
1192
+ id: event.item.call_id,
1193
+ name: event.item.name,
1194
+ arguments: event.item.arguments
1195
+ });
1196
+ }
1197
+ break;
894
1198
  case "response.output_text.delta":
895
1199
  if (event.delta) {
1200
+ streamedText += event.delta;
896
1201
  yield { type: "message:delta", content: event.delta };
897
1202
  }
898
1203
  break;
899
1204
  case "response.output_item.added":
900
1205
  if (event.item?.type === "function_call") {
901
1206
  toolCalls.set(index, {
902
- id: event.item.call_id ?? event.item.id ?? generateToolCallId(),
1207
+ id: event.item.call_id ?? "",
903
1208
  name: event.item.name ?? "",
904
1209
  arguments: event.item.arguments ?? ""
905
1210
  });
@@ -918,7 +1223,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
918
1223
  break;
919
1224
  }
920
1225
  case "response.completed": {
1226
+ if (event.response?.status && event.response.status !== "completed")
1227
+ throw new Error(`Responses stream ${event.response.status}`);
921
1228
  completed = true;
1229
+ terminalResponse = { ...responseIdentity, ...event.response };
922
1230
  const found = this.collectResponsesCitations(
923
1231
  event.response?.output,
924
1232
  citations.length
@@ -955,12 +1263,66 @@ var OpenAIAdapter = class _OpenAIAdapter {
955
1263
  throwIfAborted(request.signal);
956
1264
  if (!completed)
957
1265
  throw new Error("Responses stream ended without a completed response");
1266
+ const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
1267
+ if (output.some((item) => item.type === "function_call"))
1268
+ toolCalls.clear();
1269
+ for (const [index, item] of output.entries()) {
1270
+ if (item.type === "function_call") {
1271
+ toolCalls.set(index, {
1272
+ id: item.call_id,
1273
+ name: item.name,
1274
+ arguments: item.arguments
1275
+ });
1276
+ }
1277
+ }
1278
+ const uniqueCalls = /* @__PURE__ */ new Map();
1279
+ for (const call of toolCalls.values()) {
1280
+ if (!call.id || !call.name)
1281
+ throw invalidProviderResponse(
1282
+ "Responses stream contains an incomplete tool call"
1283
+ );
1284
+ uniqueCalls.set(call.id, call);
1285
+ }
1286
+ toolCalls.clear();
1287
+ [...uniqueCalls.values()].forEach(
1288
+ (call, index) => toolCalls.set(index, call)
1289
+ );
1290
+ if (!output.length && toolCalls.size) {
1291
+ for (const call of toolCalls.values())
1292
+ output.push({
1293
+ type: "function_call",
1294
+ call_id: call.id,
1295
+ name: call.name,
1296
+ arguments: call.arguments
1297
+ });
1298
+ }
1299
+ if (!output.length && streamedText)
1300
+ output.push({
1301
+ type: "message",
1302
+ role: "assistant",
1303
+ content: [{ type: "output_text", text: streamedText, annotations: [] }]
1304
+ });
1305
+ terminalResponse.output = output;
1306
+ const finalText = this.parseResponsesResult(terminalResponse).content;
1307
+ if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
1308
+ yield {
1309
+ type: "message:delta",
1310
+ content: finalText.slice(streamedText.length)
1311
+ };
1312
+ }
958
1313
  yield* flushToolCalls();
959
1314
  if (citations.length > 0) {
960
1315
  yield { type: "citation", citations: deduplicateCitations(citations) };
961
1316
  }
962
- yield { type: "message:end" };
963
- yield { type: "done", usage };
1317
+ yield {
1318
+ type: "message:end",
1319
+ providerMetadata: responseMetadata(terminalResponse)
1320
+ };
1321
+ yield {
1322
+ type: "done",
1323
+ usage,
1324
+ providerMetadata: responseMetadata(terminalResponse)
1325
+ };
964
1326
  }
965
1327
  async *stream(request) {
966
1328
  throwIfAborted(request.signal);