@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,3 +1,106 @@
1
+ // src/core/tool-output.ts
2
+ var imageTypes = /* @__PURE__ */ new Set([
3
+ "image/png",
4
+ "image/jpeg",
5
+ "image/webp",
6
+ "image/gif"
7
+ ]);
8
+ function normalizeToolOutput(value) {
9
+ const output = value;
10
+ if (output?.type !== "content" || !Array.isArray(output.content) || !output.content.length) {
11
+ throw new Error("Tool model output must contain a nonempty content array");
12
+ }
13
+ return {
14
+ type: "content",
15
+ content: output.content.map((part) => {
16
+ if (part?.type === "text" && typeof part.text === "string")
17
+ return { type: "text", text: part.text };
18
+ if (part?.type !== "image")
19
+ throw new Error("Unsupported tool output content type");
20
+ const sources = ["data", "url", "fileId"].filter((key) => key in part);
21
+ if (sources.length !== 1)
22
+ throw new Error(
23
+ "Tool image requires exactly one of data, url, or fileId"
24
+ );
25
+ if ("fileId" in part) {
26
+ if (part.provider !== "openai" || typeof part.fileId !== "string" || !part.fileId.trim())
27
+ throw new Error(
28
+ "Tool image file references must name the openai provider and fileId"
29
+ );
30
+ return { type: "image", fileId: part.fileId, provider: "openai" };
31
+ }
32
+ if ("url" in part) {
33
+ let url;
34
+ try {
35
+ url = new URL(part.url);
36
+ } catch {
37
+ throw new Error("Invalid tool image URL");
38
+ }
39
+ if (!["https:", "http:"].includes(url.protocol) || url.username || url.password)
40
+ throw new Error(
41
+ "Tool image URL must use HTTP(S) without credentials"
42
+ );
43
+ return { type: "image", url: url.href };
44
+ }
45
+ if (!("data" in part) || !imageTypes.has(part.mediaType))
46
+ throw new Error("Unsupported tool image media type");
47
+ let data = part.data instanceof Uint8Array ? Buffer.from(part.data).toString("base64") : part.data;
48
+ if (typeof data !== "string")
49
+ throw new Error("Tool image data must be base64 or bytes");
50
+ if (data.startsWith("data:")) {
51
+ const match = /^data:([^;,]+);base64,(.+)$/.exec(data);
52
+ if (!match || match[1] !== part.mediaType)
53
+ throw new Error(
54
+ "Tool image data URL media type does not match mediaType"
55
+ );
56
+ data = match[2];
57
+ }
58
+ if (!data.length || data.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(data))
59
+ throw new Error("Invalid tool image base64 data");
60
+ if (Buffer.byteLength(data, "base64") > 20 * 1024 * 1024)
61
+ throw new Error("Tool image exceeds 20 MiB");
62
+ if (Buffer.from(data, "base64").toString("base64") !== data)
63
+ throw new Error("Invalid tool image base64 data");
64
+ return { type: "image", data, mediaType: part.mediaType };
65
+ })
66
+ };
67
+ }
68
+ function toolOutputForProvider(value, provider) {
69
+ const output = normalizeToolOutput(value);
70
+ return output.content.map((part) => {
71
+ if (part.type === "text")
72
+ return {
73
+ type: provider === "responses" ? "input_text" : "text",
74
+ text: part.text
75
+ };
76
+ if (provider === "unsupported")
77
+ throw new Error(
78
+ "Image tool results require OpenAI Responses or Anthropic; this endpoint is not supported"
79
+ );
80
+ if ("fileId" in part) {
81
+ if (provider !== "responses")
82
+ throw new Error(
83
+ "OpenAI image file references cannot be sent to another provider"
84
+ );
85
+ return { type: "input_image", file_id: part.fileId, detail: "auto" };
86
+ }
87
+ if (provider === "responses")
88
+ return {
89
+ type: "input_image",
90
+ image_url: "url" in part ? part.url : `data:${part.mediaType};base64,${part.data}`,
91
+ detail: "auto"
92
+ };
93
+ return {
94
+ type: "image",
95
+ source: "url" in part ? { type: "url", url: part.url } : { type: "base64", media_type: part.mediaType, data: part.data }
96
+ };
97
+ });
98
+ }
99
+ function textToolOutput(value, fallback) {
100
+ if (value === void 0) return fallback;
101
+ return toolOutputForProvider(value, "unsupported").map((part) => part.text).join("\n");
102
+ }
103
+
1
104
  // src/core/openai-parameters.ts
