import gradio as gr from transformers import pipeline import PyPDF2 import re import os import io import random import time from groq import Groq # ==================== DEBUGGING ==================== print("=" * 50) print("STARTING APPLICATION - ENVIRONMENT CHECK") print("=" * 50) # List ALL environment variables (be careful with this in production!) print("\nAll Environment Variables:") for key in os.environ: if 'KEY' in key or 'TOKEN' in key or 'SECRET' in key: value = os.environ[key] masked = value[:4] + "..." + value[-4:] if len(value) > 8 else "SET" print(f" {key}: {masked}") # ==================== IMPORT HANDLING ==================== try: from google import genai from google.genai import types print("\n✅ Imported google.genai successfully") except ImportError as e: print(f"\n❌ Failed to import google.genai: {e}") genai = None types = None # ==================== CONFIGURATION ==================== # Try multiple ways to get tokens hf_token = os.environ.get("HF_TOKEN") or os.getenv("HF_TOKEN") gemini_key = os.environ.get("GEMINI_API_KEY") or os.getenv("GEMINI_API_KEY") groq_key = os.environ.get("GROQ_API_KEY") or os.getenv("GROQ_API_KEY") print(f"\nToken Check:") print(f" HF_TOKEN: {'SET (' + hf_token[:4] + '...)' if hf_token else 'NOT SET'}") print(f" GEMINI_API_KEY: {'SET (' + gemini_key[:4] + '...)' if gemini_key else 'NOT SET'}") print(f" GROQ_API_KEY: {'SET (' + groq_key[:4] + '...)' if groq_key else 'NOT SET'}") # ==================== GEMINI SETUP ==================== gemini_client = None if gemini_key and genai: try: print(f"\nAttempting Gemini setup...") try: gemini_client = genai.Client(api_key=gemini_key) print(" ✅ Gemini client initialized with new SDK") except Exception as e1: print(f" ⚠️ New SDK failed: {e1}") try: gemini_client = genai.client.Client(api_key=gemini_key) print(" ✅ Gemini client initialized with alternate method") except Exception as e2: print(f" ⚠️ Alternate failed: {e2}") # Fallback to legacy try: import google.generativeai as old_genai old_genai.configure(api_key=gemini_key) gemini_client = old_genai print(" ✅ Using legacy google.generativeai") except Exception as e3: print(f" ❌ Legacy also failed: {e3}") except Exception as e: print(f" ❌ Gemini setup error: {e}") else: if not gemini_key: print("\n⚠️ No GEMINI_API_KEY found") if not genai: print("⚠️ Google GenAI SDK not imported") # ==================== GROQ SETUP ==================== groq_client = None if groq_key: try: print(f"\nAttempting Groq setup...") groq_client = Groq(api_key=groq_key) # Test the client with a simple request test_response = groq_client.chat.completions.create( messages=[{"role": "user", "content": "Hi"}], model="llama-3.3-70b-versatile", max_completion_tokens=5 ) print(" ✅ Groq client initialized and tested successfully") except Exception as e: print(f" ❌ Groq setup error: {e}") groq_client = None else: print("\n⚠️ No GROQ_API_KEY found") print("\n" + "=" * 50) print("SETUP COMPLETE") print("=" * 50) # Lazy load summarizer summarizer = None def load_summarizer(): global summarizer if summarizer is None: try: print("Loading summarizer...") summarizer = pipeline("summarization", model="sshleifer/distilbart-cnn-12-6", device=-1) print("✅ Summarizer ready") except Exception as e: print(f"❌ Summarizer failed: {e}") return summarizer # ==================== STATUS CHECK ==================== def check_status(): statuses = [] # Gemini status if gemini_client and gemini_key: statuses.append("✅ Gemini Ready") elif gemini_key: statuses.append("⚠️ Gemini key present but client failed") else: statuses.append("❌ Gemini: Add GEMINI_API_KEY") # Groq status if groq_client and groq_key: statuses.append("✅ Groq Ready") elif groq_key: statuses.append("⚠️ Groq key present but client failed") else: statuses.append("❌ Groq: Add GROQ_API_KEY") return " | ".join(statuses) # ==================== APP 1: PDF & ESSAY TOOLS ==================== def extract_text_from_pdf(pdf_file): """Extract text from PDF - handle both file path and bytes""" if pdf_file is None: return None, "Please upload a PDF file." try: if isinstance(pdf_file, str): with open(pdf_file, 'rb') as f: pdf_reader = PyPDF2.PdfReader(f) text = "" for page in pdf_reader.pages: page_text = page.extract_text() if page_text: text += page_text + "\n" else: if hasattr(pdf_file, 'read'): pdf_bytes = pdf_file.read() if hasattr(pdf_file, 'seek'): pdf_file.seek(0) else: pdf_bytes = pdf_file if isinstance(pdf_bytes, bytes): pdf_stream = io.BytesIO(pdf_bytes) else: pdf_stream = io.BytesIO(pdf_bytes.encode() if isinstance(pdf_bytes, str) else pdf_bytes) pdf_reader = PyPDF2.PdfReader(pdf_stream) text = "" for page in pdf_reader.pages: page_text = page.extract_text() if page_text: text += page_text + "\n" text = re.sub(r'\s+', ' ', text).strip() if len(text) < 50: return None, "Could not extract text. PDF may be image-based or scanned." return text, None except Exception as e: return None, f"Error reading PDF: {str(e)}" def summarize_with_gemini(text, max_length, min_length): """Try to use Gemini for summarization""" if not gemini_client or not gemini_key: return None try: if hasattr(gemini_client, 'models'): prompt = f"Summarize the following text in {min_length}-{max_length} words. Be concise and clear:\n\n{text[:15000]}" try: response = gemini_client.models.generate_content( model="gemini-2.5-flash", contents=prompt ) return response.text except: try: response = gemini_client.models.generate_content( model="gemini-2.5-flash", contents=prompt, config=types.GenerateContentConfig( max_output_tokens=500 ) if types else None ) return response.text except: pass if hasattr(gemini_client, 'GenerativeModel'): model = gemini_client.GenerativeModel('gemini-2.5-flash') prompt = f"Summarize the following text in {min_length}-{max_length} words:\n\n{text[:15000]}" response = model.generate_content(prompt) return response.text except Exception as e: print(f"Gemini summary error: {e}") return None def summarize_pdf(pdf_file, max_length, min_length): text, error = extract_text_from_pdf(pdf_file) if error: return error gemini_result = summarize_with_gemini(text, max_length, min_length) if gemini_result: return gemini_result summ = load_summarizer() if summ: try: result = summ(text[:3500], max_length=max_length, min_length=min_length, do_sample=False) return result[0]['summary_text'] except Exception as e: return f"Summarization error: {str(e)}" return "Error: No summarization available" def generate_essay_with_gemini(prompt, essay_type, word_count, tone): """Generate essay using Gemini""" if not gemini_client or not gemini_key: return None try: full_prompt = f"""You are an expert academic writer. Write a {essay_type} essay in {tone} tone. Target length: approximately {word_count} words. Topic: {prompt} Requirements: - Engaging introduction with clear thesis statement - Well-structured body paragraphs with supporting arguments and evidence - Strong conclusion that summarizes main points - Use academic vocabulary and formal writing style Write the essay now:""" if hasattr(gemini_client, 'models'): try: response = gemini_client.models.generate_content( model="gemini-2.5-flash", contents=full_prompt ) essay = response.text.strip() except: if hasattr(gemini_client, 'GenerativeModel'): model = gemini_client.GenerativeModel('gemini-2.5-flash') response = model.generate_content(full_prompt) essay = response.text.strip() else: return None else: model = gemini_client.GenerativeModel('gemini-2.5-flash') response = model.generate_content(full_prompt) essay = response.text.strip() essay = re.sub(r'^#+\s*', '', essay) word_count_actual = len(essay.split()) return f"""# {essay_type} Essay: {prompt[:50]}{'...' if len(prompt) > 50 else ''} {essay} --- *~{word_count_actual} words | {tone} | Gemini*""" except Exception as e: print(f"Essay generation error: {e}") return None def generate_essay(prompt, essay_type, word_count, tone): if not prompt or len(prompt.strip()) < 10: return "Please provide a detailed prompt (at least 10 characters)." if gemini_client and gemini_key: gemini_result = generate_essay_with_gemini(prompt, essay_type, word_count, tone) if gemini_result: return gemini_result return f"""❌ AI generation not available. Template Essay: {prompt} Introduction: {prompt} is an important topic that requires careful consideration. This essay explores its key aspects. Body: The significance of {prompt} cannot be overstated. Various perspectives exist on this matter, with experts debating the best approaches. Research continues to reveal new insights. Conclusion: In conclusion, {prompt} represents a complex issue that demands attention. --- *Template fallback - Gemini SDK issue* Check: 1) GEMINI_API_KEY is set 2) google-genai package is installed""" def summarize_text(text, max_length, min_length): if len(text.strip()) < 100: return "Please provide at least 100 characters to summarize." gemini_result = summarize_with_gemini(text, max_length, min_length) if gemini_result: return gemini_result summ = load_summarizer() if summ: try: result = summ(text[:3500], max_length=max_length, min_length=min_length, do_sample=False) return result[0]['summary_text'] except Exception as e: return f"Error: {str(e)}" return "Error: No summarization available" # ==================== APP 2: QUIZ GENERATOR ==================== def extract_sentences(text): sentences = re.split(r'[.!?]', text) return [s.strip() for s in sentences if len(s.split()) > 6] def create_quiz(text, num_questions): sentences = extract_sentences(text) if len(sentences) < num_questions: num_questions = len(sentences) selected = random.sample(sentences, num_questions) quiz_data = [] for sentence in selected: words = sentence.split() keyword = random.choice(words[2:-2]) question = sentence.replace(keyword, "_____") all_words = list(set(text.split())) wrong = random.sample(all_words, 3) options = wrong + [keyword] random.shuffle(options) quiz_data.append({ "question": question, "options": options, "answer": keyword }) return quiz_data def start_quiz(text, num_questions, timer_minutes): if not text.strip(): return "⚠️ Please enter study material.", None, None, None, None, None, None, "" quiz = create_quiz(text, num_questions) end_time = time.time() + (timer_minutes * 60) return show_question(quiz, 0, 0, end_time) def show_question(quiz, index, score, end_time): if time.time() > end_time: return finish_quiz(score, len(quiz)) if index >= len(quiz): return finish_quiz(score, len(quiz)) q = quiz[index] remaining = int(end_time - time.time()) timer_display = f"⏳ Time Left: {remaining} seconds" return ( f"### Question {index+1}:\n\n{q['question']}", gr.update(choices=q["options"], value=None), f"Score: {score}", quiz, index, score, end_time, timer_display ) def submit_answer(selected, quiz, index, score, end_time): if selected == quiz[index]["answer"]: score += 1 index += 1 return show_question(quiz, index, score, end_time) def finish_quiz(score, total): return ( f"## ✅ Quiz Finished!\n\nFinal Score: **{score}/{total}**", gr.update(choices=[]), "", None, None, None, None, "" ) # ==================== APP 3: URDU TRANSLATOR ==================== def translate_to_urdu(text): """Translate English text to Urdu using Groq's Llama 3.3 70B""" if not text or not text.strip(): return "Please enter some text to translate." if not groq_client: return "❌ Groq API not configured. Please add GROQ_API_KEY." try: chat_completion = groq_client.chat.completions.create( messages=[ { "role": "system", "content": "You are a professional English to Urdu translator. Translate the given text accurately to Urdu (اردو) using natural, conversational language. Respond ONLY with the translation, no explanations." }, { "role": "user", "content": f"Translate to Urdu:\n\n{text}" } ], model="llama-3.3-70b-versatile", temperature=0.3, max_completion_tokens=2048, ) return chat_completion.choices[0].message.content except Exception as e: return f"Error: {str(e)}" # ==================== CSS ==================== custom_css = """ .header { text-align: center; margin-bottom: 2rem; padding: 2rem; background: linear-gradient(135deg, #059669, #6b7280); border-radius: 12px; color: white; } .header h1 { font-size: 2.5rem; margin-bottom: 0.5rem; } .status-ok { background: #d1fae5; border: 2px solid #059669; padding: 1rem; border-radius: 8px; text-align: center; color: #065f46; font-weight: bold; margin-bottom: 1rem; } .status-warn { background: #fef3c7; border: 2px solid #f59e0b; padding: 1rem; border-radius: 8px; text-align: center; color: #92400e; margin-bottom: 1rem; } .status-error { background: #fee2e2; border: 2px solid #ef4444; padding: 1rem; border-radius: 8px; text-align: center; color: #991b1b; margin-bottom: 1rem; } @import url('https://fonts.googleapis.com/css2?family=Noto+Nastaliq+Urdu&display=swap'); .urdu-text { font-family: 'Noto Nastaliq Urdu', serif; font-size: 1.5em; line-height: 2; direction: rtl; text-align: right; } """ # ==================== MAIN UI ==================== with gr.Blocks(title="Student AI Suite") as demo: # Status Display status = check_status() if "✅" in status and "❌" not in status: gr.HTML(f'
{status}
') elif "❌" in status: gr.HTML(f'
{status}
') else: gr.HTML(f'
{status}
') gr.HTML('

