Review · updated OCT 11

Muse Glimmer 30B review: one of the best local models we've tested

It scored 94 out of 100, #3 of 56. It solved 27 of 30 coding jobs and scored 98 on reading documents. Runs on a 24 GB graphics card or a Mac with 32 GB.

The short version
  • Muse Glimmer 30B is a free model from meta that you can run on your own computer. In our tests it's one of the best local models we've tested: 94 out of 100, #3 of 56.
  • It solved 27 of 30 coding jobs and scored 98 on reading documents. On our hardest tasks it scored 89.
  • Runs on a 24 GB graphics card or a Mac with 32 GB.

Coding

Our coding test is 30 programming jobs, from small ones like reading time durations or cleaning up messy data to harder ones like a config-file parser or a double-entry ledger. We run each answer against tests the model never sees, and a job only counts if everything passes. Muse Glimmer 30B got 27 of 30 right. The best local coders solved 29 of 30.

Reading documents

The second test hands the model things like an expense claim thread, a pay stub or an insurance statement, and asks for specific numbers and dates. Many questions need a bit of math, or noticing a correction further down the email. Muse Glimmer 30B scored 98; the best model scored 100.

TestScorePublic questionsSecret questions
Coding908691
Reading documents989898
Decisions9910099

On the 18 hardest tasks (included in the scores above) it scored 89. This number separates the top models.

We tested the full-size version online. The smaller versions you download usually score a little lower.

Can your computer run it?

You can download Muse Glimmer 30B in several sizes. Smaller versions fit on more computers and lose a little quality, so pick the biggest one that fits.

Your computerRuns it?Version to download
Graphics card, 8 GBNo–
Graphics card, 12 GBNo–
Graphics card, 16 GBNo–
Graphics card, 24 GBYesQ5
Graphics card, 32 GBYesQ6
Mac, 16 GBNo–
Mac, 24 GBNo–
Mac, 32 GBYesQ4
Mac, 48 GBYesQ8
Mac, 64 GBYesQ8
Mac, 96 GBYesQ8
Mac, 128 GBYesQ8

For normal-length chats. Long documents need more memory. Q4 or Q8? · How we worked this out

How to try it

Search for "Muse Glimmer 30B GGUF" in LM Studio or on Hugging Face and pick the version that fits your computer. New to this? Start with our Ollama guide.

Its actual answers

What Muse Glimmer 30B wrote for our public questions, word for word.

Coding: INI parser (hard) Didn't work
Coding: TTL LRU (hard) Worked
```python
from collections import OrderedDict


class TTLCache:
    def __init__(self, capacity: int, ttl: float, clock):
        self.capacity = capacity
        self.ttl = ttl
        self.clock = clock
        self._data = OrderedDict()          # key -> (value, expiry)
        self.hits = 0
        self.misses = 0
        self.evictions = 0
        self.expirations = 0

    def _now(self):
        return self.clock()

    def _purge_expired(self):
        now = self._now()
        expired = [k for k, (_, exp) in self._data.items() if now >= exp]
        for k in expired:
            del self._data[k]
            self.expirations += 1

    def put(self, key, value):
        if self.capacity <= 0:
            return
        now = self._now()
        if key in self._data:
            # replace existing key
            self._data[key] = (value, now + self.ttl)
            self._data.move_to_end(key)
            return

        # new key
        if len(self._data) >= self.capacity:
            self._purge_expired()
            if len(self._data) >= self.capacity:
                self._data.popitem(last=False)
                self.evictions += 1

        self._data[key] = (value, now + self.ttl)

    def get(self, key, default=None):
        if self.capacity <= 0:
            self.misses += 1
            return default
        now = self._now()
        if key in self._data:
            val, exp = self._data[key]
            if now >= exp:
                del self._data[key]
                self.expirations += 1
                self.misses += 1
                return default
            self.hits += 1
            self._data.move_to_end(key)
            return val
        self.misses += 1
        return default

    def __len__(self):
        now = self._now()
        cnt = 0
        for _, (_, exp) in self._data.items():
            if now < exp:
                cnt += 1
        return cnt

    def stats(self):
        return {
            "hits": self.hits,
            "misses": self.misses,
            "evictions": self.evictions,
            "expirations": self.expirations,
        }
```
Coding: Parse duration Worked
```python
def parse_duration(s: str) -> int:
    t = s.strip()
    if not t:
        raise ValueError("empty duration")

    n = len(t)
    i = 0
    values = {}
    last_idx = -1
    unit_to_idx = {"h": 0, "m": 1, "s": 2}

    while i < n:
        while i < n and t[i].isspace():
            i += 1
        if i >= n:
            break

        start = i
        while i < n and t[i].isdigit():
            i += 1
        if start == i:
            raise ValueError("number expected")
        num_str = t[start:i]
        # int conversion also rejects empty / too large? Python handles
        num = int(num_str)

        if i >= n:
            raise ValueError("unit expected")
        unit_char = t[i]
        i += 1
        if not unit_char.isalpha():
            raise ValueError("unit expected")
        unit = unit_char.lower()
        if unit not in unit_to_idx:
            raise ValueError(f"unknown unit {unit_char}")

        idx = unit_to_idx[unit]
        if unit in values:
            raise ValueError("repeated unit")
        if idx <= last_idx:
            raise ValueError("units out of order")

        values[unit] = num
        last_idx = idx

    total = 0
    total += values.get("h", 0) * 3600
    total += values.get("m", 0) * 60
    total += values.get("s", 0)
    return total
```
Coding: Summarize ranges Worked
```python
def summarize_ranges(nums: list[int]) -> str:
    if not nums:
        return ""
    arr = sorted(set(nums))
    parts = []
    i = 0
    n = len(arr)
    while i < n:
        start = arr[i]
        j = i
        while j + 1 < n and arr[j + 1] == arr[j] + 1:
            j += 1
        run_len = j - i + 1
        if run_len >= 3:
            parts.append(f"{start}..{arr[j]}")
        else:
            parts.extend(str(arr[k]) for k in range(i, j + 1))
        i = j + 1
    return ",".join(parts)
```
Coding: Top customers Worked
```python
import re
from typing import List, Tuple, Dict

def top_customers(rows: list[dict], n: int) -> list[tuple[str, float]]:
    data: Dict[str, list] = {}  # key -> [display_name, total]

    for row in rows:
        if not isinstance(row, dict):
            continue

        raw_name = row.get("customer")
        if raw_name is None:
            continue
        name = str(raw_name).strip()
        if not name:
            continue
        key = name.lower()

        raw_amount = row.get("amount")
        if raw_amount is None:
            continue
        amt_str = str(raw_amount)

        cleaned = re.sub(r"[^0-9\.\-]", "", amt_str)
        if not cleaned or cleaned in ("-", ".", "-.", ".-"):
            continue
        try:
            amt = float(cleaned)
        except ValueError:
            continue

        if key not in data:
            data[key] = [name, 0.0]
        data[key][1] += amt

    items: List[Tuple[str, float]] = []
    for display_name, total in data.values():
        total_rounded = round(total, 2)
        items.append((display_name, total_rounded))

    items.sort(key=lambda x: (-x[1], x[0]))
    if n < 0:
        return []
    return items[:n]
```
Coding: Slugify Worked
```python
import unicodedata
import re

def slugify(title: str, max_len: int = 50) -> str:
    # 1. transliterate accented characters to ASCII, drop others
    normalized = unicodedata.normalize('NFKD', title)
    ascii_str = normalized.encode('ascii', 'ignore').decode('ascii')
    # 2. lowercase
    lowered = ascii_str.lower()
    # 3. replace non-alphanumeric runs with '-', strip edges
    slug = re.sub(r'[^a-z0-9]+', '-', lowered)
    slug = slug.strip('-')
    if not slug:
        return ''
    if len(slug) <= max_len:
        return slug

    words = slug.split('-')
    # 4. hard cut if first word too long
    if len(words[0]) > max_len:
        return words[0][:max_len]

    # keep longest prefix of whole words within max_len
    parts = []
    cur_len = 0
    for w in words:
        add_len = len(w) if not parts else 1 + len(w)
        if cur_len + add_len <= max_len:
            parts.append(w)
            cur_len += add_len
        else:
            break
    result = '-'.join(parts).rstrip('-')
    return result
```
Coding: Token bucket Worked
```python
class TokenBucket:
    def __init__(self, capacity: float, refill_per_sec: float, clock):
        self._capacity = float(capacity)
        self._refill_per_sec = float(refill_per_sec)
        self._clock = clock
        self._tokens = float(capacity)
        self._last_time = float(clock())

    def _refill(self):
        now = float(self._clock())
        elapsed = now - self._last_time
        if elapsed > 0:
            self._tokens = min(self._capacity, self._tokens + elapsed * self._refill_per_sec)
            self._last_time = now

    def allow(self, cost: float = 1) -> bool:
        self._refill()
        cost = float(cost)
        if self._tokens >= cost:
            self._tokens -= cost
            return True
        return False

    @property
    def tokens(self) -> float:
        self._refill()
        return self._tokens
```
Decisions: Refund window (hard) 100% right
{"outcome": {"full_refund": 0.02, "partial_refund": 0.01, "store_credit": 0.96, "nothing": 0.01}, "defective": {"true": 0.05, "false": 0.95}}
Decisions: Incident matrix (hard) 100% right
{
  "severity": {
    "0": 0.01,
    "1": 0.92,
    "2": 0.06,
    "3": 0.01
  },
  "page": {
    "true": 0.07,
    "false": 0.93
  }
}
Decisions: Tool followup (hard) 100% right
{"tool": {"web_search": 0.01, "calculator": 0.01, "calendar": 0.96, "email": 0.01, "none": 0.01}, "confirm": {"true": 0.95, "false": 0.05}}
Decisions: Legit security alert (hard) 100% right
{"phishing": {"true": 0.02, "false": 0.98}, "action_needed": {"true": 0.05, "false": 0.95}}
Decisions: Meeting slot (hard) 100% right
{"slot": {"A": 0.0, "B": 1.0, "C": 0.0, "D": 0.0}, "raj_last": {"true": 1.0, "false": 0.0}}
Decisions: Review mixed (hard) 100% right
{"hardware": {"true": 0.99, "false": 0.01}, "support": {"true": 0.88, "false": 0.12}}
Decisions: Support checkout down 100% right
{"department": {"billing": 0.04, "technical": 0.94, "account": 0.01, "shipping": 0.005, "sales": 0.005}, "urgency": {"0": 0.0, "1": 0.0, "2": 0.02, "3": 0.98}, "outage": {"true": 0.98, "false": 0.02}}
Decisions: Refund wrong plan 100% right
{"department": {"billing": 0.96, "technical": 0.01, "account": 0.01, "shipping": 0.01, "sales": 0.01}, "refund": {"true": 0.99, "false": 0.01}, "tone": {"frustrated": 0.02, "calm": 0.98}}
Decisions: Moderation doxxing 100% right
{"policy": {"none": 0.01, "harassment": 0.98, "hate": 0.005, "spam": 0.003, "self_harm": 0.002}, "personal_info": {"true": 0.99, "false": 0.01}}
Decisions: Route calendar 100% right
{"tool": {"web_search": 0.02, "calculator": 0.01, "calendar": 0.92, "email": 0.03, "none": 0.02}, "confirm": {"true": 0.96, "false": 0.04}}
Decisions: Doc invoice missing due 100% right
{"doc_type": {"invoice": 0.99, "resume": 0.0025, "contract": 0.0025, "bank_statement": 0.0025, "other": 0.0025}, "missing_due_date": {"true": 0.97, "false": 0.03}}
Decisions: Phishing paypal 100% right
{"phishing": {"true": 0.99, "false": 0.01}, "risk": {"0": 0.001, "1": 0.001, "2": 0.004, "3": 0.994}}
Decisions: Pii ssn email 100% right
{
  "data_kind": {
    "none": 0.001,
    "contact": 0.005,
    "financial": 0.004,
    "government_id": 0.985,
    "health": 0.005
  },
  "sensitive": {
    "true": 0.995,
    "false": 0.005
  }
}
Decisions: Review mixed 100% right
{"sentiment": {"positive": 0.03, "neutral": 0.05, "negative": 0.92}, "defect": {"true": 0.98, "false": 0.02}, "recommend": {"true": 0.02, "false": 0.98}}
Documents: Saas escalator (hard) 100% right
{
  "year2_price_per_seat_month": 47.25,
  "year3_price_per_seat_month": 47.25,
  "year1_invoice": 58320.00,
  "year2_invoice": 61236.00,
  "addon_months_billed": 6,
  "addon_invoice": 38556.00,
  "year3_invoice": 134946.00,
  "year3_discount_percent": 15,
  "total_contract_value": 293058.00,
  "contract_end_date": "2027-02-28"
}
Documents: Expense thread 100% right
{
  "employee_id": "EMP-20417",
  "destination_city": "Lisbon",
  "trip_start": "2025-02-24",
  "trip_end": "2025-02-27",
  "approved_items": [
    {
      "date": "2025-02-24",
      "category": "airfare",
      "amount_usd": 1184.6
    },
    {
      "date": "2025-02-24",
      "category": "ground_transport",
      "amount_usd": 38.88
    },
    {
      "date": "2025-02-25",
      "category": "meals",
      "amount_usd": 229.39
    },
    {
      "date": "2025-02-26",
      "category": "lodging",
      "amount_usd": 466.56
    },
    {
      "date": "2025-02-27",
      "category": "ground_transport",
      "amount_usd": 44.82
    }
  ],
  "rejected_item_count": 1,
  "per_diem_days": 3,
  "per_diem_usd": 195,
  "total_reimbursable_usd": 2159.25,
  "approver_email": "priya.raman@corvane.com"
}
Documents: Lease amendment 100% right
{"tenants":["Marcus Lin","Sofia Lin"],"landlord":"Ridgeline Property Group LLC","zip":"97205","lease_end":"2025-11-30","original_monthly_rent":2150,"monthly_rent_from_2025_06_01":2236,"late_fee_from_2025_06_01":111.8,"security_deposit":2150,"total_pet_deposits":800,"total_monthly_payment_july_2025":2306,"move_in_payment":4700}
Documents: Ticket SLA 92% right
{"ticket_id":"48213","account_id":"ACC-7731","open_issue":"inventory_sync","resolved_issues":["billing_address","invoice_pdf"],"affected_orders":["SO-99812","SO-99820","SO-99827"],"priority":"P2","sla_due_local":"2025-09-15T15:30","sla_due_utc":"2025-09-15T20:30:00Z","reissued_invoice":"INV-2025-0812"}
Documents: Sales footnotes 100% right
{"q3_total_usd":15346000,"q2_total_usd":14464000,"q2_central_originally_reported_usd":3047000,"q2_to_q3_change_pct":6.1,"top_region_q3":"East","fastest_growing_region_q1_to_q3":"International","regions_declining_q2_to_q3":["East"],"international_q3_organic_usd":1731000,"west_excluding_mountain_q3_usd":4201000}

Size: 30B parameters. First tested OCT 11.

Models that scored about the same

Comments

Sign in with GitHub to comment. Spam and abuse are hidden automatically.