Next.js prototype for learning by teaching graph theory. A human student teaches a novice AI learner named DoDuck through a structured notebook; DoDuck may only treat that notebook as evidence.
- Pedagogy: learning-by-teaching — you explain concepts; DoDuck plays a 1st-year CS student (basic Python, graph theory in progress).
- Evidence rule: DoDuck must not use outside knowledge. Every claim is grounded in your notebook corpus (text, code, graph, and matrix blocks), cited via
sourceBlockIdsand “Box N” labels that match block order in the UI. - Curriculum: four chapters — Euler paths → adjacency matrices → trees/DFS → Dijkstra (with Python in later chapters).
- What you do: build lessons in the notebook, refresh DoDuck’s understanding, run MCQs or chapter exercises, and when coding is in play edit/run DoDuck’s Python draft to check behavior.
- This repo:
doduck_preliminaryis the notebook-first prototype. An older chat-only mock lives indoduck_test/.
- Pick a chapter tab; optionally open Study for videos/keyframes.
- Add teaching blocks in the notebook (left).
- Click Update DoDuck understanding →
POST /api/chatextracts grounded claims and merges aKnowledgeStatepluscodeDraft. - Click Assess DoDuck (MCQ) →
POST /api/assessmentusing the question library and corpus guardrails. - Use Exercise mode → submit blocks, generate animated steps via
POST /api/exercise. - When you add a code block to the notebook or exercise, or DoDuck returns a script after Update understanding (or you use Send to draft), the Python draft panel appears in the teach column for edit/run via Pyodide.
| Mode | Purpose |
|---|---|
| Teach | Understanding update, MCQ results, exercise playback; Python draft when coding is in play |
| Study | Chapter videos and keyframe thumbnails |
| Exercise | Question-library tasks and step-by-step DoDuck runs |
flowchart LR
subgraph teach [Teach mode]
notebook[Notebook blocks]
chat["POST /api/chat"]
draft[codeDraft]
py[pyRunner Pyodide]
notebook --> chat --> draft
draft --> py
end
subgraph exercise [Exercise mode]
emit[emit_python steps]
send[Send to draft]
emit --> send --> draft
end
| Path | Role |
|---|---|
app/ |
Next.js routes: single page + three API handlers |
src/home/ |
UI defaults, block IDs, exercise empty state (not routing — routes live under app/) |
src/components/ |
Notebook editors, right panels, exercise UI |
src/lib/ |
Types, corpus/grounding, term guardrails, prompts, question library |
scripts/ |
Offline tests (npm run guardrail:test) |
npm ciCreate .env.local:
OPENAI_API_KEY=your_openai_api_key_here
OPENAI_MODEL=gpt-4.1-mininpm run devOpen http://localhost:3000.
| Variable | Required | Purpose |
|---|---|---|
OPENAI_API_KEY |
Yes | Server-side OpenAI calls |
OPENAI_MODEL |
Yes | Model id (e.g. gpt-4.1-mini) |
NEXT_PUBLIC_PYODIDE_VERSION |
No | Pyodide CDN version (default 0.29.3) |
NEXT_PUBLIC_PYODIDE_INDEX_URL |
No | Full Pyodide index URL override |
| Command | Purpose |
|---|---|
npm run dev |
Development server |
npm run build |
Production build |
npm run lint |
ESLint |
npm run guardrail:test |
Offline term-guardrail checks — run when changing termKnowledge, termRegistry, or rationale sanitization |
| Goal | Start here |
|---|---|
| UI / layout / modes | app/page.tsx |
| LLM behavior | src/lib/prompts/ + matching app/api/*/route.ts |
| Chapter MCQs / tasks | src/lib/questionLibrary/ |
| DoDuck persona | src/lib/prompts/doduckPersona.ts |
When the draft panel is visible, it offers an editable script and Run via Pyodide (CDN, stdlib only; no pip). The model is instructed not to claim it executed code; you run scripts to verify teaching.
Exercise emit_python steps also offer Run for the visible snippet.
Systematic manual tests for Chapter 1 (Intro Euler): corpus variants, incremental layers, MCQ/exercise protocol, and a CSV log. See testing/README.md.
- Study keyframe images referenced in
src/lib/chapters.tsare not yet inpublic/. - Notebook code blocks do not have per-block Run yet (use the teach-panel draft when visible, or exercise
emit_pythonRun). app/page.tsxis large (~1.3k lines); split only if merges become painful..env.localis local-only and must not be committed.