Spaces:
Running
on
Zero
Running
on
Zero
import gradio as gr | |
import PyPDF2 | |
import docx | |
from openai import OpenAI | |
import io | |
import json | |
import time | |
from typing import List, Dict, Any, Optional | |
import spaces | |
import os | |
# Global variables to store API key and document text | |
API_KEY = "" | |
DOCUMENT_TEXT = "" | |
MODEL = "google/gemma-3-27b-it:free" | |
def setup_client(api_key: str): | |
"""Initialize and test API key""" | |
global API_KEY | |
try: | |
if not api_key or api_key.strip() == "": | |
return "β Please enter a valid API key" | |
# Test the API key by creating a client | |
client = OpenAI( | |
base_url="https://openrouter.ai/api/v1", | |
api_key=api_key.strip(), | |
) | |
# Store the API key globally | |
API_KEY = api_key.strip() | |
return "β API Key configured successfully!" | |
except Exception as e: | |
return f"β Error configuring API: {str(e)}" | |
def create_client() -> Optional[OpenAI]: | |
"""Create OpenAI client with stored API key""" | |
if not API_KEY: | |
return None | |
return OpenAI( | |
base_url="https://openrouter.ai/api/v1", | |
api_key=API_KEY, | |
) | |
def extract_text_from_pdf(file_path: str) -> str: | |
"""Extract text from PDF file""" | |
try: | |
with open(file_path, 'rb') as file: | |
pdf_reader = PyPDF2.PdfReader(file) | |
text = "" | |
for page_num, page in enumerate(pdf_reader.pages): | |
try: | |
page_text = page.extract_text() | |
if page_text: | |
text += page_text + "\n" | |
except Exception as e: | |
print(f"Error extracting text from page {page_num}: {e}") | |
continue | |
return text.strip() | |
except Exception as e: | |
return f"Error reading PDF: {str(e)}" | |
def extract_text_from_docx(file_path: str) -> str: | |
"""Extract text from DOCX file""" | |
try: | |
doc = docx.Document(file_path) | |
text = "" | |
for paragraph in doc.paragraphs: | |
if paragraph.text.strip(): | |
text += paragraph.text + "\n" | |
# Also extract text from tables | |
for table in doc.tables: | |
for row in table.rows: | |
for cell in row.cells: | |
if cell.text.strip(): | |
text += cell.text + "\n" | |
return text.strip() | |
except Exception as e: | |
return f"Error reading DOCX: {str(e)}" | |
def process_document(file): | |
"""Process uploaded document and extract text""" | |
global DOCUMENT_TEXT | |
print(f"Processing file: {file}") # Debug print | |
if file is None: | |
DOCUMENT_TEXT = "" | |
return "β No file uploaded", "β No document loaded" | |
try: | |
file_path = file.name if hasattr(file, 'name') else str(file) | |
print(f"File path: {file_path}") # Debug print | |
# Check if file exists | |
if not os.path.exists(file_path): | |
DOCUMENT_TEXT = "" | |
return "β File not found", "β No document loaded" | |
# Get file extension | |
file_extension = file_path.lower().split('.')[-1] | |
print(f"File extension: {file_extension}") # Debug print | |
# Extract text based on file type | |
if file_extension == 'pdf': | |
extracted_text = extract_text_from_pdf(file_path) | |
elif file_extension in ['docx', 'doc']: | |
extracted_text = extract_text_from_docx(file_path) | |
else: | |
DOCUMENT_TEXT = "" | |
return "β Unsupported file format. Please upload PDF or DOCX files.", "β No document loaded" | |
print(f"Extracted text length: {len(extracted_text) if extracted_text else 0}") # Debug print | |
# Check if extraction was successful | |
if extracted_text.startswith("Error"): | |
DOCUMENT_TEXT = "" | |
return extracted_text, "β No document loaded" | |
# Clean and set the global variable | |
DOCUMENT_TEXT = extracted_text.strip() | |
if DOCUMENT_TEXT and len(DOCUMENT_TEXT) > 10: # Minimum length check | |
word_count = len(DOCUMENT_TEXT.split()) | |
char_count = len(DOCUMENT_TEXT) | |
preview = DOCUMENT_TEXT[:300] + "..." if len(DOCUMENT_TEXT) > 300 else DOCUMENT_TEXT | |
status_msg = f"β Document loaded ({word_count} words, {char_count} characters)" | |
process_msg = f"β Document processed successfully!\nπ Word count: {word_count}\nπ Character count: {char_count}\n\nπ Preview:\n{preview}" | |
print(f"Document processed successfully. Word count: {word_count}") # Debug print | |
return process_msg, status_msg | |
else: | |
DOCUMENT_TEXT = "" | |
return "β Could not extract meaningful text from the document. The document might be empty, contain only images, or be corrupted.", "β No document loaded" | |
except Exception as e: | |
DOCUMENT_TEXT = "" | |
error_msg = f"β Error processing document: {str(e)}" | |
print(f"Error: {error_msg}") # Debug print | |
return error_msg, "β No document loaded" | |
def generate_content(prompt: str, max_tokens: int = 2000) -> str: | |
"""Generate content using the AI model""" | |
global DOCUMENT_TEXT, API_KEY | |
print(f"Generate content called. API_KEY exists: {bool(API_KEY)}, DOCUMENT_TEXT length: {len(DOCUMENT_TEXT) if DOCUMENT_TEXT else 0}") # Debug print | |
if not API_KEY or API_KEY.strip() == "": | |
return "β Please configure your API key first" | |
if not DOCUMENT_TEXT or len(DOCUMENT_TEXT.strip()) < 10: | |
return "β Please upload and process a document first. Make sure the document contains readable text." | |
try: | |
client = create_client() | |
if not client: | |
return "β Failed to create API client" | |
print("Sending request to API...") # Debug print | |
completion = client.chat.completions.create( | |
extra_headers={ | |
"HTTP-Referer": "https://educational-assistant.app", | |
"X-Title": "Educational Content Creator", | |
}, | |
model=MODEL, | |
messages=[ | |
{ | |
"role": "system", | |
"content": "You are an expert educational content creator. Create comprehensive, engaging, and pedagogically sound educational materials based on the provided document content." | |
}, | |
{ | |
"role": "user", | |
"content": f"Document Content:\n{DOCUMENT_TEXT[:4000]}\n\n{prompt}" # Limit document content to avoid token limits | |
} | |
], | |
max_tokens=max_tokens, | |
temperature=0.7 | |
) | |
result = completion.choices[0].message.content | |
print(f"API response received. Length: {len(result) if result else 0}") # Debug print | |
return result | |
except Exception as e: | |
error_msg = f"β Error generating content: {str(e)}" | |
print(f"API Error: {error_msg}") # Debug print | |
return error_msg | |
# Content generation functions with @spaces.GPU decorator | |
def generate_summary(): | |
"""Generate comprehensive summary""" | |
prompt = """Create a comprehensive summary of this document with the following structure: | |
## π Executive Summary | |
Provide a brief overview in 2-3 sentences. | |
## π― Key Points | |
List the main concepts, ideas, or arguments presented. | |
## π Detailed Summary | |
Provide a thorough summary organized by topics or sections. | |
## π‘ Important Takeaways | |
Highlight the most crucial information students should remember. | |
""" | |
return generate_content(prompt) | |
def generate_study_notes(): | |
"""Generate structured study notes""" | |
prompt = """Create comprehensive study notes from this document with: | |
## π Study Notes | |
### π Key Concepts | |
- Define important terms and concepts | |
- Explain their significance | |
### π Main Topics | |
Organize content into clear sections with: | |
- Topic headings | |
- Key points under each topic | |
- Supporting details and examples | |
### π§ Memory Aids | |
- Create mnemonics for complex information | |
- Suggest visualization techniques | |
- Provide connection points between concepts | |
### β‘ Quick Review Points | |
- Bullet points for rapid review | |
- Essential facts and figures | |
""" | |
return generate_content(prompt) | |
def generate_quiz(): | |
"""Generate quiz questions""" | |
prompt = """Create a comprehensive quiz based on this document: | |
## π Quiz Questions | |
### Multiple Choice Questions (5 questions) | |
For each question, provide: | |
- Clear question | |
- 4 options (A, B, C, D) | |
- Correct answer | |
- Brief explanation | |
### Short Answer Questions (5 questions) | |
- Questions requiring 2-3 sentence answers | |
- Cover key concepts and applications | |
### Essay Questions (2 questions) | |
- Thought-provoking questions requiring detailed responses | |
- Focus on analysis, synthesis, or evaluation | |
### Answer Key | |
Provide all correct answers with explanations. | |
""" | |
return generate_content(prompt, max_tokens=3000) | |
def generate_flashcards(): | |
"""Generate flashcards""" | |
prompt = """Create 15-20 flashcards based on this document: | |
## π΄ Flashcards | |
Format each flashcard as: | |
**Card X:** | |
**Front:** [Question/Term] | |
**Back:** [Answer/Definition/Explanation] | |
Include flashcards for: | |
- Key terms and definitions | |
- Important concepts | |
- Facts and figures | |
- Cause and effect relationships | |
- Applications and examples | |
Make questions clear and answers comprehensive but concise. | |
""" | |
return generate_content(prompt, max_tokens=2500) | |
def generate_mind_map(): | |
"""Generate mind map structure""" | |
prompt = """Create a detailed mind map structure for this document: | |
## π§ Mind Map Structure | |
**Central Topic:** [Main subject of the document] | |
### Primary Branches: | |
For each main topic, create branches with: | |
- **Branch 1:** [Topic Name] | |
- Sub-branch 1.1: [Subtopic] | |
- Detail 1.1.1 | |
- Detail 1.1.2 | |
- Sub-branch 1.2: [Subtopic] | |
- Detail 1.2.1 | |
- Detail 1.2.2 | |
### Connections: | |
- Identify relationships between different branches | |
- Note cross-references and dependencies | |
- Highlight cause-effect relationships | |
### Visual Elements Suggestions: | |
- Color coding recommendations | |
- Symbol suggestions for different types of information | |
- Emphasis techniques for key concepts | |
""" | |
return generate_content(prompt) | |
def generate_lesson_plan(): | |
"""Generate lesson plan""" | |
prompt = """Create a detailed lesson plan based on this document: | |
## π Lesson Plan | |
### Learning Objectives | |
By the end of this lesson, students will be able to: | |
- [Specific, measurable objectives] | |
### Prerequisites | |
- Required background knowledge | |
- Recommended prior reading | |
### Lesson Structure (60 minutes) | |
**Introduction (10 minutes)** | |
- Hook/attention grabber | |
- Learning objectives overview | |
**Main Content (35 minutes)** | |
- Key concepts presentation | |
- Activities and examples | |
- Discussion points | |
**Practice & Application (10 minutes)** | |
- Practice exercises | |
- Real-world applications | |
**Wrap-up & Assessment (5 minutes)** | |
- Summary of key points | |
- Quick assessment questions | |
### Materials Needed | |
- List of required resources | |
### Assessment Methods | |
- How to evaluate student understanding | |
### Homework/Extension Activities | |
- Additional practice opportunities | |
""" | |
return generate_content(prompt, max_tokens=2500) | |
def generate_concept_explanations(): | |
"""Generate detailed concept explanations""" | |
prompt = """Provide detailed explanations of key concepts from this document: | |
## π Concept Deep Dive | |
For each major concept, provide: | |
### Concept Name | |
**Definition:** Clear, precise definition | |
**Explanation:** Detailed explanation in simple terms | |
**Examples:** Real-world examples and applications | |
**Analogies:** Helpful comparisons to familiar concepts | |
**Common Misconceptions:** What students often get wrong | |
**Connection to Other Concepts:** How it relates to other topics | |
**Practice Application:** Simple exercise or question | |
--- | |
Repeat this structure for all major concepts in the document. | |
""" | |
return generate_content(prompt, max_tokens=3000) | |
def generate_practice_problems(): | |
"""Generate practice problems""" | |
prompt = """Create practice problems based on this document: | |
## πͺ Practice Problems | |
### Beginner Level (5 problems) | |
- Basic application of concepts | |
- Direct recall and simple calculations | |
- Step-by-step solutions provided | |
### Intermediate Level (5 problems) | |
- Multi-step problems | |
- Requires understanding of relationships | |
- Guided solutions with explanations | |
### Advanced Level (3 problems) | |
- Complex scenarios | |
- Requires analysis and synthesis | |
- Detailed solution strategies | |
### Challenge Problems (2 problems) | |
- Extension beyond document content | |
- Creative application | |
- Multiple solution approaches | |
**For each problem, include:** | |
- Clear problem statement | |
- Required formulas/concepts | |
- Step-by-step solution | |
- Common mistakes to avoid | |
""" | |
return generate_content(prompt, max_tokens=3500) | |
def get_document_status(): | |
"""Get current document status""" | |
global DOCUMENT_TEXT | |
if DOCUMENT_TEXT and len(DOCUMENT_TEXT.strip()) > 10: | |
word_count = len(DOCUMENT_TEXT.split()) | |
char_count = len(DOCUMENT_TEXT) | |
return f"β Document loaded ({word_count} words, {char_count} characters)" | |
else: | |
return "β No document loaded" | |
def get_api_status(): | |
"""Get current API status""" | |
global API_KEY | |
if API_KEY and API_KEY.strip(): | |
return "β API Key configured" | |
else: | |
return "β API Key not configured" | |
# Create Gradio interface | |
def create_interface(): | |
with gr.Blocks(title="π Educational Content Creator Assistant", theme=gr.themes.Soft()) as app: | |
gr.Markdown(""" | |
# π Educational Content Creator Assistant | |
Transform your documents into comprehensive educational materials using AI! | |
**Features:** Study Notes β’ Quizzes β’ Flashcards β’ Mind Maps β’ Lesson Plans β’ Practice Problems & More! | |
*Powered by ZeroGPU for enhanced performance* | |
""") | |
with gr.Row(): | |
with gr.Column(scale=1): | |
gr.Markdown("### π Setup") | |
api_key = gr.Textbox( | |
label="OpenRouter API Key", | |
type="password", | |
placeholder="Enter your OpenRouter API key...", | |
info="Get your API key from https://openrouter.ai/" | |
) | |
setup_btn = gr.Button("π§ Configure API", variant="primary") | |
setup_status = gr.Textbox(label="API Status", value=get_api_status(), interactive=False) | |
gr.Markdown("### π Document Upload") | |
file_upload = gr.File( | |
label="Upload Document (PDF or DOCX)", | |
file_types=[".pdf", ".docx", ".doc"], | |
type="filepath" | |
) | |
process_btn = gr.Button("π Process Document", variant="secondary") | |
process_status = gr.Textbox(label="Processing Status", interactive=False, lines=4) | |
# Document status indicator | |
doc_status = gr.Textbox( | |
label="Document Status", | |
value=get_document_status(), | |
interactive=False | |
) | |
with gr.Column(scale=2): | |
gr.Markdown("### π― Generate Educational Content") | |
with gr.Row(): | |
summary_btn = gr.Button("π Generate Summary", variant="primary") | |
notes_btn = gr.Button("π Study Notes", variant="primary") | |
quiz_btn = gr.Button("π Create Quiz", variant="primary") | |
with gr.Row(): | |
flashcards_btn = gr.Button("π΄ Flashcards", variant="secondary") | |
mindmap_btn = gr.Button("π§ Mind Map", variant="secondary") | |
lesson_btn = gr.Button("π Lesson Plan", variant="secondary") | |
with gr.Row(): | |
concepts_btn = gr.Button("π Concept Explanations", variant="secondary") | |
problems_btn = gr.Button("πͺ Practice Problems", variant="secondary") | |
output = gr.Textbox( | |
label="Generated Content", | |
lines=20, | |
max_lines=30, | |
placeholder="Generated educational content will appear here...", | |
show_copy_button=True | |
) | |
gr.Markdown(""" | |
### π How to Use: | |
1. **Get API Key:** Sign up at [OpenRouter](https://openrouter.ai/) and get your free API key | |
2. **Configure:** Enter your API key and click "Configure API" | |
3. **Upload:** Upload a PDF or DOCX document (make sure it contains readable text) | |
4. **Process:** Click "Process Document" to extract text | |
5. **Generate:** Choose any educational content type to generate | |
### π― Content Types: | |
- **Summary:** Comprehensive overview with key points | |
- **Study Notes:** Structured notes with key concepts and memory aids | |
- **Quiz:** Multiple choice, short answer, and essay questions with answers | |
- **Flashcards:** Question-answer pairs for memorization | |
- **Mind Map:** Visual structure of document concepts | |
- **Lesson Plan:** Complete teaching plan with objectives and activities | |
- **Concept Explanations:** Deep dive into key concepts with examples | |
- **Practice Problems:** Graded exercises from beginner to advanced | |
### π‘ Tips: | |
- Make sure your PDF contains selectable text (not just images) | |
- For best results, use documents with clear structure and headings | |
- The app works with academic papers, textbooks, reports, and study materials | |
### β‘ Performance Note: | |
This app uses ZeroGPU for enhanced processing. Functions will automatically utilize GPU resources when needed. | |
""") | |
# Event handlers | |
def setup_api_and_update_status(api_key): | |
result = setup_client(api_key) | |
status = get_api_status() | |
return result, status | |
setup_btn.click( | |
setup_api_and_update_status, | |
inputs=[api_key], | |
outputs=[setup_status, setup_status] | |
) | |
def process_and_update_all_status(file): | |
process_result, doc_status_result = process_document(file) | |
return process_result, doc_status_result | |
process_btn.click( | |
process_and_update_all_status, | |
inputs=[file_upload], | |
outputs=[process_status, doc_status] | |
) | |
# Content generation button handlers | |
summary_btn.click(generate_summary, outputs=[output]) | |
notes_btn.click(generate_study_notes, outputs=[output]) | |
quiz_btn.click(generate_quiz, outputs=[output]) | |
flashcards_btn.click(generate_flashcards, outputs=[output]) | |
mindmap_btn.click(generate_mind_map, outputs=[output]) | |
lesson_btn.click(generate_lesson_plan, outputs=[output]) | |
concepts_btn.click(generate_concept_explanations, outputs=[output]) | |
problems_btn.click(generate_practice_problems, outputs=[output]) | |
# Update status on app load | |
def update_initial_status(): | |
return get_api_status(), get_document_status() | |
app.load( | |
update_initial_status, | |
outputs=[setup_status, doc_status] | |
) | |
return app | |
# Launch the application | |
if __name__ == "__main__": | |
app = create_interface() | |
app.launch( | |
debug=True, | |
share=False | |
) |