Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions docs/auto.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,25 @@ fine-tune of the same architecture loads exactly like the official weights.

## Task classes

### Pipeline components

Exported component classes are not necessarily standalone Auto tasks. For Qwen-Image
and Qwen-Image 2.1, the VAE (`AutoencoderKLQwenImage`, `AutoencoderKLQwenImage21`),
text encoder (`QwenImageTextEncoderModel`, `QwenImage21TextEncoderModel`), and
transformer (`QwenImageTransformer2DModel`, `QwenImage21Transformer2DModel`) are
pipeline components. Load the complete model with `AutoZModel` or the generation
pipeline with `AutoZMTextToImage`, then access its `.vae`, `.text_encoder`, or
`.transformer` attribute. Direct component construction and serialization remain
available; component config types do not imply standalone Auto weight loading.

These six classes are explicitly recorded with their pipeline owners in
`COMPONENT_ONLY_MODEL_NAMES` in `zeromodels/auto/auto_mapping_names.py`. The registry
coverage test still requires every other autodetectable model to have a task mapping
or an existing documented exemption, and checks that component exclusions name real
classes with Auto-loadable owners.

### Available tasks

Like `transformers`' `AutoModelForImageClassification` / `AutoModelForObjectDetection` /
…, there is one Auto per task. `AutoZModel` loads the bare backbone; the task classes load
the model **with its head**. Each is named after the ZeroModels task suffix:
Expand Down
41 changes: 39 additions & 2 deletions tests/integration/test_auto_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,14 +297,18 @@ def test_every_autodetectable_model_class_appears_in_a_table():
"""Every model class that carries a model_type is reachable through some AutoZM table
(catches a new model added to the code but not to the hand-maintained tables). Bare
backbones with no model_type are skipped: a repo has nothing to autodetect on, and they
load via a task sibling or the concrete class."""
load via a task sibling or the concrete class. Explicit component-only classes
are covered by their Auto-loadable pipeline owner rather than a task row."""
mapped = set()
for table in names.MODEL_TASK_MAPPING_NAMES.values():
mapped.update(table.values())
missing = sorted(
name
for name, cls in _iter_model_classes()
if _has_model_type(cls) and name not in mapped and name not in _COVERAGE_EXEMPT
if _has_model_type(cls)
and name not in mapped
and name not in _COVERAGE_EXEMPT
and name not in names.COMPONENT_ONLY_MODEL_NAMES
)
assert not missing, (
"model class(es) with a model_type absent from "
Expand All @@ -313,6 +317,39 @@ def test_every_autodetectable_model_class_appears_in_a_table():
)


def test_component_only_contract_has_real_unmapped_components_and_mapped_owners():
"""Keep the explicit exclusions narrow: no stale names or hidden task rows."""
exported = dict(_iter_model_classes())
mapped = {
name
for table in names.MODEL_TASK_MAPPING_NAMES.values()
for name in table.values()
}
for component, owner in names.COMPONENT_ONLY_MODEL_NAMES.items():
assert component in exported, f"Stale component-only entry: {component}"
assert _has_model_type(exported[component]), component
assert component not in mapped, f"Remove obsolete exclusion: {component}"
assert component not in _COVERAGE_EXEMPT, component
assert owner in exported and owner in mapped, owner
assert component != owner


@pytest.mark.parametrize(
"model_type,model_name,pipeline_name",
[
("qwen_image", "QwenImageModel", "QwenImageTextToImage"),
("qwen_image_21", "QwenImage21Model", "QwenImage21TextToImage"),
],
)
def test_qwen_image_pipeline_auto_routes_remain_registered(
model_type, model_name, pipeline_name
):
assert names.MODEL_TASK_MAPPING_NAMES["Model"][model_type] == model_name
assert names.MODEL_TASK_MAPPING_NAMES["TextToImage"][model_type] == pipeline_name
assert model_name not in names.COMPONENT_ONLY_MODEL_NAMES
assert pipeline_name not in names.COMPONENT_ONLY_MODEL_NAMES


# Classes whose zm config model_type DELIBERATELY differs from the HF checkpoint's
# model_type (not the "rejects its own repo" bug): HF DeBERTa-v3 checkpoints carry the
# "deberta-v2" config type; the MiT backbone is HF's SegFormer encoder ("segformer"); the
Expand Down
13 changes: 13 additions & 0 deletions zeromodels/auto/auto_mapping_names.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@
the zeromodels-repo and ``hf:`` load paths share one table.
"""

# Public building blocks of full diffusion pipelines, not standalone Auto tasks.
# Values name the Auto-loadable owner exposing vae/text_encoder/transformer.
# Direct construction and serialization remain supported; a config model_type
# alone does not promise an independent Auto checkpoint-loading route.
COMPONENT_ONLY_MODEL_NAMES = {
"AutoencoderKLQwenImage": "QwenImageModel",
"QwenImageTextEncoderModel": "QwenImageModel",
"QwenImageTransformer2DModel": "QwenImageModel",
"AutoencoderKLQwenImage21": "QwenImage21Model",
"QwenImage21TextEncoderModel": "QwenImage21Model",
"QwenImage21Transformer2DModel": "QwenImage21Model",
}

# task suffix -> {model_type: ClassName}. AutoZModel is task 'Model';
# AutoZM<Task> for the rest (AutoZMDetect = 'Detect', ...).
MODEL_TASK_MAPPING_NAMES = {
Expand Down
Loading