Spaces:
Running
Running
| import fs from "node:fs"; | |
| import path from "node:path"; | |
| const root = path.resolve(import.meta.dirname, ".."); | |
| const outputPath = path.join(root, "assets", "local-frontier-model-data.js"); | |
| const threshold = 100_000; | |
| const hfBase = "https://huggingface.co"; | |
| const apiBase = `${hfBase}/api/models`; | |
| const routerModelsApi = "https://router.huggingface.co/v1/models"; | |
| const requestHeaders = { | |
| "user-agent": "local-frontier-model-generator", | |
| ...(process.env.HF_TOKEN | |
| ? { authorization: `Bearer ${process.env.HF_TOKEN}` } | |
| : {}), | |
| }; | |
| const cache = new Map(); | |
| const defaultCreators = [ | |
| "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", | |
| ]; | |
| const expandFields = [ | |
| "author", | |
| "cardData", | |
| "createdAt", | |
| "downloads", | |
| "downloadsAllTime", | |
| "evalResults", | |
| "gated", | |
| "gguf", | |
| "inferenceProviderMapping", | |
| "lastModified", | |
| "library_name", | |
| "likes", | |
| "model-index", | |
| "pipeline_tag", | |
| "private", | |
| "safetensors", | |
| "sha", | |
| "siblings", | |
| "tags", | |
| "transformersInfo", | |
| "trendingScore", | |
| ]; | |
| const llmPipelines = new Set([ | |
| "text-generation", | |
| "image-text-to-text", | |
| "video-text-to-text", | |
| "audio-text-to-text", | |
| "any-to-any", | |
| "text-to-audio", | |
| ]); | |
| const excludedPipelines = new Set([ | |
| "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", | |
| ]); | |
| const textModelTerms = [ | |
| "instruct", | |
| "chat", | |
| "assistant", | |
| "reasoning", | |
| "thinking", | |
| "coder", | |
| "code", | |
| "llm", | |
| "vlm", | |
| "omni", | |
| "gpt-oss", | |
| "kimi", | |
| "glm", | |
| "llama", | |
| "qwen", | |
| "gemma", | |
| "deepseek", | |
| "nemotron", | |
| "olmo", | |
| "apertus", | |
| "eurollm", | |
| ]; | |
| const excludedTerms = [ | |
| "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", | |
| ]; | |
| const excludedTokenTerms = new Set(["asr", "encoder"]); | |
| const excludedAudioTokenTerms = new Set(["speech"]); | |
| const globalArtifactSearchQueries = [ | |
| "gguf", | |
| "awq", | |
| "gptq", | |
| "fp8", | |
| "fp4", | |
| "nvfp4", | |
| "quantized", | |
| "mlx", | |
| "llama.cpp", | |
| ]; | |
| const relatedArtifactSuffixes = [ | |
| "gguf", | |
| "awq", | |
| "gptq", | |
| "fp8", | |
| "fp4", | |
| "nvfp4", | |
| "mlx", | |
| ]; | |
| const relatedVariantTokens = new Set([ | |
| "base", | |
| "chat", | |
| "flash", | |
| "instruct", | |
| "it", | |
| "pro", | |
| ]); | |
| const modelFamilyTokens = new Set([ | |
| "apertus", | |
| "deepseek", | |
| "ernie", | |
| "gemma", | |
| "glm", | |
| "gpt", | |
| "kimi", | |
| "llama", | |
| "minimax", | |
| "mistral", | |
| "mixtral", | |
| "nemotron", | |
| "olmo", | |
| "phi", | |
| "qwen", | |
| "qwen2", | |
| "qwen3", | |
| "step", | |
| ]); | |
| const codingModelNameRe = | |
| /(^|[-_/])(?:coder|coding|code)(?:$|[-_/])|(^|[-_/])(?:codellama|codeqwen|opencoder|starcoder)[a-z0-9.]*($|[-_/])/; | |
| const assumptions = { | |
| default_overhead_gb: 8, | |
| default_l_alloc_tokens: 100000, | |
| default_l_read_tokens: 32000, | |
| default_r_star_toks: 20, | |
| default_rho: 1, | |
| unit: "decimal GB", | |
| hf_pipeline_filter: | |
| "leaderboard-compatible LLM and multimodal candidate filter", | |
| hf_llm_pipeline_tags: [...llmPipelines], | |
| min_hf_downloads: threshold, | |
| generated_by: "scripts/generate-model-data.mjs", | |
| }; | |
| function sleep(ms) { | |
| return new Promise((resolve) => setTimeout(resolve, ms)); | |
| } | |
| async function fetchJson(url, optional = false) { | |
| const { payload } = await fetchJsonWithHeaders(url, optional); | |
| return payload; | |
| } | |
| async function fetchJsonWithHeaders(url, optional = false) { | |
| for (let attempt = 1; attempt <= 3; attempt++) { | |
| const response = await fetch(url, { headers: requestHeaders }); | |
| if (response.ok) { | |
| return { payload: await response.json(), headers: response.headers }; | |
| } | |
| if (optional && [401, 403, 404].includes(response.status)) { | |
| return { payload: null, headers: response.headers }; | |
| } | |
| if (response.status === 429 || response.status >= 500) { | |
| await sleep(500 * attempt); | |
| continue; | |
| } | |
| throw new Error(`${response.status} ${response.statusText}: ${url}`); | |
| } | |
| if (optional) return { payload: null, headers: new Headers() }; | |
| throw new Error(`failed after retries: ${url}`); | |
| } | |
| function parseNextLink(link) { | |
| if (!link) return null; | |
| for (const part of link.split(",")) { | |
| const match = part.match(/<([^>]+)>;\s*rel="next"/); | |
| if (match) return match[1]; | |
| } | |
| return null; | |
| } | |
| function apiUrl(params) { | |
| const search = new URLSearchParams(); | |
| for (const [key, value] of params) { | |
| search.append(key, String(value)); | |
| } | |
| return `${apiBase}?${search}`; | |
| } | |
| function detailApiUrl(id) { | |
| const search = new URLSearchParams(); | |
| for (const field of expandFields) search.append("expand", field); | |
| return `${apiBase}/${encodeURIComponent(id).replace(/%2F/g, "/")}?${search}`; | |
| } | |
| async function modelInfo(id) { | |
| const key = `model:${id}`; | |
| if (!cache.has(key)) { | |
| cache.set(key, fetchJson(detailApiUrl(id), true)); | |
| } | |
| return cache.get(key); | |
| } | |
| async function rawConfig(id) { | |
| const key = `config:${id}`; | |
| if (!cache.has(key)) { | |
| cache.set( | |
| key, | |
| fetchJson(`${hfBase}/${id}/raw/main/config.json`, true).catch(() => null), | |
| ); | |
| } | |
| return cache.get(key); | |
| } | |
| function uniquePreserveOrder(values) { | |
| const seen = new Set(); | |
| const result = []; | |
| for (const value of values) { | |
| if (!value || seen.has(value)) continue; | |
| seen.add(value); | |
| result.push(value); | |
| } | |
| return result; | |
| } | |
| function repoIdFrom(model) { | |
| const id = model?.id || model?.modelId; | |
| return typeof id === "string" ? id : ""; | |
| } | |
| function downloadCount(model) { | |
| return Number.isInteger(model?.downloads) ? model.downloads : 0; | |
| } | |
| async function fetchRouterModels() { | |
| try { | |
| const payload = await fetchJson(routerModelsApi, true); | |
| const records = payload && Array.isArray(payload.data) ? payload.data : []; | |
| return records.filter( | |
| (record) => record && typeof record.id === "string" && record.id, | |
| ); | |
| } catch (error) { | |
| console.error(`router model fetch failed: ${error.message}`); | |
| return []; | |
| } | |
| } | |
| function routerCreators(routerRecords) { | |
| return uniquePreserveOrder( | |
| routerRecords.map((record) => { | |
| if (typeof record.owned_by === "string" && record.owned_by) { | |
| return record.owned_by; | |
| } | |
| return record.id?.includes("/") ? record.id.split("/", 1)[0] : ""; | |
| }), | |
| ); | |
| } | |
| async function listCreatorModels(creator) { | |
| const params = [ | |
| ["author", creator], | |
| ["limit", 100], | |
| ["sort", "downloads"], | |
| ["direction", -1], | |
| ...expandFields.map((field) => ["expand", field]), | |
| ]; | |
| let url = apiUrl(params); | |
| const rows = []; | |
| while (url) { | |
| const { payload, headers } = await fetchJsonWithHeaders(url); | |
| if (!Array.isArray(payload)) { | |
| throw new Error(`expected model list for creator ${creator}`); | |
| } | |
| rows.push(...payload.filter((item) => item && typeof item === "object")); | |
| const minDownloads = Math.min( | |
| ...payload.map((model) => downloadCount(model)), | |
| ); | |
| if (payload.length === 0 || minDownloads < threshold) break; | |
| url = parseNextLink(headers.get("link")); | |
| } | |
| return rows; | |
| } | |
| async function listSearchModels(query) { | |
| const params = [ | |
| ["search", query], | |
| ["limit", 100], | |
| ["sort", "downloads"], | |
| ["direction", -1], | |
| ...expandFields.map((field) => ["expand", field]), | |
| ]; | |
| let url = apiUrl(params); | |
| const rows = []; | |
| while (url && rows.length < 500) { | |
| const { payload, headers } = await fetchJsonWithHeaders(url); | |
| if (!Array.isArray(payload)) { | |
| throw new Error(`expected model list for search ${query}`); | |
| } | |
| let stop = false; | |
| for (const item of payload) { | |
| if (!item || typeof item !== "object") continue; | |
| if (downloadCount(item) < threshold) { | |
| stop = true; | |
| break; | |
| } | |
| rows.push(item); | |
| if (rows.length >= 500) break; | |
| } | |
| if (stop || rows.length >= 500) break; | |
| url = parseNextLink(headers.get("link")); | |
| } | |
| return rows; | |
| } | |
| function fileCounts(record) { | |
| const counts = new Map(); | |
| for (const sibling of record.siblings || []) { | |
| const name = sibling?.rfilename; | |
| if (typeof name !== "string") continue; | |
| const lower = name.toLowerCase(); | |
| for (const [key, suffix] of [ | |
| ["safetensors", ".safetensors"], | |
| ["gguf", ".gguf"], | |
| ["bin", ".bin"], | |
| ]) { | |
| if (lower.endsWith(suffix)) { | |
| counts.set(key, (counts.get(key) || 0) + 1); | |
| } | |
| } | |
| if ( | |
| lower.endsWith("tokenizer.json") || | |
| lower.endsWith("tokenizer_config.json") | |
| ) { | |
| counts.set("tokenizer", (counts.get("tokenizer") || 0) + 1); | |
| } | |
| if (lower.endsWith("chat_template.jinja")) { | |
| counts.set("chat_template", (counts.get("chat_template") || 0) + 1); | |
| } | |
| } | |
| return counts; | |
| } | |
| function providerSummary(mapping) { | |
| const rows = Array.isArray(mapping) ? mapping : []; | |
| let supportsTools = false; | |
| let supportsStructured = false; | |
| for (const item of rows) { | |
| if (!item || typeof item !== "object") continue; | |
| supportsTools = supportsTools || item.features?.toolCalling === true; | |
| supportsStructured = | |
| supportsStructured || item.features?.structuredOutput === true; | |
| } | |
| return { supportsTools, supportsStructured }; | |
| } | |
| function routerSummary(routerEntry) { | |
| const providers = Array.isArray(routerEntry?.providers) | |
| ? routerEntry.providers | |
| : []; | |
| return { | |
| routerServed: Boolean(routerEntry), | |
| supportsTools: providers.some((provider) => provider?.supports_tools), | |
| supportsStructured: providers.some( | |
| (provider) => provider?.supports_structured_output, | |
| ), | |
| }; | |
| } | |
| function textTokens(text) { | |
| return new Set( | |
| text | |
| .toLowerCase() | |
| .split(/[^a-z0-9]+/) | |
| .filter(Boolean), | |
| ); | |
| } | |
| function hasExcludedTerm(row) { | |
| const text = `${row.id} ${row.pipeline_tag} ${row.tags}`.toLowerCase(); | |
| if (excludedTerms.some((term) => text.includes(term))) return true; | |
| const tokens = textTokens(text); | |
| if ([...excludedTokenTerms].some((token) => tokens.has(token))) return true; | |
| if ( | |
| !llmPipelines.has(row.pipeline_tag) && | |
| [...excludedAudioTokenTerms].some((token) => tokens.has(token)) | |
| ) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| function hasTextModelSignal(modelId) { | |
| if (codingModelNameRe.test(modelId)) return true; | |
| const boundaryTerms = new Set([ | |
| "assistant", | |
| "chat", | |
| "code", | |
| "coder", | |
| "coding", | |
| "instruct", | |
| "reasoning", | |
| "thinking", | |
| ]); | |
| for (const term of textModelTerms) { | |
| if (boundaryTerms.has(term)) { | |
| const boundaryRe = new RegExp(`(^|[-_/])${term}($|[-_/])`); | |
| if (boundaryRe.test(modelId)) return true; | |
| continue; | |
| } | |
| if (modelId.includes(term)) return true; | |
| } | |
| return false; | |
| } | |
| function normalizeCandidateRecord(record, routerEntry = null) { | |
| const counts = fileCounts(record); | |
| const provider = providerSummary(record.inferenceProviderMapping); | |
| const router = routerSummary(routerEntry); | |
| const id = repoIdFrom(record); | |
| const tags = asArray(record.tags).filter((tag) => typeof tag === "string"); | |
| return { | |
| id, | |
| downloads: downloadCount(record), | |
| pipeline_tag: record.pipeline_tag || "", | |
| private: record.private, | |
| tags: tags.join(","), | |
| has_chat_template: counts.get("chat_template") > 0, | |
| supports_tools_any: provider.supportsTools, | |
| supports_structured_output_any: provider.supportsStructured, | |
| router_supports_tools_any: router.supportsTools, | |
| router_supports_structured_output_any: router.supportsStructured, | |
| router_served: router.routerServed, | |
| }; | |
| } | |
| function candidateDecision(row) { | |
| const modelId = row.id.toLowerCase(); | |
| const text = `${row.id} ${row.pipeline_tag} ${row.tags}`.toLowerCase(); | |
| if (row.private === true) return [false, "private"]; | |
| if (excludedPipelines.has(row.pipeline_tag)) { | |
| return [false, `excluded pipeline: ${row.pipeline_tag}`]; | |
| } | |
| if (hasExcludedTerm(row)) return [false, "excluded narrow/non-LLM term"]; | |
| if (llmPipelines.has(row.pipeline_tag)) { | |
| return [true, `LLM pipeline: ${row.pipeline_tag}`]; | |
| } | |
| if (row.has_chat_template) return [true, "chat template"]; | |
| if (row.supports_tools_any || row.router_supports_tools_any) { | |
| return [true, "tool support"]; | |
| } | |
| if ( | |
| row.supports_structured_output_any || | |
| row.router_supports_structured_output_any | |
| ) { | |
| return [true, "structured output support"]; | |
| } | |
| if (text.includes("conversational")) return [true, "conversational tag"]; | |
| if (hasTextModelSignal(modelId)) return [true, "model-name signal"]; | |
| return [false, "no LLM signal"]; | |
| } | |
| function normalizedRepoSlug(modelId) { | |
| return modelId | |
| .split("/") | |
| .at(-1) | |
| .toLowerCase() | |
| .replace(/[^a-z0-9]+/g, "-") | |
| .replace(/^-|-$/g, ""); | |
| } | |
| function slugTokens(slug) { | |
| return slug.split("-").filter(Boolean); | |
| } | |
| function hasModelShape(slug) { | |
| const tokens = slugTokens(slug); | |
| return tokens.some((token) => modelFamilyTokens.has(token)); | |
| } | |
| function slugDedupeKey(modelId) { | |
| let slug = normalizedRepoSlug(modelId); | |
| let previous = null; | |
| while (slug && slug !== previous) { | |
| previous = slug; | |
| slug = slug.replace(/-(?:gguf|awq|gptq|fp8|fp4|mxfp8|nvfp4)-v[0-9]+$/, ""); | |
| slug = slug.replace( | |
| /-(?:gguf|awq|gptq|fp8|fp4|mxfp8|nvfp4|bf16|fp16|int2|int3|int4|int8|q[2-8](?:-(?:0|1|k|m|s|l))*|4bit|8bit|bnb|bitsandbytes|mlx|exl2|onnx|openvino|tensorrt|modelopt|qat|quantized|quant|compressed|safetensors|hf)$/, | |
| "", | |
| ); | |
| slug = slug.replace(/-qat-w[0-9]+a[0-9]+-ct$/, ""); | |
| } | |
| return slug || normalizedRepoSlug(modelId); | |
| } | |
| function shortenedVariantSlug(slug) { | |
| const tokens = slugTokens(slug); | |
| while (tokens.length > 2 && relatedVariantTokens.has(tokens.at(-1))) { | |
| tokens.pop(); | |
| } | |
| return tokens.join("-"); | |
| } | |
| function discoveryQueries(seedRows) { | |
| const queries = [...globalArtifactSearchQueries]; | |
| const ordered = [...seedRows].sort( | |
| (a, b) => b.downloads - a.downloads || a.id.localeCompare(b.id), | |
| ); | |
| for (const row of ordered.slice(0, 12)) { | |
| const slug = slugDedupeKey(row.id); | |
| if (!hasModelShape(slug)) continue; | |
| for (const candidateSlug of uniquePreserveOrder([ | |
| slug, | |
| shortenedVariantSlug(slug), | |
| ])) { | |
| if (!candidateSlug || !hasModelShape(candidateSlug)) continue; | |
| for (const suffix of relatedArtifactSuffixes) { | |
| queries.push(`${candidateSlug}-${suffix}`); | |
| } | |
| } | |
| } | |
| return uniquePreserveOrder(queries); | |
| } | |
| async function discoverAdditionalModels(seedRows, existingIds) { | |
| const discovered = new Map(); | |
| for (const query of discoveryQueries(seedRows)) { | |
| try { | |
| const rows = await listSearchModels(query); | |
| console.error(`discovery ${query}: ${rows.length}`); | |
| for (const row of rows) { | |
| const id = repoIdFrom(row); | |
| if (!id || existingIds.has(id)) continue; | |
| discovered.set(id, row); | |
| } | |
| } catch (error) { | |
| console.error(`discovery ${query} failed: ${error.message}`); | |
| } | |
| } | |
| return [...discovered.values()]; | |
| } | |
| async function downloadedModels() { | |
| const routerRecords = await fetchRouterModels(); | |
| const routerById = new Map( | |
| routerRecords.map((record) => [record.id, record]), | |
| ); | |
| const creators = uniquePreserveOrder([ | |
| ...defaultCreators, | |
| ...routerCreators(routerRecords), | |
| ]); | |
| const deduped = new Map(); | |
| for (const creator of creators) { | |
| try { | |
| const rows = await listCreatorModels(creator); | |
| console.error(`creator ${creator}: ${rows.length}`); | |
| for (const row of rows) { | |
| const id = repoIdFrom(row); | |
| if (id) deduped.set(id, row); | |
| } | |
| } catch (error) { | |
| console.error(`creator ${creator} failed: ${error.message}`); | |
| } | |
| } | |
| for (const record of routerRecords) { | |
| if (deduped.has(record.id)) continue; | |
| const detail = await modelInfo(record.id); | |
| if (detail) deduped.set(record.id, detail); | |
| } | |
| const seedRows = [...deduped.values()] | |
| .map((row) => | |
| normalizeCandidateRecord(row, routerById.get(repoIdFrom(row))), | |
| ) | |
| .filter((row) => row.downloads >= threshold && candidateDecision(row)[0]); | |
| const discoveredRows = await discoverAdditionalModels( | |
| seedRows, | |
| new Set(deduped.keys()), | |
| ); | |
| for (const row of discoveredRows) { | |
| const id = repoIdFrom(row); | |
| if (id) deduped.set(id, row); | |
| } | |
| return [...deduped.values()].filter((model) => { | |
| if (downloadCount(model) < threshold) return false; | |
| const row = normalizeCandidateRecord( | |
| model, | |
| routerById.get(repoIdFrom(model)), | |
| ); | |
| return candidateDecision(row)[0]; | |
| }); | |
| } | |
| function asArray(value) { | |
| if (Array.isArray(value)) return value; | |
| return value === undefined || value === null ? [] : [value]; | |
| } | |
| function licenseFrom(model) { | |
| return ( | |
| model.cardData?.license || | |
| model.tags?.find((tag) => tag.startsWith("license:"))?.slice(8) || | |
| "unknown" | |
| ); | |
| } | |
| function baseModelIds(model) { | |
| const ids = new Set(); | |
| for (const value of asArray(model.cardData?.base_model)) { | |
| if (typeof value === "string" && value.includes("/")) ids.add(value); | |
| } | |
| for (const tag of model.tags || []) { | |
| if (!tag.startsWith("base_model:")) continue; | |
| const parts = tag.split(":"); | |
| const id = parts.at(-1); | |
| if (id?.includes("/")) ids.add(id); | |
| } | |
| return [...ids].filter((id) => id !== model.id); | |
| } | |
| function numberFrom(...values) { | |
| for (const value of values) { | |
| const number = Number(value); | |
| if (Number.isFinite(number) && number > 0) return number; | |
| } | |
| return null; | |
| } | |
| function intFrom(...values) { | |
| const number = numberFrom(...values); | |
| return number === null ? null : Math.trunc(number); | |
| } | |
| function parseParamB(text) { | |
| const normalized = text.replace(/_/g, "-"); | |
| const moe = normalized.match( | |
| /(?:^|[-/])(\d+(?:\.\d+)?)x(\d+(?:\.\d+)?)([bm])(?:$|[-/])/i, | |
| ); | |
| if (moe) { | |
| const count = Number(moe[1]); | |
| const size = Number(moe[2]); | |
| return count * size * (moe[3].toLowerCase() === "m" ? 0.001 : 1); | |
| } | |
| const match = normalized.match(/(?:^|[-/])(\d+(?:\.\d+)?)([bm])(?:$|[-/])/i); | |
| if (!match) return null; | |
| return Number(match[1]) * (match[2].toLowerCase() === "m" ? 0.001 : 1); | |
| } | |
| function parseActiveParamB(text) { | |
| const match = text.match(/(?:^|[-_/])A(\d+(?:\.\d+)?)([BM])(?:$|[-_/])/); | |
| if (!match) return null; | |
| return Number(match[1]) * (match[2] === "M" ? 0.001 : 1); | |
| } | |
| function round(value, digits = 6) { | |
| return Number(value.toFixed(digits)); | |
| } | |
| function paramCountB(model, baseInfo, id) { | |
| const totals = [ | |
| model.safetensors?.total, | |
| baseInfo?.safetensors?.total, | |
| model.safetensors?.parameters?.F16, | |
| model.safetensors?.parameters?.BF16, | |
| model.safetensors?.parameters?.F32, | |
| ].filter((value) => Number.isFinite(value) && value > 0); | |
| if (totals.length) return round(Math.max(...totals) / 1e9, 4); | |
| const parsed = parseParamB(id) ?? parseParamB(baseInfo?.id ?? ""); | |
| return round(parsed && parsed > 0 ? parsed : 0.1, 4); | |
| } | |
| function quantizationBits(config, model) { | |
| const quant = config?.quantization_config; | |
| const explicitBits = numberFrom(quant?.bits, quant?.nbits, quant?.num_bits); | |
| if (explicitBits) return explicitBits; | |
| const groupBits = Object.values(quant?.config_groups || {}) | |
| .map((group) => numberFrom(group?.weights?.num_bits, group?.num_bits)) | |
| .filter(Boolean); | |
| if (groupBits.length) return Math.min(...groupBits); | |
| const haystack = `${model.id} ${(model.tags || []).join(" ")}`.toLowerCase(); | |
| if (haystack.includes("2-bit") || haystack.includes("q2")) return 2; | |
| if ( | |
| haystack.includes("4-bit") || | |
| haystack.includes("q4") || | |
| haystack.includes("int4") || | |
| haystack.includes("gptq") || | |
| haystack.includes("awq") || | |
| haystack.includes("mxfp4") || | |
| haystack.includes("nvfp4") || | |
| haystack.includes("fp4") | |
| ) { | |
| return 4; | |
| } | |
| if ( | |
| haystack.includes("8-bit") || | |
| haystack.includes("int8") || | |
| haystack.includes("fp8") || | |
| haystack.includes("q8") | |
| ) { | |
| return 8; | |
| } | |
| return null; | |
| } | |
| function dtypeBytes(model) { | |
| const parameters = model.safetensors?.parameters || {}; | |
| const weighted = Object.entries(parameters) | |
| .map(([dtype, count]) => { | |
| const key = dtype.toUpperCase(); | |
| const bytes = | |
| key.includes("F64") || key.includes("I64") | |
| ? 8 | |
| : key.includes("F32") || key.includes("I32") | |
| ? 4 | |
| : key.includes("F16") || key.includes("BF16") || key.includes("I16") | |
| ? 2 | |
| : key.includes("I8") | |
| ? 1 | |
| : null; | |
| return bytes ? { bytes, count: Number(count) } : null; | |
| }) | |
| .filter(Boolean); | |
| const total = weighted.reduce((sum, row) => sum + row.count, 0); | |
| if (!total) return null; | |
| return weighted.reduce((sum, row) => sum + row.bytes * row.count, 0) / total; | |
| } | |
| function precision(config, model) { | |
| const bits = quantizationBits(config, model); | |
| const haystack = `${model.id} ${(model.tags || []).join(" ")}`.toLowerCase(); | |
| if (haystack.includes("gguf")) return bits ? `GGUF ${bits}-bit` : "GGUF"; | |
| if (haystack.includes("nvfp4")) return "NVFP4"; | |
| if (haystack.includes("mxfp4")) return "MXFP4"; | |
| if (haystack.includes("fp8")) return "FP8"; | |
| if (bits) return `${bits}-bit`; | |
| const dtypes = Object.keys(model.safetensors?.parameters || {}); | |
| if (dtypes.length) return dtypes.join("/"); | |
| return "metadata-estimated"; | |
| } | |
| function bytesPerParam(config, model) { | |
| const bits = quantizationBits(config, model); | |
| if (bits) return round(bits / 8, 4); | |
| return round(dtypeBytes(model) ?? 2, 4); | |
| } | |
| function contextTokens(config) { | |
| const rope = config?.rope_scaling || {}; | |
| const value = numberFrom( | |
| config?.max_position_embeddings, | |
| config?.max_sequence_length, | |
| config?.max_seq_len, | |
| config?.seq_length, | |
| config?.n_positions, | |
| config?.n_ctx, | |
| rope?.original_max_position_embeddings, | |
| ); | |
| if (value && value < 100_000_000) return Math.trunc(value); | |
| return 2048; | |
| } | |
| function effectiveConfig(config) { | |
| if (!config || typeof config !== "object") return {}; | |
| for (const key of [ | |
| "text_config", | |
| "language_config", | |
| "llm_config", | |
| "model_config", | |
| ]) { | |
| if (config[key] && typeof config[key] === "object") return config[key]; | |
| } | |
| return config; | |
| } | |
| function architectureKind(config) { | |
| const modelType = String(config?.model_type || "").toLowerCase(); | |
| if ( | |
| modelType.includes("mamba") || | |
| modelType.includes("rwkv") || | |
| modelType.includes("recurrent") | |
| ) { | |
| return "recurrent"; | |
| } | |
| if ( | |
| config?.num_local_experts || | |
| config?.num_experts || | |
| config?.n_routed_experts || | |
| config?.num_experts_per_tok || | |
| modelType.includes("moe") | |
| ) { | |
| return "moe"; | |
| } | |
| return "dense"; | |
| } | |
| function architectureType(kind) { | |
| if (kind === "moe") return "MoE transformer"; | |
| if (kind === "recurrent") return "Recurrent state-space model"; | |
| return "Dense transformer"; | |
| } | |
| function headDim(config, attentionHeads) { | |
| return intFrom( | |
| config?.head_dim, | |
| config?.hidden_size && attentionHeads | |
| ? Number(config.hidden_size) / attentionHeads | |
| : null, | |
| config?.n_embd && attentionHeads | |
| ? Number(config.n_embd) / attentionHeads | |
| : null, | |
| ); | |
| } | |
| function kvGbPer1k(config, kind, layers, kvHeads, dim) { | |
| if (kind === "recurrent") return 0; | |
| if (!layers) return 0; | |
| const heads = kvHeads || intFrom(config?.num_attention_heads, config?.n_head); | |
| const width = | |
| dim || | |
| (numberFrom(config?.hidden_size, config?.n_embd) && heads | |
| ? numberFrom(config?.hidden_size, config?.n_embd) / heads | |
| : null); | |
| if (!heads || !width) return 0; | |
| return round((layers * 2 * heads * width * 2 * 1000) / 1e9, 6); | |
| } | |
| function shortName(id) { | |
| return id | |
| .split("/") | |
| .at(-1) | |
| .replace(/[-_](Instruct|Chat|Base|HF|GGUF|AWQ|GPTQ)$/i, "") | |
| .replace(/[-_]/g, " ") | |
| .slice(0, 48); | |
| } | |
| function dataQuality(config, model, baseInfo) { | |
| const parts = []; | |
| parts.push(config ? "config" : "no-config"); | |
| parts.push( | |
| model.safetensors?.total || baseInfo?.safetensors?.total | |
| ? "params" | |
| : "estimated-params", | |
| ); | |
| if (baseInfo && baseInfo.id !== model.id) parts.push(`base:${baseInfo.id}`); | |
| return parts.join(", "); | |
| } | |
| async function buildModel(model) { | |
| const fullModel = (await modelInfo(model.id)) || model; | |
| model = { | |
| ...fullModel, | |
| downloads: model.downloads ?? fullModel.downloads, | |
| pipeline_tag: model.pipeline_tag ?? fullModel.pipeline_tag, | |
| tags: fullModel.tags || model.tags, | |
| cardData: fullModel.cardData || model.cardData, | |
| }; | |
| const baseIds = baseModelIds(model); | |
| const baseInfo = baseIds.length ? await modelInfo(baseIds[0]) : null; | |
| const ownConfig = await rawConfig(model.id); | |
| const baseConfig = | |
| !ownConfig && baseInfo ? await rawConfig(baseInfo.id) : null; | |
| const apiConfig = | |
| model.config && Object.keys(model.config).length ? model.config : null; | |
| const rawConfigData = | |
| ownConfig || baseConfig || apiConfig || baseInfo?.config || {}; | |
| const config = effectiveConfig(rawConfigData); | |
| const configSource = ownConfig | |
| ? model.id | |
| : baseConfig | |
| ? baseInfo.id | |
| : apiConfig | |
| ? model.id | |
| : baseInfo?.id; | |
| const kind = architectureKind(config); | |
| const totalParams = paramCountB(model, baseInfo, model.id); | |
| const activeFromName = | |
| parseActiveParamB(model.id) ?? parseActiveParamB(baseInfo?.id ?? ""); | |
| const recordedExperts = intFrom( | |
| config?.num_local_experts, | |
| config?.num_experts, | |
| config?.n_routed_experts, | |
| ); | |
| const recordedExpertsPerToken = intFrom( | |
| config?.num_experts_per_tok, | |
| config?.num_experts_per_token, | |
| config?.moe_top_k, | |
| config?.num_selected_experts, | |
| ); | |
| const fallbackExperts = | |
| kind === "moe" && activeFromName && totalParams > activeFromName | |
| ? Math.max(1, Math.round(totalParams / activeFromName)) | |
| : 1; | |
| const experts = kind === "moe" ? (recordedExperts ?? fallbackExperts) : null; | |
| const expertsPerToken = | |
| kind === "moe" ? (recordedExpertsPerToken ?? 1) : null; | |
| const activeParams = | |
| kind === "moe" | |
| ? round( | |
| activeFromName ?? | |
| (experts && expertsPerToken | |
| ? Math.max(totalParams * (expertsPerToken / experts), 0.1) | |
| : totalParams), | |
| 4, | |
| ) | |
| : totalParams; | |
| const layers = intFrom( | |
| config?.num_hidden_layers, | |
| config?.n_layer, | |
| config?.num_layers, | |
| config?.decoder_layers, | |
| ); | |
| const attentionHeads = intFrom( | |
| config?.num_attention_heads, | |
| config?.n_head, | |
| config?.num_heads, | |
| ); | |
| const kvHeads = intFrom( | |
| config?.num_key_value_heads, | |
| config?.n_kv_heads, | |
| config?.multi_query_group_num, | |
| ); | |
| const dim = headDim(config, attentionHeads); | |
| const bytes = bytesPerParam(config, model); | |
| const kvGb = kvGbPer1k(config, kind, layers, kvHeads, dim); | |
| const modelType = config?.model_type || model.config?.model_type || "unknown"; | |
| const archName = asArray(config?.architectures || model.config?.architectures) | |
| .filter(Boolean) | |
| .join(", "); | |
| const quality = dataQuality(configSource ? config : null, model, baseInfo); | |
| const sources = [ | |
| { label: "Hugging Face model card", url: `${hfBase}/${model.id}` }, | |
| ]; | |
| if (configSource) { | |
| sources.push({ | |
| label: configSource === model.id ? "Model config" : "Base model config", | |
| url: `${hfBase}/${configSource}/raw/main/config.json`, | |
| }); | |
| } | |
| if (baseInfo && baseInfo.id !== model.id) { | |
| sources.push({ | |
| label: "Base model card", | |
| url: `${hfBase}/${baseInfo.id}`, | |
| }); | |
| } | |
| return { | |
| id: model.id, | |
| name: `${model.id.split("/").at(-1)} ${precision(config, model)}`, | |
| short_name: shortName(model.id), | |
| license: licenseFrom(model), | |
| hf_pipeline_tag: model.pipeline_tag || "unknown", | |
| hf_downloads: model.downloads ?? 0, | |
| tags: (model.tags || []).filter((tag) => !tag.startsWith("region:")), | |
| architecture: { | |
| type: architectureType(kind), | |
| detail: `${modelType}${archName ? ` (${archName})` : ""}`, | |
| total_params_b: totalParams, | |
| active_params_b: activeParams, | |
| layers, | |
| attention_heads: attentionHeads, | |
| kv_heads: kvHeads, | |
| head_dim: dim, | |
| max_context_tokens: contextTokens(config), | |
| routed_experts: kind === "moe" ? experts : null, | |
| routed_experts_per_token: kind === "moe" ? expertsPerToken : null, | |
| shared_experts_per_token: | |
| kind === "moe" ? (intFrom(config?.n_shared_experts) ?? 0) : null, | |
| }, | |
| adapter: { | |
| kind, | |
| weight_precision: precision(config, model), | |
| weight_bytes_per_param: bytes, | |
| resident_weight_gb: round(totalParams * bytes, 4), | |
| active_weight_gb: round(activeParams * bytes, 4), | |
| kv_alloc_gb_per_1k_tokens: kvGb, | |
| kv_read_gb_per_1k_tokens: kvGb, | |
| ...(kind === "recurrent" | |
| ? { kv_alloc_fixed_gb: 0.02, kv_read_fixed_gb: 0.002 } | |
| : {}), | |
| notes: `Generated from Hugging Face metadata on ${new Date().toISOString().slice(0, 10)}; downloads=${model.downloads ?? 0}; data quality: ${quality}. Bound weights use recorded repo precision when detectable, otherwise metadata-estimated precision.`, | |
| }, | |
| sources, | |
| }; | |
| } | |
| function escapeNonAscii(text) { | |
| return text.replace( | |
| /[^\x00-\x7F]/g, | |
| (char) => `\\u${char.charCodeAt(0).toString(16).padStart(4, "0")}`, | |
| ); | |
| } | |
| async function main() { | |
| const candidates = await downloadedModels(); | |
| console.error( | |
| `found ${candidates.length} leaderboard-compatible models >= ${threshold} downloads`, | |
| ); | |
| const models = []; | |
| for (const [index, model] of candidates.entries()) { | |
| if ( | |
| !/^[A-Za-z0-9][A-Za-z0-9._-]*\/[A-Za-z0-9][A-Za-z0-9._-]*$/.test(model.id) | |
| ) { | |
| console.error(`skipping unsupported repo id: ${model.id}`); | |
| continue; | |
| } | |
| try { | |
| models.push(await buildModel(model)); | |
| } catch (error) { | |
| console.error(`failed ${model.id}: ${error.message}`); | |
| } | |
| if ((index + 1) % 25 === 0) { | |
| console.error(`processed ${index + 1}/${candidates.length}`); | |
| } | |
| } | |
| models.sort( | |
| (a, b) => b.hf_downloads - a.hf_downloads || a.id.localeCompare(b.id), | |
| ); | |
| const data = { | |
| schema_version: "2", | |
| generated_at: new Date().toISOString(), | |
| assumptions, | |
| models, | |
| }; | |
| const body = `window.LOCAL_FRONTIER_MODEL_DATA = ${JSON.stringify(data, null, 2)};\n`; | |
| fs.writeFileSync(outputPath, escapeNonAscii(body)); | |
| console.error(`wrote ${models.length} models to ${outputPath}`); | |
| } | |
| await main(); | |