Features · Architecture · Standalone Scripts · Quick Start · Deployment · Structure · Contributing
Upload PDFs and ask questions using a Corrective RAG (CRAG) pipeline that evaluates retrieval quality, routes between document and web sources, and synthesizes grounded answers.
- Corrective RAG Pipeline — 6-node LangGraph graph that evaluates each retrieved chunk, then routes between document refinement or web search based on relevance scores
- Multi-Source Routing — CORRECT (score >= 0.7) refines document chunks, INCORRECT (< 0.3) and AMBIGUOUS (mixed) fall back to Tavily web search
- Multi-PDF Ingestion — Upload one or more PDFs, search across all at once
- Sentence-Level Filtering — Refines retrieved chunks into sentences, then keeps only relevant ones before generation
- Dual LLM Fallback Chains — Generation: Mistral -> Groq; Scoring: Gemini with optional Mistral backup for rate limits
- Persistent Vector Store — ChromaDB with HuggingFace embeddings (all-MiniLM-L6-v2)
- Streamlit Chat UI — Clean chat interface with avatars, suggestion chips, and custom styling
flowchart LR
PDF["PDF Upload(s)"] --> Loader["PyPDFLoader"]
Loader --> Splitter["RecursiveCharacterTextSplitter<br/>(chunk=900, overlap=150)"]
Splitter --> Embed["HuggingFace Embeddings<br/>(all-MiniLM-L6-v2)"]
Embed --> Vector[("ChromaDB<br/>Vector Store")]
Query["User Question"] --> Retrieve["Retrieve<br/>(k=5)"]
Vector --> Retrieve
Retrieve --> Eval["Evaluate Each Doc<br/>(scorer LLM)"]
Eval -->|"score >= 0.7<br/>CORRECT"| Refine["Refine<br/>(filter sentences)"]
Eval -->|"score < 0.3<br/>INCORRECT"| Rewrite["Rewrite Query"]
Eval -->|"mixed<br/>AMBIGUOUS"| Rewrite
Rewrite --> WebSearch["Web Search<br/>(Tavily)"]
WebSearch --> Generate["Generate Answer<br/>(LLM)"]
Refine --> Generate
Generate --> Answer["Answer"]
Query --> Answer
| Component | Stack |
|---|---|
| Frontend | Streamlit (chat UI, avatars, suggestion chips) |
| Orchestration | LangGraph StateGraph (6 nodes, conditional edges) |
| Document Loading | PyPDFLoader + RecursiveCharacterTextSplitter (900 chars, 150 overlap) |
| Embeddings | HuggingFace all-MiniLM-L6-v2 |
| Vector Store | ChromaDB (persistent local storage, similarity search k=5) |
| Generation LLM | Mistral mistral-small-latest -> Groq gpt-oss-120b fallback |
| Scorer LLM | Gemini gemini-flash-lite-latest (optional Mistral backup via second API key to avoid rate limits) |
| Web Search | Tavily (lazy-initialized, optional) |
| Secrets | st.secrets with os.getenv() fallback |
app.py and create_database.py are the original single-file versions of the pipeline — the stepping stones that grew into the full ChatPDF app. app.py runs a basic RAG chain using Groq Llama 3.3 70B with MMR retrieval and a simple prompt, no evaluation or web search fallback. create_database.py loads a single PDF, splits it into chunks, and builds the ChromaDB vector store locally.
Use them as a reference for the pipeline logic, or run them standalone:
python create_database.py
python app.pyThe full ChatPDF app (frontend.py + backend.py) is the evolved version — a Corrective RAG pipeline with LangGraph, chunk evaluation, web search fallback, sentence-level filtering, and a polished Streamlit chat UI.
git clone https://github.com/kairav7220/ChatPDF.git
cd ChatPDF
pip install -r requirements.txtSet your API keys in .env:
MISTRAL_API_KEY="your-mistral-key"
MISTRAL_API_KEY_2="your-second-mistral-key"
GROQ_API_KEY="your-groq-key"
GOOGLE_API_KEY="your-google-gemini-key"
HUGGINGFACE_API_TOKEN="your-hf-token"
TAVILY_API_KEY="your-tavily-key"streamlit run frontend.py- Upload — Add PDFs via the sidebar and click "Index documents". PDFs are split into chunks (900 chars, 150 overlap) and embedded into ChromaDB.
- Retrieve — System fetches the 5 most similar chunks from ChromaDB using cosine similarity.
- Evaluate — Each retrieved chunk is scored for relevance (0.0-1.0) by the scorer LLM. Chunks scoring >= 0.7 are marked as good.
- Route — Based on scores:
- CORRECT (at least one chunk >= 0.7): Filter good chunks through sentence-level relevance check, then generate answer from refined context.
- INCORRECT (all chunks < 0.3): Rewrite the question into a web search query, search via Tavily, generate answer from web results.
- AMBIGUOUS (mixed scores, none >= 0.7 but not all < 0.3): Same path as INCORRECT — rewrite and search the web.
On Streamlit Community Cloud, add your keys in Settings > Secrets:
MISTRAL_API_KEY = "your-mistral-key"
MISTRAL_API_KEY_2 = "your-second-mistral-key"
GROQ_API_KEY = "your-groq-key"
GOOGLE_API_KEY = "your-google-gemini-key"
HUGGINGFACE_API_TOKEN = "your-hf-token"
TAVILY_API_KEY = "your-tavily-key"
CHROMA_DIR = "chroma_db"ChatPDF/
├── frontend.py # Streamlit UI (chat, avatars, suggestion chips)
├── backend.py # CRAG pipeline (LangGraph, 6 nodes, dual LLM chains)
├── app.py # Original standalone RAG chain (reference)
├── create_database.py # Original ChromaDB builder (reference)
├── requirements.txt # Python dependencies
├── CONTRIBUTING.md # Contribution guide
├── .env.example # Required API keys template
├── .gitignore
├── .streamlit/
│ ├── config.toml # App theme
│ └── secrets.toml # Your API keys (gitignored)
└── chroma_db/ # Persistent vector store (gitignored)
MIT © kairav7220
Built with LangChain · LangGraph · ChromaDB · Mistral · Groq · Gemini · Tavily · Sentence Transformers