@yourgpt/llm-sdk 2.6.0-beta.1 → 2.6.0-beta.2

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 +112 -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 +372 -39
  5. package/dist/adapters/index.mjs +372 -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 +17 -35
  13. package/dist/index.d.ts +17 -35
  14. package/dist/index.js +229 -60
  15. package/dist/index.mjs +227 -61
  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 +123 -7
  19. package/dist/providers/anthropic/index.mjs +123 -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 +321 -31
  27. package/dist/providers/fireworks/index.mjs +321 -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 +321 -31
  31. package/dist/providers/google/index.mjs +321 -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 +750 -40
  39. package/dist/providers/openai/index.mjs +749 -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 +321 -31
  43. package/dist/providers/openrouter/index.mjs +321 -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 +321 -31
  47. package/dist/providers/togetherai/index.mjs +321 -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 +321 -31
  51. package/dist/providers/xai/index.mjs +321 -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,6 +615,64 @@ 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 getResponseReplay(metadata) {
666
+ const response = metadata?.openai?.response;
667
+ if (response === void 0) return void 0;
668
+ if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
669
+ throw new Error(
670
+ "Invalid OpenAI Responses replay metadata: output must contain typed items"
671
+ );
672
+ }
673
+ return response.output;
674
+ }
675
+
513
676
  // src/core/provider-error.ts
514
677
  function providerErrorEvent(error, code) {
515
678
  const details = error ?? {};
@@ -586,13 +749,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
586
749
  }));
587
750
  }
588
751
  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(
752
+ return useOpenAIResponses(
593
753
  request.config?.model || this.model,
594
754
  this.config.baseUrl,
595
- this.parameterOptions(request)
755
+ this.parameterOptions(request),
756
+ resolveOpenAITransport(this.config, request.config?.openai),
757
+ this.toolDefinitions(request).length > 0,
758
+ !!(request.rawMessages ?? request.messages)?.some(
759
+ (message) => message.providerMetadata?.openai?.response
760
+ ),
761
+ request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
596
762
  );
597
763
  }
598
764
  /**
@@ -625,11 +791,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
625
791
  });
626
792
  }
627
793
  buildResponsesInput(request) {
628
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages) : formatMessagesForOpenAI(request.messages, void 0);
794
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
629
795
  const input = [];
630
796
  for (const message of sourceMessages) {
631
797
  if (message.role === "system") {
632
- if (!request.systemPrompt && message.content) {
798
+ if (message.content && message.content !== request.systemPrompt) {
633
799
  input.push({
634
800
  type: "message",
635
801
  role: "developer",
@@ -639,6 +805,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
639
805
  continue;
640
806
  }
641
807
  if (message.role === "assistant") {
808
+ const replay = getResponseReplay(message.providerMetadata);
809
+ if (replay) {
810
+ input.push(...replay);
811
+ continue;
812
+ }
642
813
  const content = this.toResponsesContent(message.content, "assistant");
643
814
  if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
644
815
  input.push({
@@ -662,7 +833,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
662
833
  input.push({
663
834
  type: "function_call_output",
664
835
  call_id: message.tool_call_id,
665
- output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
836
+ output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
666
837
  });
667
838
  continue;
668
839
  }
@@ -676,10 +847,12 @@ var OpenAIAdapter = class _OpenAIAdapter {
676
847
  return input;
677
848
  }
678
849
  /** Preserve Copilot screenshot attachments on every OpenAI transport. */
679
- formatRawMessages(messages) {
850
+ formatRawMessages(messages, preserveProviderMetadata = false) {
680
851
  return messages.map((message) => {
681
- const { attachments, ...rest } = message;
682
- let content = message.content;
852
+ const { attachments, providerMetadata, modelOutput, ...rest } = message;
853
+ let content = message.role === "tool" && !preserveProviderMetadata ? textToolOutput(modelOutput, String(message.content ?? "")) : message.content;
854
+ if (preserveProviderMetadata && modelOutput)
855
+ rest.modelOutput = modelOutput;
683
856
  if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
684
857
  content = null;
685
858
  }
@@ -692,7 +865,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
692
865
  content = [...parts, ...images];
693
866
  }
694
867
  }
695
- return { ...rest, content };
868
+ return {
869
+ ...rest,
870
+ content,
871
+ ...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
872
+ };
696
873
  });
