Repository
Gemma-4-31B-IT-NVFP4-Recipe
Serve nvidia/Gemma-4-31B-IT-NVFP4 via vLLM with MTP speculative decoding, tool calling, and thinking/reasoning support
- Role
- Model
- Parameter
- Value
- temperature
- 1.0
- top_p
- 0.95
Specs
- RoleModel
- ParameterValue
- temperature1.0
- top_p0.95
- top_k64
- ParameterValue
- --max-num-seqs2
- --max-num-batched-tokens8192
- --gpu-memory-utilization0.70
- MetricValue
- Available KV cache memory27.96 GiB
- Total KV cache capacity542,037 tokens
- Max concurrency (262k ctx each)2.07×
- WorkloadTypical concurrent requests
- Short queries (≤1K tokens each)2 (hits max-num-seqs cap)
- Coding / tool calling (500–2K tokens)2
- Long context (32K+ tokens each)1–2
- gpu-mem-utilApprox. KV cache capacity
- 0.70542K tokens
- 0.85~740K tokens
- 0.95~870K tokens
- VariableDefault
- HF_TOKEN—
- PORT8888
- HOST0.0.0.0
- FlagValue
- --quantizationmodelopt
- --tensor-parallel-size1
- --gpu-memory-utilization0.70
- --max-model-len262144
- --kv-cache-dtypefp8
- --limit-mm-per-prompt{"image":4,"video":1,"audio":1}
- --chat-template./chat_template.jinja
- --reasoning-parsergemma4
- --tool-call-parsergemma4
- --attention-backendtriton_attn
- --load-formatfastsafetensors
- --speculative-config(see start.sh)
- --override-generation-config{"temperature":1.0,...}
- SymptomLikely cause
README
Gemma 4 31B IT — NVFP4
Serve Google Gemma 4 31B IT in 4-bit NVFP4 quantization via vLLM with an OpenAI-compatible endpoint, Multi-Token Prediction speculative decoding, tool calling, and thinking/reasoning support.
Automatically downloads and caches both models from Hugging Face — just set your token and run.
Models
| Role | Model | Size | Precision |
|---|---|---|---|
| 🎯 Target | nvidia/Gemma-4-31B-IT-NVFP4 |
~31B | NVFP4 (ModelOpt) |
| ⚡ Draft | google/gemma-4-31B-it-assistant |
~0.5B | BF16 |
The draft model runs 4 lightweight decoder layers with Q-only attention, sharing KV cache with the target. It produces up to 4 draft tokens per step for speculative decoding — accelerating generation without sacrificing output quality.
Quick Start
Prerequisites
- Docker with NVIDIA Container Toolkit (
nvidia-ctkcompleted) - NVIDIA GPU (tested on a DGX Spark — GB10 GPU with 128 GB unified memory)
- Hugging Face token with access to both gated models:
- https://huggingface.co/nvidia/Gemma-4-31B-IT-NVFP4
- https://huggingface.co/google/gemma-4-31B-it-assistant
Start the server
HF_TOKEN=hf_your_token_here ./start.sh
The script downloads both models on first run (cached in ~/.cache/huggingface), launches the vLLM container, and waits for it to be ready.
Use the API
curl http://localhost:8888/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "nvidia/Gemma-4-31B-IT-NVFP4",
"messages": [{"role": "user", "content": "Hello!"}]
}'
Stop the server
./stop.sh
Features
🧠 Thinking / Reasoning
Enabled by default. The model outputs its reasoning in a <channel>thought block before the final response:
<|turn|>model
<|channel|>thought
The user is asking about...
<channel|>Here's the answer...
<turn|>
Disable per-request by passing chat_template_kwargs: {"enable_thinking": false} in your API call.
🛠️ Tool Calling
Native tool calling via the gemma4 parser. Everything is pre-configured — just pass tools in your request:
curl http://localhost:8888/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "nvidia/Gemma-4-31B-IT-NVFP4",
"messages": [{"role": "user", "content": "What'\''s the weather in Paris?"}],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": { "type": "string", "description": "City name" }
},
"required": ["location"]
}
}
}]
}'
🖼️ Multi-modal (Image + Video)
The model supports images and video through a shared vision encoder. Audio has a token ID defined but the NVFP4 quantized weights do not include an audio encoder (audio_config: null).
Image example:
{
"messages": [
{
"role": "user",
"content": [
{"type": "image_url", "image_url": {"url": "https://example.com/photo.jpg"}},
{"type": "text", "text": "What's in this image?"}
]
}
]
}
Video example:
{
"messages": [
{
"role": "user",
"content": [
{"type": "video_url", "video_url": {"url": "https://example.com/video.mp4"}},
{"type": "text", "text": "Summarize this video"}
]
}
]
}
Limits: up to 4 images or 1 video (or 1 audio if supported) per prompt.
⚡ MTP Speculative Decoding
Multi-Token Prediction generates 4 draft tokens per step using the lightweight assistant model. Most effective for:
- Batched workloads
- Long-context generation
- System-prompt-heavy applications (prefix caching also active)
Recommended Client Settings
As recommended by Google for Gemma 4:
| Parameter | Value |
|---|---|
temperature |
1.0 |
top_p |
0.95 |
top_k |
64 |
Pass per-request or set as defaults in your client.
Concurrency & Performance
Tested on an NVIDIA DGX Spark (GB10 GPU, 128 GB unified memory).
Limits
| Parameter | Value | Description |
|---|---|---|
--max-num-seqs |
2 |
Hard cap on concurrent sequences |
--max-num-batched-tokens |
8192 |
Total tokens across all sequences in a single batch |
--gpu-memory-utilization |
0.70 |
vLLM memory budget |
KV cache at 0.70 gpu-memory-utilization
| Metric | Value |
|---|---|
| Available KV cache memory | 27.96 GiB |
| Total KV cache capacity | 542,037 tokens |
| Max concurrency (262k ctx each) | 2.07× |
Realistic throughput
| Workload | Typical concurrent requests |
|---|---|
| Short queries (≤1K tokens each) | 2 (hits max-num-seqs cap) |
| Coding / tool calling (500–2K tokens) | 2 |
| Long context (32K+ tokens each) | 1–2 |
Tuning for higher concurrency
The --gpu-memory-utilization of 0.70 leaves headroom. You can increase concurrency by raising it — more KV cache blocks fit, which allows more concurrent sequences at the cost of less free GPU memory:
gpu-mem-util |
Approx. KV cache capacity | Est. max concurrency (262k ctx) |
|---|---|---|
| 0.70 | 542K tokens | 2× |
| 0.85 | ~740K tokens | ~2.8× |
| 0.95 | ~870K tokens | ~3.3× |
# In start.sh, for more concurrency:
--gpu-memory-utilization 0.95
--max-num-seqs 4
--max-num-batched-tokens 16384
Monitor with nvidia-smi to ensure you don't OOM.
Configuration
Environment variables
| Variable | Default | Description |
|---|---|---|
HF_TOKEN |
— | Hugging Face token for gated models |
PORT |
8888 |
Server port |
HOST |
0.0.0.0 |
Bind address |
Key vLLM flags
| Flag | Value | Notes |
|---|---|---|
--quantization |
modelopt |
NVFP4 format from NVIDIA ModelOpt |
--tensor-parallel-size |
1 |
Bump for multi-GPU |
--gpu-memory-utilization |
0.70 |
Tune to your VRAM budget |
--max-model-len |
262144 |
256k context window |
--kv-cache-dtype |
fp8 |
Reduces KV cache memory by ~50% |
--limit-mm-per-prompt |
{"image":4,"video":1,"audio":1} |
Max images, video, or audio per request |
--chat-template |
./chat_template.jinja |
Gemma 4 canonical template |
--reasoning-parser |
gemma4 |
Parses thinking blocks |
--tool-call-parser |
gemma4 |
Native tool call format |
--attention-backend |
triton_attn |
Triton-based flash attention |
--load-format |
fastsafetensors |
Fast local model loading |
--speculative-config |
(see start.sh) | MTP with assistant model |
--override-generation-config |
{"temperature":1.0,...} |
Default sampling params |
Per-request overrides
Clients can override the template kwargs per-request:
{
"chat_template_kwargs": {
"enable_thinking": false,
"preserve_thinking": false
}
}
Files
.
├── start.sh # Launch vLLM container (auto-downloads models)
├── stop.sh # Gracefully stop the container
├── chat_template.jinja # Gemma 4 canonical chat template (390 lines)
├── README.md # This file
├── .gitignore # Ignores runtime artifacts
├── .vllm.log # Container logs (git-ignored)
├── .vllm.pid # Container PID (git-ignored)
└── .cache/ # Triton cache (git-ignored)
Python Client Examples
OpenAI SDK
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8888/v1",
api_key="not-needed",
)
response = client.chat.completions.create(
model="nvidia/Gemma-4-31B-IT-NVFP4",
messages=[{"role": "user", "content": "Write a quick sort in Python"}],
temperature=0.2,
)
print(response.choices[0].message.content)
With tool calling
tools = [
{
"type": "function",
"function": {
"name": "run_code",
"description": "Execute Python code and return stdout",
"parameters": {
"type": "object",
"properties": {
"code": {"type": "string"}
},
"required": ["code"]
}
}
}
]
response = client.chat.completions.create(
model="nvidia/Gemma-4-31B-IT-NVFP4",
messages=[{"role": "user", "content": "Calculate 42 * 37"}],
tools=tools,
)
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Container exits immediately | Out of VRAM | Lower --gpu-memory-utilization to 0.5 |
| Download hangs | Missing HF token | Pass HF_TOKEN=... |
| Port conflict | Something on :8888 | PORT=8889 ./start.sh |
| Slow first start | Downloading ~19 GB | Normal — cached after first run |
Unknown vLLM env var warnings |
Build metadata in image | Harmless, can ignore |
| MTP not speeding up | Short single-turn prompts | Most effective with batching / long contexts |
License
The model weights are governed by the Gemma License. This repository's scripts and configuration are provided under the MIT License.