official.ghost.logic
Fix startup issues for HF Spaces
09d5a09
"""
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)