Skip to content
Back to the blog
9 min read

An AI model on one graphics card: invoices on your own server, up to three times faster

August brought two open models that change the maths for companies protective of their documents: Meta's Muse Glimmer‑30B and the Qwen team's Qwen3.8‑27B, both under Apache 2.0. An invoice can be read on a server in your own building instead of being sent to an outside API. Below are four short listings: serving the model, extraction with a totals check, serving with the drafter, and measuring the speedup.

TutorialOpen sourceDeployment

Two models, one licence

Meta released Muse Glimmer on 10 August. It is a dense model of about 29.6 billion parameters, roughly 1.8 billion of them a vision encoder, so it reads scans too. The context window exceeds 131,000 tokens. The model card reports 76.0 on SWE‑Bench Verified and 83.5 on GPQA Diamond. The same month brought Qwen3.8‑27B: dense, 27 billion parameters, 262,144 tokens of context, 89.2 on GPQA Diamond and 61.7 on SWE-bench Pro.

Do not compare 76.0 with 61.7. SWE-bench Verified and SWE-bench Pro are different task sets. On SWE-bench Pro, Glimmer's card gives 51.2, but Qwen scored its model on a version of the set with corrected tasks, so even that pair is not a clean comparison. According to its card, Glimmer's GPQA score comes from an Artificial Analysis run, not the same setup as Qwen's. Both models need testing on your own documents, and the listings below work with either once you change the model name.

Apache 2.0 allows commercial use, modification and running on your own hardware with no licence fees. The one condition: when you redistribute, you keep the licence text and the attribution notices. Record the licence with the download date and pin a specific repository revision, because a different set of weights can appear under the same name. Every revision in the listings below is pinned to a commit hash.

How much memory you really need

Meta says that quantised to roughly 4 bits the language model alone takes under 20 GB, and together with the vision encoder and working memory it fits a 24 or 32 GB budget. In vLLM the number looks different. The 4‑bit NVFP4 build used by the official vLLM recipe weighs 25.42 GB, because the embeddings, the output layer and the whole vision encoder stay at full precision. On an RTX 5090 the recipe measured 28.8 GB used out of 32.6 GB at a 131,072‑token context, and 68.8 tokens per second. NVFP4 kernels run only on Blackwell-generation cards.

The practical requirement is therefore one card with 32 GB of memory, such as the RTX 5090, which launched at a suggested price of $1,999. The smaller figure in Meta's announcement refers to its own K‑Quant quantisations, not to the vLLM build. Meta ran its speed tests on K‑Quant‑17GB, and on the RTX 5090 in llama.cpp.

Bash

pip install "vllm==0.28.0"
vllm serve Inferact/Muse-Glimmer-30B-NVFP4-W4A4 \
  --revision d35cb79050f419c457611b1cee5c5d15b176f285 \
  --tokenizer-revision d35cb79050f419c457611b1cee5c5d15b176f285 \
  --served-model-name muse-glimmer \
  --max-model-len 131072 \
  --enable-auto-tool-choice --tool-call-parser muse_glimmer \
  --reasoning-parser muse_glimmer \
  --generation-config auto
Listing 1. Needs Linux with a 32 GB NVIDIA Blackwell card and vLLM 0.28.0, the first release to support Muse Glimmer (26 August 2026). The flags come from the vLLM recipe; we checked them against the vLLM 0.28.0 source and both revisions against the Hugging Face API. Without a GPU we did not run this command, only checked its syntax.

Invoice to JSON, with a totals check

The server speaks the OpenAI protocol, so the client is the ordinary openai SDK pointed at localhost. A pydantic class describes the invoice: seller NIP (the Polish tax number), invoice number, issue date, net, VAT, gross and line items. Amounts are Decimal so the comparison is exact to the grosz, with no floating-point rounding.

Python

import re, sys
from datetime import date
from decimal import Decimal
from openai import OpenAI
from pydantic import BaseModel, ValidationError

class Line(BaseModel):
    description: str
    quantity: Decimal
    net: Decimal

class Invoice(BaseModel):
    seller_nip: str
    number: str
    issue_date: date
    net: Decimal
    vat: Decimal
    gross: Decimal
    lines: list[Line]

INVOICE = """FAKTURA VAT nr FV/2026/08/117, data wystawienia 28.08.2026
Sprzedawca: Przykładowa Firma sp. z o.o., NIP 123-456-00-20
1. Wdrożenie modułu, 1 szt., netto 4 000,00 zł, VAT 23%
2. Licencja roczna, 2 szt., netto 900,00 zł, VAT 23%
Razem netto 4 900,00 zł, VAT 1 127,00 zł, brutto 6 027,00 zł"""
client = OpenAI(base_url="http://localhost:8000/v1", api_key="local")
reply = client.chat.completions.create(
    model="muse-glimmer",
    messages=[{"role": "system", "content": "Reasoning strength: low"},
              {"role": "user", "content": "Return this invoice as JSON only:\n" + INVOICE}],
    response_format={"type": "json_schema", "json_schema": {
        "name": "invoice", "schema": Invoice.model_json_schema()}},
    temperature=1.0, top_p=0.95, extra_body={"top_k": 64}, max_tokens=4000)

