Imarticuslearning commited on
Commit
7197a05
·
verified ·
1 Parent(s): 31906fc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +101 -66
app.py CHANGED
@@ -17,7 +17,8 @@ import traceback
17
  from streamlit_webrtc import webrtc_streamer, WebRtcMode
18
  from twilio.rest import Client
19
  import logging
20
- from audiorecorder import audiorecorder
 
21
 
22
 
23
  # ✅ MUST be the first Streamlit command
@@ -182,24 +183,17 @@ def generate_improvement_suggestions():
182
  ---
183
  {cleaned_initial_evaluation}
184
  ---
185
-
186
  Based on all this information, your task is to provide DETAILED and CONSTRUCTIVE suggestions for each question to help the candidate improve. Be supportive and encouraging.
187
-
188
  For EACH question, please provide:
189
  1. **How to Improve This Answer:** Specific, actionable advice on what the candidate could have added, clarified, or approached differently to make their answer better for their {level_string} level. Focus on 1-2 key improvement points.
190
  2. **Hints for an Ideal Answer:** Briefly mention 2-3 key concepts, terms, or elements that a strong answer (appropriate for their {level_string} level) would typically include. DO NOT provide a full model answer, just hints and pointers.
191
-
192
  Keep the tone positive and focused on learning.
193
-
194
  Structure your response clearly for each question. Example for one question:
195
-
196
  ---
197
  **Regarding Question X: "[Original Question Text Here]"**
198
-
199
  *How to Improve This Answer:*
200
  [Your specific suggestion 1 for improvement...]
201
  [Your specific suggestion 2 for improvement...]
202
-
203
  *Hints for an Ideal Answer (Key Points to Consider):*
204
  - Hint 1 or Key concept 1
205
  - Hint 2 or Key concept 2
@@ -289,15 +283,11 @@ def evaluate_answers():
289
  hr_prompt_template = f"""
290
  You are an experienced HR interview evaluator assessing a candidate's soft skills based on their answers to interview questions.
291
  The candidate's performance across ALL answers should inform your scores for the following parameters.
292
-
293
  **Parameters to Score (Assign a score from 1 to 5 for each):**
294
  {hr_prompt_parameter_list}
295
-
296
  After providing a score (1-5) for each of the above parameters, also write an **Overall Qualitative Feedback** section.
297
  This section should summarize the candidate's general soft skill strengths and areas for improvement, based on their communication, engagement, and professionalism throughout the interview.
298
-
299
  **REQUIRED OUTPUT FORMAT (Strictly Adhere):**
300
-
301
  **Parameter Scores (1-5):**
302
  Voice Modulation: [score]
303
  Confidence: [score]
@@ -308,7 +298,6 @@ def evaluate_answers():
308
  Basics of Grammar + SVA: [score]
309
  Persuasiveness: [score]
310
  Quality of Answers: [score]
311
-
312
  **Overall Qualitative Feedback:**
313
  [Your holistic qualitative feedback here. Be encouraging and constructive.]
314
  """
@@ -418,11 +407,9 @@ def evaluate_answers():
418
  {base_assessment_criteria_qualitative_non_hr}
419
  **YOUR RESPONSE MUST STRICTLY FOLLOW THIS FORMAT. PROVIDE SCORES FOR EACH QUESTION.**
420
  Output format:
421
-
422
  **Per-Question Scores:**
423
  Question 1 Score: [Score for Q1 out of 5]
424
  ... (repeat for all {num_answered_questions} questions provided)
425
-
426
  **Overall Evaluation Summary:**
427
  - Concept Understanding: [Overall qualitative feedback here]
428
  - Communication: [Overall qualitative feedback here]
@@ -502,7 +489,6 @@ def evaluate_answers():
502
  BEGINNER_PROMPT = """
503
  You are a friendly mock interview trainer conducting a **Beginner-level** spoken interview in the domain of **{domain}**.
504
  Ask basic verbal interview questions based on the candidate's input: **{input_text}**.
505
-
506
  Guidelines:
507
  - Ask simple conceptual questions.
508
  - Avoid jargon and complex examples.
@@ -511,7 +497,6 @@ Guidelines:
511
  Ensure the questions are clear, to the point, and suitable for a {difficulty_level}-level interview in {selected_domain}.
512
  **New Requirement:**
513
  🚫 **Do NOT repeat any questions from previous generations again and again.** Ensure all generated questions are unique and different from past sessions.
514
-
515
  **Guidelines:**
516
  ✅ Questions should focus on key concepts, best practices, and problem-solving within {selected_domain}.
517
  ✅ Ensure questions are direct, structured, and relevant to real-world applications.
@@ -522,7 +507,6 @@ Ensure the questions are clear, to the point, and suitable for a {difficulty_lev
522
  INTERMEDIATE_PROMPT = """
