ciaochris commited on
Commit
e67d493
·
verified ·
1 Parent(s): 5e9ce61

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -440
app.py CHANGED
@@ -469,446 +469,6 @@ def check_health():
469
 
470
  return problems
471
 
472
- if __name__ == "__main__":
473
- logging.info("Starting Vers3Dynamics Tutor application")
474
-
475
- health_issues = check_health()
476
- if health_issues:
477
- for issue in health_issues:
478
- logging.critical(f"Health check failed: {issue}")
479
- logging.critical("Application may not function properly due to the above issues")
480
-
481
- try:
482
- demo = create_interface()
483
- logging.info("Gradio interface created successfully")
484
-
485
- try:
486
- logging.info("Launching Gradio server")
487
- demo.queue()
488
- demo.launch(
489
- server_name="0.0.0.0",
490
- server_port=7860,
491
- debug=True
492
- )
493
- except Exception as e:
494
- logging.critical(f"Failed to launch Gradio server: {str(e)}")
495
- logging.critical(traceback.format_exc())
496
- except Exception as e:
497
- logging.critical(f"Failed to create Gradio interface: {str(e)}")
498
- logging.critical(traceback.format_exc()) client = Groq(api_key=api_key)
499
- logging.debug("Groq client initialized successfully")
500
-
501
- # Test the client with a simple API call
502
- try:
503
- models = client.models.list()
504
- logging.debug(f"API connection test successful. Available models: {[m.id for m in models.data][:3]}...")
505
- except Exception as e:
506
- logging.warning(f"API connection test failed: {e}")
507
- except Exception as e:
508
- logging.critical(f"Failed to initialize Groq client: {str(e)}")
509
- logging.critical(traceback.format_exc())
510
- raise EnvironmentError(f"Error initializing Groq API client: {str(e)}")
511
-
512
- def transcribe_audio(audio):
513
- """Transcribe audio file to text using Groq's API."""
514
- try:
515
- if audio is None:
516
- logging.debug("No audio input provided")
517
- return ""
518
-
519
- logging.debug(f"Audio input received: {type(audio)} - {audio}")
520
-
521
- audio_path = audio if isinstance(audio, str) else audio
522
- logging.debug(f"Audio path: {audio_path}")
523
- logging.debug(f"File exists: {os.path.exists(audio_path) if audio_path else False}")
524
-
525
- if audio_path and os.path.exists(audio_path):
526
- with open(audio_path, "rb") as audio_file:
527
- audio_data = audio_file.read()
528
-
529
- logging.debug(f"Audio file opened, size: {len(audio_data)} bytes")
530
-
531
- try:
532
- logging.debug("Sending audio to Groq API for transcription")
533
- transcription = client.audio.transcriptions.create(
534
- file=("audio.wav", audio_data),
535
- model="distil-whisper-large-v3-en",
536
- )
537
-
538
- logging.debug(f"Transcription response received: {transcription}")
539
-
540
- if hasattr(transcription, 'text'):
541
- result = transcription.text
542
- logging.debug(f"Transcription text: {result}")
543
- return result
544
- else:
545
- result = transcription.get('text', "Transcription succeeded but returned no text")
546
- logging.debug(f"Transcription text (alt format): {result}")
547
- return result
548
- except Exception as e:
549
- logging.error(f"Audio transcription API error: {str(e)}")
550
- return f"Audio transcription failed: {str(e)}"
551
- else:
552
- logging.warning(f"Audio file not found at path: {audio_path}")
553
- return "Audio file not found. Please try recording again."
554
- except Exception as e:
555
- logging.error(f"Error in transcription: {str(e)}")
556
- return f"Error in transcription: {str(e)}"
557
-
558
- def generate_tutor_output(subject: str, difficulty: str, student_input: str) -> Dict[str, str]:
559
- """Generate educational content based on student input."""
560
- try:
561
- logging.debug(f"Generating tutor output for subject: {subject}, difficulty: {difficulty}")
562
- logging.debug(f"Student input: {student_input}")
563
-
564
- # Define enhanced topics for all subjects
565
- topics = {
566
- "math": {
567
- "quadratic equation": "solving quadratic equations, including methods like factoring, using the quadratic formula, and completing the square",
568
- "pythagorean theorem": "the Pythagorean theorem, its proof, and applications in geometry and trigonometry",
569
- "calculus": "fundamental concepts of calculus, including limits, derivatives, and integrals",
570
- "linear algebra": "basics of linear algebra, including vectors, matrices, and linear transformations",
571
- "statistics": "key concepts in statistics, such as probability distributions, hypothesis testing, and regression analysis"
572
- },
573
- "science": {
574
- "photosynthesis": "the process of photosynthesis in plants, including light-dependent and light-independent reactions",
575
- "newton's laws": "Newton's laws of motion and their applications in classical mechanics",
576
- "periodic table": "the structure and organization of the periodic table of elements",
577
- "dna replication": "the process of DNA replication and its importance in cell division",
578
- "climate change": "the causes and effects of climate change, including global warming and its impact on ecosystems"
579
- },
580
- }
581
-
582
- subject_lower = subject.lower() if subject else "general"
583
- enhanced_topic = None
584
- for subj, subject_topics in topics.items():
585
- for topic, description in subject_topics.items():
586
- if topic.lower() in student_input.lower():
587
- enhanced_topic = description
588
- logging.debug(f"Enhanced topic detected: {topic}")
589
- break
590
- if enhanced_topic:
591
- break
592
-
593
- # Strengthened prompt with strict quiz formatting
594
- if enhanced_topic:
595
- logging.debug(f"Using enhanced topic: {enhanced_topic}")
596
- prompt = f"""
597
- You are an expert tutor specializing in {enhanced_topic} at the {difficulty} level. The student has asked: "{student_input}"
598
- Generate a detailed response as a valid JSON object with exactly these keys and content:
599
- - "lesson": A comprehensive lesson (3-4 paragraphs, at least 200 words) based on national educational standards, including historical context.
600
- - "example": A detailed step-by-step example problem with a full solution, formatted as: "Example Problem: [question]\nStep 1: [step]\nStep 2: [step]\nAnswer: [solution]".
601
- - "real_world_problem": A challenging real-world application of this concept (at least 100 words).
602
- - "quiz": A single string containing exactly 3 multiple-choice questions, each formatted as "1. [Question]\n a) [option]\n b) [option]\n c) [option]\n Correct answer: [letter]", separated by newlines.
603
- Ensure all sections are fully populated with relevant content. Return only the JSON object, enclosed in ```json``` markers, with no additional text outside the markers.
604
- """
605
- else:
606
- logging.debug(f"Using general subject: {subject_lower}")
607
- prompt = f"""
608
- You are an expert tutor in {subject_lower} at the {difficulty} level. The student has asked: "{student_input}"
609
- Generate a detailed response as a valid JSON object with exactly these keys and content:
610
- - "lesson": A descriptive, engaging lesson (3-4 paragraphs, at least 200 words) on the topic.
611
- - "example": An example problem with a full solution, formatted as: "Example Problem: [question]\nStep 1: [step]\nStep 2: [step]\nAnswer: [solution]".
612
- - "real_world_problem": A real-world problem solvable using the lesson concepts (at least 100 words).
613
- - "quiz": A single string containing exactly 3 multiple-choice questions, each formatted as "1. [Question]\n a) [option]\n b) [option]\n c) [option]\n Correct answer: [letter]", separated by newlines.
614
- Ensure all sections are fully populated with relevant content. Return only the JSON object, enclosed in ```json``` markers, with no additional text outside the markers.
615
- """
616
-
617
- # Model selection with fallback
618
- try:
619
- models = client.models.list()
620
- available_models = [m.id for m in models.data]
621
- logging.debug(f"Available models: {available_models}")
622
- target_model = "llama-3.3-70b-versatile" if "llama-3.3-70b-versatile" in available_models else available_models[0]
623
- except Exception:
624
- logging.warning("Could not fetch model list, using default model")
625
- target_model = "llama-3.3-70b-versatile"
626
-
627
- logging.debug(f"Sending prompt to model: {target_model}")
628
-
629
- completion = client.chat.completions.create(
630
- messages=[
631
- {
632
- "role": "system",
633
- "content": f"You are the world's best AI tutor, renowned for your ability to explain complex concepts clearly and engagingly. Your expertise in {subject_lower} is unparalleled, and you tailor your teaching to {difficulty} level students. Always return a complete response with all requested sections as a valid JSON object enclosed in ```json``` markers.",
634
- },
635
- {
636
- "role": "user",
637
- "content": prompt,
638
- }
639
- ],
640
- model=target_model,
641
- max_tokens=4000,
642
- )
643
-
644
- response_content = completion.choices[0].message.content
645
- logging.debug(f"Raw API response: {response_content}")
646
-
647
- # Extract JSON from response
648
- json_match = re.search(r'```json\s*([\s\S]*?)\s*```', response_content)
649
- if json_match:
650
- json_str = json_match.group(1)
651
- try:
652
- result = json.loads(json_str)
653
- logging.debug("Successfully parsed JSON from response")
654
- except json.JSONDecodeError as e:
655
- logging.warning(f"JSON parsing failed: {e}")
656
- result = None
657
- else:
658
- logging.warning("No JSON markers found in response")
659
- result = None
660
-
661
- # Improved fallback parsing
662
- if not result:
663
- logging.debug("Falling back to text parsing")
664
- sections = {
665
- "lesson": "",
666
- "example": "",
667
- "real_world_problem": "",
668
- "quiz": ""
669
- }
670
- lines = response_content.split('\n')
671
- current_section = None
672
- for line in lines:
673
- line = line.strip()
674
- if not line or line.startswith('```'):
675
- continue
676
- if any(kw in line.lower() for kw in ["lesson", "1.", "overview", "introduction"]):
677
- current_section = "lesson"
678
- sections[current_section] += line.replace("Lesson:", "").replace("1.", "").strip() + "\n"
679
- elif any(kw in line.lower() for kw in ["example", "2.", "problem", "solution"]):
680
- current_section = "example"
681
- sections[current_section] += line.replace("Example:", "").replace("2.", "").strip() + "\n"
682
- elif any(kw in line.lower() for kw in ["real-world", "application", "3.", "practical"]):
683
- current_section = "real_world_problem"
684
- sections[current_section] += line.replace("Real-World Application:", "").replace("Real-World Problem:", "").replace("3.", "").strip() + "\n"
685
- elif any(kw in line.lower() for kw in ["quiz", "4.", "question", "test"]):
686
- current_section = "quiz"
687
- sections[current_section] += line.replace("Quiz:", "").replace("4.", "").strip() + "\n"
688
- elif current_section:
689
- sections[current_section] += line + "\n"
690
-
691
- logging.debug(f"Parsed sections: {sections}")
692
- result = sections
693
-
694
- # Ensure all keys are present and non-empty
695
- for key in ["lesson", "example", "real_world_problem", "quiz"]:
696
- if key not in result or not result[key].strip():
697
- result[key] = f"No {key.replace('_', ' ')} provided - generation incomplete"
698
-
699
- return result
700
-
701
- except Exception as e:
702
- logging.error(f"Error in generate_tutor_output: {str(e)}")
703
- logging.error(traceback.format_exc())
704
- return {
705
- "lesson": f"Error: {str(e)}",
706
- "example": "",
707
- "real_world_problem": "",
708
- "quiz": ""
709
- }
710
-
711
- def process_output(output: Dict[str, Any]) -> Tuple[str, str, str, str]:
712
- """Process the output from generate_tutor_output into HTML format."""
713
- try:
714
- logging.debug(f"Processing output: {str(output)}")
715
-
716
- lesson = markdown2.markdown(output.get("lesson", "No lesson available"))
717
- example = markdown2.markdown(output.get("example", "No example available"))
718
- real_world = markdown2.markdown(output.get("real_world_problem", "No real-world application available"))
719
- quiz = markdown2.markdown(output.get("quiz", "No quiz available"))
720
-
721
- logging.debug("Output processed successfully")
722
- return lesson, example, real_world, quiz
723
- except Exception as e:
724
- logging.error(f"Error processing output: {str(e)}")
725
- logging.error(traceback.format_exc())
726
- return f"Error processing output: {str(e)}", "", "", ""
727
-
728
- def create_interface() -> gr.Blocks:
729
- """Create the Gradio interface."""
730
- logging.debug("Creating Gradio interface")
731
-
732
- with gr.Blocks(theme=gr.themes.Soft()) as demo:
733
- gr.Markdown("# 🎓 Vers3Dynamics Tutor: Your Personal Learning Companion")
734
-
735
- state = gr.State({"is_submitting": False})
736
-
737
- with gr.Row():
738
- with gr.Column(scale=2):
739
- subject = gr.Dropdown(
740
- ["Art History", "Computer Science", "Literature", "Math", "Music", "Science", "Social Science"],
741
- label="Subject",
742
- info="Choose the subject of your lesson",
743
- value="Math"
744
- )
745
- difficulty = gr.Radio(
746
- ["Primary", "Secondary", "Higher Education"],
747
- label="Difficulty Level",
748
- info="Select your proficiency level",
749
- value="Secondary"
750
- )
751
- student_input = gr.Textbox(
752
- placeholder="Type your topic or question here...",
753
- label="Type Your Question",
754
- info="Enter the topic you want to explore"
755
- )
756
- audio_input = gr.Audio(
757
- type="filepath",
758
- label="Speak Your Question",
759
- sources=["microphone"],
760
- format="wav"
761
- )
762
-
763
- with gr.Row():
764
- submit_button = gr.Button("📚 Teach Me", variant="primary")
765
- clear_button = gr.Button("🧹 Clear", variant="secondary")
766
-
767
- status_indicator = gr.Textbox(
768
- label="Status",
769
- value="Ready",
770
- interactive=False
771
- )
772
-
773
- with gr.Column(scale=3):
774
- transcription_output = gr.Textbox(label="Transcribed Audio (if provided)")
775
- lesson_output = gr.HTML(label="Lesson")
776
- example_output = gr.HTML(label="Example")
777
- real_world_output = gr.HTML(label="Real-World Application")
778
- quiz_output = gr.HTML(label="Quiz")
779
-
780
- gr.Markdown("""
781
- ### How to Use
782
- 1. Select a subject from the dropdown.
783
- 2. Choose your difficulty level.
784
- 3. Enter the topic or question you'd like to explore, or use the microphone to speak your question.
785
- 4. Click 'Teach Me' to receive a personalized lesson, example, real-world application, and quiz.
786
- 5. Review the AI-generated content to enhance your learning.
787
- 6. Use the 'Clear' button to reset all fields and start a new query.
788
- 7. Feel free to ask follow-up questions or explore new topics!
789
- Remember: This is an AI-powered educational tool. Always verify important information with authoritative sources.
790
- ### How to Record Your Voice
791
- 1. Look for the microphone icon in the "Or speak your question" section.
792
- 2. Click on the microphone icon to start recording.
793
- 3. Speak clearly and at a normal pace into your device's microphone.
794
- 4. Click the stop button (square icon) when you're done speaking.
795
- 5. You can play back your recording using the play button to check if it's clear.
796
- 6. If you're satisfied with the recording, click 'Teach Me' to process your spoken question.
797
- 7. If you're not happy with the recording, you can click the microphone icon again to start over.
798
- Note: Make sure your browser has permission to access your microphone. If you encounter any issues, try using a different browser or check your device's audio settings.
799
- """)
800
-
801
- def process_input(subject, difficulty, text_input, audio_input, state):
802
- """Process input from text or audio."""
803
- try:
804
- logging.info(f"Received inputs - subject: {subject}, difficulty: {difficulty}")
805
- logging.info(f"Text input: '{text_input}', Audio input: {audio_input}")
806
-
807
- subject = subject or "Math"
808
- difficulty = difficulty or "Secondary"
809
-
810
- if not text_input.strip() and not audio_input:
811
- return (
812
- {"is_submitting": False},
813
- "Ready",
814
- "No input provided",
815
- "Please provide a question to begin",
816
- "",
817
- "",
818
- ""
819
- )
820
-
821
- if text_input.strip():
822
- student_input = text_input.strip()
823
- transcribed_text = "Using text input"
824
- elif audio_input:
825
- transcribed_text = transcribe_audio(audio_input)
826
- student_input = transcribed_text
827
- if "error" in transcribed_text.lower():
828
- return (
829
- {"is_submitting": False},
830
- "Ready",
831
- transcribed_text,
832
- "Transcription error. Please try typing your question.",
833
- "",
834
- "",
835
- ""
836
- )
837
- else:
838
- return (
839
- {"is_submitting": False},
840
- "Ready",
841
- "No valid input",
842
- "Please provide a question",
843
- "",
844
- "",
845
- ""
846
- )
847
-
848
- tutor_output = generate_tutor_output(subject, difficulty, student_input)
849
- lesson, example, real_world, quiz = process_output(tutor_output)
850
-
851
- return (
852
- {"is_submitting": False},
853
- "Ready",
854
- transcribed_text,
855
- lesson,
856
- example,
857
- real_world,
858
- quiz
859
- )
860
-
861
- except Exception as e:
862
- logging.error(f"Error in process_input: {str(e)}")
863
- logging.error(traceback.format_exc())
864
- return (
865
- {"is_submitting": False},
866
- "Error",
867
- f"Error: {str(e)}",
868
- f"Error processing request: {str(e)}",
869
- "",
870
- "",
871
- ""
872
- )
873
-
874
- def clear_outputs():
875
- """Clear all inputs and outputs."""
876
- logging.debug("Clearing all outputs")
877
- return {"is_submitting": False}, "Ready", "", "", "", "", "", ""
878
-
879
- submit_button.click(
880
- fn=process_input,
881
- inputs=[subject, difficulty, student_input, audio_input, state],
882
- outputs=[state, status_indicator, transcription_output, lesson_output, example_output, real_world_output, quiz_output]
883
- )
884
-
885
- clear_button.click(
886
- fn=clear_outputs,
887
- inputs=[],
888
- outputs=[state, status_indicator, student_input, transcription_output, lesson_output, example_output, real_world_output, quiz_output]
889
- )
890
-
891
- return demo
892
-
893
- def check_health():
894
- """Perform a health check of required components."""
895
- problems = []
896
-
897
- if not os.getenv("GROQ_API_KEY"):
898
- problems.append("GROQ_API_KEY environment variable is not set")
899
-
900
- try:
901
- import markdown2
902
- except ImportError:
903
- problems.append("markdown2 package is not installed")
904
-
905
- try:
906
- client = Groq(api_key=os.getenv("GROQ_API_KEY") or "dummy_key_for_test")
907
- except Exception as e:
908
- problems.append(f"Groq client initialization failed: {str(e)}")
909
-
910
- return problems
911
-
912
  if __name__ == "__main__":
913
  logging.info("Starting Vers3Dynamics Tutor application")
914
 
 
469
 
470
  return problems
471
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
472
  if __name__ == "__main__":
473
  logging.info("Starting Vers3Dynamics Tutor application")
474