cvpfus Codex commited on
Commit ·
57908c8
1
Parent(s): e898b4a
Add judge demo script endpoint
Browse filesCo-authored-by: Codex <codex@openai.com>
- FIELD_NOTES.md +2 -0
- README.md +3 -1
- app.py +55 -0
- scripts/verify.py +21 -0
FIELD_NOTES.md
CHANGED
|
@@ -28,6 +28,8 @@ The app exposes the same budget through `/api/model-budget`, including numeric `
|
|
| 28 |
|
| 29 |
Runtime setup is also data-backed. `/api/runtime-setup` lists the app command, llama.cpp launch command, model-specific runtime paths, environment values, and fallbacks. The session panel renders a compact version so the demo can distinguish real model wiring from deterministic safety nets.
|
| 30 |
|
|
|
|
|
|
|
| 31 |
## Reader Mode Behavior
|
| 32 |
|
| 33 |
The frontend creates a reading queue from semantic nodes: headings, paragraphs, quotes, figures, captions, and controls. It speaks one item at a time, keeps keyboard focus synchronized with the active node, and exposes the current narration through an `aria-live` region.
|
|
|
|
| 28 |
|
| 29 |
Runtime setup is also data-backed. `/api/runtime-setup` lists the app command, llama.cpp launch command, model-specific runtime paths, environment values, and fallbacks. The session panel renders a compact version so the demo can distinguish real model wiring from deterministic safety nets.
|
| 30 |
|
| 31 |
+
The judge runbook lives at `/api/demo-script`. It keeps the live presentation repeatable by pairing visible actions with API checks for health, model budget, runtime setup, runtime status, image descriptions, reader narration, and speech.
|
| 32 |
+
|
| 33 |
## Reader Mode Behavior
|
| 34 |
|
| 35 |
The frontend creates a reading queue from semantic nodes: headings, paragraphs, quotes, figures, captions, and controls. It speaks one item at a time, keeps keyboard focus synchronized with the active node, and exposes the current narration through an `aria-live` region.
|
README.md
CHANGED
|
@@ -50,7 +50,7 @@ Start the app:
|
|
| 50 |
python app.py
|
| 51 |
```
|
| 52 |
|
| 53 |
-
Open the local URL printed by Gradio. The custom frontend calls `/api/reader-brain`, `/api/image-descriptions`, `/api/describe-image`, `/api/speak`, `/api/generate-image`, `/api/model-budget`,
|
| 54 |
|
| 55 |
Useful environment variables:
|
| 56 |
|
|
@@ -73,6 +73,8 @@ The verifier checks syntax, static assets, deterministic fallback model paths, a
|
|
| 73 |
|
| 74 |
`/api/runtime-setup` exposes the commands, environment values, and fallback paths used for the model stack so the demo can be reproduced from the same data the UI displays.
|
| 75 |
|
|
|
|
|
|
|
| 76 |
## Screen Reader Mode
|
| 77 |
|
| 78 |
The frontend builds a reading queue from semantic article nodes. When screen-reader mode is on:
|
|
|
|
| 50 |
python app.py
|
| 51 |
```
|
| 52 |
|
| 53 |
+
Open the local URL printed by Gradio. The custom frontend calls `/api/reader-brain`, `/api/image-descriptions`, `/api/describe-image`, `/api/speak`, `/api/generate-image`, `/api/model-budget`, `/api/runtime-setup`, and `/api/demo-script`.
|
| 54 |
|
| 55 |
Useful environment variables:
|
| 56 |
|
|
|
|
| 73 |
|
| 74 |
`/api/runtime-setup` exposes the commands, environment values, and fallback paths used for the model stack so the demo can be reproduced from the same data the UI displays.
|
| 75 |
|
| 76 |
+
`/api/demo-script` exposes a compact judge runbook with the visible actions and API checks that prove the submission claims.
|
| 77 |
+
|
| 78 |
## Screen Reader Mode
|
| 79 |
|
| 80 |
The frontend builds a reading queue from semantic article nodes. When screen-reader mode is on:
|
app.py
CHANGED
|
@@ -201,6 +201,50 @@ def runtime_setup_core() -> dict[str, Any]:
|
|
| 201 |
}
|
| 202 |
|
| 203 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 204 |
ARTICLE_MANIFEST: dict[str, Any] = {
|
| 205 |
"title": "A tiny model reader that turns articles into guided narration",
|
| 206 |
"reader_controls": [
|
|
@@ -218,6 +262,7 @@ ARTICLE_MANIFEST: dict[str, Any] = {
|
|
| 218 |
"models": MODEL_MANIFEST,
|
| 219 |
"model_budget": model_budget_core(),
|
| 220 |
"runtime_setup": runtime_setup_core(),
|
|
|
|
| 221 |
"reader_settings": READER_SETTINGS,
|
| 222 |
"award_evidence": AWARD_EVIDENCE,
|
| 223 |
}
|
|
@@ -538,6 +583,11 @@ async def runtime_setup() -> JSONResponse:
|
|
| 538 |
return _json(runtime_setup_core())
|
| 539 |
|
| 540 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 541 |
@app.get("/api/runtime-status")
|
| 542 |
async def runtime_status() -> JSONResponse:
|
| 543 |
return _json(_runtime_status_core())
|
|
@@ -593,6 +643,11 @@ def runtime_setup_api() -> str:
|
|
| 593 |
return json.dumps(runtime_setup_core())
|
| 594 |
|
| 595 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 596 |
@app.api(name="speak")
|
| 597 |
def speak_api(text: str, voice: str = "af_heart", speed: float = 1.0) -> str:
|
| 598 |
return json.dumps(speak_core(text, voice, speed))
|
|
|
|
| 201 |
}
|
| 202 |
|
| 203 |
|
| 204 |
+
def demo_script_core() -> dict[str, Any]:
|
| 205 |
+
return {
|
| 206 |
+
"ok": True,
|
| 207 |
+
"title": "Tiny Narrator judge demo",
|
| 208 |
+
"goal": "Show a custom article app becoming a small-model screen reader with inspectable evidence.",
|
| 209 |
+
"estimated_minutes": 3,
|
| 210 |
+
"actions": [
|
| 211 |
+
{
|
| 212 |
+
"step": 1,
|
| 213 |
+
"label": "Open the article",
|
| 214 |
+
"action": "Load the home page and scan the article plus session panel.",
|
| 215 |
+
"evidence": "Custom HTML/CSS/JS interface served by Gradio Server.",
|
| 216 |
+
},
|
| 217 |
+
{
|
| 218 |
+
"step": 2,
|
| 219 |
+
"label": "Turn on reader mode",
|
| 220 |
+
"action": "Toggle screen reader mode, then press Space or Next to narrate the first item.",
|
| 221 |
+
"evidence": "The active article node is focused, outlined, narrated, and logged.",
|
| 222 |
+
},
|
| 223 |
+
{
|
| 224 |
+
"step": 3,
|
| 225 |
+
"label": "Navigate by meaning",
|
| 226 |
+
"action": "Use Heading, Image, and Summary controls to jump through semantic article nodes.",
|
| 227 |
+
"evidence": "The reader uses article structure, image alt text, and section summaries.",
|
| 228 |
+
},
|
| 229 |
+
{
|
| 230 |
+
"step": 4,
|
| 231 |
+
"label": "Inspect small-model receipts",
|
| 232 |
+
"action": "Review the checklist, model budget, runtime plan, readiness, latency, and transcript panels.",
|
| 233 |
+
"evidence": "Tiny Titan, Llama Champion, Off-Brand, and Field Notes evidence is visible.",
|
| 234 |
+
},
|
| 235 |
+
],
|
| 236 |
+
"api_checks": [
|
| 237 |
+
{"method": "GET", "path": "/api/health", "expect": "custom Gradio Server app and model manifest"},
|
| 238 |
+
{"method": "GET", "path": "/api/model-budget", "expect": "all_models_within_limit is true"},
|
| 239 |
+
{"method": "GET", "path": "/api/runtime-setup", "expect": "llama.cpp, Kokoro, vision, and image paths"},
|
| 240 |
+
{"method": "GET", "path": "/api/runtime-status", "expect": "online or fallback-ready runtime labels"},
|
| 241 |
+
{"method": "GET", "path": "/api/image-descriptions", "expect": "three article image descriptions"},
|
| 242 |
+
{"method": "POST", "path": "/api/reader-brain", "expect": "concise narration or fallback narration"},
|
| 243 |
+
{"method": "POST", "path": "/api/speak", "expect": "Kokoro audio or audible browser fallback path"},
|
| 244 |
+
],
|
| 245 |
+
}
|
| 246 |
+
|
| 247 |
+
|
| 248 |
ARTICLE_MANIFEST: dict[str, Any] = {
|
| 249 |
"title": "A tiny model reader that turns articles into guided narration",
|
| 250 |
"reader_controls": [
|
|
|
|
| 262 |
"models": MODEL_MANIFEST,
|
| 263 |
"model_budget": model_budget_core(),
|
| 264 |
"runtime_setup": runtime_setup_core(),
|
| 265 |
+
"demo_script": demo_script_core(),
|
| 266 |
"reader_settings": READER_SETTINGS,
|
| 267 |
"award_evidence": AWARD_EVIDENCE,
|
| 268 |
}
|
|
|
|
| 583 |
return _json(runtime_setup_core())
|
| 584 |
|
| 585 |
|
| 586 |
+
@app.get("/api/demo-script")
|
| 587 |
+
async def demo_script() -> JSONResponse:
|
| 588 |
+
return _json(demo_script_core())
|
| 589 |
+
|
| 590 |
+
|
| 591 |
@app.get("/api/runtime-status")
|
| 592 |
async def runtime_status() -> JSONResponse:
|
| 593 |
return _json(_runtime_status_core())
|
|
|
|
| 643 |
return json.dumps(runtime_setup_core())
|
| 644 |
|
| 645 |
|
| 646 |
+
@app.api(name="demo_script")
|
| 647 |
+
def demo_script_api() -> str:
|
| 648 |
+
return json.dumps(demo_script_core())
|
| 649 |
+
|
| 650 |
+
|
| 651 |
@app.api(name="speak")
|
| 652 |
def speak_api(text: str, voice: str = "af_heart", speed: float = 1.0) -> str:
|
| 653 |
return json.dumps(speak_core(text, voice, speed))
|
scripts/verify.py
CHANGED
|
@@ -149,6 +149,10 @@ def verify_routes() -> None:
|
|
| 149 |
len(manifest_payload["runtime_setup"]["steps"]) == 4,
|
| 150 |
"Manifest should expose setup steps for each model role",
|
| 151 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
|
| 153 |
awards = client.get("/api/award-evidence")
|
| 154 |
assert_true(awards.status_code == 200, "Award evidence route should return 200")
|
|
@@ -184,6 +188,23 @@ def verify_routes() -> None:
|
|
| 184 |
"Runtime setup should include the llama.cpp launch command",
|
| 185 |
)
|
| 186 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 187 |
runtime = client.get("/api/runtime-status")
|
| 188 |
assert_true(runtime.status_code == 200, "Runtime status route should return 200")
|
| 189 |
runtime_payload = runtime.json()
|
|
|
|
| 149 |
len(manifest_payload["runtime_setup"]["steps"]) == 4,
|
| 150 |
"Manifest should expose setup steps for each model role",
|
| 151 |
)
|
| 152 |
+
assert_true(
|
| 153 |
+
len(manifest_payload["demo_script"]["actions"]) >= 4,
|
| 154 |
+
"Manifest should expose a judge demo script",
|
| 155 |
+
)
|
| 156 |
|
| 157 |
awards = client.get("/api/award-evidence")
|
| 158 |
assert_true(awards.status_code == 200, "Award evidence route should return 200")
|
|
|
|
| 188 |
"Runtime setup should include the llama.cpp launch command",
|
| 189 |
)
|
| 190 |
|
| 191 |
+
demo = client.get("/api/demo-script")
|
| 192 |
+
assert_true(demo.status_code == 200, "Demo script route should return 200")
|
| 193 |
+
demo_payload = demo.json()
|
| 194 |
+
assert_true(demo_payload["ok"], "Demo script payload should be ok")
|
| 195 |
+
assert_true(
|
| 196 |
+
"Tiny Narrator judge demo" == demo_payload["title"],
|
| 197 |
+
"Demo script should be labeled for judging",
|
| 198 |
+
)
|
| 199 |
+
assert_true(
|
| 200 |
+
{item["path"] for item in demo_payload["api_checks"]} >= {"/api/model-budget", "/api/runtime-setup"},
|
| 201 |
+
"Demo script should include evidence API checks",
|
| 202 |
+
)
|
| 203 |
+
assert_true(
|
| 204 |
+
any("Tiny Titan" in action["evidence"] for action in demo_payload["actions"]),
|
| 205 |
+
"Demo script should point to targeted award evidence",
|
| 206 |
+
)
|
| 207 |
+
|
| 208 |
runtime = client.get("/api/runtime-status")
|
| 209 |
assert_true(runtime.status_code == 200, "Runtime status route should return 200")
|
| 210 |
runtime_payload = runtime.json()
|