Spaces:
Running
Restore DuckDB-aware build cache logic
Browse filesThe rebase took origin/main wholesale for cache-hf-data.mjs to preserve
the new corpus-aggregates.json fetch. That dropped four pieces of the
DuckDB build path that the production HF Space deploy needs:
- isDuckDBLean mode + ESSENTIAL/JSON_FALLBACK split (skips ~2.7 GB of
JSON snapshots that OOM the Space)
- HF_DATASET_REPO env var override (so the Dockerfile ARG reaches the
cache step instead of being silently ignored)
- duckdb/ in CACHE_DIRECTORIES (else the parquet that lib/duckdb-data.ts
reads is never copied β static prerender fails)
- Binary-safe download via Buffer.from(arrayBuffer()) instead of
response.text() (text-mode would corrupt parquet)
- HF_TOKEN auth header for private/gated dataset access
Re-applies 14c3a2d's intent and adds corpus-aggregates.json to the
essential set (essential because the corpus dashboard depends on it
in both modes; optional because the prod dataset may not have it
populated yet).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- scripts/cache-hf-data.mjs +56 -14
|
@@ -18,30 +18,60 @@ import { promisify } from "util"
|
|
| 18 |
const root = path.resolve(new URL(import.meta.url).pathname, "..", "..")
|
| 19 |
const cacheDir = path.join(root, ".cache", "hf-data")
|
| 20 |
const publicDir = path.join(root, "public")
|
| 21 |
-
const HF_DATASET_REPO =
|
|
|
|
| 22 |
const HF_RESOLVE_BASE = `${HF_DATASET_REPO}/resolve/main`
|
| 23 |
const execFileAsync = promisify(execFile)
|
| 24 |
|
| 25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
"manifest.json",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
"model-cards.json",
|
| 28 |
"model-cards-lite.json",
|
| 29 |
"eval-list.json",
|
| 30 |
"eval-list-lite.json",
|
| 31 |
"developers.json",
|
| 32 |
-
"benchmark-metadata.json",
|
| 33 |
-
"eval-hierarchy.json",
|
| 34 |
-
"comparison-index.json",
|
| 35 |
-
"corpus-aggregates.json",
|
| 36 |
]
|
| 37 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
const OPTIONAL_CACHE_ROOT_FILES = new Set([
|
| 39 |
"model-cards-lite.json",
|
| 40 |
"eval-list-lite.json",
|
| 41 |
"corpus-aggregates.json",
|
| 42 |
])
|
| 43 |
|
| 44 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
|
| 46 |
const TOKEN_CASE_MAP = {
|
| 47 |
ai: "AI",
|
|
@@ -89,15 +119,20 @@ function isGitLfsPointer(contents) {
|
|
| 89 |
}
|
| 90 |
|
| 91 |
async function writeRemoteFile(relativePath, destinationPath) {
|
| 92 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
if (!response.ok) {
|
| 94 |
throw new Error(`Failed to download ${relativePath}: ${response.status} ${response.statusText}`)
|
| 95 |
}
|
| 96 |
|
| 97 |
-
const
|
| 98 |
await fs.mkdir(path.dirname(destinationPath), { recursive: true })
|
| 99 |
-
await fs.writeFile(destinationPath,
|
| 100 |
-
return
|
| 101 |
}
|
| 102 |
|
| 103 |
async function copySnapshotFile(snapshotRoot, relativePath, destinationPath) {
|
|
@@ -339,6 +374,11 @@ async function countFiles(dirPath) {
|
|
| 339 |
async function main() {
|
| 340 |
console.log("Caching HF dataset snapshot for build...\n")
|
| 341 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 342 |
await fs.mkdir(cacheDir, { recursive: true })
|
| 343 |
await fs.mkdir(publicDir, { recursive: true })
|
| 344 |
|
|
@@ -382,9 +422,11 @@ async function main() {
|
|
| 382 |
const peerRanksSuffix = peerRanksResult.source === "remote" ? ", resolved from LFS" : ""
|
| 383 |
console.log(` β peer-ranks.json (${(peerRanksResult.size / 1024).toFixed(0)} KB${peerRanksSuffix})`)
|
| 384 |
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
|
|
|
|
|
|
| 388 |
|
| 389 |
// ββ Phase 3: Detail directories βββββββββββββββββββββββββββββββββββββ
|
| 390 |
console.log("\nPhase 3: Copy detail directories")
|
|
|
|
| 18 |
const root = path.resolve(new URL(import.meta.url).pathname, "..", "..")
|
| 19 |
const cacheDir = path.join(root, ".cache", "hf-data")
|
| 20 |
const publicDir = path.join(root, "public")
|
| 21 |
+
const HF_DATASET_REPO = process.env.HF_DATASET_REPO?.trim()
|
| 22 |
+
|| "https://huggingface.co/datasets/evaleval/card_backend"
|
| 23 |
const HF_RESOLVE_BASE = `${HF_DATASET_REPO}/resolve/main`
|
| 24 |
const execFileAsync = promisify(execFile)
|
| 25 |
|
| 26 |
+
// Lean DuckDB mode: when DATA_BACKEND=duckdb, the runtime reads model/eval/
|
| 27 |
+
// developer/summary data exclusively from `duckdb/v1/*.parquet` (see
|
| 28 |
+
// lib/duckdb-data.ts:48,82). The legacy JSON-fallback artifacts
|
| 29 |
+
// (model-cards*, eval-list*, developers*, plus the per-slug detail
|
| 30 |
+
// directories developers/, evals/, models/) become dead weight β skipping
|
| 31 |
+
// them avoids OOMing the HF Space on a 2.8 GB JSON snapshot.
|
| 32 |
+
//
|
| 33 |
+
// Always keep:
|
| 34 |
+
// - manifest.json (lib/data-backend.ts:96 β /api/backend-manifest)
|
| 35 |
+
// - eval-hierarchy.json (lib/data-backend.ts:98 β /api/eval-hierarchy)
|
| 36 |
+
// - benchmark-metadata.json (lib/benchmark-metadata.ts:5 β /api/benchmark-metadata)
|
| 37 |
+
// - comparison-index.json (app/api/comparison-index/route.ts:7 β app/models/[id]/page.tsx:157)
|
| 38 |
+
// - corpus-aggregates.json (app/corpus/page.tsx via lib/hf-data:fetchCorpusAggregates)
|
| 39 |
+
// - duckdb/v1/*.parquet (lib/duckdb-data.ts read path)
|
| 40 |
+
// - public/peer-ranks.json (always written outside cacheDir; component fetches HF directly)
|
| 41 |
+
const isDuckDBLean = process.env.DATA_BACKEND?.trim().toLowerCase() === "duckdb"
|
| 42 |
+
|
| 43 |
+
const ESSENTIAL_CACHE_ROOT_FILES = [
|
| 44 |
"manifest.json",
|
| 45 |
+
"benchmark-metadata.json",
|
| 46 |
+
"eval-hierarchy.json",
|
| 47 |
+
"comparison-index.json",
|
| 48 |
+
"corpus-aggregates.json",
|
| 49 |
+
]
|
| 50 |
+
|
| 51 |
+
const JSON_FALLBACK_CACHE_ROOT_FILES = [
|
| 52 |
"model-cards.json",
|
| 53 |
"model-cards-lite.json",
|
| 54 |
"eval-list.json",
|
| 55 |
"eval-list-lite.json",
|
| 56 |
"developers.json",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
]
|
| 58 |
|
| 59 |
+
const CACHE_ROOT_FILES = isDuckDBLean
|
| 60 |
+
? ESSENTIAL_CACHE_ROOT_FILES
|
| 61 |
+
: [...ESSENTIAL_CACHE_ROOT_FILES, ...JSON_FALLBACK_CACHE_ROOT_FILES]
|
| 62 |
+
|
| 63 |
const OPTIONAL_CACHE_ROOT_FILES = new Set([
|
| 64 |
"model-cards-lite.json",
|
| 65 |
"eval-list-lite.json",
|
| 66 |
"corpus-aggregates.json",
|
| 67 |
])
|
| 68 |
|
| 69 |
+
const ESSENTIAL_CACHE_DIRECTORIES = ["duckdb"]
|
| 70 |
+
const JSON_FALLBACK_CACHE_DIRECTORIES = ["developers", "evals", "models"]
|
| 71 |
+
|
| 72 |
+
const CACHE_DIRECTORIES = isDuckDBLean
|
| 73 |
+
? ESSENTIAL_CACHE_DIRECTORIES
|
| 74 |
+
: [...JSON_FALLBACK_CACHE_DIRECTORIES, ...ESSENTIAL_CACHE_DIRECTORIES]
|
| 75 |
|
| 76 |
const TOKEN_CASE_MAP = {
|
| 77 |
ai: "AI",
|
|
|
|
| 119 |
}
|
| 120 |
|
| 121 |
async function writeRemoteFile(relativePath, destinationPath) {
|
| 122 |
+
const headers = {}
|
| 123 |
+
const hfToken = process.env.HF_TOKEN?.trim()
|
| 124 |
+
if (hfToken) {
|
| 125 |
+
headers.Authorization = `Bearer ${hfToken}`
|
| 126 |
+
}
|
| 127 |
+
const response = await fetch(`${HF_RESOLVE_BASE}/${relativePath}`, { headers })
|
| 128 |
if (!response.ok) {
|
| 129 |
throw new Error(`Failed to download ${relativePath}: ${response.status} ${response.statusText}`)
|
| 130 |
}
|
| 131 |
|
| 132 |
+
const buffer = Buffer.from(await response.arrayBuffer())
|
| 133 |
await fs.mkdir(path.dirname(destinationPath), { recursive: true })
|
| 134 |
+
await fs.writeFile(destinationPath, buffer)
|
| 135 |
+
return buffer.length
|
| 136 |
}
|
| 137 |
|
| 138 |
async function copySnapshotFile(snapshotRoot, relativePath, destinationPath) {
|
|
|
|
| 374 |
async function main() {
|
| 375 |
console.log("Caching HF dataset snapshot for build...\n")
|
| 376 |
|
| 377 |
+
if (isDuckDBLean) {
|
| 378 |
+
console.log("Lean DuckDB cache mode: skipping JSON-fallback artifacts (model-cards*, eval-list*, developers*, developers/, evals/, models/)")
|
| 379 |
+
console.log("")
|
| 380 |
+
}
|
| 381 |
+
|
| 382 |
await fs.mkdir(cacheDir, { recursive: true })
|
| 383 |
await fs.mkdir(publicDir, { recursive: true })
|
| 384 |
|
|
|
|
| 422 |
const peerRanksSuffix = peerRanksResult.source === "remote" ? ", resolved from LFS" : ""
|
| 423 |
console.log(` β peer-ranks.json (${(peerRanksResult.size / 1024).toFixed(0)} KB${peerRanksSuffix})`)
|
| 424 |
|
| 425 |
+
if (!isDuckDBLean) {
|
| 426 |
+
await normalizeCachedModelCardFile(path.join(cacheDir, "model-cards.json"))
|
| 427 |
+
await normalizeCachedModelCardFile(path.join(cacheDir, "model-cards-lite.json"))
|
| 428 |
+
console.log(" β normalized model card artifacts")
|
| 429 |
+
}
|
| 430 |
|
| 431 |
// ββ Phase 3: Detail directories βββββββββββββββββββββββββββββββββββββ
|
| 432 |
console.log("\nPhase 3: Copy detail directories")
|