Browse Source

docs(05): create phase plan for image URL server

Phase 05: Image URL Server
- 2 plans created
- 6 total tasks defined
- Request-scoped cleanup (no TTL) to prevent memory accumulation
- Ready for execution
handoff-20260429-1057
shrimbly 6 months ago
parent
commit
fc10173490
  1. 123
      .planning/phases/05-image-url-server/05-01-PLAN.md
  2. 134
      .planning/phases/05-image-url-server/05-02-PLAN.md

123
.planning/phases/05-image-url-server/05-01-PLAN.md

@ -0,0 +1,123 @@
---
phase: 05-image-url-server
plan: 01
type: execute
---
<objective>
Create local image serving infrastructure for URL-based provider inputs.
Purpose: Enable providers like Replicate and fal.ai to receive images as URLs instead of large base64 payloads, improving reliability for larger images and meeting provider API requirements.
Output: In-memory image store, serving endpoint, and utility function for URL generation.
</objective>
<execution_context>
~/.claude/get-shit-done/workflows/execute-phase.md
~/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
# Prior phase context:
@.planning/phases/03-generate-node-refactor/03-02-SUMMARY.md
# Relevant source files:
@src/app/api/generate/route.ts
@src/lib/providers/cache.ts
**Tech stack available:** Next.js API routes, in-memory caching pattern from providers/cache.ts
**Established patterns:** TTL-based caching, API route structure
**Constraining decisions:**
- Phase 03-02: Server-side provider execution, not client-side
- Phase 02-03: 10-minute cache TTL pattern established
**Provider requirements (from discovery):**
- Replicate: Accepts data URIs but recommends URLs for files >256KB. Parameter name varies by model (`image`, `input_image`, etc.)
- fal.ai: Accepts both URLs and data URIs via `image_url` parameter
</context>
<tasks>
<task type="auto">
<name>Task 1: Create in-memory image store with explicit cleanup</name>
<files>src/lib/images/store.ts</files>
<action>
Create an in-memory store for temporary image hosting with request-scoped lifecycle:
- Store structure: Map<string, { data: Buffer, mimeType: string }>
- `storeImage(base64DataUrl: string): string` - stores image, returns unique ID (use crypto.randomUUID())
- `getImage(id: string): { data: Buffer, mimeType: string } | null` - retrieves image by ID
- `deleteImage(id: string): boolean` - removes image, returns true if existed
- `deleteImages(ids: string[]): void` - bulk delete for cleanup
No TTL - callers are responsible for cleanup after use. This prevents memory accumulation since images are deleted immediately after provider fetches them.
Parse base64 data URL format: `data:{mimeType};base64,{data}` to extract mimeType and Buffer.
Export store functions.
</action>
<verify>TypeScript compiles without errors: `npx tsc --noEmit`</verify>
<done>Image store module exists with storeImage, getImage, deleteImage, deleteImages exports</done>
</task>
<task type="auto">
<name>Task 2: Create image serving API endpoint</name>
<files>src/app/api/images/[id]/route.ts</files>
<action>
Create Next.js API route to serve stored images:
- GET handler that extracts `id` from params
- Call `getImage(id)` from store
- If not found: return 404 with JSON error
- If found: return image with correct Content-Type header and binary data
- Set Cache-Control: no-store (images are temporary)
Route structure follows Next.js App Router dynamic route pattern.
Import getImage from @/lib/images/store.
</action>
<verify>Build succeeds: `npm run build` (route compiles correctly)</verify>
<done>GET /api/images/[id] returns stored images or 404</done>
</task>
<task type="auto">
<name>Task 3: Create uploadImageForUrl utility</name>
<files>src/lib/images/index.ts</files>
<action>
Create utility function that stores an image and returns its URL plus ID for cleanup:
- `uploadImageForUrl(base64DataUrl: string, baseUrl: string): { url: string, id: string }`
- Calls storeImage() to store the image
- Returns full URL `${baseUrl}/api/images/${id}` AND the id for later cleanup
- Export from index.ts along with re-exports from store.ts
The baseUrl parameter allows the caller to provide the server's base URL. Returning the ID enables callers to delete the image after use.
Also create helper to detect if an image should use URL (size threshold):
- `shouldUseImageUrl(base64DataUrl: string): boolean`
- Returns true if base64 data exceeds 256KB (Replicate's recommendation threshold)
- This allows callers to decide whether to use URL or pass base64 directly
</action>
<verify>TypeScript compiles: `npx tsc --noEmit`</verify>
<done>uploadImageForUrl (returns url+id) and shouldUseImageUrl functions exported from @/lib/images</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] `npm run build` succeeds without errors
- [ ] `npx tsc --noEmit` passes
- [ ] Files exist: src/lib/images/store.ts, src/lib/images/index.ts, src/app/api/images/[id]/route.ts
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- No TypeScript errors
- Image store, serving endpoint, and utility functions ready for integration
</success_criteria>
<output>
After completion, create `.planning/phases/05-image-url-server/05-01-SUMMARY.md`
</output>

134
.planning/phases/05-image-url-server/05-02-PLAN.md

@ -0,0 +1,134 @@
---
phase: 05-image-url-server
plan: 02
type: execute
---
<objective>
Integrate image URL serving with Replicate and fal.ai providers.
Purpose: Enable img2img workflows by passing input images to providers as URLs, completing the multi-provider image generation pipeline.
Output: Working img2img support for Replicate and fal.ai models.
</objective>
<execution_context>
~/.claude/get-shit-done/workflows/execute-phase.md
~/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
# Prior plan in this phase:
@.planning/phases/05-image-url-server/05-01-SUMMARY.md
# Relevant source files:
@src/app/api/generate/route.ts
@src/lib/images/index.ts
@src/store/workflowStore.ts
**Tech stack available:** Image URL utilities from 05-01, provider dispatch pattern in generate route
**Established patterns:** Header-based API key passing, server-side provider execution
**Provider image parameter requirements:**
- Replicate: Parameter name varies by model (commonly `image`, `input_image`). Format: URI (URL or data URI).
- fal.ai: Parameter is `image_url`. Format: URL string or data URI.
</context>
<tasks>
<task type="auto">
<name>Task 1: Update generateWithReplicate to pass image input</name>
<files>src/app/api/generate/route.ts</files>
<action>
Modify generateWithReplicate() to include image input in prediction:
- Accept images array in GenerationInput (already present)
- If images array has items, add first image to predictionInput as `image` parameter
- Use the image URL directly (caller will provide URL or data URI)
- Log image input presence for debugging
Note: Different Replicate models use different parameter names (`image`, `input_image`, `image_prompt`). Start with `image` as it's most common for img2img models. Future enhancement could detect from model schema.
Do NOT convert base64 to URL here - that happens at the call site (workflow execution).
</action>
<verify>TypeScript compiles: `npx tsc --noEmit`</verify>
<done>generateWithReplicate passes image parameter to prediction input when images provided</done>
</task>
<task type="auto">
<name>Task 2: Update generateWithFal to pass image_url parameter</name>
<files>src/app/api/generate/route.ts</files>
<action>
Modify generateWithFal() to include image_url in request:
- Accept images array in GenerationInput (already present)
- If images array has items, add first image as `image_url` in requestBody
- fal.ai accepts both URLs and data URIs in this field
- Log image input presence for debugging
fal.ai is consistent: always use `image_url` parameter name.
</action>
<verify>TypeScript compiles: `npx tsc --noEmit`</verify>
<done>generateWithFal passes image_url parameter when images provided</done>
</task>
<task type="auto">
<name>Task 3: Upload images to URL server with cleanup</name>
<files>src/app/api/generate/route.ts</files>
<action>
Update the POST handler to convert large images to URLs and clean up after use:
- Import uploadImageForUrl, shouldUseImageUrl, deleteImages from @/lib/images
- Get base URL from request: `new URL(request.url).origin`
- For Replicate and fal providers, process images array:
- Track uploaded image IDs in an array for cleanup
- If shouldUseImageUrl(image) returns true, call uploadImageForUrl(image, baseUrl), store ID
- Otherwise, pass the base64 data URI directly (small images work fine as data URIs)
- Pass processed images array to generateWithReplicate/generateWithFal
- In finally block: call deleteImages(uploadedIds) to clean up stored images
Pattern:
```typescript
const uploadedImageIds: string[] = [];
try {
// Process images, track IDs
// Call provider
} finally {
deleteImages(uploadedImageIds);
}
```
This ensures:
- Small images (<256KB) use efficient data URIs
- Large images use temporary URLs served by /api/images/[id]
- Images cleaned up immediately after provider fetches them (success or failure)
- No memory accumulation
- Gemini path unchanged (it handles base64 directly)
</action>
<verify>Build succeeds: `npm run build`</verify>
<done>Large images converted to URLs and cleaned up after provider call completes</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] `npm run build` succeeds without errors
- [ ] `npx tsc --noEmit` passes
- [ ] generateWithReplicate includes image in prediction input
- [ ] generateWithFal includes image_url in request body
- [ ] Large images converted to URLs before provider calls
</verification>
<success_criteria>
- All tasks completed
- All verification checks pass
- No TypeScript errors
- Replicate and fal.ai can receive image inputs for img2img workflows
- Phase 5 complete
</success_criteria>
<output>
After completion, create `.planning/phases/05-image-url-server/05-02-SUMMARY.md`
</output>
Loading…
Cancel
Save