-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
639dff7
commit e062879
Showing
10 changed files
with
611 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# RAG Chatbot Application | ||
|
||
This is a Retrieval-Augmented Generation (RAG) chatbot application using Langchain, Ollama, and Gradio. | ||
|
||
## Features | ||
|
||
- Uses Ollama for local language model inference | ||
- Implements RAG for more accurate and contextual responses | ||
- Uses Chroma as a vector store for efficient similarity search | ||
- Provides a user-friendly chat interface with Gradio | ||
|
||
## Setup | ||
|
||
1. Clone this repository: | ||
``` | ||
git clone https://github.com/yourusername/rag-chatbot.git | ||
cd rag-chatbot | ||
``` | ||
|
||
2. Run the setup script: | ||
``` | ||
python setup.py | ||
``` | ||
This will download necessary files, install dependencies, and set up Ollama. | ||
|
||
3. Activate the virtual environment: | ||
- On Windows: `.\venv\Scripts\activate` | ||
- On macOS/Linux: `source venv/bin/activate` | ||
|
||
4. Run the application: | ||
``` | ||
python app.py | ||
``` | ||
|
||
5. Open your web browser and go to `http://localhost:7860` to interact with the chatbot. | ||
|
||
## Configuration | ||
|
||
You can configure the application by editing the `.env` file. Available options include: | ||
|
||
- `OLLAMA_MODEL`: The Ollama model to use (default: "llama2:chat") | ||
- `OLLAMA_URL`: The URL of the Ollama server (default: "http://localhost:11434") | ||
- `INITIAL_DB`: Whether to initialize a new vector database (default: False) | ||
- `DEBUG`: Enable debug logging (default: False) | ||
|
||
## Adding New Documents | ||
|
||
To add a new document to the existing knowledge base: | ||
|
||
1. Place the new document in the `data/` directory. | ||
2. Run the following Python code: | ||
|
||
```python | ||
from data_loader import add_new_document | ||
from vector_store import get_vector_store, add_documents_to_store | ||
|
||
new_chunks = add_new_document('path/to/your/new/document.txt') | ||
vector_store = get_vector_store() | ||
add_documents_to_store(vector_store, new_chunks) | ||
``` | ||
|
||
## Troubleshooting | ||
|
||
If you encounter any issues, please check the application logs or file an issue on the GitHub repository. | ||
|
||
## Contributing | ||
|
||
Contributions are welcome! Please feel free to submit a Pull Request. | ||
|
||
## License | ||
|
||
This project is licensed under the MIT License. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
""" | ||
Main application script for the RAG Chatbot. | ||
""" | ||
|
||
import gradio as gr | ||
import logging | ||
from chatbot import chat_ollama | ||
from data_loader import get_document_chunks | ||
from vector_store import get_vector_store, VectorStoreError | ||
from config import CONFIG | ||
|
||
# Set up logging | ||
logging.basicConfig( | ||
level=logging.DEBUG if CONFIG['DEBUG'] else logging.INFO, | ||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' | ||
) | ||
logger = logging.getLogger(__name__) | ||
|
||
def main(): | ||
"""Initialize and run the RAG Chatbot application.""" | ||
try: | ||
if CONFIG['INITIAL_DB']: | ||
logger.info("Initializing vector database...") | ||
document_chunks = get_document_chunks() | ||
get_vector_store(document_chunks) | ||
else: | ||
logger.info("Loading existing vector database...") | ||
get_vector_store() | ||
|
||
gradio_interface = gr.ChatInterface( | ||
chat_ollama, | ||
chatbot=gr.Chatbot(), | ||
textbox=gr.Textbox(placeholder="Example: Who is Alice?", container=False, scale=7), | ||
title="The Ollama RAG Chatbot", | ||
description=f"Ask the {CONFIG['OLLAMA_MODEL']} chatbot a question!", | ||
theme='gradio/base', | ||
retry_btn=None, | ||
undo_btn="Delete Previous", | ||
clear_btn="Clear", | ||
) | ||
|
||
logger.info("Starting Gradio interface...") | ||
gradio_interface.launch() | ||
except VectorStoreError as e: | ||
logger.error(f"Vector store error: {str(e)}") | ||
print(f"An error occurred with the vector store: {str(e)}") | ||
except Exception as e: | ||
logger.error(f"An unexpected error occurred: {str(e)}") | ||
print(f"An unexpected error occurred: {str(e)}") | ||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
""" | ||
Core chatbot functionality for the RAG Chatbot application. | ||
""" | ||
|
||
import logging | ||
from llm import get_ollama, OllamaError | ||
from vector_store import get_vector_store, VectorStoreError | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
MAX_HISTORY_LENGTH = 5 # Adjust as needed | ||
|
||
def validate_input(message): | ||
""" | ||
Validate the user input. | ||
Args: | ||
message (str): The user's input message. | ||
Returns: | ||
bool: True if input is valid, False otherwise. | ||
""" | ||
if not message or not isinstance(message, str): | ||
return False | ||
if len(message.strip()) == 0: | ||
return False | ||
# Add more validation rules as needed | ||
return True | ||
|
||
def chat_ollama(message, history): | ||
""" | ||
Generate a response using the Ollama model and RAG. | ||
Args: | ||
message (str): The user's input message. | ||
history (list): The chat history. | ||
Returns: | ||
str: The generated response. | ||
""" | ||
if not validate_input(message): | ||
return "I'm sorry, but I couldn't understand your input. Could you please rephrase your question?" | ||
|
||
try: | ||
ollama = get_ollama() | ||
chroma_db = get_vector_store() | ||
|
||
result_chunks = chroma_db.similarity_search(message) | ||
|
||
chroma_knowledge = "" | ||
sources = "" | ||
for idx, chunk in enumerate(result_chunks, 1): | ||
chroma_knowledge += f"[{idx}]\n{chunk.page_content}\n" | ||
sources += f"[{idx}]\n{chunk.metadata['source']}\n" | ||
|
||
# Limit the history to the last MAX_HISTORY_LENGTH exchanges | ||
limited_history = history[-MAX_HISTORY_LENGTH:] | ||
history_str = "\n".join([f"Human: {h[0]}\nAI: {h[1]}" for h in limited_history]) | ||
|
||
prompt = f"""Answer the following question using the provided knowledge and the chat history: | ||
###KNOWLEDGE: {chroma_knowledge} | ||
###CHAT-HISTORY: {history_str} | ||
###QUESTION: {message}""" | ||
|
||
result = ollama(prompt) | ||
return f"{result}\n\n\nReferences:\n{sources}" | ||
except VectorStoreError as e: | ||
logger.error(f"Vector store error in chat_ollama: {str(e)}") | ||
return "I'm sorry, but I'm having trouble accessing my knowledge base right now." | ||
except OllamaError as e: | ||
logger.error(f"Ollama error in chat_ollama: {str(e)}") | ||
return "I'm sorry, but I'm having trouble generating a response right now." | ||
except Exception as e: | ||
logger.error(f"Unexpected error in chat_ollama: {str(e)}") | ||
return "I apologize, but I'm experiencing an unexpected issue. Please try again later." |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
""" | ||
Configuration settings for the RAG Chatbot application. | ||
""" | ||
|
||
import os | ||
from dotenv import load_dotenv | ||
|
||
# Load environment variables from .env file | ||
load_dotenv() | ||
|
||
CONFIG = { | ||
# Paths | ||
'BASE_DIR': os.path.dirname(os.path.abspath(__file__)), | ||
'DATA_PATH': os.getenv('DATA_PATH', os.path.join(os.path.dirname(os.path.abspath(__file__)), "data")), | ||
'CHROMA_PATH': os.getenv('CHROMA_PATH', os.path.join(os.path.dirname(os.path.abspath(__file__)), "chroma")), | ||
|
||
# Ollama settings | ||
'OLLAMA_MODEL': os.getenv('OLLAMA_MODEL', "llama2:chat"), | ||
'OLLAMA_URL': os.getenv('OLLAMA_URL', "http://localhost:11434"), | ||
|
||
# Text splitting settings | ||
'HEADERS_TO_SPLIT_ON': [ | ||
("#", "Header 1"), | ||
("##", "Header 2"), | ||
("###", "Header 3"), | ||
("####", "Header 4"), | ||
], | ||
'CHUNK_SIZE': int(os.getenv('CHUNK_SIZE', 500)), | ||
'CHUNK_OVERLAP': int(os.getenv('CHUNK_OVERLAP', 100)), | ||
|
||
# Vector DB settings | ||
'INITIAL_DB': os.getenv('INITIAL_DB', 'False').lower() == 'true', | ||
|
||
# Application settings | ||
'DEBUG': os.getenv('DEBUG', 'False').lower() == 'true', | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
""" | ||
Module for loading and preprocessing documents for the RAG Chatbot. | ||
""" | ||
|
||
import os | ||
import logging | ||
from langchain.document_loaders import TextLoader | ||
from langchain.text_splitter import MarkdownHeaderTextSplitter, RecursiveCharacterTextSplitter | ||
from config import CONFIG | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
def load_documents(): | ||
"""Load documents from the data directory.""" | ||
documents = [] | ||
for file in os.listdir(CONFIG['DATA_PATH']): | ||
try: | ||
loader = TextLoader(os.path.join(CONFIG['DATA_PATH'], file)) | ||
documents.append(loader.load()[0]) | ||
logger.info(f"Loaded document: {file}") | ||
except Exception as e: | ||
logger.error(f"Error loading document {file}: {str(e)}") | ||
return documents | ||
|
||
def split_documents(documents): | ||
"""Split documents into chunks.""" | ||
try: | ||
md_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=CONFIG['HEADERS_TO_SPLIT_ON'], strip_headers=False) | ||
chunks_array = [] | ||
|
||
for doc in documents: | ||
md_chunks = md_splitter.split_text(doc.page_content) | ||
for chunk in md_chunks: | ||
chunk.metadata.update(doc.metadata) | ||
|
||
txt_splitter = RecursiveCharacterTextSplitter( | ||
chunk_size=CONFIG['CHUNK_SIZE'], | ||
chunk_overlap=CONFIG['CHUNK_OVERLAP'], | ||
length_function=len, | ||
add_start_index=True | ||
) | ||
txt_chunks = txt_splitter.split_documents(md_chunks) | ||
chunks_array.extend(txt_chunks) | ||
|
||
logger.info(f"Split documents into {len(chunks_array)} chunks") | ||
return chunks_array | ||
except Exception as e: | ||
logger.error(f"Error splitting documents: {str(e)}") | ||
raise | ||
|
||
def get_document_chunks(): | ||
"""Load and split documents into chunks.""" | ||
documents = load_documents() | ||
return split_documents(documents) | ||
|
||
def add_new_document(file_path): | ||
"""Add a new document to the existing database.""" | ||
try: | ||
loader = TextLoader(file_path) | ||
new_doc = loader.load()[0] | ||
chunks = split_documents([new_doc]) | ||
logger.info(f"Added new document: {file_path}") | ||
return chunks | ||
except Exception as e: | ||
logger.error(f"Error adding new document {file_path}: {str(e)}") | ||
raise |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
""" | ||
Module for interacting with the Ollama language model. | ||
""" | ||
|
||
import logging | ||
from langchain_community.llms import Ollama | ||
from config import CONFIG | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
class OllamaError(Exception): | ||
"""Custom exception for Ollama-related errors.""" | ||
pass | ||
|
||
def get_ollama(): | ||
"""Initialize and return an Ollama instance.""" | ||
try: | ||
ollama = Ollama(base_url=CONFIG['OLLAMA_URL'], model=CONFIG['OLLAMA_MODEL']) | ||
# Test the connection | ||
ollama("Test") | ||
return ollama | ||
except Exception as e: | ||
logger.error(f"Error initializing Ollama: {str(e)}") | ||
raise OllamaError(f"Failed to initialize Ollama: {str(e)}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
langchain==0.0.351 | ||
langchain-community==0.0.13 | ||
chromadb==0.4.22 | ||
pdfplumber==0.10.3 | ||
gradio==4.14.0 | ||
ollama==0.1.6 | ||
python-dotenv==1.0.0 | ||
requests==2.31.0 |
Oops, something went wrong.