Spaces:
Running
Running
| import { clsx, type ClassValue } from "clsx" | |
| import { twMerge } from "tailwind-merge" | |
| export function cn(...inputs: ClassValue[]) { | |
| return twMerge(clsx(inputs)) | |
| } | |
| /** | |
| * Convert a backend route id (literal `%2F` slug form, e.g. | |
| * `openai%2Fgpt-4o`, `helm-capabilities%2Fmmlu-pro`) into the | |
| * human-readable path form (`openai/gpt-4o`) used in URLs. | |
| * | |
| * Pages mount via catch-all routes (`[...id]`) so multi-segment paths | |
| * resolve cleanly. The inverse β `params.id[]` β backend id form β is | |
| * `routeIdFromSegments`. | |
| */ | |
| export function routeIdToPath(id: string | null | undefined): string { | |
| if (!id) return "" | |
| return id.replace(/%2F/g, "/") | |
| } | |
| /** | |
| * Reconstruct the backend `%2F`-encoded id from a Next.js catch-all | |
| * params value. Accepts either the raw `string[]` from `useParams()` | |
| * or a pre-joined string for safety. Empty arrays produce "". | |
| */ | |
| export function routeIdFromSegments(value: string | string[] | undefined): string { | |
| if (value == null) return "" | |
| const joined = Array.isArray(value) ? value.join("/") : value | |
| return joined.replace(/\//g, "%2F") | |
| } | |
| /** | |
| * Build the backend `%2F`-encoded route id form from a plain canonical | |
| * model id (`org/name`). Matches the producer's `route_id` / | |
| * `model_route_id` encoding (RFC 3986 percent-encoding of the whole id), | |
| * so the result can be fed straight into `routeIdToPath` for a URL path. | |
| * | |
| * This is the model-resolution-rework replacement for the old | |
| * client-side family-route computation (since removed): the | |
| * group/leaf id is now server-provided (`model_group_id`), and we only | |
| * need to encode it for routing β never re-derive it. | |
| */ | |
| export function routeIdFromModelId(id: string | null | undefined): string { | |
| if (!id) return "" | |
| return encodeURIComponent(id.trim()) | |
| } | |
| /** | |
| * Title-case a benchmark / family / eval label that arrives in slug or | |
| * snake-case form. Example inputs and outputs: | |
| * `gdm_intercode_ctf` β `GDM Intercode CTF` | |
| * `vals ai gpqa` β `Vals AI GPQA` | |
| * `mmlu-pro` β `MMLU-Pro` | |
| * | |
| * Common AI-eval acronyms are upper-cased; everything else is title-cased. | |
| * No-op when the input already looks like prose (any character has its | |
| * canonical case position β i.e. there's at least one upper-case letter | |
| * mid-word) so we don't mangle "MMLU-Pro" or "RewardBench Chat". | |
| */ | |
| const BENCHMARK_NAME_ACRONYMS = new Set([ | |
| "ai", "ml", "llm", "llms", "nlp", "rl", "qa", "vqa", "vlm", "mt", "cv", | |
| "api", "cli", "sql", "io", "ui", "ux", | |
| "gpt", "ctf", "cve", "gdm", "mmlu", "gpqa", "bbh", "hle", "gsm8k", "aime", | |
| "ifeval", "ifbench", "humaneval", "mbpp", "gaia", "scicode", "agentharm", | |
| "csqa", "boolq", "openbookqa", "narrativeqa", "naturalquestions", "imdb", | |
| "piqa", "triviaqa", "truthfulqa", "musr", "math", "mgsm", "mmmu", "medqa", | |
| "legalbench", "bbq", | |
| ]) | |
| export function humanizeBenchmarkName(value: string | null | undefined): string { | |
| if (!value) return "" | |
| let s = value.trim() | |
| try { s = decodeURIComponent(s) } catch {} | |
| // Already prose? Any non-leading uppercase letter implies it's | |
| // already been display-formatted β leave it alone. | |
| if (/[a-z][A-Z]/.test(s)) return s | |
| return s | |
| .split(/[_\-\s]+/) | |
| .filter(Boolean) | |
| .map((word) => { | |
| const lower = word.toLowerCase() | |
| if (BENCHMARK_NAME_ACRONYMS.has(lower)) return word.toUpperCase() | |
| return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase() | |
| }) | |
| .join(" ") | |
| } | |
| /** | |
| * Render an `evaluation_id` for human eyes. | |
| * | |
| * Backend evaluation_ids are RFC 3986 percent-encoded so they're safe | |
| * as URL path segments β `wasp%2Fjudgebench-coding` for the (composite, | |
| * benchmark) pair `wasp/judgebench-coding`. The encoded form is fine for | |
| * routes and React keys, but reads as nonsense in research-view labels. | |
| * | |
| * `humanizeEvaluationId` decodes the slug and falls back to the input | |
| * when decoding fails (malformed sequences, etc.) so it's always safe | |
| * to drop in at a render site. | |
| */ | |
| export function humanizeEvaluationId(value: string | null | undefined): string { | |
| if (!value) return "" | |
| try { | |
| return decodeURIComponent(value) | |
| } catch { | |
| return value | |
| } | |
| } | |
| /** | |
| * Format an evaluation timestamp as `YYYY-MM-DD` for table cells. | |
| * | |
| * Accepts the two shapes the backend emits: | |
| * - ISO date-time strings (`"2026-04-29T00:00:00Z"`) | |
| * - Unix-epoch numerics as strings (`"1777496278.157"`) | |
| * | |
| * Returns `"Unknown"` when the value is missing or unparseable. Lossy | |
| * by design β the `Updated` column shows the date only, not time. | |
| * | |
| * Day-precision timestamps are interpreted in UTC (`toISOString()`) | |
| * rather than the local zone so a `2026-04-29T00:00:00Z` value renders | |
| * as `2026-04-29` everywhere instead of slipping to `2026-04-28` west of | |
| * UTC. | |
| */ | |
| export function formatDateISO(ts: string | null | undefined): string { | |
| if (!ts || !String(ts).trim()) return "Unknown" | |
| const raw = String(ts) | |
| const numeric = Number(raw) | |
| const parsed = | |
| !Number.isNaN(numeric) && !raw.includes("-") | |
| ? new Date(numeric * 1000) | |
| : new Date(raw) | |
| if (Number.isNaN(parsed.getTime())) return "Unknown" | |
| return parsed.toISOString().slice(0, 10) | |
| } | |