Skip to content

Commit afeb67b

Browse files
committed
fix: handle cloudflare ReadableStream image type
1 parent 8267029 commit afeb67b

File tree

2 files changed

+47
-18
lines changed

2 files changed

+47
-18
lines changed

src/server/ai/provider/cloudflare.ts

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { inCfWorker } from "@/server/lib/env";
2-
import { buildDataURI } from "@/server/lib/util";
2+
import { base64ToDataURI, readableStreamToDataURI } from "@/server/lib/util";
33
import { getContext } from "@/server/service/context";
44
import type { AiProvider, ApiProviderSettings, ApiProviderSettingsItem } from "../types/provider";
55
import { type ProviderSettingsType, doParseSettings, getProviderSettingsSchema } from "../types/provider";
@@ -54,24 +54,24 @@ const Cloudflare: AiProvider = {
5454
ability: "t2i",
5555
enabledByDefault: true,
5656
},
57-
{
58-
id: "@cf/bytedance/stable-diffusion-xl-lightning",
59-
name: "Stable Diffusion XL Lightning",
60-
ability: "t2i",
61-
enabledByDefault: true,
62-
},
6357
{
6458
id: "@cf/lykon/dreamshaper-8-lcm",
6559
name: "DreamShaper 8 LCM",
6660
ability: "t2i",
6761
enabledByDefault: true,
6862
},
6963
{
70-
id: "@cf/runwayml/stable-diffusion-v1-5-img2img",
71-
name: "Stable Diffusion v1.5 Img2Img",
72-
ability: "i2i",
64+
id: "@cf/bytedance/stable-diffusion-xl-lightning",
65+
name: "Stable Diffusion XL Lightning",
66+
ability: "t2i",
7367
enabledByDefault: true,
7468
},
69+
// {
70+
// id: "@cf/runwayml/stable-diffusion-v1-5-img2img",
71+
// name: "Stable Diffusion v1.5 Img2Img",
72+
// ability: "i2i",
73+
// enabledByDefault: true,
74+
// },
7575
{
7676
id: "@cf/stabilityai/stable-diffusion-xl-base-1.0",
7777
name: "Stable Diffusion XL Base 1.0",
@@ -92,8 +92,14 @@ const Cloudflare: AiProvider = {
9292
prompt: request.prompt,
9393
});
9494

95+
if (resp instanceof ReadableStream) {
96+
return {
97+
images: [await readableStreamToDataURI(resp)],
98+
};
99+
}
100+
95101
return {
96-
images: [buildDataURI(resp.image)],
102+
images: [base64ToDataURI(resp.image)],
97103
};
98104
}
99105

@@ -116,13 +122,13 @@ const Cloudflare: AiProvider = {
116122
if (contentType?.includes("image/png") === true) {
117123
const imageBuffer = await resp.arrayBuffer();
118124
return {
119-
images: [buildDataURI(Buffer.from(imageBuffer).toString("base64"))],
125+
images: [base64ToDataURI(Buffer.from(imageBuffer).toString("base64"))],
120126
};
121127
}
122128

123129
const result = (await resp.json()) as unknown as any;
124130
return {
125-
images: [buildDataURI(result.result.image)],
131+
images: [base64ToDataURI(result.result.image)],
126132
};
127133
},
128134
};

src/server/lib/util.ts

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,35 @@
1-
export function buildDataURI(base64: string, fmt = "png"): string {
1+
export function base64ToDataURI(base64: string, fmt = "png") {
22
return `data:image/${fmt};base64,${base64}`;
33
}
44

5-
export function buildDataURIArray(base64Array: string[], fmt = "png"): string[] {
6-
return base64Array.map((base64) => buildDataURI(base64, fmt));
5+
export async function readableStreamToDataURI(stream: ReadableStream<Uint8Array>, fmt = "png") {
6+
const reader = stream.getReader();
7+
const chunks: Uint8Array[] = [];
8+
9+
while (true) {
10+
const result = await reader.read();
11+
if (result.done) break;
12+
chunks.push(result.value);
13+
}
14+
15+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
16+
const mergedArray = new Uint8Array(totalLength);
17+
let offset = 0;
18+
for (const chunk of chunks) {
19+
mergedArray.set(chunk, offset);
20+
offset += chunk.length;
21+
}
22+
23+
let binaryString = "";
24+
for (let i = 0; i < mergedArray.length; i++) {
25+
binaryString += String.fromCharCode(mergedArray[i]!);
26+
}
27+
const base64 = btoa(binaryString);
28+
29+
return base64ToDataURI(base64, fmt);
730
}
831

9-
export async function fetchUrlToDataURI(url: string): Promise<string> {
32+
export async function fetchUrlToDataURI(url: string) {
1033
const resp = await fetch(url);
1134
if (!resp.ok) {
1235
throw new Error(`Failed to fetch URL: ${url}, status: ${resp.status}`);
@@ -17,5 +40,5 @@ export async function fetchUrlToDataURI(url: string): Promise<string> {
1740
const uint8Array = new Uint8Array(arrayBuffer);
1841
const binaryString = Array.from(uint8Array, (byte) => String.fromCharCode(byte)).join("");
1942
const base64 = btoa(binaryString);
20-
return buildDataURI(base64);
43+
return base64ToDataURI(base64);
2144
}

0 commit comments

Comments
 (0)