2
105
  function capabilities(model) {
3
106
  const gpt = /^gpt-(\d+)(?:\.(\d+))?(?:-(.+))?$/i.exec(model);
@@ -239,7 +342,7 @@ function messageToOpenAIContent(message) {
239
342
  }
240
343
  return blocks;
241
344
  }
242
- function formatMessagesForOpenAI(messages, systemPrompt) {
345
+ function formatMessagesForOpenAI(messages, systemPrompt, preserveProviderMetadata = false) {
243
346
  const formatted = [];
244
347
  if (systemPrompt) {
245
348
  formatted.push({ role: "system", content: systemPrompt });
@@ -257,7 +360,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
257
360
  const assistantMsg = {
258
361
  role: "assistant",
259
362
  // Gemini/xAI (OpenAI-compatible) reject content: "" on assistant messages with tool_calls
260
- content: hasToolCalls ? msg.content || null : msg.content
363
+ content: hasToolCalls ? msg.content || null : msg.content,
364
+ ...preserveProviderMetadata && msg.providerMetadata ? { providerMetadata: msg.providerMetadata } : {}
261
365
  };
262
366
  if (hasToolCalls) {
263
367
  assistantMsg.tool_calls = msg.tool_calls;
@@ -266,7 +370,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
266
370
  } else if (msg.role === "tool" && msg.tool_call_id) {
267
371
  formatted.push({
268
372
  role: "tool",
269
- content: msg.content ?? "",
373
+ content: preserveProviderMetadata ? msg.content ?? "" : textToolOutput(msg.modelOutput, msg.content ?? ""),
374
+ ...preserveProviderMetadata && msg.modelOutput ? { modelOutput: msg.modelOutput } : {},
270
375
  tool_call_id: msg.tool_call_id
271
376
  });
272
377
  }
@@ -502,7 +607,7 @@ function formatMessagesForXAI(messages) {
502
607
  return {
503
608
  role: "tool",
504
609
  tool_call_id: msg.toolCallId,
505
- content: msg.content
610
+ content: textToolOutput(msg.modelOutput, msg.content)
506
611
  };
507
612
  default:
508
613
  return msg;
@@ -510,7 +615,120 @@ function formatMessagesForXAI(messages) {
510
615
  });
511
616
  }
512
617
 
618
+ // src/core/openai-responses.ts
619
+ function resolveOpenAITransport(defaults, overrides) {
620
+ return {
621
+ api: overrides?.api ?? defaults.api ?? "auto",
622
+ responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
623
+ };
624
+ }
625
+ function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
626
+ const api = transport.api ?? "auto";
627
+ if (!["auto", "chat", "responses"].includes(api)) {
628
+ throw new Error(`Unknown OpenAI API selection: ${api}`);
629
+ }
630
+ if (api === "chat") {
631
+ if (transport.responses !== void 0 || nativeToolSearch) {
632
+ throw new Error(
633
+ "Responses options and native tool search require the Responses API"
634
+ );
635
+ }
636
+ return false;
637
+ }
638
+ let officialEndpoint = !baseURL;
639
+ try {
640
+ officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
641
+ } catch {
642
+ }
643
+ return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
644
+ }
645
+ function responseMetadata(response) {
646
+ const output = Array.isArray(response?.output) ? response.output : [];
647
+ return {
648
+ openai: {
649
+ response: {
650
+ id: response?.id,
651
+ model: response?.model,
652
+ status: response?.status,
653
+ output,
654
+ usage: response?.usage,
655
+ store: response?.store,
656
+ refusals: output.flatMap(
657
+ (item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
658
+ (part) => part?.type === "refusal" && typeof part.refusal === "string"
659
+ ).map((part) => part.refusal) : []
660
+ )
661
+ }
662
+ }
663
+ };
664
+ }
665
+ function responseReplayMetadata(metadata) {
666
+ const response = metadata?.openai?.response;
667
+ if (!response) return metadata;
668
+ const output = response.output.map((item) => {
669
+ const fields = ["type", "id", "status"];
670
+ switch (item.type) {
671
+ case "reasoning":
672
+ fields.push("encrypted_content");
673
+ break;
674
+ case "message":
675
+ fields.push("role", "phase");
676
+ break;
677
+ case "function_call":
678
+ fields.push("call_id", "name", "namespace", "arguments");
679
+ break;
680
+ case "tool_search_call":
681
+ fields.push("call_id", "execution", "arguments");
682
+ break;
683
+ case "tool_search_output":
684
+ fields.push("call_id", "tools");
685
+ break;
686
+ case "web_search_call":
687
+ fields.push("action");
688
+ break;
689
+ default:
690
+ return stripItemDiagnostics(item);
691
+ }
692
+ const replay = Object.fromEntries(
693
+ fields.filter((key) => item[key] !== void 0).map((key) => [key, item[key]])
694
+ );
695
+ if (item.type === "reasoning") replay.summary = [];
696
+ if (item.type === "message")
697
+ replay.content = Array.isArray(item.content) ? item.content.flatMap(
698
+ (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 }] : []
699
+ ) : [];
700
+ return stripItemDiagnostics(replay);
701
+ });
702
+ return { openai: { response: { output, refusals: [] } } };
703
+ }
704
+ function stripItemDiagnostics(item) {
705
+ const {
706
+ usage: _usage,
707
+ logprobs: _logprobs,
708
+ metadata: _metadata,
709
+ ...state
710
+ } = item;
711
+ return state;
712
+ }
713
+ function getResponseReplay(metadata) {
714
+ const response = metadata?.openai?.response;
715
+ if (response === void 0) return void 0;
716
+ if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
717
+ throw new Error(
718
+ "Invalid OpenAI Responses replay metadata: output must contain typed items"
719
+ );
720
+ }
721
+ return responseReplayMetadata(metadata)?.openai?.response?.output;
722
+ }
723
+
513
724
  // src/core/provider-error.ts
