SPB Git

spb/llmindex Public

The discriminative, contamination-resistant, fully transparent LLM ranking — updated live.

TypeScript 77.9% TeX 15.2% Python 3.7% SQL 1.4% JavaScript 1.1% Shell 0.5%
5.1 KB · 133 lines typescript
Raw Blame History
1/**2 * llmindex.io — multilingual templates (checkable cross-lingual outputs, 0-999 range)3 * Author:  Simon-Pierre Boucher4 * Contact: contact@spboucher.ai5 * License: Proprietary — © Simon-Pierre Boucher, all rights reserved6 *7 * Hardened: full 0-999 number-word space in French (including the irregular8 * 70-99 zone: soixante-dix, quatre-vingts…) and Spanish (quinientos,9 * setecientos, novecientos…), both directions (number→words and words→number).10 * Hyphen/space orthography variants are normalized at grading time.11 */12import { ANSWER_FORMAT_INSTRUCTIONS } from '../answer';13import type { Rng } from '../rng';14import type { ItemTemplate } from '../types';1516const FR_UNITS = [17  'zéro', 'un', 'deux', 'trois', 'quatre', 'cinq', 'six', 'sept', 'huit', 'neuf', 'dix',18  'onze', 'douze', 'treize', 'quatorze', 'quinze', 'seize', 'dix-sept', 'dix-huit', 'dix-neuf',19];2021function frBelowHundred(n: number): string {22  if (n < 20) return FR_UNITS[n]!;23  const tensNames: Record<number, string> = {24    20: 'vingt', 30: 'trente', 40: 'quarante', 50: 'cinquante', 60: 'soixante',25  };26  if (n < 70) {27    const t = Math.floor(n / 10) * 10;28    const u = n % 10;29    if (u === 0) return tensNames[t]!;30    if (u === 1) return `${tensNames[t]!} et un`;31    return `${tensNames[t]!}-${FR_UNITS[u]!}`;32  }33  if (n < 80) {34    if (n === 71) return 'soixante et onze';35    return `soixante-${FR_UNITS[n - 60]!}`;36  }37  if (n === 80) return 'quatre-vingts';38  if (n < 100) return `quatre-vingt-${FR_UNITS[n - 80]!}`;39  throw new Error('frBelowHundred supports 0-99');40}4142/** French number words, 0-999. */43export function numToFrench(n: number): string {44  if (n < 0 || n > 999) throw new Error('numToFrench supports 0-999');45  if (n < 100) return frBelowHundred(n);46  const hundreds = Math.floor(n / 100);47  const rest = n % 100;48  const hundredPart =49    hundreds === 1 ? 'cent' : rest === 0 ? `${FR_UNITS[hundreds]!} cents` : `${FR_UNITS[hundreds]!} cent`;50  return rest === 0 ? hundredPart : `${hundredPart} ${frBelowHundred(rest)}`;51}5253const ES_UNITS = [54  'cero', 'uno', 'dos', 'tres', 'cuatro', 'cinco', 'seis', 'siete', 'ocho', 'nueve', 'diez',55  'once', 'doce', 'trece', 'catorce', 'quince', 'dieciséis', 'diecisiete', 'dieciocho', 'diecinueve',56  'veinte', 'veintiuno', 'veintidós', 'veintitrés', 'veinticuatro', 'veinticinco', 'veintiséis',57  'veintisiete', 'veintiocho', 'veintinueve',58];5960function esBelowHundred(n: number): string {61  if (n < 30) return ES_UNITS[n]!;62  const tensNames: Record<number, string> = {63    30: 'treinta', 40: 'cuarenta', 50: 'cincuenta', 60: 'sesenta', 70: 'setenta', 80: 'ochenta', 90: 'noventa',64  };65  const t = Math.floor(n / 10) * 10;66  const u = n % 10;67  return u === 0 ? tensNames[t]! : `${tensNames[t]!} y ${ES_UNITS[u]!}`;68}6970const ES_HUNDREDS: Record<number, string> = {71  1: 'ciento', 2: 'doscientos', 3: 'trescientos', 4: 'cuatrocientos', 5: 'quinientos',72  6: 'seiscientos', 7: 'setecientos', 8: 'ochocientos', 9: 'novecientos',73};7475/** Spanish number words, 0-999. */76export function numToSpanish(n: number): string {77  if (n < 0 || n > 999) throw new Error('numToSpanish supports 0-999');78  if (n < 100) return esBelowHundred(n);79  if (n === 100) return 'cien';80  const hundreds = Math.floor(n / 100);81  const rest = n % 100;82  return rest === 0 ? ES_HUNDREDS[hundreds]! : `${ES_HUNDREDS[hundreds]!} ${esBelowHundred(rest)}`;83}8485export const multilingualNumword: ItemTemplate = {86  id: 'multilingual.numword-v2',87  domain: 'multilingual',88  description:89    'Compute a 3-digit arithmetic result and spell it in French or Spanish — full 0-999 space including the irregular French 70-99 zone.',90  paramSpace: 2 * 900 * 900,91  render(rng: Rng, perturbSeed: string) {92    const lang = rng.pick(['French', 'Spanish'] as const);93    const a = rng.int(47, 499);94    const b = rng.int(47, 460);95    const sum = a + b;96    const expected = lang === 'French' ? numToFrench(sum) : numToSpanish(sum);97    return {98      templateId: this.id,99      domain: this.domain,100      prompt:101        `Compute ${a} + ${b}, then write the result out in ${lang} number words (lowercase). ` +102        `Answer with the ${lang} words only.\n\n${ANSWER_FORMAT_INSTRUCTIONS}`,103      answerKey: expected,104      grading: 'exact',105      perturbSeed,106    };107  },108};109110export const multilingualWordToNum: ItemTemplate = {111  id: 'multilingual.wordnum-v1',112  domain: 'multilingual',113  description:114    'Reverse direction: read numbers spelled in French AND Spanish, combine them arithmetically, answer with digits.',115  paramSpace: 900 * 900,116  render(rng: Rng, perturbSeed: string) {117    const a = rng.int(61, 999); // includes the irregular French zone often118    const b = rng.int(31, 999);119    const op = rng.pick(['+', '−'] as const);120    const result = op === '+' ? a + b : a - b;121    return {122      templateId: this.id,123      domain: this.domain,124      prompt:125        `A number is written in French: « ${numToFrench(a)} ». Another is written in Spanish: « ${numToSpanish(b)} ». ` +126        `Compute (French number) ${op} (Spanish number). Answer with digits only.\n\n${ANSWER_FORMAT_INSTRUCTIONS}`,127      answerKey: String(result),128      grading: 'numeric',129      perturbSeed,130    };131  },132};133