cvpfus Codex commited on
Commit
57908c8
·
1 Parent(s): e898b4a

Add judge demo script endpoint

Browse files

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

Files changed (4) hide show
  1. FIELD_NOTES.md +2 -0
  2. README.md +3 -1
  3. app.py +55 -0
  4. 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`, and `/api/runtime-setup`.
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()