@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
  * Fireworks Provider
@@ -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
  * Fireworks Provider
@@ -1,5 +1,108 @@
1
1
  'use strict';
2
2
 
3
+ // src/core/tool-output.ts
4
+ var imageTypes = /* @__PURE__ */ new Set([
5
+ "image/png",
6
+ "image/jpeg",
7
+ "image/webp",
8
+ "image/gif"
9
+ ]);
10
+ function normalizeToolOutput(value) {
11
+ const output = value;
12
+ if (output?.type !== "content" || !Array.isArray(output.content) || !output.content.length) {
13
+ throw new Error("Tool model output must contain a nonempty content array");
14
+ }
15
+ return {
16
+ type: "content",
17
+ content: output.content.map((part) => {
18
+ if (part?.type === "text" && typeof part.text === "string")
19
+ return { type: "text", text: part.text };
20
+ if (part?.type !== "image")
21
+ throw new Error("Unsupported tool output content type");
22
+ const sources = ["data", "url", "fileId"].filter((key) => key in part);
23
+ if (sources.length !== 1)
24
+ throw new Error(
25
+ "Tool image requires exactly one of data, url, or fileId"
26
+ );
27
+ if ("fileId" in part) {
28
+ if (part.provider !== "openai" || typeof part.fileId !== "string" || !part.fileId.trim())
29
+ throw new Error(
30
+ "Tool image file references must name the openai provider and fileId"
31
+ );
32
+ return { type: "image", fileId: part.fileId, provider: "openai" };
33
+ }
34
+ if ("url" in part) {
35
+ let url;
36
+ try {
37
+ url = new URL(part.url);
38
+ } catch {
39
+ throw new Error("Invalid tool image URL");
40
+ }
41
+ if (!["https:", "http:"].includes(url.protocol) || url.username || url.password)
42
+ throw new Error(
43
+ "Tool image URL must use HTTP(S) without credentials"
44
+ );
45
+ return { type: "image", url: url.href };
46
+ }
47
+ if (!("data" in part) || !imageTypes.has(part.mediaType))
48
+ throw new Error("Unsupported tool image media type");
49
+ let data = part.data instanceof Uint8Array ? Buffer.from(part.data).toString("base64") : part.data;
50
+ if (typeof data !== "string")
51
+ throw new Error("Tool image data must be base64 or bytes");
52
+ if (data.startsWith("data:")) {
53
+ const match = /^data:([^;,]+);base64,(.+)$/.exec(data);
54
+ if (!match || match[1] !== part.mediaType)
55
+ throw new Error(
56
+ "Tool image data URL media type does not match mediaType"
57
+ );
58
+ data = match[2];
59
+ }
60
+ if (!data.length || data.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(data))
61
+ throw new Error("Invalid tool image base64 data");
62
+ if (Buffer.byteLength(data, "base64") > 20 * 1024 * 1024)
63
+ throw new Error("Tool image exceeds 20 MiB");
64
+ if (Buffer.from(data, "base64").toString("base64") !== data)
65
+ throw new Error("Invalid tool image base64 data");
66
+ return { type: "image", data, mediaType: part.mediaType };
67
+ })
68
+ };
69
+ }
70
+ function toolOutputForProvider(value, provider) {
71
+ const output = normalizeToolOutput(value);
72
+ return output.content.map((part) => {
73
+ if (part.type === "text")
74
+ return {
75
+ type: provider === "responses" ? "input_text" : "text",
76
+ text: part.text
77
+ };
78
+ if (provider === "unsupported")
79
+ throw new Error(
80
+ "Image tool results require OpenAI Responses or Anthropic; this endpoint is not supported"
81
+ );
82
+ if ("fileId" in part) {
83
+ if (provider !== "responses")
84
+ throw new Error(
85
+ "OpenAI image file references cannot be sent to another provider"
86
+ );
87
+ return { type: "input_image", file_id: part.fileId, detail: "auto" };
88
+ }
89
+ if (provider === "responses")
90
+ return {
91
+ type: "input_image",
92
+ image_url: "url" in part ? part.url : `data:${part.mediaType};base64,${part.data}`,
93
+ detail: "auto"
94
+ };
95
+ return {
96
+ type: "image",
97
+ source: "url" in part ? { type: "url", url: part.url } : { type: "base64", media_type: part.mediaType, data: part.data }
98
+ };
99
+ });
100
+ }
101
+ function textToolOutput(value, fallback) {
102
+ if (value === void 0) return fallback;
103
+ return toolOutputForProvider(value, "unsupported").map((part) => part.text).join("\n");
104
+ }
105
+
3
106
  // src/core/openai-parameters.ts
