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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +112 -0
  2. package/dist/adapters/index.d.mts +5 -5
  3. package/dist/adapters/index.d.ts +5 -5
  4. package/dist/adapters/index.js +372 -39
  5. package/dist/adapters/index.mjs +372 -39
  6. package/dist/audio-BBSwnjo8.d.mts +92 -0
  7. package/dist/audio-BBSwnjo8.d.ts +92 -0
  8. package/dist/{base-CkeT7u-O.d.mts → base-BICaQaRS.d.mts} +7 -3
  9. package/dist/{base-BdTpUAWI.d.ts → base-CpRPB8u8.d.ts} +7 -3
  10. package/dist/fallback/index.d.mts +4 -4
  11. package/dist/fallback/index.d.ts +4 -4
  12. package/dist/index.d.mts +17 -35
  13. package/dist/index.d.ts +17 -35
  14. package/dist/index.js +229 -60
  15. package/dist/index.mjs +227 -61
  16. package/dist/providers/anthropic/index.d.mts +3 -3
  17. package/dist/providers/anthropic/index.d.ts +3 -3
  18. package/dist/providers/anthropic/index.js +123 -7
  19. package/dist/providers/anthropic/index.mjs +123 -7
  20. package/dist/providers/azure/index.d.mts +3 -3
  21. package/dist/providers/azure/index.d.ts +3 -3
  22. package/dist/providers/azure/index.js +101 -4
  23. package/dist/providers/azure/index.mjs +101 -4
  24. package/dist/providers/fireworks/index.d.mts +3 -3
  25. package/dist/providers/fireworks/index.d.ts +3 -3
  26. package/dist/providers/fireworks/index.js +321 -31
  27. package/dist/providers/fireworks/index.mjs +321 -31
  28. package/dist/providers/google/index.d.mts +3 -3
  29. package/dist/providers/google/index.d.ts +3 -3
  30. package/dist/providers/google/index.js +321 -31
  31. package/dist/providers/google/index.mjs +321 -31
  32. package/dist/providers/ollama/index.d.mts +4 -4
  33. package/dist/providers/ollama/index.d.ts +4 -4
  34. package/dist/providers/ollama/index.js +87 -2
  35. package/dist/providers/ollama/index.mjs +87 -2
  36. package/dist/providers/openai/index.d.mts +25 -6
  37. package/dist/providers/openai/index.d.ts +25 -6
  38. package/dist/providers/openai/index.js +750 -40
  39. package/dist/providers/openai/index.mjs +749 -41
  40. package/dist/providers/openrouter/index.d.mts +3 -3
  41. package/dist/providers/openrouter/index.d.ts +3 -3
  42. package/dist/providers/openrouter/index.js +321 -31
  43. package/dist/providers/openrouter/index.mjs +321 -31
  44. package/dist/providers/togetherai/index.d.mts +3 -3
  45. package/dist/providers/togetherai/index.d.ts +3 -3
  46. package/dist/providers/togetherai/index.js +321 -31
  47. package/dist/providers/togetherai/index.mjs +321 -31
  48. package/dist/providers/xai/index.d.mts +3 -3
  49. package/dist/providers/xai/index.d.ts +3 -3
  50. package/dist/providers/xai/index.js +321 -31
  51. package/dist/providers/xai/index.mjs +321 -31
  52. package/dist/{types-BWRYIvl_.d.mts → types-C3RgRbEC.d.mts} +1 -1
  53. package/dist/{types-CpXcT-tY.d.ts → types-DCFqkJAy.d.ts} +1 -1
  54. package/dist/{types-NOswlhm9.d.mts → types-DHQEMFHA.d.mts} +3 -3
  55. package/dist/{types-BW3csYo5.d.mts → types-Dg1Bcg1k.d.mts} +106 -3
  56. package/dist/{types-BW3csYo5.d.ts → types-Dg1Bcg1k.d.ts} +106 -3
  57. package/dist/{types-DA54bSmX.d.ts → types-Dikj3En4.d.ts} +3 -3
  58. package/dist/yourgpt/index.d.mts +1 -1
  59. package/dist/yourgpt/index.d.ts +1 -1
  60. package/docs/multimodal-and-audio.md +170 -0
  61. package/package.json +3 -2
