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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +146 -0
  2. package/dist/adapters/index.d.mts +5 -5
  3. package/dist/adapters/index.d.ts +5 -5
  4. package/dist/adapters/index.js +437 -39
  5. package/dist/adapters/index.mjs +437 -39
  6. package/dist/audio-BBSwnjo8.d.mts +92 -0
  7. package/dist/audio-BBSwnjo8.d.ts +92 -0
  8. package/dist/{base-CkeT7u-O.d.mts → base-BICaQaRS.d.mts} +7 -3
  9. package/dist/{base-BdTpUAWI.d.ts → base-CpRPB8u8.d.ts} +7 -3
  10. package/dist/fallback/index.d.mts +4 -4
  11. package/dist/fallback/index.d.ts +4 -4
  12. package/dist/index.d.mts +20 -36
  13. package/dist/index.d.ts +20 -36
  14. package/dist/index.js +327 -69
  15. package/dist/index.mjs +325 -70
  16. package/dist/providers/anthropic/index.d.mts +3 -3
  17. package/dist/providers/anthropic/index.d.ts +3 -3
  18. package/dist/providers/anthropic/index.js +116 -7
  19. package/dist/providers/anthropic/index.mjs +116 -7
  20. package/dist/providers/azure/index.d.mts +3 -3
  21. package/dist/providers/azure/index.d.ts +3 -3
  22. package/dist/providers/azure/index.js +101 -4
  23. package/dist/providers/azure/index.mjs +101 -4
  24. package/dist/providers/fireworks/index.d.mts +3 -3
  25. package/dist/providers/fireworks/index.d.ts +3 -3
  26. package/dist/providers/fireworks/index.js +393 -31
  27. package/dist/providers/fireworks/index.mjs +393 -31
  28. package/dist/providers/google/index.d.mts +3 -3
  29. package/dist/providers/google/index.d.ts +3 -3
  30. package/dist/providers/google/index.js +393 -31
  31. package/dist/providers/google/index.mjs +393 -31
  32. package/dist/providers/ollama/index.d.mts +4 -4
  33. package/dist/providers/ollama/index.d.ts +4 -4
  34. package/dist/providers/ollama/index.js +87 -2
  35. package/dist/providers/ollama/index.mjs +87 -2
  36. package/dist/providers/openai/index.d.mts +25 -6
  37. package/dist/providers/openai/index.d.ts +25 -6
  38. package/dist/providers/openai/index.js +822 -40
  39. package/dist/providers/openai/index.mjs +821 -41
  40. package/dist/providers/openrouter/index.d.mts +3 -3
  41. package/dist/providers/openrouter/index.d.ts +3 -3
  42. package/dist/providers/openrouter/index.js +393 -31
  43. package/dist/providers/openrouter/index.mjs +393 -31
  44. package/dist/providers/togetherai/index.d.mts +3 -3
  45. package/dist/providers/togetherai/index.d.ts +3 -3
  46. package/dist/providers/togetherai/index.js +393 -31
  47. package/dist/providers/togetherai/index.mjs +393 -31
  48. package/dist/providers/xai/index.d.mts +3 -3
  49. package/dist/providers/xai/index.d.ts +3 -3
  50. package/dist/providers/xai/index.js +393 -31
  51. package/dist/providers/xai/index.mjs +393 -31
  52. package/dist/{types-BWRYIvl_.d.mts → types-C3RgRbEC.d.mts} +1 -1
  53. package/dist/{types-CpXcT-tY.d.ts → types-DCFqkJAy.d.ts} +1 -1
  54. package/dist/{types-NOswlhm9.d.mts → types-DHQEMFHA.d.mts} +3 -3
  55. package/dist/{types-BW3csYo5.d.mts → types-Dg1Bcg1k.d.mts} +106 -3
  56. package/dist/{types-BW3csYo5.d.ts → types-Dg1Bcg1k.d.ts} +106 -3
  57. package/dist/{types-DA54bSmX.d.ts → types-Dikj3En4.d.ts} +3 -3
  58. package/dist/yourgpt/index.d.mts +1 -1
  59. package/dist/yourgpt/index.d.ts +1 -1
  60. package/docs/multimodal-and-audio.md +170 -0
  61. package/package.json +3 -2
