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%
13.8 KB · 367 lines typescript
Raw Blame History
1/**2 * llmindex.io — terminal item templates: home-made shell simulation, deterministic ground truth3 * Author:  Simon-Pierre Boucher4 * Contact: contact@spboucher.ai5 * License: Proprietary — © Simon-Pierre Boucher, all rights reserved6 *7 * Original terminal-competence benchmark: no shell ever executes. A closed,8 * unambiguous POSIX subset (byte-order C-locale sort, integer-only awk forms,9 * fixed-string grep, no locale/format-dependent constructs) is simulated in10 * TypeScript; the model predicts exact output / final file tree / execution11 * traces, graded by exact match. Seeded generation of file contents and12 * pipelines makes memorization worthless.13 */14import { BLOCK_ANSWER_FORMAT_INSTRUCTIONS } from '../answer';15import type { Rng } from '../rng';16import type { ItemTemplate } from '../types';1718/* ----------------------------- shared simulator ----------------------------- */1920/** Byte-wise (C-locale) string comparison — ASCII-only data by construction. */21const byteCmp = (a: string, b: string): number => (a < b ? -1 : a > b ? 1 : 0);2223type Lines = string[];2425interface PipeStage {26  text: string;27  fn: (input: Lines) => Lines;28}2930/* ------------------------------------------------------------------- *31 * Shape A: pipeline output prediction over a generated CSV             *32 * ------------------------------------------------------------------- */3334const DEPTS = ['sales', 'eng', 'ops', 'hr', 'legal'] as const;35const FIRST = ['ana', 'bo', 'cy', 'dev', 'eli', 'fay', 'gus', 'hal', 'ivy', 'jon', 'kim', 'lou', 'max', 'ned', 'oli', 'pam'] as const;3637export const terminalPipeline: ItemTemplate = {38  id: 'terminal.pipeline.predict-v1',39  domain: 'terminal',40  description:41    'Predict the exact stdout of a 3-5 stage pipeline (grep/cut/sort/head/tail/awk subset) over a generated CSV; byte-order vs numeric sort traps included.',42  paramSpace: 16 ** 10 * 5 ** 10 * 4 ** 5,43  render(rng: Rng, perturbSeed: string) {44    // Generate CSV rows: name,dept,units,score — names unique, ASCII lowercase.45    const nRows = rng.int(9, 14);46    const names = rng.shuffle(FIRST).slice(0, nRows);47    const rows = names.map((name) => {48      const dept = rng.pick(DEPTS);49      const units = rng.int(3, 120); // 1-3 digits → numeric-vs-byte sort trap50      const score = rng.int(10, 99);51      return `${name},${dept},${units},${score}`;52    });5354    // Build a pipeline of 3-5 stages from a closed, unambiguous set.55    const targetDept = rng.pick([...new Set(rows.map((r) => r.split(',')[1]!))]);56    const stages: PipeStage[] = [];57    stages.push({58      text: `grep -F ',${targetDept},' people.csv`,59      fn: (input) => input.filter((l) => l.includes(`,${targetDept},`)),60    });6162    const variant = rng.int(0, 3);63    if (variant === 0) {64      // numeric sort on units, take top rows65      const n = rng.int(2, 3);66      stages.push({67        text: `sort -t, -k3,3n`,68        fn: (input) =>69          [...input].sort((a, b) => Number(a.split(',')[2]) - Number(b.split(',')[2]) || byteCmp(a, b)),70      });71      stages.push({ text: `tail -n ${n}`, fn: (input) => input.slice(-n) });72    } else if (variant === 1) {73      // byte-order sort on the whole line (the classic 100 < 9 trap), head74      const n = rng.int(2, 3);75      stages.push({76        text: `cut -d, -f1,3`,77        fn: (input) => input.map((l) => l.split(',').filter((_, i) => i === 0 || i === 2).join(',')),78      });79      stages.push({ text: `sort`, fn: (input) => [...input].sort(byteCmp) });80      stages.push({ text: `head -n ${n}`, fn: (input) => input.slice(0, n) });81    } else if (variant === 2) {82      // awk integer aggregation → single number83      stages.push({84        text: `awk -F, '{ s += $3 } END { print s }'`,85        fn: (input) => [String(input.reduce((s, l) => s + Number(l.split(',')[2]), 0))],86      });87    } else {88      // awk filter + count via grep -c style END counter89      const cutoff = rng.int(40, 80);90      stages.push({91        text: `awk -F, '$4 > ${cutoff} { n += 1 } END { print n }'`,92        fn: (input) => [String(input.filter((l) => Number(l.split(',')[3]) > cutoff).length)],93      });94    }9596    let out: Lines = rows;97    for (const s of stages) out = s.fn(out);98    const pipeline = stages.map((s) => s.text).join(' | ');99100    const prompt = [101      'A POSIX shell session (LC_ALL=C). The file `people.csv` contains exactly these lines (columns: name,dept,units,score):',102      '',103      '```',104      rows.join('\n'),105      '```',106      '',107      'What is the EXACT stdout of this command?',108      '',109      '```sh',110      pipeline,111      '```',112      '',113      'Notes: plain `sort` compares bytes (so "100" sorts before "9"); `sort -k3,3n` compares field 3 numerically.',114      '',115      BLOCK_ANSWER_FORMAT_INSTRUCTIONS,116    ].join('\n');117118    return {119      templateId: this.id,120      domain: this.domain,121      prompt,122      answerKey: out.join('\n'),123      grading: 'lines' as const,124      perturbSeed,125    };126  },127};128129/* ------------------------------------------------------------------- *130 * Shape B: file-tree prediction after mkdir/mv/cp/rm/touch + cd chain  *131 * ------------------------------------------------------------------- */132133const DIR_NAMES = ['src', 'docs', 'build', 'assets', 'logs', 'conf'] as const;134const FILE_STEMS = ['main', 'util', 'index', 'setup', 'notes', 'report', 'draft', 'todo'] as const;135const EXTS = ['txt', 'md', 'log', 'cfg'] as const;136137interface VirtualFs {138  files: Set<string>; // absolute paths like /proj/src/main.txt139  dirs: Set<string>; // absolute dir paths140  cwd: string;141}142143export const terminalTree: ItemTemplate = {144  id: 'terminal.fs.tree-v1',145  domain: 'terminal',146  description:147    'Track a virtual file tree through a sequence of mkdir/touch/mv/cp/rm with relative paths and cd; output the sorted final file list.',148  paramSpace: 6 ** 3 * 8 ** 5 * 4 ** 5 * 6 ** 6,149  render(rng: Rng, perturbSeed: string) {150    const fs: VirtualFs = { files: new Set(), dirs: new Set(['/proj']), cwd: '/proj' };151    const dirs = rng.shuffle(DIR_NAMES).slice(0, 3);152    for (const d of dirs) fs.dirs.add(`/proj/${d}`);153    const stems = rng.shuffle(FILE_STEMS).slice(0, 5);154    for (const [i, stem] of stems.entries()) {155      const dir = i < 2 ? '/proj' : `/proj/${rng.pick(dirs)}`;156      fs.files.add(`${dir}/${stem}.${rng.pick(EXTS)}`);157    }158159    const initialListing = [...fs.files].sort(byteCmp);160    const script: string[] = [];161    const nOps = rng.int(6, 8);162    for (let i = 0; i < nOps; i++) {163      const op = rng.int(0, 4);164      const fileArr = [...fs.files];165      if (op === 0 && fileArr.length > 1) {166        // mv file → other dir (move) or rename in place167        const f = rng.pick(fileArr);168        if (rng.next() < 0.5) {169          const destDir = rng.pick([...fs.dirs]);170          const dest = `${destDir}/${f.split('/').pop()}`;171          script.push(`mv ${rel(fs.cwd, f)} ${rel(fs.cwd, destDir)}/`);172          fs.files.delete(f);173          fs.files.add(dest);174        } else {175          const newName = `${rng.pick(FILE_STEMS)}-${rng.int(1, 9)}.${rng.pick(EXTS)}`;176          const dest = `${f.split('/').slice(0, -1).join('/')}/${newName}`;177          script.push(`mv ${rel(fs.cwd, f)} ${rel(fs.cwd, dest)}`);178          fs.files.delete(f);179          fs.files.add(dest);180        }181      } else if (op === 1) {182        const destDir = rng.pick([...fs.dirs]);183        const f = rng.pick(fileArr);184        const dest = `${destDir}/${f.split('/').pop()}`;185        if (dest !== f) {186          script.push(`cp ${rel(fs.cwd, f)} ${rel(fs.cwd, destDir)}/`);187          fs.files.add(dest);188        }189      } else if (op === 2 && fileArr.length > 3) {190        const f = rng.pick(fileArr);191        script.push(`rm ${rel(fs.cwd, f)}`);192        fs.files.delete(f);193      } else if (op === 3) {194        const d = `${rng.pick([...fs.dirs])}/${rng.pick(DIR_NAMES)}-${rng.int(1, 9)}`;195        if (!fs.dirs.has(d)) {196          script.push(`mkdir -p ${rel(fs.cwd, d)}`);197          fs.dirs.add(d);198        }199      } else {200        const dir = rng.pick([...fs.dirs]);201        const f = `${dir}/${rng.pick(FILE_STEMS)}-${rng.int(1, 9)}.${rng.pick(EXTS)}`;202        if (!fs.files.has(f)) {203          script.push(`touch ${rel(fs.cwd, f)}`);204          fs.files.add(f);205        }206      }207      // occasionally change directory (forces relative-path tracking)208      if (rng.next() < 0.3) {209        const d = rng.pick([...fs.dirs]);210        script.push(`cd ${rel(fs.cwd, d) || '.'}`);211        fs.cwd = d;212      }213    }214215    function rel(cwd: string, abs: string): string {216      if (abs === cwd) return '.';217      if (abs.startsWith(cwd + '/')) return abs.slice(cwd.length + 1);218      // walk up from cwd to root then down — keep it simple and unambiguous219      const up = cwd.split('/').filter(Boolean).length;220      return '../'.repeat(up) + abs.replace(/^\//, '');221    }222223    const finalListing = [...fs.files].sort(byteCmp);224225    const prompt = [226      'A POSIX shell session starts in `/proj`. The tree initially contains these FILES (directories exist as implied, plus empty dirs ' +227        dirs.map((d) => `\`/proj/${d}\``).join(', ') +228        '):',229      '',230      '```',231      initialListing.join('\n'),232      '```',233      '',234      'These commands run in order (all succeed; `mv x dir/` moves into the directory; paths are relative to the CURRENT working directory, which `cd` changes):',235      '',236      '```sh',237      script.join('\n'),238      '```',239      '',240      'List every file (absolute paths) that exists afterwards, one per line, sorted in byte order (C locale). Do not list directories.',241      '',242      BLOCK_ANSWER_FORMAT_INSTRUCTIONS,243    ].join('\n');244245    return {246      templateId: this.id,247      domain: this.domain,248      prompt,249      answerKey: finalListing.join('\n'),250      grading: 'lines' as const,251      perturbSeed,252    };253  },254};255256/* ------------------------------------------------------------------- *257 * Shape C: && / || short-circuit execution-trace prediction            *258 * ------------------------------------------------------------------- */259260export const terminalExitChain: ItemTemplate = {261  id: 'terminal.exit.chain-v1',262  domain: 'terminal',263  description:264    'Predict which echo statements run and the final exit status of a && / || chain over test -f / grep -q primitives with known outcomes.',265  paramSpace: 2 ** 8 * 8 ** 4 * 26 ** 2,266  render(rng: Rng, perturbSeed: string) {267    // Known world: files that exist + a haystack file with known content.268    const present = ['app.txt', 'data.txt'].filter(() => rng.next() < 0.8);269    const absent = ['ghost.txt', 'tmp.txt'];270    const words = ['amber', 'basil', 'coral', 'dune'];271    const inHaystack = rng.shuffle(words).slice(0, 2);272    const notInHaystack = words.filter((w) => !inHaystack.includes(w));273274    interface Prim {275      text: string;276      ok: boolean;277      echo?: string;278    }279    const prims: Prim[] = [];280    const nSegments = rng.int(3, 4);281    let letter = 65; // A, B, C…282    for (let i = 0; i < nSegments; i++) {283      const kind = rng.int(0, 2);284      let cond: { text: string; ok: boolean };285      if (kind === 0) {286        const usePresent = rng.next() < 0.5;287        const f = usePresent && present.length ? rng.pick(present) : rng.pick(absent);288        cond = { text: `test -f ${f}`, ok: present.includes(f) };289      } else if (kind === 1) {290        const useIn = rng.next() < 0.5;291        const w = useIn ? rng.pick(inHaystack) : rng.pick(notInHaystack);292        cond = { text: `grep -q ${w} notes.txt`, ok: inHaystack.includes(w) };293      } else {294        const ok = rng.next() < 0.5;295        cond = { text: ok ? 'true' : 'false', ok };296      }297      const thenEcho = String.fromCharCode(letter++);298      const elseEcho = String.fromCharCode(letter++);299      prims.push({ ...cond }, { text: `echo ${thenEcho}`, ok: true, echo: thenEcho }, { text: `echo ${elseEcho}`, ok: true, echo: elseEcho });300    }301302    // Build chain: cond && echo X || echo Y ; repeated (separate statements303    // joined with ';' so each triple is independent — unambiguous semantics).304    const statements: string[] = [];305    const printed: string[] = [];306    let lastExit = 0;307    for (let i = 0; i < prims.length; i += 3) {308      const cond = prims[i]!;309      const thenE = prims[i + 1]!;310      const elseE = prims[i + 2]!;311      statements.push(`${cond.text} && ${thenE.text} || ${elseE.text}`);312      // semantics: A && B || C — C runs if A fails OR B fails; echo never fails.313      if (cond.ok) {314        printed.push(thenE.echo!);315        lastExit = 0;316      } else {317        printed.push(elseE.echo!);318        lastExit = 0; // echo succeeds319      }320    }321    // Final statement without fallback → determines a non-trivial exit code.322    const finalOk = rng.next() < 0.5;323    const f = finalOk && present.length ? present[0]! : absent[0]!;324    const finalIsOk = present.includes(f);325    statements.push(`test -f ${f} && echo Z`);326    if (finalIsOk) {327      printed.push('Z');328      lastExit = 0;329    } else {330      lastExit = 1;331    }332333    const script = statements.join('\n');334    const answer = [...printed, `exit:${lastExit}`].join('\n');335336    const prompt = [337      'A POSIX shell session in a directory containing ONLY these files:',338      '',339      '```',340      [...present, 'notes.txt'].sort(byteCmp).join('\n'),341      '```',342      '',343      `\`notes.txt\` contains exactly the words: ${inHaystack.join(', ')} (one per line). No other files exist.`,344      '',345      'These statements run in order:',346      '',347      '```sh',348      script,349      '```',350      '',351      'Predict the terminal output: every line printed, in order, then a final line `exit:<N>` where N is the exit status of the LAST statement. ' +352        'Remember: `A && B || C` runs C whenever A fails (it is not a strict if/else); `test -f` succeeds only if the file exists; `grep -q` succeeds only if the word is present.',353      '',354      BLOCK_ANSWER_FORMAT_INSTRUCTIONS,355    ].join('\n');356357    return {358      templateId: this.id,359      domain: this.domain,360      prompt,361      answerKey: answer,362      grading: 'lines' as const,363      perturbSeed,364    };365  },366};367