@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,7 +1,7 @@
1
- import { L as LanguageModel } from '../../types-BW3csYo5.mjs';
2
- import { A as AIProvider } from '../../types-NOswlhm9.mjs';
1
+ import { L as LanguageModel } from '../../types-Dg1Bcg1k.mjs';
2
+ import { A as AIProvider } from '../../types-DHQEMFHA.mjs';
3
3
  import 'zod';
4
- import '../../base-CkeT7u-O.mjs';
4
+ import '../../base-BICaQaRS.mjs';
5
5
 
6
6
  /**
7
7
  * OpenRouter Provider - Modern Pattern
@@ -1,7 +1,7 @@
1
- import { L as LanguageModel } from '../../types-BW3csYo5.js';
2
- import { A as AIProvider } from '../../types-DA54bSmX.js';
1
+ import { L as LanguageModel } from '../../types-Dg1Bcg1k.js';
2
+ import { A as AIProvider } from '../../types-Dikj3En4.js';
3
3
  import 'zod';
4
- import '../../base-BdTpUAWI.js';
4
+ import '../../base-CpRPB8u8.js';
5
5
 
6
6
  /**
7
7
  * OpenRouter Provider - Modern Pattern
@@ -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
  }
@@ -505,7 +610,7 @@ function formatMessagesForOpenRouter(messages) {
505
610
  return {
506
611
  role: "tool",
507
612
  tool_call_id: msg.toolCallId,
508
- content: msg.content
613
+ content: textToolOutput(msg.modelOutput, msg.content)
509
614
  };
510
615
  default:
511
616
  return msg;
@@ -536,6 +641,64 @@ async function searchOpenRouterModels(query, apiKey) {
536
641
  );
537
642
  }
538
643
 
644
+ // src/core/openai-responses.ts
645
+ function resolveOpenAITransport(defaults, overrides) {
646
+ return {
647
+ api: overrides?.api ?? defaults.api ?? "auto",
648
+ responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
649
+ };
650
+ }
651
+ function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
652
+ const api = transport.api ?? "auto";
653
+ if (!["auto", "chat", "responses"].includes(api)) {
654
+ throw new Error(`Unknown OpenAI API selection: ${api}`);
655
+ }
656
+ if (api === "chat") {
657
+ if (transport.responses !== void 0 || nativeToolSearch) {
658
+ throw new Error(
659
+ "Responses options and native tool search require the Responses API"
660
+ );
661
+ }
662
+ return false;
663
+ }
664
+ let officialEndpoint = !baseURL;
665
+ try {
666
+ officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
667
+ } catch {
668
+ }
669
+ return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
670
+ }
671
+ function responseMetadata(response) {
672
+ const output = Array.isArray(response?.output) ? response.output : [];
673
+ return {
674
+ openai: {
675
+ response: {
676
+ id: response?.id,
677
+ model: response?.model,
678
+ status: response?.status,
679
+ output,
680
+ usage: response?.usage,
681
+ store: response?.store,
682
+ refusals: output.flatMap(
683
+ (item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
684
+ (part) => part?.type === "refusal" && typeof part.refusal === "string"
685
+ ).map((part) => part.refusal) : []
686
+ )
687
+ }
688
+ }
689
+ };
690
+ }
691
+ function getResponseReplay(metadata) {
692
+ const response = metadata?.openai?.response;
693
+ if (response === void 0) return void 0;
694
+ if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
695
+ throw new Error(
696
+ "Invalid OpenAI Responses replay metadata: output must contain typed items"
697
+ );
698
+ }
699
+ return response.output;
700
+ }
701
+
539
702
  // src/core/provider-error.ts
540
703
  function providerErrorEvent(error, code) {
541
704
  const details = error ?? {};
@@ -612,13 +775,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
612
775
  }));
613
776
  }
614
777
  shouldUseResponsesApi(request) {
615
- if (this.toolDefinitions(request).length === 0) return false;
616
- if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false)
617
- return true;
618
- return requiresOpenAIResponses(
778
+ return useOpenAIResponses(
619
779
  request.config?.model || this.model,
620
780
  this.config.baseUrl,
621
- this.parameterOptions(request)
781
+ this.parameterOptions(request),
782
+ resolveOpenAITransport(this.config, request.config?.openai),
783
+ this.toolDefinitions(request).length > 0,
784
+ !!(request.rawMessages ?? request.messages)?.some(
785
+ (message) => message.providerMetadata?.openai?.response
786
+ ),
787
+ request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
622
788
  );
623
789
  }
624
790
  /**
@@ -651,11 +817,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
651
817
  });
652
818
  }
653
819
  buildResponsesInput(request) {
654
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages) : formatMessagesForOpenAI(request.messages, void 0);
820
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
655
821
  const input = [];
656
822
  for (const message of sourceMessages) {
657
823
  if (message.role === "system") {
658
- if (!request.systemPrompt && message.content) {
824
+ if (message.content && message.content !== request.systemPrompt) {
659
825
  input.push({
660
826
  type: "message",
661
827
  role: "developer",
@@ -665,6 +831,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
665
831
  continue;
666
832
  }
667
833
  if (message.role === "assistant") {
834
+ const replay = getResponseReplay(message.providerMetadata);
835
+ if (replay) {
836
+ input.push(...replay);
837
+ continue;
838
+ }
668
839
  const content = this.toResponsesContent(message.content, "assistant");
669
840
  if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
670
841
  input.push({
@@ -688,7 +859,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
688
859
  input.push({
689
860
  type: "function_call_output",
690
861
  call_id: message.tool_call_id,
691
- output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
862
+ output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
692
863
  });
693
864
  continue;
694
865
  }
@@ -702,10 +873,12 @@ var OpenAIAdapter = class _OpenAIAdapter {
702
873
  return input;
703
874
  }
704
875
  /** Preserve Copilot screenshot attachments on every OpenAI transport. */
