diff --git a/.planning/phases/03-generate-node-refactor/03-01-PLAN.md b/.planning/phases/03-generate-node-refactor/03-01-PLAN.md new file mode 100644 index 00000000..4a923ae8 --- /dev/null +++ b/.planning/phases/03-generate-node-refactor/03-01-PLAN.md @@ -0,0 +1,143 @@ +--- +phase: 03-generate-node-refactor +plan: 01 +type: execute +--- + + +Rename NanoBanana node to Generate and add multi-provider model selector. + +Purpose: Transform the hardcoded Gemini-only node into a generic generation node that can use any provider's models. +Output: Generate node component with provider/model dropdown, updated types, store functions renamed. + + + +~/.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 phase context: +@.planning/phases/01-provider-infrastructure/01-02-SUMMARY.md +@.planning/phases/02-model-discovery/02-03-SUMMARY.md + +# Key source files: +@src/components/nodes/NanoBananaNode.tsx +@src/types/index.ts +@src/store/workflowStore.ts +@src/lib/providers/types.ts + +**Tech stack available:** Zustand, React, ProviderModel interface, /api/models endpoint +**Established patterns:** Provider self-registration, localStorage settings persistence +**Constraining decisions:** +- Keep nanoBanana node type internally for backward compatibility +- Gemini models use internal names (nano-banana, nano-banana-pro) + + + + + + Task 1: Update types for multi-provider model selection + src/types/index.ts + + 1. Add new types for selected model: + - `SelectedModel` interface with { provider: ProviderType, modelId: string, displayName: string } + - Keep existing ModelType for backward compatibility + + 2. Update NanoBananaNodeData interface: + - Add optional `selectedModel?: SelectedModel` field + - Keep existing `model: ModelType` field for Gemini backward compatibility + + 3. Add "gemini" to ProviderType union if not already present (it should be the Gemini provider identifier) + + WHY: NanoBananaNodeData needs to store either legacy Gemini model selection OR new multi-provider selection. The optional selectedModel field enables graceful migration. + + npx tsc --noEmit passes + Types compile, SelectedModel interface exists, NanoBananaNodeData has selectedModel field + + + + Task 2: Rename component to GenerateNode with model selector UI + src/components/nodes/NanoBananaNode.tsx, src/components/nodes/index.ts + + 1. Rename file from NanoBananaNode.tsx to GenerateNode.tsx + + 2. Rename component from NanoBananaNode to GenerateNode (keep function export name) + + 3. Add model selector UI that shows: + - Provider dropdown (gemini, replicate, fal) - only show enabled providers + - Model dropdown (fetched from /api/models for selected provider) + - For Gemini, show existing hardcoded models (Nano Banana, Nano Banana Pro) + - For other providers, fetch models dynamically + + 4. When provider/model changes: + - Update node data with selectedModel: { provider, modelId, displayName } + - For Gemini, also update legacy model field for backward compatibility + + 5. Keep existing aspect ratio/resolution controls for Gemini models + - Hide them for non-Gemini models (parameters will come from provider schema later) + + 6. Update index.ts to export GenerateNode and alias it as NanoBananaNode for backward compatibility: + ```typescript + export { GenerateNode } from "./GenerateNode"; + export { GenerateNode as NanoBananaNode } from "./GenerateNode"; + ``` + + WHY: The component rename reflects its new multi-provider purpose. The alias maintains backward compatibility with existing imports. + + npm run build succeeds, component renders in browser + GenerateNode.tsx exists, shows provider selector, model dropdown fetches from API for non-Gemini providers + + + + Task 3: Update store and canvas references + src/store/workflowStore.ts, src/components/WorkflowCanvas.tsx + + 1. In workflowStore.ts: + - Rename saveNanoBananaDefaults to saveGenerateDefaults (keep old name as alias) + - Rename loadNanoBananaDefaults to loadGenerateDefaults (keep old name as alias) + - Update localStorage key comment but keep actual key for migration + - Update createDefaultNodeData for "nanoBanana" case to include default selectedModel for Gemini + + 2. In WorkflowCanvas.tsx: + - Update nodeTypes registration to use GenerateNode + - Keep "nanoBanana" as the type key (internal type name unchanged) + - Update minimap color mapping if it references "nanoBanana" + + 3. In any file using NanoBananaNode import: + - Update import to use GenerateNode + - Files: ConnectionDropMenu.tsx, FloatingActionBar.tsx, etc. + + WHY: Internal references update to new names while keeping the node type string "nanoBanana" for workflow file compatibility. + + npm run build succeeds, existing workflows load correctly + All imports updated, store functions renamed with aliases, workflows still load + + + + + +Before declaring plan complete: +- [ ] `npm run build` succeeds without errors +- [ ] `npx tsc --noEmit` passes type checking +- [ ] GenerateNode renders with provider dropdown +- [ ] Gemini models show in model selector +- [ ] Existing workflows with nanoBanana nodes still load + + + + +- All tasks completed +- All verification checks pass +- GenerateNode component exists with multi-provider UI +- Types support both legacy Gemini and new multi-provider selection +- Backward compatibility maintained for existing workflows + + + +After completion, create `.planning/phases/03-generate-node-refactor/03-01-SUMMARY.md` + diff --git a/.planning/phases/03-generate-node-refactor/03-02-PLAN.md b/.planning/phases/03-generate-node-refactor/03-02-PLAN.md new file mode 100644 index 00000000..4954f2c1 --- /dev/null +++ b/.planning/phases/03-generate-node-refactor/03-02-PLAN.md @@ -0,0 +1,165 @@ +--- +phase: 03-generate-node-refactor +plan: 02 +type: execute +--- + + +Add provider-specific execution logic to the generate API route. + +Purpose: Enable the generate endpoint to route requests to the correct provider (Gemini, Replicate, fal.ai) based on the selected model. +Output: Updated /api/generate route that dispatches to provider-specific generation logic. + + + +~/.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 + + WHY: Replicate requires polling for async predictions. The generate() method encapsulates this complexity. + + TypeScript compiles, generate() method exists and doesn't throw immediately + Replicate provider has working generate() method that calls prediction API + + + + 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/video URL from response + - Convert to base64 data URL for consistency + - Handle different output formats (images array, video URL, etc.) + + WHY: fal.ai's synchronous API is simpler than Replicate. The generate() method normalizes the response format. + + TypeScript compiles, generate() method exists + fal.ai provider has working generate() method that calls fal.run endpoint + + + + 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 + + + +After completion, create `.planning/phases/03-generate-node-refactor/03-02-SUMMARY.md` + diff --git a/.planning/phases/03-generate-node-refactor/03-03-PLAN.md b/.planning/phases/03-generate-node-refactor/03-03-PLAN.md new file mode 100644 index 00000000..da0d89ad --- /dev/null +++ b/.planning/phases/03-generate-node-refactor/03-03-PLAN.md @@ -0,0 +1,154 @@ +--- +phase: 03-generate-node-refactor +plan: 03 +type: execute +--- + + +Ensure backward compatibility for existing workflows containing nanoBanana nodes. + +Purpose: Existing saved workflows must load and execute correctly without modification. +Output: Migration logic, verified backward compatibility, updated workflow execution. + + + +~/.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 +@.planning/phases/03-generate-node-refactor/03-02-SUMMARY.md + +# Key source files: +@src/store/workflowStore.ts +@src/types/index.ts +@src/components/nodes/GenerateNode.tsx + +**Tech stack available:** Zustand, workflow JSON persistence +**Established patterns:** Workflow load/save in workflowStore +**Constraining decisions:** +- Node type "nanoBanana" must remain unchanged in workflow files +- Legacy ModelType ("nano-banana", "nano-banana-pro") must still work + + + + + + Task 1: Add migration logic for loaded workflows + src/store/workflowStore.ts + + 1. In loadWorkflow() or where workflow JSON is parsed, add migration: + - When loading a nanoBanana node without selectedModel field + - Automatically set selectedModel based on legacy model field: + ```typescript + if (node.type === "nanoBanana" && node.data.model && !node.data.selectedModel) { + node.data.selectedModel = { + provider: "gemini", + modelId: node.data.model, + displayName: node.data.model === "nano-banana" ? "Nano Banana" : "Nano Banana Pro" + }; + } + ``` + + 2. Keep legacy model field synced for saves: + - When selectedModel is Gemini, also update the model field + - This ensures old versions can still read the workflow + + 3. Add version field to workflow if not present: + - Set workflowVersion in saved JSON for future migrations + - Not strictly necessary but good practice + + WHY: Migration on load allows old workflows to work without manual editing. Syncing both fields maintains bidirectional compatibility. + + Load an existing workflow file, check node data has both model and selectedModel + Old workflows load with selectedModel populated, new saves include both fields + + + + Task 2: Update executeWorkflow for multi-provider generation + src/store/workflowStore.ts + + 1. In executeWorkflow()'s nanoBanana case: + - Check if node has selectedModel + - If selectedModel exists and provider !== "gemini", pass API keys in headers: + ```typescript + const headers: Record = { + "Content-Type": "application/json" + }; + if (nodeData.selectedModel?.provider === "replicate") { + const settings = get().providerSettings; + const replicateConfig = settings.providers.find(p => p.id === "replicate"); + if (replicateConfig?.apiKey) { + headers["X-Replicate-API-Key"] = replicateConfig.apiKey; + } + } + // Similar for fal + ``` + + 2. Build request payload: + - Include selectedModel in request body for new providers + - Keep existing fields for Gemini backward compatibility + + 3. Handle response format: + - Provider generate() returns GenerationOutput with outputs array + - Extract first image/video output as outputImage + - For video outputs, store separately (Phase 6 handles video display) + + WHY: The store's execution logic needs to pass provider API keys (stored client-side) to the server route. + + Execute workflow with Gemini model, verify generation still works + Workflow execution handles both legacy and new model selection formats + + + + Task 3: Update GenerateNode to handle legacy data + src/components/nodes/GenerateNode.tsx + + 1. When component mounts or data changes: + - If model exists but not selectedModel, derive selectedModel from model + - Display the correct provider/model in dropdowns + + 2. Ensure UI state reflects actual node data: + - Provider dropdown shows "gemini" for legacy nodes + - Model dropdown shows correct Gemini model name + + 3. When saving defaults, save in both formats: + - saveGenerateDefaults should save both model and selectedModel + - Ensures new nodes get correct defaults + + WHY: The UI must correctly display legacy nodes that were migrated on load. + + Open workflow with old nanoBanana node, verify dropdowns show correct selection + GenerateNode displays correct provider/model for both legacy and new nodes + + + + + +Before declaring plan complete: +- [ ] `npm run build` succeeds without errors +- [ ] Load existing workflow with nanoBanana nodes - nodes display correctly +- [ ] Execute existing workflow - Gemini generation works +- [ ] Create new Generate node - defaults are correct +- [ ] Save and reload workflow - data persists correctly + + + + +- All tasks completed +- All verification checks pass +- Existing workflows load and execute without errors +- Migration logic populates selectedModel from legacy model field +- Both old and new format nodes work in same workflow +- Phase 3 complete + + + +After completion, create `.planning/phases/03-generate-node-refactor/03-03-SUMMARY.md` +