Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .agents/skills/adding-a-new-model/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ Read these when you need deeper detail on a specific topic:
- Find a small checkpoint on HuggingFace Hub for testing
- Have the HuggingFace `transformers` source available to reference the
PyTorch implementation
- Pin the checkpoint revision before collecting configs, weights, processors,
parity data, or goldens

## Step-by-step

Expand Down Expand Up @@ -219,6 +221,12 @@ Then add the new module to `src/mobius/_configs/per_model/__init__.py` so its
side-effect registration runs at import time. The dispatcher filters hooks by
the declared model_type strings, so unrelated models never see your hook.

Match upstream config semantics, not just field names: preserve transformation
order (rounding/scaling), explicit `None`, alias precedence, zero-as-disabled
sentinels, and wrapped/unwrapped composite configs. Add focused tests for each
nontrivial transform. Test both trusted/custom config classes and raw pinned
JSON, especially nested decoder fields such as head/layer counts.

### 7. Write tests

See the **writing-tests** skill for full details. At minimum:
Expand Down Expand Up @@ -281,6 +289,7 @@ see the [quality-checklist skill](../quality-checklist/SKILL.md).
- [ ] ORT GenAI test added to `tests/ort_genai_test.py` (text-generation and VLM models)
- [ ] CLI build works (`mobius build --model ...`)
- [ ] Multi-dtype correctness verified (fp32, fp16, bf16)
- [ ] Pinned revision reaches every Hub/processor/weight/golden call

**Note:** Default optimizer passes (CSE, deduplicate initializers, identity
elimination, remove unused nodes/opsets) are applied automatically.
Expand Down Expand Up @@ -426,6 +435,18 @@ config-driven features for the offending subclass.
> embedding table off-by-one), read
> [`references/weight-preprocessing.md`](references/weight-preprocessing.md).

### 8. Compatibility and optional dependency traps

- Guard imports of symbols from unpinned dependencies when older supported
versions may not define them.
- If remote code imports an optional package, first prove the selected model
path needs it. Scope any test-only shim to that unused import; do not add a
production fallback or require an irrelevant package.
- When extending a public component, append optional parameters after existing
positional parameters and add a positional-call compatibility test.
- Helpers with rank/shape contracts must receive a value of that rank; do not
rely on incidental compatibility from a higher-rank tensor.

## Reference examples

| Complexity | File | Why |
Expand Down
10 changes: 5 additions & 5 deletions .agents/skills/debugging-multimodal/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,11 +261,11 @@ via `MOBIUS_ORT_LOWER_OPSET_FOR_EP=1`). See `src/mobius/_flags.py`.

### Encoder input dtype alignment

Encoder task inputs should be declared with `dtype=config.dtype` so
entry tensors match the model compute dtype (float32/float16/bfloat16).
In the current codebase, multimodal encoder task builders set encoder
inputs directly to `config.dtype` (there is no `_cast_encoder_input()`
helper in `src/mobius/tasks/_base.py`).
Real vision/audio processors emit float32. Encoder graph inputs should
therefore be float32 even for fp16/bf16 exports, with one Cast to
`config.dtype` at graph entry. Verify this with an actual processor batch
and a graph I/O dtype assertion; synthetic feeds can hide the mismatch.
Some older task builders still use `config.dtype` and are not safe templates.

### GQA for KV-shared layers

Expand Down
20 changes: 20 additions & 0 deletions .agents/skills/diffusion-models/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,26 @@ Compare `named_parameters()` output with HF weight names. Use the
techniques from the **weight-name-alignment** skill to minimize
`preprocess_weights`.

### 7. Prove pipeline executability

A component graph or metadata file is not a runnable diffusion pipeline.
Before advertising runtime support, execute the complete path:

`image encoder/VAE -> latent packing -> denoiser loop + scheduler -> target token unpacking -> VAE decoder`

Verify sample/output ranks, source-vs-target token slicing, CFG semantics, and
the runtime's actual scheduler identifiers/equations. Reload final metadata and
verify every file path, port, preprocessing step, loop edge, and postprocess.
If the runtime cannot express the dataflow, reject it before emitting artifacts;
keep direct component export separate.

Validate required VAE statistics against latent channel count before graph
construction (`len(mean) == len(std) == z_dim`, positive standard deviations).
Keep model-specific component/task overrides data-driven rather than adding
pipeline-name conditionals. Fold initializers after weight loading and mark
denoiser/VAE roles by semantics, not names. Tests must use `os.path.join`/`Path`
for temporary ONNX and profiling paths so cleanup works on POSIX and Windows.

## Naming conventions for diffusers models

Diffusers uses different conventions than transformers:
Expand Down
33 changes: 21 additions & 12 deletions .agents/skills/multi-agent-coordination/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ git fetch origin && git checkout <branch> && git pull

## 2. Commit Coordination Protocol

