JacobLinCool Codex commited on
Commit
ba32aed
·
verified ·
1 Parent(s): 52c63cf

feat: add prize ledger

Browse files

Co-authored-by: Codex <noreply@openai.com>

README.md CHANGED
@@ -75,6 +75,12 @@ The `chapter` Gradio API endpoint and `Chapter` button export the public-facing
75
  one fate page per idea, each with verdict, score, targets, and closest cited pages. It is the shareable companion to
76
  the private Field Notes artifact.
77
 
 
 
 
 
 
 
78
  ## Wood Map
79
 
80
  Every scored fate page now carries a deterministic `wood_map` artifact: background dots for inked Spaces, red dots for
 
75
  one fate page per idea, each with verdict, score, targets, and closest cited pages. It is the shareable companion to
76
  the private Field Notes artifact.
77
 
78
+ ## Prize Ledger
79
+
80
+ `/api/prize-ledger` and the in-app Prize Ledger panel expose submission evidence: the documented model stack, total
81
+ parameter budget, Tiny Titan eligibility, runtime backend, and badge readiness. This keeps the demo's prize claims tied
82
+ to visible app state rather than hidden notes.
83
+
84
  ## Wood Map
85
 
86
  Every scored fate page now carries a deterministic `wood_map` artifact: background dots for inked Spaces, red dots for
app.py CHANGED
@@ -12,6 +12,7 @@ from hackathon_advisor.agent import AdvisorEngine
12
  from hackathon_advisor.chapter import build_chapter_markdown
13
  from hackathon_advisor.data import ProjectIndex
14
  from hackathon_advisor.field_notes import build_field_notes_markdown
 
15
  from hackathon_advisor.tool_contracts import resolve_tool_call, tool_schemas
16
  from hackathon_advisor.tools import TARGETS
17
  from hackathon_advisor.trace_export import build_trace_jsonl, trace_metadata
@@ -57,15 +58,17 @@ def health() -> dict:
57
 
58
  @app.get("/api/bootstrap")
59
  def bootstrap() -> dict:
 
60
  return {
61
  "project_count": len(index.projects),
62
- "runtime": engine.runtime_status(),
63
  **trace_metadata(index),
64
  "top_projects": [project.to_public_dict() for project in index.top_projects(limit=8)],
65
  "whitespace": [item.to_dict() for item in index.find_whitespace(limit=5)],
66
  "target_options": TARGETS,
67
  "default_targets": TARGETS[:3],
68
  "profile_fields": PROFILE_FIELDS,
 
69
  }
70
 
71
 
@@ -74,6 +77,11 @@ def runtime() -> dict:
74
  return engine.runtime_status()
75
 
76
 
 
 
 
 
 
77
  @app.get("/api/tool-contracts")
78
  def tool_contracts() -> dict:
