@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,7 +1,7 @@
1
- import { L as LanguageModel } from '../../types-BW3csYo5.mjs';
2
- import { A as AIProvider } from '../../types-NOswlhm9.mjs';
1
+ import { L as LanguageModel } from '../../types-Dg1Bcg1k.mjs';
2
+ import { A as AIProvider } from '../../types-DHQEMFHA.mjs';
3
3
  import 'zod';
4
- import '../../base-CkeT7u-O.mjs';
4
+ import '../../base-BICaQaRS.mjs';
5
5
 
6
6
  /**
7
7
  * OpenRouter Provider - Modern Pattern
@@ -1,7 +1,7 @@
1
- import { L as LanguageModel } from '../../types-BW3csYo5.js';
2
- import { A as AIProvider } from '../../types-DA54bSmX.js';
1
+ import { L as LanguageModel } from '../../types-Dg1Bcg1k.js';
2
+ import { A as AIProvider } from '../../types-Dikj3En4.js';
3
3
  import 'zod';
4
- import '../../base-BdTpUAWI.js';
4
+ import '../../base-CpRPB8u8.js';
5
5
 
6
6
  /**
7
7
  * OpenRouter Provider - Modern Pattern
@@ -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
  }
@@ -505,7 +610,7 @@ function formatMessagesForOpenRouter(messages) {
505
610
  return {
506
611
  role: "tool",
507
612
  tool_call_id: msg.toolCallId,
508
- content: msg.content
613
+ content: textToolOutput(msg.modelOutput, msg.content)
509
614
  };
510
615
  default:
511
616
  return msg;
@@ -536,7 +641,120 @@ async function searchOpenRouterModels(query, apiKey) {
536
641
  );
537
642
  }
538
643
 
644
+ // src/core/openai-responses.ts
645
+ function resolveOpenAITransport(defaults, overrides) {
646
+ return {
647
+ api: overrides?.api ?? defaults.api ?? "auto",
648
+ responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
649
+ };
650
+ }
651
+ function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
652
+ const api = transport.api ?? "auto";
653
+ if (!["auto", "chat", "responses"].includes(api)) {
654
+ throw new Error(`Unknown OpenAI API selection: ${api}`);
655
+ }
656
+ if (api === "chat") {
657
+ if (transport.responses !== void 0 || nativeToolSearch) {
658
+ throw new Error(
659
+ "Responses options and native tool search require the Responses API"
660
+ );
661
+ }
662
+ return false;
663
+ }
664
+ let officialEndpoint = !baseURL;
665
+ try {
666
+ officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
667
+ } catch {
668
+ }
669
+ return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
670
+ }
671
+ function responseMetadata(response) {
672
+ const output = Array.isArray(response?.output) ? response.output : [];
673
+ return {
674
+ openai: {
675
+ response: {
676
+ id: response?.id,
677
+ model: response?.model,
678
+ status: response?.status,
679
+ output,
680
+ usage: response?.usage,
681
+ store: response?.store,
682
+ refusals: output.flatMap(
683
+ (item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
684
+ (part) => part?.type === "refusal" && typeof part.refusal === "string"
685
+ ).map((part) => part.refusal) : []
686
+ )
687
+ }
688
+ }
689
+ };
690
+ }
691
+ function responseReplayMetadata(metadata) {
692
+ const response = metadata?.openai?.response;
693
+ if (!response) return metadata;
694
+ const output = response.output.map((item) => {
695
+ const fields = ["type", "id", "status"];
696
+ switch (item.type) {
697
+ case "reasoning":
698
+ fields.push("encrypted_content");
699
+ break;
700
+ case "message":
701
+ fields.push("role", "phase");
702
+ break;
703
+ case "function_call":
704
+ fields.push("call_id", "name", "namespace", "arguments");
705
+ break;
706
+ case "tool_search_call":
707
+ fields.push("call_id", "execution", "arguments");
708
+ break;
709
+ case "tool_search_output":
710
+ fields.push("call_id", "tools");
711
+ break;
712
+ case "web_search_call":
713
+ fields.push("action");
714
+ break;
715
+ default:
716
+ return stripItemDiagnostics(item);
717
+ }
718
+ const replay = Object.fromEntries(
719
+ fields.filter((key) => item[key] !== void 0).map((key) => [key, item[key]])
720
+ );
721
+ if (item.type === "reasoning") replay.summary = [];
722
+ if (item.type === "message")
723
+ replay.content = Array.isArray(item.content) ? item.content.flatMap(
724
+ (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 }] : []
725
+ ) : [];
726
+ return stripItemDiagnostics(replay);
727
+ });
728
+ return { openai: { response: { output, refusals: [] } } };
729
+ }
730
+ function stripItemDiagnostics(item) {
731
+ const {
732
+ usage: _usage,
733
+ logprobs: _logprobs,
734
+ metadata: _metadata,
735
+ ...state
736
+ } = item;
737
+ return state;
738
+ }
739
+ function getResponseReplay(metadata) {
740
+ const response = metadata?.openai?.response;
741
+ if (response === void 0) return void 0;
742
+ if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
743
+ throw new Error(
744
+ "Invalid OpenAI Responses replay metadata: output must contain typed items"
745
+ );
746
+ }
747
+ return responseReplayMetadata(metadata)?.openai?.response?.output;
748
+ }
749
+
539
750
  // src/core/provider-error.ts
751
+ function invalidProviderResponse(message) {
752
+ return Object.assign(new Error(message), {
753
+ name: "InvalidProviderResponseError",
754
+ status: 502,
755
+ code: "INVALID_PROVIDER_RESPONSE"
756
+ });
757
+ }
540
758
  function providerErrorEvent(error, code) {
541
759
  const details = error ?? {};
542
760
  const event = {
@@ -612,13 +830,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
612
830
  }));
613
831
  }
614
832
  shouldUseResponsesApi(request) {
615
- if (this.toolDefinitions(request).length === 0) return false;
616
- if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false)
617
- return true;
618
- return requiresOpenAIResponses(
833
+ return useOpenAIResponses(
619
834
  request.config?.model || this.model,
620
835
  this.config.baseUrl,
621
- this.parameterOptions(request)
836
+ this.parameterOptions(request),
837
+ resolveOpenAITransport(this.config, request.config?.openai),
838
+ this.toolDefinitions(request).length > 0,
839
+ !!(request.rawMessages ?? request.messages)?.some(
840
+ (message) => message.providerMetadata?.openai?.response
841
+ ),
842
+ request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
622
843
  );
623
844
  }
624
845
  /**
@@ -651,11 +872,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
651
872
  });
652
873
  }
653
874
  buildResponsesInput(request) {
654
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages) : formatMessagesForOpenAI(request.messages, void 0);
875
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
655
876
  const input = [];
656
877
  for (const message of sourceMessages) {
657
878
  if (message.role === "system") {
658
- if (!request.systemPrompt && message.content) {
879
+ if (message.content && message.content !== request.systemPrompt) {
659
880
  input.push({
660
881
  type: "message",
661
882
  role: "developer",
@@ -665,6 +886,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
665
886
  continue;
666
887
  }
667
888
  if (message.role === "assistant") {
889
+ const replay = getResponseReplay(message.providerMetadata);
890
+ if (replay) {
891
+ input.push(...replay);
892
+ continue;
893
+ }
668
894
  const content = this.toResponsesContent(message.content, "assistant");
669
895
  if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
670
896
  input.push({
@@ -688,7 +914,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
688
914
  input.push({
689
915
  type: "function_call_output",
690
916
  call_id: message.tool_call_id,
691
- output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
917
+ output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
692
918
  });
693
919
  continue;
694
920
  }
@@ -702,10 +928,12 @@ var OpenAIAdapter = class _OpenAIAdapter {
702
928
  return input;
703
929
  }
704
930
  /** Preserve Copilot screenshot attachments on every OpenAI transport. */
