Skip to content

convert_model_to_diffusers: export via official diffusers, drop diffusers-cosmos3 shim#98

Merged
Dinghow merged 4 commits into
mainfrom
dinghaoy/use_official_diffusers_for_model_convert
Jul 9, 2026
Merged

convert_model_to_diffusers: export via official diffusers, drop diffusers-cosmos3 shim#98
Dinghow merged 4 commits into
mainfrom
dinghaoy/use_official_diffusers_for_model_convert

Conversation

@Dinghow

@Dinghow Dinghow commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

What

Replace the packages/diffusers-cosmos3 shim with official diffusers (≥ 0.39.0) in the checkpoint exporter, and delete the package.

Why

  • diffusers 0.39.0 ships Cosmos3 natively (Cosmos3OmniPipeline, Cosmos3OmniTransformer, Cosmos3AVAEAudioTokenizer); the shim was a pre-upstream stand-in.
  • The shim's reimplemented denoising forward has a small per-step numeric drift that compounds into visibly soft t2i images — nothing should run inference through it.
  • The exporter's key handling was stale against the current flat key layout (model.-nested q_proj/vae2llm vs flat to_q/proj_in) and could not strict-load.

Changes

  • Rewrite scripts/_convert_model_to_diffusers.py against the official classes; keep the repo-specific extensions (action-projection export, vision_encoder/ sidecar for the transformers/vLLM consumers).
  • Save sound_tokenizer/ as a real Cosmos3AVAEAudioTokenizer pipeline component instead of hand-writing safetensors + patching model_index.json.
  • OSS-path fixes surfaced by e2e runs: consume the HF diffusers-layout sound_tokenizer/ directly, load vision weights from vision_encoder/-subfolder sources, resolve the text config via get_text_config() for nested Qwen3VLConfig.
  • Deps: diffusers ≥ 0.39.0 (lock 0.37.0 → 0.39.0, safetensors 0.7.0 → 0.8.0), remove the shim from extras / uv sources, drop the now-fixed diffusers audit ignores.

Relation to upstream scripts/convert_cosmos3_to_diffusers.py

Upstream diffusers ships a converter for the same task, and this rewrite adopts its verified key-remap table. It cannot be used directly here:

  • its imports target the internal i4 module namespace (projects.cosmos3.vfm.*), so it does not run against this repo;
  • it silently drops action-projection weights (it never reads the action config, and strict-load still passes because neither side has the keys);
  • it does not produce the dual-purpose repo this exporter ships (no vision_encoder/ sidecar, no top-level vLLM config / unified safetensors index).

Verification

  • Key remap round-trips exactly against the real diffusers 0.39.0 Cosmos3OmniTransformer state-dict key set (action + sound enabled), and inverts cleanly through the native inference loader's mapping.
  • E2E, public checkpoint: Cosmos3-Nano → export → official Cosmos3OmniPipeline t2i sharp; native inference re-read of the export sharp.
  • E2E, real training DCP: an i4-trainer 8B DPO checkpoint (flat net.*/net_ema.* keys, t2i-only) converts and generates sharp t2i through the official pipeline (driven via the inner exporter; the CLI wrapper asserts action_gen/sound_gen and targets full omni checkpoints).

Compat notes

  • Previously exported repos (model_index: Cosmos3OmniDiffusersPipeline) still load via an explicit Cosmos3OmniPipeline.from_pretrained(...).
  • Loading without cosmos_guardrail installed requires enable_safety_checker=False (matches upstream semantics).

Dinghow and others added 4 commits July 8, 2026 07:01
…sers-cosmos3 shim

diffusers >= 0.39.0 ships Cosmos3OmniPipeline / Cosmos3OmniTransformer /
Cosmos3AVAEAudioTokenizer natively, so the pre-upstream diffusers_cosmos3
shim is obsolete. Its own denoising __call__ also had a compounding
per-step numeric drift that produced soft t2i images, so nothing should
run inference through it.

- Rewrite scripts/_convert_model_to_diffusers.py against the official
  classes, adopting the key remap from upstream
  scripts/convert_cosmos3_to_diffusers.py (strip the language model's
  `model.` nesting; q_proj/*_moe_gen -> to_q/add_q_proj/... attention
  names; vae2llm/llm2vae -> proj_in/proj_out; time_embedder mlp.0/2 ->
  linear_1/2; sound2llm/llm2sound -> audio_proj_in/out). This also fixes
  the exporter's stale native-layout key handling, which could not
  strict-load into the current flat-layout model classes.
- Keep the repo-specific extensions upstream lacks: action projection
  export (action2llm/llm2action/action_modality_embed ->
  action_proj_in/out) and the vision_encoder/ sidecar save for the
  transformers/vLLM consumers of the exported repository.
- Build sound_tokenizer/ as a real Cosmos3AVAEAudioTokenizer pipeline
  component instead of hand-writing safetensors + patching
  model_index.json; drop the now-unconditional remap_* CLI flags.
- Construct the pipeline with enable_safety_checker=False (the converter
  env does not ship cosmos_guardrail); consumers can opt back in at load
  time.
- Remove packages/diffusers-cosmos3, bump diffusers to >=0.39.0
  (safetensors follows to 0.8.0), and drop the now-fixed diffusers audit
  ignores.
- Add a key-remap test that round-trips exporter keys through the
  inference loader's inverse mapping; the full remap was additionally
  verified against the real diffusers 0.39.0 Cosmos3OmniTransformer
  state-dict key set (action+sound enabled, exact match).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ckpoints

Three issues surfaced by an end-to-end Cosmos3-Nano export run:

- The wrapper resolved the AVAE sound tokenizer via `.hf.download()`,
  which bypasses the CheckpointConfig post_download hook that
  synthesizes the legacy .ckpt — and the HF-published sound_tokenizer/
  is already in the diffusers layout anyway. Consume
  config.json + diffusion_pytorch_model.safetensors directly (the
  converter passes that layout through unchanged), falling back to the
  legacy file pair named by the model config's avae_path.
- Vision-encoder weights in diffusers-layout source checkpoints live
  under vision_encoder/ with bare Qwen3VLVisionModel keys, not under
  the native `model.visual.` prefix; add that fallback to
  _load_vision_encoder.
- language_model.config is a nested VL config (Qwen3VLConfig) whose
  text-model fields live on text_config; resolve via get_text_config().

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The OSS CI only runs the curated GPU suites under tests/ and does not
collect package-internal *_test.py files, so this test never runs here.
The remap was already verified against the real diffusers 0.39.0
Cosmos3OmniTransformer state-dict key set and by the end-to-end
Cosmos3-Nano export + inference run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…kip-vision-encoder

Surfaced by exporting a vision-generation-only DPO DCP checkpoint (no
Qwen3-VL visual weights): vision_gen gates the generation branch, so such
checkpoints still set it, and the vision-encoder load then fails with an
unhelpful error unless --skip-vision-encoder is passed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Dinghow Dinghow merged commit b2237fd into main Jul 9, 2026
9 checks passed
@Dinghow Dinghow deleted the dinghaoy/use_official_diffusers_for_model_convert branch July 9, 2026 06:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants