/** * The page: one repo id in, one serving decision out. * * All rules live in parser_advice.js, which the parity gate holds to the * Python implementation. Nothing here decides anything — it fetches, * calls, and renders, so a rule can never be quietly different in the UI * from the one that was tested. */ import { ALL_MARKERS, advise, fetchTemplate } from './parser_advice.js'; import { progress } from './progress.js'; import { ensureNextStepStyles, nextSteps, repoFromUrl } from './tools.js'; const $ = (id) => document.getElementById(id); // A link from another tool carries the model over; arriving at an empty // form is what makes five tools feel like five tools instead of one. ensureNextStepStyles(); const carried = repoFromUrl(); if (carried) { $('repo').value = carried; } const out = $('out'); const esc = (s) => String(s).replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c])); function renderRules() { const seen = new Map(); for (const [parser, marks] of Object.entries(ALL_MARKERS)) { const key = marks.join(' '); if (!seen.has(key)) seen.set(key, []); seen.get(key).push(parser); } $('rules').innerHTML = [...seen.entries()].map(([marks, parsers]) => `
${parsers.map(esc).join(', ')}${marks.split(' ').map(esc).join(' ')}Nothing here can be decided from metadata. The repo publishes neither
chat_template.jinja nor a chat_template in
tokenizer_config.json, so there is no assistant turn to read.
Serving it will still work — the decision just has to come from somewhere
else.
The template carries no marker any parser closes on. Adding
--reasoning-parser here is the silent failure: the parser
would claim the whole answer, the request would finish with
stop, and content would come back empty.
Interchangeable for this model, because they close on the same marker:
${others.map(esc).join(', ')}. Pick the one named
for the model's family if there is one.
Its template emits ${marks.map((m) => `${esc(m)}`).join(' and ')},
which ${esc(preferred)} closes on.
Without the flag that section reaches the user as the answer.
`); } async function run(repo) { const go = $('go'); go.disabled = true; // Two small files at most, so this stays indeterminate — there is // nothing to count. It is here for the same reason as on the other // two pages: a page that shows nothing while it works reads as a page // that does not work, and the wait is not always short. const pg = progress(out).start(`reading the chat template of ${repo}`); try { const { template, source } = await fetchTemplate(repo); pg.done(); out.innerHTML = render(repo, source, advise(template)) + nextSteps('parser', repo); } catch (e) { out.innerHTML = card('problem', 'Could not read the repo', `${esc(e.message || e)}
Gated and private repos cannot be read from the browser.
`); } finally { go.disabled = false; } } $('form').addEventListener('submit', (e) => { e.preventDefault(); const v = $('repo').value.trim(); if (v) run(v); }); document.querySelectorAll('[data-ex]').forEach((b) => b.addEventListener('click', () => { $('repo').value = b.dataset.ex; run(b.dataset.ex); })); renderRules();