cvpfus Codex commited on
Commit ·
e898b4a
1
Parent(s): 79c2c78
Expose runtime setup evidence
Browse filesCo-authored-by: Codex <codex@openai.com>
- FIELD_NOTES.md +2 -0
- README.md +5 -1
- app.py +66 -0
- scripts/verify.py +20 -0
- static/app.css +31 -5
- static/app.js +32 -0
- static/index.html +5 -0
FIELD_NOTES.md
CHANGED
|
@@ -26,6 +26,8 @@ Tiny Narrator is a small-model accessibility article reader. It is not a generic
|
|
| 26 |
|
| 27 |
The app exposes the same budget through `/api/model-budget`, including numeric `params_billion` values and a boolean `all_models_within_limit` check. The session panel renders that payload so the Tiny Titan claim is visible during the demo.
|
| 28 |
|
|
|
|
|
|
|
| 29 |
## Reader Mode Behavior
|
| 30 |
|
| 31 |
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.
|
|
|
|
| 26 |
|
| 27 |
The app exposes the same budget through `/api/model-budget`, including numeric `params_billion` values and a boolean `all_models_within_limit` check. The session panel renders that payload so the Tiny Titan claim is visible during the demo.
|
| 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.
|
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`,
|
| 54 |
|
| 55 |
Useful environment variables:
|
| 56 |
|
|
@@ -71,6 +71,8 @@ The verifier checks syntax, static assets, deterministic fallback model paths, a
|
|
| 71 |
|
| 72 |
`/api/model-budget` exposes numeric parameter counts for every model role and reports whether the full stack stays within the 4B Tiny Titan limit.
|
| 73 |
|
|
|
|
|
|
|
| 74 |
## Screen Reader Mode
|
| 75 |
|
| 76 |
The frontend builds a reading queue from semantic article nodes. When screen-reader mode is on:
|
|
@@ -102,4 +104,6 @@ Reader-brain, image-description, speech, and image-generation responses include
|
|
| 102 |
|
| 103 |
The session panel also renders a manifest-backed demo checklist for Tiny Titan, Llama Champion, Off-Brand, and Field Notes evidence. A model-budget panel reads `/api/model-budget` so judges can see each role's parameter count in the live app.
|
| 104 |
|
|
|
|
|
|
|
| 105 |
`/api/runtime-status` performs a short readiness check for llama.cpp and local speech dependencies, then reports which fallback paths are ready for a live demo.
|
|
|
|
| 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 |
|
|
|
|
| 71 |
|
| 72 |
`/api/model-budget` exposes numeric parameter counts for every model role and reports whether the full stack stays within the 4B Tiny Titan limit.
|
| 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:
|
|
|
|
| 104 |
|
| 105 |
The session panel also renders a manifest-backed demo checklist for Tiny Titan, Llama Champion, Off-Brand, and Field Notes evidence. A model-budget panel reads `/api/model-budget` so judges can see each role's parameter count in the live app.
|
| 106 |
|
| 107 |
+
A runtime-plan panel reads `/api/runtime-setup` and summarizes each model path's runtime plus fallback, keeping the live demo honest about what is online and what is deterministic.
|
| 108 |
+
|
| 109 |
`/api/runtime-status` performs a short readiness check for llama.cpp and local speech dependencies, then reports which fallback paths are ready for a live demo.
|
app.py
CHANGED
|
@@ -146,6 +146,61 @@ def model_budget_core() -> dict[str, Any]:
|
|
| 146 |
}
|
| 147 |
|
| 148 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
ARTICLE_MANIFEST: dict[str, Any] = {
|
| 150 |
"title": "A tiny model reader that turns articles into guided narration",
|
| 151 |
"reader_controls": [
|
|
@@ -162,6 +217,7 @@ ARTICLE_MANIFEST: dict[str, Any] = {
|
|
| 162 |
"images": ARTICLE_IMAGES,
|
| 163 |
"models": MODEL_MANIFEST,
|
| 164 |
"model_budget": model_budget_core(),
|
|
|
|
| 165 |
"reader_settings": READER_SETTINGS,
|
| 166 |
"award_evidence": AWARD_EVIDENCE,
|
| 167 |
}
|
|
@@ -477,6 +533,11 @@ async def model_budget() -> JSONResponse:
|
|
| 477 |
return _json(model_budget_core())
|
| 478 |
|
| 479 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 480 |
@app.get("/api/runtime-status")
|
| 481 |
async def runtime_status() -> JSONResponse:
|
| 482 |
return _json(_runtime_status_core())
|
|
@@ -527,6 +588,11 @@ def model_budget_api() -> str:
|
|
| 527 |
return json.dumps(model_budget_core())
|
| 528 |
|
| 529 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 530 |
@app.api(name="speak")
|
| 531 |
def speak_api(text: str, voice: str = "af_heart", speed: float = 1.0) -> str:
|
| 532 |
return json.dumps(speak_core(text, voice, speed))
|
|
|
|
| 146 |
}
|
| 147 |
|
| 148 |
|
| 149 |
+
def runtime_setup_core() -> dict[str, Any]:
|
| 150 |
+
return {
|
| 151 |
+
"ok": True,
|
| 152 |
+
"app": {
|
| 153 |
+
"runtime": "Gradio Server",
|
| 154 |
+
"command": "python app.py",
|
| 155 |
+
"env": {
|
| 156 |
+
"GRADIO_SERVER_NAME": GRADIO_SERVER_NAME,
|
| 157 |
+
"GRADIO_SERVER_PORT": str(GRADIO_SERVER_PORT),
|
| 158 |
+
},
|
| 159 |
+
},
|
| 160 |
+
"steps": [
|
| 161 |
+
{
|
| 162 |
+
"role": "reader_brain",
|
| 163 |
+
"label": "Reader brain",
|
| 164 |
+
"model": MODEL_MANIFEST["reader_brain"]["id"],
|
| 165 |
+
"runtime": "llama.cpp",
|
| 166 |
+
"command": (
|
| 167 |
+
"llama-server -hf nvidia/NVIDIA-Nemotron-3-Nano-4B-GGUF:Q4_K_M "
|
| 168 |
+
"--alias narrator-brain --port 8080 --host 0.0.0.0"
|
| 169 |
+
),
|
| 170 |
+
"env": {"LLAMA_CPP_BASE_URL": LLAMA_CPP_BASE_URL, "LLAMA_CPP_MODEL": LLAMA_CPP_MODEL},
|
| 171 |
+
"fallback": "rule-based local narration",
|
| 172 |
+
},
|
| 173 |
+
{
|
| 174 |
+
"role": "speech",
|
| 175 |
+
"label": "Speech",
|
| 176 |
+
"model": MODEL_MANIFEST["speech"]["id"],
|
| 177 |
+
"runtime": "Python Kokoro",
|
| 178 |
+
"command": "python -m pip install kokoro soundfile",
|
| 179 |
+
"env": {},
|
| 180 |
+
"fallback": "browser speech plus visible transcript",
|
| 181 |
+
},
|
| 182 |
+
{
|
| 183 |
+
"role": "vision",
|
| 184 |
+
"label": "Image descriptions",
|
| 185 |
+
"model": MODEL_MANIFEST["vision"]["id"],
|
| 186 |
+
"runtime": "Python integration planned",
|
| 187 |
+
"command": "planned MiniCPM-V adapter",
|
| 188 |
+
"env": {},
|
| 189 |
+
"fallback": "cached deterministic alt text",
|
| 190 |
+
},
|
| 191 |
+
{
|
| 192 |
+
"role": "image_generation",
|
| 193 |
+
"label": "Article images",
|
| 194 |
+
"model": MODEL_MANIFEST["image_generation"]["id"],
|
| 195 |
+
"runtime": "Python integration planned",
|
| 196 |
+
"command": "planned FLUX.2 klein adapter",
|
| 197 |
+
"env": {},
|
| 198 |
+
"fallback": "bundled generated article assets",
|
| 199 |
+
},
|
| 200 |
+
],
|
| 201 |
+
}
|
| 202 |
+
|
| 203 |
+
|
| 204 |
ARTICLE_MANIFEST: dict[str, Any] = {
|
| 205 |
"title": "A tiny model reader that turns articles into guided narration",
|
| 206 |
"reader_controls": [
|
|
|
|
| 217 |
"images": ARTICLE_IMAGES,
|
| 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 |
}
|
|
|
|
| 533 |
return _json(model_budget_core())
|
| 534 |
|
| 535 |
|
| 536 |
+
@app.get("/api/runtime-setup")
|
| 537 |
+
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())
|
|
|
|
| 588 |
return json.dumps(model_budget_core())
|
| 589 |
|
| 590 |
|
| 591 |
+
@app.api(name="runtime_setup")
|
| 592 |
+
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))
|
scripts/verify.py
CHANGED
|
@@ -101,6 +101,8 @@ def verify_routes() -> None:
|
|
| 101 |
assert_true("awardEvidenceList" in home.text, "Home route should include award evidence list")
|
| 102 |
assert_true("budgetStatus" in home.text, "Home route should include model budget status")
|
| 103 |
assert_true("modelBudgetList" in home.text, "Home route should include model budget list")
|
|
|
|
|
|
|
| 104 |
|
| 105 |
health = client.get("/api/health")
|
| 106 |
assert_true(health.status_code == 200, "Health route should return 200")
|
|
@@ -143,6 +145,10 @@ def verify_routes() -> None:
|
|
| 143 |
manifest_payload["model_budget"]["all_models_within_limit"],
|
| 144 |
"Manifest should prove every model is within the Tiny Titan limit",
|
| 145 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 146 |
|
| 147 |
awards = client.get("/api/award-evidence")
|
| 148 |
assert_true(awards.status_code == 200, "Award evidence route should return 200")
|
|
@@ -164,6 +170,20 @@ def verify_routes() -> None:
|
|
| 164 |
"Every model budget item should be at or below the limit",
|
| 165 |
)
|
| 166 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 167 |
runtime = client.get("/api/runtime-status")
|
| 168 |
assert_true(runtime.status_code == 200, "Runtime status route should return 200")
|
| 169 |
runtime_payload = runtime.json()
|
|
|
|
| 101 |
assert_true("awardEvidenceList" in home.text, "Home route should include award evidence list")
|
| 102 |
assert_true("budgetStatus" in home.text, "Home route should include model budget status")
|
| 103 |
assert_true("modelBudgetList" in home.text, "Home route should include model budget list")
|
| 104 |
+
assert_true("runtimeSetupStatus" in home.text, "Home route should include runtime setup status")
|
| 105 |
+
assert_true("runtimeSetupList" in home.text, "Home route should include runtime setup list")
|
| 106 |
|
| 107 |
health = client.get("/api/health")
|
| 108 |
assert_true(health.status_code == 200, "Health route should return 200")
|
|
|
|
| 145 |
manifest_payload["model_budget"]["all_models_within_limit"],
|
| 146 |
"Manifest should prove every model is within the Tiny Titan limit",
|
| 147 |
)
|
| 148 |
+
assert_true(
|
| 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")
|
|
|
|
| 170 |
"Every model budget item should be at or below the limit",
|
| 171 |
)
|
| 172 |
|
| 173 |
+
setup = client.get("/api/runtime-setup")
|
| 174 |
+
assert_true(setup.status_code == 200, "Runtime setup route should return 200")
|
| 175 |
+
setup_payload = setup.json()
|
| 176 |
+
assert_true(setup_payload["ok"], "Runtime setup payload should be ok")
|
| 177 |
+
assert_true(
|
| 178 |
+
{item["role"] for item in setup_payload["steps"]}
|
| 179 |
+
== {"reader_brain", "speech", "vision", "image_generation"},
|
| 180 |
+
"Runtime setup should cover every model path",
|
| 181 |
+
)
|
| 182 |
+
assert_true(
|
| 183 |
+
"llama-server" in setup_payload["steps"][0]["command"],
|
| 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()
|
static/app.css
CHANGED
|
@@ -298,7 +298,16 @@ dd {
|
|
| 298 |
margin-top: 22px;
|
| 299 |
}
|
| 300 |
|
| 301 |
-
.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 302 |
color: var(--accent-strong);
|
| 303 |
font-size: 0.76rem;
|
| 304 |
font-weight: 850;
|
|
@@ -369,14 +378,24 @@ dd {
|
|
| 369 |
list-style: none;
|
| 370 |
}
|
| 371 |
|
| 372 |
-
.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 373 |
border: 1px solid var(--line);
|
| 374 |
border-radius: 8px;
|
| 375 |
padding: 10px 12px;
|
| 376 |
background: var(--surface);
|
| 377 |
}
|
| 378 |
|
| 379 |
-
.budget-row
|
|
|
|
| 380 |
display: flex;
|
| 381 |
align-items: center;
|
| 382 |
justify-content: space-between;
|
|
@@ -386,7 +405,8 @@ dd {
|
|
| 386 |
text-transform: uppercase;
|
| 387 |
}
|
| 388 |
|
| 389 |
-
.budget-pill
|
|
|
|
| 390 |
border-radius: 999px;
|
| 391 |
background: rgba(197, 69, 44, 0.12);
|
| 392 |
color: var(--focus);
|
|
@@ -394,7 +414,13 @@ dd {
|
|
| 394 |
font-size: 0.7rem;
|
| 395 |
}
|
| 396 |
|
| 397 |
-
.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 398 |
margin: 6px 0 0;
|
| 399 |
color: var(--muted);
|
| 400 |
font-size: 0.78rem;
|
|
|
|
| 298 |
margin-top: 22px;
|
| 299 |
}
|
| 300 |
|
| 301 |
+
.runtime-header {
|
| 302 |
+
display: flex;
|
| 303 |
+
align-items: center;
|
| 304 |
+
justify-content: space-between;
|
| 305 |
+
gap: 10px;
|
| 306 |
+
margin-top: 22px;
|
| 307 |
+
}
|
| 308 |
+
|
| 309 |
+
.budget-header span,
|
| 310 |
+
.runtime-header span {
|
| 311 |
color: var(--accent-strong);
|
| 312 |
font-size: 0.76rem;
|
| 313 |
font-weight: 850;
|
|
|
|
| 378 |
list-style: none;
|
| 379 |
}
|
| 380 |
|
| 381 |
+
.runtime-list {
|
| 382 |
+
display: grid;
|
| 383 |
+
gap: 8px;
|
| 384 |
+
margin: 12px 0 0;
|
| 385 |
+
padding: 0;
|
| 386 |
+
list-style: none;
|
| 387 |
+
}
|
| 388 |
+
|
| 389 |
+
.budget-list li,
|
| 390 |
+
.runtime-list li {
|
| 391 |
border: 1px solid var(--line);
|
| 392 |
border-radius: 8px;
|
| 393 |
padding: 10px 12px;
|
| 394 |
background: var(--surface);
|
| 395 |
}
|
| 396 |
|
| 397 |
+
.budget-row,
|
| 398 |
+
.runtime-row {
|
| 399 |
display: flex;
|
| 400 |
align-items: center;
|
| 401 |
justify-content: space-between;
|
|
|
|
| 405 |
text-transform: uppercase;
|
| 406 |
}
|
| 407 |
|
| 408 |
+
.budget-pill,
|
| 409 |
+
.runtime-pill {
|
| 410 |
border-radius: 999px;
|
| 411 |
background: rgba(197, 69, 44, 0.12);
|
| 412 |
color: var(--focus);
|
|
|
|
| 414 |
font-size: 0.7rem;
|
| 415 |
}
|
| 416 |
|
| 417 |
+
.runtime-pill {
|
| 418 |
+
background: rgba(31, 111, 104, 0.12);
|
| 419 |
+
color: var(--accent-strong);
|
| 420 |
+
}
|
| 421 |
+
|
| 422 |
+
.budget-list p,
|
| 423 |
+
.runtime-list p {
|
| 424 |
margin: 6px 0 0;
|
| 425 |
color: var(--muted);
|
| 426 |
font-size: 0.78rem;
|
static/app.js
CHANGED
|
@@ -20,6 +20,8 @@ const clearTranscriptButton = document.querySelector("#clearTranscriptButton");
|
|
| 20 |
const awardEvidenceList = document.querySelector("#awardEvidenceList");
|
| 21 |
const budgetStatus = document.querySelector("#budgetStatus");
|
| 22 |
const modelBudgetList = document.querySelector("#modelBudgetList");
|
|
|
|
|
|
|
| 23 |
|
| 24 |
const controls = {
|
| 25 |
prev: document.querySelector("#prevButton"),
|
|
@@ -196,6 +198,35 @@ async function loadModelBudget() {
|
|
| 196 |
}
|
| 197 |
}
|
| 198 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 199 |
async function loadRuntimeStatus() {
|
| 200 |
try {
|
| 201 |
const payload = await postJson("/api/runtime-status");
|
|
@@ -518,6 +549,7 @@ controls.play.addEventListener("click", () => {
|
|
| 518 |
loadManifest();
|
| 519 |
loadAwardEvidence();
|
| 520 |
loadModelBudget();
|
|
|
|
| 521 |
loadRuntimeStatus();
|
| 522 |
loadImageDescriptions();
|
| 523 |
updateSpeedValue();
|
|
|
|
| 20 |
const awardEvidenceList = document.querySelector("#awardEvidenceList");
|
| 21 |
const budgetStatus = document.querySelector("#budgetStatus");
|
| 22 |
const modelBudgetList = document.querySelector("#modelBudgetList");
|
| 23 |
+
const runtimeSetupStatus = document.querySelector("#runtimeSetupStatus");
|
| 24 |
+
const runtimeSetupList = document.querySelector("#runtimeSetupList");
|
| 25 |
|
| 26 |
const controls = {
|
| 27 |
prev: document.querySelector("#prevButton"),
|
|
|
|
| 198 |
}
|
| 199 |
}
|
| 200 |
|
| 201 |
+
async function loadRuntimeSetup() {
|
| 202 |
+
try {
|
| 203 |
+
const payload = await postJson("/api/runtime-setup");
|
| 204 |
+
runtimeSetupStatus.textContent = `${payload.steps.length} paths`;
|
| 205 |
+
runtimeSetupList.innerHTML = payload.steps
|
| 206 |
+
.map((step) => `
|
| 207 |
+
<li>
|
| 208 |
+
<div class="runtime-row">
|
| 209 |
+
<span>${escapeHtml(step.label)}</span>
|
| 210 |
+
<span class="runtime-pill">${escapeHtml(step.runtime)}</span>
|
| 211 |
+
</div>
|
| 212 |
+
<p>${escapeHtml(step.fallback)}</p>
|
| 213 |
+
</li>
|
| 214 |
+
`)
|
| 215 |
+
.join("");
|
| 216 |
+
} catch {
|
| 217 |
+
runtimeSetupStatus.textContent = "Unavailable";
|
| 218 |
+
runtimeSetupList.innerHTML = `
|
| 219 |
+
<li>
|
| 220 |
+
<div class="runtime-row">
|
| 221 |
+
<span>Runtime plan</span>
|
| 222 |
+
<span class="runtime-pill">offline</span>
|
| 223 |
+
</div>
|
| 224 |
+
<p>Setup evidence is unavailable.</p>
|
| 225 |
+
</li>
|
| 226 |
+
`;
|
| 227 |
+
}
|
| 228 |
+
}
|
| 229 |
+
|
| 230 |
async function loadRuntimeStatus() {
|
| 231 |
try {
|
| 232 |
const payload = await postJson("/api/runtime-status");
|
|
|
|
| 549 |
loadManifest();
|
| 550 |
loadAwardEvidence();
|
| 551 |
loadModelBudget();
|
| 552 |
+
loadRuntimeSetup();
|
| 553 |
loadRuntimeStatus();
|
| 554 |
loadImageDescriptions();
|
| 555 |
updateSpeedValue();
|
static/index.html
CHANGED
|
@@ -131,6 +131,11 @@
|
|
| 131 |
<span id="budgetStatus">Checking</span>
|
| 132 |
</div>
|
| 133 |
<ul id="modelBudgetList" class="budget-list" aria-label="Tiny Titan model budget"></ul>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
</aside>
|
| 135 |
</main>
|
| 136 |
|
|
|
|
| 131 |
<span id="budgetStatus">Checking</span>
|
| 132 |
</div>
|
| 133 |
<ul id="modelBudgetList" class="budget-list" aria-label="Tiny Titan model budget"></ul>
|
| 134 |
+
<div class="runtime-header">
|
| 135 |
+
<h3>Runtime Plan</h3>
|
| 136 |
+
<span id="runtimeSetupStatus">Loading</span>
|
| 137 |
+
</div>
|
| 138 |
+
<ul id="runtimeSetupList" class="runtime-list" aria-label="Model runtime setup plan"></ul>
|
| 139 |
</aside>
|
| 140 |
</main>
|
| 141 |
|