Recommend by the owner's memory tiers (RAM + VRAM): 40 GB Swift 1.5 IQ2_XS with Gyro-M named, 48 GB IQ3_XXS, 64 GB IQ3_S; MODELS.md Pick by memory

This commit is contained in:
Niko1221 committed 2026-10-19 08:26:54 +02:00
1 parent 7e3a9fec71
commit aa82a88639
8 files changed
+106 -29

No files matched your search

+1 -1
View File
@@ -22,7 +22,7 @@ There are three ways to install it. Take the first one unless you have a reason
| | |
| --- | --- |
| **GPU** | **NVIDIA** RTX 20, 30, 40 or 50 series, **12 GB VRAM or more** (8 GB runs, slowly). Measured on an RTX 5070 and an RTX 3090; RTX 20 (Turing, since 0.1.27) was tested by a contributor on an RTX 2070. **AMD** Radeon RX 7900 XT / XTX, RX 9070 / 9070 XT and Radeon AI PRO R9700 (validated), RX 7800 XT / 7700 XT and RX 9060 XT (validated by their owners), RX 6800 / 6900 series (community-reported), with 12 GB of VRAM or more. See [AMD cards](#amd-cards). |
| **RAM** | Enough for the size you pick ([which model](MODELS.md#pick-by-ram)); **64 GB** runs every size. A big GPU makes up for less RAM - the [low-RAM mode](MODELS.md#a-big-graphics-card-and-little-ram). |
| **RAM** | Enough for the size you pick ([which model](MODELS.md#pick-by-memory)); **64 GB** runs every size. A big GPU makes up for less RAM - the [low-RAM mode](MODELS.md#a-big-graphics-card-and-little-ram). |
| **CPU** | x86-64 with AVX2 (any Intel/AMD desktop CPU from the last ~8 years). AVX-512 (Ryzen 7000/9000) is a bit faster. Older CPUs without AVX2 are experimental and slow: [Older CPUs](#older-cpus-experimental). |
| **Disk** | The app and its engine take about 1-3 GB. ~70-80 GB free for the model, ~6 GB for the MTP layer (+1 GB with images). Q2_0 on an AVX-512 CPU also writes a one-time ~40 GB copy of its experts for the fast CPU kernel. On Linux with an AMD card, ROCm takes ~10 GB more when setup installs it. An NVMe SSD is strongly recommended: it makes the first start much faster. |
| **OS** | Windows 10/11, Linux (Ubuntu 22.04/24.04 and Debian 12/13 get everything installed automatically; the app also runs on Fedora 44), or a Mac with Apple Silicon (M1 or newer) and macOS 15 or newer ([Mac](MAC.md)). |
+20 -12
View File
@@ -6,23 +6,31 @@ makers' compressions). It also runs a smaller model, [Qwen3.6-35B-A3B](#qwen36-3
(`strata launch`) and the installer recommend one for your PC; this page explains the choice. Back to the
[README](../README.md).
> **On this page:** [Pick by RAM](#pick-by-ram) · [Speed](#how-fast-is-each-size) · [The sizes](#the-sizes) ·
> **On this page:** [Pick by memory](#pick-by-memory) · [Speed](#how-fast-is-each-size) · [The sizes](#the-sizes) ·
> [Will it fit?](#will-it-fit) · [The versions](#the-versions) · [Qwen3.6](#qwen36-35b-a3b) ·
> [Adding another model](#adding-or-switching-models)
## Pick by RAM
## Pick by memory
| Your RAM | Take | Why |
The tiers count RAM and VRAM (the graphics card's memory) together, the same as the [README](../README.md#the-other-sizes).
`strata launch`, setup and the install tool of the MCP server pre-select by them; the desktop app does from 0.2.1. Every
other size stays a choice. A PC with a card of 12 GB and 32 GB of RAM is in the 40 GB tier (32 + 12 = 44).
| Your memory (RAM + VRAM) | Pre-selected | Notes |
| --- | --- | --- |
| **32 GB** | **Coder** | it fits 32 GB; best at code, weaker at everything else ([why](#coder)). With a 24 GB card, Q2_0 and IQ2_XS run too ([low-RAM mode](#a-big-graphics-card-and-little-ram)) and are the better pick for general use |
| **48 GB** | **IQ2_XS** (or Q2_0, the fastest) | the larger sizes do not fit |
| **64 GB** | **IQ2_XS** (recommended), or IQ3_XXS / IQ3_S | every size fits (IQ3_S with little else open) |
| **96 GB or more** | **IQ3_S**, or [Unsloth's UD-IQ4_XS or UD-Q4_K_XL](#unsloths-files-ud-iq4_xs-to-q8_0) (~4-bit) | room for the largest sizes |
| **128-192 GB** | [Unsloth's UD-Q5_K_XL, UD-Q6_K_XL or Q8_0](#unsloths-files-ud-iq4_xs-to-q8_0) | every expert of the 5-8-bit files in RAM |
| **32 GB** | **Gyro-S**, or the **Coder** for code | The Coder needs 32 GB of RAM and keeps half of the experts, so it is weaker outside code ([why](#coder)). Gyro-S has lower quality than the 2-3-bit sizes, and the 2-bit Q2_0 decodes faster |
| **40 GB** | **Swift 1.5** IQ2_XS (39.2 GB) | Gyro-M (35 GB) is the other choice: close to IQ3_XXS in quality, but it writes answers 17-21% slower than Q2_0. Gyro runs on NVIDIA and AMD cards, not on Intel Arc or the Mac |
| **48 GB** | **IQ3_XXS** (47.0 GB) | Slower, better |
| **64 GB** | **IQ3_S** (54.8 GB), the original. Swift 1.5's IQ3_S is an equal choice | The slowest, the best: it matches the full model on the published tests |
| **96 GB or more** | [Unsloth's UD-IQ4_XS to Q8_0](#unsloths-files-ud-iq4_xs-to-q8_0), 4 to 8 bit | UD-IQ4_XS keeps every expert in RAM from about 80 GB of RAM, Q8_0 from about 136 GB. Up to 192 GB, the 5-8-bit files fit in RAM. Setup still pre-selects IQ3_S here, except on a Strix Halo |
Not sure? Take **IQ2_XS**. The **Coder** is the one that fits a 32 GB PC, but it keeps only 256 of the 512 experts,
chosen on code data, so it is weaker outside code and in languages other than English. For general use, or whenever
your RAM allows, take a full model (Q2_0, IQ2_XS or IQ3_S).
With less memory than a row says, Strata's [low-RAM mode](#a-big-graphics-card-and-little-ram) starts by itself and the
model runs slower, much slower with a small card. The figures above are RAM + VRAM; [Will it fit?](#will-it-fit) counts
RAM alone, for the sizes that keep their experts in RAM.
Not sure? Take what the installer pre-selects. The Coder fits a 32 GB PC, but it keeps only 256 of the 512 experts,
chosen on code data, so it is weaker outside code and in languages other than English. For general use, take a full
model (Q2_0, IQ2_XS or IQ3_S) whenever your memory allows.
## How fast is each size
@@ -68,7 +76,7 @@ in a pull request.
| Model | RAM+VRAM Requirements | Speed | Quality |
| --- | ---: | --- | --- |
| **Q2_0** | 37.6 GB | fastest | good |
| **IQ2_XS** | 39.2 GB | fast | better (**recommended**) |
| **IQ2_XS** | 39.2 GB | fast | better (**recommended** at 40 GB, as Swift 1.5) |
| **IQ3_XXS** | 47.0 GB | slower | great |
| **IQ3_S** | 54.8 GB | slowest | best: matches the full model on the published tests |
+38 -5
View File
@@ -2550,10 +2550,40 @@ def mem_have(model: str, ram: float, vram: float = 0.0) -> float:
return ram + vram if MODELS[model].get("mem_total") else ram
# 0.2.0, the owner's memory tiers (README "The other sizes"): RAM + VRAM together pick the size. The floors sit 2 GB
# below the tiers' names (40 / 48 / 64 GB) because the OS reports a little less than the installed memory. 64 GB and
# up: IQ3_S (the original; Swift 1.5's IQ3_S is an equal choice), 48 GB: IQ3_XXS, 40 GB: Swift 1.5's IQ2_XS, with Gyro-M
# (35 GB) as the other choice. Below 40 GB the older rules stand (Gyro-S, or the Coder for code). The Mac has its own
# rules (strata_control/mac_fit.py). tools/strata_mcp.py's recommend() holds the same floors (the app and `strata launch`).
MEMORY_TIERS = ((62.0, "qwen", "IQ3_S"), (46.0, "qwen", "IQ3_XXS"), (38.0, "swift", "IQ2_XS"))
def tier_pick(ram: float, vram: float = 0.0):
"""(family, size) for this PC's RAM + VRAM by MEMORY_TIERS, or None below the lowest tier (then the older rules pick:
Gyro-S, or the Coder). With less RAM than the size's own figure the low-RAM mode starts by itself and the model is
slower. A recommendation: every size stays a choice."""
for floor, fam, m in MEMORY_TIERS:
if ram + vram >= floor:
return fam, m
return None
def tier_note(ram: float, vram: float = 0.0, gyro: bool = True) -> str:
"""The sentence under the model menu for the lowest tier: Gyro-M is the other choice there (NVIDIA and AMD only)."""
pick = tier_pick(ram, vram)
if not gyro or pick != ("swift", "IQ2_XS"):
return ""
return ("this PC's RAM + VRAM is in the 40 GB tier: Swift 1.5's IQ2_XS is the pick; Gyro-M (35 GB) is the other choice, "
"close to IQ3_XXS in quality but 17-21% slower at writing answers than Q2_0")
def recommended_size(family: str, names: list, ram: float, vram: float = 0.0) -> str:
"""The size the menu pre-selects (and --yes takes): the family's recommended size when this PC's RAM fits it
(UD-Q4_K_XL before UD-IQ4_XS), else the largest size that fits, else the first. Recommend, never force: every
other size stays a choice."""
"""The size the menu pre-selects (and --yes takes): the memory tier's size (RAM + VRAM, MEMORY_TIERS) when this
family has it, else the family's recommended size when this PC's RAM fits it (UD-Q4_K_XL before UD-IQ4_XS), else
the largest size that fits, else the first. Recommend, never force: every other size stays a choice."""
tier = tier_pick(ram, vram)
if tier and tier[1] in names and family in ("qwen", "swift"):
return tier[1]
for m in RECOMMENDED_SIZES.get(family, ()):
if m in names and mem_have(m, ram, vram) >= MODELS[m]["ram_gb"]:
return m
@@ -6990,7 +7020,8 @@ def main() -> int:
if a.family:
family = a.family
else:
rec_fam = STRIX_HALO_FAMILY if strix_halo_recommends(gpu, ram) and STRIX_HALO_FAMILY in fams else fams[0]
tier = tier_pick(ram, low_ram_vram(gpu))
rec_fam = STRIX_HALO_FAMILY if strix_halo_recommends(gpu, ram) and STRIX_HALO_FAMILY in fams else tier[0] if tier and tier[0] in fams else fams[0]
for i, f in enumerate(fams, 1):
d = FAMILIES[f]
say(f" {i}) {d['title']:20s} {d['by']} - {d['about']}" + (" [experimental]" if d.get("experimental") else "")
@@ -7000,6 +7031,8 @@ def main() -> int:
d = OWN_PATH_FAMILIES[f]
say(f" {j}) {d['title']:20s} {d['by']} - {d['about']} [new]")
allf = fams + list(OWN_PATH_FAMILIES)
if tier_note(ram, low_ram_vram(gpu)) and rec_fam == "swift":
say(" " + tier_note(ram, low_ram_vram(gpu)))
family = allf[int(ask("Which model?", [str(i) for i in range(1, len(allf) + 1)],
str(allf.index(rec_fam) + 1), a.yes)) - 1]
if family in OWN_PATH_FAMILIES: # Qwen3.6: its own engine path, its own steps 2-7
@@ -7048,7 +7081,7 @@ def main() -> int:
fit = (f" <- fits in the low-RAM mode (the GPU holds ~{100 * low_ram_gpu_share(m, low_ram_vram(gpu)):.0f}%, "
+ ("the rest in RAM)" if low_ram_resident(m, ram, low_ram_vram(gpu)) else "the rest from the SSD)"))
say(f" {i}) {m:8s}{size_tag(family, m, installed)} {d['about']}; download {d['download_gb']:.0f} GB, uses ~{d['arena_gb']:.0f} GB of RAM{fit}")
rec = str(names.index(installed or recommended_size(family, names, ram, gpu["vram_gb"])) + 1)
rec = str(names.index(installed or recommended_size(family, names, ram, low_ram_vram(gpu))) + 1)
model = a.model or names[int(ask("Which size?", [str(i) for i in range(1, len(names) + 1)], rec, a.yes)) - 1]
budget, q4_split = None, False
in_budget = bool(MODELS[model].get("budget")) and unsloth_budget(model, ram, gpu, a.resident_budget_gib)
+13 -2
View File
@@ -99,7 +99,18 @@ class CatalogRules(unittest.TestCase):
for r in rows:
self.assertAlmostEqual(r.download_gb, models[r.model]["download_gb"])
rec = [r for r in rows if r.recommended]
self.assertEqual([(r.family, r.model) for r in rec], [("qwen", "IQ3_XXS")])
self.assertEqual([(r.family, r.model) for r in rec], [("qwen", "IQ3_S")]) # 64 + 12 GB: the 64 GB tier
def test_the_recommendation_follows_the_memory_tiers(self):
# the owner's tiers count RAM + VRAM: 44 GB is the 40 GB tier (Swift 1.5 IQ2_XS, Gyro-M named), 60 GB the 48 GB tier
from tests.terminal.launch_support import HW_32GB_RAM
with Home():
rec = catalog(HW_32GB_RAM).recommendation(HW_32GB_RAM)
self.assertEqual((rec["family"], rec["model"]), ("swift", "IQ2_XS"))
self.assertIn("Gyro-M", rec["why"])
hw = {**HW_32GB_RAM, "ram_gb": 48.0}
rec = catalog(hw).recommendation(hw)
self.assertEqual((rec["family"], rec["model"]), ("qwen", "IQ3_XXS"))
def test_fit_on_matches_native_fit_ts(self):
# apps/host/src/lib/native-fit.ts: memory = experts + 1; GPU holds experts up to vram - 5; RAM holds up to ram - 10; the rest spills
@@ -668,7 +679,7 @@ class FlagsCommand(unittest.TestCase):
def test_defaults_come_from_the_recommendation(self):
p = self.plan()
s = p["settings"]
self.assertEqual((s["family"], s["model"], s["context"], s["access"], s["tune"]), ("qwen", "IQ3_XXS", 32768, "local", "skip"))
self.assertEqual((s["family"], s["model"], s["context"], s["access"], s["tune"]), ("qwen", "IQ3_S", 32768, "local", "skip"))
self.assertTrue(any("picked" in w for w in p["warnings"]))
def test_family_and_kv_and_gpu_and_data_dir_and_set(self):
+18 -1
View File
@@ -117,6 +117,7 @@ FALLBACK_FAMILIES = {
"tag": "nvfp4-"},
}
OWN_PATH_ENGINE = (0, 1, 45) # the first engine that runs Qwen3.6-35B-A3B with pictures and parallel requests (setup.py MIN_ENGINE note)
MEMORY_TIERS = ((62.0, "qwen", "IQ3_S"), (46.0, "qwen", "IQ3_XXS"), (38.0, "swift", "IQ2_XS")) # as setup.py's: RAM + VRAM floors of the owner's 64 / 48 / 40 GB tiers
GYRO_ROOM_GB = 4.0 # recommend() suggests a Gyro size only when VRAM + RAM exceed its memory figure by this much
FALLBACK_CONTEXTS = [8192, 32768, 65536, 131072, 204800, 262144, 393216, 524288]
BENCH_PROMPT = ("Write a short story (about 300 words) about a lighthouse keeper who finds a message in a bottle. "
@@ -1015,7 +1016,23 @@ class Strata:
except Exception: # noqa: BLE001
return False
notes = []
if ram >= 60:
# 0.2.0, the owner's memory tiers (README "The other sizes"; setup.py MEMORY_TIERS holds the same floors): RAM + VRAM
# together. An APU's shared memory is the RAM itself, so only its BIOS carve-out counts as VRAM.
tvram = float(best.get("dedicated_gb", 0.0)) if best.get("uma") else float(vram)
tier = next(((f, n) for lo, f, n in MEMORY_TIERS if ram + tvram >= lo), None)
if tier and tier[1] in models and tier[0] in families:
fam, model = tier
total = f"{ram:.0f} GB of RAM + {tvram:.0f} GB of VRAM = {ram + tvram:.0f} GB"
if model == "IQ2_XS":
why = (f"{total}, the 40 GB tier: Swift 1.5's IQ2_XS (thinks for a shorter time)"
+ ("; Gyro-M (35 GB) is the other choice there, close to IQ3_XXS in quality but 17-21% slower at "
"writing answers than Q2_0" if "Gyro-M" in models and best.get("vendor") in ("nvidia", "amd") else ""))
elif model == "IQ3_XXS":
why = f"{total}, the 48 GB tier: IQ3_XXS (better quality, slower)"
else:
why = (f"{total}, the 64 GB tier: IQ3_S (matches the full model, the slowest); Swift 1.5's IQ3_S is an "
"equal choice")
elif ram >= 60:
fam, model, why = "qwen", "IQ3_XXS", f"{ram:.0f} GB of RAM: setup's own pick from 60 GB (better quality)"
elif ram >= models["Q2_0"]["ram_gb"] - 4:
fam, model, why = "qwen", "Q2_0", f"{ram:.0f} GB of RAM: the full model's fastest size fits"
+7 -3
View File
@@ -58,12 +58,16 @@ class GyroCatalog(unittest.TestCase):
def test_mcp_recommends_gyro_only_where_nothing_else_fits(self):
s = M.Strata(ROOT)
for ram, vram, family in ((64.0, 12.0, "qwen"), (48.0, 12.0, "qwen"), (32.0, 12.0, "coder")):
for ram, vram, family in ((64.0, 12.0, "qwen"), (48.0, 12.0, "qwen"), (32.0, 12.0, "swift"), (28.0, 8.0, "coder")):
self.assertEqual(s.recommend(hardware(ram, vram))["family"], family, (ram, vram))
got = s.recommend(hardware(20.0, 12.0)) # 32 GB in total, the Coder's 32 GB of RAM missing
self.assertEqual((got["family"], got["model"]), ("gyro", "Gyro-S"))
got = s.recommend(hardware(24.0, 16.0)) # 40 GB in total: the Coder fits in the low-RAM mode, so it wins
self.assertEqual((got["family"], got["model"]), ("coder", "IQ1_M"))
got = s.recommend(hardware(24.0, 16.0)) # 40 GB in total: the owner's 40 GB tier, Swift 1.5's IQ2_XS
self.assertEqual((got["family"], got["model"]), ("swift", "IQ2_XS"))
self.assertIn("Gyro-M", got["why"]) # named as the other choice (NVIDIA and AMD)
intel = hardware(24.0, 16.0)
intel["gpus"] = [{**g, "vendor": "intel"} for g in intel["gpus"]]
self.assertNotIn("Gyro", s.recommend(intel)["why"]) # never Intel Arc
self.assertIsNone(s.recommend(hardware(12.0, 8.0))["family"])
self.assertIsNone(s.recommend(hardware(16.0, 11.0))["family"]) # 27 GB: below Gyro-S's 27.6 + room
+5 -1
View File
@@ -109,7 +109,11 @@ class RecommendedSizes(unittest.TestCase):
def test_without_the_ram_the_largest_that_fits_and_iq3_s_stays_marked(self):
n = self.names("qwen")
self.assertEqual(setup.recommended_size("qwen", n, 61.0), "IQ3_XXS") # IQ3_S needs 62 GB
self.assertEqual(setup.recommended_size("qwen", n, 50.0), "IQ2_XS")
self.assertEqual(setup.recommended_size("qwen", n, 50.0), "IQ3_XXS") # 48 GB tier (RAM + VRAM)
self.assertEqual(setup.recommended_size("qwen", n, 40.0, 8.0), "IQ3_XXS") # 40 + 8 = 48 GB together
self.assertEqual(setup.recommended_size("qwen", n, 32.0, 12.0), "IQ2_XS") # 44 GB: the 40 GB tier
self.assertEqual(setup.recommended_size("swift", self.names("swift"), 32.0, 12.0), "IQ2_XS")
self.assertEqual(setup.recommended_size("qwen", n, 32.0, 32.0), "IQ3_S") # 64 GB together
self.assertEqual(setup.size_tag("qwen", "IQ3_S"), " (recommended)")
def test_coder_has_nothing_marked(self):
+4 -4
View File
@@ -242,7 +242,7 @@ class Protocol(FakeRoot):
call = r[3]["result"]
self.assertFalse(call["isError"])
self.assertIn("families", call["structuredContent"])
self.assertEqual(json.loads(call["content"][0]["text"])["recommendation"]["model"], "IQ3_XXS")
self.assertEqual(json.loads(call["content"][0]["text"])["recommendation"]["model"], "IQ3_S")
self.assertEqual(r[4]["result"], {})
self.assertNotIn(None, r) # the notification got no answer
@@ -377,7 +377,7 @@ class Install(FakeRoot):
res, err = self.call("strata_install", {})
self.assertFalse(err)
self.assertIn("Nothing was done yet", res["summary"])
self.assertEqual((res["plan"]["family"], res["plan"]["model"], res["plan"]["context"]), ("qwen", "IQ3_XXS", 32768))
self.assertEqual((res["plan"]["family"], res["plan"]["model"], res["plan"]["context"]), ("qwen", "IQ3_S", 32768))
self.assertFalse((self.root / "setup-args.json").exists())
self.assertIsNone(self.strata.install_job())
@@ -641,7 +641,7 @@ class Helpers(unittest.TestCase):
def test_recommendation_follows_ram(self):
s = M.Strata(HERE.parent)
gpu = [{"index": 0, "name": "x", "vram_gb": 24.0, "usable": True, "vendor": "nvidia"}]
cases = {96: ("qwen", "IQ3_XXS", 131072), 48: ("qwen", "Q2_0", 131072), 32: ("coder", "IQ1_M", 131072)}
cases = {96: ("qwen", "IQ3_S", 131072), 48: ("qwen", "IQ3_S", 131072), 32: ("qwen", "IQ3_XXS", 131072), 12: ("coder", "IQ1_M", 131072)} # the owner's tiers count RAM + VRAM (24 GB card): 120, 72, 56 GB; the Coder where the older rules stand
for ram, want in cases.items():
r = s.recommend({"ram_gb": ram, "gpus": gpu, "cpu": {"avx2": True}})
self.assertEqual((r["family"], r["model"], r["context"]), want, ram)
@@ -732,7 +732,7 @@ class MacCatalog(unittest.TestCase):
s = M.Strata(HERE.parent)
r = s.recommend({"ram_gb": 96, "gpus": [{"index": 0, "name": "x", "vram_gb": 24.0, "usable": True, "vendor": "nvidia"}],
"cpu": {"avx2": True}})
self.assertEqual((r["family"], r["model"]), ("qwen", "IQ3_XXS"))
self.assertEqual((r["family"], r["model"]), ("qwen", "IQ3_S"))
self.assertIn("unsloth", [f["family"] for f in s.model_table({"ram_gb": 96, "gpus": []})])