Files
LocalAI/backend/python/common/model_utils_test.py
LocalAI [bot] bcc41219f7 feat: materialize Hugging Face model artifacts (#10825)
* feat(config): add model artifact source contract

Assisted-by: Codex:GPT-5 [Codex]

* feat(downloader): add authenticated raw-byte progress

Assisted-by: Codex:GPT-5 [Codex]

* feat(huggingface): resolve immutable snapshot manifests

Assisted-by: Codex:GPT-5 [Codex]

* feat(models): add artifact storage primitives

Assisted-by: Codex:GPT-5 [Codex]

* feat(models): materialize pinned Hugging Face snapshots

Assisted-by: Codex:GPT-5 [Codex]

* feat(models): bind managed snapshots at runtime

Assisted-by: Codex:GPT-5 [Codex]

* feat(gallery): materialize model artifacts during install

Assisted-by: Codex:GPT-5 [Codex]

* feat(gallery): declare managed Hugging Face artifacts

Assisted-by: Codex:GPT-5 [Codex]

* feat(models): preload managed model artifacts

Assisted-by: Codex:GPT-5 [Codex]

* fix(gallery): retain shared artifact caches on delete

Assisted-by: Codex:GPT-5 [Codex]

* feat(models): report artifact acquisition progress

Assisted-by: Codex:GPT-5 [Codex]

* refactor(backends): load managed models from ModelFile

Assisted-by: Codex:GPT-5 [Codex]

* refactor(backends): load staged speech model snapshots

Assisted-by: Codex:GPT-5 [Codex]

* refactor(backends): use staged snapshots in engine backends

Assisted-by: Codex:GPT-5 [Codex]

* test(distributed): cover staged artifact snapshots

Assisted-by: Codex:GPT-5 [Codex]

* docs: explain managed model artifacts

Assisted-by: Codex:GPT-5 [Codex]

* docs: add product design context

Assisted-by: Codex:GPT-5 [Codex]

* feat(ui): show model artifact download progress

Assisted-by: Codex:GPT-5 [Codex]

* Eagerly materialize Hugging Face artifacts

Materialize HF-backed model references as managed GGUF artifacts during load, with lazy download retained only as fallback.

Assisted-by: Codex:GPT-5 [shell]

* Refactor HF
  downloads through a shared executor

Assisted-by: Codex:GPT-5 [shell]

* drop

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

---------

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-07-15 01:09:33 +02:00

82 lines
3.0 KiB
Python

import ast
import os
import tempfile
import unittest
from types import SimpleNamespace
from model_utils import require_snapshot_file, resolve_model_reference
class ResolveModelReferenceTest(unittest.TestCase):
def test_existing_model_file_wins(self):
with tempfile.TemporaryDirectory() as directory:
request = SimpleNamespace(Model="owner/repo", ModelFile=directory)
self.assertEqual(resolve_model_reference(request), (directory, True))
def test_missing_model_file_preserves_legacy_repo_fallback(self):
request = SimpleNamespace(Model=" owner/repo ", ModelFile="/does/not/exist")
self.assertEqual(resolve_model_reference(request), ("owner/repo", False))
def test_empty_request_uses_explicit_default(self):
request = SimpleNamespace(Model=" ", ModelFile="")
self.assertEqual(
resolve_model_reference(request, "default/repo"),
("default/repo", False),
)
def test_requires_the_only_matching_snapshot_file(self):
with tempfile.TemporaryDirectory() as directory:
checkpoint = os.path.join(directory, "model.nemo")
with open(checkpoint, "wb"):
pass
self.assertEqual(require_snapshot_file(directory, ".nemo"), checkpoint)
def test_rejects_ambiguous_packaged_files(self):
with tempfile.TemporaryDirectory() as directory:
for name in ("first.nemo", "second.nemo"):
with open(os.path.join(directory, name), "wb"):
pass
with self.assertRaisesRegex(ValueError, "exactly one"):
require_snapshot_file(directory, ".nemo")
class MigratedBackendSourceTest(unittest.TestCase):
def test_general_backends_use_shared_resolution(self):
common_dir = os.path.dirname(__file__)
python_root = os.path.dirname(common_dir)
for backend in (
"transformers",
"diffusers",
"qwen-asr",
"fish-speech",
"nemo",
"voxcpm",
"qwen-tts",
"liquid-audio",
"vllm",
"vllm-omni",
"sglang",
):
with self.subTest(backend=backend):
with open(
os.path.join(python_root, backend, "backend.py"), encoding="utf-8"
) as backend_file:
tree = ast.parse(backend_file.read())
imports = {
alias.name
for node in ast.walk(tree)
if isinstance(node, ast.ImportFrom) and node.module == "model_utils"
for alias in node.names
}
calls = {
node.func.id
for node in ast.walk(tree)
if isinstance(node, ast.Call) and isinstance(node.func, ast.Name)
}
self.assertIn("resolve_model_reference", imports)
self.assertIn("resolve_model_reference", calls)
if __name__ == "__main__":
unittest.main()