Files
2026-07-13 12:12:21 +08:00

166 lines
5.2 KiB
Python

#!/usr/bin/env python3
"""Verify that all models in hf_models.json exist on HuggingFace and all
Ollama mappings in src/providers.rs point to valid Ollama registry entries.
Usage:
python3 scripts/verify_models.py # check both
python3 scripts/verify_models.py --hf # HuggingFace only
python3 scripts/verify_models.py --ollama # Ollama only
Exits with code 1 if any model is missing. Suitable for CI.
"""
import argparse
import json
import re
import sys
import time
import urllib.request
import urllib.error
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
HF_MODELS_PATH = REPO_ROOT / "data" / "hf_models.json"
PROVIDERS_RS_PATH = REPO_ROOT / "src" / "providers.rs"
HEADERS = {"User-Agent": "llmfit-verify/1.0"}
REQUEST_DELAY = 0.3 # seconds between requests to avoid rate limiting
def check_url(url: str) -> int:
"""GET a URL and return the HTTP status code, or -1 on error."""
try:
req = urllib.request.Request(url, headers=HEADERS)
resp = urllib.request.urlopen(req, timeout=10)
return resp.status
except urllib.error.HTTPError as e:
return e.code
except Exception:
return -1
# ---------------------------------------------------------------------------
# HuggingFace verification
# ---------------------------------------------------------------------------
def load_hf_models() -> list[str]:
"""Return list of HF repo names from hf_models.json."""
with open(HF_MODELS_PATH) as f:
data = json.load(f)
return [m["name"] for m in data]
def verify_hf(models: list[str]) -> list[str]:
"""Check each HF model exists. Returns list of missing model names."""
missing = []
total = len(models)
for i, name in enumerate(models, 1):
url = f"https://huggingface.co/api/models/{name}"
status = check_url(url)
if status == 200:
print(f" [{i}/{total}] ✓ {name}")
else:
print(f" [{i}/{total}] ✗ {name} (HTTP {status})")
missing.append(name)
time.sleep(REQUEST_DELAY)
return missing
# ---------------------------------------------------------------------------
# Ollama verification
# ---------------------------------------------------------------------------
def parse_ollama_tags() -> list[str]:
"""Extract unique Ollama tags from OLLAMA_MAPPINGS in providers.rs."""
src = PROVIDERS_RS_PATH.read_text()
# Find the OLLAMA_MAPPINGS block
match = re.search(
r"const OLLAMA_MAPPINGS:.*?=.*?\[(.+?)\];",
src,
re.DOTALL,
)
if not match:
print("ERROR: Could not find OLLAMA_MAPPINGS in providers.rs")
sys.exit(2)
block = match.group(1)
# Extract the second element of each tuple: ("hf_name", "ollama_tag")
tags = re.findall(r'\(\s*"[^"]+"\s*,\s*"([^"]+)"\s*\)', block)
# Deduplicate while preserving order
seen = set()
unique = []
for tag in tags:
if tag not in seen:
seen.add(tag)
unique.append(tag)
return unique
def verify_ollama(tags: list[str]) -> list[str]:
"""Check each Ollama tag exists. Returns list of missing tags."""
missing = []
total = len(tags)
for i, tag in enumerate(tags, 1):
url = f"https://ollama.com/library/{tag}"
status = check_url(url)
if status == 200:
print(f" [{i}/{total}] ✓ {tag}")
else:
print(f" [{i}/{total}] ✗ {tag} (HTTP {status})")
missing.append(tag)
time.sleep(REQUEST_DELAY)
return missing
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
def main():
parser = argparse.ArgumentParser(description="Verify model availability")
parser.add_argument("--hf", action="store_true", help="Check HuggingFace only")
parser.add_argument("--ollama", action="store_true", help="Check Ollama only")
args = parser.parse_args()
# Default: check both
check_hf = args.hf or not args.ollama
check_ollama = args.ollama or not args.hf
failures = False
if check_hf:
models = load_hf_models()
print(f"\n=== HuggingFace: checking {len(models)} models ===\n")
missing = verify_hf(models)
if missing:
failures = True
print(f"\n{len(missing)} HuggingFace model(s) not found:")
for m in missing:
print(f" - {m}")
else:
print(f"\n All {len(models)} HuggingFace models verified ✓")
if check_ollama:
tags = parse_ollama_tags()
print(f"\n=== Ollama: checking {len(tags)} tags ===\n")
missing = verify_ollama(tags)
if missing:
failures = True
print(f"\n{len(missing)} Ollama tag(s) not found:")
for t in missing:
print(f" - {t}")
else:
print(f"\n All {len(tags)} Ollama tags verified ✓")
print()
if failures:
print("FAIL: Some models are unavailable. Fix mappings or remove entries.")
sys.exit(1)
else:
print("PASS: All models verified.")
if __name__ == "__main__":
main()