Skip to content

Commit a4c6127

Browse files
Allow FormData to be passed as input to AI bindings
``` Models like bfl/flux-2-dev accept FormData as inputs, where the user can pass multiple images as style references. This PR allows AI bindings to pass FormData as-is to the model. ```
1 parent ccc4937 commit a4c6127

File tree

3 files changed

+46
-4
lines changed

3 files changed

+46
-4
lines changed

src/cloudflare/internal/ai-api.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export type AiOptions = {
4242
};
4343

4444
export type AiInputReadableStream = {
45-
body: ReadableStream;
45+
body: ReadableStream | FormData;
4646
contentType: string;
4747
};
4848

@@ -87,7 +87,6 @@ export class AiInternalError extends Error {
8787
}
8888
}
8989

90-
// TODO: merge this function with the one with images-api.ts
9190
function isReadableStream(obj: unknown): obj is ReadableStream {
9291
return !!(
9392
obj &&
@@ -97,6 +96,17 @@ function isReadableStream(obj: unknown): obj is ReadableStream {
9796
);
9897
}
9998

99+
function isFormData(obj: unknown): obj is FormData {
100+
return !!(
101+
obj &&
102+
typeof obj === 'object' &&
103+
'append' in obj &&
104+
typeof obj.append === 'function' &&
105+
'entries' in obj &&
106+
typeof obj.entries === 'function'
107+
);
108+
}
109+
100110
/**
101111
* Find keys in inputs that have a ReadableStream
102112
* */
@@ -111,9 +121,9 @@ function findReadableStreamKeys(
111121
value &&
112122
typeof value === 'object' &&
113123
'body' in value &&
114-
isReadableStream(value.body);
124+
(isReadableStream(value.body) || isFormData(value.body));
115125

116-
if (hasReadableStreamBody || isReadableStream(value)) {
126+
if (hasReadableStreamBody || isReadableStream(value) || isFormData(value)) {
117127
readableStreamKeys.push(key);
118128
}
119129
}

src/cloudflare/internal/test/ai/ai-api-test.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,25 @@ export const tests = {
214214
);
215215
}
216216

217+
{
218+
// Test form data input
219+
const form = new FormData();
220+
form.append('prompt', 'cat');
221+
const resp = await env.ai.run('formDataInputs', {
222+
audio: {
223+
body: form,
224+
contentType: 'multipart/form-data',
225+
},
226+
});
227+
228+
assert.deepStrictEqual(resp, {
229+
inputs: {},
230+
options: { userInputs: '{}', version: '3' },
231+
requestUrl:
232+
'https://workers-binding.ai/run?version=3&userInputs=%7B%7D',
233+
});
234+
}
235+
217236
{
218237
// Test gateway option
219238
const resp = await env.ai.run(

src/cloudflare/internal/test/ai/ai-mock.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,19 @@ export default {
9797
);
9898
}
9999

100+
if (modelName === 'formDataInputs') {
101+
return Response.json(
102+
{
103+
inputs: {},
104+
options: { ...data.options },
105+
requestUrl: request.url,
106+
},
107+
{
108+
headers: respHeaders,
109+
}
110+
);
111+
}
112+
100113
if (modelName === 'inputErrorModel') {
101114
return Response.json(
102115
{

0 commit comments

Comments
 (0)