Imarticuslearning commited on
Commit
41f8fd2
Β·
verified Β·
1 Parent(s): 7197a05

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +64 -101
app.py CHANGED
@@ -17,8 +17,6 @@ import traceback
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,17 +181,24 @@ def generate_improvement_suggestions():
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,11 +288,15 @@ def evaluate_answers():
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,6 +307,7 @@ def evaluate_answers():
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,9 +417,11 @@ def evaluate_answers():
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,6 +501,7 @@ def evaluate_answers():
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,6 +510,7 @@ Guidelines:
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,6 +521,7 @@ Ensure the questions are clear, to the point, and suitable for a {difficulty_lev
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,6 +529,7 @@ Guidelines:
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,13 +540,15 @@ Ensure the questions are clear, to the point, and suitable for a {difficulty_lev
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,6 +587,7 @@ st.markdown("""
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,6 +599,7 @@ st.markdown("""
580
  50% {opacity: 0.4;}
581
  100% {opacity: 1;}
582
  }
 
583
  .summary-card {
584
  background-color: #f9f9f9;
585
  padding: 20px;
@@ -774,8 +794,8 @@ def get_ice_servers():
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,101 +869,38 @@ if st.session_state["generated_questions"]:
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,17 +918,23 @@ if st.session_state["generated_questions"]:
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,4 +1081,4 @@ if st.session_state.get("show_summary", False):
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
-
 
17
  from streamlit_webrtc import webrtc_streamer, WebRtcMode
18
  from twilio.rest import Client
19
  import logging
 
 
20
 
21
 
22
  # βœ… MUST be the first Streamlit command
 
181
  ---
182
  {cleaned_initial_evaluation}
183
  ---
184
+
185
  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.
186
+
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
+
191
  Keep the tone positive and focused on learning.
192
+
193
  Structure your response clearly for each question. Example for one question:
194
+
195
  ---
196
  **Regarding Question X: "[Original Question Text Here]"**
197
+
198
  *How to Improve This Answer:*
199
  [Your specific suggestion 1 for improvement...]
200
  [Your specific suggestion 2 for improvement...]
201
+
202
  *Hints for an Ideal Answer (Key Points to Consider):*
203
  - Hint 1 or Key concept 1
204
  - Hint 2 or Key concept 2
 
288
  hr_prompt_template = f"""
289
  You are an experienced HR interview evaluator assessing a candidate's soft skills based on their answers to interview questions.
290
  The candidate's performance across ALL answers should inform your scores for the following parameters.
291
+
292
  **Parameters to Score (Assign a score from 1 to 5 for each):**
293
  {hr_prompt_parameter_list}
294
+
295
  After providing a score (1-5) for each of the above parameters, also write an **Overall Qualitative Feedback** section.
296
  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.
297
+
298
  **REQUIRED OUTPUT FORMAT (Strictly Adhere):**
299
+
300
  **Parameter Scores (1-5):**
301
  Voice Modulation: [score]
302
  Confidence: [score]
 
307
  Basics of Grammar + SVA: [score]
308
  Persuasiveness: [score]
309
  Quality of Answers: [score]
310
+
311
  **Overall Qualitative Feedback:**
312
  [Your holistic qualitative feedback here. Be encouraging and constructive.]
313
  """
 
417
  {base_assessment_criteria_qualitative_non_hr}
418
  **YOUR RESPONSE MUST STRICTLY FOLLOW THIS FORMAT. PROVIDE SCORES FOR EACH QUESTION.**
419
  Output format:
420
+
421
  **Per-Question Scores:**
422
  Question 1 Score: [Score for Q1 out of 5]
423
  ... (repeat for all {num_answered_questions} questions provided)
424
+
425
  **Overall Evaluation Summary:**
426
  - Concept Understanding: [Overall qualitative feedback here]
427
  - Communication: [Overall qualitative feedback here]
 
501
  BEGINNER_PROMPT = """
502
  You are a friendly mock interview trainer conducting a **Beginner-level** spoken interview in the domain of **{domain}**.
503
  Ask basic verbal interview questions based on the candidate's input: **{input_text}**.
504
+
505
  Guidelines:
506
  - Ask simple conceptual questions.
507
  - Avoid jargon and complex examples.
 
510
  Ensure the questions are clear, to the point, and suitable for a {difficulty_level}-level interview in {selected_domain}.
511
  **New Requirement:**
512
  🚫 **Do NOT repeat any questions from previous generations again and again.** Ensure all generated questions are unique and different from past sessions.
513
+
514
  **Guidelines:**
515
  βœ… Questions should focus on key concepts, best practices, and problem-solving within {selected_domain}.
516
  βœ… Ensure questions are direct, structured, and relevant to real-world applications.
 
521
  INTERMEDIATE_PROMPT = """
522
  You are a professional mock interviewer conducting an **Intermediate-level** spoken interview in the domain of **{domain}**.
523
  Ask moderately challenging verbal interview questions based on the candidate's input: **{input_text}**.
524
+
525
  Guidelines:
526
  - Use a mix of conceptual and real-world scenario questions.
527
  - Include light critical thinking.
 
529
  Ensure the questions are clear, to the point, and suitable for a {difficulty_level}-level interview in {selected_domain}.
530
  **New Requirement:**
531
  🚫 **Do NOT repeat any questions from previous generations again and again.** Ensure all generated questions are unique and different from past sessions.
532
+
533
  **Guidelines:**
534
  βœ… Questions should focus on key concepts, best practices, and problem-solving within {selected_domain}.
535
  βœ… Ensure questions are direct, structured, and relevant to real-world applications.
 
540
  ADVANCED_PROMPT = """
541
  You are a strict mock interviewer conducting an **Advanced-level** spoken interview in the domain of **{domain}**.
542
  Ask deep, analytical, real-world scenario-based questions from the candidate's input: **{input_text}**.
543
+
544
  Guidelines:
545
  - Expect detailed, logical, well-structured answers.
546
+ - Include challenging β€œwhy” and β€œhow” based questions.
547
  - No need for code, but assume candidate has high expertise.
548
  Ensure the questions are clear, to the point, and suitable for a {difficulty_level}-level interview in {selected_domain}.
549
  **New Requirement:**
550
  🚫 **Do NOT repeat any questions from previous generations again and again.** Ensure all generated questions are unique and different from past sessions.
551
+
552
  **Guidelines:**
553
  βœ… Questions should focus on key concepts, best practices, and problem-solving within {selected_domain}.
554
  βœ… Ensure questions are direct, structured, and relevant to real-world applications.
 
587
  box-shadow: 0 0 0 0.2rem rgba(0,123,255,.5) !important;
588
  outline: none !important;
589
  }
590
+
591
  .timer-text {
592
  font-size: 1.3rem;
593
  font-weight: 600;
 
599
  50% {opacity: 0.4;}
600
  100% {opacity: 1;}
601
  }
602
+
603
  .summary-card {
604
  background-color: #f9f9f9;
605
  padding: 20px;
 
794
  account_sid = os.environ["TWILIO_ACCOUNT_SID"]
795
  auth_token = os.environ["TWILIO_AUTH_TOKEN"]
796
  except KeyError:
797
+ logger.warning(
798
+ "Twilio credentials are not set. Fallback to a free STUN server from Google." # noqa: E501
799
  )
800
  return [{"urls": ["stun:stun.l.google.com:19302"]}]
801
 
 
869
  if remaining > 0:
870
  st.markdown(f"<h4 class='timer-text'>πŸŽ™οΈ {remaining} seconds to answer...</h4>", unsafe_allow_html=True)
871
 
872
+ audio_value = st.audio_input("🎀 Tap to record β€” then stop when done", key=f"audio_{idx}")
873
+ if audio_value:
874
+ wav_path = f"response_{idx}.wav"
875
+ with open(wav_path, "wb") as f:
876
+ f.write(audio_value.getbuffer())
877
+ st.audio(wav_path, format="audio/wav")
878
+
879
+ st.session_state.update({
880
+ "record_phase": "listening",
881
+ "response_file": wav_path,
882
+ })
883
+ st.rerun()
884
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
885
 
886
+ else:
887
+ st.warning("⚠️ No audio captured.")
888
+ st.session_state["answers"].append({
889
+ "question": question,
890
+ "response": "[No response]"
891
+ })
892
+
893
+ st.session_state.update({
894
+ "record_phase": "idle",
895
+ "question_played": False,
896
+ "current_question_index": idx + 1
897
  })
898
 
899
+ if st.session_state["current_question_index"] == len(st.session_state["generated_questions"]):
900
+ evaluate_answers()
901
+ st.session_state["show_summary"] = True
902
 
903
+ st.rerun()
904
 
905
  else:
906
  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)
 
918
  st.rerun()
919
 
920
  elif st.session_state["record_phase"] == "listening":
921
+ st.success("🎧 Review your response below:")
922
+ st.audio(st.session_state["response_file"], format="audio/wav")
923
+ if st.button("⏹️ Confirm & Next"):
924
+ st.session_state["answers"].append({
925
+ "question": question,
926
+ "response_file": st.session_state["response_file"],
927
+ "response_text": st.session_state.get("recorded_text", "")
928
+ })
929
+
930
  st.session_state.update({
931
  "record_phase": "idle",
 
932
  "recording_started": False,
933
  "question_played": False,
934
  "question_start_time": 0.0,
935
  "current_question_index": idx + 1
936
  })
937
+
938
  if st.session_state["current_question_index"] == len(st.session_state["generated_questions"]):
939
  evaluate_answers()
940
  st.session_state["show_summary"] = True
 
1081
  keys_to_fully_clear = list(st.session_state.keys())
1082
  for key_to_del_full in keys_to_fully_clear:
1083
  del st.session_state[key_to_del_full]
1084
+