Skip to content

Commit e687153

Browse files
lfengadclaude
andcommitted
Support Cosmos3-Super task-specialized (Text2Image / Image2Video) checkpoints
These task-specialized diffusers checkpoints reuse the Cosmos3-Super architecture but omit unused modality weights and bundle their own VLM processor. Loading them previously failed, and the processor pulled a redundant full base-Super download. - inference/model.py: tolerate absent action/sound projection-head weights in the diffusers load planner, mirroring the existing vision carve-out. Fixes the masked "TypeError: cannot pickle code objects" that surfaced when DCP tried to broadcast the missing-tensor ValueError across ranks. No-op for self-consistent base checkpoints: Nano/Super provide all modality weights, so the guards never fire. - inference: add CheckpointConfig.vlm_processor_from_checkpoint. When set, the loader sources the VLM processor from the loaded checkpoint's own bundled files instead of the repository hardcoded in the model config, avoiding a redundant base-Super download. Enabled for the two task checkpoints; base Nano/Super keep their configured repository. - data/vfm/processors: clearer error when build_processor_lazy is given neither a repository nor a tokenizer_type source (never fires for existing call sites; only improves a previously-TypeError path). - docs/faq.md: add EADDRINUSE / --master-port entry. - docs/superpowers/specs: design spec for the processor-source change. Verified: Text2Image (t2i) and Image2Video (i2v) load and generate; a full base Cosmos3-Nano t2i run is unchanged with strict weight loading intact (carve-out never triggers). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 411d25b commit e687153

7 files changed

Lines changed: 269 additions & 0 deletions

File tree

cosmos_framework/data/vfm/processors/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,4 +174,9 @@ def build_processor_lazy(
174174
if subdir:
175175
local_path = os.path.join(local_path, subdir)
176176
return sys.modules[__name__].build_processor(local_path, **kwargs)
177+
if not args and "tokenizer_type" not in kwargs:
178+
raise ValueError(
179+
"build_processor_lazy requires either 'repository' (+ 'revision') or a "
180+
"'tokenizer_type' source (e.g. a local checkpoint directory); got neither."
181+
)
177182
return sys.modules[__name__].build_processor(*args, **kwargs)

cosmos_framework/inference/args.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,33 @@ def build_sample(self, *, model_config: Any) -> OmniSampleArgs:
869869
revision="main",
870870
),
871871
),
872+
# Task-specialized Super variants published as diffusers HF checkpoints.
873+
# s3_uri is unused for HF-backed checkpoints (kept for parity with the
874+
# registry schema); the architecture lives in each model YAML.
875+
"Cosmos3-Super-Image2Video": CheckpointConfig(
876+
model_memory_bytes=MODEL_MEMORY_BYTES_BY_SIZE["32B"],
877+
config_file=str(CONFIG_DIR / "model/Cosmos3-Super.yaml"),
878+
s3_uri="s3://bucket1/cosmos3_vfm/cosmos3_ga_image2video/",
879+
hf=CheckpointDirHf(
880+
repository="nvidia/Cosmos3-Super-Image2Video",
881+
revision="main",
882+
),
883+
# Self-contained checkpoint: use its bundled processor instead of
884+
# downloading the base Cosmos3-Super repo just for the tokenizer.
885+
vlm_processor_from_checkpoint=True,
886+
),
887+
"Cosmos3-Super-Text2Image": CheckpointConfig(
888+
model_memory_bytes=MODEL_MEMORY_BYTES_BY_SIZE["32B"],
889+
config_file=str(CONFIG_DIR / "model/Cosmos3-Super.yaml"),
890+
s3_uri="s3://bucket1/cosmos3_vfm/cosmos3_ga_text2image/",
891+
hf=CheckpointDirHf(
892+
repository="nvidia/Cosmos3-Super-Text2Image",
893+
revision="main",
894+
),
895+
# Self-contained checkpoint: use its bundled processor instead of
896+
# downloading the base Cosmos3-Super repo just for the tokenizer.
897+
vlm_processor_from_checkpoint=True,
898+
),
872899
}
873900
DEFAULT_CHECKPOINT_NAME = "Cosmos3-Nano"
874901
DEFAULT_CHECKPOINT = _CHECKPOINTS[DEFAULT_CHECKPOINT_NAME]

