import fs from 'fs' import path from 'path' import type { ModelData, BenchmarkStats, MetricThresholds } from './types' function pct(val: string): number | null { const s = val?.trim() if (!s) return null const n = parseFloat(s.replace('%', '')) return isNaN(n) ? null : n / 100 } function num(val: string): number | null { const s = val?.trim() if (!s) return null const n = parseFloat(s) return isNaN(n) ? null : n } export function loadData(): ModelData[] { const filePath = path.join(process.cwd(), 'public', 'data.csv') const text = fs.readFileSync(filePath, 'utf-8') const lines = text.split('\n').filter(l => l.trim()).slice(2) return lines .map(line => { const cols = line.split(',') return { rank: parseInt(cols[0]) || 0, creator: cols[1]?.trim() || '', model: cols[2]?.trim() || '', luc: { general: pct(cols[3]), physics: pct(cols[4]), career: pct(cols[5]), jd: pct(cols[6]), avg: pct(cols[7]), }, rag: { lcAbs: pct(cols[8]), lcFact: pct(cols[9]), hyAbs: pct(cols[10]), hyFact: pct(cols[11]), avg: pct(cols[12]), }, fairness: { style: num(cols[13]), con: num(cols[14]), avg: num(cols[15]), }, archived: cols[16]?.trim() === 'true', } }) .filter(m => m.creator && m.model) } export function computeStats(models: ModelData[]): BenchmarkStats { const active = models.filter(m => !m.archived) const creators = new Set(models.map(m => m.creator)) const lucScores = active.map(m => m.luc.avg).filter((v): v is number => v !== null) const ragScores = active.map(m => m.rag.avg).filter((v): v is number => v !== null) const avgLUC = lucScores.reduce((a, b) => a + b, 0) / lucScores.length const avgRAG = ragScores.reduce((a, b) => a + b, 0) / ragScores.length const topModel = active .filter(m => m.luc.avg !== null) .sort((a, b) => (b.luc.avg ?? 0) - (a.luc.avg ?? 0))[0]?.model ?? '' return { totalModels: models.length, totalCreators: creators.size, avgLUC, avgRAG, topModel, lastUpdated: new Date().toLocaleDateString('en-SG', { month: 'short', year: 'numeric' }), } } export function getMaxFairnessAvg(models: ModelData[]): number { const vals = models.map(m => m.fairness.avg).filter((v): v is number => v !== null) return Math.max(...vals) } function empiricalPercentile(vals: number[], p: number): number { const sorted = [...vals].sort((a, b) => a - b) const idx = (p / 100) * (sorted.length - 1) const lo = Math.floor(idx) const hi = Math.ceil(idx) return sorted[lo] + (idx - lo) * (sorted[hi] - sorted[lo]) } export function computeThresholds(models: ModelData[]): MetricThresholds { const active = models.filter(m => !m.archived) const luc = active.map(m => m.luc.avg).filter((v): v is number => v !== null) const rag = active.map(m => m.rag.avg).filter((v): v is number => v !== null) const fair = active.map(m => m.fairness.avg).filter((v): v is number => v !== null) return { luc: { p33: empiricalPercentile(luc, 33), p67: empiricalPercentile(luc, 67) }, rag: { p33: empiricalPercentile(rag, 33), p67: empiricalPercentile(rag, 67) }, fairness: { p33: empiricalPercentile(fair, 33), p67: empiricalPercentile(fair, 67) }, } }