@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
  }
@@ -480,7 +585,7 @@ function formatMessages(messages) {
480
585
  return {
481
586
  role: "tool",
482
587
  tool_call_id: msg.toolCallId,
483
- content: msg.content
588
+ content: textToolOutput(msg.modelOutput, msg.content)
484
589
  };
485
590
  default:
486
591
  return msg;
@@ -488,7 +593,120 @@ function formatMessages(messages) {
488
593
  });
489
594
  }
490
595
 
596
+ // src/core/openai-responses.ts
597
+ function resolveOpenAITransport(defaults, overrides) {
598
+ return {
599
+ api: overrides?.api ?? defaults.api ?? "auto",
600
+ responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
601
+ };
602
+ }
603
+ function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
604
+ const api = transport.api ?? "auto";
605
+ if (!["auto", "chat", "responses"].includes(api)) {
606
+ throw new Error(`Unknown OpenAI API selection: ${api}`);
607
+ }
608
+ if (api === "chat") {
609
+ if (transport.responses !== void 0 || nativeToolSearch) {
610
+ throw new Error(
611
+ "Responses options and native tool search require the Responses API"
612
+ );
613
+ }
614
+ return false;
615
+ }
616
+ let officialEndpoint = !baseURL;
617
+ try {
618
+ officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
619
+ } catch {
620
+ }
621
+ return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
622
+ }
623
+ function responseMetadata(response) {
624
+ const output = Array.isArray(response?.output) ? response.output : [];
625
+ return {
626
+ openai: {
627
+ response: {
628
+ id: response?.id,
629
+ model: response?.model,
630
+ status: response?.status,
631
+ output,
632
+ usage: response?.usage,
633
+ store: response?.store,
634
+ refusals: output.flatMap(
635
+ (item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
636
+ (part) => part?.type === "refusal" && typeof part.refusal === "string"
637
+ ).map((part) => part.refusal) : []
638
+ )
639
+ }
640
+ }
641
+ };
642
+ }
643
+ function responseReplayMetadata(metadata) {
644
+ const response = metadata?.openai?.response;
645
+ if (!response) return metadata;
646
+ const output = response.output.map((item) => {
647
+ const fields = ["type", "id", "status"];
648
+ switch (item.type) {
649
+ case "reasoning":
650
+ fields.push("encrypted_content");
651
+ break;
652
+ case "message":
653
+ fields.push("role", "phase");
654
+ break;
655
+ case "function_call":
656
+ fields.push("call_id", "name", "namespace", "arguments");
657
+ break;
658
+ case "tool_search_call":
659
+ fields.push("call_id", "execution", "arguments");
660
+ break;
661
+ case "tool_search_output":
662
+ fields.push("call_id", "tools");
663
+ break;
664
+ case "web_search_call":
665
+ fields.push("action");
666
+ break;
667
+ default:
668
+ return stripItemDiagnostics(item);
669
+ }
670
+ const replay = Object.fromEntries(
671
+ fields.filter((key) => item[key] !== void 0).map((key) => [key, item[key]])
672
+ );
673
+ if (item.type === "reasoning") replay.summary = [];
674
+ if (item.type === "message")
675
+ replay.content = Array.isArray(item.content) ? item.content.flatMap(
676
+ (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 }] : []
677
+ ) : [];
678
+ return stripItemDiagnostics(replay);
679
+ });
680
+ return { openai: { response: { output, refusals: [] } } };
681
+ }
682
+ function stripItemDiagnostics(item) {
683
+ const {
684
+ usage: _usage,
685
+ logprobs: _logprobs,
686
+ metadata: _metadata,
687
+ ...state
688
+ } = item;
689
+ return state;
690
+ }
691
+ function getResponseReplay(metadata) {
692
+ const response = metadata?.openai?.response;
693
+ if (response === void 0) return void 0;
694
+ if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
695
+ throw new Error(
696
+ "Invalid OpenAI Responses replay metadata: output must contain typed items"
697
+ );
698
+ }
699
+ return responseReplayMetadata(metadata)?.openai?.response?.output;
700
+ }
701
+
491
702
  // src/core/provider-error.ts
