@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,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,6 +593,64 @@ 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 getResponseReplay(metadata) {
644
+ const response = metadata?.openai?.response;
645
+ if (response === void 0) return void 0;
646
+ if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
647
+ throw new Error(
648
+ "Invalid OpenAI Responses replay metadata: output must contain typed items"
649
+ );
650
+ }
651
+ return response.output;
652
+ }
653
+
491
654
  // src/core/provider-error.ts
492
655
  function providerErrorEvent(error, code) {
493
656
  const details = error ?? {};
@@ -564,13 +727,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
564
727
  }));
565
728
  }
566
729
  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(
730
+ return useOpenAIResponses(
571
731
  request.config?.model || this.model,
572
732
  this.config.baseUrl,
573
- this.parameterOptions(request)
733
+ this.parameterOptions(request),
734
+ resolveOpenAITransport(this.config, request.config?.openai),
735
+ this.toolDefinitions(request).length > 0,
736
+ !!(request.rawMessages ?? request.messages)?.some(
737
+ (message) => message.providerMetadata?.openai?.response
738
+ ),
739
+ request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
574
740
  );
575
741
  }
576
742
  /**
@@ -603,11 +769,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
603
769
  });
604
770
  }
605
771
  buildResponsesInput(request) {
606
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages) : formatMessagesForOpenAI(request.messages, void 0);
772
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
607
773
  const input = [];
608
774
  for (const message of sourceMessages) {
609
775
  if (message.role === "system") {
610
- if (!request.systemPrompt && message.content) {
776
+ if (message.content && message.content !== request.systemPrompt) {
611
777
  input.push({
612
778
  type: "message",
613
779
  role: "developer",
@@ -617,6 +783,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
617
783
  continue;
618
784
  }
619
785
  if (message.role === "assistant") {
786
+ const replay = getResponseReplay(message.providerMetadata);
787
+ if (replay) {
788
+ input.push(...replay);
789
+ continue;
790
+ }
620
791
  const content = this.toResponsesContent(message.content, "assistant");
621
792
  if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
622
793
  input.push({
@@ -640,7 +811,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
640
811
  input.push({
641
812
  type: "function_call_output",
642
813
  call_id: message.tool_call_id,
643
- output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
814
+ output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
644
815
  });
645
816
  continue;
646
817
  }
@@ -654,10 +825,12 @@ var OpenAIAdapter = class _OpenAIAdapter {
654
825
  return input;
655
826
  }
656
827
  /** Preserve Copilot screenshot attachments on every OpenAI transport. */
657
- formatRawMessages(messages) {
828
+ formatRawMessages(messages, preserveProviderMetadata = false) {
658
829
  return messages.map((message) => {
659
- const { attachments, ...rest } = message;
660
- let content = message.content;
830
+ const { attachments, providerMetadata, modelOutput, ...rest } = message;
831
+ let content = message.role === "tool" && !preserveProviderMetadata ? textToolOutput(modelOutput, String(message.content ?? "")) : message.content;
832
+ if (preserveProviderMetadata && modelOutput)
833
+ rest.modelOutput = modelOutput;
661
834
  if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
662
835
  content = null;
663
836
  }
@@ -670,7 +843,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
670
843
  content = [...parts, ...images];
671
844
  }
672
845
  }
673
- return { ...rest, content };
846
+ return {
847
+ ...rest,
848
+ content,
849
+ ...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
850
+ };
674
851
  });
675
852
  }
676
853
  /**
@@ -751,14 +928,25 @@ var OpenAIAdapter = class _OpenAIAdapter {
751
928
  ).map((part) => part.text) : []
752
929
  ).join("") : "";
753
930
  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
- })) : [];
931
+ const seenCallIds = /* @__PURE__ */ new Set();
932
+ const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
933
+ if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
934
+ throw new Error("Responses contains an incomplete function call");
935
+ }
936
+ if (seenCallIds.has(item.call_id))
937
+ throw new Error("Responses contains duplicate function call IDs");
938
+ seenCallIds.add(item.call_id);
939
+ const args = JSON.parse(item.arguments);
940
+ if (!args || typeof args !== "object" || Array.isArray(args))
941
+ throw new Error(
942
+ "Responses function arguments must be a JSON object"
943
+ );
944
+ return { id: item.call_id, name: item.name, args };
945
+ }) : [];
759
946
  return {
760
947
  content,
761
948
  toolCalls,
949
+ providerMetadata: responseMetadata(response),
762
950
  usage: response?.usage ? {
763
951
  promptTokens: response.usage.input_tokens ?? 0,
764
952
  completionTokens: response.usage.output_tokens ?? 0,
@@ -769,6 +957,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
769
957
  }
770
958
  buildResponsesPayload(request, stream) {
771
959
  const openaiToolOptions = request.providerToolOptions?.openai;
960
+ const options = resolveOpenAITransport(
961
+ this.config,
962
+ request.config?.openai
963
+ ).responses;
964
+ const parameters = buildOpenAIParameters(
965
+ request.config?.model || this.model,
966
+ request.config?.maxTokens ?? this.config.maxTokens,
967
+ request.config?.temperature ?? this.config.temperature,
968
+ this.parameterOptions(request),
969
+ "responses"
970
+ );
772
971
  const responsesTextFormat = toOpenAIResponsesTextFormat(
773
972
  request.config?.responseFormat
774
973
  );
@@ -784,13 +983,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
784
983
  ),
785
984
  tool_choice: openaiToolOptions?.toolChoice,
786
985
  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
- ),
986
+ ...parameters,
987
+ ...options?.reasoningSummary ? {
988
+ reasoning: {
989
+ ...parameters.reasoning,
990
+ summary: options.reasoningSummary
991
+ }
992
+ } : {},
993
+ ...options?.store !== void 0 ? { store: options.store } : {},
994
+ ...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
995
+ ...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
996
+ ...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
997
+ ...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
998
+ ...options?.store === false || options?.include ? {
999
+ include: [
1000
+ .../* @__PURE__ */ new Set([
1001
+ ...options?.include ?? [],
1002
+ ...options?.store === false ? ["reasoning.encrypted_content"] : []
1003
+ ])
1004
+ ]
1005
+ } : {},
794
1006
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
795
1007
  stream