Multiple agents committing to the same branch requires discipline to avoid divergence.
Choose the branch strategy in §9 first. When multiple agents contribute to one
branch, use this protocol to avoid divergence.

**Rules**:
1. **Pull before commit**: Always `git pull origin <branch> --rebase` immediately before committing.
Expand All @@ -67,11 +68,14 @@ Checks at each stage prevent problems from compounding across agents.
- Verify the branch exists and is up to date.
- Run the relevant tests to establish a baseline — know what was already failing before you touched anything.
- Check for uncommitted changes left by previous agents in your worktree.
- Verify `inspect.getfile(mobius)` points into the agent's worktree; editable
installs from another worktree invalidate all test evidence.

### After each commit
- Run the affected tests immediately (don't batch; catch regressions early).
- Verify the commit landed on the correct branch (`git log --oneline -3`).
- Confirm the push succeeded.
- Confirm the remote ref/PR head SHA, not just local `HEAD`.

### Before final review (lead audit)
- Check ALL worktrees for unpushed commits or uncommitted changes.
Expand All @@ -85,6 +89,15 @@ Checks at each stage prevent problems from compounding across agents.
- Final test run passes.
- PR description updated with an accurate scorecard of what changed.

### CI failures and main refreshes
- Compare the exact failing test on the exact base SHA; a red overall workflow
does not prove the same job failed on base.
- Keep feature scope: document unrelated baseline names/metrics instead of
changing their code or tolerances.
- Rebase independent PRs linearly onto `origin/main`. Resolve shared registries,
exports, helpers, and guards semantically by retaining both sides; then run
shared-surface tests, lint, and review before `push --force-with-lease`.

---

## 4. Task Dependency Management (DAG)
Expand Down Expand Up @@ -171,18 +184,14 @@ Match the agent role to the task type. Mismatched roles waste agent capacity.

---

## 9. Single-Branch Strategy

For large multi-agent efforts, use **one shared feature branch** rather than one branch per agent.

**Why**: Avoids complex multi-branch merge scenarios at the end. All agents commit to the same branch via their isolated worktrees.

**Trade-offs**:
- More `pull --rebase` cycles per agent.
- Occasional push conflicts (recoverable with rebase).
- BUT: much simpler final state, single PR, linear history.
## 9. Branch Strategy

**Alternative**: Separate branches per agent merged via separate PRs — only viable when features are truly independent and don't share files.
- Use one branch only when agents contribute to one atomic PR; serialize edits
to shared files and require pull/rebase before every push.
- Use separate branches/PRs for independent deliverables (for example, separate
model architectures). This isolates CI, review, rollback, and publication.
- Never merge main into a linear-history feature branch. Rebase, resolve shared
surfaces semantically, and force-push only with `--force-with-lease`.

---

Expand Down
87 changes: 30 additions & 57 deletions .agents/skills/multimodal-models/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,16 @@ convention), not `vision_encoder`/`audio_encoder`.
The embedding model must handle `num_image_tokens=0` (text-only input) by
zero-padding `image_features` before Gather so indices stay in-bounds.

Input mixing must also:
- compute feature offsets over the flattened batch, not restart per row;
- preserve separate image/video streams or define and test an explicit packed
order; and
- cover two-row mixed-media prompts with distinguishable feature sentinels.

For cross-attentive decoders, export total-length padding masks and only caches
that are actually reusable. Do not apply a generic seq2seq cross-cache contract
to fixed encoder states without proving projection/cache semantics.

### Conditional 3-or-4-model task

Some models come in two tiers (e.g. Gemma4): small variants include an
Expand Down Expand Up @@ -219,72 +229,35 @@ models this overflows ORT's CUDA Gather kernel. **Workaround:** Split into
L separate `Embedding([V, D])` tables via `nn.ModuleList`, and use `Slice`
instead of `Gather` for per-layer projection indexing.

## Vision/audio encoder f32 input casting

> **This applies to ALL multimodal models and ALL inference paths** —
> not just ORT GenAI, and not architecture-specific.
## Processor-to-graph contract

Image and audio preprocessing universally produces **float32** output.
This is true across all frameworks and runtimes:
Run the real processor before finalizing graph I/O. Record names, shapes,
dtypes, media-row ordering, sampled frame positions, and empty-media behavior;
synthetic packed video layouts often differ from processor-native rows.

- **PIL / torchvision:** Pixel normalization outputs f32
- **torchaudio / librosa:** Mel spectrograms are f32
- **ORT GenAI image_processor:** Resize, normalize, tile → f32
- **ORT GenAI audio_processor:** Feature extraction → f32
- **ORT Python API:** Custom preprocessing pipelines → typically f32
- **Foundry Local:** Uses GenAI processors → f32

