@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
  }
@@ -478,7 +583,7 @@ function formatMessages(messages) {
478
583
  return {
479
584
  role: "tool",
480
585
  tool_call_id: msg.toolCallId,
481
- content: msg.content
586
+ content: textToolOutput(msg.modelOutput, msg.content)
482
587
  };
483
588
  default:
484
589
  return msg;
@@ -486,7 +591,120 @@ function formatMessages(messages) {
486
591
  });
487
592
  }
488
593
 
594
+ // src/core/openai-responses.ts
595
+ function resolveOpenAITransport(defaults, overrides) {
596
+ return {
597
+ api: overrides?.api ?? defaults.api ?? "auto",
598
+ responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
599
+ };
600
+ }
601
+ function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
602
+ const api = transport.api ?? "auto";
603
+ if (!["auto", "chat", "responses"].includes(api)) {
604
+ throw new Error(`Unknown OpenAI API selection: ${api}`);
605
+ }
606
+ if (api === "chat") {
607
+ if (transport.responses !== void 0 || nativeToolSearch) {
608
+ throw new Error(
609
+ "Responses options and native tool search require the Responses API"
610
+ );
611
+ }
612
+ return false;
613
+ }
614
+ let officialEndpoint = !baseURL;
615
+ try {
616
+ officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
617
+ } catch {
618
+ }
619
+ return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
620
+ }
621
+ function responseMetadata(response) {
622
+ const output = Array.isArray(response?.output) ? response.output : [];
623
+ return {
624
+ openai: {
625
+ response: {
626
+ id: response?.id,
627
+ model: response?.model,
628
+ status: response?.status,
629
+ output,
630
+ usage: response?.usage,
631
+ store: response?.store,
632
+ refusals: output.flatMap(
633
+ (item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
634
+ (part) => part?.type === "refusal" && typeof part.refusal === "string"
635
+ ).map((part) => part.refusal) : []
636
+ )
637
+ }
638
+ }
639
+ };
640
+ }
641
+ function responseReplayMetadata(metadata) {
642
+ const response = metadata?.openai?.response;
643
+ if (!response) return metadata;
644
+ const output = response.output.map((item) => {
645
+ const fields = ["type", "id", "status"];
646
+ switch (item.type) {
647
+ case "reasoning":
648
+ fields.push("encrypted_content");
649
+ break;
650
+ case "message":
651
+ fields.push("role", "phase");
652
+ break;
653
+ case "function_call":
654
+ fields.push("call_id", "name", "namespace", "arguments");
655
+ break;
656
+ case "tool_search_call":
657
+ fields.push("call_id", "execution", "arguments");
658
+ break;
659
+ case "tool_search_output":
660
+ fields.push("call_id", "tools");
661
+ break;
662
+ case "web_search_call":
663
+ fields.push("action");
664
+ break;
665
+ default:
666
+ return stripItemDiagnostics(item);
667
+ }
668
+ const replay = Object.fromEntries(
669
+ fields.filter((key) => item[key] !== void 0).map((key) => [key, item[key]])
670
+ );
671
+ if (item.type === "reasoning") replay.summary = [];
672
+ if (item.type === "message")
673
+ replay.content = Array.isArray(item.content) ? item.content.flatMap(
674
+ (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 }] : []
675
+ ) : [];
676
+ return stripItemDiagnostics(replay);
677
+ });
678
+ return { openai: { response: { output, refusals: [] } } };
679
+ }
680
+ function stripItemDiagnostics(item) {
681
+ const {
682
+ usage: _usage,
683
+ logprobs: _logprobs,
684
+ metadata: _metadata,
685
+ ...state
686
+ } = item;
687
+ return state;
688
+ }
689
+ function getResponseReplay(metadata) {
690
+ const response = metadata?.openai?.response;
691
+ if (response === void 0) return void 0;
692
+ if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
693
+ throw new Error(
694
+ "Invalid OpenAI Responses replay metadata: output must contain typed items"
695
+ );
696
+ }
697
+ return responseReplayMetadata(metadata)?.openai?.response?.output;
698
+ }
699
+
489
700
  // src/core/provider-error.ts
701
+ function invalidProviderResponse(message) {
702
+ return Object.assign(new Error(message), {
703
+ name: "InvalidProviderResponseError",
704
+ status: 502,
705
+ code: "INVALID_PROVIDER_RESPONSE"
706
+ });
707
+ }
490
708
  function providerErrorEvent(error, code) {
491
709
  const details = error ?? {};
492
710
  const event = {
@@ -562,13 +780,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
562
780
  }));