@@ -1,3 +1,106 @@
1
+ // src/core/tool-output.ts
2
+ var imageTypes = /* @__PURE__ */ new Set([
3
+ "image/png",
4
+ "image/jpeg",
5
+ "image/webp",
6
+ "image/gif"
7
+ ]);
8
+ function normalizeToolOutput(value) {
9
+ const output = value;
10
+ if (output?.type !== "content" || !Array.isArray(output.content) || !output.content.length) {
11
+ throw new Error("Tool model output must contain a nonempty content array");
12
+ }
13
+ return {
14
+ type: "content",
15
+ content: output.content.map((part) => {
16
+ if (part?.type === "text" && typeof part.text === "string")
17
+ return { type: "text", text: part.text };
18
+ if (part?.type !== "image")
19
+ throw new Error("Unsupported tool output content type");
20
+ const sources = ["data", "url", "fileId"].filter((key) => key in part);
21
+ if (sources.length !== 1)
22
+ throw new Error(
23
+ "Tool image requires exactly one of data, url, or fileId"
24
+ );
25
+ if ("fileId" in part) {
26
+ if (part.provider !== "openai" || typeof part.fileId !== "string" || !part.fileId.trim())
27
+ throw new Error(
28
+ "Tool image file references must name the openai provider and fileId"
29
+ );
30
+ return { type: "image", fileId: part.fileId, provider: "openai" };
31
+ }
32
+ if ("url" in part) {
33
+ let url;
34
+ try {
35
+ url = new URL(part.url);
36
+ } catch {
37
+ throw new Error("Invalid tool image URL");
38
+ }
39
+ if (!["https:", "http:"].includes(url.protocol) || url.username || url.password)
40
+ throw new Error(
41
+ "Tool image URL must use HTTP(S) without credentials"
42
+ );
43
+ return { type: "image", url: url.href };
44
+ }
45
+ if (!("data" in part) || !imageTypes.has(part.mediaType))
46
+ throw new Error("Unsupported tool image media type");
47
+ let data = part.data instanceof Uint8Array ? Buffer.from(part.data).toString("base64") : part.data;
48
+ if (typeof data !== "string")
49
+ throw new Error("Tool image data must be base64 or bytes");
50
+ if (data.startsWith("data:")) {
51
+ const match = /^data:([^;,]+);base64,(.+)$/.exec(data);
52
+ if (!match || match[1] !== part.mediaType)
53
+ throw new Error(
54
+ "Tool image data URL media type does not match mediaType"
55
+ );
56
+ data = match[2];
57
+ }
58
+ if (!data.length || data.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(data))
59
+ throw new Error("Invalid tool image base64 data");
60
+ if (Buffer.byteLength(data, "base64") > 20 * 1024 * 1024)
61
+ throw new Error("Tool image exceeds 20 MiB");
62
+ if (Buffer.from(data, "base64").toString("base64") !== data)
63
+ throw new Error("Invalid tool image base64 data");
64
+ return { type: "image", data, mediaType: part.mediaType };
65
+ })
66
+ };
67
+ }
68
+ function toolOutputForProvider(value, provider) {
69
+ const output = normalizeToolOutput(value);
70
+ return output.content.map((part) => {
71
+ if (part.type === "text")
72
+ return {
73
+ type: provider === "responses" ? "input_text" : "text",
74
+ text: part.text
75
+ };
76
+ if (provider === "unsupported")
77
+ throw new Error(
78
+ "Image tool results require OpenAI Responses or Anthropic; this endpoint is not supported"
79
+ );
80
+ if ("fileId" in part) {
81
+ if (provider !== "responses")
82
+ throw new Error(
83
+ "OpenAI image file references cannot be sent to another provider"
84
+ );
85
+ return { type: "input_image", file_id: part.fileId, detail: "auto" };
86
+ }
87
+ if (provider === "responses")
88
+ return {
89
+ type: "input_image",
90
+ image_url: "url" in part ? part.url : `data:${part.mediaType};base64,${part.data}`,
91
+ detail: "auto"
92
+ };
93
+ return {
94
+ type: "image",
95
+ source: "url" in part ? { type: "url", url: part.url } : { type: "base64", media_type: part.mediaType, data: part.data }
96
+ };
97
+ });
98
+ }
99
+ function textToolOutput(value, fallback) {
100
+ if (value === void 0) return fallback;
101
+ return toolOutputForProvider(value, "unsupported").map((part) => part.text).join("\n");
102
+ }
103
+
1
104
  // src/core/openai-parameters.ts