4
107
  function capabilities(model) {
5
108
  const gpt = /^gpt-(\d+)(?:\.(\d+))?(?:-(.+))?$/i.exec(model);
@@ -241,7 +344,7 @@ function messageToOpenAIContent(message) {
241
344
  }
242
345
  return blocks;
243
346
  }
244
- function formatMessagesForOpenAI(messages, systemPrompt) {
347
+ function formatMessagesForOpenAI(messages, systemPrompt, preserveProviderMetadata = false) {
245
348
  const formatted = [];
246
349
  if (systemPrompt) {
247
350
  formatted.push({ role: "system", content: systemPrompt });
@@ -259,7 +362,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
259
362
  const assistantMsg = {
260
363
  role: "assistant",
261
364
  // Gemini/xAI (OpenAI-compatible) reject content: "" on assistant messages with tool_calls
262
- content: hasToolCalls ? msg.content || null : msg.content
365
+ content: hasToolCalls ? msg.content || null : msg.content,
366
+ ...preserveProviderMetadata && msg.providerMetadata ? { providerMetadata: msg.providerMetadata } : {}
263
367
  };
264
368
  if (hasToolCalls) {
265
369
  assistantMsg.tool_calls = msg.tool_calls;
@@ -268,7 +372,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
268
372
  } else if (msg.role === "tool" && msg.tool_call_id) {
269
373
  formatted.push({
270
374
  role: "tool",
271
- content: msg.content ?? "",
375
+ content: preserveProviderMetadata ? msg.content ?? "" : textToolOutput(msg.modelOutput, msg.content ?? ""),
376
+ ...preserveProviderMetadata && msg.modelOutput ? { modelOutput: msg.modelOutput } : {},
272
377
  tool_call_id: msg.tool_call_id
273
378
  });
274
379
  }
@@ -475,7 +580,7 @@ function formatMessages(messages) {
475
580
  return {
476
581
  role: "tool",
477
582
  tool_call_id: msg.toolCallId,
478
- content: msg.content
583
+ content: textToolOutput(msg.modelOutput, msg.content)
479
584
  };
480
585
  default:
481
586
  return msg;
@@ -483,6 +588,64 @@ function formatMessages(messages) {
483
588
  });
484
589
  }
485
590
 
591
+ // src/core/openai-responses.ts
592
+ function resolveOpenAITransport(defaults, overrides) {
593
+ return {
594
+ api: overrides?.api ?? defaults.api ?? "auto",
595
+ responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
596
+ };
597
+ }
598
+ function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
599
+ const api = transport.api ?? "auto";
600
+ if (!["auto", "chat", "responses"].includes(api)) {
601
+ throw new Error(`Unknown OpenAI API selection: ${api}`);
602
+ }
603
+ if (api === "chat") {
604
+ if (transport.responses !== void 0 || nativeToolSearch) {
605
+ throw new Error(
606
+ "Responses options and native tool search require the Responses API"
607
+ );
608
+ }
609
+ return false;
610
+ }
611
+ let officialEndpoint = !baseURL;
612
+ try {
613
+ officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
614
+ } catch {
615
+ }
616
+ return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
617
+ }
618
+ function responseMetadata(response) {
619
+ const output = Array.isArray(response?.output) ? response.output : [];
620
+ return {
621
+ openai: {
622
+ response: {
623
+ id: response?.id,
624
+ model: response?.model,
625
+ status: response?.status,
626
+ output,
627
+ usage: response?.usage,
628
+ store: response?.store,
629
+ refusals: output.flatMap(
630
+ (item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
631
+ (part) => part?.type === "refusal" && typeof part.refusal === "string"
632
+ ).map((part) => part.refusal) : []
633
+ )
634
+ }
635
+ }
636
+ };
637
+ }
638
+ function getResponseReplay(metadata) {
639
+ const response = metadata?.openai?.response;
640
+ if (response === void 0) return void 0;
641
+ if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
642
+ throw new Error(
643
+ "Invalid OpenAI Responses replay metadata: output must contain typed items"
644
+ );
645
+ }
646
+ return response.output;
647
+ }
648
+
486
649
  // src/core/provider-error.ts