cosmos_framework/inference/common/args.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,17 @@ class CheckpointConfig(pydantic.BaseModel):
385385
hf: CheckpointDirHf
386386
"""Config for checkpoint on Hugging Face."""
387387

388+
vlm_processor_from_checkpoint: bool = False
389+
"""When True, load the VLM text/vision processor from the checkpoint's own
390+
bundled files (its local download directory) instead of the repository
391+
hardcoded in the model config's ``vlm_config.tokenizer`` node.
392+
393+
Set this only for self-contained checkpoints that ship their own processor
394+
at the repository root (e.g. the task-specialized Text2Image / Image2Video
395+
diffusers checkpoints). Avoids a redundant download of the base model repo
396+
just to obtain the tokenizer.
397+
"""
398+
388399
def download(self) -> str:
389400
return self.hf.download()
390401

@@ -404,6 +415,7 @@ class CheckpointArgs(ConfigArgs):
404415
model_memory_bytes: int | None
405416

406417
checkpoint_hf: CheckpointDirHf | None
418+
vlm_processor_from_checkpoint: bool = False
407419

408420
credential_path: str
409421
use_ema_weights: bool
@@ -443,6 +455,8 @@ class CheckpointOverrides(ConfigOverrides):
443455

444456
checkpoint_hf: Suppress[CheckpointDirHf | None] = None
445457
"""Hugging Face checkpoint directory."""
458+
vlm_processor_from_checkpoint: Suppress[bool] = False
459+
"""Load the VLM processor from the loaded checkpoint instead of a hardcoded repo."""
446460

447461
credential_path: Training[str] = "credentials/gcp_checkpoint.secret"
448462
"""Path to S3 credentials file for remote checkpoint loading."""
@@ -459,6 +473,7 @@ def _build_checkpoint(self, checkpoints: dict[str, CheckpointConfig]):
459473
self.model_memory_bytes = checkpoint.model_memory_bytes
460474
self.config_file = checkpoint.config_file
461475
self.checkpoint_hf = checkpoint.hf
476+
self.vlm_processor_from_checkpoint = checkpoint.vlm_processor_from_checkpoint
462477
elif self.checkpoint_path.startswith("s3://"):
463478
self.checkpoint_type = CheckpointType.DCP
464479
self.checkpoint_path = self.checkpoint_path.rstrip("/")

