Skip to content

Repository files navigation

AI Grammar Corrector

A web application that corrects English grammar and explains every edit it makes. A Groq-hosted LLM produces the corrected sentence, and ERRANT aligns the original against the correction to label each edit with a linguistic error type — so you see what changed and why, not just a rewritten paragraph.

  • Backend: Django 5.2 + Django REST Framework
  • Frontend: React 19 + Vite 7 + Tailwind CSS 3
  • LLM: Groq API (llama-3.1-8b-instant by default)
  • Annotation: ERRANT 3 + spaCy en_core_web_sm
  • Database: SQLite by default — no database server required

Contents

Quick start

Requirements: Python 3.12+, Node.js 20+, and a free Groq API key.

1. Backend

cd backend
python -m venv .venv
source .venv/bin/activate          # Windows: .venv\Scripts\activate
pip install -r requirements.txt

# Sentence tokenizer data (~40 MB)
python -m nltk.downloader punkt punkt_tab

cp .env.example .env               # then set GROQ_API_KEY and DJANGO_DEBUG=1
python manage.py migrate
python manage.py runserver

The API is now on http://localhost:8000.

The spaCy model en_core_web_sm is installed automatically by requirements.txt. If you skipped it, run python -m spacy download en_core_web_sm.

2. Frontend

In a second terminal:

cd frontend
npm install
cp .env.example .env               # defaults already point at localhost:8000
npm run dev

Open http://localhost:5173.

Minimum configuration

For local development you only need two things in backend/.env:

DJANGO_DEBUG=1
GROQ_API_KEY=your_key_here

With DJANGO_DEBUG=1, the secret key, allowed hosts and CORS origins all fall back to development defaults. In production none of them do — see Configuration.

Running with Docker

cp .env.example .env               # set DJANGO_SECRET_KEY and GROQ_API_KEY
docker compose up --build

Both services run as non-root users from multi-stage images. The backend is served by gunicorn and the frontend by nginx — neither uses a development server. Migrations run automatically on container start, and SQLite data persists in the backend-data volume.

docker compose refuses to start until DJANGO_SECRET_KEY and GROQ_API_KEY are set; every other variable has a working default.

VITE_API_URL is compiled into the frontend bundle at build time, not read at runtime. Changing it requires docker compose build frontend.

Configuration

All backend settings are read from the environment (via backend/.env when present). This table lists every variable the code actually reads.

Required in production (DJANGO_DEBUG=0)

The application raises ImproperlyConfigured and refuses to start if any of these is missing. There is no silent fallback to an insecure default.

Variable Description
DJANGO_SECRET_KEY Django signing key. Generate with python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"
GROQ_API_KEY Groq API key from https://console.groq.com/keys
DJANGO_ALLOWED_HOSTS Comma-separated hostnames. Never *
DJANGO_CORS_ALLOWED_ORIGINS Comma-separated browser origins allowed to call the API

With DJANGO_DEBUG=1 these fall back to an insecure development key, localhost,127.0.0.1,[::1], and http://localhost:5173, http://127.0.0.1:5173 respectively.

Optional

Variable Default Description
DJANGO_DEBUG 0 Enable debug mode. Never enable in production
DJANGO_LOG_LEVEL INFO Root logger level
GROQ_MODEL llama-3.1-8b-instant Groq model id
GROQ_TIMEOUT_SECONDS 30 Per-request timeout to Groq
GRAMMAR_MAX_INPUT_CHARS 1000 Maximum characters per request
API_ANON_THROTTLE_RATE 30/minute DRF throttle for anonymous callers
DJANGO_SECURE_SSL_REDIRECT 1 Redirect HTTP to HTTPS. Applied only when DJANGO_DEBUG=0
DJANGO_SECURE_HSTS_SECONDS 31536000 HSTS max-age. Applied only when DJANGO_DEBUG=0

Database

Variable Default Description
DB_ENGINE django.db.backends.sqlite3 Django database backend
DB_NAME backend/db.sqlite3 Database name or SQLite file path
DB_USER Required for non-SQLite engines
DB_PASSWORD Optional for non-SQLite engines
DB_HOST Required for non-SQLite engines
DB_PORT Required for non-SQLite engines

To use PostgreSQL or MySQL, install the driver (psycopg[binary] or mysqlclient) and set DB_ENGINE plus DB_NAME, DB_USER, DB_HOST and DB_PORT. Startup fails loudly if any of those four is missing.

The app itself defines no models; the database is used only by Django's auth, contenttypes and sessions apps.

Frontend

Variable Default Description
VITE_API_URL http://localhost:8000 Backend base URL, no trailing slash and without the /grammar_check prefix — the client appends it

API reference

Base path: /grammar_check/

POST /grammar_check/grammar_correction/

Correct text and annotate each edit.

Request

{ "text": "She go to school. He were happy." }

text is required, non-blank after trimming, and at most GRAMMAR_MAX_INPUT_CHARS characters.

Response 200

