Spaces:
Paused
Paused
Commit ·
287a98e
1
Parent(s): 6e75c75
🔧 Fix Add Models worker timeout and missing logging method
Browse files- Add missing log_user_action method to FirebaseStructuredLogger
- Add timeout protection to Firebase sync operations (5s timeout)
- Prevent worker crashes during model addition
- Ensure proper fallback to local storage on timeout
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- src/services/firebase_logger.py +28 -0
- src/services/model_families.py +56 -18
src/services/firebase_logger.py
CHANGED
|
@@ -279,6 +279,34 @@ class FirebaseStructuredLogger:
|
|
| 279 |
print(f"Failed to log structured event to Firebase: {e}")
|
| 280 |
return False
|
| 281 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 282 |
def get_analytics(self, days: int = 30) -> Dict[str, Any]:
|
| 283 |
"""Get comprehensive analytics from Firebase"""
|
| 284 |
if not self.firebase_available:
|
|
|
|
| 279 |
print(f"Failed to log structured event to Firebase: {e}")
|
| 280 |
return False
|
| 281 |
|
| 282 |
+
def log_user_action(self, user_token: str, action: str, details: Dict[str, Any] = None,
|
| 283 |
+
admin_user: str = None) -> bool:
|
| 284 |
+
"""Log a user action event (admin actions, etc.)"""
|
| 285 |
+
if not self.firebase_available:
|
| 286 |
+
return False
|
| 287 |
+
|
| 288 |
+
try:
|
| 289 |
+
with self.lock:
|
| 290 |
+
event_data = {
|
| 291 |
+
"event_type": "user_action",
|
| 292 |
+
"timestamp": datetime.now().isoformat(),
|
| 293 |
+
"user_token": user_token,
|
| 294 |
+
"action": action,
|
| 295 |
+
"details": details or {},
|
| 296 |
+
"admin_user": admin_user
|
| 297 |
+
}
|
| 298 |
+
|
| 299 |
+
# Use timestamp-based key for ordering
|
| 300 |
+
timestamp_key = int(time.time() * 1000) # milliseconds
|
| 301 |
+
event_key = f"{timestamp_key}_{uuid.uuid4().hex[:8]}"
|
| 302 |
+
|
| 303 |
+
self.db_ref.child(event_key).set(event_data)
|
| 304 |
+
return True
|
| 305 |
+
|
| 306 |
+
except Exception as e:
|
| 307 |
+
print(f"Failed to log user action to Firebase: {e}")
|
| 308 |
+
return False
|
| 309 |
+
|
| 310 |
def get_analytics(self, days: int = 30) -> Dict[str, Any]:
|
| 311 |
"""Get comprehensive analytics from Firebase"""
|
| 312 |
if not self.firebase_available:
|
src/services/model_families.py
CHANGED
|
@@ -382,17 +382,36 @@ class ModelFamilyManager:
|
|
| 382 |
|
| 383 |
if self.firebase_db:
|
| 384 |
try:
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 388 |
except Exception as e:
|
| 389 |
print(f"Failed to save model families to Firebase (sync): {e}")
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
|
|
|
|
| 396 |
|
| 397 |
def is_model_whitelisted(self, provider: AIProvider, model_id: str) -> bool:
|
| 398 |
"""Check if a model is whitelisted for use"""
|
|
@@ -716,17 +735,36 @@ class ModelFamilyManager:
|
|
| 716 |
|
| 717 |
if self.firebase_db:
|
| 718 |
try:
|
| 719 |
-
|
| 720 |
-
|
| 721 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 722 |
except Exception as e:
|
| 723 |
print(f"Failed to save custom models to Firebase (sync): {e}")
|
| 724 |
-
|
| 725 |
-
|
| 726 |
-
|
| 727 |
-
|
| 728 |
-
|
| 729 |
-
|
|
|
|
| 730 |
|
| 731 |
def _load_custom_models(self):
|
| 732 |
"""Load custom models from persistent storage"""
|
|
|
|
| 382 |
|
| 383 |
if self.firebase_db:
|
| 384 |
try:
|
| 385 |
+
import threading
|
| 386 |
+
success = [False]
|
| 387 |
+
|
| 388 |
+
def save_config():
|
| 389 |
+
try:
|
| 390 |
+
config_ref = self.firebase_db.child('model_families_config')
|
| 391 |
+
config_ref.set(config)
|
| 392 |
+
success[0] = True
|
| 393 |
+
print("Saved model families config to Firebase (sync)")
|
| 394 |
+
except Exception as e:
|
| 395 |
+
print(f"Failed to save model families to Firebase (sync): {e}")
|
| 396 |
+
|
| 397 |
+
# Run with timeout protection
|
| 398 |
+
save_thread = threading.Thread(target=save_config)
|
| 399 |
+
save_thread.daemon = True
|
| 400 |
+
save_thread.start()
|
| 401 |
+
save_thread.join(timeout=5.0) # 5 second timeout
|
| 402 |
+
|
| 403 |
+
if save_thread.is_alive():
|
| 404 |
+
print("Firebase config save timed out, falling back to local file")
|
| 405 |
+
|
| 406 |
except Exception as e:
|
| 407 |
print(f"Failed to save model families to Firebase (sync): {e}")
|
| 408 |
+
|
| 409 |
+
# Always save to local file as backup
|
| 410 |
+
try:
|
| 411 |
+
with open("model_families_config.json", 'w') as f:
|
| 412 |
+
json.dump(config, f, indent=2)
|
| 413 |
+
except Exception as e:
|
| 414 |
+
print(f"Failed to save model families to file: {e}")
|
| 415 |
|
| 416 |
def is_model_whitelisted(self, provider: AIProvider, model_id: str) -> bool:
|
| 417 |
"""Check if a model is whitelisted for use"""
|
|
|
|
| 735 |
|
| 736 |
if self.firebase_db:
|
| 737 |
try:
|
| 738 |
+
import threading
|
| 739 |
+
success = [False]
|
| 740 |
+
|
| 741 |
+
def save_custom_models():
|
| 742 |
+
try:
|
| 743 |
+
custom_models_ref = self.firebase_db.child('custom_models')
|
| 744 |
+
custom_models_ref.set(config)
|
| 745 |
+
success[0] = True
|
| 746 |
+
print("Saved custom models to Firebase (sync)")
|
| 747 |
+
except Exception as e:
|
| 748 |
+
print(f"Failed to save custom models to Firebase (sync): {e}")
|
| 749 |
+
|
| 750 |
+
# Run with timeout protection
|
| 751 |
+
save_thread = threading.Thread(target=save_custom_models)
|
| 752 |
+
save_thread.daemon = True
|
| 753 |
+
save_thread.start()
|
| 754 |
+
save_thread.join(timeout=5.0) # 5 second timeout
|
| 755 |
+
|
| 756 |
+
if save_thread.is_alive():
|
| 757 |
+
print("Firebase custom models save timed out, falling back to local file")
|
| 758 |
+
|
| 759 |
except Exception as e:
|
| 760 |
print(f"Failed to save custom models to Firebase (sync): {e}")
|
| 761 |
+
|
| 762 |
+
# Always save to local file as backup
|
| 763 |
+
try:
|
| 764 |
+
with open("custom_models.json", 'w') as f:
|
| 765 |
+
json.dump(config, f, indent=2)
|
| 766 |
+
except Exception as e:
|
| 767 |
+
print(f"Failed to save custom models to file: {e}")
|
| 768 |
|
| 769 |
def _load_custom_models(self):
|
| 770 |
"""Load custom models from persistent storage"""
|