File size: 10,919 Bytes
1f725d8
5551822
1f1b226
1f725d8
 
 
5551822
1f725d8
1f1b226
1f725d8
 
9c90775
1f725d8
9c90775
 
 
 
 
 
 
 
 
5551822
9c90775
1f725d8
5551822
 
9c90775
5551822
9c90775
5551822
9c90775
5551822
9c90775
 
 
 
 
 
 
 
 
 
5551822
9c90775
5551822
9c90775
5551822
9c90775
 
 
 
 
 
 
 
 
 
1f725d8
 
 
9c90775
1f725d8
9c90775
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1e6d8a7
9c90775
1e6d8a7
9c90775
1e6d8a7
9c90775
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1e6d8a7
 
 
9c90775
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1f725d8
 
 
5551822
1f725d8
 
 
 
9c90775
 
 
 
 
 
 
 
 
 
 
 
5551822
 
1f725d8
9c90775
 
 
 
 
 
1f725d8
 
 
9c90775
 
 
1f725d8
9c90775
 
 
 
 
 
 
1f725d8
 
9c90775
 
1f725d8
9c90775
1f725d8
9c90775
 
1f725d8
 
 
9c90775
1f725d8
 
9c90775
 
1f725d8
 
 
 
9c90775
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
---
title: Multi-Rag
emoji: πŸ€–
colorFrom: blue
colorTo: green
sdk: docker
app_file: main.py
pinned: false
short_description: This is the Multi-Rag Agent
---

# 🧠 Multi-RAG β€” Advanced Multi-Modal Retrieval Augmented Generation

<p align="center">
  <img src="https://img.shields.io/badge/Python-3.12-blue?style=for-the-badge&logo=python" />
  <img src="https://img.shields.io/badge/FastAPI-0.135-green?style=for-the-badge&logo=fastapi" />
  <img src="https://img.shields.io/badge/LangGraph-1.0-orange?style=for-the-badge" />
  <img src="https://img.shields.io/badge/LangChain-1.2-yellow?style=for-the-badge" />
  <img src="https://img.shields.io/badge/FAISS-CPU-red?style=for-the-badge" />
  <img src="https://img.shields.io/badge/Groq-LLM-purple?style=for-the-badge" />
  <img src="https://img.shields.io/badge/Docker-Ready-blue?style=for-the-badge&logo=docker" />
</p>

A production-grade, **session-aware multi-modal RAG system** built with LangGraph, FastAPI, and a hybrid retrieval pipeline. Upload PDFs, DOCX, TXT, or images β€” get intelligent, context-grounded answers powered by an adaptive agentic graph that decides when to search your documents, when to fall back to the web, and when to just chat.

---

## πŸ“Έ Agent Graph

The LangGraph pipeline is fully visualized below β€” each node represents a stage in the decision-making workflow:

![Agent Graph Workflow](graph_visualization.png)

| Node | Role |
|---|---|
| `orchastrator` | Routes query: DB search needed or direct chat? |
| `query_generation` | Generates semantically rich retrieval queries |
| `retreiver` | Hybrid FAISS + BM25 + FlashRank reranking |
| `relevance_checker` | Evaluates if retrieved docs are CORRECT / AMBIGUOUS / INCORRECT |
| `document_refiner` | Passes verified docs to context builder |
| `web_search` | Tavily-powered fallback when docs are insufficient |
| `context_builder` | Assembles multimodal context (text + tables + images) |
| `chat` | Generates final Markdown response |

---

## ✨ Features

- πŸ—‚ **Multi-Format Ingestion** β€” PDF, DOCX, TXT, PNG/JPG/HEIF; all converted to a unified PDF pipeline
- πŸ” **Hybrid Retrieval** β€” FAISS (dense) + BM25 (sparse) via `EnsembleRetriever`, re-ranked with FlashRank
- 🧩 **Multimodal Chunks** β€” Extracts text, tables (HTML), and base64-encoded images from documents
- πŸ€– **Agentic LangGraph Workflow** β€” Adaptive routing with conditional edges; not just a static RAG chain
- 🌐 **Web Search Fallback** β€” Tavily search kicks in when retrieved docs are insufficient
- πŸ’Ύ **Session Persistence** β€” Per-user thread IDs with `InMemorySaver` checkpointing; conversation history preserved
- πŸ” **Auth Middleware** β€” Lightweight session-based authentication on every request
- πŸ–₯ **Full Web UI** β€” Jinja2-rendered frontend with upload flow, chat interface, and document explorer
- 🐳 **Docker Ready** β€” Single `Dockerfile` for deployment; also supports Jenkins CI
- πŸ“ **Rotating Logs** β€” Timestamped rotating log files under `logs/`

