Muse Glimmer
Muse Glimmer is a 30B open agentic model from Meta Superintelligence Labs, found on HuggingFace. It pairs a dense text decoder with a frozen ViT-G/14 perception encoder, and is built to run agent workloads locally on a single consumer GPU.
The architecture is unusual in a few ways that matter when you fine-tune it: attention repeats a [local, local, local, global] pattern with a 2048 sliding window, RoPE is applied to the local layers only (the global layers are NoPE), attention output is sigmoid-gated, and the logits are scaled and tanh-softcapped after the LM head.
Getting started
Install Axolotl from source following the installation guide.
Install Cut Cross Entropy to reduce training VRAM usage. Both configs enable it.
Run the fine-tuning:
# QLoRA, language model only (~27 GiB VRAM) axolotl train examples/muse-glimmer/qlora.yaml # QLoRA, language model + vision tower (~23 GiB VRAM) axolotl train examples/muse-glimmer/qlora-vision.yaml
Let us know how it goes. Happy finetuning! 🚀
Tips
- Fused LoRA kernels are not available for this architecture.
- We added custom Liger Kernel support for RMSNorm, the SwiGLU MLP, RoPE, and the vision tower’s LayerNorms.
- Reasoning strength is set in the system prompt (
low/medium/high/xhigh, defaulting tohigh). Keep it consistent between training and inference. - Read more on how to load your own dataset at docs.
Dataset Format
The configs use the OpenAI Messages format as described here. Image parts use {"type": "image"}, not image_url:
{
"messages": [
{
"role": "user",
"content": [
{"type": "image"},
{"type": "text", "text": "What is in this image?"}
]
},
{
"role": "assistant",
"content": "A cat sitting on a windowsill."
}
]
}To train reasoning traces, add reasoning_content alongside content on the assistant message:
{
"role": "assistant",
"reasoning_content": "The animal has pointed ears and whiskers.",
"content": "A cat sitting on a windowsill."
}Tool calling
Tool definitions live in their own dataset column, not in the messages. The default column
name is tools (override with field_tools), and it holds a list of
JSON schema function definitions:
{
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather in a city.",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string", "description": "City name"}},
"required": ["city"]
}
}
}
],
"messages": [
{"role": "user", "content": "What's the weather in Paris?"},
{
"role": "assistant",
"tool_calls": [
{"id": "c1", "type": "function",
"function": {"name": "get_weather", "arguments": {"city": "Paris"}}}
]
},
{"role": "tool", "tool_call_id": "c1", "content": "18C, cloudy"},
{"role": "assistant", "content": "It's 18C and cloudy in Paris."}
]
}The template turns that into Harmony turns with an ATEM XML call block, and adds each tool namespace to the system block’s valid-recipient list:
# Valid recipients: "self", "get_weather.*", "user".<|eot|>
<|start|>user<|message|>What's the weather in Paris?<|eot|>
<|start|>assistant to=get_weather<|message|><atem:function_calls>
<atem:invoke name="get_weather">
<atem:parameter name="city">Paris</atem:parameter>
</atem:invoke>
</atem:function_calls><|eot|>
<|start|>tool get_weather<|message|><tool_output name="get_weather">
18C, cloudy
</tool_output><|eot|>
<|start|>assistant to=user<|message|>It's 18C and cloudy in Paris.<|eot|>
Two things to watch:
argumentsmust be a dict, not a JSON string. The template callsraise_exceptionon a string, because the Jinja sandbox cannot parse one.{"city": "Paris"}, never"{\"city\": \"Paris\"}".- Use a text-only config for tool data. The multimodal collator calls
apply_chat_templatewithout passingtools, so the tool definitions never reach the system block. Dropprocessor_typeandskip_prepare_datasetso thechat_templatestrategy handles it andfield_toolsis read. That path needseot_tokens: ["<|eot|>", "<|eom|>"], since neither terminator is the tokenizer’seos_token.
The assistant tool-call turn is trained like any other assistant turn; only the tool role’s
<tool_output> is masked, since that is input the model receives rather than text it writes.