705
- formatRawMessages(messages) {
876
+ formatRawMessages(messages, preserveProviderMetadata = false) {
706
877
  return messages.map((message) => {
707
- const { attachments, ...rest } = message;
708
- let content = message.content;
878
+ const { attachments, providerMetadata, modelOutput, ...rest } = message;
879
+ let content = message.role === "tool" && !preserveProviderMetadata ? textToolOutput(modelOutput, String(message.content ?? "")) : message.content;
880
+ if (preserveProviderMetadata && modelOutput)
881
+ rest.modelOutput = modelOutput;
709
882
  if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
710
883
  content = null;
711
884
  }
@@ -718,7 +891,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
718
891
  content = [...parts, ...images];
719
892
  }
720
893
  }
721
- return { ...rest, content };
894
+ return {
895
+ ...rest,
896
+ content,
897
+ ...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
898
+ };
722
899
  });
723
900
  }
724
901
  /**
@@ -799,14 +976,25 @@ var OpenAIAdapter = class _OpenAIAdapter {
799
976
  ).map((part) => part.text) : []
800
977
  ).join("") : "";
801
978
  const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
802
- const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
803
- id: item.call_id ?? item.id ?? generateToolCallId(),
804
- name: item.name,
805
- args: JSON.parse(item.arguments ?? "{}")
806
- })) : [];
979
+ const seenCallIds = /* @__PURE__ */ new Set();
980
+ const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
981
+ if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
982
+ throw new Error("Responses contains an incomplete function call");
983
+ }
984
+ if (seenCallIds.has(item.call_id))
985
+ throw new Error("Responses contains duplicate function call IDs");
986
+ seenCallIds.add(item.call_id);
987
+ const args = JSON.parse(item.arguments);
988
+ if (!args || typeof args !== "object" || Array.isArray(args))
989
+ throw new Error(
990
+ "Responses function arguments must be a JSON object"
991
+ );
992
+ return { id: item.call_id, name: item.name, args };
993
+ }) : [];
807
994
  return {
808
995
  content,
809
996
  toolCalls,
997
+ providerMetadata: responseMetadata(response),
810
998
  usage: response?.usage ? {
811
999
  promptTokens: response.usage.input_tokens ?? 0,
812
1000
  completionTokens: response.usage.output_tokens ?? 0,
@@ -817,6 +1005,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
817
1005
  }
818
1006
  buildResponsesPayload(request, stream) {
819
1007
  const openaiToolOptions = request.providerToolOptions?.openai;
1008
+ const options = resolveOpenAITransport(
1009
+ this.config,
1010
+ request.config?.openai
1011
+ ).responses;
1012
+ const parameters = buildOpenAIParameters(
1013
+ request.config?.model || this.model,
1014
+ request.config?.maxTokens ?? this.config.maxTokens,
1015
+ request.config?.temperature ?? this.config.temperature,
1016
+ this.parameterOptions(request),
1017
+ "responses"
1018
+ );
820
1019
  const responsesTextFormat = toOpenAIResponsesTextFormat(
821
1020
  request.config?.responseFormat
822
1021
  );
@@ -832,13 +1031,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
832
1031
  ),
833
1032
  tool_choice: openaiToolOptions?.toolChoice,
834
1033
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
835
- ...buildOpenAIParameters(
836
- modelIdForPayload,
837
- request.config?.maxTokens ?? this.config.maxTokens,
838
- request.config?.temperature ?? this.config.temperature,
839
- this.parameterOptions(request),
840
- "responses"
841
- ),
1034
+ ...parameters,
1035
+ ...options?.reasoningSummary ? {
1036
+ reasoning: {
1037
+ ...parameters.reasoning,
1038
+ summary: options.reasoningSummary
1039
+ }
1040
+ } : {},
1041
+ ...options?.store !== void 0 ? { store: options.store } : {},
1042
+ ...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
1043
+ ...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
1044
+ ...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
1045
+ ...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
1046
+ ...options?.store === false || options?.include ? {
1047
+ include: [
1048
+ .../* @__PURE__ */ new Set([
1049
+ ...options?.include ?? [],
1050
+ ...options?.store === false ? ["reasoning.encrypted_content"] : []
1051
+ ])
1052
+ ]
1053
+ } : {},
842
1054
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
843
1055
  stream
844
1056
  };
@@ -897,6 +1109,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
897
1109
  let usage;
