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:
@@ -32,6 +32,20 @@ from agent_v0.server_v1.task_planner import TaskPlanner, TaskPlan
|
||||
# Fixtures
|
||||
# =========================================================================
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _mock_vlm_model():
|
||||
"""Neutralise la résolution VLM (pas de dépendance Ollama/DGX en test).
|
||||
|
||||
Par défaut, get_vlm_model() interroge Ollama (/api/tags) ; on la fige
|
||||
pour garder les tests déterministes et hors réseau.
|
||||
"""
|
||||
with patch(
|
||||
"agent_v0.server_v1.task_planner.vlm_config.get_vlm_model",
|
||||
return_value="gemma4:latest",
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def planner():
|
||||
"""TaskPlanner avec port gemma4 factice."""
|
||||
@@ -77,6 +91,65 @@ def _mock_gemma4_response(content: str):
|
||||
return mock_resp
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# Tests : dé-hardcodage VLM (modèle via vlm_config, endpoint 11434)
|
||||
# =========================================================================
|
||||
|
||||
class TestVlmConfigDehardcode:
|
||||
"""Le modèle et l'endpoint ne doivent plus être codés en dur."""
|
||||
|
||||
def test_understand_utilise_modele_de_vlm_config(self, sample_workflows):
|
||||
"""Le payload understand() utilise le modèle résolu par vlm_config."""
|
||||
captured = {}
|
||||
|
||||
def capture_post(url, json=None, **kwargs):
|
||||
captured["url"] = url
|
||||
captured["model"] = (json or {}).get("model")
|
||||
return _mock_gemma4_response("COMPRIS: NON\nWORKFLOW: AUCUN\nBOUCLE: NON\n")
|
||||
|
||||
with patch(
|
||||
"agent_v0.server_v1.task_planner.vlm_config.get_vlm_model",
|
||||
return_value="modele-resolu:test",
|
||||
), patch("requests.post", side_effect=capture_post):
|
||||
planner = TaskPlanner(domain_id="generic")
|
||||
planner.understand("Ouvre le bloc-notes", available_workflows=sample_workflows)
|
||||
|
||||
assert captured["model"] == "modele-resolu:test"
|
||||
|
||||
def test_steps_to_actions_utilise_modele_de_vlm_config(self):
|
||||
"""Le payload _steps_to_actions() utilise le modèle résolu par vlm_config."""
|
||||
captured = {}
|
||||
|
||||
def capture_post(url, json=None, **kwargs):
|
||||
captured["model"] = (json or {}).get("model")
|
||||
return _mock_gemma4_response('{"type": "wait", "duration_ms": 100}\n')
|
||||
|
||||
with patch(
|
||||
"agent_v0.server_v1.task_planner.vlm_config.get_vlm_model",
|
||||
return_value="modele-resolu:test",
|
||||
), patch("requests.post", side_effect=capture_post):
|
||||
planner = TaskPlanner(domain_id="generic")
|
||||
planner._steps_to_actions([{"description": "1. Attendre"}], {})
|
||||
|
||||
assert captured["model"] == "modele-resolu:test"
|
||||
|
||||
def test_endpoint_par_defaut_cible_11434(self, monkeypatch, sample_workflows):
|
||||
"""Sans GEMMA4_PORT, l'endpoint vise 11434 (Ollama/tunnel DGX), pas 11435."""
|
||||
monkeypatch.delenv("GEMMA4_PORT", raising=False)
|
||||
captured = {}
|
||||
|
||||
def capture_post(url, json=None, **kwargs):
|
||||
captured["url"] = url
|
||||
return _mock_gemma4_response("COMPRIS: NON\nWORKFLOW: AUCUN\nBOUCLE: NON\n")
|
||||
|
||||
with patch("requests.post", side_effect=capture_post):
|
||||
planner = TaskPlanner(domain_id="generic")
|
||||
planner.understand("Ouvre le bloc-notes", available_workflows=sample_workflows)
|
||||
|
||||
assert ":11434" in captured["url"]
|
||||
assert ":11435" not in captured["url"]
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# Tests : understand — ordre simple
|
||||
# =========================================================================
|
||||
|
||||
Reference in New Issue
Block a user