AiAnonymize_v3 / tests /test_model_resolution.py
Alessandro Tomassini
deploy(hf): overlay README/Dockerfile da huggingface/, senza docs/binari/model
c42a5a1
Raw
History Blame Contribute Delete
9.64 kB
"""Risoluzione modelli: ordine **cache HF → filesystem (MODELS_DIR) → online**.
Il vecchio gate `ENABLE_HF_CACHE` è stato rimosso. La regola ora è:
- in cache HF → si usa la cache (ritorna il repo-id, offline-safe);
- altrimenti in `MODELS_DIR/<repo>` → si usa la folder locale;
- altrimenti, se è permessa la rete (`HF_HUB_OFFLINE` non attivo) → repo-id (download);
- altrimenti il modello NON è disponibile → `ModelUnavailable` (nessun crash a monte:
i layer lo intercettano e si auto-inibiscono).
"""
from __future__ import annotations
from pathlib import Path
import pytest
import config.runtime.models as cfg_models
# Riferimento alla funzione REALE (l'autouse fixture sotto la sostituisce):
# serve al test che verifica la lettura di RUNTIME_MODE.
_REAL_LOCAL_ONLY = cfg_models._local_only
@pytest.fixture(autouse=True)
def _default_non_local(monkeypatch):
"""Default dei test: NON local mode (l'ambiente può avere RUNTIME_MODE=local
via .env caricato da python-dotenv). I test del local mode lo riattivano."""
monkeypatch.setattr(cfg_models, "_local_only", lambda: False)
# --- local_path: cache → filesystem → online → ModelUnavailable --------------
def test_local_path_prefers_hf_cache(monkeypatch, tmp_path):
# In cache → ritorna il repo-id (i loader caricano dalla cache, anche offline).
monkeypatch.setattr(cfg_models, "MODELS_DIR", tmp_path)
monkeypatch.setattr(cfg_models, "_in_hf_cache", lambda repo: True)
repo = "DeepMount00/universal_ner_ita"
# anche se esiste la folder locale, la cache ha precedenza
(tmp_path / repo).mkdir(parents=True)
assert cfg_models.local_path(repo) == repo
def test_local_path_falls_back_to_filesystem(monkeypatch, tmp_path):
# Non in cache, ma presente in MODELS_DIR → path locale.
monkeypatch.setattr(cfg_models, "MODELS_DIR", tmp_path)
monkeypatch.setattr(cfg_models, "_in_hf_cache", lambda repo: False)
repo = "DeepMount00/Italian_NER_XXL_v2"
(tmp_path / repo).mkdir(parents=True)
assert cfg_models.local_path(repo) == str(tmp_path / repo)
def test_local_path_uses_repo_id_when_online(monkeypatch, tmp_path):
# Né cache né folder, ma rete permessa → repo-id (download dall'Hub).
monkeypatch.setattr(cfg_models, "MODELS_DIR", tmp_path)
monkeypatch.setattr(cfg_models, "_in_hf_cache", lambda repo: False)
monkeypatch.setattr(cfg_models, "_online", lambda: True)
repo = "DeepMount00/universal_ner_ita"
assert cfg_models.local_path(repo) == repo
def test_local_path_raises_when_absent_and_offline(monkeypatch, tmp_path):
# Né cache né folder, niente rete → ModelUnavailable (non FileNotFoundError).
monkeypatch.setattr(cfg_models, "MODELS_DIR", tmp_path)
monkeypatch.setattr(cfg_models, "_in_hf_cache", lambda repo: False)
monkeypatch.setattr(cfg_models, "_online", lambda: False)
with pytest.raises(cfg_models.ModelUnavailable):
cfg_models.local_path("DeepMount00/universal_ner_ita")
# --- model_available: cache OR filesystem OR online --------------------------
def test_model_available_true_when_cached(monkeypatch, tmp_path):
monkeypatch.setattr(cfg_models, "MODELS_DIR", tmp_path)
monkeypatch.setattr(cfg_models, "_in_hf_cache", lambda repo: True)
monkeypatch.setattr(cfg_models, "_online", lambda: False)
assert cfg_models.model_available("x/y") is True
def test_model_available_true_when_in_filesystem(monkeypatch, tmp_path):
monkeypatch.setattr(cfg_models, "MODELS_DIR", tmp_path)
monkeypatch.setattr(cfg_models, "_in_hf_cache", lambda repo: False)
monkeypatch.setattr(cfg_models, "_online", lambda: False)
(tmp_path / "x" / "y").mkdir(parents=True)
assert cfg_models.model_available("x/y") is True
def test_model_available_true_when_online(monkeypatch, tmp_path):
monkeypatch.setattr(cfg_models, "MODELS_DIR", tmp_path)
monkeypatch.setattr(cfg_models, "_in_hf_cache", lambda repo: False)
monkeypatch.setattr(cfg_models, "_online", lambda: True)
assert cfg_models.model_available("x/y") is True
def test_model_available_false_when_absent_and_offline(monkeypatch, tmp_path):
monkeypatch.setattr(cfg_models, "MODELS_DIR", tmp_path)
monkeypatch.setattr(cfg_models, "_in_hf_cache", lambda repo: False)
monkeypatch.setattr(cfg_models, "_online", lambda: False)
assert cfg_models.model_available("x/y") is False
# --- find_gguf: cache → filesystem → online → ModelUnavailable ---------------
def test_find_gguf_prefers_filesystem(monkeypatch, tmp_path):
monkeypatch.setattr(cfg_models, "MODELS_DIR", tmp_path)
monkeypatch.setattr(cfg_models, "_cache_gguf", lambda repo, pat: None)
repo = "unsloth/gemma-4-E2B-it-GGUF"
base = tmp_path / repo
base.mkdir(parents=True)
(base / "gemma-4-E2B-it-Q4_K_M.gguf").write_bytes(b"x")
(base / "gemma-4-E2B-it-Q8_0.gguf").write_bytes(b"x")
assert Path(cfg_models.find_gguf(repo, "*Q4_K_M.gguf")) == (
base / "gemma-4-E2B-it-Q4_K_M.gguf"
)
def test_find_gguf_prefers_cache_over_filesystem(monkeypatch, tmp_path):
# In cache → usata prima della folder (coerente con local_path).
monkeypatch.setattr(cfg_models, "MODELS_DIR", tmp_path)
monkeypatch.setattr(cfg_models, "_cache_gguf", lambda repo, pat: "/cache/x.gguf")
repo = "unsloth/gemma-4-E2B-it-GGUF"
base = tmp_path / repo
base.mkdir(parents=True)
(base / "gemma-4-E2B-it-Q4_K_M.gguf").write_bytes(b"x")
assert cfg_models.find_gguf(repo, "*Q4_K_M.gguf") == "/cache/x.gguf"
def test_find_gguf_downloads_when_online(monkeypatch, tmp_path):
monkeypatch.setattr(cfg_models, "MODELS_DIR", tmp_path)
monkeypatch.setattr(cfg_models, "_cache_gguf", lambda repo, pat: None)
monkeypatch.setattr(cfg_models, "_online", lambda: True)
monkeypatch.setattr(cfg_models, "_download_gguf", lambda repo, pat: "/dl/x.gguf")
assert cfg_models.find_gguf("unsloth/gemma-4-E2B-it-GGUF", "*Q4_K_M.gguf") == (
"/dl/x.gguf"
)
def test_find_gguf_raises_when_absent_and_offline(monkeypatch, tmp_path):
monkeypatch.setattr(cfg_models, "MODELS_DIR", tmp_path)
monkeypatch.setattr(cfg_models, "_cache_gguf", lambda repo, pat: None)
monkeypatch.setattr(cfg_models, "_online", lambda: False)
with pytest.raises(cfg_models.ModelUnavailable):
cfg_models.find_gguf("unsloth/gemma-4-E2B-it-GGUF", "*Q4_K_M.gguf")
# --- RUNTIME_MODE=local: SOLO filesystem (niente cache HF, niente rete) -------
def test_local_only_reads_runtime_mode(monkeypatch):
monkeypatch.setenv("RUNTIME_MODE", "local")
assert _REAL_LOCAL_ONLY() is True
monkeypatch.setenv("RUNTIME_MODE", "LOCAL") # case-insensitive
assert _REAL_LOCAL_ONLY() is True
monkeypatch.setenv("RUNTIME_MODE", "dedicated")
assert _REAL_LOCAL_ONLY() is False
monkeypatch.delenv("RUNTIME_MODE", raising=False)
assert _REAL_LOCAL_ONLY() is False
def test_local_mode_uses_only_filesystem(monkeypatch, tmp_path):
# In local mode, cache "piena" e rete permessa NON contano: vale solo la folder.
monkeypatch.setattr(cfg_models, "MODELS_DIR", tmp_path)
monkeypatch.setattr(cfg_models, "_local_only", lambda: True)
monkeypatch.setattr(cfg_models, "_in_hf_cache", lambda repo: True)
monkeypatch.setattr(cfg_models, "_online", lambda: True)
repo = "DeepMount00/universal_ner_ita"
(tmp_path / repo).mkdir(parents=True)
assert cfg_models.local_path(repo) == str(tmp_path / repo)
def test_local_mode_ignores_cache_and_network(monkeypatch, tmp_path):
# Modello in cache e online, ma NON in MODELS_DIR → indisponibile in local mode.
monkeypatch.setattr(cfg_models, "MODELS_DIR", tmp_path)
monkeypatch.setattr(cfg_models, "_local_only", lambda: True)
monkeypatch.setattr(cfg_models, "_in_hf_cache", lambda repo: True)
monkeypatch.setattr(cfg_models, "_online", lambda: True)
with pytest.raises(cfg_models.ModelUnavailable):
cfg_models.local_path("DeepMount00/universal_ner_ita")
def test_model_available_local_mode_filesystem_only(monkeypatch, tmp_path):
monkeypatch.setattr(cfg_models, "MODELS_DIR", tmp_path)
monkeypatch.setattr(cfg_models, "_local_only", lambda: True)
monkeypatch.setattr(cfg_models, "_in_hf_cache", lambda repo: True)
monkeypatch.setattr(cfg_models, "_online", lambda: True)
assert cfg_models.model_available("x/y") is False
(tmp_path / "x" / "y").mkdir(parents=True)
assert cfg_models.model_available("x/y") is True
def test_find_gguf_local_mode_filesystem_only(monkeypatch, tmp_path):
monkeypatch.setattr(cfg_models, "MODELS_DIR", tmp_path)
monkeypatch.setattr(cfg_models, "_local_only", lambda: True)
monkeypatch.setattr(cfg_models, "_cache_gguf", lambda repo, pat: "/cache/x.gguf")
monkeypatch.setattr(cfg_models, "_online", lambda: True)
repo = "unsloth/gemma-4-E2B-it-GGUF"
with pytest.raises(cfg_models.ModelUnavailable):
cfg_models.find_gguf(repo, "*Q4_K_M.gguf") # cache/online ignorati
base = tmp_path / repo
base.mkdir(parents=True)
(base / "m-Q4_K_M.gguf").write_bytes(b"x")
assert Path(cfg_models.find_gguf(repo, "*Q4_K_M.gguf")) == base / "m-Q4_K_M.gguf"
def test_gguf_available_local_mode_filesystem_only(monkeypatch, tmp_path):
monkeypatch.setattr(cfg_models, "MODELS_DIR", tmp_path)
monkeypatch.setattr(cfg_models, "_local_only", lambda: True)
monkeypatch.setattr(cfg_models, "_cache_gguf", lambda repo, pat: "/cache/x.gguf")
monkeypatch.setattr(cfg_models, "_online", lambda: True)
assert cfg_models.gguf_available("unsloth/gemma-4-E2B-it-GGUF", "*Q4_K_M.gguf") is False