diff --git a/AGENTS.md b/AGENTS.md index 0d0878d9eb..86e53c3718 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,18 +2,6 @@ Read [CONTRIBUTING.md](CONTRIBUTING.md) first. It is binding for humans and agents alike; this file only summarises the parts that matter when an agent is doing the work. -## AI policy - -AI-assisted code is welcome. Submitting code the contributor does not understand is not. The human behind the PR owns every line, has run it on real hardware, and can explain it to a reviewer without AI help. - -Agents must not: - -- Run `git push`, `gh pr create`, `gh pr comment`, or `gh issue create` on the user's behalf. -- Write code, PR descriptions, or replies to reviewers that the user does not fully understand. The user must be able to explain and defend every line without AI help. -- Report tests or benchmarks as run when they were not. - -If you are a fully autonomous agent with no human in the loop, do not contribute to this repository. - ## Repository layout The main subsystems: diff --git a/docs/models.md b/docs/models.md index a9b66a2480..ec2483e980 100644 --- a/docs/models.md +++ b/docs/models.md @@ -15,6 +15,7 @@ for them; other checkpoints of the same architectures work too. | Qwen3.8 / Qwen3.6 dense | [Qwen/Qwen3.8-27B](https://huggingface.co/Qwen/Qwen3.8-27B) ([-FP8](https://huggingface.co/Qwen/Qwen3.8-27B-FP8)), [RadixArk/Qwen3.8-27B-NVFP4](https://huggingface.co/RadixArk/Qwen3.8-27B-NVFP4), [Qwen/Qwen3.6-27B](https://huggingface.co/Qwen/Qwen3.6-27B) ([-FP8](https://huggingface.co/Qwen/Qwen3.6-27B-FP8)), [nvidia/Qwen3.6-27B-NVFP4](https://huggingface.co/nvidia/Qwen3.6-27B-NVFP4) | | Qwen3-MoE | [Qwen/Qwen3-30B-A3B](https://huggingface.co/Qwen/Qwen3-30B-A3B) | | Qwen3-VL | [Qwen/Qwen3-VL-8B-Instruct](https://huggingface.co/Qwen/Qwen3-VL-8B-Instruct), [Qwen/Qwen3-VL-30B-A3B-Instruct](https://huggingface.co/Qwen/Qwen3-VL-30B-A3B-Instruct) | +| Ornith-1.5 | [ornith-ai/Ornith-1.5-122B-NVFP4](https://huggingface.co/ornith-ai/Ornith-1.5-122B-NVFP4) | | gpt-oss | [openai/gpt-oss-120b](https://huggingface.co/openai/gpt-oss-120b), [openai/gpt-oss-20b](https://huggingface.co/openai/gpt-oss-20b) | | Gemma-4 | [google/gemma-4-26B-A4B-it](https://huggingface.co/google/gemma-4-26B-A4B-it), [nvidia/Gemma-4-26B-A4B-NVFP4](https://huggingface.co/nvidia/Gemma-4-26B-A4B-NVFP4), [google/gemma-4-12B-it](https://huggingface.co/google/gemma-4-12B-it), [nvidia/Gemma-4-31B-IT-NVFP4](https://huggingface.co/nvidia/Gemma-4-31B-IT-NVFP4) .. | | MiniMax-M2.5 | [nvidia/MiniMax-M2.5-NVFP4](https://huggingface.co/nvidia/MiniMax-M2.5-NVFP4) | @@ -29,7 +30,7 @@ These families accept image input by default; pass `--text-model-only` to skip t | Family | Image tokens | `--image-min-tokens` / `--image-max-tokens` | `--mm-processor-kwargs` example | | --- | --- | --- | --- | | DeepSeek-V4.1-Flash (ViT tower, streamed under `--mm-encoder-weights host`) | one feature per 42x42 pixels, plus a learned row break per row and two delimiters; checkpoint cap 1024 total span tokens | maximum caps the entire span; minimum maps to a pixel-area floor before the cap; checkpoint minimum area is 544x544 | `{"max_image_tokens": 512, "min_pixels": 295936}` | -| Qwen3.6 (both variants, every listed weight format), Qwen3.8-Flash-Next, Qwen3-VL | one token per 32x32 pixels of the resized image, dynamic resolution | pixel areas in `size.shortest_edge` / `longest_edge`; checkpoint defaults 64 to 16384 tokens | `{"size": {"longest_edge": 1048576}}` | +| Qwen3.6 (both variants, every listed weight format), Qwen3.8-Flash-Next, Qwen3-VL, Ornith-1.5 | one token per 32x32 pixels of the resized image, dynamic resolution | pixel areas in `size.shortest_edge` / `longest_edge`; checkpoint defaults 64 to 16384 tokens | `{"size": {"longest_edge": 1048576}}` | | Gemma-4 26B-A4B, 31B (`gemma4`: ViT tower, streamed under `--mm-encoder-weights host`) | one of the soft-token budgets 70 / 140 / 280 / 560 / 1120, every image scaled to its budget as far as the aspect ratio allows | the maximum picks the largest budget within it, below 70 is refused at start-up; the minimum has no effect | `{"max_soft_tokens": 1120}` | | Gemma-4 12B (`gemma4_unified`: linear patch embedder, resident under either placement) | same budgets, one 48x48 super-patch per soft token | same as the tower releases | same | | GLM-5.3-Flash (`glm5_next`: ViT tower, streamed under `--mm-encoder-weights host`) | one token per 28x28 pixels of the resized image, dynamic resolution on a canvas zero-padded to a 28-multiple | token counts, passed through as the processor's `min_image_tokens` / `max_image_tokens`; checkpoint defaults 16 to 8000 tokens | `{"max_image_tokens": 2048}` | @@ -150,3 +151,6 @@ unset on PCIe GPUs: wider grids add no bandwidth there. "reasoning_effort": 37}`, and the checkpoint's encoder validates it. Runtime window-cache controls use 128-token pages. - Qwen3.8-Flash-Next keeps a 47.7 GiB PLE n-gram table pinned in host RAM. +- Ornith-1.5 is served by the `qwen3_5_moe` loader. The checkpoint ships MTP tensors and declares + `deepstack_visual_indexes` but no DeepStack mergers: the MTP block is left on disk (the family has + no draft path) and the vision tower runs without DeepStack features. diff --git a/tests/README.md b/tests/README.md index 85865673e2..a74ade77ed 100644 --- a/tests/README.md +++ b/tests/README.md @@ -50,6 +50,7 @@ checkpoint is set: | `FREETOKEN_AIME_MIN_FREE_GIB` | `e2e/test_aime.py` — required free GPU memory (default `70`) | | `FREETOKEN_QWEN36_MODEL` | `tokenizer/test_mm_tokenize.py` — local Qwen3.6 checkpoint (image processor + tokenizer) | | `FREETOKEN_QWEN3VL_MODEL` | `tokenizer/test_mm_tokenize.py` — local Qwen3-VL checkpoint (same expansion, DeepStack family) | +| `FREETOKEN_ORNITH_MODEL` | `tokenizer/test_mm_tokenize.py` — local Ornith-1.5 checkpoint (same expansion, `qwen3_5_moe` vision tower) | | `FREETOKEN_GEMMA4_MODEL` | `models/test_gemma4_vision.py`, `tokenizer/test_mm_tokenize.py` — local Gemma-4 `gemma4` checkpoint (tower parity, boi/eoi expansion; the tokenizer test also takes the 12B `gemma4_unified` checkpoint) | | `FREETOKEN_GEMMA4_UNIFIED_MODEL` | `models/test_gemma4_vision.py` — local gemma-4-12B `gemma4_unified` checkpoint (embedder parity) | | `FREETOKEN_GLM53_MODEL` | `tokenizer/test_mm_tokenize.py` — local GLM-5.3-Flash checkpoint (image-token expansion) | diff --git a/tests/models/test_quant_config.py b/tests/models/test_quant_config.py index 6b9ecd810c..e667a53feb 100644 --- a/tests/models/test_quant_config.py +++ b/tests/models/test_quant_config.py @@ -227,6 +227,15 @@ def _gemma_tied_head(model, idx): "model.layers.{full}.self_attn.qkv_proj": FP8T, "model.layers.{lin}.mlp.shared_expert.gate_up_proj": FP8T, "model.layers.{lin}.mlp.experts": Nvfp4MoEMethod, "model.layers.{lin}.mlp.gate": BF16, "lm_head": BF16, }, check=_qwen35_fp8), + # the vendor's own Ornith-1.5 export: static per-tensor fp8 attention and GDN, W4A16 NVFP4 for the + # experts, the shared expert and lm_head; the 122B lists config_groups next to quantized_layers + Case("ornith-ai/Ornith-1.5-122B-NVFP4", ModelOptConfig, { + "model.layers.{lin}.linear_attn.in_proj_qkvz": FP8T, "model.layers.{lin}.linear_attn.in_proj_ba": BF16, + "model.layers.{lin}.linear_attn.out_proj": FP8T, "model.layers.{full}.self_attn.qkv_proj": FP8T, + "model.layers.{full}.self_attn.o_proj": FP8T, "model.layers.{lin}.mlp.shared_expert.gate_up_proj": NVFP4, + "model.layers.{lin}.mlp.shared_expert.down_proj": NVFP4, "model.layers.{lin}.mlp.experts": Nvfp4MoEMethod, + "model.layers.{lin}.mlp.gate": BF16, "model.layers.{lin}.mlp.shared_expert_gate": BF16, "lm_head": NVFP4, + }, check=_qwen35_nvfp4), # block-fp8 attention / GDN / shared expert with NVFP4 experts, shipped as one model.safetensors without an index Case("kyaky/Qwen3.6-35B-A3B-Uncensored-NVFP4", CompressedTensorsConfig, { "model.layers.{lin}.linear_attn.in_proj_qkvz": FP8B, "model.layers.{lin}.linear_attn.in_proj_ba": BF16, diff --git a/tests/tokenizer/test_mm_tokenize.py b/tests/tokenizer/test_mm_tokenize.py index 5adac8305c..40b0d8c62e 100644 --- a/tests/tokenizer/test_mm_tokenize.py +++ b/tests/tokenizer/test_mm_tokenize.py @@ -1,4 +1,4 @@ -"""Image tokenization against real Qwen VL, Gemma-4, GLM-5.3, Muse-Glimmer, MiniMax-M3 and DeepSeek-V4-Vision checkpoints (skipped when absent).""" +"""Image tokenization against real Qwen VL, Gemma-4, GLM-5.3, Muse-Glimmer, MiniMax-M3, DeepSeek-V4-Vision and Ornith-1.5 checkpoints (skipped when absent).""" from __future__ import annotations @@ -12,7 +12,11 @@ MODELS = [ p - for p in (os.environ.get("FREETOKEN_QWEN36_MODEL", ""), os.environ.get("FREETOKEN_QWEN3VL_MODEL", "")) + for p in ( + os.environ.get("FREETOKEN_QWEN36_MODEL", ""), + os.environ.get("FREETOKEN_QWEN3VL_MODEL", ""), + os.environ.get("FREETOKEN_ORNITH_MODEL", ""), + ) if os.path.exists(os.path.join(p, "config.json")) ] @@ -26,7 +30,7 @@ pytest.mark.needs_weights, pytest.mark.skipif( not MODELS and not any(os.path.exists(os.path.join(p, "config.json")) for p in (GEMMA, GLM, MUSE, MINIMAX, DSV4)), - reason="no FREETOKEN_QWEN36_MODEL / FREETOKEN_QWEN3VL_MODEL / FREETOKEN_GEMMA4_MODEL / FREETOKEN_GLM53_MODEL / FREETOKEN_MUSE_MODEL / FREETOKEN_MINIMAX_M3_MODEL / FREETOKEN_DSV4_VL_MODEL checkpoint", + reason="no FREETOKEN_QWEN36_MODEL / FREETOKEN_QWEN3VL_MODEL / FREETOKEN_ORNITH_MODEL / FREETOKEN_GEMMA4_MODEL / FREETOKEN_GLM53_MODEL / FREETOKEN_MUSE_MODEL / FREETOKEN_MINIMAX_M3_MODEL / FREETOKEN_DSV4_VL_MODEL checkpoint", ), ]