markiskorova/ai-legal-assistant
⚖️ AI Legal Assistant — a modular, explainable legal-tech platform that uses Django and GPT-4o to analyze contracts, extract clauses, assess risk, and generate transparent summaries with case-level reasoning and evidence provenance.
AI Legal Assistant
AI Legal Assistant is a modular Django + React project for document-level legal analysis with explainable findings.
Summary
AI Legal Assistant demonstrates how AI can support document-level legal review without obscuring how conclusions were reached. Users upload a legal document, start an asynchronous review run, and retrieve structured findings produced by deterministic rule checks plus schema-constrained LLM analysis. Each run persists status, chunk-level provenance, evidence spans, and optional recommendations and embeddings so the review process remains inspectable, retry-safe, and auditable after execution.
For a fuller description of product behavior, see Project Overview. For system design details, see the Software Architecture Document in docs/architecture.md.
Current Capabilities (Phase 2 + Phase 1.5 Complete)
The project currently supports:
- Uploading legal documents (
.txt,.pdf,.csv,.xlsx) - Clause extraction
- Deterministic rule checks plus LLM analysis
- Always-async review execution (
POST /v1/review/runreturnsrun_id) - Review run lifecycle tracking (
queued,running,succeeded,failed,partial) - Idempotency keys, concurrency caps, and request rate limits for review execution
- Persisted chunk artifacts with stable
chunk_idprovenance - Run-level instrumentation (
token_usage,stage_timings, cache hit/miss fields) - Persisting review runs and findings
- Retrieving findings by document (optionally by run), with pagination/sorting
- Optional finding recommendations and persisted embeddings
- pgvector bootstrap support for Postgres deployments
- A minimal React + TypeScript UI for upload, run, and findings review
Tech Stack
- Backend: Django 5 + Django REST Framework
- LLM: OpenAI API with strict JSON schema validation
- Auth library available:
djangorestframework-simplejwt - Database:
- SQLite for local dev by default
- PostgreSQL 16 in Docker Compose
- pgvector bootstrap migration for Postgres (
vectorextension + vector index/column)
- Frontend: React 18 + TypeScript + Vite
- Container orchestration: Docker Compose
- Async jobs: Celery + Redis
Repository Layout
ai-legal-assistant/
|-- apps/
| |-- accounts/
| |-- documents/
| `-- review/
|-- backend/
|-- docker/
|-- docs/
|-- frontend/
|-- docker-compose.yml
|-- Dockerfile
|-- manage.py
`-- requirements.txt
API Endpoints (Implemented)
GET /- health checkPOST /v1/documents/upload- upload/ingest a documentPOST /v1/review/run- enqueue clause extraction + rules + LLM analysis (returnsrun_id)GET /v1/review-runs/{id}- retrieve run status/progress for a review runGET /v1/documents/{id}/findings- retrieve findings for latest runGET /v1/documents/{id}/findings?run_id=<uuid>- retrieve findings for a specific runGET /v1/documents/{id}/findings?page=1&page_size=50&ordering=-created_at- paginated/sorted retrieval
Async Run Semantics
POST /v1/review/runresponse codes:202 Accepted: run was queued and task enqueue succeeded200 OK: idempotency key reused an existing unexpired run409 Conflict: idempotency key exists but is expired (older than 24h)429 Too Many Requests: concurrency or rate limit reached503 Service Unavailable: enqueue failed
- Run status values:
queued,running,succeeded,failed,partial
- Findings retrieval query params:
run_id=<uuid>(optional)page=<int>andpage_size=<int>(optional)ordering=<field>where field is one ofcreated_at,severity,source,confidence(prefix with-for descending)
- Partial-result policy:
- If deterministic stages succeed but LLM stage fails/timeouts, run is marked
partial. - Rule findings are still persisted and retrievable for that run.
- If deterministic stages succeed but LLM stage fails/timeouts, run is marked
Run Locally (Backend + Frontend)
- Create and activate a virtual environment.
- Install backend dependencies.
- Apply migrations.
- Run the backend.
- Install frontend dependencies and run Vite.
python -m venv .venv
.\.venv\Scripts\activate
pip install -r requirements.txt
python manage.py migrate
python manage.py runserverIn a second terminal:
cd frontend
npm install
npm run devRequired for async review execution in a third terminal:
celery -A backend worker -l infoURLs:
- Backend:
http://localhost:8000 - Frontend:
http://localhost:5173
Run with Docker Compose
docker compose up --buildCompose services:
db(PostgreSQL 16)redis(Celery broker/result backend)web(Django API on port 8000)worker(Celery worker)frontend(Vite dev server on port 5173)
Environment
Use .env (or copy from .env.example) for configuration:
LLM_PROVIDER(mockoropenai)OPENAI_API_KEYOPENAI_MODELOPENAI_EMBEDDING_MODELDB_NAME,DB_USER,DB_PASSWORD,DB_HOST,DB_PORTCELERY_BROKER_URL,CELERY_RESULT_BACKENDREVIEW_MAX_CONCURRENT_RUNS,REVIEW_RATE_LIMIT_PER_MINUTEREVIEW_ENABLE_PIPELINE_CACHE,REVIEW_CACHE_TTL_SECONDSREVIEW_ENABLE_EMBEDDINGS,REVIEW_EMBEDDING_PROVIDER,REVIEW_EMBEDDING_DIMREVIEW_FINDINGS_DEFAULT_PAGE_SIZE,REVIEW_FINDINGS_MAX_PAGE_SIZE
Note:
- If
LLM_PROVIDER=mock, analysis runs without external API calls. - If
LLM_PROVIDER=openaiand no API key is set, the code falls back to mock findings. - Default embedding provider is
mock; setREVIEW_EMBEDDING_PROVIDER=openaito use OpenAI embeddings. - For existing findings, run embedding backfill:
python manage.py backfill_finding_embeddings --batch-size 100
Validation Commands
.\.venv\Scripts\python.exe manage.py check
.\.venv\Scripts\python.exe manage.py test -v 1
cd frontend; npm run buildProject Docs
docs/overview.mddocs/architecture.mddocs/MVP_Checklist.mddocs/PHASE_2_Checklist.mddocs/POST_MVP_PLAN.mddocs/AI_Legal_ARCHITECTURE.md(legacy pointer)docs/verification_logs/
License
MIT