const OPENROUTER_URL = "https://openrouter.ai/api/v1/chat/completions"; function toDataUrlMaybe(item) { if (!item) return null; if (typeof item === "string") { if (item.startsWith("http://") || item.startsWith("https://") || item.startsWith("data:image/")) return item; return `data:image/png;base64,${item}`; } if (item.url) return item.url; if (item.image_url?.url) return item.image_url.url; if (item.imageUrl?.url) return item.imageUrl.url; if (item.b64_json) return `data:image/png;base64,${item.b64_json}`; if (item.data) return `data:image/png;base64,${item.data}`; return null; } function extractImageFromResponse(json) { const msg = json?.choices?.[0]?.message; if (!msg) return null; // Primary docs format: message.images[].image_url.url if (Array.isArray(msg.images) && msg.images.length) { for (const image of msg.images) { const url = image?.image_url?.url || image?.imageUrl?.url || image?.url; if (url) return url; const maybe = toDataUrlMaybe(image); if (maybe) return maybe; } } // Fallback: content parts if (Array.isArray(msg.content)) { for (const part of msg.content) { if (part?.type === "image_url") { const u = part?.image_url?.url || part?.imageUrl?.url || part?.url; if (u) return u; } if (part?.type === "output_image" && part?.image_url?.url) return part.image_url.url; } } // Extra fallback pools const fallbackPools = [json?.images, json?.data, msg?.data, json?.choices?.[0]?.images]; for (const pool of fallbackPools) { if (Array.isArray(pool) && pool.length) { const maybe = toDataUrlMaybe(pool[0]); if (maybe) return maybe; } } return null; } export async function generateImageFrame({ apiKey, model, textPrompt, previousFrames = [], imageSize = "1K", aspectRatio = "1:1" }) { // Docs recommend text first, then images in content array const content = [{ type: "text", text: textPrompt }]; for (const frame of previousFrames.slice(-2)) { content.push({ type: "image_url", image_url: { url: frame } }); } const body = { model, modalities: ["image"], // As requested: image only messages: [{ role: "user", content }], image_config: { image_size: imageSize, aspect_ratio: aspectRatio }, stream: false }; const res = await fetch(OPENROUTER_URL, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}`, "HTTP-Referer": window.location.origin, "X-Title": "vibegif.lol" }, body: JSON.stringify(body) }); const json = await res.json().catch(() => ({})); if (!res.ok) { const msg = json?.error?.message || `OpenRouter error (${res.status})`; throw new Error(msg); } const image = extractImageFromResponse(json); if (!image) throw new Error("No image found in model response."); return image; }