703
+ function invalidProviderResponse(message) {
704
+ return Object.assign(new Error(message), {
705
+ name: "InvalidProviderResponseError",
706
+ status: 502,
707
+ code: "INVALID_PROVIDER_RESPONSE"
708
+ });
709
+ }
492
710
  function providerErrorEvent(error, code) {
493
711
  const details = error ?? {};
494
712
  const event = {
@@ -564,13 +782,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
564
782
  }));
565
783
  }
566
784
  shouldUseResponsesApi(request) {
567
- if (this.toolDefinitions(request).length === 0) return false;
568
- if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false)
569
- return true;
570
- return requiresOpenAIResponses(
785
+ return useOpenAIResponses(
571
786
  request.config?.model || this.model,
572
787
  this.config.baseUrl,
573
- this.parameterOptions(request)
788
+ this.parameterOptions(request),
789
+ resolveOpenAITransport(this.config, request.config?.openai),
790
+ this.toolDefinitions(request).length > 0,
791
+ !!(request.rawMessages ?? request.messages)?.some(
792
+ (message) => message.providerMetadata?.openai?.response
793
+ ),
794
+ request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
574
795
  );
575
796
  }
576
797
  /**
@@ -603,11 +824,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
603
824
  });
604
825
  }
605
826
  buildResponsesInput(request) {
606
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages) : formatMessagesForOpenAI(request.messages, void 0);
827
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
607
828
  const input = [];
608
829
  for (const message of sourceMessages) {
609
830
  if (message.role === "system") {
610
- if (!request.systemPrompt && message.content) {
831
+ if (message.content && message.content !== request.systemPrompt) {
611
832
  input.push({
612
833
  type: "message",
613
834
  role: "developer",
@@ -617,6 +838,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
617
838
  continue;
618
839
  }
619
840
  if (message.role === "assistant") {
841
+ const replay = getResponseReplay(message.providerMetadata);
842
+ if (replay) {
843
+ input.push(...replay);
844
+ continue;
845
+ }
620
846
  const content = this.toResponsesContent(message.content, "assistant");
621
847
  if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
622
848
  input.push({
@@ -640,7 +866,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
640
866
  input.push({
641
867
  type: "function_call_output",
642
868
  call_id: message.tool_call_id,
643
- output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
869
+ output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
644
870
  });
645
871
  continue;
646
872
  }
@@ -654,10 +880,12 @@ var OpenAIAdapter = class _OpenAIAdapter {
654
880
  return input;
655
881
  }
656
882
  /** Preserve Copilot screenshot attachments on every OpenAI transport. */
657
- formatRawMessages(messages) {
883
+ formatRawMessages(messages, preserveProviderMetadata = false) {
658
884
  return messages.map((message) => {
659
- const { attachments, ...rest } = message;
660
- let content = message.content;
885
+ const { attachments, providerMetadata, modelOutput, ...rest } = message;
886
+ let content = message.role === "tool" && !preserveProviderMetadata && modelOutput !== void 0 ? textToolOutput(modelOutput, "") : message.content;
887
+ if (preserveProviderMetadata && modelOutput)
888
+ rest.modelOutput = modelOutput;
661
889
  if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
662
890
  content = null;
663
891
  }
@@ -670,7 +898,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
670
898
  content = [...parts, ...images];
671
899
  }
672
900
  }
673
- return { ...rest, content };
901
+ return {
902
+ ...rest,
903
+ content,
904
+ ...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
905
+ };
674
906
  });
675
907
  }