This means vision and audio encoder ONNX graphs must accept f32 inputs
even when the model is built in f16 or bf16. The encoder adds a
`Cast(f32 → model_dtype)` at its graph entry point so that any runtime
can feed it preprocessed data without worrying about the model's
internal precision.

### How it works
Vision/audio preprocessors emit float32. Encoder graphs therefore accept
float32 at the boundary and cast once to fp16/bf16 internally:

```
Input (f32 from ANY preprocessor — PIL, torchaudio, GenAI, etc.)
Cast(to=FLOAT16) ← inserted automatically by mobius
Vision/Audio encoder (weights in f16/bf16)
Output (model_dtype)
processor f32 -> Cast(model dtype) -> reduced-precision encoder
```

Encoder weights still use the requested dtype (f16/bf16) for memory
efficiency — only the graph inputs are f32. The Cast is a lightweight
op with negligible overhead.

### Why f32 is the universal preprocessing dtype
Test image-only, video-only, mixed media, two-row batches, and decode with zero
new media. Build kwargs conditionally; strict processors may reject video-only
arguments on image-only calls.

Preprocessing involves floating-point arithmetic (mean subtraction,
std division, resampling interpolation) where f32 is the natural
precision. Converting to f16/bf16 before these operations would lose
precision in the preprocessing itself. The model's internal precision
only matters after the preprocessed data enters the encoder.

### What mobius does

Mobius always builds encoder graphs with f32 inputs — this is the
default behavior, not gated behind any flag. It works correctly
regardless of the inference runtime:

- `--runtime ort-genai` → f32 inputs (GenAI processors output f32)
- No `--runtime` flag → f32 inputs (ORT Python API, custom runtimes)
- Foundry Local → f32 inputs (uses GenAI internally)

Without the Cast-at-input, ORT throws a type mismatch error:
```
Type Error: Type parameter (T) bound to different types
(tensor(float) and tensor(float16))
```
For composite checkpoints, generate processor assets from the parent config
even if model construction exposes an unwrapped text sub-config; test both
wrapped and unwrapped resolver forms.

### For model authors
For variable-length media, audit symbolic complexity: packed-attention metadata
must scale with rows/windows, not total-patches × media-count. Keep dense masks
only as a portable fallback and assert optimized CUDA/DML graphs lack the
quadratic construction.

If you're adding a new multimodal model, you don't need to handle this
manually — mobius inserts the Cast automatically for all encoder graphs.
If the model dtype is already f32, no Cast is needed.
Config generation is not runtime support. Execute real media through ORT GenAI;
if it cannot supply a required encoder input or position-ID rank, reject export
before writing artifacts and use the same evidence for any Foundry waiver.

## GQA for KV-shared layers (Gemma4)

Expand Down
7 changes: 7 additions & 0 deletions .agents/skills/onnx-export-quantization/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,13 @@ FP32 when the model is BF16. Add `op.CastLike(result, input)` to
ensure dtype consistency. See the `reusable-components` skill's
section on precision behaviour.

### 6. Attention rewrite schema mismatch

Quantizers may rewrite Attention to a contrib op with fewer outputs or a
different cache contract. Restrict the quantized op set when schemas do not
match, then load and execute the result; successful conversion alone is not
evidence. Document the narrowed recipe and unsupported rewrite explicitly.

## Testing quantized models

### L4: Golden data generation
Expand Down
16 changes: 16 additions & 0 deletions .agents/skills/ort-genai-config/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,15 @@ decoder_inputs = {name: name for name in decoder_input_names}
This means the genai config automatically adapts when `RemoveDeadGraphInputsPass`
removes unused inputs (e.g. `position_ids` absorbed by GQA fusion).

Hybrid cache metadata must preserve global layer indices across KV, convolution,
and recurrent states. Derive slot count from the maximum
`past_key_values.<index>.*` input index plus one; counting only `.key` inputs
silently drops non-KV or sparsely indexed layers.

CUDA Graph capture belongs on stable-shape autoregressive decoder sessions,
not one-shot variable-shape vision/embedding stages. Keep an explicit decoder
opt-out and validate capture together with shared KV buffers on the real model.

> For the full generation flow, input routing, QwenImageProcessor output
> tensors, and the multimodal processor factory, see
> [`references/multimodal-pipeline.md`](references/multimodal-pipeline.md).
Expand Down Expand Up @@ -273,6 +282,13 @@ processor doesn't provide. Either:
1. Compute them externally and inject via NamedTensors, or
2. Modify the vision model to compute them from `image_grid_thw` internally

### Config writes successfully but runtime cannot execute

Config/schema success is not runtime support. Run load plus generation through
the exported contract. If the runtime cannot route required feature inputs,
position-ID rank, cache state, scheduler, or multimodal metadata, reject export
before writing artifacts and report the exact runtime version/limitation.

### "input_ids size exceeds max length"

For image prompts, the tokenized input_ids (including image_pad tokens) can
Expand Down
Loading
Loading