725
+ function invalidProviderResponse(message) {
726
+ return Object.assign(new Error(message), {
727
+ name: "InvalidProviderResponseError",
728
+ status: 502,
729
+ code: "INVALID_PROVIDER_RESPONSE"
730
+ });
731
+ }
514
732
  function providerErrorEvent(error, code) {
515
733
  const details = error ?? {};
516
734
  const event = {
@@ -586,13 +804,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
586
804
  }));
587
805
  }
588
806
  shouldUseResponsesApi(request) {
589
- if (this.toolDefinitions(request).length === 0) return false;
590
- if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false)
591
- return true;
592
- return requiresOpenAIResponses(
807
+ return useOpenAIResponses(
593
808
  request.config?.model || this.model,
594
809
  this.config.baseUrl,
595
- this.parameterOptions(request)
810
+ this.parameterOptions(request),
811
+ resolveOpenAITransport(this.config, request.config?.openai),
812
+ this.toolDefinitions(request).length > 0,
813
+ !!(request.rawMessages ?? request.messages)?.some(
814
+ (message) => message.providerMetadata?.openai?.response
815
+ ),
816
+ request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
596
817
  );
597
818
  }
598
819
  /**
@@ -625,11 +846,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
625
846
  });
626
847
  }
627
848
  buildResponsesInput(request) {
628
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages) : formatMessagesForOpenAI(request.messages, void 0);
849
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
629
850
  const input = [];
630
851
  for (const message of sourceMessages) {
631
852
  if (message.role === "system") {
632
- if (!request.systemPrompt && message.content) {
853
+ if (message.content && message.content !== request.systemPrompt) {
633
854
  input.push({
634
855
  type: "message",
635
856
  role: "developer",
@@ -639,6 +860,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
639
860
  continue;
640
861
  }
641
862
  if (message.role === "assistant") {
863
+ const replay = getResponseReplay(message.providerMetadata);
864
+ if (replay) {
865
+ input.push(...replay);
866
+ continue;
867
+ }
642
868
  const content = this.toResponsesContent(message.content, "assistant");
643
869
  if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
644
870
  input.push({
@@ -662,7 +888,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
662
888
  input.push({
663
889
  type: "function_call_output",
664
890
  call_id: message.tool_call_id,
665
- output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
891
+ output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
666
892
  });
667
893
  continue;
668
894
  }
@@ -676,10 +902,12 @@ var OpenAIAdapter = class _OpenAIAdapter {
676
902
  return input;
677
903
  }
678
904
  /** Preserve Copilot screenshot attachments on every OpenAI transport. */
