multi-agent-chatbot / data_loader.py
yujierachel's picture
Upload 4 files
6a902ca verified
Raw
History Blame Contribute Delete
6.65 kB
import os
import logging
import pandas as pd
import openai
from pinecone import Pinecone, ServerlessSpec
from langchain.document_loaders import PyMuPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# Configuration variables - update these as needed
PDF_PATH = "machine_learning.pdf" # Path to your PDF file
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
PINECONE_API_KEY = os.getenv("PINECONE_API_KEY")
PINECONE_INDEX_NAME = "miniproject2-multi-agent-chatbot"
CHUNK_SIZE = 2500
CHUNK_OVERLAP = 50
def load_pdf():
"""Task 1: Load PDF file and extract text."""
logger.info(f"Loading PDF file: {PDF_PATH}")
try:
# Load the PDF document
loader = PyMuPDFLoader(PDF_PATH)
documents = loader.load()
# Extract text and page numbers
page_texts = [doc.page_content for doc in documents]
page_numbers = [doc.metadata["page"] + 1 for doc in documents] # Pages are 0-indexed in PyMuPDF
logger.info(f"Successfully loaded {len(page_texts)} pages from PDF")
return page_texts, page_numbers
except Exception as e:
logger.error(f"Error loading PDF: {e}")
raise
def chunk_text(page_texts, page_numbers):
"""Task 2: Break down the extracted text into smaller chunks."""
logger.info("Breaking text into chunks")
# Initialize the text splitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=CHUNK_SIZE,
chunk_overlap=CHUNK_OVERLAP
)
# Storage for chunks and their page numbers
chunks = []
chunk_page_numbers = []
previous_page_tail = ""
# Process each page
for i, (text, page_num) in enumerate(zip(page_texts, page_numbers)):
# Append previous page's tail to current page
if previous_page_tail:
text = previous_page_tail + " " + text
previous_page_tail = ""
# Split text into chunks
page_chunks = text_splitter.split_text(text)
# Store chunks with page numbers
chunks.extend(page_chunks)
chunk_page_numbers.extend([page_num] * len(page_chunks))
# Save the tail of the current page
if len(page_chunks) > 0:
previous_page_tail = page_chunks[-1][-CHUNK_OVERLAP:]
logger.info(f"Created {len(chunks)} chunks from {len(page_texts)} pages")
return chunks, chunk_page_numbers
def prepare_data(chunks, chunk_page_numbers):
"""Task 2: Prepare the data and generate embeddings."""
logger.info("Preparing data and generating embeddings")
# Check if OpenAI API key is set
if not OPENAI_API_KEY:
raise ValueError("OpenAI API key is not set")
# Create DataFrame
df = pd.DataFrame({
'text': chunks,
'page_number': chunk_page_numbers
})
# Preprocess text
df['processed_text'] = df['text'].apply(lambda x: x.replace('\n', ' ').replace('\r', ' '))
# Initialize OpenAI client
client = openai.OpenAI(api_key=OPENAI_API_KEY)
# Function to generate embeddings
def get_embedding(text):
try:
response = client.embeddings.create(
model="text-embedding-ada-002",
input=text
)
return response.data[0].embedding
except Exception as e:
logger.error(f"Error generating embedding: {e}")
raise
# Generate embeddings for each chunk
logger.info("Generating embeddings. This may take some time...")
df['embedding'] = df['processed_text'].apply(get_embedding)
logger.info(f"Generated embeddings for {len(df)} chunks")
return df
def create_pinecone_index(df):
"""Task 3: Create Pinecone index and insert data."""
logger.info("Creating Pinecone index and inserting data")
# Check if Pinecone API key is set
if not PINECONE_API_KEY:
raise ValueError("Pinecone API key is not set")
# Initialize Pinecone
pc = Pinecone(api_key=PINECONE_API_KEY)
# Check if index exists
index_exists = PINECONE_INDEX_NAME in pc.list_indexes().names()
# Create index if it doesn't exist
if not index_exists:
logger.info(f"Creating new Pinecone index: {PINECONE_INDEX_NAME}")
pc.create_index(
name=PINECONE_INDEX_NAME,
dimension=1536, # OpenAI embedding dimension
metric="cosine",
spec=ServerlessSpec(cloud="aws", region="us-east-1")
)
# Connect to index
index = pc.Index(PINECONE_INDEX_NAME)
# Insert data in batches
batch_size = 100
total_rows = len(df)
for i in range(0, total_rows, batch_size):
end_idx = min(i + batch_size, total_rows)
batch_df = df.iloc[i:end_idx]
vectors = []
for j, row in batch_df.iterrows():
# Create metadata dictionary
metadata = {
"text": row['text'],
"page_number": int(row['page_number'])
}
# Create vector
vector = {
"id": f"chunk_{j}",
"values": row['embedding'],
"metadata": metadata
}
vectors.append(vector)
# Upsert batch
index.upsert(vectors=vectors)
logger.info(f"Inserted batch {i // batch_size + 1}/{(total_rows - 1) // batch_size + 1} into Pinecone")
# Get index statistics
stats = index.describe_index_stats()
logger.info(f"Pinecone index stats: {stats}")
return stats
def main():
"""Main function to execute all tasks."""
try:
# Task 1: Load PDF and extract text
page_texts, page_numbers = load_pdf()
# Task 2: Break text into chunks
chunks, chunk_page_numbers = chunk_text(page_texts, page_numbers)
# Task 2: Prepare data and generate embeddings
df = prepare_data(chunks, chunk_page_numbers)
# Task 3: Create Pinecone index and insert data
stats = create_pinecone_index(df)
logger.info("Data processing complete! Your chatbot is ready to use.")
logger.info(f"Total vectors in Pinecone: {stats.get('total_vector_count', 0)}")
return True
except Exception as e:
logger.error(f"Error in data processing pipeline: {e}")
return False
if __name__ == "__main__":
main()