headroom-ai 0.41.0 → 0.42.0

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.
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkW6AXXD2B_cjs = require('../chunk-W6AXXD2B.cjs');
3
+ var chunkCINZDAE4_cjs = require('../chunk-CINZDAE4.cjs');
4
4
 
5
5
  // src/adapters/anthropic.ts
6
6
  function withHeadroom(client, options = {}) {
@@ -8,13 +8,13 @@ function withHeadroom(client, options = {}) {
8
8
  const compressedCreate = async (params) => {
9
9
  const messages = params.messages;
10
10
  const model = options.model ?? params.model ?? "claude-sonnet-4-5-20250929";
11
- const openaiMessages = chunkW6AXXD2B_cjs.anthropicToOpenAI(messages);
12
- const result = await chunkW6AXXD2B_cjs.compress(openaiMessages, {
11
+ const openaiMessages = chunkCINZDAE4_cjs.anthropicToOpenAI(messages);
12
+ const result = await chunkCINZDAE4_cjs.compress(openaiMessages, {
13
13
  stack: "adapter_ts_anthropic",
14
14
  ...options,
15
15
  model
16
16
  });
17
- const anthropicMessages = result.compressed ? chunkW6AXXD2B_cjs.openAIToAnthropic(result.messages) : messages;
17
+ const anthropicMessages = result.compressed ? chunkCINZDAE4_cjs.openAIToAnthropic(result.messages) : messages;
18
18
  return originalCreate({
19
19
  ...params,
20
20
  messages: anthropicMessages
@@ -1,4 +1,4 @@
1
- import { C as CompressOptions } from '../types-Cjdl75F4.cjs';
1
+ import { C as CompressOptions } from '../types-Bxcai7Tt.cjs';
2
2
 
3
3
  interface AnthropicLike {
4
4
  messages: {
@@ -1,4 +1,4 @@
1
- import { C as CompressOptions } from '../types-Cjdl75F4.js';
1
+ import { C as CompressOptions } from '../types-Bxcai7Tt.js';
2
2
 
3
3
  interface AnthropicLike {
4
4
  messages: {
@@ -1,4 +1,4 @@
1
- import { anthropicToOpenAI, compress, openAIToAnthropic } from '../chunk-WKB3ITLL.js';
1
+ import { anthropicToOpenAI, compress, openAIToAnthropic } from '../chunk-UVX5TGIN.js';
2
2
 
3
3
  // src/adapters/anthropic.ts
4
4
  function withHeadroom(client, options = {}) {
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkW6AXXD2B_cjs = require('../chunk-W6AXXD2B.cjs');
3
+ var chunkCINZDAE4_cjs = require('../chunk-CINZDAE4.cjs');
4
4
 
5
5
  // src/adapters/gemini.ts
6
6
  function withHeadroom(model, options = {}) {
@@ -9,7 +9,7 @@ function withHeadroom(model, options = {}) {
9
9
  const compressedGenerate = async (params) => {
10
10
  const contents = params.contents ?? params;
11
11
  const modelName = options.model ?? "gemini-2.0-flash";
12
- const result = await chunkW6AXXD2B_cjs.compress(
12
+ const result = await chunkCINZDAE4_cjs.compress(
13
13
  Array.isArray(contents) ? contents : [contents],
14
14
  { stack: "adapter_ts_gemini", ...options, model: modelName }
15
15
  );
@@ -19,7 +19,7 @@ function withHeadroom(model, options = {}) {
19
19
  const compressedStream = originalStream ? async (params) => {
20
20
  const contents = params.contents ?? params;
21
21
  const modelName = options.model ?? "gemini-2.0-flash";
22
- const result = await chunkW6AXXD2B_cjs.compress(
22
+ const result = await chunkCINZDAE4_cjs.compress(
23
23
  Array.isArray(contents) ? contents : [contents],
24
24
  { stack: "adapter_ts_gemini", ...options, model: modelName }
25
25
  );
@@ -1,4 +1,4 @@
1
- import { C as CompressOptions } from '../types-Cjdl75F4.cjs';
1
+ import { C as CompressOptions } from '../types-Bxcai7Tt.cjs';
2
2
 
3
3
  /**
4
4
  * Gemini adapter — wraps Google Generative AI model with auto-compression.
@@ -1,4 +1,4 @@
1
- import { C as CompressOptions } from '../types-Cjdl75F4.js';
1
+ import { C as CompressOptions } from '../types-Bxcai7Tt.js';
2
2
 
3
3
  /**
4
4
  * Gemini adapter — wraps Google Generative AI model with auto-compression.
@@ -1,4 +1,4 @@
1
- import { compress } from '../chunk-WKB3ITLL.js';
1
+ import { compress } from '../chunk-UVX5TGIN.js';
2
2
 
3
3
  // src/adapters/gemini.ts
4
4
  function withHeadroom(model, options = {}) {
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkW6AXXD2B_cjs = require('../chunk-W6AXXD2B.cjs');
3
+ var chunkCINZDAE4_cjs = require('../chunk-CINZDAE4.cjs');
4
4
 
5
5
  // src/adapters/openai.ts
6
6
  function withHeadroom(client, options = {}) {
@@ -10,7 +10,7 @@ function withHeadroom(client, options = {}) {
10
10
  const compressedCreate = async (params) => {
11
11
  const messages = params.messages;
12
12
  const model = options.model ?? params.model ?? "gpt-4o";
13
- const result = await chunkW6AXXD2B_cjs.compress(messages, {
13
+ const result = await chunkCINZDAE4_cjs.compress(messages, {
14
14
  stack: "adapter_ts_openai",
15
15
  ...options,
16
16
  model
@@ -1,4 +1,4 @@
1
- import { C as CompressOptions } from '../types-Cjdl75F4.cjs';
1
+ import { C as CompressOptions } from '../types-Bxcai7Tt.cjs';
2
2
 
3
3
  interface OpenAILike {
4
4
  chat: {
@@ -1,4 +1,4 @@
1
- import { C as CompressOptions } from '../types-Cjdl75F4.js';
1
+ import { C as CompressOptions } from '../types-Bxcai7Tt.js';
2
2
 
3
3
  interface OpenAILike {
4
4
  chat: {
@@ -1,4 +1,4 @@
1
- import { compress } from '../chunk-WKB3ITLL.js';
1
+ import { compress } from '../chunk-UVX5TGIN.js';
2
2
 
3
3
  // src/adapters/openai.ts
4
4
  function withHeadroom(client, options = {}) {
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkW6AXXD2B_cjs = require('../chunk-W6AXXD2B.cjs');
3
+ var chunkCINZDAE4_cjs = require('../chunk-CINZDAE4.cjs');
4
4
  var module$1 = require('module');
5
5
 
6
6
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
@@ -12,25 +12,25 @@ function headroomMiddleware(options = {}) {
12
12
  const prompt = params.prompt;
13
13
  if (!prompt || prompt.length === 0) return params;
14
14
  const model = options.model ?? params.modelId ?? "gpt-4o";
15
- const openaiMessages = chunkW6AXXD2B_cjs.vercelToOpenAI(prompt);
16
- const result = await chunkW6AXXD2B_cjs.compress(openaiMessages, {
15
+ const openaiMessages = chunkCINZDAE4_cjs.vercelToOpenAI(prompt);
16
+ const result = await chunkCINZDAE4_cjs.compress(openaiMessages, {
17
17
  stack: "adapter_ts_vercel_ai",
18
18
  ...options,
19
19
  model
20
20
  });
21
21
  if (!result.compressed) return params;
22
- const compressedPrompt = chunkW6AXXD2B_cjs.openAIToVercel(result.messages);
22
+ const compressedPrompt = chunkCINZDAE4_cjs.openAIToVercel(result.messages);
23
23
  return { ...params, prompt: compressedPrompt };
24
24
  }
25
25
  };
26
26
  }
27
27
  async function compressVercelMessages(messages, options = {}) {
28
- const openaiMessages = chunkW6AXXD2B_cjs.vercelToOpenAI(messages);
29
- const result = await chunkW6AXXD2B_cjs.compress(openaiMessages, {
28
+ const openaiMessages = chunkCINZDAE4_cjs.vercelToOpenAI(messages);
29
+ const result = await chunkCINZDAE4_cjs.compress(openaiMessages, {
30
30
  stack: "adapter_ts_vercel_ai",
31
31
  ...options
32
32
  });
33
- const vercelMessages = chunkW6AXXD2B_cjs.openAIToVercel(result.messages);
33
+ const vercelMessages = chunkCINZDAE4_cjs.openAIToVercel(result.messages);
34
34
  return {
35
35
  ...result,
36
36
  messages: vercelMessages
@@ -1,4 +1,4 @@
1
- import { C as CompressOptions, a as CompressResult } from '../types-Cjdl75F4.cjs';
1
+ import { C as CompressOptions, a as CompressResult } from '../types-Bxcai7Tt.cjs';
2
2
 
3
3
  /**
4
4
  * Minimal structural type for Vercel AI SDK language models.
@@ -1,4 +1,4 @@
1
- import { C as CompressOptions, a as CompressResult } from '../types-Cjdl75F4.js';
1
+ import { C as CompressOptions, a as CompressResult } from '../types-Bxcai7Tt.js';
2
2
 
3
3
  /**
4
4
  * Minimal structural type for Vercel AI SDK language models.
@@ -1,4 +1,4 @@
1
- import { vercelToOpenAI, compress, openAIToVercel } from '../chunk-WKB3ITLL.js';
1
+ import { vercelToOpenAI, compress, openAIToVercel } from '../chunk-UVX5TGIN.js';
2
2
  import { createRequire } from 'module';
3
3
 
4
4
  function headroomMiddleware(options = {}) {
@@ -614,7 +614,11 @@ function detectFormat(messages) {
614
614
  for (const part of msg.content) {
615
615
  if (part.type === "tool-call" || part.type === "tool-result") return "vercel";
616
616
  if (part.type === "tool_use" || part.type === "tool_result") return "anthropic";
617
- if (part.type === "image" && part.source?.type) return "anthropic";
617
+ if ((part.type === "image" || part.type === "document") && part.source?.type) return "anthropic";
618
+ if (part && typeof part === "object") {
619
+ if (part.type === "file" && ("data" in part || "mediaType" in part)) return "vercel";
620
+ if (part.type === "image" && "image" in part) return "vercel";
621
+ }
618
622
  }
619
623
  }
620
624
  }
@@ -633,6 +637,50 @@ function openAIImageBlock(url) {
633
637
  if (m) return { type: "image", source: { type: "base64", media_type: m[1], data: m[2] } };
634
638
  return { type: "image", source: { type: "url", url } };
635
639
  }
640
+ var ANTHROPIC_DOCUMENT_FIELDS = ["title", "context", "citations", "cache_control"];
641
+ function anthropicMediaImageUrl(block) {
642
+ if (block.type === "image") {
643
+ const url = anthropicImageUrl(block.source);
644
+ return url ? { url } : null;
645
+ }
646
+ if (block.type !== "document" || !block.source || typeof block.source !== "object") return null;
647
+ const src = block.source;
648
+ let out;
649
+ if (src.type === "base64" && src.media_type && src.data) {
650
+ out = { url: `data:${src.media_type};base64,${src.data}` };
651
+ } else if (src.type === "url" && typeof src.url === "string") {
652
+ out = { url: src.url, mime_type: "application/pdf" };
653
+ } else if (src.type === "text" && typeof src.data === "string") {
654
+ out = {
655
+ url: `data:${src.media_type || "text/plain"};base64,${bytesToBase64(new TextEncoder().encode(src.data))}`,
656
+ source_type: "text"
657
+ };
658
+ } else {
659
+ out = { url: "data:;base64,", source: src };
660
+ }
661
+ const fields = {};
662
+ for (const k of ANTHROPIC_DOCUMENT_FIELDS) if (block[k] !== void 0) fields[k] = block[k];
663
+ if (Object.keys(fields).length > 0) out.document = fields;
664
+ return out;
665
+ }
666
+ function anthropicMediaBlock(imageUrl) {
667
+ const url = imageUrl.url;
668
+ const m = /^data:([^;,]*);base64,(.*)$/s.exec(url);
669
+ const mediaType = m && m[1] || (typeof imageUrl.mime_type === "string" ? imageUrl.mime_type : void 0);
670
+ const fields = imageUrl.document && typeof imageUrl.document === "object" ? imageUrl.document : {};
671
+ if (imageUrl.source && typeof imageUrl.source === "object") {
672
+ return { type: "document", source: imageUrl.source, ...fields };
673
+ }
674
+ if (imageUrl.source_type === "text" && m) {
675
+ return { type: "document", source: { type: "text", media_type: "text/plain", data: base64ToText(m[2]) }, ...fields };
676
+ }
677
+ if (mediaType === "application/pdf") {
678
+ const source = m ? { type: "base64", media_type: "application/pdf", data: m[2] } : { type: "url", url };
679
+ return { type: "document", source, ...fields };
680
+ }
681
+ if (mediaType === void 0 || mediaType.startsWith("image/")) return openAIImageBlock(url);
682
+ return null;
683
+ }
636
684
  function anthropicToOpenAI(messages) {
637
685
  const result = [];
638
686
  for (const msg of messages) {
@@ -643,12 +691,12 @@ function anthropicToOpenAI(messages) {
643
691
  }
644
692
  if (Array.isArray(msg.content)) {
645
693
  const toolResults = msg.content.filter((b) => b.type === "tool_result");
646
- const hasImages = msg.content.some((b) => b.type === "image");
647
- if (hasImages) {
648
- const parts = msg.content.filter((b) => b.type === "text" || b.type === "image").map((b) => {
694
+ const hasMedia = msg.content.some((b) => b.type === "image" || b.type === "document");
695
+ if (hasMedia) {
696
+ const parts = msg.content.filter((b) => b.type === "text" || b.type === "image" || b.type === "document").map((b) => {
649
697
  if (b.type === "text") return { type: "text", text: b.text };
650
- const url = anthropicImageUrl(b.source);
651
- return url ? { type: "image_url", image_url: { url } } : null;
698
+ const imageUrl = anthropicMediaImageUrl(b);
699
+ return imageUrl ? { type: "image_url", image_url: imageUrl } : null;
652
700
  }).filter((p) => p !== null);
653
701
  if (parts.length > 0) {
654
702
  result.push({ role: "user", content: parts });
@@ -681,7 +729,17 @@ function anthropicToOpenAI(messages) {
681
729
  if (Array.isArray(msg.content)) {
682
730
  const textBlocks = msg.content.filter((b) => b.type === "text");
683
731
  const toolUseBlocks = msg.content.filter((b) => b.type === "tool_use");
684
- const content = textBlocks.length > 0 ? textBlocks.map((b) => b.text).join("\n") : null;
732
+ const hasMedia = msg.content.some((b) => b.type === "image" || b.type === "document");
733
+ let content;
734
+ if (hasMedia) {
735
+ content = msg.content.filter((b) => b.type === "text" || b.type === "image" || b.type === "document").map((b) => {
736
+ if (b.type === "text") return { type: "text", text: b.text };
737
+ const imageUrl = anthropicMediaImageUrl(b);
738
+ return imageUrl ? { type: "image_url", image_url: imageUrl } : null;
739
+ }).filter((p) => p !== null);
740
+ } else {
741
+ content = textBlocks.length > 0 ? textBlocks.map((b) => b.text).join("\n") : null;
742
+ }
685
743
  const openaiMsg = { role: "assistant", content };
686
744
  if (toolUseBlocks.length > 0) {
687
745
  openaiMsg.tool_calls = toolUseBlocks.map((b) => ({
@@ -715,7 +773,7 @@ function openAIToAnthropic(messages) {
715
773
  role: "user",
716
774
  content: msg.content.map((p) => {
717
775
  if (p.type === "text") return { type: "text", text: p.text };
718
- if (p.type === "image_url") return openAIImageBlock(p.image_url.url);
776
+ if (p.type === "image_url") return anthropicMediaBlock(p.image_url) ?? { type: "text", text: "" };
719
777
  return { type: "text", text: "" };
720
778
  })
721
779
  });
@@ -724,7 +782,17 @@ function openAIToAnthropic(messages) {
724
782
  }
725
783
  if (msg.role === "assistant") {
726
784
  const blocks = [];
727
- if (msg.content) blocks.push({ type: "text", text: msg.content });
785
+ if (typeof msg.content === "string") {
786
+ if (msg.content) blocks.push({ type: "text", text: msg.content });
787
+ } else if (Array.isArray(msg.content)) {
788
+ for (const g of groupAssistantParts(msg.content)) {
789
+ if ("text" in g) blocks.push({ type: "text", text: g.text });
790
+ else {
791
+ const block = anthropicMediaBlock(g.image_url);
792
+ if (block) blocks.push(block);
793
+ }
794
+ }
795
+ }
728
796
  if (msg.tool_calls) {
729
797
  for (const tc of msg.tool_calls) {
730
798
  blocks.push({
@@ -753,6 +821,121 @@ function openAIToAnthropic(messages) {
753
821
  }
754
822
  return result;
755
823
  }
824
+ function bytesToBase64(bytes) {
825
+ if (typeof Buffer !== "undefined") return Buffer.from(bytes).toString("base64");
826
+ let binary = "";
827
+ for (let i = 0; i < bytes.length; i += 32768) {
828
+ binary += String.fromCharCode(...bytes.subarray(i, i + 32768));
829
+ }
830
+ return btoa(binary);
831
+ }
832
+ function parseUrl(value) {
833
+ try {
834
+ return new URL(value);
835
+ } catch {
836
+ return null;
837
+ }
838
+ }
839
+ function base64ToText(b64) {
840
+ if (typeof Buffer !== "undefined") return Buffer.from(b64, "base64").toString("utf8");
841
+ const binary = atob(b64);
842
+ const bytes = new Uint8Array(binary.length);
843
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
844
+ return new TextDecoder().decode(bytes);
845
+ }
846
+ function isProviderReference(v) {
847
+ return !!v && typeof v === "object" && !Array.isArray(v) && !(v instanceof Uint8Array) && !(v instanceof ArrayBuffer) && !(v instanceof URL) && v.type === void 0 && Object.values(v).every((x) => typeof x === "string");
848
+ }
849
+ function vercelMediaImageUrl(p) {
850
+ let data = p.type === "file" ? p.data : p.image;
851
+ const mediaType = typeof p.mediaType === "string" ? p.mediaType : p.mimeType;
852
+ const out = { url: "" };
853
+ if (p.type === "file") out.part = "file";
854
+ if (typeof p.filename === "string") out.filename = p.filename;
855
+ if (data && typeof data === "object" && typeof data.type === "string") {
856
+ if (data.type === "data") {
857
+ out.data_tag = "data";
858
+ data = data.data;
859
+ } else if (data.type === "url") {
860
+ out.data_tag = "url";
861
+ data = data.url;
862
+ } else if (data.type === "text" && typeof data.text === "string") {
863
+ out.data_tag = "text";
864
+ out.url = `data:${mediaType ?? "text/plain"};base64,${bytesToBase64(new TextEncoder().encode(data.text))}`;
865
+ return out;
866
+ } else if (data.type === "reference" && data.reference && typeof data.reference === "object") {
867
+ out.data_tag = "reference";
868
+ out.reference = data.reference;
869
+ out.url = `data:${mediaType ?? ""};base64,`;
870
+ return out;
871
+ } else {
872
+ return null;
873
+ }
874
+ } else if (isProviderReference(data)) {
875
+ out.reference = data;
876
+ out.url = `data:${mediaType ?? ""};base64,`;
877
+ return out;
878
+ }
879
+ let carryType = false;
880
+ if (data instanceof Uint8Array || data instanceof ArrayBuffer) {
881
+ const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);
882
+ out.url = `data:${mediaType ?? ""};base64,${bytesToBase64(bytes)}`;
883
+ } else if (data instanceof URL) {
884
+ out.url = data.toString();
885
+ carryType = true;
886
+ } else if (typeof data === "string") {
887
+ if (data.startsWith("data:")) out.url = data;
888
+ else if (parseUrl(data)) {
889
+ out.url = data;
890
+ carryType = true;
891
+ } else out.url = `data:${mediaType ?? ""};base64,${data}`;
892
+ } else {
893
+ return null;
894
+ }
895
+ if (carryType && mediaType) out.mime_type = mediaType;
896
+ return out;
897
+ }
898
+ function vercelMediaPart(imageUrl) {
899
+ const url = imageUrl.url;
900
+ const tag = typeof imageUrl.data_tag === "string" ? imageUrl.data_tag : void 0;
901
+ const m = /^data:([^;,]*);base64,(.*)$/s.exec(url);
902
+ let mediaType = typeof imageUrl.mime_type === "string" ? imageUrl.mime_type : void 0;
903
+ if (m && m[1]) mediaType = m[1];
904
+ let data;
905
+ if (imageUrl.reference && typeof imageUrl.reference === "object") {
906
+ data = tag === "reference" ? { type: "reference", reference: imageUrl.reference } : imageUrl.reference;
907
+ } else if (m) {
908
+ data = m[2];
909
+ if (tag === "data") data = { type: "data", data: m[2] };
910
+ else if (tag === "text") data = { type: "text", text: base64ToText(m[2]) };
911
+ } else {
912
+ const parsed = parseUrl(url);
913
+ data = parsed ?? url;
914
+ if (tag === "url") data = { type: "url", url: parsed ?? url };
915
+ }
916
+ const isFile = imageUrl.part === "file" || mediaType !== void 0 && !mediaType.startsWith("image/");
917
+ if (isFile) {
918
+ const part2 = { type: "file", mediaType: mediaType ?? "application/octet-stream", data };
919
+ if (typeof imageUrl.filename === "string") part2.filename = imageUrl.filename;
920
+ return part2;
921
+ }
922
+ const part = { type: "image", image: data };
923
+ if (mediaType) part.mediaType = mediaType;
924
+ return part;
925
+ }
926
+ function groupAssistantParts(parts) {
927
+ const hasMedia = parts.some((p) => p.type === "image_url");
928
+ if (!hasMedia) {
929
+ const text = parts.filter((p) => p.type === "text").map((p) => p.text).join("\n");
930
+ return text ? [{ text }] : [];
931
+ }
932
+ const out = [];
933
+ for (const p of parts) {
934
+ if (p.type === "text") out.push({ text: p.text });
935
+ else if (p.type === "image_url") out.push({ image_url: p.image_url });
936
+ }
937
+ return out;
938
+ }
756
939
  function vercelToOpenAI(messages) {
757
940
  const result = [];
758
941
  for (const msg of messages) {
@@ -767,18 +950,15 @@ function vercelToOpenAI(messages) {
767
950
  }
768
951
  const parts = Array.isArray(msg.content) ? msg.content : [];
769
952
  const textParts = parts.filter((p) => p.type === "text");
770
- const imageParts = parts.filter((p) => p.type === "image");
771
- if (imageParts.length === 0 && textParts.length > 0) {
953
+ const mediaParts = parts.filter((p) => p.type === "image" || p.type === "file");
954
+ if (mediaParts.length === 0 && textParts.length > 0) {
772
955
  result.push({ role: "user", content: textParts.map((p) => p.text).join("") });
773
956
  } else {
774
- const openaiParts = parts.filter((p) => p.type === "text" || p.type === "image").map((p) => {
957
+ const openaiParts = parts.filter((p) => p.type === "text" || p.type === "image" || p.type === "file").map((p) => {
775
958
  if (p.type === "text") return { type: "text", text: p.text };
776
- if (p.type === "image") {
777
- const url = p.image instanceof URL ? p.image.toString() : String(p.image);
778
- return { type: "image_url", image_url: { url } };
779
- }
780
- return { type: "text", text: "" };
781
- });
959
+ const imageUrl = vercelMediaImageUrl(p);
960
+ return imageUrl ? { type: "image_url", image_url: imageUrl } : null;
961
+ }).filter((p) => p !== null);
782
962
  result.push({ role: "user", content: openaiParts });
783
963
  }
784
964
  continue;
@@ -791,7 +971,17 @@ function vercelToOpenAI(messages) {
791
971
  const parts = Array.isArray(msg.content) ? msg.content : [];
792
972
  const textParts = parts.filter((p) => p.type === "text");
793
973
  const toolCallParts = parts.filter((p) => p.type === "tool-call");
794
- const content = textParts.length > 0 ? textParts.map((p) => p.text).join("") : null;
974
+ const hasFiles = parts.some((p) => p.type === "file");
975
+ let content;
976
+ if (hasFiles) {
977
+ content = parts.filter((p) => p.type === "text" || p.type === "file").map((p) => {
978
+ if (p.type === "text") return { type: "text", text: p.text };
979
+ const imageUrl = vercelMediaImageUrl(p);
980
+ return imageUrl ? { type: "image_url", image_url: imageUrl } : null;
981
+ }).filter((p) => p !== null);
982
+ } else {
983
+ content = textParts.length > 0 ? textParts.map((p) => p.text).join("") : null;
984
+ }
795
985
  const openaiMsg = { role: "assistant", content };
796
986
  if (toolCallParts.length > 0) {
797
987
  openaiMsg.tool_calls = toolCallParts.map((p) => ({
@@ -843,7 +1033,7 @@ function openAIToVercel(messages) {
843
1033
  } else if (Array.isArray(msg.content)) {
844
1034
  const parts = msg.content.map((p) => {
845
1035
  if (p.type === "text") return { type: "text", text: p.text };
846
- if (p.type === "image_url") return { type: "image", image: new URL(p.image_url.url) };
1036
+ if (p.type === "image_url") return vercelMediaPart(p.image_url);
847
1037
  return { type: "text", text: "" };
848
1038
  });
849
1039
  result.push({ role: "user", content: parts });
@@ -852,7 +1042,14 @@ function openAIToVercel(messages) {
852
1042
  }
853
1043
  if (msg.role === "assistant") {
854
1044
  const parts = [];
855
- if (msg.content) parts.push({ type: "text", text: msg.content });
1045
+ if (typeof msg.content === "string") {
1046
+ if (msg.content) parts.push({ type: "text", text: msg.content });
1047
+ } else if (Array.isArray(msg.content)) {
1048
+ for (const p of msg.content) {
1049
+ if (p.type === "text") parts.push({ type: "text", text: p.text });
1050
+ else if (p.type === "image_url") parts.push(vercelMediaPart(p.image_url));
1051
+ }
1052
+ }
856
1053
  if (msg.tool_calls) {
857
1054
  for (const tc of msg.tool_calls) {
858
1055
  toolNames.set(tc.id, tc.function.name);
@@ -957,7 +1154,17 @@ function geminiToOpenAI(messages) {
957
1154
  if (role === "assistant") {
958
1155
  const textParts = parts.filter((p) => p.text !== void 0);
959
1156
  const funcCalls = parts.filter((p) => p.functionCall);
960
- const content = textParts.length > 0 ? textParts.map((p) => p.text).join("\n") : null;
1157
+ const hasMedia = parts.some((p) => p.inlineData || p.fileData);
1158
+ let content;
1159
+ if (hasMedia) {
1160
+ content = parts.filter((p) => p.text !== void 0 || p.inlineData || p.fileData).map((p) => {
1161
+ if (p.text !== void 0) return { type: "text", text: p.text };
1162
+ const imageUrl = geminiMediaImageUrl(p);
1163
+ return imageUrl ? { type: "image_url", image_url: imageUrl } : null;
1164
+ }).filter((p) => p !== null);
1165
+ } else {
1166
+ content = textParts.length > 0 ? textParts.map((p) => p.text).join("\n") : null;
1167
+ }
961
1168
  const openaiMsg = { role: "assistant", content };
962
1169
  if (funcCalls.length > 0) {
963
1170
  openaiMsg.tool_calls = funcCalls.map((p) => ({
@@ -998,7 +1205,14 @@ function openAIToGemini(messages) {
998
1205
  }
999
1206
  if (msg.role === "assistant") {
1000
1207
  const parts = [];
1001
- if (msg.content) parts.push({ text: msg.content });
1208
+ if (typeof msg.content === "string") {
1209
+ if (msg.content) parts.push({ text: msg.content });
1210
+ } else if (Array.isArray(msg.content)) {
1211
+ for (const g of groupAssistantParts(msg.content)) {
1212
+ if ("text" in g) parts.push({ text: g.text });
1213
+ else parts.push(geminiMediaPart(g.image_url));
1214
+ }
1215
+ }
1002
1216
  if (msg.tool_calls) {
1003
1217
  for (const tc of msg.tool_calls) {
1004
1218
  parts.push({
@@ -1190,5 +1404,5 @@ exports.parseSSE = parseSSE;
1190
1404
  exports.snakeToCamel = snakeToCamel;
1191
1405
  exports.toOpenAI = toOpenAI;
1192
1406
  exports.vercelToOpenAI = vercelToOpenAI;
1193
- //# sourceMappingURL=chunk-W6AXXD2B.cjs.map
1194
- //# sourceMappingURL=chunk-W6AXXD2B.cjs.map
1407
+ //# sourceMappingURL=chunk-CINZDAE4.cjs.map
1408
+ //# sourceMappingURL=chunk-CINZDAE4.cjs.map