Skip to content

chaudhary-pawan/querymypdf

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

46 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ“„ QueryMyPDF β€” AI-Powered Document Assistant

Python Streamlit LangGraph Gemini FAISS License

Upload any PDF. Ask anything. Get instant, context-aware AI answers.

Live Demo Β· Report Bug Β· Request Feature


🌟 Overview

QueryMyPDF is a production-grade, cloud-deployable AI document assistant built on a Retrieval-Augmented Generation (RAG) pipeline. It lets users upload PDF files and have rich, intelligent conversations about their content β€” powered by Google's Gemini 2.5 Flash LLM, a hybrid BM25 + FAISS retrieval engine, and a LangGraph agentic workflow with persistent memory.

The app ships with a fully custom dark-themed Streamlit UI (glassmorphism design, animated bubbles, real-time token streaming) that looks and feels like a modern chat product.


✨ Features

πŸ€– AI & RAG Pipeline

  • Hybrid Retrieval β€” combines BM25 (keyword/lexical search) and FAISS (semantic/vector search) for best-of-both-worlds document retrieval; results are merged and deduplicated so the LLM always gets the most relevant context
  • Gemini 2.5 Flash LLM β€” Google's latest fast multimodal model via langchain-google-genai
  • HuggingFace Embeddings β€” sentence-transformers/all-MiniLM-L6-v2 for fast, high-quality sentence embeddings (free tier friendly)
  • LangGraph Agentic Loop β€” the chatbot is a proper tool-calling agent (not a simple chain): it decides when to call the RAG tool and can handle conversational turns without unnecessary retrieval
  • Persistent Conversation Memory β€” SqliteSaver checkpointer persists the full message graph per session, surviving page re-runs

πŸ“„ Document Handling

  • Upload any PDF via the sidebar
  • Text extracted page-by-page using PyPDFLoader
  • Smart chunking with RecursiveCharacterTextSplitter (1 500-character chunks, 150-character overlap) β€” preserves semantic coherence across page boundaries
  • Per-session retriever storage keyed by UUID thread ID β€” multiple users are fully isolated

πŸ’¬ Chat UX

  • Real-time streaming β€” tokens appear word-by-word as the model generates them; a blinking cursor shows live generation
  • Stop generation button β€” interrupt streaming at any point
  • Graceful error handling β€” distinguishes between rate-limit (429), service unavailable (503), and generic errors, surfacing human-readable messages
  • Fallback invoke path if the stream yields no content
  • Clear Chat button to reset conversation while keeping the document loaded
  • Document metadata panel shows filename, page count, and chunk count after indexing