705
- formatRawMessages(messages) {
931
+ formatRawMessages(messages, preserveProviderMetadata = false) {
706
932
  return messages.map((message) => {
707
- const { attachments, ...rest } = message;
708
- let content = message.content;
933
+ const { attachments, providerMetadata, modelOutput, ...rest } = message;
934
+ let content = message.role === "tool" && !preserveProviderMetadata && modelOutput !== void 0 ? textToolOutput(modelOutput, "") : message.content;
935
+ if (preserveProviderMetadata && modelOutput)
936
+ rest.modelOutput = modelOutput;
709
937
  if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
710
938
  content = null;
711
939
  }
@@ -718,7 +946,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
718
946
  content = [...parts, ...images];
719
947
  }
720
948
  }
721
- return { ...rest, content };
949
+ return {
950
+ ...rest,
951
+ content,
952
+ ...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
953
+ };
722
954
  });
723
955
  }
724
956
  /**
@@ -799,14 +1031,40 @@ var OpenAIAdapter = class _OpenAIAdapter {
799
1031
  ).map((part) => part.text) : []
800
1032
  ).join("") : "";
801
1033
  const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
802
- const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
803
- id: item.call_id ?? item.id ?? generateToolCallId(),
804
- name: item.name,
805
- args: JSON.parse(item.arguments ?? "{}")
806
- })) : [];
1034
+ const seenCallIds = /* @__PURE__ */ new Set();
1035
+ const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
1036
+ if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
1037
+ throw invalidProviderResponse(
1038
+ "Responses contains an incomplete function call"
1039
+ );
1040
+ }
1041
+ if (seenCallIds.has(item.call_id))
1042
+ throw invalidProviderResponse(
1043
+ "Responses contains duplicate function call IDs"
1044
+ );
1045
+ seenCallIds.add(item.call_id);
1046
+ let args;
1047
+ try {
1048
+ args = JSON.parse(item.arguments);
1049
+ } catch {
1050
+ throw invalidProviderResponse(
1051
+ "Responses function arguments must be valid JSON"
1052
+ );
1053
+ }
1054
+ if (!args || typeof args !== "object" || Array.isArray(args))
1055
+ throw invalidProviderResponse(
1056
+ "Responses function arguments must be a JSON object"
1057
+ );
1058
+ return {
1059
+ id: item.call_id,
1060
+ name: item.name,
1061
+ args
1062
+ };
1063
+ }) : [];
807
1064
  return {
808
1065
  content,
809
1066
  toolCalls,
1067
+ providerMetadata: responseMetadata(response),
810
1068
  usage: response?.usage ? {
811
1069
  promptTokens: response.usage.input_tokens ?? 0,
812
1070
  completionTokens: response.usage.output_tokens ?? 0,
@@ -817,6 +1075,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
817
1075
  }
818
1076
  buildResponsesPayload(request, stream) {
819
1077
  const openaiToolOptions = request.providerToolOptions?.openai;
1078
+ const options = resolveOpenAITransport(
1079
+ this.config,
1080
+ request.config?.openai
1081
+ ).responses;
1082
+ const parameters = buildOpenAIParameters(
1083
+ request.config?.model || this.model,
1084
+ request.config?.maxTokens ?? this.config.maxTokens,
1085
+ request.config?.temperature ?? this.config.temperature,
1086
+ this.parameterOptions(request),
1087
+ "responses"
1088
+ );
820
1089
  const responsesTextFormat = toOpenAIResponsesTextFormat(
821
1090
  request.config?.responseFormat
822
1091
  );
@@ -832,13 +1101,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
832
1101
  ),
833
1102
  tool_choice: openaiToolOptions?.toolChoice,
834
1103
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
835
- ...buildOpenAIParameters(
836
- modelIdForPayload,
837
- request.config?.maxTokens ?? this.config.maxTokens,
838
- request.config?.temperature ?? this.config.temperature,
839
- this.parameterOptions(request),
840
- "responses"
841
- ),
1104
+ ...parameters,
1105
+ ...options?.reasoningSummary ? {
1106
+ reasoning: {
1107
+ ...parameters.reasoning,
1108
+ summary: options.reasoningSummary
1109
+ }
1110
+ } : {},
1111
+ ...options?.store !== void 0 ? { store: options.store } : {},
1112
+ ...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
1113
+ ...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
1114
+ ...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
1115
+ ...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
1116
+ ...options?.store === false || options?.include ? {
1117
+ include: [
1118
+ .../* @__PURE__ */ new Set([
1119
+ ...options?.include ?? [],
1120
+ ...options?.store === false ? ["reasoning.encrypted_content"] : []
1121
+ ])
1122
+ ]
1123
+ } : {},
842
1124
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
843
1125
  stream
844
1126
  };
