1- import type { LanguageModelV3Prompt , SharedV3ProviderOptions } from "@ai-sdk/provider" ;
2- import type { WorkersAIChatPrompt } from "./workersai-chat-prompt" ;
1+ import type {
2+ LanguageModelV3DataContent ,
3+ LanguageModelV3Prompt ,
4+ } from "@ai-sdk/provider" ;
5+ import type { WorkersAIContentPart , WorkersAIChatPrompt } from "./workersai-chat-prompt" ;
6+
7+ /**
8+ * Normalise any LanguageModelV3DataContent value to a Uint8Array.
9+ *
10+ * Handles:
11+ * - Uint8Array → returned as-is
12+ * - string → decoded from base64 (with or without data-URL prefix)
13+ * - URL → not supported (Workers AI needs raw bytes, not a reference)
14+ */
15+ function toUint8Array ( data : LanguageModelV3DataContent ) : Uint8Array | null {
16+ if ( data instanceof Uint8Array ) {
17+ return data ;
18+ }
19+
20+ if ( typeof data === "string" ) {
21+ let base64 = data ;
22+ if ( base64 . startsWith ( "data:" ) ) {
23+ const commaIndex = base64 . indexOf ( "," ) ;
24+ if ( commaIndex >= 0 ) {
25+ base64 = base64 . slice ( commaIndex + 1 ) ;
26+ }
27+ }
28+ const binaryString = atob ( base64 ) ;
29+ const bytes = new Uint8Array ( binaryString . length ) ;
30+ for ( let i = 0 ; i < binaryString . length ; i ++ ) {
31+ bytes [ i ] = binaryString . charCodeAt ( i ) ;
32+ }
33+ return bytes ;
34+ }
35+
36+ if ( data instanceof URL ) {
37+ throw new Error (
38+ "URL image sources are not supported by Workers AI. " +
39+ "Provide image data as a Uint8Array or base64 string instead." ,
40+ ) ;
41+ }
42+
43+ return null ;
44+ }
45+
46+ function uint8ArrayToBase64 ( bytes : Uint8Array ) : string {
47+ let binary = "" ;
48+ const chunkSize = 8192 ;
49+ for ( let i = 0 ; i < bytes . length ; i += chunkSize ) {
50+ const chunk = bytes . subarray ( i , Math . min ( i + chunkSize , bytes . length ) ) ;
51+ binary += String . fromCharCode ( ...chunk ) ;
52+ }
53+ return btoa ( binary ) ;
54+ }
355
456export function convertToWorkersAIChatMessages ( prompt : LanguageModelV3Prompt ) : {
557 messages : WorkersAIChatPrompt ;
6- images : {
7- mediaType : string | undefined ;
8- image : Uint8Array ;
9- providerOptions : SharedV3ProviderOptions | undefined ;
10- } [ ] ;
1158} {
1259 const messages : WorkersAIChatPrompt = [ ] ;
13- const images : {
14- mediaType : string | undefined ;
15- image : Uint8Array ;
16- providerOptions : SharedV3ProviderOptions | undefined ;
17- } [ ] = [ ] ;
1860
1961 for ( const { role, content } of prompt ) {
2062 switch ( role ) {
@@ -25,6 +67,7 @@ export function convertToWorkersAIChatMessages(prompt: LanguageModelV3Prompt): {
2567
2668 case "user" : {
2769 const textParts : string [ ] = [ ] ;
70+ const imageParts : { image : Uint8Array ; mediaType : string | undefined } [ ] = [ ] ;
2871
2972 for ( const part of content ) {
3073 switch ( part . type ) {
@@ -33,23 +76,36 @@ export function convertToWorkersAIChatMessages(prompt: LanguageModelV3Prompt): {
3376 break ;
3477 }
3578 case "file" : {
36- if ( part . data instanceof Uint8Array ) {
37- images . push ( {
38- image : part . data ,
79+ const imageBytes = toUint8Array ( part . data ) ;
80+ if ( imageBytes ) {
81+ imageParts . push ( {
82+ image : imageBytes ,
3983 mediaType : part . mediaType ,
40- providerOptions : part . providerOptions ,
4184 } ) ;
4285 }
43- // Don't push empty strings for image parts
4486 break ;
4587 }
4688 }
4789 }
4890
49- messages . push ( {
50- content : textParts . join ( "\n" ) ,
51- role : "user" ,
52- } ) ;
91+ if ( imageParts . length > 0 ) {
92+ const contentArray : WorkersAIContentPart [ ] = [ ] ;
93+ if ( textParts . length > 0 ) {
94+ contentArray . push ( { type : "text" , text : textParts . join ( "\n" ) } ) ;
95+ }
96+ for ( const img of imageParts ) {
97+ const base64 = uint8ArrayToBase64 ( img . image ) ;
98+ const mediaType = img . mediaType || "image/png" ;
99+ contentArray . push ( {
100+ type : "image_url" ,
101+ image_url : { url : `data:${ mediaType } ;base64,${ base64 } ` } ,
102+ } ) ;
103+ }
104+ messages . push ( { content : contentArray , role : "user" } ) ;
105+ } else {
106+ messages . push ( { content : textParts . join ( "\n" ) , role : "user" } ) ;
107+ }
108+
53109 break ;
54110 }
55111
@@ -144,5 +200,5 @@ export function convertToWorkersAIChatMessages(prompt: LanguageModelV3Prompt): {
144200 }
145201 }
146202
147- return { images , messages } ;
203+ return { messages } ;
148204}
0 commit comments