File size: 5,439 Bytes
09801ca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
Settings configuration for the application
"""
import os
import logging
from pathlib import Path
from dotenv import load_dotenv

# Configure Basic Logging
# Configure Basic Logging
import sys

# Custom log filter to redact sensitive information
class SensitiveDataFilter(logging.Filter):
    """Filter to redact sensitive data from logs"""
    
    SENSITIVE_PATTERNS = [
        ('gsk_', 8),  # Groq API keys
        ('sk-', 20),  # OpenAI-style keys
        ('Bearer ', 20),  # JWT tokens
        ('password', 0),  # Passwords
        ('secret', 0),  # Secrets
        ('api_key', 0),  # API keys
        ('token', 0),  # Tokens
    ]
    
    def filter(self, record):
        if hasattr(record, 'msg') and isinstance(record.msg, str):
            msg = record.msg
            for pattern, chars_to_show in self.SENSITIVE_PATTERNS:
                if pattern.lower() in msg.lower():
                    # Redact sensitive values
                    import re
                    # Redact values after = or : 
                    msg = re.sub(
                        rf'({pattern}[=:\s]*)[^\s,\]}}]+',
                        rf'\1[REDACTED]',
                        msg,
                        flags=re.IGNORECASE
                    )
            record.msg = msg
        return True

# Create handler with filter
handler = logging.StreamHandler(sys.stdout)
handler.addFilter(SensitiveDataFilter())

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[handler],
    force=True
)
logger = logging.getLogger("settings")

# Load .env file
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
ENV_PATH = PROJECT_ROOT / ".env"
BACKEND_ENV = Path(__file__).resolve().parent.parent / ".env"

FRONTEND_ENV = PROJECT_ROOT / "frontend" / ".env"

env_loaded = False
for env_file in [ENV_PATH, BACKEND_ENV, FRONTEND_ENV]:
    if env_file.exists():
        load_dotenv(env_file, override=True)
        logger.info(f"Loaded .env from: {env_file}")
        env_loaded = True
        
if not env_loaded:
    logger.warning("No specific .env file found, using system environment")

class Settings:
    """Application settings"""
    # Base paths
    BASE_DIR = Path(__file__).resolve().parent.parent
    STORAGE = BASE_DIR / "storage"
    UPLOADS = STORAGE / "uploads"
    FAISS_DIR = STORAGE / "faiss"
    GRAPH_DIR = STORAGE / "graph"
    
    # API settings
    API_VERSION = "v1"
    
    # Model settings - GROQ PRIMARY
    MODEL_NAME = "groq/llama-3.3-70b-versatile"
    FAST_MODEL = "groq/llama-3.1-8b-instant"
    REASONING_MODEL = "groq/llama-3.3-70b-versatile"
    FALLBACK_MODEL = "groq/llama-3.1-8b-instant"
    
    EMBEDDING_MODEL = "sentence-transformers/all-MiniLM-L6-v2"

    # LLM Provider Support (groq | huggingface | ollama)
    LLM_PROVIDER = os.environ.get("LLM_PROVIDER", "groq").lower()

    # API Keys Management
    GROQ_API_KEYS = []
    
    # Processing settings
    CHUNK_SIZE = 700
    CHUNK_OVERLAP = 150
    EMBED_DIM = 384
    GRAPH_MAX_NODES = 100
    
    # ⚡ PERFORMANCE OPTIMIZATION SETTINGS
    FAST_MODE = False  # Set True for faster (less detailed) responses
    CACHE_ENABLED = True  # Enable response caching
    CACHE_TTL_SECONDS = 3600  # 1 hour cache TTL
    MAX_TOKENS_FAST = 500  # Max tokens for fast mode
    MAX_TOKENS_NORMAL = 1500  # Max tokens for normal mode
    MAX_RAG_STEPS = 2  # Limit RAG iterations
    MAX_AGENT_STEPS = 3  # Limit agent iterations
    
    # Exchange Rates (MVP Static Source)
    EXCHANGE_RATES = {
        "INR_TO_USD": 88.0,
        "INR_TO_EUR": 92.0,
        "INR_TO_GBP": 110.0,
        "USD_TO_INR": 88.0,
        "EUR_TO_INR": 92.0,
        "GBP_TO_INR": 110.0,
    }
    
    @classmethod
    def load_api_keys(cls):
        """Robustly load API keys"""
        # Primary Key
        main_key = os.environ.get("GROQ_API_KEY")
        if main_key:
            cls.GROQ_API_KEYS.append(main_key)
            
        # Fallback Keys (GROQ_KEY_1 to GROQ_KEY_10)
        for i in range(1, 11):
            key = os.environ.get(f"GROQ_KEY_{i}")
            if key and key not in cls.GROQ_API_KEYS:
                cls.GROQ_API_KEYS.append(key)
                
        # List Keys
        if os.environ.get("GROQ_API_KEYS_LIST"):
            for k in os.environ.get("GROQ_API_KEYS_LIST").split(","):
                clean_k = k.strip()
                if clean_k and clean_k not in cls.GROQ_API_KEYS:
                    cls.GROQ_API_KEYS.append(clean_k)
                    
        if not cls.GROQ_API_KEYS:
            logger.warning("No GROQ API keys found! Set GROQ_API_KEY in .env")
        else:
            # Set the primary key for libraries that expect it env-wide
            os.environ["GROQ_API_KEY"] = cls.GROQ_API_KEYS[0]
            logger.info(f"Loaded {len(cls.GROQ_API_KEYS)} Groq API keys")

        # Gemini / Google
        if os.environ.get("GEMINI_API_KEY"):
            os.environ["GOOGLE_API_KEY"] = os.environ.get("GEMINI_API_KEY")
            
        # Nvidia
        if os.environ.get("NVIDIA_API_KEY"):
            logger.info("Loaded Nvidia API key")

    @classmethod
    def ensure_directories(cls):
        """Create necessary directories"""
        for path in [cls.STORAGE, cls.UPLOADS, cls.FAISS_DIR, cls.GRAPH_DIR]:
            path.mkdir(exist_ok=True, parents=True)

# Initialize
Settings.load_api_keys()
Settings.ensure_directories()