697
874
  }
698
875
  /**
@@ -773,14 +950,25 @@ var OpenAIAdapter = class _OpenAIAdapter {
773
950
  ).map((part) => part.text) : []
774
951
  ).join("") : "";
775
952
  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
- })) : [];
953
+ const seenCallIds = /* @__PURE__ */ new Set();
954
+ const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
955
+ if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
956
+ throw new Error("Responses contains an incomplete function call");
957
+ }
958
+ if (seenCallIds.has(item.call_id))
959
+ throw new Error("Responses contains duplicate function call IDs");
960
+ seenCallIds.add(item.call_id);
961
+ const args = JSON.parse(item.arguments);
962
+ if (!args || typeof args !== "object" || Array.isArray(args))
963
+ throw new Error(
964
+ "Responses function arguments must be a JSON object"
965
+ );
966
+ return { id: item.call_id, name: item.name, args };
967
+ }) : [];
781
968
  return {
782
969
  content,
783
970
  toolCalls,
971
+ providerMetadata: responseMetadata(response),
784
972
  usage: response?.usage ? {
785
973
  promptTokens: response.usage.input_tokens ?? 0,
786
974
  completionTokens: response.usage.output_tokens ?? 0,
@@ -791,6 +979,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
791
979
  }
792
980
  buildResponsesPayload(request, stream) {
793
981
  const openaiToolOptions = request.providerToolOptions?.openai;
982
+ const options = resolveOpenAITransport(
983
+ this.config,
984
+ request.config?.openai
985
+ ).responses;
986
+ const parameters = buildOpenAIParameters(
987
+ request.config?.model || this.model,
988
+ request.config?.maxTokens ?? this.config.maxTokens,
989
+ request.config?.temperature ?? this.config.temperature,
990
+ this.parameterOptions(request),
991
+ "responses"
992
+ );
794
993
  const responsesTextFormat = toOpenAIResponsesTextFormat(
795
994
  request.config?.responseFormat
796
995
  );
@@ -806,13 +1005,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
806
1005
  ),
807
1006
  tool_choice: openaiToolOptions?.toolChoice,
808
1007
  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
- ),
1008
+ ...parameters,
1009
+ ...options?.reasoningSummary ? {
1010
+ reasoning: {
1011
+ ...parameters.reasoning,
1012
+ summary: options.reasoningSummary
1013
+ }
1014
+ } : {},
1015
+ ...options?.store !== void 0 ? { store: options.store } : {},
1016
+ ...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
1017
+ ...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
1018
+ ...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
1019
+ ...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
1020
+ ...options?.store === false || options?.include ? {
1021
+ include: [
1022
+ .../* @__PURE__ */ new Set([
1023
+ ...options?.include ?? [],
1024
+ ...options?.store === false ? ["reasoning.encrypted_content"] : []
1025
+ ])
1026
+ ]
1027
+ } : {},
816
1028
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
817
1029
  stream
818
1030
  };
@@ -871,6 +1083,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
871
1083
  let usage;
872
1084
  const citations = [];
873
1085
  let completed = false;
