File size: 7,337 Bytes
72bff80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
181
182
183
184
185
"""
Production-grade configuration management with environment-based settings.
"""
import os
from typing import Optional
from dotenv import load_dotenv
from enum import Enum

load_dotenv()


class Environment(str, Enum):
    DEVELOPMENT = "development"
    STAGING = "staging"
    PRODUCTION = "production"


class Config:
    """Base configuration class with defaults."""
    
    # Environment
    ENVIRONMENT: Environment = Environment(os.getenv("ENVIRONMENT", "development"))
    DEBUG: bool = ENVIRONMENT == Environment.DEVELOPMENT
    
    # Application
    APP_NAME: str = "Insurance RAG System"
    VERSION: str = "1.0.0"
    PORT: int = int(os.getenv("PORT", 7860))
    HOST: str = os.getenv("HOST", "0.0.0.0")
    
    # LLM Configuration
    GROQ_API_KEY: str = os.getenv("GROQ_API_KEY", "")
    GROQ_MODEL_SMALL: str = os.getenv("GROQ_MODEL_SMALL", "llama-3.1-8b-instant")
    GROQ_MODEL_MEDIUM: str = os.getenv("GROQ_MODEL_MEDIUM", "llama-3.1-8b-instant")
    GROQ_MODEL_LARGE: str = os.getenv("GROQ_MODEL_LARGE", "llama-3.1-8b-instant")
    LLM_TIMEOUT: int = int(os.getenv("LLM_TIMEOUT", 30))
    LLM_MAX_RETRIES: int = int(os.getenv("LLM_MAX_RETRIES", 3))
    
    # LangChain / LangSmith Tracing
    LANGCHAIN_TRACING_V2: bool = os.getenv("LANGCHAIN_TRACING_V2", "false").lower() == "true"
    LANGCHAIN_ENDPOINT: str = os.getenv("LANGCHAIN_ENDPOINT", "https://api.smith.langchain.com")
    LANGCHAIN_API_KEY: Optional[str] = os.getenv("LANGCHAIN_API_KEY")
    LANGCHAIN_PROJECT: str = os.getenv("LANGCHAIN_PROJECT", "Insurance-RAG")
    
    # Logging Configuration
    LOG_LEVEL: str = os.getenv("LOG_LEVEL", "INFO")
    LOG_FILE_PATH: str = os.getenv("LOG_FILE_PATH", "logs/app.log")
    LOG_MAX_BYTES: int = int(os.getenv("LOG_MAX_BYTES", 10485760))  # 10MB
    LOG_BACKUP_COUNT: int = int(os.getenv("LOG_BACKUP_COUNT", 5))
    LOG_FORMAT: str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
    
    # Performance Configuration
    MAX_WORKERS: int = int(os.getenv("MAX_WORKERS", 4))
    REQUEST_TIMEOUT: int = int(os.getenv("REQUEST_TIMEOUT", 30))
    CACHE_TTL: int = int(os.getenv("CACHE_TTL", 300))  # 5 minutes
    CACHE_MAX_SIZE: int = int(os.getenv("CACHE_MAX_SIZE", 1000))
    
    # Vector Store Configuration
    VECTOR_STORE_PATH: str = os.getenv("VECTOR_STORE_PATH", "rag/faiss_index")
    EMBEDDING_MODEL: str = os.getenv("EMBEDDING_MODEL", "sentence-transformers/all-MiniLM-L6-v2")
    EMBEDDING_CACHE_PATH: str = os.getenv("EMBEDDING_CACHE_PATH", "rag/embeddings_cache.json")
    LLM_CACHE_DB_PATH: str = os.getenv("LLM_CACHE_DB_PATH", "rag/llm_cache.db")
    
    # Retrieval Configuration
    DEFAULT_RETRIEVAL_K: int = int(os.getenv("DEFAULT_RETRIEVAL_K", 5))
    MAX_RETRIEVAL_K: int = int(os.getenv("MAX_RETRIEVAL_K", 20))
    RETRIEVAL_SCORE_THRESHOLD: float = float(os.getenv("RETRIEVAL_SCORE_THRESHOLD", 0.5))
    
    # Security Configuration
    MAX_FILE_SIZE_MB: int = int(os.getenv("MAX_FILE_SIZE_MB", 50))
    ALLOWED_FILE_TYPES: list = os.getenv("ALLOWED_FILE_TYPES", "pdf,docx").split(",")
    RATE_LIMIT_PER_MINUTE: int = int(os.getenv("RATE_LIMIT_PER_MINUTE", 60))
    ENABLE_API_KEY_AUTH: bool = os.getenv("ENABLE_API_KEY_AUTH", "false").lower() == "true"
    API_KEY: Optional[str] = os.getenv("API_KEY", None)
    ENABLE_CORS: bool = os.getenv("ENABLE_CORS", "true").lower() == "true"
    CORS_ORIGINS: list = os.getenv("CORS_ORIGINS", "*").split(",")
    
    # Monitoring Configuration
    ENABLE_METRICS: bool = os.getenv("ENABLE_METRICS", "true").lower() == "true"
    METRICS_PORT: int = int(os.getenv("METRICS_PORT", 9090))
    REQUEST_LOG_DB_PATH: str = os.getenv("REQUEST_LOG_DB_PATH", "utils/request_logs.db")
    
    # Circuit Breaker Configuration
    CIRCUIT_BREAKER_FAILURE_THRESHOLD: int = int(os.getenv("CIRCUIT_BREAKER_FAILURE_THRESHOLD", 5))
    CIRCUIT_BREAKER_TIMEOUT: int = int(os.getenv("CIRCUIT_BREAKER_TIMEOUT", 60))
    CIRCUIT_BREAKER_EXPECTED_EXCEPTION: type = Exception
    
    # Document Processing
    DOCS_DIR: str = os.getenv("DOCS_DIR", "docs")
    BROCHURE_CHUNK_SIZE: int = int(os.getenv("BROCHURE_CHUNK_SIZE", 2600))
    BROCHURE_CHUNK_OVERLAP: int = int(os.getenv("BROCHURE_CHUNK_OVERLAP", 400))
    CIS_CHUNK_SIZE: int = int(os.getenv("CIS_CHUNK_SIZE", 1300))
    CIS_CHUNK_OVERLAP: int = int(os.getenv("CIS_CHUNK_OVERLAP", 160))
    TABLE_CHUNK_SIZE: int = int(os.getenv("TABLE_CHUNK_SIZE", 800))
    TABLE_CHUNK_OVERLAP: int = int(os.getenv("TABLE_CHUNK_OVERLAP", 100))
    
    @classmethod
    def validate(cls) -> bool:
        """Validate critical configuration values."""
        errors = []
        
        # Check required API keys
        if not cls.GROQ_API_KEY:
            errors.append("GROQ_API_KEY is not set")
        
        # Validate file size limits
        if cls.MAX_FILE_SIZE_MB <= 0 or cls.MAX_FILE_SIZE_MB > 500:
            errors.append(f"MAX_FILE_SIZE_MB must be between 1 and 500, got {cls.MAX_FILE_SIZE_MB}")
        
        # Validate rate limits
        if cls.RATE_LIMIT_PER_MINUTE <= 0:
            errors.append(f"RATE_LIMIT_PER_MINUTE must be positive, got {cls.RATE_LIMIT_PER_MINUTE}")
        
        # Validate timeouts
        if cls.REQUEST_TIMEOUT <= 0:
            errors.append(f"REQUEST_TIMEOUT must be positive, got {cls.REQUEST_TIMEOUT}")
        
        # Validate cache settings
        if cls.CACHE_TTL < 0:
            errors.append(f"CACHE_TTL cannot be negative, got {cls.CACHE_TTL}")
        
        # Validate API key auth
        if cls.ENABLE_API_KEY_AUTH and not cls.API_KEY:
            errors.append("ENABLE_API_KEY_AUTH is true but API_KEY is not set")
        
        if errors:
            error_msg = "\n".join(f"  - {err}" for err in errors)
            raise ValueError(f"Configuration validation failed:\n{error_msg}")
        
        return True
    
    @classmethod
    def get_summary(cls) -> dict:
        """Get configuration summary for logging."""
        return {
            "environment": cls.ENVIRONMENT.value,
            "debug": cls.DEBUG,
            "app_name": cls.APP_NAME,
            "version": cls.VERSION,
            "port": cls.PORT,
            "log_level": cls.LOG_LEVEL,
            "max_file_size_mb": cls.MAX_FILE_SIZE_MB,
            "rate_limit_per_minute": cls.RATE_LIMIT_PER_MINUTE,
            "cache_ttl": cls.CACHE_TTL,
            "enable_metrics": cls.ENABLE_METRICS,
            "enable_api_key_auth": cls.ENABLE_API_KEY_AUTH,
        }


class DevelopmentConfig(Config):
    """Development-specific configuration."""
    DEBUG = True
    LOG_LEVEL = "DEBUG"


class ProductionConfig(Config):
    """Production-specific configuration."""
    DEBUG = False
    LOG_LEVEL = "WARNING"


# Configuration factory
def get_config() -> Config:
    """Get configuration based on environment."""
    env = os.getenv("ENVIRONMENT", "development")
    
    config_map = {
        Environment.DEVELOPMENT: DevelopmentConfig,
        Environment.STAGING: Config,
        Environment.PRODUCTION: ProductionConfig,
    }
    
    return config_map.get(Environment(env), Config)


# Global config instance
config = get_config()

# Validate on import
try:
    config.validate()
except ValueError as e:
    print(f"[CONFIG ERROR] {e}")
    print("[CONFIG] Continuing with invalid configuration - some features may not work correctly")