Spaces:
Running
Running
Fall back to another repo's GGUF header; hide non-llama.cpp GGUFs
Browse files- app/detail.py +48 -21
- app/main.py +8 -3
app/detail.py
CHANGED
|
@@ -158,17 +158,27 @@ def read_header(repo: str, path: str, client: httpx.Client):
|
|
| 158 |
raise NeedMoreBytes
|
| 159 |
|
| 160 |
|
| 161 |
-
def
|
| 162 |
-
"""The smallest real quant of
|
| 163 |
-
and a small single file keeps the tensor table in the part we read. Files too small
|
| 164 |
-
the model (drafts, heads, encoders) are skipped.
|
|
|
|
| 165 |
floor = min_model_bytes(params)
|
|
|
|
| 166 |
for rf in repos:
|
| 167 |
real = [q for q in rf.quants if (q.size or 0) >= floor]
|
| 168 |
singles = [q for q in real if len(q.files) == 1] or real
|
| 169 |
if singles:
|
| 170 |
-
|
| 171 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
|
| 173 |
|
| 174 |
def plausible_geometry(d: dict) -> bool:
|
|
@@ -187,6 +197,8 @@ class DetailCache:
|
|
| 187 |
self._inflight: dict[str, threading.Event] = {}
|
| 188 |
self.repo_files: dict[str, RepoFiles] = {}
|
| 189 |
self.geometries: dict[str, dict] = {}
|
|
|
|
|
|
|
| 190 |
self.fetch = fetch
|
| 191 |
self.pool = ThreadPoolExecutor(max_workers=6, thread_name_prefix="detail")
|
| 192 |
for path in (snapshot, self._geo_file):
|
|
@@ -229,7 +241,8 @@ class DetailCache:
|
|
| 229 |
def get(self, group: Group, timeout: float = 20.0, with_geometry: bool = True,
|
| 230 |
repos: int = REPOS_PER_MODEL) -> ModelDetail:
|
| 231 |
wanted = [r.id for r in group.repos[:repos] if self._stale(r.id)]
|
| 232 |
-
need_geo = with_geometry and group.key not in self.geometries
|
|
|
|
| 233 |
if self.fetch and (wanted or need_geo):
|
| 234 |
with self._lock:
|
| 235 |
ev = self._inflight.get(group.key)
|
|
@@ -257,25 +270,39 @@ class DetailCache:
|
|
| 257 |
if need_geo:
|
| 258 |
ordered = [self.repo_files[r.id] for r in group.repos if r.id in self.repo_files]
|
| 259 |
params = next((r.params for r in group.repos if r.params), None)
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
try:
|
| 264 |
if not BUDGET.acquire():
|
| 265 |
raise TimeoutError("hub budget")
|
| 266 |
h = read_header(repo, q.first_file, client)
|
| 267 |
-
g = from_header(h, q.size)
|
| 268 |
-
vocab = h.metadata.get("tokenizer.ggml.tokens")
|
| 269 |
-
n_vocab = vocab.get("array_len") if isinstance(vocab, dict) else None
|
| 270 |
-
g.params = g.params or params # the header's own weight count when it lists every tensor
|
| 271 |
-
record = {**geometry_to_json(g, n_vocab), "_from": f"{repo}/{q.first_file}"}
|
| 272 |
-
if plausible_geometry(record):
|
| 273 |
-
self.geometries[group.key] = record
|
| 274 |
-
self._save_geometries()
|
| 275 |
-
else:
|
| 276 |
-
log.info("implausible geometry for %s from %s", group.key, q.first_file)
|
| 277 |
except Exception as e:
|
| 278 |
-
log.info("header read failed for %s: %s", group.key, type(e).__name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 279 |
finally:
|
| 280 |
done.set()
|
| 281 |
with self._lock:
|
|
|
|
| 158 |
raise NeedMoreBytes
|
| 159 |
|
| 160 |
|
| 161 |
+
def header_candidates(repos: list[RepoFiles], params: float | None = None, limit: int = 3) -> list[tuple[str, QuantFile]]:
|
| 162 |
+
"""The smallest real quant of each repo, most downloaded repo first: every quant shares the
|
| 163 |
+
geometry, and a small single file keeps the tensor table in the part we read. Files too small
|
| 164 |
+
to be the model (drafts, heads, encoders) are skipped. Later repos are fallbacks for a first
|
| 165 |
+
file that isn't a llama.cpp GGUF (a ComfyUI export, another engine's own format)."""
|
| 166 |
floor = min_model_bytes(params)
|
| 167 |
+
out = []
|
| 168 |
for rf in repos:
|
| 169 |
real = [q for q in rf.quants if (q.size or 0) >= floor]
|
| 170 |
singles = [q for q in real if len(q.files) == 1] or real
|
| 171 |
if singles:
|
| 172 |
+
out.append((rf.repo, min(singles, key=lambda q: q.size or 0)))
|
| 173 |
+
if len(out) >= limit:
|
| 174 |
+
break
|
| 175 |
+
return out
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
def llama_cpp_header(h) -> bool:
|
| 179 |
+
"""llama.cpp names tensors blk.N.* and keys <arch>.block_count; ComfyUI and some engines'
|
| 180 |
+
GGUFs keep Hugging Face names (model.layers.N.*) that llama.cpp can't load."""
|
| 181 |
+
return bool(h.arch_key("block_count")) and not any(k.startswith("comfy.") for k in h.metadata)
|
| 182 |
|
| 183 |
|
| 184 |
def plausible_geometry(d: dict) -> bool:
|
|
|
|
| 197 |
self._inflight: dict[str, threading.Event] = {}
|
| 198 |
self.repo_files: dict[str, RepoFiles] = {}
|
| 199 |
self.geometries: dict[str, dict] = {}
|
| 200 |
+
self.geometry_failed: dict[str, float] = {} # group key -> when its headers couldn't be planned
|
| 201 |
+
self.not_llama_cpp: set[str] = set() # every file read is a format llama.cpp can't load (ComfyUI, ...)
|
| 202 |
self.fetch = fetch
|
| 203 |
self.pool = ThreadPoolExecutor(max_workers=6, thread_name_prefix="detail")
|
| 204 |
for path in (snapshot, self._geo_file):
|
|
|
|
| 241 |
def get(self, group: Group, timeout: float = 20.0, with_geometry: bool = True,
|
| 242 |
repos: int = REPOS_PER_MODEL) -> ModelDetail:
|
| 243 |
wanted = [r.id for r in group.repos[:repos] if self._stale(r.id)]
|
| 244 |
+
need_geo = (with_geometry and group.key not in self.geometries
|
| 245 |
+
and time.time() - self.geometry_failed.get(group.key, 0) > FILES_TTL)
|
| 246 |
if self.fetch and (wanted or need_geo):
|
| 247 |
with self._lock:
|
| 248 |
ev = self._inflight.get(group.key)
|
|
|
|
| 270 |
if need_geo:
|
| 271 |
ordered = [self.repo_files[r.id] for r in group.repos if r.id in self.repo_files]
|
| 272 |
params = next((r.params for r in group.repos if r.params), None)
|
| 273 |
+
readable = foreign = 0
|
| 274 |
+
candidates = header_candidates(ordered, params)
|
| 275 |
+
for repo, q in candidates:
|
| 276 |
try:
|
| 277 |
if not BUDGET.acquire():
|
| 278 |
raise TimeoutError("hub budget")
|
| 279 |
h = read_header(repo, q.first_file, client)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 280 |
except Exception as e:
|
| 281 |
+
log.info("header read failed for %s from %s: %s", group.key, q.first_file, type(e).__name__)
|
| 282 |
+
continue
|
| 283 |
+
readable += 1
|
| 284 |
+
if not llama_cpp_header(h):
|
| 285 |
+
foreign += 1
|
| 286 |
+
log.info("not a llama.cpp GGUF: %s/%s", repo, q.first_file)
|
| 287 |
+
continue
|
| 288 |
+
g = from_header(h, q.size)
|
| 289 |
+
vocab = h.metadata.get("tokenizer.ggml.tokens")
|
| 290 |
+
n_vocab = vocab.get("array_len") if isinstance(vocab, dict) else None
|
| 291 |
+
g.params = g.params or params # the header's own weight count when it lists every tensor
|
| 292 |
+
record = {**geometry_to_json(g, n_vocab), "_from": f"{repo}/{q.first_file}"}
|
| 293 |
+
if plausible_geometry(record):
|
| 294 |
+
self.geometries[group.key] = record
|
| 295 |
+
self.geometry_failed.pop(group.key, None)
|
| 296 |
+
self.not_llama_cpp.discard(group.key)
|
| 297 |
+
self._save_geometries()
|
| 298 |
+
break
|
| 299 |
+
log.info("implausible geometry for %s from %s", group.key, q.first_file)
|
| 300 |
+
else:
|
| 301 |
+
if candidates and readable == len(candidates):
|
| 302 |
+
# every file was read and none could be planned: don't re-read on every visit
|
| 303 |
+
if foreign == readable:
|
| 304 |
+
self.not_llama_cpp.add(group.key)
|
| 305 |
+
self.geometry_failed[group.key] = time.time()
|
| 306 |
finally:
|
| 307 |
done.set()
|
| 308 |
with self._lock:
|
app/main.py
CHANGED
|
@@ -144,6 +144,10 @@ class State:
|
|
| 144 |
self.stats.invalidate()
|
| 145 |
|
| 146 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
def _only_auxiliary(s) -> bool:
|
| 148 |
"""Every file we could read is too small to be the model (a repo of draft models or
|
| 149 |
speculative-decoding heads, which nobody runs on their own), or below the chosen bit level."""
|
|
@@ -226,7 +230,7 @@ def create_app(state: State | None = None) -> FastAPI:
|
|
| 226 |
groups = st.catalogue.matching(req.family or None, req.size or None, req.search, req.moe_only, req.vision_only)
|
| 227 |
community = st.stats.community()
|
| 228 |
cached = {g.key: d for g in groups if (d := st.details.peek(g))}
|
| 229 |
-
prelim = [s for s in st.engine.score(groups, q, cached, community) if not
|
| 230 |
if (req.fits_only or q.vram_only) and q.hardware:
|
| 231 |
prelim = [s for s in prelim if s.best and s.best.fit and s.best.fit.tier not in ("no", "no_vram")]
|
| 232 |
prelim = sort_results(prelim, req.sort, community)
|
|
@@ -244,7 +248,7 @@ def create_app(state: State | None = None) -> FastAPI:
|
|
| 244 |
fresh = {g.key: d for g in missing if (d := st.details.peek(g))}
|
| 245 |
rescored = {s.group.key: s for s in st.engine.score([s.group for s in window], q, {**cached, **fresh}, community)}
|
| 246 |
prelim = [rescored.get(s.group.key, s) for s in prelim]
|
| 247 |
-
prelim = [s for s in prelim if not
|
| 248 |
if (req.fits_only or q.vram_only) and q.hardware:
|
| 249 |
prelim = [s for s in prelim if s.best and s.best.fit and s.best.fit.tier not in ("no", "no_vram")]
|
| 250 |
prelim = sort_results(prelim, req.sort, community)
|
|
@@ -252,7 +256,8 @@ def create_app(state: State | None = None) -> FastAPI:
|
|
| 252 |
notices = []
|
| 253 |
if st.catalogue.degraded:
|
| 254 |
notices.append("Showing a cached copy of the model list.")
|
| 255 |
-
|
|
|
|
| 256 |
if est:
|
| 257 |
notices.append(f"{est} result(s) are estimated from the parameter count until their files are read.")
|
| 258 |
return {"total": len(prelim), "offset": req.offset, "results": [s.to_dict(q) for s in page],
|
|
|
|
| 144 |
self.stats.invalidate()
|
| 145 |
|
| 146 |
|
| 147 |
+
def _hidden(s, details) -> bool:
|
| 148 |
+
return _only_auxiliary(s) or s.group.key in details.not_llama_cpp
|
| 149 |
+
|
| 150 |
+
|
| 151 |
def _only_auxiliary(s) -> bool:
|
| 152 |
"""Every file we could read is too small to be the model (a repo of draft models or
|
| 153 |
speculative-decoding heads, which nobody runs on their own), or below the chosen bit level."""
|
|
|
|
| 230 |
groups = st.catalogue.matching(req.family or None, req.size or None, req.search, req.moe_only, req.vision_only)
|
| 231 |
community = st.stats.community()
|
| 232 |
cached = {g.key: d for g in groups if (d := st.details.peek(g))}
|
| 233 |
+
prelim = [s for s in st.engine.score(groups, q, cached, community) if not _hidden(s, st.details)]
|
| 234 |
if (req.fits_only or q.vram_only) and q.hardware:
|
| 235 |
prelim = [s for s in prelim if s.best and s.best.fit and s.best.fit.tier not in ("no", "no_vram")]
|
| 236 |
prelim = sort_results(prelim, req.sort, community)
|
|
|
|
| 248 |
fresh = {g.key: d for g in missing if (d := st.details.peek(g))}
|
| 249 |
rescored = {s.group.key: s for s in st.engine.score([s.group for s in window], q, {**cached, **fresh}, community)}
|
| 250 |
prelim = [rescored.get(s.group.key, s) for s in prelim]
|
| 251 |
+
prelim = [s for s in prelim if not _hidden(s, st.details)]
|
| 252 |
if (req.fits_only or q.vram_only) and q.hardware:
|
| 253 |
prelim = [s for s in prelim if s.best and s.best.fit and s.best.fit.tier not in ("no", "no_vram")]
|
| 254 |
prelim = sort_results(prelim, req.sort, community)
|
|
|
|
| 256 |
notices = []
|
| 257 |
if st.catalogue.degraded:
|
| 258 |
notices.append("Showing a cached copy of the model list.")
|
| 259 |
+
# Only models whose files are still to be read; a model whose GGUFs can't be planned stays marked (est.)
|
| 260 |
+
est = sum(1 for s in page if s.best and s.best.estimated and s.group.key not in st.details.geometry_failed)
|
| 261 |
if est:
|
| 262 |
notices.append(f"{est} result(s) are estimated from the parameter count until their files are read.")
|
| 263 |
return {"total": len(prelim), "offset": req.offset, "results": [s.to_dict(q) for s in page],
|