Browse Source

docs(01): create phase 1 provider infrastructure plans

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
shrimbly 6 months ago
parent
commit
75d61b9a47
  1. 188
      .planning/phases/01-provider-infrastructure/01-01-PLAN.md
  2. 200
      .planning/phases/01-provider-infrastructure/01-02-PLAN.md

188
.planning/phases/01-provider-infrastructure/01-01-PLAN.md

@ -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>

200
.planning/phases/01-provider-infrastructure/01-02-PLAN.md

@ -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…
Cancel
Save