|
| 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