- Frontend v4 accessible sur réseau local (192.168.1.40) - Ports ouverts: 3002 (frontend), 5001 (backend), 5004 (dashboard) - Ollama GPU fonctionnel - Self-healing interactif - Dashboard confiance Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
547 lines
16 KiB
Python
547 lines
16 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Diagnostic de Performance du Backend Visual Workflow Builder
|
|
|
|
Auteur : Dom, Alice, Kiro - 08 janvier 2026
|
|
|
|
Ce script diagnostique les problèmes de performance et d'erreurs
|
|
au démarrage du backend Visual Workflow Builder.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import time
|
|
import traceback
|
|
import importlib.util
|
|
from pathlib import Path
|
|
from typing import List, Dict, Any, Optional
|
|
|
|
def print_section(title: str):
|
|
"""Affiche une section avec formatage."""
|
|
print(f"\n{'='*60}")
|
|
print(f" {title}")
|
|
print(f"{'='*60}")
|
|
|
|
def print_subsection(title: str):
|
|
"""Affiche une sous-section."""
|
|
print(f"\n{'-'*40}")
|
|
print(f" {title}")
|
|
print(f"{'-'*40}")
|
|
|
|
def check_python_version():
|
|
"""Vérifie la version de Python."""
|
|
print_subsection("Version Python")
|
|
version = sys.version_info
|
|
print(f"Version Python: {version.major}.{version.minor}.{version.micro}")
|
|
|
|
if version.major < 3 or (version.major == 3 and version.minor < 8):
|
|
print("❌ ERREUR: Python 3.8+ requis")
|
|
return False
|
|
else:
|
|
print("✅ Version Python compatible")
|
|
return True
|
|
|
|
def check_backend_structure():
|
|
"""Vérifie la structure du backend."""
|
|
print_subsection("Structure du Backend")
|
|
|
|
backend_path = Path("visual_workflow_builder/backend")
|
|
if not backend_path.exists():
|
|
print("❌ ERREUR: Répertoire backend introuvable")
|
|
return False
|
|
|
|
required_files = [
|
|
"app.py",
|
|
"models.py",
|
|
"requirements.txt",
|
|
".env",
|
|
"api/workflows.py"
|
|
]
|
|
|
|
missing_files = []
|
|
for file in required_files:
|
|
file_path = backend_path / file
|
|
if file_path.exists():
|
|
print(f"✅ {file}")
|
|
else:
|
|
print(f"❌ {file} - MANQUANT")
|
|
missing_files.append(file)
|
|
|
|
return len(missing_files) == 0
|
|
|
|
def check_dependencies():
|
|
"""Vérifie les dépendances Python."""
|
|
print_subsection("Dépendances Python")
|
|
|
|
# Lire requirements.txt
|
|
req_path = Path("visual_workflow_builder/backend/requirements.txt")
|
|
if not req_path.exists():
|
|
print("❌ requirements.txt introuvable")
|
|
return False
|
|
|
|
with open(req_path, 'r') as f:
|
|
requirements = f.read().splitlines()
|
|
|
|
# Filtrer les commentaires et lignes vides
|
|
packages = []
|
|
for line in requirements:
|
|
line = line.strip()
|
|
if line and not line.startswith('#'):
|
|
# Extraire le nom du package (avant ==, >=, etc.)
|
|
package_name = line.split('==')[0].split('>=')[0].split('<=')[0].strip()
|
|
packages.append(package_name)
|
|
|
|
missing_packages = []
|
|
for package in packages:
|
|
try:
|
|
__import__(package.replace('-', '_'))
|
|
print(f"✅ {package}")
|
|
except ImportError:
|
|
print(f"❌ {package} - NON INSTALLÉ")
|
|
missing_packages.append(package)
|
|
|
|
if missing_packages:
|
|
print(f"\n⚠️ Packages manquants: {', '.join(missing_packages)}")
|
|
print("Commande pour installer:")
|
|
print("cd visual_workflow_builder/backend && pip install -r requirements.txt")
|
|
return False
|
|
|
|
return True
|
|
|
|
def check_core_rpa_imports():
|
|
"""Vérifie les imports du core RPA Vision V3."""
|
|
print_subsection("Imports Core RPA Vision V3")
|
|
|
|
core_modules = [
|
|
"core.capture.screen_capturer",
|
|
"core.detection.ui_detector",
|
|
"core.embedding.fusion_engine"
|
|
]
|
|
|
|
available_modules = []
|
|
missing_modules = []
|
|
|
|
for module in core_modules:
|
|
try:
|
|
spec = importlib.util.find_spec(module)
|
|
if spec is not None:
|
|
print(f"✅ {module}")
|
|
available_modules.append(module)
|
|
else:
|
|
print(f"❌ {module} - MODULE INTROUVABLE")
|
|
missing_modules.append(module)
|
|
except Exception as e:
|
|
print(f"❌ {module} - ERREUR: {e}")
|
|
missing_modules.append(module)
|
|
|
|
if missing_modules:
|
|
print(f"\n⚠️ Modules Core RPA manquants: {len(missing_modules)}/{len(core_modules)}")
|
|
print("Ces modules sont optionnels mais causent des ralentissements au démarrage")
|
|
|
|
return len(missing_modules) == 0
|
|
|
|
def check_optional_blueprints():
|
|
"""Vérifie les blueprints optionnels."""
|
|
print_subsection("Blueprints Optionnels")
|
|
|
|
backend_path = Path("visual_workflow_builder/backend")
|
|
optional_blueprints = [
|
|
"api/self_healing.py",
|
|
"api/visual_targets.py",
|
|
"api/element_detection.py",
|
|
"api/analytics.py",
|
|
"api/templates.py",
|
|
"api/executions.py",
|
|
"api/import_export.py",
|
|
"api/websocket_handlers.py"
|
|
]
|
|
|
|
available_blueprints = []
|
|
missing_blueprints = []
|
|
|
|
for blueprint in optional_blueprints:
|
|
blueprint_path = backend_path / blueprint
|
|
if blueprint_path.exists():
|
|
print(f"✅ {blueprint}")
|
|
available_blueprints.append(blueprint)
|
|
else:
|
|
print(f"❌ {blueprint} - MANQUANT")
|
|
missing_blueprints.append(blueprint)
|
|
|
|
print(f"\nBluprints disponibles: {len(available_blueprints)}/{len(optional_blueprints)}")
|
|
|
|
return available_blueprints, missing_blueprints
|
|
|
|
def check_database_setup():
|
|
"""Vérifie la configuration de la base de données."""
|
|
print_subsection("Configuration Base de Données")
|
|
|
|
# Vérifier le répertoire data
|
|
data_path = Path("data")
|
|
if not data_path.exists():
|
|
print("⚠️ Répertoire 'data' manquant - sera créé automatiquement")
|
|
data_path.mkdir(exist_ok=True)
|
|
else:
|
|
print("✅ Répertoire 'data' existe")
|
|
|
|
# Vérifier le répertoire workflows
|
|
workflows_path = data_path / "workflows"
|
|
if not workflows_path.exists():
|
|
print("⚠️ Répertoire 'data/workflows' manquant - sera créé automatiquement")
|
|
workflows_path.mkdir(exist_ok=True)
|
|
else:
|
|
print("✅ Répertoire 'data/workflows' existe")
|
|
|
|
return True
|
|
|
|
def test_minimal_import():
|
|
"""Test d'import minimal du backend."""
|
|
print_subsection("Test d'Import Minimal")
|
|
|
|
try:
|
|
# Changer vers le répertoire backend
|
|
original_cwd = os.getcwd()
|
|
backend_path = Path("visual_workflow_builder/backend")
|
|
os.chdir(backend_path)
|
|
|
|
# Ajouter le répertoire au path
|
|
sys.path.insert(0, str(backend_path.absolute()))
|
|
|
|
start_time = time.time()
|
|
|
|
# Test d'import des modèles
|
|
print("Import des modèles...")
|
|
import models
|
|
print("✅ models.py importé")
|
|
|
|
# Test d'import des services
|
|
print("Import des services...")
|
|
from services import serialization
|
|
print("✅ services.serialization importé")
|
|
|
|
# Test d'import de l'API workflows
|
|
print("Import de l'API workflows...")
|
|
from api import workflows
|
|
print("✅ api.workflows importé")
|
|
|
|
import_time = time.time() - start_time
|
|
print(f"\n✅ Import minimal réussi en {import_time:.2f}s")
|
|
|
|
# Restaurer le répertoire
|
|
os.chdir(original_cwd)
|
|
sys.path.remove(str(backend_path.absolute()))
|
|
|
|
return True, import_time
|
|
|
|
except Exception as e:
|
|
print(f"❌ ERREUR lors de l'import: {e}")
|
|
traceback.print_exc()
|
|
|
|
# Restaurer le répertoire
|
|
os.chdir(original_cwd)
|
|
if str(backend_path.absolute()) in sys.path:
|
|
sys.path.remove(str(backend_path.absolute()))
|
|
|
|
return False, 0
|
|
|
|
def analyze_app_py():
|
|
"""Analyse le fichier app.py pour identifier les goulots d'étranglement."""
|
|
print_subsection("Analyse de app.py")
|
|
|
|
app_path = Path("visual_workflow_builder/backend/app.py")
|
|
if not app_path.exists():
|
|
print("❌ app.py introuvable")
|
|
return
|
|
|
|
with open(app_path, 'r', encoding='utf-8') as f:
|
|
content = f.read()
|
|
|
|
# Analyser les imports lourds
|
|
heavy_imports = [
|
|
"core.capture.screen_capturer",
|
|
"core.detection.ui_detector",
|
|
"core.embedding.fusion_engine"
|
|
]
|
|
|
|
print("Imports lourds détectés:")
|
|
for import_name in heavy_imports:
|
|
if import_name in content:
|
|
print(f"⚠️ {import_name} - peut ralentir le démarrage")
|
|
|
|
# Analyser les blueprints optionnels
|
|
optional_imports = [
|
|
"api.self_healing",
|
|
"api.visual_targets",
|
|
"api.element_detection",
|
|
"api.analytics",
|
|
"api.templates",
|
|
"api.executions",
|
|
"api.import_export"
|
|
]
|
|
|
|
print("\nBluprints optionnels chargés:")
|
|
for blueprint in optional_imports:
|
|
if blueprint in content:
|
|
print(f"⚠️ {blueprint} - chargement conditionnel")
|
|
|
|
def generate_lightweight_app():
|
|
"""Génère une version allégée de app.py."""
|
|
print_subsection("Génération d'une Version Allégée")
|
|
|
|
lightweight_app = '''#!/usr/bin/env python3
|
|
"""
|
|
Visual Workflow Builder - Backend Flask Application (Version Allégée)
|
|
|
|
Auteur : Dom, Alice, Kiro - 08 janvier 2026
|
|
|
|
Version optimisée pour un démarrage rapide avec uniquement les fonctionnalités essentielles.
|
|
"""
|
|
|
|
from flask import Flask
|
|
from flask_cors import CORS
|
|
import os
|
|
from dotenv import load_dotenv
|
|
|
|
# Load environment variables
|
|
load_dotenv()
|
|
|
|
# Initialize Flask app
|
|
app = Flask(__name__)
|
|
|
|
# Configuration minimale
|
|
app.config['SECRET_KEY'] = os.getenv('SECRET_KEY', 'dev-secret-key-change-in-production')
|
|
app.config['MAX_CONTENT_LENGTH'] = 10 * 1024 * 1024 # 10MB max upload
|
|
|
|
# Enable CORS
|
|
CORS(app, resources={
|
|
r"/api/*": {
|
|
"origins": os.getenv('CORS_ORIGINS', 'http://localhost:3000').split(','),
|
|
"methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
|
"allow_headers": ["Content-Type", "Authorization"]
|
|
}
|
|
})
|
|
|
|
# Import et register uniquement les blueprints essentiels
|
|
try:
|
|
from api.workflows import workflows_bp
|
|
app.register_blueprint(workflows_bp, url_prefix='/api/workflows')
|
|
print("✅ Blueprint workflows chargé")
|
|
except ImportError as e:
|
|
print(f"❌ Erreur chargement workflows: {e}")
|
|
|
|
try:
|
|
from api.errors import error_response
|
|
except ImportError:
|
|
def error_response(code, message):
|
|
return {"error": message}, code
|
|
|
|
# Health check endpoint
|
|
@app.route('/health')
|
|
def health_check():
|
|
"""Health check endpoint pour monitoring"""
|
|
return {'status': 'healthy', 'version': '1.0.0-lightweight'}
|
|
|
|
@app.route('/')
|
|
def index():
|
|
"""Page d'accueil du backend"""
|
|
return {
|
|
'message': 'Visual Workflow Builder Backend (Version Allégée)',
|
|
'version': '1.0.0-lightweight',
|
|
'endpoints': ['/health', '/api/workflows']
|
|
}
|
|
|
|
# Global error handlers
|
|
@app.errorhandler(404)
|
|
def not_found(error):
|
|
return error_response(404, "Resource not found")
|
|
|
|
@app.errorhandler(500)
|
|
def internal_error(error):
|
|
return error_response(500, "Internal server error")
|
|
|
|
if __name__ == '__main__':
|
|
port = int(os.getenv('PORT', 5002))
|
|
debug = os.getenv('FLASK_ENV') == 'development'
|
|
|
|
print(f"🚀 Démarrage du backend allégé sur le port {port}")
|
|
print(f"🔧 Mode debug: {debug}")
|
|
|
|
app.run(
|
|
host='0.0.0.0',
|
|
port=port,
|
|
debug=debug,
|
|
use_reloader=debug
|
|
)
|
|
'''
|
|
|
|
# Écrire le fichier
|
|
lightweight_path = Path("visual_workflow_builder/backend/app_lightweight.py")
|
|
with open(lightweight_path, 'w', encoding='utf-8') as f:
|
|
f.write(lightweight_app)
|
|
|
|
print(f"✅ Version allégée créée: {lightweight_path}")
|
|
print("Pour utiliser la version allégée:")
|
|
print("cd visual_workflow_builder/backend && python app_lightweight.py")
|
|
|
|
def generate_startup_script():
|
|
"""Génère un script de démarrage optimisé."""
|
|
print_subsection("Script de Démarrage Optimisé")
|
|
|
|
startup_script = '''#!/bin/bash
|
|
|
|
# Script de démarrage optimisé du backend Visual Workflow Builder
|
|
# Auteur : Dom, Alice, Kiro - 08 janvier 2026
|
|
|
|
echo "🚀 Démarrage du Backend Visual Workflow Builder"
|
|
echo "================================================"
|
|
|
|
# Détecter python ou python3
|
|
if command -v python3 > /dev/null; then
|
|
PYTHON_CMD=python3
|
|
elif command -v python > /dev/null; then
|
|
PYTHON_CMD=python
|
|
else
|
|
echo "❌ Erreur: Python n'est pas installé"
|
|
exit 1
|
|
fi
|
|
|
|
echo "🐍 Utilisation de: $PYTHON_CMD"
|
|
|
|
# Vérifier si nous sommes dans le bon répertoire
|
|
if [ ! -f "app.py" ]; then
|
|
echo "❌ Erreur: app.py introuvable. Exécutez depuis visual_workflow_builder/backend/"
|
|
exit 1
|
|
fi
|
|
|
|
# Créer les répertoires nécessaires
|
|
echo "📁 Création des répertoires..."
|
|
mkdir -p ../../data/workflows
|
|
mkdir -p logs
|
|
|
|
# Vérifier les dépendances critiques
|
|
echo "🔍 Vérification des dépendances..."
|
|
$PYTHON_CMD -c "import flask, flask_cors; print('✅ Dépendances de base OK')" || {
|
|
echo "❌ Dépendances manquantes. Installation..."
|
|
pip install flask flask-cors python-dotenv PyYAML
|
|
}
|
|
|
|
# Choix du mode de démarrage
|
|
echo ""
|
|
echo "Choisissez le mode de démarrage:"
|
|
echo "1) Mode normal (toutes les fonctionnalités)"
|
|
echo "2) Mode allégé (démarrage rapide)"
|
|
echo "3) Mode debug (développement)"
|
|
echo ""
|
|
read -p "Votre choix (1-3): " choice
|
|
|
|
case $choice in
|
|
1)
|
|
echo "🚀 Démarrage en mode normal..."
|
|
export FLASK_ENV=production
|
|
$PYTHON_CMD app.py
|
|
;;
|
|
2)
|
|
echo "⚡ Démarrage en mode allégé..."
|
|
export FLASK_ENV=production
|
|
$PYTHON_CMD app_lightweight.py
|
|
;;
|
|
3)
|
|
echo "🔧 Démarrage en mode debug..."
|
|
export FLASK_ENV=development
|
|
export FLASK_DEBUG=1
|
|
$PYTHON_CMD app.py
|
|
;;
|
|
*)
|
|
echo "❌ Choix invalide. Démarrage en mode normal..."
|
|
$PYTHON_CMD app.py
|
|
;;
|
|
esac
|
|
'''
|
|
|
|
# Écrire le script
|
|
script_path = Path("visual_workflow_builder/backend/start_optimized.sh")
|
|
with open(script_path, 'w', encoding='utf-8') as f:
|
|
f.write(startup_script)
|
|
|
|
# Rendre exécutable
|
|
os.chmod(script_path, 0o755)
|
|
|
|
print(f"✅ Script optimisé créé: {script_path}")
|
|
print("Pour utiliser le script optimisé:")
|
|
print("cd visual_workflow_builder/backend && ./start_optimized.sh")
|
|
|
|
def main():
|
|
"""Fonction principale de diagnostic."""
|
|
print_section("DIAGNOSTIC BACKEND VISUAL WORKFLOW BUILDER")
|
|
print("Auteur : Dom, Alice, Kiro - 08 janvier 2026")
|
|
|
|
# Vérifications de base
|
|
issues = []
|
|
|
|
if not check_python_version():
|
|
issues.append("Version Python incompatible")
|
|
|
|
if not check_backend_structure():
|
|
issues.append("Structure backend incomplète")
|
|
|
|
if not check_dependencies():
|
|
issues.append("Dépendances manquantes")
|
|
|
|
# Vérifications avancées
|
|
check_database_setup()
|
|
|
|
core_available = check_core_rpa_imports()
|
|
if not core_available:
|
|
issues.append("Modules Core RPA manquants (ralentissement)")
|
|
|
|
available_bp, missing_bp = check_optional_blueprints()
|
|
if len(missing_bp) > len(available_bp):
|
|
issues.append("Nombreux blueprints manquants")
|
|
|
|
# Test d'import
|
|
import_success, import_time = test_minimal_import()
|
|
if not import_success:
|
|
issues.append("Échec de l'import minimal")
|
|
elif import_time > 5.0:
|
|
issues.append(f"Import lent ({import_time:.2f}s)")
|
|
|
|
# Analyse du code
|
|
analyze_app_py()
|
|
|
|
# Résumé
|
|
print_section("RÉSUMÉ DU DIAGNOSTIC")
|
|
|
|
if not issues:
|
|
print("✅ Aucun problème critique détecté")
|
|
print(f"⚡ Temps d'import: {import_time:.2f}s")
|
|
else:
|
|
print("⚠️ Problèmes détectés:")
|
|
for i, issue in enumerate(issues, 1):
|
|
print(f" {i}. {issue}")
|
|
|
|
# Solutions
|
|
print_section("SOLUTIONS RECOMMANDÉES")
|
|
|
|
if "Dépendances manquantes" in issues:
|
|
print("1. Installer les dépendances:")
|
|
print(" cd visual_workflow_builder/backend")
|
|
print(" pip install -r requirements.txt")
|
|
|
|
if "Modules Core RPA manquants" in issues or import_time > 3.0:
|
|
print("2. Utiliser la version allégée pour un démarrage rapide:")
|
|
generate_lightweight_app()
|
|
|
|
print("3. Script de démarrage optimisé:")
|
|
generate_startup_script()
|
|
|
|
if import_time > 5.0:
|
|
print("4. Optimisations supplémentaires:")
|
|
print(" - Désactiver les blueprints non utilisés")
|
|
print(" - Utiliser un environnement virtuel dédié")
|
|
print(" - Vérifier les imports circulaires")
|
|
|
|
print_section("DIAGNOSTIC TERMINÉ")
|
|
print("Pour plus d'aide, consultez la documentation dans docs/")
|
|
|
|
if __name__ == "__main__":
|
|
main() |