Spaces:
Running on Zero
Running on Zero
File size: 6,918 Bytes
12d2e34 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | """
Describing a machine in terms the maths cares about.
The whole job here is to turn "I have a Windows laptop with an RTX 3060 and
16 GB of RAM" into two numbers the advisor can reason about:
- fast_budget_gb : memory the model can use *on the fast path* (the GPU, or
on Apple Silicon, the shared memory the GPU can borrow)
- total_budget_gb: the absolute most a model can use if we let it spill onto
ordinary RAM (slower, but it runs)
Everything is deliberately conservative. We'd rather say "this might be tight"
and be wrong than promise something that then fails to load.
"""
from dataclasses import dataclass
# --------------------------------------------------------------------------
# Common consumer GPUs -> VRAM (GB). So people pick a name, not a number.
# VRAM is baked into the label too, because some cards ship in two sizes.
# --------------------------------------------------------------------------
GPU_PRESETS: dict[str, float] = {
# NVIDIA RTX 50-series
"NVIDIA RTX 5090 (32 GB)": 32,
"NVIDIA RTX 5080 (16 GB)": 16,
"NVIDIA RTX 5070 Ti (16 GB)": 16,
"NVIDIA RTX 5070 (12 GB)": 12,
"NVIDIA RTX 5060 Ti (16 GB)": 16,
"NVIDIA RTX 5060 (8 GB)": 8,
# NVIDIA RTX 40-series
"NVIDIA RTX 4090 (24 GB)": 24,
"NVIDIA RTX 4080 (16 GB)": 16,
"NVIDIA RTX 4070 Ti (12 GB)": 12,
"NVIDIA RTX 4070 (12 GB)": 12,
"NVIDIA RTX 4060 Ti (16 GB)": 16,
"NVIDIA RTX 4060 (8 GB)": 8,
# NVIDIA RTX 30-series
"NVIDIA RTX 3090 (24 GB)": 24,
"NVIDIA RTX 3080 (10 GB)": 10,
"NVIDIA RTX 3070 (8 GB)": 8,
"NVIDIA RTX 3060 (12 GB)": 12,
"NVIDIA RTX 3050 (8 GB)": 8,
# Older / budget NVIDIA
"NVIDIA GTX 1660 (6 GB)": 6,
"NVIDIA GTX 1650 (4 GB)": 4,
# AMD
"AMD RX 7900 XTX (24 GB)": 24,
"AMD RX 7800 XT (16 GB)": 16,
"AMD RX 7600 (8 GB)": 8,
"AMD RX 6700 XT (12 GB)": 12,
# Laptop integrated (no real VRAM — uses shared system RAM)
"Intel built-in graphics (no separate card)": 0,
"AMD built-in graphics (no separate card)": 0,
}
# Apple Silicon: there's no separate VRAM. The GPU shares system memory, and
# macOS lets it borrow a large slice. We treat it specially below.
APPLE_CHIPS: dict[str, int] = {
"Apple M1 / M2 / M3 / M4 (base)": 8, # default RAM if they don't know
"Apple M-series Pro": 16,
"Apple M-series Max": 32,
"Apple M-series Ultra": 64,
}
@dataclass
class HardwareSpec:
"""A machine, described just enough to reason about it."""
os: str = "windows" # windows | macos | linux
ram_gb: float = 16.0 # system RAM
gpu_vendor: str = "none" # nvidia | amd | apple | intel | none
vram_gb: float = 0.0 # dedicated GPU memory (0 if shared/none)
is_apple_silicon: bool = False
gpu_label: str = "No dedicated graphics card"
form_factor: str = "laptop" # laptop | desktop | mac | sbc
# -- derived memory budgets -------------------------------------------
@property
def fast_budget_gb(self) -> float:
"""Memory available on the *fast* path (GPU / Apple shared memory)."""
if self.is_apple_silicon:
# macOS lets the GPU use a large fraction of unified memory.
# ~70% is a safe, widely-quoted working figure.
return round(self.ram_gb * 0.70, 1)
if self.gpu_vendor in ("nvidia", "amd") and self.vram_gb > 0:
# Leave headroom for the display, driver, and other apps.
return round(self.vram_gb * 0.85, 1)
# Integrated graphics / CPU-only: no meaningful fast path.
return 0.0
@property
def os_reserve_gb(self) -> float:
"""RAM we set aside for the operating system + other open programs.
Windows idles heavy; a headless Raspberry Pi barely uses anything.
Being honest here matters: too small a reserve over-promises.
"""
return {
"sbc": 1.0,
"mac": 3.0,
"desktop": 3.0,
"laptop": 3.5,
}.get(self.form_factor, 3.0) if self.os != "linux" else {
"sbc": 1.0,
}.get(self.form_factor, 2.0)
@property
def total_budget_gb(self) -> float:
"""The most a model can use if it spills onto ordinary RAM (slower)."""
if self.is_apple_silicon:
return self.fast_budget_gb # unified memory — same pool
# Dedicated VRAM (fully usable on the fast path) PLUS a conservative
# slice of system RAM for CPU offload, after reserving room for the OS.
ram_for_model = max(0.0, self.ram_gb - self.os_reserve_gb) * 0.9
return round(self.vram_gb + ram_for_model, 1)
@property
def has_fast_path(self) -> bool:
return self.fast_budget_gb >= 1.0
def build_spec(
*,
computer_kind: str,
ram_gb: float,
gpu_choice: str,
apple_chip: str | None = None,
) -> HardwareSpec:
"""Turn friendly UI selections into a HardwareSpec.
computer_kind: "Windows laptop/desktop", "Mac", "Linux PC",
"Raspberry Pi / mini PC"
gpu_choice: a key from GPU_PRESETS, or one of the "don't know" options.
"""
kind = computer_kind.lower()
# ---- Mac / Apple Silicon -------------------------------------------
if "mac" in kind:
chip = apple_chip or "Apple M1 / M2 / M3 / M4 (base)"
return HardwareSpec(
os="macos",
ram_gb=ram_gb,
gpu_vendor="apple",
vram_gb=0.0,
is_apple_silicon=True,
gpu_label=f"{chip} (shares your {ram_gb:g} GB of memory)",
form_factor="mac",
)
# ---- Raspberry Pi / tiny single-board ------------------------------
if "raspberry" in kind or "mini" in kind or "sbc" in kind:
return HardwareSpec(
os="linux",
ram_gb=ram_gb,
gpu_vendor="none",
vram_gb=0.0,
gpu_label="No dedicated graphics card (tiny computer)",
form_factor="sbc",
)
# ---- Windows / Linux PC with a possible discrete GPU ---------------
os_name = "linux" if "linux" in kind else "windows"
form = "desktop" if "desktop" in kind else "laptop"
vram = GPU_PRESETS.get(gpu_choice, 0.0)
if "nvidia" in gpu_choice.lower():
vendor = "nvidia"
elif "amd" in gpu_choice.lower() and "built-in" not in gpu_choice.lower():
vendor = "amd"
elif "built-in" in gpu_choice.lower():
vendor = "intel" if "intel" in gpu_choice.lower() else "amd"
else:
vendor = "none"
label = gpu_choice if vram > 0 else "No dedicated graphics card (built-in graphics only)"
return HardwareSpec(
os=os_name,
ram_gb=ram_gb,
gpu_vendor=vendor,
vram_gb=vram,
is_apple_silicon=False,
gpu_label=label,
form_factor=form,
)
|