cosmos_framework/inference/inference.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,15 @@ def _create(cls, setup_args: SetupArgs, **kwargs: Any) -> Self:
10501050
config = None
10511051
else:
10521052
model_dict = setup_args.load_model_config_dict()
1053+
if setup_args.vlm_processor_from_checkpoint:
1054+
# Source the VLM processor from the loaded checkpoint's own
1055+
# bundled files instead of the repository hardcoded in the
1056+
# model config. Drops the redundant base-model download.
1057+
tokenizer_cfg = model_dict["config"]["vlm_config"]["tokenizer"]
1058+
tokenizer_cfg.pop("repository", None)
1059+
tokenizer_cfg.pop("revision", None)
1060+
tokenizer_cfg.pop("subdir", None)
1061+
tokenizer_cfg["tokenizer_type"] = str(checkpoint_path)
10531062
config = Cosmos3OmniConfig(model=model_dict)
10541063
model = Cosmos3OmniModel.from_pretrained_dcp(
10551064
checkpoint_path,

cosmos_framework/inference/model.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,16 @@ def set_up_planner(
318318
missing_keys = set(target_state_dict) - loaded_keys
319319
if not self.has_vision_weights:
320320
missing_keys = {key for key in missing_keys if not key.startswith("language_model.visual.")}
321+
# Task-specialized checkpoints (e.g. Text2Image, Image2Video) omit the
322+
# optional generative-modality projection heads (action, sound). They
323+
# are unused for those tasks, so tolerate their absence the same way
324+
# vision weights are tolerated when the checkpoint provides none of them.
325+
for modality_prefixes in (
326+
("action2llm.", "llm2action.", "action_modality_embed"),
327+
("sound2llm.", "llm2sound.", "sound_modality_embed"),
328+
):
329+
if not any(key.startswith(modality_prefixes) for key in loaded_keys):
330+
missing_keys = {key for key in missing_keys if not key.startswith(modality_prefixes)}
321331
if missing_keys:
322332
sample = sorted(missing_keys)[:10]
323333
raise ValueError(

docs/faq.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,25 @@ Delete the torchinductor cache under the /tmp directory, `rm -rf /tmp/torchinduc
225225

226226
---
227227

228+
### Q: I get `torch.distributed.DistNetworkError: ... port: 29500 ... EADDRINUSE, address already in use`
229+
230+
`torchrun` defaults its rendezvous to port `29500`. The error means that port is already taken on the node — usually because another `torchrun` job (yours or someone else's on a shared node) is still using it.
231+
232+
Pass a different free port with `--master-port`, placed **before** `-m` (it is a `torchrun` argument, not an inference argument):
233+
234+
```shell
235+
torchrun --nproc-per-node=8 --master-port=29501 -m cosmos_framework.scripts.inference \
236+
--parallelism-preset=throughput \
237+
-i "inputs/omni/t2i.json" \
238+
-o outputs/omni_t2i \
239+
--checkpoint-path Cosmos3-Super-Text2Image \
240+
--seed=0
241+
```
242+
243+
Any free port works (e.g. `29501`, `29510`); give each concurrent job on the same node a distinct port. Alternatively, `--rdzv-endpoint=localhost:0` lets `torchrun` auto-pick a free port.
244+
245+
---
246+
228247
## Training
229248

230249
### Q: I get `torch.cuda.OutOfMemoryError` during training (SFT)
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
# Design: Optional VLM-Processor Source ("unset → use loaded checkpoint")
2+
3+
**Date:** 2026-06-03
4+
**Status:** Approved (pending spec review)
5+
**Area:** Cosmos3 inference — model/processor loading
6+
7+
## Problem
8+
9+
When loading a checkpoint, the VLM text/vision processor (tokenizer, chat
10+
template, image/video preprocessor) is sourced from a repository hardcoded in
11+
the model config:
12+
13+
```yaml
14+
# cosmos_framework/inference/configs/model/Cosmos3-Super.yaml
15+
vlm_config:
16+
tokenizer:
17+
_target_: cosmos3._src.vfm.processors.build_processor_lazy
18+
repository: nvidia/Cosmos3-Super
19+
revision: main
20+
```
21+
22+
`build_processor_lazy(repository="nvidia/Cosmos3-Super", revision="main")`
23+
downloads that repo via `CheckpointDirHf(...).download()` (with `--include '*'`,
24+
i.e. **all 27 transformer shards of the 32B base model**) just to read the
25+
processor files from its root.
26+
27+
This is wasteful and conceptually wrong for the **task-specialized** checkpoints
28+
(`Cosmos3-Super-Text2Image`, `Cosmos3-Super-Image2Video`). Those are
29+
self-contained HF checkpoints that already bundle their own processor at the
30+
repository root (`preprocessor_config.json`, `video_preprocessor_config.json`,
31+
`tokenizer.json`, `tokenizer_config.json`, `chat_template.json`, `merges.txt`,
32+
`vocab.json`, plus a `text_tokenizer/` subdir). They reuse `Cosmos3-Super.yaml`
33+
only because they share the Super architecture — but doing so drags in a full,
34+
redundant download of the base Super repo solely for the processor.
35+
36+
## Goal
37+
38+
Make the processor source configurable with the following semantics, confirmed
39+
with the requester:
40+
41+
- **`repository` unset in the tokenizer config → load the processor from the
42+
checkpoint currently being loaded** (its already-downloaded local directory).
43+
- **`repository` set (today's `Cosmos3-Nano` / `Cosmos3-Super` case) → load from
44+
that repository, exactly as today.**
45+
46+
Base `Cosmos3-Nano` / `Cosmos3-Super` behavior must be unchanged. The
47+
task-specialized checkpoints must stop downloading base Super for the processor.
48+
49+
## Key Facts (verified)
50+
51+
- `OmniMoTModel.set_up_tokenizers()` builds the processor via
52+
`self.vlm_processor = lazy_instantiate(self.vlm_config.tokenizer)`
53+
(`cosmos_framework/model/vfm/omni_mot_model.py:140`). This runs inside
54+
`Cosmos3OmniModel(config)` construction.
55+
- `build_processor_lazy(*args, repository=None, revision=None, subdir="", **kwargs)`
56+
(`cosmos_framework/data/vfm/processors/__init__.py:141`) already has two modes:
57+
- `repository` set → download repo, then `build_processor(local_path, **kwargs)`.
58+
- `repository` is `None` → `build_processor(*args, **kwargs)` (needs a
59+
positional/`tokenizer_type` source).
60+
- `build_processor(tokenizer_type, ...)`
61+
(`cosmos_framework/data/vfm/processors/__init__.py:100`) already loads from a
62+
**local directory**: `if os.path.isdir(tokenizer_type): return
63+
Qwen3VLProcessor(tokenizer_type, ...)`.
64+
- `OmniInference._create` (`cosmos_framework/inference/inference.py`, the
65+
`else` / non-`MODULE` branch ~line 1047) has, in the same scope:
66+
`checkpoint_path = setup_args.download_checkpoint()` (the already-downloaded
67+
local dir) and the raw `model_dict = setup_args.load_model_config_dict()`,
68+
immediately before `config = Cosmos3OmniConfig(model=model_dict)`.
69+
- The dict path to the processor node is
70+
`model_dict["config"]["vlm_config"]["tokenizer"]` (verified against
71+
`Cosmos3-Super.yaml` structure: `model.config.vlm_config.tokenizer`).
72+
- The active checkpoint's registry entry is the `CheckpointConfig` defined in
73+
`cosmos_framework/inference/common/args.py:370` (fields: `model_memory_bytes`,
74+
`config_file`, `s3_uri`, `hf`, methods `download()`, `pretrained_kwargs()`).
75+
The registry dict is `OmniSetupArgs.CHECKPOINTS` (`= _CHECKPOINTS` in
76+
`args.py`), keyed by checkpoint name.
77+
78+
## Design
79+
80+
### 1. Registry flag
81+
82+
Add one field to `CheckpointConfig` (`cosmos_framework/inference/common/args.py`):
83+
84+
```python
85+
vlm_processor_from_checkpoint: bool = False
86+
"""When True, load the VLM text/vision processor from the checkpoint's own
87+
bundled files (the local download directory) instead of the repository
88+
hardcoded in the model config's ``vlm_config.tokenizer`` node."""
89+
```
90+
91+
Set `vlm_processor_from_checkpoint=True` on the two task-specialized entries in
92+
`_CHECKPOINTS` (`cosmos_framework/inference/args.py`):
93+
`Cosmos3-Super-Text2Image` and `Cosmos3-Super-Image2Video`.
94+
`Cosmos3-Nano` and `Cosmos3-Super` keep the default `False`.
95+
96+
### 2. Loader injection
97+
98+
In `OmniInference._create`, in the non-`MODULE` branch, after `checkpoint_path`
99+
and `model_dict` are obtained and **before** `Cosmos3OmniConfig(model=model_dict)`:
100+
101+
```python
102+
ckpt_cfg = setup_args.CHECKPOINTS.get(setup_args.checkpoint_path)
103+
if ckpt_cfg is not None and ckpt_cfg.vlm_processor_from_checkpoint:
104+
tok = model_dict["config"]["vlm_config"]["tokenizer"]
105+
tok.pop("repository", None)
106+
tok.pop("revision", None)
107+
tok.pop("subdir", None)
108+
tok["tokenizer_type"] = str(checkpoint_path)
109+
```
110+
111+
This drives `build_processor_lazy` down its existing `repository is None` path →
112+
`build_processor(tokenizer_type=<checkpoint_dir>)` → `os.path.isdir` →
113+
`Qwen3VLProcessor(<checkpoint_dir>)`. The processor loads from the
114+
already-downloaded checkpoint; **no additional download** occurs.
115+
116+
Notes:
117+
- The rewrite is a no-op for checkpoints not in the registry
118+
(`setup_args.checkpoint_path` is a custom path/URI) — `.get` returns `None`.
119+
- The rewrite mutates the raw `model_dict` (cleaner than mutating the
120+
post-transform `Cosmos3OmniConfig.model`).
121+
- This branch is the one the task-specialized checkpoints take (they use a YAML
122+
`config_file`, i.e. `config_file_type != MODULE`). The `MODULE` /
123+
`load_model_from_checkpoint` branch is out of scope (base DCP experiments).
124+
125+
### 3. No behavioral change to `build_processor_lazy` / `build_processor`
126+
127+
Both already support the local-dir mode. Only one small robustness improvement:
128+
tighten the error when `build_processor_lazy` is called with neither
129+
`repository` nor a `tokenizer_type` source, so a future misconfiguration fails
130+
with a clear message instead of a `TypeError` on a missing positional arg.
131+
132+
### 4. Behavior matrix
133+
134+
| Checkpoint | `repository` in YAML | flag | Processor source | Extra download |
135+
| -------------------------- | -------------------- | ----- | ----------------------------------------- | -------------- |
136+
| `Cosmos3-Nano` | `nvidia/Cosmos3-Nano`| False | `nvidia/Cosmos3-Nano` (as today) | as today |
137+
| `Cosmos3-Super` | `nvidia/Cosmos3-Super`| False| `nvidia/Cosmos3-Super` (as today) | as today |
138+
| `Cosmos3-Super-Text2Image` | (overridden away) | True | the loaded checkpoint dir | none |
139+
| `Cosmos3-Super-Image2Video`| (overridden away) | True | the loaded checkpoint dir | none |
140+
141+
### 5. Safety / failure mode
142+
143+
If a flagged checkpoint directory does not contain the processor files,
144+
`Qwen3VLProcessor(<dir>)` raises a clear file-not-found / load error at
145+
`set_up_tokenizers` time. The flag is therefore only set for self-contained
146+
checkpoints (the task HF repos qualify — verified file listings). Documented as
147+
a precondition of the flag.
148+
149+
## Components & boundaries
150+
151+
- **`CheckpointConfig` (common/args.py):** declares the capability flag. Pure
152+
data; no behavior change.
153+
- **Checkpoint registry (`args.py`):** declares which checkpoints are
154+
self-contained. Data only.
155+
- **`OmniInference._create` (inference.py):** the single place that translates
156+
the flag + `checkpoint_path` into a processor-config rewrite. The only new
157+
control flow.
158+
- **`build_processor_lazy` / `build_processor`:** unchanged dispatch; consume a
159+
local dir. (One error-message hardening.)
160+
161+
## Testing
162+
163+
1. **Unit — config rewrite.** Construct `model_dict` (or a minimal stand-in with
164+
the `config.vlm_config.tokenizer` node) and invoke the rewrite logic for:
165+
- a flagged checkpoint → assert the tokenizer node has `tokenizer_type ==
166+
str(checkpoint_path)` and no `repository`/`revision`/`subdir`;
167+
- an unflagged checkpoint → assert the node is untouched (still has
168+
`repository`).
169+
If the rewrite is extracted into a small helper, test the helper directly.
170+
2. **Unit — `build_processor` local dir.** Given a temp dir with the minimal
171+
processor files, assert `build_processor(<dir>)` returns a `Qwen3VLProcessor`
172+
(mirrors existing local-artifact behavior; may already be covered).
173+
3. **End-to-end smoke (manual / existing).** The already-validated t2i run
174+
(`Cosmos3-Super-Text2Image`, 8 GPUs) completes and the logs show **no**
175+
`hf download ... nvidia/Cosmos3-Super ... --include '*'` line for the
176+
processor.
177+
178+
## Out of scope
179+
180+
- The `MODULE` / `load_model_from_checkpoint` (base DCP experiment) path.
181+
- Changing the base `Cosmos3-Nano` / `Cosmos3-Super` processor source.
182+
- The separate i2v sampling host-RAM OOM issue (tracked elsewhere).
183+
- Subdir selection within the checkpoint (root layout is sufficient and matches
184+
the current `subdir: ""` behavior).

0 commit comments

Comments
 (0)