feat: gemma4 enrichit les éléments sans OCR via Docker (port 11435)
Quand l'OCR et SomEngine ne trouvent pas de texte sur un élément cliqué,
gemma4 (Ollama 0.20 Docker) analyse le screenshot fenêtre + position du
clic pour identifier l'élément ("voiture elec", "Settings", etc.).
Résultat : 0 clic sans by_text (vs 3 avant). Validation locale 7/8 (87%).
L'onglet Bloc-notes est maintenant correctement identifié.
Docker : ollama/ollama:0.20.2 sur port 11435 (GEMMA4_PORT env var).
Host : Ollama 0.16.3 sur port 11434 (qwen2.5vl grounding).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -430,6 +430,66 @@ def _needs_post_wait(action: dict) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Gemma4 : lecture du texte visible sur les éléments sans OCR
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Port du Docker Ollama 0.20 (gemma4)
|
||||
_GEMMA4_PORT = os.environ.get("GEMMA4_PORT", "11435")
|
||||
|
||||
|
||||
def _gemma4_read_element(
|
||||
img_b64: str,
|
||||
window_title: str = "",
|
||||
click_pos: tuple = None,
|
||||
) -> str:
|
||||
"""Demander à gemma4 d'identifier l'élément cliqué.
|
||||
|
||||
Peut recevoir soit un crop (80x80) soit un screenshot fenêtre complet.
|
||||
Si click_pos est fourni, c'est un screenshot fenêtre et gemma4 doit
|
||||
identifier l'élément à cette position.
|
||||
|
||||
Returns:
|
||||
Le texte lu (ex: "voiture electrique.txt") ou chaîne vide.
|
||||
"""
|
||||
import requests as _requests
|
||||
|
||||
context = f" in '{window_title}'" if window_title else ""
|
||||
if click_pos:
|
||||
prompt = (
|
||||
f"This is a screenshot of a window{context}. "
|
||||
f"The user clicked at position ({click_pos[0]}, {click_pos[1]}). "
|
||||
"What is the exact text or label of the element that was clicked? "
|
||||
"Answer ONLY the text, nothing else."
|
||||
)
|
||||
else:
|
||||
prompt = (
|
||||
f"This is a cropped UI element{context}. "
|
||||
"Read the exact text on this element. "
|
||||
"If it's an icon with no text, describe it in 2-3 words.\n"
|
||||
"Answer ONLY the text or label, nothing else."
|
||||
)
|
||||
|
||||
try:
|
||||
resp = _requests.post(f"http://localhost:{_GEMMA4_PORT}/api/chat", json={
|
||||
"model": "gemma4:e4b",
|
||||
"messages": [{"role": "user", "content": prompt, "images": [img_b64]}],
|
||||
"stream": False,
|
||||
"think": False,
|
||||
"options": {"temperature": 0.1, "num_predict": 30},
|
||||
}, timeout=15)
|
||||
if resp.ok:
|
||||
content = resp.json().get("message", {}).get("content", "").strip()
|
||||
# Nettoyer : retirer guillemets, points, préfixes
|
||||
content = content.strip('"\'').rstrip(".").strip()
|
||||
if content and 2 <= len(content) <= 60:
|
||||
return content
|
||||
except Exception as e:
|
||||
logger.debug("gemma4 read element échoué : %s", e)
|
||||
|
||||
return ""
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# VLM identification d'éléments UI (pour les éléments sans texte OCR)
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -903,6 +963,27 @@ def enrich_click_from_screenshot(
|
||||
element_text = som_elem["label"]
|
||||
text_source = "ocr"
|
||||
|
||||
# ── 5b. Gemma4 : identifier l'élément cliqué via le screenshot fenêtre ──
|
||||
# Quand l'OCR et SomEngine ne trouvent pas de texte, gemma4 (port 11435)
|
||||
# reçoit le screenshot fenêtre + la position du clic et décrit l'élément.
|
||||
# Un seul appel, une seule fois, pendant l'enregistrement.
|
||||
if not element_text:
|
||||
# Essayer avec le screenshot fenêtre (contexte complet)
|
||||
win_screenshot = None
|
||||
if session_dir and screenshot_id:
|
||||
win_path = Path(session_dir) / "shots" / f"{screenshot_id}_window.png"
|
||||
if win_path.is_file():
|
||||
win_screenshot = base64.b64encode(win_path.read_bytes()).decode()
|
||||
# Fallback sur le crop
|
||||
img_b64 = win_screenshot or anchor_b64
|
||||
element_text = _gemma4_read_element(
|
||||
img_b64, window_title,
|
||||
click_pos=(click_x, click_y) if win_screenshot else None,
|
||||
)
|
||||
if element_text:
|
||||
text_source = "vlm"
|
||||
logger.info("gemma4 a lu l'élément : '%s'", element_text)
|
||||
|
||||
# ── 6. Coordonnées normalisées ──
|
||||
by_position = [
|
||||
round(click_x / screen_w, 6) if screen_w > 0 else 0.0,
|
||||
|
||||
Reference in New Issue
Block a user