GradeAI is a desktop app for teachers to upload one or more text-based student exam PDFs, extract their text, analyze them with a local Ollama model, and display structured grades, feedback, and detected issues.
This is a vibe-coding project built with Codex.
This application is intended for local use only. Do not use real personal or student data unless you have proper consent and comply with applicable data protection laws.
For testing and development, use anonymized or synthetic data only.
- Frontend: Electron + React + TypeScript
- Backend: Python + FastAPI
- LLM: Ollama with
qwen3:8bby default - Communication: REST API on
localhost - Grading scale: German grades 1-6, including half grades and decimal-comma grades
.
├── backend
│ ├── app
│ │ ├── api
│ │ │ └── routes
│ │ ├── clients
│ │ ├── core
│ │ ├── resources
│ │ ├── schemas
│ │ ├── services
│ │ ├── utils
│ │ ├── main.py
│ │ └── standalone.py
│ ├── tests
│ │ ├── api
│ │ ├── clients
│ │ ├── core
│ │ ├── schemas
│ │ ├── services
│ │ └── utils
│ ├── requirements-dev.txt
│ └── requirements.txt
├── frontend
│ ├── electron
│ │ └── ipc
│ ├── e2e
│ ├── resources
│ ├── src
│ │ ├── app
│ │ ├── components
│ │ ├── features
│ │ ├── services
│ │ ├── styles
│ │ ├── types
│ │ └── utils
│ └── package.json
├── scripts
│ ├── setup.mjs
│ ├── dev.mjs
│ ├── check.mjs
│ └── backend-dev.mjs
├── package.json
├── .gitignore
└── README.md
- Git
- Node.js 20+
- Python 3.11+
- Ollama installed locally for development, unless you provide a bundled runtime under
frontend/resources/bin/ollama
GradeAI integrates with Ollama through Ollama's local HTTP API. In development, install Ollama locally. For a packaged desktop app, you can bundle the Ollama runtime binary under frontend/resources/bin/ollama, and GradeAI will use that before falling back to a system installation. The app tries to start ollama serve automatically and checks whether the configured model is available.
Start Ollama manually if you are running the backend without Electron, or if the automatic startup cannot find the ollama command:
ollama servePull the default model:
ollama pull qwen3:8bYou can use another local Ollama model by setting GRADEAI_OLLAMA_MODEL.
If Ollama is running but the model is missing, GradeAI can download the configured model from the desktop UI.
Run the project setup from the repository root:
npm run setupThis creates backend/.venv, installs Python dependencies, and installs frontend dependencies.
npm run devThe root dev script starts Electron and points it at the Python executable in backend/.venv. Electron then starts the FastAPI backend as a child process.
Default local services:
- FastAPI backend:
http://127.0.0.1:8000 - Ollama:
http://127.0.0.1:11434
Useful for API debugging:
npm run backend:devThen open the API docs at http://127.0.0.1:8000/docs.
Run Python bytecode compilation and TypeScript checking:
npm run checkGradeAI packages the desktop app with electron-builder and bundles the Python backend with PyInstaller.
Build the frontend only:
npm run build:frontendBundle the backend into a standalone executable for the current platform:
npm run bundle:backendThe backend binary is written to:
frontend/resources/backend/<platform>-<arch>/
Examples:
frontend/resources/backend/darwin-arm64/gradeai-backend
frontend/resources/backend/win32-x64/gradeai-backend.exe
Package the final desktop app for the current platform:
npm run package:desktopBuild a macOS .dmg on macOS:
npm run package:macBuild a Windows NSIS .exe installer on Windows:
npm run package:winInstallers are written to:
frontend/release/
For release builds, run the packaging command on the target platform. Build macOS installers on macOS and Windows installers on Windows.
Run backend and frontend unit tests:
npm run test:unitnpm test is an alias for npm run test:unit.
Run the full suite, including unit, integration, and Electron E2E tests:
npm run test:allRun tests with coverage:
npm run test:coverageCoverage reports are written to:
backend/htmlcov/index.htmlfrontend/coverage/index.html
Run one side only:
npm run backend:test
npm run frontend:test
npm run backend:test:unit
npm run frontend:test:unit
npm run backend:test:coverage
npm run frontend:test:coverageRun backend integration tests:
npm run test:integrationRun Electron E2E smoke tests:
npm run test:e2eThe E2E suite launches the built Electron app against a fake local backend. It does not require a real Ollama model.
Environment variables:
GRADEAI_PYTHON: Python executable used by Electron to start the backendGRADEAI_BACKEND_EXECUTABLE: standalone backend executable used by Electron instead of PythonGRADEAI_BACKEND_PORT: backend port, default8000GRADEAI_OLLAMA_URL: Ollama base URL, defaulthttp://127.0.0.1:11434GRADEAI_OLLAMA_MODEL: Ollama model, defaultqwen3:8bGRADEAI_MAX_PDF_SIZE_MB: maximum upload size, default20GRADEAI_OLLAMA_BINARY: Ollama executable path, defaultollamaGRADEAI_SKIP_OLLAMA=1: do not try to startollama servefrom ElectronGRADEAI_SKIP_BACKEND=1: do not start FastAPI from Electron
Copy backend/.env.example to backend/.env for local backend configuration.
GET /api/ollama/status
Returns whether Ollama is reachable and whether the configured model exists locally.
POST /api/ollama/pull
Downloads the configured Ollama model when Ollama is reachable.
POST /api/upload/extract-text
Multipart form field:
file: PDF file
Returns extracted text for a text-based PDF.
POST /api/analyze
Multipart form field:
file: PDF file
Response:
{
"student_name": "Jane Smith",
"grade": "2,5",
"feedback": "The exam is generally clearly structured...",
"issues": ["Reasoning is too brief in two places", "The solution path for task 3 is missing"],
"points_summary": "42 out of 50 points were detected; subtask 3b carried significant weight."
}POST /api/analyze/batch
Multipart form field:
files: one or more PDF files
Response:
{
"results": [
{
"filename": "student-a.pdf",
"status": "success",
"analysis": {
"student_name": "Jane Smith",
"grade": "2,5",
"feedback": "The exam is generally clearly structured...",
"issues": ["Reasoning is too brief in two places"],
"points_summary": "42 out of 50 points were detected."
},
"error": null
},
{
"filename": "scan.pdf",
"status": "error",
"analysis": null,
"error": "No selectable text was found in the PDF. Scanned PDFs need OCR, which is not implemented yet."
}
]
}- Only text-based PDFs are supported.
- Scanned documents and handwriting are not processed yet.
- The grade is model-generated feedback and should be reviewed by the teacher.
- LLM output can still vary slightly. GradeAI uses deterministic Ollama options and a fixed grade scale to reduce variation.
For development, installing Ollama separately is acceptable. For a production desktop app, GradeAI is prepared to use a bundled Ollama runtime before falling back to the system ollama command.
Place platform-specific Ollama binaries here before building an installer:
frontend/resources/bin/ollama/
├── darwin-arm64/ollama
├── darwin-x64/ollama
├── linux-x64/ollama
└── win32-x64/ollama.exe
Build a packaged app:
npm run package:desktopRecommended production strategy:
- Build the Python backend into a platform-specific executable with PyInstaller.
- Bundle the small Ollama runtime binary with the app.
- Store model files in the app data directory through
OLLAMA_MODELS. - Let the user download the configured model on first run from inside the app.
Runtime behavior:
- Electron starts the bundled backend automatically when the app launches.
- If no bundled backend exists, Electron falls back to the development Python environment.
- If Ollama is missing or not running, the UI shows "Please install Ollama" with a link to https://ollama.com/download.
- If the configured model is missing, the UI shows the required
ollama pull ...command and a download button. - If the backend cannot be reached, the UI shows a backend startup error panel.
Fully offline installers are possible, but they become very large because models such as qwen3:8b are multiple GB. In that case, ship a separate offline installer artifact that preloads the Ollama model directory instead of committing model files to Git.
See ROADMAP.md.
Planned next features include:
- Configurable grade scale with whole grades, half grades, and decimal-comma grades
- Optional model answer upload
- Free-text field for additional grading criteria passed to the LLM
- Multiple languages for UI, prompts, and feedback
- OCR for scanned documents
- Handwriting recognition
- Email sending
Initialize version control if this directory is not already a repository:
git init
git add .
git commit -m "Initial GradeAI app"MIT. See LICENSE.