@@ -1,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,27 @@ function messageToOpenAIContent(message) {
420
523
  }
421
524
  return blocks;
422
525
  }
526
+ function extractAnthropicSystemPrompt(messages, systemPrompt) {
527
+ const parts = systemPrompt ? [systemPrompt] : [];
528
+ for (const message of messages) {
529
+ if (message.role !== "system") continue;
530
+ if (typeof message.content === "string") {
531
+ if (message.content) parts.push(message.content);
532
+ } else if (Array.isArray(message.content)) {
533
+ for (const block of message.content) {
534
+ if (block?.type !== "text" || typeof block.text !== "string") {
535
+ throw new TypeError("Anthropic system messages must contain text.");
536
+ }
537
+ if (block.text) parts.push(block.text);
538
+ }
539
+ } else {
540
+ throw new TypeError("Anthropic system messages must contain text.");
541
+ }
542
+ }
543
+ return parts.join("\n");
544
+ }
423
545
  function formatMessagesForAnthropic(messages, systemPrompt) {
546
+ const system = extractAnthropicSystemPrompt(messages, systemPrompt);
424
547
  const formatted = [];
425
548
  for (let i = 0; i < messages.length; i++) {
426
549
  const msg = messages[i];
@@ -449,7 +572,7 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
449
572
  {
450
573
  type: "tool_result",
451
574
  tool_use_id: msg.tool_call_id,
452
- content: msg.content ?? ""
575
+ content: msg.modelOutput ? toolOutputForProvider(msg.modelOutput, "anthropic") : msg.content ?? ""
453
576
  }
454
577
  ];
455
578
  while (i + 1 < messages.length && messages[i + 1].role === "tool") {
@@ -459,7 +582,7 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
459
582
  toolResults.push({
460
583
  type: "tool_result",
461
584
  tool_use_id: nextTool.tool_call_id,
462
- content: nextTool.content ?? ""
585
+ content: nextTool.modelOutput ? toolOutputForProvider(nextTool.modelOutput, "anthropic") : nextTool.content ?? ""
463
586
  });
464
587
  }
465
588
  }
@@ -475,11 +598,11 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
475
598
  }
476
599
  }
477
600
  return {
478
- system: systemPrompt || "",
601
+ system,
479
602
  messages: formatted
480
603
  };
481
604
  }
