feat(server): R1 — import auto du workflow appris vers la DB VWB (gated)
Some checks failed
tests / Lint (ruff + black) (push) Failing after 1m44s
tests / Tests unitaires (sans GPU) (push) Failing after 1m49s
tests / Tests sécurité (critique) (push) Has been skipped

finalize_session appelle _maybe_import_to_vwb : si RPA_R1_AUTO_IMPORT (OFF par
défaut), le workflow appris est assaini (sanitize_workflow_dict) puis importé en
DB VWB rejouable via le pont idempotent (import_core_workflow_to_db), dans un
app-context VWB lazy mutualisé (vwb_db). NON bloquant : un échec n'interrompt
jamais la finalisation. Rend l'appris rejouable sans geste manuel (R1).
Tests : câblage du seam + gating du flag + non-régression.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dom
2026-06-29 17:44:24 +02:00
parent 6075717353
commit c82829f2bb
2 changed files with 256 additions and 0 deletions

View File

@@ -3066,6 +3066,8 @@ class StreamProcessor:
saved_path = self._persist_workflow(workflow, session_id, machine_id=machine_id)
# Stocker le machine_id dans le workflow pour le filtrage
workflow._machine_id = machine_id
# R1 : import auto en DB VWB (rejouable) — gated RPA_R1_AUTO_IMPORT, non bloquant.
self._maybe_import_to_vwb(workflow, session_id, machine_id)
# Récupérer les métadonnées applicatives de la session
session_state = self.session_manager.get_session(session_id)
@@ -4444,6 +4446,45 @@ class StreamProcessor:
logger.error(f"Erreur sauvegarde workflow {session_id}: {e}")
return None
def _import_workflow_to_vwb(self, workflow, session_id: str, machine_id: str) -> Dict[str, Any]:
"""Importer le workflow appris dans la DB VWB rejouable (Maillon A / R1).
Rend l'appris rejouable sans geste manuel, de façon idempotente (fusion
par signature de trajectoire). Suppose un app-context VWB actif fournissant
``db.session`` (créé par l'appelant côté worker).
"""
from .pii_sanitizer import sanitize_workflow_dict
from services.learned_workflow_bridge import import_core_workflow_to_db
from db.models import db
# Assainir la PII (cibles OCR `by_text`, noms) avant dépôt en DB VWB.
core_dict = sanitize_workflow_dict(workflow.to_dict())
return import_core_workflow_to_db(
core_dict,
machine_id=machine_id,
source_session_id=session_id,
db_session=db.session,
)
def _vwb_app_context(self):
"""Couplage worker→DB VWB mutualisé (un seul pont, cf. vwb_db).
Délègue au helper module ``vwb_db.vwb_app_context`` partagé entre R1 et
l'extraction métier — pas de duplication de l'app Flask/init_app.
"""
from .vwb_db import vwb_app_context
return vwb_app_context()
def _maybe_import_to_vwb(self, workflow, session_id: str, machine_id: str) -> None:
"""Import auto de l'appris en DB VWB, gated par RPA_R1_AUTO_IMPORT (OFF
par défaut) et NON bloquant : un échec ne casse jamais la finalisation."""
if os.environ.get("RPA_R1_AUTO_IMPORT", "false").lower() not in ("true", "1", "yes"):
return
try:
with self._vwb_app_context():
self._import_workflow_to_vwb(workflow, session_id, machine_id)
except Exception as e:
logger.warning("[R1] import VWB auto échoué (non bloquant): %s", e)
def _build_raw_session_fallback(self, session, raw_dict):
"""Construire un RawSession manuellement si from_dict échoue."""
from core.models.raw_session import RawSession, Event, Screenshot, RawWindowContext