| import asyncio.base_events |
|
|
| _orig_del = asyncio.base_events.BaseEventLoop.__del__ |
|
|
| def _patched_del(self): |
| try: |
| _orig_del(self) |
| except ValueError as e: |
| if "Invalid file descriptor" not in str(e): |
| raise |
|
|
| asyncio.base_events.BaseEventLoop.__del__ = _patched_del |
|
|
| import os |
| import gradio as gr |
|
|
| from extractors import extract_text_dispatcher |
| from llm import generate_notes |
| from chat import chat |
| from rag import chunk_text, build_collection |
| from flashcards import ( |
| render_card, |
| generate_flashcards_handler, |
| flip_card, |
| next_card, |
| prev_card, |
| ) |
| from exam import ( |
| render_question, |
| generate_exam_handler, |
| next_question, |
| prev_question, |
| record_answer, |
| grade_exam, |
| ) |
|
|
| |
|
|
| def process_file_uploads(files, history): |
| """ |
| Rebuilds study_context AND the RAG vector store from scratch based on |
| whatever files are CURRENTLY in the upload widget. Runs on every change |
| to the file list — additions AND deletions. |
| """ |
| if not files: |
| |
| return history, "", None, "No files currently loaded." |
|
|
| file_list = files if isinstance(files, list) else [files] |
|
|
| new_extracted_text = "" |
| filenames = [] |
|
|
| for file_obj in file_list: |
| file_path = file_obj.name if hasattr(file_obj, "name") else file_obj |
| filename = os.path.basename(file_path) |
| filenames.append(filename) |
|
|
| try: |
| extracted = extract_text_dispatcher(file_path) |
| new_extracted_text += f"\n--- Start of {filename} ---\n{extracted}\n--- End of {filename} ---\n" |
| except Exception as e: |
| new_extracted_text += f"\n--- Start of {filename} ---\n[Error processing file: {str(e)}]\n--- End of {filename} ---\n" |
|
|
| system_msg = f"📁 Currently loaded: {', '.join(filenames)}" |
| history = history + [{"role": "assistant", "content": system_msg}] |
| upload_status_text = f"✅ **Currently loaded:** {', '.join(filenames)}" |
|
|
| chunks = chunk_text(new_extracted_text) |
| collection = build_collection(chunks) |
|
|
| return history, new_extracted_text, collection, upload_status_text |
|
|
|
|
| def put_message_in_chatbot(message, history): |
| return "", history + [{"role": "user", "content": message}] |
|
|
|
|
| |
|
|
| with gr.Blocks() as ui: |
| |
| study_context = gr.State(value="") |
| vector_store_state = gr.State(value=None) |
|
|
| |
| flashcards_state = gr.State(value=[]) |
| current_index = gr.State(value=0) |
| show_answer = gr.State(value=False) |
|
|
| |
| exam_questions_state = gr.State(value=[]) |
| exam_index = gr.State(value=0) |
| user_answers_state = gr.State(value={}) |
| exam_submitted_state = gr.State(value=False) |
| |
|
|
| gr.Markdown("## Study LLM") |
|
|
|
|
| with gr.Tab("Upload Files"): |
| with gr.Row(): |
| upload = gr.File( |
| file_types=[".pdf", ".pptx", ".docx", ".txt", ".mp4", ".avi", ".mov", ".png", ".jpg", ".jpeg"], |
| file_count="multiple", |
| label="Drag and drop your study materials here", |
| ) |
| with gr.Row(): |
| upload_status = gr.Markdown("No files currently loaded.") |
|
|
| with gr.Tab("Chat"): |
| with gr.Row(): |
| chatbot = gr.Chatbot(height=500) |
|
|
| with gr.Row(): |
| message = gr.Textbox(label="Chat with our AI Assistant:", placeholder="Ask something about your study notes...") |
| |
|
|
| with gr.Tab("Notes"): |
| with gr.Column(): |
| generate_notes_button = gr.Button("Generate Study Notes", variant="primary") |
| notes_output = gr.Markdown(label="Generated Study Notes", latex_delimiters=[ |
| {"left": "$$", "right": "$$", "display": True}, |
| {"left": "$", "right": "$", "display": False}, |
| {"left": "\\[", "right": "\\]", "display": True}, |
| {"left": "\\(", "right": "\\)", "display": False}, |
| ]) |
|
|
| with gr.Tab("Flashcards"): |
| with gr.Column(): |
| generate_flashcards_button = gr.Button("Generate Flashcards", variant="primary") |
| card_display = gr.Markdown(label="Flashcard", latex_delimiters=[ |
| {"left": "$$", "right": "$$", "display": True}, |
| {"left": "$", "right": "$", "display": False}, |
| {"left": "\\[", "right": "\\]", "display": True}, |
| {"left": "\\(", "right": "\\)", "display": False}, |
| ]) |
| with gr.Row(): |
| prev_button = gr.Button("⬅ Previous") |
| flip_button = gr.Button("Flip") |
| next_button = gr.Button("Next ➡") |
| |
| with gr.Tab("Practice Exam"): |
| with gr.Column(): |
| generate_exam_button = gr.Button("Generate Practice Exam", variant="primary") |
| question_display = gr.Markdown(label="Question", latex_delimiters=[ |
| {"left": "$$", "right": "$$", "display": True}, |
| {"left": "$", "right": "$", "display": False}, |
| {"left": "\\[", "right": "\\]", "display": True}, |
| {"left": "\\(", "right": "\\)", "display": False}, |
| ]) |
| answer_choices = gr.Radio(choices=[], label="Select an answer") |
| with gr.Row(): |
| prev_question_button = gr.Button("⬅ Previous Question") |
| submit_exam_button = gr.Button("Submit Exam") |
| next_question_button = gr.Button("Next Question ➡") |
| results_display = gr.Markdown(label="Results") |
| |
|
|
| |
|
|
| |
| upload.change( |
| fn=process_file_uploads, |
| inputs=[upload, chatbot], |
| outputs=[chatbot, study_context, vector_store_state, upload_status], |
| ) |
|
|
| |
| message.submit( |
| put_message_in_chatbot, |
| inputs=[message, chatbot], |
| outputs=[message, chatbot], |
| ).then( |
| chat, |
| inputs=[chatbot, study_context, vector_store_state], |
| outputs=chatbot, |
| ) |
|
|
| |
| generate_notes_button.click( |
| fn=lambda: "⏳ Generating notes...", |
| outputs=[notes_output], |
| ).then( |
| fn=generate_notes, |
| inputs=[study_context], |
| outputs=[notes_output], |
| ) |
|
|
|
|
| |
| generate_flashcards_button.click( |
| fn=lambda: "⏳ Generating flashcards...", |
| outputs=[card_display], |
| ).then( |
| fn=generate_flashcards_handler, |
| inputs=[study_context], |
| outputs=[flashcards_state, current_index, show_answer, card_display], |
| ) |
|
|
| flip_button.click( |
| fn=flip_card, |
| inputs=[flashcards_state, current_index, show_answer], |
| outputs=[show_answer, card_display], |
| ) |
|
|
| next_button.click( |
| fn=next_card, |
| inputs=[flashcards_state, current_index], |
| outputs=[current_index, show_answer, card_display], |
| ) |
|
|
| prev_button.click( |
| fn=prev_card, |
| inputs=[flashcards_state, current_index], |
| outputs=[current_index, show_answer, card_display], |
| ) |
|
|
| |
| generate_exam_button.click( |
| fn=generate_exam_handler, |
| inputs=[study_context], |
| outputs=[exam_questions_state, exam_index, user_answers_state, exam_submitted_state, question_display, answer_choices, results_display] |
| ) |
|
|
| next_question_button.click( |
| fn=next_question, |
| inputs=[exam_questions_state, exam_index, user_answers_state], |
| outputs=[exam_index, question_display, answer_choices] |
| ) |
|
|
| prev_question_button.click( |
| fn=prev_question, |
| inputs=[exam_questions_state, exam_index, user_answers_state], |
| outputs=[exam_index, question_display, answer_choices] |
| ) |
|
|
| submit_exam_button.click( |
| fn=grade_exam, |
| inputs=[exam_questions_state, user_answers_state], |
| outputs=[results_display] |
| ) |
|
|
| answer_choices.change( |
| fn=record_answer, |
| inputs=[user_answers_state, exam_index, answer_choices], |
| outputs=[user_answers_state] |
| ) |
|
|
| ui.launch(theme=gr.themes.Ocean()) |