563
781
  }
564
782
  shouldUseResponsesApi(request) {
565
- if (this.toolDefinitions(request).length === 0) return false;
566
- if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false)
567
- return true;
568
- return requiresOpenAIResponses(
783
+ return useOpenAIResponses(
569
784
  request.config?.model || this.model,
570
785
  this.config.baseUrl,
571
- this.parameterOptions(request)
786
+ this.parameterOptions(request),
787
+ resolveOpenAITransport(this.config, request.config?.openai),
788
+ this.toolDefinitions(request).length > 0,
789
+ !!(request.rawMessages ?? request.messages)?.some(
790
+ (message) => message.providerMetadata?.openai?.response
791
+ ),
792
+ request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
572
793
  );
573
794
  }
574
795
  /**
@@ -601,11 +822,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
601
822
  });
602
823
  }
603
824
  buildResponsesInput(request) {
604
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages) : formatMessagesForOpenAI(request.messages, void 0);
825
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
605
826
  const input = [];
606
827
  for (const message of sourceMessages) {
607
828
  if (message.role === "system") {
608
- if (!request.systemPrompt && message.content) {
829
+ if (message.content && message.content !== request.systemPrompt) {
609
830
  input.push({
610
831
  type: "message",
611
832
  role: "developer",
@@ -615,6 +836,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
615
836
  continue;
616
837
  }
617
838
  if (message.role === "assistant") {
839
+ const replay = getResponseReplay(message.providerMetadata);
840
+ if (replay) {
841
+ input.push(...replay);
842
+ continue;
843
+ }
618
844
  const content = this.toResponsesContent(message.content, "assistant");
619
845
  if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
620
846
  input.push({
@@ -638,7 +864,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
638
864
  input.push({
639
865
  type: "function_call_output",
640
866
  call_id: message.tool_call_id,
641
- output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
867
+ output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
642
868
  });
643
869
  continue;
644
870
  }
@@ -652,10 +878,12 @@ var OpenAIAdapter = class _OpenAIAdapter {
652
878
  return input;
653
879
  }
654
880
  /** Preserve Copilot screenshot attachments on every OpenAI transport. */
655
- formatRawMessages(messages) {
881
+ formatRawMessages(messages, preserveProviderMetadata = false) {
656
882
  return messages.map((message) => {
657
- const { attachments, ...rest } = message;
658
- let content = message.content;
883
+ const { attachments, providerMetadata, modelOutput, ...rest } = message;
884
+ let content = message.role === "tool" && !preserveProviderMetadata && modelOutput !== void 0 ? textToolOutput(modelOutput, "") : message.content;
885
+ if (preserveProviderMetadata && modelOutput)
886
+ rest.modelOutput = modelOutput;
659
887
  if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
660
888
  content = null;
661
889
  }
@@ -668,7 +896,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
668
896
  content = [...parts, ...images];
669
897
  }
670
898
  }
671
- return { ...rest, content };
899
+ return {
900
+ ...rest,
901
+ content,
902
+ ...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
903
+ };
672
904
  });
673
905
  }
