fix(ORA): logger.info→print pour que les logs apparaissent dans nohup
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 12s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped

Le logging Python ne traverse pas le nohup de Flask. Tous les autres
modules (execute.py, intelligent_executor.py) utilisent print().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dom
2026-04-22 16:16:25 +02:00
parent eba6fea779
commit 4ab2c15e5c

View File

@@ -14,15 +14,6 @@ import logging
import time import time
import subprocess import subprocess
import sys import sys
# Forcer les logs ORA dans stdout (visible dans nohup)
logging.basicConfig(level=logging.INFO, stream=sys.stdout,
format='%(message)s', force=False)
if not logging.getLogger(__name__).handlers:
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(logging.Formatter('%(message)s'))
logging.getLogger(__name__).addHandler(handler)
logging.getLogger(__name__).setLevel(logging.INFO)
import base64 import base64
import os import os
import json import json
@@ -171,7 +162,7 @@ class ORALoop:
window_title = self._get_active_window_title() window_title = self._get_active_window_title()
ts = time.time() ts = time.time()
logger.info(f"👁 [ORA/observe] titre='{window_title}' phash={phash}") print(f"👁 [ORA/observe] titre='{window_title}' phash={phash}")
return Observation( return Observation(
screenshot=screenshot, screenshot=screenshot,
phash=phash, phash=phash,
@@ -284,7 +275,7 @@ class ORALoop:
expected_after=expected, expected_after=expected,
confidence=confidence, confidence=confidence,
) )
logger.info(f"🧠 [ORA/reason] {decision.action} target='{decision.target}' value='{decision.value[:50]}'") print(f"🧠 [ORA/reason] {decision.action} target='{decision.target}' value='{decision.value[:50]}'")
return decision return decision
# ─── Phase 2b : RAISONNE (mode instruction) ───────── # ─── Phase 2b : RAISONNE (mode instruction) ─────────
@@ -363,7 +354,7 @@ Règles:
ollama_url = os.environ.get("OLLAMA_URL", "http://localhost:11434") ollama_url = os.environ.get("OLLAMA_URL", "http://localhost:11434")
model = os.environ.get("RPA_REASONING_MODEL", "qwen2.5vl:7b") model = os.environ.get("RPA_REASONING_MODEL", "qwen2.5vl:7b")
logger.info(f"🧠 [ORA/reason_instruction] Appel VLM {model}...") print(f"🧠 [ORA/reason_instruction] Appel VLM {model}...")
response = requests.post( response = requests.post(
f"{ollama_url}/api/generate", f"{ollama_url}/api/generate",
@@ -452,7 +443,7 @@ Règles:
Returns: Returns:
LoopResult. LoopResult.
""" """
logger.info(f"🚀 [ORA/instruction] Démarrage: '{instruction}' (max {self.max_steps} étapes)") print(f"🚀 [ORA/instruction] Démarrage: '{instruction}' (max {self.max_steps} étapes)")
# --- Initialiser le contexte cognitif --- # --- Initialiser le contexte cognitif ---
if COGNITIVE_AVAILABLE and CognitiveContext is not None: if COGNITIVE_AVAILABLE and CognitiveContext is not None:
@@ -467,8 +458,8 @@ Règles:
return LoopResult(success=False, steps_completed=step_num, return LoopResult(success=False, steps_completed=step_num,
total_steps=self.max_steps, reason="stopped") total_steps=self.max_steps, reason="stopped")
logger.info(f"\n{'='*60}") print(f"\n{'='*60}")
logger.info(f"📋 [ORA/instruction] Étape {step_num + 1}/{self.max_steps}") print(f"📋 [ORA/instruction] Étape {step_num + 1}/{self.max_steps}")
# --- 1. Observer --- # --- 1. Observer ---
pre = self.observe() pre = self.observe()
@@ -478,7 +469,7 @@ Règles:
# --- Objectif atteint --- # --- Objectif atteint ---
if decision.done: if decision.done:
logger.info(f"✅ [ORA/instruction] Objectif atteint en {step_num + 1} étapes: {decision.reasoning}") print(f"✅ [ORA/instruction] Objectif atteint en {step_num + 1} étapes: {decision.reasoning}")
if self.ctx: if self.ctx:
self.ctx.record_action('done', decision.target, result='Objectif atteint', success=True) self.ctx.record_action('done', decision.target, result='Objectif atteint', success=True)
return LoopResult( return LoopResult(
@@ -555,7 +546,7 @@ Règles:
if not verification.success: if not verification.success:
retried = False retried = False
for retry in range(self.max_retries): for retry in range(self.max_retries):
logger.info(f"🔄 [ORA/instruction] Retry {retry + 1}/{self.max_retries}") print(f"🔄 [ORA/instruction] Retry {retry + 1}/{self.max_retries}")
pre_retry = self.observe() pre_retry = self.observe()
self.act(decision) self.act(decision)
time.sleep(0.3) time.sleep(0.3)
@@ -563,7 +554,7 @@ Règles:
verification = self.verify(pre_retry, post_retry, decision) verification = self.verify(pre_retry, post_retry, decision)
if verification.success: if verification.success:
retried = True retried = True
logger.info(f"✅ [ORA/instruction] Retry {retry + 1} réussi") print(f"✅ [ORA/instruction] Retry {retry + 1} réussi")
if self.ctx: if self.ctx:
self.ctx.record_action( self.ctx.record_action(
decision.action, decision.target, decision.action, decision.target,
@@ -615,7 +606,7 @@ Règles:
if step_params is None: if step_params is None:
step_params = {} step_params = {}
logger.info(f"🎯 [ORA/act] {decision.action} target='{decision.target}' value='{decision.value[:50]}'") print(f"🎯 [ORA/act] {decision.action} target='{decision.target}' value='{decision.value[:50]}'")
try: try:
if decision.action == 'click': if decision.action == 'click':
@@ -686,7 +677,7 @@ Règles:
distance = self._phash_distance(pre.phash, post.phash) distance = self._phash_distance(pre.phash, post.phash)
change_level = self._classify_change(distance) change_level = self._classify_change(distance)
logger.info(f"🔍 [ORA/verify] pHash distance={distance}{change_level}") print(f"🔍 [ORA/verify] pHash distance={distance}{change_level}")
# Déterminer le succès selon le niveau de changement # Déterminer le succès selon le niveau de changement
if change_level == 'same': if change_level == 'same':
@@ -720,10 +711,10 @@ Règles:
detail += f" | VLM: matches={matches_expected}, état='{actual_state[:80]}'" detail += f" | VLM: matches={matches_expected}, état='{actual_state[:80]}'"
# Le VLM a le dernier mot # Le VLM a le dernier mot
success = matches_expected success = matches_expected
logger.info(f"🔍 [ORA/verify/VLM] matches={matches_expected} état='{actual_state[:60]}'") print(f"🔍 [ORA/verify/VLM] matches={matches_expected} état='{actual_state[:60]}'")
emoji = "" if success else "" emoji = "" if success else ""
logger.info(f"{emoji} [ORA/verify] success={success} level={change_level}{detail[:100]}") print(f"{emoji} [ORA/verify] success={success} level={change_level}{detail[:100]}")
return VerificationResult( return VerificationResult(
success=success, success=success,
@@ -762,15 +753,15 @@ Règles:
reason=f"Trop d'étapes ({total} > max {self.max_steps})" reason=f"Trop d'étapes ({total} > max {self.max_steps})"
) )
logger.info(f"🚀 [ORA] Démarrage workflow: {total} étapes, verify={self.verify_level}, retries={self.max_retries}") print(f"🚀 [ORA] Démarrage workflow: {total} étapes, verify={self.verify_level}, retries={self.max_retries}")
for i, step in enumerate(steps): for i, step in enumerate(steps):
if not self._should_continue(): if not self._should_continue():
logger.info("⛔ [ORA] Arrêt demandé") logger.info("⛔ [ORA] Arrêt demandé")
return LoopResult(success=False, steps_completed=i, total_steps=total, reason="stopped") return LoopResult(success=False, steps_completed=i, total_steps=total, reason="stopped")
logger.info(f"\n{'='*60}") print(f"\n{'='*60}")
logger.info(f"📋 [ORA] Étape {i+1}/{total}: {step.get('action_type', '?')}{step.get('label', '')}") print(f"📋 [ORA] Étape {i+1}/{total}: {step.get('action_type', '?')}{step.get('label', '')}")
# --- 1. Observer l'état pré-action --- # --- 1. Observer l'état pré-action ---
pre = self.observe() pre = self.observe()
@@ -820,7 +811,7 @@ Règles:
# Réessayer # Réessayer
retried = False retried = False
for retry in range(self.max_retries): for retry in range(self.max_retries):
logger.info(f"🔄 [ORA] Retry {retry+1}/{self.max_retries} pour étape {i+1}") print(f"🔄 [ORA] Retry {retry+1}/{self.max_retries} pour étape {i+1}")
pre_retry = self.observe() pre_retry = self.observe()
act_success = self.act(decision, step) act_success = self.act(decision, step)
time.sleep(0.3) time.sleep(0.3)
@@ -828,7 +819,7 @@ Règles:
verification = self.verify(pre_retry, post_retry, decision) verification = self.verify(pre_retry, post_retry, decision)
if verification.success: if verification.success:
retried = True retried = True
logger.info(f"✅ [ORA] Retry {retry+1} réussi") print(f"✅ [ORA] Retry {retry+1} réussi")
break break
if not retried and not verification.success: if not retried and not verification.success:
logger.warning(f"❌ [ORA] Étape {i+1} échouée après {self.max_retries} retries") logger.warning(f"❌ [ORA] Étape {i+1} échouée après {self.max_retries} retries")
@@ -841,7 +832,7 @@ Règles:
if on_progress: if on_progress:
on_progress(i + 1, total, verification) on_progress(i + 1, total, verification)
logger.info(f"✅ [ORA] Workflow terminé avec succès: {total}/{total} étapes") print(f"✅ [ORA] Workflow terminé avec succès: {total}/{total} étapes")
return LoopResult(success=True, steps_completed=total, total_steps=total) return LoopResult(success=True, steps_completed=total, total_steps=total)
# ═══════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════
@@ -872,12 +863,12 @@ Règles:
try: try:
from core.execution.input_handler import _grounding_ui_tars from core.execution.input_handler import _grounding_ui_tars
click_label = target_desc or target_text click_label = target_desc or target_text
logger.info(f"🎯 [ORA/UI-TARS] Recherche: '{click_label}'") print(f"🎯 [ORA/UI-TARS] Recherche: '{click_label}'")
result = _grounding_ui_tars(target_text, target_desc) result = _grounding_ui_tars(target_text, target_desc)
if result: if result:
x, y = result['x'], result['y'] x, y = result['x'], result['y']
method_used = 'ui_tars' method_used = 'ui_tars'
logger.info(f"✅ [ORA/UI-TARS] Trouvé à ({x}, {y})") print(f"✅ [ORA/UI-TARS] Trouvé à ({x}, {y})")
except Exception as e: except Exception as e:
logger.debug(f"⚠️ [ORA/UI-TARS] Erreur: {e}") logger.debug(f"⚠️ [ORA/UI-TARS] Erreur: {e}")
@@ -902,7 +893,7 @@ Règles:
result_tm = cv2.matchTemplate(screen_cv, anchor_cv, cv2.TM_CCOEFF_NORMED) result_tm = cv2.matchTemplate(screen_cv, anchor_cv, cv2.TM_CCOEFF_NORMED)
_, max_val, _, max_loc = cv2.minMaxLoc(result_tm) _, max_val, _, max_loc = cv2.minMaxLoc(result_tm)
elapsed_ms = (time.time() - t0) * 1000 elapsed_ms = (time.time() - t0) * 1000
logger.info(f"⚡ [ORA/template] score={max_val:.3f} pos={max_loc} ({elapsed_ms:.0f}ms)") print(f"⚡ [ORA/template] score={max_val:.3f} pos={max_loc} ({elapsed_ms:.0f}ms)")
if max_val > 0.75: if max_val > 0.75:
x = max_loc[0] + anchor_cv.shape[1] // 2 x = max_loc[0] + anchor_cv.shape[1] // 2
y = max_loc[1] + anchor_cv.shape[0] // 2 y = max_loc[1] + anchor_cv.shape[0] // 2
@@ -918,7 +909,7 @@ Règles:
if result: if result:
x, y = result['x'], result['y'] x, y = result['x'], result['y']
method_used = 'ocr' method_used = 'ocr'
logger.info(f"🔍 [ORA/OCR] Trouvé à ({x}, {y})") print(f"🔍 [ORA/OCR] Trouvé à ({x}, {y})")
except Exception as e: except Exception as e:
logger.debug(f"⚠️ [ORA/OCR] Erreur: {e}") logger.debug(f"⚠️ [ORA/OCR] Erreur: {e}")
@@ -934,7 +925,7 @@ Règles:
logger.error(f"❌ [ORA/click] Impossible de localiser '{target_text}' — aucune méthode n'a fonctionné") logger.error(f"❌ [ORA/click] Impossible de localiser '{target_text}' — aucune méthode n'a fonctionné")
return False return False
logger.info(f"🖱️ [ORA/click] {decision.value} à ({x}, {y}) via {method_used}") print(f"🖱️ [ORA/click] {decision.value} à ({x}, {y}) via {method_used}")
if decision.value == 'double': if decision.value == 'double':
pyautogui.doubleClick(x, y) pyautogui.doubleClick(x, y)
@@ -999,7 +990,7 @@ Règles:
# Raccourci clavier normal # Raccourci clavier normal
keys = value.split('+') keys = value.split('+')
logger.info(f"⌨️ [ORA/hotkey] {'+'.join(keys)}") print(f"⌨️ [ORA/hotkey] {'+'.join(keys)}")
pyautogui.hotkey(*keys) pyautogui.hotkey(*keys)
return True return True
@@ -1010,7 +1001,7 @@ Règles:
except (ValueError, TypeError): except (ValueError, TypeError):
timeout_ms = 5000 timeout_ms = 5000
logger.info(f"⏳ [ORA/wait] {timeout_ms}ms") print(f"⏳ [ORA/wait] {timeout_ms}ms")
time.sleep(timeout_ms / 1000) time.sleep(timeout_ms / 1000)
return True return True
@@ -1036,7 +1027,7 @@ Règles:
amount = int(nums[0]) amount = int(nums[0])
scroll_value = amount if direction in ('up', 'left') else -amount scroll_value = amount if direction in ('up', 'left') else -amount
logger.info(f"📜 [ORA/scroll] {direction} x{amount}") print(f"📜 [ORA/scroll] {direction} x{amount}")
if direction in ('left', 'right'): if direction in ('left', 'right'):
pyautogui.hscroll(scroll_value) pyautogui.hscroll(scroll_value)