├── LICENSE ├── README.md ├── demovideo.mp4 ├── rag-app ├── .gitignore ├── Dockerfile ├── README.md ├── app │ ├── __init__.py │ ├── rag_chain.py │ └── server.py ├── frontend │ ├── .gitignore │ ├── README.md │ ├── components.json │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ ├── src │ │ ├── App.css │ │ ├── App.test.tsx │ │ ├── App.tsx │ │ ├── components │ │ │ └── ui │ │ │ │ ├── button.tsx │ │ │ │ ├── input.tsx │ │ │ │ ├── label.tsx │ │ │ │ ├── skeleton.tsx │ │ │ │ └── textarea.tsx │ │ ├── index.css │ │ ├── index.tsx │ │ ├── logo.svg │ │ ├── react-app-env.d.ts │ │ ├── reportWebVitals.ts │ │ ├── setupTests.ts │ │ └── utils.ts │ ├── tailwind.config.js │ └── tsconfig.json ├── packages │ └── README.md ├── pdf-documents │ ├── .DS_Store │ ├── John_F_Kennedy.pdf │ ├── Joseph_P_Kennedy_Sr.pdf │ ├── Kathleen_Cavendish_Marchioness_of_Hartington.pdf │ └── Robert_F_Kennedy.pdf ├── poetry.lock ├── pyproject.toml ├── rag-data-loader │ └── rag_load_and_process.py ├── rag_app │ └── __init__.py └── tests │ └── __init__.py └── rag.png /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Bensmail Anis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RAGbot - Generative AI RAG Application 2 | 3 | A powerful chatbot assistant leveraging Retrieval-Augmented Generation (RAG) to answer questions from multiple PDF documents. RAGbot generates accurate responses and provides source references, making it an ideal assistant for working with domain-specific PDFs. 4 | 5 | ![rag](https://github.com/user-attachments/assets/58719381-f326-41ff-be0f-f20eedd1c7fc) 6 | 7 | ## Table of Contents 8 | 9 | - [Project Structure](#project-structure) 10 | - [Installation](#installation) 11 | - [Backend Setup](#backend-setup) 12 | - [Frontend Setup](#frontend-setup) 13 | - [Usage](#usage) 14 | - [Running the Backend](#running-the-backend) 15 | - [Running the Frontend](#running-the-frontend) 16 | - [Video Demo](#video-demo) 17 | - [License](#license) 18 | 19 | ## Project Structure 20 | - You will find a helpful readme files in the backend and frontend directories. 21 | ### Backend (`rag-app`) 22 | 23 | - **Core Files** 24 | - `pyproject.toml` and `poetry.lock`: Dependency management files for the backend. 25 | - `rag-data-loader/`: Module for loading and processing PDF documents. 26 | - `pdf-documents/`: Directory to store the input PDF files. 27 | - `app/`: Main backend logic, including: 28 | - `server.py`: API entry point for the backend server. 29 | - `rag_chain.py`: Logic for connecting LangChain with PDF data. 30 | 31 | - **Utilities** 32 | - `.env`: Environment variable configurations , put "OPENAI_API_KEY" and Langchain tracing keys . 33 | - `Dockerfile`: Containerization support for the backend. 34 | 35 | ### Frontend (`frontend`) 36 | 37 | - **Core Files** 38 | - `src/`: React source code for the RAGbot user interface. 39 | - `public/`: Static assets for the frontend. 40 | 41 | - **Configuration** 42 | - `package.json` and `package-lock.json`: Frontend dependencies and scripts. 43 | - `tailwind.config.js` and `tsconfig.json`: Configuration files for Tailwind CSS and TypeScript. 44 | 45 | ### Additional Files 46 | 47 | - `README.md`: Project documentation. 48 | - `LICENSE`: Licensing details for the project. 49 | 50 | ## Installation 51 | 52 | ### Backend Setup 53 | 54 | 1. Install Poetry: 55 | 56 | ```bash 57 | pip install poetry 58 | ``` 59 | 60 | 2. Navigate to the `rag-app` directory: 61 | 62 | ```bash 63 | cd rag-app 64 | ``` 65 | 66 | 3. Install dependencies: 67 | 68 | ```bash 69 | poetry install 70 | ``` 71 | ### Important Notes : 72 | - Ensure that a PostgreSQL database is installed with the `pgvector` extension enabled. This is required for storing and querying vector embeddings efficiently. 73 | - Check the official documentations and pgvector github repo. 74 | - Next, connect your created database to your RAG-data-loader/rag_load_process_and_process.py by providing your credentials in PGVector.from_documents() method and in app/rag_chain in PGVector() and 'postgres_memory_url' where you will create another database for chat history. 75 | ### Frontend Setup 76 | 77 | 1. Navigate to the `frontend` directory: 78 | 79 | ```bash 80 | cd frontend 81 | ``` 82 | 83 | 2. Install frontend dependencies: 84 | 85 | ```bash 86 | npm install 87 | ``` 88 | 89 | 3. Start the frontend: 90 | 91 | ```bash 92 | npm start 93 | ``` 94 | 95 | ## Usage 96 | 97 | ### Running the Backend 98 | 99 | Run the backend using one of the following commands: 100 | 101 | - Using LangChain: 102 | 103 | ```bash 104 | poetry run langchain serve 105 | ``` 106 | 107 | - Using Uvicorn: 108 | 109 | ```bash 110 | uvicorn app.server:app --reload 111 | ``` 112 | 113 | The backend will start on `http://localhost:8000` by default. 114 | 115 | ### Running the Frontend 116 | 117 | After setting up the frontend, visit `http://localhost:3000` in your browser to interact with the RAGbot. The frontend communicates with the backend to process PDFs and generate responses. 118 | 119 | ## Video Demo 120 | 121 | Watch the video demo below to see RAGbot in action: 122 | 123 | https://github.com/user-attachments/assets/92bc31c4-96b6-4634-bb9b-36af525d4328 124 | 125 | 126 | 127 | ## License 128 | 129 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. 130 | -------------------------------------------------------------------------------- /demovideo.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bensmail-anis/RAGbot---Generative_AI-RAG-Application/1b81e5b3f3193c8afdfd34ae6f1c24a51342964d/demovideo.mp4 -------------------------------------------------------------------------------- /rag-app/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | 3 | .env -------------------------------------------------------------------------------- /rag-app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.11-slim 2 | 3 | RUN pip install poetry==1.6.1 4 | 5 | RUN poetry config virtualenvs.create false 6 | 7 | WORKDIR /code 8 | 9 | COPY ./pyproject.toml ./README.md ./poetry.lock* ./ 10 | 11 | COPY ./package[s] ./packages 12 | 13 | RUN poetry install --no-interaction --no-ansi --no-root 14 | 15 | COPY ./app ./app 16 | 17 | RUN poetry install --no-interaction --no-ansi 18 | 19 | EXPOSE 8080 20 | 21 | CMD exec uvicorn app.server:app --host 0.0.0.0 --port 8080 22 | -------------------------------------------------------------------------------- /rag-app/README.md: -------------------------------------------------------------------------------- 1 | # rag-app 2 | 3 | ## Installation 4 | 5 | Install the LangChain CLI if you haven't yet 6 | 7 | ```bash 8 | pip install -U langchain-cli 9 | ``` 10 | 11 | ## Adding packages 12 | 13 | ```bash 14 | # adding packages from 15 | # https://github.com/langchain-ai/langchain/tree/master/templates 16 | langchain app add $PROJECT_NAME 17 | 18 | # adding custom GitHub repo packages 19 | langchain app add --repo $OWNER/$REPO 20 | # or with whole git string (supports other git providers): 21 | # langchain app add git+https://github.com/hwchase17/chain-of-verification 22 | 23 | # with a custom api mount point (defaults to `/{package_name}`) 24 | langchain app add $PROJECT_NAME --api_path=/my/custom/path/rag 25 | ``` 26 | 27 | Note: you remove packages by their api path 28 | 29 | ```bash 30 | langchain app remove my/custom/path/rag 31 | ``` 32 | 33 | ## Setup LangSmith (Optional) 34 | LangSmith will help us trace, monitor and debug LangChain applications. 35 | You can sign up for LangSmith [here](https://smith.langchain.com/). 36 | If you don't have access, you can skip this section 37 | 38 | 39 | ```shell 40 | export LANGCHAIN_TRACING_V2=true 41 | export LANGCHAIN_API_KEY= 42 | export LANGCHAIN_PROJECT= # if not specified, defaults to "default" 43 | ``` 44 | 45 | ## Launch LangServe 46 | 47 | ```bash 48 | langchain serve 49 | ``` 50 | 51 | ## Running in Docker 52 | 53 | This project folder includes a Dockerfile that allows you to easily build and host your LangServe app. 54 | 55 | ### Building the Image 56 | 57 | To build the image, you simply: 58 | 59 | ```shell 60 | docker build . -t my-langserve-app 61 | ``` 62 | 63 | If you tag your image with something other than `my-langserve-app`, 64 | note it for use in the next step. 65 | 66 | ### Running the Image Locally 67 | 68 | To run the image, you'll need to include any environment variables 69 | necessary for your application. 70 | 71 | In the below example, we inject the `OPENAI_API_KEY` environment 72 | variable with the value set in my local environment 73 | (`$OPENAI_API_KEY`) 74 | 75 | We also expose port 8080 with the `-p 8080:8080` option. 76 | 77 | ```shell 78 | docker run -e OPENAI_API_KEY=$OPENAI_API_KEY -p 8080:8080 my-langserve-app 79 | ``` 80 | -------------------------------------------------------------------------------- /rag-app/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bensmail-anis/RAGbot---Generative_AI-RAG-Application/1b81e5b3f3193c8afdfd34ae6f1c24a51342964d/rag-app/app/__init__.py -------------------------------------------------------------------------------- /rag-app/app/rag_chain.py: -------------------------------------------------------------------------------- 1 | import os 2 | from operator import itemgetter 3 | from typing import TypedDict 4 | 5 | from dotenv import load_dotenv 6 | from langchain_community.vectorstores.pgvector import PGVector 7 | from langchain_core.prompts import ChatPromptTemplate 8 | from langchain_openai import ChatOpenAI, OpenAIEmbeddings 9 | from langchain_core.runnables import RunnableParallel 10 | from langchain.retrievers.multi_query import MultiQueryRetriever 11 | from langchain_core.runnables.history import RunnableWithMessageHistory 12 | from langchain_community.chat_message_histories import SQLChatMessageHistory 13 | from langchain.prompts import PromptTemplate 14 | from langchain_core.runnables import RunnablePassthrough 15 | from langchain_core.output_parsers import StrOutputParser 16 | from langchain_core.messages import get_buffer_string 17 | 18 | load_dotenv() 19 | 20 | # Add explicit error handling for database connection 21 | try: 22 | vector_store = PGVector( 23 | collection_name="collection164", 24 | connection_string="postgresql+psycopg://postgres:super4869@localhost:5432/rag", 25 | embedding_function=OpenAIEmbeddings() 26 | ) 27 | except Exception as e: 28 | print(f"Vector Store Connection Error: {e}") 29 | raise 30 | 31 | template = """ 32 | You are RAGbot assistant, a powerful chatbot that leverages Retrieval-Augmented Generation (RAG) to answer questions from multiple PDF documents. 33 | Answer given the following context: 34 | {context} 35 | 36 | Question: {question} 37 | 38 | If there is no context , try to use your knowledge. 39 | """ 40 | 41 | ANSWER_PROMPT = ChatPromptTemplate.from_template(template) 42 | 43 | llm = ChatOpenAI(temperature=0, model='gpt-4o-mini', streaming=True) 44 | 45 | 46 | class RagInput(TypedDict): 47 | question: str 48 | 49 | multiquery = MultiQueryRetriever.from_llm( 50 | retriever=vector_store.as_retriever(), 51 | llm=llm, 52 | ) 53 | 54 | old_chain = ( 55 | RunnableParallel( 56 | context=(itemgetter("question") | multiquery), 57 | question=itemgetter("question") 58 | ) | 59 | RunnableParallel( 60 | answer=(ANSWER_PROMPT | llm), 61 | docs=itemgetter("context") 62 | ) 63 | ).with_types(input_type=RagInput) 64 | 65 | postgres_memory_url = "postgresql+psycopg://postgres:super4869@localhost:5432/pdf_rag_history" 66 | 67 | get_session_history = lambda session_id: SQLChatMessageHistory( 68 | connection_string=postgres_memory_url, 69 | session_id=session_id, 70 | async_mode=True 71 | ) 72 | 73 | template_with_history=""" 74 | Given the following conversation and a follow 75 | up question, rephrase the follow up question 76 | to be a standalone question, in its original 77 | language 78 | 79 | Chat History: 80 | {chat_history} 81 | Follow Up Input: {question} 82 | Standalone question:""" 83 | 84 | standalone_question_prompt = PromptTemplate.from_template(template_with_history) 85 | 86 | standalone_question_mini_chain = RunnableParallel( 87 | question=RunnableParallel( 88 | question=RunnablePassthrough(), 89 | chat_history=lambda x:get_buffer_string(x["chat_history"]) 90 | ) 91 | | standalone_question_prompt 92 | | llm 93 | | StrOutputParser() 94 | ) 95 | 96 | 97 | final_chain = RunnableWithMessageHistory( 98 | runnable=standalone_question_mini_chain | old_chain, 99 | input_messages_key="question", 100 | history_messages_key="chat_history", 101 | output_messages_key="answer", 102 | get_session_history=get_session_history, 103 | ) -------------------------------------------------------------------------------- /rag-app/app/server.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI, File, UploadFile, HTTPException 2 | from fastapi.responses import RedirectResponse 3 | from fastapi.middleware.cors import CORSMiddleware 4 | from langserve import add_routes 5 | from starlette.staticfiles import StaticFiles 6 | import os 7 | import shutil 8 | import subprocess 9 | from app.rag_chain import final_chain 10 | 11 | app = FastAPI() 12 | 13 | app.add_middleware( 14 | CORSMiddleware, 15 | allow_origins=[ 16 | "http://localhost:3000" 17 | ], 18 | allow_credentials=True, 19 | allow_methods=["*"], 20 | allow_headers=["*"], 21 | ) 22 | 23 | app.mount("/rag/static", StaticFiles(directory="./pdf-documents"), name="static") 24 | @app.get("/") 25 | async def redirect_root_to_docs(): 26 | return RedirectResponse("/docs") 27 | 28 | pdf_directory = "./pdf-documents" 29 | 30 | import logging 31 | from fastapi.responses import JSONResponse 32 | 33 | logger = logging.getLogger("uvicorn") 34 | 35 | @app.post("/upload") 36 | async def upload_files(files: list[UploadFile] = File(...)): 37 | uploaded_files = [] 38 | for file in files: 39 | try: 40 | file_path = os.path.join(pdf_directory, file.filename) 41 | with open(file_path, "wb") as buffer: 42 | shutil.copyfileobj(file.file, buffer) 43 | uploaded_files.append(file.filename) 44 | logger.info(f"Uploaded file: {file.filename}") 45 | except Exception as e: 46 | logger.error(f"Error saving file {file.filename}: {e}") 47 | raise HTTPException(status_code=500, detail=f"Could not save file: {e}") 48 | 49 | return JSONResponse( 50 | content={"message": "Files uploaded successfully", "filenames": uploaded_files}, 51 | status_code=200 52 | ) 53 | 54 | 55 | 56 | @app.post("/load-and-process-pdfs") 57 | async def load_and_process_pdfs(): 58 | try: 59 | # Run the script using poetry's virtual environment 60 | result = subprocess.run( 61 | ["poetry", "run", "python", "./rag-data-loader/rag_load_and_process.py"], 62 | check=True, 63 | stdout=subprocess.PIPE, 64 | stderr=subprocess.PIPE, 65 | text=True 66 | ) 67 | 68 | logger.info(f"Script executed successfully. Output:\n{result.stdout}") 69 | return JSONResponse( 70 | content={"message": "PDFs loaded and processed successfully", "output": result.stdout}, 71 | status_code=200 72 | ) 73 | except subprocess.CalledProcessError as e: 74 | logger.error(f"Script execution failed. Error:\n{e.stderr}") 75 | return JSONResponse( 76 | content={"error": "Failed to execute script", "details": e.stderr}, 77 | status_code=500 78 | ) 79 | except Exception as e: 80 | logger.error(f"Unexpected error: {e}") 81 | return JSONResponse( 82 | content={"error": "An unexpected error occurred", "details": str(e)}, 83 | status_code=500 84 | ) 85 | 86 | # Edit this to add the chain you want to add 87 | add_routes(app, final_chain, path="/rag") 88 | 89 | if __name__ == "__main__": 90 | import uvicorn 91 | 92 | uvicorn.run(app, host="0.0.0.0", port=8000) -------------------------------------------------------------------------------- /rag-app/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /rag-app/frontend/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 13 | 14 | The page will reload if you make edits.\ 15 | You will also see any lint errors in the console. 16 | 17 | ### `npm test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `npm run build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `npm run eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 35 | 36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 39 | 40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | -------------------------------------------------------------------------------- /rag-app/frontend/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "src/index.css", 9 | "baseColor": "zinc", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/src/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } 22 | -------------------------------------------------------------------------------- /rag-app/frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@microsoft/fetch-event-source": "^2.0.1", 7 | "@radix-ui/react-label": "^2.1.0", 8 | "@radix-ui/react-slot": "^1.1.0", 9 | "class-variance-authority": "^0.7.1", 10 | "clsx": "^2.1.1", 11 | "cra-template-typescript": "1.2.0", 12 | "lucide-react": "^0.468.0", 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0", 15 | "react-scripts": "5.0.1", 16 | "react-toastify": "^10.0.6", 17 | "tailwind-merge": "^2.5.5", 18 | "tailwindcss-animate": "^1.0.7", 19 | "web-vitals": "^4.2.4" 20 | }, 21 | "scripts": { 22 | "start": "react-scripts start", 23 | "build": "react-scripts build", 24 | "test": "react-scripts test", 25 | "eject": "react-scripts eject" 26 | }, 27 | "eslintConfig": { 28 | "extends": [ 29 | "react-app", 30 | "react-app/jest" 31 | ] 32 | }, 33 | "browserslist": { 34 | "production": [ 35 | ">0.2%", 36 | "not dead", 37 | "not op_mini all" 38 | ], 39 | "development": [ 40 | "last 1 chrome version", 41 | "last 1 firefox version", 42 | "last 1 safari version" 43 | ] 44 | }, 45 | "devDependencies": { 46 | "@babel/plugin-proposal-private-property-in-object": "^7.21.11", 47 | "@testing-library/jest-dom": "^6.6.3", 48 | "@types/jest": "^29.5.14", 49 | "@types/react": "^19.0.1", 50 | "@types/react-dom": "^19.0.1", 51 | "@types/testing-library__react": "^10.2.0", 52 | "@types/uuid": "^10.0.0", 53 | "tailwindcss": "^3.4.16" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /rag-app/frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bensmail-anis/RAGbot---Generative_AI-RAG-Application/1b81e5b3f3193c8afdfd34ae6f1c24a51342964d/rag-app/frontend/public/favicon.ico -------------------------------------------------------------------------------- /rag-app/frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /rag-app/frontend/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bensmail-anis/RAGbot---Generative_AI-RAG-Application/1b81e5b3f3193c8afdfd34ae6f1c24a51342964d/rag-app/frontend/public/logo192.png -------------------------------------------------------------------------------- /rag-app/frontend/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bensmail-anis/RAGbot---Generative_AI-RAG-Application/1b81e5b3f3193c8afdfd34ae6f1c24a51342964d/rag-app/frontend/public/logo512.png -------------------------------------------------------------------------------- /rag-app/frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /rag-app/frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /rag-app/frontend/src/App.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | :root { 7 | --background: 0 0% 100%; 8 | --foreground: 222.2 47.4% 11.2%; 9 | --muted: 210 40% 96.1%; 10 | --muted-foreground: 215.4 16.3% 46.9%; 11 | --popover: 0 0% 100%; 12 | --popover-foreground: 222.2 47.4% 11.2%; 13 | --border: 214.3 31.8% 91.4%; 14 | --input: 214.3 31.8% 91.4%; 15 | --card: 0 0% 100%; 16 | --card-foreground: 222.2 47.4% 11.2%; 17 | --primary: 222.2 47.4% 11.2%; 18 | --primary-foreground: 210 40% 98%; 19 | --secondary: 210 40% 96.1%; 20 | --secondary-foreground: 222.2 47.4% 11.2%; 21 | --accent: 210 40% 96.1%; 22 | --accent-foreground: 222.2 47.4% 11.2%; 23 | --destructive: 0 100% 50%; 24 | --destructive-foreground: 210 40% 98%; 25 | --ring: 215 20.2% 65.1%; 26 | --radius: 0.5rem; 27 | } 28 | 29 | .dark { 30 | --background: 224 71% 4%; 31 | --foreground: 213 31% 91%; 32 | --muted: 223 47% 11%; 33 | --muted-foreground: 215.4 16.3% 56.9%; 34 | --accent: 216 34% 17%; 35 | --accent-foreground: 210 40% 98%; 36 | --popover: 224 71% 4%; 37 | --popover-foreground: 215 20.2% 65.1%; 38 | --border: 216 34% 17%; 39 | --input: 216 34% 17%; 40 | --card: 224 71% 4%; 41 | --card-foreground: 213 31% 91%; 42 | --primary: 210 40% 98%; 43 | --primary-foreground: 222.2 47.4% 1.2%; 44 | --secondary: 222.2 47.4% 11.2%; 45 | --secondary-foreground: 210 40% 98%; 46 | --destructive: 0 63% 31%; 47 | --destructive-foreground: 210 40% 98%; 48 | --ring: 216 34% 17%; 49 | } 50 | } 51 | 52 | @layer base { 53 | * { 54 | @apply border-border; 55 | } 56 | body { 57 | @apply font-sans antialiased bg-background text-foreground; 58 | } 59 | } 60 | 61 | 62 | 63 | 64 | .App { 65 | text-align: center; 66 | } 67 | 68 | .App-logo { 69 | height: 40vmin; 70 | pointer-events: none; 71 | } 72 | 73 | @media (prefers-reduced-motion: no-preference) { 74 | .App-logo { 75 | animation: App-logo-spin infinite 20s linear; 76 | } 77 | } 78 | 79 | .App-header { 80 | background-color: #282c34; 81 | min-height: 100vh; 82 | display: flex; 83 | flex-direction: column; 84 | align-items: center; 85 | justify-content: center; 86 | font-size: calc(10px + 2vmin); 87 | color: white; 88 | } 89 | 90 | .App-link { 91 | color: #61dafb; 92 | } 93 | 94 | @keyframes App-logo-spin { 95 | from { 96 | transform: rotate(0deg); 97 | } 98 | to { 99 | transform: rotate(360deg); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /rag-app/frontend/src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders learn react link', () => { 6 | render(); 7 | const linkElement = screen.getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /rag-app/frontend/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React, {useState, useEffect, useRef} from 'react'; 2 | import './App.css'; 3 | import {fetchEventSource} from "@microsoft/fetch-event-source"; 4 | import {v4 as uuidv4} from 'uuid'; 5 | import { ToastContainer, toast } from 'react-toastify'; 6 | import {Input} from './components/ui/input'; 7 | import { Label } from './components/ui/label'; 8 | import 'react-toastify/dist/ReactToastify.css'; 9 | import { Textarea } from './components/ui/textarea'; 10 | import { FileText, Send, Sparkle, Upload } from 'lucide-react'; 11 | import { Button } from './components/ui/button'; 12 | import { Skeleton } from './components/ui/skeleton'; 13 | 14 | 15 | interface Message { 16 | message: string; 17 | isUser: boolean; 18 | sources?: string[]; 19 | } 20 | 21 | function App() { 22 | const [inputValue, setInputValue] = useState("") 23 | const [messages, setMessages] = useState([]); 24 | const [selectedFiles, setSelectedFiles] = useState(null); 25 | const sessionIdRef = useRef(uuidv4()); 26 | 27 | useEffect(() => { 28 | sessionIdRef.current = uuidv4(); 29 | }, []); 30 | 31 | 32 | const setPartialMessage = (chunk: string, sources: string[] = []) => { 33 | setMessages(prevMessages => { 34 | if (prevMessages.length > 0 && !prevMessages[prevMessages.length - 1].isUser) { 35 | return [...prevMessages.slice(0, -1), { 36 | message: prevMessages[prevMessages.length - 1].message + chunk, 37 | isUser: false, 38 | sources: [...new Set([...prevMessages[prevMessages.length - 1].sources || [], ...sources])] 39 | }]; 40 | } 41 | return [...prevMessages, {message: chunk, isUser: false, sources}]; 42 | }); 43 | }; 44 | 45 | function handleReceiveMessage(data: string) { 46 | let parsedData = JSON.parse(data); 47 | 48 | if (parsedData.answer) { 49 | setPartialMessage(parsedData.answer.content) 50 | } 51 | 52 | if (parsedData.docs) { 53 | setPartialMessage("", parsedData.docs.map((doc: any) => doc.metadata.source)) 54 | } 55 | } 56 | 57 | const handleSendMessage = async (message: string) => { 58 | setInputValue("") 59 | 60 | setMessages(prevMessages => [...prevMessages, {message, isUser: true}]); 61 | 62 | await fetchEventSource(`http://localhost:8000/rag/stream`, { 63 | method: 'POST', 64 | openWhenHidden: true, 65 | headers: { 66 | 'Content-Type': 'application/json', 67 | }, 68 | body: JSON.stringify({ 69 | input: { 70 | question: message, 71 | }, 72 | config: { 73 | configurable: { 74 | sessionId: sessionIdRef.current 75 | } 76 | } 77 | }), 78 | onmessage(event) { 79 | if (event.event === "data") { 80 | handleReceiveMessage(event.data); 81 | } 82 | }, 83 | }) 84 | } 85 | 86 | const handleKeyPress = (event: React.KeyboardEvent) => { 87 | if (event.key === "Enter" && !event.shiftKey) { 88 | handleSendMessage(inputValue.trim()) 89 | } 90 | } 91 | 92 | function formatSource(source: string) { 93 | return source.split("/").pop() || ""; 94 | } 95 | 96 | const handleUploadFiles = async () => { 97 | if (!selectedFiles) { 98 | console.error('No files selected'); 99 | return; 100 | } 101 | 102 | const formData = new FormData(); 103 | Array.from(selectedFiles).forEach((file) => { 104 | formData.append('files', file); 105 | }); 106 | 107 | try { 108 | const response = await fetch('http://localhost:8000/upload', { 109 | method: 'POST', 110 | body: formData, // Fetch automatically adds headers for `multipart/form-data`. 111 | }); 112 | 113 | if (response.ok) { 114 | const responseData = await response.json(); 115 | console.log(responseData.message); 116 | const notify = () => toast.success(`Upload successful: ${responseData.message}`); 117 | notify(); 118 | } else { 119 | console.error('Upload failed:', response.status, response.statusText); 120 | } 121 | } catch (error) { 122 | console.error('Error uploading files:', error); 123 | } 124 | }; 125 | 126 | 127 | const loadAndProcessPDFs = async () => { 128 | try { 129 | const response = await fetch('http://localhost:8000/load-and-process-pdfs', { 130 | method: 'POST', 131 | }); 132 | 133 | const responseData = await response.json(); 134 | 135 | if (response.ok) { 136 | // Success case: Show success message and output 137 | console.log(responseData.message); 138 | const notify = () => toast.success(`Success: ${responseData.message}\n\nOutput:\n${responseData.output}`); 139 | notify(); 140 | } else { 141 | // Error case: Show error and details 142 | console.error('Error:', responseData.error); 143 | const notify = () => toast.error(`Error: ${responseData.error}\n\nDetails:\n${responseData.details}`); 144 | notify(); 145 | } 146 | } catch (error) { 147 | console.error('Network error:', error); 148 | 149 | // Handle the case where `error` is not a standard Error object 150 | if (error instanceof Error) { 151 | const notify = () => toast.error(`Network error:`); 152 | notify(); 153 | } else { 154 | const notify = () => toast.error('An unexpected network error occurred.'); 155 | notify(); 156 | } 157 | } 158 | }; 159 | 160 | 161 | return ( 162 |
163 |
164 |
165 |
166 | 167 |

RAG BOT

168 |
169 |
170 | 171 |
172 |
173 |
174 |
175 |
176 |

Welcome to RAG-BOT

177 |

178 | Upload a PDF and ask questions about its content. 179 |

180 |
181 |
182 | 183 |
184 | {messages.map((msg, index) => ( 185 |
187 | {msg.message} 188 | {/* Source */} 189 | {!msg.isUser && ( 190 |
191 |
192 | {msg.sources?.map((source, index) => ( 193 | 202 | ))} 203 |
204 | )} 205 |
206 | ))} 207 |
208 |
209 | 216 |
217 | 220 |
221 | {/* Reordered elements */} 222 |
223 | setSelectedFiles(e.target.files)} 229 | className="file:mr-4 w-fit h-fit file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-primary file:text-primary-foreground hover:file:bg-primary/90" 230 | /> 231 | 234 |
235 |
236 | 239 |
240 |
241 |
242 | 243 |
244 |
245 |
246 |

