feat(vwb): log supervised competence verdicts

This commit is contained in:
Dom
2026-05-29 18:36:06 +02:00
parent 7ad260d02f
commit aba849324a
18 changed files with 1082 additions and 5 deletions

View File

@@ -49,6 +49,7 @@ def competence_yaml_to_vwb_preview(
"review_status": "preview",
"tags": ["lea", "competence", competence.learning_state],
"competence_id": competence.id,
"verdict_endpoint": f"/api/v1/lea/competences/{competence.id}/verdict",
"learning_state": competence.learning_state,
"source_path": competence.source_path,
"readonly": True,
@@ -120,6 +121,8 @@ def _method_to_vwb_step(
"timeout_ms": int(params.get("timeout_ms", 5000)),
"poll_interval_ms": int(params.get("poll_interval_ms", 250)),
"evidence_required": params.get("evidence_required", "window_or_process"),
"supervised_popup_detection": True,
"popup_policy": "pause_only",
**source_params,
}
return _step(
@@ -185,6 +188,7 @@ def _pause_step(
"message": message,
"phase": phase,
"verdict_required": verdict_required,
"verdict_endpoint": f"/api/v1/lea/competences/{competence.id}/verdict",
"competence_id": competence.id,
"source": f"lea_competence:{competence.id}",
"write_back_enabled": False,

View File

@@ -0,0 +1,69 @@
"""Helpers for supervised popup detection without auto-resolution."""
from __future__ import annotations
from typing import Any, Dict, Mapping, Optional
def build_unexpected_popup_pause(
detected_popup: Optional[Mapping[str, Any]],
*,
expected_state: Mapping[str, Any],
competence_id: str = "",
source_method_id: str = "",
) -> Optional[Dict[str, Any]]:
"""Return a human-pause payload when a detected popup is not expected."""
if not detected_popup:
return None
popup = dict(detected_popup)
popup_title = _popup_title(popup)
if popup_title and _title_is_expected(popup_title, expected_state):
return None
competence_label = f" pour {competence_id}" if competence_id else ""
title_label = popup_title or str(popup.get("pattern") or "popup inconnue")
return {
"needs_human": True,
"pause_reason": "unexpected_popup",
"message": (
f"Popup inattendue detectee{competence_label}: '{title_label}'. "
"Mode supervise: aucune resolution automatique n'est lancee."
),
"detected_popup": popup,
"expected_state": dict(expected_state or {}),
"competence_id": competence_id,
"source_method_id": source_method_id,
"auto_resolution": False,
"write_back_enabled": False,
}
def _popup_title(popup: Mapping[str, Any]) -> str:
for key in ("title", "window_title", "target", "pattern", "name"):
value = popup.get(key)
if isinstance(value, str) and value.strip():
return value.strip()
return ""
def _title_is_expected(title: str, expected_state: Mapping[str, Any]) -> bool:
normalized = _norm(title)
exact = expected_state.get("window_title_in")
if isinstance(exact, str):
exact = [exact]
if isinstance(exact, list) and any(_norm(candidate) == normalized for candidate in exact):
return True
contains = expected_state.get("window_title_contains")
if isinstance(contains, str):
contains = [contains]
if isinstance(contains, list) and any(_norm(candidate) in normalized for candidate in contains):
return True
return False
def _norm(value: Any) -> str:
return str(value or "").strip().casefold()