forked from Fahad1110136/AI-AnamolyDetector
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconfig.py
More file actions
278 lines (216 loc) · 8.36 KB
/
Copy pathconfig.py
File metadata and controls
278 lines (216 loc) · 8.36 KB
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
"""
Configuration module for AI Anomaly Detection System.
Handles API keys, thresholds, and system-wide settings.
Security: Uses lazy loading for secrets - keys are only accessed when
actually needed, following the principle of least privilege.
"""
import os
from dataclasses import dataclass, field
from functools import cached_property
from pathlib import Path
from typing import Optional
# Load .env file if present (must be called before accessing env vars)
def _load_dotenv():
"""Load environment variables from .env file if it exists."""
try:
from dotenv import load_dotenv
env_path = Path(__file__).parent / ".env"
if env_path.exists():
load_dotenv(env_path)
return True
return False
except ImportError:
return False
# Load on module import
_ENV_LOADED = _load_dotenv()
def _get_env(key: str, default: str = "") -> str:
"""Safely get environment variable with default."""
return os.getenv(key, default)
def _get_env_float(key: str, default: float) -> float:
"""Safely get float from environment variable."""
try:
return float(os.getenv(key, str(default)))
except ValueError:
return default
@dataclass
class DetectionThresholds:
"""Risk and confidence thresholds for decision routing."""
low_risk: float = field(default_factory=lambda: _get_env_float("THRESHOLD_LOW_RISK", 0.2))
high_risk: float = field(default_factory=lambda: _get_env_float("THRESHOLD_HIGH_RISK", 0.8))
mid_confidence: float = field(default_factory=lambda: _get_env_float("THRESHOLD_MID_CONFIDENCE", 0.6))
high_confidence: float = field(default_factory=lambda: _get_env_float("THRESHOLD_HIGH_CONFIDENCE", 0.85))
injection_block_threshold: float = 0.5
@dataclass
class DetectorWeights:
"""Weights for combining detector scores."""
hallucination: float = 1.0
policy: float = 1.2
injection: float = 1.5
class SecureConfig:
"""
Secure configuration with lazy-loaded secrets.
API keys are NOT loaded at import time - they are only accessed
when explicitly requested, following least privilege principle.
"""
def __init__(self):
self._groq_key: Optional[str] = None
self._cerebras_key: Optional[str] = None
self._keys_loaded = False
# =========================================================================
# SECRETS - Lazy loaded, accessed only when needed
# =========================================================================
@property
def groq_api_key(self) -> str:
"""
Get Groq API key. Loaded lazily on first access.
Raises ValueError if not configured when accessed.
"""
if self._groq_key is None:
self._groq_key = _get_env("GROQ_API_KEY", "")
return self._groq_key
@property
def cerebras_api_key(self) -> str:
"""
Get Cerebras API key. Loaded lazily on first access.
Raises ValueError if not configured when accessed.
"""
if self._cerebras_key is None:
self._cerebras_key = _get_env("CEREBRAS_API_KEY", "")
return self._cerebras_key
def has_groq_key(self) -> bool:
"""Check if Groq API key is configured without exposing it."""
return bool(self.groq_api_key)
def has_cerebras_key(self) -> bool:
"""Check if Cerebras API key is configured without exposing it."""
return bool(self.cerebras_api_key)
def has_any_llm_key(self) -> bool:
"""Check if at least one LLM API key is configured."""
return self.has_groq_key() or self.has_cerebras_key()
def validate_keys(self) -> tuple[bool, str]:
"""
Validate that required API keys are present.
Returns (is_valid, message).
"""
if not self.has_any_llm_key():
return False, (
"No LLM API keys configured. "
"Set GROQ_API_KEY or CEREBRAS_API_KEY in your .env file. "
"See .env.example for reference."
)
return True, "API keys configured successfully"
# =========================================================================
# NON-SECRET CONFIG - Safe to load at any time
# =========================================================================
@cached_property
def primary_provider(self) -> str:
"""Primary LLM provider to use."""
default = "groq" if self.has_groq_key() else "cerebras"
return _get_env("LLM_PRIMARY_PROVIDER", default)
@cached_property
def groq_model(self) -> str:
"""Groq model to use."""
return _get_env("GROQ_MODEL", "llama-3.3-70b-versatile")
@cached_property
def cerebras_model(self) -> str:
"""Cerebras model to use."""
return _get_env("CEREBRAS_MODEL", "llama3.1-8b")
@cached_property
def max_tokens(self) -> int:
"""Maximum tokens for LLM responses."""
return 1024
@cached_property
def temperature(self) -> float:
"""LLM temperature setting."""
return 0.7
@cached_property
def vector_db_path(self) -> str:
"""Path to vector database storage."""
return _get_env("VECTOR_DB_PATH", "./chroma_db")
@cached_property
def system_memory_path(self) -> str:
"""Path to system memory file."""
return _get_env("SYSTEM_MEMORY_PATH", "./system_memory.json")
@cached_property
def thresholds(self) -> DetectionThresholds:
"""Detection thresholds."""
return DetectionThresholds()
@cached_property
def weights(self) -> DetectorWeights:
"""Detector weights."""
return DetectorWeights()
@cached_property
def high_risk_domains(self) -> tuple:
"""Domains requiring extra scrutiny."""
return ("medical", "legal", "financial", "self-harm")
# =============================================================================
# LEGACY COMPATIBILITY - For backward compatibility with existing code
# =============================================================================
@dataclass
class LLMConfig:
"""LLM provider configuration (legacy compatibility wrapper)."""
def __init__(self):
self._secure = SecureConfig()
@property
def groq_api_key(self) -> str:
return self._secure.groq_api_key
@property
def cerebras_api_key(self) -> str:
return self._secure.cerebras_api_key
@property
def primary_provider(self) -> str:
return self._secure.primary_provider
@property
def groq_model(self) -> str:
return self._secure.groq_model
@property
def cerebras_model(self) -> str:
return self._secure.cerebras_model
@property
def max_tokens(self) -> int:
return self._secure.max_tokens
@property
def temperature(self) -> float:
return self._secure.temperature
class SystemConfig:
"""
Master configuration container.
Provides unified access to all configuration while
maintaining lazy loading of secrets.
"""
def __init__(self):
self._secure = SecureConfig()
self._llm: Optional[LLMConfig] = None
@property
def llm(self) -> LLMConfig:
if self._llm is None:
self._llm = LLMConfig()
return self._llm
@property
def thresholds(self) -> DetectionThresholds:
return self._secure.thresholds
@property
def weights(self) -> DetectorWeights:
return self._secure.weights
@property
def vector_db_path(self) -> str:
return self._secure.vector_db_path
@property
def system_memory_path(self) -> str:
return self._secure.system_memory_path
@property
def high_risk_domains(self) -> tuple:
return self._secure.high_risk_domains
def validate(self) -> tuple[bool, str]:
"""Validate configuration is complete and valid."""
return self._secure.validate_keys()
def load_config() -> SystemConfig:
"""Load configuration with environment variable overrides."""
config = SystemConfig()
is_valid, message = config.validate()
if not is_valid:
print(f"WARNING: {message}")
return config
# Global config instance - secrets NOT loaded until accessed
CONFIG = load_config()
# Expose SecureConfig for direct use
SECURE_CONFIG = SecureConfig()