File size: 7,552 Bytes
1838600
9d6dc45
1838600
9d6dc45
 
1838600
 
 
 
 
 
 
 
 
 
 
9d6dc45
 
 
 
 
cc22bc2
9d6dc45
1838600
9d6dc45
1838600
639c192
9d6dc45
e6999f9
 
 
 
 
9d6dc45
cc22bc2
9d6dc45
 
 
 
76908f5
 
6ece290
 
 
3fb98b1
76908f5
9d6dc45
1838600
 
 
 
9d6dc45
 
af01f41
1838600
 
82a7380
 
df5a519
4cc19a3
 
df5a519
4cc19a3
 
df5a519
667605e
df5a519
82a7380
df5a519
82a7380
df5a519
82a7380
 
e6999f9
9d6dc45
 
 
 
 
 
cc22bc2
9d6dc45
 
 
 
 
 
 
cc22bc2
 
9d6dc45
1838600
639c192
9d6dc45
1838600
 
 
 
7b1aabb
 
 
cc22bc2
639c192
cc22bc2
639c192
5d11c1e
1838600
 
 
 
 
 
9d6dc45
 
 
 
 
 
 
 
1838600
 
 
 
 
 
 
 
 
 
 
 
76908f5
 
 
1838600
 
 
 
 
 
 
76908f5
 
 
1838600
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# app/config.py - Application configuration (Production Hardened)

from pydantic_settings import BaseSettings, SettingsConfigDict
from typing import Optional
from functools import lru_cache
from dotenv import load_dotenv
import os

# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# FIX 1: HF-SAFE DOTENV LOADING
# On HuggingFace, secrets are injected at runtime. Don't override them.
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
if os.getenv("HF_SPACE_ID") is None:
    load_dotenv(override=True)  # Local dev โ†’ .env
else:
    load_dotenv(override=False) # HF prod โ†’ Secrets only


class Settings(BaseSettings):
    """Application settings loaded from environment variables."""
    
    # Application
    APP_NAME: str = "Scam Honeypot API"
    VERSION: str = "2.5.0"
    DEBUG: bool = False
    GUVI_API_KEY: str = ""  # Must be set via Environment Variable (HF Secrets)
    GUVI_CALLBACK_URL: str = "https://hackathon.guvi.in/api/updateHoneyPotFinalResult"
    
    # SOC Hardening (SIEM Integration)
    SYSLOG_ENABLED: bool = False
    SYSLOG_HOST: str = "localhost"
    SYSLOG_PORT: int = 514
    
    # LLM Configuration
    LLM_PROVIDER: str = "groq"
    OPENAI_API_KEY: Optional[str] = None
    ANTHROPIC_API_KEY: Optional[str] = None
    GROQ_API_KEY: Optional[str] = None
    OPENROUTER_API_KEY: Optional[str] = None

    # Local HF (Offline / Free-Tier) Inference
    # FIX: Enable by default as crash-proof fallback when Groq fails
    USE_LOCAL_HF_MODEL: bool = True  # Auto-fallback when API fails
    HF_LOCAL_MODEL_NAME: str = "facebook/opt-125m"  # Tiny model, runs on CPU
    HF_LOCAL_MAX_TOKENS: int = 80  # Short responses like real SMS/chat
    HF_LOCAL_DEVICE: str = "cpu"  # Explicit so HF Spaces & local dev behave consistently
    
    # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
    # FIX 2: EXPLICIT MODEL DEFAULTS (No None = No Surprises)
    # Aligned with Groq limits and capability-aware fallback chains
    # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
    GPT_MODEL: str = "gpt-4-turbo-preview"
    CLAUDE_MODEL: str = "claude-3-sonnet-20240229"
    GROQ_MODEL: str = "llama-3.3-70b-versatile"
    
    # Per-task model routing (Production Grade)
    MAX_RETRIES: int = 2
    
    # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
    # FIX: HARD LLM CALL LIMITS (Prevent rate limit exhaustion)
    # OPTIMIZED for GUVI's 30s timeout & Groq free tier rate limits
    # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
    MAX_LLM_CALLS_PER_REQUEST: int = 6  # Reduced from 10 - fits within 20s timeout
    MAX_LLM_CALLS_PER_SESSION: int = 20  # Reduced from 30 - prevents key exhaustion
    
    # Per-task model routing (Production Grade)
    # FIX: llama-3.3-70b fails JSON_SCHEMA mode with 400. Use qwen3-32b for structured.
    GROQ_FAST_MODEL: str = "llama-3.1-8b-instant" # Blazing fast (~300 tokens/sec)
    GROQ_SMART_MODEL: str = "qwen/qwen3-32b" # Native reasoning, 60 RPM
    GROQ_NATURAL_MODEL: str = "moonshotai/kimi-k2-instruct-0905" 
    GROQ_STRUCTURED_MODEL: str = "qwen/qwen3-32b" # FIX: Supports JSON_SCHEMA properly
    GROQ_SAFETY_MODEL: str = "meta-llama/llama-guard-4-12b" 
    GROQ_SAFEGUARD_MODEL: str = "openai/gpt-oss-safeguard-20b"

    OPENROUTER_MODEL: str = "meta-llama/llama-3.1-70b-instruct"
    
    # LLM parameters
    LLM_TEMPERATURE: float = 0.7
    LLM_MAX_TOKENS: int = 500
    
    # Conversation
    MAX_CONVERSATION_LENGTH: int = 50
    CONVERSATION_TTL_HOURS: int = 24
    
    # Rate Limiting
    RATE_LIMIT_PER_MINUTE: int = 60
    
    # Feature Flags
    ENABLE_LLM_DETECTION: bool = True
    ENABLE_LLM_RESPONSES: bool = True
    ENABLE_THREAT_INTELLIGENCE: bool = True
    ENABLE_LAW_ENFORCEMENT_API: bool = False  # Disabled for hackathon
    ENABLE_ENGAGEMENT_DELAY: bool = False
    
    # Forensic Clinic (Compound Systems)
    ENABLE_MATH_FORENSICS: bool = False  # ๐Ÿงฎ Claim Verifier (Compound-Mini)
    ENABLE_VISUAL_EVIDENCE: bool = False # ๐Ÿ“Š Chart Lab (Compound)
    
    # Database (SQLite default, PostgreSQL/Supabase via env)
    DATABASE_URL: str = "sqlite+aiosqlite:///./data/honeypot.db"
    
    # Compliance
    SANDBOX_MODE: bool = False
    ANONYMIZE_LOGS: bool = True
    SYNTHETIC_DATA_ONLY: bool = False
    
    model_config = SettingsConfigDict(
        env_file=".env",
        env_file_encoding="utf-8",
        case_sensitive=True,
        extra="allow"
    )


