diff --git a/agent_v0/agent_v1/core/executor.py b/agent_v0/agent_v1/core/executor.py index d3d0f9a34..85733c2ca 100644 --- a/agent_v0/agent_v1/core/executor.py +++ b/agent_v0/agent_v1/core/executor.py @@ -675,6 +675,68 @@ class ActionExecutorV1: ) return None + def _try_dialog_resolver_server( + self, + current_title: str, + ) -> Optional[Dict[str, Any]]: + """P1 : fallback DialogResolver serveur si catalog local pas de match. + + Léa interroge l'endpoint ``/api/v1/dialog/resolve`` qui consulte + le catalog centralisé (10 entrées vs 2 locales). Si match auto + + action click_button, convertit le format ``DialogResolution`` en + ``dialog_spec`` compatible avec ``_handle_known_runtime_dialog`` + (réutilise la cascade serveur VLM + template existante). + + Désactivé si ``RPA_DIALOG_RESOLVER_AGENT_ENABLED`` n'est pas + positionné (flag agent séparé du flag serveur). Retour ``None`` + sur 503 (flag serveur OFF), pas de match, policy != auto, ou + action incompatible. + """ + if os.environ.get("RPA_DIALOG_RESOLVER_AGENT_ENABLED", "").lower() not in ( + "1", "true", "yes", "on" + ): + return None + try: + from ..config import SERVER_URL + import requests as _requests + if not SERVER_URL: + return None + resp = _requests.post( + f"{SERVER_URL.rstrip('/')}/api/v1/dialog/resolve", + json={"current_title": current_title, "evidence_texts": []}, + headers=self._auth_headers(), + timeout=3, + ) + if not resp.ok: + return None + data = resp.json() + if not data.get("matched") or data.get("policy") != "auto": + return None + action = data.get("action") or {} + if action.get("type") != "click_button": + return None + button_labels = action.get("fallback_button_labels") or [] + if action.get("button_label"): + button_labels = [action["button_label"]] + [ + b for b in button_labels if b != action["button_label"] + ] + button_labels = tuple(b for b in button_labels if b) + if not button_labels: + return None + logger.info( + f"[P1-DialogResolver] Match serveur: dialog={data.get('dialog_id')} " + f"button_texts={button_labels}" + ) + return { + "id": f"server:{data.get('dialog_id')}", + "title_patterns": (), + "button_texts": button_labels, + "skip_current_action_after_handle": True, + } + except Exception as exc: + logger.warning(f"[P1-DialogResolver] échec: {exc}") + return None + def _maybe_handle_runtime_dialog_before_pause( self, action: Dict[str, Any], @@ -691,6 +753,9 @@ class ActionExecutorV1: on applique le reflexe associe. """ dialog_spec = self._match_known_runtime_dialog(current_title) + if not dialog_spec: + # P1 : fallback DialogResolver serveur si catalog local pas de match + dialog_spec = self._try_dialog_resolver_server(current_title) if not dialog_spec: return None