An in-house RAG-powered AI tool for attorneys at Edelson PC to query, summarize, and extract facts from case folders.
- Document Ingestion: Upload multiple files (PDFs, Word docs, text files)
- Intelligent Q&A: Ask questions and get cited answers from your documents
- Document Summarization: Generate comprehensive summaries of any document
- Auto Classification: Documents automatically classified by type (deposition, motion, etc.)
- Conversation Memory: Multi-turn conversations with context awareness
- Hybrid Search: Dense + sparse + ColBERT reranking for best retrieval
- Matter Management: Organize documents by case/matter
- Citation Tracking: Every answer includes source references with file names and page numbers
- Backend: FastAPI + Python
- Frontend: React + TypeScript + Vite
- Vector Store: Qdrant
- Database: PostgreSQL
- LLM: Anthropic Claude Sonnet 4
- Embeddings: OpenAI text-embedding-3-small
- Agent Framework: LangGraph
- Document Processing: LangChain
- Python 3.11+
- Node.js 18+
- Docker and Docker Compose
uv(Python package manager)
# Clone the repository
cd casedoc-pro
# Copy environment file
cp .env.example .env
# Edit .env and add your API keys
# OPENAI_API_KEY=your_key_here
# ANTHROPIC_API_KEY=your_key_here# Start Qdrant and PostgreSQL
docker-compose up -d
# Verify services are running
docker-compose pscd backend
# Install dependencies with uv
uv sync
# Run the backend server
uv run python main.pyBackend will be available at http://localhost:8000
cd frontend
# Install dependencies
npm install
# Run the dev server
npm run devFrontend will be available at http://localhost:5173
- Click "Ingest New Folder" in the sidebar
- Enter a matter name (e.g., "Smith v. Acme Corp")
- Choose files to upload (supports multiple files)
- Click "Ingest" and wait for processing
The system will:
- Parse all PDFs, DOCX, and TXT files
- Automatically classify document types (deposition, motion, etc.)
- Extract text and chunk documents
- Create hybrid embeddings (dense + sparse + ColBERT)
- Store in Qdrant vector database and PostgreSQL
- Select a matter from the sidebar
- Type your question in the chat input
- Press Enter or click "Send"
The AI will:
- Use hybrid search to retrieve relevant chunks
- Generate an answer using Claude with conversation context
- Provide citations with file names and page numbers
Regular Q&A:
- "What did the plaintiff claim in their complaint?"
- "What are the key dates in this case?"
- "Who testified about the safety procedures?"
- "What damages are being sought?"
Follow-up Questions (using conversation history):
- "What did Smith say about the accident?"
- "When did that happen?" ← Knows "that" refers to the accident
- "Were there any witnesses?"
Document Summarization:
- "Summarize deposition_smith.pdf"
- "Give me a summary of the contract"
- "Summarize the motion for summary judgment"
casedoc-pro/
├── backend/
│ ├── main.py # FastAPI app with endpoints
│ ├── config.py # Configuration and settings
│ ├── models.py # Database and API models
│ ├── ingest.py # Document ingestion pipeline
│ ├── retriever.py # Vector search and retrieval
│ ├── agent.py # LangGraph agent (router + RAG)
│ ├── chunker.py # Text chunking with LangChain
│ └── parsers/ # Document loaders
│ └── document_loader.py
├── frontend/
│ └── src/
│ ├── App.tsx # Main app with sidebar
│ ├── api/
│ │ └── client.ts # API client functions
│ └── components/
│ ├── ChatWindow.tsx
│ ├── MessageBubble.tsx
│ └── CitationChip.tsx
├── docker-compose.yml # Qdrant + PostgreSQL
├── .env.example # Environment template
└── README.md
Ingest a folder of documents into a matter.
Request:
{
"matter_name": "Smith v. Acme Corp",
"folder_path": "/path/to/case/folder"
}Response:
{
"matter_id": "uuid",
"files_processed": 24,
"chunks_created": 312,
"files_skipped": [],
"skipped_reason": ""
}Ask a question about case documents.
Request:
{
"matter_id": "uuid",
"message": "What did the plaintiff claim?"
}Response:
{
"answer": "According to the complaint...",
"citations": [
{
"file_name": "complaint.pdf",
"page_number": 3,
"chunk_id": "uuid"
}
]
}Get all matters with document counts.
Response:
[
{
"matter_id": "uuid",
"matter_name": "Smith v. Acme Corp",
"document_count": 24,
"created_at": "2025-02-25T12:00:00"
}
]Completed Features:
- ✅ Multi-file upload ingestion (PDF, DOCX, TXT)
- ✅ Fixed chunking (400 tokens, 80 overlap)
- ✅ Hybrid search (dense + sparse + ColBERT reranking)
- ✅ LangGraph agent with routing
- ✅ Document summarization
- ✅ Automatic doc-type classification (using Claude)
- ✅ Conversation history and context
- ✅ Chat interface with citations
- ✅ Matter management
- ✅ Production-grade logging
- ✅ Comprehensive error handling
Not Yet Implemented (V2+):
- ❌ Doc-type-aware chunking strategies
- ❌ Neighbor chunk expansion
- ❌ Multi-agent orchestration with parallel execution
- ❌ Fact extraction agent
- ❌ Drafting and critique agents
- ❌ Authentication
- ❌ Streaming responses
Import errors:
cd backend
uv syncDatabase connection errors:
docker-compose down
docker-compose up -dType errors:
cd frontend
npm installCORS errors: Make sure backend is running on port 8000 and frontend on port 5173.
- Add MMR re-ranking for better retrieval diversity
- Implement doc-type-aware chunking
- Add neighbor chunk context expansion
- Claude-powered metadata extraction
- Document drafting capabilities
- Streaming responses
- Authentication and multi-user support
- Advanced reranking (Voyage/Cohere)
Internal use only - Edelson PC
⏺ Excellent document! This is a very well-thought-out design. Let me break down my analysis:
🎯 My Assessment: This is brilliant and feasible
What I Love About This Design:
- Three-Level Classification is Smart - Level 1 (structural signals) handles 70-80% for free ✅ - Level 2 (targeted AI) handles edge cases cheaply ✅ - Level 3 (paragraph fallback) always works ✅ - This is production-grade thinking - not just "throw it all at Claude"
- Doc-Type Aware Chunking Makes Sense - Keeping Q&A exchanges together for depositions is exactly right - Section-based chunking for motions preserves legal arguments - One line per docket entry is perfect - This will dramatically improve retrieval quality
- Retrieval Controls UI is Genius - Gives attorneys control without overwhelming them - Auto-suggests smart defaults based on doc type - Search provenance line builds trust - This is what makes the tool attorney-grade vs. toy
- Single-Pass Architecture - Classify → Chunk → Embed in one pass - No re-chunking needed later - Efficient and clean
📊 Implementation Complexity Analysis
Easy Wins (1-2 days each):
✅ Retrieval Controls UI (search scope, fetch K, rerank K)
- Just add 3 dropdowns + state management
- Modify retriever to accept these parameters
- Already have hybrid search, just expose the knobs
✅ Dynamic Doc Type Dropdown
- New endpoint: GET /matters/{matter_id}/doc_types
- Query: SELECT doc_type, COUNT(*) FROM documents GROUP BY doc_type
- Already storing doc_type, just expose it
✅ Search Provenance Line
- Return metadata from retrieval agent
- Display under each response
Medium Effort (3-5 days):
🟡 Level 1: Structural Signal Detection
- Write signal detectors for each doc type
- This is just pattern matching, no AI needed
- Test with your existing RFA files
🟡 Level 2: Targeted AI Classification
- Already have Claude integration
- Just need the JSON prompt + fallback logic
- 500 tokens = very cheap
🟡 Schema Additions
- Add classification_confidence, boundary_type, avg_chunk_tokens
- These are simple column adds
Big Lift (1-2 weeks):
🔴 Doc-Type Aware Chunking
- Need custom chunkers for each doc type:
- chunk_deposition() - Q&A exchange detection
- chunk_motion() - Section heading detection
- chunk_docket() - Line-by-line split
- chunk_medical() - Encounter boundary detection
- chunk_fallback() - Paragraph-aware sliding window
This is the heavyweight refactor - but it's isolated to the chunking module.
🔴 Retrieval Config Per Doc Type
- Modify retriever to adjust fetch_k/rerank_k based on doc_type
- Not hard, but needs testing across doc types
🚧 Refactoring Impact Assessment
What Breaks:
❌ Nothing breaks! This is additive.
What Changes:
- chunker.py - biggest change - Currently: One chunk_documents() function - New: Router function + 5 specialized chunkers - Old function stays as fallback
- ingest_files_hybrid.py - Add Level 1 structural detection - Call Level 2 if confidence low - Route to appropriate chunker based on doc_type
- retriever_hybrid.py - Accept doc_type_filter, fetch_k, rerank_k parameters - Apply doc_type filter to Qdrant query - Use custom K values instead of hardcoded 50/15
- models.py - Add 3 columns to documents table - Add new Pydantic models for retrieval controls
- Frontend - Add 3 dropdowns above chat - Update ChatRequest to include new params - Display provenance line
🎯 Recommended Implementation Order
I suggest phased rollout so you can test each piece:
Phase 1: Retrieval Controls (Quick Win - 1-2 days)
Enable attorneys to control what they're searching without waiting for smart chunking.
- Add retrieval controls UI (3 dropdowns)
- Add GET /matters/{matter_id}/doc_types endpoint
- Modify retriever to accept filters
- Add provenance line
Value: Immediate control over search scope. Works with existing chunks.
Phase 2: Better Classification (Medium - 3-4 days)
Get accurate doc types for better filtering.
- Implement Level 1 structural detectors
- Implement Level 2 Claude-based fallback
- Add confidence scoring
- Re-classify existing documents (migration script)
Value: Doc type filter actually works reliably.
Phase 3: Smart Chunking (Big - 1-2 weeks)
The real quality improvement.
- Build specialized chunkers (one at a time)
- Test each chunker independently
- Add router logic in ingestion
- Add avg_chunk_tokens tracking
- Implement retrieval config lookup
Value: Dramatically better retrieval quality. This is the differentiator.
💡 My Recommendation
Start with Phase 1 immediately. It's high-value, low-risk, and gives you:
- Working doc type filter (with current "unknown" → "all")
- Exposed fetch_k/rerank_k knobs
- Search provenance
Then we can tackle classification and smart chunking when you're ready.
🤔 Questions for You:
- Do you want to start with Phase 1 (retrieval controls)? This is the quickest win and works with what you have now.
- Do you have sample documents of each type (deposition, motion, docket, medical) so we can test the chunkers properly?
- Should we reclassify your existing RFA documents first, or test on fresh uploads?
What do you think? Want to start with Phase 1?