487
650
  function providerErrorEvent(error, code) {
488
651
  const details = error ?? {};
@@ -559,13 +722,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
559
722
  }));
560
723
  }
561
724
  shouldUseResponsesApi(request) {
562
- if (this.toolDefinitions(request).length === 0) return false;
563
- if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false)
564
- return true;
565
- return requiresOpenAIResponses(
725
+ return useOpenAIResponses(
566
726
  request.config?.model || this.model,
567
727
  this.config.baseUrl,
568
- this.parameterOptions(request)
728
+ this.parameterOptions(request),
729
+ resolveOpenAITransport(this.config, request.config?.openai),
730
+ this.toolDefinitions(request).length > 0,
731
+ !!(request.rawMessages ?? request.messages)?.some(
732
+ (message) => message.providerMetadata?.openai?.response
733
+ ),
734
+ request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
569
735
  );
570
736
  }
571
737
  /**
@@ -598,11 +764,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
598
764
  });
599
765
  }
600
766
  buildResponsesInput(request) {
601
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages) : formatMessagesForOpenAI(request.messages, void 0);
767
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
602
768
  const input = [];
603
769
  for (const message of sourceMessages) {
604
770
  if (message.role === "system") {
605
- if (!request.systemPrompt && message.content) {
771
+ if (message.content && message.content !== request.systemPrompt) {
606
772
  input.push({
607
773
  type: "message",
608
774
  role: "developer",
@@ -612,6 +778,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
612
778
  continue;
613
779
  }
614
780
  if (message.role === "assistant") {
781
+ const replay = getResponseReplay(message.providerMetadata);
782
+ if (replay) {
783
+ input.push(...replay);
784
+ continue;
785
+ }
615
786
  const content = this.toResponsesContent(message.content, "assistant");
616
787
  if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
617
788
  input.push({
@@ -635,7 +806,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
635
806
  input.push({
636
807
  type: "function_call_output",
637
808
  call_id: message.tool_call_id,
638
- output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
809
+ output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
639
810
  });
640
811
  continue;
641
812
  }
@@ -649,10 +820,12 @@ var OpenAIAdapter = class _OpenAIAdapter {
649
820
  return input;
650
821
  }
651
822
  /** Preserve Copilot screenshot attachments on every OpenAI transport. */
652
- formatRawMessages(messages) {
823
+ formatRawMessages(messages, preserveProviderMetadata = false) {
653
824
  return messages.map((message) => {
654
- const { attachments, ...rest } = message;
655
- let content = message.content;
825
+ const { attachments, providerMetadata, modelOutput, ...rest } = message;
826
+ let content = message.role === "tool" && !preserveProviderMetadata ? textToolOutput(modelOutput, String(message.content ?? "")) : message.content;
827
+ if (preserveProviderMetadata && modelOutput)
828
+ rest.modelOutput = modelOutput;
656
829
  if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
657
830
  content = null;
658
831
  }
@@ -665,7 +838,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
665
838
  content = [...parts, ...images];
666
839
  }
667
840
  }
668
- return { ...rest, content };
841
+ return {
842
+ ...rest,
843
+ content,
844
+ ...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
845
+ };
669
846
  });
670
847
  }
