v1.0 - Version stable: multi-PC, détection UI-DETR-1, 3 modes exécution

- 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>
This commit is contained in:
Dom
2026-01-29 11:23:51 +01:00
parent 21bfa3b337
commit a27b74cf22
1595 changed files with 412691 additions and 400 deletions

View File

@@ -1833,6 +1833,234 @@ def api_services_stop_all():
return jsonify({"results": results, "timestamp": datetime.now().isoformat()})
# =============================================================================
# API Configuration - Interface de configuration centralisee
# =============================================================================
CONFIG_FILE_PATH = BASE_PATH / "data" / "config" / "system_config.json"
def load_system_config():
"""Charge la configuration systeme depuis le fichier JSON."""
try:
if CONFIG_FILE_PATH.exists():
with open(CONFIG_FILE_PATH, 'r', encoding='utf-8') as f:
return json.load(f)
except Exception as e:
api_logger.error(f"Erreur chargement config: {e}")
# Config par defaut
return {
"version": "1.0.0",
"services": {},
"llm": {"provider": "ollama", "base_url": "http://localhost:11434", "model": "qwen2.5:7b"},
"vlm": {"provider": "ollama", "base_url": "http://localhost:11434", "model": "qwen2.5vl:7b"},
"detection": {"owl_model": "google/owlv2-base-patch16-ensemble", "confidence_threshold": 0.3},
"database": {"type": "sqlite", "path": "data/training/workflows.db"},
"security": {"enable_encryption": True, "require_authentication": False}
}
def save_system_config(config):
"""Sauvegarde la configuration systeme dans le fichier JSON."""
try:
CONFIG_FILE_PATH.parent.mkdir(parents=True, exist_ok=True)
with open(CONFIG_FILE_PATH, 'w', encoding='utf-8') as f:
json.dump(config, f, indent=2, ensure_ascii=False)
return True
except Exception as e:
api_logger.error(f"Erreur sauvegarde config: {e}")
return False
@app.route('/api/config')
def get_config():
"""Retourne la configuration complete du systeme."""
try:
config = load_system_config()
return jsonify({
"success": True,
"config": config,
"timestamp": datetime.now().isoformat()
})
except Exception as e:
return jsonify({"success": False, "error": str(e)}), 500
@app.route('/api/config', methods=['POST'])
def update_config():
"""Met a jour la configuration complete du systeme."""
try:
new_config = request.get_json()
if not new_config:
return jsonify({"success": False, "error": "Configuration vide"}), 400
# Garder la version
current = load_system_config()
new_config['version'] = current.get('version', '1.0.0')
if save_system_config(new_config):
api_logger.info("Configuration systeme mise a jour")
return jsonify({
"success": True,
"message": "Configuration sauvegardee",
"timestamp": datetime.now().isoformat()
})
else:
return jsonify({"success": False, "error": "Erreur lors de la sauvegarde"}), 500
except Exception as e:
return jsonify({"success": False, "error": str(e)}), 500
@app.route('/api/config/<section>')
def get_config_section(section):
"""Retourne une section specifique de la configuration."""
try:
config = load_system_config()
if section not in config:
return jsonify({"success": False, "error": f"Section '{section}' introuvable"}), 404
return jsonify({
"success": True,
"section": section,
"data": config[section],
"timestamp": datetime.now().isoformat()
})
except Exception as e:
return jsonify({"success": False, "error": str(e)}), 500
@app.route('/api/config/<section>', methods=['PUT'])
def update_config_section(section):
"""Met a jour une section specifique de la configuration."""
try:
section_data = request.get_json()
if not section_data:
return jsonify({"success": False, "error": "Donnees vides"}), 400
config = load_system_config()
config[section] = section_data
if save_system_config(config):
api_logger.info(f"Section config '{section}' mise a jour")
return jsonify({
"success": True,
"message": f"Section '{section}' sauvegardee",
"timestamp": datetime.now().isoformat()
})
else:
return jsonify({"success": False, "error": "Erreur lors de la sauvegarde"}), 500
except Exception as e:
return jsonify({"success": False, "error": str(e)}), 500
@app.route('/api/config/test-connection', methods=['POST'])
def test_connection():
"""Teste la connexion a un service (LLM, base de donnees, etc.)."""
try:
data = request.get_json()
service_type = data.get('type')
if service_type == 'ollama':
# Test connexion Ollama
import urllib.request
url = data.get('base_url', 'http://localhost:11434') + '/api/tags'
try:
with urllib.request.urlopen(url, timeout=5) as response:
models = json.loads(response.read().decode())
return jsonify({
"success": True,
"message": "Connexion Ollama OK",
"models": [m.get('name') for m in models.get('models', [])]
})
except Exception as e:
return jsonify({"success": False, "error": f"Connexion Ollama echouee: {e}"})
elif service_type == 'database':
# Test connexion base de donnees
db_path = data.get('path') or 'data/training/workflows.db'
full_path = BASE_PATH / db_path
if full_path.exists():
return jsonify({"success": True, "message": f"Base de donnees accessible: {db_path}"})
else:
return jsonify({"success": False, "error": f"Fichier introuvable: {full_path}"})
elif service_type == 'service':
# Test connexion a un service HTTP
port = data.get('port')
host = data.get('host', 'localhost')
import socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(2)
try:
s.connect((host, int(port)))
return jsonify({"success": True, "message": f"Service {host}:{port} accessible"})
except:
return jsonify({"success": False, "error": f"Service {host}:{port} inaccessible"})
return jsonify({"success": False, "error": "Type de test inconnu"})
except Exception as e:
return jsonify({"success": False, "error": str(e)}), 500
@app.route('/api/config/ollama-models')
def get_ollama_models():
"""Recupere la liste des modeles Ollama disponibles."""
try:
config = load_system_config()
base_url = config.get('llm', {}).get('base_url', 'http://localhost:11434')
import urllib.request
url = base_url + '/api/tags'
with urllib.request.urlopen(url, timeout=5) as response:
data = json.loads(response.read().decode())
models = [m.get('name') for m in data.get('models', [])]
return jsonify({
"success": True,
"models": models,
"timestamp": datetime.now().isoformat()
})
except Exception as e:
return jsonify({"success": False, "error": str(e), "models": []}), 500
@app.route('/api/config/export')
def export_config():
"""Exporte la configuration en JSON."""
try:
config = load_system_config()
return Response(
json.dumps(config, indent=2, ensure_ascii=False),
mimetype='application/json',
headers={'Content-Disposition': 'attachment;filename=rpa_config.json'}
)
except Exception as e:
return jsonify({"success": False, "error": str(e)}), 500
@app.route('/api/config/import', methods=['POST'])
def import_config():
"""Importe une configuration depuis un fichier JSON."""
try:
if 'file' in request.files:
file = request.files['file']
config = json.load(file)
else:
config = request.get_json()
if not config:
return jsonify({"success": False, "error": "Configuration vide"}), 400
if save_system_config(config):
return jsonify({
"success": True,
"message": "Configuration importee avec succes",
"timestamp": datetime.now().isoformat()
})
else:
return jsonify({"success": False, "error": "Erreur lors de l'import"}), 500
except json.JSONDecodeError:
return jsonify({"success": False, "error": "Format JSON invalide"}), 400
except Exception as e:
return jsonify({"success": False, "error": str(e)}), 500
# =============================================================================
# Main
# =============================================================================