482
- function formatMessagesForOpenAI(messages, systemPrompt) {
605
+ function formatMessagesForOpenAI(messages, systemPrompt, preserveProviderMetadata = false) {
483
606
  const formatted = [];
484
607
  if (systemPrompt) {
485
608
  formatted.push({ role: "system", content: systemPrompt });
@@ -497,7 +620,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
497
620
  const assistantMsg = {
498
621
  role: "assistant",
499
622
  // Gemini/xAI (OpenAI-compatible) reject content: "" on assistant messages with tool_calls
500
- content: hasToolCalls ? msg.content || null : msg.content
623
+ content: hasToolCalls ? msg.content || null : msg.content,
624
+ ...preserveProviderMetadata && msg.providerMetadata ? { providerMetadata: msg.providerMetadata } : {}
501
625
  };
502
626
  if (hasToolCalls) {
503
627
  assistantMsg.tool_calls = msg.tool_calls;
@@ -506,7 +630,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
506
630
  } else if (msg.role === "tool" && msg.tool_call_id) {
507
631
  formatted.push({
508
632
  role: "tool",
509
- content: msg.content ?? "",
633
+ content: preserveProviderMetadata ? msg.content ?? "" : textToolOutput(msg.modelOutput, msg.content ?? ""),
634
+ ...preserveProviderMetadata && msg.modelOutput ? { modelOutput: msg.modelOutput } : {},
510
635
  tool_call_id: msg.tool_call_id
511
636
  });
512
637
  }
@@ -514,6 +639,64 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
514
639
  return formatted;
515
640
  }
516
641
 
642
+ // src/core/openai-responses.ts
643
+ function resolveOpenAITransport(defaults, overrides) {
644
+ return {
645
+ api: overrides?.api ?? defaults.api ?? "auto",
646
+ responses: defaults.responses !== void 0 || overrides?.responses !== void 0 ? { ...defaults.responses, ...overrides?.responses } : void 0
647
+ };
648
+ }
649
+ function useOpenAIResponses(model, baseURL, parameters, transport, hasTools, hasReplay, nativeToolSearch = false) {
650
+ const api = transport.api ?? "auto";
651
+ if (!["auto", "chat", "responses"].includes(api)) {
652
+ throw new Error(`Unknown OpenAI API selection: ${api}`);
653
+ }
654
+ if (api === "chat") {
655
+ if (transport.responses !== void 0 || nativeToolSearch) {
656
+ throw new Error(
657
+ "Responses options and native tool search require the Responses API"
658
+ );
659
+ }
660
+ return false;
661
+ }
662
+ let officialEndpoint = !baseURL;
663
+ try {
664
+ officialEndpoint = !baseURL || new URL(baseURL).hostname === "api.openai.com";
665
+ } catch {
666
+ }
667
+ return api === "responses" || transport.responses !== void 0 || hasReplay && officialEndpoint || nativeToolSearch || hasTools && requiresOpenAIResponses(model, baseURL, parameters);
668
+ }
669
+ function responseMetadata(response) {
670
+ const output = Array.isArray(response?.output) ? response.output : [];
671
+ return {
672
+ openai: {
673
+ response: {
674
+ id: response?.id,
675
+ model: response?.model,
676
+ status: response?.status,
677
+ output,
678
+ usage: response?.usage,
679
+ store: response?.store,
680
+ refusals: output.flatMap(
681
+ (item) => item?.type === "message" && Array.isArray(item.content) ? item.content.filter(
682
+ (part) => part?.type === "refusal" && typeof part.refusal === "string"
683
+ ).map((part) => part.refusal) : []
684
+ )
685
+ }
686
+ }
687
+ };
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 response.output;
698
+ }
699
+
517
700
  // src/core/provider-error.ts
518
701
  function providerErrorEvent(error, code) {
519
702
  const details = error ?? {};
@@ -590,13 +773,16 @@ var OpenAIAdapter = class _OpenAIAdapter {
590
773
  }));
591
774
  }
592
775
  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(
776
+ return useOpenAIResponses(
597
777
  request.config?.model || this.model,
598
778
  this.config.baseUrl,
599
- this.parameterOptions(request)
779
+ this.parameterOptions(request),
780
+ resolveOpenAITransport(this.config, request.config?.openai),
781
+ this.toolDefinitions(request).length > 0,
782
+ !!(request.rawMessages ?? request.messages)?.some(
783
+ (message) => message.providerMetadata?.openai?.response
784
+ ),
785
+ request.providerToolOptions?.openai?.nativeToolSearch?.enabled === true && request.providerToolOptions.openai.nativeToolSearch.useResponsesApi !== false
600
786
  );
601
787
  }
602
788
  /**
@@ -629,11 +815,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
629
815
  });
630
816
  }
631
817
  buildResponsesInput(request) {
632
- const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages) : formatMessagesForOpenAI(request.messages, void 0);
818
+ const sourceMessages = request.rawMessages && request.rawMessages.length > 0 ? this.formatRawMessages(request.rawMessages, true) : formatMessagesForOpenAI(request.messages, void 0, true);
633
819
  const input = [];
634
820
  for (const message of sourceMessages) {
635
821
  if (message.role === "system") {
636
- if (!request.systemPrompt && message.content) {
822
+ if (message.content && message.content !== request.systemPrompt) {
637
823
  input.push({
638
824
  type: "message",
639
825
  role: "developer",
@@ -643,6 +829,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
643
829
  continue;
644
830
  }
645
831
  if (message.role === "assistant") {
832
+ const replay = getResponseReplay(message.providerMetadata);
833
+ if (replay) {
834
+ input.push(...replay);
835
+ continue;
836
+ }
646
837
  const content = this.toResponsesContent(message.content, "assistant");
647
838
  if (typeof content === "string" ? content : Array.isArray(content) && content.length > 0) {
648
839
  input.push({
@@ -666,7 +857,7 @@ var OpenAIAdapter = class _OpenAIAdapter {
666
857
  input.push({
667
858
  type: "function_call_output",
668
859
  call_id: message.tool_call_id,
669
- output: typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
860
+ output: message.modelOutput ? toolOutputForProvider(message.modelOutput, "responses") : typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? null)
670
861
  });
671
862
  continue;
672
863
  }
@@ -680,10 +871,12 @@ var OpenAIAdapter = class _OpenAIAdapter {
680
871
  return input;
681
872
  }
682
873
  /** Preserve Copilot screenshot attachments on every OpenAI transport. */
