@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,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);
@@ -422,7 +525,20 @@ function messageToOpenAIContent(message) {
422
525
  }
423
526
  return blocks;
424
527
  }
528
+ function extractAnthropicSystemPrompt(messages, systemPrompt) {
529
+ const explicit = typeof systemPrompt === "string" ? systemPrompt : "";
530
+ const parts = explicit ? [explicit] : [];
531
+ for (const message of messages) {
532
+ if (message.role !== "system") continue;
533
+ const text = typeof message.content === "string" ? message.content : Array.isArray(message.content) ? message.content.filter(
534
+ (block) => block?.type === "text" && typeof block.text === "string"
535
+ ).map((block) => block.text).filter(Boolean).join("\n") : "";
536
+ if (text && text !== explicit) parts.push(text);
537
+ }
538
+ return parts.join("\n");
539
+ }
425
540
  function formatMessagesForAnthropic(messages, systemPrompt) {
541
+ const system = extractAnthropicSystemPrompt(messages, systemPrompt);
426
542
  const formatted = [];
427
543
  for (let i = 0; i < messages.length; i++) {
428
544
  const msg = messages[i];
@@ -451,7 +567,7 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
451
567
  {
452
568
  type: "tool_result",
453
569
  tool_use_id: msg.tool_call_id,
454
- content: msg.content ?? ""
570
+ content: msg.modelOutput ? toolOutputForProvider(msg.modelOutput, "anthropic") : msg.content ?? ""
455
571
  }
456
572
  ];
457
573
  while (i + 1 < messages.length && messages[i + 1].role === "tool") {
@@ -461,7 +577,7 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
461
577
  toolResults.push({
462
578
  type: "tool_result",
463
579
  tool_use_id: nextTool.tool_call_id,
464
- content: nextTool.content ?? ""
580
+ content: nextTool.modelOutput ? toolOutputForProvider(nextTool.modelOutput, "anthropic") : nextTool.content ?? ""
465
581
  });
466
582
  }
467
583
  }
@@ -477,11 +593,11 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
477
593
  }
478
594
  }
479
595
  return {
480
- system: systemPrompt || "",
596
+ system,
481
597
  messages: formatted
482
598
  };
483
599
  }
