feat(gui): garde-fou runtime — désactive un moteur optionnel non embarqué
Condition du GO-CONDITIONNEL Qwen sur le lot engine capabilities (cb3b767/890edb3/5e5f0bd) : un profil YAML forçant enable_eds/enable_gliner ne doit pas déclencher un chargement voué à l'échec silencieux. NerManagers.ensure_loaded() applique désormais un garde-fou via la sonde engine_capabilities.capabilities_map() (injectable) AVANT toute tentative de load EDS/GLiNER : si le moteur optionnel demandé est indisponible dans le build courant → warning + désactivation forcée dans les réglages runtime. Best-effort (sonde en échec ⇒ réglages inchangés, les try/except de load protègent déjà). Sonde légère (find_spec), aucun import lourd. CamemBERT (requis) inchangé. Diff limité au garde-fou + tests cibles. TDD : 4 tests (test_gui_v6_engine_bridge.py) — eds/gliner indispo désactivés et jamais chargés, moteur dispo conservé, fail-safe sonde. 282 unit passed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -126,6 +126,89 @@ def test_optional_manager_failure_is_tolerated():
|
||||
assert managers.use_hf is True
|
||||
|
||||
|
||||
# -- garde-fou capabilities runtime ----------------------------------------
|
||||
|
||||
|
||||
class _FakeCap:
|
||||
"""Capability minimale pour injecter une sonde dans les tests."""
|
||||
|
||||
def __init__(self, available, reason="(test)"):
|
||||
self.available = available
|
||||
self.reason = reason
|
||||
|
||||
|
||||
def _caps_provider(eds_ok, gliner_ok):
|
||||
def provider():
|
||||
return {
|
||||
"camembert": _FakeCap(True),
|
||||
"eds": _FakeCap(eds_ok),
|
||||
"gliner": _FakeCap(gliner_ok),
|
||||
}
|
||||
|
||||
return provider
|
||||
|
||||
|
||||
def test_guard_disables_unavailable_eds_before_load():
|
||||
# Profil/config forçant EDS alors que le moteur n'est pas embarqué.
|
||||
settings = EngineSettings(enable_eds=True)
|
||||
counter = {"camembert": 0, "eds": 0, "gliner": 0}
|
||||
managers = NerManagers(
|
||||
settings,
|
||||
factories=_counting_factories(counter),
|
||||
caps_provider=_caps_provider(eds_ok=False, gliner_ok=True),
|
||||
)
|
||||
assert managers.ensure_loaded() == ManagerState.READY
|
||||
assert settings.enable_eds is False # désactivation forcée
|
||||
assert counter["eds"] == 0 # jamais tenté de charger
|
||||
assert managers.as_kwargs()["ner_manager"] is None
|
||||
|
||||
|
||||
def test_guard_disables_unavailable_gliner_before_load():
|
||||
settings = EngineSettings(enable_gliner=True)
|
||||
counter = {"camembert": 0, "eds": 0, "gliner": 0}
|
||||
managers = NerManagers(
|
||||
settings,
|
||||
factories=_counting_factories(counter),
|
||||
caps_provider=_caps_provider(eds_ok=True, gliner_ok=False),
|
||||
)
|
||||
assert managers.ensure_loaded() == ManagerState.READY
|
||||
assert settings.enable_gliner is False
|
||||
assert counter["gliner"] == 0
|
||||
assert managers.as_kwargs()["gliner_manager"] is None
|
||||
|
||||
|
||||
def test_guard_keeps_available_engine_enabled():
|
||||
settings = EngineSettings(enable_eds=True, enable_gliner=True)
|
||||
counter = {"camembert": 0, "eds": 0, "gliner": 0}
|
||||
managers = NerManagers(
|
||||
settings,
|
||||
factories=_counting_factories(counter),
|
||||
caps_provider=_caps_provider(eds_ok=True, gliner_ok=True),
|
||||
)
|
||||
assert managers.ensure_loaded() == ManagerState.READY
|
||||
assert settings.enable_eds is True
|
||||
assert settings.enable_gliner is True
|
||||
assert counter["eds"] == 1
|
||||
assert counter["gliner"] == 1
|
||||
|
||||
|
||||
def test_guard_failsafe_when_probe_raises():
|
||||
settings = EngineSettings(enable_eds=True)
|
||||
counter = {"camembert": 0, "eds": 0, "gliner": 0}
|
||||
|
||||
def boom():
|
||||
raise RuntimeError("probe ko")
|
||||
|
||||
managers = NerManagers(
|
||||
settings, factories=_counting_factories(counter), caps_provider=boom
|
||||
)
|
||||
# Best-effort : une sonde en échec ne bloque pas le chargement et ne
|
||||
# modifie pas les réglages (les try/except de load protègent déjà).
|
||||
assert managers.ensure_loaded() == ManagerState.READY
|
||||
assert settings.enable_eds is True
|
||||
assert counter["eds"] == 1
|
||||
|
||||
|
||||
# -- make_process_fn -------------------------------------------------------
|
||||
|
||||
def test_process_fn_calls_engine_with_kwargs(tmp_path):
|
||||
|
||||
Reference in New Issue
Block a user