@lru_cache()
def get_settings() -> Settings:
    return Settings()


settings = get_settings()


# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
# FIX 3 & 4: STARTUP VALIDATION (Fail Fast)
# โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
def validate_production_config():
    """Validates critical config at startup. Raises RuntimeError if invalid."""
    errors = []
    
    # FIX 3: GUVI_API_KEY must be set for scoring
    if not settings.GUVI_API_KEY:
        errors.append("GUVI_API_KEY missing โ€” scoring impossible")

    # FIX 4: Exactly ONE *external* LLM provider key must be set
    # EXCEPTION: When USE_LOCAL_HF_MODEL=True we allow zero external keys
    active_keys = [
        ("GROQ_API_KEY", settings.GROQ_API_KEY),
        ("OPENAI_API_KEY", settings.OPENAI_API_KEY),
        ("ANTHROPIC_API_KEY", settings.ANTHROPIC_API_KEY),
        ("OPENROUTER_API_KEY", settings.OPENROUTER_API_KEY),
    ]
    set_keys = [(name, key) for name, key in active_keys if key]

    if len(set_keys) == 0 and not settings.USE_LOCAL_HF_MODEL:
        errors.append("No LLM API key set โ€” system cannot function (set USE_LOCAL_HF_MODEL=True to enable offline mode)")
    elif len(set_keys) > 1:
        key_names = [name for name, _ in set_keys]
        errors.append(f"Multiple LLM API keys set ({', '.join(key_names)}) โ€” please use exactly one")
    
    # In production, fail hard
    if errors and not settings.DEBUG:
        raise RuntimeError("๐Ÿšจ CONFIG VALIDATION FAILED:\n  - " + "\n  - ".join(errors))
    elif errors:
        # In debug mode, just warn
        import warnings
        for err in errors:
            warnings.warn(f"โš ๏ธ CONFIG WARNING: {err}")


# Run validation at import time (fail fast)
validate_production_config()