484
- function formatMessagesForOpenAI(messages, systemPrompt) {
600
+ function formatMessagesForOpenAI(messages, systemPrompt, preserveProviderMetadata = false) {
485
601
  const formatted = [];
486
602
  if (systemPrompt) {
487
603
  formatted.push({ role: "system", content: systemPrompt });
@@ -499,7 +615,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
499
615
  const assistantMsg = {
500
616
  role: "assistant",
501
617
  // Gemini/xAI (OpenAI-compatible) reject content: "" on assistant messages with tool_calls
502
- content: hasToolCalls ? msg.content || null : msg.content
618
+ content: hasToolCalls ? msg.content || null : msg.content,
619
+ ...preserveProviderMetadata && msg.providerMetadata ? { providerMetadata: msg.providerMetadata } : {}
503
620
  };
504
621
  if (hasToolCalls) {
505
622
  assistantMsg.tool_calls = msg.tool_calls;
@@ -508,7 +625,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
508
625
  } else if (msg.role === "tool" && msg.tool_call_id) {
509
626
  formatted.push({
510
627
  role: "tool",
511
- content: msg.content ?? "",
628
+ content: preserveProviderMetadata ? msg.content ?? "" : textToolOutput(msg.modelOutput, msg.content ?? ""),
629
+ ...preserveProviderMetadata && msg.modelOutput ? { modelOutput: msg.modelOutput } : {},
512
630
  tool_call_id: msg.tool_call_id
513
631
  });
514
632
  }
@@ -516,7 +634,120 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
516
634
  return formatted;
517
635
  }
518
636
 
637
+ // src/core/openai-responses.ts
638
+ function resolveOpenAITransport(defaults, overrides) {
639
+ return {
640
+ api: overrides?.api ?? defaults.api ?? "auto",
641
+ responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
642
+ };
643
+ }
644
+ function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
645
+ const api = transport.api ?? "auto";
646
+ if (!["auto", "chat", "responses"].includes(api)) {
647
+ throw new Error(`Unknown OpenAI API selection: ${api}`);
648
+ }
649
+ if (api === "chat") {
650
+ if (transport.responses !== void 0 || nativeToolSearch) {
651
+ throw new Error(
652
+ "Responses options and native tool search require the Responses API"
653
+ );
654
+ }
655
+ return false;
656
+ }
657
+ let officialEndpoint = !baseURL;
658
+ try {
659
+ officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
660
+ } catch {
661
+ }
662
+ return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
663
+ }
664
+ function responseMetadata(response) {
665
+ const output = Array.isArray(response?.output) ? response.output : [];
666
+ return {
667
+ openai: {
668
+ response: {
669
+ id: response?.id,
670
+ model: response?.model,
671
+ status: response?.status,
672
+ output,
673
+ usage: response?.usage,
674
+ store: response?.store,
675
+ refusals: output.flatMap(
676
+ (item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
677
+ (part) => part?.type === "refusal" && typeof part.refusal === "string"
678
+ ).map((part) => part.refusal) : []
679
+ )
680
+ }
681
+ }
682
+ };
683
+ }
684
+ function responseReplayMetadata(metadata) {
685
+ const response = metadata?.openai?.response;
686
+ if (!response) return metadata;
687
+ const output = response.output.map((item) => {
688
+ const fields = ["type", "id", "status"];
689
+ switch (item.type) {
690
+ case "reasoning":
691
+ fields.push("encrypted_content");
692
+ break;
693
+ case "message":
694
+ fields.push("role", "phase");
695
+ break;
696
+ case "function_call":
697
+ fields.push("call_id", "name", "namespace", "arguments");
698
+ break;
699
+ case "tool_search_call":
700
+ fields.push("call_id", "execution", "arguments");
701
+ break;
702
+ case "tool_search_output":
703
+ fields.push("call_id", "tools");
704
+ break;
705
+ case "web_search_call":
706
+ fields.push("action");
707
+ break;
708
+ default:
709
+ return stripItemDiagnostics(item);
710
+ }
711
+ const replay = Object.fromEntries(
712
+ fields.filter((key) => item[key] !== void 0).map((key) => [key, item[key]])
713
+ );
714
+ if (item.type === "reasoning") replay.summary = [];
715
+ if (item.type === "message")
716
+ replay.content = Array.isArray(item.content) ? item.content.flatMap(
717
+ (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 }] : []
718
+ ) : [];
719
+ return stripItemDiagnostics(replay);
720
+ });
721
+ return { openai: { response: { output, refusals: [] } } };
722
+ }
723
+ function stripItemDiagnostics(item) {
724
+ const {
725
+ usage: _usage,
726
+ logprobs: _logprobs,
727
+ metadata: _metadata,
728
+ ...state
729
+ } = item;
730
+ return state;
731
+ }
732
+ function getResponseReplay(metadata) {
733
+ const response = metadata?.openai?.response;
734
+ if (response === void 0) return void 0;
735
+ if (!Array.isArray(response.output) || response.output.some((item) => !item || typeof item.type !== "string")) {
736
+ throw new Error(
737
+ "Invalid OpenAI Responses replay metadata: output must contain typed items"
738
+ );
739
+ }
740
+ return responseReplayMetadata(metadata)?.openai?.response?.output;
741
+ }
742
+
519
743
  // src/core/provider-error.ts
744
+ function invalidProviderResponse(message) {
745
+ return Object.assign(new Error(message), {
746
+ name: "InvalidProviderResponseError",
747
+ status: 502,
748
+ code: "INVALID_PROVIDER_RESPONSE"
749
+ });
750
+ }
520
751
  function providerErrorEvent(error, code) {
521
752
  const details = error ?? {};
522
753
  const event = {
@@ -592,13 +823,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
592
823
  }));
593
824
  }
594
825
  shouldUseResponsesApi(request) {
595
- if (this.toolDefinitions(request).length === 0) return false;
596
- if (request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false)
597
- return true;
598
- return requiresOpenAIResponses(
826
+ return useOpenAIResponses(
599
827
  request.config?.model || this.model,
600
828
  this.config.baseUrl,
601
- this.parameterOptions(request)
829
+ this.parameterOptions(request),
830
+ resolveOpenAITransport(this.config, request.config?.openai),
831
+ this.toolDefinitions(request).length > 0,
832
+ !!(request.rawMessages ?? request.messages)?.some(
833
+ (message) => message.providerMetadata?.openai?.response
834
+ ),
835
+ request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
602
836
  );
603
837
  }
604
838
  /**
@@ -631,11 +865,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
631
865
  });
632
866
  }
633
867
  buildResponsesInput(request) {
634
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages) : formatMessagesForOpenAI(request.messages, void 0);
868
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
635
869
  const input = [];
636
870
  for (const message of sourceMessages) {
637
871
  if (message.role === "system") {
638
- if (!request.systemPrompt && message.content) {
872
+ if (message.content && message.content !== request.systemPrompt) {
639
873
  input.push({
640
874
  type: "message",
641
875
  role: "developer",
@@ -645,6 +879,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
645
879
  continue;
646
880
  }
647
881
  if (message.role === "assistant") {
882
+ const replay = getResponseReplay(message.providerMetadata);
883
+ if (replay) {
884
+ input.push(...replay);
885
+ continue;
886
+ }
648
887
  const content = this.toResponsesContent(message.content, "assistant");
649
888
  if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
650
889
  input.push({
@@ -668,7 +907,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
668
907
  input.push({
669
908
  type: "function_call_output",
670
909
  call_id: message.tool_call_id,
671
- output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
910
+ output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
672
911
  });
673
912
  continue;
674
913
  }
@@ -682,10 +921,12 @@ var OpenAIAdapter = class _OpenAIAdapter {
682
921
  return input;
683
922
  }
684
923
  /** Preserve Copilot screenshot attachments on every OpenAI transport. */
685
- formatRawMessages(messages) {
924
+ formatRawMessages(messages, preserveProviderMetadata = false) {
686
925
  return messages.map((message) => {
687
- const { attachments, ...rest } = message;
688
- let content = message.content;
926
+ const { attachments, providerMetadata, modelOutput, ...rest } = message;
927
+ let content = message.role === "tool" && !preserveProviderMetadata && modelOutput !== void 0 ? textToolOutput(modelOutput, "") : message.content;
928
+ if (preserveProviderMetadata && modelOutput)
929
+ rest.modelOutput = modelOutput;
689
930
  if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
690
931
  content = null;
691
932
  }
@@ -698,7 +939,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
698
939
  content = [...parts, ...images];
699
940
  }
700
941
  }
701
- return { ...rest, content };
942
+ return {
943
+ ...rest,
944
+ content,
945
+ ...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
946
+ };
702
947
  });
703
948
  }
