Commit
·
e2eb926
1
Parent(s):
1b4b874
Add initialization diagnostics and background initialization
Browse files- Add initialization tracking (attempted, error state)
- Improve /api/health endpoint with detailed diagnostics
- Add background initialization thread for Gunicorn compatibility
- Initialize orchestrator on module import (works with Gunicorn)
- Add initialization error tracking and reporting
- Better error messages in health check endpoint
- flask_api_standalone.py +77 -10
flask_api_standalone.py
CHANGED
|
@@ -141,10 +141,15 @@ def set_security_headers(response):
|
|
| 141 |
# Global orchestrator
|
| 142 |
orchestrator = None
|
| 143 |
orchestrator_available = False
|
|
|
|
|
|
|
| 144 |
|
| 145 |
def initialize_orchestrator():
|
| 146 |
"""Initialize the AI orchestrator with local GPU models"""
|
| 147 |
-
global orchestrator, orchestrator_available
|
|
|
|
|
|
|
|
|
|
| 148 |
|
| 149 |
try:
|
| 150 |
logger.info("=" * 60)
|
|
@@ -271,6 +276,7 @@ def initialize_orchestrator():
|
|
| 271 |
except Exception as fallback_error:
|
| 272 |
logger.error(f"❌ Failed to initialize in API-only mode: {fallback_error}", exc_info=True)
|
| 273 |
orchestrator_available = False
|
|
|
|
| 274 |
return False
|
| 275 |
except Exception as e:
|
| 276 |
logger.error("=" * 60)
|
|
@@ -281,6 +287,7 @@ def initialize_orchestrator():
|
|
| 281 |
logger.error("=" * 60)
|
| 282 |
logger.error("Full traceback:", exc_info=True)
|
| 283 |
orchestrator_available = False
|
|
|
|
| 284 |
return False
|
| 285 |
|
| 286 |
# Root endpoint
|
|
@@ -309,11 +316,24 @@ def root():
|
|
| 309 |
# Health check
|
| 310 |
@app.route('/api/health', methods=['GET'])
|
| 311 |
def health_check():
|
| 312 |
-
"""Health check endpoint"""
|
| 313 |
-
|
| 314 |
-
'status': 'healthy' if orchestrator_available else '
|
| 315 |
-
'orchestrator_ready': orchestrator_available
|
| 316 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 317 |
|
| 318 |
# Chat endpoint
|
| 319 |
@app.route('/api/chat', methods=['POST'])
|
|
@@ -642,14 +662,61 @@ def set_context_mode():
|
|
| 642 |
'error': str(e)
|
| 643 |
}), 500
|
| 644 |
|
| 645 |
-
# Initialize on
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 646 |
if __name__ == '__main__':
|
| 647 |
logger.info("=" * 60)
|
| 648 |
-
logger.info("STARTING PURE FLASK API")
|
| 649 |
logger.info("=" * 60)
|
| 650 |
|
| 651 |
-
#
|
| 652 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 653 |
|
| 654 |
port = int(os.getenv('PORT', 7860))
|
| 655 |
|
|
|
|
| 141 |
# Global orchestrator
|
| 142 |
orchestrator = None
|
| 143 |
orchestrator_available = False
|
| 144 |
+
initialization_attempted = False
|
| 145 |
+
initialization_error = None
|
| 146 |
|
| 147 |
def initialize_orchestrator():
|
| 148 |
"""Initialize the AI orchestrator with local GPU models"""
|
| 149 |
+
global orchestrator, orchestrator_available, initialization_attempted, initialization_error
|
| 150 |
+
|
| 151 |
+
initialization_attempted = True
|
| 152 |
+
initialization_error = None
|
| 153 |
|
| 154 |
try:
|
| 155 |
logger.info("=" * 60)
|
|
|
|
| 276 |
except Exception as fallback_error:
|
| 277 |
logger.error(f"❌ Failed to initialize in API-only mode: {fallback_error}", exc_info=True)
|
| 278 |
orchestrator_available = False
|
| 279 |
+
initialization_error = str(fallback_error)
|
| 280 |
return False
|
| 281 |
except Exception as e:
|
| 282 |
logger.error("=" * 60)
|
|
|
|
| 287 |
logger.error("=" * 60)
|
| 288 |
logger.error("Full traceback:", exc_info=True)
|
| 289 |
orchestrator_available = False
|
| 290 |
+
initialization_error = f"{type(e).__name__}: {str(e)}"
|
| 291 |
return False
|
| 292 |
|
| 293 |
# Root endpoint
|
|
|
|
| 316 |
# Health check
|
| 317 |
@app.route('/api/health', methods=['GET'])
|
| 318 |
def health_check():
|
| 319 |
+
"""Health check endpoint with detailed diagnostics"""
|
| 320 |
+
status = {
|
| 321 |
+
'status': 'healthy' if orchestrator_available else 'unhealthy',
|
| 322 |
+
'orchestrator_ready': orchestrator_available,
|
| 323 |
+
'initialization_attempted': initialization_attempted,
|
| 324 |
+
}
|
| 325 |
+
|
| 326 |
+
if not orchestrator_available:
|
| 327 |
+
if initialization_error:
|
| 328 |
+
status['error'] = initialization_error
|
| 329 |
+
status['message'] = 'Initialization failed. Check logs for details.'
|
| 330 |
+
elif initialization_attempted:
|
| 331 |
+
status['message'] = 'Initialization completed but orchestrator not available'
|
| 332 |
+
else:
|
| 333 |
+
status['message'] = 'Initialization not yet attempted'
|
| 334 |
+
status['help'] = 'Try POST /api/initialize to trigger initialization'
|
| 335 |
+
|
| 336 |
+
return jsonify(status)
|
| 337 |
|
| 338 |
# Chat endpoint
|
| 339 |
@app.route('/api/chat', methods=['POST'])
|
|
|
|
| 662 |
'error': str(e)
|
| 663 |
}), 500
|
| 664 |
|
| 665 |
+
# Initialize orchestrator on module import (for Gunicorn compatibility)
|
| 666 |
+
# This ensures initialization happens even when running via Gunicorn
|
| 667 |
+
logger.info("=" * 60)
|
| 668 |
+
logger.info("FLASK API MODULE LOADED")
|
| 669 |
+
logger.info("=" * 60)
|
| 670 |
+
logger.info("Initializing orchestrator on module import...")
|
| 671 |
+
|
| 672 |
+
# Use a flag to prevent multiple simultaneous initializations
|
| 673 |
+
import threading
|
| 674 |
+
_init_lock = threading.Lock()
|
| 675 |
+
_init_started = False
|
| 676 |
+
|
| 677 |
+
def _start_initialization():
|
| 678 |
+
"""Start initialization in background thread to avoid blocking"""
|
| 679 |
+
global _init_started
|
| 680 |
+
with _init_lock:
|
| 681 |
+
if _init_started:
|
| 682 |
+
return
|
| 683 |
+
_init_started = True
|
| 684 |
+
|
| 685 |
+
def init_worker():
|
| 686 |
+
try:
|
| 687 |
+
logger.info("Background initialization thread started")
|
| 688 |
+
initialize_orchestrator()
|
| 689 |
+
except Exception as e:
|
| 690 |
+
logger.error(f"Background initialization failed: {e}", exc_info=True)
|
| 691 |
+
|
| 692 |
+
# Start initialization in background thread
|
| 693 |
+
init_thread = threading.Thread(target=init_worker, daemon=True, name="OrchInit")
|
| 694 |
+
init_thread.start()
|
| 695 |
+
logger.info("Initialization thread started (non-blocking)")
|
| 696 |
+
|
| 697 |
+
# Start initialization when module is imported
|
| 698 |
+
_start_initialization()
|
| 699 |
+
|
| 700 |
+
# Initialize on startup (for direct execution)
|
| 701 |
if __name__ == '__main__':
|
| 702 |
logger.info("=" * 60)
|
| 703 |
+
logger.info("STARTING PURE FLASK API (Direct Execution)")
|
| 704 |
logger.info("=" * 60)
|
| 705 |
|
| 706 |
+
# Wait a moment for background initialization if it hasn't completed
|
| 707 |
+
import time
|
| 708 |
+
if not orchestrator_available:
|
| 709 |
+
logger.info("Waiting for background initialization to complete...")
|
| 710 |
+
for i in range(30): # Wait up to 30 seconds
|
| 711 |
+
if orchestrator_available:
|
| 712 |
+
break
|
| 713 |
+
time.sleep(1)
|
| 714 |
+
if i % 5 == 0:
|
| 715 |
+
logger.info(f"Still waiting... ({i}s)")
|
| 716 |
+
|
| 717 |
+
if not orchestrator_available:
|
| 718 |
+
logger.warning("Orchestrator not ready after wait, attempting direct initialization...")
|
| 719 |
+
initialize_orchestrator()
|
| 720 |
|
| 721 |
port = int(os.getenv('PORT', 7860))
|
| 722 |
|