codelion commited on
Commit
8d1298a
·
verified ·
1 Parent(s): 51c167d

Fall back to another repo's GGUF header; hide non-llama.cpp GGUFs

Browse files
Files changed (2) hide show
  1. app/detail.py +48 -21
  2. 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 header_candidate(repos: list[RepoFiles], params: float | None = None) -> tuple[str, QuantFile] | None:
162
- """The smallest real quant of the most downloaded repo: every quant shares the geometry,
163
- and a small single file keeps the tensor table in the part we read. Files too small to be
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
- return rf.repo, min(singles, key=lambda q: q.size or 0)
171
- return None
 
 
 
 
 
 
 
 
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
- cand = header_candidate(ordered, params)
261
- if cand:
262
- repo, q = cand
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 _only_auxiliary(s)]
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 _only_auxiliary(s)]
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
- est = sum(1 for s in page if s.best and s.best.estimated)
 
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],