(prompt: string)
| 86 | // The model takes multipart form fields and returns the image as |
| 87 | // a base64 string. |
| 88 | async #runModel(prompt: string): Promise<Uint8Array> { |
| 89 | const form = new FormData(); |
| 90 | form.append("prompt", prompt); |
| 91 | form.append("width", "1024"); |
| 92 | form.append("height", "1024"); |
| 93 | |
| 94 | // FormData doesn't expose its serialized body or boundary. |
| 95 | // Passing it through a Response constructor serializes it and |
| 96 | // sets the multipart Content-Type with the boundary the model |
| 97 | // needs to parse the fields. |
| 98 | const formResponse = new Response(form); |
| 99 | const body = formResponse.body; |
| 100 | const contentType = formResponse.headers.get("content-type"); |
| 101 | if (body === null || contentType === null) { |
| 102 | throw new Error("failed to serialize the model request body"); |
| 103 | } |
| 104 | |
| 105 | // The model's multipart input shape isn't in the generated Ai |
| 106 | // types yet, so call run() through a minimal typed view of the |
| 107 | // binding. Keep the call on env.AI: the binding's run() is a |
| 108 | // method that relies on its own `this`, so a detached reference |
| 109 | // would throw inside the binding. |
| 110 | const ai = this.env.AI as unknown as { |
| 111 | run( |
| 112 | model: string, |
| 113 | input: { multipart: { body: ReadableStream; contentType: string } }, |
| 114 | ): Promise<{ image?: string }>; |
| 115 | }; |
| 116 | const result = await ai.run(IMAGE_MODEL, { multipart: { body, contentType } }); |
| 117 | |
| 118 | if (typeof result.image !== "string") { |
| 119 | throw new Error("image model returned no image"); |
| 120 | } |
| 121 | return decodeBase64(result.image); |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | export default { |
no test coverage detected