676
908
  /**
@@ -751,14 +983,40 @@ var OpenAIAdapter = class _OpenAIAdapter {
751
983
  ).map((part) => part.text) : []
752
984
  ).join("") : "";
753
985
  const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
754
- const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
755
- id: item.call_id ?? item.id ?? generateToolCallId(),
756
- name: item.name,
757
- args: JSON.parse(item.arguments ?? "{}")
758
- })) : [];
986
+ const seenCallIds = /* @__PURE__ */ new Set();
987
+ const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
988
+ if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
989
+ throw invalidProviderResponse(
990
+ "Responses contains an incomplete function call"
991
+ );
992
+ }
993
+ if (seenCallIds.has(item.call_id))
994
+ throw invalidProviderResponse(
995
+ "Responses contains duplicate function call IDs"
996
+ );
997
+ seenCallIds.add(item.call_id);
998
+ let args;
999
+ try {
1000
+ args = JSON.parse(item.arguments);
1001
+ } catch {
1002
+ throw invalidProviderResponse(
1003
+ "Responses function arguments must be valid JSON"
1004
+ );
1005
+ }
1006
+ if (!args || typeof args !== "object" || Array.isArray(args))
1007
+ throw invalidProviderResponse(
1008
+ "Responses function arguments must be a JSON object"
1009
+ );
1010
+ return {
1011
+ id: item.call_id,
1012
+ name: item.name,
1013
+ args
1014
+ };
1015
+ }) : [];
759
1016
  return {
760
1017
  content,
761
1018
  toolCalls,
1019
+ providerMetadata: responseMetadata(response),
762
1020
  usage: response?.usage ? {
763
1021
  promptTokens: response.usage.input_tokens ?? 0,
764
1022
  completionTokens: response.usage.output_tokens ?? 0,
@@ -769,6 +1027,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
769
1027
  }
770
1028
  buildResponsesPayload(request, stream) {
771
1029
  const openaiToolOptions = request.providerToolOptions?.openai;
1030
+ const options = resolveOpenAITransport(
1031
+ this.config,
1032
+ request.config?.openai
1033
+ ).responses;
1034
+ const parameters = buildOpenAIParameters(
1035
+ request.config?.model || this.model,
1036
+ request.config?.maxTokens ?? this.config.maxTokens,
1037
+ request.config?.temperature ?? this.config.temperature,
1038
+ this.parameterOptions(request),
1039
+ "responses"
1040
+ );
772
1041
  const responsesTextFormat = toOpenAIResponsesTextFormat(
773
1042
  request.config?.responseFormat
774
1043
  );
@@ -784,13 +1053,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
784
1053
  ),
785
1054
  tool_choice: openaiToolOptions?.toolChoice,
786
1055
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
787
- ...buildOpenAIParameters(
788
- modelIdForPayload,
789
- request.config?.maxTokens ?? this.config.maxTokens,
790
- request.config?.temperature ?? this.config.temperature,
791
- this.parameterOptions(request),
792
- "responses"
793
- ),
1056
+ ...parameters,
1057
+ ...options?.reasoningSummary ? {
1058
+ reasoning: {
1059
+ ...parameters.reasoning,
1060
+ summary: options.reasoningSummary
1061
+ }
1062
+ } : {},
1063
+ ...options?.store !== void 0 ? { store: options.store } : {},
1064
+ ...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
1065
+ ...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
1066
+ ...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
1067
+ ...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
1068
+ ...options?.store === false || options?.include ? {
1069
+ include: [
1070
+ .../* @__PURE__ */ new Set([
1071
+ ...options?.include ?? [],
1072
+ ...options?.store === false ? ["reasoning.encrypted_content"] : []
1073
+ ])
1074
+ ]
1075
+ } : {},
794
1076
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
795
1077
  stream
796
1078
  };
@@ -849,6 +1131,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
849
1131
  let usage;
850
1132
  const citations = [];
851
1133
  let completed = false;
