""" Configuration management for D'n'D Campaign Manager Handles API keys, model settings, and application config """ import os from pathlib import Path from typing import Optional from pydantic import BaseModel, Field from dotenv import load_dotenv # Load environment variables load_dotenv() class ModelConfig(BaseModel): """Configuration for AI models""" # Primary model for generation primary_provider: str = Field(default="anthropic", description="anthropic, openai, or google") primary_model: str = Field(default="claude-3-haiku-20240307", description="Model name") # Memory model for long context memory_provider: str = Field(default="google", description="Provider for campaign memory") memory_model: str = Field(default="gemini-2.0-flash-exp", description="2M token context model") # Temperature settings creative_temp: float = Field(default=0.9, description="For character/story generation") balanced_temp: float = Field(default=0.7, description="For general tasks") precise_temp: float = Field(default=0.3, description="For rules/stats") # Token limits max_tokens_generation: int = Field(default=2000, description="For content generation") max_tokens_memory: int = Field(default=1000000, description="For memory queries") class DatabaseConfig(BaseModel): """Database configuration""" db_type: str = Field(default="sqlite", description="Database type") db_path: Path = Field(default=Path("data/dnd_campaign_manager.db"), description="SQLite path") def ensure_db_dir(self): """Ensure database directory exists""" self.db_path.parent.mkdir(parents=True, exist_ok=True) class MCPConfig(BaseModel): """MCP Server configuration""" server_name: str = Field(default="dnd-campaign-manager", description="MCP server name") server_version: str = Field(default="2.0.0", description="MCP server version") # MCP tools to expose enable_character_tools: bool = Field(default=True, description="Character creation tools") enable_campaign_tools: bool = Field(default=True, description="Campaign management tools") enable_npc_tools: bool = Field(default=True, description="NPC generation tools") enable_loot_tools: bool = Field(default=True, description="Loot generation tools") enable_encounter_tools: bool = Field(default=True, description="Encounter generation tools") # MCP resources to expose enable_character_resources: bool = Field(default=True, description="Character data resources") enable_campaign_resources: bool = Field(default=True, description="Campaign data resources") class AppConfig(BaseModel): """Application configuration""" app_name: str = Field(default="D'n'D Campaign Manager", description="Application name") app_version: str = Field(default="2.0.0", description="Application version") # Gradio settings gradio_share: bool = Field(default=False, description="Enable Gradio sharing") gradio_server_name: str = Field(default="0.0.0.0", description="Server host") gradio_server_port: int = Field(default=7860, description="Server port") # Data persistence enable_persistence: bool = Field(default=True, description="Save data to database") auto_backup: bool = Field(default=True, description="Auto-backup campaigns") backup_interval_hours: int = Field(default=24, description="Backup interval") # Feature flags enable_image_generation: bool = Field(default=True, description="Character portraits") enable_voice_generation: bool = Field(default=False, description="NPC voices (future)") enable_music_generation: bool = Field(default=False, description="Background music (future)") class Config: """Main configuration class""" def __init__(self): # API Keys self.anthropic_api_key: Optional[str] = os.getenv("ANTHROPIC_API_KEY") self.openai_api_key: Optional[str] = os.getenv("OPENAI_API_KEY") self.google_api_key: Optional[str] = os.getenv("GOOGLE_API_KEY") self.huggingface_api_key: Optional[str] = os.getenv("HUGGINGFACE_API_KEY") # Sub-configurations self.model = ModelConfig() self.database = DatabaseConfig() self.mcp = MCPConfig() self.app = AppConfig() # Validate configuration self._validate() def _validate(self): """Validate configuration and API keys""" # Don't fail startup on missing keys - just warn # The UI will show appropriate error messages when features are used if not self.anthropic_api_key and self.model.primary_provider == "anthropic": print("⚠️ Warning: ANTHROPIC_API_KEY not found in environment") print(" Configure API keys in Settings → Variables and secrets") if not self.google_api_key and self.model.memory_provider == "google": print("⚠️ Warning: GOOGLE_API_KEY not found for memory model") # Ensure database directory exists self.database.ensure_db_dir() def has_any_api_key(self) -> bool: """Check if at least one API key is configured""" return bool( self.anthropic_api_key or self.openai_api_key or self.google_api_key ) def get_available_providers(self) -> list[str]: """Get list of providers with configured API keys""" providers = [] if self.anthropic_api_key: providers.append("anthropic") if self.openai_api_key: providers.append("openai") if self.google_api_key: providers.append("google") return providers def get_model_config(self, task_type: str = "balanced") -> dict: """Get model configuration for specific task type""" temp_map = { "creative": self.model.creative_temp, "balanced": self.model.balanced_temp, "precise": self.model.precise_temp } return { "model": self.model.primary_model, "temperature": temp_map.get(task_type, self.model.balanced_temp), "max_tokens": self.model.max_tokens_generation } def get_memory_config(self) -> dict: """Get configuration for memory/context model""" return { "model": self.model.memory_model, "temperature": self.model.balanced_temp, "max_tokens": self.model.max_tokens_memory } # Global configuration instance config = Config() # Export commonly used values ANTHROPIC_API_KEY = config.anthropic_api_key GOOGLE_API_KEY = config.google_api_key OPENAI_API_KEY = config.openai_api_key HUGGINGFACE_API_KEY = config.huggingface_api_key DB_PATH = config.database.db_path MCP_SERVER_NAME = config.mcp.server_name # Data directories DATA_DIR = Path("data") CAMPAIGNS_DIR = DATA_DIR / "campaigns" CHARACTERS_DIR = DATA_DIR / "characters" TEMPLATES_DIR = DATA_DIR / "templates" # Ensure directories exist for directory in [DATA_DIR, CAMPAIGNS_DIR, CHARACTERS_DIR, TEMPLATES_DIR]: directory.mkdir(parents=True, exist_ok=True)