{
  "results": [
    {
      "Input": "She go to school.",
      "Correct": "She goes to school.",
      "Errant": [
        {
          "o_start": 1, "o_end": 2, "o_str": "go",
          "c_start": 1, "c_end": 2, "c_str": "goes",
          "e_type": "R:VERB:SVA"
        }
      ]
    }
  ]
}

The input is split into sentences and each is corrected independently, so results has one entry per sentence. e_type is an ERRANT code — prefix M (missing), U (unnecessary) or R (replacement), plus a part of speech. The frontend maps these to readable labels in frontend/src/constants/errorTypes.js.

Status codes

Code Meaning
200 Success
400 text missing, blank, or too long
405 Method other than POST
429 Throttle rate exceeded
502 Groq unreachable, rate-limited, or returned unusable output
503 Server misconfigured — no valid GROQ_API_KEY, or missing spaCy/NLTK data

A failed correction returns an error status. It never returns the original text dressed up as a correction.

GET /grammar_check/health/

Liveness probe used by the container healthcheck.

Response 200

{ "status": "ok" }

Testing and linting

# Backend
cd backend
pip install -r requirements-dev.txt
DJANGO_DEBUG=1 python manage.py test     # 30 tests
DJANGO_DEBUG=1 pylint CorrectionAPP GrammerCorrection manage.py
pip-audit --requirement requirements.txt

# Frontend
cd frontend
npm run lint
npm run build
npm audit --audit-level=high

CI runs exactly these commands plus a Docker image build — see CONTRIBUTING.md and .github/workflows/ci.yml.

Privacy

This application is deliberately stateless with respect to user content:

  • Submitted text is not written to disk, a database, or a log file.
  • Client IP addresses are not recorded.
  • No contact form, no analytics, no cookies for anonymous users.

The one unavoidable disclosure is that submitted text is sent to Groq for processing. Review Groq's privacy policy before submitting confidential material.

If you add logging or a reverse proxy, check that it does not reintroduce request-body or IP logging. See SECURITY.md.

Limitations

  • English only. ERRANT is loaded with the English model, and the prompt assumes English.
  • Non-deterministic. Corrections come from an LLM at temperature=0, which reduces but does not eliminate variation. The model can miss errors, and can occasionally rewrite meaning rather than only fixing grammar.
  • ERRANT labels are heuristic. Edit classification is derived from an automatic alignment and is not always linguistically perfect.
  • One Groq call per sentence. Long inputs cost proportionally more and take longer. The default 1000-character cap keeps this bounded.
  • The API is unauthenticated. Rate limiting is the only abuse control, and it is in-memory per process — it does not coordinate across workers or replicas. Put authentication or a gateway in front of a public deployment.
  • First request is slow. The spaCy model loads lazily on the first annotation, adding a few seconds.
  • No streaming. Responses are returned only when every sentence is done.

Project layout

.
├── backend/
│   ├── CorrectionAPP/            # Django app
│   │   ├── services/             # Business logic
│   │   │   ├── exceptions.py     # Domain error types
│   │   │   └── grammar.py        # Groq + ERRANT orchestration
│   │   ├── tests/                # Test suite
│   │   ├── serializers.py        # Request validation
│   │   ├── urls.py
│   │   └── views.py              # Thin HTTP layer
│   ├── GrammerCorrection/        # Django project settings
│   ├── Dockerfile                # Multi-stage, non-root, gunicorn
│   ├── pyproject.toml            # pylint configuration
│   ├── requirements.txt
│   └── requirements-dev.txt
├── frontend/
│   ├── src/
│   │   ├── components/           # Presentational components
│   │   ├── constants/            # Shared literals
│   │   ├── hooks/                # Stateful logic
│   │   ├── pages/
│   │   └── services/             # API client
│   ├── Dockerfile                # Multi-stage, non-root, nginx
│   └── nginx.conf
├── .github/workflows/ci.yml
├── docker-compose.yml
├── CONTRIBUTING.md
├── SECURITY.md
└── LICENSE

On the spelling of GrammerCorrection and CorrectionAPP: both are misspelled/non-idiomatic, and both are intentionally left alone. GrammerCorrection is referenced by DJANGO_SETTINGS_MODULE, wsgi.py, asgi.py and the Dockerfile; CorrectionAPP is the Django app label stored in the django_migrations table of existing databases. Renaming them would break running deployments for a cosmetic gain. The corresponding pylint warning is suppressed and documented in backend/pyproject.toml.

Licence

GNU AGPL-3.0-or-later.

Note the network clause: if you run a modified version of this software as a network service, you must offer its source code to the users of that service.

Branding and logos belonging to Pfactorial Technologies Pvt Ltd (frontend/public/logo/, frontend/public/icon/, and the product links in frontend/src/constants/products.js) are the property of their owner and are not covered by the software licence. Replace them if you fork this project.

About

Grammar checking and proofreading application designed to help users improve their writing by detecting and correcting grammar, spelling, punctuation, and sentence structure issues.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages