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