gladguy commited on
Commit
3bbffd1
·
1 Parent(s): a6383fe

Add ElevenLabs TTS for VIVA questions

Browse files
Files changed (1) hide show
  1. app.py +67 -5
app.py CHANGED
@@ -13,11 +13,57 @@ load_dotenv()
13
 
14
  SERPAPI_KEY = os.getenv("SERPAPI_KEY")
15
  HYPERBOLIC_API_KEY = os.getenv("HYPERBOLIC_API_KEY")
 
16
 
17
  # Hyperbolic API configuration
18
  HYPERBOLIC_API_URL = "https://api.hyperbolic.xyz/v1/chat/completions"
19
  HYPERBOLIC_MODEL = "meta-llama/Llama-3.3-70B-Instruct"
20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  def is_anatomy_related(query: str) -> tuple[bool, str]:
22
  """
23
  Validate if the query is anatomy-related using the LLM.
@@ -516,6 +562,10 @@ def start_viva_mode(topic, image):
516
 
517
  # Start with question 1
518
  q1 = questions[0]
 
 
 
 
519
  return (
520
  gr.update(visible=True), # Show VIVA container
521
  f"**VIVA MODE ACTIVE** 📝\nTopic: {topic}", # viva_status
@@ -525,7 +575,8 @@ def start_viva_mode(topic, image):
525
  "", # Clear answer input
526
  "", # Clear feedback
527
  gr.update(interactive=True, value="Submit Answer"), # Enable submit button
528
- questions # Store questions in state
 
529
  )
530
 
531
 
@@ -544,13 +595,18 @@ def submit_viva_answer(answer, questions, current_q_idx):
544
  next_q = questions[next_idx]
545
  next_question = f"### Question {next_idx + 1} of 5\n\n**{next_q['question']}**"
546
  next_hint = f"💡 **Hint:** {next_q.get('hint', 'Think carefully about the anatomical relationships.')}"
 
 
 
 
547
  return (
548
  next_question, # Show next question
549
  next_hint, # Show next hint
550
  "", # Clear answer box
551
  feedback_text, # Show feedback for current answer
552
  gr.update(interactive=True, value="Submit Answer"), # Keep button enabled
553
- next_idx # Update question index
 
554
  )
555
  else:
556
  # VIVA complete
@@ -561,7 +617,8 @@ def submit_viva_answer(answer, questions, current_q_idx):
561
  "", # Clear answer
562
  feedback_text, # Final feedback
563
  gr.update(interactive=False, value="VIVA Complete"),
564
- next_idx
 
565
  )
566
 
567
 
@@ -701,6 +758,9 @@ with gr.Blocks(title="AnatomyBot - MBBS Anatomy Tutor") as demo:
701
  current_question_display = gr.Markdown("### Question will appear here")
702
  hint_display = gr.Markdown("💡 Hint will appear here")
703
 
 
 
 
704
  student_answer = gr.Textbox(
705
  label="Your Answer",
706
  placeholder="Type your answer here...",
@@ -739,7 +799,8 @@ with gr.Blocks(title="AnatomyBot - MBBS Anatomy Tutor") as demo:
739
  viva_container, viva_status, viva_image,
740
  current_question_display, hint_display,
741
  student_answer, feedback_display, submit_answer_btn,
742
- viva_questions_state
 
743
  ]
744
  ).then(
745
  fn=lambda: gr.update(selected=1), # Switch to VIVA tab
@@ -755,7 +816,8 @@ with gr.Blocks(title="AnatomyBot - MBBS Anatomy Tutor") as demo:
755
  inputs=[student_answer, viva_questions_state, current_question_idx],
756
  outputs=[
757
  current_question_display, hint_display, student_answer,
758
- feedback_display, submit_answer_btn, current_question_idx
 
759
  ]
760
  )
761
 
 
13
 
14
  SERPAPI_KEY = os.getenv("SERPAPI_KEY")
15
  HYPERBOLIC_API_KEY = os.getenv("HYPERBOLIC_API_KEY")
16
+ ELEVENLABS_API_KEY = os.getenv("ELEVENLABS_API_KEY")
17
 
18
  # Hyperbolic API configuration
19
  HYPERBOLIC_API_URL = "https://api.hyperbolic.xyz/v1/chat/completions"
20
  HYPERBOLIC_MODEL = "meta-llama/Llama-3.3-70B-Instruct"
21
 
22
+ # ElevenLabs API configuration
23
+ ELEVENLABS_API_URL = "https://api.elevenlabs.io/v1/text-to-speech"
24
+ # Using a standard "Professor" like voice (e.g., "Brian" - a deep, authoritative British voice, or similar)
25
+ # Voice ID for "Brian": nPczCjzI2devNBz1zQrb
26
+ ELEVENLABS_VOICE_ID = "nPczCjzI2devNBz1zQrb"
27
+
28
+ def generate_audio(text: str) -> str:
29
+ """
30
+ Generate audio from text using ElevenLabs API.
31
+ Returns path to temporary audio file or None if failed.
32
+ """
33
+ if not ELEVENLABS_API_KEY or not text:
34
+ return None
35
+
36
+ try:
37
+ url = f"{ELEVENLABS_API_URL}/{ELEVENLABS_VOICE_ID}"
38
+ headers = {
39
+ "Accept": "audio/mpeg",
40
+ "Content-Type": "application/json",
41
+ "xi-api-key": ELEVENLABS_API_KEY
42
+ }
43
+ data = {
44
+ "text": text,
45
+ "model_id": "eleven_monolingual_v1",
46
+ "voice_settings": {
47
+ "stability": 0.5,
48
+ "similarity_boost": 0.5
49
+ }
50
+ }
51
+
52
+ response = requests.post(url, json=data, headers=headers)
53
+
54
+ if response.status_code == 200:
55
+ # Save to temp file
56
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as f:
57
+ f.write(response.content)
58
+ return f.name
59
+ else:
60
+ print(f"ElevenLabs API Error: {response.text}")
61
+ return None
62
+
63
+ except Exception as e:
64
+ print(f"Error generating audio: {str(e)}")
65
+ return None
66
+
67
  def is_anatomy_related(query: str) -> tuple[bool, str]:
68
  """
