diff --git a/agent_v0/server_v1/resolve_engine.py b/agent_v0/server_v1/resolve_engine.py index 28d4389a0..1712e2b83 100644 --- a/agent_v0/server_v1/resolve_engine.py +++ b/agent_v0/server_v1/resolve_engine.py @@ -2194,21 +2194,16 @@ def _validate_resolution_quality( dy = abs(resolved_y - fallback_y_pct) if dx > _RESOLUTION_MAX_DRIFT or dy > _RESOLUTION_MAX_DRIFT: logger.warning( - "[REPLAY] Resolution REJETÉE (drift trop grand) : " - "method=%s resolved=(%.3f, %.3f) expected=(%.3f, %.3f) " - "drift=(%.3f, %.3f) max=%.2f", - method, resolved_x, resolved_y, - fallback_x_pct, fallback_y_pct, - dx, dy, _RESOLUTION_MAX_DRIFT, + "[REPLAY] Drift trop grand (%.3f, %.3f) > %.2f — fallback coords enregistrées (%.3f, %.3f)", + dx, dy, _RESOLUTION_MAX_DRIFT, fallback_x_pct, fallback_y_pct, ) + # Fallback : coordonnées enregistrées lors de la capture (écran identique = safe) return { - "resolved": False, - "method": f"rejected_drift_{method}", - "reason": f"drift_dx{dx:.3f}_dy{dy:.3f}_max{_RESOLUTION_MAX_DRIFT:.2f}", + "resolved": True, + "method": "fallback_recorded_coords", + "reason": f"drift_dx{dx:.3f}_dy{dy:.3f}_using_recorded", "original_method": method, "original_score": score, - "drift_dx": round(dx, 3), - "drift_dy": round(dy, 3), "x_pct": fallback_x_pct, "y_pct": fallback_y_pct, } diff --git a/core/llm/t2a_decision.py b/core/llm/t2a_decision.py index 9902f4b05..e3cf14e40 100644 --- a/core/llm/t2a_decision.py +++ b/core/llm/t2a_decision.py @@ -49,6 +49,13 @@ Réponds STRICTEMENT en JSON valide, sans texte avant ni après : "elements_pour_hospitalisation": [], "elements_pour_forfait": [], "decision": "FORFAIT_URGENCE" | "REQUALIFICATION_HOSPITALISATION", + "decision_court": "UHCD" | "Forfait Urgences", + "preuve_critere1": "", + "critere1_valide": true | false, + "preuve_critere2": "", + "critere2_valide": true | false, + "preuve_critere3": "", + "critere3_valide": true | false, "justification": "<2-3 phrases s'appuyant explicitement sur les faits ci-dessus>", "confiance": "elevee" | "moyenne" | "faible" }} @@ -94,9 +101,8 @@ def analyze_dpi( "keep_alive": "5m", "options": { "temperature": 0.1, - "num_predict": 4000, - "num_ctx": 4096, - "reasoning_effort": "minimal", + "num_predict": 1500, + "num_ctx": 16384, }, } data = json.dumps(payload).encode("utf-8") diff --git a/visual_workflow_builder/backend/api_v3/dag_execute.py b/visual_workflow_builder/backend/api_v3/dag_execute.py index e98ffb41d..c25940e69 100644 --- a/visual_workflow_builder/backend/api_v3/dag_execute.py +++ b/visual_workflow_builder/backend/api_v3/dag_execute.py @@ -972,6 +972,13 @@ def execute_windows(): anchor_id, ) + # Propagation du by_text (ciblage textuel prioritaire sur template) + _by_text = params.get('by_text', '') + if _by_text: + action['by_text'] = _by_text + if 'target_spec' in action: + action['target_spec']['by_text'] = _by_text + # Mapper le bouton selon le type de clic VWB if vwb_type == 'double_click_anchor': action['button'] = 'double' @@ -1043,11 +1050,26 @@ def execute_windows(): # Sinon, retirer les actions fichiers du flux principal data['actions'] = non_file_actions + # Token Bearer pour le streaming server (auth obligatoire) + _stream_token = os.environ.get('RPA_API_TOKEN', '') + _stream_headers = {'Authorization': f'Bearer {_stream_token}'} if _stream_token else {} + + # L'agent Windows poll sous session "agent_demo_user" (= agent_{user_id}, user_id="demo_user") + # On injecte directement dans cette session pour éviter le transfer cross-session + # et pour que /replay/raw ne tente pas l'auto-détection d'une session "sess_*" + # (qui échoue avec "Aucune session Agent V1 active" si l'agent n'a pas créé de session V1). + if not data.get('session_id'): + data['session_id'] = 'agent_demo_user' + # Injecter le machine_id pour le ciblage multi-machine # Chercher la première machine Windows connectée si pas spécifié if 'machine_id' not in data or not data.get('machine_id'): try: - machines_resp = req.get('http://localhost:5005/api/v1/traces/stream/machines', timeout=3) + machines_resp = req.get( + 'http://localhost:5005/api/v1/traces/stream/machines', + headers=_stream_headers, + timeout=3, + ) if machines_resp.ok: machines = machines_resp.json().get('machines', []) for m in machines: @@ -1062,6 +1084,7 @@ def execute_windows(): resp = req.post( 'http://localhost:5005/api/v1/traces/stream/replay/raw', json=data, + headers=_stream_headers, timeout=30, ) return jsonify(resp.json()), resp.status_code diff --git a/visual_workflow_builder/backend/instance/workflows.db b/visual_workflow_builder/backend/instance/workflows.db index 7f74e27ff..46a49c442 100644 Binary files a/visual_workflow_builder/backend/instance/workflows.db and b/visual_workflow_builder/backend/instance/workflows.db differ