1086
+ let terminalResponse;
1087
+ let responseIdentity = {};
1088
+ const outputItems = /* @__PURE__ */ new Map();
1089
+ let streamedText = "";
874
1090
  function* flushToolCalls() {
875
1091
  for (const call of toolCalls.values()) {
876
1092
  if (!call.name)
@@ -887,17 +1103,36 @@ var OpenAIAdapter = class _OpenAIAdapter {
887
1103
  for await (const event of stream) {
888
1104
  throwIfAborted(request.signal);
889
1105
  logProviderPayload("openai", "stream chunk", event, request.debug);
1106
+ if (completed)
1107
+ throw new Error("Responses stream emitted an event after completion");
890
1108
  const index = event?.output_index ?? 0;
891
1109
  switch (event?.type) {
1110
+ case "response.created":
1111
+ responseIdentity = {
1112
+ id: event.response?.id,
1113
+ model: event.response?.model
1114
+ };
1115
+ break;
1116
+ case "response.output_item.done":
1117
+ if (event.item) outputItems.set(index, event.item);
1118
+ if (event.item?.type === "function_call") {
1119
+ toolCalls.set(index, {
1120
+ id: event.item.call_id,
1121
+ name: event.item.name,
1122
+ arguments: event.item.arguments
1123
+ });
1124
+ }
1125
+ break;
892
1126
  case "response.output_text.delta":
893
1127
  if (event.delta) {
1128
+ streamedText += event.delta;
894
1129
  yield { type: "message:delta", content: event.delta };
895
1130
  }
896
1131
  break;
897
1132
  case "response.output_item.added":
898
1133
  if (event.item?.type === "function_call") {
899
1134
  toolCalls.set(index, {
900
- id: event.item.call_id ?? event.item.id ?? generateToolCallId(),
1135
+ id: event.item.call_id ?? "",
901
1136
  name: event.item.name ?? "",
902
1137
  arguments: event.item.arguments ?? ""
903
1138
  });
@@ -916,7 +1151,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
916
1151
  break;
917
1152
  }
918
1153
  case "response.completed": {
1154
+ if (event.response?.status && event.response.status !== "completed")
1155
+ throw new Error(`Responses stream ${event.response.status}`);
919
1156
  completed = true;
1157
+ terminalResponse = { ...responseIdentity, ...event.response };
920
1158
  const found = this.collectResponsesCitations(
921
1159
  event.response?.output,
922
1160
  citations.length
@@ -953,12 +1191,64 @@ var OpenAIAdapter = class _OpenAIAdapter {
953
1191
  throwIfAborted(request.signal);
954
1192
  if (!completed)
955
1193
  throw new Error("Responses stream ended without a completed response");
1194
+ const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
1195
+ if (output.some((item) => item.type === "function_call"))
1196
+ toolCalls.clear();
1197
+ for (const [index, item] of output.entries()) {
1198
+ if (item.type === "function_call") {
1199
+ toolCalls.set(index, {
1200
+ id: item.call_id,
1201
+ name: item.name,
1202
+ arguments: item.arguments
1203
+ });
1204
+ }
1205
+ }
1206
+ const uniqueCalls = /* @__PURE__ */ new Map();
1207
+ for (const call of toolCalls.values()) {
1208
+ if (!call.id || !call.name)
1209
+ throw new Error("Responses stream contains an incomplete tool call");
1210
+ uniqueCalls.set(call.id, call);
1211
+ }
1212
+ toolCalls.clear();
1213
+ [...uniqueCalls.values()].forEach(
1214
+ (call, index) => toolCalls.set(index, call)
1215
+ );
1216
+ if (!output.length && toolCalls.size) {
1217
+ for (const call of toolCalls.values())
1218
+ output.push({
1219
+ type: "function_call",
1220
+ call_id: call.id,
1221
+ name: call.name,
1222
+ arguments: call.arguments
1223
+ });
1224
+ }
1225
+ if (!output.length && streamedText)
1226
+ output.push({
1227
+ type: "message",
1228
+ role: "assistant",
1229
+ content: [{ type: "output_text", text: streamedText, annotations: [] }]
1230
+ });
1231
+ terminalResponse.output = output;
1232
+ const finalText = this.parseResponsesResult(terminalResponse).content;
1233
+ if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
1234
+ yield {
1235
+ type: "message:delta",
1236
+ content: finalText.slice(streamedText.length)
1237
+ };
1238
+ }
956
1239
  yield* flushToolCalls();
957
1240
  if (citations.length > 0) {
958
1241
  yield { type: "citation", citations: deduplicateCitations(citations) };
959
1242
  }
960
- yield { type: "message:end" };
961
- yield { type: "done", usage };
1243
+ yield {
1244
+ type: "message:end",
1245
+ providerMetadata: responseMetadata(terminalResponse)
1246
+ };
1247
+ yield {
1248
+ type: "done",
1249
+ usage,
1250
+ providerMetadata: responseMetadata(terminalResponse)
1251
+ };
962
1252
  }
963
1253
  async *stream(request) {
964
1254
  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 */