Chaitali-24csu246/Legislative-Analyser
Legal Document Analyzer
Special focus on token compression
AI-assisted legal document review. Please note that this is not a substitute for qualified legal advice
A local, legal document analyzer built with Streamlit, LangGraph, and Ollama. Upload a contract or legal PDF and get an executive summary, flagged risks, and improvement suggestions. Everything is processed on your own machine, no data is sent to external APIs.
Table of Contents
- Features
- Architecture Overview
- Token Compression Pipeline
- Agent Graph
- Setup Instructions
- Configuration
- Usage
- Deployment
- Technical Reference
- Limitations
Features
Architecture Overview
┌─────────────────────────────────────────────────────────┐
│ Streamlit UI │
│ Sidebar (upload · model select) │ Main (tabs · DL) │
└───────────────────┬─────────────────────────────────────┘
│ PDF / TXT bytes
▼
┌─────────────────────────────────────────────────────────┐
│ load_doc() │
│ TXT → decode │ PDF → pymupdf4llm → Markdown │
└───────────────────┬─────────────────────────────────────┘
│ raw text
▼
┌─────────────────────────────────────────────────────────┐
│ preprocess_node (Layer 1-3) │
│ strip_noise → dedup_paragraphs → chunk_document │
└──────┬────────────────────────────────────────┬─────────┘
│ chunks │ meta
▼ ▼
┌──────────────────────────────────┐ ┌───────────────┐
│ route_chunks (Layer 4) │ │ Metrics row │
│ RISK_KEYWORDS / SUGGEST_KEYWORDS│ │ (UI only) │
└──────┬──────────────┬────────────┘ └───────────────┘
│ │
┌────▼────┐ ┌─────▼──────┐ ┌──────────────┐
│summarize│ │ risks │ │ suggestions │
│ node │ │ node │ │ node │
│(map-red)│ │(top-5 chks)│ │(top-5 chks) │
└────┬────┘ └─────┬──────┘ └──────┬───────┘
└──────────────┴─────────────────┘
│
▼
┌────────────────┐
│ compile_report │
│ _node │
│ + validate_all │
└────────┬───────┘
│
▼
final_report (MD)Token Compression Pipeline
The pipeline is designed to squeeze as many tokens as possible out of a document before it reaches the LLM — preserving legal meaning while cutting noise.
Layer 1 — Noise Strip (strip_noise)
Removes lines that carry zero legal meaning:
- Horizontal rules (
---,═══) - Page numbers (
Page 3 of 14, lone digits) - Boilerplate markers (
[FORM],[SIGNATURE PAGE]) - Signature underscores (
___________) - Lone exhibit/schedule headers
- Collapses 3+ blank lines to 2, strips zero-width spaces
Typical saving: 0–10%
Layer 2 — Paragraph Deduplication (dedup_paragraphs)
Legal contracts repeat definitions, notice clauses, and recital boilerplate. This layer:
- Splits on double newlines
- Normalises each paragraph (lowercase, collapsed whitespace) as a lookup key
- Keeps only the first occurrence of any paragraph ≥ 40 chars
- Always keeps short paragraphs (headings, labels < 40 chars)
Typical saving: 5–15% on long contracts
Layer 3 — Semantic Chunking (chunk_document)
Splits the document into LLM-sized pieces using a priority-ordered pattern list:
Each section is then enforced to CHUNK_WORD_LIMIT = 1,200 words (~1,600 tokens), leaving headroom for the prompt template inside llama3.2's 4k context. Orphan chunks under 50 words are merged with their neighbour.
Layer 4 — Keyword Routing (route_chunks)
Instead of sending all chunks to every LLM node, each node receives only the top-N highest-scoring chunks for its task:
- Risks node: scored against
RISK_KEYWORDS(liability, indemnif, terminat, breach…) → top 5 - Suggestions node: scored against
SUGGEST_KEYWORDS(payment, IP, confidential, arbitrat…) → top 5 - Summary node: receives all chunks via map-reduce (accuracy priority)
- Fallback: if no keywords match (generic/non-standard docs), falls back to first N chunks
Agent Graph
Built with LangGraph StateGraph. The graph is compiled once and cached via @st.cache_resource.
preprocess
│
├──► summarize ──┐
├──► risks ─┤──► compile ──► END
└──► suggestions ──┘summarize, risks, and suggestions run in parallel (LangGraph fan-out). The compile node waits for all three before assembling the final report and running validation.
Map-Reduce Summary
For multi-chunk documents:
- Map phase: each chunk is summarised to ≤
MAX_MINI_WORDS = 100words viaThreadPoolExecutor(up to 6 workers) - Guard: combined mini-summaries are trimmed to
MAX_SYNTHESIS_WORDS = 2,000words if needed - Reduce phase: one LLM call synthesises all mini-summaries into an 8–12 bullet executive summary
Single-chunk documents skip the map phase entirely.
Output Validation
Each LLM output is checked for:
- Non-empty, non-error response
- Minimum 20 words
- Presence of domain-appropriate signal words (e.g. risks output must contain at least 2 of:
risk,concern,issue,breach,high/medium/low…)
Failed validations are shown as warning badges in the UI without blocking the report.
Setup Instructions
Prerequisites
1. Install Ollama
# macOS / Linux
curl -fsSL https://ollama.com/install.sh | sh
# Windows
# Download from https://ollama.com/download
irm https://ollama.com/install.ps1 | iex
2. Pull a model
# Recommended — fast, fits in 4 GB RAM
ollama pull llama3.2:3b
# Alternatively — better quality, needs ~8 GB RAM
ollama pull mistral:7bVerify Ollama is running:
ollama list # should show your pulled model
curl http://localhost:11434 # should return "Ollama is running"3. Clone / download the project
# Navigate to where you want the project first
cd path/to/your/folder # e.g. cd Documents or cd D:\Projects
# Then clone — it will create the folder there
git clone https://github.com/Chaitali-24csu246/Legislative-analyser
cd Legislative-analyser4. Create a virtual environment
python -m venv .venv
# Activate
source .venv/bin/activate # macOS / Linux
.venv\Scripts\activate # Windows PowerShell5. Install dependencies
pip install --upgrade pip
pip install -r requirements.txt6. Configure environment (optional)
Create a .env file in the project root if your Ollama instance is not on the default port:
OLLAMA_BASE_URL=http://localhost:11434The default is http://localhost:11434 — you only need this file if you've changed the port or are running Ollama on a remote host.
7. Run the app
streamlit run app2.pyOpen http://localhost:8501 in your browser.
Configuration
All tuneable constants are at the top of app.py:
Usage
- Open the app at
http://localhost:8501 - Upload a PDF or TXT legal document using the sidebar uploader
- Optionally change the model name in the sidebar (must be already pulled in Ollama)
- Click ⚡ Analyze Document
- View results across four tabs: Summary · Risks · Suggestions · Full Report
- Click ⬇ Download Report (.md) to save the Markdown report
Results are cached by file hash — re-uploading the same document skips re-analysis. Use Clear Cache to force a re-run.
Deployment
Option A — Local (default)
Follow the setup instructions above. Everything runs on your machine.
Option B — Docker
Create a Dockerfile in the project root:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8501
CMD ["streamlit", "run", "app.py", \
"--server.port=8501", \
"--server.address=0.0.0.0", \
"--server.headless=true"]Build and run:
docker build -t legal-analyzer .
# Point OLLAMA_BASE_URL at your host's Ollama instance
docker run -p 8501:8501 \
-e OLLAMA_BASE_URL=http://host.docker.internal:11434 \
legal-analyzerNote:host.docker.internalresolves to your host machine on Docker Desktop (Mac/Windows). On Linux, use--network=hostinstead and setOLLAMA_BASE_URL=http://localhost:11434.
Option C — Streamlit Community Cloud
⚠️ Streamlit Community Cloud has no persistent compute, so Ollama cannot run there. Use this option only if you host Ollama on a separate always-on server (e.g. a VPS or home server with a public URL).
- Push the repo to GitHub (public or private)
- Go to share.streamlit.io → New app
- Select your repo, branch, and
app.py - Under Advanced settings → Secrets, add:
OLLAMA_BASE_URL = "https://your-ollama-server.example.com"- Deploy
Option D — Self-hosted VPS (e.g. DigitalOcean, Hetzner, AWS EC2)
# On the server — install Ollama and pull model
curl -fsSL https://ollama.com/install.sh | sh
ollama pull llama3.2:3b
# Clone and run
git clone https://github.com/your-org/legal-analyzer.git
cd legal-analyzer
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
# Run with nohup or use a systemd service / tmux session
nohup streamlit run app.py \
--server.port=8501 \
--server.address=0.0.0.0 \
--server.headless=true &Open port 8501 in your firewall/security group. For production, put nginx in front as a reverse proxy with HTTPS.
Technical Reference
Key Functions
State Schema (AgentState)
class AgentState(TypedDict, total=False):
original_text : str # raw extracted text
clean_text : str # after noise strip + dedup
chunks : List[str] # semantic chunks
summary : str # executive summary
risks : str # identified risks
suggestions : str # improvement suggestions
final_report : str # compiled Markdown report
validation : Dict[str, Any] # per-field validation results
meta : Dict[str, Any] # compression metricsSupported File Types
Scanned PDFs (image-only, no text layer) will return an error — convert them with OCR first (e.g. Adobe Acrobat, ocrmypdf).
Limitations
- Not legal advice — outputs are AI-generated and may miss critical clauses or misinterpret legal language. Always have a qualified attorney review important documents.
- Scanned PDFs — image-only PDFs cannot be processed without an OCR step.
- Context window — very long documents (100k+ words) may hit chunk limits even after compression. Reduce
CHUNK_WORD_LIMITor switch to a model with a larger context window. - Model quality —
llama3.2:3bis fast but may paraphrase heavily or miss nuance.mistral:7borllama3:8bgive noticeably better results at the cost of speed. - Language — optimised for English-language legal documents.
- Privacy — while inference is local, ensure your Ollama instance is not publicly exposed if processing sensitive documents.
Project Structure
legal-analyzer/
├── app.py # Main application (all logic + UI)
├── requirements.txt # Python dependencies
├── .env # Optional — override OLLAMA_BASE_URL
├── README.md # This file
└── .gitignore # Recommended: exclude .env, .venv/, __pycache__/Recommended .gitignore
.venv/
__pycache__/
*.pyc
.env
*.pdf
*.txt
.streamlit/secrets.tomlBuilt for the HPE Gen AI for GenZ project.
