@yourgpt/llm-sdk 2.6.0-beta.0 → 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 (64) hide show
  1. package/README.md +189 -0
  2. package/dist/adapters/index.d.mts +11 -13
  3. package/dist/adapters/index.d.ts +11 -13
  4. package/dist/adapters/index.js +610 -186
  5. package/dist/adapters/index.mjs +610 -186
  6. package/dist/audio-BBSwnjo8.d.mts +92 -0
  7. package/dist/audio-BBSwnjo8.d.ts +92 -0
  8. package/dist/{base-tNgbBaSo.d.mts → base-BICaQaRS.d.mts} +8 -3
  9. package/dist/{base-C58Dsr9p.d.ts → base-CpRPB8u8.d.ts} +8 -3
  10. package/dist/fallback/index.d.mts +4 -4
  11. package/dist/fallback/index.d.ts +4 -4
  12. package/dist/fallback/index.js +75 -39
  13. package/dist/fallback/index.mjs +75 -39
  14. package/dist/index.d.mts +18 -35
  15. package/dist/index.d.ts +18 -35
  16. package/dist/index.js +304 -88
  17. package/dist/index.mjs +302 -89
  18. package/dist/providers/anthropic/index.d.mts +3 -3
  19. package/dist/providers/anthropic/index.d.ts +3 -3
  20. package/dist/providers/anthropic/index.js +123 -7
  21. package/dist/providers/anthropic/index.mjs +123 -7
  22. package/dist/providers/azure/index.d.mts +3 -3
  23. package/dist/providers/azure/index.d.ts +3 -3
  24. package/dist/providers/azure/index.js +101 -4
  25. package/dist/providers/azure/index.mjs +101 -4
  26. package/dist/providers/fireworks/index.d.mts +3 -3
  27. package/dist/providers/fireworks/index.d.ts +3 -3
  28. package/dist/providers/fireworks/index.js +559 -178
  29. package/dist/providers/fireworks/index.mjs +559 -178
  30. package/dist/providers/google/index.d.mts +3 -3
  31. package/dist/providers/google/index.d.ts +3 -3
  32. package/dist/providers/google/index.js +559 -178
  33. package/dist/providers/google/index.mjs +559 -178
  34. package/dist/providers/ollama/index.d.mts +4 -4
  35. package/dist/providers/ollama/index.d.ts +4 -4
  36. package/dist/providers/ollama/index.js +87 -2
  37. package/dist/providers/ollama/index.mjs +87 -2
  38. package/dist/providers/openai/index.d.mts +25 -23
  39. package/dist/providers/openai/index.d.ts +25 -23
  40. package/dist/providers/openai/index.js +1397 -481
  41. package/dist/providers/openai/index.mjs +1396 -482
  42. package/dist/providers/openrouter/index.d.mts +3 -3
  43. package/dist/providers/openrouter/index.d.ts +3 -3
  44. package/dist/providers/openrouter/index.js +559 -178
  45. package/dist/providers/openrouter/index.mjs +559 -178
  46. package/dist/providers/togetherai/index.d.mts +3 -3
  47. package/dist/providers/togetherai/index.d.ts +3 -3
  48. package/dist/providers/togetherai/index.js +559 -178
  49. package/dist/providers/togetherai/index.mjs +559 -178
  50. package/dist/providers/xai/index.d.mts +3 -3
  51. package/dist/providers/xai/index.d.ts +3 -3
  52. package/dist/providers/xai/index.js +559 -178
  53. package/dist/providers/xai/index.mjs +559 -178
  54. package/dist/{types-B6dhnguR.d.mts → types-C3RgRbEC.d.mts} +11 -4
  55. package/dist/{types-CCxPmkmK.d.ts → types-DCFqkJAy.d.ts} +11 -4
  56. package/dist/{types-BQ31QIsA.d.ts → types-DHQEMFHA.d.mts} +3 -11
  57. package/dist/{types-BkQCSiIt.d.mts → types-Dg1Bcg1k.d.mts} +127 -3
  58. package/dist/{types-BkQCSiIt.d.ts → types-Dg1Bcg1k.d.ts} +127 -3
  59. package/dist/{types-BSSiJW2o.d.mts → types-Dikj3En4.d.ts} +3 -11
  60. package/dist/yourgpt/index.d.mts +1 -1
  61. package/dist/yourgpt/index.d.ts +1 -1
  62. package/docs/multimodal-and-audio.md +170 -0
  63. package/package.json +117 -49
  64. package/LICENSE +0 -21
