feat(corrections): Add Correction Packs system for cross-workflow learning
Implement a complete system for capitalizing user corrections across multiple workflows and sessions. This enables automatic application of learned fixes when similar failures occur in different contexts. New components: - core/corrections/models.py: CorrectionKey, Correction, CorrectionPack models - core/corrections/correction_repository.py: JSON storage with atomic writes - core/corrections/aggregator.py: Aggregation by hash and quality filtering - core/corrections/correction_pack_service.py: CRUD, export/import, versioning - backend/api/correction_packs.py: REST API with 15 endpoints Features: - MD5-based key hashing for correction deduplication - Export/import in JSON and YAML formats - Version history with rollback support - Cross-workflow pattern detection - Integration with SelfHealingEngine for automatic application - 29 unit tests (all passing) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
201
visual_workflow_builder/backend/app.py
Normal file
201
visual_workflow_builder/backend/app.py
Normal file
@@ -0,0 +1,201 @@
|
||||
"""
|
||||
Visual Workflow Builder - Backend Flask Application
|
||||
|
||||
This is the main entry point for the Visual Workflow Builder backend API.
|
||||
It provides REST endpoints for workflow management and WebSocket support
|
||||
for real-time execution updates.
|
||||
"""
|
||||
|
||||
from flask import Flask
|
||||
from flask_cors import CORS
|
||||
from flask_socketio import SocketIO
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_caching import Cache
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables
|
||||
load_dotenv()
|
||||
|
||||
# Initialize Flask app
|
||||
app = Flask(__name__)
|
||||
|
||||
# Configuration
|
||||
app.config['SECRET_KEY'] = os.getenv('SECRET_KEY', 'dev-secret-key-change-in-production')
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv('DATABASE_URL', 'sqlite:///workflows.db')
|
||||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||||
app.config['MAX_CONTENT_LENGTH'] = 10 * 1024 * 1024 # 10MB max upload
|
||||
app.config['CACHE_TYPE'] = 'redis' if os.getenv('REDIS_URL') else 'simple'
|
||||
app.config['CACHE_REDIS_URL'] = os.getenv('REDIS_URL', 'redis://localhost:6379/0')
|
||||
|
||||
# Initialize extensions
|
||||
db = SQLAlchemy(app)
|
||||
cache = Cache(app)
|
||||
socketio = SocketIO(
|
||||
app,
|
||||
cors_allowed_origins="*",
|
||||
async_mode='threading',
|
||||
logger=True,
|
||||
engineio_logger=True
|
||||
)
|
||||
|
||||
# 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 and register blueprints (minimal set)
|
||||
from api.workflows import workflows_bp
|
||||
from api.screen_capture import screen_capture_bp
|
||||
from api.real_demo import real_demo_bp
|
||||
from api.errors import error_response
|
||||
|
||||
app.register_blueprint(workflows_bp, url_prefix='/api/workflows')
|
||||
app.register_blueprint(screen_capture_bp, url_prefix='/api/screen-capture')
|
||||
app.register_blueprint(real_demo_bp)
|
||||
|
||||
# Optional / Phase 2+ blueprints (loaded only if modules are available)
|
||||
try:
|
||||
from api.self_healing import self_healing_bp
|
||||
app.register_blueprint(self_healing_bp)
|
||||
except ImportError as e:
|
||||
print(f"⚠️ Blueprint self_healing désactivé: {e}")
|
||||
|
||||
try:
|
||||
from api.visual_targets import visual_targets_bp, init_visual_target_manager
|
||||
app.register_blueprint(visual_targets_bp)
|
||||
VISUAL_TARGETS_BP_AVAILABLE = True
|
||||
except ImportError as e:
|
||||
print(f"⚠️ Blueprint visual_targets désactivé: {e}")
|
||||
VISUAL_TARGETS_BP_AVAILABLE = False
|
||||
init_visual_target_manager = None
|
||||
|
||||
try:
|
||||
from api.element_detection import element_detection_bp, init_element_detection
|
||||
app.register_blueprint(element_detection_bp)
|
||||
ELEMENT_DETECTION_BP_AVAILABLE = True
|
||||
except ImportError as e:
|
||||
print(f"⚠️ Blueprint element_detection désactivé: {e}")
|
||||
ELEMENT_DETECTION_BP_AVAILABLE = False
|
||||
init_element_detection = None
|
||||
|
||||
try:
|
||||
from api.analytics import analytics_bp
|
||||
app.register_blueprint(analytics_bp, url_prefix='/api/analytics')
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
# Register other blueprints (optional - depends on Phase 2+ services)
|
||||
try:
|
||||
from api.templates import templates_bp
|
||||
app.register_blueprint(templates_bp, url_prefix='/api/templates')
|
||||
except ImportError as e:
|
||||
print(f"⚠️ Blueprint templates désactivé: {e}")
|
||||
|
||||
from api.node_types import node_types_bp
|
||||
app.register_blueprint(node_types_bp, url_prefix='/api/node-types')
|
||||
|
||||
try:
|
||||
from api.executions import executions_bp
|
||||
app.register_blueprint(executions_bp, url_prefix='/api/executions')
|
||||
except ImportError as e:
|
||||
print(f"⚠️ Blueprint executions désactivé: {e}")
|
||||
|
||||
try:
|
||||
from api.import_export import import_export_bp
|
||||
app.register_blueprint(import_export_bp, url_prefix='/api')
|
||||
except ImportError as e:
|
||||
print(f"⚠️ Blueprint import_export désactivé: {e}")
|
||||
|
||||
try:
|
||||
from api.correction_packs import correction_packs_bp
|
||||
app.register_blueprint(correction_packs_bp, url_prefix='/api')
|
||||
print("✅ Blueprint correction_packs enregistré")
|
||||
except ImportError as e:
|
||||
print(f"⚠️ Blueprint correction_packs désactivé: {e}")
|
||||
|
||||
|
||||
# Import WebSocket handlers (optional)
|
||||
try:
|
||||
from api import websocket_handlers # noqa: F401
|
||||
except Exception as e:
|
||||
print(f"⚠️ WebSocket handlers désactivés: {e}")
|
||||
|
||||
# Global error handlers
|
||||
@app.errorhandler(404)
|
||||
def not_found(error):
|
||||
"""Handle 404 errors"""
|
||||
return error_response(404, "Resource not found")
|
||||
|
||||
@app.errorhandler(405)
|
||||
def method_not_allowed(error):
|
||||
"""Handle 405 errors"""
|
||||
return error_response(405, "Method not allowed")
|
||||
|
||||
@app.errorhandler(500)
|
||||
def internal_error(error):
|
||||
"""Handle 500 errors"""
|
||||
return error_response(500, "Internal server error")
|
||||
|
||||
@app.errorhandler(Exception)
|
||||
def handle_exception(error):
|
||||
"""Handle all unhandled exceptions"""
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return error_response(500, f"Unexpected error: {str(error)}")
|
||||
|
||||
# Health check endpoint
|
||||
@app.route('/health')
|
||||
def health_check():
|
||||
"""Health check endpoint for monitoring"""
|
||||
return {'status': 'healthy', 'version': '1.0.0'}
|
||||
|
||||
# Create database tables
|
||||
with app.app_context():
|
||||
db.create_all()
|
||||
|
||||
# Initialize VisualTargetManager with RPA Vision V3 components (optional)
|
||||
try:
|
||||
from core.capture.screen_capturer import ScreenCapturer
|
||||
from core.detection.ui_detector import UIDetector
|
||||
from core.embedding.fusion_engine import FusionEngine
|
||||
|
||||
# Only initialize if the related blueprints were actually loaded
|
||||
if VISUAL_TARGETS_BP_AVAILABLE and init_visual_target_manager:
|
||||
screen_capturer = ScreenCapturer()
|
||||
ui_detector = UIDetector()
|
||||
fusion_engine = FusionEngine()
|
||||
init_visual_target_manager(screen_capturer, ui_detector, fusion_engine)
|
||||
|
||||
if ELEMENT_DETECTION_BP_AVAILABLE and init_element_detection:
|
||||
# Reuse the same instances when possible
|
||||
if 'ui_detector' not in locals():
|
||||
ui_detector = UIDetector()
|
||||
if 'screen_capturer' not in locals():
|
||||
screen_capturer = ScreenCapturer()
|
||||
init_element_detection(ui_detector, screen_capturer)
|
||||
|
||||
if (VISUAL_TARGETS_BP_AVAILABLE and init_visual_target_manager) or (ELEMENT_DETECTION_BP_AVAILABLE and init_element_detection):
|
||||
print("✅ Services visuels initialisés (VisualTargets / ElementDetection)")
|
||||
except ImportError as e:
|
||||
print(f"⚠️ Core RPA non disponible pour l'initialisation visuelle: {e}")
|
||||
except Exception as e:
|
||||
print(f"❌ Erreur lors de l'initialisation des services visuels: {e}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
port = int(os.getenv('PORT', 5002))
|
||||
debug = os.getenv('FLASK_ENV') == 'development'
|
||||
|
||||
socketio.run(
|
||||
app,
|
||||
host='0.0.0.0',
|
||||
port=port,
|
||||
debug=debug,
|
||||
use_reloader=debug,
|
||||
allow_unsafe_werkzeug=True # For development only
|
||||
)
|
||||
Reference in New Issue
Block a user