Spaces:
Sleeping
Sleeping
File size: 6,652 Bytes
6a902ca | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | 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() |