247 | © 2023 RAG-BOT. Made By Anis Bensmail. All rights reserved. 248 |

249 |
250 | 253 | 256 | 259 |
260 |
261 |
262 | 263 |
264 | ); 265 | 266 | } 267 | 268 | export default App; -------------------------------------------------------------------------------- /rag-app/frontend/src/components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { Slot } from "@radix-ui/react-slot" 3 | import { cva, type VariantProps } from "class-variance-authority" 4 | 5 | import { cn } from "../../utils" 6 | 7 | const buttonVariants = cva( 8 | "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", 9 | { 10 | variants: { 11 | variant: { 12 | default: 13 | "bg-primary text-primary-foreground shadow hover:bg-primary/90", 14 | destructive: 15 | "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", 16 | outline: 17 | "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", 18 | secondary: 19 | "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", 20 | ghost: "hover:bg-accent hover:text-accent-foreground", 21 | link: "text-primary underline-offset-4 hover:underline", 22 | }, 23 | size: { 24 | default: "h-9 px-4 py-2", 25 | sm: "h-8 rounded-md px-3 text-xs", 26 | lg: "h-10 rounded-md px-8", 27 | icon: "h-9 w-9", 28 | }, 29 | }, 30 | defaultVariants: { 31 | variant: "default", 32 | size: "default", 33 | }, 34 | } 35 | ) 36 | 37 | export interface ButtonProps 38 | extends React.ButtonHTMLAttributes, 39 | VariantProps { 40 | asChild?: boolean 41 | } 42 | 43 | const Button = React.forwardRef( 44 | ({ className, variant, size, asChild = false, ...props }, ref) => { 45 | const Comp = asChild ? Slot : "button" 46 | return ( 47 | 52 | ) 53 | } 54 | ) 55 | Button.displayName = "Button" 56 | 57 | export { Button, buttonVariants } 58 | -------------------------------------------------------------------------------- /rag-app/frontend/src/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "../../utils" 4 | 5 | const Input = React.forwardRef>( 6 | ({ className, type, ...props }, ref) => { 7 | return ( 8 | 17 | ) 18 | } 19 | ) 20 | Input.displayName = "Input" 21 | 22 | export { Input } 23 | -------------------------------------------------------------------------------- /rag-app/frontend/src/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as LabelPrimitive from "@radix-ui/react-label" 3 | import { cva, type VariantProps } from "class-variance-authority" 4 | 5 | import { cn } from "../../utils" 6 | 7 | const labelVariants = cva( 8 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 9 | ) 10 | 11 | const Label = React.forwardRef< 12 | React.ElementRef, 13 | React.ComponentPropsWithoutRef & 14 | VariantProps 15 | >(({ className, ...props }, ref) => ( 16 | 21 | )) 22 | Label.displayName = LabelPrimitive.Root.displayName 23 | 24 | export { Label } 25 | -------------------------------------------------------------------------------- /rag-app/frontend/src/components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "../../utils" 2 | 3 | function Skeleton({ 4 | className, 5 | ...props 6 | }: React.HTMLAttributes) { 7 | return ( 8 |
12 | ) 13 | } 14 | 15 | export { Skeleton } 16 | -------------------------------------------------------------------------------- /rag-app/frontend/src/components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "../../utils" 4 | 5 | const Textarea = React.forwardRef< 6 | HTMLTextAreaElement, 7 | React.ComponentProps<"textarea"> 8 | >(({ className, ...props }, ref) => { 9 | return ( 10 |