898
1110
  const citations = [];
899
1111
  let completed = false;
1112
+ let terminalResponse;
1113
+ let responseIdentity = {};
1114
+ const outputItems = /* @__PURE__ */ new Map();
1115
+ let streamedText = "";
900
1116
  function* flushToolCalls() {
901
1117
  for (const call of toolCalls.values()) {
902
1118
  if (!call.name)
@@ -913,17 +1129,36 @@ var OpenAIAdapter = class _OpenAIAdapter {
913
1129
  for await (const event of stream) {
914
1130
  throwIfAborted(request.signal);
915
1131
  logProviderPayload("openai", "stream chunk", event, request.debug);
1132
+ if (completed)
1133
+ throw new Error("Responses stream emitted an event after completion");
916
1134
  const index = event?.output_index ?? 0;
917
1135
  switch (event?.type) {
1136
+ case "response.created":
1137
+ responseIdentity = {
1138
+ id: event.response?.id,
1139
+ model: event.response?.model
1140
+ };
1141
+ break;
1142
+ case "response.output_item.done":
1143
+ if (event.item) outputItems.set(index, event.item);
1144
+ if (event.item?.type === "function_call") {
1145
+ toolCalls.set(index, {
1146
+ id: event.item.call_id,
1147
+ name: event.item.name,
1148
+ arguments: event.item.arguments
1149
+ });
1150
+ }
1151
+ break;
918
1152
  case "response.output_text.delta":
919
1153
  if (event.delta) {
1154
+ streamedText += event.delta;
920
1155
  yield { type: "message:delta", content: event.delta };
921
1156
  }
922
1157
  break;
923
1158
  case "response.output_item.added":
924
1159
  if (event.item?.type === "function_call") {
925
1160
  toolCalls.set(index, {
926
- id: event.item.call_id ?? event.item.id ?? generateToolCallId(),
1161
+ id: event.item.call_id ?? "",
927
1162
  name: event.item.name ?? "",
928
1163
  arguments: event.item.arguments ?? ""
929
1164
  });
@@ -942,7 +1177,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
942
1177
  break;
943
1178
  }
944
1179
  case "response.completed": {
1180
+ if (event.response?.status && event.response.status !== "completed")
1181
+ throw new Error(`Responses stream ${event.response.status}`);
945
1182
  completed = true;
1183
+ terminalResponse = { ...responseIdentity, ...event.response };
946
1184
  const found = this.collectResponsesCitations(
947
1185
  event.response?.output,
948
1186
  citations.length
@@ -979,12 +1217,64 @@ var OpenAIAdapter = class _OpenAIAdapter {
979
1217
  throwIfAborted(request.signal);
980
1218
  if (!completed)
981
1219
  throw new Error("Responses stream ended without a completed response");
1220
+ const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
1221
+ if (output.some((item) => item.type === "function_call"))
1222
+ toolCalls.clear();
1223
+ for (const [index, item] of output.entries()) {
1224
+ if (item.type === "function_call") {
1225
+ toolCalls.set(index, {
1226
+ id: item.call_id,
1227
+ name: item.name,
1228
+ arguments: item.arguments
1229
+ });
1230
+ }
1231
+ }
1232
+ const uniqueCalls = /* @__PURE__ */ new Map();
1233
+ for (const call of toolCalls.values()) {
1234
+ if (!call.id || !call.name)
1235
+ throw new Error("Responses stream contains an incomplete tool call");
1236
+ uniqueCalls.set(call.id, call);
1237
+ }
1238
+ toolCalls.clear();
1239
+ [...uniqueCalls.values()].forEach(
1240
+ (call, index) => toolCalls.set(index, call)
1241
+ );
1242
+ if (!output.length && toolCalls.size) {
1243
+ for (const call of toolCalls.values())
1244
+ output.push({
1245
+ type: "function_call",
1246
+ call_id: call.id,
1247
+ name: call.name,
1248
+ arguments: call.arguments
1249
+ });
1250
+ }
1251
+ if (!output.length && streamedText)
1252
+ output.push({
1253
+ type: "message",
1254
+ role: "assistant",
1255
+ content: [{ type: "output_text", text: streamedText, annotations: [] }]
1256
+ });
1257
+ terminalResponse.output = output;
1258
+ const finalText = this.parseResponsesResult(terminalResponse).content;
1259
+ if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
1260
+ yield {
1261
+ type: "message:delta",
1262
+ content: finalText.slice(streamedText.length)
1263
+ };
1264
+ }
982
1265
  yield* flushToolCalls();
983
1266
  if (citations.length > 0) {
984
1267
  yield { type: "citation", citations: deduplicateCitations(citations) };
985
1268
  }
986
- yield { type: "message:end" };
987
- yield { type: "done", usage };
1269
+ yield {
1270
+ type: "message:end",
1271
+ providerMetadata: responseMetadata(terminalResponse)
1272
+ };
1273
+ yield {
1274
+ type: "done",
1275
+ usage,
1276
+ providerMetadata: responseMetadata(terminalResponse)
1277
+ };
988
1278
  }
989
1279
  async *stream(request) {
990
1280
  throwIfAborted(request.signal);