Files
FusionAGI/fusionagi/settings.py
Devin AI f14d63f14d
Some checks failed
CI / lint (pull_request) Failing after 47s
CI / test (3.10) (pull_request) Failing after 39s
CI / test (3.11) (pull_request) Failing after 37s
CI / test (3.12) (pull_request) Successful in 1m10s
CI / docker (pull_request) Has been skipped
Full optimization: 38 improvements across frontend, backend, infrastructure, and docs
Frontend (17 items):
- Virtualized message list with batch loading
- CSS split with skeleton, drawer, search filter, message action styles
- Code splitting via React.lazy + Suspense for Admin/Ethics/Settings pages
- Skeleton loading components (Skeleton, SkeletonCard, SkeletonGrid)
- Debounced search/filter component (SearchFilter)
- Error boundary with fallback UI
- Keyboard shortcuts (Ctrl+K search, Ctrl+Enter send, Escape dismiss)
- Page transition animations (fade-in)
- PWA support (manifest.json + service worker)
- WebSocket auto-reconnect with exponential backoff (10 retries)
- Chat history persistence to localStorage (500 msg limit)
- Message edit/delete on hover
- Copy-to-clipboard on code blocks
- Mobile drawer (bottom-sheet for consensus panel)
- File upload support
- User preferences sync to backend

Testing (8 items):
- Component tests: Toast, Markdown, ChatMessage, Avatar, ErrorBoundary, Skeleton
- Hook tests: useChatHistory
- E2E smoke tests (5 tests)
- Accessibility audit utility

Backend (12 items):
- Vector memory with cosine similarity search
- TTS/STT adapter factory wiring
- Geometry kernel with orphan detection
- Tenant registry with CRUD operations
- Response cache with TTL
- Connection pool (async)
- Background task queue
- Health check endpoints (/health, /ready)
- Request tracing middleware (X-Request-ID)
- API key rotation mechanism
- Environment-based config (settings.py)
- API route documentation improvements

Infrastructure (4 items):
- Grafana dashboard template
- Database migration system
- Storybook configuration

Documentation (3 items):
- ADR-001: Advisory Governance Model
- ADR-002: Twelve-Head Architecture
- ADR-003: Consequence Engine

552 Python tests + 45 frontend tests passing, 0 ruff errors.

Co-Authored-By: Nakamoto, S <defi@defi-oracle.io>
2026-05-02 03:08:08 +00:00

107 lines
4.7 KiB
Python

"""Environment-based configuration using Pydantic Settings.
All settings are configurable via environment variables or .env file.
"""
from __future__ import annotations
from pydantic import BaseModel, Field
class APIConfig(BaseModel):
"""API server configuration."""
host: str = Field(default="0.0.0.0", description="Server bind host")
port: int = Field(default=8000, description="Server bind port")
workers: int = Field(default=1, description="Number of worker processes")
cors_origins: list[str] = Field(default=["*"], description="CORS allowed origins")
api_key: str | None = Field(default=None, description="API key for authentication")
rate_limit: int = Field(default=120, description="Rate limit (requests per window)")
rate_window: float = Field(default=60.0, description="Rate limit window in seconds")
class DatabaseConfig(BaseModel):
"""Database configuration."""
url: str = Field(default="sqlite:///fusionagi.db", description="Database URL")
pool_size: int = Field(default=5, description="Connection pool size")
max_overflow: int = Field(default=10, description="Max overflow connections")
echo: bool = Field(default=False, description="Echo SQL statements")
class CacheConfig(BaseModel):
"""Cache configuration."""
enabled: bool = Field(default=True, description="Enable response caching")
max_size: int = Field(default=1000, description="Max cached entries")
ttl_seconds: float = Field(default=300.0, description="Cache TTL in seconds")
backend: str = Field(default="memory", description="Cache backend (memory or redis)")
redis_url: str | None = Field(default=None, description="Redis URL if backend is redis")
class LoggingConfig(BaseModel):
"""Logging configuration."""
level: str = Field(default="INFO", description="Log level")
format: str = Field(default="json", description="Log format (json or text)")
correlation_id_header: str = Field(default="X-Request-ID", description="Request ID header")
class GovernanceConfig(BaseModel):
"""Governance configuration."""
mode: str = Field(default="advisory", description="Governance mode (advisory or enforcing)")
max_file_size: int | None = Field(default=None, description="Max file size in bytes (None=unlimited)")
allow_private_urls: bool = Field(default=True, description="Allow private/internal URLs")
class FusionAGIConfig(BaseModel):
"""Root configuration for FusionAGI."""
api: APIConfig = Field(default_factory=APIConfig)
database: DatabaseConfig = Field(default_factory=DatabaseConfig)
cache: CacheConfig = Field(default_factory=CacheConfig)
logging: LoggingConfig = Field(default_factory=LoggingConfig)
governance: GovernanceConfig = Field(default_factory=GovernanceConfig)
tenant_isolation: bool = Field(default=True, description="Enable tenant isolation")
max_concurrent_tasks: int = Field(default=5, description="Max background tasks")
def load_config() -> FusionAGIConfig:
"""Load configuration from environment variables.
Environment variables are mapped using the pattern:
FUSIONAGI_<SECTION>_<KEY> (e.g., FUSIONAGI_API_PORT=9000)
"""
import os
config = FusionAGIConfig()
env_map = {
"FUSIONAGI_API_HOST": ("api", "host"),
"FUSIONAGI_API_PORT": ("api", "port"),
"FUSIONAGI_API_WORKERS": ("api", "workers"),
"FUSIONAGI_API_KEY": ("api", "api_key"),
"FUSIONAGI_RATE_LIMIT": ("api", "rate_limit"),
"FUSIONAGI_RATE_WINDOW": ("api", "rate_window"),
"FUSIONAGI_DB_URL": ("database", "url"),
"FUSIONAGI_DB_POOL_SIZE": ("database", "pool_size"),
"FUSIONAGI_CACHE_ENABLED": ("cache", "enabled"),
"FUSIONAGI_CACHE_TTL": ("cache", "ttl_seconds"),
"FUSIONAGI_CACHE_BACKEND": ("cache", "backend"),
"FUSIONAGI_REDIS_URL": ("cache", "redis_url"),
"FUSIONAGI_LOG_LEVEL": ("logging", "level"),
"FUSIONAGI_LOG_FORMAT": ("logging", "format"),
"FUSIONAGI_GOVERNANCE_MODE": ("governance", "mode"),
}
for env_var, (section, key) in env_map.items():
value = os.environ.get(env_var)
if value is not None:
section_obj = getattr(config, section)
field_info = type(section_obj).model_fields.get(key)
if field_info and field_info.annotation:
annotation = field_info.annotation
if annotation is int:
value = int(value) # type: ignore[assignment]
elif annotation is float:
value = float(value) # type: ignore[assignment]
elif annotation is bool:
value = value.lower() in ("true", "1", "yes") # type: ignore[assignment]
setattr(section_obj, key, value)
return config