Deploy with vLLM
Serve Muse Glimmer with vLLM(opens in new tab) for high-throughput, low-latency inference with an OpenAI-compatible API endpoint. vLLM handles continuous batching, PagedAttention, and tensor parallelism out of the box.
Prerequisites
- Docker with the NVIDIA container runtime
- An NVIDIA GPU with drivers for CUDA 13.0, which is what the image is pinned to
- 72 GB of VRAM to serve, on one card or across several
Install
Docker is the supported path:
bashdocker pull vllm/vllm-openai:muse-glimmer
pip install vllm doesn't serve this model. Muse Glimmer support in vLLM is an open, unmerged pull request (vllm-project/vllm#51655(opens in new tab)), so the model code and the muse_glimmer parsers are absent from every released wheel. The recipe sets pip: false for that reason, and the image is how you get the unreleased code.
The image is roughly 10.5 GB and publishes linux/amd64 and linux/arm64. There's no published ROCm image; on ROCm, build from the pull request.
What the image gives you:
| Capability | How you get it |
|---|---|
Text model (MuseGlimmerForCausalLM) | Automatic, with no trust_remote_code |
| Tool-call parser | --tool-call-parser muse_glimmer |
| Reasoning parser | --reasoning-parser muse_glimmer |
| Chat template | Ships with the checkpoint as chat_template.jinja. Don't pass --chat-template. |
| Model config | Automatic (muse-glimmer, muse_glimmer_text, muse_glimmer_vision) |
Parser names use underscores. vLLM matches muse_glimmer literally, so the hyphenated spelling fails. --served-model-name muse-glimmer is a label you choose, and it stays hyphenated.
Start the server
The image sets ENTRYPOINT ["vllm", "serve"], so everything after the image name is appended to that. Pass the model and flags directly rather than retyping vllm serve or calling python -m vllm.entrypoints.openai.api_server.
bashdocker run --rm --gpus all --ipc=host \-p 8000:8000 \-v ~/.cache/huggingface:/root/.cache/huggingface \vllm/vllm-openai:muse-glimmer \meta-models/Muse-Glimmer-30B \--served-model-name muse-glimmer \--tensor-parallel-size 1 \--enable-auto-tool-choice \--tool-call-parser muse_glimmer \--reasoning-parser muse_glimmer \--generation-config auto
The flags from --served-model-name down are the recipe's argument list for a single card. The docker run wrapper around them is standard vLLM boilerplate, so adjust it to your host. Mounting ~/.cache/huggingface is what stops the container re-downloading roughly 60 GB of weights on every start.
To serve weights you've already downloaded, mount them and pass the path instead of a Hub id:
bashdocker run --rm --gpus all --ipc=host \-p 8000:8000 \-v /path/to/Muse-Glimmer-30B:/model \vllm/vllm-openai:muse-glimmer \/model \--served-model-name muse-glimmer \--tensor-parallel-size 1 \--enable-auto-tool-choice \--tool-call-parser muse_glimmer \--reasoning-parser muse_glimmer \--generation-config auto
To serve across multiple GPUs, raise the tensor-parallel size:
bashdocker run --rm --gpus all --ipc=host \-p 8000:8000 \-v ~/.cache/huggingface:/root/.cache/huggingface \vllm/vllm-openai:muse-glimmer \meta-models/Muse-Glimmer-30B \--served-model-name muse-glimmer \--tensor-parallel-size 2 \--enable-auto-tool-choice \--tool-call-parser muse_glimmer \--reasoning-parser muse_glimmer \--generation-config auto
For tighter memory, at a cost in throughput and context:
bashdocker run --rm --gpus all --ipc=host \-p 8000:8000 \-v ~/.cache/huggingface:/root/.cache/huggingface \vllm/vllm-openai:muse-glimmer \meta-models/Muse-Glimmer-30B \--served-model-name muse-glimmer \--gpu-memory-utilization 0.9 --max-model-len 8300 --enforce-eager \--enable-auto-tool-choice \--tool-call-parser muse_glimmer \--reasoning-parser muse_glimmer \--generation-config auto
The flags that carry Muse Glimmer-specific behavior:
| Flag | Why |
|---|---|
--served-model-name muse-glimmer | The name the API answers to. Every example here sends "model": "muse-glimmer". |
--enable-auto-tool-choice | Lets the model decide when to call a tool. |
--tool-call-parser muse_glimmer | Converts native ATEM output into standard OpenAI tool_calls. |
--reasoning-parser muse_glimmer | Routes the thinking channel to message.reasoning instead of leaking it into content. |
--generation-config auto | Picks up the checkpoint's published sampling settings and stop tokens. |
--tensor-parallel-size N | Number of GPUs for tensor parallelism. |
--max-model-len N | Caps the sequence length to limit KV-cache memory. |
Sampling
Serve with --generation-config auto, as every command above does, and vLLM picks up the checkpoint's published sampling settings: temperature 1.0, top_p 0.95, and top_k 64.
Send a request
The server exposes an OpenAI-compatible chat completions endpoint:
curlcurl http://localhost:8000/v1/chat/completions \-H "Content-Type: application/json" \-d '{"model": "muse-glimmer","messages": [{"role": "system", "content": "You are a helpful assistant."},{"role": "user", "content": "Write a Python function to compute Fibonacci numbers."}],"max_tokens": 2048,"stream": true}'
Pythonfrom openai import OpenAIclient = OpenAI(base_url="http://localhost:8000/v1", api_key="unused")stream = client.chat.completions.create(model="muse-glimmer",messages=[{"role": "system", "content": "You are a helpful assistant."},{"role": "user", "content": "Write a Python function to compute Fibonacci numbers."},],max_tokens=2048,stream=True,)for chunk in stream:delta = chunk.choices[0].delta.contentif delta:print(delta, end="", flush=True)
Neither request sets sampling parameters, so the server applies the published settings from --generation-config auto.
Verify tool calling
curlcurl http://localhost:8000/v1/chat/completions \-H "Content-Type: application/json" \-d '{"model": "muse-glimmer","messages": [{"role": "user", "content": "What is the weather in Paris in celsius? Use the tool."}],"tools": [{"type": "function", "function": {"name": "get_weather","description": "Get current weather for a city.","parameters": {"type": "object", "properties": {"city": {"type": "string"},"units": {"type": "string", "enum": ["celsius", "fahrenheit"]}},"required": ["city"]}}}],"tool_choice": "auto"}'
You get back get_weather(city="Paris", units="celsius"), with the reasoning under message.reasoning, or delta.reasoning when streaming. Parsing happens server-side, so an OpenAI-compatible harness needs no ATEM-specific handling.
Stop tokens
Muse Glimmer needs eos_token_id = [200001, 200008], which is <|end_of_text|> and <|eot|>. Serving with --generation-config auto picks these up from the checkpoint.
These token IDs come from the converted checkpoint. Confirm them against your checkpoint's tokenizer_config.json, since older exports can differ. See special tokens for the full chat-format token set.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
ValueError: model architectures ... are not supported | Running a released vLLM wheel rather than the image | The model code isn't in any wheel. Use vllm/vllm-openai:muse-glimmer. |
invalid tool call parser | Hyphenated parser name | Use underscores: muse_glimmer. |
| Tool calls returned as plain text | Parser not enabled | Pass --tool-call-parser muse_glimmer. The chat template comes from the checkpoint. |
| Only the first tool call runs | Harness expects one tool_calls array | One call per message. Read consecutive assistant messages. |
| Generation never stops | Wrong stop tokens | See Stop tokens. Never stop on the end-of-message token. |
| Reasoning leaking into content | No reasoning parser | Pass --reasoning-parser muse_glimmer, which routes reasoning to message.reasoning. |
| Flat, repetitive answers | Running greedy | Drop "temperature": 0 and use the published settings. |
| Out of memory shortly after startup | Sized to the 60 GB weights rather than the 72 GB serving footprint | Lower --max-model-len, raise --tensor-parallel-size, or serve a quantized checkpoint. |
Next steps
Add speculative decoding to reduce per-token latency. For CPU or mixed inference, see llama.cpp; for other runtimes, see Run inference.