Spaces:
Sleeping
Sleeping
Chia Woon Yap
commited on
Update app.py
Browse files
app.py
CHANGED
|
@@ -443,316 +443,6 @@ def tutor_ai_chatbot():
|
|
| 443 |
app.launch(share=True) # Set share=True to create a public link
|
| 444 |
|
| 445 |
|
| 446 |
-
# Launch the AI chatbot
|
| 447 |
-
if __name__ == "__main__":
|
| 448 |
-
tutor_ai_chatbot()
|
| 449 |
-
|
| 450 |
-
|
| 451 |
-
|
| 452 |
-
|
| 453 |
-
|
| 454 |
-
# Short-term memory for the LLM
|
| 455 |
-
chat_memory = []
|
| 456 |
-
|
| 457 |
-
# Prompt for quiz generation with added remark
|
| 458 |
-
quiz_prompt = """
|
| 459 |
-
You are an AI assistant specialized in education and assessment creation. Given an uploaded document or text, generate a quiz with a mix of multiple-choice questions (MCQs) and fill-in-the-blank questions. The quiz should be directly based on the key concepts, facts, and details from the provided material.
|
| 460 |
-
Generate 20 Questions.
|
| 461 |
-
Remove all unnecessary formatting generated by the LLM, including <think> tags, asterisks, markdown formatting, and any bold or italic text, as well as **, ###, ##, and # tags.
|
| 462 |
-
For each question:
|
| 463 |
-
- Provide 4 answer choices (for MCQs), with only one correct answer.
|
| 464 |
-
- Ensure fill-in-the-blank questions focus on key terms, phrases, or concepts from the document.
|
| 465 |
-
- Include an answer key for all questions.
|
| 466 |
-
- Ensure questions vary in difficulty and encourage comprehension rather than memorization.
|
| 467 |
-
- Additionally, implement an instant feedback mechanism:
|
| 468 |
-
- When a user selects an answer, indicate whether it is correct or incorrect.
|
| 469 |
-
- If incorrect, provide a brief explanation from the document to guide learning.
|
| 470 |
-
- Ensure responses are concise and educational to enhance understanding.
|
| 471 |
-
Output Example:
|
| 472 |
-
1. Fill in the blank: The LLM Agent framework has a central decision-making unit called the _______________________.
|
| 473 |
-
Answer: Agent Core
|
| 474 |
-
Feedback: The Agent Core is the central component of the LLM Agent framework, responsible for managing goals, tool instructions, planning modules, memory integration, and agent persona.
|
| 475 |
-
2. What is the main limitation of LLM-based applications?
|
| 476 |
-
a) Limited token capacity
|
| 477 |
-
b) Lack of domain expertise
|
| 478 |
-
c) Prone to hallucination
|
| 479 |
-
d) All of the above
|
| 480 |
-
Answer: d) All of the above
|
| 481 |
-
Feedback: LLM-based applications have several limitations, including limited token capacity, lack of domain expertise, and being prone to hallucination, among others.
|
| 482 |
-
"""
|
| 483 |
-
|
| 484 |
-
# Function to clean AI response by removing unwanted formatting
|
| 485 |
-
def clean_response(response):
|
| 486 |
-
"""Removes <think> tags, asterisks, and markdown formatting."""
|
| 487 |
-
cleaned_text = re.sub(r"<think>.*?</think>", "", response, flags=re.DOTALL)
|
| 488 |
-
cleaned_text = re.sub(r"(\*\*|\*|\[|\])", "", cleaned_text)
|
| 489 |
-
cleaned_text = re.sub(r"^##+\s*", "", cleaned_text, flags=re.MULTILINE)
|
| 490 |
-
cleaned_text = re.sub(r"\\", "", cleaned_text)
|
| 491 |
-
cleaned_text = re.sub(r"---", "", cleaned_text)
|
| 492 |
-
return cleaned_text.strip()
|
| 493 |
-
|
| 494 |
-
# Function to generate quiz based on content
|
| 495 |
-
def generate_quiz(content):
|
| 496 |
-
prompt = f"{quiz_prompt}\n\nDocument content:\n{content}"
|
| 497 |
-
response = chat_model([HumanMessage(content=prompt)])
|
| 498 |
-
cleaned_response = clean_response(response.content)
|
| 499 |
-
return cleaned_response
|
| 500 |
-
|
| 501 |
-
# Function to retrieve relevant documents from vectorstore based on user query
|
| 502 |
-
def retrieve_documents(query):
|
| 503 |
-
results = vectorstore.similarity_search(query, k=3)
|
| 504 |
-
return [doc.page_content for doc in results]
|
| 505 |
-
|
| 506 |
-
# Function to handle chatbot interactions with short-term memory
|
| 507 |
-
def chat_with_groq(user_input):
|
| 508 |
-
try:
|
| 509 |
-
# Retrieve relevant documents for additional context
|
| 510 |
-
relevant_docs = retrieve_documents(user_input)
|
| 511 |
-
context = "\n".join(relevant_docs) if relevant_docs else "No relevant documents found."
|
| 512 |
-
|
| 513 |
-
# Construct proper prompting with conversation history
|
| 514 |
-
system_prompt = "You are a helpful AI assistant. Answer questions accurately and concisely."
|
| 515 |
-
conversation_history = "\n".join(chat_memory[-10:]) # Keep the last 10 exchanges
|
| 516 |
-
prompt = f"{system_prompt}\n\nConversation History:\n{conversation_history}\n\nUser Input: {user_input}\n\nContext:\n{context}"
|
| 517 |
-
|
| 518 |
-
# Call the chat model
|
| 519 |
-
response = chat_model([HumanMessage(content=prompt)])
|
| 520 |
-
|
| 521 |
-
# Clean response to remove any unwanted formatting
|
| 522 |
-
cleaned_response_text = clean_response(response.content)
|
| 523 |
-
|
| 524 |
-
# Append conversation history
|
| 525 |
-
chat_memory.append(f"User: {user_input}")
|
| 526 |
-
chat_memory.append(f"AI: {cleaned_response_text}")
|
| 527 |
-
|
| 528 |
-
# Convert response to speech
|
| 529 |
-
audio_file = speech_playback(cleaned_response_text)
|
| 530 |
-
|
| 531 |
-
# Ensure the return format is a list of tuples
|
| 532 |
-
return [(user_input, cleaned_response_text)], audio_file
|
| 533 |
-
except Exception as e:
|
| 534 |
-
return [("Error", str(e))], None
|
| 535 |
-
|
| 536 |
-
|
| 537 |
-
# Function to play response as speech using gTTS
|
| 538 |
-
def speech_playback(text):
|
| 539 |
-
try:
|
| 540 |
-
# Generate a unique filename for each audio file
|
| 541 |
-
unique_id = str(uuid.uuid4())
|
| 542 |
-
audio_file = f"output_audio_{unique_id}.mp3"
|
| 543 |
-
|
| 544 |
-
# Convert text to speech
|
| 545 |
-
tts = gtts.gTTS(text, lang='en')
|
| 546 |
-
tts.save(audio_file)
|
| 547 |
-
|
| 548 |
-
# Return the path to the audio file
|
| 549 |
-
return audio_file
|
| 550 |
-
except Exception as e:
|
| 551 |
-
print(f"Error in speech_playback: {e}")
|
| 552 |
-
return None
|
| 553 |
-
|
| 554 |
-
# Function to detect encoding safely
|
| 555 |
-
def detect_encoding(file_path):
|
| 556 |
-
try:
|
| 557 |
-
with open(file_path, "rb") as f:
|
| 558 |
-
raw_data = f.read(4096)
|
| 559 |
-
detected = chardet.detect(raw_data)
|
| 560 |
-
encoding = detected["encoding"]
|
| 561 |
-
return encoding if encoding else "utf-8"
|
| 562 |
-
except Exception:
|
| 563 |
-
return "utf-8"
|
| 564 |
-
|
| 565 |
-
# Function to extract text from PDF
|
| 566 |
-
def extract_text_from_pdf(pdf_path):
|
| 567 |
-
try:
|
| 568 |
-
doc = fitz.open(pdf_path)
|
| 569 |
-
text = "\n".join([page.get_text("text") for page in doc])
|
| 570 |
-
return text if text.strip() else "No extractable text found."
|
| 571 |
-
except Exception as e:
|
| 572 |
-
return f"Error extracting text from PDF: {str(e)}"
|
| 573 |
-
|
| 574 |
-
# Function to extract text from Word files (.docx)
|
| 575 |
-
def extract_text_from_docx(docx_path):
|
| 576 |
-
try:
|
| 577 |
-
doc = docx.Document(docx_path)
|
| 578 |
-
text = "\n".join([para.text for para in doc.paragraphs])
|
| 579 |
-
return text if text.strip() else "No extractable text found."
|
| 580 |
-
except Exception as e:
|
| 581 |
-
return f"Error extracting text from Word document: {str(e)}"
|
| 582 |
-
|
| 583 |
-
# Function to extract text from PowerPoint files (.pptx)
|
| 584 |
-
def extract_text_from_pptx(pptx_path):
|
| 585 |
-
try:
|
| 586 |
-
presentation = Presentation(pptx_path)
|
| 587 |
-
text = ""
|
| 588 |
-
for slide in presentation.slides:
|
| 589 |
-
for shape in slide.shapes:
|
| 590 |
-
if hasattr(shape, "text"):
|
| 591 |
-
text += shape.text + "\n"
|
| 592 |
-
return text if text.strip() else "No extractable text found."
|
| 593 |
-
except Exception as e:
|
| 594 |
-
return f"Error extracting text from PowerPoint: {str(e)}"
|
| 595 |
-
|
| 596 |
-
# Function to process documents safely
|
| 597 |
-
def process_document(file):
|
| 598 |
-
try:
|
| 599 |
-
file_extension = os.path.splitext(file.name)[-1].lower()
|
| 600 |
-
if file_extension in [".png", ".jpg", ".jpeg"]:
|
| 601 |
-
return "Error: Images cannot be processed for text extraction."
|
| 602 |
-
if file_extension == ".pdf":
|
| 603 |
-
content = extract_text_from_pdf(file.name)
|
| 604 |
-
elif file_extension == ".docx":
|
| 605 |
-
content = extract_text_from_docx(file.name)
|
| 606 |
-
elif file_extension == ".pptx":
|
| 607 |
-
content = extract_text_from_pptx(file.name)
|
| 608 |
-
else:
|
| 609 |
-
encoding = detect_encoding(file.name)
|
| 610 |
-
with open(file.name, "r", encoding=encoding, errors="replace") as f:
|
| 611 |
-
content = f.read()
|
| 612 |
-
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
|
| 613 |
-
documents = [Document(page_content=chunk) for chunk in text_splitter.split_text(content)]
|
| 614 |
-
vectorstore.add_documents(documents)
|
| 615 |
-
quiz = generate_quiz(content)
|
| 616 |
-
return f"Document processed successfully (File Type: {file_extension}). Quiz generated:\n{quiz}"
|
| 617 |
-
except Exception as e:
|
| 618 |
-
return f"Error processing document: {str(e)}"
|
| 619 |
-
|
| 620 |
-
# Function to handle speech-to-text conversion
|
| 621 |
-
def transcribe_audio(audio):
|
| 622 |
-
sr, y = audio
|
| 623 |
-
if y.ndim > 1:
|
| 624 |
-
y = y.mean(axis=1)
|
| 625 |
-
y = y.astype(np.float32)
|
| 626 |
-
y /= np.max(np.abs(y))
|
| 627 |
-
return transcriber({"sampling_rate": sr, "raw": y})["text"]
|
| 628 |
-
|
| 629 |
-
# Modify chat_with_groq function to return audio file for playback
|
| 630 |
-
def chat_with_groq(user_input):
|
| 631 |
-
try:
|
| 632 |
-
# Retrieve relevant documents for additional context
|
| 633 |
-
relevant_docs = retrieve_documents(user_input)
|
| 634 |
-
context = "\n".join(relevant_docs) if relevant_docs else "No relevant documents found."
|
| 635 |
-
|
| 636 |
-
# Construct proper prompting with conversation history
|
| 637 |
-
system_prompt = "You are a helpful AI assistant. Answer questions accurately and concisely."
|
| 638 |
-
conversation_history = "\n".join(chat_memory[-10:]) # Keep the last 10 exchanges
|
| 639 |
-
prompt = f"{system_prompt}\n\nConversation History:\n{conversation_history}\n\nUser Input: {user_input}\n\nContext:\n{context}"
|
| 640 |
-
|
| 641 |
-
# Call the chat model
|
| 642 |
-
response = chat_model([HumanMessage(content=prompt)])
|
| 643 |
-
|
| 644 |
-
# Clean response to remove any unwanted formatting
|
| 645 |
-
cleaned_response_text = clean_response(response.content)
|
| 646 |
-
|
| 647 |
-
# Append conversation history
|
| 648 |
-
chat_memory.append(f"User: {user_input}")
|
| 649 |
-
chat_memory.append(f"AI: {cleaned_response_text}")
|
| 650 |
-
|
| 651 |
-
# Convert response to speech
|
| 652 |
-
audio_file = speech_playback(cleaned_response_text)
|
| 653 |
-
|
| 654 |
-
# Return both chat response and audio file path
|
| 655 |
-
return [(user_input, cleaned_response_text)], audio_file # Return as a tuple
|
| 656 |
-
except Exception as e:
|
| 657 |
-
return [("Error", str(e))], None
|
| 658 |
-
|
| 659 |
-
#__________________________________________________________________________________________________________________________
|
| 660 |
-
|
| 661 |
-
|
| 662 |
-
|
| 663 |
-
def tutor_ai_chatbot():
|
| 664 |
-
"""Main Gradio interface for the Tutor AI Chatbot."""
|
| 665 |
-
with gr.Blocks() as app:
|
| 666 |
-
gr.Markdown("# 📚 AI Tutor - We.(POC)")
|
| 667 |
-
gr.Markdown("An interactive Personal AI Tutor chatbot to help with your learning needs.")
|
| 668 |
-
|
| 669 |
-
# Chatbot Tab
|
| 670 |
-
with gr.Tab("AI Chatbot"):
|
| 671 |
-
with gr.Row():
|
| 672 |
-
with gr.Column(scale=3):
|
| 673 |
-
chatbot = gr.Chatbot(height=500) # Chatbot display area
|
| 674 |
-
with gr.Row():
|
| 675 |
-
msg = gr.Textbox(label="Ask a question", placeholder="Type your question here...")
|
| 676 |
-
submit = gr.Button("Send")
|
| 677 |
-
# Create the chat interface
|
| 678 |
-
#msg = gr.ChatInterface(fn=chat_with_groq, type="messages", autofocus=False) #test
|
| 679 |
-
|
| 680 |
-
#with gr.Row():
|
| 681 |
-
with gr.Column(scale=1):
|
| 682 |
-
audio_input = gr.Audio(type="numpy", label="Record or Upload Audio") # Audio input for speech-to-text
|
| 683 |
-
|
| 684 |
-
|
| 685 |
-
with gr.Column(scale=1):
|
| 686 |
-
audio_playback = gr.Audio(label="Audio Response", type="filepath")
|
| 687 |
-
|
| 688 |
-
# Clear chat history button
|
| 689 |
-
clear_btn = gr.Button("Clear Chat")
|
| 690 |
-
|
| 691 |
-
# Handle chat interaction
|
| 692 |
-
submit.click(
|
| 693 |
-
chat_with_groq,
|
| 694 |
-
inputs=[msg],
|
| 695 |
-
outputs=[chatbot, audio_playback]
|
| 696 |
-
)
|
| 697 |
-
|
| 698 |
-
# Clear chat history function
|
| 699 |
-
def clear_chat_history():
|
| 700 |
-
return None, None
|
| 701 |
-
|
| 702 |
-
clear_btn.click(clear_chat_history, inputs=None, outputs=[chatbot, audio_playback]) #,audio_input
|
| 703 |
-
|
| 704 |
-
# Also allow Enter key to submit
|
| 705 |
-
msg.submit(
|
| 706 |
-
chat_with_groq,
|
| 707 |
-
inputs=[msg],
|
| 708 |
-
outputs=[chatbot, audio_playback]
|
| 709 |
-
)
|
| 710 |
-
|
| 711 |
-
# Add some examples of questions students might ask
|
| 712 |
-
with gr.Accordion("Example Questions", open=False):
|
| 713 |
-
gr.Examples(
|
| 714 |
-
examples=[
|
| 715 |
-
"Can you explain the concept of RLHF AI?",
|
| 716 |
-
"What are AI transformers?",
|
| 717 |
-
"What is MoE AI?",
|
| 718 |
-
"What's gate networks AI?",
|
| 719 |
-
"I am making a switch, please generating baking recipe?"
|
| 720 |
-
],
|
| 721 |
-
inputs=msg
|
| 722 |
-
)
|
| 723 |
-
|
| 724 |
-
# Upload Notes & Generate Quiz Tab
|
| 725 |
-
with gr.Tab("Upload Notes & Generate Quiz"):
|
| 726 |
-
with gr.Row():
|
| 727 |
-
with gr.Column(scale=2):
|
| 728 |
-
file_input = gr.File(label="Upload Lecture Notes (PDF, DOCX, PPTX) [Must be less than 6k of words]")
|
| 729 |
-
#generate_btn = gr.Button("Generate Quiz")
|
| 730 |
-
with gr.Column(scale=3):
|
| 731 |
-
quiz_output = gr.Textbox(label="Generated Quiz", lines=10)
|
| 732 |
-
|
| 733 |
-
|
| 734 |
-
# Introduction Video
|
| 735 |
-
with gr.Tab("Introduction Video"):
|
| 736 |
-
with gr.Row():
|
| 737 |
-
with gr.Column(scale=1):
|
| 738 |
-
#with gr.Column(scale=1): # Adjust scale for equal width
|
| 739 |
-
gr.Markdown("### Welcome to the Introduction Video") # Adding a heading
|
| 740 |
-
gr.Markdown("Music from Xu Mengyuan - China-O, musician Xu Mengyuan YUAN! | 徐梦圆 - China-O 音乐人徐梦圆YUAN! ") # Adding descriptive text
|
| 741 |
-
#gr.Video("https://github.com/lesterchia1/AI_tutor/raw/main/We%20not%20me%20video.mp4", label="Introduction Video")
|
| 742 |
-
gr.Video("https://huggingface.co/spaces/Lesterchia174/FPOC2_AI-Tutor_Chatbot/raw/main/We%20not%20me%20video.mp4", label="Introduction Video")
|
| 743 |
-
|
| 744 |
-
# Connect the button to the document processing function
|
| 745 |
-
audio_input.change(fn=transcribe_audio, inputs=audio_input, outputs=msg) # transcribe and fill the msg textbox
|
| 746 |
-
file_input.change(process_document, inputs=file_input, outputs=quiz_output)
|
| 747 |
-
|
| 748 |
-
|
| 749 |
-
# Launch the application
|
| 750 |
-
app.launch(share=True) # Set share=True to create a public link
|
| 751 |
-
|
| 752 |
-
# Add cleanup function to be triggered periodically (e.g., every time a button is clicked or after certain actions)
|
| 753 |
-
#demo.load(lambda: cleanup_old_files(directory="./", age_limit=60), inputs=[], outputs=[])
|
| 754 |
-
|
| 755 |
-
|
| 756 |
# Launch the AI chatbot
|
| 757 |
if __name__ == "__main__":
|
| 758 |
tutor_ai_chatbot()
|
|
|
|
| 443 |
app.launch(share=True) # Set share=True to create a public link
|
| 444 |
|
| 445 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 446 |
# Launch the AI chatbot
|
| 447 |
if __name__ == "__main__":
|
| 448 |
tutor_ai_chatbot()
|