Open-weight Β· runs on your device

Muse Glimmer β€” local agent console

Meta's Muse Glimmer 30B is Apache-2.0 and small enough to run on one consumer GPU. This page is an agent scaffold for it that has no backend at all: it renders the model's prompt format in your browser, streams from a server on your own machine, and runs every tool in this tab. The support records it reasons over β€” names, emails, card fragments, a live-looking API key β€” never leave your device, which is the entire reason to run open weights locally.

30B Β· ~29.6B params Apache 2.0 16.8 GB @ 4-bit prompt format: 24/24 byte-exact 1 real turn @ 0.26 tok/s, no GPU

1 Β· Connect your local runtime

This page talks to http://localhost. Browsers allow that from an HTTPS page because loopback is a trustworthy origin β€” but your runtime still has to permit the cross-origin request. Nothing is sent anywhere else; there is no server behind this site to send it to.

2 Β· Capability tracks

Each track is a scripted scenario over the local workspace, scored by reading the run's action ledger β€” which tools were called, with what arguments, and what changed β€” rather than by asking a model to grade the prose. You can read every prompt before running anything.

3 Β· Protocol inspector

No model required. This is the exact byte string this page would send to a completion endpoint for the selected track β€” channel tokens, tool preamble, ATEM examples and all. It is generated by the same code path the agent loop uses.


  

Why the wire format is the interesting part

Muse Glimmer does not emit OpenAI-style JSON tool calls. Its chat template builds a channel-token transcript and asks for calls in a dialect the template itself calls ATEM:

<|start|>assistant to=self<|message|>Search first, then escalate.<|eom|>
<|start|>assistant to=workspace.search_tickets<|message|><atem:function_calls>
<atem:invoke name="workspace.search_tickets">
<atem:parameter name="query">billing</atem:parameter>
<atem:parameter name="limit">20</atem:parameter>
</atem:invoke>
</atem:function_calls><|eot|>

Three consequences that cost real debugging time:

A trap worth naming. The tool preamble the template injects into the system message contains two complete, syntactically valid ATEM blocks β€” the $FUNCTION_NAME syntax sketch and the example_tool_name worked example. Anything that scans a whole prompt for tool calls, rather than only the completion, will report two calls the model never made. The conformance suite pins this as a test so it cannot regress into a surprise.

Conformance against the real chat template

The renderer in protocol/atem.js is a hand-written port. To keep it honest, every fixture is also rendered by the actual Jinja template shipped in meta-models/Muse-Glimmer-30B, under the same environment transformers.apply_chat_template builds, and the two outputs are compared byte for byte.

Loading results…

Regenerate with python3 conformance/render_reference.py && node conformance/run.mjs.

Setup

You need the weights and a server. Roughly 17–20 GB on disk, 24 GB of VRAM for the smaller quant.

Ollama

ollama pull muse-glimmer:30b-q4_
# CORS: the page's origin must be allowed
OLLAMA_ORIGINS="https://carlosrymer.github.io" ollama serve

llama.cpp

llama-server -hf meta-models/Muse-Glimmer-30B-GGUF \
  --host 127.0.0.1 --port 8080 \
  --spec-type draft-dflash --spec-draft-n-max 15   # optional: DFlash speculative decoding

LM Studio

Load Muse-Glimmer-30B-GGUF, start the local server on port 1234, and enable CORS in the server tab.

Sampling follows the model card: temperature 1.0, top_p 0.95, top_k 64. Reasoning strength is set through the system prompt, which is why this page renders the prompt itself rather than delegating to the runtime.

What I verified, and what I did not

I built this on a cloud container with no GPU, 15 GiB of RAM and 4 cores, against a 15.61 GiB quant. I expected that to be a hard stop. It wasn't quite β€” llama.cpp mmaps the file, so the model runs with its weights paged off disk. Brutally slowly: 6.1 tok/s prefill, 0.26 tok/s generation, 8.6 minutes for a 117-token turn. But it runs. Worth saying plainly, because "runs on consumer hardware" and "runs on a machine with no GPU at all" are different claims, and only the first is Meta's.

So I have exactly one real completion, and it did its job: given a prompt rendered by this page's own renderPrompt(), the model reasoned in the to=self channel, chose the right tool, and emitted well-formed ATEM with correctly typed arguments. Those bytes are committed verbatim in conformance/real-output/ and are now a test case.

It caught a bug 24 synthetic fixtures had missed. My reasoning-channel regex required an assistant prefix and an <|eom|> terminator. Real output has neither β€” the completion opens with a bare  to=self<|message|> because the role token lives in the prompt, and the model switched straight to <|start|>assistant to=… without closing the channel. The failure mode was exactly the one described above: private chain-of-thought, raw markup and all, rendered to the user as the answer.

So, to be exact about the split:

The tracks are built so that when you run them on your own hardware, the scoring is mechanical and the answer is yours, not mine. No model responses on this page are simulated or pre-recorded β€” if you have not connected a runtime, you see prompts, not pretend output.