text = reply.choices[0].message.content or ""
found = re.search(r"\{.*\}", text, re.DOTALL)  # tolerate prose or a code fence around it
try:
    invoice = Invoice.model_validate_json(found.group(0) if found else "")
except ValidationError as err:
    sys.exit(f"Reply is not a valid invoice:\n{err}")
cents = Decimal("0.01")
if (invoice.net + invoice.vat).quantize(cents) != invoice.gross.quantize(cents):
    sys.exit(f"Totals do not add up: {invoice.net} + {invoice.vat} != {invoice.gross}")
print(invoice.model_dump_json(indent=2))
Listing 2. Needs Python 3.10 or later, pip install openai==3.8.0 (pydantic comes with it) and the server from listing 1. We ran it against a local stub of an OpenAI-compatible server with four replies: clean JSON, JSON in a code block surrounded by prose, a gross total one grosz too high, and a reply with no JSON. The first two produced the correct result and the last two exited with a clear error. We did not run a real model.

The script sends response_format with the schema but does not trust it. In vLLM 0.28.0 with the muse_glimmer reasoning parser the schema is skipped without any warning: the request succeeds, but the model answers in free text that ignores the schema. Issue 52594 has described this since 17 August and was still open at the time of writing. So the script pulls the JSON object out of the reply itself, validates it against the schema and checks net plus VAT against gross. Anything that fails goes to a person, not to accounting.

The sampling parameters come from the vLLM recipe: temperature 1.0, top_p 0.95, top_k 64. The recipe advises against greedy decoding for this model and notes that even at temperature 0 identical requests returned different lengths. Cheap next checks are the NIP checksum and whether the line items add up to the net amount.

The drafter: the same model, faster

Speculative decoding adds a small helper model, the drafter. The drafter proposes the next few tokens, and the large model checks them all in one pass and keeps the ones it would have produced itself. DFlash, published in February by Chen, Liang and Liu, proposes a whole block of tokens in a single pass using block diffusion. The authors report more than sixfold lossless acceleration, and vLLM has supported DFlash since version 0.20.0.

Glimmer ships its own DFlash drafter, meta-models/Muse‑Glimmer‑30B‑assistant, at 5.11 GB. It predicts blocks of 16 tokens, so the number of speculative tokens is fixed at 15. With greedy decoding the output is, in principle, identical token for token. With sampling the drafter preserves the model's probability distribution, so quality is the same, although two runs will not produce the same characters.

Bar chart of the DFlash drafter speedup reported by Meta: 3.1 times on NVIDIA RTX 5090, 1.8 times on Apple M5 Max, 1.5 times on Apple M4 Max.
Meta's measurement for the K‑Quant‑17GB quantisation with a quantised drafter. On the RTX 5090 the model card gives a rise from 74.9 to 233.4 tokens per second. Meta measured at batch size 1, one request at a time, with greedy decoding.Source: Meta AI Research, 10 August 2026Open full size

In vLLM the drafter does not fit next to the model on a single 32 GB card: the vLLM recipe records running out of memory at startup. On two RTX 5090 cards the same recipe measured a rise from 68.8 to about 240 tokens per second. Note that this compares one card with two, not just the effect of the drafter.

Bash

# Two 32 GB cards: the 5.11 GB drafter does not fit next to the model on one
vllm serve Inferact/Muse-Glimmer-30B-NVFP4-W4A4 \
  --revision d35cb79050f419c457611b1cee5c5d15b176f285 \
  --tokenizer-revision d35cb79050f419c457611b1cee5c5d15b176f285 \
  --served-model-name muse-glimmer \
  --tensor-parallel-size 2 \
  --max-model-len 131072 --max-num-seqs 32 \
  --enable-auto-tool-choice --tool-call-parser muse_glimmer \
  --reasoning-parser muse_glimmer \
  --generation-config auto \
  --speculative-config '{"method": "dflash",
    "model": "meta-models/Muse-Glimmer-30B-assistant",
    "revision": "e8192f3a8f617f74be2ce220360c89ef4789f39f",
    "num_speculative_tokens": 15}'
Listing 3. Same requirements as listing 1, but two 32 GB cards. The drafter configuration and the limit of 32 concurrent sequences come from the vLLM recipe. We checked the JSON in --speculative-config and the shell syntax locally, but did not run the command.

When the speedup stops helping

