Spaces:
Sleeping
Sleeping
Update app/core/model_loader.py
Browse files- app/core/model_loader.py +35 -7
app/core/model_loader.py
CHANGED
|
@@ -13,6 +13,11 @@ load_dotenv()
|
|
| 13 |
logger = logging.getLogger(__name__)
|
| 14 |
STATS_FILE = "usage_stats.json"
|
| 15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
class LLMSingleton:
|
| 17 |
_instance = None
|
| 18 |
_instance_lock = threading.Lock()
|
|
@@ -29,21 +34,26 @@ class LLMSingleton:
|
|
| 29 |
if self._instance is not None:
|
| 30 |
raise Exception("Singleton instance already exists!")
|
| 31 |
|
| 32 |
-
|
|
|
|
|
|
|
|
|
|
| 33 |
if not self.api_key:
|
| 34 |
-
logger.warning("⚠️ GROQ_API_KEY not found
|
| 35 |
self.client = None
|
| 36 |
else:
|
| 37 |
self.client = Groq(api_key=self.api_key)
|
| 38 |
|
| 39 |
-
#
|
| 40 |
-
raw_model = os.getenv("GROQ_MODEL",
|
| 41 |
clean_model = raw_model.strip().strip('"').strip("'") if raw_model else ""
|
| 42 |
-
self.model_name = clean_model if clean_model else
|
| 43 |
|
|
|
|
|
|
|
|
|
|
| 44 |
self._stats_lock = threading.Lock()
|
| 45 |
self._rpm_lock = threading.Lock()
|
| 46 |
-
|
| 47 |
self.stats = self._load_stats()
|
| 48 |
self._check_daily_reset()
|
| 49 |
|
|
@@ -125,6 +135,7 @@ class LLMSingleton:
|
|
| 125 |
self.stats["total_requests"] += 1
|
| 126 |
self.stats["daily_requests_count"] += 1
|
| 127 |
self._save_stats()
|
|
|
|
| 128 |
with self._rpm_lock:
|
| 129 |
self.requests_this_minute += 1
|
| 130 |
return True
|
|
@@ -146,6 +157,7 @@ class LLMSingleton:
|
|
| 146 |
retries = 0
|
| 147 |
max_retries = 3
|
| 148 |
base_delay = 2
|
|
|
|
| 149 |
|
| 150 |
while retries <= max_retries:
|
| 151 |
try:
|
|
@@ -154,7 +166,7 @@ class LLMSingleton:
|
|
| 154 |
self._save_stats()
|
| 155 |
|
| 156 |
chat_completion = self.client.chat.completions.create(
|
| 157 |
-
model=
|
| 158 |
messages=[
|
| 159 |
{
|
| 160 |
"role": "system",
|
|
@@ -179,10 +191,22 @@ class LLMSingleton:
|
|
| 179 |
|
| 180 |
except Exception as e:
|
| 181 |
error_str = str(e)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
if "429" in error_str or "rate_limit_exceeded" in error_str.lower():
|
| 183 |
with self._stats_lock:
|
| 184 |
self.stats["rate_limit_hits"] += 1
|
| 185 |
self._save_stats()
|
|
|
|
| 186 |
wait_time = (base_delay * (2**retries)) + random.uniform(0.5, 1.5)
|
| 187 |
logger.warning(
|
| 188 |
f"⚠️ Groq rate limit hit. Retrying in {wait_time:.2f}s... (Attempt {retries + 1}/{max_retries})"
|
|
@@ -199,6 +223,7 @@ class LLMSingleton:
|
|
| 199 |
with self._stats_lock:
|
| 200 |
self.stats["errors"] += 1
|
| 201 |
self._save_stats()
|
|
|
|
| 202 |
logger.error("❌ Max retries reached. Request failed.")
|
| 203 |
raise RuntimeError("RATE_LIMIT_EXCEEDED")
|
| 204 |
|
|
@@ -238,8 +263,11 @@ class LLMSingleton:
|
|
| 238 |
self._save_stats()
|
| 239 |
|
| 240 |
return response_text
|
|
|
|
| 241 |
except Exception as e:
|
| 242 |
logger.error(f"Groq text generation failed: {e}")
|
| 243 |
return f"Error generating content: {str(e)}"
|
| 244 |
|
|
|
|
|
|
|
| 245 |
llm_engine = LLMSingleton.get_instance()
|
|
|
|
| 13 |
logger = logging.getLogger(__name__)
|
| 14 |
STATS_FILE = "usage_stats.json"
|
| 15 |
|
| 16 |
+
# Supported active Groq free-tier models (Fallback list)
|
| 17 |
+
DEFAULT_MODEL = "llama-3.1-8b-instant"
|
| 18 |
+
FALLBACK_MODEL = "llama-3.3-70b-versatile"
|
| 19 |
+
|
| 20 |
+
|
| 21 |
class LLMSingleton:
|
| 22 |
_instance = None
|
| 23 |
_instance_lock = threading.Lock()
|
|
|
|
| 34 |
if self._instance is not None:
|
| 35 |
raise Exception("Singleton instance already exists!")
|
| 36 |
|
| 37 |
+
# 1. Load and sanitize API Key
|
| 38 |
+
raw_key = os.getenv("GROQ_API_KEY", "")
|
| 39 |
+
self.api_key = raw_key.strip().strip('"').strip("'") if raw_key else ""
|
| 40 |
+
|
| 41 |
if not self.api_key:
|
| 42 |
+
logger.warning("⚠️ GROQ_API_KEY not found or empty. AI features will fail.")
|
| 43 |
self.client = None
|
| 44 |
else:
|
| 45 |
self.client = Groq(api_key=self.api_key)
|
| 46 |
|
| 47 |
+
# 2. Load and sanitize Model Name
|
| 48 |
+
raw_model = os.getenv("GROQ_MODEL", DEFAULT_MODEL)
|
| 49 |
clean_model = raw_model.strip().strip('"').strip("'") if raw_model else ""
|
| 50 |
+
self.model_name = clean_model if clean_model else DEFAULT_MODEL
|
| 51 |
|
| 52 |
+
logger.info(f"🔑 Groq Client initialized with model target: {self.model_name}")
|
| 53 |
+
|
| 54 |
+
# 3. Threading locks and usage stats setup
|
| 55 |
self._stats_lock = threading.Lock()
|
| 56 |
self._rpm_lock = threading.Lock()
|
|
|
|
| 57 |
self.stats = self._load_stats()
|
| 58 |
self._check_daily_reset()
|
| 59 |
|
|
|
|
| 135 |
self.stats["total_requests"] += 1
|
| 136 |
self.stats["daily_requests_count"] += 1
|
| 137 |
self._save_stats()
|
| 138 |
+
|
| 139 |
with self._rpm_lock:
|
| 140 |
self.requests_this_minute += 1
|
| 141 |
return True
|
|
|
|
| 157 |
retries = 0
|
| 158 |
max_retries = 3
|
| 159 |
base_delay = 2
|
| 160 |
+
active_model = self.model_name
|
| 161 |
|
| 162 |
while retries <= max_retries:
|
| 163 |
try:
|
|
|
|
| 166 |
self._save_stats()
|
| 167 |
|
| 168 |
chat_completion = self.client.chat.completions.create(
|
| 169 |
+
model=active_model,
|
| 170 |
messages=[
|
| 171 |
{
|
| 172 |
"role": "system",
|
|
|
|
| 191 |
|
| 192 |
except Exception as e:
|
| 193 |
error_str = str(e)
|
| 194 |
+
|
| 195 |
+
# Automatic Model Fallback handling if a 404/model_not_found occurs
|
| 196 |
+
if "404" in error_str or "model_not_found" in error_str.lower():
|
| 197 |
+
if active_model != FALLBACK_MODEL:
|
| 198 |
+
logger.warning(
|
| 199 |
+
f"⚠️ Model target '{active_model}' rejected (404). Falling back to '{FALLBACK_MODEL}'..."
|
| 200 |
+
)
|
| 201 |
+
active_model = FALLBACK_MODEL
|
| 202 |
+
continue
|
| 203 |
+
|
| 204 |
+
# Rate Limit handling (HTTP 429)
|
| 205 |
if "429" in error_str or "rate_limit_exceeded" in error_str.lower():
|
| 206 |
with self._stats_lock:
|
| 207 |
self.stats["rate_limit_hits"] += 1
|
| 208 |
self._save_stats()
|
| 209 |
+
|
| 210 |
wait_time = (base_delay * (2**retries)) + random.uniform(0.5, 1.5)
|
| 211 |
logger.warning(
|
| 212 |
f"⚠️ Groq rate limit hit. Retrying in {wait_time:.2f}s... (Attempt {retries + 1}/{max_retries})"
|
|
|
|
| 223 |
with self._stats_lock:
|
| 224 |
self.stats["errors"] += 1
|
| 225 |
self._save_stats()
|
| 226 |
+
|
| 227 |
logger.error("❌ Max retries reached. Request failed.")
|
| 228 |
raise RuntimeError("RATE_LIMIT_EXCEEDED")
|
| 229 |
|
|
|
|
| 263 |
self._save_stats()
|
| 264 |
|
| 265 |
return response_text
|
| 266 |
+
|
| 267 |
except Exception as e:
|
| 268 |
logger.error(f"Groq text generation failed: {e}")
|
| 269 |
return f"Error generating content: {str(e)}"
|
| 270 |
|
| 271 |
+
|
| 272 |
+
# Export global instance
|
| 273 |
llm_engine = LLMSingleton.get_instance()
|