File size: 7,099 Bytes
71b378e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
09d5a09
 
71b378e
09d5a09
 
71b378e
 
09d5a09
71b378e
 
 
 
09d5a09
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71b378e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
"""
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)