2
105
  function capabilities(model) {
3
106
  const gpt = /^gpt-(\d+)(?:\.(\d+))?(?:-(.+))?$/i.exec(model);
@@ -239,7 +342,7 @@ function messageToOpenAIContent(message) {
239
342
  }
240
343
  return blocks;
241
344
  }
242
- function formatMessagesForOpenAI(messages, systemPrompt) {
345
+ function formatMessagesForOpenAI(messages, systemPrompt, preserveProviderMetadata = false) {
243
346
  const formatted = [];
244
347
  if (systemPrompt) {
245
348
  formatted.push({ role: "system", content: systemPrompt });
@@ -257,7 +360,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
257
360
  const assistantMsg = {
258
361
  role: "assistant",
259
362
  // Gemini/xAI (OpenAI-compatible) reject content: "" on assistant messages with tool_calls
260
- content: hasToolCalls ? msg.content || null : msg.content
363
+ content: hasToolCalls ? msg.content || null : msg.content,
364
+ ...preserveProviderMetadata && msg.providerMetadata ? { providerMetadata: msg.providerMetadata } : {}
261
365
  };
262
366
  if (hasToolCalls) {
263
367
  assistantMsg.tool_calls = msg.tool_calls;
@@ -266,7 +370,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
266
370
  } else if (msg.role === "tool" && msg.tool_call_id) {
267
371
  formatted.push({
268
372
  role: "tool",
269
- content: msg.content ?? "",
373
+ content: preserveProviderMetadata ? msg.content ?? "" : textToolOutput(msg.modelOutput, msg.content ?? ""),
374
+ ...preserveProviderMetadata && msg.modelOutput ? { modelOutput: msg.modelOutput } : {},
270
375
  tool_call_id: msg.tool_call_id
271
376
  });
272
377
  }
@@ -473,7 +578,7 @@ function formatMessages(messages) {
473
578
  return {
474
579
  role: "tool",
475
580
  tool_call_id: msg.toolCallId,
476
- content: msg.content
581
+ content: textToolOutput(msg.modelOutput, msg.content)
477
582
  };
478
583
  default:
479
584
  return msg;
@@ -481,7 +586,120 @@ function formatMessages(messages) {
481
586
  });
482
587
  }
483
588
 
589
+ // src/core/openai-responses.ts
590
+ function resolveOpenAITransport(defaults, overrides) {
591
+ return {
592
+ api: overrides?.api ?? defaults.api ?? "auto",
593
+ responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
594
+ };
595
+ }
596
+ function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
597
+ const api = transport.api ?? "auto";
598
+ if (!["auto", "chat", "responses"].includes(api)) {
599
+ throw new Error(`Unknown OpenAI API selection: ${api}`);
600
+ }
601
+ if (api === "chat") {
602
+ if (transport.responses !== void 0 || nativeToolSearch) {
603
+ throw new Error(
604
+ "Responses options and native tool search require the Responses API"
605
+ );
606
+ }
607
+ return false;
608
+ }
609
+ let officialEndpoint = !baseURL;
610
+ try {
611
+ officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
612
+ } catch {
613
+ }
614
+ return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
615
+ }
616
+ function responseMetadata(response) {
617
+ const output = Array.isArray(response?.output) ? response.output : [];
618
+ return {
619
+ openai: {
620
+ response: {
621
+ id: response?.id,
622
+ model: response?.model,
623
+ status: response?.status,
624
+ output,
625
+ usage: response?.usage,
626
+ store: response?.store,
627
+ refusals: output.flatMap(
628
+ (item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
629
+ (part) => part?.type === "refusal" && typeof part.refusal === "string"
630
+ ).map((part) => part.refusal) : []
631
+ )
632
+ }
633
+ }
634
+ };
635
+ }
636
+ function responseReplayMetadata(metadata) {
637
+ const response = metadata?.openai?.response;
638
+ if (!response) return metadata;
639
+ const output = response.output.map((item) => {
640
+ const fields = ["type", "id", "status"];
641
+ switch (item.type) {
642
+ case "reasoning":
643
+ fields.push("encrypted_content");
644
+ break;
645
+ case "message":
646
+ fields.push("role", "phase");
647
+ break;
648
+ case "function_call":
649
+ fields.push("call_id", "name", "namespace", "arguments");
650
+ break;
651
+ case "tool_search_call":
652
+ fields.push("call_id", "execution", "arguments");
653
+ break;
654
+ case "tool_search_output":
655
+ fields.push("call_id", "tools");
656
+ break;
657
+ case "web_search_call":
658
+ fields.push("action");
659
+ break;
660
+ default:
661
+ return stripItemDiagnostics(item);
662
+ }
663
+ const replay = Object.fromEntries(
664
+ fields.filter((key) => item[key] !== void 0).map((key) => [key, item[key]])
665
+ );
666
+ if (item.type === "reasoning") replay.summary = [];
667
+ if (item.type === "message")
668
+ replay.content = Array.isArray(item.content) ? item.content.flatMap(
669
+ (part) => part?.type === "output_text" && typeof part.text === "string" ? [{ type: "output_text", text: part.text, annotations: [] }] : part?.type === "refusal" && typeof part.refusal === "string" ? [{ type: "refusal", refusal: part.refusal }] : []
670
+ ) : [];
671
+ return stripItemDiagnostics(replay);
672
+ });
673
+ return { openai: { response: { output, refusals: [] } } };
674
+ }
675
+ function stripItemDiagnostics(item) {
676
+ const {
677
+ usage: _usage,
678
+ logprobs: _logprobs,
679
+ metadata: _metadata,
680
+ ...state
681
+ } = item;
682
+ return state;
683
+ }
684
+ function getResponseReplay(metadata) {
685
+ const response = metadata?.openai?.response;
686
+ if (response === void 0) return void 0;
687
+ if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
688
+ throw new Error(
689
+ "Invalid OpenAI Responses replay metadata: output must contain typed items"
690
+ );
691
+ }
692
+ return responseReplayMetadata(metadata)?.openai?.response?.output;
693
+ }
694
+
484
695
  // src/core/provider-error.ts
