feat(p1x): de-hardcode VLM models/endpoints to vlm_config (DGX-ready)

Migre les call-sites VLM serveur vers la configuration centrale pour
fonctionner sur DGX (tunnel Ollama 11434), où gemma4:* est absent et le
port Docker 11435 est mort.

- task_planner, replay_verifier, domain_context, ir_builder, resolve_engine
  (popup): modele -> vlm_config.get_vlm_model(), defaut 11435 -> 11434
  (override GEMMA4_PORT legacy conserve)
- resolve_engine (grounding bbox x2): nouvel helper
  vlm_config.get_bbox_grounding_model() (var dediee RPA_BBOX_GROUNDING_MODEL,
  fallback RPA_GROUNDING_MODEL puis qwen2.5vl:7b-rpa) -> desambiguise le
  conflit D5-v3b, bbox_2d + num_ctx 4096 preserves
- safety_checks_provider: defaut -> get_vlm_model(), override
  RPA_SAFETY_CHECKS_LLM_MODEL preserve
- ui_detector: default_factory + resolution lazy (corrige aussi un gel a
  l'import), pas d'appel reseau a l'import
- field_extractor: property lazy via vlm_config

TDD strict (RED->GREEN), 305 tests verts, tests mockes HTTP (zero dependance
DGX reel), aucun alias Ollama.

Hors perimetre (arbitrage Dom): client Lea agent_v1/executor.py (gele),
chemin V4 observe_reason_act (RPA_REASONING_MODEL), core/config.py defaults.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dom
2026-06-03 14:06:03 +02:00
parent 4e7c2a7628
commit 4dc7d840d6
21 changed files with 739 additions and 37 deletions

View File

@@ -11,7 +11,7 @@ Basée sur l'architecture éprouvée de la V2.
from typing import List, Dict, Optional, Any, Tuple
from pathlib import Path
from dataclasses import dataclass
from dataclasses import dataclass, field
import logging
import os
import time
@@ -25,6 +25,7 @@ logger = logging.getLogger(__name__)
from ..models.ui_element import UIElement, UIElementEmbeddings, VisualFeatures
from .ollama_client import OllamaClient, check_ollama_available
from . import vlm_config
# Import OWL-v2 (optionnel)
try:
@@ -71,10 +72,13 @@ class BoundingBox:
@dataclass
class DetectionConfig:
"""Configuration de la détection UI hybride"""
# VLM — modèle configurable via variable d'environnement RPA_VLM_MODEL
# Par défaut : gemma4:e4b (meilleur grounding + contextualisation)
# Fallback : qwen3-vl:8b si gemma4 non disponible
vlm_model: str = os.environ.get("RPA_VLM_MODEL", os.environ.get("VLM_MODEL", "gemma4:e4b"))
# VLM — modèle configurable via RPA_VLM_MODEL / VLM_MODEL.
# default_factory : lu à l'instanciation (pas figé à l'import) ; None si non
# défini → résolution lazy via vlm_config.get_vlm_model() dans _initialize_vlm
# (pas de hardcode, pas d'appel réseau à l'import).
vlm_model: Optional[str] = field(
default_factory=lambda: os.environ.get("RPA_VLM_MODEL") or os.environ.get("VLM_MODEL")
)
vlm_endpoint: str = "http://localhost:11434"
use_vlm_classification: bool = True # Utiliser VLM pour classifier
@@ -136,11 +140,16 @@ class UIDetector:
"""Initialiser le client VLM"""
try:
if check_ollama_available(self.config.vlm_endpoint):
# Résolution lazy : si aucun modèle explicite, vlm_config résout
# (avec fallback) en interrogeant /api/tags. On normalise la config
# pour que les métadonnées de sortie reflètent le modèle réel.
model = self.config.vlm_model or vlm_config.get_vlm_model(self.config.vlm_endpoint)
self.config.vlm_model = model
self.vlm_client = OllamaClient(
endpoint=self.config.vlm_endpoint,
model=self.config.vlm_model
model=model
)
logger.info(f"✓ VLM initialized: {self.config.vlm_model}")
logger.info(f"✓ VLM initialized: {model}")
else:
logger.warning("Ollama not available, VLM classification disabled")
self.vlm_client = None

View File

@@ -234,6 +234,33 @@ def get_grounding_profile(endpoint: str = DEFAULT_OLLAMA_ENDPOINT) -> dict:
}
def get_bbox_grounding_model() -> str:
"""Retourne le modèle pour le grounding **format bbox_2d natif** (qwen2.5vl).
Distinct de get_grounding_profile() (format JSON {x_pct,y_pct} via prefill,
défaut qwen3.5:9b). Les chemins bbox_2d de resolve_engine
(`parse_bbox_to_norm` / `parse_bbox_to_norm_validated`) exigent un modèle
de la famille qwen2.5vl qui émet des coordonnées en pixels.
D5-v3b (2026-06-03) : désambiguïse l'env var. Historiquement le site bbox
lisait `RPA_GROUNDING_MODEL`, partagé avec get_grounding_profile() qui
attend un modèle JSON → conflit documenté. On introduit une var dédiée.
Ordre de résolution :
1. RPA_BBOX_GROUNDING_MODEL (dédié, prioritaire)
2. RPA_GROUNDING_MODEL (rétrocompat — ancien comportement)
3. DEFAULT_GROUNDING_FALLBACK (qwen2.5vl:7b-rpa, présent sur DGX)
Returns:
Nom du modèle bbox_2d (ex: "qwen2.5vl:7b-rpa")
"""
return (
os.environ.get("RPA_BBOX_GROUNDING_MODEL")
or os.environ.get("RPA_GROUNDING_MODEL")
or DEFAULT_GROUNDING_FALLBACK
)
def needs_think_false(model_name: str) -> bool:
"""Détermine si un modèle nécessite think=false dans le payload.