Spaces:
Sleeping
Sleeping
| 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() |