FastAPI backend for RAGfolio β a multimodal RAG pipeline that ingests ICICI Prudential mutual fund factsheets and answers queries using both retrieved text chunks and rendered chart images.
π₯οΈ Frontend repo: RAGfolio-Frontend
π Live demo: ragfolio-frontend.vercel.app
- Multi modal Ability:
The pipeline is structured into two primary workflows: Ingestion and Retrieval/Generation.
- Ingestion Engine:
- Parses complex PDFs and logically chunks text based on semantic document structure (e.g., font sizes, headers, sub-headers) rather than arbitrary token limits.
- Visually clusters vector paths to detect, isolate, and extract charts and diagrams.
- Embeds both text chunks and images into a single, unified multimodal vector space.
- Multimodal Retrieval:
- A single natural language query searches the vector space for both relevant text excerpts and relevant charts simultaneously.
- Retrieved text and high-resolution base64-encoded images are injected directly into a multimodal LLM prompt to synthesize an accurate, well-cited response.
Standard RAG splits text every N tokens, which breaks mid-sentence and destroys context. This pipeline uses PyMuPDF to analyse font sizes across the document and classify every text block as page_title, section_header, sub_header, or body. Chunks are delimited by section boundaries β a chunk only closes when a new header appears. For sections exceeding 2,000 tokens, a RecursiveCharacterTextSplitter fallback is applied within the section, preserving the section metadata on every sub-chunk.
Instead of extracting all embedded images (which includes logos, banners, decorative lines), the pipeline runs a union-find spatial clustering algorithm on PDF vector drawing paths. Clusters that meet minimum area and element count thresholds are flagged as chart regions and rendered to high-resolution PNGs. This separates meaningful financial charts from page decoration without any ML classifier.
Text chunks and chart images share a single Qdrant collection with a modality field. Gemini Embedding 2.0 is natively multimodal β both modalities project into the same 3072-dimension space. A text query can therefore surface relevant charts directly without an intermediate OCR or captioning step.
In a shared embedding space, text-to-text cosine similarity scores (~0.65β0.75) are systematically higher than text-to-image scores (~0.32β0.47). A single unified threshold would either flood results with text and suppress images, or lower the bar enough to return noisy text matches. The solution: two parallel query_points calls with separate thresholds (text: 0.5, image: 0.3), merged before the LLM prompt is built.
- Financial Analysis: Querying annual reports where critical data is locked inside complex charts.
- Medical Records: Searching patient histories that include both typed notes and diagnostic imagery.
- Technical Manuals: Retrieving instructions that heavily rely on accompanying diagrams and schematics.
app/rag/extract.py: The core ingestion engine handling PDF parsing, font-size classification, and visual clustering.app/rag/embedding.py: Manages the generation of unified multimodal embeddings via Google GenAI.app/rag/qdrant.py: Interfaces with Qdrant for creating collections, indexing payloads, and parallel querying.app/rag/retrival.py: Orchestrates the final retrieval step, combining text and images into a single prompt for the Gemini LLM.
- Python 3.11+
- A Qdrant Cloud cluster (free tier works)
- A Google AI Studio API key with Gemini access
- An Auth0 application (Single Page App type)
git clone https://github.com/jaygajera17/RAGfolio-Backend.git
cd RAGfolio-Backend
python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS / Linux
source .venv/bin/activatepip install -r requirements.txtCreate a .env file in the project root:
# Google Gemini
GOOGLE_API_KEY=your-google-ai-studio-api-key
# Qdrant Cloud
QDRANT_HOST=https://your-cluster.qdrant.io
QDRANT_API_KEY=your-qdrant-api-key
DEFAULT_COLLECTION=test2
PDF_PATH=static/fund-factsheet-for-may-2026-51-97.pdf
# Auth0
AUTH0_DOMAIN=your-tenant.auth0.com
AUTH0_CLIENT_ID=your-auth0-client-id
AUTH0_CLIENT_SECRET=your-auth0-client-secret
APP_BASE_URL=http://127.0.0.1:8000| Variable | Where to find it |
|---|---|
GOOGLE_API_KEY |
Google AI Studio β API keys |
QDRANT_HOST |
Qdrant Cloud dashboard β your cluster β Endpoint |
QDRANT_API_KEY |
Qdrant Cloud dashboard β your cluster β API Keys |
DEFAULT_COLLECTION |
The Qdrant collection name to use (defaults to test2 if not set) |
PDF_PATH |
(Optional) Path to the local PDF file for ingestion (defaults to static/fund-factsheet-for-may-2026-51-97.pdf if not set; not used in production) |
AUTH0_DOMAIN |
Auth0 dashboard β Applications β your app β Domain |
AUTH0_CLIENT_ID |
Auth0 dashboard β Applications β your app β Client ID |
AUTH0_CLIENT_SECRET |
Auth0 dashboard β Applications β your app β Client Secret |
APP_BASE_URL |
Must match exactly what is set in Auth0 callback URLs |
Auth0 callback URLs: In your Auth0 application settings, set Allowed Callback URLs to
http://127.0.0.1:8000/auth/callbackand Allowed Logout URLs tohttp://127.0.0.1:8000.
uvicorn app.main:app --reloadAPI docs available at http://127.0.0.1:8000/docs.
With the server running, trigger ingestion via curl or the /docs UI:
curl -X POST http://127.0.0.1:8000/api/v1/rag/ingest \
-H "Authorization: Bearer YOUR_AUTH0_ACCESS_TOKEN"The backend is deployed to Vercel as a serverless function. Because Vercel has a 250MB deployment size limit, PDF ingestion dependencies (PyMuPDF, etc.) are stripped from the production bundle β ingestion is intended to be run locally or in a separate worker.