---
phase: 03-generate-node-refactor
plan: 02
type: execute
---
Add provider-specific execution logic to the generate API route for image generation.
Purpose: Enable the generate endpoint to route requests to the correct provider (Gemini, Replicate, fal.ai) based on the selected model, filtering to image outputs only.
Output: Updated /api/generate route that dispatches to provider-specific image generation logic.
Note: Video generation will use a separate endpoint in Phase 6.
~/.claude/get-shit-done/workflows/execute-phase.md
~/.claude/get-shit-done/templates/summary.md
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
# Prior plan context:
@.planning/phases/03-generate-node-refactor/03-01-SUMMARY.md
# Key source files:
@src/app/api/generate/route.ts
@src/lib/providers/types.ts
@src/lib/providers/replicate.ts
@src/lib/providers/fal.ts
@src/lib/providers/index.ts
**Tech stack available:** ProviderInterface with generate() method, provider registry
**Established patterns:** API routes return { success, data/error }, provider self-registration
**Constraining decisions:**
- Gemini generation logic already works, preserve it
- Provider implementations have generate() method defined in ProviderInterface
Task 1: Implement generate() method in Replicate provider
src/lib/providers/replicate.ts
1. Implement the generate() method that currently throws "not implemented":
- Accept GenerationInput with model, prompt, images, parameters
- Call Replicate's prediction API: POST https://api.replicate.com/v1/predictions
- Handle input format: { version: model.id, input: { prompt, image?, ...parameters } }
- Poll for completion (Replicate is async) or use webhook pattern
- Return GenerationOutput with image/video data
2. Handle image input:
- If images provided, check if Replicate model expects URLs
- For now, skip image input (Phase 5 adds image URL server)
- Log warning if images provided but skipped
3. Fetch output:
- Poll GET /predictions/{id} until status is "succeeded" or "failed"
- Convert output URL to base64 data URL
- Return in GenerationOutput format with type: "image"
4. Filter to image models only:
- Only accept models with text-to-image or image-to-image capabilities
- Return error if video model passed (video generation is Phase 6)
WHY: Replicate requires polling for async predictions. The generate() method encapsulates this complexity. Image-only filter keeps this endpoint focused.
TypeScript compiles, generate() method exists and doesn't throw immediately
Replicate provider has working generate() method that calls prediction API for image models
Task 2: Implement generate() method in fal.ai provider
src/lib/providers/fal.ts
1. Implement the generate() method:
- Accept GenerationInput with model, prompt, images, parameters
- Call fal.ai's run endpoint: POST https://fal.run/{model.id}
- Include prompt and parameters in request body
- fal.ai is synchronous (waits for result)
2. Handle auth:
- Use "Key {apiKey}" header format (already established)
- API key optional but rate limited without it
3. Parse response:
- Extract image URL from response
- Convert to base64 data URL for consistency
- Handle images array output format
4. Filter to image models only:
- Only accept models with text-to-image or image-to-image capabilities
- Return error if video model passed (video generation is Phase 6)
WHY: fal.ai's synchronous API is simpler than Replicate. The generate() method normalizes the response format. Image-only filter keeps this endpoint focused.
TypeScript compiles, generate() method exists
fal.ai provider has working generate() method that calls fal.run endpoint for image models
Task 3: Update generate API route to dispatch by provider
src/app/api/generate/route.ts
1. Update request type to accept either:
- Legacy format: { model: ModelType, prompt, images, aspectRatio, resolution, useGoogleSearch }
- New format: { selectedModel: SelectedModel, prompt, images, parameters }
2. Add provider dispatch logic:
```typescript
if (body.selectedModel) {
// New multi-provider path
const { provider, modelId } = body.selectedModel;
if (provider === "gemini") {
// Use existing Gemini logic
} else {
// Get provider and call generate()
const providerImpl = getProvider(provider);
const model = await providerImpl.getModel(modelId);
const result = await providerImpl.generate({
model,
prompt: body.prompt,
images: body.images,
parameters: body.parameters
});
return NextResponse.json(result);
}
} else {
// Legacy Gemini path (existing code)
}
```
3. Extract Gemini-specific logic into helper function for clarity
4. Ensure API key handling:
- For Gemini: use GEMINI_API_KEY from env
- For Replicate/fal: get from provider.getApiKey() (localStorage via client header)
5. Add client headers for API keys:
- Accept X-Replicate-API-Key and X-Fal-API-Key headers
- Pass to provider implementations
WHY: The route becomes a dispatcher that preserves existing Gemini behavior while enabling new providers.
npm run build succeeds, existing Gemini generation still works
Generate route dispatches to correct provider, Gemini still works as before
Before declaring plan complete:
- [ ] `npm run build` succeeds without errors
- [ ] Existing Gemini generation works (test with existing workflow)
- [ ] Replicate provider generate() method implemented
- [ ] fal.ai provider generate() method implemented
- [ ] API route accepts both legacy and new request formats
- All tasks completed
- All verification checks pass
- Generate API routes to correct provider based on selectedModel
- Gemini generation unchanged for backward compatibility
- Replicate and fal.ai providers have generate() implementations