import { evalFixtures, generateBoostedPrompt, harnessPolicies, modelAdapterProfiles, runLocalFixtureComparison, taskTypeLabels } from "./index.ts"; import type { BoostedPrompt, PromptScoreDimension, TaskType } from "./index.ts"; const rawPrompt = query("#rawPrompt"); const taskType = query("#taskType"); const adapterProfile = query("#adapterProfile"); const harnessPolicy = query("#harnessPolicy"); const boostedPromptOutput = query("#boostedPrompt"); const taskSpecCards = query("#taskSpecCards"); const scoreGrid = query("#scoreGrid"); const evalRows = query("#evalRows"); const jsonExport = query("#jsonExport"); const overallBadge = query("#overallBadge"); const wordBadge = query("#wordBadge"); const generateButton = query("#generateButton"); const copyPromptButton = query("#copyPromptButton"); const downloadJsonButton = query("#downloadJsonButton"); let currentBoostedPrompt: BoostedPrompt; function boot() { populateControls(); renderFixtureComparison(); generate(); generateButton.addEventListener("click", generate); rawPrompt.addEventListener("input", generate); taskType.addEventListener("change", generate); adapterProfile.addEventListener("change", generate); harnessPolicy.addEventListener("change", generate); copyPromptButton.addEventListener("click", copyPrompt); downloadJsonButton.addEventListener("click", downloadJson); } function populateControls() { taskType.innerHTML = Object.entries(taskTypeLabels) .map(([value, label]) => option(value, label, value === "long_document")) .join(""); adapterProfile.innerHTML = modelAdapterProfiles .map((profile) => option(profile.id, `${profile.label} - ${profile.style} (${profile.evidence})`, profile.id === "gpt-openai")) .join(""); harnessPolicy.innerHTML = harnessPolicies .map((policy) => option(policy.id, policy.label, policy.id === "blank")) .join(""); } function generate() { currentBoostedPrompt = generateBoostedPrompt(rawPrompt.value, { taskType: taskType.value as TaskType, modelProfileId: adapterProfile.value, harnessPolicyId: harnessPolicy.value }); boostedPromptOutput.textContent = currentBoostedPrompt.prompt; renderTaskSpec(currentBoostedPrompt); renderScore(currentBoostedPrompt); renderJson(currentBoostedPrompt); wordBadge.textContent = `${countWords(rawPrompt.value)} words`; } function renderTaskSpec(boostedPrompt: BoostedPrompt) { const { taskSpec, modelProfile, harnessPolicy } = boostedPrompt; taskSpecCards.innerHTML = [ infoCard("Goal", taskSpec.goal), infoCard("Task Type", taskTypeLabels[taskSpec.taskType]), listCard("Inputs", taskSpec.inputs), listCard("Observed Deliverables", taskSpec.deliverables), listCard("Suggested Scaffolding", taskSpec.suggestedScaffolding.deliverables), listCard("Constraints", taskSpec.constraints), listCard("Ambiguities", taskSpec.ambiguities), infoCard("Input Adequacy", `${taskSpec.inputAdequacy.status} (${taskSpec.inputAdequacy.score}/100)`), infoCard("Harness", harnessPolicy.label), infoCard("Adapter", `${modelProfile.label}: ${modelProfile.summary}`), infoCard("Evidence", `${modelProfile.evidence}: ${modelProfile.evidenceNote}`) ].join(""); } function renderScore(boostedPrompt: BoostedPrompt) { const { score } = boostedPrompt; overallBadge.textContent = `${score.overall} / 100`; overallBadge.className = `badge grade-${score.grade}`; scoreGrid.innerHTML = [ `
Overall ${score.overall}

${score.findings[0]}

`, ...score.dimensions.map(renderDimension) ].join(""); } function renderDimension(dimension: PromptScoreDimension) { const percent = Math.round((dimension.value / 5) * 100); return `
${escapeHtml(dimension.label)} ${dimension.value}/5

${escapeHtml(dimension.evidence)}

`; } function renderFixtureComparison() { const rows = runLocalFixtureComparison(); evalRows.innerHTML = rows .map(({ fixture, best }) => { const finding = best.score.findings[0] || "No heuristic finding."; return ` ${fixture.id} ${escapeHtml(taskTypeLabels[fixture.taskType])} ${escapeHtml(best.modelProfile.label)} ${best.score.overall} ${escapeHtml(finding)} `; }) .join(""); if (evalFixtures.length !== rows.length) { console.warn("Fixture comparison did not evaluate all fixtures."); } } function renderJson(boostedPrompt: BoostedPrompt) { const portable = { id: boostedPrompt.id, raw_user_prompt: boostedPrompt.rawUserPrompt, task_spec: boostedPrompt.taskSpec, harness_policy: boostedPrompt.harnessPolicy, model_adapter: boostedPrompt.modelProfile, optimized_prompt: boostedPrompt.prompt, eval_plan: boostedPrompt.evalPlan, input_adequacy: boostedPrompt.inputAdequacy, score: boostedPrompt.score, rationale: boostedPrompt.rationale }; jsonExport.textContent = JSON.stringify(portable, null, 2); } async function copyPrompt() { try { await navigator.clipboard.writeText(currentBoostedPrompt.prompt); flashButton(copyPromptButton, "Copied"); } catch { flashButton(copyPromptButton, "Copy blocked"); } } function downloadJson() { const blob = new Blob([jsonExport.textContent || "{}"], { type: "application/json" }); const href = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = href; link.download = `${currentBoostedPrompt.id}.json`; document.body.appendChild(link); link.click(); link.remove(); URL.revokeObjectURL(href); flashButton(downloadJsonButton, "Exported"); } function infoCard(label: string, value: string) { return `
${escapeHtml(label)}

${escapeHtml(value)}

`; } function listCard(label: string, items: string[]) { return `
${escapeHtml(label)}
    ${items.map((item) => `
  • ${escapeHtml(item)}
  • `).join("")}
`; } function option(value: string, label: string, selected = false) { return ``; } function query(selector: string): T { const element = document.querySelector(selector); if (!element) { throw new Error(`Missing element: ${selector}`); } return element; } function flashButton(button: HTMLButtonElement, text: string) { const previous = button.textContent || ""; button.textContent = text; window.setTimeout(() => { button.textContent = previous; }, 1100); } function escapeHtml(value: string) { return value .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """) .replaceAll("'", "'"); } function countWords(value: string) { return value.trim().split(/\s+/).filter(Boolean).length; } boot();