Spaces:
Runtime error
Runtime error
Commit Β·
7e20750
1
Parent(s): b6f7301
deploy: Streamlit Space setup + button-triggered training
Browse files- README.md: switch SDK to streamlit, app_file β demo/streamlit_app.py
- requirements.txt: streamlit-compatible deps (drop CUDA index + gradio)
- app.py: training no longer auto-starts on boot; judges must click
'βΆ Start Training' button; orange warning banner explains that
starting a run will overwrite the current trained policy
- data/.gitkeep: ensures data/ dir exists on Space filesystem
- configs/training_config.yaml: spawn_threshold 0.40 (auto-spawn fix)
- env/spindleflow_env.py: re-enable _maybe_spawn_specialist in reset()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- README.md +3 -3
- app.py +54 -11
- data/.gitkeep +0 -0
- requirements.txt +5 -9
README.md
CHANGED
|
@@ -3,9 +3,9 @@ title: SpindleFlow RL
|
|
| 3 |
emoji: π€
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: purple
|
| 6 |
-
sdk:
|
| 7 |
-
sdk_version: "
|
| 8 |
-
app_file:
|
| 9 |
pinned: false
|
| 10 |
---
|
| 11 |
|
|
|
|
| 3 |
emoji: π€
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: purple
|
| 6 |
+
sdk: streamlit
|
| 7 |
+
sdk_version: "1.44.0"
|
| 8 |
+
app_file: demo/streamlit_app.py
|
| 9 |
pinned: false
|
| 10 |
---
|
| 11 |
|
app.py
CHANGED
|
@@ -532,32 +532,47 @@ model = RecurrentPPO.load(hf_hub_download("{HF_REPO}", "spindleflow_model.zip"))
|
|
| 532 |
_write_status("error", error=str(exc))
|
| 533 |
|
| 534 |
|
| 535 |
-
# ββ
|
| 536 |
-
|
| 537 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 538 |
|
| 539 |
|
| 540 |
# ββ Gradio UI βββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 541 |
def _get_state():
|
| 542 |
-
# Read from files β works across Gradio worker processes
|
| 543 |
try:
|
| 544 |
with open(_STATUS_FILE, "r", encoding="utf-8") as f:
|
| 545 |
s = json.load(f)
|
| 546 |
phase, done, error = s.get("phase", "starting"), s.get("done", False), s.get("error")
|
| 547 |
except Exception:
|
| 548 |
-
phase, done, error = "
|
| 549 |
|
| 550 |
try:
|
| 551 |
with open(_LOG_FILE, "r", encoding="utf-8") as f:
|
| 552 |
lines = f.readlines()
|
| 553 |
log_text = "".join(lines[-120:])
|
| 554 |
except Exception:
|
| 555 |
-
log_text = ""
|
| 556 |
|
| 557 |
if done:
|
| 558 |
label = "β
Training complete β model pushed to HF Hub"
|
| 559 |
elif error:
|
| 560 |
label = f"β Error: {error}"
|
|
|
|
|
|
|
| 561 |
else:
|
| 562 |
icons = {"starting": "β³", "training": "π", "saving": "πΎ", "uploading": "π€"}
|
| 563 |
label = f"{icons.get(phase, 'π')} {phase.capitalize()}..."
|
|
@@ -577,6 +592,9 @@ body, .gradio-container { background: #0f172a !important; }
|
|
| 577 |
background: #0f172a !important; color: #94a3b8 !important;
|
| 578 |
border: 1px solid #1e293b !important;
|
| 579 |
}
|
|
|
|
|
|
|
|
|
|
| 580 |
h1 { color: #f1f5f9 !important; }
|
| 581 |
p, label { color: #94a3b8 !important; }
|
| 582 |
footer { display: none !important; }
|
|
@@ -585,29 +603,54 @@ footer { display: none !important; }
|
|
| 585 |
with gr.Blocks(title="SpindleFlow RL Training", css=CSS) as demo:
|
| 586 |
gr.Markdown("# π€ SpindleFlow RL β Training Dashboard")
|
| 587 |
gr.Markdown(
|
| 588 |
-
"
|
| 589 |
-
"
|
|
|
|
| 590 |
)
|
| 591 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 592 |
with gr.Row():
|
| 593 |
status_box = gr.Textbox(
|
| 594 |
label="Status",
|
| 595 |
-
value="
|
| 596 |
interactive=False,
|
| 597 |
scale=4,
|
| 598 |
elem_classes="status-box",
|
| 599 |
)
|
| 600 |
-
refresh_btn = gr.Button("π Refresh
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 601 |
|
| 602 |
log_box = gr.Textbox(
|
| 603 |
label="Training log (last 120 lines)",
|
| 604 |
-
value="",
|
| 605 |
lines=28,
|
| 606 |
max_lines=40,
|
| 607 |
interactive=False,
|
| 608 |
elem_classes="log-box",
|
| 609 |
)
|
| 610 |
|
|
|
|
| 611 |
refresh_btn.click(fn=_get_state, outputs=[status_box, log_box])
|
| 612 |
demo.load(fn=_get_state, outputs=[status_box, log_box])
|
| 613 |
timer = gr.Timer(value=10)
|
|
|
|
| 532 |
_write_status("error", error=str(exc))
|
| 533 |
|
| 534 |
|
| 535 |
+
# ββ Training state (NOT auto-started β judge must click the button) ββββββββββ
|
| 536 |
+
_training_started = False
|
| 537 |
+
_training_lock = threading.Lock()
|
| 538 |
+
|
| 539 |
+
|
| 540 |
+
def _start_training_once():
|
| 541 |
+
"""Start the training thread exactly once; safe to call multiple times."""
|
| 542 |
+
global _training_started
|
| 543 |
+
with _training_lock:
|
| 544 |
+
if _training_started:
|
| 545 |
+
return "β οΈ Training is already running β see the log below."
|
| 546 |
+
_training_started = True
|
| 547 |
+
_write_status("starting")
|
| 548 |
+
_log("Training started by user request.")
|
| 549 |
+
t = threading.Thread(target=_training_thread, daemon=True)
|
| 550 |
+
t.start()
|
| 551 |
+
return "π Training started! Logs will appear below (auto-refresh every 10 s)."
|
| 552 |
|
| 553 |
|
| 554 |
# ββ Gradio UI βββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 555 |
def _get_state():
|
|
|
|
| 556 |
try:
|
| 557 |
with open(_STATUS_FILE, "r", encoding="utf-8") as f:
|
| 558 |
s = json.load(f)
|
| 559 |
phase, done, error = s.get("phase", "starting"), s.get("done", False), s.get("error")
|
| 560 |
except Exception:
|
| 561 |
+
phase, done, error = "idle", False, None
|
| 562 |
|
| 563 |
try:
|
| 564 |
with open(_LOG_FILE, "r", encoding="utf-8") as f:
|
| 565 |
lines = f.readlines()
|
| 566 |
log_text = "".join(lines[-120:])
|
| 567 |
except Exception:
|
| 568 |
+
log_text = "(no logs yet β click βΆ Start Training to begin)"
|
| 569 |
|
| 570 |
if done:
|
| 571 |
label = "β
Training complete β model pushed to HF Hub"
|
| 572 |
elif error:
|
| 573 |
label = f"β Error: {error}"
|
| 574 |
+
elif phase == "idle":
|
| 575 |
+
label = "π€ Idle β click βΆ Start Training below to begin a fresh run"
|
| 576 |
else:
|
| 577 |
icons = {"starting": "β³", "training": "π", "saving": "πΎ", "uploading": "π€"}
|
| 578 |
label = f"{icons.get(phase, 'π')} {phase.capitalize()}..."
|
|
|
|
| 592 |
background: #0f172a !important; color: #94a3b8 !important;
|
| 593 |
border: 1px solid #1e293b !important;
|
| 594 |
}
|
| 595 |
+
.warning-box { background: #431407 !important; border: 1px solid #ea580c !important;
|
| 596 |
+
border-radius: 8px !important; padding: 12px !important; }
|
| 597 |
+
.warning-box p { color: #fed7aa !important; font-size: 0.9rem !important; }
|
| 598 |
h1 { color: #f1f5f9 !important; }
|
| 599 |
p, label { color: #94a3b8 !important; }
|
| 600 |
footer { display: none !important; }
|
|
|
|
| 603 |
with gr.Blocks(title="SpindleFlow RL Training", css=CSS) as demo:
|
| 604 |
gr.Markdown("# π€ SpindleFlow RL β Training Dashboard")
|
| 605 |
gr.Markdown(
|
| 606 |
+
"This Space runs the **RecurrentPPO (LSTM PPO)** training for SpindleFlow RL. "
|
| 607 |
+
"The trained model is pushed to HF Hub when training completes and is loaded "
|
| 608 |
+
"automatically by the [SpindleFlow Demo Space](https://huggingface.co/spaces/garvitsachdeva/spindleflow-demo)."
|
| 609 |
)
|
| 610 |
|
| 611 |
+
# ββ Warning banner ββββββββββββββββββββββββββββββββββββββββ
|
| 612 |
+
gr.HTML("""
|
| 613 |
+
<div class="warning-box" style="background:#431407;border:1px solid #ea580c;
|
| 614 |
+
border-radius:8px;padding:14px;margin-bottom:8px;">
|
| 615 |
+
<strong style="color:#fb923c;font-size:1rem;">β οΈ Warning β Read Before Starting</strong>
|
| 616 |
+
<p style="color:#fed7aa;margin:6px 0 0;font-size:0.88rem;">
|
| 617 |
+
Clicking <strong>βΆ Start Training</strong> will launch a full retraining run
|
| 618 |
+
(~30 k steps). When complete it <strong>overwrites</strong>
|
| 619 |
+
<code>spindleflow_model.zip</code> in the model repo with the new weights.
|
| 620 |
+
The live demo Space will then load this new model on its next cold start.
|
| 621 |
+
Only start a new run if you intentionally want to replace the current trained policy.
|
| 622 |
+
</p>
|
| 623 |
+
</div>
|
| 624 |
+
""")
|
| 625 |
+
|
| 626 |
with gr.Row():
|
| 627 |
status_box = gr.Textbox(
|
| 628 |
label="Status",
|
| 629 |
+
value="π€ Idle β click βΆ Start Training to begin",
|
| 630 |
interactive=False,
|
| 631 |
scale=4,
|
| 632 |
elem_classes="status-box",
|
| 633 |
)
|
| 634 |
+
refresh_btn = gr.Button("π Refresh", scale=1, variant="secondary")
|
| 635 |
+
|
| 636 |
+
with gr.Row():
|
| 637 |
+
start_btn = gr.Button("βΆ Start Training", scale=3, variant="primary")
|
| 638 |
+
gr.Markdown(
|
| 639 |
+
"<span style='color:#94a3b8;font-size:0.8rem;line-height:2.5'>"
|
| 640 |
+
"Requires HF_TOKEN + HF_MODEL_REPO secrets to be set in Space Settings.</span>",
|
| 641 |
+
scale=3,
|
| 642 |
+
)
|
| 643 |
|
| 644 |
log_box = gr.Textbox(
|
| 645 |
label="Training log (last 120 lines)",
|
| 646 |
+
value="(no logs yet β click βΆ Start Training to begin)",
|
| 647 |
lines=28,
|
| 648 |
max_lines=40,
|
| 649 |
interactive=False,
|
| 650 |
elem_classes="log-box",
|
| 651 |
)
|
| 652 |
|
| 653 |
+
start_btn.click(fn=_start_training_once, outputs=[status_box])
|
| 654 |
refresh_btn.click(fn=_get_state, outputs=[status_box, log_box])
|
| 655 |
demo.load(fn=_get_state, outputs=[status_box, log_box])
|
| 656 |
timer = gr.Timer(value=10)
|
data/.gitkeep
ADDED
|
File without changes
|
requirements.txt
CHANGED
|
@@ -1,20 +1,16 @@
|
|
| 1 |
-
--index-url https://download.pytorch.org/whl/cu121
|
| 2 |
-
--extra-index-url https://pypi.org/simple
|
| 3 |
-
torch
|
| 4 |
-
openenv>=0.1.0
|
| 5 |
stable-baselines3>=2.3.0
|
| 6 |
sb3-contrib>=2.3.0
|
| 7 |
gymnasium>=0.29.1
|
|
|
|
| 8 |
numpy>=1.26.0
|
| 9 |
sentence-transformers>=3.0.0
|
| 10 |
openai>=1.30.0
|
| 11 |
pyyaml>=6.0.1
|
| 12 |
transformers>=4.40.0
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
matplotlib>=3.8.0
|
| 18 |
-
audioop-lts>=0.2.1; python_version>="3.13"
|
| 19 |
click>=8.0.0
|
| 20 |
python-dotenv>=1.0.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
stable-baselines3>=2.3.0
|
| 2 |
sb3-contrib>=2.3.0
|
| 3 |
gymnasium>=0.29.1
|
| 4 |
+
torch>=2.2.0
|
| 5 |
numpy>=1.26.0
|
| 6 |
sentence-transformers>=3.0.0
|
| 7 |
openai>=1.30.0
|
| 8 |
pyyaml>=6.0.1
|
| 9 |
transformers>=4.40.0
|
| 10 |
+
huggingface_hub>=0.23.0
|
| 11 |
+
streamlit>=1.32.0
|
| 12 |
+
plotly>=5.20.0
|
| 13 |
+
scipy>=1.12.0
|
| 14 |
matplotlib>=3.8.0
|
|
|
|
| 15 |
click>=8.0.0
|
| 16 |
python-dotenv>=1.0.0
|