# Per-Phase AI Provider Configuration - Implementation Plan > **Created**: 2024-12-30 > **Approach**: UI-First with incremental wiring > **Estimated Total Effort**: 20-25 hours ## Overview Allow users to configure which AI provider/model to use for each distinct phase of the application. This gives users fine-grained control over cost, speed, and quality tradeoffs. --- ## Current State Analysis ### Existing Settings Fields (UNUSED) These fields already exist in `GlobalSettings` but are not wired up: ```typescript // libs/types/src/settenhancementModel: AgentModel; // Currently ignored, hardcoded to 'sonnet' validationModel: AgentModel; // Currently ignored, hardcoded to 'opus' ``` ### All AI Usage Phases | Phase | Location | Current Model | Priority | | ------------------- | -------------------------------- | ------------------ | -------- | | Feature Execution | `auto-mode-service.ts` | Per-feature | ✅ Done | | Enhancement | `enhance.ts` | Hardcoded `sonnet` | ✅ Done | | GitHub Validation | `validate-issue.ts` | Hardcoded `opus` | ✅ Done | | File Description | `describe-file.ts` | Hardcoded `haiku` | P2 | | Image Description | `describe-image.ts` | Hardcoded `haiku` | P2 | | App Spec Generation | `generate-spec.ts` | SDK default | P2 | | Feature from Spec | `generate-features-from-spec.ts` | SDK default | P3 | | Backlog Planning | `generate-plan.ts` | SDK default | P3 | | Project Analysis | `analyze-project.ts` | Hardcoded default | P3 | --- ## Phase 1: Type Definitions & Settings Structure **Effort**: 2-3 hours **Files**: `libs/types/src/settings.ts` ### 1.1 Add PhaseModelConfig Type ```typescript /** * Configuration for AI models used in different application phases */ export interface PhaseModelConfig { // Quick tasks - recommend fast/cheap models (Haiku, Cursor auto) enhancementModel: AgentModel | CursorModelId; fileDescriptionModel: AgentModel | CursorModelId; imageDescriptionModel: AgentModel | CursorModelId; // Validation tasks - recommend smart models (Sonnet, Opus) validationModel: AgentModel | CursorModelId; // Generation tasks - recommend powerful models (Opus, Sonnet) specGenerationModel: AgentModel | CursorModelId; featureGenerationModel: AgentModel | CursorModelId; backlogPlanningModel: AgentModel | CursorModelId; projectAnalysisModel: AgentModel | CursorModelId; } ``` ### 1.2 Update GlobalSettings ```typescript export interface GlobalSettings { // ... existing fields ... // Phase-specific model configuration phaseModels: PhaseModelConfig; // Legacy fields (keep for backwards compatibility) enhancementModel?: AgentModel; // Deprecated, use phaseModels validationModel?: AgentModel; // Deprecated, use phaseModels } ``` ### 1.3 Default Values ```typescript export const DEFAULT_PHASE_MODELS: PhaseModelConfig = { // Quick tasks - use fast models enhancementModel: 'sonnet', fileDescriptionModel: 'haiku', imageDescriptionModel: 'haiku', // Validation - use smart models validationModel: 'sonnet', // Generation - use powerful models specGenerationModel: 'opus', featureGenerationModel: 'sonnet', backlogPlanningModel: 'sonnet', projectAnalysisModel: 'sonnet', }; ``` ### 1.4 Migration Helper ```typescript // In settings-service.ts function migrateSettings(settings: GlobalSettings): GlobalSettings { // Migrate legacy fields to new structure if (!settings.phaseModels) { settings.phaseModels = { ...DEFAULT_PHASE_MODELS, enhancementModel: settings.enhancementModel || 'sonnet', validationModel: settings.validationModel || 'opus', }; } return settings; } ``` --- ## Phase 2: Settings UI **Effort**: 6-8 hours **Files**: - `apps/ui/src/components/views/settings-view.tsx` - `apps/ui/src/components/views/settings-view/phase-models-tab.tsx` (new) ### 2.1 Create PhaseModelsTab Component ``` ┌─────────────────────────────────────────────────────────────┐ │ AI Phase Configuration │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Configure which AI model to use for each application task. │ │ Cursor models require cursor-agent CLI installed. │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ QUICK TASKS │ │ │ │ Fast models recommended for speed and cost savings │ │ │ ├─────────────────────────────────────────────────────┤ │ │ │ │ │ │ │ Feature Enhancement │ │ │ │ Improves feature names and descriptions │ │ │ │ [Claude Sonnet ▼] [Haiku] [Cursor Auto] │ │ │ │ │ │ │ │ File Descriptions │ │ │ │ Generates descriptions for context files │ │ │ │ [Claude Haiku ▼] [Sonnet] [Cursor Auto] │ │ │ │ │ │ │ │ Image Descriptions │ │ │ │ Analyzes and describes context images │ │ │ │ [Claude Haiku ▼] [Sonnet] [Cursor Auto] │ │ │ │ │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ VALIDATION TASKS │ │ │ │ Smart models recommended for accuracy │ │ │ ├─────────────────────────────────────────────────────┤ │ │ │ │ │ │ │ GitHub Issue Validation │ │ │ │ Validates and improves GitHub issues │ │ │ │ [Claude Sonnet ▼] [Opus] [Cursor Sonnet] │ │ │ │ │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ GENERATION TASKS │ │ │ │ Powerful models recommended for quality │ │ │ ├─────────────────────────────────────────────────────┤ │ │ │ │ │ │ │ App Specification │ │ │ │ Generates full application specifications │ │ │ │ [Claude Opus ▼] [Sonnet] [Cursor Opus] │ │ │ │ │ │ │ │ Feature Generation │ │ │ │ Creates features from specifications │ │ │ │ [Claude Sonnet ▼] [Opus] [Cursor Auto] │ │ │ │ │ │ │ │ Backlog Planning │ │ │ │ Reorganizes and prioritizes backlog │ │ │ │ [Claude Sonnet ▼] [Opus] [Cursor Auto] │ │ │ │ │ │ │ │ Project Analysis │ │ │ │ Analyzes project structure for suggestions │ │ │ │ [Claude Sonnet ▼] [Opus] [Cursor Auto] │ │ │ │ │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ [Reset to Defaults] [Save Changes] │ │ │ └─────────────────────────────────────────────────────────────┘ ``` ### 2.2 PhaseModelSelector Component Reusable component for selecting model per phase: ```typescript interface PhaseModelSelectorProps { phase: keyof PhaseModelConfig; label: string; description: string; value: AgentModel | CursorModelId; onChange: (value: AgentModel | CursorModelId) => void; recommendedModels?: string[]; } ``` Features: - Shows both Claude and Cursor models - Indicates which provider each model uses - Shows "Recommended" badge on suggested models - Disables Cursor models if CLI not installed - Shows thinking level indicator for supported models ### 2.3 Add Tab to Settings View ```typescript // In settings-view.tsx, add new tab const SETTINGS_TABS = [ { id: 'general', label: 'General', icon: Settings }, { id: 'ai-profiles', label: 'AI Profiles', icon: Bot }, { id: 'phase-models', label: 'Phase Models', icon: Workflow }, // NEW { id: 'providers', label: 'Providers', icon: Key }, // ... ]; ``` --- ## Phase 3: Wire Enhancement Route (P1) **Effort**: 2-3 hours **Files**: `apps/server/src/routes/enhance-prompt/routes/enhance.ts` ### 3.1 Current Code ```typescript // BEFORE - Hardcoded const model = CLAUDE_MODEL_MAP.sonnet; ``` ### 3.2 Updated Code ```typescript // AFTER - Uses settings import { SettingsService } from '@/services/settings-service.js'; import { ProviderFactory } from '@/providers/provider-factory.js'; const settingsService = new SettingsService(dataDir); const settings = await settingsService.getSettings(); const modelId = settings.phaseModels?.enhancementModel || 'sonnet'; // Resolve to full model string const provider = ProviderFactory.getProviderForModel(modelId); const model = resolveModelString(modelId); ``` ### 3.3 Test Cases - [ ] Default behavior (uses sonnet) still works - [ ] Changing to haiku in settings uses haiku - [ ] Changing to cursor-auto routes to Cursor provider - [ ] Invalid model falls back to default --- ## Phase 4: Wire Validation Route (P1) **Effort**: 2-3 hours **Files**: `apps/server/src/routes/github/routes/validate-issue.ts` ### 4.1 Current Code ```typescript // BEFORE - Has model param but defaults to hardcoded const model = request.body.model || 'opus'; ``` ### 4.2 Updated Code ```typescript // AFTER - Uses settings as default const settings = await settingsService.getSettings(); const defaultModel = settings.phaseModels?.validationModel || 'opus'; const model = request.body.model || defaultModel; ``` ### 4.3 Test Cases - [ ] Default uses configured model from settings - [ ] Explicit model in request overrides settings - [ ] Cursor models work for validation --- ## Phase 5: Wire Context Description Routes (P2) **Effort**: 3-4 hours **Files**: - `apps/server/src/routes/context/routes/describe-file.ts` - `apps/server/src/routes/context/routes/describe-image.ts` ### 5.1 Pattern Same pattern as enhancement - replace hardcoded `haiku` with settings lookup. ### 5.2 Test Cases - [ ] File description uses configured model - [ ] Image description uses configured model (with vision support check) - [ ] Fallback to haiku if model doesn't support vision --- ## Phase 6: Wire Generation Routes (P2) **Effort**: 4-5 hours **Files**: - `apps/server/src/routes/app-spec/generate-spec.ts` - `apps/server/src/routes/app-spec/generate-features-from-spec.ts` ### 6.1 Pattern These routes use the Claude SDK directly. Need to: 1. Load settings 2. Resolve model string 3. Pass to SDK configuration ### 6.2 Test Cases - [ ] App spec generation uses configured model - [ ] Feature generation uses configured model - [ ] Works with both Claude and Cursor providers --- ## Phase 7: Wire Remaining Routes (P3) **Effort**: 4-5 hours **Files**: - `apps/server/src/routes/backlog-plan/generate-plan.ts` - `apps/server/src/routes/auto-mode/routes/analyze-project.ts` ### 7.1 Pattern Same settings injection pattern. ### 7.2 Test Cases - [ ] Backlog planning uses configured model - [ ] Project analysis uses configured model --- ## Implementation Order ``` Phase 1: Types & Settings Structure Phase 2: Settings UI (Phase Models tab) Phase 8: Quick Model Override Component <- Right after UI for easier testing Phase 9: Integration Points for Override <- Wire override to each feature Then wire routes (testing both global + override together): Phase 3: Enhancement Route - Done Phase 4: Validation Route - Broken validation parsing ! need urgent fix to properly validate cursor cli output Phase 5: Context Routes (file/image description) Phase 6: Generation Routes (spec, features) Phase 7: Remaining Routes (backlog, analysis) ``` --- ## File Changes Summary ### New Files - `apps/ui/src/components/views/settings-view/phase-models-tab.tsx` - `apps/ui/src/components/views/settings-view/phase-model-selector.tsx` ### Modified Files | File | Changes | | ---------------------------------------------------------------- | ------------------------- | | `libs/types/src/settings.ts` | Add PhaseModelConfig type | | `apps/server/src/services/settings-service.ts` | Add migration logic | | `apps/ui/src/components/views/settings-view.tsx` | Add Phase Models tab | | `apps/server/src/routes/enhance-prompt/routes/enhance.ts` | Use settings | | `apps/server/src/routes/github/routes/validate-issue.ts` | Use settings | | `apps/server/src/routes/context/routes/describe-file.ts` | Use settings | | `apps/server/src/routes/context/routes/describe-image.ts` | Use settings | | `apps/server/src/routes/app-spec/generate-spec.ts` | Use settings | | `apps/server/src/routes/app-spec/generate-features-from-spec.ts` | Use settings | | `apps/server/src/routes/backlog-plan/generate-plan.ts` | Use settings | | `apps/server/src/routes/auto-mode/routes/analyze-project.ts` | Use settings | --- ## Testing Strategy ### Unit Tests - Settings migration preserves existing values - Default values applied correctly - Model resolution works for both providers ### Integration Tests - Each phase uses configured model - Provider factory routes correctly - Cursor fallback when CLI not available ### E2E Tests - Settings UI saves correctly - Changes persist across restarts - Each feature works with non-default model --- ## Rollback Plan If issues arise: 1. All routes have fallback to hardcoded defaults 2. Settings migration is additive (doesn't remove old fields) 3. Can revert individual routes independently --- ## Success Criteria - [ ] Users can configure model for each phase via Settings UI - [ ] All 8+ phases respect configured model - [ ] Cursor models work for all applicable phases - [ ] Graceful fallback when Cursor CLI not available - [ ] Settings persist across app restarts - [ ] No regression in existing functionality --- --- ## Phase 8: Quick Model Override Component (P1) **Effort**: 4-6 hours **Files**: - `apps/ui/src/components/shared/model-override-popover.tsx` (new) - `apps/ui/src/components/shared/model-override-trigger.tsx` (new) ### 8.1 Concept Global defaults are great, but users often want to override for a specific run: - "Use Opus for this complex feature" - "Use Cursor for this quick fix" - "Use Haiku to save costs on this simple task" ### 8.2 Component: ModelOverrideTrigger A small gear/settings icon that opens the override popover: ```typescript interface ModelOverrideTriggerProps { // Current effective model (from global settings or explicit override) currentModel: string; // Callback when user selects override onModelChange: (model: string | null) => void; // Optional: which phase this is for (shows recommended models) phase?: keyof PhaseModelConfig; // Size variants for different contexts size?: 'sm' | 'md' | 'lg'; // Show as icon-only or with label variant?: 'icon' | 'button' | 'inline'; } ``` ### 8.3 Component: ModelOverridePopover ``` ┌──────────────────────────────────────────────┐ │ Model Override [x] │ ├──────────────────────────────────────────────┤ │ │ │ Current: Claude Sonnet (from settings) │ │ │ │ ○ Use Global Setting │ │ └─ Claude Sonnet │ │ │ │ ● Override for this run: │ │ │ │ CLAUDE │ │ ┌──────┐ ┌──────┐ ┌───────┐ │ │ │ Opus │ │Sonnet│ │ Haiku │ │ │ └──────┘ └──────┘ └───────┘ │ │ │ │ CURSOR │ │ ┌──────┐ ┌────────┐ ┌───────┐ │ │ │ Auto │ │Sonnet45│ │GPT-5.2│ │ │ └──────┘ └────────┘ └───────┘ │ │ │ │ [Clear Override] [Apply] │ │ │ └──────────────────────────────────────────────┘ ``` ### 8.4 Usage Examples **In Feature Modal (existing model selector enhancement):** ```tsx