# Carte fonctionnelle RPA Vision V3 — 2026-05-08 Branche : `feature/qw-suite-mai` | HEAD : `731b5bcae` Vue produit (pas code). Inventaire des fonctionnalités telles qu'elles existent réellement dans le repo à cette date. --- ## 1. Modes opérationnels de Léa (agent_v1) Le client Léa V1 (`agent_v0/agent_v1/`) n'expose **pas** d'enum `MODE_*` discret. Son comportement runtime est piloté par trois booléens cumulables dans `AgentState` (`ui/shared_state.py`) : | Mode | Module(s) concerné(s) | Activation | Statut | |---|---|---|---| | Capture / enregistrement | `ui/shared_state.py:30` (`_recording`), `core/captor.py`, `vision/capturer.py` | Bouton "Démarrer" dans systray (`smart_tray.py:377`) ou ChatWindow → `state.start_recording(name)` | actif | | Replay (polling serveur) | `main.py:130` (`_replay_poll_loop`), `core/executor.py:510-1900` | Boucle daemon permanente, lancée à l'init de `AgentV1` indépendamment de toute session — poll `GET /replay/next` toutes les 1 s sur `agent_{user_id}` | actif | | Heartbeat permanent (background) | `main.py:131` (`_background_heartbeat_loop`) | Daemon permanent, screenshot toutes les 5 s vers `POST /traces/stream/image` (session `bg_{machine_id}`) | actif | | Heartbeat session | `main.py:434` (`_heartbeat_loop`) | Démarre seulement quand `session_id` actif (pendant un enregistrement) | actif | | Watchdog fichier `command.json` | `main.py:247` (`_command_watchdog_loop`) | Poll fichier `C:\rpa_vision\command.json` toutes les 1 s, exécute `execute_normalized_order` | actif (legacy GHOST replay) | | Capture à la demande HTTP | `ui/capture_server.py` | Mini-serveur HTTP local port 5006 lancé au boot | actif | | Auto-stop session | `main.py:160` (`_auto_stop_loop`) | Notifie 10 min avant et stoppe à `MAX_SESSION_DURATION_S` | actif | **Modes "shadow / copilot / assisté / autonomous"** : ils n'existent **pas** côté client Léa V1. Côté serveur, `execution_mode` est un paramètre de replay (`"autonomous"` par défaut, voir `api_stream.py:2969`, `replay_engine.py:1520`). Les valeurs détectées : `"autonomous"`, `"verified"`, `"supervised"` (déduit du test `_exec_mode != "autonomous"` à `api_stream.py:2974`). Le frontend VWB définit en plus `'basic' | 'intelligent' | 'debug' | 'verified'` (`types.ts:15`) — **ce sont des modes VWB, pas des modes Léa**. [À VÉRIFIER PAR DOM] Endpoints `/api/v1/shadow/*` (start/stop/feedback/build/understanding) existent côté serveur (`api_stream.py:1661-1820`) mais aucun n'est appelé depuis le client Léa V1 (grep dans `agent_v0/agent_v1/` : zéro hit). [À VÉRIFIER PAR DOM] --- ## 2. Capacités du serveur (rpa-streaming + dépendances) 54 endpoints exposés par `agent_v0/server_v1/api_stream.py`. ### 2.1 Streaming session / heartbeat - `POST /api/v1/traces/stream/register` — Enregistrer une session (session_id + machine_id) - `POST /api/v1/traces/stream/event` — Pousser un événement clavier/souris/fenêtre - `POST /api/v1/traces/stream/image` — Pousser un screenshot (heartbeat ou shot d'action) - `POST /api/v1/traces/stream/finalize` — Clore une session - `GET /api/v1/traces/stream/processing/status` — État de la file de traitement - `POST /api/v1/traces/stream/processing/requeue` — Re-traiter une session déjà finalisée - `GET /api/v1/traces/stream/stats` — Statistiques globales du serveur - `GET /api/v1/traces/stream/machines` — Liste machines enrôlées - `GET /api/v1/traces/stream/sessions` — Liste sessions (filtrable par machine_id) ### 2.2 Replay (next/report/resolve_target/pause) - `POST /api/v1/traces/stream/replay` — Lancer un replay depuis un workflow_id - `POST /api/v1/traces/stream/replay/raw` — Lancer un replay depuis une liste d'actions brutes - `POST /api/v1/traces/stream/replay-session` — Re-rejouer une session enregistrée - `POST /api/v1/traces/stream/replay/single` — Enqueuer une action unique - `POST /api/v1/traces/stream/replay/plan` — Lancer depuis un ExecutionPlan (V4) - `POST /api/v1/traces/stream/workflow/compile` — Compiler session → WorkflowIR + ExecutionPlan - `GET /api/v1/traces/stream/replay/next` — Action suivante à exécuter (pollée par Léa) - `POST /api/v1/traces/stream/replay/result` — Rapport d'exécution d'une action - `POST /api/v1/traces/stream/replay/error_callback` — Callback erreur configurable - `GET /api/v1/traces/stream/replay/{replay_id}` — État d'un replay - `GET /api/v1/traces/stream/replays` — Liste des replays - `POST /api/v1/traces/stream/replay/{replay_id}/resume` — Reprendre après pause supervisée - `POST /api/v1/traces/stream/replay/{replay_id}/cancel` — Annuler un replay - `POST /api/v1/traces/stream/replay/resolve_target` — Résoudre la position d'une ancre (cascade vLLM/Ollama) - `POST /api/v1/traces/stream/replay/pre_analyze` — Pré-analyse de l'écran avant action ### 2.3 Extraction (text / table / décision T2A) Pas d'endpoint HTTP dédié — ces actions sont enqueuées côté serveur via le replay et traitées sans round-trip Léa par `replay_engine.py:_handle_extract_text_action / _handle_extract_table_action / _handle_t2a_decision_action` (modules `core/llm/ocr_extractor.py` et `core/llm/t2a_decision.py`). ### 2.4 Federation / learning packs - `GET /api/v1/traces/stream/learning-pack/export` — Export anonymisé (par client_id) - `POST /api/v1/traces/stream/learning-pack/import` — Import + merge dans FAISS global ### 2.5 Health / monitoring - `GET /health` — Healthcheck simple - `GET /api/v1/traces/stream/workflows` — Liste workflows visibles - `POST /api/v1/traces/stream/reload-workflows` — Rechargement à chaud - `GET /api/v1/traces/stream/workflow/{workflow_id}` — Détail workflow - `GET /api/v1/traces/stream/session/{session_id}` — Détail session - `GET /api/v1/audit/history` — Historique audit (RGPD/IA Act) - `GET /api/v1/audit/summary` — Résumé audit - `GET /api/v1/audit/export` — Export audit ### 2.6 Autres - `POST /api/v1/shadow/start` — Démarrer un observateur shadow (existe, voir §1) - `POST /api/v1/shadow/stop` — Arrêter - `POST /api/v1/shadow/feedback` — Feedback humain sur une étape observée - `GET /api/v1/shadow/{session_id}/understanding` — Lire la compréhension construite - `POST /api/v1/shadow/build` — Compiler en workflow - `POST /api/v1/task` — Tâche planifiée (TaskPlanner) - `GET /api/v1/task/capabilities` — Capacités déclarées (action types) - `POST /api/v1/chat/session` — Créer une session de chat serveur - `POST /api/v1/chat/{session_id}/message` — Envoyer message - `GET /api/v1/chat/{session_id}/history` — Historique - `POST /api/v1/chat/{session_id}/confirm` — Confirmer un plan - `GET /api/v1/chat/sessions` — Liste sessions chat - `POST /api/v1/agents/enroll` — Enrôler un nouvel agent (nouvelle machine) - `POST /api/v1/agents/uninstall` — Désenrôler - `GET /api/v1/agents/fleet` — État de la flotte --- ## 3. Stack VLM / grounding active Synthèse de `docs/HISTORIQUE_VLM_IMPLEMENTATIONS_2026-05-08.md` (§1, §6). | Modèle | Backend | Module appelant | Statut | |---|---|---|---| | `InfiX-ai/InfiGUI-G1-3B` 4-bit NF4 | Transformers in-process (Flask) | `core/grounding/server.py` (port 8200) | câblé mais inactif (pas dans la cascade actuelle) | | `InfiX-ai/InfiGUI-G1-3B` 4-bit NF4 | Transformers subprocess one-shot | `core/grounding/infigui_worker.py` | câblé mais inactif (utilisé en fallback de la socket) | | `InfiX-ai/InfiGUI-G1-3B` 4-bit NF4 | Transformers daemon Unix socket `/run/rpa/grounding.sock` | `core/grounding/infigui_server.py` (service systemd `rpa-grounding.service`) | service présent — `rpa-grounding.service.parked` détecté dans `/etc/systemd/system/` [À VÉRIFIER PAR DOM] | | `Qwen/Qwen2.5-VL-7B-Instruct-AWQ` | vLLM HTTP OpenAI-compat (port `VLLM_PORT=8100`) | `agent_v0/server_v1/resolve_engine.py:785-816` (`_resolve_by_grounding`) | utilisé en prod — essai 1 (fallback Ollama) [À VÉRIFIER PAR DOM si vLLM tourne] | | `qwen2.5vl:7b` | Ollama HTTP `/api/chat` | `resolve_engine.py:818-832` (fallback de vLLM) | utilisé en prod — fallback principal de la cascade | | `qwen2.5vl:7b` | Ollama HTTP `/api/chat` | `resolve_engine.py:2536-2585` (`_locate_popup_button`) | utilisé en prod — cas spécifique popup | | `qwen3-vl:8b`, `gemma4:e4b` | Ollama HTTP | `core/detection/ollama_client.py` (utilisé par `ui_detector.py`, `som_engine.py`, `vram_orchestrator.py`) | utilisé en prod — détection UI + SoM côté core/detection | | `qwen2.5vl:3b` | Ollama HTTP | `visual_workflow_builder/backend/api_v3/capture.py:245` (description anchor) | utilisé en prod — chaîne capture VWB | | `qwen3-vl:8b` | Ollama HTTP | `visual_workflow_builder/backend/api_v3/dag_execute.py:468` (LLMActionHandler) | utilisé en prod — DAG executor LLM | | `qwen2.5vl` | Ollama HTTP | `visual_workflow_builder/backend/catalog_routes_v2_vlm.py` | utilisé en prod — catalog UI | | OpenAI-compat cloud (OpenAI/Gemini/Anthropic) | HTTP cloud (opt-in `VLM_ALLOW_CLOUD=true`) | `visual_workflow_builder/backend/vlm_provider.py` | câblé mais inactif (cloud désactivé par défaut, contraire à la directive 100% local) | | `cckevinn/SeeClick` (Qwen-VL) | Transformers in-process | `core/detection/seeclick_adapter.py` | téléchargé non utilisé (signalé "cassé" par commit `d1b556b6c`, exporté par `__init__.py` mais zéro call site actif) | | `Owlv2` (Google OWL-v2) | Transformers in-process | `core/detection/owl_detector.py` (via `ui_detector.py:31,113,126`) | câblé mais inactif (présent dans la chaîne de détection — bench récent inconnu) [À VÉRIFIER PAR DOM] | | `ByteDance-Seed/UI-TARS-1.5-7B` | Transformers (référencé) | `tools/start_grounding_server.sh` | référencé en doc seulement (modèle remplacé par InfiGUI dans le code par commit `77faa03ec`) | --- ## 4. Capacités du VWB (visual_workflow_builder) ### 4.1 Modes de construction de workflows Trois voies coexistent : 1. **Capture interactive** : sélection de zones/ancres via `POST /api/v3/capture/screen` + `POST /api/v3/capture/select` (frontend `CapturePanel.tsx`, `CaptureLibrary.tsx`). 2. **Édition manuelle dans le canvas** : ajout d'étapes via `POST /api/v3/workflow/{id}/step` (frontend `StepNode.tsx`, `ToolPalette.tsx`, `PropertiesPanel.tsx`). 3. **Import de workflow appris par Léa** : `POST /api/v3/learned-workflows/{id}/import` lit les workflows produits côté streaming server (sessions enregistrées) et les insère en SQLite VWB. ### 4.2 Types d'actions supportées 36 types listés dans `frontend_v4/src/types.ts:40-82` (constante `ACTIONS`). **Souris** : - `click_anchor` — Clic gauche sur élément visuel — needs anchor : oui - `double_click_anchor` — Double-clic — needs anchor : oui - `right_click_anchor` — Clic droit (menu contextuel) — needs anchor : oui - `hover_anchor` — Survol — needs anchor : oui - `drag_drop_anchor` — Glisser-déposer vers cible — needs anchor : oui - `scroll_to_anchor` — Défiler jusqu'à élément — needs anchor : oui - `focus_anchor` — Donner focus clavier — needs anchor : oui **Clavier** : - `type_text` — Saisir texte (templating `{{var}}`) — needs anchor : non - `type_secret` — Saisir secret depuis coffre-fort — needs anchor : non - `keyboard_shortcut` — Combinaison touches — needs anchor : non **Attente** : - `wait_for_anchor` — Attendre apparition élément — needs anchor : oui **Données** : - `extract_text` — OCR EasyOCR fr+en sur dernier screenshot → variable — needs anchor : non - `extract_table` — OCR + filtre regex → liste structurée → variable — needs anchor : oui - `screenshot_evidence` — Capture preuve — needs anchor : non - `download_to_folder` — Télécharger fichier — needs anchor : non - `db_save_data` / `db_read_data` — BDD locale — needs anchor : non - `import_excel` / `db_foreach` — Boucle Excel/CSV → BDD — needs anchor : non **Logique** : - `visual_condition` — Branchement si ancre trouvée — needs anchor : oui (hidden : true) - `loop_visual` — Boucle tant qu'ancre visible — needs anchor : oui (hidden : true) - `pause_for_human` — Pause supervisée + safety_checks (QW4) — needs anchor : non - `t2a_decision` — Analyse DPI urgences via LLM local (qwen2.5:7b par défaut) — needs anchor : non **IA (Ollama vision/text)** : - `ai_ocr` — OCR IA sur ancre — needs anchor : oui - `ai_summarize` — Résumé LLM — needs anchor : non - `ai_extract` — Extraction structurée IA — needs anchor : oui - `ai_classify` — Classification — needs anchor : non - `ai_analyze_text` — Analyse libre — needs anchor : non - `ai_custom` — Appel IA libre avec system prompt — needs anchor : non **LLM via DAGExecutor (parallèle)** : - `llm_analyze` / `llm_translate` / `llm_extract_data` / `llm_generate` — needs anchor : non **Fichiers** : - `file_list_dir` / `file_create_dir` / `file_move` / `file_copy` / `file_sort_by_ext` — needs anchor : non **Validation** : - `verify_element_exists` — needs anchor : oui - `verify_text_content` — needs anchor : oui ### 4.3 Intégration avec Léa Le VWB **ne pousse pas** un workflow à Léa : il l'**enregistre** côté streaming server. Mécanisme : 1. Workflow sauvé en SQLite VWB (`workflows.db`). 2. `POST /api/v3/workflow/{id}/export-for-lea` (`learned_workflows.py:413`) sérialise et envoie au streaming server (proxy `STREAMING_SERVER_URL=http://localhost:5005`). 3. Lancement : frontend appelle `POST /api/v3/execute/start` (`execute.py:1528`) qui transite vers `POST /api/v1/traces/stream/replay` côté streaming server. 4. Léa V1 récupère ensuite les actions une à une via son polling `GET /replay/next` (cf. §1). ### 4.4 Bibliothèque de captures Disponible. Architecture v2 (avril 2026) : - PNG HD écrit dans `data/library_captures/{id}.png` (source de vérité) - `data/capture_library.json` = métadonnées + thumbnail base64 640×360 q85 (rapide à charger pour la grille) - Endpoints : `GET/POST /api/v3/capture/library`, `POST /api/v3/capture/library/upload`, `GET /api/v3/capture/library/{id}/full`, `DELETE /api/v3/capture/library/{id}` - Permet à l'utilisateur de réutiliser des captures (ancres) entre workflows sans recapturer. --- ## 5. Capacités de l'agent_chat ### 5.1 Endpoints 23 routes Flask dans `agent_chat/app.py` : - `GET /` — UI principale chat - `GET /classic` — UI classique - `GET /api/status` — Statut serveur - `GET /api/workflows` — Liste workflows disponibles - `POST /api/workflows/refresh` — Recharger - `GET /api/machines` — Liste machines - `POST /api/search` — Recherche workflow - `POST /api/execute` — Exécuter un workflow nommé - `GET /api/history` — Historique conversations - `POST /api/chat` — Endpoint chat principal (routage NLP) - `POST /api/gpu/` — Contrôle GPU (start/stop/status) - `GET /api/llm/status` — Statut Ollama - `POST /api/llm/model` — Changer modèle actif - `POST /api/agent/plan` — Planifier (autonomous_planner) - `POST /api/agent/execute` — Lancer plan - `GET /api/agent/status` — Statut agent - `GET /api/gestures` — Catalogue de gestures réflexes - `POST /api/chat/upload` — Upload pièce jointe - `GET /api/help` — Aide - `POST /api/urgences/parse` — Parsing intent "traite N dossiers" (gemma3:1b) - `POST /api/urgences/start` — Démarre l'orchestrateur urgences - `GET /api/urgences/status/` — État orchestrateur - `GET /api/urgences/list` — Liste orchestrations en cours ### 5.2 Cas d'usage métier - **Orchestration urgences GHT** (`urgences_orchestrator.py`) : reçoit "traite N dossiers" en chat, parse via `gemma3:1b`, ouvre Chrome (Win+R) sur la maquette Easily Assure via `/replay/raw`, extrait la liste IPP avec `extract_table`, puis pour chaque IPP lance le workflow `Urgence_unit` via `/replay` avec `variables={"patient_id": ipp}`. Synthèse finale postée dans le chat. État pollable via `/api/urgences/status/`. - **Recherche/exécution workflow par nom naturel** (`/api/search` + `/api/execute`) — résolution sémantique nom utilisateur → workflow_id. - **Plan autonome** (`/api/agent/plan` + `/api/agent/execute`) — `autonomous_planner.py` planifie un workflow inédit à partir d'un objectif libre via `qwen2.5:7b`. ### 5.3 Modèles LLM utilisés - `gemma3:1b` — NLP intent parsing urgences (`urgences_orchestrator.py:58`, env `LEA_NLP_MODEL`) - `qwen2.5:7b` — chat principal + autonomous_planner (`app.py:229,319`, `intent_parser.py:283,690`) - `qwen3:8b` — modèle Léa par défaut env `LEA_LLM_MODEL` (`app.py:675`), avec `think=False` désactivé (qwen3) --- ## 6. Modules orphelins (code présent mais non câblé) | Module | Chemin | Pourquoi orphelin (factuel) | Mentionné en commit ou doc ? | |---|---|---|---| | `core/grounding/fast_pipeline.py` | `core/grounding/` | Référencé uniquement par `core/execution/observe_reason_act.py:1639` (lui-même semi-orphelin, voir ci-dessous). Zéro import depuis `agent_v0/server_v1/`, `visual_workflow_builder/backend/`, `agent_chat/`. | commit `b30d4b665` (Phase 4 FAST→SMART→THINK) | | `core/grounding/fast_detector.py` | `core/grounding/` | Zéro import depuis le code actif. | commit `ea36bba5c` (Phase 1-2) | | `core/grounding/smart_matcher.py` | `core/grounding/` | Zéro import depuis le code actif. | commit `ea36bba5c` | | `core/grounding/think_arbiter.py` | `core/grounding/` | Zéro import depuis le code actif. | commit `e4a48e78b` (Phase 3) | | `core/grounding/shadow_learning_hook.py` | `core/grounding/` | Zéro import dans `agent_v0/server_v1/`, `visual_workflow_builder/`, `agent_chat/`. | commit `73cea2385` (Phase 6) ; mémoire mentionne "ShadowLearningHook non branché" | | `core/grounding/template_matcher.py` | `core/grounding/` | Zéro import depuis le code actif. | commit `9da589c8c` (création) | | `core/grounding/pipeline.py` | `core/grounding/` | Zéro import depuis le code actif. | commit `9da589c8c` | | `core/grounding/element_signature.py` | `core/grounding/` | Zéro import depuis le code actif. | commit `e4a48e78b` | | `core/grounding/server.py` (Flask 8200) | `core/grounding/` | Daemon Flask single-thread alternatif à `infigui_server.py` (Unix socket). Pas de service systemd actif pointant dessus. | doc `tools/start_grounding_server.sh` (toujours obsolète, pointe UI-TARS) | | `core/detection/seeclick_adapter.py` | `core/detection/` | Encore exporté par `core/detection/__init__.py` mais zéro call site actif. Signalé "cassé" par commit `d1b556b6c`. | commit `21bfa3b33` (création) ; `d1b556b6c` (suppression call site) | | `core/learning/target_memory_store.py` | `core/learning/` | Référencé par `agent_v0/server_v1/replay_memory.py:62` et `replay_learner.py:210` — **pas orphelin** (semi-actif via replay_memory). | mémoire "V3/V4 découplés au runtime" | | `core/learning/continuous_learner.py` | `core/learning/` | Zéro import depuis code actif. | — | | `core/learning/feedback_processor.py` | `core/learning/` | Zéro import. | — | | `core/learning/versioned_store.py` | `core/learning/` | Zéro import. | — | | `core/execution/target_resolver.py` | `core/execution/` | Zéro import depuis code actif. | mémoire "TargetResolver cross-frame bug" | | `core/execution/target_memory.py` | `core/execution/` | Zéro import. | — | | `core/execution/action_executor.py` | `core/execution/` | Zéro import. | — | | `core/execution/execution_robustness.py` | `core/execution/` | Zéro import. | — | | `core/execution/recovery_strategies.py` | `core/execution/` | Zéro import. | — | | `core/execution/observe_reason_act.py` | `core/execution/` | Importé par `visual_workflow_builder/backend/api_v3/execute.py:1431,1955` (mode `verified` ORALoop). Pas pleinement orphelin mais activé seulement dans ce mode. | mémoire "Phase 5 intégration FAST→SMART→THINK dans ORA" | | `core/healing/healing_engine.py` + `confidence_scorer.py` + `recovery_logger.py` + `learning_repository.py` | `core/healing/` | Zéro import depuis `agent_v0/server_v1/` et `visual_workflow_builder/backend/api_v3/`. Seul `execution_integration.py` est référencé (2 occurrences). | — | | Service systemd `rpa-streaming.service` | `deploy/systemd/` | Présent dans le repo mais `/etc/systemd/system/rpa-streaming.service.parked` → **inactif** sur la machine de Dom. | — | | Service systemd `rpa-grounding.service` | `deploy/systemd/` | `/etc/systemd/system/rpa-grounding.service.parked` → **inactif**. | commit `3d6868f02` | | Service systemd `rpa-vision-v3-api.service` / `rpa-vision-v3-worker.service` / `rpa-vision-v3-dashboard.service` / `rpa-agent-chat.service` | `deploy/systemd/` | Tous trouvés `.parked` dans `/etc/systemd/system/`. | — | | `agent_v0/server_v1/safety_checks_provider.py` | `agent_v0/server_v1/` | **Pas orphelin** : importé par `api_stream.py:2980` (QW4 build_pause_payload). Listé pour contexte. | mémoire "QW4 safety_checks hybrides" | | Endpoints `/api/v1/shadow/*` | `agent_v0/server_v1/api_stream.py:1661-1820` | Définis côté serveur, **aucun appelant identifié** dans `agent_v0/agent_v1/` (Léa client) ni dans `visual_workflow_builder/`, ni dans `agent_chat/`. | — [À VÉRIFIER PAR DOM] | --- ## 7. À vérifier avec Dom (synthèse des `[À VÉRIFIER PAR DOM]`) - **Modes Léa "shadow / copilot / assisté"** : seuls les booléens `_recording` / `_replay_active` existent côté client. Confirme-tu qu'aujourd'hui Léa V1 n'a effectivement que ces deux modes runtime (et que les modes VWB `basic/intelligent/debug/verified` ne pilotent rien du client) ? - **Endpoints `/api/v1/shadow/*`** côté serveur : zéro appelant identifié dans le repo. Sont-ils consommés par un script externe / une démo, ou candidats à archivage ? - **Service `rpa-grounding.service`** : présent dans `deploy/systemd/` mais en `.parked` sur la machine. La cascade vLLM→Ollama tourne donc sans `infigui_server.py` ? Confirmer que le grounding Transformers est bien désactivé en prod actuellement. - **Service `rpa-streaming.service`** : trouvé `.parked` dans `/etc/systemd/system/`. Le streaming server tourne-t-il via `svc.sh` / `run.sh` au lieu de systemd ? - **`core/detection/seeclick_adapter.py`** : encore exporté par `__init__.py` mais signalé cassé. Sortir de l'export ou tenter une réparation pour Qwen3-VL ? - **`core/detection/owl_detector.py` (Owlv2)** : câblé via `ui_detector.py` mais aucun bench récent. Encore appelé en prod ou candidat à l'archivage ? - **vLLM (port 8100)** : code prêt dans `resolve_engine.py:785-816`. Confirmer si vLLM tourne actuellement ou si la cascade saute systématiquement à Ollama. - **Mode VWB `verified`** : seul mode qui active `core/execution/observe_reason_act.py` (ORALoop). Est-il utilisé en démo GHT ou réservé au debug ?