PromptBoost / src /app.ts
smlflg's picture
Initial public upload from Projekte/PromptBoost (part 5)
638c806 verified
Raw History Blame Contribute Delete
7.43 kB
import {
evalFixtures,
generateBoostedPrompt,
harnessPolicies,
modelAdapterProfiles,
runLocalFixtureComparison,
taskTypeLabels
} from "./index.ts";
import type { BoostedPrompt, PromptScoreDimension, TaskType } from "./index.ts";
const rawPrompt = query<HTMLTextAreaElement>("#rawPrompt");
const taskType = query<HTMLSelectElement>("#taskType");
const adapterProfile = query<HTMLSelectElement>("#adapterProfile");
const harnessPolicy = query<HTMLSelectElement>("#harnessPolicy");
const boostedPromptOutput = query<HTMLElement>("#boostedPrompt");
const taskSpecCards = query<HTMLElement>("#taskSpecCards");
const scoreGrid = query<HTMLElement>("#scoreGrid");
const evalRows = query<HTMLElement>("#evalRows");
const jsonExport = query<HTMLElement>("#jsonExport");
const overallBadge = query<HTMLElement>("#overallBadge");
const wordBadge = query<HTMLElement>("#wordBadge");
const generateButton = query<HTMLButtonElement>("#generateButton");
const copyPromptButton = query<HTMLButtonElement>("#copyPromptButton");
const downloadJsonButton = query<HTMLButtonElement>("#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 = [
`<article class="score-card hero-score">
<span>Overall</span>
<strong>${score.overall}</strong>
<p>${score.findings[0]}</p>
</article>`,
...score.dimensions.map(renderDimension)
].join("");
}
function renderDimension(dimension: PromptScoreDimension) {
const percent = Math.round((dimension.value / 5) * 100);
return `<article class="score-card">
<span>${escapeHtml(dimension.label)}</span>
<strong>${dimension.value}/5</strong>
<div class="bar"><i style="width: ${percent}%"></i></div>
<p>${escapeHtml(dimension.evidence)}</p>
</article>`;
}
function renderFixtureComparison() {
const rows = runLocalFixtureComparison();
evalRows.innerHTML = rows
.map(({ fixture, best }) => {
const finding = best.score.findings[0] || "No heuristic finding.";
return `<tr>
<td>${fixture.id}</td>
<td>${escapeHtml(taskTypeLabels[fixture.taskType])}</td>
<td>${escapeHtml(best.modelProfile.label)}</td>
<td>${best.score.overall}</td>
<td>${escapeHtml(finding)}</td>
</tr>`;
})
.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 `<article class="spec-card">
<span>${escapeHtml(label)}</span>
<p>${escapeHtml(value)}</p>
</article>`;
}
function listCard(label: string, items: string[]) {
return `<article class="spec-card">
<span>${escapeHtml(label)}</span>
<ul>${items.map((item) => `<li>${escapeHtml(item)}</li>`).join("")}</ul>
</article>`;
}
function option(value: string, label: string, selected = false) {
return `<option value="${escapeHtml(value)}"${selected ? " selected" : ""}>${escapeHtml(label)}</option>`;
}
function query<T extends Element>(selector: string): T {
const element = document.querySelector<T>(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("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&#039;");
}
function countWords(value: string) {
return value.trim().split(/\s+/).filter(Boolean).length;
}
boot();