Legal help is expensive, jargon-heavy, and often out of reach for people who need it most. For PromptWars: Virtual (Exclusive Edition) β a hackathon by Hack2Skill β I built Sahayak, a RAG-based legal assistant that answers questions and summarizes documents in plain language, grounded in real context instead of confident guesses.
π Live: https://legal-ai-eight-liart.vercel.app/
π Code: https://github.com/Divyansh0208/Legal-AI (MIT)
What it does
- Ask β a tenancy, consumer-rights, or contract question. Answers are retrieved from indexed context, not hallucinated. No matching context? It says so, instead of guessing.
- Upload β a PDF (scanned or native) β plain-language summary: what kind of document it is, what it obligates you to, what's worth double-checking.
- Voice β ask by speaking, transcribed via Whisper. Every response is grounded in retrieved context and explicit about what it can't confirm β no confident guessing when the docs don't cover something.
Architecture
User (voice/text/doc)
β
βΌ
React + Vite Frontend
β REST
βΌ
FastAPI Backend
βββΊ Groq Whisper API (speech-to-text)
βββΊ PyMuPDF + pytesseract (parsing / OCR)
βββΊ sentence-transformers (embeddings)
βββΊ ChromaDB (vector retrieval)
βββΊ Groq LLM API (answer generation)
βββΊ SQLite (app data)
The RAG hot path is intentionally thin β no framework ceremony for a single retrieval + one chat completion call:
def answer_question(question: str) -> dict:
hits = retrieve(question)
context = "\n\n---\n\n".join(h["text"] for h in hits) if hits else ""
completion = _client.chat.completions.create(
model=settings.GROQ_LLM_MODEL,
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": RAG_ANSWER_TEMPLATE.format(context=context, question=question)},
],
temperature=0.2,
max_tokens=800,
)
return {"answer": completion.choices[0].message.content,
"sources": [{"source": h["source"], "distance": h["distance"]} for h in hits]}
The part I'm actually proud of: the guardrails
Anyone can wire an LLM to a text box. The parts that took real effort:
- Untrusted-context system prompt β uploaded documents and retrieved chunks are treated as data, never instructions. Prompt-injection resistant by design.
-
Real upload validation β
python-magicsniffs actual file bytes; a relabeled.exedoesn't sneak through as.pdf. -
Rate limiting β
slowapion query, upload, and voice endpoints. - Security headers β CSP, X-Frame-Options, HSTS in production.
-
5 CI workflows on every push β lint, pytest, bandit (SAST), gitleaks (secrets), pip-audit (deps), axe-core (a11y) β auto-deploy to Render/Vercel on merge to
main. None of that shows up in a demo GIF. All of it is why the demo GIF is trustworthy.
Stack
| Layer | Tech |
|---|---|
| Backend | FastAPI |
| LLM | Groq (openai/gpt-oss-120b) |
| Retrieval | ChromaDB + sentence-transformers (all-MiniLM-L6-v2) |
| Parsing | PyMuPDF, pytesseract |
| Speech | Groq Whisper |
| Frontend | React + Vite, Tailwind |
| DB | SQLite |
| Deploy | Render + Vercel |
What's next
- Multi-language support (deferred)
- Persisted chat sessions (currently in-memory)
- More jurisdiction-specific templates If you've hit a real legal-access gap, or want to poke holes in the grounding/refusal behavior β feedback welcome. MIT-licensed, issues/PRs open.












