From ae88e710e578dd51bc2d4a94d1ce8fdfb8da8b84 Mon Sep 17 00:00:00 2001 From: IMvision12 Date: Sat, 3 Oct 2026 15:11:50 -0700 Subject: [PATCH] fix --- docs/auto.md | 19 ++++++++++++ tests/integration/test_auto_registry.py | 41 +++++++++++++++++++++++-- zeromodels/auto/auto_mapping_names.py | 13 ++++++++ 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/docs/auto.md b/docs/auto.md index 3011cf39..eeab82d6 100644 --- a/docs/auto.md +++ b/docs/auto.md @@ -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: diff --git a/tests/integration/test_auto_registry.py b/tests/integration/test_auto_registry.py index 0873cbf5..d4e53f8a 100644 --- a/tests/integration/test_auto_registry.py +++ b/tests/integration/test_auto_registry.py @@ -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 " @@ -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 diff --git a/zeromodels/auto/auto_mapping_names.py b/zeromodels/auto/auto_mapping_names.py index 6b925e3b..a659a0e0 100644 --- a/zeromodels/auto/auto_mapping_names.py +++ b/zeromodels/auto/auto_mapping_names.py @@ -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 for the rest (AutoZMDetect = 'Detect', ...). MODEL_TASK_MAPPING_NAMES = {