Spaces:
Sleeping
Sleeping
| """ | |
| Configuration Settings for Smart Auto-Complete | |
| Manages environment variables and application configuration | |
| """ | |
| import logging | |
| import os | |
| from typing import Any, Dict, Optional | |
| from dotenv import load_dotenv | |
| # Load environment variables from .env file | |
| load_dotenv() | |
| logger = logging.getLogger(__name__) | |
| class AppSettings: | |
| """ | |
| Application settings manager | |
| Loads configuration from environment variables with sensible defaults | |
| """ | |
| def __init__(self): | |
| """Initialize settings from environment variables""" | |
| # API Configuration | |
| self.OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "").strip() | |
| self.ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY", "").strip() | |
| self.DEFAULT_PROVIDER = os.getenv("DEFAULT_PROVIDER", "openai").lower() | |
| # Application Settings | |
| self.MAX_SUGGESTIONS = int(os.getenv("MAX_SUGGESTIONS", "5")) | |
| self.DEBOUNCE_DELAY = int(os.getenv("DEBOUNCE_DELAY", "300")) # milliseconds | |
| self.CACHE_TTL = int(os.getenv("CACHE_TTL", "3600")) # seconds | |
| self.MAX_INPUT_LENGTH = int(os.getenv("MAX_INPUT_LENGTH", "1000")) | |
| # Cache Configuration | |
| self.CACHE_MAX_SIZE = int(os.getenv("CACHE_MAX_SIZE", "1000")) | |
| self.CACHE_ENABLED = os.getenv("CACHE_ENABLED", "true").lower() == "true" | |
| # Logging Configuration | |
| self.LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO").upper() | |
| self.LOG_FORMAT = os.getenv( | |
| "LOG_FORMAT", "%(asctime)s - %(name)s - %(levelname)s - %(message)s" | |
| ) | |
| # API Rate Limiting | |
| self.RATE_LIMIT_REQUESTS_PER_MINUTE = int( | |
| os.getenv("RATE_LIMIT_REQUESTS_PER_MINUTE", "60") | |
| ) | |
| self.RATE_LIMIT_ENABLED = ( | |
| os.getenv("RATE_LIMIT_ENABLED", "true").lower() == "true" | |
| ) | |
| # Model Configuration | |
| self.OPENAI_MODEL = os.getenv("OPENAI_MODEL", "gpt-3.5-turbo") | |
| self.ANTHROPIC_MODEL = os.getenv("ANTHROPIC_MODEL", "claude-3-haiku-20240307") | |
| # Temperature settings for different contexts | |
| self.TEMPERATURE_EMAIL = float(os.getenv("TEMPERATURE_EMAIL", "0.6")) | |
| self.TEMPERATURE_CREATIVE = float(os.getenv("TEMPERATURE_CREATIVE", "0.8")) | |
| self.TEMPERATURE_GENERAL = float(os.getenv("TEMPERATURE_GENERAL", "0.7")) | |
| # Default token limits for different contexts | |
| self.DEFAULT_TOKENS_EMAIL = int(os.getenv("DEFAULT_TOKENS_EMAIL", "200")) | |
| self.DEFAULT_TOKENS_CREATIVE = int(os.getenv("DEFAULT_TOKENS_CREATIVE", "250")) | |
| self.DEFAULT_TOKENS_GENERAL = int(os.getenv("DEFAULT_TOKENS_GENERAL", "200")) | |
| # UI Configuration | |
| self.UI_THEME = os.getenv("UI_THEME", "soft") | |
| self.UI_TITLE = os.getenv("UI_TITLE", "🚀 Smart Auto-Complete") | |
| self.UI_DESCRIPTION = os.getenv( | |
| "UI_DESCRIPTION", "Intelligent text completion powered by AI" | |
| ) | |
| # Server Configuration | |
| self.SERVER_HOST = os.getenv("SERVER_HOST", "0.0.0.0") | |
| self.SERVER_PORT = int(os.getenv("SERVER_PORT", "7860")) | |
| self.SERVER_SHARE = os.getenv("SERVER_SHARE", "false").lower() == "true" | |
| # Security Settings | |
| self.ENABLE_INPUT_SANITIZATION = ( | |
| os.getenv("ENABLE_INPUT_SANITIZATION", "true").lower() == "true" | |
| ) | |
| self.MAX_CONCURRENT_REQUESTS = int(os.getenv("MAX_CONCURRENT_REQUESTS", "10")) | |
| # Development Settings | |
| self.DEBUG_MODE = os.getenv("DEBUG_MODE", "false").lower() == "true" | |
| self.ENABLE_ANALYTICS = os.getenv("ENABLE_ANALYTICS", "true").lower() == "true" | |
| # Validate settings after initialization | |
| self._validate_settings() | |
| logger.info("Application settings loaded successfully") | |
| def _validate_settings(self): | |
| """Validate configuration settings""" | |
| errors = [] | |
| warnings = [] | |
| # Check API keys | |
| if not self.OPENAI_API_KEY and not self.ANTHROPIC_API_KEY: | |
| errors.append( | |
| "No API keys configured. Set OPENAI_API_KEY or ANTHROPIC_API_KEY" | |
| ) | |
| # Validate provider | |
| if self.DEFAULT_PROVIDER not in ["openai", "anthropic"]: | |
| warnings.append( | |
| f"Invalid DEFAULT_PROVIDER: {self.DEFAULT_PROVIDER}. Using 'openai'" | |
| ) | |
| self.DEFAULT_PROVIDER = "openai" | |
| # Validate numeric ranges | |
| if not (1 <= self.MAX_SUGGESTIONS <= 20): | |
| warnings.append( | |
| f"MAX_SUGGESTIONS should be 1-20, got {self.MAX_SUGGESTIONS}" | |
| ) | |
| self.MAX_SUGGESTIONS = max(1, min(20, self.MAX_SUGGESTIONS)) | |
| if not (100 <= self.DEBOUNCE_DELAY <= 2000): | |
| warnings.append( | |
| f"DEBOUNCE_DELAY should be 100-2000ms, got {self.DEBOUNCE_DELAY}" | |
| ) | |
| self.DEBOUNCE_DELAY = max(100, min(2000, self.DEBOUNCE_DELAY)) | |
| if not (100 <= self.MAX_INPUT_LENGTH <= 10000): | |
| warnings.append( | |
| f"MAX_INPUT_LENGTH should be 100-10000, got {self.MAX_INPUT_LENGTH}" | |
| ) | |
| self.MAX_INPUT_LENGTH = max(100, min(10000, self.MAX_INPUT_LENGTH)) | |
| # Validate temperature ranges | |
| for temp_attr in [ | |
| "TEMPERATURE_EMAIL", | |
| "TEMPERATURE_CREATIVE", | |
| "TEMPERATURE_GENERAL", | |
| ]: | |
| temp_value = getattr(self, temp_attr) | |
| if not (0.0 <= temp_value <= 2.0): | |
| warnings.append(f"{temp_attr} should be 0.0-2.0, got {temp_value}") | |
| setattr(self, temp_attr, max(0.0, min(2.0, temp_value))) | |
| # Log validation results | |
| if errors: | |
| for error in errors: | |
| logger.error(f"Configuration error: {error}") | |
| if warnings: | |
| for warning in warnings: | |
| logger.warning(f"Configuration warning: {warning}") | |
| def validate_api_keys(self) -> bool: | |
| """ | |
| Validate that at least one API key is properly configured | |
| Returns: | |
| True if at least one valid API key is available | |
| """ | |
| from src.utils import validate_api_key | |
| openai_valid = self.OPENAI_API_KEY and validate_api_key( | |
| self.OPENAI_API_KEY, "openai" | |
| ) | |
| anthropic_valid = self.ANTHROPIC_API_KEY and validate_api_key( | |
| self.ANTHROPIC_API_KEY, "anthropic" | |
| ) | |
| return openai_valid or anthropic_valid | |
| def get_context_config(self, context: str) -> Dict[str, Any]: | |
| """ | |
| Get configuration for a specific context | |
| Args: | |
| context: Context name (email, code, creative, general) | |
| Returns: | |
| Dictionary with context-specific configuration | |
| """ | |
| context_configs = { | |
| "email": { | |
| "temperature": self.TEMPERATURE_EMAIL, | |
| "default_tokens": self.DEFAULT_TOKENS_EMAIL, | |
| "model_preference": "openai", # Generally better for professional text | |
| }, | |
| "creative": { | |
| "temperature": self.TEMPERATURE_CREATIVE, | |
| "default_tokens": self.DEFAULT_TOKENS_CREATIVE, | |
| "model_preference": "anthropic", # Often better for creative content | |
| }, | |
| "general": { | |
| "temperature": self.TEMPERATURE_GENERAL, | |
| "default_tokens": self.DEFAULT_TOKENS_GENERAL, | |
| "model_preference": self.DEFAULT_PROVIDER, | |
| }, | |
| } | |
| return context_configs.get(context, context_configs["general"]) | |
| def get_model_for_provider(self, provider: str) -> str: | |
| """ | |
| Get the model name for a specific provider | |
| Args: | |
| provider: Provider name (openai, anthropic) | |
| Returns: | |
| Model name string | |
| """ | |
| if provider == "openai": | |
| return self.OPENAI_MODEL | |
| elif provider == "anthropic": | |
| return self.ANTHROPIC_MODEL | |
| else: | |
| return self.OPENAI_MODEL # Default fallback | |
| def to_dict(self) -> Dict[str, Any]: | |
| """ | |
| Convert settings to dictionary (excluding sensitive data) | |
| Returns: | |
| Dictionary with non-sensitive configuration | |
| """ | |
| return { | |
| "max_suggestions": self.MAX_SUGGESTIONS, | |
| "debounce_delay": self.DEBOUNCE_DELAY, | |
| "cache_ttl": self.CACHE_TTL, | |
| "max_input_length": self.MAX_INPUT_LENGTH, | |
| "cache_enabled": self.CACHE_ENABLED, | |
| "log_level": self.LOG_LEVEL, | |
| "rate_limit_enabled": self.RATE_LIMIT_ENABLED, | |
| "rate_limit_requests_per_minute": self.RATE_LIMIT_REQUESTS_PER_MINUTE, | |
| "default_provider": self.DEFAULT_PROVIDER, | |
| "openai_model": self.OPENAI_MODEL, | |
| "anthropic_model": self.ANTHROPIC_MODEL, | |
| "ui_theme": self.UI_THEME, | |
| "ui_title": self.UI_TITLE, | |
| "server_host": self.SERVER_HOST, | |
| "server_port": self.SERVER_PORT, | |
| "debug_mode": self.DEBUG_MODE, | |
| "has_openai_key": bool(self.OPENAI_API_KEY), | |
| "has_anthropic_key": bool(self.ANTHROPIC_API_KEY), | |
| } | |
| def update_from_dict(self, config_dict: Dict[str, Any]): | |
| """ | |
| Update settings from a dictionary | |
| Args: | |
| config_dict: Dictionary with configuration updates | |
| """ | |
| for key, value in config_dict.items(): | |
| if hasattr(self, key.upper()): | |
| setattr(self, key.upper(), value) | |
| logger.info(f"Updated setting {key.upper()} = {value}") | |
| # Re-validate after updates | |
| self._validate_settings() | |
| def __str__(self) -> str: | |
| """String representation of settings (safe for logging)""" | |
| safe_dict = self.to_dict() | |
| return f"AppSettings({safe_dict})" | |
| # Global settings instance | |
| settings = AppSettings() | |