MonishRaman's picture
Upload 31 files
af25a2a verified
Raw
History Blame Contribute Delete
20.8 kB
"""
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(),
)