mirror of
https://github.com/leonvanzyl/autocoder.git
synced 2026-01-31 14:43:35 +00:00
feat: Add global settings modal and simplify agent controls
Adds a settings system for global configuration with YOLO mode toggle and
model selection. Simplifies the agent control UI by removing redundant
status indicator and pause functionality.
## Settings System
- New SettingsModal with YOLO mode toggle and model selection
- Settings persisted in SQLite (registry.db) - shared across all projects
- Models fetched from API endpoint (/api/settings/models)
- Single source of truth for models in registry.py - easy to add new models
- Optimistic UI updates with rollback on error
## Agent Control Simplification
- Removed StatusIndicator ("STOPPED"/"RUNNING" label) - redundant
- Removed Pause/Resume buttons - just Start/Stop toggle now
- Start button shows flame icon with fiery gradient when YOLO mode enabled
## Code Review Fixes
- Added focus trap to SettingsModal for accessibility
- Fixed YOLO button color contrast (WCAG AA compliance)
- Added model validation to AgentStartRequest schema
- Added model to AgentStatus response
- Added aria-labels to all icon-only buttons
- Added role="radiogroup" to model selection
- Added loading indicator during settings save
- Added SQLite timeout (30s) and retry logic with exponential backoff
- Added thread-safe database engine initialization
- Added orphaned lock file cleanup on server startup
## Files Changed
- registry.py: Model config, Settings CRUD, SQLite improvements
- server/routers/settings.py: New settings API
- server/schemas.py: Settings schemas with validation
- server/services/process_manager.py: Model param, orphan cleanup
- ui/src/components/SettingsModal.tsx: New modal component
- ui/src/components/AgentControl.tsx: Simplified to Start/Stop only
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,11 +6,20 @@ Request/Response models for the API endpoints.
|
||||
"""
|
||||
|
||||
import base64
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
# Import model constants from registry (single source of truth)
|
||||
_root = Path(__file__).parent.parent
|
||||
if str(_root) not in sys.path:
|
||||
sys.path.insert(0, str(_root))
|
||||
|
||||
from registry import AVAILABLE_MODELS, DEFAULT_MODEL, VALID_MODELS
|
||||
|
||||
# ============================================================================
|
||||
# Project Schemas
|
||||
# ============================================================================
|
||||
@@ -102,7 +111,16 @@ class FeatureListResponse(BaseModel):
|
||||
|
||||
class AgentStartRequest(BaseModel):
|
||||
"""Request schema for starting the agent."""
|
||||
yolo_mode: bool = False
|
||||
yolo_mode: bool | None = None # None means use global settings
|
||||
model: str | None = None # None means use global settings
|
||||
|
||||
@field_validator('model')
|
||||
@classmethod
|
||||
def validate_model(cls, v: str | None) -> str | None:
|
||||
"""Validate model is in the allowed list."""
|
||||
if v is not None and v not in VALID_MODELS:
|
||||
raise ValueError(f"Invalid model. Must be one of: {VALID_MODELS}")
|
||||
return v
|
||||
|
||||
|
||||
class AgentStatus(BaseModel):
|
||||
@@ -111,6 +129,7 @@ class AgentStatus(BaseModel):
|
||||
pid: int | None = None
|
||||
started_at: datetime | None = None
|
||||
yolo_mode: bool = False
|
||||
model: str | None = None # Model being used by running agent
|
||||
|
||||
|
||||
class AgentActionResponse(BaseModel):
|
||||
@@ -239,3 +258,41 @@ class CreateDirectoryRequest(BaseModel):
|
||||
"""Request to create a new directory."""
|
||||
parent_path: str
|
||||
name: str = Field(..., min_length=1, max_length=255)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Settings Schemas
|
||||
# ============================================================================
|
||||
|
||||
# Note: VALID_MODELS and DEFAULT_MODEL are imported from registry at the top of this file
|
||||
|
||||
|
||||
class ModelInfo(BaseModel):
|
||||
"""Information about an available model."""
|
||||
id: str
|
||||
name: str
|
||||
|
||||
|
||||
class SettingsResponse(BaseModel):
|
||||
"""Response schema for global settings."""
|
||||
yolo_mode: bool = False
|
||||
model: str = DEFAULT_MODEL
|
||||
|
||||
|
||||
class ModelsResponse(BaseModel):
|
||||
"""Response schema for available models list."""
|
||||
models: list[ModelInfo]
|
||||
default: str
|
||||
|
||||
|
||||
class SettingsUpdate(BaseModel):
|
||||
"""Request schema for updating global settings."""
|
||||
yolo_mode: bool | None = None
|
||||
model: str | None = None
|
||||
|
||||
@field_validator('model')
|
||||
@classmethod
|
||||
def validate_model(cls, v: str | None) -> str | None:
|
||||
if v is not None and v not in VALID_MODELS:
|
||||
raise ValueError(f"Invalid model. Must be one of: {VALID_MODELS}")
|
||||
return v
|
||||
|
||||
Reference in New Issue
Block a user