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 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:
ignore:
- 're:.*lm_head'
- 're:.*visual.*'
- 're:.*mlp.gate$'
- 're:.*shared_expert_gate$'
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
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:
Table with columns: Flag, Why| 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). |
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):
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):
- model_name: kat-coder
litellm_params:
model: openai/kat-coder
api_base: http://127.0.0.1:8000/v1
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
Table with columns: Mode, temperature, top_p, top_k, presence_penalty| 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.
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.
Table with columns: Metric, Value| 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) |
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.
Table with columns: Concurrency (subagents), Per-agent context, Total KV used, KV utilization, Notes| 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% |
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
# 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):
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$",
],
)
oneshot(model=model, recipe=recipe)
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):
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
🇷🇺 Русская версия
KAT-Coder-V2.5-Dev FP8-Dynamic (готов для vLLM)
FP8-Dynamic посттренировочная квантизация модели 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.
Table | |
|---|
| Базовая модель | 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 |
|
Что это за модель
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, чтобы модель уместилась на одном 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-список:
ignore:
- 're:.*lm_head'
- 're:.*visual.*'
- 're:.*mlp.gate$'
- 're:.*shared_expert_gate$'
Совместимость
- 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.
Рекомендации по сэмплингу
Table with columns: Режим, temperature, top_p, top_k, presence_penalty| Режим | 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 токена), все ядра прогреты.
Table with columns: Метрика, Значение| Метрика | Значение |
|---|
| Холодный старт — веса | 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 запрос) |
Ядра, выбранные 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), а не память.
Table with columns: Конкурентность (субагенты), Контекст на агента, Всего KV занято, Утилизация KV, Примечание| Конкурентность (субагенты) | Контекст на агента | Всего 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 % |
Рекомендация: --max-num-seqs 12 даёт 12 параллельных кодинг-агентов, каждый с полным окном 256k, используя лишь ~53 % KV — безопасный прод-сетап. Поднимайте до 22 только для чисто батчевого throughput с короткими сессиями.
Воспроизведение квантизации
Скрипт, рецепт и требования — см. английскую секцию выше (всё идентично). Время на DGX Spark (только CPU): ~5 мин. На выходе 35,77 ГБ одним model.safetensors.
Атрибуция