Spaces:
Sleeping
Sleeping
Alessandro Tomassini
deploy(hf): overlay README/Dockerfile da huggingface/, senza docs/binari/model
c42a5a1 | """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 | |
| 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 | |