feat: chat unifié, GestureCatalog, Copilot, Léa UI, extraction données, vérification replay

Refonte majeure du système Agent Chat et ajout de nombreux modules :

- Chat unifié : suppression du dual Workflows/Agent Libre, tout passe par /api/chat
  avec résolution en 3 niveaux (workflow → geste → "montre-moi")
- GestureCatalog : 38 raccourcis clavier universels Windows avec matching sémantique,
  substitution automatique dans les replays, et endpoint /api/gestures
- Mode Copilot : exécution pas-à-pas des workflows avec validation humaine via WebSocket
  (approve/skip/abort) avant chaque action
- Léa UI (agent_v0/lea_ui/) : interface PyQt5 pour Windows avec overlay transparent
  pour feedback visuel pendant le replay
- Data Extraction (core/extraction/) : moteur d'extraction visuelle de données
  (OCR + VLM → SQLite), avec schémas YAML et export CSV/Excel
- ReplayVerifier (agent_v0/server_v1/) : vérification post-action par comparaison
  de screenshots, avec logique de retry (max 3)
- IntentParser durci : meilleur fallback regex, type GREETING, patterns améliorés
- Dashboard : nouvelles pages gestures, streaming, extractions
- Tests : 63 tests GestureCatalog, 47 tests extraction, corrections tests existants
- Dépréciation : /api/agent/plan et /api/agent/execute retournent HTTP 410,
  suppression du code hardcodé _plan_to_replay_actions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dom
2026-03-15 10:02:09 +01:00
parent 74a1cb4e03
commit cf495dd82f
93 changed files with 12463 additions and 1080 deletions

View File

@@ -239,33 +239,36 @@ class TestWorkflowPipelineExtractNodeVector:
# Nettoyer fichier temporaire
Path(tmp_path).unlink(missing_ok=True)
def test_extract_node_vector_legacy_format(self):
"""Test extraction vecteur format legacy (screen_template)"""
def test_extract_node_vector_v2_format(self):
"""Test extraction vecteur format v2 (template.embedding.vector_id)"""
pipeline = WorkflowPipeline()
# Créer fichier temporaire avec vecteur
with tempfile.NamedTemporaryFile(suffix='.npy', delete=False) as tmp:
test_vector = np.array([0.9, 1.0, 1.1, 1.2], dtype=np.float32)
np.save(tmp.name, test_vector)
tmp_path = tmp.name
try:
# Mock node avec screen_template legacy
# Mock node avec template.embedding.vector_id (format v2)
node = Mock()
node.template = None # Pas de template moderne
screen_template = Mock()
screen_template.embedding_prototype_path = tmp_path
node.screen_template = screen_template
node.metadata = {}
embedding = Mock()
embedding.vector_id = tmp_path
template = Mock()
template.embedding = embedding
template.embedding_prototype = None
node.template = template
# Extraire vecteur
vector = pipeline._extract_node_vector(node)
# Vérifier résultat
assert vector is not None
assert isinstance(vector, np.ndarray)
assert vector.dtype == np.float32
assert np.allclose(vector, [0.9, 1.0, 1.1, 1.2])
finally:
# Nettoyer fichier temporaire
Path(tmp_path).unlink(missing_ok=True)
@@ -277,19 +280,19 @@ class TestWorkflowPipelineExtractNodeVector:
# Test avec node sans vecteur
node = Mock()
node.template = None
node.screen_template = None
node.metadata = {}
vector = pipeline._extract_node_vector(node)
assert vector is None
# Test avec template mais pas de vecteur
node2 = Mock()
template = Mock()
template.embedding_prototype = None
template.embedding = None
node2.template = template
node2.screen_template = None
node2.metadata = {}
vector2 = pipeline._extract_node_vector(node2)
assert vector2 is None