696
+ function invalidProviderResponse(message) {
697
+ return Object.assign(new Error(message), {
698
+ name: "InvalidProviderResponseError",
699
+ status: 502,
700
+ code: "INVALID_PROVIDER_RESPONSE"
701
+ });
702
+ }
485
703
  function providerErrorEvent(error, code) {
486
704
  const details = error ?? {};
487
705
  const event = {
@@ -557,13 +775,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
557
775
  }));
558
776
  }
559
777
  shouldUseResponsesApi(request) {
560
- if (this.toolDefinitions(request).length === 0) return false;
561
- if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false)
562
- return true;
563
- return requiresOpenAIResponses(
778
+ return useOpenAIResponses(
564
779
  request.config?.model || this.model,
565
780
  this.config.baseUrl,
566
- 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
567
788
  );
568
789
  }
569
790
  /**
@@ -596,11 +817,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
596
817
  });
597
818
  }
598
819
  buildResponsesInput(request) {
599
- 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);
600
821
  const input = [];
601
822
  for (const message of sourceMessages) {
602
823
  if (message.role === "system") {
603
- if (!request.systemPrompt && message.content) {
824
+ if (message.content && message.content !== request.systemPrompt) {
604
825
  input.push({
605
826
  type: "message",
606
827
  role: "developer",
@@ -610,6 +831,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
610
831
  continue;
611
832
  }
612
833
  if (message.role === "assistant") {
834
+ const replay = getResponseReplay(message.providerMetadata);
835
+ if (replay) {
836
+ input.push(...replay);
837
+ continue;
838
+ }
613
839
  const content = this.toResponsesContent(message.content, "assistant");
614
840
  if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
615
841
  input.push({
@@ -633,7 +859,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
633
859
  input.push({
634
860
  type: "function_call_output",
635
861
  call_id: message.tool_call_id,
636
- 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)
637
863
  });
638
864
  continue;
639
865
  }
@@ -647,10 +873,12 @@ var OpenAIAdapter = class _OpenAIAdapter {
647
873
  return input;
648
874
  }
649
875
  /** Preserve Copilot screenshot attachments on every OpenAI transport. */
