quantprobe 1.6.1__tar.gz → 1.6.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {quantprobe-1.6.1/quantprobe.egg-info → quantprobe-1.6.2}/PKG-INFO +1 -1
- {quantprobe-1.6.1 → quantprobe-1.6.2}/pyproject.toml +1 -1
- {quantprobe-1.6.1 → quantprobe-1.6.2}/quantprobe/__init__.py +1 -1
- {quantprobe-1.6.1 → quantprobe-1.6.2}/quantprobe/auto.py +59 -11
- {quantprobe-1.6.1 → quantprobe-1.6.2}/quantprobe/cli.py +3 -3
- {quantprobe-1.6.1 → quantprobe-1.6.2}/quantprobe/optimize.py +6 -5
- {quantprobe-1.6.1 → quantprobe-1.6.2}/quantprobe/plan.py +5 -1
- {quantprobe-1.6.1 → quantprobe-1.6.2/quantprobe.egg-info}/PKG-INFO +1 -1
- {quantprobe-1.6.1 → quantprobe-1.6.2}/LICENSE +0 -0
- {quantprobe-1.6.1 → quantprobe-1.6.2}/README.md +0 -0
- {quantprobe-1.6.1 → quantprobe-1.6.2}/quantprobe/__main__.py +0 -0
- {quantprobe-1.6.1 → quantprobe-1.6.2}/quantprobe/dashboard.py +0 -0
- {quantprobe-1.6.1 → quantprobe-1.6.2}/quantprobe/detect.py +0 -0
- {quantprobe-1.6.1 → quantprobe-1.6.2}/quantprobe/fetch.py +0 -0
- {quantprobe-1.6.1 → quantprobe-1.6.2}/quantprobe/probe.py +0 -0
- {quantprobe-1.6.1 → quantprobe-1.6.2}/quantprobe/runtime.py +0 -0
- {quantprobe-1.6.1 → quantprobe-1.6.2}/quantprobe/spec.py +0 -0
- {quantprobe-1.6.1 → quantprobe-1.6.2}/quantprobe/target.py +0 -0
- {quantprobe-1.6.1 → quantprobe-1.6.2}/quantprobe.egg-info/SOURCES.txt +0 -0
- {quantprobe-1.6.1 → quantprobe-1.6.2}/quantprobe.egg-info/dependency_links.txt +0 -0
- {quantprobe-1.6.1 → quantprobe-1.6.2}/quantprobe.egg-info/entry_points.txt +0 -0
- {quantprobe-1.6.1 → quantprobe-1.6.2}/quantprobe.egg-info/requires.txt +0 -0
- {quantprobe-1.6.1 → quantprobe-1.6.2}/quantprobe.egg-info/top_level.txt +0 -0
- {quantprobe-1.6.1 → quantprobe-1.6.2}/setup.cfg +0 -0
- {quantprobe-1.6.1 → quantprobe-1.6.2}/tests/test_engine.py +0 -0
- {quantprobe-1.6.1 → quantprobe-1.6.2}/tests/test_evaluator.py +0 -0
- {quantprobe-1.6.1 → quantprobe-1.6.2}/tests/test_pipeline.py +0 -0
- {quantprobe-1.6.1 → quantprobe-1.6.2}/tests/test_transforms.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quantprobe
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.2
|
|
4
4
|
Summary: Probe-then-quantize for LLMs: measure a model's fragility curve, plan bit/tier placement by the tiered decode law, and emit ready-to-run llama.cpp recipes.
|
|
5
5
|
Author: Federico Sciuca
|
|
6
6
|
License: MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "quantprobe"
|
|
7
|
-
version = "1.6.
|
|
7
|
+
version = "1.6.2"
|
|
8
8
|
description = "Probe-then-quantize for LLMs: measure a model's fragility curve, plan bit/tier placement by the tiered decode law, and emit ready-to-run llama.cpp recipes."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -8,4 +8,4 @@ Three commands, all grounded in measured laws (see LAWS.md in the repo):
|
|
|
8
8
|
Every constant in `plan` is fitted from measurements published in the repo, including two
|
|
9
9
|
pre-registered hardware predictions confirmed within 8%.
|
|
10
10
|
"""
|
|
11
|
-
__version__ = "1.6.
|
|
11
|
+
__version__ = "1.6.2"
|
|
@@ -24,17 +24,51 @@ MODEL_REPOS = {
|
|
|
24
24
|
"laguna-s": ("unsloth/Laguna-S-2.1-GGUF", 117.6, 8, 2.5, True),
|
|
25
25
|
"gemma-12b": ("unsloth/gemma-4-12b-it-GGUF", 11.9, 11.9, 11.9, False),
|
|
26
26
|
"mistral-7b": ("unsloth/Mistral-7B-Instruct-v0.3-GGUF", 7.2, 7.2, 7.2, False),
|
|
27
|
+
"qwen3-235b": ("unsloth/Qwen3-235B-A22B-GGUF", 235.1, 22, 7.5, True), # total exact (HF safetensors)
|
|
28
|
+
"glm-4.7": ("unsloth/GLM-4.7-GGUF", 358.3, 32, 8, True), # total exact; a/ne [est]
|
|
29
|
+
"glm-744b": ("unsloth/GLM-5.2-GGUF", 753.3, 32, 8, True), # GLM-5.2, total exact 753.3B; a/ne [est]
|
|
30
|
+
"kimi-k2.6": ("unsloth/Kimi-K2.6-GGUF", 1058.6, 32, 6, True), # total exact; a/ne [est]
|
|
31
|
+
"gpt-oss-120b": ("unsloth/gpt-oss-120b-GGUF", 120.4, 5.1, 1.8, True), # total exact, A5.1B official
|
|
32
|
+
"llama-70b": ("unsloth/Llama-3.3-70B-Instruct-GGUF", 70.6, 70.6, 70.6, False),
|
|
33
|
+
"deepseek-16b": ("bartowski/DeepSeek-Coder-V2-Lite-Instruct-GGUF", 15.7, 2.4, 1.3, True),
|
|
27
34
|
}
|
|
28
35
|
|
|
29
36
|
|
|
30
37
|
def list_ggufs(repo):
|
|
31
|
-
"""[(path, size_bytes)] for a HF repo, via the public tree API
|
|
32
|
-
|
|
38
|
+
"""[(path, size_bytes)] for a HF repo, via the public tree API (recursive: big repos
|
|
39
|
+
keep quants in subfolders). Split multi-part files (-00001-of-000NN) are grouped into
|
|
40
|
+
ONE logical entry: path = first part, size = sum of parts — so effective-bits stays honest."""
|
|
41
|
+
import re
|
|
42
|
+
req = urllib.request.Request(f"https://huggingface.co/api/models/{repo}/tree/main?recursive=true",
|
|
33
43
|
headers={"User-Agent": "quantprobe-auto"})
|
|
34
44
|
with urllib.request.urlopen(req, timeout=30) as r:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
45
|
+
raw = [(f["path"], f.get("size", 0)) for f in json.load(r)
|
|
46
|
+
if f["path"].endswith(".gguf") and f.get("size", 0) > 1e8
|
|
47
|
+
and "mmproj" not in f["path"].lower() and "draft" not in f["path"].lower()]
|
|
48
|
+
groups, singles = {}, []
|
|
49
|
+
for path, size in raw:
|
|
50
|
+
m = re.match(r"^(.*)-(\d{5})-of-(\d{5})\.gguf$", path)
|
|
51
|
+
if m:
|
|
52
|
+
g = groups.setdefault(m.group(1), {"total": 0, "first": None, "n": int(m.group(3))})
|
|
53
|
+
g["total"] += size
|
|
54
|
+
if m.group(2) == "00001":
|
|
55
|
+
g["first"] = path
|
|
56
|
+
else:
|
|
57
|
+
singles.append((path, size))
|
|
58
|
+
for g in groups.values():
|
|
59
|
+
if g["first"]:
|
|
60
|
+
singles.append((g["first"], g["total"]))
|
|
61
|
+
return singles
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def split_parts(first_part):
|
|
65
|
+
"""All part paths of a split file, derived from the -00001-of-000NN first part."""
|
|
66
|
+
import re
|
|
67
|
+
m = re.match(r"^(.*)-(\d{5})-of-(\d{5})\.gguf$", first_part)
|
|
68
|
+
if not m:
|
|
69
|
+
return [first_part]
|
|
70
|
+
base, n = m.group(1), int(m.group(3))
|
|
71
|
+
return [f"{base}-{i:05d}-of-{n:05d}.gguf" for i in range(1, n + 1)]
|
|
38
72
|
|
|
39
73
|
|
|
40
74
|
WIKI_URL = "https://huggingface.co/datasets/ggml-org/ci/resolve/main/wikitext-2-raw-v1.zip"
|
|
@@ -136,9 +170,19 @@ def run(a):
|
|
|
136
170
|
continue
|
|
137
171
|
scored.append((abs(bits - want_bits), -cfgs[0][1], path, size, bits, cfgs[0]))
|
|
138
172
|
if not scored:
|
|
139
|
-
|
|
173
|
+
avail = sorted(set(round(s * 8 / (t * 1e9), 1) for _, s in files))
|
|
174
|
+
raise SystemExit(
|
|
175
|
+
f"no ready-to-run quant in {repo} for this machine yet (available bit-levels: {avail}).\n"
|
|
176
|
+
" Big new models often publish low-bit quants days after release. Meanwhile:\n"
|
|
177
|
+
" - predictions for any bits now: quantprobe plan --model <preset> --bits 2.5\n"
|
|
178
|
+
+ (" - or build your own from the high-precision source: re-run with --custom\n"
|
|
179
|
+
if pick_source(files, t) else ""))
|
|
140
180
|
scored.sort()
|
|
141
181
|
_, _, path, size, bits, best = scored[0]
|
|
182
|
+
if abs(bits - want_bits) > 2:
|
|
183
|
+
print(f"\n[quantprobe auto] note: no ~{want_bits:g}-bit-class file in {repo} yet - closest "
|
|
184
|
+
f"available is {bits:.1f}-bit. Low-bit quants for new models often land days after "
|
|
185
|
+
f"release; plan/optimize predict any bits meanwhile.")
|
|
142
186
|
|
|
143
187
|
if getattr(a, "custom", False) and want_bits >= 3.5 and not getattr(a, "force_custom", False):
|
|
144
188
|
print(f"\n[quantprobe auto --custom] this machine doesn't need the surgery: the optimizer")
|
|
@@ -179,20 +223,24 @@ def run(a):
|
|
|
179
223
|
a.gguf = out; a.bits = None
|
|
180
224
|
runtime.run(a)
|
|
181
225
|
return out
|
|
226
|
+
parts = split_parts(path)
|
|
182
227
|
print(f"\n[quantprobe auto] optimizer wants ~{want_bits:g}-bit; closest file in {repo}:")
|
|
183
|
-
print(f" {path} ({size/1e9:.1f} GB, {bits:.2f} effective bits
|
|
228
|
+
print(f" {path} ({size/1e9:.1f} GB, {bits:.2f} effective bits"
|
|
229
|
+
+ (f", {len(parts)} parts" if len(parts) > 1 else "") + ")")
|
|
184
230
|
print(f" predicted on this machine: {best[1]:.1f} tok/s ({best[0]})")
|
|
185
231
|
if getattr(a, "dry", False):
|
|
186
232
|
print(" (--dry: nothing downloaded)")
|
|
187
233
|
return path
|
|
188
|
-
# 3. fetch it (resumable), then hand off
|
|
234
|
+
# 3. fetch it (resumable; all parts when split), then hand off
|
|
189
235
|
from . import fetch as fetchmod
|
|
190
236
|
dest = getattr(a, "dir", None) or "./models"
|
|
191
237
|
import os
|
|
192
238
|
os.makedirs(dest, exist_ok=True)
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
239
|
+
for i, part in enumerate(parts):
|
|
240
|
+
if len(parts) > 1:
|
|
241
|
+
print(f"[quantprobe auto] part {i+1}/{len(parts)}: {os.path.basename(part)}")
|
|
242
|
+
if not fetchmod.fetch(repo, dest, part, fetchmod.token()):
|
|
243
|
+
raise SystemExit("download failed (it resumes: re-run the same command)")
|
|
196
244
|
full = os.path.join(dest, os.path.basename(path))
|
|
197
245
|
print(f"\n[quantprobe auto] ready. Run it:")
|
|
198
246
|
print(f" quantprobe run --gguf {full}")
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
import argparse
|
|
4
4
|
|
|
5
|
-
from .plan import numlist
|
|
5
|
+
from .plan import numlist, MODELS as _MODELS
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def main():
|
|
@@ -30,7 +30,7 @@ def main():
|
|
|
30
30
|
p.add_argument("--dry-run", action="store_true")
|
|
31
31
|
|
|
32
32
|
q = sub.add_parser("plan", help="evaluate every bit/tier placement, predict tok/s (Law 4)")
|
|
33
|
-
q.add_argument("--model", default=None, help="preset:
|
|
33
|
+
q.add_argument("--model", default=None, help="preset: " + " ".join(sorted(_MODELS)))
|
|
34
34
|
q.add_argument("--machine", default=None, help="preset: 2016-xmp 2016 gaming ddr5 colibri")
|
|
35
35
|
q.add_argument("--bits", type=float, default=None, help="effective bits/weight (default: read from --gguf, else 2.5)")
|
|
36
36
|
q.add_argument("--total", type=float, help="total params (B)")
|
|
@@ -93,7 +93,7 @@ def main():
|
|
|
93
93
|
|
|
94
94
|
au = sub.add_parser("auto", help="ONE command: model in, running setup out - optimizer picks the bits, the closest quant is fetched, run command printed")
|
|
95
95
|
au.add_argument("target", nargs="?", default=None,
|
|
96
|
-
help="model preset (qwen3-30b, qwen3-coder, glm-air, laguna-s, gemma-12b, mistral-7b) or a HF GGUF repo id; omit it and quantprobe ASKS (interactive)")
|
|
96
|
+
help="model preset (qwen3-30b, qwen3-coder, glm-air, laguna-s, gemma-12b, mistral-7b, qwen3-235b, glm-4.7, glm-744b, kimi-k2.6, gpt-oss-120b, llama-70b, deepseek-16b) or a HF GGUF repo id; omit it and quantprobe ASKS (interactive)")
|
|
97
97
|
au.add_argument("--dir", default="./models", help="download directory (default ./models)")
|
|
98
98
|
au.add_argument("--run", action="store_true", help="launch chat immediately after the download")
|
|
99
99
|
hwargs(au)
|
|
@@ -13,7 +13,8 @@ Levers and their measured gates:
|
|
|
13
13
|
prune REAP-class 50% expert pruning (total x0.82, file shrinks, active bytes UNCHANGED).
|
|
14
14
|
Measured +39% out-of-domain perplexity (pre-registration #8) — domain-specialized,
|
|
15
15
|
never ranked first without --allow-prune.
|
|
16
|
-
hardware
|
|
16
|
+
hardware upgrade deltas from the projections table: XMP (free), +16GB RAM, NVMe. Ranked by
|
|
17
|
+
relative cost internally; no currency figures are shown (region- and time-dependent).
|
|
17
18
|
"""
|
|
18
19
|
from __future__ import annotations
|
|
19
20
|
from . import plan as planmod
|
|
@@ -28,9 +29,9 @@ REALIZE = {
|
|
|
28
29
|
}
|
|
29
30
|
HW_DELTAS = [
|
|
30
31
|
("as-is", 0, {}),
|
|
31
|
-
("+16GB RAM (
|
|
32
|
-
("NVMe SSD
|
|
33
|
-
("+RAM & NVMe
|
|
32
|
+
("+16GB RAM (used)", 40, {"rc": +16}),
|
|
33
|
+
("NVMe SSD", 100, {"db_min": 3.5}),
|
|
34
|
+
("+RAM & NVMe", 140, {"rc": +16, "db_min": 3.5}),
|
|
34
35
|
]
|
|
35
36
|
|
|
36
37
|
|
|
@@ -121,7 +122,7 @@ def run(a):
|
|
|
121
122
|
print(f" {headline}\n")
|
|
122
123
|
for i, r in enumerate(ranked[:6]):
|
|
123
124
|
star = "*" if i == 0 else " "
|
|
124
|
-
euro = "free" if r["euro"] == 0 else
|
|
125
|
+
euro = "free" if r["euro"] == 0 else "hw upgrade"
|
|
125
126
|
print(f" {star} {r['tps']:6.1f} tok/s quality x{r['q']:.2f} {euro:>7s} {r['desc']}")
|
|
126
127
|
for tag in r["tags"]:
|
|
127
128
|
print(f" [{tag}]")
|
|
@@ -17,7 +17,11 @@ MODELS = {
|
|
|
17
17
|
"gemma-12b": dict(t=11.9, a=11.9, ne=11.9, moe=False, kvp=65536, hint="Gemma 4 12B"), # [est] SWA: long-ctx slope from global layers only
|
|
18
18
|
"mistral-7b": dict(t=7.2, a=7.2, ne=7.2, moe=False, kvp=131072, hint="Mistral 7B"), # 32L x 8KV x 128d (exact)
|
|
19
19
|
"glm-air": dict(t=110, a=12, ne=2.7, moe=True, kvp=94208, hint="GLM-4.5-Air 106B"), # [est]
|
|
20
|
-
"glm-744b": dict(t=
|
|
20
|
+
"glm-744b": dict(t=753.3, a=32, ne=8, moe=True, kvp=188416, hint="GLM-5.2 (753B)"), # total exact (HF safetensors); a/ne/kvp [est]
|
|
21
|
+
"qwen3-235b": dict(t=235.1, a=22, ne=7.5, moe=True, kvp=192512, hint="Qwen3-235B-A22B"), # total exact; kvp: 94L x 4KV x 128d [est]
|
|
22
|
+
"kimi-k2.6": dict(t=1058.6, a=32, ne=6, moe=True, kvp=70272, hint="Kimi K2.6 (1T MLA)"), # total exact; kvp: 61L x 576 MLA latent [est]
|
|
23
|
+
"gpt-oss-120b": dict(t=120.4, a=5.1, ne=1.8, moe=True, kvp=73728, hint="gpt-oss-120b"), # total+active official; kvp: 36L x 8KV x 64d [est]
|
|
24
|
+
"llama-70b": dict(t=70.6, a=70.6, ne=70.6, moe=False, kvp=327680, hint="Llama-3.3-70B"), # dense; kvp: 80L x 8KV x 128d
|
|
21
25
|
}
|
|
22
26
|
DEFAULT_KVP = 98304 # custom models without --kv-per-pos: typical GQA mid-size (Qwen3-30B class)
|
|
23
27
|
ETA_KV = 0.70 # KV-read efficiency. Single-point calibration: measured tg32 d0->d16384
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quantprobe
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.2
|
|
4
4
|
Summary: Probe-then-quantize for LLMs: measure a model's fragility curve, plan bit/tier placement by the tiered decode law, and emit ready-to-run llama.cpp recipes.
|
|
5
5
|
Author: Federico Sciuca
|
|
6
6
|
License: MIT
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|