fix(data): verify GGUF source provenance; unlink wrong-model quants

GGUF source candidates were matched by bare model name only, so models
sharing a name across orgs got linked to quants of the wrong weights:
tiny-random/gemma-3 (9M test stub) linked to mradermacher/gemma-3-GGUF
(a 2.5B model) — downloading 2.5 GB while llmfit predicted 0.0 GB disk.
tiny-random/phi-4 (3M) pointed at quants of microsoft/phi-4 (14.7B).

Scraper: candidate GGUF repos must now declare a base_model tag matching
the source repo. Mismatches are still accepted when the declared base has
~the same parameter count (safetensors total, +-30%) — that's a mirror or
re-upload of identical weights (e.g. unsloth re-uploads pointing at the
canonical upstream), not a wrong link.

Data: audited all 1,656 linked quant repos against the HF API with the
same rules — 19 wrong-model links removed (14 confirmed by parameter
mismatch, 5 unverifiable), 1,510 verified, 295 mirror links kept.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Alex Jones 2026-07-10 08:39:29 +01:00
parent 61f6a4705c
commit 63b2d3aada
2 changed files with 132 additions and 163 deletions

View file

@ -446,13 +446,7 @@
"vocab_size": 1000,
"moe_intermediate_size": 37,
"shared_expert_intermediate_size": null,
"_discovered": true,
"gguf_sources": [
{
"repo": "mradermacher/tiny-random-gpt2-GGUF",
"provider": "mradermacher"
}
]
"_discovered": true
},
{
"name": "Harley-ml/TinyWord2-128k",
@ -1825,13 +1819,7 @@
"moe_intermediate_size": 64,
"shared_expert_intermediate_size": null,
"_discovered": true,
"license": "apache-2.0",
"gguf_sources": [
{
"repo": "mradermacher/tiny-random-qwen3-GGUF",
"provider": "mradermacher"
}
]
"license": "apache-2.0"
},
{
"name": "np-cr/testing-qwen3",
@ -1889,13 +1877,7 @@
"vocab_size": 99,
"moe_intermediate_size": 37,
"shared_expert_intermediate_size": null,
"_discovered": true,
"gguf_sources": [
{
"repo": "mradermacher/tiny-random-gpt2-GGUF",
"provider": "mradermacher"
}
]
"_discovered": true
},
{
"name": "tiny-random/qwen3-next-moe",
@ -1981,21 +1963,7 @@
"vocab_size": null,
"moe_intermediate_size": null,
"shared_expert_intermediate_size": null,
"_discovered": true,
"gguf_sources": [
{
"repo": "unsloth/phi-4-GGUF",
"provider": "unsloth"
},
{
"repo": "bartowski/phi-4-GGUF",
"provider": "bartowski"
},
{
"repo": "mradermacher/phi-4-GGUF",
"provider": "mradermacher"
}
]
"_discovered": true
},
{
"name": "explosion-testing/llama2-kv-sharing",
@ -3237,13 +3205,7 @@
"vocab_size": 262208,
"moe_intermediate_size": 128,
"shared_expert_intermediate_size": null,
"_discovered": true,
"gguf_sources": [
{
"repo": "mradermacher/gemma-3-GGUF",
"provider": "mradermacher"
}
]
"_discovered": true
},
{
"name": "tiiuae/falcon-mamba-tiny-dev",
@ -3721,13 +3683,7 @@
"moe_intermediate_size": 512,
"shared_expert_intermediate_size": null,
"_discovered": true,
"license": "apache-2.0",
"gguf_sources": [
{
"repo": "mradermacher/pythia-14m-GGUF",
"provider": "mradermacher"
}
]
"license": "apache-2.0"
},
{
"name": "AAzurez/pythia-14mtest",
@ -4704,13 +4660,7 @@
"vocab_size": 32000,
"moe_intermediate_size": 768,
"shared_expert_intermediate_size": null,
"_discovered": true,
"gguf_sources": [
{
"repo": "mradermacher/llama2.c-stories15M-GGUF",
"provider": "mradermacher"
}
]
"_discovered": true
},
{
"name": "c-bone/CrystaLLM-pi_base",
@ -4851,13 +4801,7 @@
"moe_intermediate_size": 1024,
"shared_expert_intermediate_size": null,
"_discovered": true,
"license": "apache-2.0",
"gguf_sources": [
{
"repo": "mradermacher/pythia-31m-GGUF",
"provider": "mradermacher"
}
]
"license": "apache-2.0"
},
{
"name": "anwgpt/anwgpt4.1-chat",
@ -37851,14 +37795,14 @@
"shared_expert_intermediate_size": null,
"_discovered": true,
"gguf_sources": [
{
"repo": "unsloth/Qwen3.5-0.8B-GGUF",
"provider": "unsloth"
},
{
"repo": "ggml-org/Qwen3.5-0.8B-GGUF",
"provider": "ggml-org"
},
{
"repo": "unsloth/Qwen3.5-0.8B-GGUF",
"provider": "unsloth"
},
{
"repo": "mradermacher/Qwen3.5-0.8B-GGUF",
"provider": "mradermacher"
@ -44122,6 +44066,10 @@
"shared_expert_intermediate_size": null,
"_discovered": true,
"gguf_sources": [
{
"repo": "mradermacher/Llama-3.2-1B-Instruct-GGUF",
"provider": "mradermacher"
},
{
"repo": "unsloth/Llama-3.2-1B-Instruct-GGUF",
"provider": "unsloth"
@ -44129,10 +44077,6 @@
{
"repo": "bartowski/Llama-3.2-1B-Instruct-GGUF",
"provider": "bartowski"
},
{
"repo": "mradermacher/Llama-3.2-1B-Instruct-GGUF",
"provider": "mradermacher"
}
]
},
@ -64522,6 +64466,10 @@
"shared_expert_intermediate_size": null,
"_discovered": true,
"gguf_sources": [
{
"repo": "mradermacher/Llama-3.2-3B-Instruct-GGUF",
"provider": "mradermacher"
},
{
"repo": "unsloth/Llama-3.2-3B-Instruct-GGUF",
"provider": "unsloth"
@ -64529,10 +64477,6 @@
{
"repo": "bartowski/Llama-3.2-3B-Instruct-GGUF",
"provider": "bartowski"
},
{
"repo": "mradermacher/Llama-3.2-3B-Instruct-GGUF",
"provider": "mradermacher"
}
]
},
@ -82639,13 +82583,7 @@
"vocab_size": null,
"moe_intermediate_size": null,
"shared_expert_intermediate_size": null,
"_discovered": true,
"gguf_sources": [
{
"repo": "mradermacher/llama-2-7b-miniguanaco-GGUF",
"provider": "mradermacher"
}
]
"_discovered": true
},
{
"name": "davzoku/cria-llama2-7b-v1.3",
@ -85114,13 +85052,13 @@
"hf_likes": 63,
"release_date": "2024-03-13",
"license": "llama2",
"capabilities": [],
"gguf_sources": [
{
"repo": "mradermacher/CodeLlama-7b-Instruct-hf-GGUF",
"provider": "mradermacher"
}
],
"capabilities": []
]
},
{
"name": "microsoft/Orca-2-7b",
@ -87575,13 +87513,7 @@
"vocab_size": 32000,
"moe_intermediate_size": 14336,
"shared_expert_intermediate_size": null,
"_discovered": true,
"gguf_sources": [
{
"repo": "mradermacher/WizardLM-2-7B-GGUF",
"provider": "mradermacher"
}
]
"_discovered": true
},
{
"name": "castorini/rank_zephyr_7b_v1_full",
@ -89718,13 +89650,13 @@
"shared_expert_intermediate_size": null,
"_discovered": true,
"gguf_sources": [
{
"repo": "bartowski/Hermes-2-Pro-Mistral-7B-GGUF",
"provider": "bartowski"
},
{
"repo": "mradermacher/Hermes-2-Pro-Mistral-7B-GGUF",
"provider": "mradermacher"
},
{
"repo": "bartowski/Hermes-2-Pro-Mistral-7B-GGUF",
"provider": "bartowski"
}
]
},
@ -100461,13 +100393,13 @@
"shared_expert_intermediate_size": null,
"_discovered": true,
"gguf_sources": [
{
"repo": "bartowski/bagel-8b-v1.0-GGUF",
"provider": "bartowski"
},
{
"repo": "mradermacher/bagel-8b-v1.0-GGUF",
"provider": "mradermacher"
},
{
"repo": "bartowski/bagel-8b-v1.0-GGUF",
"provider": "bartowski"
}
]
},
@ -109164,13 +109096,7 @@
"vocab_size": 155136,
"moe_intermediate_size": 14336,
"shared_expert_intermediate_size": null,
"_discovered": true,
"gguf_sources": [
{
"repo": "mradermacher/zeta-2-GGUF",
"provider": "mradermacher"
}
]
"_discovered": true
},
{
"name": "ByteDance-Seed/Seed-Coder-8B-Instruct",
@ -114111,13 +114037,13 @@
"shared_expert_intermediate_size": null,
"_discovered": true,
"gguf_sources": [
{
"repo": "bartowski/dolphin-2.9.1-yi-1.5-9b-GGUF",
"provider": "bartowski"
},
{
"repo": "mradermacher/dolphin-2.9.1-yi-1.5-9b-GGUF",
"provider": "mradermacher"
},
{
"repo": "bartowski/dolphin-2.9.1-yi-1.5-9b-GGUF",
"provider": "bartowski"
}
]
},
@ -123143,13 +123069,13 @@
"hf_likes": 29,
"release_date": "2024-03-13",
"license": "llama2",
"capabilities": [],
"gguf_sources": [
{
"repo": "mradermacher/CodeLlama-13b-Instruct-hf-GGUF",
"provider": "mradermacher"
}
],
"capabilities": []
]
},
{
"name": "microsoft/Orca-2-13b",
@ -135292,10 +135218,6 @@
"active_experts": 1,
"active_parameters": 22238214144,
"gguf_sources": [
{
"repo": "bartowski/dolphin-2.9.1-mixtral-1x22b-GGUF",
"provider": "bartowski"
},
{
"repo": "mradermacher/dolphin-2.9.1-mixtral-1x22b-GGUF",
"provider": "mradermacher"
@ -137203,10 +137125,6 @@
"active_experts": 2,
"active_parameters": 12680687307,
"gguf_sources": [
{
"repo": "bartowski/Beyonder-4x7B-v3-GGUF",
"provider": "bartowski"
},
{
"repo": "mradermacher/Beyonder-4x7B-v3-GGUF",
"provider": "mradermacher"
@ -155181,13 +155099,13 @@
"hf_likes": 19,
"release_date": "2024-03-14",
"license": "llama2",
"capabilities": [],
"gguf_sources": [
{
"repo": "mradermacher/CodeLlama-34b-Instruct-hf-GGUF",
"provider": "mradermacher"
}
],
"capabilities": []
]
},
{
"name": "lmstudio-community/Qwen3.6-35B-A3B-MLX-4bit",
@ -164693,13 +164611,7 @@
"vocab_size": null,
"moe_intermediate_size": null,
"shared_expert_intermediate_size": null,
"_discovered": true,
"gguf_sources": [
{
"repo": "mradermacher/StableBeluga2-GGUF",
"provider": "mradermacher"
}
]
"_discovered": true
},
{
"name": "sophosympatheia/Midnight-Miqu-70B-v1.5",
@ -167221,10 +167133,6 @@
"shared_expert_intermediate_size": null,
"_discovered": true,
"gguf_sources": [
{
"repo": "bartowski/airoboros-dpo-70b-3.3-GGUF",
"provider": "bartowski"
},
{
"repo": "mradermacher/airoboros-dpo-70b-3.3-GGUF",
"provider": "mradermacher"
@ -168132,13 +168040,13 @@
"shared_expert_intermediate_size": null,
"_discovered": true,
"gguf_sources": [
{
"repo": "bartowski/dolphin-2.9.1-llama-3-70b-GGUF",
"provider": "bartowski"
},
{
"repo": "mradermacher/dolphin-2.9.1-llama-3-70b-GGUF",
"provider": "mradermacher"
},
{
"repo": "bartowski/dolphin-2.9.1-llama-3-70b-GGUF",
"provider": "bartowski"
}
]
},
@ -170483,13 +170391,7 @@
"is_moe": true,
"num_experts": 512,
"active_experts": 10,
"active_parameters": 5418496512,
"gguf_sources": [
{
"repo": "mradermacher/Huihui-Qwen3-Coder-Next-Opus-4.6-Reasoning-Distilled-abliterated-GGUF",
"provider": "mradermacher"
}
]
"active_parameters": 5418496512
},
{
"name": "Intel/Qwen3-Coder-Next-int4-AutoRound",
@ -177651,13 +177553,7 @@
"is_moe": true,
"num_experts": 128,
"active_experts": 8,
"active_parameters": 25713366280,
"gguf_sources": [
{
"repo": "mradermacher/Rio-3.0-Open-GGUF",
"provider": "mradermacher"
}
]
"active_parameters": 25713366280
},
{
"name": "lmstudio-community/Qwen3-235B-A22B-Instruct-2507-MLX-4bit",

View file

@ -1104,20 +1104,91 @@ def _model_gguf_repo_candidates(repo_id: str) -> list[tuple[str, str]]:
return candidates
def check_gguf_repo_exists(repo_id: str) -> bool:
"""Check if a HuggingFace repo exists and has GGUF files."""
def _base_models_from_tags(tags: list) -> list[str]:
"""Extract base-model repo ids from HF tags.
Quant repos carry tags like `base_model:tomaszki/gemma-3` and
`base_model:quantized:tomaszki/gemma-3` — the repo id is always the
segment after the last colon.
"""
return [
t.rsplit(":", 1)[-1].lower()
for t in tags
if isinstance(t, str) and t.startswith("base_model:")
]
_REPO_PARAMS_CACHE: dict[str, int | None] = {}
_MIRROR_PARAMS_TOLERANCE = 0.30
def _repo_total_params(repo_id: str) -> int | None:
"""Total parameter count of a repo from its safetensors metadata."""
if repo_id in _REPO_PARAMS_CACHE:
return _REPO_PARAMS_CACHE[repo_id]
url = f"{HF_API}/{repo_id}"
req = urllib.request.Request(url, headers=_auth_headers())
total = None
try:
with urllib.request.urlopen(req, timeout=10) as resp:
info = json.loads(resp.read().decode())
st = info.get("safetensors") or {}
raw = st.get("total")
total = int(raw) if raw else None
except Exception:
pass
_REPO_PARAMS_CACHE[repo_id] = total
return total
def check_gguf_repo_exists(
repo_id: str,
source_repo_id: str | None = None,
source_params: int | None = None,
) -> bool:
"""Check that a HuggingFace repo exists, has GGUF files, and — when the
repo declares `base_model` tags — was actually quantized from
`source_repo_id`.
Candidate repo names are built from the bare model name only, so different
orgs' models with the same name (e.g. `tiny-random/gemma-3` vs
`tomaszki/gemma-3`) would otherwise be linked to the wrong quant.
A base_model mismatch is still accepted when the declared base has ~the
same parameter count as the source model (`source_params`): that's a
mirror/re-upload of the same weights (e.g. unsloth re-uploads pointing at
the canonical upstream). Repos without base_model tags are accepted as
before (unverifiable).
"""
url = f"{HF_API}/{repo_id}"
req = urllib.request.Request(url, headers=_auth_headers())
try:
with urllib.request.urlopen(req, timeout=10) as resp:
info = json.loads(resp.read().decode())
tags = info.get("tags", [])
return "gguf" in tags
if "gguf" not in tags:
return False
if source_repo_id:
bases = _base_models_from_tags(tags)
if bases and source_repo_id.lower() not in bases:
if not source_params:
return False
base_params = next(
(p for p in (_repo_total_params(b) for b in bases) if p),
None,
)
if not base_params:
return False
ratio = base_params / source_params
return abs(ratio - 1.0) <= _MIRROR_PARAMS_TOLERANCE
return True
except Exception:
return False
def _resolve_gguf_sources(repo_id: str) -> tuple[list[dict], list[tuple[str, bool]]]:
def _resolve_gguf_sources(
repo_id: str, source_params: int | None = None
) -> tuple[list[dict], list[tuple[str, bool]]]:
"""Resolve GGUF sources for a single model repo.
Returns (sources, checks) where checks is [(candidate_repo, exists), ...].
@ -1125,7 +1196,9 @@ def _resolve_gguf_sources(repo_id: str) -> tuple[list[dict], list[tuple[str, boo
sources: list[dict] = []
checks: list[tuple[str, bool]] = []
for provider, candidate_repo in _model_gguf_repo_candidates(repo_id):
exists = check_gguf_repo_exists(candidate_repo)
exists = check_gguf_repo_exists(
candidate_repo, source_repo_id=repo_id, source_params=source_params
)
checks.append((candidate_repo, exists))
if exists:
sources.append({"repo": candidate_repo, "provider": provider})
@ -1145,7 +1218,7 @@ def enrich_gguf_sources(models: list[dict], threads: int = 1) -> int:
total = len(models)
from datetime import datetime, timezone
to_check: list[tuple[int, str]] = []
to_check: list[tuple[int, str, int | None]] = []
for i, model in enumerate(models, 1):
repo_id = model["name"]
@ -1159,7 +1232,7 @@ def enrich_gguf_sources(models: list[dict], threads: int = 1) -> int:
sources = cache[repo_id]["sources"]
cache_hits += 1
else:
to_check.append((i, repo_id))
to_check.append((i, repo_id, model.get("parameters_raw")))
continue
if sources:
@ -1179,8 +1252,8 @@ def enrich_gguf_sources(models: list[dict], threads: int = 1) -> int:
enriched += 1
if threads <= 1:
for idx, repo_id in to_check:
sources, checks = _resolve_gguf_sources(repo_id)
for idx, repo_id, params_raw in to_check:
sources, checks = _resolve_gguf_sources(repo_id, params_raw)
print(f" [{idx}/{total}] {repo_id}")
for candidate_repo, exists in checks:
mark = "✓" if exists else "✗"
@ -1191,8 +1264,8 @@ def enrich_gguf_sources(models: list[dict], threads: int = 1) -> int:
print(f" Using {threads} threads for GGUF source checks")
future_to_meta: dict[concurrent.futures.Future, tuple[int, str]] = {}
with concurrent.futures.ThreadPoolExecutor(max_workers=threads) as executor:
for idx, repo_id in to_check:
future = executor.submit(_resolve_gguf_sources, repo_id)
for idx, repo_id, params_raw in to_check:
future = executor.submit(_resolve_gguf_sources, repo_id, params_raw)
future_to_meta[future] = (idx, repo_id)
for future in concurrent.futures.as_completed(future_to_meta):