@@ -897,6 +1179,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
897
1179
  let usage;
898
1180
  const citations = [];
899
1181
  let completed = false;
1182
+ let terminalResponse;
1183
+ let responseIdentity = {};
1184
+ const outputItems = /* @__PURE__ */ new Map();
1185
+ let streamedText = "";
900
1186
  function* flushToolCalls() {
901
1187
  for (const call of toolCalls.values()) {
902
1188
  if (!call.name)
@@ -913,17 +1199,36 @@ var OpenAIAdapter = class _OpenAIAdapter {
913
1199
  for await (const event of stream) {
914
1200
  throwIfAborted(request.signal);
915
1201
  logProviderPayload("openai", "stream chunk", event, request.debug);
1202
+ if (completed)
1203
+ throw new Error("Responses stream emitted an event after completion");
916
1204
  const index = event?.output_index ?? 0;
917
1205
  switch (event?.type) {
1206
+ case "response.created":
1207
+ responseIdentity = {
1208
+ id: event.response?.id,
1209
+ model: event.response?.model
1210
+ };
1211
+ break;
1212
+ case "response.output_item.done":
1213
+ if (event.item) outputItems.set(index, event.item);
1214
+ if (event.item?.type === "function_call") {
1215
+ toolCalls.set(index, {
1216
+ id: event.item.call_id,
1217
+ name: event.item.name,
1218
+ arguments: event.item.arguments
1219
+ });
1220
+ }
1221
+ break;
918
1222
  case "response.output_text.delta":
919
1223
  if (event.delta) {
1224
+ streamedText += event.delta;
920
1225
  yield { type: "message:delta", content: event.delta };
921
1226
  }
922
1227
  break;
923
1228
  case "response.output_item.added":
924
1229
  if (event.item?.type === "function_call") {
925
1230
  toolCalls.set(index, {
926
- id: event.item.call_id ?? event.item.id ?? generateToolCallId(),
1231
+ id: event.item.call_id ?? "",
927
1232
  name: event.item.name ?? "",
928
1233
  arguments: event.item.arguments ?? ""
929
1234
  });
@@ -942,7 +1247,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
942
1247
  break;
943
1248
  }
944
1249
  case "response.completed": {
1250
+ if (event.response?.status && event.response.status !== "completed")
1251
+ throw new Error(`Responses stream ${event.response.status}`);
945
1252
  completed = true;
1253
+ terminalResponse = { ...responseIdentity, ...event.response };
946
1254
  const found = this.collectResponsesCitations(
947
1255
  event.response?.output,
948
1256
  citations.length
@@ -979,12 +1287,66 @@ var OpenAIAdapter = class _OpenAIAdapter {
979
1287
  throwIfAborted(request.signal);
980
1288
  if (!completed)
981
1289
  throw new Error("Responses stream ended without a completed response");
1290
+ const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
1291
+ if (output.some((item) => item.type === "function_call"))
1292
+ toolCalls.clear();
1293
+ for (const [index, item] of output.entries()) {
1294
+ if (item.type === "function_call") {
1295
+ toolCalls.set(index, {
1296
+ id: item.call_id,
1297
+ name: item.name,
1298
+ arguments: item.arguments
1299
+ });
1300
+ }
1301
+ }
1302
+ const uniqueCalls = /* @__PURE__ */ new Map();
1303
+ for (const call of toolCalls.values()) {
1304
+ if (!call.id || !call.name)
1305
+ throw invalidProviderResponse(
1306
+ "Responses stream contains an incomplete tool call"
1307
+ );
1308
+ uniqueCalls.set(call.id, call);
1309
+ }
1310
+ toolCalls.clear();
1311
+ [...uniqueCalls.values()].forEach(
1312
+ (call, index) => toolCalls.set(index, call)
1313
+ );
1314
+ if (!output.length && toolCalls.size) {
1315
+ for (const call of toolCalls.values())
1316
+ output.push({
1317
+ type: "function_call",
1318
+ call_id: call.id,
1319
+ name: call.name,
1320
+ arguments: call.arguments
1321
+ });
1322
+ }
1323
+ if (!output.length && streamedText)
1324
+ output.push({
1325
+ type: "message",
1326
+ role: "assistant",
1327
+ content: [{ type: "output_text", text: streamedText, annotations: [] }]
1328
+ });
1329
+ terminalResponse.output = output;
1330
+ const finalText = this.parseResponsesResult(terminalResponse).content;
1331
+ if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
1332
+ yield {
1333
+ type: "message:delta",
1334
+ content: finalText.slice(streamedText.length)
1335
+ };
1336
+ }
982
1337
  yield* flushToolCalls();
983
1338
  if (citations.length > 0) {
984
1339
  yield { type: "citation", citations: deduplicateCitations(citations) };
985
1340
  }
986
- yield { type: "message:end" };
987
- yield { type: "done", usage };
1341
+ yield {
1342
+ type: "message:end",
1343
+ providerMetadata: responseMetadata(terminalResponse)
1344
+ };
1345
+ yield {
1346
+ type: "done",
1347
+ usage,
1348
+ providerMetadata: responseMetadata(terminalResponse)
1349
+ };
988
1350
  }
989
1351
  async *stream(request) {
990
1352
  throwIfAborted(request.signal);