plane-mode-scholar / scripts /demo_autopilot.py
PMS Sync
Deploy 000b308 (flattened for HF Spaces)
4577459
Raw
History Blame Contribute Delete
6.16 kB
"""Terminal demo of the Plane Mode Scholar autopilot — fine-tuned Nemotron coach.
Records a real, local FLY autopilot run (no network, no mocks) so the agent
chain and grounded coach output can be captured as a submittable demo.
PMS_GGUF_DIR=models/nemotron python scripts/demo_autopilot.py
"""
from __future__ import annotations
import json
import os
import sys
import tempfile
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
os.environ.setdefault("PMS_INFERENCE_BACKEND", "llamacpp")
os.environ.setdefault("PMS_LLAMACPP_MODE", "embedded")
os.environ.setdefault("PMS_USE_FINETUNED", "true")
os.environ.setdefault("PMS_GGUF_DIR", str(ROOT / "models" / "nemotron"))
os.environ.setdefault("PMS_LLAMACPP_N_GPU_LAYERS", "0")
os.environ.setdefault("PMS_MAX_NEW_TOKENS", "320")
C = {
"reset": "\033[0m",
"dim": "\033[2m",
"bold": "\033[1m",
"teal": "\033[38;5;43m",
"copper": "\033[38;5;173m",
"amber": "\033[38;5;214m",
"green": "\033[38;5;48m",
"blue": "\033[38;5;75m",
"purple": "\033[38;5;141m",
"red": "\033[38;5;203m",
"gray": "\033[38;5;245m",
}
PHASE_COLOR = {
"BOOT": "teal",
"PACK": "teal",
"SESSION": "blue",
"PLAN": "amber",
"RETRIEVE": "purple",
"EXPLAIN": "green",
"QUIZ": "blue",
"MEMORY": "copper",
"DONE": "teal",
"ERROR": "red",
}
DEMO_NOTES = """# Introduction to Machine Learning
## Chapter 1: Supervised Learning
Supervised learning trains a model on labeled input-output pairs so it can
predict the output for unseen inputs. Classification predicts discrete labels;
regression predicts continuous values.
## Chapter 2: Gradient Descent
Gradient descent minimizes a loss function by repeatedly stepping in the
direction of the negative gradient. The learning rate controls the step size:
too large overshoots the minimum, too small converges slowly.
## Chapter 3: Overfitting and Regularization
A model overfits when it memorizes training noise and fails to generalize.
Regularization (L1/L2) and dropout penalize complexity to improve test accuracy.
"""
def c(text: str, color: str) -> str:
return f"{C.get(color, '')}{text}{C['reset']}"
def slow_print(text: str, delay: float = 0.012) -> None:
for ch in text:
sys.stdout.write(ch)
sys.stdout.flush()
time.sleep(delay)
sys.stdout.write("\n")
sys.stdout.flush()
def banner() -> None:
line = "═" * 64
print(c(line, "teal"))
print(c(" ✈ PLANE MODE SCHOLAR // AUTOPILOT DEMO", "bold"))
print(c(" Fine-tuned NVIDIA Nemotron 3 Nano 4B · offline study coach", "gray"))
print(c(line, "teal"))
print()
def chain_step(phase: str, message: str) -> None:
color = PHASE_COLOR.get(phase, "gray")
tag = c(f"[{phase:<8}]", color)
print(f" {tag} {c(message, 'gray')}")
def main() -> None:
banner()
from plane_mode_scholar.core import llm
from plane_mode_scholar.core.study_agent import StudyAgent
from plane_mode_scholar.gradio_ui.layout import STATE, quick_upload_pack
user_id = "demo_judge"
tmp = Path(tempfile.mkdtemp()) / "intro_to_ml.md"
tmp.write_text(DEMO_NOTES, encoding="utf-8")
print(c("▸ Uploading study material: intro_to_ml.md", "copper"))
pack = quick_upload_pack([str(tmp)], user_id)
print(c(f" ✓ Indexed pack {pack.get('pack_id', '')[:24]}… "
f"({pack.get('file_count', 1)} file, 3 chapters)", "green"))
print()
print(c("▸ Backend:", "copper"),
c(f"{os.environ['PMS_INFERENCE_BACKEND']} (GGUF + study-coach LoRA)", "gray"))
print(c("▸ Tap FLY → autonomous monitor → plan → act loop", "copper"))
print()
print(c("─" * 64, "teal"))
print(c(" AGENT CHAIN", "amber"))
print(c("─" * 64, "teal"))
agent = StudyAgent(STATE.store, STATE.orchestrator)
coach_text = ""
quiz_shown = False
t_start = time.time()
for event in agent.run_autopilot(user_id=user_id, pack_id=pack["pack_id"], max_steps=4):
phase = event.get("phase", "")
msg = event.get("message", "")
data = event.get("data", {})
if phase == "STREAM":
continue
if phase == "EXPLAIN":
chain_step("EXPLAIN", f"Coach explanation ready ({data.get('inference_ms', 0)} ms)")
coach_text = data.get("response", msg)
print()
print(c(" ┌─ COACH ─────────────────────────────────────────────────", "green"))
for para in coach_text.split("\n"):
slow_print(c(" │ ", "green") + para, delay=0.004)
print(c(" └─────────────────────────────────────────────────────────", "green"))
print()
continue
if phase == "QUIZ" and not quiz_shown:
q = data.get("question", {})
chain_step("QUIZ", "Generated a comprehension check")
print()
print(c(" ┌─ QUIZ ──────────────────────────────────────────────────", "blue"))
print(c(" │ ", "blue") + c(q.get("question", msg), "bold"))
for i, opt in enumerate(q.get("options", [])):
print(c(" │ ", "blue") + f"{'ABCD'[i]}. {opt}")
print(c(" └─────────────────────────────────────────────────────────", "blue"))
print()
quiz_shown = True
continue
chain_step(phase, msg)
time.sleep(0.25)
elapsed = time.time() - t_start
print(c("─" * 64, "teal"))
print(c(f" ✓ Autopilot complete in {elapsed:.1f}s — "
f"grounded, offline, fine-tuned.", "green"))
print(c("─" * 64, "teal"))
if __name__ == "__main__":
main()