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