Spaces:
Sleeping
Sleeping
| import os | |
| import tempfile | |
| import gradio as gr | |
| import PyPDF2 | |
| import faiss | |
| import numpy as np | |
| from gtts import gTTS | |
| from sentence_transformers import SentenceTransformer | |
| import requests | |
| import json | |
| from typing import List, Optional | |
| import io | |
| # Configuration | |
| GROQ_API_KEY = "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" | |
| GROQ_URL = "https://api.groq.com/openai/v1/chat/completions" | |
| GROQ_MODEL = "llama3-8b-8192" | |
| # Global variables to store the current session | |
| current_index = None | |
| current_texts = None | |
| current_embeddings = None | |
| embed_model = None | |
| def initialize_model(): | |
| """Initialize the embedding model""" | |
| global embed_model | |
| if embed_model is None: | |
| embed_model = SentenceTransformer('all-MiniLM-L6-v2') | |
| return embed_model | |
| def load_pdf_text(file_path): | |
| """Extract text from PDF file""" | |
| try: | |
| with open(file_path, 'rb') as file: | |
| pdf_reader = PyPDF2.PdfReader(file) | |
| text = "" | |
| for page in pdf_reader.pages: | |
| text += page.extract_text() + "\n" | |
| return text | |
| except Exception as e: | |
| return f"Error reading PDF: {str(e)}" | |
| def split_text(text, chunk_size=500, overlap=50): | |
| """Split text into chunks with overlap""" | |
| chunks = [] | |
| start = 0 | |
| while start < len(text): | |
| end = min(start + chunk_size, len(text)) | |
| chunks.append(text[start:end]) | |
| start += chunk_size - overlap | |
| return chunks | |
| def build_faiss_index(texts): | |
| """Build FAISS index from text chunks""" | |
| global embed_model, current_index, current_texts, current_embeddings | |
| embed_model = initialize_model() | |
| embeddings = embed_model.encode(texts, convert_to_numpy=True) | |
| dim = embeddings.shape[1] | |
| index = faiss.IndexFlatL2(dim) | |
| index.add(embeddings.astype('float32')) | |
| current_index = index | |
| current_texts = texts | |
| current_embeddings = embeddings | |
| return index | |
| def search_relevant_chunks(question, top_k=3): | |
| """Search for relevant text chunks""" | |
| global current_index, current_texts, embed_model | |
| if current_index is None or current_texts is None: | |
| return [] | |
| question_embedding = embed_model.encode([question], convert_to_numpy=True) | |
| D, I = current_index.search(question_embedding.astype('float32'), top_k) | |
| relevant_chunks = [current_texts[i] for i in I[0] if i < len(current_texts)] | |
| return relevant_chunks | |
| def generate_tutor_response(context, question): | |
| """Generate response using Groq API""" | |
| prompt = ( | |
| f"You are a friendly and knowledgeable AI tutor. Based on the provided context from the student's notes, " | |
| f"answer their question in a clear, educational manner. If the context doesn't contain enough information, " | |
| f"say so and provide general guidance.\n\n" | |
| f"Context from notes:\n{context}\n\n" | |
| f"Student Question: {question}\n\n" | |
| f"Please provide a helpful, tutor-like response:" | |
| ) | |
| headers = { | |
| "Authorization": f"Bearer {GROQ_API_KEY}", | |
| "Content-Type": "application/json" | |
| } | |
| data = { | |
| "model": GROQ_MODEL, | |
| "messages": [ | |
| {"role": "system", "content": "You are a helpful and friendly AI tutor who explains concepts clearly and encourages learning."}, | |
| {"role": "user", "content": prompt} | |
| ], | |
| "temperature": 0.7, | |
| "max_tokens": 1000 | |
| } | |
| try: | |
| response = requests.post(GROQ_URL, headers=headers, json=data, timeout=30) | |
| result = response.json() | |
| if "error" in result: | |
| return f"I'm sorry, I encountered an error: {result['error']['message']}" | |
| return result["choices"][0]["message"]["content"] | |
| except Exception as e: | |
| return f"I'm sorry, I couldn't process your question right now. Error: {str(e)}" | |
| def create_audio_response(text): | |
| """Create audio file from text using gTTS""" | |
| try: | |
| tts = gTTS(text=text, lang='en', slow=False) | |
| # Create temporary file | |
| temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.mp3') | |
| tts.save(temp_file.name) | |
| return temp_file.name | |
| except Exception as e: | |
| print(f"Error creating audio: {e}") | |
| return None | |
| def upload_pdf(file): | |
| """Handle PDF upload and processing""" | |
| if file is None: | |
| return "Please upload a PDF file.", None | |
| try: | |
| # Extract text from PDF | |
| text = load_pdf_text(file.name) | |
| if text.startswith("Error"): | |
| return text, None | |
| # Split into chunks | |
| chunks = split_text(text, chunk_size=500, overlap=50) | |
| if len(chunks) == 0: | |
| return "No text found in the PDF. Please upload a different file.", None | |
| # Build FAISS index | |
| build_faiss_index(chunks) | |
| return f"β PDF processed successfully! Found {len(chunks)} text chunks. You can now ask questions about your document.", None | |
| except Exception as e: | |
| return f"Error processing PDF: {str(e)}", None | |
| def chat_with_pdf(message, history): | |
| """Handle chat interaction""" | |
| global current_index, current_texts | |
| if current_index is None or current_texts is None: | |
| return "Please upload a PDF file first before asking questions." | |
| if not message.strip(): | |
| return "Please ask a question about your uploaded document." | |
| try: | |
| # Find relevant chunks | |
| relevant_chunks = search_relevant_chunks(message, top_k=3) | |
| if not relevant_chunks: | |
| context = "No relevant information found in the uploaded document." | |
| else: | |
| context = " ".join(relevant_chunks) | |
| # Generate response | |
| response = generate_tutor_response(context, message) | |
| return response | |
| except Exception as e: | |
| return f"I'm sorry, I encountered an error while processing your question: {str(e)}" | |
| def get_audio_response(message, history): | |
| """Get audio version of the latest response""" | |
| if not history: | |
| return None | |
| latest_response = history[-1][1] # Get the latest bot response | |
| if latest_response: | |
| audio_file = create_audio_response(latest_response) | |
| return audio_file | |
| return None | |
| # Create Gradio interface | |
| def create_interface(): | |
| with gr.Blocks(title="AI PDF Tutor", theme=gr.themes.Soft()) as iface: | |
| gr.Markdown( | |
| """ | |
| # π AI PDF Tutor with Voice | |
| Upload your PDF study materials and chat with an AI tutor that can answer questions about your documents. | |
| The tutor can also read responses aloud! | |
| """ | |
| ) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("### π Upload Your PDF") | |
| file_upload = gr.File( | |
| label="Upload PDF Document", | |
| file_types=[".pdf"], | |
| type="filepath" | |
| ) | |
| upload_status = gr.Textbox( | |
| label="Upload Status", | |
| value="No file uploaded yet.", | |
| interactive=False | |
| ) | |
| with gr.Column(scale=2): | |
| gr.Markdown("### π¬ Chat with Your Document") | |
| chatbot = gr.Chatbot( | |
| label="AI Tutor Chat", | |
| height=400 | |
| ) | |
| with gr.Row(): | |
| msg_input = gr.Textbox( | |
| label="Ask a question about your document", | |
| placeholder="e.g., What are the key concepts in chapter 1?", | |
| lines=2, | |
| scale=4 | |
| ) | |
| audio_btn = gr.Button("π Listen", scale=1) | |
| audio_output = gr.Audio(label="Tutor Response (Audio)", visible=True) | |
| # Event handlers | |
| file_upload.change( | |
| fn=upload_pdf, | |
| inputs=[file_upload], | |
| outputs=[upload_status, audio_output] | |
| ) | |
| msg_input.submit( | |
| fn=chat_with_pdf, | |
| inputs=[msg_input, chatbot], | |
| outputs=[chatbot] | |
| ).then( | |
| lambda: "", | |
| outputs=[msg_input] | |
| ) | |
| audio_btn.click( | |
| fn=get_audio_response, | |
| inputs=[msg_input, chatbot], | |
| outputs=[audio_output] | |
| ) | |
| # Examples | |
| gr.Examples( | |
| examples=[ | |
| ["What are the main topics covered in this document?"], | |
| ["Can you summarize the key points?"], | |
| ["Explain the methodology used in this research."], | |
| ["What are the conclusions of this study?"] | |
| ], | |
| inputs=[msg_input] | |
| ) | |
| return iface | |
| # Launch the app | |
| if __name__ == "__main__": | |
| interface = create_interface() | |
| interface.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| share=True | |
| ) |