796
1008
  };
@@ -849,6 +1061,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
849
1061
  let usage;
850
1062
  const citations = [];
851
1063
  let completed = false;
1064
+ let terminalResponse;
1065
+ let responseIdentity = {};
1066
+ const outputItems = /* @__PURE__ */ new Map();
1067
+ let streamedText = "";
852
1068
  function* flushToolCalls() {
853
1069
  for (const call of toolCalls.values()) {
854
1070
  if (!call.name)
@@ -865,17 +1081,36 @@ var OpenAIAdapter = class _OpenAIAdapter {
865
1081
  for await (const event of stream) {
866
1082
  throwIfAborted(request.signal);
867
1083
  logProviderPayload("openai", "stream chunk", event, request.debug);
1084
+ if (completed)
1085
+ throw new Error("Responses stream emitted an event after completion");
868
1086
  const index = event?.output_index ?? 0;
869
1087
  switch (event?.type) {
1088
+ case "response.created":
1089
+ responseIdentity = {
1090
+ id: event.response?.id,
1091
+ model: event.response?.model
1092
+ };
1093
+ break;
1094
+ case "response.output_item.done":
1095
+ if (event.item) outputItems.set(index, event.item);
1096
+ if (event.item?.type === "function_call") {
1097
+ toolCalls.set(index, {
1098
+ id: event.item.call_id,
1099
+ name: event.item.name,
1100
+ arguments: event.item.arguments
1101
+ });
1102
+ }
1103
+ break;
870
1104
  case "response.output_text.delta":
871
1105
  if (event.delta) {
1106
+ streamedText += event.delta;
872
1107
  yield { type: "message:delta", content: event.delta };
873
1108
  }
874
1109
  break;
875
1110
  case "response.output_item.added":
876
1111
  if (event.item?.type === "function_call") {
877
1112
  toolCalls.set(index, {
878
- id: event.item.call_id ?? event.item.id ?? generateToolCallId(),
1113
+ id: event.item.call_id ?? "",
879
1114
  name: event.item.name ?? "",
880
1115
  arguments: event.item.arguments ?? ""
881
1116
  });
@@ -894,7 +1129,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
894
1129
  break;
895
1130
  }
896
1131
  case "response.completed": {
1132
+ if (event.response?.status && event.response.status !== "completed")
1133
+ throw new Error(`Responses stream ${event.response.status}`);
897
1134
  completed = true;
1135
+ terminalResponse = { ...responseIdentity, ...event.response };
898
1136
  const found = this.collectResponsesCitations(
899
1137
  event.response?.output,
900
1138
  citations.length
@@ -931,12 +1169,64 @@ var OpenAIAdapter = class _OpenAIAdapter {
931
1169
  throwIfAborted(request.signal);
932
1170
  if (!completed)
933
1171
  throw new Error("Responses stream ended without a completed response");
1172
+ const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
1173
+ if (output.some((item) => item.type === "function_call"))
1174
+ toolCalls.clear();
1175
+ for (const [index, item] of output.entries()) {
1176
+ if (item.type === "function_call") {
1177
+ toolCalls.set(index, {
1178
+ id: item.call_id,
1179
+ name: item.name,
1180
+ arguments: item.arguments
1181
+ });
1182
+ }
1183
+ }
1184
+ const uniqueCalls = /* @__PURE__ */ new Map();
1185
+ for (const call of toolCalls.values()) {
1186
+ if (!call.id || !call.name)
1187
+ throw new Error("Responses stream contains an incomplete tool call");
1188
+ uniqueCalls.set(call.id, call);
1189
+ }
1190
+ toolCalls.clear();
1191
+ [...uniqueCalls.values()].forEach(
1192
+ (call, index) => toolCalls.set(index, call)
1193
+ );
1194
+ if (!output.length && toolCalls.size) {
1195
+ for (const call of toolCalls.values())
1196
+ output.push({
1197
+ type: "function_call",
1198
+ call_id: call.id,
1199
+ name: call.name,
1200
+ arguments: call.arguments
1201
+ });
1202
+ }
1203
+ if (!output.length && streamedText)
1204
+ output.push({
1205
+ type: "message",
1206
+ role: "assistant",
1207
+ content: [{ type: "output_text", text: streamedText, annotations: [] }]
1208
+ });
1209
+ terminalResponse.output = output;
1210
+ const finalText = this.parseResponsesResult(terminalResponse).content;
1211
+ if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
1212
+ yield {
1213
+ type: "message:delta",
1214
+ content: finalText.slice(streamedText.length)
1215
+ };
1216
+ }
934
1217
  yield* flushToolCalls();
935
1218
  if (citations.length > 0) {
936
1219
  yield { type: "citation", citations: deduplicateCitations(citations) };
937
1220
  }
938
- yield { type: "message:end" };
939
- yield { type: "done", usage };
1221
+ yield {
1222
+ type: "message:end",
1223
+ providerMetadata: responseMetadata(terminalResponse)
1224
+ };
1225
+ yield {
1226
+ type: "done",
1227
+ usage,
1228
+ providerMetadata: responseMetadata(terminalResponse)
1229
+ };
940
1230
  }
941
1231
  async *stream(request) {
942
1232
  throwIfAborted(request.signal);