Abhyas documentation
A user manual for the learner and the teacher, a walkthrough of one week at the centre, and the technical reference for whoever deploys it.
Demo access
The hosted demo is gated. Open Request access, leave your email, and Abhyas sends you the access PIN together with a temporary login that works for two days. Enter the PIN at /demo (or the short link /app), sign in, and you are at one synthetic centre, Sharada Coaching Centre, Vijayawada, with five courses, thirty lessons and twelve learners already on file.
Your temporary login is given a prepared learner profile: a copy of the demo learner's courses in progress, quiz attempts and flashcards, so the desk is not empty on your first visit. The same login can also open the Teaching desk, so you see both sides. The public site always stays at /; the signed-in desk lives at /desk.
Accounts & roles
Who uses it. Abhyas is the desk of one coaching centre. Each person signs in with their own account, and everything they do is recorded against their username in the activity timeline.
Getting in. On a fresh installation the first person to open the app creates the admin account. After that, sign-up is by invitation only: an admin opens Users, enters a person's email and role, and Abhyas emails a join link (or shows the link on screen when no email channel is configured). On the public demo, access requests issue temporary logins instead.
Roles. Admin can do everything and manages users. Teacher authors courses and lessons, sets and grades assignments, and sees the class and the at-risk radar. Learner studies: lessons, quizzes, practice, plans and assignments. Pages a role cannot use are not linked, and the server refuses the request regardless.
Sessions are signed cookies, so they survive across server instances. Admins can change roles and deactivate accounts; nobody can deactivate themselves.
Courses & lessons
Catalogue. Search by title, description or teacher; filter by category (school, languages, commerce & GST, computer skills, competitive exams) and level (beginner, intermediate, advanced). A course you are enrolled in shows your completion instead of the enrolment count.
Course page. Lessons in order, each with its minutes and, once you have taken a quiz on it, its mastery. Enrol once; the button then becomes Continue and always points at the first lesson you have not completed. The study plan and the course's assignments sit beside the lesson list.
Lesson reader. The teaching text, a video if the teacher attached a YouTube link, previous and next links, the quiz box and the flashcard box. Mark as complete records the completion once (a second click changes nothing), awards 10 XP, and extends your streak if you were also active yesterday. Completing the last lesson of a course issues a certificate.
Certificates. Numbered ABH-<year>-NNNN, one per learner per course, printable, and verifiable by anyone at /verify/<number> without signing in.
Quizzes
Generation. A quiz is five multiple-choice questions built from the lesson you have open, at one of four levels: basic (recall, obviously different options), core (main ideas, look-alike options), advanced (apply an idea; statements that differ in one detail) and expert (dense statements, subtle swaps). Without a model key the questions are cloze and statement questions drawn from the lesson's own sentences and key terms; with a key the model writes them and its answer is accepted only if it fits the same shape.
Taking it. Answer every question and submit. A question left blank counts as wrong rather than blocking the submission. The result page marks each question, shows the explanation for the ones you missed, and lists every earlier attempt on that lesson.
Mastery and level. Mastery for a lesson is your best score so far; a course's mastery is the average over the lessons you have attempted. After each attempt the suggested level moves: 80% or better goes up one level, under 50% goes down one, in between stays. Practice again is pre-set to that level.
Practice
Cards. Make flashcards on a lesson turns its key terms and definition sentences into cards (term on the front, the lesson's sentence on the back). Making cards twice does not duplicate them.
Review. The practice page shows only cards that are due today or earlier, oldest first, one at a time. Flip the card, then rate yourself: Blank (0), Almost (2), Hard (3), Good (4), Easy (5). Keys 1–5 work too. When the queue is empty the page says so and names the next due date.
The schedule (SM-2). Each card carries an ease (starting at 2.5), an interval in days and a repetition count. After a rating q: ease′ = ease + 0.1 − (5 − q)(0.08 + (5 − q) × 0.02), never below 1.3. A rating under 3 resets the interval to 1 day and the count to zero. Otherwise the interval is 1 day after the first success, 6 days after the second, then round(interval × ease) each time after that. Rating Easy on a new card therefore brings it back tomorrow, then in six days, then in about sixteen.
Study plans
On a course page, pick a pace and a finish date. The lessons you have not completed are spread evenly, in order, over the days up to that date: relaxed uses every available day, steady about 70% of them, intense about 40%, so an intense plan finishes earlier. No day carries more lessons than the spread needs.
Today's tasks appear on your desk. A planned lesson whose day has passed is overdue and counts against you on the teacher's radar. Rebuild plan with a new pace or date keeps every lesson you have completed and re-spreads only the rest from today.
Teaching desk
Desk. Every course with enrolled, completed, mean completion, mean mastery and submissions waiting; the at-risk radar; and the centre's recent activity.
At-risk radar. Three flags, computed on every open enrolment: inactive for seven days or more (critical at fourteen), low mastery under 50% with at least one quiz attempt, and behind plan when planned lessons are overdue. Each flag links to the learner's page, which shows their courses, mastery, cards due, submissions and timeline.
Authoring. Create a course (title, category, level, hours, description), then add lessons with a title, minutes, an optional YouTube link and the teaching text. Lessons can be moved up or down and edited. Quizzes and cards are built from the text, so a fuller lesson gives better questions.
Assignments. Set a brief with a due date and a maximum score. Learners submit a written answer; a submission after the due date is flagged late, and re-submitting replaces the earlier answer and clears its grade. Grade with a score (refused above the maximum) and one line of feedback. Suggest a grade applies a rubric: 40% coverage of the brief's key terms, 25% length (120 words is full marks), 20% structure (five sentences), 15% specifics such as figures and examples. It is a suggestion with reasons, never a verdict.
Tutor
The Tutor button opens a drawer with two assistants. Both work offline; when a model key is configured the same request goes to the model first, and its answer is accepted only if it is grounded in the lesson. The badge at the top names the engine that actually answered.
- Ask about this lesson. Type a question while a lesson is open. Offline, the tutor returns the sentences of the lesson that share the most content words with your question and quotes them. If nothing in the lesson matches, it says so instead of guessing.
- What should I do next? One step with a reason, in a fixed order: review cards if any are due; catch up on an overdue planned lesson; continue the next lesson; take a quiz on a completed course with mastery under 80%; make a plan; otherwise pick a course. The same recommendation heads your desk.
A week at the centre
- Monday evening, Anjali opens her desk. Thirteen cards are due, so the tutor says review first. Ten minutes later the queue is empty and her streak reads six days. She continues Class 10 Mathematics at Quadratic equations, marks it complete, and takes the core quiz: 4 of 5, so the next suggested level is advanced.
- Tuesday, a busy day. She misses her planned lesson. Wednesday's desk shows one overdue task, and the tutor's first suggestion is to catch up on it rather than start something new.
- Thursday, Ravi opens the teaching desk. The radar flags Eswar, inactive for twelve days in Quantitative Aptitude, and Farhan at 38% mastery in Mathematics. He opens Farhan's page, sees two attempts under half marks on Polynomials, and calls him in for a Saturday session.
- Friday, grading. Three submissions wait on Solve two quadratic equations by two methods. Suggest a grade proposes 7 of 10 for one answer that never mentions the discriminant; Ravi reads it, agrees, and adds one line of feedback.
- Saturday, Bhavani finishes Spoken English. Marking the last lesson complete issues certificate ABH-2026-0002. She prints it and sends the verify link to her employer.
Architecture
Abhyas is a single FastAPI process rendering Jinja2 templates over a single SQLite file, with plain CSS and a small amount of vanilla JavaScript for the flashcard flip, the grade suggestion and the tutor drawer. There is no build step and no front-end framework.
| Module | Responsibility |
|---|---|
app/db.py | Connection, idempotent schema (twelve tables: courses, lessons, quizzes, attempts, flashcards, enrolments, plans, assignments, submissions, certificates, learners, events), event logging. |
app/domain.py | SM-2, adaptive level, plan generation and status, streaks and XP, progress and mastery reads, dashboards, class view, at-risk flags, certificate numbering, the grading rubric. |
app/tutor.py | Provider layer (Anthropic → OpenRouter → heuristic); quiz generation and grading; flashcard generation; ask-the-lesson; what-next. |
app/auth.py | Users, PBKDF2 passwords, roles, invites, signed sessions and stateless temporary logins. |
app/mail.py | Outbound email through Resend, with an on-screen fallback and the demo sink. |
app/seed.py | Deterministic synthetic seed: five courses, thirty lessons of real teaching text, twelve learners with thirty days of history. |
app/main.py | Routes, forms, JSON API, tutor endpoints, teaching desk. |
api/index.py | Hosted-twin wrapper: betadoc site at the root, PIN gate, email-first access, the app behind it. No application code changes. |
The three rules
Progress is computed, never typed. The enrolment stores only the list of completed lessons. Completion percentage, mastery, streaks, plan status, class figures and every at-risk flag are computed from completions, attempts and reviews on every read. Nothing about progress is cached in a column that can drift.
The tutor degrades honestly. tutor.provider() picks an engine from the environment; every call is wrapped so a failing or misbehaving model falls back to the rules engine, and the response carries the engine that answered. Model output must validate against the heuristic's schema or it is discarded.
The learner owns the pace. Plans are rebuilt around what the learner has actually done, quiz levels follow the learner's score, and cards return on the learner's own ratings.
Configuration
| Variable | Meaning |
|---|---|
ABHYAS_DB | Path to the SQLite file (default abhyas.db beside the app; /tmp/abhyas.db on the twin). |
ABHYAS_SEED | 1 seeds an empty database with the synthetic centre and the demo accounts. |
ABHYAS_HOME | Where the signed-in desk lives: / locally, /desk on the twin so the public site can keep the root. |
ABHYAS_SHOW_DEMO_LOGINS | Whether the login page lists the seeded demo accounts (on locally, off on the public twin). |
FEED_TOKEN | Token for the JSON API; random per process if unset. |
ANTHROPIC_API_KEY | Enables the Anthropic provider (official SDK, structured JSON output; model via ABHYAS_MODEL, default claude-opus-5). |
OPENROUTER_API_KEY | Enables the OpenRouter provider (model via ABHYAS_OPENROUTER_MODEL). |
RESEND_API_KEY, RESEND_FROM | Enables outbound email through Resend (invites, access mails). Without them the email is shown on screen to copy. |
ABHYAS_BASE_URL | Public address used inside invite, verify and access links. |
ABHYAS_MAIL_SINK | Demo only: deliver every email to this one inbox instead of the recipient. |
SESSION_SECRET | Signs session cookies and temporary logins; set it explicitly on any multi-instance host. |
BETADOC_PIN, BETADOC_SHOW_PIN, OWNER_EMAIL | Twin only: the demo PIN (sent by email on request, never shown unless BETADOC_SHOW_PIN=1) and who gets notified of access requests. |
# local
uv venv .venv && uv pip install --python .venv/bin/python -r requirements.txt
.venv/bin/uvicorn app.main:app --port 8361 # or: pm2 start ecosystem.config.js
.venv/bin/pytest -q # the test suite
.venv/bin/python tools/walk.py # headless walk, 0 console errors
API reference
Browser routes use the signed session cookie. The one machine endpoint is authenticated by a feed token passed as ?token= or Authorization: Bearer; without it the API answers 401 with a JSON body. Every form route below also accepts a JSON body and answers JSON when the request carries Accept: application/json.
| Endpoint | Returns |
|---|---|
GET /healthz | {ok, app, db, courses, lessons, learners, ai, email, users, time} — ai names the tutor engine in use (anthropic, openrouter or heuristic). |
GET /api/v1/courses?token=… | {count, courses:[{id, title, category, level, hours, teacher, lessons, enrolled}]} |
POST /courses/{id}/enrol | {ok, created} — 201 on the first enrolment, 200 when already enrolled. |
POST /courses/{id}/plan {pace, target_date} | {ok, pace, target_date, tasks:[{day, lesson_id}]}; PATCH the same path adjusts the pace or date and rebuilds. |
POST /lessons/{id}/complete | {ok, changed, certificate} — certificate is the number issued when this completed the course. |
POST /lessons/{id}/quiz {level, n} | {id, level, provider, questions:[{question, options, explanation}]} (answers withheld). |
POST /quiz/{id}/submit {answers:[…]} | {attempt_id, correct, total, score, marks:[{given, correct, ok, explanation}], suggested_level} |
POST /lessons/{id}/flashcards | {added, provider} |
POST /flashcards/{id}/review {quality} | {ok, ease, interval, reps, due} |
POST /tutor/ask {lesson_id, question} | {answer, covered, quote, provider, lesson} |
GET /tutor/next | {action, label, url, reason, provider} |
GET /verify/{number} | Public. The certificate's learner, course and date, or 404. |
GET /teach/submissions/{id}/suggest | {score, max_score, points:[…], coverage, provider} (teacher/admin session). |
POST /teach/submissions/{id}/grade {score, feedback} | {ok, score}; 422 when the score exceeds the maximum. |
Known limits
- Three fixed roles (admin, teacher, learner); there are no batches, parents or per-course permissions yet.
- The hosted twin's database is ephemeral by design; the on-premises lane is the stateful one.
- Quizzes and cards are built from the lesson text, so a thin lesson gives thin questions. Write a full paragraph or more per lesson.
- YouTube is the only embedded video host; other links open in a new tab.
- Model providers are optional and unconfigured on the public demo, so the badge reads “Works without AI” there.