Skip to content

Latest commit

 

History

History
221 lines (170 loc) · 10.7 KB

File metadata and controls

221 lines (170 loc) · 10.7 KB

Python Tutor

A private, offline Python tutor that runs entirely on your own machine.

Lessons, an interactive code lab, and an AI mentor — powered by a local LLM (Gemma via Ollama). No accounts, no cloud, no telemetry. Open a browser, learn Python, write code, get feedback. Your code and your questions never leave the laptop.

┌─────────────────────────────────────────────────────────┐
│  Read a lesson  →  Run code in the lab  →  Ask tutor    │
│                       (all local, all offline)          │
└─────────────────────────────────────────────────────────┘

Why it exists

For… It gives you
Self-learners A guided Python curriculum with a chat tutor on demand.
Educators A drop-in lab where students run code and get evidence-based hints.
Privacy-minded teams A tutor that works on an air-gapped laptop — nothing phones home.
Tinkerers A clean FastAPI + static-PWA stack to remix and extend.

What you get

Feature What it does
🧠 Local LLM tutor Chat with a model running on your machine (default: gemma3:4b via Ollama).
📓 Lesson library A PWA you can install, with a Python-foundations curriculum.
🧪 Inline code lab Edit → RunEvaluate. The tutor sees the real output, not a guess.
Graded exercises Visible + hidden test cases, per-assertion pass/fail.
📚 Official docs links Answers cite docs.python.org and friends from a curated allowlist — no hallucinated URLs.
🛡 Prototype-grade safety Static AST scan, isolated subprocess, timeouts, rlimits, scrubbed env.
🔌 Works offline UI runs without the LLM; only chat/evaluate need Ollama up.

How it works

flowchart LR
    Student((You)) -->|reads, edits, asks| UI[PWA frontend]
    UI -->|/api/run| Sandbox[Python sandbox<br/>subprocess + AST scan]
    UI -->|/api/evaluate| Evidence[Evidence packet<br/>code + output + docs]
    UI -->|/api/chat| Chat[Chat]
    Evidence --> LLM[Local LLM<br/>Ollama / Gemma]
    Chat --> LLM
    LLM --> UI
    Sandbox --> UI
Loading

The teaching loop:

flowchart LR
    A[Read lesson] --> B[Edit code]
    B --> C[Run]
    C --> D{Worked?}
    D -- yes --> E[Reflect / next lesson]
    D -- no --> F[Evaluate]
    F --> G[Hint-first feedback<br/>+ docs link]
    G --> B
Loading

The LLM teaches, explains, and guides. The runtime verifies — the tutor never claims code works without running it.


Quick start

Two commands. macOS or Linux. Python 3.10+.

gh repo clone StewAlexander-com/python-tutor
cd python-tutor
./install.sh        # sets up venv, then prompts y/N for any host-level step
./run.sh            # serves UI + API at http://localhost:8001/

Open http://localhost:8001/ — you'll land on the lesson list with the code lab and floating "Ask tutor" panel.

install.sh only touches the repo on its own. Installing Ollama, starting the daemon, pulling the model, or launching the app are all opt-in y/N prompts. Press Enter and nothing changes on your host.

Run ./install.sh --help or ./run.sh --help for every option. The most common shapes:

./install.sh --yes               # trusted host: install Ollama, pull model, launch
./install.sh --noninteractive    # CI: never prompt, default everything to "no"
./install.sh --skip-ollama       # set up Python only; skip every Ollama probe
./install.sh --model llama3.1:8b # use a different model than gemma3:4b
./run.sh --port 8042             # choose a different port
./run.sh --open-browser          # open the URL once /api/health is green

The classic env vars (TUTOR_NONINTERACTIVE, PYTHON_TUTOR_ASSUME_YES, TUTOR_SKIP_OLLAMA, TUTOR_MODEL, TUTOR_PORT, …) still work — the flags are sugar on top of them.

Full env-var list and design rationale: docs/install-runtime-workflow.md.


Install reliability

install.sh and run.sh are designed so the obvious failures fail loudly with a concrete next step. The most common ones:

Symptom What to do
"Python 3.10+ is required and was not found" brew install python@3.12 / apt install python3.12 and re-run.
pip install fails on DNS / proxy / pypi The script detects this and prints offline/proxy/wheelhouse recipes. See install-audit.md.
"Port 8001 is already in use" ./run.sh --port 8002 (probe uses /dev/tcp, no lsof needed).
Ollama installed but daemon down on :11434 Answer y to "Start ollama serve now?" or run it yourself in another Terminal.
gh repo clone fails with auth error gh auth statusgh auth login. Public clone via HTTPS also works.
Repo was moved after install -> "venv broken" The script auto-rebuilds. Virtualenvs hard-code their own path; relocating is unsupported by Python itself.

Detailed runbook and the audit that produced these mitigations: docs/install-audit.md.


Architecture at a glance

┌──────────────────────────┐      ┌──────────────────────────┐
│  frontend/  (static PWA) │◀────▶│  backend/  (FastAPI)     │
│  lesson list • code lab  │      │  /api/run /api/evaluate  │
│  floating chat FAB       │      │  /api/chat /api/exercises│
└──────────────────────────┘      └──────────┬───────────────┘
                                             │
                                             ▼
                                  ┌──────────────────────────┐
                                  │  Ollama (local LLM)      │
                                  │  default: gemma3:4b      │
                                  └──────────────────────────┘
Layer Where it lives Read more
Frontend (PWA) frontend/ frontend/README.md
Backend (FastAPI) backend/ backend/README.md
Curriculum & exercises curriculum/ curriculum/exercises/README.md
Sandbox & safety backend/app/safety.py docs/safety-and-sandboxing.md
Architecture docs/architecture.md
UX workflow docs/ux-workflow.md

A word on safety

The sandbox is prototype safety, not production isolation. It is stronger than a bare subprocess.run, but a local single-user tutor is its design target — not a multi-tenant code execution service.

In force Not in force
Static AST scan (rejects subprocess, socket, ctypes, pickle, os.system, exec, eval, __import__, …) Kernel-level isolation
Isolated python -I -B subprocess, scrubbed env Defense against side-channel attacks
Per-call tempdir at 0o700, removed after run macOS RLIMIT_AS (Python ignores it)
Wall-clock timeout + process-group kill Windows POSIX rlimits
POSIX rlimits: CPU, memory, file size, nproc
Output truncation + code-size cap

For multi-tenant or hostile workloads, wrap the runner in a container, a microVM, or a restricted user. Details and threat model: docs/safety-and-sandboxing.md.


Documentation citations

The tutor only cites official Python docs from a curated allowlistdocs.python.org, peps.python.org, packaging.python.org, plus the official sites for NumPy, pandas, Matplotlib, SciPy, Flask, FastAPI, Django, Requests, HTTPX, SQLAlchemy, pytest, and mypy. URLs are never generated by the LLM: they come from an in-repo map (backend/app/docs_refs.py) or exercise-supplied references. When online, each link is HEAD-checked before display; unreachable links are dropped or flagged "unverified".


CI

GitHub Actions runs on every push and pull request: backend tests, a static safety scan over the curriculum, and a Markdown link sanity check. See .github/workflows/ci.yml.


Going deeper


Credits

The static PWA frontend was adapted from Python Power User (MIT).