From a6495e68c9c5e453df5756e6c20521d6017d14ea Mon Sep 17 00:00:00 2001 From: Tom Spencer Date: Wed, 31 Dec 2025 08:20:36 -0800 Subject: [PATCH] Convert local sample images to base64 for Gemini API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Gemini API requires base64-encoded image data, not URL paths. Added convertLocalImagesToBase64() function that reads local images from public/sample-images/ and converts them to data URLs before returning the workflow. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/app/api/quickstart/route.ts | 55 ++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/src/app/api/quickstart/route.ts b/src/app/api/quickstart/route.ts index 145eaee7..0f94de44 100644 --- a/src/app/api/quickstart/route.ts +++ b/src/app/api/quickstart/route.ts @@ -8,9 +8,60 @@ import { repairWorkflowJSON, parseJSONFromResponse, } from "@/lib/quickstart/validation"; +import { ImageInputNodeData } from "@/types"; +import * as fs from "fs"; +import * as path from "path"; export const maxDuration = 60; // 1 minute timeout +/** + * Convert local image paths (e.g., /sample-images/model.jpg) to base64 data URLs + */ +async function convertLocalImagesToBase64(workflow: WorkflowFile): Promise { + const updatedNodes = await Promise.all( + workflow.nodes.map(async (node) => { + if (node.type === "imageInput") { + const data = node.data as ImageInputNodeData; + // Check if image is a local path (starts with /sample-images/) + if (data.image && data.image.startsWith("/sample-images/")) { + try { + // Read file from public folder + const publicPath = path.join(process.cwd(), "public", data.image); + const fileBuffer = fs.readFileSync(publicPath); + const base64 = fileBuffer.toString("base64"); + + // Determine MIME type from extension + const ext = path.extname(data.image).toLowerCase(); + const mimeType = ext === ".png" ? "image/png" + : ext === ".webp" ? "image/webp" + : "image/jpeg"; + + const dataUrl = `data:${mimeType};base64,${base64}`; + + return { + ...node, + data: { + ...data, + image: dataUrl, + }, + }; + } catch (error) { + console.error(`Failed to convert image to base64: ${data.image}`, error); + // Return node unchanged if conversion fails + return node; + } + } + } + return node; + }) + ); + + return { + ...workflow, + nodes: updatedNodes, + }; +} + interface QuickstartRequest { description: string; contentLevel: ContentLevel; @@ -43,10 +94,12 @@ export async function POST(request: NextRequest) { console.log(`[Quickstart:${requestId}] Using preset template: ${templateId}`); try { const workflow = getPresetTemplate(templateId, contentLevel); + // Convert any local image paths to base64 for the Gemini API + const workflowWithBase64 = await convertLocalImagesToBase64(workflow); console.log(`[Quickstart:${requestId}] Preset template loaded successfully`); return NextResponse.json({ success: true, - workflow, + workflow: workflowWithBase64, }); } catch (error) { console.error(`[Quickstart:${requestId}] Preset template error:`, error);