679
- formatRawMessages(messages) {
905
+ formatRawMessages(messages, preserveProviderMetadata = false) {
680
906
  return messages.map((message) => {
681
- const { attachments, ...rest } = message;
682
- let content = message.content;
907
+ const { attachments, providerMetadata, modelOutput, ...rest } = message;
908
+ let content = message.role === "tool" && !preserveProviderMetadata && modelOutput !== void 0 ? textToolOutput(modelOutput, "") : message.content;
909
+ if (preserveProviderMetadata && modelOutput)
910
+ rest.modelOutput = modelOutput;
683
911
  if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
684
912
  content = null;
685
913
  }
@@ -692,7 +920,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
692
920
  content = [...parts, ...images];
693
921
  }
694
922
  }
695
- return { ...rest, content };
923
+ return {
924
+ ...rest,
925
+ content,
926
+ ...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
927
+ };
696
928
  });
697
929
  }
698
930
  /**
@@ -773,14 +1005,40 @@ var OpenAIAdapter = class _OpenAIAdapter {
773
1005
  ).map((part) => part.text) : []
774
1006
  ).join("") : "";
775
1007
  const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
776
- const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
777
- id: item.call_id ?? item.id ?? generateToolCallId(),
778
- name: item.name,
779
- args: JSON.parse(item.arguments ?? "{}")
780
- })) : [];
1008
+ const seenCallIds = /* @__PURE__ */ new Set();
1009
+ const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
1010
+ if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
1011
+ throw invalidProviderResponse(
1012
+ "Responses contains an incomplete function call"
1013
+ );
1014
+ }
1015
+ if (seenCallIds.has(item.call_id))
1016
+ throw invalidProviderResponse(
1017
+ "Responses contains duplicate function call IDs"
1018
+ );
1019
+ seenCallIds.add(item.call_id);
1020
+ let args;
1021
+ try {
1022
+ args = JSON.parse(item.arguments);
1023
+ } catch {
1024
+ throw invalidProviderResponse(
1025
+ "Responses function arguments must be valid JSON"
1026
+ );
1027
+ }
1028
+ if (!args || typeof args !== "object" || Array.isArray(args))
1029
+ throw invalidProviderResponse(
1030
+ "Responses function arguments must be a JSON object"
1031
+ );
1032
+ return {
1033
+ id: item.call_id,
1034
+ name: item.name,
1035
+ args
1036
+ };
1037
+ }) : [];
781
1038
  return {
782
1039
  content,
783
1040
  toolCalls,
1041
+ providerMetadata: responseMetadata(response),
784
1042
  usage: response?.usage ? {
785
1043
  promptTokens: response.usage.input_tokens ?? 0,
786
1044
  completionTokens: response.usage.output_tokens ?? 0,
@@ -791,6 +1049,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
791
1049
  }
792
1050
  buildResponsesPayload(request, stream) {
793
1051
  const openaiToolOptions = request.providerToolOptions?.openai;
1052
+ const options = resolveOpenAITransport(
1053
+ this.config,
1054
+ request.config?.openai
1055
+ ).responses;
1056
+ const parameters = buildOpenAIParameters(
1057
+ request.config?.model || this.model,
1058
+ request.config?.maxTokens ?? this.config.maxTokens,
1059
+ request.config?.temperature ?? this.config.temperature,
1060
+ this.parameterOptions(request),
1061
+ "responses"
1062
+ );
794
1063
  const responsesTextFormat = toOpenAIResponsesTextFormat(
795
1064
  request.config?.responseFormat
796
1065
  );
@@ -806,13 +1075,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
806
1075
  ),
807
1076
  tool_choice: openaiToolOptions?.toolChoice,
808
1077
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
809
- ...buildOpenAIParameters(
810
- modelIdForPayload,
811
- request.config?.maxTokens ?? this.config.maxTokens,
812
- request.config?.temperature ?? this.config.temperature,
813
- this.parameterOptions(request),
814
- "responses"
815
- ),
1078
+ ...parameters,
1079
+ ...options?.reasoningSummary ? {
1080
+ reasoning: {
1081
+ ...parameters.reasoning,
1082
+ summary: options.reasoningSummary
1083
+ }
1084
+ } : {},
1085
+ ...options?.store !== void 0 ? { store: options.store } : {},
1086
+ ...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
1087
+ ...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
1088
+ ...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
1089
+ ...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
1090
+ ...options?.store === false || options?.include ? {
1091
+ include: [
1092
+ .../* @__PURE__ */ new Set([
1093
+ ...options?.include ?? [],
1094
+ ...options?.store === false ? ["reasoning.encrypted_content"] : []
1095
+ ])
1096
+ ]
1097
+ } : {},
816
1098
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
817
1099
  stream
818
1100
  };