704
949
  /**
@@ -779,14 +1024,40 @@ var OpenAIAdapter = class _OpenAIAdapter {
779
1024
  ).map((part) => part.text) : []
780
1025
  ).join("") : "";
781
1026
  const content = textFromOutput || (typeof response?.output_text === "string" ? response.output_text : "");
782
- const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => ({
783
- id: item.call_id ?? item.id ?? generateToolCallId(),
784
- name: item.name,
785
- args: JSON.parse(item.arguments ?? "{}")
786
- })) : [];
1027
+ const seenCallIds = /* @__PURE__ */ new Set();
1028
+ const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
1029
+ if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
1030
+ throw invalidProviderResponse(
1031
+ "Responses contains an incomplete function call"
1032
+ );
1033
+ }
1034
+ if (seenCallIds.has(item.call_id))
1035
+ throw invalidProviderResponse(
1036
+ "Responses contains duplicate function call IDs"
1037
+ );
1038
+ seenCallIds.add(item.call_id);
1039
+ let args;
1040
+ try {
1041
+ args = JSON.parse(item.arguments);
1042
+ } catch {
1043
+ throw invalidProviderResponse(
1044
+ "Responses function arguments must be valid JSON"
1045
+ );
1046
+ }
1047
+ if (!args || typeof args !== "object" || Array.isArray(args))
1048
+ throw invalidProviderResponse(
1049
+ "Responses function arguments must be a JSON object"
1050
+ );
1051
+ return {
1052
+ id: item.call_id,
1053
+ name: item.name,
1054
+ args
1055
+ };
1056
+ }) : [];
787
1057
  return {
788
1058
  content,
789
1059
  toolCalls,
1060
+ providerMetadata: responseMetadata(response),
790
1061
  usage: response?.usage ? {
791
1062
  promptTokens: response.usage.input_tokens ?? 0,
792
1063
  completionTokens: response.usage.output_tokens ?? 0,
@@ -797,6 +1068,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
797
1068
  }
798
1069
  buildResponsesPayload(request, stream) {
799
1070
  const openaiToolOptions = request.providerToolOptions?.openai;
1071
+ const options = resolveOpenAITransport(
1072
+ this.config,
1073
+ request.config?.openai
1074
+ ).responses;
1075
+ const parameters = buildOpenAIParameters(
1076
+ request.config?.model || this.model,
1077
+ request.config?.maxTokens ?? this.config.maxTokens,
1078
+ request.config?.temperature ?? this.config.temperature,
1079
+ this.parameterOptions(request),
1080
+ "responses"
1081
+ );
800
1082
  const responsesTextFormat = toOpenAIResponsesTextFormat(
801
1083
  request.config?.responseFormat
802
1084
  );
@@ -812,13 +1094,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
812
1094
  ),
813
1095
  tool_choice: openaiToolOptions?.toolChoice,
814
1096
  parallel_tool_calls: openaiToolOptions?.parallelToolCalls,
815
- ...buildOpenAIParameters(
816
- modelIdForPayload,
817
- request.config?.maxTokens ?? this.config.maxTokens,
818
- request.config?.temperature ?? this.config.temperature,
819
- this.parameterOptions(request),
820
- "responses"
821
- ),
1097
+ ...parameters,
1098
+ ...options?.reasoningSummary ? {
1099
+ reasoning: {
1100
+ ...parameters.reasoning,
1101
+ summary: options.reasoningSummary
1102
+ }
1103
+ } : {},
1104
+ ...options?.store !== void 0 ? { store: options.store } : {},
1105
+ ...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
1106
+ ...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
1107
+ ...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
1108
+ ...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
1109
+ ...options?.store === false || options?.include ? {
1110
+ include: [
1111
+ .../* @__PURE__ */ new Set([
1112
+ ...options?.include ?? [],
1113
+ ...options?.store === false ? ["reasoning.encrypted_content"] : []
1114
+ ])
1115
+ ]
1116
+ } : {},
822
1117
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
823
1118
  stream
824
1119
  };
@@ -877,6 +1172,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
877
1172
  let usage;
878
1173
  const citations = [];
879
1174
  let completed = false;