523
  You are a professional mock interviewer conducting an **Intermediate-level** spoken interview in the domain of **{domain}**.
524
  Ask moderately challenging verbal interview questions based on the candidate's input: **{input_text}**.
525
-
526
  Guidelines:
527
  - Use a mix of conceptual and real-world scenario questions.
528
  - Include light critical thinking.
@@ -530,7 +514,6 @@ Guidelines:
530
  Ensure the questions are clear, to the point, and suitable for a {difficulty_level}-level interview in {selected_domain}.
531
  **New Requirement:**
532
  🚫 **Do NOT repeat any questions from previous generations again and again.** Ensure all generated questions are unique and different from past sessions.
533
-
534
  **Guidelines:**
535
  ✅ Questions should focus on key concepts, best practices, and problem-solving within {selected_domain}.
536
  ✅ Ensure questions are direct, structured, and relevant to real-world applications.
@@ -541,15 +524,13 @@ Ensure the questions are clear, to the point, and suitable for a {difficulty_lev
541
  ADVANCED_PROMPT = """
542
  You are a strict mock interviewer conducting an **Advanced-level** spoken interview in the domain of **{domain}**.
543
  Ask deep, analytical, real-world scenario-based questions from the candidate's input: **{input_text}**.
544
-
545
  Guidelines:
546
  - Expect detailed, logical, well-structured answers.
547
- - Include challenging why and how based questions.
548
  - No need for code, but assume candidate has high expertise.
549
  Ensure the questions are clear, to the point, and suitable for a {difficulty_level}-level interview in {selected_domain}.
550
  **New Requirement:**
551
  🚫 **Do NOT repeat any questions from previous generations again and again.** Ensure all generated questions are unique and different from past sessions.
552
-
553
  **Guidelines:**
554
  ✅ Questions should focus on key concepts, best practices, and problem-solving within {selected_domain}.
555
  ✅ Ensure questions are direct, structured, and relevant to real-world applications.
@@ -588,7 +569,6 @@ st.markdown("""
588
  box-shadow: 0 0 0 0.2rem rgba(0,123,255,.5) !important;
589
  outline: none !important;
590
  }
591
-
592
  .timer-text {
593
  font-size: 1.3rem;
594
  font-weight: 600;
@@ -600,7 +580,6 @@ st.markdown("""
600
  50% {opacity: 0.4;}
601
  100% {opacity: 1;}
602
  }
603
-
604
  .summary-card {
605
  background-color: #f9f9f9;
606
  padding: 20px;
@@ -795,8 +774,8 @@ def get_ice_servers():
795
  account_sid = os.environ["TWILIO_ACCOUNT_SID"]
796
  auth_token = os.environ["TWILIO_AUTH_TOKEN"]
797
  except KeyError:
798
- logger.warning(
799
- "Twilio credentials are not set. Fallback to a free STUN server from Google." # noqa: E501
800
  )
801
  return [{"urls": ["stun:stun.l.google.com:19302"]}]
802
 
@@ -870,39 +849,101 @@ if st.session_state["generated_questions"]:
870
  if remaining > 0:
871
  st.markdown(f"<h4 class='timer-text'>🎙️ {remaining} seconds to answer...</h4>", unsafe_allow_html=True)
872
 
873
- audio_bytes = audiorecorder("Click to start recording", "Click to stop")
874
- if audio_bytes:
875
- wav_path = f"response_{idx}.wav"
876
- with open(wav_path, "wb") as f:
877
- f.write(audio_bytes)
878
- st.audio(wav_path, format="audio/wav")
879
-
880
-
881
- st.session_state.update({
882
- "record_phase": "listening",
883
- "response_file": wav_path,
884
- })
885
- st.rerun()
886
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
887
 
888
- else:
889
- st.warning("⚠️ No audio captured.")
890
- st.session_state["answers"].append({
891
- "question": question,
892
- "response": "[No response]"
893
- })
894
-
895
- st.session_state.update({
896
- "record_phase": "idle",
897
- "question_played": False,
898
- "current_question_index": idx + 1
899
  })
900
 
901
- if st.session_state["current_question_index"] == len(st.session_state["generated_questions"]):
902
- evaluate_answers()
903
- st.session_state["show_summary"] = True
904
 
905
- st.rerun()
906
 
907
  else:
908
  st.markdown("<div style='padding:10px; background:#fff3e0; border-left:5px solid orange;'>⚠️ <strong>No response detected.</strong> Moving to next question...</div>", unsafe_allow_html=True)
@@ -920,23 +961,17 @@ if st.session_state["generated_questions"]:
920
  st.rerun()
921
 
922
  elif st.session_state["record_phase"] == "listening":
923
- st.success("🎧 Review your response below:")
924
- st.audio(st.session_state["response_file"], format="audio/wav")
925
- if st.button("⏹️ Confirm & Next"):
926
- st.session_state["answers"].append({
927
- "question": question,
928
- "response_file": st.session_state["response_file"],
929
- "response_text": st.session_state.get("recorded_text", "")
930
- })
931
-
932
  st.session_state.update({
933
  "record_phase": "idle",
 
934
  "recording_started": False,
935
  "question_played": False,
936
  "question_start_time": 0.0,
937
  "current_question_index": idx + 1
938
  })
939
-
940
  if st.session_state["current_question_index"] == len(st.session_state["generated_questions"]):
941
  evaluate_answers()
942
  st.session_state["show_summary"] = True
@@ -1083,4 +1118,4 @@ if st.session_state.get("show_summary", False):
1083
  keys_to_fully_clear = list(st.session_state.keys())
1084
  for key_to_del_full in keys_to_fully_clear:
1085
  del st.session_state[key_to_del_full]
1086
-
 
17
  from streamlit_webrtc import webrtc_streamer, WebRtcMode
18
  from twilio.rest import Client
19
  import logging
20
+ import numpy as np
21
+ import soundfile as sf
22
 
23
 
24
  # ✅ MUST be the first Streamlit command
 
183
  ---
184
  {cleaned_initial_evaluation}
185
  ---
 
186
  Based on all this information, your task is to provide DETAILED and CONSTRUCTIVE suggestions for each question to help the candidate improve. Be supportive and encouraging.
 
187
  For EACH question, please provide:
188
  1. **How to Improve This Answer:** Specific, actionable advice on what the candidate could have added, clarified, or approached differently to make their answer better for their {level_string} level. Focus on 1-2 key improvement points.
189
  2. **Hints for an Ideal Answer:** Briefly mention 2-3 key concepts, terms, or elements that a strong answer (appropriate for their {level_string} level) would typically include. DO NOT provide a full model answer, just hints and pointers.
 
190
  Keep the tone positive and focused on learning.
 
191
  Structure your response clearly for each question. Example for one question:
 
192
  ---
193
  **Regarding Question X: "[Original Question Text Here]"**
 
194
  *How to Improve This Answer:*
195
  [Your specific suggestion 1 for improvement...]
196
  [Your specific suggestion 2 for improvement...]
 
197
  *Hints for an Ideal Answer (Key Points to Consider):*
198
  - Hint 1 or Key concept 1
199
  - Hint 2 or Key concept 2
 
283
  hr_prompt_template = f"""
284
  You are an experienced HR interview evaluator assessing a candidate's soft skills based on their answers to interview questions.
285
  The candidate's performance across ALL answers should inform your scores for the following parameters.
 
286
  **Parameters to Score (Assign a score from 1 to 5 for each):**
287
  {hr_prompt_parameter_list}
 
288
  After providing a score (1-5) for each of the above parameters, also write an **Overall Qualitative Feedback** section.
289
  This section should summarize the candidate's general soft skill strengths and areas for improvement, based on their communication, engagement, and professionalism throughout the interview.
 
290
  **REQUIRED OUTPUT FORMAT (Strictly Adhere):**
 
291
  **Parameter Scores (1-5):**
292
  Voice Modulation: [score]
293
  Confidence: [score]
 
298
  Basics of Grammar + SVA: [score]
299
  Persuasiveness: [score]
300
  Quality of Answers: [score]
 
301
  **Overall Qualitative Feedback:**
302
  [Your holistic qualitative feedback here. Be encouraging and constructive.]
303
  """
 
407
  {base_assessment_criteria_qualitative_non_hr}
408
  **YOUR RESPONSE MUST STRICTLY FOLLOW THIS FORMAT. PROVIDE SCORES FOR EACH QUESTION.**
409
  Output format:
 
410
  **Per-Question Scores:**
411
  Question 1 Score: [Score for Q1 out of 5]
412
  ... (repeat for all {num_answered_questions} questions provided)
 
413
  **Overall Evaluation Summary:**
414
  - Concept Understanding: [Overall qualitative feedback here]
415
  - Communication: [Overall qualitative feedback here]
 
489
  BEGINNER_PROMPT = """
490
  You are a friendly mock interview trainer conducting a **Beginner-level** spoken interview in the domain of **{domain}**.
491
  Ask basic verbal interview questions based on the candidate's input: **{input_text}**.
 
492
  Guidelines:
493
  - Ask simple conceptual questions.
494
  - Avoid jargon and complex examples.
 
497
  Ensure the questions are clear, to the point, and suitable for a {difficulty_level}-level interview in {selected_domain}.
498
  **New Requirement:**
499
  🚫 **Do NOT repeat any questions from previous generations again and again.** Ensure all generated questions are unique and different from past sessions.
 
500
  **Guidelines:**
501
  ✅ Questions should focus on key concepts, best practices, and problem-solving within {selected_domain}.
502
  ✅ Ensure questions are direct, structured, and relevant to real-world applications.
 
507
  INTERMEDIATE_PROMPT = """
508
  You are a professional mock interviewer conducting an **Intermediate-level** spoken interview in the domain of **{domain}**.
509
  Ask moderately challenging verbal interview questions based on the candidate's input: **{input_text}**.
 
510
  Guidelines:
511
  - Use a mix of conceptual and real-world scenario questions.
512
  - Include light critical thinking.
 
514
  Ensure the questions are clear, to the point, and suitable for a {difficulty_level}-level interview in {selected_domain}.
515
  **New Requirement:**
516
  🚫 **Do NOT repeat any questions from previous generations again and again.** Ensure all generated questions are unique and different from past sessions.
 
517
  **Guidelines:**
518
  ✅ Questions should focus on key concepts, best practices, and problem-solving within {selected_domain}.
519
  ✅ Ensure questions are direct, structured, and relevant to real-world applications.
 
524
  ADVANCED_PROMPT = """
525
  You are a strict mock interviewer conducting an **Advanced-level** spoken interview in the domain of **{domain}**.
526
  Ask deep, analytical, real-world scenario-based questions from the candidate's input: **{input_text}**.
 
527
  Guidelines:
528
  - Expect detailed, logical, well-structured answers.
529
+ - Include challenging "why" and "how" based questions.
530
  - No need for code, but assume candidate has high expertise.
531
  Ensure the questions are clear, to the point, and suitable for a {difficulty_level}-level interview in {selected_domain}.
532
  **New Requirement:**
533
  🚫 **Do NOT repeat any questions from previous generations again and again.** Ensure all generated questions are unique and different from past sessions.
 
534
  **Guidelines:**
535
  ✅ Questions should focus on key concepts, best practices, and problem-solving within {selected_domain}.
536
  ✅ Ensure questions are direct, structured, and relevant to real-world applications.
 
569
  box-shadow: 0 0 0 0.2rem rgba(0,123,255,.5) !important;
570
  outline: none !important;
571
  }
 
572
  .timer-text {
573
  font-size: 1.3rem;
574
  font-weight: 600;
 
580
  50% {opacity: 0.4;}
581
  100% {opacity: 1;}
582
  }
 
583
  .summary-card {
584
  background-color: #f9f9f9;
585
  padding: 20px;
 
774
  account_sid = os.environ["TWILIO_ACCOUNT_SID"]
775
  auth_token = os.environ["TWILIO_AUTH_TOKEN"]
776
  except KeyError:
777
+ st.warning(
778
+ "Twilio credentials are not set. Fallback to a free STUN server from Google."
779
  )
780
  return [{"urls": ["stun:stun.l.google.com:19302"]}]
781
 
 
849
  if remaining > 0:
850
  st.markdown(f"<h4 class='timer-text'>🎙️ {remaining} seconds to answer...</h4>", unsafe_allow_html=True)
851
 
852
+ # Create two columns for recording options
853
+ col1, col2 = st.columns(2)
854
+
855
+ with col1:
856
+ st.markdown("**🎤 Voice Recording (WebRTC):**")
857
+ # Try WebRTC recording
858
+ try:
859
+ webrtc_ctx = webrtc_streamer(
860
+ key = f"webrtc_{idx}",
861
+ mode=WebRtcMode.SENDONLY,
862
+ audio_receiver_size=1024,
863
+ media_stream_constraints={"audio": True, "video": False},
864
+ rtc_configuration={"iceServers": get_ice_servers()},
865
+ )
866
+
867
+ if webrtc_ctx.state.playing:
868
+ if st.button("⏹️ Stop Voice Recording", key=f"stop_voice_{idx}"):
869
+ wav_path = f"response_{idx}.wav"
870
+ try:
871
+ frames = webrtc_ctx.audio_receiver.get_frames(timeout=1)
872
+ except Exception as e:
873
+ st.error(f"⚠️ Audio capture error: {e}")
874
+ frames = []
875
+
876
+ if frames:
877
+ try:
878
+ pcm = np.concatenate([f.to_ndarray() for f in frames], axis=0)
879
+ sample_rate = frames[0].sample_rate
880
+ sf.write(wav_path, pcm, sample_rate)
881
+ st.audio(wav_path)
882
+ st.session_state["answers"].append({
883
+ "question": question,
884
+ "response_file": wav_path
885
+ })
886
+ except Exception as e:
887
+ st.error(f"⚠��� Error saving recording: {e}")
888
+ st.session_state["answers"].append({
889
+ "question": question,
890
+ "response": "[Error saving recording]"
891
+ })
892
+ else:
893
+ st.warning("⚠️ No audio captured.")
894
+ st.session_state["answers"].append({
895
+ "question": question,
896
+ "response": "[No response]"
897
+ })
898
+
899
+ st.session_state.update({
900
+ "record_phase": "idle",
901
+ "question_played": False,
902
+ "current_question_index": idx + 1
903
+ })
904
+
905
+ if st.session_state["current_question_index"] == len(st.session_state["generated_questions"]):
906
+ evaluate_answers()
907
+ st.session_state["show_summary"] = True
908
+
909
+ st.experimental_rerun()
910
+ except Exception as webrtc_error:
911
+ st.warning("⚠️ Voice recording not available")
912
+ st.info("This is common on Hugging Face Spaces")
913
+
914
+ with col2:
915
+ st.markdown("**✍️ Text Input (Alternative):**")
916
+ # Always show text input as alternative
917
+ text_response = st.text_area(
918
+ f"Type your answer:",
919
+ key=f"text_response_{idx}",
920
+ height=150,
921
+ placeholder="Enter your response here..."
922
+ )
923
+
924
+ if st.button("✅ Submit Text Answer", key=f"submit_text_{idx}"):
925
+ if text_response.strip():
926
+ st.session_state["answers"].append({
927
+ "question": question,
928
+ "response": text_response.strip()
929
+ })
930
+ else:
931
+ st.session_state["answers"].append({
932
+ "question": question,
933
+ "response": "[No text response provided]"
934
+ })
935
 
936
+ st.session_state.update({
937
+ "record_phase": "idle",
938
+ "question_played": False,
939
+ "current_question_index": idx + 1
 
 
 
 
 
 
 
940
  })
941
 
942
+ if st.session_state["current_question_index"] == len(st.session_state["generated_questions"]):
943
+ evaluate_answers()
944
+ st.session_state["show_summary"] = True
945
 
946
+ st.rerun()
947
 
948
  else:
949
  st.markdown("<div style='padding:10px; background:#fff3e0; border-left:5px solid orange;'>⚠️ <strong>No response detected.</strong> Moving to next question...</div>", unsafe_allow_html=True)
 
961
  st.rerun()
962
 
963
  elif st.session_state["record_phase"] == "listening":
964
+ st.success("🎧 Listening... You may stop when ready.")
965
+ if st.button("⏹️ Stop Recording"):
966
+ st.session_state["answers"].append({"question": question, "response": st.session_state["recorded_text"]})
 
 
 
 
 
 
967
  st.session_state.update({
968
  "record_phase": "idle",
969
+ "recorded_text": "",
970
  "recording_started": False,
971
  "question_played": False,
972
  "question_start_time": 0.0,
973
  "current_question_index": idx + 1
974
  })
 
975
  if st.session_state["current_question_index"] == len(st.session_state["generated_questions"]):
976
  evaluate_answers()
977
  st.session_state["show_summary"] = True
 
1118
  keys_to_fully_clear = list(st.session_state.keys())
1119
  for key_to_del_full in keys_to_fully_clear:
1120
  del st.session_state[key_to_del_full]
1121
+