A Mexican student pastes their assignment brief and the teacher's rubric. Studymation turns that into a finished, formatted Word document — institutional cover page, structured sections, and citations that were actually looked up in academic databases rather than invented by a language model.
The brief is the product's contract with the student. The rubric is not a "notes" field: it is parsed into a list of deliverables that the pipeline must satisfy and that a later step audits. Attachments (the PDF the teacher handed out) are parsed and used the same way.
The pipeline is not one giant prompt. Each stage is a step with its own model
configuration, its own failure mode and its own audit trail — and which steps run at all
depends on what was asked for. A GenerationProfile derived from the document type
decides whether the document needs a thesis plan, an introduction, a conclusion, an
overlap check between sections, or citations. A one-page answer and a research essay go
through genuinely different pipelines, not the same pipeline with a different word count.
The progress screen is not an animation: it polls the document's real current_step, so
what you see is the stage the backend is actually on.
Word is the format Mexican schools ask for, so the output is a real .docx built with
python-docx: cover page from the school's own template, heading styles, tables, charts and
diagrams when the document calls for them, in-text citations and an APA 7 reference list
that only contains sources marked as used.
(The document above was produced by running the assembler step with the repository's own test fixtures — cover, styles, citation and reference page are the system's output; the prose is fixture text, since this environment has no LLM keys.)
Citations are found, ranked and validated — or reported as missing. Candidates come
from Semantic Scholar, Crossref and Brave, are deduplicated, scored against the section
they would support, and revalidated even when they come from cache. A DOI that resolves is
only one signal; relevance decides. If nothing survives, the section is recorded as
found=False with the rejection reasons and the reference list does not grow. The audit
is stored with the document and exposed at
GET /api/v1/documents/{id}/citation-audit.
The rubric is a contract. ContractValidationStep re-reads the finished content
against the deliverables extracted from the rubric and logs auditable warnings
(contract_deliverable_missing, contract_deliverable_constraints_violated,
contract_duplicate_sections)
instead of silently shipping a document that ignored half the assignment.
The quality gate never blocks delivery. A terminal step scans the rendered .docx for
artifacts a student would notice — raw Mermaid, unresolved placeholders, stray markdown,
charts without provenance — and reports them. The student still gets the file; the issue
gets logged.
Content is written in phases. Global plan (thesis and claims per section), sections in parallel with awareness of their neighbours, then introduction and conclusion written from what the sections actually say, then a Jaccard overlap check that rewrites sections that say the same thing twice.
| Layer | What is there |
|---|---|
| Accounts | Email + password or Google OAuth, JWT in an HttpOnly cookie |
| Plans | free, pro, max — plan-gated features, per-plan rate limits and per-plan LLM models |
| Credits | 1 credit = 1 MXN, immutable ledger, SELECT FOR UPDATE on balance, idempotent by reference id |
| Payments | Stripe Checkout for both subscriptions and credit packs, webhook with signature validation |
| Schools | Each institution is a folder (backend/schools/<id>/school.json) with its own cover format, colours and logo |
| Attachments | .txt, .md, .pdf, .docx parsed through a plugin registry, size- and count-limited |
| Storage | DigitalOcean Spaces (S3-compatible); free-plan documents expire and are swept by a maintenance endpoint |
| Admin | Users, subscriptions and per-generation cost tracking |
FastAPI (async, Python 3.11) + PostgreSQL through SQLAlchemy 2.0 and Alembic, Redis for rate limiting, Next.js 15 + React 19 for the frontend, Docker Compose for both. LLM access goes through a provider-agnostic layer (OpenAI, DeepSeek, Anthropic) resolved per plan and per task, so structure generation, content generation, rubric extraction and JSON repair can each run on a different model.
backend/app/
├── api/ # routers: auth · documents · payments · admin (/api/v1)
├── core/
│ ├── document/ # pipeline (structure → content → contract → citations → assembly → gate)
│ ├── citations/ # candidate search, dedup, ranking, validation, cache
│ ├── llm/ # provider-agnostic layer + prompts
│ ├── attachments/# parser registry per file type
│ └── storage/ # S3-compatible provider
├── models/ schemas/ services/ middleware/
frontend/src/app/
├── (app)/dashboard/ # generate · documents · credits · account
├── (auth)/ # login · registro
└── admin/ # users · subscriptions · costs
Everything runs through make; the dev stack is Docker Compose (API, PostgreSQL, Redis).
make up-detached # start the stack
make migrate # apply migrations
make fe-dev # Next.js dev server with hot reload
make dev # both at once
make test # pytest, excluding tests that call external APIs
make check # ruff + mypyThe backend needs a .env; at minimum a database URL and a JWT secret. LLM, citation,
Stripe and storage keys are optional in development — without them the app runs, but
document generation stops at the first LLM call.
→ Documentation index · Setup · Architecture · API reference · Pipeline · Citations · Runbook · Troubleshooting · Referencia técnica en español
Studymation is an assistance tool: the student accepts responsibility for the work before generating, the document is meant to be reviewed and edited, and the system refuses to fabricate the one thing that would be hardest to check — the sources.