1134
+ let terminalResponse;
1135
+ let responseIdentity = {};
1136
+ const outputItems = /* @__PURE__ */ new Map();
1137
+ let streamedText = "";
852
1138
  function* flushToolCalls() {
853
1139
  for (const call of toolCalls.values()) {
854
1140
  if (!call.name)
@@ -865,17 +1151,36 @@ var OpenAIAdapter = class _OpenAIAdapter {
865
1151
  for await (const event of stream) {
866
1152
  throwIfAborted(request.signal);
867
1153
  logProviderPayload("openai", "stream chunk", event, request.debug);
1154
+ if (completed)
1155
+ throw new Error("Responses stream emitted an event after completion");
868
1156
  const index = event?.output_index ?? 0;
869
1157
  switch (event?.type) {
1158
+ case "response.created":
1159
+ responseIdentity = {
1160
+ id: event.response?.id,
1161
+ model: event.response?.model
1162
+ };
1163
+ break;
1164
+ case "response.output_item.done":
1165
+ if (event.item) outputItems.set(index, event.item);
1166
+ if (event.item?.type === "function_call") {
1167
+ toolCalls.set(index, {
1168
+ id: event.item.call_id,
1169
+ name: event.item.name,
1170
+ arguments: event.item.arguments
1171
+ });
1172
+ }
1173
+ break;
870
1174
  case "response.output_text.delta":
871
1175
  if (event.delta) {
1176
+ streamedText += event.delta;
872
1177
  yield { type: "message:delta", content: event.delta };
873
1178
  }
874
1179
  break;
875
1180
  case "response.output_item.added":
876
1181
  if (event.item?.type === "function_call") {
877
1182
  toolCalls.set(index, {
878
- id: event.item.call_id ?? event.item.id ?? generateToolCallId(),
1183
+ id: event.item.call_id ?? "",
879
1184
  name: event.item.name ?? "",
880
1185
  arguments: event.item.arguments ?? ""
881
1186
  });
@@ -894,7 +1199,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
894
1199
  break;
895
1200
  }
896
1201
  case "response.completed": {
1202
+ if (event.response?.status && event.response.status !== "completed")
1203
+ throw new Error(`Responses stream ${event.response.status}`);
897
1204
  completed = true;
1205
+ terminalResponse = { ...responseIdentity, ...event.response };
898
1206
  const found = this.collectResponsesCitations(
899
1207
  event.response?.output,
900
1208
  citations.length
@@ -931,12 +1239,66 @@ var OpenAIAdapter = class _OpenAIAdapter {
931
1239
  throwIfAborted(request.signal);
932
1240
  if (!completed)
933
1241
  throw new Error("Responses stream ended without a completed response");
1242
+ const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
1243
+ if (output.some((item) => item.type === "function_call"))
1244
+ toolCalls.clear();
1245
+ for (const [index, item] of output.entries()) {
1246
+ if (item.type === "function_call") {
1247
+ toolCalls.set(index, {
1248
+ id: item.call_id,
1249
+ name: item.name,
1250
+ arguments: item.arguments
1251
+ });
1252
+ }
1253
+ }
1254
+ const uniqueCalls = /* @__PURE__ */ new Map();
1255
+ for (const call of toolCalls.values()) {
1256
+ if (!call.id || !call.name)
1257
+ throw invalidProviderResponse(
1258
+ "Responses stream contains an incomplete tool call"
1259
+ );
1260
+ uniqueCalls.set(call.id, call);
1261
+ }
1262
+ toolCalls.clear();
1263
+ [...uniqueCalls.values()].forEach(
1264
+ (call, index) => toolCalls.set(index, call)
1265
+ );
1266
+ if (!output.length && toolCalls.size) {
1267
+ for (const call of toolCalls.values())
1268
+ output.push({
1269
+ type: "function_call",
1270
+ call_id: call.id,
1271
+ name: call.name,
1272
+ arguments: call.arguments
1273
+ });
1274
+ }
1275
+ if (!output.length && streamedText)
1276
+ output.push({
1277
+ type: "message",
1278
+ role: "assistant",
1279
+ content: [{ type: "output_text", text: streamedText, annotations: [] }]
1280
+ });
1281
+ terminalResponse.output = output;
1282
+ const finalText = this.parseResponsesResult(terminalResponse).content;
1283
+ if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
1284
+ yield {
1285
+ type: "message:delta",
1286
+ content: finalText.slice(streamedText.length)
1287
+ };
1288
+ }
934
1289
  yield* flushToolCalls();
935
1290
  if (citations.length > 0) {
936
1291
  yield { type: "citation", citations: deduplicateCitations(citations) };
937
1292
  }
938
- yield { type: "message:end" };
939
- yield { type: "done", usage };
1293
+ yield {
1294
+ type: "message:end",
1295
+ providerMetadata: responseMetadata(terminalResponse)
1296
+ };
1297
+ yield {
1298
+ type: "done",
1299
+ usage,
1300
+ providerMetadata: responseMetadata(terminalResponse)
1301
+ };
940
1302
  }
941
1303
  async *stream(request) {
942
1304
  throwIfAborted(request.signal);