🎓 Student AI Suite

Essay Generator • PDF Summarizer • Quiz Generator • Urdu Translator

') with gr.Tabs(): # ==================== TAB 1: PDF & ESSAY ==================== with gr.TabItem("📄 Essay & PDF Tools"): with gr.Tabs(): with gr.TabItem("📑 PDF Summarizer"): pdf_input = gr.File(label="Upload PDF", file_types=[".pdf"], type="binary") with gr.Row(): max_len = gr.Slider(50, 500, 200, step=10, label="Max Length") min_len = gr.Slider(20, 200, 50, step=10, label="Min Length") summarize_btn = gr.Button("📝 Summarize PDF", variant="primary") pdf_output = gr.Textbox(label="Summary", lines=12) gr.Markdown("---") text_input = gr.Textbox(label="Or paste text", lines=5) text_btn = gr.Button("Summarize Text") text_output = gr.Textbox(label="Summary", lines=8) with gr.TabItem("✍️ Essay Generator"): prompt_input = gr.Textbox(label="Essay Topic", placeholder="Example: 'The impact of AI on education'", lines=3) essay_type = gr.Dropdown(["Argumentative", "Expository", "Descriptive", "Persuasive"], value="Argumentative") tone = gr.Dropdown(["Academic", "Formal", "Neutral"], value="Academic") words = gr.Slider(200, 1000, 500, step=50, label="Word Count") gen_btn = gr.Button("✨ Generate Essay", variant="primary") essay_output = gr.Textbox(label="Generated Essay", lines=25) # ==================== TAB 2: QUIZ GENERATOR ==================== with gr.TabItem("🎯 Smart Quiz Generator"): gr.Markdown("### Paste your study text → generate quiz → test yourself!") quiz_text_input = gr.Textbox(lines=6, label="Study Material") with gr.Row(): num_questions = gr.Slider(1, 10, value=5, step=1, label="Number of Questions") timer_minutes = gr.Slider(1, 10, value=2, step=1, label="Timer (Minutes)") start_btn = gr.Button("Start Quiz", variant="primary") question_box = gr.Markdown() options_radio = gr.Radio(label="Select Answer") submit_btn = gr.Button("Submit Answer", variant="secondary") score_display = gr.Markdown() timer_display = gr.Markdown() # Hidden states for quiz quiz_state = gr.State() index_state = gr.State() score_state = gr.State() endtime_state = gr.State() # ==================== TAB 3: URDU TRANSLATOR ==================== with gr.TabItem("🌍 English to Urdu"): gr.Markdown("### Powered by Groq AI") with gr.Row(): with gr.Column(): input_text = gr.Textbox( label="English Text", placeholder="Enter text to translate...", lines=4 ) translate_btn = gr.Button("Translate", variant="primary") with gr.Column(): output_text = gr.Textbox( label="Urdu Translation (اردو)", lines=4, elem_classes=["urdu-text"], interactive=False ) examples = [ "Hello, how are you?", "I love Pakistan.", "The weather is beautiful today." ] gr.Examples(examples=examples, inputs=input_text) # ==================== EVENT HANDLERS ==================== # PDF & Essay handlers summarize_btn.click(summarize_pdf, [pdf_input, max_len, min_len], pdf_output) text_btn.click(summarize_text, [text_input, max_len, min_len], text_output) gen_btn.click(generate_essay, [prompt_input, essay_type, words, tone], essay_output) # Quiz handlers start_btn.click( start_quiz, inputs=[quiz_text_input, num_questions, timer_minutes], outputs=[ question_box, options_radio, score_display, quiz_state, index_state, score_state, endtime_state, timer_display ] ) submit_btn.click( submit_answer, inputs=[options_radio, quiz_state, index_state, score_state, endtime_state], outputs=[ question_box, options_radio, score_display, quiz_state, index_state, score_state, endtime_state, timer_display ] ) # Urdu translator handlers translate_btn.click(fn=translate_to_urdu, inputs=input_text, outputs=output_text) input_text.submit(fn=translate_to_urdu, inputs=input_text, outputs=output_text) if __name__ == "__main__": demo.launch(server_name="0.0.0.0", server_port=7860, css=custom_css)