69
  Validate if the query is anatomy-related using the LLM.
 
562
 
563
  # Start with question 1
564
  q1 = questions[0]
565
+
566
+ # Generate audio for first question
567
+ audio_path = generate_audio(q1['question'])
568
+
569
  return (
570
  gr.update(visible=True), # Show VIVA container
571
  f"**VIVA MODE ACTIVE** 📝\nTopic: {topic}", # viva_status
 
575
  "", # Clear answer input
576
  "", # Clear feedback
577
  gr.update(interactive=True, value="Submit Answer"), # Enable submit button
578
+ questions, # Store questions in state
579
+ audio_path # Return audio path
580
  )
581
 
582
 
 
595
  next_q = questions[next_idx]
596
  next_question = f"### Question {next_idx + 1} of 5\n\n**{next_q['question']}**"
597
  next_hint = f"💡 **Hint:** {next_q.get('hint', 'Think carefully about the anatomical relationships.')}"
598
+
599
+ # Generate audio for next question
600
+ audio_path = generate_audio(next_q['question'])
601
+
602
  return (
603
  next_question, # Show next question
604
  next_hint, # Show next hint
605
  "", # Clear answer box
606
  feedback_text, # Show feedback for current answer
607
  gr.update(interactive=True, value="Submit Answer"), # Keep button enabled
608
+ next_idx, # Update question index
609
+ audio_path # Play next question audio
610
  )
611
  else:
612
  # VIVA complete
 
617
  "", # Clear answer
618
  feedback_text, # Final feedback
619
  gr.update(interactive=False, value="VIVA Complete"),
620
+ next_idx,
621
+ None # No audio
622
  )
623
 
624
 
 
758
  current_question_display = gr.Markdown("### Question will appear here")
759
  hint_display = gr.Markdown("💡 Hint will appear here")
760
 
761
+ # Audio player for question
762
+ question_audio = gr.Audio(label="🔊 Listen to Question", autoplay=True, interactive=False)
763
+
764
  student_answer = gr.Textbox(
765
  label="Your Answer",
766
  placeholder="Type your answer here...",
 
799
  viva_container, viva_status, viva_image,
800
  current_question_display, hint_display,
801
  student_answer, feedback_display, submit_answer_btn,
802
+ viva_questions_state,
803
+ question_audio # Output audio
804
  ]
805
  ).then(
806
  fn=lambda: gr.update(selected=1), # Switch to VIVA tab
 
816
  inputs=[student_answer, viva_questions_state, current_question_idx],
817
  outputs=[
818
  current_question_display, hint_display, student_answer,
819
+ feedback_display, submit_answer_btn, current_question_idx,
820
+ question_audio # Output audio for next question
821
  ]
822
  )
823