[misc]: model_id is the only load key — HF repo strings are card ingredients, never resolvable
This commit is contained in:
+14
-39
@@ -1,16 +1,13 @@
|
||||
"""The catalog — one place that maps names to (card, pipeline builder).
|
||||
|
||||
Identity policy (three levels):
|
||||
Identity policy (two levels, nothing else resolves):
|
||||
|
||||
* ``model_id`` is the PRIMARY identity — it names a *servable* (weights plus
|
||||
loop semantics, precision regime, conventions, defaults). One HF repo can
|
||||
back many cards (base / distilled / precision variants) and one card can
|
||||
draw from many repos (the aligned Wan card loads the official-layout DiT
|
||||
and the diffusers-layout encoder/VAE), so repo strings cannot be identities.
|
||||
* HF repo strings are *ingredients* (``card.weights``, ``ComponentSpec.source``)
|
||||
accepted here as a convenience alias — but only while unambiguous. As soon
|
||||
as two cards reference the same repo, resolving by that repo fails with the
|
||||
candidate list; it never picks silently.
|
||||
* ``model_id`` is THE identity — it names a *servable* (weights plus loop
|
||||
semantics, precision regime, conventions, defaults). HF repo strings are
|
||||
ingredients on the card (``card.weights``, ``ComponentSpec.source``), never
|
||||
load keys: one repo can back many cards (base / distilled / precision
|
||||
variants) and one card can draw from many repos (the aligned Wan card loads
|
||||
the official-layout DiT and the diffusers-layout encoder/VAE).
|
||||
* ``card.digest()`` is the machine identity: evidence records, T1 baselines,
|
||||
and environment manifests key on it, never on names.
|
||||
|
||||
@@ -33,32 +30,10 @@ PIPELINES: dict[str, Callable[[], Any]] = {
|
||||
}
|
||||
|
||||
|
||||
def _build_aliases(cards: dict[str, Any]) -> dict[str, list[str]]:
|
||||
"""HF repo string -> model_ids referencing it (as card weights or as a
|
||||
per-component source). Values with several ids are ambiguous aliases."""
|
||||
out: dict[str, list[str]] = {}
|
||||
for card in cards.values():
|
||||
refs = {card.weights} | {s.source for s in card.components.values() if s.source}
|
||||
for ref in sorted(refs):
|
||||
ids = out.setdefault(ref, [])
|
||||
if card.model_id not in ids:
|
||||
ids.append(card.model_id)
|
||||
return out
|
||||
|
||||
|
||||
_ALIASES = _build_aliases(CARDS)
|
||||
|
||||
|
||||
def resolve(name: str) -> tuple[ModelCard, Callable[[], Any]]:
|
||||
"""model_id (primary), or an unambiguous HF repo alias -> (card, pipeline
|
||||
builder)."""
|
||||
if name in CARDS:
|
||||
return CARDS[name], PIPELINES[name]
|
||||
hits = _ALIASES.get(name, [])
|
||||
if len(hits) == 1:
|
||||
return CARDS[hits[0]], PIPELINES[hits[0]]
|
||||
if hits:
|
||||
raise KeyError(f"{name!r} is a weights repo referenced by several cards "
|
||||
f"{hits} — load by model_id")
|
||||
raise KeyError(f"unknown model {name!r}; known ids: {sorted(CARDS)}; "
|
||||
f"known repo aliases: {sorted(_ALIASES)}")
|
||||
def resolve(model_id: str) -> tuple[ModelCard, Callable[[], Any]]:
|
||||
"""model_id -> (card, pipeline builder). Only model ids resolve — pass an
|
||||
HF repo string and you get the catalog, not a guess."""
|
||||
if model_id not in CARDS:
|
||||
raise KeyError(f"unknown model {model_id!r}; known ids: {sorted(CARDS)} "
|
||||
f"(HF repo strings are card ingredients, not load keys)")
|
||||
return CARDS[model_id], PIPELINES[model_id]
|
||||
|
||||
+4
-3
@@ -117,8 +117,9 @@ class Model:
|
||||
f"digest={self.card.digest()})")
|
||||
|
||||
|
||||
def load(model: str, *, root: str | None = None, device: str | None = None) -> Model:
|
||||
"""Resolve a model id or HF repo id, load it resident, return the handle."""
|
||||
def load(model_id: str, *, root: str | None = None, device: str | None = None) -> Model:
|
||||
"""Resolve a model id from the catalog, load it resident, return the
|
||||
handle. HF repo strings are card ingredients, not load keys."""
|
||||
from fastvideo2.registry import resolve
|
||||
card, build_pipeline = resolve(model)
|
||||
card, build_pipeline = resolve(model_id)
|
||||
return Model(card, build_pipeline(), _load_instance(card, root=root, device=device))
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
"""T0: the identity policy — model_id primary, HF repos as unambiguous aliases."""
|
||||
from types import SimpleNamespace
|
||||
|
||||
"""T0: the identity policy — model_id is the only load key."""
|
||||
import pytest
|
||||
|
||||
from fastvideo2.registry import CARDS, _build_aliases, resolve
|
||||
from fastvideo2.registry import CARDS, PIPELINES, resolve
|
||||
from fastvideo2.wan21 import WAN21_T2V_1_3B
|
||||
|
||||
|
||||
@@ -12,31 +10,16 @@ def test_resolve_by_model_id():
|
||||
assert card is WAN21_T2V_1_3B and callable(build)
|
||||
|
||||
|
||||
def test_resolve_by_weights_repo_alias():
|
||||
card, _ = resolve("Wan-AI/Wan2.1-T2V-1.3B-Diffusers")
|
||||
assert card.model_id == "wan2.1-t2v-1.3b"
|
||||
def test_hf_repo_strings_do_not_resolve():
|
||||
for repo in ("Wan-AI/Wan2.1-T2V-1.3B-Diffusers", "Wan-AI/Wan2.1-T2V-1.3B"):
|
||||
with pytest.raises(KeyError, match="not load keys"):
|
||||
resolve(repo)
|
||||
|
||||
|
||||
def test_resolve_by_component_source_alias():
|
||||
# the official-layout DiT repo is an ingredient of the same card
|
||||
card, _ = resolve("Wan-AI/Wan2.1-T2V-1.3B")
|
||||
assert card.model_id == "wan2.1-t2v-1.3b"
|
||||
|
||||
|
||||
def test_unknown_name_lists_ids_and_aliases():
|
||||
def test_unknown_id_lists_catalog():
|
||||
with pytest.raises(KeyError, match="known ids"):
|
||||
resolve("no/such-model")
|
||||
|
||||
|
||||
def test_ambiguous_repo_alias_fails_closed():
|
||||
def fake(mid):
|
||||
spec = SimpleNamespace(source="")
|
||||
return SimpleNamespace(model_id=mid, weights="acme/shared-repo",
|
||||
components={"c": spec})
|
||||
aliases = _build_aliases({"a": fake("card-a"), "b": fake("card-b")})
|
||||
assert aliases["acme/shared-repo"] == ["card-a", "card-b"] # resolve() would error
|
||||
resolve("no-such-model")
|
||||
|
||||
|
||||
def test_every_card_id_has_a_pipeline():
|
||||
from fastvideo2.registry import PIPELINES
|
||||
assert set(CARDS) == set(PIPELINES)
|
||||
|
||||
Reference in New Issue
Block a user