// Our prompts and Astra's reasoning:
// The JSON (scripts/gen_traces.py) has {items}; an item is one network: {label, gif (optional), correct, prompt (blocks // of text, with a number where that many weights are left out), question, weights, quoted, brief (the key sentences of // Astra's answer, a code block, its final answer and confidence, in its order), answer (its whole answer, markdown), // tokens, thinking_tokens, effort, thinking (the headings of its hidden thinking, which is all the API returns of it)}. // One tab per item, a compact card: a replay across its top (`media`, an SVG, with the right answer under it) or an // exchange (`chat`: {user, guess, replies: [{model, says, follows}]}, the user's guess in red where a reply follows it), the // network's animation (or, for a // diffusion model, six images it generates) beside three parts, each with its label: the Prompt (the question in a // sentence, what it rules out, and what comes with it; the whole prompt on "Show the full prompt"), the // Answer (the core of Astra's final answer, its confidence and whether it is right, or the weights it // wrote), and the Reasoning (the key sentences of its reply as steps in a box that scrolls, its bold terms // highlighted; the whole reply on "Read full reasoning"). Marked (between ⦃ ⦄ in the JSON, and // the numbers of `marks` in the rows of weights): where Astra reads the weights, in its answer and in the prompt. The // left / right keys switch the tabs. An item may give its question line (`ask`, `rule`), what came with it (`given`, // for weights that were not given as text), its number of runs instead of token counts, a score to show instead of // right or wrong (`result`), and an episode to play beside it (`play`, played by js/handplay.js). A figure's setup sits in
at its top right; a click // elsewhere, or Escape, closes it. (() => { const esc = (s) => s.replace(/&/g, "&").replace(//g, ">"); const n = (x) => x.toLocaleString("en-US"); // the final answer: a heading "## Game" / "## Answer: A", or a label "Game: ..." / "**Images:** ..." / "Ranking: ..." const ANSWER = /^(?:#+\s*)?(?:\*\*)?(Game|Images|Answer|Ranking)\b(?:\*\*)?\s*(?::(?:\*\*)?\s*(.*))?$/; function inline(s) { return esc(s) .replace(/\*\*(.+?)\*\*/g, "$1") .replace(/(^|[\s(])\*([^*\s][^*]*?)\*(?=[\s).,;:]|$)/g, "$1$2") .replace(/`([^`]+)`/g, '$1') .replace(/⟦/g, '').replace(/⟧/g, "") .replace(/⦃/g, '').replace(/⦄/g, ""); } // Prompt text: as it is, with the parts Astra needs to read the weights marked (between ⦃ ⦄). const ptext = (s) => esc(s).replace(/⦃/g, '').replace(/⦄/g, ""); // A code block without the indent it has inside a list item. function pre(lines) { const indent = Math.min(...lines.filter((l) => l.trim()).map((l) => l.match(/^\s*/)[0].length)); return `
${esc(lines.map((l) => l.slice(indent)).join("\n"))}
`; } // A markdown table (its lines) -> HTML. function table(lines) { const cells = (l) => l.trim().replace(/^\||\|$/g, "").split("|").map((c) => inline(c.trim())); const rows = lines.filter((l) => !/^[\s|:-]+$/.test(l)); const [head, ...body] = rows.map(cells); return `${head.map((c) => ``).join("")}` + body.map((r) => `${r.map((c) => ``).join("")}`).join("") + "
${c}
${c}
"; } // An answer in markdown -> HTML: headings, paragraphs, lists, tables, code blocks, display math (typeset by // MathJax afterwards); the final answer marked. function reply(md) { const out = []; let list = false, fence = null, rows = null, math = null, markNext = false; const closeList = () => { if (list) { out.push(""); list = false; } }; const closeTable = () => { if (rows) { out.push(table(rows)); rows = null; } }; for (const raw of md.split("\n")) { if (/^\s*```/.test(raw)) { if (fence === null) { closeList(); closeTable(); fence = []; } else { out.push(pre(fence)); fence = null; } continue; } if (fence !== null) { fence.push(raw); continue; } const line = raw.trim(); if (math !== null || line.startsWith("\\[")) { if (math === null) { closeList(); closeTable(); math = []; } math.push(line); if (line.endsWith("\\]")) { out.push(`
${esc(math.join("\n"))}
`); math = null; } continue; } if (line.startsWith("|")) { closeList(); (rows = rows || []).push(line); continue; } closeTable(); if (!line) { closeList(); continue; } const m = line.match(ANSWER); if (m) { closeList(); const rest = (m[2] || "").trim(); out.push(`
${esc(m[1])}
` + (rest ? `

${inline(rest)}

` : "")); markNext = !rest; continue; } const head = line.match(/^#+\s*(.*)$/) || line.match(/^(?:\*\*)?([A-Z][A-Za-z ]{2,30}):(?:\*\*)?$/); if (head) { closeList(); out.push(`
${inline(head[1].replace(/:$/, ""))}
`); continue; } let html = inline(line.replace(/^[-*]\s+/, "")); if (markNext) { html = `${html}`; markNext = false; } if (/^[-*]\s+/.test(line)) { if (!list) { out.push("