Skip to content

Commit 1746cf4

Browse files
committed
[refactor]: Z-Image reuses the shared Qwen3 encoder instead of a bespoke port
main gained a config-driven Qwen3 text encoder (Qwen3ForCausalLM + Qwen3TextConfig) via the Flux2 Klein port (hao-ai-lab#1349). It returns BaseEncoderOutput(last_hidden_state, hidden_states), supports GQA + the same qkv/gate_up fusion, and update_model_arch populates Z-Image-Turbo's dims (2048/24/16) from its config.json — so our parallel Qwen3Model was ~540 lines of duplicate. Drop the bespoke encoder + config (resolved during the rebase onto main; the files are now identical to main) and wire Z-Image to the shared encoder: - registry: map Z-Image-Turbo's "Qwen3Model" architecture string -> Qwen3ForCausalLM. - encoder parity test: use Qwen3TextConfig; move the strict-load allowlist ({"lm_head.weight"}) from a class attribute to a test-side constant, since the shared encoder's loader is intentionally lenient (serves multiple models). - PORT_STATUS / README: mark the encoder "reused"; the prior A40 parity PASS was measured against the removed bespoke encoder, so text-encoder parity is flagged RE-VALIDATION PENDING against the shared class. Scheduler/tokenizer/VAE parity are unaffected and still PASS. The gated scheduler option (use_reference_discrete_timesteps, default False) is unchanged and still needed for Z-Image timestep parity.
1 parent fbbe764 commit 1746cf4

3 files changed

Lines changed: 42 additions & 28 deletions

File tree

tests/local_tests/zimage/PORT_STATUS.md

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@
1414
- phase: Phase 4 (component parity)
1515
- status: in_progress
1616
- owner: parity
17-
- last_updated: 2026-05-12
17+
- last_updated: 2026-06-21
1818

1919
## Component Matrix
2020
| Component | Type | Reuse/Port | Official Definition | Official Instantiation | FastVideo Target | Prototype | Conversion | Parity | Open Issues |
2121
|---|---|---|---|---|---|---|---|---|---|
22-
| Text encoder (Qwen3) | text_encoder | ported | `zimage.Qwen3Model` (HF-Qwen3 layout) | `Qwen3ForCausalLM` checkpoint at `<weights>/text_encoder/` | `fastvideo/models/encoders/qwen3.py` + `fastvideo/configs/models/encoders/qwen3.py` | DONE | not_needed (raw safetensors load) | PASS (fp32 + bf16) | I001, I003 |
22+
| Text encoder (Qwen3) | text_encoder | reused | `zimage.Qwen3Model` (HF-Qwen3 layout) | `Qwen3ForCausalLM` checkpoint at `<weights>/text_encoder/` | shared `fastvideo/models/encoders/qwen3.py::Qwen3ForCausalLM` (+ `Qwen3TextConfig`), added for Flux2 Klein; Z-Image's `Qwen3Model` arch string routes to it in the registry | DONE | not_needed (raw safetensors load) | RE-VALIDATION PENDING (shared encoder) | I001, I003 |
2323
| Tokenizer | tokenizer | reused | `AutoTokenizer` | `<weights>/tokenizer/` | `fastvideo/models/loader/component_loader.py::TokenizerLoader` | DONE | not_needed | PASS | none |
2424
| VAE | vae | reused | `zimage.AutoencoderKL` (Diffusers-compatible) | `<weights>/vae/` | `fastvideo/models/vaes/autoencoder_kl.py` | DONE | not_needed | PASS (decode only) | encode-path parity deferred to pipeline |
2525
| Scheduler | scheduler | reused (with extension) | `zimage.FlowMatchEulerDiscreteScheduler` | `<weights>/scheduler/scheduler_config.json` | `fastvideo/models/schedulers/scheduling_flow_match_euler_discrete.py` (added `use_reference_discrete_timesteps`) | DONE | not_needed | PASS | I002 |
@@ -30,7 +30,7 @@
3030
- conversion_script: `<none — encoder loads raw safetensors via tests/local_tests/zimage/test_zimage_encoder_parity.py helpers; VAE/tokenizer/scheduler use HF subfolder loaders>`
3131
- converted_weights_dir: `<none>`
3232
- source_layout: diffusers
33-
- strict_load_status: `pass_with_documented_exclusions` (Qwen3Model.ALLOWED_UNEXPECTED_KEYS = {"lm_head.weight"})
33+
- strict_load_status: `pass_with_documented_exclusions` (allowlist `{"lm_head.weight"}` asserted in the encoder parity test; the shared encoder is body-only and owns no LM head)
3434
- passthrough_components: VAE config, scheduler config, tokenizer assets
3535
- retry_history: `<none>`
3636

@@ -40,23 +40,23 @@
4040
| Scheduler | `pytest tests/local_tests/zimage/test_zimage_scheduler_parity.py -v -s` | PASS (2/2) on Z-Image-Turbo, A40, 2026-05-12 | full `scheduler_config.json` now forwarded (was 3 keys) |
4141
| Tokenizer | `pytest tests/local_tests/zimage/test_zimage_tokenizer_parity.py -v -s` | PASS (2/2) on Z-Image-Turbo, A40, 2026-05-12 | tokenizer resolves to `Qwen2TokenizerFast`; `apply_chat_template` parity included |
4242
| VAE decode | `pytest tests/local_tests/zimage/test_zimage_vae_parity.py -v -s` | PASS (1/1) on Z-Image-Turbo, A40, 2026-05-12 | encode-path deferred |
43-
| Text encoder fp32 | `pytest tests/local_tests/zimage/test_zimage_encoder_parity.py::test_zimage_qwen3_encoder_parity_forward[fp32]` | PASS on Z-Image-Turbo, A40, 2026-05-12 | bit-exact (`last_hidden_state` max=0.0000, `hidden_states[-2]` max=0.0012) across both batches — FastVideo Qwen3 port is numerically correct |
44-
| Text encoder bf16 | `pytest tests/local_tests/zimage/test_zimage_encoder_parity.py::test_zimage_qwen3_encoder_parity_forward[bf16]` | PASS on Z-Image-Turbo, A40, 2026-05-12 | empirical (worst across 2 batches): `last_hidden_state` mean=0.0168 median=0.0127 (thresholds 0.025 / 0.020, 1.5x headroom); `hidden_states[-2]` mean=0.0739 median=0.0625 (thresholds 0.120 / 0.100, 1.6x headroom). Per-layer diag confirms monotonic accumulation across 35 layers, no single-layer spike — textbook bf16-tail signature |
45-
| Per-layer bf16 diag | `pytest tests/local_tests/zimage/test_zimage_encoder_parity.py::test_zimage_qwen3_encoder_per_layer_bf16_diagnostic -v -s` | PASS (informational only) on Z-Image-Turbo, A40, 2026-05-12 | prints 37 hidden-state diffs (embedding + 35 layers + post-norm) for future debugging |
43+
| Text encoder fp32 | `pytest tests/local_tests/zimage/test_zimage_encoder_parity.py::test_zimage_qwen3_encoder_parity_forward[fp32]` | RE-VALIDATION PENDING (shared encoder) | Prior PASS (A40, 2026-05-12, bit-exact: `last_hidden_state` max=0.0000, `hidden_states[-2]` max=0.0012) was against the now-removed bespoke `Qwen3Model`. The test now drives the shared `Qwen3ForCausalLM`; re-run on A40 to confirm parity carries over. |
44+
| Text encoder bf16 | `pytest tests/local_tests/zimage/test_zimage_encoder_parity.py::test_zimage_qwen3_encoder_parity_forward[bf16]` | RE-VALIDATION PENDING (shared encoder) | Prior PASS (A40, 2026-05-12): `last_hidden_state` mean=0.0168 median=0.0127 (thresholds 0.025 / 0.020); `hidden_states[-2]` mean=0.0739 median=0.0625 (thresholds 0.120 / 0.100). Measured against the bespoke encoder; thresholds may need recalibrating against the shared one. Re-run on A40. |
45+
| Per-layer bf16 diag | `pytest tests/local_tests/zimage/test_zimage_encoder_parity.py::test_zimage_qwen3_encoder_per_layer_bf16_diagnostic -v -s` | RE-VALIDATION PENDING (shared encoder) | Informational. Prior run (A40, 2026-05-12) printed 37 hidden-state diffs (embedding + 35 layers + post-norm); confirm the shared encoder's `hidden_states` length/indexing matches HF on re-run. |
4646

4747
## Open Questions
4848
| ID | Question | Owner | Needed By Phase | Status | Resolution |
4949
|---|---|---|---|---|---|
5050
| Q001 | Pin a Z-Image reference clone SHA in the README before handoff | prep | Phase 1 | resolved | Pinned `Tongyi-MAI/Z-Image@26f23eda626ffadda020b04ff79488e1d72004cd` (2026-05-12) |
5151
| Q002 | Final HF id for published Z-Image weights | prep | Phase 1 | resolved | `Tongyi-MAI/Z-Image-Turbo` (6B, 8 NFE, fits 16 GB) and `Tongyi-MAI/Z-Image` (full, 32.9 GB). Both Apache-2.0 |
52-
| Q003 | Does Z-Image use Qwen3 chat-template tokenization at pipeline runtime? Currently `Qwen3Config.is_chat_model=False` | pipeline | Phase 6 | open | |
52+
| Q003 | Does Z-Image use Qwen3 chat-template tokenization at pipeline runtime? The shared `Qwen3TextConfig` (from Flux2 Klein) defaults `is_chat_model=True`; Z-Image's removed bespoke config had `False`. Reconcile when wiring the pipeline preset. | pipeline | Phase 6 | open | |
5353

5454
## Issues And Blockers
5555
| ID | Phase | Component | Severity | Issue | Evidence | Owner | Status | Resolution |
5656
|---|---|---|---|---|---|---|---|---|
57-
| I001 | Phase 4 | text_encoder | medium | `Qwen3ForCausalLM` checkpoints ship `lm_head.weight`; encoder-only `Qwen3Model` does not own an LM head. Strict-load must allowlist this key. | `fastvideo/models/encoders/qwen3.py::Qwen3Model.ALLOWED_UNEXPECTED_KEYS`; encoder parity test asserts the unexpected-key set ⊆ allowlist | parity | resolved | Allowlist landed in this PR; loader raises on any other unexpected key. |
57+
| I001 | Phase 4 | text_encoder | medium | Z-Image-Turbo ships a full Qwen3 checkpoint with `lm_head.weight`; the shared encoder (`Qwen3ForCausalLM`) is body-only and owns no LM head, so that key goes unmatched. Must be allowlisted; anything else is a real silent drop. | encoder parity test asserts the unexpected-key set ⊆ `_ALLOWED_UNEXPECTED_KEYS = {"lm_head.weight"}` | parity | resolved | Allowlist enforced in the parity test (the shared encoder's loader is intentionally lenient as it serves multiple models, so the contract lives test-side). |
5858
| I002 | Phase 5/6 | scheduler | high | `scheduler_config.json` at `<weights>/Z-Image/scheduler/` does not pin `use_reference_discrete_timesteps=True`. Stock loaders will silently fall back to default timestep mode (numerically different — parity tests prove the divergence). | `tests/local_tests/zimage/test_zimage_scheduler_parity.py` sets the flag programmatically | pipeline | open | Pin the flag in `scheduler_config.json` when wiring the pipeline preset. |
59-
| I003 | Phase 6 | text_encoder | low | `Qwen3ArchConfig.text_len=512``tokenizer_kwargs.max_length=512`, but parity tests tokenize at 96/128. Pipeline preset must reconcile. | `fastvideo/configs/models/encoders/base.py::TextEncoderArchConfig.__post_init__` | pipeline | open | Set the correct `text_len` from the Z-Image preset config when adding the pipeline preset. |
59+
| I003 | Phase 6 | text_encoder | low | The shared `Qwen3TextArchConfig.text_len=512``tokenizer_kwargs.max_length=512`, but parity tests tokenize at 96/128. Pipeline preset must reconcile. | `fastvideo/configs/models/encoders/base.py::TextEncoderArchConfig.__post_init__` | pipeline | open | Set the correct `text_len` from the Z-Image preset config when adding the pipeline preset. |
6060
| I004 | Phase 4 | transformer | high | `ZImageTransformer2DModel` not yet ported | PR body, in-progress | port | open | Future PR. |
6161
| I005 | Phase 6 | pipeline | high | No FastVideo pipeline class, registry entry, preset, or example yet | n/a | pipeline | open | Future PR. |
6262

@@ -67,11 +67,13 @@
6767
## Decisions
6868
| Date | Decision | Rationale | Impact |
6969
|---|---|---|---|
70+
| 2026-06-21 | Reuse the shared `Qwen3ForCausalLM` encoder (+ `Qwen3TextConfig`) instead of the bespoke `Qwen3Model` this PR originally added. | `main` gained a config-driven Qwen3 text encoder via the Flux2 Klein port (#1349); it returns `BaseEncoderOutput(last_hidden_state, hidden_states)`, supports GQA + the same qkv/gate_up fusion, and `update_model_arch` populates Z-Image-Turbo's dims (2048/24/16) from its `config.json`. Keeping our parallel encoder would have been ~540 lines of duplicate maintenance. | Dropped `fastvideo/models/encoders/qwen3.py` + `fastvideo/configs/models/encoders/qwen3.py` from this PR; added a 1-line registry map (`"Qwen3Model" → Qwen3ForCausalLM`). Encoder parity needs re-validation on A40 against the shared class (see Parity Commands). |
7071
| 2026-05-12 | bf16 encoder parity uses distribution checks (mean + median) instead of element-wise `assert_close`; thresholds calibrated to empirical Z-Image-Turbo numbers on A40 + 1.5–1.6x headroom. | Z-Image-Turbo's Qwen3 text encoder is 35 layers (not the 24 originally assumed). Cross-kernel bf16 (FastVideo's fused QKVParallel + MergedColumnParallel + SiluAndMul vs HF's unfused equivalents) accumulates into a long max tail (~4.0 at layer 34) but median stays low (0.06). Per-layer diagnostic test confirmed growth is smooth and monotonic with no single-layer spike — textbook bf16-tail signature, fp32 is bit-exact. Element-wise `assert_close` is meaningless on this profile; mean + median + the per-layer diag together detect real bugs (which push mean ≫ atol AND median > 0.01). | Final assertion shape: `last_hidden_state` mean < 0.025, median < 0.020; `hidden_states[-2]` mean < 0.120, median < 0.100. Validated on NVIDIA A40 (driver 565.57.01, 46068 MiB) 2026-05-12. |
7172
| 2026-05-12 | `AutoModel.from_pretrained` uses `dtype=` (not `torch_dtype=`). | transformers 4.57.3 emits `torch_dtype is deprecated! Use dtype instead!` warning. Mrinaald's original `dtype=` kwarg was correct; the temporary switch to `torch_dtype=` (in response to a Copilot review comment) was reverted. ||
7273
| 2026-05-12 | Scheduler parity forwards the full `scheduler_config.json` dict (minus Diffusers loader keys), not 3 hand-picked keys. | Future on-disk fields (`time_shift_type`, `invert_sigmas`, etc.) would have been silently dropped. | Makes parity reflect the actual on-disk config. |
7374

7475
## Handoff Notes
75-
- Component parity (scheduler / tokenizer / VAE / Qwen3 fp32 + bf16) is **fully validated** on `Tongyi-MAI/Z-Image-Turbo` weights on A40 as of 2026-05-12.
76-
- Loader-side strictness is contract-asserted in the encoder parity test; do not relax `ALLOWED_UNEXPECTED_KEYS` without updating the test.
76+
- Scheduler / tokenizer / VAE parity are **fully validated** on `Tongyi-MAI/Z-Image-Turbo` weights on A40 (2026-05-12) and are unaffected by the encoder-reuse pivot.
77+
- Text-encoder parity (fp32 + bf16) needs **re-running on A40** against the shared `Qwen3ForCausalLM` — the 2026-05-12 PASS numbers were against the removed bespoke `Qwen3Model`. The fp32 path is expected to stay bit-exact (the shared encoder shipped validated for Flux2 Klein), but bf16 thresholds may need recalibration; confirm before claiming PASS.
78+
- Loader-side strictness is contract-asserted in the encoder parity test via `_ALLOWED_UNEXPECTED_KEYS`; do not relax it without updating the test.
7779
- Next port-stack steps (separate PR, not in #1339 scope): `ZImageTransformer2DModel` port (I004), pipeline preset including `use_reference_discrete_timesteps=True` pinned in `scheduler_config.json` (I002), conversion-or-direct-load story, SSIM media regression (blocked on PR #1321's `media_extension` helper landing for T2I `.png` output).

tests/local_tests/zimage/README.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,23 +62,26 @@ pytest tests/local_tests/zimage/ -v -s
6262
| Scheduler (`FlowMatchEulerDiscreteScheduler` + `use_reference_discrete_timesteps`) | [`test_zimage_scheduler_parity.py`](./test_zimage_scheduler_parity.py) | full `scheduler_config.json` forwarded; pipeline must also pin the new flag at load time | PASS |
6363
| Tokenizer (`TokenizerLoader` vs `AutoTokenizer`) | [`test_zimage_tokenizer_parity.py`](./test_zimage_tokenizer_parity.py) | `apply_chat_template` parity included | PASS |
6464
| VAE decode (`AutoencoderKL`) | [`test_zimage_vae_parity.py`](./test_zimage_vae_parity.py) | decode-only; encode path deferred until pipeline | PASS |
65-
| Text encoder (`Qwen3Model`) | [`test_zimage_encoder_parity.py`](./test_zimage_encoder_parity.py) | parametrized fp32 + bf16; bf16 uses calibrated `atol=0.05` + diagnostic prints + abs-mean drift check | PASS |
65+
| Text encoder (shared `Qwen3ForCausalLM`, reused) | [`test_zimage_encoder_parity.py`](./test_zimage_encoder_parity.py) | parametrized fp32 + bf16; bf16 uses calibrated distribution checks + diagnostic prints. Z-Image's `Qwen3Model` checkpoint routes to the shared encoder via the registry | RE-VALIDATION PENDING (shared encoder) |
6666

6767
## Known Blockers / Open Items
6868

6969
See [`PORT_STATUS.md`](./PORT_STATUS.md) for the live tracker. Highlights:
7070

71-
- `Qwen3Model.ALLOWED_UNEXPECTED_KEYS = {"lm_head.weight"}``Qwen3ForCausalLM`
72-
checkpoints carry an LM head the encoder bucket does not need. Loader raises
73-
if any other unexpected key appears.
71+
- The text encoder reuses the shared `Qwen3ForCausalLM` (added for Flux2 Klein,
72+
#1349); Z-Image-Turbo's `Qwen3Model` architecture string routes to it via the
73+
model registry. Z-Image-Turbo's full Qwen3 checkpoint carries an `lm_head.weight`
74+
the body-only encoder does not own, so the encoder parity test allowlists exactly
75+
that key (`_ALLOWED_UNEXPECTED_KEYS`) and fails on any other unmatched key.
7476
- `tests/local_tests/zimage/test_zimage_scheduler_parity.py` builds the
7577
FastVideo scheduler with `use_reference_discrete_timesteps=True` programmatically.
7678
When the pipeline lands, `<repo_root>/official_weights/Z-Image/scheduler/scheduler_config.json`
7779
must pin this flag, otherwise stock loaders will silently fall back to the
7880
default Diffusers timestep mode (numerically different).
79-
- `Qwen3ArchConfig.text_len = 512` derives `tokenizer_kwargs.max_length = 512`
81+
- The shared `Qwen3TextArchConfig.text_len = 512` derives `tokenizer_kwargs.max_length = 512`
8082
via `TextEncoderArchConfig.__post_init__`, but the parity tests tokenize at
81-
`max_length=96..128`. Reconcile when the pipeline preset lands.
83+
`max_length=96..128`. Reconcile when the pipeline preset lands. (Also note the
84+
shared config defaults `is_chat_model=True`, vs Z-Image's removed bespoke `False`.)
8285

8386
## Review Notes
8487

tests/local_tests/zimage/test_zimage_encoder_parity.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
55
This compares:
66
1) direct transformers AutoModel output, and
7-
2) FastVideo Qwen3Model wrapper output,
7+
2) FastVideo's shared Qwen3 encoder (``Qwen3ForCausalLM``, added for Flux2
8+
Klein and reused here — Z-Image-Turbo's ``Qwen3Model`` checkpoint routes
9+
to it via the model registry),
810
911
using identical local checkpoint, tokenization, and inputs.
1012
@@ -24,10 +26,17 @@
2426
from transformers import AutoModel, AutoTokenizer
2527
from safetensors.torch import safe_open
2628

27-
from fastvideo.configs.models.encoders.qwen3 import Qwen3Config
29+
from fastvideo.configs.models.encoders.qwen3 import Qwen3TextConfig
2830
from fastvideo.distributed.parallel_state import cleanup_dist_env_and_memory, maybe_init_distributed_environment_and_model_parallel
2931
from fastvideo.models.registry import ModelRegistry
3032

33+
# Strict-load contract: the shared Qwen3 encoder is body-only (embed_tokens +
34+
# layers + norm, no lm_head). Z-Image-Turbo ships a full Qwen3 checkpoint, so
35+
# `lm_head.weight` is the only key that may go unmatched; anything else means a
36+
# real silent drop. Enforced here in the test since the shared encoder's loader
37+
# is intentionally lenient (it's used by multiple models).
38+
_ALLOWED_UNEXPECTED_KEYS = {"lm_head.weight"}
39+
3140

3241
REPO_ROOT = Path(__file__).resolve().parents[3]
3342
ZIMAGE_TEXT_ENCODER_DIR = REPO_ROOT / "official_weights" / "Z-Image" / "text_encoder"
@@ -172,17 +181,17 @@ def test_zimage_qwen3_encoder_parity_forward(dtype: torch.dtype):
172181
for k in ("_name_or_path", "transformers_version", "model_type", "torch_dtype"):
173182
cfg_raw.pop(k, None)
174183

175-
cfg = Qwen3Config()
184+
cfg = Qwen3TextConfig()
176185
cfg.update_model_arch(cfg_raw)
177186
fv = fv_cls(cfg).eval()
178187
loaded = fv.load_weights(_iter_pretrained_safetensors(ZIMAGE_TEXT_ENCODER_DIR))
179188
assert loaded, "No Qwen3 weights were loaded into FastVideo model"
180189
fv = fv.to(device=device, dtype=dtype)
181190

182-
# Per add-model-02-parity strict-load contract: assert the production
183-
# loader's allowlist matches the actual checkpoint surface. Non-encoder
184-
# heads like `lm_head.weight` (from `Qwen3ForCausalLM` checkpoints) are
185-
# the only acceptable unexpected keys; anything else means a real drop.
191+
# Strict-load contract: assert the checkpoint surface matches the model's
192+
# params modulo the allowlist. Non-encoder heads like `lm_head.weight`
193+
# (the body-only encoder has no lm_head) are the only acceptable unmatched
194+
# keys; anything else means a real silent drop.
186195
checkpoint_keys = {name for name, _ in _iter_pretrained_safetensors(ZIMAGE_TEXT_ENCODER_DIR)}
187196
checkpoint_keys = {k[len("model."):] if k.startswith("model.") else k for k in checkpoint_keys}
188197
param_names = {name for name, _ in fv.named_parameters()}
@@ -198,9 +207,9 @@ def test_zimage_qwen3_encoder_parity_forward(dtype: torch.dtype):
198207
else:
199208
mapped_keys.add(ckpt_name)
200209
unexpected = mapped_keys - param_names - {"rotary_emb.inv_freq", "rotary_emb.cos_cached", "rotary_emb.sin_cached"}
201-
assert unexpected <= fv_cls.ALLOWED_UNEXPECTED_KEYS, (
202-
f"Unexpected checkpoint keys not in ALLOWED_UNEXPECTED_KEYS: "
203-
f"{sorted(unexpected - fv_cls.ALLOWED_UNEXPECTED_KEYS)}"
210+
assert unexpected <= _ALLOWED_UNEXPECTED_KEYS, (
211+
f"Unexpected checkpoint keys not in allowlist: "
212+
f"{sorted(unexpected - _ALLOWED_UNEXPECTED_KEYS)}"
204213
)
205214

206215
with torch.no_grad():
@@ -301,7 +310,7 @@ def test_zimage_qwen3_encoder_per_layer_bf16_diagnostic():
301310
cfg_raw = _load_json(ZIMAGE_TEXT_ENCODER_DIR / "config.json")
302311
for k in ("_name_or_path", "transformers_version", "model_type", "torch_dtype"):
303312
cfg_raw.pop(k, None)
304-
cfg = Qwen3Config()
313+
cfg = Qwen3TextConfig()
305314
cfg.update_model_arch(cfg_raw)
306315
fv = fv_cls(cfg).eval()
307316
fv.load_weights(_iter_pretrained_safetensors(ZIMAGE_TEXT_ENCODER_DIR))

0 commit comments

Comments
 (0)