674
906
  /**
@@ -749,14 +981,40 @@ var OpenAIAdapter = class _OpenAIAdapter {
749
981
  ).map((part) => part.text) : []
750
982
  ).join("") : "";
751
983
  const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
752
- const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
753
- id: item.call_id ?? item.id ?? generateToolCallId(),
754
- name: item.name,
755
- args: JSON.parse(item.arguments ?? "{}")
756
- })) : [];
984
+ const seenCallIds = /* @__PURE__ */ new Set();
985
+ const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
986
+ if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
987
+ throw invalidProviderResponse(
988
+ "Responses contains an incomplete function call"
989
+ );
990
+ }
991
+ if (seenCallIds.has(item.call_id))
992
+ throw invalidProviderResponse(
993
+ "Responses contains duplicate function call IDs"
994
+ );
995
+ seenCallIds.add(item.call_id);
996
+ let args;
997
+ try {
998
+ args = JSON.parse(item.arguments);
999
+ } catch {
1000
+ throw invalidProviderResponse(
1001
+ "Responses function arguments must be valid JSON"
1002
+ );
1003
+ }
1004
+ if (!args || typeof args !== "object" || Array.isArray(args))
1005
+ throw invalidProviderResponse(
1006
+ "Responses function arguments must be a JSON object"
1007
+ );
1008
+ return {
1009
+ id: item.call_id,
1010
+ name: item.name,
1011
+ args
1012
+ };
1013
+ }) : [];
757
1014
  return {
758
1015
  content,
759
1016
  toolCalls,
1017
+ providerMetadata: responseMetadata(response),
760
1018
  usage: response?.usage ? {
761
1019
  promptTokens: response.usage.input_tokens ?? 0,
762
1020
  completionTokens: response.usage.output_tokens ?? 0,
@@ -767,6 +1025,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
767
1025
  }
768
1026
  buildResponsesPayload(request, stream) {
769
1027
  const openaiToolOptions = request.providerToolOptions?.openai;
1028
+ const options = resolveOpenAITransport(
1029
+ this.config,
1030
+ request.config?.openai
1031
+ ).responses;
1032
+ const parameters = buildOpenAIParameters(
1033
+ request.config?.model || this.model,
1034
+ request.config?.maxTokens ?? this.config.maxTokens,
1035
+ request.config?.temperature ?? this.config.temperature,
1036
+ this.parameterOptions(request),
1037
+ "responses"
1038
+ );
770
1039
  const responsesTextFormat = toOpenAIResponsesTextFormat(
771
1040
  request.config?.responseFormat
772
1041
  );
@@ -782,13 +1051,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
782
1051
  ),
783
1052
  tool_choice: openaiToolOptions?.toolChoice,
784
1053
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
785
- ...buildOpenAIParameters(
786
- modelIdForPayload,
787
- request.config?.maxTokens ?? this.config.maxTokens,
788
- request.config?.temperature ?? this.config.temperature,
789
- this.parameterOptions(request),
790
- "responses"
791
- ),
1054
+ ...parameters,
1055
+ ...options?.reasoningSummary ? {
1056
+ reasoning: {
1057
+ ...parameters.reasoning,
1058
+ summary: options.reasoningSummary
1059
+ }
1060
+ } : {},
1061
+ ...options?.store !== void 0 ? { store: options.store } : {},
1062
+ ...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
1063
+ ...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
1064
+ ...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
1065
+ ...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
1066
+ ...options?.store === false || options?.include ? {
1067
+ include: [
1068
+ .../* @__PURE__ */ new Set([
1069
+ ...options?.include ?? [],
1070
+ ...options?.store === false ? ["reasoning.encrypted_content"] : []
1071
+ ])
1072
+ ]
1073
+ } : {},
792
1074
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
793
1075
  stream
794
1076
  };
@@ -847,6 +1129,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
847
1129
  let usage;
848
1130
  const citations = [];
849
1131
  let completed = false;
