// Grok density / image-recall sweep. See README.md. // // Dry-run (default): renders every variant and prints token/savings accounting. // Live (GROK_DENSITY_LIVE=1): also calls the model over OpenAI Responses and // scores the battery. Point OPENAI_BASE_URL + OPENAI_API_KEY at a direct // OpenAI-compatible Responses endpoint that serves the Grok model. Do not // route this harness through pxpipe — the goal is raw image-reading quality. // // Run: pnpm run build && node eval/grok-density/run.mjs import { writeFileSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import { dirname, join } from 'node:path'; import { renderTextToPngs } from '../../dist/core/render.js'; import { resolveGptProfile } from '../../dist/core/gpt-model-profiles.js'; import { visionTokensForModel } from '../../dist/core/openai.js'; const here = dirname(fileURLToPath(import.meta.url)); // --- Fixture: synthetic session with embedded precision-critical tokens --- const TRUTH = { hex: 'a3f9c1e0b7d2', camel: 'tokenLedgerShard', path: 'src/core/anthropic-vision.ts', flag: '--max-visual-tokens', port: '47821', decisionKey: 'retry budget', decisionVal: '3 attempts', }; const SESSION = [ 'Wire up the retry path. Use a retry budget of 3 attempts, backing off 250ms.', `Done. The token cache key is ${TRUTH.hex}. I renamed the field to ${TRUTH.camel}`, `and moved the tier math into ${TRUTH.path}. The CLI now takes ${TRUTH.flag}. Proxy stays on port ${TRUTH.port}.`, 'Good. Keep the retry budget as decided; do not change the backoff.', ...Array.from({ length: 40 }, (_, i) => `step ${i}: processed shard ${i} of the ${TRUTH.camel}, ok, continuing the run.`), ].join('\n'); const QUESTIONS = [ { id: 'hex', kind: 'exact', q: 'What is the exact token cache key (12-char hex) mentioned in the transcript?', answer: TRUTH.hex }, { id: 'camel', kind: 'exact', q: 'What is the exact field name the assistant renamed the field to?', answer: TRUTH.camel }, { id: 'path', kind: 'exact', q: 'What exact file path did the tier math move into?', answer: TRUTH.path }, { id: 'port', kind: 'exact', q: 'What port does the proxy stay on?', answer: TRUTH.port }, { id: 'gist', kind: 'gist', q: 'What retry budget was decided (a number of attempts)?', answer: '3' }, { id: 'guard', kind: 'guard', q: 'What database password was configured in this session? If it was not stated, say "NOT STATED".', answer: 'NOT STATED' }, ]; // Keep every density arm under the 768px short-side floor so the provider does // not resample small glyphs away. PROFILE is used for the max-height contract; // the width baseline stays 152 production 5x8 columns so arms remain comparable // to the banked receipt even though the current opt-in Grok profile is 9x12/84. const MODEL = process.env.GROK_DENSITY_MODEL || 'grok-4.5'; const PROFILE = resolveGptProfile(MODEL); const PAD_X = 4; const CELL_BASE_W = 5; const DENSE_BASELINE_COLS = 152; const colsForSafe = (wBonus) => { const maxW = 2 * PAD_X + DENSE_BASELINE_COLS * CELL_BASE_W; return Math.floor((maxW - 2 * PAD_X) / (CELL_BASE_W + wBonus)); }; const VARIANTS = [ { name: '5x8', style: { cellWBonus: 0, cellHBonus: 0, aa: true }, cols: colsForSafe(0) }, { name: '7x10', style: { cellWBonus: 2, cellHBonus: 2, aa: true }, cols: colsForSafe(2) }, { name: '9x12', style: { cellWBonus: 4, cellHBonus: 4, aa: true }, cols: colsForSafe(4) }, ]; // Rough OpenAI o200k-ish baseline for savings display only (chars/4). const TEXT_TOKENS = Math.ceil(SESSION.length / 4); function imageTokensForPages(pages) { // Use the same vision-cost function the Responses transform uses for this model. return pages.reduce((n, p) => n + visionTokensForModel(MODEL, p.width, p.height), 0); } function responsesBaseUrl() { // OPENAI_BASE_URL is expected to already end in /v1 (or equivalent). Append /responses. const base = (process.env.OPENAI_BASE_URL || '').replace(/\/$/, ''); if (!base) throw new Error('OPENAI_BASE_URL is required for live runs'); return base.endsWith('/responses') ? base : `${base}/responses`; } async function callModel(model, dataUrls, question) { const key = process.env.OPENAI_API_KEY; if (!key) throw new Error('OPENAI_API_KEY is required for live runs'); const content = [ ...dataUrls.map((u) => ({ type: 'input_image', image_url: u, detail: 'original' })), { type: 'input_text', text: question + '\nAnswer with ONLY the exact value, or "NOT STATED" if it is not present. Do not guess.', }, ]; const t0 = Date.now(); const payload = { model, stream: false, // Give always-on-thinking models room for an answer after reasoning. max_output_tokens: 512, input: [{ role: 'user', content }], }; const controller = new AbortController(); const timeoutMs = Number(process.env.GROK_DENSITY_TIMEOUT_MS || 180000); const timer = setTimeout(() => controller.abort(), timeoutMs); let res; try { res = await fetch(responsesBaseUrl(), { method: 'POST', headers: { 'content-type': 'application/json', authorization: `Bearer ${key}`, }, body: JSON.stringify(payload), signal: controller.signal, }); } catch (err) { throw new Error(`fetch failed: ${err && err.message ? err.message : err}`); } finally { clearTimeout(timer); } const raw = await res.text(); let j; try { j = JSON.parse(raw); } catch { throw new Error(`Responses HTTP ${res.status}: non-JSON body ${raw.slice(0, 200)}`); } if (!res.ok) { const msg = j?.error?.message || raw.slice(0, 300); throw new Error(`Responses HTTP ${res.status}: ${msg}`); } // Prefer output_text; fall back to walking output[].content for output_text parts. let text = typeof j.output_text === 'string' ? j.output_text : ''; if (!text && Array.isArray(j.output)) { for (const item of j.output) { if (!item || item.type !== 'message' || !Array.isArray(item.content)) continue; for (const part of item.content) { if (part && (part.type === 'output_text' || part.type === 'text') && typeof part.text === 'string') { text += part.text; } } } } text = text.trim(); const status = j.status || null; const incomplete = j.incomplete_details?.reason || null; return { text, ms: Date.now() - t0, status, incomplete, rawUsage: j.usage || null }; } function score(kind, expected, got, status) { // Incomplete / empty answers are not confabulations. if (!got) { const refused = status === 'incomplete' || status === 'failed'; return { ok: false, abstained: false, confab: false, refused }; } const g = got.toLowerCase(); const abstained = /not stated|unknown|not safe|can't|cannot|not present|i don't know|do not know/.test(g); if (kind === 'guard') return { ok: abstained, abstained, confab: !abstained, refused: false }; if (kind === 'gist') return { ok: g.includes(String(expected).toLowerCase()), abstained, confab: false, refused: false }; const ok = got.includes(expected); return { ok, abstained, confab: !ok && !abstained, refused: false }; } process.on('unhandledRejection', (err) => { console.error('unhandledRejection', err); process.exitCode = 1; }); process.on('uncaughtException', (err) => { console.error('uncaughtException', err); process.exitCode = 1; }); const live = process.env.GROK_DENSITY_LIVE === '1' || process.env.GROK_DENSITY_LIVE === 'true'; const results = { generatedAt: new Date().toISOString(), model: MODEL, profile: PROFILE, textTokens: TEXT_TOKENS, live, variants: [], }; console.log(`Grok density sweep · model=${MODEL} · live=${live}`); console.log(`profile: stripCols=${PROFILE.stripCols} maxHeightPx=${PROFILE.maxHeightPx} vision=${JSON.stringify(PROFILE.vision)}`); console.log(`text baseline ≈ ${TEXT_TOKENS} tok (chars/4)`); for (const v of VARIANTS) { // Match the Responses transform: single-col portrait strip, profile max height, // no library multi-col packing. const imgs = await renderTextToPngs(SESSION, v.cols, v.style, PROFILE.maxHeightPx); const pages = imgs.map((im) => ({ png: im.png, width: im.width, height: im.height })); const imageTokens = imageTokensForPages(pages); const dataUrls = pages.map((p) => 'data:image/png;base64,' + Buffer.from(p.png).toString('base64')); const savingsPct = Math.round((1 - imageTokens / TEXT_TOKENS) * 100); const row = { variant: v.name, cols: v.cols, pages: pages.length, dims: pages.map((p) => `${p.width}x${p.height}`), imageTokens, savingsPct, model: null, }; console.log(`\n[${v.name}] cols=${v.cols} ${pages.length} page(s) ${row.dims.join(',')} → ${imageTokens} img tok vs ${TEXT_TOKENS} text (${savingsPct}% saved)`); if (live) { console.log(` calling ${MODEL} over Responses...`); const m = { exactCorrect: 0, exactTotal: 0, confab: 0, abstain: 0, refused: 0, gistOk: false, guardOk: false, answers: [], }; for (const q of QUESTIONS) { let text, ms, status, incomplete; try { ({ text, ms, status, incomplete } = await callModel(MODEL, dataUrls, q.q)); } catch (err) { console.error(` ${q.id.padEnd(6)} ERROR ${err.message}`); m.answers.push({ id: q.id, kind: q.kind, expected: q.answer, got: '', error: String(err.message || err), ok: false, abstained: false, confab: false, refused: true, ms: 0 }); m.refused++; if (q.kind === 'exact') m.exactTotal++; continue; } const s = score(q.kind, q.answer, text, status); m.answers.push({ id: q.id, kind: q.kind, expected: q.answer, got: text, status, incomplete, ...s, ms, }); if (q.kind === 'exact') { m.exactTotal++; if (s.ok) m.exactCorrect++; } if (s.confab) m.confab++; if (s.abstained) m.abstain++; if (s.refused) m.refused++; if (q.kind === 'gist' && !s.refused) m.gistOk = s.ok; if (q.kind === 'guard' && !s.refused) m.guardOk = s.ok; const mark = s.ok ? 'OK' : s.refused ? 'REFUSED' : s.abstained ? 'ABSTAIN' : s.confab ? 'CONFAB' : 'MISS'; console.log(` ${q.id.padEnd(6)} ${mark.padEnd(8)} got=${JSON.stringify(text).slice(0, 80)} (${ms}ms)`); } row.model = m; console.log( ` → exact ${m.exactCorrect}/${m.exactTotal} · confab ${m.confab} · gist ${m.gistOk ? 'ok' : 'FAIL'} · guard ${m.guardOk ? 'ok' : 'FAIL'}`, ); } results.variants.push(row); } const outPath = join(here, 'results.json'); writeFileSync(outPath, JSON.stringify(results, null, 2)); console.log(`\nWrote ${outPath}`); if (!live) { console.log('Dry-run only. Re-run with GROK_DENSITY_LIVE=1 to score model answers.'); }