The drafter uses compute the card leaves idle when it serves one request and mostly waits on memory. With many concurrent requests that spare compute shrinks. In the DFlash paper, on Qwen3‑8B and HumanEval tasks, the speedup falls from 4.2 times with one request to 2.4 times with 32 concurrent ones. Headline numbers are almost always for a single request: LMSYS reports more than 4.3 times the throughput for a large Qwen 3.5 model at exactly that setting.

  • The kind of text matters. In the same paper the drafter lands 8.01 tokens at a time on average on maths tasks and 6.50 on code. Your invoices will give a different number.
  • The drafter takes memory that would otherwise go to the context cache, which sets how many requests you can serve at once.
  • Meta's figures describe a single stream on a developer's machine. A server handling a whole department is a different load profile.

Python

import asyncio, time
from openai import AsyncOpenAI

client = AsyncOpenAI(base_url="http://localhost:8000/v1", api_key="local")
PROMPTS = [f"Opisz krok {i} zamknięcia miesiąca w małej firmie." for i in range(1, 17)]

async def ask(prompt: str, gate: asyncio.Semaphore) -> int:
    async with gate:
        reply = await client.chat.completions.create(
            model="muse-glimmer", messages=[{"role": "user", "content": prompt}],
            max_tokens=512, temperature=1.0, top_p=0.95, extra_body={"top_k": 64})
        return reply.usage.completion_tokens

async def main() -> None:
    await ask("Rozgrzewka.", asyncio.Semaphore(1))  # first request warms up the server
    for concurrency in (1, 8):
        gate = asyncio.Semaphore(concurrency)
        start = time.perf_counter()
        tokens = sum(await asyncio.gather(*(ask(p, gate) for p in PROMPTS)))
        print(f"concurrency {concurrency}: {tokens / (time.perf_counter() - start):.1f} tok/s")

asyncio.run(main())
Listing 4. Needs pip install openai==3.8.0 and a running server from listing 1 or 3. We ran it against the same stub with a 50 ms delay and a fixed 100 tokens per reply, to check that it runs and counts correctly. The numbers it printed described the stub, not a graphics card.

A measurement plan for your hardware

Five steps before you decide

  1. 01Baseline: the server from listing 1, run listing 4, record tokens per second at 1 and 8 concurrent requests.
  2. 02A fair comparison: if the drafter needs a second card, also measure the model without the drafter on two cards, with --tensor-parallel-size 2. Otherwise you are measuring the second card, not the drafter.
  3. 03With the drafter: the server from listing 3, the same script. Divide by the baseline separately for 1 and for 8 requests. Put excerpts from your own documents in PROMPTS, because the drafter's hit rate depends on the text.
  4. 04Quality: run 50 to 100 real invoices through listing 2 with and without the drafter. The number rejected by the totals check should match within random variation.
  5. 05Decision: for a queue of documents processed one after another, the drafter usually pays off. If the gain at 8 requests is small and the number of users is growing, give that memory to serving more requests instead.

Sources

  1. 01Meta AI Research, Introducing Muse Glimmerpublished 10 August 2026
  2. 02Hugging Face, model card meta-models/Muse‑Glimmer‑30Bpublished 10 August 2026
  3. 03Hugging Face, Meta is back with Muse Glimmerpublished 10 August 2026
  4. 04Hugging Face, model card Qwen/Qwen3.8‑27Blast updated 14 August 2026
  5. 05vLLM recipes, Muse‑Glimmer‑30Brevision of 31 August 2026
  6. 06vLLM, release v0.28.0published 26 August 2026
  7. 07vLLM issue 52594, muse_glimmer cannot combine reasoning with structured outputsopened 17 August 2026
  8. 08Chen, Liang, Liu, DFlash: Block Diffusion for Flash Speculative Decodingv1 5 February 2026, v2 28 May 2026
  9. 09vLLM Blog, Speculators v0.5.0: DFlash Support and Online Trainingpublished 28 May 2026
  10. 10LMSYS, The next generation of speculative decoding: DFlash and Spec V2published 15 June 2026
  11. 11NVIDIA, GeForce RTX 50 Series announcementpublished 6 January 2025

Keep reading

6 min read

Jev: a model that returns a decision, not a sentence

TypeSafe AI released a model on 15 September that writes no text at all. It hands back a chosen option and a probability, costs $0.042 per million input tokens, and charges nothing for output. Here is what survives once the marketing is subtracted.

Read
7 min read

Agent skills: why five beat a hundred

With five skills in the pool, 29.6% of the skills an agent actually uses are the right one; with a hundred, 3.3%. And in August a public skills registry served clones that stole SSH keys. Four rules for a team working with agents.

Read

Show us the process that costs your team the most time

Describe it in a few sentences. We’ll tell you whether it can be improved, roughly what that would cost, and whether it needs AI at all.