""" AI Study Assistant - Main Application Gradio UI for the intelligent study assistant """ import gradio as gr import os from typing import Tuple from config import ( APP_TITLE, APP_DESCRIPTION, SUPPORTED_FILE_TYPES, MAX_INPUT_LENGTH, ) from core.utils import ( extract_text_from_file, truncate_text, format_error_message, log_event, create_directory, ) from core.validator import InputValidator, PromptValidator from features.summarizer import Summarizer from features.quiz_generator import QuizGenerator from features.explainer import Explainer from features.doubt_solver import DoubtSolver # Initialize features summarizer = Summarizer() quiz_gen = QuizGenerator() explainer = Explainer() doubt_solver = DoubtSolver() # Initialize validator validator = InputValidator() # =========================== # CORE PROCESSING FUNCTIONS # =========================== def process_file_upload(file_obj) -> Tuple[str, str]: """ Process uploaded file and extract text. Args: file_obj: Gradio file object Returns: Tuple of (file_text, status_message) """ if not file_obj: return "", "No file selected" try: file_path = file_obj # Validate file extension file_ext = os.path.splitext(file_path)[1].lower() if file_ext not in SUPPORTED_FILE_TYPES: return "", f"Unsupported file type: {file_ext}" # Extract text text = extract_text_from_file(file_path) log_event("FILE_PROCESSED", f"File type: {file_ext}, Size: {len(text)}") return text, f"✓ File processed ({len(text)} characters)" except Exception as e: error_msg = format_error_message(e) log_event("FILE_ERROR", str(e)) return "", error_msg def generate_summary( input_text: str, mode: str, use_file: bool, uploaded_file ) -> str: """Generate summary based on input.""" try: text = input_text if use_file and uploaded_file: text, status = process_file_upload(uploaded_file) if not text: return f"Error: {status}" if not text: return "Error: No input provided" # Validate is_valid, msg = validator.validate_input(text) if not is_valid: return f"Validation Error: {msg}" log_event("FEATURE_START", f"Generating summary in {mode} mode") success, result = summarizer.summarize(text, mode=mode) if success: return f""" **Summary ({mode.upper()} MODE)** {result} --- ✓ Generation successful """ else: return f"Error: {result}" except Exception as e: error_msg = format_error_message(e) log_event("FEATURE_ERROR", f"Summary: {str(e)}") return error_msg def generate_quiz( input_text: str, num_questions: int, mode: str, use_file: bool, uploaded_file ) -> str: """Generate quiz based on input.""" try: text = input_text if use_file and uploaded_file: text, status = process_file_upload(uploaded_file) if not text: return f"Error: {status}" if not text: return "Error: No input provided" is_valid, msg = validator.validate_input(text) if not is_valid: return f"Validation Error: {msg}" log_event("FEATURE_START", f"Generating {num_questions} quiz questions in {mode} mode") success, result = quiz_gen.generate_quiz(text, num_questions=num_questions, mode=mode) if success: return f""" **QUIZ ({mode.upper()} MODE)** {result} --- ✓ Quiz generated successfully """ else: return f"Error: {result}" except Exception as e: error_msg = format_error_message(e) log_event("FEATURE_ERROR", f"Quiz: {str(e)}") return error_msg def explain_concept( concept: str, context_text: str, mode: str, use_file: bool, uploaded_file ) -> str: """Explain a concept.""" try: context = context_text if use_file and uploaded_file: context, status = process_file_upload(uploaded_file) if not context: context = context_text if not concept: return "Error: Please enter a concept to explain" is_valid, msg = validator.validate_input(concept) if not is_valid: return f"Validation Error: {msg}" log_event("FEATURE_START", f"Explaining '{concept}' in {mode} mode") success, result = explainer.explain(concept, context=context, mode=mode) if success: return f""" **EXPLANATION - {concept.upper()} ({mode.upper()} MODE)** {result} --- ✓ Explanation generated """ else: return f"Error: {result}" except Exception as e: error_msg = format_error_message(e) log_event("FEATURE_ERROR", f"Explainer: {str(e)}") return error_msg def solve_doubt( question: str, context_text: str, mode: str, use_file: bool, uploaded_file ) -> str: """Solve a student's doubt.""" try: context = context_text if use_file and uploaded_file: context, status = process_file_upload(uploaded_file) if not context: context = context_text if not question: return "Error: Please enter your doubt/question" is_valid, msg = validator.validate_input(question) if not is_valid: return f"Validation Error: {msg}" log_event("FEATURE_START", f"Solving doubt in {mode} mode") success, result = doubt_solver.solve(question, context=context, mode=mode) if success: return f""" **DOUBT SOLVER ({mode.upper()} MODE)** {result} --- ✓ Doubt solved successfully """ else: return f"Error: {result}" except Exception as e: error_msg = format_error_message(e) log_event("FEATURE_ERROR", f"DoubtSolver: {str(e)}") return error_msg def compare_modes(input_text: str, feature_type: str, extra_input: str = "") -> str: """Compare outputs across different modes.""" try: if not input_text and not extra_input: return "Error: Please provide input" modes = ["normal", "detailed", "teacher", "exam"] results = {} log_event("FEATURE_START", f"Comparing modes for {feature_type}") if feature_type == "Summary": for mode in modes: success, result = summarizer.summarize(input_text, mode=mode) results[mode] = result if success else f"Error: {result}" elif feature_type == "Explainer": concept = extra_input or input_text for mode in modes: success, result = explainer.explain(concept, mode=mode) results[mode] = result if success else f"Error: {result}" elif feature_type == "Doubt Solver": question = extra_input or input_text for mode in modes: success, result = doubt_solver.solve(question, mode=mode) results[mode] = result if success else f"Error: {result}" # Format comparison output = f"**MODE COMPARISON - {feature_type.upper()}**\n\n" for mode, result in results.items(): output += f"## {mode.upper()} Mode\n{result}\n\n---\n\n" return output except Exception as e: error_msg = format_error_message(e) log_event("FEATURE_ERROR", f"Mode comparison: {str(e)}") return error_msg # =========================== # CREATE DIRECTORIES # =========================== create_directory("uploads") create_directory("data/outputs") create_directory("prompts/modes") create_directory("core") create_directory("features") # =========================== # BUILD GRADIO INTERFACE # =========================== with gr.Blocks( title=APP_TITLE ) as demo: # Header gr.Markdown(f"# 🎓 {APP_TITLE}") gr.Markdown(APP_DESCRIPTION) # Tabs for different features with gr.Tabs(): # ===== TAB 1: SUMMARIZER ===== with gr.TabItem("📄 Summary Generator"): with gr.Row(): with gr.Column(scale=1): gr.Markdown("### Input Options") use_summary_file = gr.Checkbox( label="Upload File", value=False ) summary_uploaded_file = gr.File( label="Choose File (.txt, .pdf, .md)", visible=False, file_types=SUPPORTED_FILE_TYPES ) summary_text_input = gr.Textbox( label="Or paste text here", lines=10, placeholder="Paste your notes or topic here...", max_lines=20 ) with gr.Column(scale=1): gr.Markdown("### Settings") summary_mode = gr.Radio( label="Select Mode", choices=["normal", "detailed", "teacher", "exam"], value="normal" ) summary_btn = gr.Button( "Generate Summary", variant="primary" ) summary_output = gr.Textbox( label="✨ Your Summary", lines=15, interactive=False ) # Toggle file upload visibility use_summary_file.change( lambda x: gr.File(visible=x), use_summary_file, summary_uploaded_file ) # Generate on click summary_btn.click( generate_summary, inputs=[ summary_text_input, summary_mode, use_summary_file, summary_uploaded_file ], outputs=summary_output ) # ===== TAB 2: QUIZ GENERATOR ===== with gr.TabItem("❓ Quiz Generator"): with gr.Row(): with gr.Column(scale=1): gr.Markdown("### Input") use_quiz_file = gr.Checkbox( label="Upload File", value=False ) quiz_uploaded_file = gr.File( label="Choose File", visible=False, file_types=SUPPORTED_FILE_TYPES ) quiz_text_input = gr.Textbox( label="Or paste text", lines=10, placeholder="Paste your study material..." ) with gr.Column(scale=1): gr.Markdown("### Quiz Settings") quiz_num_questions = gr.Slider( label="Number of Questions", minimum=1, maximum=20, value=5, step=1 ) quiz_mode = gr.Radio( label="Mode", choices=["normal", "detailed", "teacher", "exam"], value="normal" ) quiz_btn = gr.Button( "Generate Quiz", variant="primary" ) quiz_output = gr.Textbox( label="📋 Your Quiz", lines=20, interactive=False ) use_quiz_file.change( lambda x: gr.File(visible=x), use_quiz_file, quiz_uploaded_file ) quiz_btn.click( generate_quiz, inputs=[ quiz_text_input, quiz_num_questions, quiz_mode, use_quiz_file, quiz_uploaded_file ], outputs=quiz_output ) # ===== TAB 3: EXPLAINER ===== with gr.TabItem("🔍 Concept Explainer"): with gr.Row(): with gr.Column(scale=1): gr.Markdown("### What to Explain") concept_input = gr.Textbox( label="Concept to Explain", placeholder="e.g., Photosynthesis, Recursion, etc." ) gr.Markdown("### Optional Context") use_explain_file = gr.Checkbox( label="Add Context from File", value=False ) explain_uploaded_file = gr.File( label="Choose File", visible=False, file_types=SUPPORTED_FILE_TYPES ) context_input = gr.Textbox( label="Or paste context", lines=8, placeholder="Paste related notes for context..." ) with gr.Column(scale=1): gr.Markdown("### Explanation Mode") explain_mode = gr.Radio( label="Select Mode", choices=["normal", "detailed", "teacher", "exam"], value="teacher" ) explain_btn = gr.Button( "Explain Concept", variant="primary" ) explain_output = gr.Textbox( label="📚 Explanation", lines=20, interactive=False ) use_explain_file.change( lambda x: gr.File(visible=x), use_explain_file, explain_uploaded_file ) explain_btn.click( explain_concept, inputs=[ concept_input, context_input, explain_mode, use_explain_file, explain_uploaded_file ], outputs=explain_output ) # ===== TAB 4: DOUBT SOLVER ===== with gr.TabItem("🧠 Doubt Solver"): with gr.Row(): with gr.Column(scale=1): gr.Markdown("### Your Doubt") question_input = gr.Textbox( label="Ask Your Question", lines=8, placeholder="Type your doubt or question here...", max_lines=15 ) gr.Markdown("### Add Context (Optional)") use_doubt_file = gr.Checkbox( label="Add Notes from File", value=False ) doubt_uploaded_file = gr.File( label="Choose File", visible=False, file_types=SUPPORTED_FILE_TYPES ) doubt_context_input = gr.Textbox( label="Or paste notes", lines=8, placeholder="Paste relevant notes..." ) with gr.Column(scale=1): gr.Markdown("### Solution Mode") doubt_mode = gr.Radio( label="Explanation Style", choices=["normal", "detailed", "teacher", "exam"], value="teacher" ) doubt_btn = gr.Button( "Solve Doubt", variant="primary" ) doubt_output = gr.Textbox( label="💡 Solution", lines=20, interactive=False ) use_doubt_file.change( lambda x: gr.File(visible=x), use_doubt_file, doubt_uploaded_file ) doubt_btn.click( solve_doubt, inputs=[ question_input, doubt_context_input, doubt_mode, use_doubt_file, doubt_uploaded_file ], outputs=doubt_output ) # ===== TAB 5: COMPARE MODES ===== with gr.TabItem("🔄 Mode Comparison"): gr.Markdown("### Compare how different modes respond to your input") with gr.Row(): with gr.Column(scale=1): compare_feature = gr.Radio( label="Select Feature", choices=["Summary", "Explainer", "Doubt Solver"], value="Summary" ) compare_input = gr.Textbox( label="Input Text", lines=10, placeholder="Paste text or ask a question..." ) compare_extra = gr.Textbox( label="Extra Input (if needed)", placeholder="For Explainer: concept to explain", visible=False ) compare_btn = gr.Button( "Compare All Modes", variant="primary" ) compare_output = gr.Textbox( label="📊 Mode Comparison Results", lines=25, interactive=False ) # Show/hide extra input based on feature selection compare_feature.change( lambda x: gr.Textbox(visible=(x != "Summary")), compare_feature, compare_extra ) compare_btn.click( compare_modes, inputs=[ compare_input, compare_feature, compare_extra ], outputs=compare_output ) # Footer gr.Markdown(""" --- ### 💡 Tips: - **Upload files** (PDF, TXT, MD) for faster processing - **Try different modes** to see how prompts affect responses - **Use Doubt Solver** with context for better answers - **Compare modes** to understand prompt engineering ### 🎓 Features Powered By: - **Prompt Engineering**: Dynamic prompts adapt to your needs - **Chain-of-Thought**: Step-by-step reasoning for complex problems - **Few-Shot Learning**: Consistent, high-quality outputs - **Input Validation**: Safety & security built-in *Built with ❤️ for smarter studying* """) # =========================== # RUN APPLICATION # =========================== if __name__ == "__main__": log_event("APP_START", "AI Study Assistant launched") demo.launch( share=False, server_name="0.0.0.0", server_port=7860, show_error=True, theme=gr.themes.Soft(), )