🎨 UI / Design

  • Fully custom CSS dark theme (#08081a background, radial violet/indigo gradients)
  • Glassmorphism sidebar with backdrop-filter: blur(24px)
  • Animated chat bubbles β€” user messages aligned right (purple gradient), AI messages aligned left (frosted glass)
  • Animated pulsing dot on the AI label during generation
  • Floating empty-state illustrations with CSS @keyframes animation
  • Responsive layout using Streamlit wide mode
  • Google Fonts: Outfit (headings) + Inter (body)

βš™οΈ Performance & Reliability

  • Exponential back-off retry on embedding calls β€” handles HuggingFace free-tier 429 / 503 errors automatically (up to 5 retries, starting at 2 s)
  • Batch embedding β€” texts are embedded in batches of 50 with a 1-second pause between batches to stay within rate limits
  • @st.cache_resource on the LLM, embedding model, and compiled graph β€” these are built exactly once per server process, keeping cold start times low
  • SQLite-backed graph checkpointing β€” no Redis or external service needed

πŸ—οΈ Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   Streamlit UI (APP.py)              β”‚
β”‚  Sidebar: PDF Upload β†’ "Build Knowledge Base" btn   β”‚
β”‚  Main: Scrollable chat container + chat_input bar   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                   β”‚ ingest_pdf()
                   β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚               PDF Ingestion Pipeline                 β”‚
β”‚  PyPDFLoader β†’ RecursiveCharacterTextSplitter        β”‚
β”‚     β†’ HuggingFace Embeddings (batch + retry)         β”‚
β”‚     β†’ FAISS vector store  +  BM25 retriever          β”‚
β”‚  Stored in _THREAD_RETRIEVERS[thread_id]             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                   β”‚ chatbot.stream()
                   β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              LangGraph Agent (StateGraph)            β”‚
β”‚                                                      β”‚
β”‚   START β†’ [chat_node] ──tools_condition──► [tools]  β”‚
β”‚                β–²                                 β”‚   β”‚
β”‚                β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                      β”‚
β”‚  chat_node: Gemini 2.5 Flash + bound rag_tool        β”‚
β”‚  tool_node: rag_tool β†’ FAISS + BM25 hybrid search   β”‚
β”‚  checkpointer: SqliteSaver (chatbot.db)              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Data Flow

  1. Upload β€” User selects a PDF β†’ bytes streamed to ingest_pdf()
  2. Index β€” PDF is loaded, split into chunks, embedded, and stored in FAISS + BM25 under the session's thread_id
  3. Query β€” User types a question β†’ HumanMessage fed into the LangGraph agent
  4. Retrieve β€” Agent calls rag_tool(query, thread_id) β†’ BM25 finds keyword matches, FAISS finds semantic matches β†’ top 8 unique chunks returned as context
  5. Generate β€” Gemini 2.5 Flash synthesizes an answer from the retrieved context, streamed token-by-token to the UI
  6. Persist β€” Full message history stored in SQLite, survives Streamlit re-runs

πŸ› οΈ Tech Stack

Layer Technology Purpose
UI Streamlit + Custom CSS Interactive chat interface
LLM Google Gemini 2.5 Flash Natural language generation
Embeddings HuggingFace all-MiniLM-L6-v2 Dense vector representations
Vector Store FAISS (CPU) Semantic similarity search
Keyword Search BM25 (rank-bm25) Lexical / term-frequency search
Agent Framework LangGraph StateGraph Tool-calling agent orchestration
Memory LangGraph SqliteSaver Persistent conversation history
PDF Parsing LangChain PyPDFLoader Page-level text extraction
Text Splitting RecursiveCharacterTextSplitter Semantic chunking
Orchestration LangChain Core Chains, tools, message types
Deployment Vercel Static output / serverless

πŸ“‚ Project Structure

QueryMyPDF/
β”œβ”€β”€ APP.py              # Streamlit frontend β€” UI, state management, streaming
β”œβ”€β”€ RAG_backend.py      # RAG pipeline β€” ingestion, retrieval, LangGraph agent
β”œβ”€β”€ embed_test.py       # Utility script to test Google GenAI embeddings
β”œβ”€β”€ requirements.txt    # Python dependencies
β”œβ”€β”€ vercel.json         # Vercel deployment configuration
β”œβ”€β”€ assets/             # Screenshot images for documentation
β”‚   β”œβ”€β”€ PDF_loader.png
β”‚   β”œβ”€β”€ PDF_loader2.png
β”‚   β”œβ”€β”€ response.png
β”‚   β”œβ”€β”€ response1.png
β”‚   β”œβ”€β”€ source_doc.png
β”‚   └── source_doc1.png
└── public/
    └── index.html      # Static landing / redirect page

πŸ“Έ Screenshots

Open the application

Visit Screenshot
QueryMyPDF Open App

πŸ“‚ PDF Upload & Indexing

PDF Upload

πŸ’¬ Build Knowledge base

Knowledge Base

πŸ’¬ Chat

Chat Part 1 Chat Part 2


πŸš€ Getting Started

Prerequisites


Want to test the product ?

Visit - https://querymypdf.onrender.com/

πŸ§‘β€πŸ’» Usage

  1. Upload a PDF using the sidebar file uploader
  2. Click ⚑ Build Knowledge Base to index the document (chunks + embeddings are built)
  3. The sidebar shows a green βœ” Active badge with page and chunk counts
  4. Ask any question in the chat input bar at the bottom
  5. Watch the answer stream in real-time β€” press ⏸ to stop generation at any time
  6. Click πŸ—‘οΈ Clear Chat to start a fresh conversation on the same document

πŸ”‘ Environment Variables

Variable Required Description
GOOGLE_API_KEY βœ… Yes Google Gemini API key
GEMINI_API_KEY Optional Alias for GOOGLE_API_KEY
HF_TOKEN βœ… Yes HuggingFace API token for embeddings

🧠 Key Design Decisions

Why Hybrid Retrieval (BM25 + FAISS)?

Pure vector search can miss exact keyword matches (e.g., specific names, codes, dates). Pure BM25 misses semantic similarity. Combining both and deduplicating gives consistently better recall across document types.

Why LangGraph over a simple chain?

LangGraph models the chatbot as a stateful agent with conditional edges. The LLM decides whether retrieval is necessary β€” avoiding unnecessary API calls on greetings or follow-up clarifications, and allowing the architecture to scale to additional tools (web search, calculator, etc.) without refactoring.

Why SQLite for memory?

Zero-dependency persistence. The SqliteSaver checkpointer stores the full message graph per thread_id, meaning conversation context survives Streamlit widget re-runs without any external infrastructure.

Why batch embedding with exponential back-off?

HuggingFace Inference API free tier enforces strict rate limits. Batching (50 texts/batch) and backing off on 429/503 responses makes the app reliable without requiring a paid plan.


πŸ“¦ Dependencies

streamlit                    # Web UI framework
langchain>=0.3               # LLM orchestration
langchain-core>=0.3          # Core abstractions
langchain-community>=0.3     # FAISS, BM25, PyPDFLoader
langchain-text-splitters>=0.3
langchain-google-genai>=2.0  # Gemini LLM integration
langchain-huggingface>=0.1   # HuggingFace embeddings
langgraph                    # Agentic state graph
langgraph-checkpoint-sqlite  # SQLite memory checkpointer
faiss-cpu                    # Vector similarity search
rank-bm25                    # BM25 keyword retrieval
pypdf                        # PDF parsing
python-dotenv                # .env file support
requests                     # HTTP utilities
google-genai                 # Google GenAI SDK

🀝 Contributing

Contributions are welcome! Please open an issue first to discuss what you'd like to change.

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

πŸ“„ License

Distributed under the MIT License. See LICENSE for more information.


πŸ‘€ Author

Pawan Chaudhary
GitHub: @chaudhary-pawan


Made by Pawan using Streamlit Β· LangGraph Β· Gemini Β· FAISS

About

QueryMyPDF is an AI-powered RAG application that lets users upload PDFs and interact with them conversationally. It extracts text, converts it into searchable embeddings using FAISS, retrieves relevant sections, and generates natural answers using Gemini LLM.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages