Skip to content

The model shop

The shop is the feature meant to make local models feel free rather than fiddly: instead of a catalog listing a parameter count and a download size and leaving you to guess whether it’ll actually run, it looks at your actual hardware and gives you a verdict you can act on. This page documents the honesty properties that verdict is built on. The logic described here (agent/hearth_hw.py, agent/hearth_shop.py) is built, tested, and correct today; the shop’s actual on-screen interface is not built yet. See docs/windows.md for where the project stands overall.

Fit verdicts, not vibes

Every model the shop lists gets a verdict, computed from your real detected hardware against that model’s real memory requirements, not a one-size-fits-all label. Best to worst:

VerdictWhat it means
greatFits fully in VRAM at the requested context, with roomy headroom (at least 20% of total VRAM free). Runs fast, nothing to worry about.
goodFits fully in VRAM, but headroom is tight. Runs fine today; a bigger prompt or a second loaded model could push it over.
reduced_contextDoesn’t fit at the requested context, but does at a shorter one. The verdict says exactly how short.
cpu_spilloverDoesn’t fit VRAM at any useful context, but the weights and KV cache fit in system RAM. It will run, just slowly. No speed number is predicted - see below.
wont_fitDoesn’t fit VRAM or RAM. The shop’s own rule: never recommend this model on this machine.

When nothing in the listing earns better than wont_fit on a given machine, the shop’s recommendation function returns nothing rather than headline a model that can’t actually run. An honest “nothing fits” beats a confident wrong answer.

Why KV-cache math instead of parameter count

This is the part most people, and most model catalogs, get wrong. Model weights are not the whole memory story: context length is usually what actually breaks the fit. Every token of context an attention model holds onto costs a fixed number of bytes per token, spread across every layer and every KV head, and that cost scales linearly with context length regardless of how big the weights are. A 14B model at a short context might fit comfortably in 16GB; the exact same model at a long context can need roughly twice the memory of its weights alone once the KV cache is added in.

So every built-in catalog entry carries kv_bytes_per_token, computed from that model’s attention architecture (layer count, KV-head count, head dimension) rather than from parameter count alone - the arithmetic itself is never a guess. What isn’t always known with the same confidence is the architecture figures that arithmetic runs on, and each entry says so through its kv_confidence label: published_config when the layer and head counts come straight from the model family’s published architecture, high confidence; recalled_estimate when they’re recalled from the model’s technical report rather than reverified against a live config file, good-faith but not gospel; and conservative_overestimate when the exact attention configuration isn’t confidently known at all, in which case the entry deliberately assumes the least favorable case (no grouped-query attention) rather than inventing a number - safe to over-count KV cost, never safe to under-count it. The confidence label is the honest part of this system: it exists precisely because not every number behind the math is equally certain, and it says so wherever that’s true instead of presenting every verdict with the same confidence.

The concrete payoff: on a 6GB RTX 2060, Ollama’s own default context for that card is 4096 tokens. Hearth’s calculator instead selects 16384, verified with the model actually loading at that context length, because the KV-cache arithmetic says a 7B Q4 coding model still leaves about 0.80GB of headroom there. The same model at 32768 tokens doesn’t fit at all on that card - the calculation is linear, so the ladder just stops climbing once a rung fails to clear a real safety margin.

Live listings, and a verdict per quantisation

The shop no longer ships a hardcoded list of models. search_shop() asks Hugging Face for real GGUF repositories and asks each one for its real file listing, through agent/hearth_hf.py. A search for “qwen coder” returns what is actually on the Hub, at the sizes the Hub reports.

That changes the shape of the decision. A repository does not hold “a model”, it holds five to fifteen quantisations of one set of weights, sometimes more than an order of magnitude apart in size. A single verdict for the whole repository would be meaningless, so every fit verdict is computed per quantisation, against that quantisation’s real file size, with split models summed across their parts. On a 6GB RTX 2060, one 7B coding repository spans every tier in the table above: Q2_K through Q4_K_M are great, Q5_K_M is good, Q6_K is reduced_context, Q8_0 is cpu_spillover, and FP16 is wont_fit.

The shop then makes the choice for you: the largest quantisation in the best verdict tier the repository offers. Best tier first and size second, because a quantisation that runs with room to spare beats a larger one that only just fits, and “only just fits” is precisely what a longer prompt breaks. When nothing fits in VRAM at all the size preference inverts to the smallest, which spills least. Every other quantisation stays in the listing with its own verdict, so the choice is visible rather than hidden.

One row per quantisation, not one per file. Qwen’s official GGUF repositories publish most quantisations twice, once as a single file and once split into numbered parts a couple of hundred bytes apart in total, so a listing built straight off the file tree shows two visually identical rows for nearly every quantisation and reads as a bug. The two editions collapse into one row: the single file, since it is one request with one hash to verify and nothing to reassemble, unless the split lands in a better verdict tier, which the collapse will not trade away. The other edition is not thrown away. It rides on the surviving row, fully graded, with a part count alongside it, because “this download arrives in four pieces” and “there is a version of this that fits my filesystem’s file size limit” are real questions.

The Hub reports sizes and filenames. It does not report layer counts or KV head counts, which is what the KV arithmetic above needs. So the shop resolves that figure from a table of known model families first, falling back to a size heuristic that assumes plain multi-head attention, and finally to one fixed conservative figure. The heuristic deliberately over-counts, often several-fold, for any model using grouped-query attention: over-counting produces a pessimistic verdict, under-counting produces a confident “it fits” that is wrong only after several gigabytes have been downloaded. Every entry reports which of the three sources it used and how much to trust it, in the same kv_confidence vocabulary the built-in entries use.

When Hugging Face can’t be reached

The shop degrades to a labelled fallback rather than to an exception or a blank screen: the built-in reference catalog, graded against the same hardware, so a first run with no network can still answer “what could this machine run”. It does not pretend to answer “what should I install”, because nothing can be installed without a network: every fallback entry is marked not downloadable, and the listing itself always reports ok: false with a machine-readable error kind, so a caller that never looks at the source field still cannot mistake the fallback for live Hub data.

Why there’s no predicted tokens per second

Deliberately out of scope: predicting throughput from memory bandwidth and parameter count. That kind of prediction breaks badly on mixture-of-experts models, where the active parameters used per token are a small fraction of the model’s total weights - a naive formula built on total parameter count would call an MoE model slow when it’s actually fast, and would steer users away from exactly the local coding models most capable of helping them. Predicting a number you know breaks on an entire model family is worse than not predicting one at all, so the shop doesn’t. The plan instead is to measure real throughput on the user’s own machine and show that, once the shop has an interface to show it in.

The VRAM number behind a verdict might be a guess

Every verdict is graded against a VRAM reading, and that reading is not always precise. On Windows, nvidia-smi gives an exact figure when it’s available. When it isn’t, detection falls back to PowerShell or the deprecated wmic, both of which read Win32_VideoController.AdapterRAM - a signed 32-bit field that misreports any card above roughly 4GB, sometimes reading low, sometimes reading a wrapped or even negative value. Every reading that comes from that fallback path is marked approximate, and the verdict logic checks that flag explicitly: a result that would otherwise be great on an approximate reading is downgraded to good, because a confident “great” built on a guessed VRAM number isn’t actually great. Anywhere the shop shows you a verdict, it’s obligated to also say when the number behind it is a guess.

What’s built, what’s still just data

hearth_hw.probe() and hearth_shop.catalog_with_verdicts() are real, tested, callable functions today: pure detection and pure arithmetic, no network calls, no writes. hearth_shop.search_shop(), hearth_shop.repo_quants() and hearth_shop.recommend_live() are real and tested too, and they do reach the network, through hearth_hf; they never raise on a network failure, they return a structured result. hearth_hf can also download a chosen file, resumably and with its hash verified.

What doesn’t exist yet is anything to click: there is no shop screen, no download button, no progress bar. The desktop server’s GET /models route still serves the offline reference catalog rather than a live search. See docs/windows.md for the full state of the project.