Browse Source
Phase 01: Provider Infrastructure - 2 plans created - 5 total tasks defined - Ready for execution Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>handoff-20260429-1057
2 changed files with 388 additions and 0 deletions
@ -0,0 +1,188 @@ |
|||
--- |
|||
phase: 01-provider-infrastructure |
|||
plan: 01 |
|||
type: execute |
|||
--- |
|||
|
|||
<objective> |
|||
Add provider settings UI enabling users to configure Replicate and fal.ai API keys in ProjectSetupModal. |
|||
|
|||
Purpose: Establish the foundation for multi-provider support by allowing users to enable/disable providers and securely store API keys. |
|||
Output: Provider settings tab in ProjectSetupModal with API key inputs for Replicate and fal.ai, persisted to localStorage. |
|||
</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 |
|||
|
|||
**Key files:** |
|||
@src/types/index.ts |
|||
@src/store/workflowStore.ts |
|||
@src/components/ProjectSetupModal.tsx |
|||
|
|||
**Established patterns:** |
|||
- Types defined in `src/types/index.ts` with PascalCase interfaces |
|||
- Settings persisted via localStorage with `node-banana-*` key prefix |
|||
- Zustand store pattern in `workflowStore.ts` with inline actions |
|||
- Modal UI pattern from existing ProjectSetupModal (neutral-* Tailwind colors, consistent spacing) |
|||
|
|||
**Constraints:** |
|||
- Gemini remains default, Replicate/fal.ai are optional add-ons |
|||
- API keys stored in localStorage (client-side only, no server storage) |
|||
- Follow existing naming conventions: `node-banana-provider-settings` |
|||
</context> |
|||
|
|||
<tasks> |
|||
|
|||
<task type="auto"> |
|||
<name>Task 1: Add provider types to types/index.ts</name> |
|||
<files>src/types/index.ts</files> |
|||
<action> |
|||
Add provider-related types at the end of the file: |
|||
|
|||
1. `ProviderType` union: `"gemini" | "replicate" | "fal"` |
|||
2. `ProviderConfig` interface with fields: |
|||
- `id: ProviderType` |
|||
- `name: string` (display name) |
|||
- `enabled: boolean` |
|||
- `apiKey: string | null` |
|||
- `apiKeyEnvVar?: string` (for Gemini which uses env var) |
|||
3. `ProviderSettings` interface with field: |
|||
- `providers: Record<ProviderType, ProviderConfig>` |
|||
|
|||
Keep existing types unchanged. Add new types after `NodeGroup` interface. |
|||
</action> |
|||
<verify>npm run build passes with no TypeScript errors</verify> |
|||
<done>ProviderType, ProviderConfig, and ProviderSettings types exist and are exported</done> |
|||
</task> |
|||
|
|||
<task type="auto"> |
|||
<name>Task 2: Add provider settings state to workflowStore</name> |
|||
<files>src/store/workflowStore.ts</files> |
|||
<action> |
|||
Add provider settings management to the Zustand store: |
|||
|
|||
1. Add storage key constant: `PROVIDER_SETTINGS_KEY = "node-banana-provider-settings"` |
|||
|
|||
2. Add helper functions (after existing localStorage helpers around line 310): |
|||
- `getProviderSettings(): ProviderSettings` - loads from localStorage, returns defaults if not found |
|||
- `saveProviderSettings(settings: ProviderSettings): void` - saves to localStorage |
|||
|
|||
3. Default provider settings (Gemini enabled by default with env var, others disabled): |
|||
```typescript |
|||
const defaultProviderSettings: ProviderSettings = { |
|||
providers: { |
|||
gemini: { id: "gemini", name: "Google Gemini", enabled: true, apiKey: null, apiKeyEnvVar: "GEMINI_API_KEY" }, |
|||
replicate: { id: "replicate", name: "Replicate", enabled: false, apiKey: null }, |
|||
fal: { id: "fal", name: "fal.ai", enabled: false, apiKey: null }, |
|||
} |
|||
}; |
|||
``` |
|||
|
|||
4. Add to store state interface: |
|||
- `providerSettings: ProviderSettings` |
|||
|
|||
5. Add store actions: |
|||
- `updateProviderSettings: (settings: ProviderSettings) => void` - updates state and saves to localStorage |
|||
- `updateProviderApiKey: (providerId: ProviderType, apiKey: string | null) => void` - updates single provider key |
|||
- `toggleProvider: (providerId: ProviderType, enabled: boolean) => void` - enables/disables provider |
|||
|
|||
6. Initialize `providerSettings` in create() using `getProviderSettings()` |
|||
|
|||
Import ProviderType and ProviderSettings from "@/types". |
|||
</action> |
|||
<verify>npm run build passes, store exports new actions</verify> |
|||
<done>Provider settings state and actions exist in workflowStore, persisted to localStorage</done> |
|||
</task> |
|||
|
|||
<task type="auto"> |
|||
<name>Task 3: Add Providers tab to ProjectSetupModal</name> |
|||
<files>src/components/ProjectSetupModal.tsx</files> |
|||
<action> |
|||
Extend ProjectSetupModal with a tabbed interface and Providers settings: |
|||
|
|||
1. Add tab state: `const [activeTab, setActiveTab] = useState<"project" | "providers">("project")` |
|||
|
|||
2. Import provider state from store: |
|||
```typescript |
|||
const { providerSettings, updateProviderApiKey, toggleProvider } = useWorkflowStore(); |
|||
``` |
|||
|
|||
3. Add local state for provider form (to avoid saving on every keystroke): |
|||
```typescript |
|||
const [localProviders, setLocalProviders] = useState(providerSettings); |
|||
``` |
|||
Sync localProviders when modal opens (in useEffect). |
|||
|
|||
4. Add tab bar below the title: |
|||
```tsx |
|||
<div className="flex gap-4 border-b border-neutral-700 mb-4"> |
|||
<button |
|||
onClick={() => setActiveTab("project")} |
|||
className={`pb-2 text-sm ${activeTab === "project" ? "text-neutral-100 border-b-2 border-white" : "text-neutral-400"}`} |
|||
> |
|||
Project |
|||
</button> |
|||
<button |
|||
onClick={() => setActiveTab("providers")} |
|||
className={`pb-2 text-sm ${activeTab === "providers" ? "text-neutral-100 border-b-2 border-white" : "text-neutral-400"}`} |
|||
> |
|||
Providers |
|||
</button> |
|||
</div> |
|||
``` |
|||
|
|||
5. Wrap existing form content in `{activeTab === "project" && (...)}` block. |
|||
|
|||
6. Add Providers tab content `{activeTab === "providers" && (...)}`: |
|||
- Show each provider (Gemini, Replicate, fal.ai) in a card-like section |
|||
- For Gemini: Show "Configured via GEMINI_API_KEY environment variable" (no input field, just info) |
|||
- For Replicate and fal.ai: |
|||
- Toggle switch to enable/disable |
|||
- API key input field (type="password", only shown when enabled) |
|||
- Show/hide password toggle button |
|||
- Use consistent neutral-* Tailwind classes matching existing UI |
|||
|
|||
7. Update handleSave to also save provider settings when on providers tab: |
|||
- Call `updateProviderApiKey` for each changed provider |
|||
- Call `toggleProvider` for each changed enabled state |
|||
|
|||
8. When in "providers" tab and user clicks Save, save provider settings and close modal (no directory validation needed). |
|||
|
|||
Follow existing modal patterns: neutral-800 background, neutral-700 borders, consistent button styling. |
|||
</action> |
|||
<verify>npm run dev, open Project Settings, switch to Providers tab, toggle Replicate on, enter API key, save, reload page, verify settings persisted</verify> |
|||
<done>ProjectSetupModal has Providers tab with API key inputs for Replicate and fal.ai, settings persist across page reloads</done> |
|||
</task> |
|||
|
|||
</tasks> |
|||
|
|||
<verification> |
|||
Before declaring plan complete: |
|||
- [ ] `npm run build` succeeds without errors |
|||
- [ ] `npm run lint` passes |
|||
- [ ] Provider types exported from src/types/index.ts |
|||
- [ ] Provider settings load/save via localStorage |
|||
- [ ] ProjectSetupModal shows Project and Providers tabs |
|||
- [ ] Replicate/fal.ai can be enabled with API keys |
|||
- [ ] Settings persist after page reload |
|||
</verification> |
|||
|
|||
<success_criteria> |
|||
|
|||
- All tasks completed |
|||
- All verification checks pass |
|||
- No TypeScript errors |
|||
- Users can configure Replicate and fal.ai API keys in settings |
|||
- Settings persist to localStorage |
|||
</success_criteria> |
|||
|
|||
<output> |
|||
After completion, create `.planning/phases/01-provider-infrastructure/01-01-SUMMARY.md` |
|||
</output> |
|||
@ -0,0 +1,200 @@ |
|||
--- |
|||
phase: 01-provider-infrastructure |
|||
plan: 02 |
|||
type: execute |
|||
--- |
|||
|
|||
<objective> |
|||
Create provider abstraction layer defining how providers expose model discovery and generation capabilities. |
|||
|
|||
Purpose: Establish the interface contract that all providers (Gemini, Replicate, fal.ai) will implement, enabling uniform access to different AI services. |
|||
Output: Provider abstraction interfaces and types in src/lib/providers/ ready for implementation in Phase 2. |
|||
</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 context:** |
|||
Plan 01-01 added ProviderType, ProviderConfig, and ProviderSettings types. |
|||
|
|||
**Key files:** |
|||
@src/types/index.ts |
|||
@src/app/api/generate/route.ts |
|||
|
|||
**Established patterns:** |
|||
- Feature libraries in `src/lib/{feature}/` (see quickstart/) |
|||
- Types defined with PascalCase interfaces |
|||
- Prefer fetch over SDKs per PROJECT.md constraints |
|||
|
|||
**Constraints:** |
|||
- No hardcoded schemas - model parameters fetched at runtime |
|||
- Fetch over SDKs for bundle size |
|||
- Dynamic model discovery for future-proofing |
|||
</context> |
|||
|
|||
<tasks> |
|||
|
|||
<task type="auto"> |
|||
<name>Task 1: Create provider abstraction interfaces</name> |
|||
<files>src/lib/providers/types.ts</files> |
|||
<action> |
|||
Create `src/lib/providers/` directory and `types.ts` with provider abstractions: |
|||
|
|||
1. `ProviderModel` interface - represents a model from any provider: |
|||
```typescript |
|||
interface ProviderModel { |
|||
id: string; // Provider-specific model ID (e.g., "stability-ai/sdxl") |
|||
name: string; // Display name |
|||
description: string | null; // Model description |
|||
provider: ProviderType; // Which provider this is from |
|||
capabilities: ModelCapability[]; // What it can do |
|||
coverImage?: string; // Thumbnail URL |
|||
pricing?: { // Optional pricing info |
|||
type: "per-run" | "per-second"; |
|||
amount: number; |
|||
currency: string; |
|||
}; |
|||
} |
|||
``` |
|||
|
|||
2. `ModelCapability` union: |
|||
```typescript |
|||
type ModelCapability = "text-to-image" | "image-to-image" | "text-to-video" | "image-to-video"; |
|||
``` |
|||
|
|||
3. `GenerationInput` interface - unified input for all providers: |
|||
```typescript |
|||
interface GenerationInput { |
|||
model: ProviderModel; |
|||
prompt: string; |
|||
images?: string[]; // Base64 data URLs or HTTP URLs |
|||
parameters?: Record<string, unknown>; // Model-specific params |
|||
} |
|||
``` |
|||
|
|||
4. `GenerationOutput` interface: |
|||
```typescript |
|||
interface GenerationOutput { |
|||
success: boolean; |
|||
outputs?: Array<{ |
|||
type: "image" | "video"; |
|||
data: string; // Base64 data URL |
|||
url?: string; // Original URL if applicable |
|||
}>; |
|||
error?: string; |
|||
} |
|||
``` |
|||
|
|||
5. `ProviderInterface` - contract all providers implement: |
|||
```typescript |
|||
interface ProviderInterface { |
|||
id: ProviderType; |
|||
name: string; |
|||
|
|||
// Model discovery |
|||
listModels(): Promise<ProviderModel[]>; |
|||
getModel(modelId: string): Promise<ProviderModel | null>; |
|||
searchModels(query: string): Promise<ProviderModel[]>; |
|||
|
|||
// Generation |
|||
generate(input: GenerationInput): Promise<GenerationOutput>; |
|||
|
|||
// Utilities |
|||
isConfigured(): boolean; // Check if API key is set |
|||
getApiKey(): string | null; |
|||
} |
|||
``` |
|||
|
|||
Import ProviderType from "@/types". |
|||
Export all interfaces and types. |
|||
</action> |
|||
<verify>npm run build passes, types can be imported from "@/lib/providers/types"</verify> |
|||
<done>Provider abstraction interfaces defined in src/lib/providers/types.ts</done> |
|||
</task> |
|||
|
|||
<task type="auto"> |
|||
<name>Task 2: Create provider registry and factory</name> |
|||
<files>src/lib/providers/index.ts</files> |
|||
<action> |
|||
Create provider registry that will be populated by specific provider implementations in Phase 2: |
|||
|
|||
1. Create `src/lib/providers/index.ts` with: |
|||
|
|||
2. Export all types from types.ts: |
|||
```typescript |
|||
export * from "./types"; |
|||
``` |
|||
|
|||
3. Provider registry (empty for now, filled in Phase 2): |
|||
```typescript |
|||
import { ProviderType } from "@/types"; |
|||
import { ProviderInterface } from "./types"; |
|||
|
|||
// Registry populated by provider implementations |
|||
const providerRegistry: Partial<Record<ProviderType, ProviderInterface>> = {}; |
|||
|
|||
export function registerProvider(provider: ProviderInterface): void { |
|||
providerRegistry[provider.id] = provider; |
|||
} |
|||
|
|||
export function getProvider(id: ProviderType): ProviderInterface | undefined { |
|||
return providerRegistry[id]; |
|||
} |
|||
|
|||
export function getConfiguredProviders(): ProviderInterface[] { |
|||
return Object.values(providerRegistry).filter( |
|||
(p): p is ProviderInterface => p !== undefined && p.isConfigured() |
|||
); |
|||
} |
|||
|
|||
export function getAllProviders(): ProviderInterface[] { |
|||
return Object.values(providerRegistry).filter( |
|||
(p): p is ProviderInterface => p !== undefined |
|||
); |
|||
} |
|||
``` |
|||
|
|||
4. Add JSDoc comments explaining: |
|||
- Registry will be populated when provider modules are imported |
|||
- Phase 2 will add Replicate and fal.ai implementations |
|||
- Gemini implementation is special-cased in existing generate route for now |
|||
|
|||
This creates the foundation without implementing actual providers yet. |
|||
</action> |
|||
<verify>npm run build passes, registry functions can be imported from "@/lib/providers"</verify> |
|||
<done>Provider registry and factory functions exist in src/lib/providers/index.ts</done> |
|||
</task> |
|||
|
|||
</tasks> |
|||
|
|||
<verification> |
|||
Before declaring plan complete: |
|||
- [ ] `npm run build` succeeds without errors |
|||
- [ ] `npm run lint` passes |
|||
- [ ] src/lib/providers/types.ts exists with all interfaces |
|||
- [ ] src/lib/providers/index.ts exports types and registry functions |
|||
- [ ] Types can be imported: `import { ProviderModel, ProviderInterface } from "@/lib/providers"` |
|||
</verification> |
|||
|
|||
<success_criteria> |
|||
|
|||
- All tasks completed |
|||
- All verification checks pass |
|||
- No TypeScript errors |
|||
- Provider abstraction layer ready for Phase 2 implementations |
|||
- Phase 1 complete |
|||
</success_criteria> |
|||
|
|||
<output> |
|||
After completion, create `.planning/phases/01-provider-infrastructure/01-02-SUMMARY.md` |
|||
|
|||
Include in summary: |
|||
- Phase 1 complete, ready for Phase 2: Model Discovery |
|||
</output> |
|||
Loading…
Reference in new issue