6.4 KiB
| phase | plan | type |
|---|---|---|
| 21-fix-image-input-deduplication | 1 | execute |
Purpose: Ensure image-to-image generation works correctly with Gemini and that the deduplication system consistently prevents duplicate files in the generations folder. Output: Working image-to-image generation with Gemini Pro, consistent hashing across all image saving operations.
<execution_context> ~/.claude/get-shit-done/workflows/execute-phase.md ~/.claude/get-shit-done/templates/summary.md ~/.claude/get-shit-done/references/checkpoints.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.mdKey source files:
@src/app/api/generate/route.ts @src/app/api/save-generation/route.ts @src/utils/imageStorage.ts
Prior phase with related hashing decision:
@.planning/phases/13-fix-duplicate-generations/13-01-SUMMARY.md
Tech stack available: Next.js 16, Zustand, @google/genai SDK Established patterns: MD5 content hashing for deduplication (Phase 13)
Issues being addressed:
- nano-banana-pro model generates without considering image inputs (image data sent but not used)
- Input and generated images have duplicate files despite different hashes - need consistent hashing approach
- Generated images should be prepended with prompt details like generated videos
Research findings:
- Gemini SDK docs show that for image editing, the order matters: image should come BEFORE text in the parts array
- Current implementation sends [text, ...images] but should send [...images, text] for image-to-image
- imageStorage.ts uses position-based sampling hash while save-generation uses MD5 - these are incompatible
Current code (around line 59):
const requestParts = [
{ text: prompt }, // Text first - WRONG for image-to-image
...imageData.map(...) // Images second
];
Change to:
const requestParts = [
...imageData.map(...) // Images first - correct for image editing
{ text: prompt }, // Text second
];
WHY: Gemini's image generation models treat the first content type as primary input. For text-to-image, text should be first. For image-to-image (editing), images should be first so the model knows it's modifying existing images.
IMPORTANT: Only reorder when images are present. If no images, text should still be the only part.
Build succeeds with npm run build. Manual test: Create workflow with ImageInput → GenerateImage (nano-banana-pro) → Output, connect an image, add prompt like "make it blue", run workflow. Output should visually reference the input image.
When images are connected to a Gemini GenerateImage node, the generated output incorporates/transforms the input image based on the prompt.
The current approach at line 209-211 uses a weak hash based on string length and sampled substrings:
const hash = `${folder}-${len}-${imageData.substring(50, 100)}-${imageData.substring(mid, mid + 50)}-${imageData.substring(Math.max(0, len - 50))}`;
This is problematic because:
- Different hash method than save-generation API (MD5)
- Same image can get different hashes if saved via different paths
- Position-based sampling is weak for dedup (similar images may have same samples)
Replace with:
- Import crypto at top:
import crypto from "crypto"; - Create helper:
function computeContentHash(data: string): string { return crypto.createHash("md5").update(data).digest("hex"); } - Replace the hash computation:
const hash =${folder}-${computeContentHash(imageData)};(folder prefix still needed to separate inputs/generations)
WHY: MD5 is already used in save-generation (Phase 13 decision), provides reliable deduplication, and is fast enough for our use case.
Build succeeds with npm run build. TypeScript types check with no errors.
imageStorage.ts uses MD5 hashing, consistent with save-generation API. Same image saved twice gets same hash and is deduplicated.
<success_criteria>
- All tasks completed
- All verification checks pass
- Image-to-image generation with nano-banana-pro produces visually related output
- Deduplication uses consistent MD5 hashing across all image save paths
- No regression in text-to-image generation (still works when no image connected) </success_criteria>