feat(health): gate vision + détection des modèles aveugles

Détecte les modèles VLM/grounding « aveugles » (capabilities sans vision, ex.
UI-TARS réimporté sans mmproj) pour éviter le HTTP 500 silencieux masqué par
la cascade de grounding.

- core/detection/model_health.py : has_vision_capability() (cache, fail-open)
  + smoke_check_models()
- core/execution/input_handler.py : gate vision dans _grounding_ui_tars
  (skip propre vers niveau 3 si modèle aveugle, plus de 500 silencieux)
- tests/unit/test_model_health.py : 6 tests (vision/aveugle/fail-open/cache/smoke)

Incident 2026-06-08 : UI-TARS sans mmproj -> niveau 2 cascade en 500 silencieux,
non détecté (hors chemin runtime démo + échec avalé par fallback + zéro test).
NB : le smoke non bloquant au démarrage (api_stream.py startup) reste dans le WIP
de la branche, mélangé au préflight non committé.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dom
2026-06-08 11:51:18 +02:00
parent 5b2afa3629
commit d00fe7b00b
3 changed files with 167 additions and 0 deletions

View File

@@ -590,6 +590,16 @@ def _grounding_ui_tars(target_text: str, target_description: str = "", monitor_i
ollama_url = os.environ.get("OLLAMA_URL", "http://localhost:11434")
model = "0000/ui-tars-1.5-7b-q8_0:7b"
# Gate santé : ne pas envoyer d'image à un modèle « aveugle » (sans capacité vision).
# Évite le HTTP 500 silencieux qui masquait la panne (incident 2026-06-08, UI-TARS sans mmproj).
from core.detection.model_health import has_vision_capability
if not has_vision_capability(model, ollama_url):
logger.warning(
"[Grounding/UI-TARS] modèle '%s' sans capacité 'vision' — skip propre vers niveau 3",
model,
)
return None
logger.info(f"[Grounding/UI-TARS] Envoi à {model}: '{prompt}'")
response = requests.post(