79
  return {
 
12
  from hackathon_advisor.chapter import build_chapter_markdown
13
  from hackathon_advisor.data import ProjectIndex
14
  from hackathon_advisor.field_notes import build_field_notes_markdown
15
+ from hackathon_advisor.prize_ledger import prize_ledger
16
  from hackathon_advisor.tool_contracts import resolve_tool_call, tool_schemas
17
  from hackathon_advisor.tools import TARGETS
18
  from hackathon_advisor.trace_export import build_trace_jsonl, trace_metadata
 
58
 
59
  @app.get("/api/bootstrap")
60
  def bootstrap() -> dict:
61
+ runtime_status = engine.runtime_status()
62
  return {
63
  "project_count": len(index.projects),
64
+ "runtime": runtime_status,
65
  **trace_metadata(index),
66
  "top_projects": [project.to_public_dict() for project in index.top_projects(limit=8)],
67
  "whitespace": [item.to_dict() for item in index.find_whitespace(limit=5)],
68
  "target_options": TARGETS,
69
  "default_targets": TARGETS[:3],
70
  "profile_fields": PROFILE_FIELDS,
71
+ "prize_ledger": prize_ledger(runtime_status),
72
  }
73
 
74
 
 
77
  return engine.runtime_status()
78
 
79
 
80
+ @app.get("/api/prize-ledger")
81
+ def prize_ledger_endpoint() -> dict:
82
+ return prize_ledger(engine.runtime_status())
83
+
84
+
85
  @app.get("/api/tool-contracts")
86
  def tool_contracts() -> dict:
87
  return {
hackathon_advisor/prize_ledger.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+
6
+ MODEL_STACK = [
7
+ {
8
+ "role": "LLM brain",
9
+ "model": "openbmb/MiniCPM5-1B",
10
+ "params_b": 1.08,
11
+ "status": "optional runtime adapter",
12
+ "runtime": "transformers / GGUF-ready",
13
+ },
14
+ {
15
+ "role": "Retriever",
16
+ "model": "offline TF-IDF snapshot",
17
+ "params_b": 0.0,
18
+ "status": "deployed",
19
+ "runtime": "local sparse index",
20
+ },
21
+ {
22
+ "role": "Planned embedder",
23
+ "model": "google/embeddinggemma-300m",
24
+ "params_b": 0.30,
25
+ "status": "documented build path",
26
+ "runtime": "sentence-transformers / llama.cpp",
27
+ },
28
+ {
29
+ "role": "Voice bonus",
30
+ "model": "nvidia/nemotron-speech-streaming-en-0.6b",
31
+ "params_b": 0.60,
32
+ "status": "deferred bonus",
33
+ "runtime": "batch ASR",
34
+ },
35
+ ]
36
+
37
+
38
+ BADGE_LEDGER = [
39
+ {
40
+ "name": "Off the Grid",
41
+ "status": "ready",
42
+ "evidence": "Runtime uses a checked-in snapshot and local search; no proprietary inference API.",
43
+ },
44
+ {
45
+ "name": "Off-Brand",
46
+ "status": "ready",
47
+ "evidence": "Custom gr.Server frontend renders the agent as The Unwritten Almanac.",
48
+ },
49
+ {
50
+ "name": "Sharing is Caring",
51
+ "status": "ready",
52
+ "evidence": "JSONL trace export and checked-in sample trace are published with the Space.",
53
+ },
54
+ {
55
+ "name": "Field Notes",
56
+ "status": "ready",
57
+ "evidence": "Field Notes markdown export is generated from exact session state.",
58
+ },
59
+ {
60
+ "name": "Tiny Titan",
61
+ "status": "eligible",
62
+ "evidence": "Documented stack stays under 4B parameters; largest model is MiniCPM5-1B.",
63
+ },
64
+ {
65
+ "name": "Well-Tuned",
66
+ "status": "planned",
67
+ "evidence": "Plan includes a MiniCPM5 LoRA path; adapter publication remains a separate build milestone.",
68
+ },
69
+ {
70
+ "name": "Llama Champion",
71
+ "status": "planned",
72
+ "evidence": "MiniCPM5 GGUF and EmbeddingGemma GGUF paths are documented; runtime does not depend on them yet.",
73
+ },
74
+ ]
75
+
76
+
77
+ def prize_ledger(runtime: dict[str, Any]) -> dict[str, Any]:
78
+ total_params = round(sum(float(item["params_b"]) for item in MODEL_STACK), 2)
79
+ largest = max(MODEL_STACK, key=lambda item: float(item["params_b"]))
80
+ return {
81
+ "runtime": runtime,
82
+ "model_stack": MODEL_STACK,
83
+ "total_params_b": total_params,
84
+ "largest_model": {
85
+ "model": largest["model"],
86
+ "params_b": largest["params_b"],
87
+ },
88
+ "tiny_titan_limit_b": 4.0,
89
+ "tiny_titan_eligible": total_params <= 4.0 and float(largest["params_b"]) <= 4.0,
90
+ "badges": BADGE_LEDGER,
91
+ }
static/app.js CHANGED
@@ -10,6 +10,7 @@ const whitespaceEl = document.querySelector("#whitespace");
10
  const ideasEl = document.querySelector("#ideas");
11
  const targetsEl = document.querySelector("#targets");
12
  const profileEl = document.querySelector("#profile");
 
13
  const woodMapEl = document.querySelector("#wood-map");
14
  const scoreEl = document.querySelector("#score");
15
  const planEl = document.querySelector("#plan");
@@ -145,6 +146,7 @@ async function bootstrap() {
145
  renderProvenance(data);
146
  renderTargets(session.targets);
147
  renderProfile(session.profile);
 
148
  renderRestoredSession(data);
149
  renderWhitespace(data.whitespace || []);
150
  }
@@ -266,6 +268,37 @@ function renderProfile(profile) {
266
  }
267
  }
268
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269
  function handleEvent(event) {
270
  if (event.type === "start") {
271
  if (event.corrections?.length) {
 
10
  const ideasEl = document.querySelector("#ideas");
11
  const targetsEl = document.querySelector("#targets");
12
  const profileEl = document.querySelector("#profile");
13
+ const prizeLedgerEl = document.querySelector("#prize-ledger");
14
  const woodMapEl = document.querySelector("#wood-map");
15
  const scoreEl = document.querySelector("#score");
16
  const planEl = document.querySelector("#plan");
 
146
  renderProvenance(data);
147
  renderTargets(session.targets);
148
  renderProfile(session.profile);
149
+ renderPrizeLedger(data.prize_ledger || null);
150
  renderRestoredSession(data);
151
  renderWhitespace(data.whitespace || []);
152
  }
 
268
  }
269
  }
270
 
271
+ function renderPrizeLedger(ledger) {
272
+ prizeLedgerEl.innerHTML = "";
273
+ if (!ledger) {
274
+ prizeLedgerEl.innerHTML = `<div class="empty">No prize ledger loaded.</div>`;
275
+ return;
276
+ }
277
+ const readyBadges = (ledger.badges || []).filter((badge) => badge.status === "ready").length;
278
+ const badgeCount = (ledger.badges || []).length;
279
+ const header = document.createElement("div");
280
+ header.className = "ledger-summary";
281
+ header.innerHTML = `
282
+ <strong>${Number(ledger.total_params_b || 0).toFixed(2)}B params</strong>
283
+ <span>${ledger.tiny_titan_eligible ? "Tiny Titan eligible" : "Over Tiny Titan limit"}</span>
284
+ <span>${readyBadges}/${badgeCount} ready</span>
285
+ <span>${escapeHtml(ledger.runtime?.backend || "runtime")}</span>
286
+ `;
287
+ const badges = document.createElement("div");
288
+ badges.className = "badge-list";
289
+ for (const badge of (ledger.badges || []).slice(0, 7)) {
290
+ const item = document.createElement("div");
291
+ item.className = `badge-item ${badge.status || "planned"}`;
292
+ item.title = badge.evidence || badge.name;
293
+ item.innerHTML = `
294
+ <strong>${escapeHtml(badge.name)}</strong>
295
+ <span>${escapeHtml(badge.status)}</span>
296
+ `;
297
+ badges.append(item);
298
+ }
299
+ prizeLedgerEl.append(header, badges);
300
+ }
301
+
302
  function handleEvent(event) {
303
  if (event.type === "start") {
304
  if (event.corrections?.length) {
static/index.html CHANGED
@@ -61,6 +61,10 @@
61
  <h2>Profile</h2>
62
  <div id="profile" class="profile-grid"></div>
63
  </article>
 
 
 
 
64
  <article>
65
  <h2>Idea Board</h2>
66
  <div id="ideas" class="idea-list"></div>
 
61
  <h2>Profile</h2>
62
  <div id="profile" class="profile-grid"></div>
63
  </article>
64
+ <article class="wide-panel">
65
+ <h2>Prize Ledger</h2>
66
+ <div id="prize-ledger" class="prize-ledger"></div>
67
+ </article>
68
  <article>
69
  <h2>Idea Board</h2>
70
  <div id="ideas" class="idea-list"></div>
static/styles.css CHANGED
@@ -302,7 +302,8 @@ button:disabled {
302
  .trace-list,
303
  .target-list,
304
  .profile-grid,
305
- .wood-map {
 
306
  display: grid;
307
  gap: 9px;
308
  }
@@ -426,6 +427,57 @@ button:disabled {
426
  box-shadow: 0 0 0 3px rgba(47, 122, 73, 0.13);
427
  }
428
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
429
  .wood-map-field {
430
  position: relative;
431
  min-height: 138px;
@@ -526,6 +578,11 @@ button:disabled {
526
  grid-template-columns: 1fr;
527
  }
528
 
 
 
 
 
 
529
  .command-row {
530
  grid-template-columns: repeat(3, minmax(0, 1fr));
531
  }
 
302
  .trace-list,
303
  .target-list,
304
  .profile-grid,
305
+ .wood-map,
306
+ .prize-ledger {
307
  display: grid;
308
  gap: 9px;
309
  }
 
427
  box-shadow: 0 0 0 3px rgba(47, 122, 73, 0.13);
428
  }
429
 
430
+ .ledger-summary {
431
+ display: grid;
432
+ grid-template-columns: repeat(4, minmax(0, 1fr));
433
+ gap: 8px;
434
+ }
435
+
436
+ .ledger-summary strong,
437
+ .ledger-summary span,
438
+ .badge-item {
439
+ min-width: 0;
440
+ border-left: 3px solid rgba(80, 47, 22, 0.48);
441
+ border-radius: 0 8px 8px 0;
442
+ background: rgba(255, 241, 196, 0.34);
443
+ padding: 8px 10px;
444
+ color: #2a170d;
445
+ font-size: 0.76rem;
446
+ line-height: 1.25;
447
+ font-weight: 900;
448
+ }
449
+
450
+ .badge-list {
451
+ display: grid;
452
+ grid-template-columns: repeat(3, minmax(0, 1fr));
453
+ gap: 7px;
454
+ }
455
+
456
+ .badge-item {
457
+ display: flex;
458
+ align-items: center;
459
+ justify-content: space-between;
460
+ gap: 8px;
461
+ }
462
+
463
+ .badge-item.ready {
464
+ border-left-color: var(--leaf);
465
+ }
466
+
467
+ .badge-item.eligible {
468
+ border-left-color: var(--gold);
469
+ }
470
+
471
+ .badge-item.planned {
472
+ border-left-color: var(--muted-ink);
473
+ }
474
+
475
+ .badge-item span {
476
+ color: var(--muted-ink);
477
+ font-size: 0.68rem;
478
+ text-transform: uppercase;
479
+ }
480
+
481
  .wood-map-field {
482
  position: relative;
483
  min-height: 138px;
 
578
  grid-template-columns: 1fr;
579
  }
580
 
581
+ .ledger-summary,
582
+ .badge-list {
583
+ grid-template-columns: 1fr;
584
+ }
585
+
586
  .command-row {
587
  grid-template-columns: repeat(3, minmax(0, 1fr));
588
  }
tests/test_app.py CHANGED
@@ -7,6 +7,7 @@ from app import (
7
  field_notes_artifact,
8
  health,
9
  index,
 
10
  runtime,
11
  tool_contract_check,
12
  tool_contracts,
@@ -34,6 +35,7 @@ def test_bootstrap_exposes_index_metadata() -> None:
34
  assert payload["top_projects"]
35
  assert payload["default_targets"] == payload["target_options"][:3]
36
  assert "skills" in payload["profile_fields"]
 
37
 
38
 
39
  def test_trace_artifact_endpoint_exports_jsonl() -> None:
@@ -94,3 +96,11 @@ def test_runtime_endpoint_reports_planner() -> None:
94
  assert payload["backend"] == "rules"
95
  assert payload["model_id"] == "deterministic-tool-router"
96
  assert payload["loaded"] is True
 
 
 
 
 
 
 
 
 
7
  field_notes_artifact,
8
  health,
9
  index,
10
+ prize_ledger_endpoint,
11
  runtime,
12
  tool_contract_check,
13
  tool_contracts,
 
35
  assert payload["top_projects"]
36
  assert payload["default_targets"] == payload["target_options"][:3]
37
  assert "skills" in payload["profile_fields"]
38
+ assert payload["prize_ledger"]["tiny_titan_eligible"] is True
39
 
40
 
41
  def test_trace_artifact_endpoint_exports_jsonl() -> None:
 
96
  assert payload["backend"] == "rules"
97
  assert payload["model_id"] == "deterministic-tool-router"
98
  assert payload["loaded"] is True
99
+
100
+
101
+ def test_prize_ledger_endpoint_reports_submission_evidence() -> None:
102
+ payload = prize_ledger_endpoint()
103
+
104
+ assert payload["runtime"]["backend"] == "rules"
105
+ assert payload["tiny_titan_eligible"] is True
106
+ assert any(badge["name"] == "Sharing is Caring" for badge in payload["badges"])
tests/test_prize_ledger.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from hackathon_advisor.prize_ledger import prize_ledger
2
+
3
+
4
+ def test_prize_ledger_tracks_param_budget_and_badges() -> None:
5
+ payload = prize_ledger({"backend": "rules", "model_id": "deterministic-tool-router"})
6
+
7
+ assert payload["runtime"]["backend"] == "rules"
8
+ assert payload["total_params_b"] <= payload["tiny_titan_limit_b"]
9
+ assert payload["tiny_titan_eligible"] is True
10
+ assert payload["largest_model"]["model"] == "openbmb/MiniCPM5-1B"
11
+ badges = {badge["name"]: badge["status"] for badge in payload["badges"]}
12
+ assert badges["Off the Grid"] == "ready"
13
+ assert badges["Well-Tuned"] == "planned"