FP8-Dynamic quantization of Kwaipilot/KAT-Coder-V2.5-Dev (vLLM-ready, shared_expert_gate kept BF16)
28f3401 verified | base_model: Kwaipilot/KAT-Coder-V2.5-Dev | |
| license: apache-2.0 | |
| language: | |
| - en | |
| - ru | |
| - code | |
| library_name: vllm | |
| pipeline_tag: text-generation | |
| tags: | |
| - vllm | |
| - fp8 | |
| - fp8-dynamic | |
| - quantized | |
| - llmcompressor | |
| - compressed-tensors | |
| - qwen3.5 | |
| - qwen3.6 | |
| - qwen3_5_moe | |
| - moe | |
| - code | |
| - coding | |
| - agentic | |
| - tool-use | |
| - dgx-spark | |
| - gb10 | |
| - llama-swap | |
| - ngram | |
| quantized_from: Kwaipilot/KAT-Coder-V2.5-Dev | |
| quant_method: compressed-tensors | |
| bits: 8 | |
| # KAT-Coder-V2.5-Dev FP8-Dynamic (vLLM-ready) | |
| An **FP8-Dynamic post-training quantization** of [`Kwaipilot/KAT-Coder-V2.5-Dev`](https://huggingface.co/Kwaipilot/KAT-Coder-V2.5-Dev), a 35B (3B-active) hybrid-attention MoE coding model from the Kwaipilot KAT-Coder V2.5 family. This checkpoint halves VRAM and roughly doubles KV-cache capacity versus the BF16 original while keeping >99.9% of parameters in FP8, and is verified to **load and serve on a single NVIDIA DGX Spark (GB10 / sm_121a)** under vLLM. | |
| | | | | |
| |---|---| | |
| | **Base model** | `Kwaipilot/KAT-Coder-V2.5-Dev` (Qwen3.6-35B-A3B) | | |
| | **Architecture** | `Qwen3_5MoeForConditionalGeneration` (`qwen3_5_moe`) | | |
| | **Params** | **35B total / 3B active** (MoE: 256 experts, 8 per token) | | |
| | **Layers** | 40, hybrid **`[linear, linear, linear, full] × 10`** (Gated-DeltaNet linear attention + full attention, 3:1) | | |
| | **Hidden / vocab** | 2048 / 248,320 | | |
| | **Context** | **262,144 tokens** (native `max_position_embeddings`) | | |
| | **Quantization** | FP8-Dynamic: W8A8 float, per-**channel** weights (static, `memoryless_minmax`), per-**token** activations (dynamic) | | |
| | **dtype mix** | **94.0% `float8_e4m3fn`** (33.62 GB) + **6.0% `bfloat16`** (2.14 GB) | | |
| | **Checkpoint size** | **35.77 GB** (single `model.safetensors`) | | |
| | **VRAM @ util 0.80** | weights **33.52 GiB** + KV cache **64.16 GiB** on a 121.6 GiB device | | |
| | **License** | Apache-2.0 (inherited from base) | | |
| --- | |
| ## What this model is | |
| `KAT-Coder-V2.5-Dev` is a **coder/agentic** model from the Kwaipilot KAT-Coder V2.5 technical report, fine-tuned on top of the **Qwen3.6-35B-A3B** base. The base is a **hybrid linear/full-attention MoE**: 40 layers in a `3× linear-attention (Gated DeltaNet) + 1× full-attention` repeating pattern, with **256 routed experts** (8 activated per token) plus a shared expert. Only **~3B params fire per token**, which makes decode cheap on memory-bound devices like the DGX Spark, while the full 35B capacity is available for quality. | |
| This derivative applies **data-free FP8-Dynamic PTQ** via [llmcompressor](https://github.com/vllm-project/llm-compressor) so the model fits a single 128 GB-class GPU with a large KV budget, enabling many concurrent agent sessions. | |
| --- | |
| ## ⚠️ Critical: `shared_expert_gate` is kept BF16 (read before re-quantizing) | |
| This is the single most important gotcha when serving this checkpoint with vLLM. | |
| vLLM's `Qwen3_5` / `Qwen3Next` MoE implementation instantiates **`shared_expert_gate` as an *unquantized* `ReplicatedLinear`** (see `vllm/model_executor/models/qwen3_next.py`, the `self.shared_expert_gate = ReplicatedLinear(..., quant_config=None, ...)` line). If you quantize that module to FP8 and ship a `weight_scale`, vLLM will fail at load time with: | |
| ``` | |
| ValueError: There is no module or parameter named 'layers.0.mlp.shared_expert_gate.weight_scale' | |
| in Qwen3_5Model. The available parameters belonging to layers.0.mlp.shared_expert_gate | |
| (ReplicatedLinear) are: {'layers.0.mlp.shared_expert_gate.weight'} | |
| ``` | |
| **Fix (already applied in this checkpoint):** the recipe ignores `re:.*shared_expert_gate$`, so those **40 tensors stay `bfloat16`** and load cleanly. The routed experts and the `shared_expert` MLP projections remain FP8 (served by vLLM's TRITON Fp8 MoE backend). If you re-quantize from scratch, you **must** include this ignore entry. | |
| The full ignore list: | |
| ```yaml | |
| ignore: | |
| - 're:.*lm_head' # never quantize the output head | |
| - 're:.*visual.*' # text-only release ships no vision weights | |
| - 're:.*mlp.gate$' # TopK router (Parameter, auto-skipped — kept for parity) | |
| - 're:.*shared_expert_gate$' # ← REQUIRED: vLLM loads this unquantized | |
| ``` | |
| --- | |
| ## Compatibility | |
| - **vLLM ≥ 0.26.0** — verified on `ghcr.io/timothystewart6/vllm-gb10:v0.26.0-gb10.6` (vLLM `0.26.1.dev0+g568afb3a1.d20260801`, CUDA 13.2, PyTorch 2.11, FlashInfer v0.6.14). | |
| - **`--language-model-only` is mandatory.** The base is a multimodal ConditionalGeneration class but this release ships text-only weights; without the flag vLLM tries to init a vision tower that isn't in the checkpoint. | |
| - **Hardware:** DGX Spark (GB10 SoC, `sm_121a`). The FP8 path runs through Triton `scaled_mm` + Marlin (CutlassFP8 kernel) + TRITON Fp8 MoE + FlashInfer. DeepGEMM/MXFP4 are **not** available on `sm_121` — leave `VLLM_USE_DEEP_GEMM=0`. | |
| - No MTP heads (`mtp_num_hidden_layers: 0`). For speculation use **n-gram** (free) or EAGLE3 (if you train a head). | |
| --- | |
| ## Quick start — `vllm serve` | |
| ```bash | |
| docker run --rm --gpus all --ipc host --network host --privileged \ | |
| -v ~/.cache/huggingface:/root/.cache/huggingface \ | |
| -e HF_TOKEN=$HF_TOKEN \ | |
| ghcr.io/timothystewart6/vllm-gb10:v0.26.0-gb10.6 \ | |
| vllm serve vovannovig2/KAT-Coder-V2.5-Dev-FP8-Dynamic \ | |
| --host 0.0.0.0 --port 8000 --served-model-name kat-coder \ | |
| --language-model-only \ | |
| --kv-cache-dtype fp8 \ | |
| --max-model-len 262144 \ | |
| --enable-prefix-caching --enable-chunked-prefill --trust-remote-code \ | |
| --tensor-parallel-size 1 \ | |
| --safetensors-load-strategy prefetch \ | |
| --gpu-memory-utilization 0.80 \ | |
| --max-num-seqs 12 --max-num-batched-tokens 8192 \ | |
| --reasoning-parser qwen3 --enable-auto-tool-choice --tool-call-parser qwen3_coder \ | |
| --generation-config auto \ | |
| --speculative-config '{"method":"ngram","num_speculative_tokens":3,"prompt_lookup_max":4}' | |
| ``` | |
| **Required / recommended flags:** | |
| | Flag | Why | | |
| |---|---| | |
| | `--language-model-only` | Skip the absent vision tower. **Mandatory.** | | |
| | `--kv-cache-dtype fp8` | Halves KV memory; confirmed working on sm_121. | | |
| | `--reasoning-parser qwen3` | Parses `<think>...</think>` into OpenAI `reasoning_content`. | | |
| | `--tool-call-parser qwen3_coder` | Native tool format `<tool_call><function=NAME><parameter=K>V</parameter>…` (NOT `qwen3_xml`). | | |
| | `--speculative-config ngram` | Free 1.5–2.5× on code (no MTP head shipped). | | |
| | `--enable-prefix-caching` | Huge wins for agentic / repeated system prompts. | | |
| | `--gpu-memory-utilization 0.80` | Reserves OS/CPU headroom on the unified-memory SoC. | | |
| | `--max-num-seqs 12` | 12 parallel decode streams; KV has headroom for more (see capacity table). | | |
| --- | |
| ## Production deployment on DGX Spark (LiteLLM + llama-swap swap stack) | |
| This checkpoint is served in production behind a **swap architecture**: only **one** vLLM model is hot at a time, swapped on-demand so a single 128 GB device can host several large models without contention. | |
| ``` | |
| Clients / OpenCode ─► LiteLLM :14000 ─► llama-swap :8000 ─► ONE active vLLM container | |
| (aliases, guardrails, (systemd, (port auto-assigned alphabetically; | |
| prefix-cache, redis) -watch-config kat-coder → :8002) | |
| hot-reload) | |
| ``` | |
| **llama-swap** block (`config.yaml`): | |
| ```yaml | |
| kat-coder: | |
| name: "KAT-Coder V2.5 Dev (Qwen3.6-35B-A3B MoE FP8-Dynamic)" | |
| cmd: | | |
| docker run --rm --name vllm-kat ${docker_common} ${vllm_image} vllm serve | |
| vovannovig2/KAT-Coder-V2.5-Dev-FP8-Dynamic | |
| --host 127.0.0.1 --port ${PORT} --served-model-name kat-coder | |
| --language-model-only --kv-cache-dtype fp8 --max-model-len 262144 | |
| --enable-prefix-caching --enable-chunked-prefill --trust-remote-code | |
| --tensor-parallel-size 1 --safetensors-load-strategy prefetch | |
| --gpu-memory-utilization 0.80 --max-num-seqs 12 --max-num-batched-tokens 8192 | |
| --reasoning-parser qwen3 --enable-auto-tool-choice --tool-call-parser qwen3_coder | |
| --generation-config auto | |
| --speculative-config '{"method":"ngram","num_speculative_tokens":3,"prompt_lookup_max":4}' | |
| proxy: http://127.0.0.1:${PORT} | |
| checkEndpoint: /health | |
| cmdStop: docker stop -t 60 vllm-kat | |
| unloadTimeout: 180 | |
| ``` | |
| **LiteLLM** entry (`config.yaml`): | |
| ```yaml | |
| - model_name: kat-coder | |
| litellm_params: | |
| model: openai/kat-coder # MUST match llama-swap key + --served-model-name | |
| api_base: http://127.0.0.1:8000/v1 # always llama-swap, never the raw vLLM port | |
| api_key: sk-local-noauth | |
| temperature: 1.0 | |
| top_p: 0.95 | |
| top_k: 20 | |
| presence_penalty: 1.5 | |
| max_tokens: 65536 | |
| model_info: | |
| mode: chat | |
| supports_reasoning: true | |
| supports_function_calling: true | |
| max_input_tokens: 262144 | |
| max_output_tokens: 65536 | |
| ``` | |
| Swapping is request-driven: asking for `kat-coder` unloads whatever is hot (e.g. ThinkingCap) and cold-loads this model (~6 min end-to-end, see performance table). `sendLoadingState: true` streams load progress to chat UIs. | |
| --- | |
| ## Sampling recommendations | |
| | Mode | temperature | top_p | top_k | presence_penalty | | |
| |---|---|---|---|---| | |
| | **Thinking (default)** | 1.0 | 0.95 | 20 | 1.5 | | |
| | **Non-thinking / instruct** | 0.7 | 0.80 | 20 | 1.5 | | |
| The chat template is Qwen3 ChatML with `<think>…</think>` reasoning delimiters and `enable_thinking` / `preserve_thinking` kwargs. `generation_config.json` ships `temperature=1.0, top_k=20, top_p=0.95`. | |
| --- | |
| ## Performance on DGX Spark (measured) | |
| Single DGX Spark, GB10 (sm_121a), 121.63 GiB unified memory, `--gpu-memory-utilization 0.80`, `--kv-cache-dtype fp8`, n-gram spec decode (3 tokens), all kernels warm. | |
| | Metric | Value | | |
| |---|---| | |
| | Cold load — weights | **224.4 s** (33.52 GiB, EXT4 prefetch) | | |
| | Cold load — engine init | **99.2 s** (compile 29.0 s + profile + CUDA-graph capture) | | |
| | **Total cold start to ready** | **~6 min** | | |
| | Weight VRAM | **33.52 GiB** | | |
| | KV cache VRAM | **64.16 GiB** (FP8) | | |
| | **GPU KV cache size** | **5,960,926 tokens** | | |
| | Decode throughput (single stream, E2E via LiteLLM→llama-swap→vLLM) | **~47–50 tok/s** | | |
| | Prefill throughput (1 req) | ~30 tok/s | | |
| **Kernels selected by vLLM (verbatim from logs):** | |
| - `Selected CutlassFP8ScaledMMLinearKernel for CompressedTensorsW8A8Fp8` | |
| - `Using TRITON Fp8 MoE backend` (out of AITER/FLASHINFER/DEEPGEMM/MARLIN/…) | |
| - `Using FLASHINFER attention backend` | |
| - `FlashInfer resolved … kv_cache_dtype=torch.float8_e4m3fn, arch=sm121` | |
| - `Using Triton/FLA GDN prefill kernel (head_k_dim=128)` (linear-attention layers) | |
| --- | |
| ## Subagent / concurrency capacity (one DGX Spark) | |
| The hybrid architecture only stores KV state for the **10 full-attention layers** (the 30 linear-attention layers are recurrent/stateful, ~no KV). Combined with FP8 KV cache this yields a very large token budget. | |
| **KV budget math:** `5,960,926 tokens / 262,144 = 22.7` → up to **22 full 256k-context sessions fit in the KV cache**. The scheduler cap (`--max-num-seqs`) is the binding constraint, not memory. | |
| | Concurrency (subagents) | Per-agent context | Total KV used | KV utilization | Notes | | |
| |---|---|---|---|---| | |
| | **12** | **262,144 (full)** | 3.15 M | **52.8%** | Default config; healthy headroom | | |
| | 12 | 131,072 (128k) | 1.57 M | 26.4% | Light | | |
| | 12 | 65,536 (64k) | 0.79 M | 13.2% | Comfortable | | |
| | 22 (raise `--max-num-seqs`) | 262,144 (full) | 5.77 M | 96.8% | Max before OOM; aggressive | | |
| | 8 | 262,144 (full) | 2.10 M | 35.2% | Conservative; matches dense-model pattern | | |
| **Recommendation:** `--max-num-seqs 12` gives **12 parallel coding agents, each with the full 256k window, using only ~53% of KV** — the safe production setting. Push to 22 only for pure batch throughput with short-lived sessions. | |
| --- | |
| ## Reproducing the quantization | |
| ```bash | |
| # 1. Environment (host or container with CUDA; quantization itself runs CPU-only) | |
| uv pip install "llmcompressor>=0.12" "transformers>=4.57" accelerate | |
| # 2. Free ≥80 GB RAM (unload any other model from the GPU). Add ≥48 GB NVMe swap — | |
| # the MoE expert linearization pass spikes memory well above the 69 GB BF16 footprint. | |
| sudo fallocate -l 48G /swapfile2 && sudo chmod 600 /swapfile2 && \ | |
| sudo mkswap /swapfile2 && sudo swapon /swapfile2 | |
| # 3. Run (data-free PTQ; no calibration dataset needed) | |
| CUDA_VISIBLE_DEVICES="" python quantize.py | |
| ``` | |
| `quantize.py` (verbatim recipe used for this checkpoint): | |
| ```python | |
| from llmcompressor import oneshot | |
| from llmcompressor.modifiers.quantization import QuantizationModifier | |
| from llmcompressor.utils import load_context | |
| from transformers import AutoTokenizer, Qwen3_5MoeForConditionalGeneration | |
| MODEL_ID = "Kwaipilot/KAT-Coder-V2.5-Dev" | |
| with load_context(Qwen3_5MoeForConditionalGeneration): | |
| model = Qwen3_5MoeForConditionalGeneration.from_pretrained( | |
| MODEL_ID, dtype="bfloat16", low_cpu_mem_usage=True) | |
| tokenizer = AutoTokenizer.from_pretrained(MODEL_ID) | |
| recipe = QuantizationModifier( | |
| targets="Linear", | |
| scheme="FP8_DYNAMIC", | |
| ignore=[ | |
| "re:.*lm_head", | |
| "re:.*visual.*", | |
| "re:.*mlp.gate$", | |
| "re:.*shared_expert_gate$", # REQUIRED — see "Critical" section above | |
| ], | |
| ) | |
| oneshot(model=model, recipe=recipe) | |
| # text-only release: strip the randomly-initialized vision tower before saving | |
| for owner in (model, getattr(model, "model", None)): | |
| for attr in ("visual", "vision_tower", "vision_model"): | |
| if owner is not None and hasattr(owner, attr): | |
| try: delattr(owner, attr) | |
| except Exception: setattr(owner, attr, None) | |
| model.save_pretrained("KAT-Coder-V2.5-Dev-FP8-Dynamic", save_compressed=True) | |
| tokenizer.save_pretrained("KAT-Coder-V2.5-Dev-FP8-Dynamic") | |
| ``` | |
| `recipe.yaml` (also shipped in this repo): | |
| ```yaml | |
| default_stage: | |
| default_modifiers: | |
| QuantizationModifier: | |
| targets: [Linear] | |
| ignore: ['re:.*lm_head', 're:.*visual.*', 're:.*mlp.gate$', 're:.*shared_expert_gate$'] | |
| scheme: FP8_DYNAMIC | |
| bypass_divisibility_checks: false | |
| ``` | |
| Wall time on DGX Spark (CPU-only): ~5 min (load + MoE linearization + RTN + compressed save). Output: 35.77 GB single `model.safetensors`. | |
| --- | |
| ## Verification | |
| Boot-time confirmation from a live vLLM serve on DGX Spark: | |
| ``` | |
| Model loading took 33.52 GiB memory and 224.445083 seconds | |
| GPU KV cache size: 5,960,926 tokens | |
| Free memory on device (110.54/121.63 GiB) ... Actual usage is 33.52 GiB for weight, | |
| 1.16 GiB for peak activation, -1.86 GiB for non-torch, -0.01 GiB for CUDAGraph. | |
| Current kv cache memory in use is 64.16 GiB. | |
| FlashInfer resolved ... kv_cache_dtype=torch.float8_e4m3fn, arch=sm121 | |
| Starting vLLM server on http://127.0.0.1:8002 | |
| ``` | |
| Tool-calling (`qwen3_coder` parser) and `<think>` reasoning (`qwen3` parser) both verified end-to-end through LiteLLM → llama-swap → vLLM. | |
| --- | |
| ## Attribution | |
| - **Base model:** [`Kwaipilot/KAT-Coder-V2.5-Dev`](https://huggingface.co/Kwaipilot/KAT-Coder-V2.5-Dev) — Kwaipilot KAT-Coder V2.5 (Apache-2.0). | |
| - **Quantization:** [llmcompressor](https://github.com/vllm-project/llm-compressor) (`FP8_DYNAMIC` scheme, data-free PTQ). | |
| - **Serving:** [vLLM](https://github.com/vllm-project/vllm) — verified on the [`vllm-gb10`](https://github.com/timothystewart6/vllm-gb10) DGX-Spark image `v0.26.0-gb10.6`. | |
| - **License:** Apache-2.0 (inherited). | |
| --- | |
| --- | |
| # 🇷🇺 Русская версия | |
| # KAT-Coder-V2.5-Dev FP8-Dynamic (готов для vLLM) | |
| **FP8-Dynamic посттренировочная квантизация** модели [`Kwaipilot/KAT-Coder-V2.5-Dev`](https://huggingface.co/Kwaipilot/KAT-Coder-V2.5-Dev) — 35B (3B активных) гибридно-внимательный MoE-кодер из семейства Kwaipilot KAT-Coder V2.5. Этот чекпункт вдвое снижает потребление VRAM и примерно вдвое расширяет ёмкость KV-кэша относительно BF16-оригинала, оставляя >99,9 % параметров в FP8, и **проверен на загрузку и инференс на одном NVIDIA DGX Spark (GB10 / sm_121a)** под vLLM. | |
| | | | | |
| |---|---| | |
| | **Базовая модель** | `Kwaipilot/KAT-Coder-V2.5-Dev` (Qwen3.6-35B-A3B) | | |
| | **Архитектура** | `Qwen3_5MoeForConditionalGeneration` (`qwen3_5_moe`) | | |
| | **Параметры** | **35B всего / 3B активных** (MoE: 256 экспертов, 8 на токен) | | |
| | **Слои** | 40, гибрид **`[linear, linear, linear, full] × 10`** (линейное внимание Gated DeltaNet + полное внимание, 3:1) | | |
| | **Hidden / словарь** | 2048 / 248 320 | | |
| | **Контекст** | **262 144 токена** (нативный `max_position_embeddings`) | | |
| | **Квантизация** | FP8-Dynamic: W8A8 float, веса per-**channel** (статичные, `memoryless_minmax`), активации per-**token** (динамические) | | |
| | **Распределение dtype** | **94,0 % `float8_e4m3fn`** (33,62 ГБ) + **6,0 % `bfloat16`** (2,14 ГБ) | | |
| | **Размер чекпойнта** | **35,77 ГБ** (один `model.safetensors`) | | |
| | **VRAM при util 0.80** | веса **33,52 ГиБ** + KV-кэш **64,16 ГиБ** на устройстве 121,6 ГиБ | | |
| | **Лицензия** | Apache-2.0 (унаследована от базы) | | |
| --- | |
| ## Что это за модель | |
| `KAT-Coder-V2.5-Dev` — **кодер/агентная** модель из технического отчёта Kwaipilot KAT-Coder V2.5, дообученная на базе **Qwen3.6-35B-A3B**. База — **гибридный linear/full-attention MoE**: 40 слоёв в паттерне `3× линейное внимание (Gated DeltaNet) + 1× полное внимание`, с **256 маршрутизируемыми экспертами** (8 активируются на токен) плюс общий эксперт. На токен срабатывает лишь **~3B параметров**, что делает декод дешёвым на устройствах с ограниченной пропускной способностью памяти (как DGX Spark), но сохраняет полную ёмкость 35B для качества. | |
| Этот дериватив применяет **data-free FP8-Dynamic PTQ** через [llmcompressor](https://github.com/vllm-project/llm-compressor), чтобы модель уместилась на одном GPU класса 128 ГБ с большим KV-бюджетом — это позволяет держать множество параллельных агентских сессий. | |
| --- | |
| ## ⚠️ Критично: `shared_expert_gate` остаётся BF16 (прочитать перед повторной квантизацией) | |
| Это единственный и самый важный подводный камень при обслуживании этого чекпойнта в vLLM. | |
| Реализация MoE `Qwen3_5` / `Qwen3Next` в vLLM создаёт **`shared_expert_gate` как *неквантованную* `ReplicatedLinear`** (см. `vllm/model_executor/models/qwen3_next.py`, строку `self.shared_expert_gate = ReplicatedLinear(..., quant_config=None, ...)`). Если этот модуль квантовать в FP8 и приложить `weight_scale`, vLLM упадёт при загрузке: | |
| ``` | |
| ValueError: There is no module or parameter named 'layers.0.mlp.shared_expert_gate.weight_scale' | |
| in Qwen3_5Model. The available parameters belonging to layers.0.mlp.shared_expert_gate | |
| (ReplicatedLinear) are: {'layers.0.mlp.shared_expert_gate.weight'} | |
| ``` | |
| **Решение (уже применено в этом чекпойнте):** рецепт игнорирует `re:.*shared_expert_gate$`, поэтому эти **40 тензоров остаются `bfloat16`** и грузятся чисто. Маршрутизируемые эксперты и проекции `shared_expert` MLP остаются FP8 (обслуживаются TRITON Fp8 MoE-бэкендом vLLM). При повторной квантизации с нуля **обязательно** включите этот ignore. | |
| Полный ignore-список: | |
| ```yaml | |
| ignore: | |
| - 're:.*lm_head' # никогда не квантовать выходную голову | |
| - 're:.*visual.*' # text-only релиз не содержит vision-весов | |
| - 're:.*mlp.gate$' # TopK-роутер (Parameter, авто-пропуск — для единообразия) | |
| - 're:.*shared_expert_gate$' # ← ОБЯЗАТЕЛЬНО: vLLM грузит это неквантованным | |
| ``` | |
| --- | |
| ## Совместимость | |
| - **vLLM ≥ 0.26.0** — проверено на `ghcr.io/timothystewart6/vllm-gb10:v0.26.0-gb10.6` (vLLM `0.26.1.dev0+g568afb3a1.d20260801`, CUDA 13.2, PyTorch 2.11, FlashInfer v0.6.14). | |
| - **`--language-model-only` обязателен.** База — мультимодальный класс ConditionalGeneration, но этот релиз содержит только текстовые веса; без флага vLLM попытается инициализировать vision-tower, которого нет в чекпойнте. | |
| - **Железо:** DGX Spark (SoC GB10, `sm_121a`). FP8-путь идёт через Triton `scaled_mm` + Marlin (ядро CutlassFP8) + TRITON Fp8 MoE + FlashInfer. DeepGEMM/MXFP4 на `sm_121` **недоступны** — оставьте `VLLM_USE_DEEP_GEMM=0`. | |
| - Голов MTP нет (`mtp_num_hidden_layers: 0`). Для спекулятивного декода используйте **n-gram** (бесплатно) или EAGLE3 (если обучите голову). | |
| --- | |
| ## Быстрый старт — `vllm serve` | |
| См. английскую секцию выше — команда идентична. Ключевые флаги: `--language-model-only` (обязателен), `--kv-cache-dtype fp8`, `--reasoning-parser qwen3`, `--tool-call-parser qwen3_coder` (НЕ `qwen3_xml`), `--speculative-config ngram`. | |
| --- | |
| ## Продуктивное развёртывание на DGX Spark (LiteLLM + llama-swap swap-стек) | |
| Этот чекпойнт обслуживается в продакшене за **swap-архитектурой**: одновременно **только одна** vLLM-модель горячая и меняется по требованию, что позволяет одному устройству 128 ГБ держать несколько крупных моделей без конфликтов. | |
| ``` | |
| Клиенты / OpenCode ─► LiteLLM :14000 ─► llama-swap :8000 ─► ОДИН активный vLLM-контейнер | |
| (алиасы, гвардраилы, (systemd, (порт назначается по алфавиту; | |
| prefix-cache, redis) -watch-config kat-coder → :8002) | |
| hot-reload) | |
| ``` | |
| Полные YAML-блоки для llama-swap и LiteLLM см. в английской секции. Swap происходит по запросу: обращение к `kat-coder` выгружает текущую модель (напр. ThinkingCap) и холоднозагружает эту (~6 мин до готовности, см. таблицу производительности). `sendLoadingState: true` транслирует прогресс загрузки в chat-UI. | |
| --- | |
| ## Рекомендации по сэмплингу | |
| | Режим | temperature | top_p | top_k | presence_penalty | | |
| |---|---|---|---|---| | |
| | **Thinking (по умолчанию)** | 1.0 | 0.95 | 20 | 1.5 | | |
| | **Non-thinking / инструкции** | 0.7 | 0.80 | 20 | 1.5 | | |
| Чат-шаблон — Qwen3 ChatML с разделителями рассуждения `<think>…</think>` и параметрами `enable_thinking` / `preserve_thinking`. В `generation_config.json` идут `temperature=1.0, top_k=20, top_p=0.95`. | |
| --- | |
| ## Производительность на DGX Spark (замеры) | |
| Один DGX Spark, GB10 (sm_121a), 121,63 ГиБ объединённой памяти, `--gpu-memory-utilization 0.80`, `--kv-cache-dtype fp8`, n-gram spec decode (3 токена), все ядра прогреты. | |
| | Метрика | Значение | | |
| |---|---| | |
| | Холодный старт — веса | **224,4 с** (33,52 ГиБ, EXT4 prefetch) | | |
| | Холодный старт — init engine | **99,2 с** (compile 29,0 с + profile + захват CUDA-graph) | | |
| | **Полный холодный старт до готовности** | **~6 мин** | | |
| | VRAM под веса | **33,52 ГиБ** | | |
| | VRAM под KV-кэш | **64,16 ГиБ** (FP8) | | |
| | **Размер GPU KV-кэша** | **5 960 926 токенов** | | |
| | Декод (один поток, E2E через LiteLLM→llama-swap→vLLM) | **~47–50 ток/с** | | |
| | Префилл (1 запрос) | ~30 ток/с | | |
| **Ядра, выбранные vLLM (verbatim из логов):** CutlassFP8ScaledMMLinearKernel (веса), TRITON Fp8 MoE (эксперты), FLASHINFER (полное внимание), Triton/FLA GDN (линейное внимание, head_k_dim=128). | |
| --- | |
| ## Ёмкость под субагентов / конкурентность (один DGX Spark) | |
| Гибридная архитектура хранит KV-состояние только для **10 слоёв полного внимания** (30 линейно-внимательных слоёв — рекуррентные/состоянием, почти без KV). В сочетании с FP8 KV-кэшем это даёт очень большой токенный бюджет. | |
| **Математика KV-бюджета:** `5 960 926 токенов / 262 144 = 22,7` → до **22 полных сессий с окном 256k влезают в KV-кэш**. Ограничивающий фактор — cap планировщика (`--max-num-seqs`), а не память. | |
| | Конкурентность (субагенты) | Контекст на агента | Всего KV занято | Утилизация KV | Примечание | | |
| |---|---|---|---|---| | |
| | **12** | **262 144 (полный)** | 3,15 М | **52,8 %** | Конфиг по умолчанию; здоровый запас | | |
| | 12 | 131 072 (128k) | 1,57 М | 26,4 % | Лёгкий режим | | |
| | 12 | 65 536 (64k) | 0,79 М | 13,2 % | Комфортно | | |
| | 22 (поднять `--max-num-seqs`) | 262 144 (полный) | 5,77 М | 96,8 % | Максимум до OOM; агрессивно | | |
| | 8 | 262 144 (полный) | 2,10 М | 35,2 % | Консервативно; как для dense-моделей | | |
| **Рекомендация:** `--max-num-seqs 12` даёт **12 параллельных кодинг-агентов, каждый с полным окном 256k, используя лишь ~53 % KV** — безопасный прод-сетап. Поднимайте до 22 только для чисто батчевого throughput с короткими сессиями. | |
| --- | |
| ## Воспроизведение квантизации | |
| Скрипт, рецепт и требования — см. английскую секцию выше (всё идентично). Время на DGX Spark (только CPU): ~5 мин. На выходе 35,77 ГБ одним `model.safetensors`. | |
| --- | |
| ## Атрибуция | |
| - **Базовая модель:** [`Kwaipilot/KAT-Coder-V2.5-Dev`](https://huggingface.co/Kwaipilot/KAT-Coder-V2.5-Dev) — Kwaipilot KAT-Coder V2.5 (Apache-2.0). | |
| - **Квантизация:** [llmcompressor](https://github.com/vllm-project/llm-compressor) (схема `FP8_DYNAMIC`, data-free PTQ). | |
| - **Обслуживание:** [vLLM](https://github.com/vllm-project/vllm) — проверено на образе [`vllm-gb10`](https://github.com/timothystewart6/vllm-gb10) для DGX Spark `v0.26.0-gb10.6`. | |
| - **Лицензия:** Apache-2.0 (унаследована). | |