650
- formatRawMessages(messages) {
876
+ formatRawMessages(messages, preserveProviderMetadata = false) {
651
877
  return messages.map((message) => {
652
- const { attachments, ...rest } = message;
653
- let content = message.content;
878
+ const { attachments, providerMetadata, modelOutput, ...rest } = message;
879
+ let content = message.role === "tool" && !preserveProviderMetadata && modelOutput !== void 0 ? textToolOutput(modelOutput, "") : message.content;
880
+ if (preserveProviderMetadata && modelOutput)
881
+ rest.modelOutput = modelOutput;
654
882
  if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
655
883
  content = null;
656
884
  }
@@ -663,7 +891,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
663
891
  content = [...parts, ...images];
664
892
  }
665
893
  }
666
- return { ...rest, content };
894
+ return {
895
+ ...rest,
896
+ content,
897
+ ...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
898
+ };
667
899
  });
668
900
  }
669
901
  /**
@@ -744,14 +976,40 @@ var OpenAIAdapter = class _OpenAIAdapter {
744
976
  ).map((part) => part.text) : []
745
977
  ).join("") : "";
746
978
  const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
747
- const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
748
- id: item.call_id ?? item.id ?? generateToolCallId(),
749
- name: item.name,
750
- args: JSON.parse(item.arguments ?? "{}")
751
- })) : [];
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 invalidProviderResponse(
983
+ "Responses contains an incomplete function call"
984
+ );
985
+ }
986
+ if (seenCallIds.has(item.call_id))
987
+ throw invalidProviderResponse(
988
+ "Responses contains duplicate function call IDs"
989
+ );
990
+ seenCallIds.add(item.call_id);
991
+ let args;
992
+ try {
993
+ args = JSON.parse(item.arguments);
994
+ } catch {
995
+ throw invalidProviderResponse(
996
+ "Responses function arguments must be valid JSON"
997
+ );
998
+ }
999
+ if (!args || typeof args !== "object" || Array.isArray(args))
1000
+ throw invalidProviderResponse(
1001
+ "Responses function arguments must be a JSON object"
1002
+ );
1003
+ return {
1004
+ id: item.call_id,
1005
+ name: item.name,
1006
+ args
1007
+ };
1008
+ }) : [];
752
1009
  return {
753
1010
  content,
754
1011
  toolCalls,
1012
+ providerMetadata: responseMetadata(response),
755
1013
  usage: response?.usage ? {
756
1014
  promptTokens: response.usage.input_tokens ?? 0,
757
1015
  completionTokens: response.usage.output_tokens ?? 0,
@@ -762,6 +1020,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
762
1020
  }
763
1021
  buildResponsesPayload(request, stream) {
764
1022
  const openaiToolOptions = request.providerToolOptions?.openai;
1023
+ const options = resolveOpenAITransport(
1024
+ this.config,
1025
+ request.config?.openai
1026
+ ).responses;
1027
+ const parameters = buildOpenAIParameters(
1028
+ request.config?.model || this.model,
1029
+ request.config?.maxTokens ?? this.config.maxTokens,
1030
+ request.config?.temperature ?? this.config.temperature,
1031
+ this.parameterOptions(request),
1032
+ "responses"
1033
+ );
765
1034
  const responsesTextFormat = toOpenAIResponsesTextFormat(
766
1035
  request.config?.responseFormat
767
1036
  );
@@ -777,13 +1046,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
777
1046
  ),
778
1047
  tool_choice: openaiToolOptions?.toolChoice,
779
1048
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
780
- ...buildOpenAIParameters(
781
- modelIdForPayload,
782
- request.config?.maxTokens ?? this.config.maxTokens,
783
- request.config?.temperature ?? this.config.temperature,
784
- this.parameterOptions(request),
785
- "responses"
786
- ),
1049
+ ...parameters,
1050
+ ...options?.reasoningSummary ? {
1051
+ reasoning: {
1052
+ ...parameters.reasoning,
1053
+ summary: options.reasoningSummary
1054
+ }
1055
+ } : {},
1056
+ ...options?.store !== void 0 ? { store: options.store } : {},
1057
+ ...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
1058
+ ...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
1059
+ ...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
1060
+ ...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
1061
+ ...options?.store === false || options?.include ? {
1062
+ include: [
1063
+ .../* @__PURE__ */ new Set([
1064
+ ...options?.include ?? [],
1065
+ ...options?.store === false ? ["reasoning.encrypted_content"] : []
1066
+ ])
1067
+ ]
1068
+ } : {},
787
1069
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
788
1070
  stream
