agentic-forecasting / js /tasks.js
Chennzi's picture
Deploy the website
d8793dc verified
Raw History Blame Contribute Delete
9.33 kB
// Tasks: the taxonomy as a browser. Empty branches stay visible, so coverage gaps show.
import { sparkline } from "./charts.js";
import { color, dayLabel, empty, fmt, h, redraws, sectionHead, system } from "./util.js";
const leafKey = (domain, leaf) => `${domain}/${leaf}`;
/** Best agent, expert forecast and best TSFM on a task's live leaderboard. */
function headline(rows) {
const best = (kind) => rows.filter((r) => system(r.system).kind === kind).sort((a, b) => a.scaled_crps - b.scaled_crps)[0];
return [["Agent", best("agent")], ["Expert", best("expert")], ["TSFM", best("tsfm")]].filter(([, r]) => r);
}
function scoreMini(rows) {
const lines = headline(rows);
if (!lines.length) return h("span", { class: "muted small" }, "not scored yet");
const max = d3.max(lines, ([, r]) => r.scaled_crps) * 1.1;
return h("div", { class: "score-mini", title: "Scaled CRPS on live forecasts, common pairs; lower is better, 1 = seasonal naive" },
lines.map(([label, r]) => h("div", { class: "row" },
h("span", { title: system(r.system).label }, label),
h("span", { class: "bar-track" }, h("span", { class: "bar-fill", style: { width: `${(r.scaled_crps / max) * 100}%`, background: color(r.system) } })),
h("span", { class: "v" }, fmt.ratio(r.scaled_crps)))));
}
export function tasksPage(app, index, params) {
const { taxonomy, tasks } = index;
const domains = taxonomy.domains;
const leafCount = d3.rollup(tasks, (v) => v.length, (t) => t.taxonomy);
const domainCount = d3.rollup(tasks, (v) => v.length, (t) => t.taxonomy?.split("/")[0]);
const leaves = domains.flatMap((d) => d.leaves.map((l) => ({ ...l, domain: d, key: leafKey(d.id, l.id) })));
const covered = leaves.filter((l) => leafCount.get(l.key));
const names = new Map(leaves.map((l) => [l.key, `${l.domain.name} › ${l.name}`]));
const drivers = taxonomy.facets.driver;
const state = { area: params.get("area") || "", q: params.get("q") || "", driver: params.get("driver") || "", tradable: params.get("tradable") || "" };
const tree = h("nav", { class: "tree", "aria-label": "Taxonomy" });
const toolbar = h("div", { class: "toolbar" });
const listing = h("div");
function save() {
const query = new URLSearchParams(Object.entries(state).filter(([, v]) => v));
history.replaceState(null, "", `#/tasks${query.size ? `?${query}` : ""}`);
}
function renderTree() {
const pick = (area) => () => { state.area = state.area === area ? "" : area; save(); render(); };
tree.replaceChildren(
h("div", { class: "tree-head" }, h("span", { class: "eyebrow" }, "Taxonomy"),
h("span", { class: "note" }, `${covered.length} of ${leaves.length} areas covered`)),
h("div", { class: "coverage", title: `${covered.length} of ${leaves.length} areas have a task` },
h("div", { style: { width: `${(covered.length / leaves.length) * 100}%` } })),
h("button", { type: "button", "aria-pressed": String(!state.area), onclick: pick("") },
h("span", {}, "All tasks"), h("span", { class: "count" }, String(tasks.length))),
...domains.flatMap((d) => [
h("button", { type: "button", class: "domain", "aria-pressed": String(state.area === d.id), onclick: pick(d.id) },
h("span", {}, d.name), h("span", { class: "count" }, String(domainCount.get(d.id) || 0))),
d.leaves.map((l) => {
const key = leafKey(d.id, l.id);
const n = leafCount.get(key) || 0;
return h("button", { type: "button", class: `leaf${n ? "" : " gap"}`, "aria-pressed": String(state.area === key), onclick: pick(key),
title: n ? null : `Candidates: ${l.examples.join("; ")}` },
h("span", {}, l.name), h("span", { class: "count" }, String(n)));
}),
]).flat());
}
function matches(t) {
if (state.area && !(t.taxonomy === state.area || t.taxonomy?.startsWith(`${state.area}/`))) return false;
if (state.driver && t.driver !== state.driver) return false;
if (state.tradable && String(Boolean(t.tradable)) !== state.tradable) return false;
const q = state.q.trim().toLowerCase();
return !q || [t.title, t.question, t.summary, t.region, names.get(t.taxonomy)].some((s) => s?.toLowerCase().includes(q));
}
function renderToolbar() {
const facet = (key, value, text) => h("button", {
type: "button", class: "chip", "aria-pressed": String(state[key] === value),
onclick: () => { state[key] = state[key] === value ? "" : value; save(); render(); },
}, text);
const search = h("input", { class: "search", type: "search", placeholder: "Search tasks", value: state.q, "aria-label": "Search tasks" });
search.addEventListener("input", () => { state.q = search.value; save(); renderListing(); });
toolbar.replaceChildren(search,
h("div", { class: "facets" },
h("div", { class: "chips" }, h("span", { class: "facet-label" }, "Driver"), Object.entries(drivers).map(([id, text]) => facet("driver", id, text))),
h("div", { class: "chips" }, h("span", { class: "facet-label" }, "Market"), facet("tradable", "false", "Not tradable"), facet("tradable", "true", "Tradable controls"))));
}
function renderListing() {
const shown = tasks.filter(matches);
const leaf = leaves.find((l) => l.key === state.area);
if (!shown.length) {
listing.replaceChildren(leaf
? h("div", { class: "gap-card" }, h("h4", {}, `No task in ${leaf.domain.name} › ${leaf.name} yet`),
h("p", { class: "secondary small" }, `Candidate targets: ${leaf.examples.join("; ")}.`))
: empty("No task matches these filters."));
return;
}
listing.replaceChildren(h("div", { class: "card" }, h("div", { class: "table-wrap" }, h("table", {},
h("thead", {}, h("tr", {}, h("th", { class: "col-task" }, "Task"), h("th", { class: "hide-sm" }, "Recent values"),
h("th", { class: "hide-sm" }, "Run days"), h("th", {}, "Live scaled CRPS"))),
h("tbody", {}, shown.map((t) => {
const spark = h("div", { class: "chart", style: { width: "120px" } });
redraws.push(() => sparkline(spark, { truth: t.truth }));
const open = () => { location.hash = `#/t/${t.id}`; };
return h("tr", { class: "link", onclick: open },
h("td", { class: "col-task" }, h("a", { class: "task-title", href: `#/t/${t.id}`, onclick: (e) => e.stopPropagation() }, t.title),
h("span", { class: "task-q" }, t.question),
h("div", { class: "chips", style: { marginTop: "6px" } },
h("span", { class: "chip" }, names.get(t.taxonomy) || "unclassified"),
t.driver ? h("span", { class: "chip" }, drivers[t.driver]) : null,
t.tradable ? h("span", { class: "chip" }, "tradable") : null,
h("span", { class: "note" }, `${t.region} · ${t.unit}`))),
h("td", { class: "hide-sm" }, spark, h("span", { class: "cell-sub" }, `last ${dayLabel(t.last_observed)}`)),
h("td", { class: "nowrap hide-sm" }, h("div", {}, String(t.run_days)), h("span", { class: "cell-sub" }, t.live_since ? `since ${dayLabel(t.live_since)}` : "not started")),
h("td", {}, scoreMini(t.leaderboard)));
}))))));
redraws.forEach((draw) => draw());
}
function render() {
redraws.length = 0;
renderTree();
renderToolbar();
renderListing();
}
const coverageTable = h("div", { class: "card" }, h("div", { class: "table-wrap" }, h("table", {},
h("thead", {}, h("tr", {}, h("th", {}, "Domain"), h("th", { class: "num" }, "Areas covered"), h("th", { class: "num" }, "Tasks"), h("th", {}, "Open areas and candidate targets"))),
h("tbody", {}, domains.map((d) => {
const open = d.leaves.filter((l) => !leafCount.get(leafKey(d.id, l.id)));
return h("tr", {},
h("td", {}, h("strong", {}, d.name)),
h("td", { class: "num" }, `${d.leaves.length - open.length} / ${d.leaves.length}`),
h("td", { class: "num" }, String(domainCount.get(d.id) || 0)),
h("td", { class: "small secondary" }, open.length
? open.map((l, i) => [i ? " · " : "", h("span", { title: l.examples.join("; ") }, l.name)])
: h("span", { class: "muted" }, "all covered")));
})))));
const agents = index.systems.filter((s) => s.kind === "agent").length;
app.replaceChildren(
h("header", { class: "page-head" },
h("span", { class: "eyebrow" }, "Tasks"),
h("h1", {}, "Forecasting tasks"),
h("p", { class: "lead" }, "Each task is a real series with an expert forecast to beat. Tasks are organised by a taxonomy of the systems that generate them; empty areas are gaps to fill."),
h("div", { class: "stats" },
[[tasks.length, "tasks"], [d3.sum(tasks, (t) => t.run_days), "run days"], [agents, "agents"],
[`${covered.length} / ${leaves.length}`, "areas covered"]]
.map(([v, l]) => h("div", { class: "stat" }, h("div", { class: "v" }, String(v)), h("div", { class: "l" }, l))))),
h("section", { class: "section browser" }, tree, h("div", {}, toolbar, listing)),
h("section", { class: "section" },
sectionHead("coverage", "Coverage", "Tasks per domain of the taxonomy. Open areas list the targets that would fill them; the taxonomy itself is revised as tasks are added."),
coverageTable));
render();
}