683
- formatRawMessages(messages) {
874
+ formatRawMessages(messages, preserveProviderMetadata = false) {
684
875
  return messages.map((message) => {
685
- const { attachments, ...rest } = message;
686
- let content = message.content;
876
+ const { attachments, providerMetadata, modelOutput, ...rest } = message;
877
+ let content = message.role === "tool" && !preserveProviderMetadata ? textToolOutput(modelOutput, String(message.content ?? "")) : message.content;
878
+ if (preserveProviderMetadata && modelOutput)
879
+ rest.modelOutput = modelOutput;
687
880
  if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0 && content === "") {
688
881
  content = null;
689
882
  }
@@ -696,7 +889,11 @@ var OpenAIAdapter = class _OpenAIAdapter {
696
889
  content = [...parts, ...images];
697
890
  }
698
891
  }
699
- return { ...rest, content };
892
+ return {
893
+ ...rest,
894
+ content,
895
+ ...preserveProviderMetadata && providerMetadata ? { providerMetadata } : {}
896
+ };
700
897
  });
701
898
  }
702
899
  /**
@@ -777,14 +974,25 @@ var OpenAIAdapter = class _OpenAIAdapter {
777
974
  ).map((part) => part.text) : []
778
975
  ).join("") : "";
779
976
  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
- })) : [];
977
+ const seenCallIds = /* @__PURE__ */ new Set();
978
+ const toolCalls = Array.isArray(response?.output) ? response.output.filter((item) => item?.type === "function_call").map((item) => {
979
+ if (typeof item.call_id !== "string" || !item.call_id || typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.status && item.status !== "completed") {
980
+ throw new Error("Responses contains an incomplete function call");
981
+ }
982
+ if (seenCallIds.has(item.call_id))
983
+ throw new Error("Responses contains duplicate function call IDs");
984
+ seenCallIds.add(item.call_id);
985
+ const args = JSON.parse(item.arguments);
986
+ if (!args || typeof args !== "object" || Array.isArray(args))
987
+ throw new Error(
988
+ "Responses function arguments must be a JSON object"
989
+ );
990
+ return { id: item.call_id, name: item.name, args };
991
+ }) : [];
785
992
  return {
786
993
  content,
787
994
  toolCalls,
995
+ providerMetadata: responseMetadata(response),
788
996
  usage: response?.usage ? {
789
997
  promptTokens: response.usage.input_tokens ?? 0,
790
998
  completionTokens: response.usage.output_tokens ?? 0,
@@ -795,6 +1003,17 @@ var OpenAIAdapter = class _OpenAIAdapter {
795
1003
  }
796
1004
  buildResponsesPayload(request, stream) {
797
1005
  const openaiToolOptions = request.providerToolOptions?.openai;
1006
+ const options = resolveOpenAITransport(
1007
+ this.config,
1008
+ request.config?.openai
1009
+ ).responses;
1010
+ const parameters = buildOpenAIParameters(
1011
+ request.config?.model || this.model,
1012
+ request.config?.maxTokens ?? this.config.maxTokens,
1013
+ request.config?.temperature ?? this.config.temperature,
1014
+ this.parameterOptions(request),
1015
+ "responses"
1016
+ );
798
1017
  const responsesTextFormat = toOpenAIResponsesTextFormat(
799
1018
  request.config?.responseFormat
800
1019
  );
@@ -810,13 +1029,26 @@ var OpenAIAdapter = class _OpenAIAdapter {
810
1029
  ),
811
1030
  tool_choice: openaiToolOptions?.toolChoice,
812
1031
  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
- ),
1032
+ ...parameters,
1033
+ ...options?.reasoningSummary ? {
1034
+ reasoning: {
1035
+ ...parameters.reasoning,
1036
+ summary: options.reasoningSummary
1037
+ }
1038
+ } : {},
1039
+ ...options?.store !== void 0 ? { store: options.store } : {},
1040
+ ...options?.previousResponseId !== void 0 ? { previous_response_id: options.previousResponseId } : {},
1041
+ ...options?.promptCacheKey !== void 0 ? { prompt_cache_key: options.promptCacheKey } : {},
1042
+ ...options?.promptCacheOptions !== void 0 ? { prompt_cache_options: options.promptCacheOptions } : {},
1043
+ ...options?.promptCacheRetention !== void 0 ? { prompt_cache_retention: options.promptCacheRetention } : {},
1044
+ ...options?.store === false || options?.include ? {
1045
+ include: [
1046
+ .../* @__PURE__ */ new Set([
1047
+ ...options?.include ?? [],
1048
+ ...options?.store === false ? ["reasoning.encrypted_content"] : []
1049
+ ])
1050
+ ]
1051
+ } : {},
820
1052
  ...responsesTextFormat ? { text: { format: responsesTextFormat } } : {},
821
1053
  stream
822
1054
  };
@@ -875,6 +1107,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
875
1107
  let usage;
876
1108
  const citations = [];
877
1109
  let completed = false;
1110
+ let terminalResponse;
1111
+ let responseIdentity = {};
1112
+ const outputItems = /* @__PURE__ */ new Map();
1113
+ let streamedText = "";
878
1114
  function* flushToolCalls() {
879
1115
  for (const call of toolCalls.values()) {
880
1116
  if (!call.name)
@@ -891,17 +1127,36 @@ var OpenAIAdapter = class _OpenAIAdapter {
891
1127
  for await (const event of stream) {
892
1128
  throwIfAborted(request.signal);
893
1129
  logProviderPayload("openai", "stream chunk", event, request.debug);
1130
+ if (completed)
1131
+ throw new Error("Responses stream emitted an event after completion");
894
1132
  const index = event?.output_index ?? 0;
895
1133
  switch (event?.type) {
1134
+ case "response.created":
1135
+ responseIdentity = {
1136
+ id: event.response?.id,
1137
+ model: event.response?.model
1138
+ };
1139
+ break;
1140
+ case "response.output_item.done":
1141
+ if (event.item) outputItems.set(index, event.item);
1142
+ if (event.item?.type === "function_call") {
1143
+ toolCalls.set(index, {
1144
+ id: event.item.call_id,
1145
+ name: event.item.name,
1146
+ arguments: event.item.arguments
1147
+ });
1148
+ }
1149
+ break;
896
1150
  case "response.output_text.delta":
897
1151
  if (event.delta) {
1152
+ streamedText += event.delta;
898
1153
  yield { type: "message:delta", content: event.delta };
899
1154
  }
900
1155
  break;
901
1156
  case "response.output_item.added":
902
1157
  if (event.item?.type === "function_call") {
903
1158
  toolCalls.set(index, {
904
- id: event.item.call_id ?? event.item.id ?? generateToolCallId(),
1159
+ id: event.item.call_id ?? "",
905
1160
  name: event.item.name ?? "",
906
1161
  arguments: event.item.arguments ?? ""
907
1162
  });
@@ -920,7 +1175,10 @@ var OpenAIAdapter = class _OpenAIAdapter {
920
1175
  break;
921
1176
  }
922
1177
  case "response.completed": {
1178
+ if (event.response?.status && event.response.status !== "completed")
1179
+ throw new Error(`Responses stream ${event.response.status}`);
923
1180
  completed = true;
1181
+ terminalResponse = { ...responseIdentity, ...event.response };
924
1182
  const found = this.collectResponsesCitations(
925
1183
  event.response?.output,
926
1184
  citations.length
@@ -957,12 +1215,64 @@ var OpenAIAdapter = class _OpenAIAdapter {
957
1215
  throwIfAborted(request.signal);
958
1216
  if (!completed)
959
1217
  throw new Error("Responses stream ended without a completed response");
1218
+ const output = terminalResponse.output?.length ? terminalResponse.output : [...outputItems.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
1219
+ if (output.some((item) => item.type === "function_call"))
1220
+ toolCalls.clear();
1221
+ for (const [index, item] of output.entries()) {
1222
+ if (item.type === "function_call") {
1223
+ toolCalls.set(index, {
1224
+ id: item.call_id,
1225
+ name: item.name,
1226
+ arguments: item.arguments
1227
+ });
1228
+ }
1229
+ }
1230
+ const uniqueCalls = /* @__PURE__ */ new Map();
1231
+ for (const call of toolCalls.values()) {
1232
+ if (!call.id || !call.name)
1233
+ throw new Error("Responses stream contains an incomplete tool call");
1234
+ uniqueCalls.set(call.id, call);
1235
+ }
1236
+ toolCalls.clear();
1237
+ [...uniqueCalls.values()].forEach(
1238
+ (call, index) => toolCalls.set(index, call)
1239
+ );
1240
+ if (!output.length && toolCalls.size) {
1241
+ for (const call of toolCalls.values())
1242
+ output.push({
1243
+ type: "function_call",
1244
+ call_id: call.id,
1245
+ name: call.name,
1246
+ arguments: call.arguments
1247
+ });
1248
+ }
1249
+ if (!output.length && streamedText)
1250
+ output.push({
1251
+ type: "message",
1252
+ role: "assistant",
1253
+ content: [{ type: "output_text", text: streamedText, annotations: [] }]
1254
+ });
1255
+ terminalResponse.output = output;
1256
+ const finalText = this.parseResponsesResult(terminalResponse).content;
1257
+ if (finalText.startsWith(streamedText) && finalText.length > streamedText.length) {
1258
+ yield {
1259
+ type: "message:delta",
1260
+ content: finalText.slice(streamedText.length)
1261
+ };
1262
+ }
960
1263
  yield* flushToolCalls();
961
1264
  if (citations.length > 0) {
962
1265
  yield { type: "citation", citations: deduplicateCitations(citations) };
963
1266
  }
964
- yield { type: "message:end" };
965
- yield { type: "done", usage };
1267
+ yield {
1268
+ type: "message:end",
1269
+ providerMetadata: responseMetadata(terminalResponse)
1270
+ };
1271
+ yield {
1272
+ type: "done",
1273
+ usage,
1274
+ providerMetadata: responseMetadata(terminalResponse)
1275
+ };
966
1276
  }
967
1277
  async *stream(request) {
968
1278
  throwIfAborted(request.signal);
@@ -1348,7 +1658,7 @@ var AnthropicAdapter = class {
1348
1658
  }
1349
1659
  pendingToolResults.push({
1350
1660
  tool_use_id: toolCallId,
1351
- content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
1661
+ content: msg.modelOutput ? toolOutputForProvider(msg.modelOutput, "anthropic") : typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
1352
1662
  });
1353
1663
  } else if (msg.role === "user") {
1354
1664
  if (pendingToolResults.length > 0) {
@@ -1469,14 +1779,23 @@ var AnthropicAdapter = class {
1469
1779
  buildRequestOptions(request) {
1470
1780
  const responseFormat = request.config?.responseFormat;
1471
1781
  const jsonObjectSuffix = responseFormat?.type === "json_object" ? "\n\nRespond with a single JSON object and no other text." : "";
1472
- const systemMessage = (request.systemPrompt || "") + jsonObjectSuffix;
1782
+ let systemMessage;
1473
1783
  let messages;
1474
1784
  if (request.rawMessages && request.rawMessages.length > 0) {
1785
+ systemMessage = extractAnthropicSystemPrompt(
1786
+ request.rawMessages,
1787
+ request.systemPrompt
1788
+ );
1475
1789
  messages = this.convertToAnthropicMessages(request.rawMessages);
1476
1790
  } else {
1477
- const formatted = formatMessagesForAnthropic(request.messages, void 0);
1791
+ const formatted = formatMessagesForAnthropic(
1792
+ request.messages,
1793
+ request.systemPrompt
1794
+ );
1795
+ systemMessage = formatted.system;
1478
1796
  messages = formatted.messages;
1479
1797
  }
1798
+ systemMessage += jsonObjectSuffix;
1480
1799
  const anthropicNativeSearch = request.providerToolOptions?.anthropic?.nativeToolSearch;
1481
1800
  const tools = anthropicNativeSearch?.enabled ? this.buildNativeSearchTools(
1482
1801
  request.toolDefinitions ?? [],
@@ -1819,7 +2138,7 @@ async function formatMessagesForOllama(messages, systemPrompt) {
1819
2138
  for (const msg of messages) {
1820
2139
  const baseMessage = {
1821
2140
  role: msg.role,
1822
- content: msg.content ?? ""
2141
+ content: msg.role === "tool" ? textToolOutput(msg.modelOutput, msg.content ?? "") : msg.content ?? ""
1823
2142
  };
1824
2143
  if (msg.role === "user" && msg.metadata?.attachments) {
1825
2144
  const images = await extractImagesFromAttachments(
@@ -1856,7 +2175,7 @@ async function processRawMessagesForOllama(rawMessages, systemPrompt) {
1856
2175
  for (const msg of rawMessages) {
1857
2176
  const baseMessage = {
1858
2177
  role: msg.role,
1859
- content: msg.content ?? ""
2178
+ content: msg.role === "tool" ? textToolOutput(msg.modelOutput, String(msg.content ?? "")) : msg.content ?? ""
1860
2179
  };
1861
2180
  if (msg.role === "user" && msg.attachments && Array.isArray(msg.attachments)) {
1862
2181
  const attachments = msg.attachments;
@@ -2066,6 +2385,10 @@ function messageToGeminiContent(msg) {
2066
2385
  if (msg.role === "system") return null;
2067
2386
  const parts = [];
2068
2387
  if (msg.role === "tool" && msg.tool_call_id) {
2388
+ msg = {
2389
+ ...msg,
2390
+ content: textToolOutput(msg.modelOutput, msg.content ?? "")
2391
+ };
2069
2392
  let responseData;
2070
2393
  try {
2071
2394
  responseData = JSON.parse(msg.content || "{}");
@@ -2526,6 +2849,13 @@ var AzureAdapter = class {
2526
2849
  let messages;
2527
2850
  if (request.rawMessages && request.rawMessages.length > 0) {
2528
2851
  const processedMessages = request.rawMessages.map((msg) => {
2852
+ const { modelOutput, ...rest } = msg;
2853
+ msg = {
2854
+ ...rest,
2855
+ ...msg.role === "tool" ? {
2856
+ content: textToolOutput(modelOutput, String(msg.content ?? ""))
2857
+ } : {}
2858
+ };
2529
2859
  const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
2530
2860
  if (hasAttachments) {
2531
2861
  const content = [];
@@ -2656,7 +2986,10 @@ var AzureAdapter = class {
2656
2986
  const client = await this.getClient();
2657
2987
  let messages;
2658
2988
  if (request.rawMessages && request.rawMessages.length > 0) {
2659
- messages = request.rawMessages;
2989
+ messages = request.rawMessages.map(({ modelOutput, ...msg }) => ({
2990
+ ...msg,
2991
+ ...msg.role === "tool" ? { content: textToolOutput(modelOutput, String(msg.content ?? "")) } : {}
2992
+ }));
2660
2993
  if (request.systemPrompt) {
2661
2994
  const hasSystem = messages.some((m) => m.role === "system");
2662
2995
  if (!hasSystem) {