From 8a4e98bdab430e70b43c5635bd75ce0a90ef6bd2 Mon Sep 17 00:00:00 2001 From: shrimbly Date: Fri, 2 Jan 2026 22:32:56 +1300 Subject: [PATCH] Improve system prompt for AI workflow builder. --- src/app/api/quickstart/route.ts | 4 +- src/lib/quickstart/prompts.ts | 341 +++++++++++++++++++++++++++----- 2 files changed, 299 insertions(+), 46 deletions(-) diff --git a/src/app/api/quickstart/route.ts b/src/app/api/quickstart/route.ts index 0f94de44..8da0b3f1 100644 --- a/src/app/api/quickstart/route.ts +++ b/src/app/api/quickstart/route.ts @@ -148,11 +148,11 @@ export async function POST(request: NextRequest) { const startTime = Date.now(); const response = await ai.models.generateContent({ - model: "gemini-2.0-flash", + model: "gemini-3-flash-preview", contents: prompt, config: { temperature: 0.3, // Lower for more consistent JSON output - maxOutputTokens: 4096, + maxOutputTokens: 16384, // Increased for complex workflows with many nodes }, }); diff --git a/src/lib/quickstart/prompts.ts b/src/lib/quickstart/prompts.ts index a69fea14..0a52db0b 100644 --- a/src/lib/quickstart/prompts.ts +++ b/src/lib/quickstart/prompts.ts @@ -18,36 +18,39 @@ You MUST output ONLY valid JSON. No explanations, no markdown, no code blocks. J ### 1. imageInput Purpose: Load/display input images from user -- Outputs: "image" handle (green) +- Outputs: "image" handle (green, right side of node) - Data structure: { "image": null, "filename": null, - "dimensions": null + "dimensions": null, + "customTitle": "Descriptive name for this input" } ### 2. prompt Purpose: Text prompts that feed into generation or LLM nodes -- Outputs: "text" handle (blue) +- Outputs: "text" handle (blue, right side of node) - Data structure: { - "prompt": "${contentLevel === "empty" ? "" : contentLevel === "minimal" ? "Enter your prompt here..." : "Your detailed prompt text"}" + "prompt": "${contentLevel === "empty" ? "" : contentLevel === "minimal" ? "Enter your prompt here..." : "Your detailed prompt text"}", + "customTitle": "Descriptive name for this prompt" } ### 3. annotation -Purpose: Draw/annotate on images before generation -- Inputs: "image" handle -- Outputs: "image" handle +Purpose: Draw/annotate on images before generation. Only use this if the user asks for annotation capability. +- Inputs: "image" handle (left side) +- Outputs: "image" handle (right side) - Data structure: { "sourceImage": null, "annotations": [], - "outputImage": null + "outputImage": null, + "customTitle": "Annotation step" } ### 4. nanoBanana -Purpose: AI image generation using Gemini (REQUIRES both image AND text inputs) -- Inputs: "image" handle (accepts multiple), "text" handle (required) +Purpose: AI image generation using Gemini (REQUIRES both image AND text inputs). This is the primary node for image generation. +- Inputs: "image" handle (accepts multiple connections), "text" handle (required) - Outputs: "image" handle - Data structure: { @@ -61,7 +64,8 @@ Purpose: AI image generation using Gemini (REQUIRES both image AND text inputs) "status": "idle", "error": null, "imageHistory": [], - "selectedHistoryIndex": 0 + "selectedHistoryIndex": 0, + "customTitle": "Generation step name" } ### 5. llmGenerate @@ -78,75 +82,323 @@ Purpose: Text generation using LLM (for prompt expansion, analysis, etc.) "temperature": 0.7, "maxTokens": 8192, "status": "idle", - "error": null + "error": null, + "customTitle": "LLM step name" + } + +### 6. splitGrid +Purpose: Split a grid/contact sheet image into individual cells for parallel processing. Use this when the user wants to generate a grid of images and then process each cell separately. +- Inputs: "image" handle (left side) +- Outputs: "reference" handle (connects to child imageInput nodes) +- IMPORTANT: When using splitGrid, you MUST also create the child nodes for each grid cell: + - For each cell: 1 imageInput + 1 prompt + 1 nanoBanana + - For a 2x2 grid (4 cells): create 4 imageInputs, 4 prompts, 4 nanoBananas + - Connect splitGrid → each imageInput via "reference" handles + - Connect each imageInput → its nanoBanana via "image" handles + - Connect each prompt → its nanoBanana via "text" handles +- Data structure: + { + "sourceImage": null, + "targetCount": 4, + "gridRows": 2, + "gridCols": 2, + "defaultPrompt": "Enhance this frame...", + "generateSettings": { + "aspectRatio": "2:3", + "resolution": "2K", + "model": "nano-banana-pro", + "useGoogleSearch": false + }, + "childNodeIds": [ + { "imageInput": "imageInput-10", "prompt": "prompt-10", "nanoBanana": "nanoBanana-10" }, + { "imageInput": "imageInput-11", "prompt": "prompt-11", "nanoBanana": "nanoBanana-11" }, + { "imageInput": "imageInput-12", "prompt": "prompt-12", "nanoBanana": "nanoBanana-12" }, + { "imageInput": "imageInput-13", "prompt": "prompt-13", "nanoBanana": "nanoBanana-13" } + ], + "isConfigured": true, + "status": "idle", + "error": null, + "customTitle": "Split Grid" } -### 6. output -Purpose: Display final generated images -- Inputs: "image" handle +### 7. output +Purpose: Display final generated images (optional - not required) +- Inputs: "image" handle (left side) - Data structure: { - "image": null + "image": null, + "customTitle": "Final output" } -## Connection Rules (VERY IMPORTANT) -1. "image" handles can ONLY connect to "image" handles -2. "text" handles can ONLY connect to "text" handles -3. nanoBanana REQUIRES both an image input AND a text input to work -4. Connections flow from source (output) to target (input) +## EDGES/CONNECTIONS - CRITICAL SECTION + +Edges connect nodes together. Every edge MUST have these fields: + +\`\`\`json +{ + "id": "edge-{sourceNodeId}-{targetNodeId}-{sourceHandle}-{targetHandle}", + "source": "{sourceNodeId}", + "sourceHandle": "{handleType}", + "target": "{targetNodeId}", + "targetHandle": "{handleType}" +} +\`\`\` + +### Connection Rules +1. **Type matching is mandatory**: + - "image" handles connect ONLY to "image" handles + - "text" handles connect ONLY to "text" handles +2. **Direction**: Data flows from source (output, right side) → target (input, left side) +3. **nanoBanana nodes REQUIRE TWO incoming edges**: + - One edge bringing "image" data + - One edge bringing "text" data +4. **Multiple image inputs**: nanoBanana can receive multiple image edges (for multi-image context) + +### Edge Examples + +**Connecting imageInput to nanoBanana (image → image):** +\`\`\`json +{ + "id": "edge-imageInput-1-nanoBanana-1-image-image", + "source": "imageInput-1", + "sourceHandle": "image", + "target": "nanoBanana-1", + "targetHandle": "image" +} +\`\`\` + +**Connecting prompt to nanoBanana (text → text):** +\`\`\`json +{ + "id": "edge-prompt-1-nanoBanana-1-text-text", + "source": "prompt-1", + "sourceHandle": "text", + "target": "nanoBanana-1", + "targetHandle": "text" +} +\`\`\` + +**Chaining nanoBanana to nanoBanana (image → image):** +\`\`\`json +{ + "id": "edge-nanoBanana-1-nanoBanana-2-image-image", + "source": "nanoBanana-1", + "sourceHandle": "image", + "target": "nanoBanana-2", + "targetHandle": "image" +} +\`\`\` + +**Connecting prompt to llmGenerate, then llmGenerate to nanoBanana:** +\`\`\`json +{ + "id": "edge-prompt-1-llmGenerate-1-text-text", + "source": "prompt-1", + "sourceHandle": "text", + "target": "llmGenerate-1", + "targetHandle": "text" +}, +{ + "id": "edge-llmGenerate-1-nanoBanana-1-text-text", + "source": "llmGenerate-1", + "sourceHandle": "text", + "target": "nanoBanana-1", + "targetHandle": "text" +} +\`\`\` + +**Connecting nanoBanana output to splitGrid (for splitting a grid image):** +\`\`\`json +{ + "id": "edge-nanoBanana-1-splitGrid-1-image-image", + "source": "nanoBanana-1", + "sourceHandle": "image", + "target": "splitGrid-1", + "targetHandle": "image" +} +\`\`\` + +**Connecting splitGrid to its child imageInput nodes (reference edges):** +\`\`\`json +{ + "id": "edge-splitGrid-1-imageInput-10-reference-reference", + "source": "splitGrid-1", + "sourceHandle": "reference", + "target": "imageInput-10", + "targetHandle": "reference", + "type": "reference" +} +\`\`\` + +**Connecting child nodes within a splitGrid cell (imageInput + prompt → nanoBanana):** +\`\`\`json +{ + "id": "edge-imageInput-10-nanoBanana-10-image-image", + "source": "imageInput-10", + "sourceHandle": "image", + "target": "nanoBanana-10", + "targetHandle": "image" +}, +{ + "id": "edge-prompt-10-nanoBanana-10-text-text", + "source": "prompt-10", + "sourceHandle": "text", + "target": "nanoBanana-10", + "targetHandle": "text" +} +\`\`\` ## Node Layout Guidelines -- Start input nodes on the left (x: 50-100) -- Flow left to right -- Horizontal spacing: ~400px between columns -- Vertical spacing: ~330px between rows +- Start input nodes on the left (x: 50-150) +- Flow left to right, increasing x position +- Horizontal spacing: ~350-400px between columns +- Vertical spacing: ~300-330px between rows +- Prompt nodes should be positioned near the generation node they feed into - Use these dimensions: - imageInput: { width: 300, height: 280 } - annotation: { width: 300, height: 280 } - prompt: { width: 320, height: 220 } - nanoBanana: { width: 300, height: 300 } - llmGenerate: { width: 320, height: 360 } + - splitGrid: { width: 300, height: 320 } - output: { width: 320, height: 320 } -## Node ID Format -Use format: "{type}-{number}" (e.g., "imageInput-1", "prompt-1", "nanoBanana-1") +## Groups (Optional - for organizing complex workflows) + +Groups visually organize related nodes. Include if the workflow has 4+ nodes: + +\`\`\`json +"groups": { + "group-1": { + "id": "group-1", + "name": "Input Images", + "color": "blue", + "position": { "x": 30, "y": 80 }, + "size": { "width": 360, "height": 600 } + } +} +\`\`\` + +Nodes reference their group via \`"groupId": "group-1"\`. +Available colors: "neutral", "blue", "green", "purple", "orange" -## Edge ID Format -Use format: "edge-{source}-{target}-{sourceHandle}-{targetHandle}" +## Node ID Format +Use format: "{type}-{number}" starting from 1 +Examples: "imageInput-1", "imageInput-2", "prompt-1", "nanoBanana-1" ## Content Level: ${contentLevel.toUpperCase()} ${contentLevel === "empty" ? "- Leave ALL prompt fields completely empty (empty string)" : ""} -${contentLevel === "minimal" ? '- Add brief placeholder prompts like "Describe your style here..." or "Enter your prompt..."' : ""} +${contentLevel === "minimal" ? '- Add brief placeholder prompts like "Describe your scene here..." or "Enter style instructions..."' : ""} ${contentLevel === "full" ? "- Add complete, detailed example prompts that demonstrate the workflow's purpose" : ""} -## User's Request -"${description}" +## COMPLETE EXAMPLE WORKFLOW + +Here is an example of a "Background Swap" workflow that combines a character with a new background: -## Required JSON Structure +\`\`\`json { "version": 1, "id": "wf_${timestamp}_quickstart", - "name": "Descriptive Workflow Name", + "name": "Background Swap", "nodes": [ { - "id": "nodeType-1", - "type": "nodeType", + "id": "imageInput-1", + "type": "imageInput", "position": { "x": 50, "y": 100 }, - "data": { ... }, + "data": { + "image": null, + "filename": null, + "dimensions": null, + "customTitle": "Character" + }, "style": { "width": 300, "height": 280 } + }, + { + "id": "imageInput-2", + "type": "imageInput", + "position": { "x": 50, "y": 420 }, + "data": { + "image": null, + "filename": null, + "dimensions": null, + "customTitle": "New Background" + }, + "style": { "width": 300, "height": 280 } + }, + { + "id": "prompt-1", + "type": "prompt", + "position": { "x": 400, "y": 100 }, + "data": { + "prompt": "Place the character from the first image into the background scene from the second image. Match the lighting and color grading so it looks like a natural photograph. Preserve all details of the character's appearance.", + "customTitle": "Combine Instructions" + }, + "style": { "width": 320, "height": 220 } + }, + { + "id": "nanoBanana-1", + "type": "nanoBanana", + "position": { "x": 780, "y": 200 }, + "data": { + "inputImages": [], + "inputPrompt": null, + "outputImage": null, + "aspectRatio": "1:1", + "resolution": "1K", + "model": "nano-banana-pro", + "useGoogleSearch": false, + "status": "idle", + "error": null, + "imageHistory": [], + "selectedHistoryIndex": 0, + "customTitle": "Generate Composite" + }, + "style": { "width": 300, "height": 300 } } ], "edges": [ { - "id": "edge-source-target-handle-handle", - "source": "sourceNodeId", - "sourceHandle": "image|text", - "target": "targetNodeId", - "targetHandle": "image|text" + "id": "edge-imageInput-1-nanoBanana-1-image-image", + "source": "imageInput-1", + "sourceHandle": "image", + "target": "nanoBanana-1", + "targetHandle": "image" + }, + { + "id": "edge-imageInput-2-nanoBanana-1-image-image", + "source": "imageInput-2", + "sourceHandle": "image", + "target": "nanoBanana-1", + "targetHandle": "image" + }, + { + "id": "edge-prompt-1-nanoBanana-1-text-text", + "source": "prompt-1", + "sourceHandle": "text", + "target": "nanoBanana-1", + "targetHandle": "text" } ], "edgeStyle": "curved" } +\`\`\` + +Notice how: +- Every nanoBanana has BOTH image edge(s) AND a text edge connected to it +- Edge IDs follow the pattern exactly: "edge-{source}-{target}-{sourceHandle}-{targetHandle}" +- Nodes are laid out left-to-right with proper spacing +- customTitle makes each node's purpose clear + +## User's Request +"${description}" + +## CHECKLIST BEFORE OUTPUT +1. ✓ Every nanoBanana node has at least one "image" edge AND one "text" edge targeting it +2. ✓ All edge IDs follow the format: "edge-{source}-{target}-{sourceHandle}-{targetHandle}" +3. ✓ Handle types match: image→image, text→text, reference→reference +4. ✓ Nodes have customTitle fields describing their purpose +5. ✓ Layout flows left-to-right with proper spacing +6. ✓ If using splitGrid: child nodes are created (imageInput + prompt + nanoBanana per cell), childNodeIds array is populated, and reference edges connect splitGrid to each child imageInput Generate a practical, well-organized workflow for: "${description}" @@ -159,13 +411,14 @@ OUTPUT ONLY THE JSON:`; export function buildSimplePrompt(description: string): string { return `Create a Node Banana workflow JSON for: "${description}" -Node types: imageInput (output: image), prompt (output: text), nanoBanana (inputs: image+text, output: image), llmGenerate (input: text, output: text), annotation (input: image, output: image), output (input: image). +Node types: imageInput (output: image), prompt (output: text), nanoBanana (inputs: image+text, output: image), llmGenerate (input: text, output: text), annotation (input: image, output: image), splitGrid (input: image, creates child nodes for each cell), output (input: image). Rules: -- nanoBanana NEEDS both image and text inputs +- nanoBanana NEEDS both image and text inputs - create edges for BOTH - image handles connect to image, text to text - Node IDs: type-number (e.g., imageInput-1) - Edge IDs: edge-source-target-sourceHandle-targetHandle +- Every edge needs: id, source, sourceHandle, target, targetHandle Return ONLY valid JSON with: version:1, name, nodes[], edges[], edgeStyle:"curved"`; }