1132
+ let terminalResponse;
1133
+ let responseIdentity = {};
1134
+ const outputItems = /* @__PURE__ */ new Map();
1135
+ let streamedText = "";
850
1136
  function* flushToolCalls() {
851
1137
  for (const call of toolCalls.values()) {
852
1138
  if (!call.name)
@@ -863,17 +1149,36 @@ var OpenAIAdapter = class _OpenAIAdapter {
863
1149
  for await (const event of stream) {
864
1150
  throwIfAborted(request.signal);
865
1151
  logProviderPayload("openai", "stream chunk", event, request.debug);
1152
+ if (completed)
1153
+ throw new Error("Responses stream emitted an event after completion");
866
1154
  const index = event?.output_index ?? 0;
867
1155
  switch (event?.type) {
1156
+ case "response.created":
1157
+ responseIdentity = {
1158
+ id: event.response?.id,
1159
+ model: event.response?.model
1160
+ };
1161
+ break;
1162
+ case "response.output_item.done":
1163
+ if (event.item) outputItems.set(index, event.item);
1164
+ if (event.item?.type === "function_call") {
1165
+ toolCalls.set(index, {
1166
+ id: event.item.call_id,
1167
+ name: event.item.name,
1168
+ arguments: event.item.arguments
1169
+ });
1170
+ }
1171
+ break;
868
1172
  case "response.output_text.delta":
869
1173
  if (event.delta) {
1174
+ streamedText += event.delta;
870
1175
  yield { type: "message:delta", content: event.delta };
871
1176
  }
872
1177
  break;
873
1178
  case "response.output_item.added":
874
1179
  if (event.item?.type === "function_call") {
875
1180
  toolCalls.set(index, {
876
- id: event.item.call_id ?? event.item.id ?? generateToolCallId(),
1181
+ id: event.item.call_id ?? "",
877
1182
  name: event.item.name ?? "",
878
1183
  arguments: event.item.arguments ?? ""
879
1184
  });
@@ -892,7 +1197,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
892
1197
  break;
893
1198
  }
894
1199
  case "response.completed": {
1200
+ if (event.response?.status && event.response.status !== "completed")
1201
+ throw new Error(`Responses stream ${event.response.status}`);
895
1202
  completed = true;
1203
+ terminalResponse = { ...responseIdentity, ...event.response };
896
1204
  const found = this.collectResponsesCitations(
897
1205
  event.response?.output,
898
1206
  citations.length
@@ -929,12 +1237,66 @@ var OpenAIAdapter = class _OpenAIAdapter {
929
1237
  throwIfAborted(request.signal);
930
1238
  if (!completed)
931
1239
  throw new Error("Responses stream ended without a completed response");
1240
+ const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
1241
+ if (output.some((item) => item.type === "function_call"))
1242
+ toolCalls.clear();
1243
+ for (const [index, item] of output.entries()) {
1244
+ if (item.type === "function_call") {
1245
+ toolCalls.set(index, {
1246
+ id: item.call_id,
1247
+ name: item.name,
1248
+ arguments: item.arguments
1249
+ });
1250
+ }
1251
+ }
1252
+ const uniqueCalls = /* @__PURE__ */ new Map();
1253
+ for (const call of toolCalls.values()) {
1254
+ if (!call.id || !call.name)
1255
+ throw invalidProviderResponse(
1256
+ "Responses stream contains an incomplete tool call"
1257
+ );
1258
+ uniqueCalls.set(call.id, call);
1259
+ }
1260
+ toolCalls.clear();
1261
+ [...uniqueCalls.values()].forEach(
1262
+ (call, index) => toolCalls.set(index, call)
1263
+ );
1264
+ if (!output.length && toolCalls.size) {
1265
+ for (const call of toolCalls.values())
1266
+ output.push({
1267
+ type: "function_call",
1268
+ call_id: call.id,
1269
+ name: call.name,
1270
+ arguments: call.arguments
1271
+ });
1272
+ }
1273
+ if (!output.length && streamedText)
1274
+ output.push({
1275
+ type: "message",
1276
+ role: "assistant",
1277
+ content: [{ type: "output_text", text: streamedText, annotations: [] }]
1278
+ });
1279
+ terminalResponse.output = output;
1280
+ const finalText = this.parseResponsesResult(terminalResponse).content;
1281
+ if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
1282
+ yield {
1283
+ type: "message:delta",
1284
+ content: finalText.slice(streamedText.length)
1285
+ };
1286
+ }
932
1287
  yield* flushToolCalls();
933
1288
  if (citations.length > 0) {
934
1289
  yield { type: "citation", citations: deduplicateCitations(citations) };
935
1290
  }
936
- yield { type: "message:end" };
937
- yield { type: "done", usage };
1291
+ yield {
1292
+ type: "message:end",
1293
+ providerMetadata: responseMetadata(terminalResponse)
1294
+ };
1295
+ yield {
1296
+ type: "done",
1297
+ usage,
1298
+ providerMetadata: responseMetadata(terminalResponse)
1299
+ };
938
1300
  }
939
1301
  async *stream(request) {
940
1302
  throwIfAborted(request.signal);
@@ -1,7 +1,7 @@
1
- import { L as LanguageModel } from '../../types-BW3csYo5.mjs';
2
- import { X as XAIProviderConfig, A as AIProvider } from '../../types-NOswlhm9.mjs';
1
+ import { L as LanguageModel } from '../../types-Dg1Bcg1k.mjs';
2
+ import { X as XAIProviderConfig, 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
  * xAI Provider - Modern Pattern
@@ -1,7 +1,7 @@
1
- import { L as LanguageModel } from '../../types-BW3csYo5.js';
2
- import { X as XAIProviderConfig, A as AIProvider } from '../../types-DA54bSmX.js';
1
+ import { L as LanguageModel } from '../../types-Dg1Bcg1k.js';
2
+ import { X as XAIProviderConfig, 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
  * xAI Provider - Modern Pattern