@@ -871,6 +1153,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
871
1153
  let usage;
872
1154
  const citations = [];
873
1155
  let completed = false;
1156
+ let terminalResponse;
1157
+ let responseIdentity = {};
1158
+ const outputItems = /* @__PURE__ */ new Map();
1159
+ let streamedText = "";
874
1160
  function* flushToolCalls() {
875
1161
  for (const call of toolCalls.values()) {
876
1162
  if (!call.name)
@@ -887,17 +1173,36 @@ var OpenAIAdapter = class _OpenAIAdapter {
887
1173
  for await (const event of stream) {
888
1174
  throwIfAborted(request.signal);
889
1175
  logProviderPayload("openai", "stream chunk", event, request.debug);
1176
+ if (completed)
1177
+ throw new Error("Responses stream emitted an event after completion");
890
1178
  const index = event?.output_index ?? 0;
891
1179
  switch (event?.type) {
1180
+ case "response.created":
1181
+ responseIdentity = {
1182
+ id: event.response?.id,
1183
+ model: event.response?.model
1184
+ };
1185
+ break;
1186
+ case "response.output_item.done":
1187
+ if (event.item) outputItems.set(index, event.item);
1188
+ if (event.item?.type === "function_call") {
1189
+ toolCalls.set(index, {
1190
+ id: event.item.call_id,
1191
+ name: event.item.name,
1192
+ arguments: event.item.arguments
1193
+ });
1194
+ }
1195
+ break;
892
1196
  case "response.output_text.delta":
893
1197
  if (event.delta) {
1198
+ streamedText += event.delta;
894
1199
  yield { type: "message:delta", content: event.delta };
895
1200
  }
896
1201
  break;
897
1202
  case "response.output_item.added":
898
1203
  if (event.item?.type === "function_call") {
899
1204
  toolCalls.set(index, {
900
- id: event.item.call_id ?? event.item.id ?? generateToolCallId(),
1205
+ id: event.item.call_id ?? "",
901
1206
  name: event.item.name ?? "",
902
1207
  arguments: event.item.arguments ?? ""
903
1208
  });
@@ -916,7 +1221,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
916
1221
  break;
917
1222
  }
918
1223
  case "response.completed": {
1224
+ if (event.response?.status && event.response.status !== "completed")
1225
+ throw new Error(`Responses stream ${event.response.status}`);
919
1226
  completed = true;
1227
+ terminalResponse = { ...responseIdentity, ...event.response };
920
1228
  const found = this.collectResponsesCitations(
921
1229
  event.response?.output,
922
1230
  citations.length
@@ -953,12 +1261,66 @@ var OpenAIAdapter = class _OpenAIAdapter {
953
1261
  throwIfAborted(request.signal);
954
1262
  if (!completed)
955
1263
  throw new Error("Responses stream ended without a completed response");
1264
+ const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
1265
+ if (output.some((item) => item.type === "function_call"))
1266
+ toolCalls.clear();
1267
+ for (const [index, item] of output.entries()) {
1268
+ if (item.type === "function_call") {
1269
+ toolCalls.set(index, {
1270
+ id: item.call_id,
1271
+ name: item.name,
1272
+ arguments: item.arguments
1273
+ });
1274
+ }
1275
+ }
1276
+ const uniqueCalls = /* @__PURE__ */ new Map();
1277
+ for (const call of toolCalls.values()) {
1278
+ if (!call.id || !call.name)
1279
+ throw invalidProviderResponse(
1280
+ "Responses stream contains an incomplete tool call"
1281
+ );
1282
+ uniqueCalls.set(call.id, call);
1283
+ }
1284
+ toolCalls.clear();
1285
+ [...uniqueCalls.values()].forEach(
1286
+ (call, index) => toolCalls.set(index, call)
1287
+ );
1288
+ if (!output.length && toolCalls.size) {
1289
+ for (const call of toolCalls.values())
1290
+ output.push({
1291
+ type: "function_call",
1292
+ call_id: call.id,
1293
+ name: call.name,
1294
+ arguments: call.arguments
1295
+ });
1296
+ }
1297
+ if (!output.length && streamedText)
1298
+ output.push({
1299
+ type: "message",
1300
+ role: "assistant",
1301
+ content: [{ type: "output_text", text: streamedText, annotations: [] }]
1302
+ });
1303
+ terminalResponse.output = output;
1304
+ const finalText = this.parseResponsesResult(terminalResponse).content;
1305
+ if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
1306
+ yield {
1307
+ type: "message:delta",
1308
+ content: finalText.slice(streamedText.length)
1309
+ };
1310
+ }
956
1311
  yield* flushToolCalls();
957
1312
  if (citations.length > 0) {
958
1313
  yield { type: "citation", citations: deduplicateCitations(citations) };
959
1314
  }
960
- yield { type: "message:end" };
961
- yield { type: "done", usage };
1315
+ yield {
1316
+ type: "message:end",
1317
+ providerMetadata: responseMetadata(terminalResponse)
1318
+ };
1319
+ yield {
1320
+ type: "done",
1321
+ usage,
1322
+ providerMetadata: responseMetadata(terminalResponse)
1323
+ };
962
1324
  }
963
1325
  async *stream(request) {
964
1326
  throwIfAborted(request.signal);
@@ -1,4 +1,4 @@
1
- import { L as LLMAdapter } from './base-CkeT7u-O.mjs';
1
+ import { L as LLMAdapter } from './base-BICaQaRS.mjs';
2
2
 
3
3
  /**
4
4
  * Fallback Chain & Routing Strategy Types
@@ -1,4 +1,4 @@
1
- import { L as LLMAdapter } from './base-BdTpUAWI.js';
1
+ import { L as LLMAdapter } from './base-CpRPB8u8.js';
2
2
 
3
3
  /**
4
4
  * Fallback Chain & Routing Strategy Types
@@ -1,5 +1,5 @@
1
- import { a7 as OpenAIParameterOptions, d as ToolDefinition, _ as UnifiedToolCall, $ as UnifiedToolResult } from './types-BW3csYo5.mjs';
2
- import { L as LLMAdapter } from './base-CkeT7u-O.mjs';
1
+ import { ae as OpenAIParameterOptions, O as OpenAITransportOptions, e as ToolDefinition, a1 as UnifiedToolCall, a2 as UnifiedToolResult } from './types-Dg1Bcg1k.mjs';
2
+ import { L as LLMAdapter } from './base-BICaQaRS.mjs';
3
3
 
4
4
  /**
5
5
  * Provider formatter interface
@@ -251,7 +251,7 @@ interface BaseProviderConfig {
251
251
  /**
252
252
  * OpenAI provider configuration
253
253
  */
254
- interface OpenAIProviderConfig extends BaseProviderConfig, OpenAIParameterOptions {
254
+ interface OpenAIProviderConfig extends BaseProviderConfig, OpenAIParameterOptions, OpenAITransportOptions {
255
255
  /** OpenAI organization ID */
256
256
  organization?: string;
257
257
  /** OpenAI project ID */