Student roster
Generate a personalised course pack
Pick the audience and the subjects to plan for. The agent reads the student's last-two-term averages, pulls Alberta curriculum outcomes at the target grade, and writes a plan that cites real outcome codes.
Plans on file
Summary
{{ currentPlan.plan.summary_markdown }}
{{ subjectName(sec.subject_code) }} ({{ sec.subject_code }})
- {{ a.modality }} {{ a.title }} · {{ a.minutes }} min · {{ a.materials.join(', ') }}
Ask a follow-up
Conversational Claude over this student's record and plan. Audience: {{ role }}.
How the staff portal works
A short tour of what each screen does and how the AI is wired up underneath.
1. Roster
100 synthetic Alberta K-12 students with ASIN, grade level, complexity flags, and three years of term-by-term marks. Filter by grade, complexity, or struggling status.
2. Student detail
Pick a student to see their subject snapshot (last-two-term averages with a status badge), complexity supports, and teacher commentary from the iep_notes table.
3. Plan generator
Pick audience and subjects. The agent runs two steps:
- Per subject, compute the last-two-term average. Pick a direction: remediate (under 60), on-grade, or accelerate (88 or above). Set a target grade by shifting from current grade.
- Pull the top curriculum outcomes for the target grade from the read-only LearnAlberta.ca mirror. Hand to Vertex Claude Sonnet with a tool-use schema. The LLM picks 3-5 outcomes per subject from the candidate set (no inventing codes) and proposes 2-3 activities.
4. Multi-modal output
From a generated plan, produce a narrated audio version with the custom ElevenLabs voice and a cover image with OpenAI gpt-image-1. Both persist as BYTEA in Postgres; playback works directly off /api/media/:id/bytes.
5. Conversational helper
A chat box over the student record plus the latest plan summary. The system prompt tunes to the current role (teacher / parent / student). Replies are capped under 200 words.
API surface
| Method | Path | Purpose |
|---|---|---|
| GET | /api/health | Service + DB ping |
| GET | /api/curriculum/subjects | List curriculum subjects |
| GET | /api/curriculum/courses?subject= | Courses (grades) per subject |
| GET | /api/curriculum/outcomes | Outcome search |
| GET | /api/students | Roster (filterable) |
| GET | /api/students/:id | Student + grade history + notes + plans |
| POST | /api/plans/:student_id | Generate a plan |
| GET | /api/plans/:id | Plan + sections + media |
| POST | /api/media/audio | Generate ElevenLabs TTS (persists BYTEA) |
| POST | /api/media/images | Generate OpenAI image (persists BYTEA) |
| GET | /api/media/:id/bytes | Stream the asset bytes |
| POST | /api/ai/chat | Conversational Claude over student + plan |
Per-pack cost calculator
Adjust the assumptions; the totals update live. List prices as of January 2026.
| Cadence | Plans | Audio | Images | Chats | USD |
|---|---|---|---|---|---|
| Per day | {{ costRow(1).plans }} | {{ costRow(1).audio }} | {{ costRow(1).images }} | {{ costRow(1).chats }} | ${{ costRow(1).total.toFixed(2) }} |
| Per week | {{ costRow(7).plans }} | {{ costRow(7).audio }} | {{ costRow(7).images }} | {{ costRow(7).chats }} | ${{ costRow(7).total.toFixed(2) }} |
| Per month | {{ costRow(cost.days_per_month).plans }} | {{ costRow(cost.days_per_month).audio }} | {{ costRow(cost.days_per_month).images }} | {{ costRow(cost.days_per_month).chats }} | ${{ costRow(cost.days_per_month).total.toFixed(2) }} |