Spaces:
Running
Running
| #!/usr/bin/env python3 | |
| """Build an HTML Open LLM Distribution Leaderboard from Hugging Face Hub metadata.""" | |
| from __future__ import annotations | |
| import argparse | |
| import csv | |
| import html | |
| import json | |
| import os | |
| import re | |
| import shutil | |
| import tempfile | |
| import time | |
| import urllib.error | |
| import urllib.parse | |
| import urllib.request | |
| from collections import Counter, defaultdict | |
| from datetime import datetime, timezone | |
| from pathlib import Path | |
| from typing import Any | |
| HF_MODELS_API = "https://huggingface.co/api/models" | |
| HF_ROUTER_MODELS_API = "https://router.huggingface.co/v1/models" | |
| USER_AGENT = "leaderboard/0.1" | |
| DEFAULT_MIN_DOWNLOADS = 100_000 | |
| DEFAULT_TOP_N = 250 | |
| DEFAULT_MAX_AGE_DAYS = 365 | |
| DEFAULT_BUCKET_DIR = Path(os.environ.get("OPEN_MODEL_BUCKET_DIR", "/data")) | |
| DEFAULT_LOCAL_DATA_DIR = Path(os.environ.get("OPEN_MODEL_LOCAL_DATA_DIR", "data")) | |
| DEFAULT_CREATORS = [ | |
| "CohereLabs", | |
| "EssentialAI", | |
| "MiniMaxAI", | |
| "Qwen", | |
| "Sao10K", | |
| "aisingapore", | |
| "allenai", | |
| "alpindale", | |
| "baidu", | |
| "deepcogito", | |
| "deepseek-ai", | |
| "google", | |
| "inclusionAI", | |
| "meta-llama", | |
| "moonshotai", | |
| "nvidia", | |
| "openai", | |
| "pearl-ai", | |
| "stepfun-ai", | |
| "swiss-ai", | |
| "utter-project", | |
| "zai-org", | |
| ] | |
| EXPAND_FIELDS = [ | |
| "author", | |
| "cardData", | |
| "createdAt", | |
| "downloads", | |
| "downloadsAllTime", | |
| "evalResults", | |
| "gated", | |
| "gguf", | |
| "inferenceProviderMapping", | |
| "lastModified", | |
| "library_name", | |
| "likes", | |
| "model-index", | |
| "pipeline_tag", | |
| "private", | |
| "safetensors", | |
| "sha", | |
| "siblings", | |
| "tags", | |
| "transformersInfo", | |
| "trendingScore", | |
| ] | |
| MODEL_CSV_FIELDS = [ | |
| "id", | |
| "author", | |
| "downloads", | |
| "downloads_all_time", | |
| "likes", | |
| "trending_score", | |
| "parameter_count", | |
| "parameter_count_b", | |
| "capacity_class", | |
| "pipeline_tag", | |
| "library_name", | |
| "license", | |
| "base_model_relation", | |
| "gated", | |
| "private", | |
| "created_at", | |
| "last_modified", | |
| "sha", | |
| "base_models", | |
| "has_safetensors", | |
| "has_gguf", | |
| "has_tokenizer", | |
| "has_chat_template", | |
| "siblings_count", | |
| "safetensors_files", | |
| "gguf_files", | |
| "bin_files", | |
| "eval_results_count", | |
| "inference_provider_count", | |
| "inference_providers", | |
| "live_inference_providers", | |
| "supports_tools_any", | |
| "supports_structured_output_any", | |
| "max_context_length", | |
| "router_served", | |
| "router_provider_count", | |
| "router_providers", | |
| "router_supports_tools_any", | |
| "router_supports_structured_output_any", | |
| "router_max_context_length", | |
| "tags", | |
| "group_key", | |
| "candidate_class", | |
| "candidate", | |
| "candidate_reason", | |
| ] | |
| GROUP_CSV_FIELDS = [ | |
| "rank", | |
| "group_key", | |
| "representative_id", | |
| "author", | |
| "downloads_per_month", | |
| "group_downloads_per_month", | |
| "representative_downloads_per_month", | |
| "downloads", | |
| "group_downloads", | |
| "representative_downloads", | |
| "downloads_all_time", | |
| "group_downloads_all_time", | |
| "representative_downloads_all_time", | |
| "likes_per_month", | |
| "group_likes_per_month", | |
| "representative_likes_per_month", | |
| "likes", | |
| "group_likes", | |
| "representative_likes", | |
| "parameter_count_b", | |
| "capacity_class", | |
| "candidate_class", | |
| "pipeline_tag", | |
| "license", | |
| "router_served", | |
| "supports_tools", | |
| "supports_structured_output", | |
| "artifact_count", | |
| "artifacts", | |
| "artifact_signals", | |
| "created_at", | |
| "last_modified", | |
| ] | |
| SECONDS_PER_DAY = 86_400 | |
| DAYS_PER_MONTH = 365.2425 / 12 | |
| MIN_DOWNLOAD_RATE_AGE_DAYS = DAYS_PER_MONTH | |
| MIN_LIKE_RATE_AGE_DAYS = DAYS_PER_MONTH | |
| ARTIFACT_REVISION_SUFFIX_RE = re.compile(r"-(?:gguf|awq|gptq|fp8|fp4|mxfp8|nvfp4)-v[0-9]+$") | |
| ARTIFACT_SUFFIX_RE = re.compile( | |
| r"-(?:" | |
| r"gguf|awq|gptq|fp8|fp4|mxfp8|nvfp4|bf16|fp16|int2|int3|int4|int8|" | |
| r"q[2-8](?:-(?:0|1|k|m|s|l))*|" | |
| r"4bit|8bit|bnb|bitsandbytes|mlx|exl2|onnx|openvino|tensorrt|" | |
| r"modelopt|qat|quantized|quant|compressed|safetensors|hf" | |
| r")$" | |
| ) | |
| QAT_SUFFIX_RE = re.compile(r"-qat-w[0-9]+a[0-9]+-ct$") | |
| LLM_PIPELINES = { | |
| "text-generation", | |
| "image-text-to-text", | |
| "video-text-to-text", | |
| "audio-text-to-text", | |
| "any-to-any", | |
| "text-to-audio", | |
| } | |
| EXCLUDED_PIPELINES = { | |
| "feature-extraction", | |
| "sentence-similarity", | |
| "text-ranking", | |
| "text-classification", | |
| "token-classification", | |
| "fill-mask", | |
| "automatic-speech-recognition", | |
| "text-to-speech", | |
| "voice-activity-detection", | |
| "image-classification", | |
| "image-segmentation", | |
| "object-detection", | |
| "zero-shot-image-classification", | |
| "zero-shot-object-detection", | |
| "image-text-to-image", | |
| "text-to-image", | |
| "image-to-image", | |
| "image-to-video", | |
| "text-to-video", | |
| "unconditional-image-generation", | |
| "image-to-3d", | |
| "text-to-3d", | |
| "robotics", | |
| "time-series-forecasting", | |
| "graph-ml", | |
| } | |
| TEXT_MODEL_TERMS = ( | |
| "instruct", | |
| "chat", | |
| "assistant", | |
| "reasoning", | |
| "thinking", | |
| "coder", | |
| "code", | |
| "llm", | |
| "vlm", | |
| "omni", | |
| "gpt-oss", | |
| "kimi", | |
| "glm", | |
| "llama", | |
| "qwen", | |
| "gemma", | |
| "deepseek", | |
| "nemotron", | |
| "olmo", | |
| "apertus", | |
| "eurollm", | |
| ) | |
| CODING_MODEL_NAME_RE = re.compile( | |
| r"(^|[-_/])(?:coder|coding|code)(?:$|[-_/])|" | |
| r"(^|[-_/])(?:codellama|codeqwen|opencoder|starcoder)[a-z0-9.]*($|[-_/])" | |
| ) | |
| PACKAGING_RELATION_TERMS = { | |
| "adapter", | |
| "compressed", | |
| "converted", | |
| "gguf", | |
| "quantized", | |
| } | |
| PACKAGING_SIGNAL_TERMS = { | |
| "2bit", | |
| "3bit", | |
| "4bit", | |
| "8bit", | |
| "awq", | |
| "bf16", | |
| "bnb", | |
| "compressed", | |
| "exl2", | |
| "fp4", | |
| "fp8", | |
| "gguf", | |
| "gptq", | |
| "int2", | |
| "int3", | |
| "int4", | |
| "int8", | |
| "llama.cpp", | |
| "mlx", | |
| "mxfp8", | |
| "nvfp4", | |
| "qat", | |
| "q2", | |
| "q3", | |
| "q4", | |
| "q5", | |
| "q6", | |
| "q8", | |
| "quant", | |
| "quantized", | |
| } | |
| DERIVATIVE_STRONG_TOKENS = { | |
| "adapter", | |
| "awq", | |
| "draft", | |
| "fp4", | |
| "fp8", | |
| "gguf", | |
| "gptq", | |
| "lora", | |
| "mlp", | |
| "mlx", | |
| "mxfp8", | |
| "nvfp4", | |
| "qat", | |
| "quant", | |
| "quantized", | |
| "speculative", | |
| "unquantized", | |
| } | |
| DERIVATIVE_AMBIGUOUS_TOKENS = { | |
| "assistant", | |
| } | |
| DERIVATIVE_COMPONENT_PREFIX_TOKENS = { | |
| "adapter", | |
| "assistant", | |
| "draft", | |
| "lora", | |
| "mlp", | |
| "speculative", | |
| } | |
| DERIVATIVE_SAFE_SUFFIX_TOKENS = DERIVATIVE_STRONG_TOKENS | DERIVATIVE_AMBIGUOUS_TOKENS | { | |
| "block", | |
| "dynamic", | |
| "static", | |
| "turbo", | |
| } | |
| PEER_VARIANT_TOKENS = { | |
| "base", | |
| "chat", | |
| "coder", | |
| "coding", | |
| "flash", | |
| "instruct", | |
| "math", | |
| "pro", | |
| "reasoning", | |
| "thinking", | |
| "vision", | |
| "vl", | |
| } | |
| MODEL_FAMILY_TOKENS = { | |
| "apertus", | |
| "deepseek", | |
| "ernie", | |
| "gemma", | |
| "glm", | |
| "gpt", | |
| "kimi", | |
| "llama", | |
| "minimax", | |
| "mistral", | |
| "mixtral", | |
| "nemotron", | |
| "olmo", | |
| "phi", | |
| "qwen", | |
| "qwen2", | |
| "qwen3", | |
| "step", | |
| } | |
| GLOBAL_ARTIFACT_SEARCH_QUERIES = ( | |
| "gguf", | |
| "awq", | |
| "gptq", | |
| "fp8", | |
| "fp4", | |
| "nvfp4", | |
| "quantized", | |
| "mlx", | |
| "llama.cpp", | |
| ) | |
| RELATED_ARTIFACT_SUFFIXES = ( | |
| "gguf", | |
| "awq", | |
| "gptq", | |
| "fp8", | |
| "fp4", | |
| "nvfp4", | |
| "mlx", | |
| ) | |
| RELATED_VARIANT_TOKENS = { | |
| "base", | |
| "chat", | |
| "flash", | |
| "instruct", | |
| "it", | |
| "pro", | |
| } | |
| DEFAULT_DISCOVERY_MAX_RESULTS_PER_QUERY = 500 | |
| DEFAULT_RELATED_DISCOVERY_SEED_LIMIT = 12 | |
| EXCLUDED_TERMS = ( | |
| "embedding", | |
| "embed", | |
| "reranker", | |
| "rerank", | |
| "whisper", | |
| "wav2vec", | |
| "speaker", | |
| "stable-diffusion", | |
| "diffusers", | |
| "flux", | |
| "sdxl", | |
| "clip", | |
| "siglip", | |
| "depth-anything", | |
| "ocr", | |
| "optical-character-recognition", | |
| "document-parse", | |
| "document-parser", | |
| "document-understanding", | |
| "pdf-parser", | |
| "receipt", | |
| ) | |
| EXCLUDED_TOKEN_TERMS = { | |
| "asr", | |
| "encoder", | |
| } | |
| EXCLUDED_AUDIO_TOKEN_TERMS = { | |
| "speech", | |
| } | |
| SPECIAL_ARCHITECTURE_TERMS = ( | |
| "dllm", | |
| "diffusion-language", | |
| "diffusion_language", | |
| "diffusion language", | |
| "text-diffusion", | |
| "text_diffusion", | |
| "language-diffusion", | |
| "language_diffusion", | |
| ) | |
| def now_utc() -> datetime: | |
| return datetime.now(timezone.utc) | |
| def stamp(dt: datetime | None = None) -> str: | |
| return (dt or now_utc()).strftime("%Y%m%dT%H%M%SZ") | |
| def iso(dt: datetime | None = None) -> str: | |
| return (dt or now_utc()).isoformat(timespec="seconds").replace("+00:00", "Z") | |
| def env_creators() -> list[str]: | |
| raw = os.environ.get("OPEN_MODEL_CREATORS", "") | |
| extra = [item.strip() for item in raw.replace("\n", ",").split(",") if item.strip()] | |
| return unique_preserve_order(DEFAULT_CREATORS + extra) | |
| def unique_preserve_order(values: list[str]) -> list[str]: | |
| seen: set[str] = set() | |
| out: list[str] = [] | |
| for value in values: | |
| if value in seen: | |
| continue | |
| seen.add(value) | |
| out.append(value) | |
| return out | |
| def str_list(value: Any) -> list[str]: | |
| if value is None: | |
| return [] | |
| if isinstance(value, str): | |
| return [value] | |
| if isinstance(value, list): | |
| return [item for item in value if isinstance(item, str)] | |
| return [] | |
| def request_json(url: str, token: str | None, retries: int = 4) -> tuple[Any, dict[str, str]]: | |
| headers = {"User-Agent": USER_AGENT} | |
| if token: | |
| headers["Authorization"] = f"Bearer {token}" | |
| request = urllib.request.Request(url, headers=headers) | |
| for attempt in range(retries + 1): | |
| try: | |
| with urllib.request.urlopen(request, timeout=90) as response: | |
| payload = json.loads(response.read().decode("utf-8")) | |
| return payload, dict(response.headers.items()) | |
| except urllib.error.HTTPError as err: | |
| if err.code in {429, 500, 502, 503, 504} and attempt < retries: | |
| time.sleep(min(2**attempt, 10)) | |
| continue | |
| body = err.read().decode("utf-8", errors="replace") | |
| raise RuntimeError(f"HTTP {err.code} for {url}: {body[:500]}") from err | |
| except urllib.error.URLError as err: | |
| if attempt < retries: | |
| time.sleep(min(2**attempt, 10)) | |
| continue | |
| raise RuntimeError(f"Request failed for {url}: {err}") from err | |
| raise RuntimeError(f"Request failed after retries: {url}") | |
| def parse_next_link(link_header: str | None) -> str | None: | |
| if not link_header: | |
| return None | |
| for part in link_header.split(","): | |
| match = re.search(r'<([^>]+)>;\s*rel="next"', part) | |
| if match: | |
| return match.group(1) | |
| return None | |
| def fetch_router_models(token: str | None) -> list[dict[str, Any]]: | |
| payload, _ = request_json(HF_ROUTER_MODELS_API, token) | |
| records = payload.get("data", []) if isinstance(payload, dict) else [] | |
| return [item for item in records if isinstance(item, dict) and item.get("id")] | |
| def router_creators(router_records: list[dict[str, Any]]) -> list[str]: | |
| creators = [] | |
| for record in router_records: | |
| owner = record.get("owned_by") | |
| if isinstance(owner, str) and owner: | |
| creators.append(owner) | |
| continue | |
| model_id = record.get("id") | |
| if isinstance(model_id, str) and "/" in model_id: | |
| creators.append(model_id.split("/", 1)[0]) | |
| return unique_preserve_order(creators) | |
| def list_creator_models( | |
| creator: str, | |
| *, | |
| page_size: int, | |
| sort: str, | |
| direction: int, | |
| token: str | None, | |
| limit: int | None, | |
| sleep_seconds: float, | |
| ) -> list[dict[str, Any]]: | |
| params: list[tuple[str, str | int]] = [ | |
| ("author", creator), | |
| ("limit", page_size), | |
| ("sort", sort), | |
| ("direction", direction), | |
| ] | |
| params.extend(("expand", field) for field in EXPAND_FIELDS) | |
| url = f"{HF_MODELS_API}?{urllib.parse.urlencode(params, doseq=True)}" | |
| models: list[dict[str, Any]] = [] | |
| while url: | |
| payload, headers = request_json(url, token) | |
| if not isinstance(payload, list): | |
| raise RuntimeError(f"Expected list payload for {creator}, got {type(payload).__name__}") | |
| for item in payload: | |
| if isinstance(item, dict): | |
| models.append(item) | |
| if limit is not None and len(models) >= limit: | |
| return models | |
| url = parse_next_link(headers.get("Link") or headers.get("link")) | |
| if url and sleep_seconds > 0: | |
| time.sleep(sleep_seconds) | |
| return models | |
| def list_search_models( | |
| search: str, | |
| *, | |
| page_size: int, | |
| min_downloads: int, | |
| max_results: int, | |
| token: str | None, | |
| sleep_seconds: float, | |
| ) -> list[dict[str, Any]]: | |
| params: list[tuple[str, str | int]] = [ | |
| ("search", search), | |
| ("limit", page_size), | |
| ("sort", "downloads"), | |
| ("direction", -1), | |
| ] | |
| params.extend(("expand", field) for field in EXPAND_FIELDS) | |
| url = f"{HF_MODELS_API}?{urllib.parse.urlencode(params, doseq=True)}" | |
| models: list[dict[str, Any]] = [] | |
| while url and len(models) < max_results: | |
| payload, headers = request_json(url, token) | |
| if not isinstance(payload, list): | |
| raise RuntimeError(f"Expected list payload for search {search}, got {type(payload).__name__}") | |
| stop = False | |
| for item in payload: | |
| if not isinstance(item, dict): | |
| continue | |
| downloads = int_metric(item.get("downloads")) | |
| if downloads <= min_downloads: | |
| stop = True | |
| break | |
| models.append(item) | |
| if len(models) >= max_results: | |
| break | |
| if stop or len(models) >= max_results: | |
| break | |
| url = parse_next_link(headers.get("Link") or headers.get("link")) | |
| if url and sleep_seconds > 0: | |
| time.sleep(sleep_seconds) | |
| return models | |
| def shortened_variant_slug(slug: str) -> str: | |
| tokens = slug_tokens(slug) | |
| while len(tokens) > 2 and tokens[-1] in RELATED_VARIANT_TOKENS: | |
| tokens.pop() | |
| return "-".join(tokens) | |
| def discovery_queries(seed_rows: list[dict[str, Any]], seed_limit: int = DEFAULT_RELATED_DISCOVERY_SEED_LIMIT) -> list[str]: | |
| queries = list(GLOBAL_ARTIFACT_SEARCH_QUERIES) | |
| ordered = sorted( | |
| seed_rows, | |
| key=lambda row: (int_metric(row.get("downloads")), int_metric(row.get("downloads_all_time"))), | |
| reverse=True, | |
| ) | |
| for row in ordered[:seed_limit]: | |
| slug = slug_dedupe_key(str(row.get("id") or "")) | |
| if not has_model_shape(slug): | |
| continue | |
| candidate_slugs = unique_preserve_order([slug, shortened_variant_slug(slug)]) | |
| for candidate_slug in candidate_slugs: | |
| if not candidate_slug or not has_model_shape(candidate_slug): | |
| continue | |
| for suffix in RELATED_ARTIFACT_SUFFIXES: | |
| queries.append(f"{candidate_slug}-{suffix}") | |
| return unique_preserve_order(queries) | |
| def discover_additional_models( | |
| *, | |
| seed_rows: list[dict[str, Any]], | |
| existing_ids: set[str], | |
| min_downloads: int, | |
| page_size: int, | |
| token: str | None, | |
| sleep_seconds: float, | |
| max_results_per_query: int, | |
| seed_limit: int, | |
| ) -> tuple[list[dict[str, Any]], list[dict[str, str]], dict[str, Any]]: | |
| discovered: dict[str, dict[str, Any]] = {} | |
| errors: list[dict[str, str]] = [] | |
| queries = discovery_queries(seed_rows, seed_limit=seed_limit) | |
| query_counts: dict[str, int] = {} | |
| for query in queries: | |
| try: | |
| rows = list_search_models( | |
| query, | |
| page_size=page_size, | |
| min_downloads=min_downloads, | |
| max_results=max_results_per_query, | |
| token=token, | |
| sleep_seconds=sleep_seconds, | |
| ) | |
| except Exception as err: # noqa: BLE001 | |
| errors.append({"discovery_query": query, "error": str(err)}) | |
| continue | |
| query_counts[query] = len(rows) | |
| for row in rows: | |
| model_id = row.get("id") or row.get("modelId") | |
| if not isinstance(model_id, str) or not model_id or model_id in existing_ids: | |
| continue | |
| discovered[model_id] = row | |
| summary = { | |
| "enabled": True, | |
| "queries": len(queries), | |
| "query_counts": query_counts, | |
| "records": len(discovered), | |
| } | |
| return list(discovered.values()), errors, summary | |
| def find_license(record: dict[str, Any]) -> str: | |
| card = record.get("cardData") | |
| if isinstance(card, dict) and isinstance(card.get("license"), str): | |
| return card["license"] | |
| for tag in str_list(record.get("tags")): | |
| if tag.startswith("license:"): | |
| return tag.split(":", 1)[1] | |
| return "" | |
| def find_base_models(record: dict[str, Any]) -> list[str]: | |
| values: list[str] = [] | |
| card = record.get("cardData") | |
| if isinstance(card, dict): | |
| values.extend(str_list(card.get("base_model"))) | |
| for tag in str_list(record.get("tags")): | |
| if tag.startswith("base_model:"): | |
| values.append(tag.split(":", 1)[1]) | |
| return unique_preserve_order(values) | |
| def find_base_model_relation(record: dict[str, Any]) -> str: | |
| card = record.get("cardData") | |
| if isinstance(card, dict) and isinstance(card.get("base_model_relation"), str): | |
| return str(card["base_model_relation"]) | |
| return "" | |
| def file_counts(record: dict[str, Any]) -> Counter[str]: | |
| counts: Counter[str] = Counter() | |
| for sibling in record.get("siblings") or []: | |
| if not isinstance(sibling, dict): | |
| continue | |
| name = sibling.get("rfilename") | |
| if not isinstance(name, str): | |
| continue | |
| lower = name.lower() | |
| if lower.endswith(".safetensors"): | |
| counts["safetensors"] += 1 | |
| elif lower.endswith(".gguf"): | |
| counts["gguf"] += 1 | |
| elif lower.endswith(".bin"): | |
| counts["bin"] += 1 | |
| if lower.endswith("tokenizer.json") or lower.endswith("tokenizer_config.json"): | |
| counts["tokenizer"] += 1 | |
| if lower.endswith("chat_template.jinja"): | |
| counts["chat_template"] += 1 | |
| return counts | |
| def extract_parameter_count(record: dict[str, Any]) -> int | None: | |
| safetensors = record.get("safetensors") | |
| if isinstance(safetensors, dict): | |
| total = safetensors.get("total") | |
| if isinstance(total, int): | |
| return total | |
| params = safetensors.get("parameters") | |
| if isinstance(params, dict): | |
| values = [value for value in params.values() if isinstance(value, int)] | |
| if values: | |
| return max(values) | |
| gguf = record.get("gguf") | |
| if isinstance(gguf, dict): | |
| for key in ("total", "parameters", "parameter_count", "parameterCount"): | |
| value = gguf.get(key) | |
| if isinstance(value, int): | |
| return value | |
| return None | |
| def capacity_class(parameter_count: int | None) -> str: | |
| if parameter_count is None: | |
| return "unknown" | |
| billion = parameter_count / 1_000_000_000 | |
| if billion <= 1: | |
| return "tiny" | |
| if billion <= 20: | |
| return "small" | |
| if billion <= 50: | |
| return "medium" | |
| return "large" | |
| def provider_summary(mapping: Any) -> dict[str, Any]: | |
| if not isinstance(mapping, list): | |
| mapping = [] | |
| providers: list[str] = [] | |
| live: list[str] = [] | |
| supports_tools = False | |
| supports_structured = False | |
| context_lengths: list[int] = [] | |
| for item in mapping: | |
| if not isinstance(item, dict): | |
| continue | |
| provider = item.get("provider") | |
| if isinstance(provider, str): | |
| providers.append(provider) | |
| if item.get("status") == "live": | |
| live.append(provider) | |
| features = item.get("features") | |
| if isinstance(features, dict): | |
| supports_tools = supports_tools or features.get("toolCalling") is True | |
| supports_structured = supports_structured or features.get("structuredOutput") is True | |
| details = item.get("providerDetails") | |
| if isinstance(details, dict): | |
| context = details.get("context_length") | |
| if isinstance(context, int): | |
| context_lengths.append(context) | |
| return { | |
| "providers": sorted(set(providers)), | |
| "live": sorted(set(live)), | |
| "supports_tools_any": supports_tools, | |
| "supports_structured_output_any": supports_structured, | |
| "max_context_length": max(context_lengths) if context_lengths else "", | |
| } | |
| def router_summary(router_entry: dict[str, Any] | None) -> dict[str, Any]: | |
| if not router_entry: | |
| return { | |
| "router_served": False, | |
| "router_provider_count": 0, | |
| "router_providers": [], | |
| "router_supports_tools_any": False, | |
| "router_supports_structured_output_any": False, | |
| "router_max_context_length": "", | |
| } | |
| providers = router_entry.get("providers") | |
| if not isinstance(providers, list): | |
| providers = [] | |
| provider_names: list[str] = [] | |
| context_lengths: list[int] = [] | |
| supports_tools = False | |
| supports_structured = False | |
| for provider in providers: | |
| if not isinstance(provider, dict): | |
| continue | |
| name = provider.get("provider") | |
| if isinstance(name, str): | |
| provider_names.append(name) | |
| supports_tools = supports_tools or provider.get("supports_tools") is True | |
| supports_structured = supports_structured or provider.get("supports_structured_output") is True | |
| context = provider.get("context_length") | |
| if isinstance(context, int): | |
| context_lengths.append(context) | |
| return { | |
| "router_served": True, | |
| "router_provider_count": len(provider_names), | |
| "router_providers": sorted(set(provider_names)), | |
| "router_supports_tools_any": supports_tools, | |
| "router_supports_structured_output_any": supports_structured, | |
| "router_max_context_length": max(context_lengths) if context_lengths else "", | |
| } | |
| def normalize_record(record: dict[str, Any], router_entry: dict[str, Any] | None) -> dict[str, Any]: | |
| counts = file_counts(record) | |
| parameter_count = extract_parameter_count(record) | |
| provider = provider_summary(record.get("inferenceProviderMapping")) | |
| router = router_summary(router_entry) | |
| tags = str_list(record.get("tags")) | |
| model_id = record.get("id") or record.get("modelId") or "" | |
| normalized = { | |
| "id": model_id, | |
| "author": record.get("author") or (str(model_id).split("/", 1)[0] if "/" in str(model_id) else ""), | |
| "downloads": record.get("downloads") if isinstance(record.get("downloads"), int) else 0, | |
| "downloads_all_time": record.get("downloadsAllTime") | |
| if isinstance(record.get("downloadsAllTime"), int) | |
| else "", | |
| "likes": record.get("likes") if isinstance(record.get("likes"), int) else 0, | |
| "trending_score": record.get("trendingScore") | |
| if isinstance(record.get("trendingScore"), int) | |
| else 0, | |
| "parameter_count": parameter_count or "", | |
| "parameter_count_b": round(parameter_count / 1_000_000_000, 3) if parameter_count else "", | |
| "capacity_class": capacity_class(parameter_count), | |
| "pipeline_tag": record.get("pipeline_tag") or "", | |
| "library_name": record.get("library_name") or "", | |
| "license": find_license(record), | |
| "base_model_relation": find_base_model_relation(record), | |
| "gated": record.get("gated"), | |
| "private": record.get("private"), | |
| "created_at": record.get("createdAt") or "", | |
| "last_modified": record.get("lastModified") or "", | |
| "sha": record.get("sha") or "", | |
| "base_models": ",".join(find_base_models(record)), | |
| "has_safetensors": bool(record.get("safetensors")) or counts["safetensors"] > 0, | |
| "has_gguf": bool(record.get("gguf")) or counts["gguf"] > 0, | |
| "has_tokenizer": counts["tokenizer"] > 0, | |
| "has_chat_template": counts["chat_template"] > 0, | |
| "siblings_count": len(record.get("siblings") or []), | |
| "safetensors_files": counts["safetensors"], | |
| "gguf_files": counts["gguf"], | |
| "bin_files": counts["bin"], | |
| "eval_results_count": len(record.get("evalResults") or []), | |
| "inference_provider_count": len(provider["providers"]), | |
| "inference_providers": ",".join(provider["providers"]), | |
| "live_inference_providers": ",".join(provider["live"]), | |
| "supports_tools_any": provider["supports_tools_any"], | |
| "supports_structured_output_any": provider["supports_structured_output_any"], | |
| "max_context_length": provider["max_context_length"], | |
| "router_served": router["router_served"], | |
| "router_provider_count": router["router_provider_count"], | |
| "router_providers": ",".join(router["router_providers"]), | |
| "router_supports_tools_any": router["router_supports_tools_any"], | |
| "router_supports_structured_output_any": router["router_supports_structured_output_any"], | |
| "router_max_context_length": router["router_max_context_length"], | |
| "tags": ",".join(tags), | |
| } | |
| normalized["candidate_class"] = candidate_class(normalized) | |
| normalized["candidate"], normalized["candidate_reason"] = candidate_decision(normalized) | |
| return normalized | |
| def normalized_repo_slug(model_id: str) -> str: | |
| slug = model_id.rsplit("/", 1)[-1].lower() | |
| slug = re.sub(r"[^a-z0-9]+", "-", slug) | |
| return slug.strip("-") | |
| def slug_dedupe_key(model_id: str) -> str: | |
| slug = normalized_repo_slug(model_id) | |
| previous = None | |
| while slug and slug != previous: | |
| previous = slug | |
| slug = QAT_SUFFIX_RE.sub("", slug) | |
| slug = ARTIFACT_REVISION_SUFFIX_RE.sub("", slug) | |
| slug = ARTIFACT_SUFFIX_RE.sub("", slug) | |
| return slug or normalized_repo_slug(model_id) | |
| def is_artifact_slug(model_id: str) -> bool: | |
| return normalized_repo_slug(model_id) != slug_dedupe_key(model_id) | |
| def quantized_base_refs(row: dict[str, Any]) -> list[str]: | |
| refs: list[str] = [] | |
| for source in [str(row.get("base_models") or ""), str(row.get("tags") or "")]: | |
| refs.extend(re.findall(r"(?:^|,)(?:base_model:)?quantized:([^,]+)", source)) | |
| return unique_preserve_order(refs) | |
| def base_model_refs(row: dict[str, Any]) -> list[tuple[str, str]]: | |
| refs: list[tuple[str, str]] = [] | |
| for value in str(row.get("base_models") or "").split(","): | |
| value = value.strip() | |
| if not value: | |
| continue | |
| relation = "" | |
| ref = value | |
| if ":" in value: | |
| prefix, rest = value.split(":", 1) | |
| if prefix in PACKAGING_RELATION_TERMS or prefix in {"finetune", "merge"}: | |
| relation = prefix | |
| ref = rest | |
| if ref: | |
| refs.append((relation, ref)) | |
| return refs | |
| def packaging_signals(row: dict[str, Any]) -> list[str]: | |
| text = f"{row.get('id', '')} {row.get('tags', '')} {row.get('library_name', '')}".lower() | |
| signals: list[str] = [] | |
| for term in sorted(PACKAGING_SIGNAL_TERMS, key=len, reverse=True): | |
| if term in text: | |
| signals.append(term) | |
| if row.get("has_gguf") is True: | |
| signals.append("gguf") | |
| return unique_preserve_order(signals) | |
| def packaging_base_refs(row: dict[str, Any]) -> list[str]: | |
| refs = list(quantized_base_refs(row)) | |
| relation = str(row.get("base_model_relation") or "").lower() | |
| if relation and relation not in PACKAGING_RELATION_TERMS: | |
| return unique_preserve_order(refs) | |
| for ref_relation, ref in base_model_refs(row): | |
| if ref_relation: | |
| if ref_relation in PACKAGING_RELATION_TERMS: | |
| refs.append(ref) | |
| continue | |
| if relation in PACKAGING_RELATION_TERMS: | |
| refs.append(ref) | |
| return unique_preserve_order(refs) | |
| def artifact_signals(row: dict[str, Any]) -> list[str]: | |
| text = f"{row.get('id', '')} {row.get('tags', '')} {row.get('library_name', '')}".lower() | |
| signals: list[str] = [] | |
| for name in ("gguf", "awq", "gptq", "fp8", "fp4", "nvfp4", "mxfp8", "bf16", "mlx", "qat"): | |
| if name in text: | |
| signals.append(name) | |
| if row.get("has_safetensors") is True: | |
| signals.append("safetensors") | |
| return unique_preserve_order(signals) | |
| def has_special_language_architecture(row: dict[str, Any]) -> bool: | |
| text = f"{row.get('id', '')} {row.get('pipeline_tag', '')} {row.get('tags', '')}".lower() | |
| if any(term in text for term in SPECIAL_ARCHITECTURE_TERMS): | |
| return True | |
| if "diffusion" in text and ( | |
| row.get("pipeline_tag") in LLM_PIPELINES | |
| or "text-generation" in text | |
| or "image-text-to-text" in text | |
| or "conversational" in text | |
| or "llm" in text | |
| ): | |
| return True | |
| return False | |
| def text_tokens(text: str) -> set[str]: | |
| return {token for token in re.split(r"[^a-z0-9]+", text.lower()) if token} | |
| def has_excluded_term(row: dict[str, Any]) -> bool: | |
| text = f"{row.get('id', '')} {row.get('pipeline_tag', '')} {row.get('tags', '')}".lower() | |
| if any(term in text for term in EXCLUDED_TERMS): | |
| return True | |
| tokens = text_tokens(text) | |
| if tokens & EXCLUDED_TOKEN_TERMS: | |
| return True | |
| pipeline = str(row.get("pipeline_tag") or "") | |
| if pipeline not in LLM_PIPELINES and tokens & EXCLUDED_AUDIO_TOKEN_TERMS: | |
| return True | |
| return False | |
| def is_coding_model_name(model_id: str) -> bool: | |
| return bool(CODING_MODEL_NAME_RE.search(model_id.lower())) | |
| def candidate_class(row: dict[str, Any]) -> str: | |
| model_id = str(row.get("id") or "").lower() | |
| text = f"{row.get('id', '')} {row.get('pipeline_tag', '')} {row.get('tags', '')}".lower() | |
| if has_special_language_architecture(row): | |
| return "special" | |
| if ( | |
| row.get("pipeline_tag") in {"image-text-to-text", "video-text-to-text", "audio-text-to-text", "any-to-any"} | |
| or "vision-language" in text | |
| or "multimodal" in text | |
| or "omni" in text | |
| or bool(re.search(r"(^|[-_/])vl($|[-_/])", model_id)) | |
| ): | |
| return "multimodal" | |
| if is_coding_model_name(model_id): | |
| return "coding" | |
| return "text" | |
| def has_text_model_signal(model_id: str) -> bool: | |
| if is_coding_model_name(model_id): | |
| return True | |
| boundary_terms = {"assistant", "chat", "code", "coder", "coding", "instruct", "reasoning", "thinking"} | |
| for term in TEXT_MODEL_TERMS: | |
| if term in boundary_terms: | |
| if re.search(rf"(^|[-_/]){re.escape(term)}($|[-_/])", model_id): | |
| return True | |
| continue | |
| if term in model_id: | |
| return True | |
| return False | |
| def candidate_decision(row: dict[str, Any]) -> tuple[bool, str]: | |
| model_id = str(row.get("id") or "").lower() | |
| text = f"{row.get('id', '')} {row.get('pipeline_tag', '')} {row.get('tags', '')}".lower() | |
| pipeline = str(row.get("pipeline_tag") or "") | |
| if row.get("private") is True: | |
| return False, "private" | |
| if pipeline in EXCLUDED_PIPELINES: | |
| return False, f"excluded pipeline: {pipeline}" | |
| if has_excluded_term(row): | |
| return False, "excluded narrow/non-LLM term" | |
| if pipeline in LLM_PIPELINES: | |
| return True, f"LLM pipeline: {pipeline}" | |
| if row.get("has_chat_template") is True: | |
| return True, "chat template" | |
| if row.get("supports_tools_any") is True or row.get("router_supports_tools_any") is True: | |
| return True, "tool support" | |
| if row.get("supports_structured_output_any") is True or row.get("router_supports_structured_output_any") is True: | |
| return True, "structured output support" | |
| if "conversational" in text: | |
| return True, "conversational tag" | |
| if has_text_model_signal(model_id): | |
| return True, "model-name signal" | |
| return False, "no LLM signal" | |
| def parse_hf_datetime(value: str) -> datetime | None: | |
| if not value: | |
| return None | |
| try: | |
| return datetime.fromisoformat(value.replace("Z", "+00:00")) | |
| except ValueError: | |
| return None | |
| def int_metric(value: Any) -> int: | |
| if isinstance(value, bool): | |
| return int(value) | |
| if isinstance(value, int): | |
| return value | |
| if isinstance(value, float): | |
| return int(value) | |
| if isinstance(value, str): | |
| stripped = value.strip() | |
| if stripped: | |
| try: | |
| return int(float(stripped)) | |
| except ValueError: | |
| return 0 | |
| return 0 | |
| def slug_tokens(slug: str) -> list[str]: | |
| return [token for token in slug.split("-") if token] | |
| def has_model_shape(slug: str) -> bool: | |
| tokens = slug_tokens(slug) | |
| family = any( | |
| token in MODEL_FAMILY_TOKENS or any(token.startswith(f"{family}") for family in MODEL_FAMILY_TOKENS) | |
| for token in tokens[:3] | |
| ) | |
| sized_or_versioned = any( | |
| bool(re.fullmatch(r"[a-z]?[0-9]+(?:[a-z]*[0-9]*)?[bm]?", token)) and any(char.isdigit() for char in token) | |
| for token in tokens | |
| ) | |
| return family and sized_or_versioned and len(tokens) >= 2 | |
| def token_sequence_match(haystack: str, needle: str) -> tuple[list[str], list[str]] | None: | |
| haystack_tokens = slug_tokens(haystack) | |
| needle_tokens = slug_tokens(needle) | |
| if len(needle_tokens) < 2 or len(needle_tokens) >= len(haystack_tokens): | |
| return None | |
| for index in range(len(haystack_tokens) - len(needle_tokens) + 1): | |
| if haystack_tokens[index : index + len(needle_tokens)] == needle_tokens: | |
| return haystack_tokens[:index], haystack_tokens[index + len(needle_tokens) :] | |
| return None | |
| def component_scale(row: dict[str, Any], target: dict[str, Any] | None) -> bool: | |
| if target is None: | |
| return False | |
| try: | |
| row_params = float(row.get("parameter_count_b") or 0) | |
| target_params = float(target.get("parameter_count_b") or 0) | |
| except (TypeError, ValueError): | |
| return False | |
| return row_params > 0 and target_params > 0 and row_params < target_params * 0.5 | |
| def derivative_safe_suffix_token(token: str) -> bool: | |
| return ( | |
| token in DERIVATIVE_SAFE_SUFFIX_TOKENS | |
| or bool(re.fullmatch(r"q[2-8]|[0-9]+bit|[0-9]+", token)) | |
| or token in {"k", "m", "s", "l"} | |
| ) | |
| def derivative_tokens_allow_grouping( | |
| prefix_tokens: list[str], | |
| suffix_tokens: list[str], | |
| row: dict[str, Any], | |
| target: dict[str, Any] | None, | |
| ) -> bool: | |
| extra = set(prefix_tokens + suffix_tokens) | |
| if any(token in MODEL_FAMILY_TOKENS for token in extra): | |
| return False | |
| if extra & PEER_VARIANT_TOKENS: | |
| return False | |
| if prefix_tokens: | |
| prefix = set(prefix_tokens) | |
| if not (prefix & DERIVATIVE_COMPONENT_PREFIX_TOKENS): | |
| return False | |
| if any(not derivative_safe_suffix_token(token) for token in suffix_tokens): | |
| return False | |
| if extra & DERIVATIVE_STRONG_TOKENS: | |
| return True | |
| if extra & DERIVATIVE_AMBIGUOUS_TOKENS: | |
| return bool(packaging_signals(row)) or component_scale(row, target) | |
| return False | |
| def contained_lineage_key( | |
| slug: str, | |
| known_keys: set[str], | |
| row: dict[str, Any], | |
| key_representatives: dict[str, dict[str, Any]], | |
| ) -> str | None: | |
| matches: list[tuple[int, str, list[str], list[str]]] = [] | |
| for key in known_keys: | |
| if key == slug or not has_model_shape(key): | |
| continue | |
| match = token_sequence_match(slug, key) | |
| if match is None: | |
| continue | |
| prefix, suffix = match | |
| target = key_representatives.get(key) | |
| if derivative_tokens_allow_grouping(prefix, suffix, row, target): | |
| matches.append((len(slug_tokens(key)), key, prefix, suffix)) | |
| if not matches: | |
| return None | |
| matches.sort(reverse=True) | |
| return matches[0][1] | |
| def resolve_lineage_key( | |
| key: str, | |
| row: dict[str, Any], | |
| known_keys: set[str], | |
| key_representatives: dict[str, dict[str, Any]], | |
| ) -> str: | |
| return contained_lineage_key(key, known_keys, row, key_representatives) or key | |
| def representative_for_rows(rows: list[dict[str, Any]]) -> dict[str, Any]: | |
| return sorted( | |
| rows, | |
| key=lambda row: ( | |
| int_metric(row.get("downloads_all_time")), | |
| int_metric(row.get("downloads")), | |
| int_metric(row.get("likes")), | |
| str(row.get("id") or ""), | |
| ), | |
| reverse=True, | |
| )[0] | |
| def group_models(rows: list[dict[str, Any]]) -> list[dict[str, Any]]: | |
| by_id = {str(row["id"]): row for row in rows} | |
| initial_key_map: dict[str, list[dict[str, Any]]] = defaultdict(list) | |
| for row in rows: | |
| initial_key_map[slug_dedupe_key(str(row["id"]))].append(row) | |
| known_keys = set(initial_key_map) | |
| key_representatives = {key: representative_for_rows(values) for key, values in initial_key_map.items()} | |
| group_map: dict[str, list[dict[str, Any]]] = defaultdict(list) | |
| for row in rows: | |
| key = slug_dedupe_key(str(row["id"])) | |
| for base in packaging_base_refs(row): | |
| if base in by_id: | |
| key = resolve_lineage_key(slug_dedupe_key(base), row, known_keys, key_representatives) | |
| break | |
| else: | |
| candidate_keys = known_keys - {key} | |
| contained_key = contained_lineage_key( | |
| normalized_repo_slug(str(row["id"])), | |
| candidate_keys, | |
| row, | |
| key_representatives, | |
| ) | |
| if contained_key: | |
| key = contained_key | |
| group_map[key].append(row) | |
| for key in list(group_map): | |
| target = f"{key}-it" | |
| if key.startswith("gemma-") and target in group_map and all(is_artifact_slug(str(row["id"])) for row in group_map[key]): | |
| group_map[target].extend(group_map.pop(key)) | |
| groups: list[dict[str, Any]] = [] | |
| for key, artifacts in group_map.items(): | |
| ordered_artifacts = sorted( | |
| artifacts, | |
| key=lambda row: ( | |
| int_metric(row.get("downloads_all_time")), | |
| int_metric(row.get("downloads")), | |
| int_metric(row.get("likes")), | |
| str(row.get("id") or ""), | |
| ), | |
| reverse=True, | |
| ) | |
| representative = ordered_artifacts[0] | |
| group_downloads = sum(int_metric(row.get("downloads")) for row in ordered_artifacts) | |
| group_downloads_all_time = sum(int_metric(row.get("downloads_all_time")) for row in ordered_artifacts) | |
| group_likes = sum(int_metric(row.get("likes")) for row in ordered_artifacts) | |
| signals = unique_preserve_order([signal for row in ordered_artifacts for signal in artifact_signals(row)]) | |
| router_served = any(row.get("router_served") is True for row in ordered_artifacts) | |
| supports_tools = any( | |
| row.get("supports_tools_any") is True or row.get("router_supports_tools_any") is True | |
| for row in ordered_artifacts | |
| ) | |
| supports_structured = any( | |
| row.get("supports_structured_output_any") is True | |
| or row.get("router_supports_structured_output_any") is True | |
| for row in ordered_artifacts | |
| ) | |
| group = { | |
| "group_key": key, | |
| "representative_id": representative["id"], | |
| "author": representative["author"], | |
| "downloads": group_downloads, | |
| "group_downloads": group_downloads, | |
| "representative_downloads": int_metric(representative.get("downloads")), | |
| "downloads_all_time": group_downloads_all_time, | |
| "group_downloads_all_time": group_downloads_all_time, | |
| "representative_downloads_all_time": int_metric(representative.get("downloads_all_time")), | |
| "likes": group_likes, | |
| "group_likes": group_likes, | |
| "representative_likes": int_metric(representative.get("likes")), | |
| "parameter_count_b": representative["parameter_count_b"], | |
| "capacity_class": representative["capacity_class"], | |
| "candidate_class": representative["candidate_class"], | |
| "pipeline_tag": representative["pipeline_tag"], | |
| "license": representative["license"] or "unknown", | |
| "router_served": router_served, | |
| "supports_tools": supports_tools, | |
| "supports_structured_output": supports_structured, | |
| "artifact_count": len(ordered_artifacts), | |
| "artifacts": [row["id"] for row in ordered_artifacts], | |
| "artifact_signals": signals, | |
| "created_at": representative["created_at"], | |
| "last_modified": representative["last_modified"], | |
| } | |
| groups.append(group) | |
| for row in ordered_artifacts: | |
| row["group_key"] = key | |
| groups.sort( | |
| key=lambda row: ( | |
| int_metric(row.get("downloads_all_time")), | |
| int_metric(row.get("downloads")), | |
| int_metric(row.get("likes")), | |
| str(row.get("group_key") or ""), | |
| ), | |
| reverse=True, | |
| ) | |
| for index, group in enumerate(groups, start=1): | |
| group["rank"] = index | |
| return groups | |
| def age_days(created_at: Any, as_of: datetime) -> float: | |
| created = parse_hf_datetime(str(created_at or "")) | |
| if created is None: | |
| return 0.0 | |
| return max((as_of - created).total_seconds() / SECONDS_PER_DAY, 0.0) | |
| def rate_metrics(group: dict[str, Any], as_of: datetime) -> dict[str, float]: | |
| age = age_days(group.get("created_at"), as_of) | |
| download_age = max(age, MIN_DOWNLOAD_RATE_AGE_DAYS) | |
| like_age = max(age, MIN_LIKE_RATE_AGE_DAYS) | |
| group_downloads_all_time = int_metric(group.get("group_downloads_all_time", group.get("downloads_all_time"))) | |
| representative_downloads_all_time = int_metric( | |
| group.get("representative_downloads_all_time", group_downloads_all_time) | |
| ) | |
| group_likes = int_metric(group.get("group_likes", group.get("likes"))) | |
| representative_likes = int_metric(group.get("representative_likes", group_likes)) | |
| group_downloads_per_month = round(group_downloads_all_time * DAYS_PER_MONTH / download_age, 4) | |
| representative_downloads_per_month = round(representative_downloads_all_time * DAYS_PER_MONTH / download_age, 4) | |
| group_likes_per_month = round(group_likes * DAYS_PER_MONTH / like_age, 4) | |
| representative_likes_per_month = round(representative_likes * DAYS_PER_MONTH / like_age, 4) | |
| return { | |
| "age_days": round(age, 4), | |
| "downloads_per_month": group_downloads_per_month, | |
| "group_downloads_per_month": group_downloads_per_month, | |
| "representative_downloads_per_month": representative_downloads_per_month, | |
| "likes_per_month": group_likes_per_month, | |
| "group_likes_per_month": group_likes_per_month, | |
| "representative_likes_per_month": representative_likes_per_month, | |
| } | |
| def add_rate_metrics(groups: list[dict[str, Any]], as_of: datetime) -> None: | |
| for group in groups: | |
| group.update(rate_metrics(group, as_of)) | |
| groups.sort( | |
| key=lambda row: ( | |
| float(row.get("downloads_per_month") or 0), | |
| int_metric(row.get("downloads_all_time")), | |
| int_metric(row.get("downloads")), | |
| float(row.get("likes_per_month") or 0), | |
| str(row.get("group_key") or ""), | |
| ), | |
| reverse=True, | |
| ) | |
| for index, group in enumerate(groups, start=1): | |
| group["rank"] = index | |
| def compare_group_snapshots(before: list[dict[str, Any]], after: list[dict[str, Any]]) -> dict[str, Any]: | |
| before_by_key = {str(group.get("group_key")): group for group in before} | |
| after_by_key = {str(group.get("group_key")): group for group in after} | |
| before_keys = set(before_by_key) | |
| after_keys = set(after_by_key) | |
| changed_artifacts: list[dict[str, Any]] = [] | |
| changed_metrics: list[dict[str, Any]] = [] | |
| for key in sorted(before_keys & after_keys): | |
| before_group = before_by_key[key] | |
| after_group = after_by_key[key] | |
| before_artifacts = list(before_group.get("artifacts") or []) | |
| after_artifacts = list(after_group.get("artifacts") or []) | |
| if before_artifacts != after_artifacts: | |
| changed_artifacts.append( | |
| { | |
| "group_key": key, | |
| "before_artifacts": before_artifacts, | |
| "after_artifacts": after_artifacts, | |
| } | |
| ) | |
| metric_delta: dict[str, Any] = {"group_key": key} | |
| changed = False | |
| for field in ("rank", "downloads", "downloads_all_time", "downloads_per_month", "likes_per_month"): | |
| before_value = before_group.get(field) | |
| after_value = after_group.get(field) | |
| if before_value != after_value: | |
| metric_delta[field] = {"before": before_value, "after": after_value} | |
| changed = True | |
| if changed: | |
| changed_metrics.append(metric_delta) | |
| return { | |
| "before_groups": len(before), | |
| "after_groups": len(after), | |
| "groups_added": sorted(after_keys - before_keys), | |
| "groups_removed": sorted(before_keys - after_keys), | |
| "artifact_changes": changed_artifacts, | |
| "metric_changes": changed_metrics, | |
| } | |
| def collect_metadata( | |
| *, | |
| creators: list[str], | |
| min_downloads: int, | |
| page_size: int, | |
| limit_per_creator: int | None, | |
| token: str | None, | |
| sleep_seconds: float, | |
| enable_discovery: bool, | |
| discovery_max_results_per_query: int, | |
| related_discovery_seed_limit: int, | |
| ) -> tuple[list[dict[str, Any]], list[dict[str, Any]], list[dict[str, str]], list[dict[str, Any]], dict[str, Any]]: | |
| router_records = fetch_router_models(token) | |
| creators = unique_preserve_order(creators + router_creators(router_records)) | |
| router_by_id = {record["id"]: record for record in router_records} | |
| raw_rows: list[dict[str, Any]] = [] | |
| errors: list[dict[str, str]] = [] | |
| for creator in creators: | |
| try: | |
| raw_rows.extend( | |
| list_creator_models( | |
| creator, | |
| page_size=page_size, | |
| sort="downloads", | |
| direction=-1, | |
| token=token, | |
| limit=limit_per_creator, | |
| sleep_seconds=sleep_seconds, | |
| ) | |
| ) | |
| except Exception as err: # noqa: BLE001 | |
| errors.append({"creator": creator, "error": str(err)}) | |
| deduped_raw: dict[str, dict[str, Any]] = {} | |
| for row in raw_rows: | |
| model_id = row.get("id") or row.get("modelId") | |
| if isinstance(model_id, str) and model_id: | |
| deduped_raw[model_id] = row | |
| for record in router_records: | |
| model_id = record.get("id") | |
| if isinstance(model_id, str) and model_id not in deduped_raw: | |
| try: | |
| quoted = urllib.parse.quote(model_id, safe="/") | |
| detail, _ = request_json(f"{HF_MODELS_API}/{quoted}?{urllib.parse.urlencode([('expand', field) for field in EXPAND_FIELDS], doseq=True)}", token) | |
| if isinstance(detail, dict): | |
| deduped_raw[model_id] = detail | |
| except Exception as err: # noqa: BLE001 | |
| errors.append({"model": model_id, "error": str(err)}) | |
| discovery_summary: dict[str, Any] = {"enabled": False, "queries": 0, "query_counts": {}, "records": 0} | |
| if enable_discovery: | |
| initial_normalized = [ | |
| normalize_record(row, router_by_id.get(str(row.get("id") or row.get("modelId") or ""))) | |
| for row in deduped_raw.values() | |
| ] | |
| seed_rows = [ | |
| row | |
| for row in initial_normalized | |
| if int_metric(row.get("downloads")) > min_downloads and row.get("candidate") is True | |
| ] | |
| discovered_rows, discovery_errors, discovery_summary = discover_additional_models( | |
| seed_rows=seed_rows, | |
| existing_ids=set(deduped_raw), | |
| min_downloads=min_downloads, | |
| page_size=page_size, | |
| token=token, | |
| sleep_seconds=sleep_seconds, | |
| max_results_per_query=discovery_max_results_per_query, | |
| seed_limit=related_discovery_seed_limit, | |
| ) | |
| errors.extend(discovery_errors) | |
| for row in discovered_rows: | |
| model_id = row.get("id") or row.get("modelId") | |
| if isinstance(model_id, str) and model_id: | |
| deduped_raw[model_id] = row | |
| normalized = [normalize_record(row, router_by_id.get(str(row.get("id") or row.get("modelId") or ""))) for row in deduped_raw.values()] | |
| normalized.sort(key=lambda row: (int(row.get("downloads") or 0), int(row.get("likes") or 0), str(row.get("id"))), reverse=True) | |
| popular = [row for row in normalized if int(row.get("downloads") or 0) > min_downloads] | |
| return normalized, popular, errors, router_records, discovery_summary | |
| def build_payload( | |
| *, | |
| min_downloads: int = DEFAULT_MIN_DOWNLOADS, | |
| top_n: int = DEFAULT_TOP_N, | |
| max_age_days: int = DEFAULT_MAX_AGE_DAYS, | |
| creators: list[str] | None = None, | |
| page_size: int = 100, | |
| limit_per_creator: int | None = None, | |
| token: str | None = None, | |
| sleep_seconds: float = 0.05, | |
| enable_discovery: bool | None = None, | |
| discovery_max_results_per_query: int | None = None, | |
| related_discovery_seed_limit: int | None = None, | |
| ) -> dict[str, Any]: | |
| started = iso() | |
| if enable_discovery is None: | |
| enable_discovery = os.environ.get("OPEN_MODEL_ENABLE_DISCOVERY", "1").lower() not in {"0", "false", "no", "off"} | |
| if discovery_max_results_per_query is None: | |
| discovery_max_results_per_query = int( | |
| os.environ.get("OPEN_MODEL_DISCOVERY_MAX_RESULTS_PER_QUERY", DEFAULT_DISCOVERY_MAX_RESULTS_PER_QUERY) | |
| ) | |
| if related_discovery_seed_limit is None: | |
| related_discovery_seed_limit = int( | |
| os.environ.get("OPEN_MODEL_RELATED_DISCOVERY_SEED_LIMIT", DEFAULT_RELATED_DISCOVERY_SEED_LIMIT) | |
| ) | |
| normalized, popular, errors, router_records, discovery_summary = collect_metadata( | |
| creators=creators or env_creators(), | |
| min_downloads=min_downloads, | |
| page_size=page_size, | |
| limit_per_creator=limit_per_creator, | |
| token=token, | |
| sleep_seconds=sleep_seconds, | |
| enable_discovery=enable_discovery, | |
| discovery_max_results_per_query=discovery_max_results_per_query, | |
| related_discovery_seed_limit=related_discovery_seed_limit, | |
| ) | |
| candidates = [row for row in popular if row.get("candidate") is True] | |
| groups = group_models(candidates) | |
| generated_at_dt = now_utc() | |
| add_rate_metrics(groups, generated_at_dt) | |
| class_counts = Counter(group["candidate_class"] for group in groups) | |
| creator_counts = Counter(group["author"] for group in groups) | |
| summary = { | |
| "generated_at": iso(generated_at_dt), | |
| "started_at": started, | |
| "source_model_records": len(normalized), | |
| "popular_model_records": len(popular), | |
| "candidate_artifacts": len(candidates), | |
| "leaderboard_groups": len(groups), | |
| "multi_artifact_groups": sum(1 for group in groups if int(group["artifact_count"]) > 1), | |
| "router_records": len(router_records), | |
| "discovered_model_records": int(discovery_summary.get("records") or 0), | |
| "discovery_queries": int(discovery_summary.get("queries") or 0), | |
| "tool_supported_groups": sum(1 for group in groups if group["supports_tools"]), | |
| "structured_output_groups": sum(1 for group in groups if group["supports_structured_output"]), | |
| "class_counts": dict(sorted(class_counts.items())), | |
| "top_creators": [{"creator": name, "groups": count} for name, count in creator_counts.most_common(12)], | |
| "errors": errors, | |
| "discovery": discovery_summary, | |
| } | |
| return { | |
| "schema_version": 1, | |
| "title": "Open LLM Distribution Leaderboard", | |
| "generated_at": summary["generated_at"], | |
| "filters": { | |
| "min_downloads": min_downloads, | |
| "default_age_filter_days": max_age_days, | |
| "age_filter_options_days": ["off", 183, 365, 548, 730], | |
| "top_n": top_n, | |
| "metric_scope_default": "group", | |
| "candidate_filter": "Auto-filtered LLM-ish text, coding, multimodal, and special-architecture model repos; excludes embeddings, rerankers, audio utilities, image-only generation, and narrow support models.", | |
| "grouping": "Artifacts are grouped by normalized slug and explicit quantized base-model metadata when available.", | |
| }, | |
| "summary": summary, | |
| "groups": groups, | |
| "all_group_count": len(groups), | |
| "models": candidates, | |
| "popular_models": popular, | |
| "router_models": router_records, | |
| } | |
| def format_compact_number(value: Any) -> str: | |
| try: | |
| number = float(value) | |
| except (TypeError, ValueError): | |
| return "0" | |
| sign = "-" if number < 0 else "" | |
| number = abs(number) | |
| if 0 < number < 0.1: | |
| return f"{sign}<0.1" | |
| units = [(1_000_000_000, "B"), (1_000_000, "M"), (1_000, "K")] | |
| for index, (threshold, suffix) in enumerate(units): | |
| if number >= threshold: | |
| compact = number / threshold | |
| if round(compact, 1) >= 1000 and index > 0: | |
| threshold, suffix = units[index - 1] | |
| compact = number / threshold | |
| text = f"{compact:.1f}".removesuffix(".0") | |
| return f"{sign}{text}{suffix}" | |
| text = f"{number:.1f}".removesuffix(".0") | |
| return f"{sign}{text}" | |
| def format_params(value: Any) -> str: | |
| if value in {"", None}: | |
| return "unknown" | |
| try: | |
| number = float(value) | |
| except (TypeError, ValueError): | |
| return str(value) | |
| if number >= 100: | |
| return f"{number:.0f}B" | |
| if number >= 10: | |
| return f"{number:.1f}B" | |
| return f"{number:.2f}B" | |
| def format_date(value: Any) -> str: | |
| parsed = parse_hf_datetime(str(value or "")) | |
| if parsed is None: | |
| return "unknown" | |
| return parsed.date().isoformat() | |
| def format_age(created_at: Any, as_of: datetime) -> str: | |
| if parse_hf_datetime(str(created_at or "")) is None: | |
| return "" | |
| age = age_days(created_at, as_of) | |
| if age < 1: | |
| return "today" | |
| years = int(age // 365.2425) | |
| remaining_days = age - (years * 365.2425) | |
| months = int(remaining_days // DAYS_PER_MONTH) | |
| days = int(age) | |
| parts: list[str] = [] | |
| if years: | |
| parts.append(f"{years}yr") | |
| if months: | |
| parts.append(f"{months}mo") | |
| return " ".join(parts) | |
| if months: | |
| return f"{months}mo" | |
| return f"{days}d" | |
| def format_created(value: Any, as_of: datetime) -> str: | |
| date = format_date(value) | |
| age = format_age(value, as_of) | |
| if date == "unknown" or not age: | |
| return date | |
| return f"{date} ({age} ago)" | |
| def render_created(value: Any, as_of: datetime) -> str: | |
| date = format_date(value) | |
| age = format_age(value, as_of) | |
| if date == "unknown" or not age: | |
| return f'<span class="date-stamp">{html.escape(date)}</span>' | |
| return ( | |
| f'<span class="date-stamp">{html.escape(date)}</span>' | |
| f'<span class="age-stamp">{html.escape(age)} ago</span>' | |
| ) | |
| def model_url(model_id: str) -> str: | |
| return f"https://huggingface.co/{urllib.parse.quote(model_id, safe='/')}" | |
| def pill(label: str, tone: str = "neutral") -> str: | |
| return f'<span class="pill pill-{html.escape(tone)}">{html.escape(label)}</span>' | |
| def render_html(payload: dict[str, Any]) -> str: | |
| summary = payload["summary"] | |
| groups = payload["groups"] | |
| filters = payload["filters"] | |
| generated_js = json.dumps(str(payload["generated_at"])) | |
| default_age_days = str(filters.get("default_age_filter_days") or filters.get("max_age_days") or DEFAULT_MAX_AGE_DAYS) | |
| top_n_js = json.dumps(filters.get("top_n") or DEFAULT_TOP_N) | |
| min_downloads_label = format_compact_number(filters.get("min_downloads") or DEFAULT_MIN_DOWNLOADS) | |
| generated_at_dt = parse_hf_datetime(str(payload["generated_at"])) or now_utc() | |
| generated_at_label = generated_at_dt.date().isoformat() | |
| def age_selected(value: str) -> str: | |
| return " selected" if value == default_age_days else "" | |
| def attr(value: Any) -> str: | |
| return html.escape(str(value), quote=True) | |
| def repo_count_label(count: int) -> str: | |
| return f"{count} repo" if count == 1 else f"{count} repos" | |
| def more_variants_label(count: int) -> str: | |
| return f"{count} more variant" if count == 1 else f"{count} more variants" | |
| def artifact_link(model_id: str) -> str: | |
| return ( | |
| f'<a href="{html.escape(model_url(model_id))}" target="_blank" rel="noreferrer">' | |
| f"{html.escape(model_id)}</a>" | |
| ) | |
| table_rows = [] | |
| for group in groups: | |
| if ( | |
| "group_downloads_per_month" not in group | |
| or "representative_downloads_per_month" not in group | |
| or "group_likes_per_month" not in group | |
| or "representative_likes_per_month" not in group | |
| ): | |
| group.update(rate_metrics(group, generated_at_dt)) | |
| artifact_count = int_metric(group.get("artifact_count")) or len(group.get("artifacts") or []) | |
| group_downloads_per_month = float( | |
| group.get("group_downloads_per_month", group.get("downloads_per_month", group.get("downloads"))) or 0 | |
| ) | |
| representative_downloads_per_month = float( | |
| group.get("representative_downloads_per_month", group_downloads_per_month) or 0 | |
| ) | |
| group_likes_per_month = float(group.get("group_likes_per_month", group.get("likes_per_month")) or 0) | |
| representative_likes_per_month = float(group.get("representative_likes_per_month", group_likes_per_month) or 0) | |
| group_downloads = int_metric(group.get("group_downloads", group.get("downloads"))) | |
| representative_downloads = int_metric(group.get("representative_downloads", group_downloads)) | |
| group_downloads_all_time = int_metric(group.get("group_downloads_all_time", group.get("downloads_all_time"))) | |
| representative_downloads_all_time = int_metric( | |
| group.get("representative_downloads_all_time", group_downloads_all_time) | |
| ) | |
| group_likes = int_metric(group.get("group_likes", group.get("likes"))) | |
| representative_likes = int_metric(group.get("representative_likes", group_likes)) | |
| repo_label = repo_count_label(artifact_count) | |
| group_downloads_detail_primary = format_compact_number(group_downloads_all_time) + " all-time across " + repo_label | |
| group_downloads_detail_secondary = format_compact_number(group_downloads) + " recent HF downloads" | |
| representative_downloads_detail_primary = ( | |
| format_compact_number(representative_downloads_all_time) + " all-time for representative" | |
| ) | |
| representative_downloads_detail_secondary = ( | |
| format_compact_number(representative_downloads) + " recent HF downloads" | |
| ) | |
| group_likes_detail_primary = format_compact_number(group_likes) + " likes across " + repo_label | |
| group_likes_detail_secondary = "grouped repos" | |
| representative_likes_detail_primary = format_compact_number(representative_likes) + " likes for representative" | |
| representative_likes_detail_secondary = "main repo only" | |
| group_downloads_label = format_compact_number(group_downloads_per_month) + "/mo" | |
| representative_downloads_label = format_compact_number(representative_downloads_per_month) + "/mo" | |
| group_likes_label = format_compact_number(group_likes_per_month) + "/mo" | |
| representative_likes_label = format_compact_number(representative_likes_per_month) + "/mo" | |
| group_downloads_tooltip = "\n".join( | |
| [group_downloads_label, group_downloads_detail_primary, group_downloads_detail_secondary] | |
| ) | |
| representative_downloads_tooltip = "\n".join( | |
| [ | |
| representative_downloads_label, | |
| representative_downloads_detail_primary, | |
| representative_downloads_detail_secondary, | |
| ] | |
| ) | |
| group_likes_tooltip = "\n".join([group_likes_label, group_likes_detail_primary, group_likes_detail_secondary]) | |
| representative_likes_tooltip = "\n".join( | |
| [representative_likes_label, representative_likes_detail_primary, representative_likes_detail_secondary] | |
| ) | |
| artifacts = [str(item) for item in group.get("artifacts") or []] | |
| visible_artifact = artifact_link(artifacts[0]) if artifacts else "" | |
| more_count = max(len(artifacts) - 1, 0) | |
| extra_artifacts = "".join(artifact_link(item) for item in artifacts[1:]) | |
| more_label = more_variants_label(more_count) | |
| artifact_toggle = ( | |
| f'<button class="artifact-toggle" type="button" aria-expanded="false" ' | |
| f'data-more-label="{attr(more_label)}">+ {html.escape(more_label)}</button>' | |
| if more_count | |
| else "" | |
| ) | |
| artifact_more = f'<span class="artifact-more">{artifact_toggle}</span>' if more_count else "" | |
| artifact_extra = f'<span class="artifact-extra" hidden>{extra_artifacts}</span>' if more_count else "" | |
| row_attrs = { | |
| "data-class": group["candidate_class"], | |
| "data-creator": group["author"], | |
| "data-created": group.get("created_at") or "", | |
| "data-group-downloads-per-month": group_downloads_per_month, | |
| "data-representative-downloads-per-month": representative_downloads_per_month, | |
| "data-group-likes-per-month": group_likes_per_month, | |
| "data-representative-likes-per-month": representative_likes_per_month, | |
| "data-group-downloads-per-month-label": group_downloads_label, | |
| "data-representative-downloads-per-month-label": representative_downloads_label, | |
| "data-group-likes-per-month-label": group_likes_label, | |
| "data-representative-likes-per-month-label": representative_likes_label, | |
| "data-group-downloads-tooltip": group_downloads_tooltip, | |
| "data-representative-downloads-tooltip": representative_downloads_tooltip, | |
| "data-group-likes-tooltip": group_likes_tooltip, | |
| "data-representative-likes-tooltip": representative_likes_tooltip, | |
| } | |
| row_attr_text = " ".join(f'{name}="{attr(value)}"' for name, value in row_attrs.items()) | |
| table_rows.append( | |
| f""" | |
| <tr {row_attr_text}> | |
| <td class="rank" data-label="Rank">{group['rank']}</td> | |
| <td class="model-cell" data-label="Model"> | |
| <a class="model-link" href="{html.escape(model_url(str(group['representative_id'])))}" target="_blank" rel="noreferrer">{html.escape(str(group['group_key']))}</a> | |
| </td> | |
| <td class="num metric has-tooltip" data-label="Avg Downloads / Month" data-metric="downloads_per_month" data-tooltip="{attr(group_downloads_tooltip)}"><span class="metric-value">{group_downloads_label}</span></td> | |
| <td class="num metric has-tooltip" data-label="Avg Likes / month" data-metric="likes_per_month" data-tooltip="{attr(group_likes_tooltip)}"><span class="metric-value">{group_likes_label}</span></td> | |
| <td data-label="Params">{format_params(group['parameter_count_b'])}</td> | |
| <td class="date-cell" data-label="Created">{render_created(group.get('created_at'), generated_at_dt)}</td> | |
| <td class="artifacts" data-label="Related repos"> | |
| <span class="artifact-list"><span class="artifact-primary">{visible_artifact}</span>{artifact_more}{artifact_extra}</span> | |
| </td> | |
| </tr> | |
| """ | |
| ) | |
| return f"""<!doctype html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| <title>Open LLM Distribution Leaderboard</title> | |
| <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ctext y='.9em' font-size='90'%3E%F0%9F%8F%86%3C/text%3E%3C/svg%3E" /> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Archivo:wght@600;700;800&family=Atkinson+Hyperlegible:wght@400;700&display=swap'); | |
| :root {{ | |
| --bg: oklch(16% 0.012 235); | |
| --panel: oklch(22% 0.014 235); | |
| --field: oklch(19% 0.012 235); | |
| --header: oklch(27% 0.016 235); | |
| --hover: oklch(25% 0.014 235); | |
| --ink: oklch(94% 0.012 220); | |
| --muted: oklch(72% 0.025 225); | |
| --line: oklch(34% 0.018 235); | |
| --accent: oklch(75% 0.12 176); | |
| --accent-2: oklch(79% 0.14 76); | |
| --link: oklch(78% 0.105 212); | |
| --shadow: 0 18px 44px oklch(8% 0.012 235 / 0.42); | |
| }} | |
| * {{ box-sizing: border-box; }} | |
| html {{ scroll-behavior: smooth; }} | |
| body {{ | |
| margin: 0; | |
| background: var(--bg); | |
| color: var(--ink); | |
| color-scheme: dark; | |
| font-family: "Atkinson Hyperlegible", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; | |
| font-size: 20px; | |
| letter-spacing: 0; | |
| }} | |
| .shell {{ width: min(1120px, calc(100% - 32px)); margin: 0 auto; padding: 24px 0; }} | |
| .topbar {{ | |
| display: grid; | |
| grid-template-columns: 1fr; | |
| gap: 16px; | |
| align-items: center; | |
| margin-bottom: 18px; | |
| }} | |
| .title-row {{ | |
| position: relative; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| min-height: 54px; | |
| }} | |
| .title-block {{ | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 5px; | |
| min-width: 0; | |
| }} | |
| h1 {{ | |
| margin: 0; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| gap: 12px; | |
| flex-wrap: nowrap; | |
| text-align: center; | |
| font-family: "Archivo", ui-sans-serif, system-ui, sans-serif; | |
| font-size: 46px; | |
| line-height: 1; | |
| letter-spacing: 0; | |
| white-space: normal; | |
| }} | |
| .hf-logo {{ | |
| display: block; | |
| width: clamp(32px, 4.2vw, 54px); | |
| height: clamp(32px, 4.2vw, 54px); | |
| flex: 0 0 auto; | |
| }} | |
| .title-text span {{ display: inline; }} | |
| .title-tail {{ white-space: nowrap; }} | |
| .title-note {{ | |
| margin: 0; | |
| color: var(--muted); | |
| font-size: 13px; | |
| line-height: 1.25; | |
| text-align: center; | |
| }} | |
| .info-link {{ | |
| display: inline-flex; | |
| position: relative; | |
| top: 0; | |
| width: 0.52em; | |
| height: 0.52em; | |
| margin-left: 0.08em; | |
| color: var(--muted); | |
| align-items: center; | |
| justify-content: center; | |
| vertical-align: super; | |
| text-decoration: none; | |
| line-height: 1; | |
| }} | |
| .info-icon {{ display: block; width: 100%; height: 100%; stroke-width: 2.35; }} | |
| .filters-toggle {{ | |
| position: absolute; | |
| top: 50%; | |
| width: 40px; | |
| height: 40px; | |
| transform: translateY(-50%); | |
| border: 1px solid var(--line); | |
| border-radius: 999px; | |
| background: var(--field); | |
| color: var(--ink); | |
| align-items: center; | |
| justify-content: center; | |
| }} | |
| .info-link:hover, | |
| .filters-toggle:hover {{ border-color: var(--accent); color: var(--accent); }} | |
| .filters-toggle {{ | |
| left: 0; | |
| display: none; | |
| padding: 0; | |
| cursor: pointer; | |
| }} | |
| .filters-toggle span {{ | |
| display: block; | |
| width: 17px; | |
| height: 2px; | |
| border-radius: 999px; | |
| background: currentColor; | |
| }} | |
| .controls {{ | |
| display: grid; | |
| grid-template-columns: minmax(220px, 1fr) minmax(160px, 190px) minmax(220px, 250px) minmax(170px, 210px) minmax(130px, 160px); | |
| gap: 10px; | |
| align-items: end; | |
| width: 100%; | |
| font-size: 16px; | |
| }} | |
| .source-links {{ | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| gap: 14px; | |
| flex-wrap: wrap; | |
| color: var(--muted); | |
| font-size: 16px; | |
| }} | |
| .source-links a {{ color: var(--link); text-decoration: none; font-weight: 700; }} | |
| .source-links a:hover {{ color: var(--accent); text-decoration: underline; }} | |
| .control {{ margin: 0; }} | |
| label {{ display: block; margin-bottom: 5px; font-size: 12px; color: var(--muted); font-weight: 700; text-transform: uppercase; }} | |
| input, select {{ | |
| width: 100%; | |
| border: 1px solid var(--line); | |
| border-radius: 6px; | |
| padding: 9px 10px; | |
| background: var(--field); | |
| color: var(--ink); | |
| font-family: inherit; | |
| font-size: inherit; | |
| line-height: 1.35; | |
| }} | |
| select {{ | |
| -webkit-appearance: none; | |
| appearance: none; | |
| padding-right: 42px; | |
| background-color: var(--field); | |
| background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='%23aeb9c7' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4 6 4 4 4-4'/%3E%3C/svg%3E"); | |
| background-repeat: no-repeat; | |
| background-position: right 16px center; | |
| background-size: 16px 16px; | |
| }} | |
| input::placeholder {{ color: oklch(66% 0.018 225); }} | |
| input:focus, select:focus {{ outline: 2px solid var(--accent); outline-offset: 1px; }} | |
| .table-wrap {{ background: var(--panel); border: 1px solid var(--line); border-radius: 8px; overflow-x: auto; box-shadow: var(--shadow); }} | |
| .method {{ | |
| margin-top: 22px; | |
| padding: 24px 18px 18px; | |
| border-top: 1px solid var(--line); | |
| color: var(--muted); | |
| font-size: 16px; | |
| line-height: 1.55; | |
| }} | |
| .method .source-links {{ | |
| justify-content: flex-start; | |
| margin-top: 2px; | |
| padding-top: 14px; | |
| border-top: 1px solid var(--line); | |
| }} | |
| .method h2 {{ | |
| margin: 0 0 16px; | |
| color: var(--ink); | |
| font-family: "Archivo", ui-sans-serif, system-ui, sans-serif; | |
| font-size: 22px; | |
| line-height: 1.2; | |
| }} | |
| .method-grid {{ | |
| columns: 2 320px; | |
| column-gap: 34px; | |
| }} | |
| .method-item {{ | |
| break-inside: avoid; | |
| margin: 0 0 20px; | |
| }} | |
| .method-item strong {{ | |
| display: block; | |
| margin-bottom: 5px; | |
| color: var(--ink); | |
| font-size: 15px; | |
| text-transform: uppercase; | |
| }} | |
| .formula {{ | |
| display: inline-block; | |
| margin-top: 2px; | |
| color: var(--ink); | |
| font-variant-numeric: tabular-nums; | |
| }} | |
| .site-footer {{ | |
| padding: 22px 0 6px; | |
| color: var(--muted); | |
| font-size: 15px; | |
| line-height: 1.4; | |
| text-align: center; | |
| }} | |
| .site-footer a {{ | |
| color: var(--link); | |
| font-weight: 700; | |
| text-decoration: none; | |
| }} | |
| .site-footer a:hover {{ color: var(--accent); text-decoration: underline; }} | |
| table {{ width: 100%; min-width: 980px; border-collapse: collapse; table-layout: fixed; }} | |
| th, td {{ padding: 10px 14px; border-bottom: 1px solid var(--line); vertical-align: middle; text-align: left; font-size: 16px; }} | |
| td {{ overflow-wrap: anywhere; }} | |
| th {{ | |
| position: sticky; | |
| top: 0; | |
| background: var(--header); | |
| z-index: 1; | |
| color: var(--muted); | |
| font-size: 12px; | |
| line-height: 1.22; | |
| text-transform: uppercase; | |
| vertical-align: middle; | |
| overflow-wrap: normal; | |
| word-break: normal; | |
| hyphens: none; | |
| }} | |
| th:nth-child(1), td:nth-child(1) {{ width: 62px; }} | |
| th:nth-child(2), td:nth-child(2) {{ width: 260px; }} | |
| th:nth-child(3), td:nth-child(3) {{ width: 132px; }} | |
| th:nth-child(4), td:nth-child(4) {{ width: 116px; }} | |
| th:nth-child(5), td:nth-child(5) {{ width: 80px; }} | |
| th:nth-child(6), td:nth-child(6) {{ width: 124px; }} | |
| tbody tr:hover {{ background: var(--hover); }} | |
| .rank {{ font-weight: 800; color: var(--accent-2); }} | |
| .model-cell {{ min-width: 260px; }} | |
| .model-link {{ display: block; color: var(--ink); font-weight: 800; text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }} | |
| .model-link:hover {{ color: var(--accent); }} | |
| .num {{ text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }} | |
| .metric {{ position: relative; }} | |
| .metric.has-tooltip:hover::after, | |
| .metric.has-tooltip:focus-within::after {{ | |
| content: attr(data-tooltip); | |
| position: absolute; | |
| right: 10px; | |
| top: calc(100% + 8px); | |
| z-index: 50; | |
| width: max-content; | |
| max-width: 280px; | |
| padding: 8px 10px; | |
| border: 1px solid var(--line); | |
| border-radius: 6px; | |
| background: oklch(18% 0.014 235); | |
| color: var(--ink); | |
| box-shadow: var(--shadow); | |
| font-size: 13px; | |
| line-height: 1.35; | |
| text-align: left; | |
| white-space: pre-line; | |
| pointer-events: none; | |
| }} | |
| .date-cell {{ | |
| white-space: nowrap; | |
| font-variant-numeric: tabular-nums; | |
| overflow-wrap: normal; | |
| }} | |
| .date-stamp, | |
| .age-stamp {{ | |
| display: block; | |
| white-space: nowrap; | |
| }} | |
| .age-stamp {{ | |
| margin-top: 3px; | |
| color: var(--muted); | |
| font-size: 14px; | |
| }} | |
| .artifacts {{ min-width: 280px; color: var(--muted); }} | |
| .artifact-list {{ | |
| display: block; | |
| min-width: 0; | |
| overflow: hidden; | |
| }} | |
| .artifact-list.is-expanded {{ | |
| overflow: visible; | |
| }} | |
| .artifact-primary, | |
| .artifact-more {{ | |
| display: block; | |
| min-width: 0; | |
| }} | |
| .artifact-more {{ | |
| margin-top: 3px; | |
| line-height: 1.2; | |
| }} | |
| .artifact-primary > a {{ | |
| display: block; | |
| min-width: 0; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| white-space: nowrap; | |
| }} | |
| .artifact-list.is-expanded .artifact-primary > a, | |
| .artifact-list.is-expanded .artifact-extra a {{ | |
| max-width: none; | |
| white-space: normal; | |
| overflow-wrap: anywhere; | |
| }} | |
| .artifact-extra {{ | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 5px 10px; | |
| margin-top: 6px; | |
| }} | |
| .artifact-extra[hidden] {{ | |
| display: none; | |
| }} | |
| .artifact-toggle {{ | |
| flex: 0 0 auto; | |
| border: 0; | |
| padding: 0; | |
| background: transparent; | |
| color: var(--accent); | |
| font: inherit; | |
| font-size: 14px; | |
| font-weight: 700; | |
| cursor: pointer; | |
| white-space: nowrap; | |
| }} | |
| .artifact-toggle:hover {{ color: var(--accent-2); text-decoration: underline; }} | |
| .artifacts a {{ color: var(--link); text-decoration: none; }} | |
| .artifacts a:hover {{ color: var(--accent); text-decoration: underline; }} | |
| @media (max-width: 980px) {{ | |
| .shell {{ padding: 16px 0; }} | |
| .topbar {{ grid-template-columns: 1fr; }} | |
| .controls {{ grid-template-columns: minmax(0, 1fr) repeat(4, minmax(135px, 1fr)); }} | |
| h1 {{ font-size: 38px; }} | |
| }} | |
| @media (max-width: 860px) {{ | |
| .controls {{ grid-template-columns: 1fr 1fr; }} | |
| .search-control {{ grid-column: 1 / -1; }} | |
| }} | |
| @media (max-width: 720px) {{ | |
| .shell {{ width: min(1120px, calc(100% - 24px)); padding: 12px 0; }} | |
| .topbar {{ position: relative; gap: 12px; margin-bottom: 12px; }} | |
| .title-row {{ min-height: 48px; padding: 0 50px; }} | |
| .title-block {{ gap: 4px; }} | |
| h1 {{ gap: 8px; font-size: clamp(22px, 6.2vw, 32px); line-height: 1.05; white-space: normal; }} | |
| .title-note {{ font-size: 11px; line-height: 1.25; }} | |
| .title-text {{ min-width: 0; }} | |
| .hf-logo {{ width: 30px; height: 30px; }} | |
| .filters-toggle {{ width: 38px; height: 38px; }} | |
| .info-link {{ top: 0; width: 0.58em; height: 0.58em; margin-left: 0.1em; }} | |
| .filters-toggle {{ display: inline-flex; flex-direction: column; gap: 4px; }} | |
| .controls {{ | |
| display: none; | |
| position: static; | |
| z-index: 20; | |
| grid-template-columns: 1fr; | |
| align-items: stretch; | |
| padding: 12px; | |
| border: 1px solid var(--line); | |
| border-radius: 8px; | |
| background: var(--panel); | |
| box-shadow: var(--shadow); | |
| font-size: 14px; | |
| }} | |
| .controls.is-open {{ display: grid; }} | |
| .search-control {{ grid-column: auto; }} | |
| label {{ font-size: 11px; }} | |
| input, select {{ padding: 8px 9px; }} | |
| select {{ padding-right: 40px; background-position: right 14px center; }} | |
| .table-wrap {{ overflow-x: auto; -webkit-overflow-scrolling: touch; }} | |
| table {{ min-width: 760px; }} | |
| th, td {{ padding: 10px 12px; font-size: 14px; }} | |
| th {{ font-size: 11px; }} | |
| th:nth-child(7), | |
| td:nth-child(7) {{ display: none; }} | |
| .metric small, | |
| .age-stamp {{ font-size: 12px; }} | |
| .method {{ padding: 18px 16px; }} | |
| .method-grid {{ columns: 1; }} | |
| .method-item {{ margin-bottom: 16px; }} | |
| }} | |
| @media (max-width: 420px) {{ | |
| .title-row {{ padding: 0 44px; }} | |
| h1 {{ font-size: clamp(20px, 6vw, 24px); }} | |
| .hf-logo {{ width: 26px; height: 26px; }} | |
| .filters-toggle {{ width: 36px; height: 36px; }} | |
| .info-link {{ width: 0.6em; height: 0.6em; }} | |
| table {{ min-width: 720px; }} | |
| }} | |
| </style> | |
| </head> | |
| <body> | |
| <main class="shell"> | |
| <header class="topbar"> | |
| <div class="title-row"> | |
| <button class="filters-toggle" type="button" aria-controls="filters-panel" aria-expanded="false" aria-label="Filters"> | |
| <span aria-hidden="true"></span> | |
| <span aria-hidden="true"></span> | |
| <span aria-hidden="true"></span> | |
| </button> | |
| <div class="title-block"> | |
| <h1 aria-label="Open LLM Distribution Leaderboard"><img class="hf-logo" src="https://huggingface.co/front/assets/huggingface_logo-noborder.svg" alt="" /><span class="title-text"><span>Open LLM Distribution </span><span class="title-tail">Leaderboard<a class="info-link" href="#method" aria-label="How this is calculated"><svg class="lucide lucide-info info-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="M12 16v-4"></path><path d="M12 8h.01"></path></svg></a></span></span></h1> | |
| <p class="title-note">This is work in progress, there might be inaccuracies. Last updated: {html.escape(generated_at_label)}.</p> | |
| </div> | |
| </div> | |
| <nav id="filters-panel" class="controls" aria-label="Leaderboard filters"> | |
| <div class="control search-control"> | |
| <label for="search">Search</label> | |
| <input id="search" type="search" placeholder="model, creator, artifact" /> | |
| </div> | |
| <div class="control"> | |
| <label for="ageFilter">Don't show older than</label> | |
| <select id="ageFilter"> | |
| <option value=""{age_selected("off")}>Off</option> | |
| <option value="183"{age_selected("183")}>6 months</option> | |
| <option value="365"{age_selected("365")}>1 year</option> | |
| <option value="548"{age_selected("548")}>1.5 years</option> | |
| <option value="730"{age_selected("730")}>2 years</option> | |
| </select> | |
| </div> | |
| <div class="control"> | |
| <label for="sortFilter">Sort by</label> | |
| <select id="sortFilter"> | |
| <option value="downloads_per_month">Avg Downloads / Month</option> | |
| <option value="likes_per_month">Avg Likes / month</option> | |
| </select> | |
| </div> | |
| <div class="control"> | |
| <label for="scopeFilter">Metric scope</label> | |
| <select id="scopeFilter"> | |
| <option value="group">Grouped repos</option> | |
| <option value="representative">Representative repo</option> | |
| </select> | |
| </div> | |
| <div class="control"> | |
| <label for="classFilter">Class</label> | |
| <select id="classFilter"> | |
| <option value="">All classes</option> | |
| <option value="text">Text</option> | |
| <option value="coding">Coding</option> | |
| <option value="multimodal">Multimodal</option> | |
| <option value="special">Special</option> | |
| </select> | |
| </div> | |
| </nav> | |
| </header> | |
| <section class="table-wrap"> | |
| <table id="leaderboard"> | |
| <thead> | |
| <tr> | |
| <th data-sort="num">Rank</th> | |
| <th data-sort="text">Model</th> | |
| <th data-sort="num">Avg Downloads / Month</th> | |
| <th data-sort="num">Avg Likes / month</th> | |
| <th data-sort="text">Params</th> | |
| <th data-sort="text">Created</th> | |
| <th data-sort="text">Related repos</th> | |
| </tr> | |
| </thead> | |
| <tbody>{''.join(table_rows)}</tbody> | |
| </table> | |
| </section> | |
| <section id="method" class="method" aria-labelledby="method-title"> | |
| <h2 id="method-title">How This Is Calculated</h2> | |
| <div class="method-grid"> | |
| <div class="method-item"> | |
| <strong>What counts</strong> | |
| Public Hugging Face model repos above the minimum download threshold are scanned, then filtered for LLM-ish text, coding, multimodal, and special architecture signals. | |
| </div> | |
| <div class="method-item"> | |
| <strong>Models</strong> | |
| Related repos are grouped by normalized model name and explicit base-model metadata when available. The main row uses the most-downloaded repo in that group. | |
| </div> | |
| <div class="method-item"> | |
| <strong>Metric scope</strong> | |
| Grouped metrics sum downloads, all-time downloads, and likes across related lineage repos, including package, quantized, and component repos when metadata or slug evidence links them safely. Representative metrics use only the main repo shown in the Model column. | |
| </div> | |
| <div class="method-item"> | |
| <strong>Default sort</strong> | |
| Rows are sorted by average downloads per month unless you choose likes per month. Sorting follows the selected metric scope, and visible rank is recalculated after filters. | |
| </div> | |
| <div class="method-item"> | |
| <strong>Rates</strong> | |
| <span class="formula">avg downloads/month = all-time downloads / max(model age, 1 month)</span><br /> | |
| <span class="formula">avg likes/month = total likes / max(model age, 1 month)</span> | |
| </div> | |
| <div class="method-item"> | |
| <strong>Coverage</strong> | |
| Discovery scans configured creators, router models, and popular artifact searches. Valid popular artifacts without a safe grouping target stay as standalone rows. The snapshot omits repos below the configured download floor, currently {min_downloads_label}. | |
| </div> | |
| <div class="method-item"> | |
| <strong>Age filter</strong> | |
| The default view hides models older than one year. Use Off to include older models from the generated snapshot. | |
| </div> | |
| <div class="method-item"> | |
| <strong>Snapshots</strong> | |
| The leaderboard updates daily. Each refresh writes a new latest view and keeps timestamped historical snapshots in the bucket. Ages are calculated against the snapshot generation time. | |
| </div> | |
| <div class="method-item"> | |
| <strong>Download counts</strong> | |
| Hugging Face download counts are public, deduplicated counts over tracked model files, meant to approximate model pulls rather than raw requests for every split model file. The recent downloads field is a recent-window count; downloadsAllTime is the same style of count over all time. This leaderboard uses downloadsAllTime divided by model age for Avg Downloads / Month, while recent downloads appear only in metric tooltips. | |
| </div> | |
| </div> | |
| <div class="source-links" aria-label="Sources"> | |
| <span>Sources</span> | |
| <a href="https://huggingface.co/spaces/osolmaz/leaderboard" target="_blank" rel="noreferrer">Space source</a> | |
| <a href="https://huggingface.co/buckets/osolmaz/leaderboard" target="_blank" rel="noreferrer">Data bucket</a> | |
| </div> | |
| </section> | |
| <footer class="site-footer"> | |
| Built by <a href="https://huggingface.co/osolmaz" target="_blank" rel="noreferrer">Onur Solmaz</a> with <span aria-label="Hugging Face">🤗</span> | |
| </footer> | |
| </main> | |
| <script> | |
| const search = document.querySelector('#search'); | |
| const ageFilter = document.querySelector('#ageFilter'); | |
| const sortFilter = document.querySelector('#sortFilter'); | |
| const scopeFilter = document.querySelector('#scopeFilter'); | |
| const classFilter = document.querySelector('#classFilter'); | |
| const filtersPanel = document.querySelector('#filters-panel'); | |
| const filterToggle = document.querySelector('.filters-toggle'); | |
| const rows = Array.from(document.querySelectorAll('#leaderboard tbody tr')); | |
| const tbody = document.querySelector('#leaderboard tbody'); | |
| const generatedAt = new Date({generated_js}); | |
| const rowLimit = Number({top_n_js}) || rows.length; | |
| const dayMs = 24 * 60 * 60 * 1000; | |
| function setFiltersOpen(open) {{ | |
| if (!filterToggle || !filtersPanel) return; | |
| filterToggle.setAttribute('aria-expanded', open ? 'true' : 'false'); | |
| filtersPanel.classList.toggle('is-open', open); | |
| }} | |
| function matchesAge(row, maxDays) {{ | |
| if (!maxDays) return true; | |
| const createdAt = new Date(row.dataset.created || ''); | |
| if (Number.isNaN(createdAt.getTime()) || Number.isNaN(generatedAt.getTime())) return false; | |
| return ((generatedAt - createdAt) / dayMs) <= Number(maxDays); | |
| }} | |
| function selectedScope() {{ | |
| return scopeFilter.value === 'representative' ? 'representative' : 'group'; | |
| }} | |
| function scopedDatasetKey(metric) {{ | |
| const scope = selectedScope(); | |
| return metric === 'likes_per_month' ? `${{scope}}LikesPerMonth` : `${{scope}}DownloadsPerMonth`; | |
| }} | |
| function updateMetricCell(row, metric) {{ | |
| const scope = selectedScope(); | |
| const cell = row.querySelector(`[data-metric="${{metric}}"]`); | |
| if (!cell) return; | |
| const value = cell.querySelector('.metric-value'); | |
| const prefix = metric === 'likes_per_month' ? `${{scope}}Likes` : `${{scope}}Downloads`; | |
| const rate = 'PerMonth'; | |
| const tooltip = row.dataset[`${{prefix}}Tooltip`]; | |
| if (value) value.textContent = row.dataset[`${{prefix}}${{rate}}Label`] || value.textContent; | |
| if (tooltip) {{ | |
| cell.dataset.tooltip = tooltip; | |
| cell.setAttribute('aria-label', tooltip.replace(/\\n/g, ', ')); | |
| }} | |
| }} | |
| function updateScopedMetrics() {{ | |
| rows.forEach(row => {{ | |
| updateMetricCell(row, 'downloads_per_month'); | |
| updateMetricCell(row, 'likes_per_month'); | |
| }}); | |
| }} | |
| function sortRows() {{ | |
| const key = scopedDatasetKey(sortFilter.value); | |
| rows.sort((a, b) => {{ | |
| const bv = Number(b.dataset[key]) || 0; | |
| const av = Number(a.dataset[key]) || 0; | |
| if (bv !== av) return bv - av; | |
| return a.innerText.localeCompare(b.innerText); | |
| }}); | |
| rows.forEach(row => tbody.appendChild(row)); | |
| }} | |
| function applyFilters() {{ | |
| updateScopedMetrics(); | |
| sortRows(); | |
| const q = search.value.trim().toLowerCase(); | |
| const maxDays = ageFilter.value; | |
| const cls = classFilter.value; | |
| let visible = 0; | |
| let visibleRank = 1; | |
| for (const row of rows) {{ | |
| const matchesText = !q || row.innerText.toLowerCase().includes(q); | |
| const matchesDate = matchesAge(row, maxDays); | |
| const matchesClass = !cls || row.dataset.class === cls; | |
| const matches = matchesText && matchesDate && matchesClass; | |
| const show = matches && visible < rowLimit; | |
| row.style.display = show ? '' : 'none'; | |
| if (show) {{ | |
| row.children[0].textContent = String(visibleRank); | |
| visibleRank += 1; | |
| visible += 1; | |
| }} | |
| }} | |
| }} | |
| search.addEventListener('input', applyFilters); | |
| ageFilter.addEventListener('change', applyFilters); | |
| sortFilter.addEventListener('change', applyFilters); | |
| scopeFilter.addEventListener('change', applyFilters); | |
| classFilter.addEventListener('change', applyFilters); | |
| tbody.addEventListener('click', event => {{ | |
| const target = event.target; | |
| if (!(target instanceof Element)) return; | |
| const button = target.closest('.artifact-toggle'); | |
| if (!button) return; | |
| const list = button.closest('.artifact-list'); | |
| const extra = list?.querySelector('.artifact-extra'); | |
| if (!list || !extra) return; | |
| const expanded = button.getAttribute('aria-expanded') === 'true'; | |
| button.setAttribute('aria-expanded', expanded ? 'false' : 'true'); | |
| list.classList.toggle('is-expanded', !expanded); | |
| extra.hidden = expanded; | |
| button.textContent = expanded ? `+ ${{button.dataset.moreLabel || 'more variants'}}` : 'Show fewer variants'; | |
| }}); | |
| filterToggle?.addEventListener('click', () => {{ | |
| setFiltersOpen(filterToggle.getAttribute('aria-expanded') !== 'true'); | |
| }}); | |
| document.addEventListener('click', event => {{ | |
| if (!filterToggle || !filtersPanel || filterToggle.getAttribute('aria-expanded') !== 'true') return; | |
| const target = event.target; | |
| if (target instanceof Node && (filterToggle.contains(target) || filtersPanel.contains(target))) return; | |
| setFiltersOpen(false); | |
| }}); | |
| document.addEventListener('keydown', event => {{ | |
| if (event.key === 'Escape') setFiltersOpen(false); | |
| }}); | |
| applyFilters(); | |
| </script> | |
| </body> | |
| </html> | |
| """ | |
| def write_json(path: Path, payload: Any) -> None: | |
| path.parent.mkdir(parents=True, exist_ok=True) | |
| path.write_text(json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=False) + "\n", encoding="utf-8") | |
| def write_jsonl(path: Path, rows: list[dict[str, Any]]) -> None: | |
| path.parent.mkdir(parents=True, exist_ok=True) | |
| with path.open("w", encoding="utf-8") as handle: | |
| for row in rows: | |
| handle.write(json.dumps(row, sort_keys=True, ensure_ascii=False) + "\n") | |
| def write_csv(path: Path, rows: list[dict[str, Any]], fieldnames: list[str]) -> None: | |
| path.parent.mkdir(parents=True, exist_ok=True) | |
| with path.open("w", encoding="utf-8", newline="") as handle: | |
| writer = csv.DictWriter(handle, fieldnames=fieldnames, lineterminator="\n") | |
| writer.writeheader() | |
| for row in rows: | |
| writer.writerow({field: row.get(field, "") for field in fieldnames}) | |
| def copy_tree_contents(source: Path, dest: Path) -> None: | |
| dest.mkdir(parents=True, exist_ok=True) | |
| for path in source.iterdir(): | |
| target = dest / path.name | |
| if path.is_dir(): | |
| if target.exists(): | |
| shutil.rmtree(target) | |
| shutil.copytree(path, target) | |
| else: | |
| shutil.copy2(path, target) | |
| def write_outputs(payload: dict[str, Any], output_dir: Path) -> Path: | |
| run_stamp = stamp() | |
| with tempfile.TemporaryDirectory(prefix="open-llm-leaderboard-") as tmp: | |
| tmp_path = Path(tmp) | |
| latest = tmp_path / "latest" | |
| snapshot = tmp_path / "snapshots" / run_stamp | |
| html_text = render_html(payload) | |
| for base in (latest, snapshot): | |
| base.mkdir(parents=True, exist_ok=True) | |
| (base / "index.html").write_text(html_text, encoding="utf-8") | |
| write_json(base / "leaderboard.json", payload) | |
| write_json(base / "summary.json", payload["summary"]) | |
| write_json(base / "groups.json", payload["groups"]) | |
| write_jsonl(base / "models.jsonl", payload["models"]) | |
| write_csv(base / "groups.csv", payload["groups"], GROUP_CSV_FIELDS) | |
| write_csv(base / "models.csv", payload["models"], MODEL_CSV_FIELDS) | |
| output_dir.mkdir(parents=True, exist_ok=True) | |
| copy_tree_contents(tmp_path / "latest", output_dir / "latest") | |
| copy_tree_contents(tmp_path / "snapshots", output_dir / "snapshots") | |
| (output_dir / "README.md").write_text( | |
| "# Open LLM Distribution Leaderboard Data\n\n" | |
| "Mutable bucket-backed data for the Open LLM Distribution Leaderboard Space.\n\n" | |
| "- `latest/index.html`: rendered leaderboard HTML.\n" | |
| "- `latest/leaderboard.json`: full machine-readable latest payload.\n" | |
| "- `latest/groups.csv`: grouped leaderboard table.\n" | |
| "- `latest/models.csv`: candidate artifact table.\n" | |
| "- `snapshots/`: timestamped historical runs.\n", | |
| encoding="utf-8", | |
| ) | |
| return output_dir / "latest" / "index.html" | |
| def refresh( | |
| *, | |
| output_dir: Path, | |
| min_downloads: int = DEFAULT_MIN_DOWNLOADS, | |
| top_n: int = DEFAULT_TOP_N, | |
| max_age_days: int = DEFAULT_MAX_AGE_DAYS, | |
| limit_per_creator: int | None = None, | |
| token: str | None = None, | |
| enable_discovery: bool | None = None, | |
| ) -> dict[str, Any]: | |
| payload = build_payload( | |
| min_downloads=min_downloads, | |
| top_n=top_n, | |
| max_age_days=max_age_days, | |
| limit_per_creator=limit_per_creator, | |
| token=token, | |
| enable_discovery=enable_discovery, | |
| ) | |
| html_path = write_outputs(payload, output_dir) | |
| payload["html_path"] = str(html_path) | |
| return payload | |
| def parse_args() -> argparse.Namespace: | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument("--output-dir", type=Path, default=DEFAULT_LOCAL_DATA_DIR) | |
| parser.add_argument("--min-downloads", type=int, default=int(os.environ.get("OPEN_MODEL_MIN_DOWNLOADS", DEFAULT_MIN_DOWNLOADS))) | |
| parser.add_argument("--top-n", type=int, default=int(os.environ.get("OPEN_MODEL_TOP_N", DEFAULT_TOP_N))) | |
| parser.add_argument("--max-age-days", type=int, default=int(os.environ.get("OPEN_MODEL_MAX_AGE_DAYS", DEFAULT_MAX_AGE_DAYS))) | |
| parser.add_argument("--limit-per-creator", type=int, default=None) | |
| parser.add_argument("--disable-discovery", action="store_true") | |
| return parser.parse_args() | |
| def main() -> int: | |
| args = parse_args() | |
| token = os.environ.get("HF_TOKEN") or os.environ.get("HUGGING_FACE_HUB_TOKEN") | |
| payload = refresh( | |
| output_dir=args.output_dir, | |
| min_downloads=args.min_downloads, | |
| top_n=args.top_n, | |
| max_age_days=args.max_age_days, | |
| limit_per_creator=args.limit_per_creator, | |
| token=token, | |
| enable_discovery=False if args.disable_discovery else None, | |
| ) | |
| print( | |
| json.dumps( | |
| { | |
| "generated_at": payload["generated_at"], | |
| "groups": payload["summary"]["leaderboard_groups"], | |
| "artifacts": payload["summary"]["candidate_artifacts"], | |
| "html_path": payload["html_path"], | |
| }, | |
| sort_keys=True, | |
| ) | |
| ) | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |