"""Tests pour le feature flag AGENT_CHAT_ENABLE_OWL (C1b). Contexte : depuis 2026-05-25, OWL-v2 ne se charge plus au boot du service rpa-agent-chat par défaut (économie ~600 MiB VRAM constatée par Codex après restart C1). Activation via AGENT_CHAT_ENABLE_OWL=1. Référence : inbox_claude/2026-05-25_1327_codex-to-claude_C1-post-restart-ok-c1b-vram.md Fix : agent_chat/autonomous_planner.py _init_visual_detection() l. 139-... """ from __future__ import annotations import sys from pathlib import Path import pytest ROOT = Path(__file__).resolve().parents[2] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) @pytest.mark.unit def test_owl_skipped_by_default(monkeypatch): """Sans AGENT_CHAT_ENABLE_OWL, OWL ne doit PAS se charger au boot.""" monkeypatch.delenv("AGENT_CHAT_ENABLE_OWL", raising=False) from agent_chat.autonomous_planner import AutonomousPlanner planner = AutonomousPlanner(llm_model="qwen2.5:7b") assert planner._owl_detector is None, ( f"OWL chargé alors que flag OFF (économie VRAM perdue) : " f"{planner._owl_detector}" ) @pytest.mark.unit def test_owl_skipped_when_flag_zero(monkeypatch): """AGENT_CHAT_ENABLE_OWL=0 → OWL skip.""" monkeypatch.setenv("AGENT_CHAT_ENABLE_OWL", "0") from agent_chat.autonomous_planner import AutonomousPlanner planner = AutonomousPlanner(llm_model="qwen2.5:7b") assert planner._owl_detector is None @pytest.mark.unit def test_owl_skipped_when_flag_false(monkeypatch): """AGENT_CHAT_ENABLE_OWL=false → OWL skip (alias accepté).""" monkeypatch.setenv("AGENT_CHAT_ENABLE_OWL", "false") from agent_chat.autonomous_planner import AutonomousPlanner planner = AutonomousPlanner(llm_model="qwen2.5:7b") assert planner._owl_detector is None @pytest.mark.unit def test_owl_init_attempted_when_flag_one(monkeypatch): """AGENT_CHAT_ENABLE_OWL=1 → tentative d'init (succès ou échec rattrapé). Le test ne valide PAS que OWL charge effectivement (dépend GPU + modèle HF disponible), juste que le code passe la garde du flag et tente l'init. On mocke OwlDetector pour vérifier qu'il est instancié. """ monkeypatch.setenv("AGENT_CHAT_ENABLE_OWL", "1") from agent_chat import autonomous_planner as ap_module calls = [] class FakeOwl: def __init__(self, **kwargs): calls.append(kwargs) monkeypatch.setattr(ap_module, "OwlDetector", FakeOwl) monkeypatch.setattr(ap_module, "VISUAL_DETECTION_AVAILABLE", True) planner = ap_module.AutonomousPlanner(llm_model="qwen2.5:7b") assert planner._owl_detector is not None, ( "OWL doit être instancié quand AGENT_CHAT_ENABLE_OWL=1" ) assert len(calls) == 1 assert calls[0].get("confidence_threshold") == 0.1 @pytest.mark.unit def test_owl_device_override(monkeypatch): """AGENT_CHAT_OWL_DEVICE=cpu force le device CPU même si CUDA dispo.""" monkeypatch.setenv("AGENT_CHAT_ENABLE_OWL", "1") monkeypatch.setenv("AGENT_CHAT_OWL_DEVICE", "cpu") from agent_chat import autonomous_planner as ap_module calls = [] class FakeOwl: def __init__(self, **kwargs): calls.append(kwargs) monkeypatch.setattr(ap_module, "OwlDetector", FakeOwl) monkeypatch.setattr(ap_module, "VISUAL_DETECTION_AVAILABLE", True) ap_module.AutonomousPlanner(llm_model="qwen2.5:7b") assert calls[0].get("device") == "cpu" @pytest.mark.unit def test_owl_init_exception_caught(monkeypatch): """Si OWL crash à l'init (OOM CUDA, modèle absent, etc.), AutonomousPlanner doit continuer à booter avec _owl_detector=None.""" monkeypatch.setenv("AGENT_CHAT_ENABLE_OWL", "1") from agent_chat import autonomous_planner as ap_module class CrashOwl: def __init__(self, **kwargs): raise RuntimeError("CUDA out of memory (simulation)") monkeypatch.setattr(ap_module, "OwlDetector", CrashOwl) monkeypatch.setattr(ap_module, "VISUAL_DETECTION_AVAILABLE", True) planner = ap_module.AutonomousPlanner(llm_model="qwen2.5:7b") assert planner._owl_detector is None, ( "L'exception doit être catchée — AutonomousPlanner ne doit pas crash" )