diff --git a/.github/release/examples/kimi-k25/README.md b/.github/release/examples/kimi-k25/README.md
index 40f05e9b..06cc0041 100644
--- a/.github/release/examples/kimi-k25/README.md
+++ b/.github/release/examples/kimi-k25/README.md
@@ -1,5 +1,7 @@
# Kimi K2.5 LoRA 微调:安装、训练与对话
+[English version](README_EN.md)
+
本教程使用 KTransformers `0.7.0.post4` 和 NekoQA 数据集,让 Kimi K2.5
学会猫娘风格的语气,再用 SGLang 加载训练结果进行对话。原始模型权重不变,
训练结果单独保存为 LoRA。
diff --git a/.github/release/examples/kimi-k25/README_EN.md b/.github/release/examples/kimi-k25/README_EN.md
new file mode 100644
index 00000000..d7d838d2
--- /dev/null
+++ b/.github/release/examples/kimi-k25/README_EN.md
@@ -0,0 +1,438 @@
+# Fine-tune Kimi K2.5 with LoRA: installation, training, and chat
+
+[Chinese version](README.md)
+
+This guide uses KTransformers `0.7.0.post4` and the NekoQA dataset to teach Kimi K2.5
+a catgirl-inspired conversational style. You will then load the trained LoRA in SGLang
+and chat with the model. The original model weights stay unchanged; the LoRA is saved
+separately.
+
+Follow the four steps below. You only need to supply paths for the model and training
+output. If you want to check your setup first, use the optional four-step smoke
+test and resume in the appendix after Step 2.
+
+## Before you begin
+
+Reference setup used for validation:
+
+| Component | Configuration |
+| --- | --- |
+| GPU | 8 × RTX 5090 for training; 4 × RTX 5090 for inference |
+| CPU / RAM | Dual AMD EPYC 9355; approximately 1.5 TiB RAM |
+| OS | Linux x86_64, glibc 2.35+; Python 3.11 and 3.12 validated separately |
+| CUDA | CUDA 12.8 toolkit and a C++ compiler; tested driver: 580.173.02 |
+
+Check that `nvidia-smi` and `nvcc --version` work and that the required GPUs are free.
+In addition to the model, environments, and caches, allow room for training outputs:
+**each checkpoint is approximately 29 GiB, and the converted inference LoRA is
+approximately 9.6 GiB**. This configuration retains up to two checkpoints, plus the
+final LoRA and temporary files. Use persistent storage with ample free space; do not
+put the output under `/dev/shm`.
+
+This guide covers **text-only LoRA fine-tuning of Kimi K2.5**.
+
+## 1. Install
+
+**The core packages come from PyPI. Matching LLaMA-Factory, PEFT, and TRL wheels are
+included in the release kit.** The commands below install separate training and
+inference environments. You do not need to clone repositories or edit source code.
+
+Open a Bash shell on a disk with sufficient space, then download and unpack the kit:
+
+```bash
+mkdir kimi-post4-work
+cd kimi-post4-work
+curl --fail --location --retry 5 --remote-name \
+ https://github.com/kvcache-ai/ktransformers/releases/download/v0.7.0.post4/kimi-k25-post4-user-kit-r2.tar.gz
+printf '%s %s\n' \
+ 88fa5e473e06b67b5ac39605de82da09207ba1b82555f6d4b98d2b6a81de8813 \
+ kimi-k25-post4-user-kit-r2.tar.gz | sha256sum --check
+tar -xzf kimi-k25-post4-user-kit-r2.tar.gz
+cd kimi-k25-post4
+```
+
+Run the remaining commands from this `kimi-k25-post4/` directory in the **same shell**.
+The kit contains the training YAML files, data-preparation scripts, and locked
+installation manifests.
+
+Create two environments: `train-env` for training and `serve-env` for inference. Use
+the same Python version for both; neither environment needs to be activated manually.
+Set `KIMI_PYTHON` below to either Python 3.12 or 3.11. The matching lockfiles are
+selected automatically. If PyPI is slow in your region, you may use the Tsinghua
+mirror:
+
+```bash
+unset PYTHONPATH PYTHONHOME
+export PYTHONNOUSERSITE=1
+export HF_HOME="$PWD/cache/huggingface"
+export XDG_CACHE_HOME="$PWD/cache"
+export TRITON_CACHE_DIR="$PWD/cache/triton"
+export TMPDIR="$PWD/tmp"
+mkdir -p "$TMPDIR"
+export KIMI_PYPI_INDEX=https://pypi.org/simple
+# In mainland China, the previous line can be replaced with: https://pypi.tuna.tsinghua.edu.cn/simple
+KIMI_PYTHON=python3.12 # Change to python3.11 if using Python 3.11
+KIMI_PYTHON_TAG=$("$KIMI_PYTHON" -c '
+import sys
+assert sys.version_info[:2] in ((3, 11), (3, 12)), "Use Python 3.11 or 3.12"
+print("cp%d%d" % sys.version_info[:2])
+') || exit 1
+"$KIMI_PYTHON" -m venv train-env
+train-env/bin/python -m pip install --index-url "$KIMI_PYPI_INDEX" pip==25.2
+train-env/bin/python -m pip install \
+ --index-url "$KIMI_PYPI_INDEX" --timeout 120 --retries 10 --resume-retries 10 \
+ --only-binary=:all: --no-binary=antlr4-python3-runtime \
+ --require-hashes --find-links training-tools -r "locks/$KIMI_PYTHON_TAG/train.lock"
+train-env/bin/python -m pip check
+
+"$KIMI_PYTHON" -m venv serve-env
+serve-env/bin/python -m pip install --index-url "$KIMI_PYPI_INDEX" pip==25.2
+serve-env/bin/python -m pip install \
+ --index-url "$KIMI_PYPI_INDEX" --timeout 120 --retries 10 --resume-retries 10 \
+ --only-binary=:all: --require-hashes -r "locks/$KIMI_PYTHON_TAG/serve.lock"
+serve-env/bin/python -m pip check
+```
+
+**Success check:** both `pip check` commands print `No broken requirements found.`
+The lockfiles pin the compatible versions. Do not install upstream `transformers` or
+`accelerate` into these environments, or replace the included LLaMA-Factory wheel
+with its latest upstream release.
+
+## 2. Prepare the model and data
+
+Set `KIMI_MODEL` to the model directory and `KIMI_OUTPUT` to a **new** training
+output directory. NekoQA is downloaded and processed by the commands below; you do
+not need to put a dataset path in the YAML.
+
+If you already have the exact model or dataset revisions, skip their respective
+`hf download` commands. Downloads require access to Hugging Face. For an offline
+machine, copy the complete model and dataset from a connected machine; changing the
+PyPI mirror will not fix a Hugging Face download issue.
+
+```bash
+export KIMI_MODEL=/absolute/path/to/Kimi-K2.5
+export KIMI_OUTPUT=/absolute/path/to/new-kimi-output
+export OMP_NUM_THREADS=1
+export MKL_NUM_THREADS=1
+export TOKENIZERS_PARALLELISM=false
+mkdir -p "$KIMI_OUTPUT"
+
+train-env/bin/hf download moonshotai/Kimi-K2.5 \
+ --revision 54383e83fa343a1331754112fb9e3410c55efa2f --local-dir "$KIMI_MODEL"
+train-env/bin/hf download liumindmind/NekoQA-10K NekoQA-10K.json \
+ --repo-type dataset --revision 1b2110c996a8237823b86c1a3d3e8a6762b38430 \
+ --local-dir data-source
+train-env/bin/python tools/split_nekoqa.py \
+ --input data-source/NekoQA-10K.json --output-dir neko-splits
+train-env/bin/python tools/prepare_kimi_data.py \
+ --model "$KIMI_MODEL" --trust-remote-code \
+ --input neko-splits/neko_train.json --eval-input neko-splits/neko_eval.json \
+ --output-dir prepared-neko
+```
+
+**Success check:** `prepared-neko/` contains processed data and `dataset_info.json`.
+The scripts split the training, validation, and held-out questions and format them
+for Kimi's chat template. The YAML setting `template: empty` is intentional for this
+preprocessed data. Keep it and do not skip preprocessing.
+
+Use the complete original model. Do not convert it to full BF16 or modify its
+files. `--trust-remote-code` executes downloaded model code, so verify that you
+trust the model source.
+
+## 3. Train the style LoRA
+
+The following command uses 8 GPUs and [train-neko.yaml](train-neko.yaml) for one
+epoch over NekoQA. Per-GPU batch size is 1, gradient accumulation is 8, and the
+maximum sequence length is 4096. Paths are passed to the YAML on the command line;
+you do not need to edit the file:
+
+```bash
+PATH="$PWD/train-env/bin:$PATH" CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 \
+train-env/bin/accelerate launch --config_file examples/fsdp2_8gpu.yaml \
+ --no_python train-env/bin/llamafactory-cli train examples/train-neko.yaml \
+ model_name_or_path="$KIMI_MODEL" kt_weight_path="$KIMI_MODEL" \
+ dataset_dir="$PWD/prepared-neko" output_dir="$KIMI_OUTPUT/neko"
+```
+
+**Expected progress:** the log continues to report steps, loss, and grad norm,
+without `nan` or `inf`. The epoch has about 149 steps. Training validates and saves
+at step 100, producing `$KIMI_OUTPUT/neko/checkpoint-100/`. Let training finish
+normally before proceeding to inference.
+
+This configuration trains both attention and expert LoRA with rank 8, alpha 16,
+and dropout 0. Packing is disabled, and both CPU and GPU activations are
+recomputed. Keep these settings unchanged for your first run. The 4096-token
+limit is a maximum; samples are not padded to that length.
+
+The next step uses `checkpoint-100` because that checkpoint has already shown a
+clear style change in validation. Keep the one-epoch configuration: **do not set
+`max_steps: 100` merely to obtain the step-100 checkpoint**, because doing so
+changes the learning-rate schedule. If training is interrupted, keep the complete
+checkpoint and append
+`resume_from_checkpoint="$KIMI_OUTPUT/neko/checkpoint-100"` to the training command
+to resume from that save point. Use an existing checkpoint path and leave the
+other training settings unchanged.
+
+## 4. Load the LoRA and chat
+
+**Convert the training artifact first.** The checkpoint contains LoRA weights for
+regular modules and experts. This script converts both to SGLang's adapter format
+without changing the original checkpoint:
+
+```bash
+export KIMI_CHECKPOINT="$KIMI_OUTPUT/neko/checkpoint-100"
+export KIMI_ADAPTER="$KIMI_OUTPUT/sglang-neko"
+train-env/bin/python tools/convert_kt_to_sglang_adapter.py \
+ "$KIMI_CHECKPOINT" "$KIMI_ADAPTER" --base-model-name-or-path "$KIMI_MODEL"
+```
+
+**Start the inference server.** Make sure training has exited and GPUs 0–3 are
+free, then run the following in the same shell:
+
+```bash
+set -o pipefail
+PATH="$PWD/serve-env/bin:$PATH" CUDA_VISIBLE_DEVICES=0,1,2,3 \
+serve-env/bin/python -m sglang.launch_server \
+ --model-path "$KIMI_MODEL" --trust-remote-code \
+ --served-model-name kimi --host 127.0.0.1 --port 30000 \
+ --tensor-parallel-size 4 --dtype bfloat16 --context-length 2048 \
+ --max-total-tokens 4096 --chunked-prefill-size 256 --max-running-requests 4 \
+ --mem-fraction-static 0.75 --disable-cuda-graph --disable-radix-cache \
+ --attention-backend triton --grammar-backend llguidance \
+ --kt-method RAWINT4 --kt-weight-path "$KIMI_MODEL" \
+ --kt-cpuinfer 64 --kt-threadpool-count 2 --kt-num-gpu-experts 0 \
+ --random-seed 42 --watchdog-timeout 900 \
+ --enable-lora --lora-backend triton --lora-paths "neko=$KIMI_ADAPTER" \
+ 2>&1 | tee "$KIMI_ADAPTER.server.log"
+```
+
+The first launch may take time to compile kernels and load the model. The server
+keeps this terminal occupied. Its log should report `Loaded KT expert LoRA for layer ...`
+for expert layers 1–60.
+
+**Send a question.** In a second terminal, wait for `/health` to succeed and then
+send a chat request:
+
+```bash
+curl --noproxy 127.0.0.1 --fail http://127.0.0.1:30000/health
+curl --noproxy 127.0.0.1 --fail http://127.0.0.1:30000/v1/chat/completions \
+ -H 'Content-Type: application/json' \
+ -d '{"model":"kimi:neko","messages":[{"role":"user","content":"我今天学习有点累,请用两句话鼓励我。"}],"temperature":0,"seed":42,"max_tokens":1024,"chat_template_kwargs":{"thinking":false,"enable_thinking":false}}'
+```
+
+`kimi:neko` selects the loaded LoRA. Inspect `choices[0].message.content` in the
+response: it should be a complete answer in the trained style. The prompt does
+not tell the model to play a catgirl; this lets you observe the style it learned.
+
+You have now installed, trained, and run the adapter. The appendices below are
+optional.
+
+## Appendix
+
+
+Four-step smoke test and resume
+
+Run this after Steps 1 and 2, in the same shell and working directory, with 8
+free GPUs. It checks training, saving, resuming, and adapter loading. Use Step 3
+for the actual style training. The two smoke runs and one adapter conversion
+require at least another 140 GiB of persistent disk space, excluding the model,
+environments, and caches.
+
+First, run four steps into a separate `smoke-a/` directory:
+
+```bash
+PATH="$PWD/train-env/bin:$PATH" CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 \
+train-env/bin/accelerate launch --config_file examples/fsdp2_8gpu.yaml \
+ --no_python train-env/bin/llamafactory-cli train examples/train-smoke.yaml \
+ model_name_or_path="$KIMI_MODEL" kt_weight_path="$KIMI_MODEL" \
+ dataset_dir="$PWD/prepared-neko" output_dir="$KIMI_OUTPUT/smoke-a"
+```
+
+A successful run creates `checkpoint-2` and `checkpoint-4`, with finite loss
+and grad norm. Restart from the step-2 checkpoint into another directory:
+
+```bash
+PATH="$PWD/train-env/bin:$PATH" CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 \
+train-env/bin/accelerate launch --config_file examples/fsdp2_8gpu.yaml \
+ --no_python train-env/bin/llamafactory-cli train examples/train-smoke.yaml \
+ model_name_or_path="$KIMI_MODEL" kt_weight_path="$KIMI_MODEL" \
+ dataset_dir="$PWD/prepared-neko" output_dir="$KIMI_OUTPUT/smoke-b" \
+ resume_from_checkpoint="$KIMI_OUTPUT/smoke-a/checkpoint-2"
+```
+
+The log should resume at step 2 and finish at step 4. Keep the GPU count, data,
+and training configuration unchanged. `max_steps: 4` means four steps total, not
+four *additional* steps. `resume_from_checkpoint` restores the training state;
+`adapter_name_or_path` only loads the LoRA and is not a substitute.
+
+To check inference, run the conversion below, then use the **server startup and
+chat commands** from Step 4. Do not rerun Step 4's `checkpoint-100` path assignment
+or conversion command:
+
+```bash
+export KIMI_CHECKPOINT="$KIMI_OUTPUT/smoke-b/checkpoint-4"
+export KIMI_ADAPTER="$KIMI_OUTPUT/sglang-smoke"
+train-env/bin/python tools/convert_kt_to_sglang_adapter.py \
+ "$KIMI_CHECKPOINT" "$KIMI_ADAPTER" --base-model-name-or-path "$KIMI_MODEL"
+```
+
+When finished, stop the inference server with Ctrl-C and confirm that its GPUs
+are free before starting Step 3. The production run starts from the original
+model, not the smoke-test LoRA.
+
+
+
+
+Evaluate the trained style on 32 held-out questions
+
+Step 2 reserves 32 questions in `neko-splits/heldout.json` that are never used
+for training. Do not add a catgirl role instruction. Check whether the tone
+changes while answers remain relevant and complete.
+
+To compare with the base model, stop the LoRA server and start a **new process**
+with the Step 4 command but remove `--enable-lora`, `--lora-backend`, and
+`--lora-paths`. Use `model: kimi` in the request. This version loads CPU expert
+LoRA at startup; merely changing the model name within the same server does
+not disable it.
+
+In a second terminal, change to the same `kimi-k25-post4/` directory and run
+the script below. Its output file must not already exist, so previous results
+are not overwritten. For a separate base-model comparison, change `model_id`
+to `kimi` and use a different output filename.
+
+```bash
+serve-env/bin/python - <<'PY'
+import json
+from pathlib import Path
+import requests
+
+model_id = "kimi:neko"
+questions = json.loads(Path("neko-splits/heldout.json").read_text())
+session = requests.Session()
+session.trust_env = False
+with Path("neko-heldout-responses.jsonl").open("x", encoding="utf-8") as output:
+ for index, row in enumerate(questions, 1):
+ body = {
+ "model": model_id,
+ "messages": [{"role": "user", "content": row["prompt"]}],
+ "temperature": 0, "seed": 42, "max_tokens": 1024,
+ "chat_template_kwargs": {"thinking": False, "enable_thinking": False},
+ }
+ response = session.post("http://127.0.0.1:30000/v1/chat/completions", json=body, timeout=900)
+ response.raise_for_status()
+ output.write(json.dumps({"prompt": row["prompt"], "response": response.json()}, ensure_ascii=False) + "\n")
+ output.flush()
+ print(f"{index}/{len(questions)}", flush=True)
+PY
+```
+
+Open `neko-heldout-responses.jsonl` and inspect `choices[0].message.content`
+and `finish_reason`. A `finish_reason` of `length` means the answer was cut off
+at the token limit and should not count as complete.
+
+Besides style, check factual accuracy and instruction following. Examples
+include “17+25, output only the number,” exact text copying, and JSON-format
+instructions.
+
+
+
+
+Troubleshooting
+
+| Symptom | Check |
+| --- | --- |
+| pip download times out | Retry the same install command or use the Tsinghua mirror. If the mirror lacks a pinned version, switch back to `https://pypi.org/simple`. Keep the version and hash checks. |
+| `hf download` times out | Check Hugging Face access. You can download the pinned revision on a connected machine and copy it in full. Changing the PyPI mirror will not help. |
+| pip reports dependency conflicts or cannot find LF/PEFT/TRL | Use fresh environments, unpack the matching release kit, and keep `--find-links training-tools`. |
+| Unexpected loss or repeated chat-template text | Run data preparation first; keep `template: empty` and `packing: false`. |
+| Rank 0 disappears during loading; Gloo connection closes | Check the rank-0 log, host OOM records, and free RAM on **each NUMA node**. One node can run out even if the host has free RAM. Avoid putting large checkpoints in RAM or loading multiple models at once. |
+| Resume starts from step 0 | Pass `resume_from_checkpoint` pointing to a complete checkpoint, not just an adapter. |
+| Inference looks like the base model or only partly changed | Confirm conversion included both LoRA types, start a new server, check that expert layers 1–60 loaded, and request `kimi:neko`. |
+
+Do not use `--no-deps` or disable version checks to bypass installation
+conflicts. Do not set `FORCE_TORCHRUN` or wrap the training command in another
+`torchrun`.
+
+
+
+
+Pinned versions, configuration files, and validation details
+
+The lockfiles contain these versions; you do not need to install them individually:
+
+| Source | Pinned version |
+| --- | --- |
+| PyPI: `ktransformers`, `kt-kernel`, `sglang-kt` | `0.7.0.post4` |
+| PyPI: `transformers-kt` | `5.6.0.post5` |
+| PyPI: `accelerate-kt` | `1.14.0.post3` |
+| Release `training-tools/`: LLaMA-Factory | `0.9.6.dev0+kt.20260912` |
+| Release `training-tools/`: PEFT and TRL | `0.18.1+kt.20260912` and `0.24.0+kt.20260912` |
+
+The LLaMA-Factory wheel comes from the
+[pinned KT installation branch](https://github.com/yyj6666667/LlamaFactory/tree/564574eb0214840ea4b9464b2f9178a52a075d73).
+The PEFT and TRL wheels only adjust dependency metadata; they do not change
+runtime code. These three companion wheels are not on PyPI. Upstream
+`transformers` and `accelerate` use the same Python module names as the KT
+packages, so do not install both variants together. The installation uses
+Torch 2.9.1. ANTLR 4.9.3 is built from a hash-checked official source
+distribution; the other dependencies use wheels.
+
+`locks/cp311/` and `locks/cp312/` pin package versions and file hashes for
+their respective Python interpreters. Python 3.11 uses NumPy 2.4.6, SciPy
+1.17.1, and ContourPy 1.3.3; Python 3.12 retains the separately validated
+versions. Both use the same training YAML without parameter changes.
+
+The repository and release-kit YAML values match. File mapping:
+
+| Repository file | Release-kit file | Purpose |
+| --- | --- | --- |
+| [train-neko.yaml](train-neko.yaml) | `examples/train-neko.yaml` | One-epoch style training |
+| [train.yaml](train.yaml) | `examples/train-smoke.yaml` | Four-step smoke test |
+| [fsdp2_8gpu.yaml](fsdp2_8gpu.yaml) | `examples/fsdp2_8gpu.yaml` | Eight-GPU launch configuration |
+
+The data are deduplicated and split with seed 42 into 9,477 training examples,
+468 validation examples, and 32 held-out questions. Preprocessing uses Kimi's
+native non-thinking template; only the answer contributes to loss. Keep
+`template: empty` and `train_on_prompt: false`; do not feed raw Neko JSON to
+training. The longest processed example is 2,120 tokens.
+
+A complete checkpoint should contain the following. Keep `save_only_model: false`:
+
+- `adapter_model.safetensors`, `fused_expert_lora.safetensors`, and adapter configuration;
+- `kt_optimizer.index.json` and eight `optimizer_rank_*.pt` files;
+- `scheduler.pt`, `trainer_state.json`, and eight `rng_state_*.pth` files.
+
+After both smoke runs, you can check that continuous training and resumed
+training produced identical LoRA tensors. The comparison checks tensor
+contents, not file serialization order:
+
+```bash
+train-env/bin/python - "$KIMI_OUTPUT" <<'PY'
+from pathlib import Path
+import sys
+import torch
+from safetensors import safe_open
+
+root = Path(sys.argv[1])
+for name in ("adapter_model.safetensors", "fused_expert_lora.safetensors"):
+ a = root / "smoke-a/checkpoint-4" / name
+ b = root / "smoke-b/checkpoint-4" / name
+ with safe_open(a, framework="pt", device="cpu") as left, safe_open(b, framework="pt", device="cpu") as right:
+ assert set(left.keys()) == set(right.keys()), name
+ for key in left.keys():
+ assert torch.equal(left.get_tensor(key), right.get_tensor(key)), (name, key)
+ print(name, "EXACT_RESUME_PASSED")
+PY
+```
+
+Both files should print `EXACT_RESUME_PASSED`. The full validation also compared
+each rank's optimizer and RNG state, scheduler, and post-resume loss. Adapter
+conversion exported 610 regular LoRA tensors and 138,240 expert LoRA tensors;
+SGLang loaded expert layers 1–60.
+
+For source provenance, package checksums, and validation results, see the
+[release and validation record](https://github.com/kvcache-ai/ktransformers/releases/tag/v0.7.0.post4).
+Keep your training logs, complete checkpoints, data splits, and inference
+responses to make your own results reproducible.
+
+