1175
+ let terminalResponse;
1176
+ let responseIdentity = {};
1177
+ const outputItems = /* @__PURE__ */ new Map();
1178
+ let streamedText = "";
880
1179
  function* flushToolCalls() {
881
1180
  for (const call of toolCalls.values()) {
882
1181
  if (!call.name)
@@ -893,17 +1192,36 @@ var OpenAIAdapter = class _OpenAIAdapter {
893
1192
  for await (const event of stream) {
894
1193
  throwIfAborted(request.signal);
895
1194
  logProviderPayload("openai", "stream chunk", event, request.debug);
1195
+ if (completed)
1196
+ throw new Error("Responses stream emitted an event after completion");
896
1197
  const index = event?.output_index ?? 0;
897
1198
  switch (event?.type) {
1199
+ case "response.created":
1200
+ responseIdentity = {
1201
+ id: event.response?.id,
1202
+ model: event.response?.model
1203
+ };
1204
+ break;
1205
+ case "response.output_item.done":
1206
+ if (event.item) outputItems.set(index, event.item);
1207
+ if (event.item?.type === "function_call") {
1208
+ toolCalls.set(index, {
1209
+ id: event.item.call_id,
1210
+ name: event.item.name,
1211
+ arguments: event.item.arguments
1212
+ });
1213
+ }
1214
+ break;
898
1215
  case "response.output_text.delta":
899
1216
  if (event.delta) {
1217
+ streamedText += event.delta;
900
1218
  yield { type: "message:delta", content: event.delta };
901
1219
  }
902
1220
  break;
903
1221
  case "response.output_item.added":
904
1222
  if (event.item?.type === "function_call") {
905
1223
  toolCalls.set(index, {
906
- id: event.item.call_id ?? event.item.id ?? generateToolCallId(),
1224
+ id: event.item.call_id ?? "",
907
1225
  name: event.item.name ?? "",
908
1226
  arguments: event.item.arguments ?? ""
909
1227
  });
@@ -922,7 +1240,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
922
1240
  break;
923
1241
  }
924
1242
  case "response.completed": {
1243
+ if (event.response?.status && event.response.status !== "completed")
1244
+ throw new Error(`Responses stream ${event.response.status}`);
925
1245
  completed = true;
1246
+ terminalResponse = { ...responseIdentity, ...event.response };
926
1247
  const found = this.collectResponsesCitations(
927
1248
  event.response?.output,
928
1249
  citations.length
@@ -959,12 +1280,66 @@ var OpenAIAdapter = class _OpenAIAdapter {
959
1280
  throwIfAborted(request.signal);
960
1281
  if (!completed)
961
1282
  throw new Error("Responses stream ended without a completed response");
1283
+ const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
1284
+ if (output.some((item) => item.type === "function_call"))
1285
+ toolCalls.clear();
1286
+ for (const [index, item] of output.entries()) {
1287
+ if (item.type === "function_call") {
1288
+ toolCalls.set(index, {
1289
+ id: item.call_id,
1290
+ name: item.name,
1291
+ arguments: item.arguments
1292
+ });
1293
+ }
1294
+ }
1295
+ const uniqueCalls = /* @__PURE__ */ new Map();
1296
+ for (const call of toolCalls.values()) {
1297
+ if (!call.id || !call.name)
1298
+ throw invalidProviderResponse(
1299
+ "Responses stream contains an incomplete tool call"
1300
+ );
1301
+ uniqueCalls.set(call.id, call);
1302
+ }
1303
+ toolCalls.clear();
1304
+ [...uniqueCalls.values()].forEach(
1305
+ (call, index) => toolCalls.set(index, call)
1306
+ );
1307
+ if (!output.length && toolCalls.size) {
1308
+ for (const call of toolCalls.values())
1309
+ output.push({
1310
+ type: "function_call",
1311
+ call_id: call.id,
1312
+ name: call.name,
1313
+ arguments: call.arguments
1314
+ });
1315
+ }
1316
+ if (!output.length && streamedText)
1317
+ output.push({
1318
+ type: "message",
1319
+ role: "assistant",
1320
+ content: [{ type: "output_text", text: streamedText, annotations: [] }]
1321
+ });
1322
+ terminalResponse.output = output;
1323
+ const finalText = this.parseResponsesResult(terminalResponse).content;
1324
+ if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
1325
+ yield {
1326
+ type: "message:delta",
1327
+ content: finalText.slice(streamedText.length)
1328
+ };
1329
+ }
962
1330
  yield* flushToolCalls();
963
1331
  if (citations.length > 0) {
964
1332
  yield { type: "citation", citations: deduplicateCitations(citations) };
965
1333
  }
966
- yield { type: "message:end" };
967
- yield { type: "done", usage };
1334
+ yield {
1335
+ type: "message:end",
1336
+ providerMetadata: responseMetadata(terminalResponse)
1337
+ };
1338
+ yield {
1339
+ type: "done",
1340
+ usage,
1341
+ providerMetadata: responseMetadata(terminalResponse)
1342
+ };
968
1343
  }
969
1344
  async *stream(request) {
970
1345
  throwIfAborted(request.signal);
@@ -1350,7 +1725,7 @@ var AnthropicAdapter = class {
1350
1725
  }
1351
1726
  pendingToolResults.push({
1352
1727
  tool_use_id: toolCallId,
1353
- content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
1728
+ content: msg.modelOutput ? toolOutputForProvider(msg.modelOutput, "anthropic") : typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
1354
1729
  });
1355
1730
  } else if (msg.role === "user") {
1356
1731
  if (pendingToolResults.length > 0) {
@@ -1471,14 +1846,23 @@ var AnthropicAdapter = class {
1471
1846
  buildRequestOptions(request) {
1472
1847
  const responseFormat = request.config?.responseFormat;
1473
1848
  const jsonObjectSuffix = responseFormat?.type === "json_object" ? "\n\nRespond with a single JSON object and no other text." : "";
1474
- const systemMessage = (request.systemPrompt || "") + jsonObjectSuffix;
1849
+ let systemMessage;
1475
1850
  let messages;
1476
1851
  if (request.rawMessages && request.rawMessages.length > 0) {
1852
+ systemMessage = extractAnthropicSystemPrompt(
1853
+ request.rawMessages,
1854
+ request.systemPrompt
1855
+ );
1477
1856
  messages = this.convertToAnthropicMessages(request.rawMessages);
1478
1857
  } else {
1479
- const formatted = formatMessagesForAnthropic(request.messages, void 0);
1858
+ const formatted = formatMessagesForAnthropic(
1859
+ request.messages,
1860
+ request.systemPrompt
1861
+ );
1862
+ systemMessage = formatted.system;
1480
1863
  messages = formatted.messages;
1481
1864
  }
1865
+ systemMessage += jsonObjectSuffix;
1482
1866
  const anthropicNativeSearch = request.providerToolOptions?.anthropic?.nativeToolSearch;
1483
1867
  const tools = anthropicNativeSearch?.enabled ? this.buildNativeSearchTools(
1484
1868
  request.toolDefinitions ?? [],
@@ -1821,7 +2205,7 @@ async function formatMessagesForOllama(messages, systemPrompt) {
1821
2205
  for (const msg of messages) {
1822
2206
  const baseMessage = {
1823
2207
  role: msg.role,
1824
- content: msg.content ?? ""
2208
+ content: msg.role === "tool" ? textToolOutput(msg.modelOutput, msg.content ?? "") : msg.content ?? ""
1825
2209
  };
1826
2210
  if (msg.role === "user" && msg.metadata?.attachments) {
1827
2211
  const images = await extractImagesFromAttachments(
@@ -1858,7 +2242,7 @@ async function processRawMessagesForOllama(rawMessages, systemPrompt) {
1858
2242
  for (const msg of rawMessages) {
1859
2243
  const baseMessage = {
1860
2244
  role: msg.role,
1861
- content: msg.content ?? ""
2245
+ content: msg.role === "tool" ? textToolOutput(msg.modelOutput, String(msg.content ?? "")) : msg.content ?? ""
1862
2246
  };
1863
2247
  if (msg.role === "user" && msg.attachments && Array.isArray(msg.attachments)) {
1864
2248
  const attachments = msg.attachments;
@@ -2068,6 +2452,10 @@ function messageToGeminiContent(msg) {
2068
2452
  if (msg.role === "system") return null;
2069
2453
  const parts = [];
2070
2454
  if (msg.role === "tool" && msg.tool_call_id) {
2455
+ msg = {
2456
+ ...msg,
2457
+ content: textToolOutput(msg.modelOutput, msg.content ?? "")
2458
+ };
2071
2459
  let responseData;
2072
2460
  try {
2073
2461
  responseData = JSON.parse(msg.content || "{}");
@@ -2528,6 +2916,13 @@ var AzureAdapter = class {
2528
2916
  let messages;
2529
2917
  if (request.rawMessages && request.rawMessages.length > 0) {
2530
2918
  const processedMessages = request.rawMessages.map((msg) => {
2919
+ const { modelOutput, ...rest } = msg;
2920
+ msg = {
2921
+ ...rest,
2922
+ ...msg.role === "tool" ? {
2923
+ content: textToolOutput(modelOutput, String(msg.content ?? ""))
2924
+ } : {}
2925
+ };
2531
2926
  const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
2532
2927
  if (hasAttachments) {
2533
2928
  const content = [];
@@ -2658,7 +3053,10 @@ var AzureAdapter = class {
2658
3053
  const client = await this.getClient();
2659
3054
  let messages;
2660
3055
  if (request.rawMessages && request.rawMessages.length > 0) {
2661
- messages = request.rawMessages;
3056
+ messages = request.rawMessages.map(({ modelOutput, ...msg }) => ({
3057
+ ...msg,
3058
+ ...msg.role === "tool" ? { content: textToolOutput(modelOutput, String(msg.content ?? "")) } : {}
3059
+ }));
2662
3060
  if (request.systemPrompt) {
2663
3061
  const hasSystem = messages.some((m) => m.role === "system");
2664
3062
  if (!hasSystem) {