Spaces:
Sleeping
Sleeping
Upload folder using huggingface_hub
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +13 -0
- .gitignore +9 -1
- .vscode/settings.json +4 -0
- README.md +90 -85
- api/MultiRag/controllers/loadUserContent_component.py +21 -0
- api/MultiRag/models/__init__.py +0 -0
- api/MultiRag/routes/analyse_url.py +18 -0
- api/MultiRag/routes/chat_route.py +30 -20
- api/MultiRag/routes/delete_thread_route.py +29 -0
- api/MultiRag/routes/get_all_thread_route.py +16 -0
- api/MultiRag/routes/get_available_file_fomates_route.py +10 -0
- api/MultiRag/routes/load_conversation_route.py +17 -0
- api/MultiRag/routes/pages_route.py +13 -13
- api/MultiRag/routes/uploader_route.py +96 -46
- api/constants/__init__.py +8 -0
- api/main.py +13 -13
- docs copy/AI_Intro.pdf +0 -0
- docs copy/google.docx +3 -0
- docs copy/growing_ai_tools.txt +1 -0
- docs copy/lena.png +3 -0
- docs/AI_Intro.pdf +0 -0
- docs/Optical_Recognition.png +0 -0
- docs/google.docx +3 -0
- docs/growing_ai_tools.txt +1 -0
- docs/lena.png +3 -0
- exception/__init__.py +3 -23
- graph.png +0 -0
- images/attention_mechanism.png +3 -0
- images/common_mistakes.png +3 -0
- images/conclusion.png +3 -0
- images/machine_learning_overview.png +3 -0
- images/ml_common_mistakes.png +3 -0
- images/ml_model_example.png +3 -0
- images/ml_workflow_diagram.png +3 -0
- images/transformer_application.png +3 -0
- images/transformer_architecture.png +3 -0
- logs/05_02_2026_19_15_40.log.1 +0 -0
- logs/05_02_2026_19_15_40.log.2 +0 -0
- main.py +1 -1
- notebook/blip_image_captioning_large.ipynb +0 -0
- pyproject.toml +10 -0
- src/MultiRag/components/__init__.py +0 -0
- src/MultiRag/components/content_embedder.py +58 -0
- src/MultiRag/components/run_graph.py +20 -0
- src/MultiRag/constants/__init__.py +16 -1
- src/MultiRag/entity/artifact_entity.py +6 -0
- src/MultiRag/entity/config_entity.py +9 -0
- src/MultiRag/graph/builder.py +137 -21
- src/MultiRag/graph/worker/builder.py +62 -0
- src/MultiRag/llm/llm_loader.py +8 -3
.gitattributes
CHANGED
|
@@ -33,3 +33,16 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
docs/google.docx filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
docs/lena.png filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
docs[[:space:]]copy/google.docx filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
docs[[:space:]]copy/lena.png filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
images/attention_mechanism.png filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
images/common_mistakes.png filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
images/conclusion.png filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
images/machine_learning_overview.png filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
images/ml_common_mistakes.png filter=lfs diff=lfs merge=lfs -text
|
| 45 |
+
images/ml_model_example.png filter=lfs diff=lfs merge=lfs -text
|
| 46 |
+
images/ml_workflow_diagram.png filter=lfs diff=lfs merge=lfs -text
|
| 47 |
+
images/transformer_application.png filter=lfs diff=lfs merge=lfs -text
|
| 48 |
+
images/transformer_architecture.png filter=lfs diff=lfs merge=lfs -text
|
.gitignore
CHANGED
|
@@ -205,4 +205,12 @@ cython_debug/
|
|
| 205 |
marimo/_static/
|
| 206 |
marimo/_lsp/
|
| 207 |
__marimo__/
|
| 208 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 205 |
marimo/_static/
|
| 206 |
marimo/_lsp/
|
| 207 |
__marimo__/
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
db/
|
| 212 |
+
api/public/
|
| 213 |
+
|
| 214 |
+
scratch/
|
| 215 |
+
docs/
|
| 216 |
+
|
.vscode/settings.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"python-envs.defaultEnvManager": "ms-python.python:venv",
|
| 3 |
+
"python-envs.defaultPackageManager": "ms-python.python:pip"
|
| 4 |
+
}
|
README.md
CHANGED
|
@@ -1,134 +1,139 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
---
|
| 4 |
-
title: Multi-Rag
|
| 5 |
-
emoji:
|
| 6 |
colorFrom: blue
|
| 7 |
colorTo: green
|
| 8 |
sdk: docker
|
| 9 |
-
app_file:
|
| 10 |
-
app_port: 7860
|
| 11 |
pinned: false
|
|
|
|
| 12 |
---
|
| 13 |
|
| 14 |
|
| 15 |
<div align="center">
|
| 16 |
-
<h1>
|
| 17 |
-
<p><strong>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
</div>
|
| 19 |
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
|
| 24 |
---
|
| 25 |
|
| 26 |
-
##
|
| 27 |
|
| 28 |
-
- **
|
| 29 |
-
- **
|
| 30 |
-
- **
|
| 31 |
-
- **
|
| 32 |
-
- **
|
| 33 |
-
- **
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
|
| 35 |
## 🛠️ Tech Stack
|
| 36 |
|
| 37 |
-
- **
|
| 38 |
-
- **
|
| 39 |
-
- **
|
| 40 |
-
- **Vector
|
| 41 |
-
- **
|
| 42 |
-
- **
|
|
|
|
| 43 |
|
| 44 |
---
|
| 45 |
|
| 46 |
-
##
|
| 47 |
|
| 48 |
### Prerequisites
|
| 49 |
-
|
| 50 |
-
-
|
| 51 |
-
-
|
| 52 |
|
| 53 |
### 1. Installation
|
| 54 |
-
|
| 55 |
-
1. **Clone the repository**:
|
| 56 |
```bash
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
|
| 61 |
-
|
| 62 |
-
```bash
|
| 63 |
uv sync
|
| 64 |
```
|
| 65 |
|
| 66 |
-
### 2. Environment
|
| 67 |
-
|
| 68 |
-
Create a `.env` file in the root of the project and place your necessary API keys inside.
|
| 69 |
-
|
| 70 |
```env
|
| 71 |
-
#
|
| 72 |
-
|
|
|
|
|
|
|
| 73 |
|
| 74 |
-
#
|
| 75 |
-
|
| 76 |
-
AWS_SECRET_ACCESS_KEY="your_secret"
|
| 77 |
-
AWS_REGION_NAME="us-east-1"
|
| 78 |
-
|
| 79 |
-
# OpenAI
|
| 80 |
-
OPENAI_API_KEY="sk-..."
|
| 81 |
```
|
| 82 |
|
| 83 |
-
### 3. Run the
|
| 84 |
-
|
| 85 |
-
Simply launch the FastAPI application:
|
| 86 |
```bash
|
| 87 |
-
|
|
|
|
| 88 |
```
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
---
|
| 92 |
-
|
| 93 |
-
## 🎨 Walkthrough of the Application
|
| 94 |
-
|
| 95 |
-
### 🏠 Home Page (`/`)
|
| 96 |
-
An elegant gateway into the available AI agent interfaces.
|
| 97 |
-
|
| 98 |
-
### ✍️ Blog Agent (`/blog`)
|
| 99 |
-
The flagship feature. Enter a topic, and Bloggig will autonomously research the subject, plan its structure, write the content in Markdown, and generate relevant images. It features a real-time "pipeline console" to track the agent's progress.
|
| 100 |
-
|
| 101 |
-
### 🌐 Web Summarizer (`/web`)
|
| 102 |
-
Paste any URL or YouTube Link to extract and summarize content using our custom LangGraph architecture.
|
| 103 |
-
|
| 104 |
-
### 💬 Chat MultiGraph (`/chat`)
|
| 105 |
-
Engage with your locally uploaded documents via RAG (Retrieval-Augmented Generation) with intelligent memory buffers.
|
| 106 |
|
| 107 |
---
|
| 108 |
|
| 109 |
## 📂 Project Structure
|
| 110 |
|
| 111 |
```bash
|
| 112 |
-
|
| 113 |
-
├─ api/
|
| 114 |
-
|
| 115 |
-
│
|
| 116 |
-
│
|
| 117 |
-
├─
|
| 118 |
-
│
|
| 119 |
-
│
|
| 120 |
-
│
|
| 121 |
-
|
| 122 |
-
├─
|
| 123 |
-
├─
|
| 124 |
-
|
| 125 |
-
├─ data/ # Raw document storage for RAG
|
| 126 |
-
├─ db/ # Local FAISS vector database storage
|
| 127 |
-
└─ pyproject.toml # Project dependencies (uv)
|
| 128 |
```
|
| 129 |
|
| 130 |
---
|
| 131 |
|
| 132 |
<div align="center">
|
| 133 |
-
<p>
|
| 134 |
</div>
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Multi-Rag
|
| 3 |
+
emoji: 🎓
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: green
|
| 6 |
sdk: docker
|
| 7 |
+
app_file: main.py
|
|
|
|
| 8 |
pinned: false
|
| 9 |
+
short_description: This is the Agentic Blog Writing Agent
|
| 10 |
---
|
| 11 |
|
| 12 |
|
| 13 |
<div align="center">
|
| 14 |
+
<h1>🚀 Multi-RAG AI Pipeline</h1>
|
| 15 |
+
<p><strong>Advanced Multi-Agent RAG Orchestration powered by LangGraph, AWS Bedrock, and FAISS</strong></p>
|
| 16 |
+
|
| 17 |
+
[](https://www.python.org/)
|
| 18 |
+
[](https://github.com/langchain-ai/langgraph)
|
| 19 |
+
[](https://fastapi.tiangolo.com/)
|
| 20 |
+
[](https://github.com/facebookresearch/faiss)
|
| 21 |
</div>
|
| 22 |
|
| 23 |
+
---
|
| 24 |
+
|
| 25 |
+
## 📖 Overview
|
| 26 |
+
|
| 27 |
+
**Multi-RAG AI** is a state-of-the-art, multi-agent RAG (Retrieval-Augmented Generation) pipeline designed for high-performance document intelligence. It leverages **LangGraph** for sophisticated orchestration, allowing an autonomous "Orchestrator" agent to decide which specialized workers (PDF, DOCX, TXT, Images, Web Search) are needed to answer complex user queries.
|
| 28 |
+
|
| 29 |
+
### Why Multi-RAG?
|
| 30 |
+
- **Intelligent Fan-out**: The orchestrator can trigger multiple workers in parallel to gather information from different sources.
|
| 31 |
+
- **Dynamic Routing**: Automatically detects file types and routes tasks to specialized loaders.
|
| 32 |
+
- **OCR Integration**: Built-in support for image processing and optical character recognition.
|
| 33 |
+
- **Web Search Fallback**: If local documents are insufficient, the agents can autonomously search the live web.
|
| 34 |
+
|
| 35 |
+
---
|
| 36 |
+
|
| 37 |
+
## 🏗️ Architecture
|
| 38 |
+
|
| 39 |
+
The system is built as a nested graph structure, providing a clean separation between high-level orchestration and low-level specialized tasks.
|
| 40 |
+
|
| 41 |
+
### 1. Main Orchestration Graph
|
| 42 |
+
The main graph handles the interaction between the user, the orchestrator, and the final chat response.
|
| 43 |
|
| 44 |
+

|
| 45 |
+
|
| 46 |
+
### 2. Worker Sub-Graph
|
| 47 |
+
The worker sub-graph is responsible for specialized information retrieval from various file formats.
|
| 48 |
+
|
| 49 |
+

|
| 50 |
|
| 51 |
---
|
| 52 |
|
| 53 |
+
## ✨ Key Features
|
| 54 |
|
| 55 |
+
- **📂 Multi-Format Support**:
|
| 56 |
+
- **PDF**: Deep document parsing.
|
| 57 |
+
- **DOCX**: Microsoft Word document integration.
|
| 58 |
+
- **TXT**: Plain text analysis.
|
| 59 |
+
- **Images (OCR)**: Extraction of text from PNG/JPG using specialized loaders.
|
| 60 |
+
- **🤖 Autonomous Orchestration**: Uses a Llama-3.3-70B model on **AWS Bedrock** with a manual JSON fallback mechanism for 100% reliable structured output.
|
| 61 |
+
- **🔍 Hybrid Retrieval**: Combines local FAISS vector stores with real-time Google Search integration.
|
| 62 |
+
- **🧠 Persistence & Memory**: Full multi-turn conversation support with LangGraph checkpointers.
|
| 63 |
+
- **⚡ Modern Tech Stack**: Built with `uv` for lightning-fast dependency management and `FastAPI` for a high-performance backend.
|
| 64 |
+
|
| 65 |
+
---
|
| 66 |
|
| 67 |
## 🛠️ Tech Stack
|
| 68 |
|
| 69 |
+
- **Core**: [Python 3.12](https://www.python.org/)
|
| 70 |
+
- **Orchestration**: [LangGraph](https://github.com/langchain-ai/langgraph) & [LangChain](https://github.com/langchain-ai/langchain)
|
| 71 |
+
- **Large Language Models**: [AWS Bedrock](https://aws.amazon.com/bedrock/) (Llama 3.3 70B)
|
| 72 |
+
- **Vector Storage**: [FAISS](https://github.com/facebookresearch/faiss)
|
| 73 |
+
- **Embeddings**: [HuggingFace](https://huggingface.co/) (all-MiniLM-L6-v2)
|
| 74 |
+
- **Backend API**: [FastAPI](https://fastapi.tiangolo.com/)
|
| 75 |
+
- **Package Management**: [uv](https://github.com/astral-sh/uv)
|
| 76 |
|
| 77 |
---
|
| 78 |
|
| 79 |
+
## 🚀 Getting Started
|
| 80 |
|
| 81 |
### Prerequisites
|
| 82 |
+
- Python 3.12+
|
| 83 |
+
- `uv` installed (`pip install uv`)
|
| 84 |
+
- AWS Credentials (for Bedrock access)
|
| 85 |
|
| 86 |
### 1. Installation
|
|
|
|
|
|
|
| 87 |
```bash
|
| 88 |
+
# Clone the repository
|
| 89 |
+
git clone https://github.com/VashuTheGreat/Multi-Rag.git
|
| 90 |
+
cd Multi-Rag
|
| 91 |
|
| 92 |
+
# Install dependencies
|
|
|
|
| 93 |
uv sync
|
| 94 |
```
|
| 95 |
|
| 96 |
+
### 2. Environment Setup
|
| 97 |
+
Create a `.env` file in the root directory:
|
|
|
|
|
|
|
| 98 |
```env
|
| 99 |
+
# AWS Bedrock Config
|
| 100 |
+
AWS_ACCESS_KEY_ID=your_access_key
|
| 101 |
+
AWS_SECRET_ACCESS_KEY=your_secret_key
|
| 102 |
+
AWS_REGION_NAME=us-east-1
|
| 103 |
|
| 104 |
+
# Tooling (e.g., Search API keys if applicable)
|
| 105 |
+
# ...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
```
|
| 107 |
|
| 108 |
+
### 3. Run the Application
|
|
|
|
|
|
|
| 109 |
```bash
|
| 110 |
+
# Start the FastAPI server
|
| 111 |
+
uv run main.py
|
| 112 |
```
|
| 113 |
+
Navigate to `http://127.0.0.1:8000` to start chatting with your documents!
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
|
| 115 |
---
|
| 116 |
|
| 117 |
## 📂 Project Structure
|
| 118 |
|
| 119 |
```bash
|
| 120 |
+
Multi-Rag/
|
| 121 |
+
├── api/ # FastAPI Endpoints & Controllers
|
| 122 |
+
├── src/
|
| 123 |
+
│ └── MultiRag/
|
| 124 |
+
│ ├── components/ # Core graph runners & embedders
|
| 125 |
+
│ ├── graph/ # LangGraph definitions (Main & Worker)
|
| 126 |
+
│ ├── models/ # Pydantic state & output schemas
|
| 127 |
+
│ ├── nodes/ # Individual graph node implementations
|
| 128 |
+
│ ├── prompts/ # LLM system prompts
|
| 129 |
+
│ └── utils/ # Ingestion & document processing utilities
|
| 130 |
+
├── static/ # Frontend assets (CSS, JS)
|
| 131 |
+
├── templates/ # Jinja2 HTML templates
|
| 132 |
+
└── db/ # Local FAISS index persistence
|
|
|
|
|
|
|
|
|
|
| 133 |
```
|
| 134 |
|
| 135 |
---
|
| 136 |
|
| 137 |
<div align="center">
|
| 138 |
+
<p>Built with 💖 for the future of Agentic RAG.</p>
|
| 139 |
</div>
|
api/MultiRag/controllers/loadUserContent_component.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
from utils.asyncHandler import asyncHandler
|
| 3 |
+
from utils.main_utils import load_yaml
|
| 4 |
+
from api.constants import DATA_FOLDER_PATH,USER_CONTENT_FILE_NAME
|
| 5 |
+
from src.MultiRag.models.rag_model import Content
|
| 6 |
+
|
| 7 |
+
@asyncHandler
|
| 8 |
+
async def load_user_content(thread_id):
|
| 9 |
+
user_data = load_yaml(f"{DATA_FOLDER_PATH}/{thread_id}/{USER_CONTENT_FILE_NAME}")
|
| 10 |
+
user_content = []
|
| 11 |
+
if user_data:
|
| 12 |
+
for content in user_data.get("Contents", []):
|
| 13 |
+
user_content.append(
|
| 14 |
+
Content(
|
| 15 |
+
name=content["name"],
|
| 16 |
+
about=content["about"],
|
| 17 |
+
path=content["path"]
|
| 18 |
+
)
|
| 19 |
+
)
|
| 20 |
+
|
| 21 |
+
return user_content
|
api/MultiRag/models/__init__.py
ADDED
|
File without changes
|
api/MultiRag/routes/analyse_url.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import fastapi
|
| 2 |
+
|
| 3 |
+
import logging
|
| 4 |
+
|
| 5 |
+
router = fastapi.APIRouter()
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
@router.post("/analyse_url")
|
| 9 |
+
async def analyse_url(thread_id:str,url: str):
|
| 10 |
+
try:
|
| 11 |
+
|
| 12 |
+
if not url:
|
| 13 |
+
return {"data": "URL missing in headers"}
|
| 14 |
+
res = await run_agent(thread_id, url)
|
| 15 |
+
return {"data": res}
|
| 16 |
+
except Exception as e:
|
| 17 |
+
logging.error(f"Chat endpoint error: {e}")
|
| 18 |
+
return {"data": "Failed to chat"}
|
api/MultiRag/routes/chat_route.py
CHANGED
|
@@ -1,32 +1,31 @@
|
|
| 1 |
from fastapi import APIRouter, Request, Query
|
| 2 |
import logging
|
| 3 |
import logging
|
|
|
|
| 4 |
from src.MultiRag.graph.builder import graph
|
|
|
|
|
|
|
|
|
|
| 5 |
|
| 6 |
router = APIRouter()
|
| 7 |
|
| 8 |
-
|
| 9 |
-
async def run_agent(user_id, userQuery: str):
|
| 10 |
-
logging.info("Starting AIAgents application
|
| 11 |
-
|
| 12 |
-
config = {"configurable": {"thread_id": user_id}}
|
| 13 |
-
initial_state = {
|
| 14 |
-
"userQuery": userQuery,
|
| 15 |
-
"db_path": f"db/{user_id}",
|
| 16 |
-
"docs_path": f"data/{user_id}",
|
| 17 |
-
"k": 3
|
| 18 |
-
}
|
| 19 |
try:
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
res =
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
return res
|
| 25 |
except Exception as e:
|
| 26 |
logging.error(f"Application failed: {e}")
|
| 27 |
-
|
| 28 |
-
logging.error(traceback.format_exc())
|
| 29 |
-
return "Chat failed due to internal error"
|
| 30 |
finally:
|
| 31 |
logging.info("AIAgents application finished.")
|
| 32 |
|
|
@@ -35,10 +34,21 @@ async def run_agent(user_id, userQuery: str):
|
|
| 35 |
async def chat(req: Request, message: str = Query(...)):
|
| 36 |
try:
|
| 37 |
user_id = req.headers.get("user_id")
|
|
|
|
| 38 |
if not user_id:
|
| 39 |
return {"data": "User ID missing in headers"}
|
| 40 |
-
res = await run_agent(user_id, message)
|
| 41 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
except Exception as e:
|
| 43 |
logging.error(f"Chat endpoint error: {e}")
|
| 44 |
return {"data": "Chat failed"}
|
|
|
|
|
|
|
|
|
| 1 |
from fastapi import APIRouter, Request, Query
|
| 2 |
import logging
|
| 3 |
import logging
|
| 4 |
+
from src.MultiRag.pipeline.run_pipeline import RunPipeline
|
| 5 |
from src.MultiRag.graph.builder import graph
|
| 6 |
+
from src.MultiRag.models.rag_model import Content
|
| 7 |
+
from api.MultiRag.controllers.loadUserContent_component import load_user_content
|
| 8 |
+
from exception import MyException
|
| 9 |
|
| 10 |
router = APIRouter()
|
| 11 |
|
| 12 |
+
run_pipeline = RunPipeline()
|
| 13 |
+
async def run_agent(user_id, thread_id, userQuery: str):
|
| 14 |
+
logging.info(f"Starting AIAgents application for thread: {thread_id}")
|
| 15 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
try:
|
| 17 |
+
temp_user_content = await load_user_content(thread_id)
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
res = await run_pipeline.initiate(
|
| 21 |
+
thread_id=thread_id,
|
| 22 |
+
query=userQuery,
|
| 23 |
+
userContent=temp_user_content
|
| 24 |
+
)
|
| 25 |
return res
|
| 26 |
except Exception as e:
|
| 27 |
logging.error(f"Application failed: {e}")
|
| 28 |
+
raise MyException("AIAgents application failed") from e
|
|
|
|
|
|
|
| 29 |
finally:
|
| 30 |
logging.info("AIAgents application finished.")
|
| 31 |
|
|
|
|
| 34 |
async def chat(req: Request, message: str = Query(...)):
|
| 35 |
try:
|
| 36 |
user_id = req.headers.get("user_id")
|
| 37 |
+
thread_id = req.headers.get("thread_id") or user_id
|
| 38 |
if not user_id:
|
| 39 |
return {"data": "User ID missing in headers"}
|
| 40 |
+
res = await run_agent(user_id, thread_id, message)
|
| 41 |
+
|
| 42 |
+
# Extract the last message content to send to frontend
|
| 43 |
+
messages = res.get("messages", [])
|
| 44 |
+
if messages:
|
| 45 |
+
last_msg = messages[-1]
|
| 46 |
+
content = last_msg.content if hasattr(last_msg, 'content') else str(last_msg)
|
| 47 |
+
return {"data": content}
|
| 48 |
+
|
| 49 |
+
return {"data": "No response from agent."}
|
| 50 |
except Exception as e:
|
| 51 |
logging.error(f"Chat endpoint error: {e}")
|
| 52 |
return {"data": "Chat failed"}
|
| 53 |
+
|
| 54 |
+
|
api/MultiRag/routes/delete_thread_route.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import fastapi
|
| 2 |
+
import logging
|
| 3 |
+
import os
|
| 4 |
+
import shutil
|
| 5 |
+
from exception import MyException
|
| 6 |
+
from api.constants import DATA_FOLDER_PATH,DB_FOLDER_PATH
|
| 7 |
+
from src.MultiRag.graph.builder import deleteThread
|
| 8 |
+
router = fastapi.APIRouter()
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
@router.delete("/delete_thread")
|
| 12 |
+
async def delete_thread(thread_id: str):
|
| 13 |
+
try:
|
| 14 |
+
logging.info(f"Attempting to delete thread {thread_id}")
|
| 15 |
+
await deleteThread(thread_id)
|
| 16 |
+
|
| 17 |
+
data_path = f"{DATA_FOLDER_PATH}/{thread_id}"
|
| 18 |
+
db_path = f"{DB_FOLDER_PATH}/{thread_id}"
|
| 19 |
+
|
| 20 |
+
if os.path.exists(data_path):
|
| 21 |
+
shutil.rmtree(data_path)
|
| 22 |
+
if os.path.exists(db_path):
|
| 23 |
+
shutil.rmtree(db_path)
|
| 24 |
+
|
| 25 |
+
logging.info(f"Successfully deleted thread {thread_id}")
|
| 26 |
+
return {"message": f"Thread {thread_id} has been deleted."}
|
| 27 |
+
except Exception as e:
|
| 28 |
+
logging.error(f"Failed to delete thread {thread_id}: {str(e)}")
|
| 29 |
+
raise MyException("Failed to delete thread") from e
|
api/MultiRag/routes/get_all_thread_route.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from src.MultiRag.graph.builder import retrieve_all_threads
|
| 2 |
+
import fastapi
|
| 3 |
+
import logging
|
| 4 |
+
|
| 5 |
+
router = fastapi.APIRouter()
|
| 6 |
+
|
| 7 |
+
@router.get("/get_all_thread")
|
| 8 |
+
async def get_all_thread():
|
| 9 |
+
try:
|
| 10 |
+
logging.info("Received request to get all threads")
|
| 11 |
+
threads = await retrieve_all_threads()
|
| 12 |
+
logging.info(f"Retrieved all threads successfully {threads}")
|
| 13 |
+
return {"threads": threads}
|
| 14 |
+
except Exception as e:
|
| 15 |
+
logging.error(f"Error retrieving threads: {e}")
|
| 16 |
+
return {"message": "Failed to retrieve threads"}
|
api/MultiRag/routes/get_available_file_fomates_route.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
import fastapi
|
| 4 |
+
from api.constants import AVAILABLE_ANALYSIS
|
| 5 |
+
router = fastapi.APIRouter()
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
@router.get("/")
|
| 9 |
+
async def get_available_file_fomates():
|
| 10 |
+
return {"message": "Available file formats: pdf, txt, docx, image","data":AVAILABLE_ANALYSIS}
|
api/MultiRag/routes/load_conversation_route.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import fastapi
|
| 2 |
+
from src.MultiRag.graph.builder import load_conversation
|
| 3 |
+
import logging
|
| 4 |
+
|
| 5 |
+
router = fastapi.APIRouter()
|
| 6 |
+
|
| 7 |
+
@router.get("/load_conversation")
|
| 8 |
+
async def get_conversation(thread_id: str):
|
| 9 |
+
try:
|
| 10 |
+
logging.info(f"Loading conversation for thread_id: {thread_id}")
|
| 11 |
+
messages = await load_conversation(thread_id)
|
| 12 |
+
logging.info(f"Conversation loaded successfully for thread_id: {thread_id}")
|
| 13 |
+
return {"messages": messages}
|
| 14 |
+
except Exception as e:
|
| 15 |
+
logging.error(f"Error loading conversation for thread_id: {thread_id}: {e}")
|
| 16 |
+
return {"message": "Failed to load conversation"}
|
| 17 |
+
|
api/MultiRag/routes/pages_route.py
CHANGED
|
@@ -8,24 +8,24 @@ templates = Jinja2Templates(directory="templates")
|
|
| 8 |
|
| 9 |
_APP_USER_ID = os.getenv("APP_API_KEY", "")
|
| 10 |
|
| 11 |
-
@router.get("/")
|
| 12 |
-
async def read_root(request: Request):
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
|
| 18 |
|
| 19 |
-
@router.get("/
|
| 20 |
async def chat_model(request: Request):
|
| 21 |
return templates.TemplateResponse(
|
| 22 |
name="chat.html",
|
| 23 |
context={"request": request, "app_user_id": _APP_USER_ID}
|
| 24 |
)
|
| 25 |
|
| 26 |
-
@router.get("/web")
|
| 27 |
-
async def web_page(request: Request):
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
|
|
|
| 8 |
|
| 9 |
_APP_USER_ID = os.getenv("APP_API_KEY", "")
|
| 10 |
|
| 11 |
+
# @router.get("/")
|
| 12 |
+
# async def read_root(request: Request):
|
| 13 |
+
# return templates.TemplateResponse(
|
| 14 |
+
# name="home.html",
|
| 15 |
+
# context={"request": request, "app_user_id": _APP_USER_ID}
|
| 16 |
+
# )
|
| 17 |
|
| 18 |
|
| 19 |
+
@router.get("/")
|
| 20 |
async def chat_model(request: Request):
|
| 21 |
return templates.TemplateResponse(
|
| 22 |
name="chat.html",
|
| 23 |
context={"request": request, "app_user_id": _APP_USER_ID}
|
| 24 |
)
|
| 25 |
|
| 26 |
+
# @router.get("/web")
|
| 27 |
+
# async def web_page(request: Request):
|
| 28 |
+
# return templates.TemplateResponse(
|
| 29 |
+
# name="web.html",
|
| 30 |
+
# context={"request": request, "app_user_id": _APP_USER_ID}
|
| 31 |
+
# )
|
api/MultiRag/routes/uploader_route.py
CHANGED
|
@@ -1,73 +1,123 @@
|
|
| 1 |
import fastapi
|
| 2 |
from fastapi import UploadFile, Request, BackgroundTasks
|
| 3 |
import os
|
| 4 |
-
import shutil
|
| 5 |
-
import asyncio
|
| 6 |
import logging
|
| 7 |
-
from src.MultiRag.constants import CONTENT_PERSISTENT_TIME, DATA_FOLDER_PATH, DB_FOLDER_PATH
|
| 8 |
from src.MultiRag.graph.builder import deleteThread
|
| 9 |
from utils.asyncHandler import asyncHandler
|
| 10 |
-
from
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
|
|
|
| 16 |
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
folder_path = f"{DATA_FOLDER_PATH}/{user_id}"
|
| 20 |
-
db_path = f"{DB_FOLDER_PATH}/{user_id}"
|
| 21 |
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
-
|
| 27 |
-
|
| 28 |
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
logging.warning(f"DB delete attempt {attempt+1} failed: {e}")
|
| 41 |
-
await asyncio.sleep(3)
|
| 42 |
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
| 44 |
|
|
|
|
|
|
|
| 45 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
|
| 47 |
-
|
| 48 |
|
|
|
|
|
|
|
|
|
|
| 49 |
|
| 50 |
-
@router.post("/
|
| 51 |
-
async def
|
| 52 |
-
req: Request,
|
| 53 |
-
file: UploadFile,
|
| 54 |
-
background_tasks: BackgroundTasks
|
| 55 |
-
):
|
| 56 |
try:
|
| 57 |
user_id = req.headers.get("user_id")
|
|
|
|
|
|
|
|
|
|
| 58 |
|
| 59 |
-
folder = f"{DATA_FOLDER_PATH}/{
|
| 60 |
os.makedirs(folder, exist_ok=True)
|
| 61 |
|
| 62 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
|
| 64 |
-
|
| 65 |
-
|
|
|
|
|
|
|
| 66 |
|
| 67 |
-
#
|
| 68 |
-
|
| 69 |
|
| 70 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
|
| 72 |
except Exception as e:
|
| 73 |
-
|
|
|
|
|
|
| 1 |
import fastapi
|
| 2 |
from fastapi import UploadFile, Request, BackgroundTasks
|
| 3 |
import os
|
|
|
|
|
|
|
| 4 |
import logging
|
|
|
|
| 5 |
from src.MultiRag.graph.builder import deleteThread
|
| 6 |
from utils.asyncHandler import asyncHandler
|
| 7 |
+
from utils.main_utils import write_yaml, load_yaml
|
| 8 |
+
from src.MultiRag.models.rag_model import Content
|
| 9 |
+
from src.MultiRag.components.content_embedder import ContentEmbedder
|
| 10 |
+
from src.MultiRag.entity.config_entity import ContentEmbedderConfig
|
| 11 |
+
from api.constants import DATA_FOLDER_PATH, USER_CONTENT_FILE_NAME
|
| 12 |
+
from src.MultiRag.graph.builder import graph
|
| 13 |
+
from langchain_core.messages import HumanMessage
|
| 14 |
|
| 15 |
+
router = fastapi.APIRouter()
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
+
async def generate_retreivers(thread_id: str):
|
| 18 |
+
yaml_path = f"{DATA_FOLDER_PATH}/{thread_id}/{USER_CONTENT_FILE_NAME}"
|
| 19 |
+
yaml_content = load_yaml(yaml_path)
|
| 20 |
+
|
| 21 |
+
if not yaml_content or 'Contents' not in yaml_content:
|
| 22 |
+
logging.warning(f"No contents found in {yaml_path}")
|
| 23 |
+
return
|
| 24 |
+
|
| 25 |
+
for content_dict in yaml_content['Contents']:
|
| 26 |
+
name = content_dict.get("name")
|
| 27 |
+
path = content_dict.get("path")
|
| 28 |
+
|
| 29 |
+
logging.info(f"Processing content: {name}")
|
| 30 |
+
|
| 31 |
+
content_embedder_config = ContentEmbedderConfig(
|
| 32 |
+
file_path=path,
|
| 33 |
+
vector_store_path=f"db/{thread_id}/{name}",
|
| 34 |
+
)
|
| 35 |
+
component = ContentEmbedder(content_embedder_config=content_embedder_config)
|
| 36 |
+
retreiver = await component.embed_content()
|
| 37 |
+
logging.info(f"Generated retreiver for {name}: {retreiver}")
|
| 38 |
+
|
| 39 |
+
@router.post("/")
|
| 40 |
+
async def post_content(
|
| 41 |
+
req: Request,
|
| 42 |
+
file: UploadFile
|
| 43 |
+
):
|
| 44 |
+
try:
|
| 45 |
+
user_id = req.headers.get("user_id")
|
| 46 |
+
thread_id = req.headers.get("thread_id") or user_id
|
| 47 |
+
if not user_id:
|
| 48 |
+
return {"message": "User ID missing in headers"}
|
| 49 |
|
| 50 |
+
folder = f"{DATA_FOLDER_PATH}/{thread_id}"
|
| 51 |
+
os.makedirs(folder, exist_ok=True)
|
| 52 |
|
| 53 |
+
saved_file_path = f"{folder}/{file.filename}"
|
| 54 |
+
with open(saved_file_path, "wb") as f:
|
| 55 |
+
f.write(await file.read())
|
| 56 |
|
| 57 |
+
yaml_path = f"{folder}/{USER_CONTENT_FILE_NAME}"
|
| 58 |
+
|
| 59 |
+
content_entry = {
|
| 60 |
+
"name": file.filename,
|
| 61 |
+
"about": file.filename,
|
| 62 |
+
"path": saved_file_path
|
| 63 |
+
}
|
|
|
|
|
|
|
| 64 |
|
| 65 |
+
# Append to YAML
|
| 66 |
+
write_yaml(yaml_path, {"Contents": [content_entry]}, mode="a")
|
| 67 |
+
|
| 68 |
+
logging.info(f"File uploaded and entry added to YAML: {file.filename}")
|
| 69 |
|
| 70 |
+
# Trigger retriever generation
|
| 71 |
+
await generate_retreivers(thread_id)
|
| 72 |
|
| 73 |
+
# Notify the AI about the upload in the thread history
|
| 74 |
+
config = {"configurable": {"thread_id": thread_id}}
|
| 75 |
+
notification = HumanMessage(content=f"[SYSTEM NOTIFICATION]: User has uploaded a new file: {file.filename}. Please keep this in mind for future queries.")
|
| 76 |
+
await graph.aupdate_state(config, {"messages": [notification]})
|
| 77 |
|
| 78 |
+
return {"message": "File uploaded successfully"}
|
| 79 |
|
| 80 |
+
except Exception as e:
|
| 81 |
+
logging.error(f"File upload failed: {e}")
|
| 82 |
+
return {"message": f"File upload failed: {str(e)}"}
|
| 83 |
|
| 84 |
+
@router.post("/upload_url")
|
| 85 |
+
async def upload_url(req: Request, url: str):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
try:
|
| 87 |
user_id = req.headers.get("user_id")
|
| 88 |
+
thread_id = req.headers.get("thread_id") or user_id
|
| 89 |
+
if not user_id:
|
| 90 |
+
return {"message": "User ID missing in headers"}
|
| 91 |
|
| 92 |
+
folder = f"{DATA_FOLDER_PATH}/{thread_id}"
|
| 93 |
os.makedirs(folder, exist_ok=True)
|
| 94 |
|
| 95 |
+
yaml_path = f"{folder}/{USER_CONTENT_FILE_NAME}"
|
| 96 |
+
|
| 97 |
+
# Use a truncated URL for the name
|
| 98 |
+
display_name = (url[:50] + '...') if len(url) > 50 else url
|
| 99 |
+
|
| 100 |
+
content_entry = {
|
| 101 |
+
"name": display_name,
|
| 102 |
+
"about": url,
|
| 103 |
+
"path": url
|
| 104 |
+
}
|
| 105 |
|
| 106 |
+
# Append to YAML
|
| 107 |
+
write_yaml(yaml_path, {"Contents": [content_entry]}, mode="a")
|
| 108 |
+
|
| 109 |
+
logging.info(f"URL entry added to YAML: {url}")
|
| 110 |
|
| 111 |
+
# Trigger retriever generation (if the embedder supports URLs)
|
| 112 |
+
await generate_retreivers(thread_id)
|
| 113 |
|
| 114 |
+
# Notify the AI about the URL upload
|
| 115 |
+
config = {"configurable": {"thread_id": thread_id}}
|
| 116 |
+
notification = HumanMessage(content=f"[SYSTEM NOTIFICATION]: User has uploaded a new URL: {url}. Please keep this in mind for future queries.")
|
| 117 |
+
await graph.aupdate_state(config, {"messages": [notification]})
|
| 118 |
+
|
| 119 |
+
return {"message": "URL uploaded successfully"}
|
| 120 |
|
| 121 |
except Exception as e:
|
| 122 |
+
logging.error(f"URL upload failed: {e}")
|
| 123 |
+
return {"message": f"URL upload failed: {str(e)}"}
|
api/constants/__init__.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
DATA_FOLDER_PATH="api/public"
|
| 2 |
+
CONTENT_PERSISTENT_TIME=5
|
| 3 |
+
DB_FOLDER_PATH="db"
|
| 4 |
+
|
| 5 |
+
AVAILABLE_ANALYSIS=['pdf','txt','docs','docx','png','url']
|
| 6 |
+
|
| 7 |
+
USER_CONTENT_FILE_NAME="USER_CONTENT.yml"
|
| 8 |
+
|
api/main.py
CHANGED
|
@@ -1,9 +1,6 @@
|
|
| 1 |
from fastapi import FastAPI, Request
|
| 2 |
from fastapi.responses import JSONResponse
|
| 3 |
-
from api.MultiRag.routes import chat_route, uploader_route, pages_route
|
| 4 |
-
from api.Web.routes import web_talk_routes
|
| 5 |
-
from api.Blog.routes import page_route_blog,blog_router
|
| 6 |
-
from api.Web.routes import page_route_web
|
| 7 |
app = FastAPI()
|
| 8 |
|
| 9 |
@app.middleware("http")
|
|
@@ -34,22 +31,25 @@ async def check_user_id(request: Request, call_next):
|
|
| 34 |
return response
|
| 35 |
|
| 36 |
app.include_router(pages_route.router)
|
| 37 |
-
app.include_router(prefix="/chat", router=chat_route.router)
|
| 38 |
-
app.include_router(prefix="/uploader", router=uploader_route.router)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
|
| 40 |
|
| 41 |
|
| 42 |
|
|
|
|
|
|
|
|
|
|
| 43 |
|
| 44 |
-
# -------------------- Web -------------------------------
|
| 45 |
-
app.include_router(page_route_web.router)
|
| 46 |
-
app.include_router(prefix="/web",router=web_talk_routes.router)
|
| 47 |
|
| 48 |
|
| 49 |
|
| 50 |
|
| 51 |
-
|
| 52 |
-
#
|
| 53 |
-
app.include_router(
|
| 54 |
-
app.include_router(prefix="/blog",router=blog_router.router)
|
| 55 |
|
|
|
|
| 1 |
from fastapi import FastAPI, Request
|
| 2 |
from fastapi.responses import JSONResponse
|
| 3 |
+
from api.MultiRag.routes import chat_route, uploader_route, pages_route,get_all_thread_route,load_conversation_route,get_available_file_fomates_route, delete_thread_route
|
|
|
|
|
|
|
|
|
|
| 4 |
app = FastAPI()
|
| 5 |
|
| 6 |
@app.middleware("http")
|
|
|
|
| 31 |
return response
|
| 32 |
|
| 33 |
app.include_router(pages_route.router)
|
| 34 |
+
app.include_router(prefix="/api/v1/chat", router=chat_route.router)
|
| 35 |
+
app.include_router(prefix="/api/v1/uploader", router=uploader_route.router)
|
| 36 |
+
app.include_router(prefix="/api/v1/thread", router=get_all_thread_route.router)
|
| 37 |
+
app.include_router(prefix="/api/v1/thread", router=delete_thread_route.router)
|
| 38 |
+
app.include_router(prefix="/api/v1/conversation", router=load_conversation_route.router)
|
| 39 |
+
app.include_router(prefix="/api/v1/file_formats", router=get_available_file_fomates_route.router)
|
| 40 |
|
| 41 |
|
| 42 |
|
| 43 |
|
| 44 |
+
# # -------------------- Web -------------------------------
|
| 45 |
+
# app.include_router(page_route_web.router)
|
| 46 |
+
# app.include_router(prefix="/web",router=web_talk_routes.router)
|
| 47 |
|
|
|
|
|
|
|
|
|
|
| 48 |
|
| 49 |
|
| 50 |
|
| 51 |
|
| 52 |
+
# # ------------ Blog --------------------
|
| 53 |
+
# app.include_router(page_route_blog.router)
|
| 54 |
+
# app.include_router(prefix="/blog",router=blog_router.router)
|
|
|
|
| 55 |
|
docs copy/AI_Intro.pdf
ADDED
|
Binary file (41.5 kB). View file
|
|
|
docs copy/google.docx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7eee211e7bc83917dde195f15c5458d6877dce8ba9fe080479c26a58e8da4c6a
|
| 3 |
+
size 3020407
|
docs copy/growing_ai_tools.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
THE LATEST GROWING AI MODELS (2024-2025)LARGE LANGUAGE MODELS (LLMs) & MULTIMODALGemini 1.5 Pro (Google): Known for its massive context window (up to 2 million tokens), allowing it to process entire libraries or long videos in one go.GPT-4o (OpenAI): An "omni" model designed for seamless real-time interaction across text, audio, and vision.Claude 3.5 Sonnet (Anthropic): Widely praised for its human-like reasoning, coding abilities, and "Artifacts" UI feature.Llama 3.1 (Meta): The leading open-source model series, providing high performance for developers to build private AI applications.DeepSeek-V3: An emerging powerhouse from China gaining traction for its efficiency and strong performance in logic and coding.VIDEO GENERATION MODELS (THE NEW FRONTIER)Sora (OpenAI): A world-simulating model that creates highly realistic 60-second videos.Veo (Google): Google's latest high-definition video generation model with cinematic control.Kling / Luma Dream Machine: Rapidly growing tools accessible to the public for generating high-quality AI video from text prompts.Runway Gen-3 Alpha: A professional-grade video model used by filmmakers and creators for precise motion control.IMAGE & CREATIVE MODELSMidjourney v6: Continues to lead in artistic quality and photorealism.Flux.1 (Black Forest Labs): A new open-weights model that has quickly become a favorite for its incredible detail and ability to render text inside images.DALL-E 3: Integrated into ChatGPT and Bing, focused on strict adherence to complex user prompts.SPECIALIZED & RESEARCH MODELSAlphaFold 3 (Google DeepMind): A revolutionary model for biology that predicts the structure and interactions of all life’s molecules.Grok-2 (xAI): Elon Musk’s AI model integrated into X (Twitter), designed for real-time information access and "edgy" personality.Trends to Watch:Small Language Models (SLMs): Models like Phi-3 or Gemma designed to run locally on phones and laptops.Agentic AI: Models designed not just to talk, but to use tools and complete multi-step tasks autonomously.
|
docs copy/lena.png
ADDED
|
Git LFS Details
|
docs/AI_Intro.pdf
ADDED
|
Binary file (41.5 kB). View file
|
|
|
docs/Optical_Recognition.png
ADDED
|
docs/google.docx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7eee211e7bc83917dde195f15c5458d6877dce8ba9fe080479c26a58e8da4c6a
|
| 3 |
+
size 3020407
|
docs/growing_ai_tools.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
THE LATEST GROWING AI MODELS (2024-2025)LARGE LANGUAGE MODELS (LLMs) & MULTIMODALGemini 1.5 Pro (Google): Known for its massive context window (up to 2 million tokens), allowing it to process entire libraries or long videos in one go.GPT-4o (OpenAI): An "omni" model designed for seamless real-time interaction across text, audio, and vision.Claude 3.5 Sonnet (Anthropic): Widely praised for its human-like reasoning, coding abilities, and "Artifacts" UI feature.Llama 3.1 (Meta): The leading open-source model series, providing high performance for developers to build private AI applications.DeepSeek-V3: An emerging powerhouse from China gaining traction for its efficiency and strong performance in logic and coding.VIDEO GENERATION MODELS (THE NEW FRONTIER)Sora (OpenAI): A world-simulating model that creates highly realistic 60-second videos.Veo (Google): Google's latest high-definition video generation model with cinematic control.Kling / Luma Dream Machine: Rapidly growing tools accessible to the public for generating high-quality AI video from text prompts.Runway Gen-3 Alpha: A professional-grade video model used by filmmakers and creators for precise motion control.IMAGE & CREATIVE MODELSMidjourney v6: Continues to lead in artistic quality and photorealism.Flux.1 (Black Forest Labs): A new open-weights model that has quickly become a favorite for its incredible detail and ability to render text inside images.DALL-E 3: Integrated into ChatGPT and Bing, focused on strict adherence to complex user prompts.SPECIALIZED & RESEARCH MODELSAlphaFold 3 (Google DeepMind): A revolutionary model for biology that predicts the structure and interactions of all life’s molecules.Grok-2 (xAI): Elon Musk’s AI model integrated into X (Twitter), designed for real-time information access and "edgy" personality.Trends to Watch:Small Language Models (SLMs): Models like Phi-3 or Gemma designed to run locally on phones and laptops.Agentic AI: Models designed not just to talk, but to use tools and complete multi-step tasks autonomously.
|
docs/lena.png
ADDED
|
Git LFS Details
|
exception/__init__.py
CHANGED
|
@@ -1,35 +1,15 @@
|
|
| 1 |
import sys
|
| 2 |
import logging
|
| 3 |
|
| 4 |
-
def error_message_detail(error:Exception,error_detail:sys)->str:
|
| 5 |
-
_, _, exc_tb = error_detail.exc_info()
|
| 6 |
|
| 7 |
-
# Walk the traceback to find the actual source of the error
|
| 8 |
-
while exc_tb.tb_next is not None:
|
| 9 |
-
exc_tb = exc_tb.tb_next
|
| 10 |
-
|
| 11 |
-
# Get the file name where the exception occurred
|
| 12 |
-
file_name = exc_tb.tb_frame.f_code.co_filename
|
| 13 |
-
|
| 14 |
-
# Create a formatted error message string with file name, line number, and the actual error
|
| 15 |
-
line_number = exc_tb.tb_lineno
|
| 16 |
-
error_message = f"Error occurred in python script: [{file_name}] at line number [{line_number}]: {str(error)}"
|
| 17 |
-
|
| 18 |
-
# Log the error for better tracking
|
| 19 |
-
logging.error(error_message)
|
| 20 |
-
|
| 21 |
-
return error_message
|
| 22 |
|
| 23 |
class MyException(Exception):
|
| 24 |
-
def __init__(self, error_message: str, error_detail: sys):
|
| 25 |
-
# Call the base class constructor with the error message
|
| 26 |
super().__init__(error_message)
|
| 27 |
|
| 28 |
-
|
| 29 |
-
self.error_message = error_message_detail(error_message, error_detail)
|
| 30 |
-
|
| 31 |
def __str__(self) -> str:
|
| 32 |
"""
|
| 33 |
Returns the string representation of the error message.
|
| 34 |
"""
|
| 35 |
-
return self.
|
|
|
|
| 1 |
import sys
|
| 2 |
import logging
|
| 3 |
|
|
|
|
|
|
|
| 4 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
|
| 6 |
class MyException(Exception):
|
| 7 |
+
def __init__(self, error_message: str, error_detail: sys = None):
|
|
|
|
| 8 |
super().__init__(error_message)
|
| 9 |
|
| 10 |
+
logging.exception(error_message)
|
|
|
|
|
|
|
| 11 |
def __str__(self) -> str:
|
| 12 |
"""
|
| 13 |
Returns the string representation of the error message.
|
| 14 |
"""
|
| 15 |
+
return self.args[0]
|
graph.png
CHANGED
|
|
images/attention_mechanism.png
ADDED
|
Git LFS Details
|
images/common_mistakes.png
ADDED
|
Git LFS Details
|
images/conclusion.png
ADDED
|
Git LFS Details
|
images/machine_learning_overview.png
ADDED
|
Git LFS Details
|
images/ml_common_mistakes.png
ADDED
|
Git LFS Details
|
images/ml_model_example.png
ADDED
|
Git LFS Details
|
images/ml_workflow_diagram.png
ADDED
|
Git LFS Details
|
images/transformer_application.png
ADDED
|
Git LFS Details
|
images/transformer_architecture.png
ADDED
|
Git LFS Details
|
logs/05_02_2026_19_15_40.log.1
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
logs/05_02_2026_19_15_40.log.2
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
main.py
CHANGED
|
@@ -20,6 +20,6 @@ if __name__ == "__main__":
|
|
| 20 |
"main:app",
|
| 21 |
host="0.0.0.0",
|
| 22 |
port=7860,
|
| 23 |
-
reload=
|
| 24 |
reload_excludes=["db/*", "data/*", "logs/*", "vector_db/*", ".venv/*"],
|
| 25 |
)
|
|
|
|
| 20 |
"main:app",
|
| 21 |
host="0.0.0.0",
|
| 22 |
port=7860,
|
| 23 |
+
reload=True,
|
| 24 |
reload_excludes=["db/*", "data/*", "logs/*", "vector_db/*", ".venv/*"],
|
| 25 |
)
|
notebook/blip_image_captioning_large.ipynb
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
pyproject.toml
CHANGED
|
@@ -6,7 +6,9 @@ readme = "README.md"
|
|
| 6 |
requires-python = ">=3.12"
|
| 7 |
dependencies = [
|
| 8 |
"bert-extractive-summarizer>=0.10.1",
|
|
|
|
| 9 |
"dotenv>=0.9.9",
|
|
|
|
| 10 |
"faiss-cpu>=1.13.2",
|
| 11 |
"fastapi>=0.135.1",
|
| 12 |
"keybert>=0.9.0",
|
|
@@ -16,13 +18,21 @@ dependencies = [
|
|
| 16 |
"langchain-community>=0.4.1",
|
| 17 |
"langchain-core>=1.2.17",
|
| 18 |
"langchain-google-genai>=4.2.1",
|
|
|
|
| 19 |
"langchain-huggingface>=1.2.1",
|
| 20 |
"langchain-ollama>=1.0.1",
|
|
|
|
| 21 |
"langgraph>=1.0.10",
|
|
|
|
|
|
|
|
|
|
| 22 |
"pillow>=12.1.1",
|
|
|
|
| 23 |
"python-multipart>=0.0.22",
|
| 24 |
"sentence-transformers>=5.2.3",
|
| 25 |
"transformers>=5.3.0",
|
| 26 |
"unstructured>=0.21.5",
|
|
|
|
|
|
|
| 27 |
"youtube-transcript-api>=1.2.4",
|
| 28 |
]
|
|
|
|
| 6 |
requires-python = ">=3.12"
|
| 7 |
dependencies = [
|
| 8 |
"bert-extractive-summarizer>=0.10.1",
|
| 9 |
+
"docx2txt>=0.9",
|
| 10 |
"dotenv>=0.9.9",
|
| 11 |
+
"easyocr>=1.7.2",
|
| 12 |
"faiss-cpu>=1.13.2",
|
| 13 |
"fastapi>=0.135.1",
|
| 14 |
"keybert>=0.9.0",
|
|
|
|
| 18 |
"langchain-community>=0.4.1",
|
| 19 |
"langchain-core>=1.2.17",
|
| 20 |
"langchain-google-genai>=4.2.1",
|
| 21 |
+
"langchain-groq>=1.1.2",
|
| 22 |
"langchain-huggingface>=1.2.1",
|
| 23 |
"langchain-ollama>=1.0.1",
|
| 24 |
+
"langchain-tavily>=0.2.18",
|
| 25 |
"langgraph>=1.0.10",
|
| 26 |
+
"pdf2image>=1.17.0",
|
| 27 |
+
"pdfminer-six>=20260107",
|
| 28 |
+
"pi-heif>=1.3.0",
|
| 29 |
"pillow>=12.1.1",
|
| 30 |
+
"pytesseract>=0.3.13",
|
| 31 |
"python-multipart>=0.0.22",
|
| 32 |
"sentence-transformers>=5.2.3",
|
| 33 |
"transformers>=5.3.0",
|
| 34 |
"unstructured>=0.21.5",
|
| 35 |
+
"unstructured-inference>=1.6.11",
|
| 36 |
+
"unstructured-pytesseract>=0.3.15",
|
| 37 |
"youtube-transcript-api>=1.2.4",
|
| 38 |
]
|
src/MultiRag/components/__init__.py
ADDED
|
File without changes
|
src/MultiRag/components/content_embedder.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
from utils.asyncHandler import asyncHandler
|
| 3 |
+
from src.MultiRag.entity.config_entity import ContentEmbedderConfig
|
| 4 |
+
from src.MultiRag.utils.ingestion_utils import create_vector_store,create_retreiver
|
| 5 |
+
from src.MultiRag.constants import RETREIVER_DEFAULT_K
|
| 6 |
+
from src.MultiRag.entity.artifact_entity import RetrievalArtifact
|
| 7 |
+
from abc import ABC, abstractmethod
|
| 8 |
+
import logging
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class Retreiver(ABC):
|
| 12 |
+
def __init__(self):
|
| 13 |
+
pass
|
| 14 |
+
|
| 15 |
+
@abstractmethod
|
| 16 |
+
async def retreive(self, query: str):
|
| 17 |
+
pass
|
| 18 |
+
|
| 19 |
+
class ContentRetreiver(Retreiver):
|
| 20 |
+
def __init__(self, retriever):
|
| 21 |
+
self.retriever = retriever
|
| 22 |
+
|
| 23 |
+
async def retreive(self, query: str):
|
| 24 |
+
return await self.retriever.ainvoke(query)
|
| 25 |
+
class ContentEmbedder:
|
| 26 |
+
def __init__(self, content_embedder_config: ContentEmbedderConfig):
|
| 27 |
+
self.content_embedder_config = content_embedder_config
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
@asyncHandler
|
| 31 |
+
async def embed_PDF(self):
|
| 32 |
+
vector_store = await create_vector_store(path=self.content_embedder_config.vector_store_path, docs=self.content_embedder_config.file_path)
|
| 33 |
+
return vector_store
|
| 34 |
+
|
| 35 |
+
@asyncHandler
|
| 36 |
+
async def create_retriever(self,vector_store, k:int = RETREIVER_DEFAULT_K)->RetrievalArtifact:
|
| 37 |
+
retriever = await create_retreiver(vectorstore=vector_store, k=k)
|
| 38 |
+
return retriever
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
@asyncHandler
|
| 44 |
+
async def embed_content(self)->RetrievalArtifact:
|
| 45 |
+
logging.info("Starting content embedding process...")
|
| 46 |
+
|
| 47 |
+
vector_store = await self.embed_PDF()
|
| 48 |
+
if vector_store is None:
|
| 49 |
+
logging.warning("No vector store created. Returning empty artifact.")
|
| 50 |
+
return RetrievalArtifact(retreivar=None)
|
| 51 |
+
|
| 52 |
+
logging.info("PDF embedding completed. Creating retriever...")
|
| 53 |
+
retriever = await self.create_retriever(vector_store=vector_store)
|
| 54 |
+
|
| 55 |
+
content_retriever = ContentRetreiver(retriever=retriever)
|
| 56 |
+
logging.info("Retriever created successfully.")
|
| 57 |
+
return RetrievalArtifact(retreivar=content_retriever)
|
| 58 |
+
|
src/MultiRag/components/run_graph.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from src.MultiRag.graph.builder import graph
|
| 2 |
+
from utils.asyncHandler import asyncHandler
|
| 3 |
+
from src.MultiRag.models.rag_model import State
|
| 4 |
+
|
| 5 |
+
import logging
|
| 6 |
+
|
| 7 |
+
class RunComponent:
|
| 8 |
+
def __init__(self):
|
| 9 |
+
pass
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
@asyncHandler
|
| 13 |
+
async def run(self,state:State, thread_id:str):
|
| 14 |
+
logging.info("Entered in the run_component")
|
| 15 |
+
logging.info(f"Running graph with thread_id: {thread_id}")
|
| 16 |
+
|
| 17 |
+
config = {"configurable": {"thread_id": thread_id}}
|
| 18 |
+
res=await graph.ainvoke(state, config)
|
| 19 |
+
logging.info(f"Graph execution completed")
|
| 20 |
+
return res
|
src/MultiRag/constants/__init__.py
CHANGED
|
@@ -9,7 +9,7 @@ RETREIVER_DEFAULT_K=3
|
|
| 9 |
LOGS_DIR="logs"
|
| 10 |
LLM_MODEL_ID = "us.meta.llama3-3-70b-instruct-v1:0"
|
| 11 |
LLM_REGION = "us-east-1"
|
| 12 |
-
|
| 13 |
|
| 14 |
TOP_K_KEYWORDS=10
|
| 15 |
|
|
@@ -19,3 +19,18 @@ DB_FOLDER_PATH="db"
|
|
| 19 |
|
| 20 |
|
| 21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
LOGS_DIR="logs"
|
| 10 |
LLM_MODEL_ID = "us.meta.llama3-3-70b-instruct-v1:0"
|
| 11 |
LLM_REGION = "us-east-1"
|
| 12 |
+
MODEL_NAME="llama-3.3-70b-versatile"
|
| 13 |
|
| 14 |
TOP_K_KEYWORDS=10
|
| 15 |
|
|
|
|
| 19 |
|
| 20 |
|
| 21 |
|
| 22 |
+
AVAILABLE_ANALYSIS=['pdf','txt','docs','docx','png','url', 'search']
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
# ====================== DB =======================
|
| 27 |
+
DB_FOLDER_PATH="db"
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
# ====================== Tool ======================
|
| 32 |
+
SEARCH_MAX_RESULT=5
|
| 33 |
+
SEARCH_TOPIC='general'
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
|
src/MultiRag/entity/artifact_entity.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from dataclasses import dataclass
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
@dataclass
|
| 5 |
+
class RetrievalArtifact:
|
| 6 |
+
retreivar: object
|
src/MultiRag/entity/config_entity.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from dataclasses import dataclass
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
@dataclass
|
| 6 |
+
class ContentEmbedderConfig:
|
| 7 |
+
file_path: str
|
| 8 |
+
vector_store_path: str
|
| 9 |
+
file_types: str = "pdf"
|
src/MultiRag/graph/builder.py
CHANGED
|
@@ -1,43 +1,136 @@
|
|
| 1 |
import logging
|
| 2 |
from langgraph.graph import START, END, StateGraph
|
| 3 |
from src.MultiRag.models.rag_model import State
|
| 4 |
-
from src.MultiRag.nodes.
|
| 5 |
-
from src.MultiRag.
|
| 6 |
-
from src.MultiRag.nodes.
|
| 7 |
-
from src.MultiRag.nodes.
|
|
|
|
| 8 |
from src.MultiRag.memory import memory
|
| 9 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
graph_builder = StateGraph(State)
|
| 11 |
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
|
| 18 |
-
|
| 19 |
-
graph_builder.
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
logging.info("Compiling graph...")
|
| 26 |
graph = graph_builder.compile(checkpointer=memory)
|
| 27 |
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
logging.info("Graph compiled successfully.")
|
| 32 |
|
| 33 |
|
| 34 |
|
| 35 |
|
| 36 |
-
|
| 37 |
async def deleteThread(thread_id: str):
|
| 38 |
try:
|
| 39 |
cp = memory
|
| 40 |
-
# Check if thread exists first
|
| 41 |
state = await cp.aget_tuple(config={'configurable': {'thread_id': thread_id}})
|
| 42 |
if state is None:
|
| 43 |
logging.info(f"Thread {thread_id} not found, nothing to delete.")
|
|
@@ -49,3 +142,26 @@ async def deleteThread(thread_id: str):
|
|
| 49 |
except Exception as e:
|
| 50 |
logging.error(f"Error deleting thread {thread_id}: {e}")
|
| 51 |
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import logging
|
| 2 |
from langgraph.graph import START, END, StateGraph
|
| 3 |
from src.MultiRag.models.rag_model import State
|
| 4 |
+
from src.MultiRag.nodes.chat_node import chat_node
|
| 5 |
+
from src.MultiRag.graph.worker.builder import graph as worker_sub_graph
|
| 6 |
+
from src.MultiRag.nodes.orchestrator_node import orchestrator_node
|
| 7 |
+
from src.MultiRag.nodes.reducer_node import reducer_node
|
| 8 |
+
from langgraph.prebuilt import ToolNode
|
| 9 |
from src.MultiRag.memory import memory
|
| 10 |
+
from langgraph.types import Send
|
| 11 |
+
from src.MultiRag.tools.web_search import WebSearch
|
| 12 |
+
from langchain.agents.middleware import ToolCallLimitMiddleware
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
tool_limiter = ToolCallLimitMiddleware(
|
| 17 |
+
run_limit=3,
|
| 18 |
+
exit_behavior="continue",
|
| 19 |
+
)
|
| 20 |
+
|
| 21 |
+
def enforce_tool_limit(state: State):
|
| 22 |
+
updates = tool_limiter.after_model(state, runtime=None)
|
| 23 |
+
return updates or {}
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def after_tool_limit(state: State):
|
| 27 |
+
if state.get("jump_to") == "end":
|
| 28 |
+
return "chat_node"
|
| 29 |
+
|
| 30 |
+
last_message = state.get("messages", [])[-1]
|
| 31 |
+
if hasattr(last_message, "tool_calls") and last_message.tool_calls:
|
| 32 |
+
return "tools"
|
| 33 |
+
|
| 34 |
+
return "chat_node"
|
| 35 |
+
logging.info("Initializing StateGraph with State model...")
|
| 36 |
graph_builder = StateGraph(State)
|
| 37 |
|
| 38 |
+
def fanout(state: State):
|
| 39 |
+
logging.info("Evaluating fanout condition from orchestrator_node")
|
| 40 |
+
|
| 41 |
+
plan = state.get("plan")
|
| 42 |
+
if not plan:
|
| 43 |
+
logging.warning("No plan found in state, defaulting to chat_node")
|
| 44 |
+
return "chat_node"
|
| 45 |
+
|
| 46 |
+
if not plan.use_worker:
|
| 47 |
+
logging.info("Orchestrator decided to bypass workers and go to chat")
|
| 48 |
+
return "chat_node"
|
| 49 |
+
|
| 50 |
+
tasks = plan.tasks or []
|
| 51 |
+
if not tasks:
|
| 52 |
+
logging.info("No tasks to execute, going to chat_node")
|
| 53 |
+
return "chat_node"
|
| 54 |
+
|
| 55 |
+
logging.info(f"Fanning out {len(tasks)} tasks to workers")
|
| 56 |
+
|
| 57 |
+
return [
|
| 58 |
+
Send(
|
| 59 |
+
"worker",
|
| 60 |
+
{
|
| 61 |
+
"plan_to_retrieve": task.instruction,
|
| 62 |
+
"file_type": task.file_type,
|
| 63 |
+
"file_path": task.file_path,
|
| 64 |
+
"thread_id": state.get("thread_id", "1"),
|
| 65 |
+
"worker_result": [],
|
| 66 |
+
},
|
| 67 |
+
)
|
| 68 |
+
for task in tasks
|
| 69 |
+
]
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def should_continue(state: State):
|
| 73 |
+
last_message=state.get("messages", [])[-1] if state.get("messages") else None
|
| 74 |
+
if last_message.tool_calls:
|
| 75 |
+
return "tool_limit"
|
| 76 |
+
return END
|
| 77 |
+
logging.info("Adding nodes to graph builder: orchestrator_node, chat_node, worker, reducer_node")
|
| 78 |
+
graph_builder.add_node("orchestrator_node", orchestrator_node)
|
| 79 |
+
graph_builder.add_node("chat_node", chat_node)
|
| 80 |
+
graph_builder.add_node("worker", worker_sub_graph)
|
| 81 |
+
graph_builder.add_node("reducer_node", reducer_node)
|
| 82 |
+
graph_builder.add_node("tools", ToolNode([WebSearch().search]))
|
| 83 |
+
graph_builder.add_node("tool_limit", enforce_tool_limit)
|
| 84 |
+
|
| 85 |
+
logging.info("Configuring graph edges and flow...")
|
| 86 |
+
graph_builder.add_edge(START, "orchestrator_node")
|
| 87 |
|
| 88 |
+
logging.info("Setting up conditional edges from orchestrator_node using fanout")
|
| 89 |
+
graph_builder.add_conditional_edges(
|
| 90 |
+
"orchestrator_node",
|
| 91 |
+
fanout,
|
| 92 |
+
{
|
| 93 |
+
"worker": "worker",
|
| 94 |
+
"chat_node": "chat_node"
|
| 95 |
+
}
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
logging.info("Connecting worker to reducer_node and then to chat_node")
|
| 99 |
+
graph_builder.add_edge("worker", "reducer_node")
|
| 100 |
+
graph_builder.add_edge("reducer_node", "chat_node")
|
| 101 |
+
graph_builder.add_conditional_edges(
|
| 102 |
+
"chat_node",
|
| 103 |
+
should_continue,
|
| 104 |
+
["tool_limit", END]
|
| 105 |
+
)
|
| 106 |
+
# graph_builder.add_conditional_edges("chat_node", should_continue, ["tools", END])
|
| 107 |
+
graph_builder.add_conditional_edges(
|
| 108 |
+
"tool_limit",
|
| 109 |
+
after_tool_limit,
|
| 110 |
+
["tools", "chat_node"]
|
| 111 |
+
)
|
| 112 |
+
graph_builder.add_edge("tools", "chat_node")
|
| 113 |
|
| 114 |
logging.info("Compiling graph...")
|
| 115 |
graph = graph_builder.compile(checkpointer=memory)
|
| 116 |
|
| 117 |
+
try:
|
| 118 |
+
png_data = graph.get_graph(xray=1).draw_mermaid_png()
|
| 119 |
+
with open("graph.png", "wb") as f:
|
| 120 |
+
f.write(png_data)
|
| 121 |
+
logging.info("Graph visualization saved to graph.png")
|
| 122 |
+
except Exception as e:
|
| 123 |
+
logging.warning(f"Could not generate graph visualization: {e}")
|
| 124 |
+
|
| 125 |
logging.info("Graph compiled successfully.")
|
| 126 |
|
| 127 |
|
| 128 |
|
| 129 |
|
| 130 |
+
|
| 131 |
async def deleteThread(thread_id: str):
|
| 132 |
try:
|
| 133 |
cp = memory
|
|
|
|
| 134 |
state = await cp.aget_tuple(config={'configurable': {'thread_id': thread_id}})
|
| 135 |
if state is None:
|
| 136 |
logging.info(f"Thread {thread_id} not found, nothing to delete.")
|
|
|
|
| 142 |
except Exception as e:
|
| 143 |
logging.error(f"Error deleting thread {thread_id}: {e}")
|
| 144 |
return False
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
async def retrieve_all_threads():
|
| 149 |
+
try:
|
| 150 |
+
cp=memory
|
| 151 |
+
all_threads = set()
|
| 152 |
+
for checkpoint in cp.list(None):
|
| 153 |
+
all_threads.add(checkpoint.config["configurable"]["thread_id"])
|
| 154 |
+
return list(all_threads)
|
| 155 |
+
except Exception as e:
|
| 156 |
+
logging.error(f"Error retrieving threads: {e}")
|
| 157 |
+
return []
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
async def load_conversation(thread_id):
|
| 162 |
+
try:
|
| 163 |
+
state = graph.get_state(config={'configurable': {'thread_id': thread_id}})
|
| 164 |
+
return state.values.get('messages', [])
|
| 165 |
+
except Exception as e:
|
| 166 |
+
logging.error(f"Error loading conversation: {e}")
|
| 167 |
+
return []
|
src/MultiRag/graph/worker/builder.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from langgraph.graph import StateGraph, START, END
|
| 2 |
+
from src.MultiRag.models.worker_model import State
|
| 3 |
+
from src.MultiRag.nodes.worker import (
|
| 4 |
+
pdf,
|
| 5 |
+
txt,
|
| 6 |
+
docs,
|
| 7 |
+
image,
|
| 8 |
+
url,
|
| 9 |
+
decider,
|
| 10 |
+
search
|
| 11 |
+
)
|
| 12 |
+
from src.MultiRag.constants import AVAILABLE_ANALYSIS
|
| 13 |
+
import logging
|
| 14 |
+
|
| 15 |
+
logging.info("Building worker sub graph")
|
| 16 |
+
|
| 17 |
+
graph = StateGraph(State)
|
| 18 |
+
|
| 19 |
+
graph.add_node("decider", decider.decider_node)
|
| 20 |
+
graph.add_node("pdf", pdf.pdf_node)
|
| 21 |
+
graph.add_node("txt", txt.txt_node)
|
| 22 |
+
graph.add_node("docs", docs.docs_node)
|
| 23 |
+
graph.add_node("url", url.url_node)
|
| 24 |
+
graph.add_node("image", image.image_node)
|
| 25 |
+
graph.add_node("search", search.search_node)
|
| 26 |
+
|
| 27 |
+
def route_fn(state: State):
|
| 28 |
+
logging.info(f"Routing based on file_type: {state.file_type}")
|
| 29 |
+
if state.file_type in AVAILABLE_ANALYSIS:
|
| 30 |
+
return state.file_type
|
| 31 |
+
return "end"
|
| 32 |
+
|
| 33 |
+
graph.add_conditional_edges(
|
| 34 |
+
START,
|
| 35 |
+
route_fn,
|
| 36 |
+
{
|
| 37 |
+
"pdf": "pdf",
|
| 38 |
+
"txt": "txt",
|
| 39 |
+
"docs": "docs",
|
| 40 |
+
"png": "image",
|
| 41 |
+
"url": "url",
|
| 42 |
+
"search": "search",
|
| 43 |
+
"end":END
|
| 44 |
+
}
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
graph.add_edge("pdf", END)
|
| 48 |
+
graph.add_edge("txt", END)
|
| 49 |
+
graph.add_edge("docs", END)
|
| 50 |
+
graph.add_edge("url", END)
|
| 51 |
+
graph.add_edge("image", END)
|
| 52 |
+
graph.add_edge("search", END)
|
| 53 |
+
|
| 54 |
+
graph = graph.compile()
|
| 55 |
+
|
| 56 |
+
try:
|
| 57 |
+
with open("worker_sub_graph.png", "wb") as f:
|
| 58 |
+
f.write(graph.get_graph().draw_mermaid_png())
|
| 59 |
+
logging.info("Graph image saved successfully")
|
| 60 |
+
except Exception as e:
|
| 61 |
+
logging.error(f"Error saving graph: {e}")
|
| 62 |
+
raise Exception(e)
|
src/MultiRag/llm/llm_loader.py
CHANGED
|
@@ -1,9 +1,14 @@
|
|
| 1 |
from langchain_aws import ChatBedrockConverse
|
| 2 |
-
|
| 3 |
-
from src.MultiRag.constants import LLM_MODEL_ID,LLM_REGION
|
| 4 |
import logging
|
| 5 |
llm = ChatBedrockConverse(
|
| 6 |
model_id=LLM_MODEL_ID,
|
| 7 |
region_name=LLM_REGION
|
| 8 |
)
|
| 9 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
from langchain_aws import ChatBedrockConverse
|
| 2 |
+
from langchain_groq import ChatGroq
|
| 3 |
+
from src.MultiRag.constants import LLM_MODEL_ID,LLM_REGION,MODEL_NAME
|
| 4 |
import logging
|
| 5 |
llm = ChatBedrockConverse(
|
| 6 |
model_id=LLM_MODEL_ID,
|
| 7 |
region_name=LLM_REGION
|
| 8 |
)
|
| 9 |
+
|
| 10 |
+
# llm=ChatGroq(
|
| 11 |
+
# model=MODEL_NAME
|
| 12 |
+
# )
|
| 13 |
+
# logging.info(f"LLM initialized with model_id={LLM_MODEL_ID}, region_name={LLM_REGION}")
|
| 14 |
+
logging.info(f"LLM initialized with model_name:{MODEL_NAME}")
|