Downloads · 30 days
37
13% of all-time downloads
sulabhkatiyar/eagle3-sarvam-30b
eagle3-sarvam-30b is a text generation model from sulabhkatiyar. Use it when you need the model to write or continue text. It is set up for vllm. The card lists the license as apache-2.0.
A lightweight one-layer Llama-style Eagle3 draft head trained to accelerate inference for the 30B-parameter multilingual model sarvamai/sarvam-30b under speculative decoding. The draft proposes candidate continuations…
Downloads · 30 days
37
13% of all-time downloads
All-time downloads
293
Public
Parameters
2.4B
9.4 GB on disk
Likes
0
Public
Click a slice to open those files.
.safetensors4.7 GB · 100%
How the weights are stored.
BF162.4B · 100%
From the Hugging Face model README
A lightweight one-layer Llama-style Eagle3 draft head trained to accelerate inference for the 30B-parameter multilingual model
sarvamai/sarvam-30b under speculative decoding. The draft proposes
candidate continuations conditioned on auxiliary hidden states pulled from three mid-target layers; the target verifies
the proposals in a single forward pass, accepting any agreed-upon prefix in one shot. This yields a 2.53x mean
end-to-end speedup across MT-Bench, HumanEval, GSM8K and a multilingual prompt set, at zero quality cost
(speculative decoding is mathematically lossless: the final output distribution is identical to the target running alone).
| Metric | Value |
|---|---|
| Combined throughput speedup vs target-only baseline | 2.527x |
| Combined Mean Acceptance Length (MAL) | 3.31 tokens / target step |
| Combined pos-0 draft acceptance | 0.678 |
Eagle3 (paper) trains a small auto-regressive draft head that ingests auxiliary hidden states from three layers of a frozen target model and emits proposed continuations one token at a time. At inference, the target verifies a tree of draft proposals in parallel and accepts the longest agreed-upon path, so each expensive target forward yields many emitted tokens instead of one. The draft is small (~4.7 GB here) and adds modest overhead per target step; net throughput rises in proportion to how many draft tokens the target ratifies.
from vllm import LLM, SamplingParams
llm = LLM(
model="sarvamai/sarvam-30b",
speculative_config={
"method": "eagle3",
"model": "sulabhkatiyar/eagle3-sarvam-30b",
"num_speculative_tokens": 7,
},
trust_remote_code=True,
dtype="bfloat16",
gpu_memory_utilization=0.9,
max_model_len=4096,
disable_log_stats=False, # required if you want real-time Mean Acceptance Length
)
prompts = ["Explain quantum computing in simple terms."]
out = llm.generate(prompts, SamplingParams(temperature=0.0, max_tokens=512))
print(out[0].outputs[0].text)
Stock vLLM does not load this draft out of the box. Five patches must be applied to your vLLM install. The patch script and its one supporting source file are bundled in this repo and can be downloaded and applied with three commands:
# 1. Download both files (the script + its sibling model file) into the layout it expects
mkdir -p vllm_patches scripts
curl -fsSL https://huggingface.co/sulabhkatiyar/eagle3-sarvam-30b/resolve/main/scripts/apply_vllm_patches.sh \
-o scripts/apply_vllm_patches.sh
curl -fsSL https://huggingface.co/sulabhkatiyar/eagle3-sarvam-30b/resolve/main/vllm_patches/sarvam.py \
-o vllm_patches/sarvam.py
chmod +x scripts/apply_vllm_patches.sh
# 2. Point the script at the venv where vLLM is installed (use $VIRTUAL_ENV after `source venv/bin/activate`,
# or pass --venv /absolute/path explicitly). The script wants the venv root, not site-packages.
bash scripts/apply_vllm_patches.sh --venv "$(python -c 'import sys; print(sys.prefix)')"
The script is idempotent and self-verifies after every patch. Re-apply after any pip install / pip upgrade of vLLM
(the patches are local edits to files inside site-packages/vllm/ and do not survive a vLLM reinstall).
| Patch | What it does |
|---|---|
| A | Registers SarvamMoEForCausalLM and SarvamMLAForCausalLM in vLLM's model registry and drops sarvam.py into vllm/model_executor/models/ |
| B | Teaches Eagle3's spec-decode path to find the input embedding via word_embeddings / get_input_embeddings() (Sarvam exposes neither embed_tokens nor embedding) |
| C | Adds the SupportsEagle3 interface to BailingMoE (Sarvam's underlying backbone) so the draft can hook the target |
| D | Adds sarvam to the Eagle3 target-architecture allow-list in vllm/config/speculative.py |
| E | Reads eagle_aux_hidden_state_layer_ids from the nested eagle_config block of the draft's config.json (vllm/v1/worker/gpu_model_runner.py). Without this, vLLM falls back to a generic heuristic and feeds the draft the wrong auxiliary hidden states; per-position acceptance collapses past position 0 |
A note on metrics: vLLM's LLM.get_metrics() returns 0 for speculative decoding unless disable_log_stats=False is
passed to the LLM(...) constructor. The Mean Acceptance Length numbers in this card are scraped from the engine's
SpecDecoding metrics: Mean acceptance length: X.XX log line, not from get_metrics().
The draft was trained on 247,959 multi-turn instruction-tuning records in SpecForge format:
{"conversations": [{"role": "user", "content": ...}, {"role": "assistant", "content": ...}, ...]}.
User prompts were sampled from public English and Indic corpora, then the assistant turns were
re-generated by the target sarvam-30b itself with greedy decoding under vLLM so that the draft's training
distribution matches what it will see at deployment time. About 94% of records have two turns (single-turn
prompt/response) and the remaining ~6% have 4–20 turns (multi-turn instruction data).
| Source | Records |
|---|---|
English (mlabonne/open-perfectblend) | 80,000 |
Hindi (SandLogicTechnologies/Indic_Chat_Dataset) | 27,000 |
| Bengali | 11,000 |
| Tamil | 11,000 |
| Telugu | 11,000 |
| Marathi | 8,000 |
| Kannada | 8,000 |
| Gujarati | 8,000 |
| Malayalam | 8,000 |
| Odia | 5,500 |
| Punjabi | 5,500 |
| Urdu | 5,500 |
Low-resource Indic (11 langs from ai4bharat/sangraha + Wikipedia) | 8,998 |
Hindi multi-turn (sarvamai/samvaad-hi-v1) | 16,000 |
Math (openai/gsm8k) | 7,473 |
Math (meta-math/MetaMathQA) | 17,500 |
Reasoning (open-thoughts/OpenThoughts-114k) | 11,000 |
| Total used for training | 247,959 |
| Held-out (fixed, seed 42, used during training for monitoring) | 512 |
The aggregate file was shuffled with seed 42 before writing, so the per-record language tag is not preserved inline. The breakdown above reflects the actual sampling-time arguments and matches the byte-for-byte counts on disk.
Roughly 0.2% (~484 / 247,959) of records were flagged by a heuristic degeneracy filter (repetition loops or truncation artifacts) but were kept. Filtering this category was found in prior experiments to regress draft acceptance — small amounts of imperfect responses make the draft more robust to non-canonical target output at inference time.
| Item | Value |
|---|---|
| Framework | SpecForge @ d5fb617, HF backend |
| Target model | sarvamai/sarvam-30b (19 layers, hidden 4096, vocab 262144, SarvamMoE arch) |
| Draft architecture | 1-layer Llama, hidden 4096, vocab 262144 (matches target) |
| Optimizer | AdamW |
| Learning rate | 1e-4, cosine to ~0 |
| Epochs | 1 |
| Batch size | 8 |
| Sequence length | 512 |
| Auxiliary hidden state layer ids (training side) | [0, 9, 18] |
| Loss | Cross-entropy + KL on TTT positions 0..6 |
| Hardware | 1 x AMD MI300X (192 GB HBM3) |
| Wall time | 17.94 h |
| Total optimizer steps | 28,335 |
| Total tokens seen | ~116M |
| Seed | 42 |
The draft consumes auxiliary hidden states from three of the target's 19 layers. SpecForge captured those during
training via register_forward_hook, which delivers the output of layer i. The deployed config.json ships
eagle_aux_hidden_state_layer_ids = [1, 10, 18] rather than the training-side [0, 9, 18]: vLLM's runtime captures
the input to layer i (= the output of layer i-1), so the runtime indices [1, 10, 18] reproduce the same
hidden-state set the draft was trained against. Two of three channels align perfectly; the third (layer 18) is the
closest available approximation since the target has only 19 layers and there is no idx = 19. If you serve through a
framework that captures layer outputs (matching SpecForge's training convention directly), revert the config to
[0, 9, 18]. This is a single one-line change to the draft's config.json.
All numbers below are from a single MI300X using vLLM with greedy decoding (temperature=0.0),
num_speculative_tokens=7, max_tokens=512, max_model_len=4096, gpu_memory_utilization=0.9. Baselines are
target-only with the same generation settings. MAL is the cumulative
SpecDecoding metrics: Mean acceptance length line scraped from the vLLM engine log.
| Dataset | Prompts | Baseline tok/s | Spec tok/s | Speedup | MAL | Pos-0 accept |
|---|---|---|---|---|---|---|
| MT-Bench | 80 | 40.34 | 94.21 | 2.336x | 2.78 | 0.627 |
| HumanEval | 164 | 40.40 | 90.04 | 2.229x | 3.12 | 0.705 |
| GSM8K | 200 | 39.68 | 126.76 | 3.194x | 4.79 | 0.808 |
| Multilingual | 50 | 39.77 | 93.37 | 2.348x | 2.54 | 0.574 |
| Combined | 494 | 40.05 | 101.10 | 2.527x | 3.31 | 0.678 |


The multilingual prompt set contains 50 free-form instruction prompts split as English 15 / Hindi 15 / Tamil 10 / Bengali 10 — i.e. 4 of the 12 languages the draft was trained on (see "Per-source counts" above for the full training mix). Per-language tok/s is averaged across that language's prompts; sample sizes are small, so per-language numbers are indicative rather than tight confidence intervals.
| Language | Prompts | Baseline tok/s | Spec tok/s | Speedup |
|---|---|---|---|---|
| English | 15 | 39.33 | 110.31 | 2.805x |
| Hindi | 15 | 39.95 | 98.55 | 2.467x |
| Tamil | 10 | 40.01 | 85.50 | 2.137x |
| Bengali | 10 | 39.84 | 87.53 | 2.197x |

eagle_aux_hidden_state_layer_ids = [1, 10, 18] is
correct for vLLM's input-capture convention. SGLang or any framework that captures layer outputs (matching
SpecForge's training convention) should revert to [0, 9, 18].sarvamai/sarvam-30b.If you use this draft, please cite both this artefact and the Eagle3 paper.
@misc{katiyar2026eagle3sarvam,
title = {Eagle3 Speculative-Decoding Draft Head for Sarvam-30B},
author = {Sulabh Katiyar},
year = {2026},
howpublished = {\url{https://huggingface.co/sulabhkatiyar/eagle3-sarvam-30b}}
}
@article{li2025eagle3,
title = {EAGLE-3: Scaling up Inference Acceleration of Large Language Models via Training-Time Test},
author = {Li, Yuhui and Wei, Fangyun and Zhang, Chao and Zhang, Hongyang},
year = {2025},
journal = {arXiv preprint arXiv:2503.01840}
}
sarvam-30b target model.Apache-2.0, matching the base model.