feat: support reference images via /images/edits
This commit is contained in:
@@ -2,6 +2,61 @@ import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
||||
import { generateImage } from "ai";
|
||||
import index from "./index.html";
|
||||
|
||||
type Size = `${number}x${number}`;
|
||||
|
||||
async function generateWithReference({
|
||||
baseURL,
|
||||
apiKey,
|
||||
model,
|
||||
prompt,
|
||||
size,
|
||||
referenceImages,
|
||||
}: {
|
||||
baseURL: string;
|
||||
apiKey: string;
|
||||
model: string;
|
||||
prompt: string;
|
||||
size: Size;
|
||||
referenceImages: string[];
|
||||
}): Promise<string[]> {
|
||||
const form = new FormData();
|
||||
form.append("model", model);
|
||||
form.append("prompt", prompt);
|
||||
form.append("size", size);
|
||||
|
||||
for (let i = 0; i < referenceImages.length; i++) {
|
||||
const dataUrl = referenceImages[i];
|
||||
if (!dataUrl) continue;
|
||||
const match = dataUrl.match(/^data:([^;]+);base64,(.+)$/);
|
||||
if (!match) continue;
|
||||
const mime = match[1]!;
|
||||
const b64 = match[2]!;
|
||||
const bytes = Buffer.from(b64, "base64");
|
||||
const ext = mime.split("/")[1] ?? "png";
|
||||
form.append("image", new Blob([bytes], { type: mime }), `ref-${i}.${ext}`);
|
||||
}
|
||||
|
||||
const url = `${baseURL.replace(/\/+$/, "")}/images/edits`;
|
||||
const res = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: { Authorization: `Bearer ${apiKey}` },
|
||||
body: form,
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const text = await res.text().catch(() => "");
|
||||
throw new Error(`Upstream ${res.status}: ${text || res.statusText}`);
|
||||
}
|
||||
|
||||
const data = (await res.json()) as {
|
||||
data?: Array<{ b64_json?: string }>;
|
||||
};
|
||||
|
||||
return (data.data ?? [])
|
||||
.map((item) => (item.b64_json ? `data:image/png;base64,${item.b64_json}` : null))
|
||||
.filter((s): s is string => s !== null);
|
||||
}
|
||||
|
||||
const server = Bun.serve({
|
||||
hostname: "0.0.0.0",
|
||||
routes: {
|
||||
@@ -9,13 +64,15 @@ const server = Bun.serve({
|
||||
"/api/generate": {
|
||||
POST: async (req) => {
|
||||
try {
|
||||
const { baseURL, apiKey, model, prompt, size } = (await req.json()) as {
|
||||
baseURL?: string;
|
||||
apiKey?: string;
|
||||
model?: string;
|
||||
prompt?: string;
|
||||
size?: `${number}x${number}`;
|
||||
};
|
||||
const { baseURL, apiKey, model, prompt, size, referenceImages } =
|
||||
(await req.json()) as {
|
||||
baseURL?: string;
|
||||
apiKey?: string;
|
||||
model?: string;
|
||||
prompt?: string;
|
||||
size?: Size;
|
||||
referenceImages?: string[];
|
||||
};
|
||||
|
||||
if (!baseURL || !apiKey || !model || !prompt) {
|
||||
return Response.json(
|
||||
@@ -24,6 +81,18 @@ const server = Bun.serve({
|
||||
);
|
||||
}
|
||||
|
||||
if (Array.isArray(referenceImages) && referenceImages.length > 0) {
|
||||
const images = await generateWithReference({
|
||||
baseURL,
|
||||
apiKey,
|
||||
model,
|
||||
prompt,
|
||||
size: size ?? "1024x1024",
|
||||
referenceImages,
|
||||
});
|
||||
return Response.json({ images });
|
||||
}
|
||||
|
||||
const provider = createOpenAICompatible({
|
||||
name: "custom",
|
||||
apiKey,
|
||||
|
||||
Reference in New Issue
Block a user