671
848
  /**
@@ -746,14 +923,25 @@ var OpenAIAdapter = class _OpenAIAdapter {
746
923
  ).map((part) => part.text) : []
747
924
  ).join("") : "";
748
925
  const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
749
- const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
750
- id: item.call_id ?? item.id ?? generateToolCallId(),
751
- name: item.name,
752
- args: JSON.parse(item.arguments ?? "{}")
753
- })) : [];
926
+ const seenCallIds = /* @__PURE__ */ new Set();
927
+ const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
928
+ if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
929
+ throw new Error("Responses contains an incomplete function call");
930
+ }
931
+ if (seenCallIds.has(item.call_id))
932
+ throw new Error("Responses contains duplicate function call IDs");
933
+ seenCallIds.add(item.call_id);
934
+ const args = JSON.parse(item.arguments);
935
+ if (!args || typeof args !== "object" || Array.isArray(args))
936
+ throw new Error(
937
+ "Responses function arguments must be a JSON object"
938
+ );
939
+ return { id: item.call_id, name: item.name, args };
940
+ }) : [];
754
941
  return {
755
942
  content,
756
943
  toolCalls,
944
+ providerMetadata: responseMetadata(response),
757
945
  usage: response?.usage ? {
758
946
  promptTokens: response.usage.input_tokens ?? 0,
759
947
  completionTokens: response.usage.output_tokens ?? 0,
@@ -764,6 +952,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
764
952
  }
765
953
  buildResponsesPayload(request, stream) {
766
954
  const openaiToolOptions = request.providerToolOptions?.openai;
955
+ const options = resolveOpenAITransport(
956
+ this.config,
957
+ request.config?.openai
958
+ ).responses;
959
+ const parameters = buildOpenAIParameters(
960
+ request.config?.model || this.model,
961
+ request.config?.maxTokens ?? this.config.maxTokens,
962
+ request.config?.temperature ?? this.config.temperature,
963
+ this.parameterOptions(request),
964
+ "responses"
965
+ );
767
966
  const responsesTextFormat = toOpenAIResponsesTextFormat(
768
967
  request.config?.responseFormat
769
968
  );
@@ -779,13 +978,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
779
978
  ),
780
979
  tool_choice: openaiToolOptions?.toolChoice,
781
980
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
782
- ...buildOpenAIParameters(
783
- modelIdForPayload,
784
- request.config?.maxTokens ?? this.config.maxTokens,
785
- request.config?.temperature ?? this.config.temperature,
786
- this.parameterOptions(request),
787
- "responses"
788
- ),
981
+ ...parameters,
982
+ ...options?.reasoningSummary ? {
983
+ reasoning: {
984
+ ...parameters.reasoning,
985
+ summary: options.reasoningSummary
986
+ }
987
+ } : {},
988
+ ...options?.store !== void 0 ? { store: options.store } : {},
989
+ ...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
990
+ ...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
991
+ ...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
992
+ ...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
993
+ ...options?.store === false || options?.include ? {
994
+ include: [
995
+ .../* @__PURE__ */ new Set([
996
+ ...options?.include ?? [],
997
+ ...options?.store === false ? ["reasoning.encrypted_content"] : []
998
+ ])
999
+ ]
1000
+ } : {},
789
1001
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
790
1002
  stream
791
1003
  };
@@ -844,6 +1056,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
844
1056
  let usage;
845
1057
  const citations = [];
846
1058
  let completed = false;