789
1071
  };
@@ -842,6 +1124,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
842
1124
  let usage;
843
1125
  const citations = [];
844
1126
  let completed = false;
1127
+ let terminalResponse;
1128
+ let responseIdentity = {};
1129
+ const outputItems = /* @__PURE__ */ new Map();
1130
+ let streamedText = "";
845
1131
  function* flushToolCalls() {
846
1132
  for (const call of toolCalls.values()) {
847
1133
  if (!call.name)
@@ -858,17 +1144,36 @@ var OpenAIAdapter = class _OpenAIAdapter {
858
1144
  for await (const event of stream) {
859
1145
  throwIfAborted(request.signal);
860
1146
  logProviderPayload("openai", "stream chunk", event, request.debug);
1147
+ if (completed)
1148
+ throw new Error("Responses stream emitted an event after completion");
861
1149
  const index = event?.output_index ?? 0;
862
1150
  switch (event?.type) {
1151
+ case "response.created":
1152
+ responseIdentity = {
1153
+ id: event.response?.id,
1154
+ model: event.response?.model
1155
+ };
1156
+ break;
1157
+ case "response.output_item.done":
1158
+ if (event.item) outputItems.set(index, event.item);
1159
+ if (event.item?.type === "function_call") {
1160
+ toolCalls.set(index, {
1161
+ id: event.item.call_id,
1162
+ name: event.item.name,
1163
+ arguments: event.item.arguments
1164
+ });
1165
+ }
1166
+ break;
863
1167
  case "response.output_text.delta":
864
1168
  if (event.delta) {
1169
+ streamedText += event.delta;
865
1170
  yield { type: "message:delta", content: event.delta };
866
1171
  }
867
1172
  break;
868
1173
  case "response.output_item.added":
869
1174
  if (event.item?.type === "function_call") {
870
1175
  toolCalls.set(index, {
871
- id: event.item.call_id ?? event.item.id ?? generateToolCallId(),
1176
+ id: event.item.call_id ?? "",
872
1177
  name: event.item.name ?? "",
873
1178
  arguments: event.item.arguments ?? ""
874
1179
  });
@@ -887,7 +1192,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
887
1192
  break;
888
1193
  }
889
1194
  case "response.completed": {
1195
+ if (event.response?.status && event.response.status !== "completed")
1196
+ throw new Error(`Responses stream ${event.response.status}`);
890
1197
  completed = true;
1198
+ terminalResponse = { ...responseIdentity, ...event.response };
891
1199
  const found = this.collectResponsesCitations(
892
1200
  event.response?.output,
893
1201
  citations.length
@@ -924,12 +1232,66 @@ var OpenAIAdapter = class _OpenAIAdapter {
924
1232
  throwIfAborted(request.signal);
925
1233
  if (!completed)
926
1234
  throw new Error("Responses stream ended without a completed response");
1235
+ const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
1236
+ if (output.some((item) => item.type === "function_call"))
1237
+ toolCalls.clear();
1238
+ for (const [index, item] of output.entries()) {
1239
+ if (item.type === "function_call") {
1240
+ toolCalls.set(index, {
1241
+ id: item.call_id,
1242
+ name: item.name,
1243
+ arguments: item.arguments
1244
+ });
1245
+ }
1246
+ }
1247
+ const uniqueCalls = /* @__PURE__ */ new Map();
1248
+ for (const call of toolCalls.values()) {
1249
+ if (!call.id || !call.name)
1250
+ throw invalidProviderResponse(
1251
+ "Responses stream contains an incomplete tool call"
1252
+ );
1253
+ uniqueCalls.set(call.id, call);
1254
+ }
1255
+ toolCalls.clear();
1256
+ [...uniqueCalls.values()].forEach(
1257
+ (call, index) => toolCalls.set(index, call)
1258
+ );
1259
+ if (!output.length && toolCalls.size) {
1260
+ for (const call of toolCalls.values())
1261
+ output.push({
1262
+ type: "function_call",
1263
+ call_id: call.id,
1264
+ name: call.name,
1265
+ arguments: call.arguments
1266
+ });
1267
+ }
1268
+ if (!output.length && streamedText)
1269
+ output.push({
1270
+ type: "message",
1271
+ role: "assistant",
1272
+ content: [{ type: "output_text", text: streamedText, annotations: [] }]
1273
+ });
1274
+ terminalResponse.output = output;
1275
+ const finalText = this.parseResponsesResult(terminalResponse).content;
1276
+ if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
1277
+ yield {
1278
+ type: "message:delta",
1279
+ content: finalText.slice(streamedText.length)
1280
+ };
1281
+ }
927
1282
  yield* flushToolCalls();
928
1283
  if (citations.length > 0) {
929
1284
  yield { type: "citation", citations: deduplicateCitations(citations) };
930
1285
  }
931
- yield { type: "message:end" };
932
- yield { type: "done", usage };
1286
+ yield {
1287
+ type: "message:end",
1288
+ providerMetadata: responseMetadata(terminalResponse)
1289
+ };
1290
+ yield {
1291
+ type: "done",
1292
+ usage,
1293
+ providerMetadata: responseMetadata(terminalResponse)
1294
+ };
933
1295
  }
934
1296
  async *stream(request) {
935
1297
  throwIfAborted(request.signal);
@@ -1,7 +1,7 @@
1
- import { L as LanguageModel } from '../../types-BW3csYo5.mjs';
2
- import { G as GoogleProviderConfig, A as AIProvider } from '../../types-NOswlhm9.mjs';
1
+ import { L as LanguageModel } from '../../types-Dg1Bcg1k.mjs';
2
+ import { G as GoogleProviderConfig, 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
  * Google Provider - OpenAI-Compatible
@@ -1,7 +1,7 @@
1
- import { L as LanguageModel } from '../../types-BW3csYo5.js';
2
- import { G as GoogleProviderConfig, A as AIProvider } from '../../types-DA54bSmX.js';
1
+ import { L as LanguageModel } from '../../types-Dg1Bcg1k.js';
2
+ import { G as GoogleProviderConfig, 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
  * Google Provider - OpenAI-Compatible