---

## πŸ— Architecture

```
Multi-Rag/
β”œβ”€β”€ main.py                          # Entrypoint β€” loads .env, starts FastAPI
β”œβ”€β”€ api/
β”‚   β”œβ”€β”€ main.py                      # FastAPI app, middleware, router registration
β”‚   β”œβ”€β”€ routes/
β”‚   β”‚   β”œβ”€β”€ upload_router.py         # File upload handling
β”‚   β”‚   β”œβ”€β”€ ingest_docs_router.py    # Triggers vectorization pipeline
β”‚   β”‚   β”œβ”€β”€ chat_router.py           # Chat endpoint β†’ LangGraph invocation
β”‚   β”‚   β”œβ”€β”€ user_router.py           # Session/thread management
β”‚   β”‚   β”œβ”€β”€ load_conversation_router.py  # Restore chat history
β”‚   β”‚   └── frontend_router.py       # Serves HTML pages
β”‚   β”œβ”€β”€ middlewares/
β”‚   β”‚   └── Authenticate_middleware.py
β”‚   β”œβ”€β”€ templates/                   # Jinja2 HTML templates
β”‚   └── static/                      # CSS / JS assets
β”‚
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ graphs/
β”‚   β”‚   └── builder.py               # LangGraph StateGraph definition
β”‚   β”œβ”€β”€ nodes/
β”‚   β”‚   └── main_nodes.py            # All 8 node implementations
β”‚   β”œβ”€β”€ states/
β”‚   β”‚   └── Main_State.py            # LangGraph State + Pydantic output schemas
β”‚   β”œβ”€β”€ pipeline/
β”‚   β”‚   β”œβ”€β”€ Vectiorizer_pipeline.py  # Ingestion + Transformation orchestration
β”‚   β”‚   └── GraphRunner_pipeline.py  # Graph execution wrapper
β”‚   β”œβ”€β”€ components/
β”‚   β”‚   β”œβ”€β”€ data_ingestion.py        # File-to-PDF conversion dispatch
β”‚   β”‚   β”œβ”€β”€ data_transformation.py   # PDF β†’ chunks β†’ FAISS vector store
β”‚   β”‚   └── run_graph.py             # graph.ainvoke() wrapper
β”‚   β”œβ”€β”€ retrievers/
β”‚   β”‚   └── create_retreivers.py     # Hybrid retriever + FlashRank compression
β”‚   β”œβ”€β”€ prompts/
β”‚   β”‚   └── prompt_templates.py      # All LLM prompt templates
β”‚   β”œβ”€β”€ entity/
β”‚   β”‚   β”œβ”€β”€ config_entity.py         # Dataclass configs
β”‚   β”‚   └── artifact_entity.py       # Dataclass artifacts
β”‚   β”œβ”€β”€ llm/
β”‚   β”‚   └── llm_loader.py            # Groq ChatGroq instantiation
β”‚   β”œβ”€β”€ memory/
β”‚   β”‚   └── __init__.py              # InMemorySaver checkpointer
β”‚   β”œβ”€β”€ tools/
β”‚   β”‚   └── __init__.py              # Tavily web search StructuredTool
β”‚   β”œβ”€β”€ constants/
β”‚   β”‚   └── __init__.py              # Global constants
β”‚   └── utils/
β”‚       β”œβ”€β”€ ingestion_utils.py       # image_to_pdf, text_to_pdf, docs_to_pdf
β”‚       └── asyncHandler.py          # Async decorator for uniform error handling
β”‚
β”œβ”€β”€ exception/
β”‚   └── __init__.py                  # MyException with structured logging
β”œβ”€β”€ logger/
β”‚   └── __init__.py                  # RotatingFileHandler setup
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ jenkins
└── pyproject.toml
```

---

## πŸ”„ RAG Pipeline Flow

