Files
HCFS/hcfs-python/hcfs/api/config.py
2025-07-30 09:34:16 +10:00

288 lines
11 KiB
Python

"""
Configuration management for HCFS API.
Handles environment-based configuration with validation and defaults.
"""
import os
from typing import List, Optional, Dict, Any
from pydantic import BaseSettings, Field, validator
from pathlib import Path
class DatabaseConfig(BaseSettings):
"""Database configuration settings."""
# SQLite settings
db_path: str = Field(default="hcfs_production.db", description="Path to SQLite database")
vector_db_path: str = Field(default="hcfs_vectors_production.db", description="Path to vector database")
# Connection settings
pool_size: int = Field(default=10, description="Database connection pool size")
max_overflow: int = Field(default=20, description="Maximum connection overflow")
pool_timeout: int = Field(default=30, description="Connection pool timeout in seconds")
# Performance settings
cache_size: int = Field(default=1000, description="Database cache size")
enable_wal_mode: bool = Field(default=True, description="Enable SQLite WAL mode")
synchronous_mode: str = Field(default="NORMAL", description="SQLite synchronous mode")
class Config:
env_prefix = "HCFS_DB_"
class EmbeddingConfig(BaseSettings):
"""Embedding system configuration."""
# Model settings
model_name: str = Field(default="mini", description="Embedding model to use")
cache_size: int = Field(default=2000, description="Embedding cache size")
batch_size: int = Field(default=32, description="Batch processing size")
# Performance settings
max_workers: int = Field(default=4, description="Maximum worker threads")
timeout_seconds: int = Field(default=300, description="Operation timeout")
# Vector database settings
vector_dimension: int = Field(default=384, description="Vector dimension")
similarity_threshold: float = Field(default=0.0, description="Default similarity threshold")
class Config:
env_prefix = "HCFS_EMBEDDING_"
class APIConfig(BaseSettings):
"""API server configuration."""
# Server settings
host: str = Field(default="0.0.0.0", description="Server host")
port: int = Field(default=8000, description="Server port")
workers: int = Field(default=1, description="Number of worker processes")
# Security settings
secret_key: str = Field(default="dev-secret-key-change-in-production", description="JWT secret key")
algorithm: str = Field(default="HS256", description="JWT algorithm")
token_expire_minutes: int = Field(default=30, description="JWT token expiration time")
# CORS settings
cors_origins: List[str] = Field(
default=["http://localhost:3000", "http://localhost:8080"],
description="Allowed CORS origins"
)
cors_credentials: bool = Field(default=True, description="Allow credentials in CORS")
# Rate limiting
rate_limit_requests: int = Field(default=100, description="Requests per minute")
rate_limit_burst: int = Field(default=20, description="Burst requests allowed")
# Feature flags
enable_auth: bool = Field(default=True, description="Enable authentication")
enable_websocket: bool = Field(default=True, description="Enable WebSocket support")
enable_metrics: bool = Field(default=True, description="Enable Prometheus metrics")
enable_docs: bool = Field(default=True, description="Enable API documentation")
class Config:
env_prefix = "HCFS_API_"
class MonitoringConfig(BaseSettings):
"""Monitoring and observability configuration."""
# Logging settings
log_level: str = Field(default="INFO", description="Logging level")
log_format: str = Field(default="json", description="Log format (json/text)")
log_file: Optional[str] = Field(default=None, description="Log file path")
# Metrics settings
metrics_enabled: bool = Field(default=True, description="Enable metrics collection")
metrics_port: int = Field(default=9090, description="Metrics server port")
# Health check settings
health_check_interval: int = Field(default=30, description="Health check interval in seconds")
health_check_timeout: int = Field(default=5, description="Health check timeout")
# Tracing settings
tracing_enabled: bool = Field(default=False, description="Enable distributed tracing")
tracing_sample_rate: float = Field(default=0.1, description="Tracing sample rate")
jaeger_endpoint: Optional[str] = Field(default=None, description="Jaeger endpoint")
class Config:
env_prefix = "HCFS_MONITORING_"
class RedisConfig(BaseSettings):
"""Redis configuration for caching and rate limiting."""
# Connection settings
host: str = Field(default="localhost", description="Redis host")
port: int = Field(default=6379, description="Redis port")
db: int = Field(default=0, description="Redis database number")
password: Optional[str] = Field(default=None, description="Redis password")
# Pool settings
max_connections: int = Field(default=20, description="Maximum Redis connections")
socket_timeout: int = Field(default=5, description="Socket timeout in seconds")
# Cache settings
default_ttl: int = Field(default=3600, description="Default cache TTL in seconds")
key_prefix: str = Field(default="hcfs:", description="Redis key prefix")
class Config:
env_prefix = "HCFS_REDIS_"
class SecurityConfig(BaseSettings):
"""Security configuration."""
# Authentication
require_auth: bool = Field(default=True, description="Require authentication")
api_key_header: str = Field(default="X-API-Key", description="API key header name")
# Rate limiting
rate_limit_enabled: bool = Field(default=True, description="Enable rate limiting")
rate_limit_storage: str = Field(default="memory", description="Rate limit storage (memory/redis)")
# HTTPS settings
force_https: bool = Field(default=False, description="Force HTTPS in production")
hsts_max_age: int = Field(default=31536000, description="HSTS max age")
# Request validation
max_request_size: int = Field(default=10 * 1024 * 1024, description="Maximum request size in bytes")
max_query_params: int = Field(default=100, description="Maximum query parameters")
# Content security
allowed_content_types: List[str] = Field(
default=["application/json", "application/x-www-form-urlencoded", "multipart/form-data"],
description="Allowed content types"
)
class Config:
env_prefix = "HCFS_SECURITY_"
class HCFSConfig(BaseSettings):
"""Main HCFS configuration combining all subsystem configs."""
# Environment
environment: str = Field(default="development", description="Environment (development/staging/production)")
debug: bool = Field(default=False, description="Enable debug mode")
# Application info
app_name: str = Field(default="HCFS API", description="Application name")
app_version: str = Field(default="2.0.0", description="Application version")
app_description: str = Field(default="Context-Aware Hierarchical Context File System API", description="App description")
# Configuration file path
config_file: Optional[str] = Field(default=None, description="Path to configuration file")
# Subsystem configurations
database: DatabaseConfig = Field(default_factory=DatabaseConfig)
embedding: EmbeddingConfig = Field(default_factory=EmbeddingConfig)
api: APIConfig = Field(default_factory=APIConfig)
monitoring: MonitoringConfig = Field(default_factory=MonitoringConfig)
redis: RedisConfig = Field(default_factory=RedisConfig)
security: SecurityConfig = Field(default_factory=SecurityConfig)
class Config:
env_prefix = "HCFS_"
env_file = ".env"
env_file_encoding = "utf-8"
@validator('environment')
def validate_environment(cls, v):
"""Validate environment value."""
allowed = ['development', 'staging', 'production']
if v not in allowed:
raise ValueError(f'Environment must be one of: {allowed}')
return v
@validator('debug')
def validate_debug_in_production(cls, v, values):
"""Ensure debug is disabled in production."""
if values.get('environment') == 'production' and v:
raise ValueError('Debug mode cannot be enabled in production')
return v
def is_production(self) -> bool:
"""Check if running in production environment."""
return self.environment == 'production'
def is_development(self) -> bool:
"""Check if running in development environment."""
return self.environment == 'development'
def get_database_url(self) -> str:
"""Get database URL."""
return f"sqlite:///{self.database.db_path}"
def get_redis_url(self) -> str:
"""Get Redis URL."""
if self.redis.password:
return f"redis://:{self.redis.password}@{self.redis.host}:{self.redis.port}/{self.redis.db}"
return f"redis://{self.redis.host}:{self.redis.port}/{self.redis.db}"
def load_from_file(self, config_path: str) -> None:
"""Load configuration from YAML file."""
import yaml
config_file = Path(config_path)
if not config_file.exists():
raise FileNotFoundError(f"Configuration file not found: {config_path}")
with open(config_file, 'r') as f:
config_data = yaml.safe_load(f)
# Update configuration
for key, value in config_data.items():
if hasattr(self, key):
setattr(self, key, value)
def to_dict(self) -> Dict[str, Any]:
"""Convert configuration to dictionary."""
return self.dict()
def save_to_file(self, config_path: str) -> None:
"""Save configuration to YAML file."""
import yaml
config_data = self.to_dict()
with open(config_path, 'w') as f:
yaml.dump(config_data, f, default_flow_style=False, indent=2)
# Global configuration instance
config = HCFSConfig()
def get_config() -> HCFSConfig:
"""Get the global configuration instance."""
return config
def load_config(config_path: Optional[str] = None, **overrides) -> HCFSConfig:
"""Load configuration with optional file and overrides."""
global config
# Load from file if provided
if config_path:
config.load_from_file(config_path)
# Apply overrides
for key, value in overrides.items():
if hasattr(config, key):
setattr(config, key, value)
return config
def create_config_template(output_path: str = "hcfs_config.yaml") -> None:
"""Create a configuration template file."""
template_config = HCFSConfig()
template_config.save_to_file(output_path)
print(f"Configuration template created: {output_path}")
if __name__ == "__main__":
# Create configuration template
create_config_template()