@@ -1,7 +1,7 @@
1
- import { L as LanguageModel } from '../../types-BkQCSiIt.mjs';
2
- import { a as AnthropicProviderConfig, A as AIProvider } from '../../types-BSSiJW2o.mjs';
1
+ import { L as LanguageModel } from '../../types-Dg1Bcg1k.mjs';
2
+ import { a as AnthropicProviderConfig, A as AIProvider } from '../../types-DHQEMFHA.mjs';
3
3
  import 'zod';
4
- import '../../base-tNgbBaSo.mjs';
4
+ import '../../base-BICaQaRS.mjs';
5
5
 
6
6
  /**
7
7
  * Anthropic Provider - Modern Pattern
@@ -1,7 +1,7 @@
1
- import { L as LanguageModel } from '../../types-BkQCSiIt.js';
2
- import { a as AnthropicProviderConfig, A as AIProvider } from '../../types-BQ31QIsA.js';
1
+ import { L as LanguageModel } from '../../types-Dg1Bcg1k.js';
2
+ import { a as AnthropicProviderConfig, A as AIProvider } from '../../types-Dikj3En4.js';
3
3
  import 'zod';
4
- import '../../base-C58Dsr9p.js';
4
+ import '../../base-CpRPB8u8.js';
5
5
 
6
6
  /**
7
7
  * Anthropic Provider - Modern Pattern
@@ -1,5 +1,92 @@
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: "text",
76
+ text: part.text
77
+ };
78
+ if ("fileId" in part) {
79
+ throw new Error(
80
+ "OpenAI image file references cannot be sent to another provider"
81
+ );
82
+ }
83
+ return {
84
+ type: "image",
85
+ source: "url" in part ? { type: "url", url: part.url } : { type: "base64", media_type: part.mediaType, data: part.data }
86
+ };
87
+ });
88
+ }
89
+
3
90
  // src/adapters/base.ts
4
91
  function stringifyForDebug(value) {
5
92
  return JSON.stringify(
@@ -206,7 +293,27 @@ function messageToAnthropicContent(message) {
206
293
  }
207
294
  return blocks;
208
295
  }
296
+ function extractAnthropicSystemPrompt(messages, systemPrompt) {
297
+ const parts = systemPrompt ? [systemPrompt] : [];
298
+ for (const message of messages) {
299
+ if (message.role !== "system") continue;
300
+ if (typeof message.content === "string") {
301
+ if (message.content) parts.push(message.content);
302
+ } else if (Array.isArray(message.content)) {
303
+ for (const block of message.content) {
304
+ if (block?.type !== "text" || typeof block.text !== "string") {
305
+ throw new TypeError("Anthropic system messages must contain text.");
306
+ }
307
+ if (block.text) parts.push(block.text);
308
+ }
309
+ } else {
310
+ throw new TypeError("Anthropic system messages must contain text.");
311
+ }
312
+ }
313
+ return parts.join("\n");
314
+ }
209
315
  function formatMessagesForAnthropic(messages, systemPrompt) {
316
+ const system = extractAnthropicSystemPrompt(messages, systemPrompt);
210
317
  const formatted = [];
211
318
  for (let i = 0; i < messages.length; i++) {
212
319
  const msg = messages[i];
@@ -235,7 +342,7 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
235
342
  {
236
343
  type: "tool_result",
237
344
  tool_use_id: msg.tool_call_id,
238
- content: msg.content ?? ""
345
+ content: msg.modelOutput ? toolOutputForProvider(msg.modelOutput) : msg.content ?? ""
239
346
  }
240
347
  ];
241
348
  while (i + 1 < messages.length && messages[i + 1].role === "tool") {
@@ -245,7 +352,7 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
245
352
  toolResults.push({
246
353
  type: "tool_result",
247
354
  tool_use_id: nextTool.tool_call_id,
248
- content: nextTool.content ?? ""
355
+ content: nextTool.modelOutput ? toolOutputForProvider(nextTool.modelOutput) : nextTool.content ?? ""
249
356
  });
250
357
  }
251
358
  }
@@ -261,7 +368,7 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
261
368
  }
262
369
  }
263
370
  return {
264
- system: "",
371
+ system,
265
372
  messages: formatted
266
373
  };
267
374
  }
@@ -631,7 +738,7 @@ function formatMessagesForAnthropic2(messages) {
631
738
  }
632
739
  pendingToolResults.push({
633
740
  toolCallId,
634
- content: msg.content
741
+ content: msg.modelOutput ? toolOutputForProvider(msg.modelOutput) : msg.content
635
742
  });
636
743
  }
637
744
  }
@@ -750,7 +857,7 @@ var AnthropicAdapter = class {
750
857
  }
751
858
  pendingToolResults.push({
752
859
  tool_use_id: toolCallId,
753
- content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
860
+ content: msg.modelOutput ? toolOutputForProvider(msg.modelOutput) : typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
754
861
  });
755
862
  } else if (msg.role === "user") {
756
863
  if (pendingToolResults.length > 0) {
@@ -871,14 +978,23 @@ var AnthropicAdapter = class {
871
978
  buildRequestOptions(request) {
872
979
  const responseFormat = request.config?.responseFormat;
873
980
  const jsonObjectSuffix = responseFormat?.type === "json_object" ? "\n\nRespond with a single JSON object and no other text." : "";
874
- const systemMessage = (request.systemPrompt || "") + jsonObjectSuffix;
981
+ let systemMessage;
875
982
  let messages;
876
983
  if (request.rawMessages && request.rawMessages.length > 0) {
984
+ systemMessage = extractAnthropicSystemPrompt(
985
+ request.rawMessages,
986
+ request.systemPrompt
987
+ );
877
988
  messages = this.convertToAnthropicMessages(request.rawMessages);
878
989
  } else {
879
- const formatted = formatMessagesForAnthropic(request.messages);
990
+ const formatted = formatMessagesForAnthropic(
991
+ request.messages,
992
+ request.systemPrompt
993
+ );
994
+ systemMessage = formatted.system;
880
995
  messages = formatted.messages;
881
996
  }
997
+ systemMessage += jsonObjectSuffix;
882
998
  const anthropicNativeSearch = request.providerToolOptions?.anthropic?.nativeToolSearch;
883
999
  const tools = anthropicNativeSearch?.enabled ? this.buildNativeSearchTools(
884
1000
  request.toolDefinitions ?? [],
@@ -1,3 +1,90 @@
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: "text",
74
+ text: part.text
75
+ };
76
+ if ("fileId" in part) {
77
+ throw new Error(
78
+ "OpenAI image file references cannot be sent to another provider"
79
+ );
80
+ }
81
+ return {
82
+ type: "image",
83
+ source: "url" in part ? { type: "url", url: part.url } : { type: "base64", media_type: part.mediaType, data: part.data }
84
+ };
85
+ });
86
+ }
87
+
1
88
  // src/adapters/base.ts
2
89
  function stringifyForDebug(value) {
3
90
  return JSON.stringify(
@@ -204,7 +291,27 @@ function messageToAnthropicContent(message) {
204
291
  }
205
292
  return blocks;
206
293
  }
294
+ function extractAnthropicSystemPrompt(messages, systemPrompt) {
295
+ const parts = systemPrompt ? [systemPrompt] : [];
296
+ for (const message of messages) {
297
+ if (message.role !== "system") continue;
298
+ if (typeof message.content === "string") {
299
+ if (message.content) parts.push(message.content);
300
+ } else if (Array.isArray(message.content)) {
301
+ for (const block of message.content) {
302
+ if (block?.type !== "text" || typeof block.text !== "string") {
303
+ throw new TypeError("Anthropic system messages must contain text.");
304
+ }
305
+ if (block.text) parts.push(block.text);
306
+ }
307
+ } else {
308
+ throw new TypeError("Anthropic system messages must contain text.");
309
+ }
310
+ }
311
+ return parts.join("\n");
312
+ }
207
313
  function formatMessagesForAnthropic(messages, systemPrompt) {
314
+ const system = extractAnthropicSystemPrompt(messages, systemPrompt);
208
315
  const formatted = [];
209
316
  for (let i = 0; i < messages.length; i++) {
210
317
  const msg = messages[i];
@@ -233,7 +340,7 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
233
340
  {
234
341
  type: "tool_result",
235
342
  tool_use_id: msg.tool_call_id,
236
- content: msg.content ?? ""
343
+ content: msg.modelOutput ? toolOutputForProvider(msg.modelOutput) : msg.content ?? ""
237
344
  }
238
345
  ];
239
346
  while (i + 1 < messages.length && messages[i + 1].role === "tool") {
@@ -243,7 +350,7 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
243
350
  toolResults.push({
244
351
  type: "tool_result",
245
352
  tool_use_id: nextTool.tool_call_id,
246
- content: nextTool.content ?? ""
353
+ content: nextTool.modelOutput ? toolOutputForProvider(nextTool.modelOutput) : nextTool.content ?? ""
247
354
  });
248
355
  }
249
356
  }
@@ -259,7 +366,7 @@ function formatMessagesForAnthropic(messages, systemPrompt) {
259
366
  }
260
367
  }
261
368
  return {
262
- system: "",
369
+ system,
263
370
  messages: formatted
264
371
  };
265
372
  }
@@ -629,7 +736,7 @@ function formatMessagesForAnthropic2(messages) {
629
736
  }
630
737
  pendingToolResults.push({
631
738
  toolCallId,
632
- content: msg.content
739
+ content: msg.modelOutput ? toolOutputForProvider(msg.modelOutput) : msg.content
633
740
  });
634
741
  }
635
742
  }
@@ -748,7 +855,7 @@ var AnthropicAdapter = class {
748
855
  }
749
856
  pendingToolResults.push({
750
857
  tool_use_id: toolCallId,
751
- content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
858
+ content: msg.modelOutput ? toolOutputForProvider(msg.modelOutput) : typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
752
859
  });
753
860
  } else if (msg.role === "user") {
754
861
  if (pendingToolResults.length > 0) {
@@ -869,14 +976,23 @@ var AnthropicAdapter = class {
869
976
  buildRequestOptions(request) {
870
977
  const responseFormat = request.config?.responseFormat;
871
978
  const jsonObjectSuffix = responseFormat?.type === "json_object" ? "\n\nRespond with a single JSON object and no other text." : "";
872
- const systemMessage = (request.systemPrompt || "") + jsonObjectSuffix;
979
+ let systemMessage;
873
980
  let messages;
874
981
  if (request.rawMessages && request.rawMessages.length > 0) {
982
+ systemMessage = extractAnthropicSystemPrompt(
983
+ request.rawMessages,
984
+ request.systemPrompt
985
+ );
875
986
  messages = this.convertToAnthropicMessages(request.rawMessages);
876
987
  } else {
877
- const formatted = formatMessagesForAnthropic(request.messages);
988
+ const formatted = formatMessagesForAnthropic(
989
+ request.messages,
990
+ request.systemPrompt
991
+ );
992
+ systemMessage = formatted.system;
878
993
  messages = formatted.messages;
879
994
  }
995
+ systemMessage += jsonObjectSuffix;
880
996
  const anthropicNativeSearch = request.providerToolOptions?.anthropic?.nativeToolSearch;
881
997
  const tools = anthropicNativeSearch?.enabled ? this.buildNativeSearchTools(
882
998
  request.toolDefinitions ?? [],
@@ -1,7 +1,7 @@
1
- import { b as AzureProviderConfig, A as AIProvider } from '../../types-BSSiJW2o.mjs';
2
- import '../../types-BkQCSiIt.mjs';
1
+ import { b as AzureProviderConfig, A as AIProvider } from '../../types-DHQEMFHA.mjs';
2
+ import '../../types-Dg1Bcg1k.mjs';
3
3
  import 'zod';
4
- import '../../base-tNgbBaSo.mjs';
4
+ import '../../base-BICaQaRS.mjs';
5
5
 
6
6
  /**
7
7
  * Azure OpenAI Provider
@@ -1,7 +1,7 @@
1
- import { b as AzureProviderConfig, A as AIProvider } from '../../types-BQ31QIsA.js';
2
- import '../../types-BkQCSiIt.js';
1
+ import { b as AzureProviderConfig, A as AIProvider } from '../../types-Dikj3En4.js';
2
+ import '../../types-Dg1Bcg1k.js';
3
3
  import 'zod';
4
- import '../../base-C58Dsr9p.js';
4
+ import '../../base-CpRPB8u8.js';
5
5
 
6
6
  /**
7
7
  * Azure OpenAI Provider
@@ -1,5 +1,90 @@
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: "text",
76
+ text: part.text
77
+ };
78
+ throw new Error(
79
+ "Image tool results require OpenAI Responses or Anthropic; this endpoint is not supported"
80
+ );
81
+ });
82
+ }
83
+ function textToolOutput(value, fallback) {
84
+ if (value === void 0) return fallback;
85
+ return toolOutputForProvider(value).map((part) => part.text).join("\n");
86
+ }
87
+
3
88
  // src/core/utils.ts
4
89
  function generateId(prefix = "id") {
5
90
  return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
@@ -189,7 +274,7 @@ function messageToOpenAIContent(message) {
189
274
  }
190
275
  return blocks;
191
276
  }
192
- function formatMessagesForOpenAI(messages, systemPrompt) {
277
+ function formatMessagesForOpenAI(messages, systemPrompt, preserveProviderMetadata = false) {
193
278
  const formatted = [];
194
279
  if (systemPrompt) {
195
280
  formatted.push({ role: "system", content: systemPrompt });
@@ -207,7 +292,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
207
292
  const assistantMsg = {
208
293
  role: "assistant",
209
294
  // Gemini/xAI (OpenAI-compatible) reject content: "" on assistant messages with tool_calls
210
- content: hasToolCalls ? msg.content || null : msg.content
295
+ content: hasToolCalls ? msg.content || null : msg.content,
296
+ ...preserveProviderMetadata && msg.providerMetadata ? { providerMetadata: msg.providerMetadata } : {}
211
297
  };
212
298
  if (hasToolCalls) {
213
299
  assistantMsg.tool_calls = msg.tool_calls;
@@ -216,7 +302,8 @@ function formatMessagesForOpenAI(messages, systemPrompt) {
216
302
  } else if (msg.role === "tool" && msg.tool_call_id) {
217
303
  formatted.push({
218
304
  role: "tool",
219
- content: msg.content ?? "",
305
+ content: preserveProviderMetadata ? msg.content ?? "" : textToolOutput(msg.modelOutput, msg.content ?? ""),
306
+ ...preserveProviderMetadata && msg.modelOutput ? { modelOutput: msg.modelOutput } : {},
220
307
  tool_call_id: msg.tool_call_id
221
308
  });
222
309
  }
@@ -256,6 +343,13 @@ var AzureAdapter = class {
256
343
  let messages;
257
344
  if (request.rawMessages && request.rawMessages.length > 0) {
258
345
  const processedMessages = request.rawMessages.map((msg) => {
346
+ const { modelOutput, ...rest } = msg;
347
+ msg = {
348
+ ...rest,
349
+ ...msg.role === "tool" ? {
350
+ content: textToolOutput(modelOutput, String(msg.content ?? ""))
351
+ } : {}
352
+ };
259
353
  const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
260
354
  if (hasAttachments) {
261
355
  const content = [];
@@ -386,7 +480,10 @@ var AzureAdapter = class {
386
480
  const client = await this.getClient();
387
481
  let messages;
388
482
  if (request.rawMessages && request.rawMessages.length > 0) {
389
- messages = request.rawMessages;
483
+ messages = request.rawMessages.map(({ modelOutput, ...msg }) => ({
484
+ ...msg,
485
+ ...msg.role === "tool" ? { content: textToolOutput(modelOutput, String(msg.content ?? "")) } : {}
486
+ }));
390
487
  if (request.systemPrompt) {
391
488
  const hasSystem = messages.some((m) => m.role === "system");
392
489
  if (!hasSystem) {