import os import time import json import base64 from datetime import datetime import gradio as gr from google import genai from google.genai import types # ── Gemini Client Setup ── api_key = os.environ.get("GEMINI_API_KEY", "") client = genai.Client(api_key=api_key) if api_key else None # ── Mock Data & Configuration ── SUBJECTS = [ {"id": "math", "label": "Mathematics", "emoji": "📐", "description": "Algebra, Calculus, Geometry & more"}, {"id": "science", "label": "Science", "emoji": "🔬", "description": "Physics, Chemistry, Biology & more"}, {"id": "history", "label": "History", "emoji": "🏛️", "description": "World events, civilizations & culture"}, {"id": "literature", "label": "Literature", "emoji": "📖", "description": "Essays, poetry, novels & analysis"}, {"id": "geography", "label": "Geography", "emoji": "🌍", "description": "Maps, climate, human geography"}, {"id": "cs", "label": "Computer Science", "emoji": "💻", "description": "Algorithms, data structures & coding"}, ] SUGGESTIONS = { "math": ["Explain the quadratic formula", "What is a derivative?", "How do matrices work?"], "science": ["Explain Newton's third law", "What is DNA replication?", "How does nuclear fusion work?"], "history": ["What caused WWI?", "Explain the French Revolution", "Who was Genghis Khan?"], "literature": ["Analyse Hamlet's soliloquy", "What is a metaphor?", "Explain stream of consciousness"], "geography": ["What causes earthquakes?", "Explain plate tectonics", "What is the water cycle?"], "cs": ["Explain Big O notation", "What is recursion?", "How does a hash table work?"], } def get_greeting(): h = datetime.now().hour if h < 12: return "Good morning 👋" elif h < 17: return "Good afternoon 👋" else: return "Good evening 👋" # ── Gemini Stream & Logic Functions ── def predict(message, history, subject_id, file_input): if not client: yield "⚠️ Please configure GEMINI_API_KEY in your Hugging Face Space secrets." return subj = next((s["label"] for s in SUBJECTS if s["id"] == subject_id), "General Studies") system_instruction = f"You are an expert AI Tutor specializing in {subj}. Help the student understand concepts clearly." formatted_contents = [] for turn in history: formatted_contents.append(types.Content(role="user", parts=[types.Part.from_text(turn[0])])) formatted_contents.append(types.Content(role="model", parts=[types.Part.from_text(turn[1])])) current_parts = [types.Part.from_text(message)] if file_input is not None: try: with open(file_input.name, "rb") as f: pdf_bytes = f.read() current_parts.append( types.Part.from_bytes( data=pdf_bytes, mime_type="application/pdf" ) ) except Exception as e: yield f"Error reading PDF file: {str(e)}" return formatted_contents.append(types.Content(role="user", parts=current_parts)) try: response_stream = client.models.generate_content_stream( model='gemini-2.5-flash', contents=formatted_contents, config=types.GenerateContentConfig( system_instruction=system_instruction, temperature=0.7, ) ) partial_message = "" for chunk in response_stream: if chunk.text: partial_message += chunk.text yield partial_message except Exception as e: yield f"An error occurred: {str(e)}" # ── Dynamic Features Generators ── def generate_quiz(subject_id): subj = next((s["label"] for s in SUBJECTS if s["id"] == subject_id), "General Studies") if not client: return "API key missing." prompt = f"Generate a 3-question multiple choice quiz about {subj} with answers at the bottom. Format beautifully with markdown." response = client.models.generate_content(model='gemini-2.5-flash', contents=prompt) return response.text def generate_notes(history, subject_id): subj = next((s["label"] for s in SUBJECTS if s["id"] == subject_id), "General Studies") if not client: return "API key missing." prompt = f"Based on this study history for {subj}: {str(history)}. Create highly structured, clean summary revision notes with key bullet points." response = client.models.generate_content(model='gemini-2.5-flash', contents=prompt) return response.text def generate_flashcards(history, subject_id): subj = next((s["label"] for s in SUBJECTS if s["id"] == subject_id), "General Studies") if not client: return "API key missing." prompt = f"Based on this study history for {subj}: {str(history)}. Generate 4 interactive flashcards formatted as 'Question: [Text] | Answer: [Text]'." response = client.models.generate_content(model='gemini-2.5-flash', contents=prompt) return response.text # ── Gradio UI Construction ── with gr.Blocks(theme=gr.themes.Soft(primary_hue="indigo", secondary_hue="slate")) as demo: current_subject_id = gr.State(value="math") with gr.Row(): # SIDEBAR with gr.Column(scale=1, min_width=280): gr.Markdown("## ⚡ StudyAI Dashboard") greeting_md = gr.Markdown(f"*{get_greeting()}*") subject_dropdown = gr.Dropdown( choices=[(s["label"] + " " + s["emoji"], s["id"]) for s in SUBJECTS], value="math", label="📚 Select Active Subject", interactive=True ) gr.Markdown("---") gr.Markdown("⏱️ **Focus Pomodoro Timer**") gr.HTML("
25:00
") with gr.Row(): gr.Button("▶️ Start", size="sm") gr.Button("⏹️ Reset", size="sm") gr.Markdown("---") gr.Markdown("📝 **Todo Checklist**") todo_list = gr.CheckboxGroup( choices=["Read textbook chapters", "Complete AI Quiz assignment", "Review Generated Notes"], label="Tasks for Session" ) new_todo = gr.Textbox(placeholder="+ Add custom task & press Enter", show_label=False) def add_todo(task, current_todos): if task.strip(): current_todos.append(task) return gr.update(choices=current_todos, value=current_todos), "" new_todo.submit(add_todo, [new_todo, todo_list], [todo_list, new_todo]) # MAIN WORKSPACE with gr.Column(scale=4): with gr.Tabs() as main_tabs: # TAB 1: Chat with gr.Tab("💬 AI Chat Workspace", id="chat_tab"): subject_info_banner = gr.Markdown("### 📐 Mathematics \n*Algebra, Calculus, Geometry & more*") suggestion_box = gr.Radio( choices=SUGGESTIONS["math"], label="💡 Instant Suggestions (Click to choose, then type or send)", interactive=True ) file_upload = gr.File(label="📂 Upload Study Material (PDF up to 50MB)", file_types=[".pdf"]) chatbot = gr.Chatbot(height=400, show_copy_button=True) with gr.Row(): msg_input = gr.Textbox(placeholder="Ask anything...", scale=4, show_label=False) audio_input = gr.Audio(sources=["microphone"], type="filepath", label="🎙️ Voice", scale=1) submit_btn = gr.Button("Send 🚀", variant="primary", scale=1) def update_subject_view(sub_id): subj_obj = next((s for s in SUBJECTS if s["id"] == sub_id), SUBJECTS[0]) banner_text = f"### {subj_obj['emoji']} {subj_obj['label']} \n*{subj_obj['description']}*" return banner_text, gr.update(choices=SUGGESTIONS.get(sub_id, [])), sub_id, [] subject_dropdown.change( update_subject_view, inputs=[subject_dropdown], outputs=[subject_info_banner, suggestion_box, current_subject_id, chatbot] ) suggestion_box.change(lambda x: x, inputs=[suggestion_box], outputs=[msg_input]) def handle_audio(audio_path): if audio_path: return f"[Voice Note Uploaded: Analyzing audio content]" return "" audio_input.change(handle_audio, inputs=[audio_input], outputs=[msg_input]) def respond(message, chat_history, sub_id, pdf_file): if not message.strip(): return "", chat_history chat_history.append((message, "")) for chunk in predict(message, chat_history[:-1], sub_id, pdf_file): chat_history[-1] = (message, chunk) yield "", chat_history submit_btn.click(respond, [msg_input, chatbot, current_subject_id, file_upload], [msg_input, chatbot]) msg_input.submit(respond, [msg_input, chatbot, current_subject_id, file_upload], [msg_input, chatbot]) # TAB 2: Quiz with gr.Tab("🎯 AI Quiz Mode", id="quiz_tab"): quiz_generate_btn = gr.Button("✨ Generate New Custom Quiz via Gemini", variant="secondary") quiz_output = gr.Markdown("*Click the button above to dynamically render an AI quiz.*") quiz_generate_btn.click(generate_quiz, inputs=[current_subject_id], outputs=[quiz_output]) # TAB 3: Notes with gr.Tab("📝 Summary Study Notes", id="notes_tab"): notes_generate_btn = gr.Button("📖 Compile Revision Notes from Chat History", variant="secondary") notes_output = gr.Markdown("*Click above to transform chat threads into notes.*") notes_generate_btn.click(generate_notes, inputs=[chatbot, current_subject_id], outputs=[notes_output]) # TAB 4: Flashcards with gr.Tab("🃏 Active Flashcards", id="flashcards_tab"): flash_generate_btn = gr.Button("🔄 Formulate Flashcards from Chat History", variant="secondary") flash_output = gr.Markdown("*Click above to extract flashcards.*") flash_generate_btn.click(generate_flashcards, inputs=[chatbot, current_subject_id], outputs=[flash_output]) gr.Markdown("⚡ *Powered by Gemini 2.5 Flash API.*") if __name__ == "__main__": demo.queue() demo.launch()