```
User Uploads Files
       β”‚
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              Vectorization Pipeline              β”‚
β”‚                                                  β”‚
β”‚  File β†’ docs_to_pdf β†’ partition_pdf (hi_res)    β”‚
β”‚       β†’ chunk_by_title β†’ FAISS + Embeddings     β”‚
β”‚       β†’ saved per thread_id                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚
       β–Ό
User Sends Chat Message
       β”‚
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                LangGraph Agent                   β”‚
β”‚                                                  β”‚
β”‚  Orchestrator ──→ Query Generation               β”‚
β”‚                       β”‚                          β”‚
β”‚                   Retriever (Hybrid)              β”‚
β”‚                       β”‚                          β”‚
β”‚               Relevance Checker                  β”‚
β”‚              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”                β”‚
β”‚         CORRECT/           INCORRECT             β”‚
β”‚         AMBIGUOUS                β”‚               β”‚
β”‚              β”‚             Web Search            β”‚
β”‚         Document                β”‚                β”‚
β”‚         Refiner                 β”‚                β”‚
β”‚              β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜               β”‚
β”‚                  Context Builder                  β”‚
β”‚                       β”‚                          β”‚
β”‚                     Chat                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

---

## βš™οΈ Tech Stack

| Layer | Technology |
|---|---|
| **LLM** | Groq (`llama-3.x` / configurable) |
| **Embeddings** | `sentence-transformers/all-MiniLM-L6-v2` via HuggingFace |
| **Vector Store** | FAISS (CPU) |
| **Sparse Retrieval** | BM25 (`rank-bm25`) |
| **Reranking** | FlashRank |
| **Web Search** | Tavily (`langchain-tavily`) |
| **Agent Framework** | LangGraph 1.x (`StateGraph`) |
| **Orchestration** | LangChain 1.x |
| **Document Parsing** | `unstructured[all-docs]` + `pdfminer-six` + `pdf2image` |
| **OCR** | EasyOCR + Tesseract |
| **API** | FastAPI + Uvicorn |
| **Frontend** | Jinja2 + Vanilla JS |
| **Memory** | LangGraph `InMemorySaver` |
| **Packaging** | `uv` + `pyproject.toml` |

---

## πŸš€ Getting Started

### Prerequisites

```bash
# System dependencies (Ubuntu/Debian)
sudo apt-get install -y \
  tesseract-ocr \
  libtesseract-dev \
  poppler-utils \
  libmagic-dev
```

### Installation

```bash
# Clone the repo
git clone https://github.com/VashuTheGreat/Multi-Rag.git
cd Multi-Rag

# Create virtual environment with uv
pip install uv
uv venv
source .venv/bin/activate

# Install all dependencies
uv sync
```

### Environment Variables

Copy `.env.example` to `.env` and fill in your keys:

```bash
cp .env.example .env
```

```env
GROQ_API_KEY=your_groq_api_key
TAVILY_API_KEY=your_tavily_api_key
```

### Run

```bash
uvicorn main:app --host 0.0.0.0 --port 8000 --reload
```

Navigate to `http://localhost:8000`

---

## 🐳 Docker

```bash
docker build -t multi-rag .
docker run -p 7860:7860 --env-file .env multi-rag
```

---

## πŸ“ Supported File Types

| Format | Conversion Path |
|---|---|
| `.pdf` | Used directly by `unstructured` |
| `.docx` | `python-docx` β†’ `fpdf2` β†’ PDF |
| `.txt` | `fpdf2` β†’ PDF |
| `.png / .jpg / .heif` | `Pillow` β†’ PDF |

---

## 🧩 Key Design Decisions

- **Adaptive Routing** β€” The orchestrator decides per-query whether vector search is needed, avoiding unnecessary DB calls for greetings/small talk.
- **Hybrid Retrieval** β€” FAISS (70%) + BM25 (30%) ensemble captures both semantic and keyword relevance; FlashRank re-ranks the top results.
- **Relevance Gating** β€” A dedicated LLM call classifies retrieved docs as `CORRECT`, `AMBIGUOUS`, or `INCORRECT` before deciding whether to use them or fall back to web search.
- **Per-Thread Isolation** β€” Each user session gets its own `thread_id`; vector stores and artifacts are namespaced by thread to prevent cross-user data leakage.
- **Multimodal Context** β€” The `context_builder` node assembles text, HTML tables, and base64 images extracted from document chunks into a rich multimodal prompt.

---

## πŸ“œ License

[MIT](LICENSE)

---

## πŸ‘€ Author

**VashuTheGreat (Vansh Sharma)**

> Built with β˜• and an unhealthy obsession with RAG pipelines.