export function fmtPct(v: number | null, decimals = 0): string { if (v === null) return '—' return `${(v * 100).toFixed(decimals)}%` } export function fmtNum(v: number | null, decimals = 4): string { if (v === null) return '—' return v.toFixed(decimals) } /* Score scale: green → light green → amber → red */ export function lucColor(v: number | null): string { if (v === null) return 'var(--text-3)' if (v >= 0.9) return '#22C55E' if (v >= 0.75) return '#4ADE80' if (v >= 0.6) return '#F0A030' return '#F4333D' } export function ragColor(v: number | null): string { if (v === null) return 'var(--text-3)' if (v >= 0.65) return '#22C55E' if (v >= 0.5) return '#4ADE80' if (v >= 0.35) return '#F0A030' return '#F4333D' } export function fairnessColor(avg: number | null): string { if (avg === null) return 'var(--text-3)' if (avg <= 0.1) return '#22C55E' if (avg <= 0.2) return '#4ADE80' if (avg <= 0.4) return '#F0A030' return '#F4333D' } export const CREATOR_COLORS: Record = { Anthropic: '#e07040', Google: '#4285f5', OpenAI: '#5ac8a8', xAI: '#000000', Meta: '#1877f2', DeepSeek: '#8b7fff', Moonshot: '#ff9bb0', Alibaba: '#ff8800', zAI: '#3B5CEB', 'MiniMax AI': '#E8441A', } export function creatorColor(creator: string): string { return CREATOR_COLORS[creator] ?? '#6b7280' } export const GUARDRAIL_CREATOR_COLORS: Record = { GovTech: '#CC2984', Microsoft: '#00A4EF', Amazon: '#FF9900', Meta: '#1877f2', OpenAI: '#5ac8a8', } export function guardrailCreatorColor(creator: string): string { return GUARDRAIL_CREATOR_COLORS[creator] ?? CREATOR_COLORS[creator] ?? '#6b7280' } export function inferModelSize(model: string): 'Small' | 'Medium' | 'Large' { const n = model.toLowerCase() // Small: lightweight / edge tier (≤10B open-weight, or Haiku/Nano/Lite families) if (/haiku|nano|\blite\b|\bsmall\b/.test(n)) return 'Small' // Explicit parameter count in the name (e.g. "70B", "235B", "20B") const paramMatch = n.match(/(\d+)b\b/) if (paramMatch) { const params = parseInt(paramMatch[1], 10) if (params <= 10) return 'Small' if (params <= 40) return 'Medium' return 'Large' } // Medium: mid-tier commercial families or mid-size open-weight if (/\bmini\b|sonnet|\bflash\b|\bplus\b|\bchat\b|\bfast\b|gemma/.test(n)) return 'Medium' // Large: flagship / pro / opus, or any unmatched model (defaults to Large) return 'Large' } export function normalizedFairness(avg: number | null, maxAvg: number): number | null { if (avg === null || maxAvg === 0) return null return Math.max(0, 1 - avg / maxAvg) }