1059
+ let terminalResponse;
1060
+ let responseIdentity = {};
1061
+ const outputItems = /* @__PURE__ */ new Map();
1062
+ let streamedText = "";
847
1063
  function* flushToolCalls() {
848
1064
  for (const call of toolCalls.values()) {
849
1065
  if (!call.name)
@@ -860,17 +1076,36 @@ var OpenAIAdapter = class _OpenAIAdapter {
860
1076
  for await (const event of stream) {
861
1077
  throwIfAborted(request.signal);
862
1078
  logProviderPayload("openai", "stream chunk", event, request.debug);
1079
+ if (completed)
1080
+ throw new Error("Responses stream emitted an event after completion");
863
1081
  const index = event?.output_index ?? 0;
864
1082
  switch (event?.type) {
1083
+ case "response.created":
1084
+ responseIdentity = {
1085
+ id: event.response?.id,
1086
+ model: event.response?.model
1087
+ };
1088
+ break;
1089
+ case "response.output_item.done":
1090
+ if (event.item) outputItems.set(index, event.item);
1091
+ if (event.item?.type === "function_call") {
1092
+ toolCalls.set(index, {
1093
+ id: event.item.call_id,
1094
+ name: event.item.name,
1095
+ arguments: event.item.arguments
1096
+ });
1097
+ }
1098
+ break;
865
1099
  case "response.output_text.delta":
866
1100
  if (event.delta) {
1101
+ streamedText += event.delta;
867
1102
  yield { type: "message:delta", content: event.delta };
868
1103
  }
869
1104
  break;
870
1105
  case "response.output_item.added":
871
1106
  if (event.item?.type === "function_call") {
872
1107
  toolCalls.set(index, {
873
- id: event.item.call_id ?? event.item.id ?? generateToolCallId(),
1108
+ id: event.item.call_id ?? "",
874
1109
  name: event.item.name ?? "",
875
1110
  arguments: event.item.arguments ?? ""
876
1111
  });
@@ -889,7 +1124,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
889
1124
  break;
890
1125
  }
891
1126
  case "response.completed": {
1127
+ if (event.response?.status && event.response.status !== "completed")
1128
+ throw new Error(`Responses stream ${event.response.status}`);
892
1129
  completed = true;
1130
+ terminalResponse = { ...responseIdentity, ...event.response };
893
1131
  const found = this.collectResponsesCitations(
894
1132
  event.response?.output,
895
1133
  citations.length
@@ -926,12 +1164,64 @@ var OpenAIAdapter = class _OpenAIAdapter {
926
1164
  throwIfAborted(request.signal);
927
1165
  if (!completed)
928
1166
  throw new Error("Responses stream ended without a completed response");
1167
+ const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
1168
+ if (output.some((item) => item.type === "function_call"))
1169
+ toolCalls.clear();
1170
+ for (const [index, item] of output.entries()) {
1171
+ if (item.type === "function_call") {
1172
+ toolCalls.set(index, {
1173
+ id: item.call_id,
1174
+ name: item.name,
1175
+ arguments: item.arguments
1176
+ });
1177
+ }
1178
+ }
1179
+ const uniqueCalls = /* @__PURE__ */ new Map();
1180
+ for (const call of toolCalls.values()) {
1181
+ if (!call.id || !call.name)
1182
+ throw new Error("Responses stream contains an incomplete tool call");
1183
+ uniqueCalls.set(call.id, call);
1184
+ }
1185
+ toolCalls.clear();
1186
+ [...uniqueCalls.values()].forEach(
1187
+ (call, index) => toolCalls.set(index, call)
1188
+ );
1189
+ if (!output.length && toolCalls.size) {
1190
+ for (const call of toolCalls.values())
1191
+ output.push({
1192
+ type: "function_call",
1193
+ call_id: call.id,
1194
+ name: call.name,
1195
+ arguments: call.arguments
1196
+ });
1197
+ }
1198
+ if (!output.length && streamedText)
1199
+ output.push({
1200
+ type: "message",
1201
+ role: "assistant",
1202
+ content: [{ type: "output_text", text: streamedText, annotations: [] }]
1203
+ });
1204
+ terminalResponse.output = output;
1205
+ const finalText = this.parseResponsesResult(terminalResponse).content;
1206
+ if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
1207
+ yield {
1208
+ type: "message:delta",
1209
+ content: finalText.slice(streamedText.length)
1210
+ };
1211
+ }
929
1212
  yield* flushToolCalls();
930
1213
  if (citations.length > 0) {
931
1214
  yield { type: "citation", citations: deduplicateCitations(citations) };
932
1215
  }
933
- yield { type: "message:end" };
934
- yield { type: "done", usage };
1216
+ yield {
1217
+ type: "message:end",
1218
+ providerMetadata: responseMetadata(terminalResponse)
1219
+ };
1220
+ yield {
1221
+ type: "done",
1222
+ usage,
1223
+ providerMetadata: responseMetadata(terminalResponse)
1224
+ };
935
1225
  }
936
1226
  async *stream(request) {
937
1227
  throwIfAborted(request.signal);