ruslanmv commited on
Commit
5798cfc
·
1 Parent(s): 6b288db

First commit

Browse files
.gitignore ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+
2
+ .env
3
+ /__pycache__
4
+ /.gradio
5
+ /hr_interviewer
6
+ /knowledge
7
+ /reports
8
+ *.json
9
+ *.json
ai_config.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from io import BytesIO
2
+
3
+ from langchain_openai import ChatOpenAI
4
+ from openai import OpenAI
5
+ import tiktoken
6
+ import os
7
+ from dotenv import load_dotenv
8
+ import os
9
+ from langchain.text_splitter import MarkdownHeaderTextSplitter
10
+
11
+ # Load environment variables from .env file
12
+ load_dotenv()
13
+
14
+ # IBM Connection Parameters (using loaded env variables)
15
+ openai_api_key = os.getenv("OPENAI_API_KEY")
16
+
17
+ def n_of_questions():
18
+ n_of_questions = 5
19
+ return n_of_questions
20
+
21
+ #openai_api_key = os.environ.get("openai_api_key")
22
+
23
+ model = "gpt-3.5-turbo-1106"
24
+
25
+ def load_model(openai_api_key):
26
+ return ChatOpenAI(
27
+ model_name=model,
28
+ openai_api_key=openai_api_key,
29
+ temperature=0.5
30
+ )
31
+
32
+ # Initialize the OpenAI client with the API key
33
+ client = OpenAI(api_key=openai_api_key)
34
+
35
+
36
+ def convert_text_to_speech(text, output, voice):
37
+ try:
38
+ # Convert the final text to speech
39
+ response = client.audio.speech.create(model="tts-1-hd", voice=voice, input=text)
40
+
41
+ if isinstance(output, BytesIO):
42
+ # If output is a BytesIO object, write directly to it
43
+ for chunk in response.iter_bytes():
44
+ output.write(chunk)
45
+ else:
46
+ # If output is a file path, open and write to it
47
+ with open(output, 'wb') as f:
48
+ for chunk in response.iter_bytes():
49
+ f.write(chunk)
50
+
51
+ except Exception as e:
52
+ print(f"An error occurred: {e}")
53
+ # Fallback in case of error
54
+ response = client.audio.speech.create(model="tts-1-hd", voice=voice, input='Here is my Report.')
55
+
56
+ if isinstance(output, BytesIO):
57
+ for chunk in response.iter_bytes():
58
+ output.write(chunk)
59
+ else:
60
+ with open(output, 'wb') as f:
61
+ for chunk in response.iter_bytes():
62
+ f.write(chunk)
63
+
64
+
65
+ def transcribe_audio(audio):
66
+ try:
67
+ audio_file = open(audio, "rb")
68
+ transcription = client.audio.transcriptions.create(
69
+ model="whisper-1",
70
+ file=audio_file
71
+ )
72
+ return transcription.text
73
+ except Exception as e:
74
+ return "Audio transcription failed. Please try again."
75
+
76
+
77
+ def split_text_with_langchain(text, headers_to_split_on):
78
+ markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
79
+ docs = markdown_splitter.create_documents([text])
80
+ return docs
app-v1.py ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import grad as gr
2
+ import tempfile
3
+ import os
4
+ import json
5
+ from io import BytesIO
6
+ from gpt import read_questions_from_json, conduct_interview_with_user_input # Import from gpt.py
7
+ from ai_config import convert_text_to_speech, load_model
8
+ from knowledge_retrieval import setup_knowledge_retrieval, generate_report
9
+ from prompt_instructions import get_interview_initial_message_hr, get_default_hr_questions
10
+ from settings import language
11
+ from utils import save_interview_history
12
+ from questions import generate_and_save_questions_from_pdf
13
+
14
+ CONFIG_PATH = "config.json"
15
+ QUESTIONS_PATH = "questions.json"
16
+
17
+ class InterviewState:
18
+ def __init__(self):
19
+ self.reset()
20
+
21
+ def reset(self, voice="alloy"):
22
+ self.question_count = 0
23
+ self.interview_history = []
24
+ self.selected_interviewer = voice
25
+ self.interview_finished = False
26
+ self.audio_enabled = True
27
+ self.temp_audio_files = []
28
+ self.admin_authenticated = False
29
+ self.config = load_config()
30
+ self.technical_questions = []
31
+
32
+ def load_config():
33
+ if os.path.exists(CONFIG_PATH):
34
+ with open(CONFIG_PATH, "r") as f:
35
+ return json.load(f)
36
+ else:
37
+ return {"n_of_questions": 5, "type_of_interview": "Standard"}
38
+
39
+ def save_config(config):
40
+ with open(CONFIG_PATH, "w") as f:
41
+ json.dump(config, f, indent=4)
42
+
43
+ def save_questions(questions):
44
+ with open(QUESTIONS_PATH, "w") as f:
45
+ json.dump(questions, f, indent=4)
46
+
47
+ def load_questions():
48
+ if os.path.exists(QUESTIONS_PATH):
49
+ with open(QUESTIONS_PATH, "r") as f:
50
+ return json.load(f)
51
+ return []
52
+
53
+ interview_state = InterviewState()
54
+
55
+ # Load knowledge base and generate technical questions
56
+ def load_knowledge_base(file_input, n_questions_to_generate):
57
+ if not file_input:
58
+ return "❌ Error: No document uploaded."
59
+
60
+ llm = load_model(os.getenv("OPENAI_API_KEY"))
61
+ try:
62
+ _, _, retriever = setup_knowledge_retrieval(llm, language=language, file_path=file_input)
63
+ technical_questions = generate_and_save_questions_from_pdf(file_input, n_questions_to_generate)
64
+ save_questions(technical_questions)
65
+
66
+ return f"✅ {len(technical_questions)} technical questions generated and saved."
67
+ except Exception as e:
68
+ return f"❌ Error: {e}"
69
+
70
+ def reset_interview_action(voice):
71
+ interview_state.reset(voice)
72
+ config = interview_state.config
73
+ n_of_questions = config.get("n_of_questions", 5)
74
+ initial_message = {
75
+ "role": "assistant",
76
+ "content": get_interview_initial_message_hr(n_of_questions)
77
+ }
78
+
79
+ if config["type_of_interview"] == "Technical":
80
+ technical_questions = load_questions()
81
+
82
+ if not technical_questions:
83
+ return [{"role": "assistant", "content": "No technical questions available. Please contact the admin."}], None, gr.Textbox(interactive=False)
84
+
85
+ # Prepare for displaying questions one at a time
86
+ interview_state.technical_questions = technical_questions
87
+ interview_state.question_count = 0
88
+ return (
89
+ [initial_message],
90
+ None,
91
+ gr.Textbox(interactive=True, placeholder="Technical interview started. Answer the questions below...")
92
+ )
93
+ else:
94
+ initial_audio_buffer = BytesIO()
95
+ convert_text_to_speech(initial_message["content"], initial_audio_buffer, voice)
96
+ initial_audio_buffer.seek(0)
97
+
98
+ with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as temp_file:
99
+ temp_audio_path = temp_file.name
100
+ temp_file.write(initial_audio_buffer.getvalue())
101
+
102
+ interview_state.temp_audio_files.append(temp_audio_path)
103
+ return (
104
+ [initial_message],
105
+ gr.Audio(value=temp_audio_path, autoplay=True),
106
+ gr.Textbox(interactive=True, placeholder="Type your answer here...")
107
+ )
108
+
109
+ def start_interview():
110
+ interview_config = load_config()
111
+ interview_state.config = interview_config
112
+ return reset_interview_action(interview_state.selected_interviewer)
113
+
114
+ def update_config(n_of_questions, interview_type):
115
+ config = {
116
+ "n_of_questions": int(n_of_questions),
117
+ "type_of_interview": interview_type
118
+ }
119
+ save_config(config)
120
+ return "✅ Configuration updated successfully."
121
+
122
+ def update_knowledge_base_and_generate_questions(file_input, n_questions_to_generate):
123
+ return load_knowledge_base(file_input, n_questions_to_generate)
124
+
125
+ def bot_response(chatbot, message):
126
+ config = interview_state.config
127
+
128
+ if config["type_of_interview"] == "Standard":
129
+ response = get_default_hr_questions(interview_state.question_count + 1)
130
+ chatbot.append({"role": "assistant", "content": response})
131
+ interview_state.question_count += 1
132
+ else:
133
+ if interview_state.question_count < len(interview_state.technical_questions):
134
+ question = interview_state.technical_questions[interview_state.question_count]
135
+ chatbot.append({"role": "assistant", "content": f"Q{interview_state.question_count + 1}: {question}"})
136
+ interview_state.question_count += 1
137
+ chatbot.append({"role": "user", "content": message}) # Append user response after the question
138
+ else:
139
+ chatbot.append({"role": "assistant", "content": "All questions completed."})
140
+ interview_state.interview_finished = True
141
+
142
+ if interview_state.interview_finished:
143
+ report_content = generate_report(interview_state.report_chain, [msg["content"] for msg in chatbot if msg["role"] == "user"], language)
144
+ txt_path = save_interview_history([msg["content"] for msg in chatbot], language)
145
+ return chatbot, gr.File(visible=True, value=txt_path)
146
+
147
+ return chatbot, None
148
+
149
+ def create_app():
150
+ with gr.Blocks(title="AI HR Interviewer") as demo:
151
+ gr.Markdown("## 🧑‍💼 HR Interviewer Application")
152
+
153
+ with gr.Row():
154
+ user_role = gr.Dropdown(choices=["Admin", "Candidate"], label="Select User Role", value="Candidate")
155
+ password_input = gr.Textbox(label="Enter Admin Password", type="password", visible=False)
156
+ login_button = gr.Button("Login", visible=False)
157
+ password_status = gr.Markdown("", visible=False)
158
+
159
+ admin_tab = gr.Tab("Admin Settings", visible=False)
160
+ interview_tab = gr.Tab("Interview", visible=True)
161
+
162
+ user_role.change(lambda role: (gr.update(visible=role == "Admin"),) * 2, inputs=[user_role], outputs=[password_input, login_button])
163
+
164
+ def authenticate_admin(password):
165
+ if password == "password1":
166
+ interview_state.admin_authenticated = True
167
+ return "✅ Password correct", gr.update(visible=False), gr.update(visible=True)
168
+ else:
169
+ return "❌ Incorrect password.", gr.update(visible=True), gr.update(visible=False)
170
+
171
+ login_button.click(authenticate_admin, inputs=[password_input], outputs=[password_status, password_input, admin_tab])
172
+
173
+ with admin_tab:
174
+ file_input = gr.File(label="Upload Knowledge Base Document", type="filepath")
175
+ n_questions_input = gr.Number(label="Number of Questions", value=10)
176
+ update_button = gr.Button("Update Knowledge Base")
177
+ update_status = gr.Markdown("")
178
+ update_button.click(update_knowledge_base_and_generate_questions, inputs=[file_input, n_questions_input], outputs=[update_status])
179
+
180
+ n_questions_interview_input = gr.Number(label="Number of Questions for Interview", value=5)
181
+ interview_type_input = gr.Dropdown(choices=["Standard", "Technical"], label="Type of Interview", value="Standard")
182
+ save_config_button = gr.Button("Save Configuration")
183
+ config_status = gr.Markdown("")
184
+ save_config_button.click(update_config, inputs=[n_questions_interview_input, interview_type_input], outputs=[config_status])
185
+
186
+ with interview_tab:
187
+ reset_button = gr.Button("Start Interview")
188
+ chatbot = gr.Chatbot(label="Chat Session", type="messages")
189
+ msg_input = gr.Textbox(label="💬 Type your message here...", interactive=True)
190
+ send_button = gr.Button("Send")
191
+
192
+ reset_button.click(start_interview, inputs=[], outputs=[chatbot])
193
+
194
+ msg_input.submit(lambda msg, hist: ("", hist + [{"role": "user", "content": msg}]), inputs=[msg_input, chatbot], outputs=[msg_input, chatbot]).then(
195
+ bot_response, [chatbot, msg_input], [chatbot]
196
+ )
197
+
198
+ send_button.click(lambda msg, hist: ("", hist + [{"role": "user", "content": msg}]), inputs=[msg_input, chatbot], outputs=[msg_input, chatbot]).then(
199
+ bot_response, [chatbot, msg_input], [chatbot]
200
+ )
201
+
202
+ return demo
203
+
204
+ def cleanup():
205
+ for audio_file in interview_state.temp_audio_files:
206
+ if os.path.exists(audio_file):
207
+ os.unlink(audio_file)
208
+
209
+ if __name__ == "__main__":
210
+ app = create_app()
211
+ try:
212
+ app.launch(server_name="0.0.0.0", server_port=7860, debug=True)
213
+ finally:
214
+ cleanup()
app.py ADDED
@@ -0,0 +1,551 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import tempfile
3
+ import os
4
+ import json
5
+ from io import BytesIO
6
+ import subprocess
7
+ from collections import deque
8
+ from dotenv import load_dotenv
9
+ from langchain_openai import ChatOpenAI
10
+ from langchain.schema import HumanMessage, SystemMessage
11
+
12
+ # Imports from other modules
13
+ from generatorgr import (
14
+ generate_and_save_questions as generate_questions_manager,
15
+ update_max_questions,
16
+ )
17
+ from generator import (
18
+ PROFESSIONS_FILE,
19
+ TYPES_FILE,
20
+ OUTPUT_FILE,
21
+ load_json_data,
22
+ generate_questions,
23
+ )
24
+ from splitgpt import (
25
+ generate_and_save_questions_from_pdf3
26
+ )
27
+
28
+ # Placeholder imports for the manager application
29
+ # Ensure these modules and functions are correctly implemented in their respective files
30
+ from ai_config import convert_text_to_speech, load_model # Placeholder, needs implementation
31
+ from knowledge_retrieval import (
32
+ setup_knowledge_retrieval,
33
+ get_next_response,
34
+ generate_report,
35
+ get_initial_question,
36
+ ) # Placeholder, needs implementation
37
+ from prompt_instructions import (
38
+ get_interview_initial_message_hr,
39
+ get_default_hr_questions,
40
+ ) # Placeholder, needs implementation
41
+ from settings import language # Placeholder, needs implementation
42
+ from utils import save_interview_history # Placeholder, needs implementation
43
+
44
+
45
+ class InterviewState:
46
+ def __init__(self):
47
+ self.reset()
48
+
49
+ def reset(self, voice="alloy"):
50
+ self.question_count = 0
51
+ self.interview_history = []
52
+ self.selected_interviewer = voice
53
+ self.interview_finished = False
54
+ self.audio_enabled = True
55
+ self.temp_audio_files = []
56
+ self.initial_audio_path = None
57
+ self.admin_authenticated = False
58
+ self.document_loaded = False
59
+ self.knowledge_retrieval_setup = False
60
+ self.interview_chain = None
61
+ self.report_chain = None
62
+ self.current_questions = [] # Store the current set of questions
63
+
64
+ def get_voice_setting(self):
65
+ return self.selected_interviewer
66
+
67
+
68
+ interview_state = InterviewState()
69
+
70
+
71
+ def reset_interview_action(voice):
72
+ interview_state.reset(voice)
73
+ n_of_questions = 5 # Default questions
74
+ print(f"[DEBUG] Interview reset. Voice: {voice}")
75
+
76
+ initial_message = {
77
+ "role": "assistant",
78
+ "content": get_interview_initial_message_hr(n_of_questions),
79
+ }
80
+ print(f"[DEBUG] Interview reset. Voice: {voice}")
81
+ # Convert the initial message to speech
82
+ initial_audio_buffer = BytesIO()
83
+ convert_text_to_speech(initial_message["content"], initial_audio_buffer, voice)
84
+ initial_audio_buffer.seek(0)
85
+
86
+ with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as temp_file:
87
+ temp_audio_path = temp_file.name
88
+ temp_file.write(initial_audio_buffer.getvalue())
89
+
90
+ interview_state.temp_audio_files.append(temp_audio_path)
91
+ print(f"[DEBUG] Audio file saved at {temp_audio_path}")
92
+
93
+ return (
94
+ [initial_message],
95
+ gr.Audio(value=temp_audio_path, autoplay=True),
96
+ gr.Textbox(interactive=True),
97
+ )
98
+
99
+
100
+ def start_interview():
101
+
102
+ return reset_interview_action(interview_state.selected_interviewer)
103
+
104
+
105
+ import os
106
+ from datetime import datetime
107
+
108
+ def store_interview_report(report_content, folder_path="reports"):
109
+ """
110
+ Stores the interview report in a specified reports folder.
111
+
112
+ Args:
113
+ report_content (str): The content of the report to store.
114
+ folder_path (str): The directory where the report will be saved.
115
+
116
+ Returns:
117
+ str: The file path of the saved report.
118
+ """
119
+ os.makedirs(folder_path, exist_ok=True)
120
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
121
+ file_path = os.path.join(folder_path, f"interview_report_{timestamp}.txt")
122
+
123
+ try:
124
+ with open(file_path, "w", encoding="utf-8") as file:
125
+ file.write(report_content)
126
+ print(f"[DEBUG] Interview report saved at {file_path}")
127
+ return file_path
128
+ except Exception as e:
129
+ print(f"[ERROR] Failed to save interview report: {e}")
130
+ return None
131
+
132
+
133
+ def bot_response(chatbot, message):
134
+ n_of_questions = 5 # Default value
135
+ interview_state.question_count += 1
136
+ voice = interview_state.get_voice_setting()
137
+
138
+ if interview_state.question_count == 1:
139
+ response = get_initial_question(interview_state.interview_chain)
140
+ else:
141
+ response = get_next_response(
142
+ interview_state.interview_chain,
143
+ message["content"],
144
+ [msg["content"] for msg in chatbot if msg.get("role") == "user"],
145
+ interview_state.question_count,
146
+ )
147
+
148
+ # Generate and save the bot's audio response
149
+ audio_buffer = BytesIO()
150
+ convert_text_to_speech(response, audio_buffer, voice)
151
+ audio_buffer.seek(0)
152
+ with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as temp_file:
153
+ temp_audio_path = temp_file.name
154
+ temp_file.write(audio_buffer.getvalue())
155
+
156
+ interview_state.temp_audio_files.append(temp_audio_path)
157
+ chatbot.append({"role": "assistant", "content": response})
158
+
159
+ # Check if the interview is finished
160
+ if interview_state.question_count >= n_of_questions:
161
+ interview_state.interview_finished = True
162
+ conclusion_message = (
163
+ "Thank you for your time. The interview is complete. Please review your report."
164
+ )
165
+
166
+ # Generate conclusion audio message
167
+ conclusion_audio_buffer = BytesIO()
168
+ convert_text_to_speech(conclusion_message, conclusion_audio_buffer, voice)
169
+ conclusion_audio_buffer.seek(0)
170
+ with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as temp_conclusion_file:
171
+ temp_conclusion_audio_path = temp_conclusion_file.name
172
+ temp_conclusion_file.write(conclusion_audio_buffer.getvalue())
173
+ interview_state.temp_audio_files.append(temp_conclusion_audio_path)
174
+
175
+ # Append conclusion message to chatbot history
176
+ chatbot.append({"role": "system", "content": conclusion_message})
177
+
178
+ # Generate the HR report content
179
+ report_content = generate_report(
180
+ interview_state.report_chain,
181
+ [msg["content"] for msg in chatbot],
182
+ language,
183
+ )
184
+
185
+ # Save the interview history
186
+ txt_path = save_interview_history(
187
+ [msg["content"] for msg in chatbot], language
188
+ )
189
+ print(f"[DEBUG] Interview history saved at: {txt_path}")
190
+
191
+ # Save the report to the reports folder
192
+ report_file_path = store_interview_report(report_content)
193
+ print(f"[DEBUG] Interview report saved at: {report_file_path}")
194
+
195
+ return chatbot, gr.File(visible=True, value=txt_path), gr.Audio(value=temp_conclusion_audio_path, autoplay=True)
196
+
197
+ return chatbot, gr.Audio(value=temp_audio_path, autoplay=True)
198
+
199
+
200
+ # --- Candidate Interview Implementation ---
201
+ load_dotenv()
202
+
203
+ # Function to read questions from JSON
204
+ def read_questions_from_json(file_path):
205
+ if not os.path.exists(file_path):
206
+ raise FileNotFoundError(f"The file '{file_path}' does not exist.")
207
+
208
+ with open(file_path, 'r') as f:
209
+ questions_list = json.load(f)
210
+
211
+ if not questions_list:
212
+ raise ValueError("The JSON file is empty or has invalid content.")
213
+
214
+ return questions_list
215
+
216
+ # Conduct interview and handle user input
217
+
218
+ import os
219
+ import json
220
+ from io import BytesIO
221
+ import tempfile
222
+ from collections import deque
223
+ from langchain_openai import ChatOpenAI
224
+ from langchain.schema import HumanMessage, SystemMessage
225
+
226
+ # Placeholder imports (ensure these are correctly implemented)
227
+ from ai_config import convert_text_to_speech # For text-to-speech
228
+ from knowledge_retrieval import generate_report # For report generation
229
+ from utils import save_interview_history # For saving interview history
230
+ from settings import language # Placeholder, needs implementation
231
+
232
+ # Assuming you have interview_state defined elsewhere and accessible here
233
+ # interview_state = InterviewState() # You might need to initialize this or pass it as a parameter
234
+
235
+ def conduct_interview(questions, language="English", history_limit=5):
236
+ openai_api_key = os.getenv("OPENAI_API_KEY")
237
+ if not openai_api_key:
238
+ raise RuntimeError(
239
+ "OpenAI API key not found. Please add it to your .env file as OPENAI_API_KEY."
240
+ )
241
+
242
+ chat = ChatOpenAI(
243
+ openai_api_key=openai_api_key, model="gpt-4", temperature=0.7, max_tokens=750
244
+ )
245
+
246
+ conversation_history = deque(maxlen=history_limit)
247
+ system_prompt = (
248
+ f"You are Sarah, an empathetic HR interviewer conducting a technical interview in {language}. "
249
+ "Respond to user follow-up questions politely and concisely. If the user is confused, provide clear clarification."
250
+ )
251
+
252
+ interview_data = []
253
+ current_question_index = [0]
254
+
255
+ initial_message = (
256
+ "👋 Hi there, I'm Sarah, your friendly AI HR assistant! "
257
+ "I'll guide you through a series of interview questions to learn more about you. "
258
+ "Take your time and answer each question thoughtfully."
259
+ )
260
+
261
+ def interview_step(user_input, history):
262
+
263
+ if user_input.lower() in ["exit", "quit"]:
264
+ history.append(
265
+ {
266
+ "role": "assistant",
267
+ "content": "The interview has ended at your request. Thank you for your time!",
268
+ }
269
+ )
270
+ return history, ""
271
+
272
+ question_text = questions[current_question_index[0]]
273
+ history_content = "\n".join(
274
+ [
275
+ f"Q: {entry['question']}\nA: {entry['answer']}"
276
+ for entry in conversation_history
277
+ ]
278
+ )
279
+ combined_prompt = (
280
+ f"{system_prompt}\n\nPrevious conversation history:\n{history_content}\n\n"
281
+ f"Current question: {question_text}\nUser's input: {user_input}\n\n"
282
+ "Respond in a warm and conversational way, offering natural follow-ups if needed."
283
+ )
284
+
285
+ messages = [
286
+ SystemMessage(content=system_prompt),
287
+ HumanMessage(content=combined_prompt),
288
+ ]
289
+
290
+ response = chat.invoke(messages)
291
+ response_content = response.content.strip()
292
+
293
+ # --- Integrated bot_response functionality starts here ---
294
+
295
+ interview_state.question_count += 1
296
+ voice = interview_state.get_voice_setting() # Get voice setting
297
+
298
+ # Generate and save the bot's audio response
299
+ audio_buffer = BytesIO()
300
+ convert_text_to_speech(response_content, audio_buffer, voice)
301
+ audio_buffer.seek(0)
302
+ with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as temp_file:
303
+ temp_audio_path = temp_file.name
304
+ temp_file.write(audio_buffer.getvalue())
305
+
306
+ interview_state.temp_audio_files.append(temp_audio_path)
307
+
308
+ # --- Integrated bot_response functionality ends here ---
309
+
310
+ conversation_history.append({"question": question_text, "answer": user_input})
311
+ interview_data.append({"question": question_text, "answer": user_input})
312
+ history.append({"role": "user", "content": user_input})
313
+ history.append({"role": "assistant", "content": response_content, "audio": temp_audio_path}) # Store audio path
314
+
315
+ if current_question_index[0] + 1 < len(questions):
316
+ current_question_index[0] += 1
317
+ next_question = f"Alright, let's move on. {questions[current_question_index[0]]}"
318
+ history.append({"role": "assistant", "content": next_question})
319
+
320
+ else:
321
+ conclusion_message = "That wraps up our interview. Thank you so much for your responses—it's been great learning more about you!"
322
+ history.append(
323
+ {"role": "assistant", "content": conclusion_message}
324
+ )
325
+
326
+ # --- Generate report and save history (only at the end) ---
327
+ interview_state.interview_finished = True
328
+
329
+ # Generate the HR report content
330
+ report_content = generate_report(
331
+ interview_state.report_chain,
332
+ [msg["content"] for msg in history if msg["role"] != "system"], # Consider only user/assistant messages
333
+ language,
334
+ )
335
+
336
+ # Save the interview history
337
+ txt_path = save_interview_history(
338
+ [msg["content"] for msg in history if msg["role"] != "system"], language # Consider only user/assistant messages
339
+ )
340
+ print(f"[DEBUG] Interview history saved at: {txt_path}")
341
+
342
+ # Save the report to the reports folder
343
+ report_file_path = store_interview_report(report_content)
344
+ print(f"[DEBUG] Interview report saved at: {report_file_path}")
345
+
346
+ return history, ""
347
+
348
+ return interview_step, initial_message
349
+
350
+
351
+
352
+ def launch_candidate_app():
353
+ QUESTIONS_FILE_PATH = "questions.json"
354
+
355
+ def start_interview_ui():
356
+ # Reload questions every time the interview starts
357
+ interview_state.current_questions = read_questions_from_json(QUESTIONS_FILE_PATH)
358
+ interview_func, initial_message = conduct_interview(interview_state.current_questions)
359
+ interview_state.interview_func = interview_func
360
+
361
+ history = [{"role": "assistant", "content": initial_message}]
362
+ history.append({"role": "assistant", "content": "Let's begin! Here's your first question: " + interview_state.current_questions[0]})
363
+ return history, ""
364
+
365
+ def clear_interview_ui():
366
+ # Reset state when clearing the interview
367
+ interview_state.reset()
368
+ return [], ""
369
+
370
+ def on_enter_submit_ui(history, user_response):
371
+ if not user_response.strip():
372
+ return history, ""
373
+ history, _ = interview_state.interview_func(user_response, history)
374
+ return history, ""
375
+
376
+ with gr.Blocks(title="AI HR Interview Assistant") as candidate_app:
377
+ gr.Markdown("<h1 style='text-align: center;'>👋 Welcome to Your AI HR Interview Assistant</h1>")
378
+ start_btn = gr.Button("Start Interview", variant="primary")
379
+ chatbot = gr.Chatbot(label="Interview Chat", height=650, type="messages")
380
+ user_input = gr.Textbox(label="Your Response", placeholder="Type your answer here...", lines=1)
381
+ with gr.Row():
382
+ submit_btn = gr.Button("Submit")
383
+ clear_btn = gr.Button("Clear Chat")
384
+
385
+ start_btn.click(start_interview_ui, inputs=[], outputs=[chatbot, user_input])
386
+ submit_btn.click(on_enter_submit_ui, inputs=[chatbot, user_input], outputs=[chatbot, user_input])
387
+ user_input.submit(on_enter_submit_ui, inputs=[chatbot, user_input], outputs=[chatbot, user_input])
388
+ clear_btn.click(clear_interview_ui, inputs=[], outputs=[chatbot, user_input])
389
+
390
+ return candidate_app
391
+
392
+
393
+ def create_manager_app():
394
+ with gr.Blocks(
395
+ title="AI HR Interviewer Manager",
396
+ css="""
397
+ .tab-button {
398
+ background-color: #f0f0f0;
399
+ color: #333;
400
+ padding: 10px 20px;
401
+ border: none;
402
+ cursor: pointer;
403
+ font-size: 16px;
404
+ transition: background-color 0.3s ease;
405
+ }
406
+ .tab-button:hover {
407
+ background-color: #d0d0d0;
408
+ }
409
+ .tab-button.selected {
410
+ background-color: #666;
411
+ color: white;
412
+ }
413
+ """,
414
+ ) as manager_app:
415
+ gr.HTML(
416
+ """
417
+ <div style='text-align: center; margin-bottom: 20px;'>
418
+ <h1 style='font-size: 36px; color: #333;'>AI HR Interviewer Manager</h1>
419
+ <p style='font-size: 18px; color: #666;'>Select your role to start the interview process.</p>
420
+ </div>
421
+ """
422
+ )
423
+
424
+ with gr.Row():
425
+ user_role = gr.Dropdown(
426
+ choices=["Admin", "Candidate"],
427
+ label="Select User Role",
428
+ value="Candidate",
429
+ )
430
+ proceed_button = gr.Button("👉 Proceed")
431
+
432
+ candidate_ui = gr.Column(visible=False)
433
+ admin_ui = gr.Column(visible=False)
434
+
435
+ with candidate_ui:
436
+ gr.Markdown("## 🚀 Candidate Interview")
437
+ candidate_app = launch_candidate_app()
438
+
439
+ with admin_ui:
440
+ gr.Markdown("## 🔒 Admin Panel")
441
+ with gr.Tab("Generate Questions"):
442
+ try:
443
+ professions_data = load_json_data(PROFESSIONS_FILE)
444
+ types_data = load_json_data(TYPES_FILE)
445
+ except (FileNotFoundError, json.JSONDecodeError) as e:
446
+ print(f"Error loading data from JSON files: {e}")
447
+ professions_data = []
448
+ types_data = []
449
+
450
+ profession_names = [
451
+ item["profession"] for item in professions_data
452
+ ]
453
+ interview_types = [item["type"] for item in types_data]
454
+
455
+ with gr.Row():
456
+ profession_input = gr.Dropdown(
457
+ label="Select Profession", choices=profession_names
458
+ )
459
+ interview_type_input = gr.Dropdown(
460
+ label="Select Interview Type", choices=interview_types
461
+ )
462
+
463
+ num_questions_input = gr.Number(
464
+ label="Number of Questions (1-20)",
465
+ value=5,
466
+ precision=0,
467
+ minimum=1,
468
+ maximum=20,
469
+ )
470
+ overwrite_input = gr.Checkbox(
471
+ label="Overwrite all_questions.json?", value=True
472
+ )
473
+ # Update num_questions_input when interview_type_input changes
474
+ interview_type_input.change(
475
+ fn=update_max_questions,
476
+ inputs=interview_type_input,
477
+ outputs=num_questions_input,
478
+ )
479
+ generate_button = gr.Button("Generate Questions")
480
+
481
+ output_text = gr.Textbox(label="Output")
482
+ question_output = gr.JSON(label="Generated Questions")
483
+
484
+ generate_button.click(
485
+ generate_questions_manager,
486
+ inputs=[
487
+ profession_input,
488
+ interview_type_input,
489
+ num_questions_input,
490
+ overwrite_input,
491
+ ],
492
+ outputs=[output_text, question_output],
493
+ )
494
+
495
+
496
+ with gr.Tab("Generate from PDF"):
497
+ gr.Markdown("### 📄 Upload PDF for Question Generation")
498
+ pdf_file_input = gr.File(label="Upload PDF File", type="filepath")
499
+ num_questions_pdf_input = gr.Number(label="Number of Questions", value=5, precision=0)
500
+
501
+ pdf_status_output = gr.Textbox(label="Status", lines=3)
502
+ pdf_question_output = gr.JSON(label="Generated Questions")
503
+
504
+ generate_pdf_button = gr.Button("Generate Questions from PDF")
505
+
506
+ def update_pdf_ui(pdf_path, num_questions):
507
+ for status, questions in generate_and_save_questions_from_pdf3(pdf_path, num_questions):
508
+ yield gr.update(value=status), gr.update(value=questions)
509
+
510
+ generate_pdf_button.click(
511
+ update_pdf_ui,
512
+ inputs=[pdf_file_input, num_questions_pdf_input],
513
+ outputs=[pdf_status_output, pdf_question_output],
514
+ )
515
+
516
+
517
+
518
+
519
+ def show_selected_ui(role):
520
+ if role == "Candidate":
521
+ return {candidate_ui: gr.Column(visible=True), admin_ui: gr.Column(visible=False)}
522
+
523
+ elif role == "Admin":
524
+ return {candidate_ui: gr.Column(visible=False), admin_ui: gr.Column(visible=True)}
525
+ else:
526
+ return {candidate_ui: gr.Column(visible=False), admin_ui: gr.Column(visible=False)}
527
+
528
+
529
+ proceed_button.click(
530
+ show_selected_ui,
531
+ inputs=[user_role],
532
+ outputs=[candidate_ui, admin_ui],
533
+ )
534
+
535
+ return manager_app
536
+
537
+ def cleanup():
538
+ for audio_file in interview_state.temp_audio_files:
539
+ try:
540
+ if os.path.exists(audio_file):
541
+ os.unlink(audio_file)
542
+ except Exception as e:
543
+ print(f"Error deleting file {audio_file}: {e}")
544
+
545
+
546
+ if __name__ == "__main__":
547
+ manager_app = create_manager_app()
548
+ try:
549
+ manager_app.launch(server_name="0.0.0.0", server_port=7860, debug=True)
550
+ finally:
551
+ cleanup()
generator.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ from dotenv import load_dotenv
4
+ from langchain_openai import ChatOpenAI
5
+ from langchain.schema import HumanMessage, SystemMessage
6
+
7
+ # Load environment variables
8
+ load_dotenv()
9
+
10
+ # File paths
11
+ PROFESSIONS_FILE = "professions.json"
12
+ TYPES_FILE = "types.json"
13
+ OUTPUT_FILE = "all_questions.json"
14
+
15
+ def generate_questions(profession, interview_type, description, max_questions):
16
+ """
17
+ Generates interview questions using the OpenAI API based on profession, type, and description.
18
+ """
19
+ openai_api_key = os.getenv("OPENAI_API_KEY")
20
+
21
+ if not openai_api_key:
22
+ raise RuntimeError(
23
+ "OpenAI API key not found. Please add it to your .env file as OPENAI_API_KEY."
24
+ )
25
+
26
+ chat = ChatOpenAI(
27
+ openai_api_key=openai_api_key, model="gpt-4", temperature=0.7, max_tokens=750
28
+ )
29
+
30
+ messages = [
31
+ SystemMessage(
32
+ content="You are an expert interviewer who generates concise technical interview questions for HR interviews. "
33
+ "Answer only with questions. Do not number the questions. Each question should be a separate string. "
34
+ "The questions should be appropriate for "
35
+ f"the {interview_type} stage of the interview process and relevant to the {profession} profession."
36
+ f" Generate no more than {max_questions} questions."
37
+
38
+ ),
39
+ HumanMessage(
40
+ content=f"Generate interview questions for the role of '{profession}'. "
41
+ f"Interview Type: '{interview_type}'. "
42
+ f"Description of the role: '{description}'. "
43
+
44
+ ),
45
+ ]
46
+
47
+ try:
48
+ print(f"[DEBUG] Sending request to OpenAI for {profession} - {interview_type}")
49
+ response = chat.invoke(messages)
50
+ # Directly split the response into individual questions without numbering
51
+ questions = [q.strip() for q in response.content.split("\n") if q.strip()]
52
+
53
+ except Exception as e:
54
+ print(f"[ERROR] Failed to generate questions: {e}")
55
+ questions = ["An error occurred while generating questions."]
56
+
57
+ return questions
58
+
59
+
60
+ def load_json_data(filepath):
61
+ """Loads data from a JSON file."""
62
+ with open(filepath, "r") as f:
63
+ return json.load(f)
64
+
65
+
66
+ def save_questions_to_file(output_file, all_questions, overwrite=True):
67
+ """
68
+ Saves the questions to the specified JSON file.
69
+
70
+ Args:
71
+ output_file: The path to the output JSON file.
72
+ all_questions: The list of question dictionaries to save.
73
+ overwrite: If True, overwrites the file if it exists. If False, appends to the file.
74
+ """
75
+ if overwrite:
76
+ with open(output_file, "w") as outfile:
77
+ json.dump(all_questions, outfile, indent=4)
78
+ else:
79
+ try:
80
+ existing_questions = load_json_data(output_file)
81
+ except (FileNotFoundError, json.JSONDecodeError):
82
+ existing_questions = []
83
+
84
+ existing_questions.extend(all_questions)
85
+
86
+ with open(output_file, "w") as outfile:
87
+ json.dump(existing_questions, outfile, indent=4)
88
+
89
+
90
+ def main(overwrite_output=True):
91
+ """
92
+ Main function to generate and save interview questions.
93
+ """
94
+ try:
95
+ professions_data = load_json_data(PROFESSIONS_FILE)
96
+ types_data = load_json_data(TYPES_FILE)
97
+ except FileNotFoundError as e:
98
+ print(f"Error: File not found - {e}")
99
+ return
100
+ except json.JSONDecodeError as e:
101
+ print(f"Error: Invalid JSON format in file - {e}")
102
+ return
103
+
104
+ all_questions = []
105
+
106
+ for profession_info in professions_data:
107
+ profession = profession_info["profession"]
108
+ description = profession_info["description"]
109
+
110
+ for interview_type_info in types_data:
111
+ interview_type = interview_type_info["type"]
112
+ max_questions = interview_type_info.get("max_questions", 5)
113
+
114
+ questions = generate_questions(
115
+ profession, interview_type, description, max_questions
116
+ )
117
+
118
+ all_questions.append(
119
+ {
120
+ "profession": profession,
121
+ "interview_type": interview_type,
122
+ "description": description,
123
+ "max_questions": max_questions,
124
+ "questions": questions,
125
+ }
126
+ )
127
+ # Save the questions, either overwriting or appending based on the parameter
128
+ save_questions_to_file(OUTPUT_FILE, all_questions, overwrite=overwrite_output)
129
+ print(f"[INFO] Questions saved to {OUTPUT_FILE}")
130
+
131
+
132
+ if __name__ == "__main__":
133
+ # Set overwrite_output to True to overwrite the existing file, False to append
134
+ main(overwrite_output=True)
generatorgr.py ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import json
3
+ import time
4
+ import os
5
+
6
+ from generator import PROFESSIONS_FILE, TYPES_FILE, OUTPUT_FILE
7
+ from generator import generate_questions, load_json_data, save_questions_to_file
8
+
9
+ # Load professions and interview types from JSON files
10
+ try:
11
+ professions_data = load_json_data(PROFESSIONS_FILE)
12
+ types_data = load_json_data(TYPES_FILE)
13
+ except (FileNotFoundError, json.JSONDecodeError) as e:
14
+ print(f"Error loading data from JSON files: {e}")
15
+ professions_data = []
16
+ types_data = []
17
+
18
+ # Extract profession names and interview types for the dropdown menus
19
+ profession_names = [item["profession"] for item in professions_data]
20
+ interview_types = [item["type"] for item in types_data]
21
+
22
+ # Define path for the questions.json file
23
+ QUESTIONS_FILE = "questions.json"
24
+
25
+
26
+ def generate_and_save_questions(profession, interview_type, num_questions, overwrite=True, progress=gr.Progress()):
27
+ """
28
+ Generates questions using the generate_questions function and saves them to JSON files.
29
+ Provides progress updates.
30
+ """
31
+ profession_info = next(
32
+ (item for item in professions_data if item["profession"] == profession), None
33
+ )
34
+ interview_type_info = next(
35
+ (item for item in types_data if item["type"] == interview_type), None
36
+ )
37
+
38
+ if profession_info is None or interview_type_info is None:
39
+ return "Error: Invalid profession or interview type selected.", None
40
+
41
+ description = profession_info["description"]
42
+ max_questions = min(int(num_questions), 20) # Ensure max is 20
43
+
44
+ progress(0, desc="Starting question generation...")
45
+
46
+ questions = generate_questions(
47
+ profession, interview_type, description, max_questions
48
+ )
49
+
50
+ progress(0.5, desc=f"Generated {len(questions)} questions. Saving...")
51
+
52
+ # Save the generated questions to the all_questions.json file
53
+
54
+ all_questions_entry = {
55
+ "profession": profession,
56
+ "interview_type": interview_type,
57
+ "description": description,
58
+ "max_questions": max_questions,
59
+ "questions": questions,
60
+ }
61
+
62
+
63
+ save_questions_to_file(OUTPUT_FILE, [all_questions_entry], overwrite=overwrite)
64
+
65
+ # Save the generated questions to the new questions.json file
66
+ with open(QUESTIONS_FILE, "w") as outfile:
67
+ json.dump(questions, outfile, indent=4)
68
+
69
+ progress(1, desc="Questions saved.")
70
+
71
+ return (
72
+ f"✅ Questions generated and saved for {profession} ({interview_type}). Max questions: {max_questions}",
73
+ questions,
74
+ )
75
+
76
+
77
+
78
+ def update_max_questions(interview_type):
79
+ """
80
+ Updates the default value of the number input based on the selected interview type.
81
+ """
82
+ interview_type_info = next(
83
+ (item for item in types_data if item["type"] == interview_type), None
84
+ )
85
+ if interview_type_info:
86
+ default_max_questions = interview_type_info.get("max_questions", 5)
87
+ return gr.update(value=default_max_questions, minimum=1, maximum=20)
88
+ else:
89
+ return gr.update(value=5, minimum=1, maximum=20)
90
+
91
+
92
+ with gr.Blocks() as demo:
93
+ gr.Markdown("## 📄 Interview Question Generator for IBM CIC")
94
+ with gr.Row():
95
+ profession_input = gr.Dropdown(label="Select Profession", choices=profession_names)
96
+ interview_type_input = gr.Dropdown(label="Select Interview Type", choices=interview_types)
97
+
98
+ num_questions_input = gr.Number(
99
+ label="Number of Questions (1-20)", value=5, precision=0, minimum=1, maximum=20
100
+ )
101
+
102
+ generate_button = gr.Button("Generate Questions")
103
+
104
+ output_text = gr.Textbox(label="Output")
105
+ question_output = gr.JSON(label="Generated Questions")
106
+
107
+ # Update num_questions_input when interview_type_input changes
108
+ interview_type_input.change(
109
+ fn=update_max_questions,
110
+ inputs=interview_type_input,
111
+ outputs=num_questions_input,
112
+ )
113
+
114
+ generate_button.click(
115
+ generate_and_save_questions,
116
+ inputs=[profession_input, interview_type_input, num_questions_input],
117
+ outputs=[output_text, question_output],
118
+ )
119
+
120
+ if __name__ == "__main__":
121
+ demo.queue().launch()
gpt-general.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ from dotenv import load_dotenv
4
+ from langchain_openai import ChatOpenAI # Correct import from langchain-openai
5
+ from langchain.schema import HumanMessage, SystemMessage # For creating structured chat messages
6
+
7
+ # Load environment variables
8
+ load_dotenv()
9
+
10
+ # Function to read questions from JSON
11
+ # The JSON is expected to contain a list of dictionaries or strings.
12
+ def read_questions_from_json(file_path):
13
+ """
14
+ Reads questions from a JSON file.
15
+ """
16
+ if not os.path.exists(file_path):
17
+ raise FileNotFoundError(f"The file '{file_path}' does not exist.")
18
+
19
+ with open(file_path, 'r') as f:
20
+ questions_list = json.load(f)
21
+
22
+ if not questions_list:
23
+ raise ValueError("The JSON file is empty or has invalid content.")
24
+
25
+ return questions_list
26
+
27
+ # Function to generate interview questions using LLM and collect user answers
28
+ def conduct_interview_with_llm(questions, language="English"):
29
+ """
30
+ Generates interview questions using the LLM and collects user responses.
31
+ """
32
+ openai_api_key = os.getenv("OPENAI_API_KEY")
33
+ if not openai_api_key:
34
+ raise RuntimeError("OpenAI API key not found. Please add it to your .env file as OPENAI_API_KEY.")
35
+
36
+ chat = ChatOpenAI(
37
+ openai_api_key=openai_api_key, model="gpt-4", temperature=0.7, max_tokens=750
38
+ )
39
+
40
+ interview_data = []
41
+ print("\n--- Technical Interview Started ---\n")
42
+
43
+ for index, question_text in enumerate(questions):
44
+ # Create the system and user prompts
45
+ system_prompt = f"You are Sarah, a compassionate and empathetic HR professional conducting a technical interview in {language}."
46
+ messages = [
47
+ SystemMessage(content=system_prompt),
48
+ HumanMessage(content=f"Generate the next interview question based on the context and previous history. Current question number: {index + 1}/{len(questions)}.")
49
+ ]
50
+
51
+ try:
52
+ # Generate a question from the LLM
53
+ print(f"Generating question {index + 1}...")
54
+ response = chat.invoke(messages)
55
+ llm_generated_question = response.content.strip()
56
+ print(f"Q{index + 1}: {llm_generated_question}")
57
+
58
+ # Collect the user’s answer
59
+ user_answer = input("Your answer: ").strip()
60
+ interview_data.append({"question": llm_generated_question, "answer": user_answer})
61
+
62
+ except Exception as e:
63
+ print(f"Error with OpenAI API: {e}")
64
+ interview_data.append({"question": "An error occurred while generating the question.", "answer": "No answer recorded."})
65
+
66
+ print("\n--- Technical Interview Completed ---\n")
67
+ return interview_data
68
+
69
+ # Function to save interview to a text file
70
+ def save_interview_to_file(interview_data, file_path):
71
+ """
72
+ Saves the questions and answers to a text file.
73
+ """
74
+ with open(file_path, 'w') as f:
75
+ for entry in interview_data:
76
+ f.write(f"Q: {entry['question']}\n")
77
+ f.write(f"A: {entry['answer']}\n\n")
78
+
79
+ print(f"Interview saved to {file_path}")
80
+
81
+ if __name__ == "__main__":
82
+ QUESTIONS_FILE_PATH = "questions.json"
83
+ INTERVIEW_FILE_PATH = "interview.txt"
84
+
85
+ try:
86
+ # Read questions from JSON file
87
+ questions = read_questions_from_json(QUESTIONS_FILE_PATH)
88
+
89
+ # Conduct the interview
90
+ interview_results = conduct_interview_with_llm(questions, language="English")
91
+
92
+ # Save the interview to a text file
93
+ save_interview_to_file(interview_results, INTERVIEW_FILE_PATH)
94
+
95
+ except Exception as e:
96
+ print(f"Error: {e}")
gpt.py ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ from collections import deque
4
+ from dotenv import load_dotenv
5
+ from langchain_openai import ChatOpenAI
6
+ from langchain.schema import HumanMessage, SystemMessage
7
+
8
+ # Load environment variables
9
+ load_dotenv()
10
+
11
+ # Function to read questions from JSON
12
+ def read_questions_from_json(file_path):
13
+ if not os.path.exists(file_path):
14
+ raise FileNotFoundError(f"The file '{file_path}' does not exist.")
15
+
16
+ with open(file_path, 'r') as f:
17
+ questions_list = json.load(f)
18
+
19
+ if not questions_list:
20
+ raise ValueError("The JSON file is empty or has invalid content.")
21
+
22
+ return questions_list
23
+
24
+ # Function to handle user input and responses from the LLM
25
+ def handle_user_input(chat, system_prompt, conversation_history, question_text):
26
+ while True:
27
+ user_input = input(f"Your response: ").strip()
28
+ if user_input.lower() in ["exit", "quit"]:
29
+ print("Interview terminated as requested.")
30
+ return "[Interview Terminated by User]", True
31
+
32
+ history_content = "\n".join([f"Q: {entry['question']}\nA: {entry['answer']}" for entry in conversation_history])
33
+ combined_prompt = (f"{system_prompt}\n\nPrevious conversation history:\n{history_content}\n\n"
34
+ f"Current question: {question_text}\nUser's input: {user_input}\n\n"
35
+ "Respond naturally to any follow-up questions or requests for clarification, and let the user know when you're ready to proceed.")
36
+
37
+ messages = [
38
+ SystemMessage(content=system_prompt),
39
+ HumanMessage(content=combined_prompt)
40
+ ]
41
+
42
+ response = chat.invoke(messages)
43
+ response_content = response.content.strip()
44
+
45
+ if "proceed" in response_content.lower() or "continue" in response_content.lower():
46
+ print("Understood. Let’s continue to the next question.")
47
+ return user_input, False
48
+ else:
49
+ print(f"LLM's Response: {response_content}")
50
+ #print("Whenever you're ready, just let me know if you want to move forward or discuss further.")
51
+ print("Whenever you're ready, just let me know, if you want to move to the next question tell me proceed, or provide further clarifications.")
52
+
53
+ # Function to conduct the interview
54
+ def conduct_interview_with_user_input(questions, language="English", history_limit=5):
55
+ openai_api_key = os.getenv("OPENAI_API_KEY")
56
+ if not openai_api_key:
57
+ raise RuntimeError("OpenAI API key not found. Please add it to your .env file as OPENAI_API_KEY.")
58
+
59
+ chat = ChatOpenAI(
60
+ openai_api_key=openai_api_key, model="gpt-4", temperature=0.7, max_tokens=750
61
+ )
62
+
63
+ interview_data = []
64
+ conversation_history = deque(maxlen=history_limit)
65
+ system_prompt = (f"You are Sarah, an empathetic HR interviewer conducting a technical interview in {language}. "
66
+ "Respond to user follow-up questions politely and concisely. If the user is confused, provide clear clarification.")
67
+
68
+ print("\n--- Technical Interview Started ---\n")
69
+
70
+ for index, question_text in enumerate(questions):
71
+ print(f"{index + 1}/{len(questions)}: {question_text}")
72
+ try:
73
+ user_answer, terminate = handle_user_input(chat, system_prompt, conversation_history, question_text)
74
+ if terminate:
75
+ break
76
+
77
+ conversation_history.append({"question": question_text, "answer": user_answer})
78
+ interview_data.append({"question": question_text, "answer": user_answer})
79
+
80
+ if index + 1 == len(questions):
81
+ print("Thank you for your time. This concludes the interview. We will prepare a report based on the gathered information.")
82
+
83
+ except Exception as e:
84
+ print(f"Error during the interview process: {e}")
85
+ interview_data.append({"question": question_text, "answer": "No answer recorded due to an error."})
86
+
87
+ print("\n--- Technical Interview Completed ---\n")
88
+ return interview_data
89
+
90
+ # Function to save interview to a text file
91
+ def save_interview_to_file(interview_data, file_path):
92
+ with open(file_path, 'w') as f:
93
+ for entry in interview_data:
94
+ f.write(f"Q: {entry['question']}\n")
95
+ f.write(f"A: {entry['answer']}\n\n")
96
+
97
+ print(f"Interview saved to {file_path}")
98
+
99
+ if __name__ == "__main__":
100
+ QUESTIONS_FILE_PATH = "questions.json"
101
+ INTERVIEW_FILE_PATH = "interview.txt"
102
+
103
+ try:
104
+ questions = read_questions_from_json(QUESTIONS_FILE_PATH)
105
+ interview_results = conduct_interview_with_user_input(questions, language="English")
106
+ save_interview_to_file(interview_results, INTERVIEW_FILE_PATH)
107
+
108
+ except Exception as e:
109
+ print(f"Error: {e}")
gptgr-manager.py ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import tempfile
3
+ import os
4
+ import json
5
+ from io import BytesIO
6
+ from collections import deque
7
+ from dotenv import load_dotenv
8
+ from langchain_openai import ChatOpenAI
9
+ from langchain.schema import HumanMessage, SystemMessage
10
+
11
+ # Load environment variables
12
+ load_dotenv()
13
+
14
+ class InterviewState:
15
+ def __init__(self):
16
+ self.reset()
17
+
18
+ def reset(self, voice="alloy"):
19
+ self.question_count = 0
20
+ self.interview_history = []
21
+ self.selected_interviewer = voice
22
+ self.interview_finished = False
23
+ self.audio_enabled = True
24
+ self.temp_audio_files = []
25
+ self.initial_audio_path = None
26
+ self.admin_authenticated = False
27
+ self.document_loaded = False
28
+ self.knowledge_retrieval_setup = False
29
+ self.interview_chain = None
30
+ self.report_chain = None
31
+
32
+ def get_voice_setting(self):
33
+ return self.selected_interviewer
34
+
35
+ interview_state = InterviewState()
36
+
37
+ # Function to read questions from JSON
38
+ def read_questions_from_json(file_path):
39
+ if not os.path.exists(file_path):
40
+ raise FileNotFoundError(f"The file '{file_path}' does not exist.")
41
+
42
+ with open(file_path, 'r') as f:
43
+ questions_list = json.load(f)
44
+
45
+ if not questions_list:
46
+ raise ValueError("The JSON file is empty or has invalid content.")
47
+
48
+ return questions_list
49
+
50
+ # Conduct interview and handle user input
51
+ def conduct_interview(questions, language="English", history_limit=5):
52
+ openai_api_key = os.getenv("OPENAI_API_KEY")
53
+ if not openai_api_key:
54
+ raise RuntimeError("OpenAI API key not found. Please add it to your .env file as OPENAI_API_KEY.")
55
+
56
+ chat = ChatOpenAI(
57
+ openai_api_key=openai_api_key, model="gpt-4", temperature=0.7, max_tokens=750
58
+ )
59
+
60
+ conversation_history = deque(maxlen=history_limit)
61
+ system_prompt = (f"You are Sarah, an empathetic HR interviewer conducting a technical interview in {language}. "
62
+ "Respond to user follow-up questions politely and concisely. If the user is confused, provide clear clarification.")
63
+
64
+ interview_data = []
65
+ current_question_index = [0]
66
+
67
+ initial_message = ("👋 Hi there, I'm Sarah, your friendly AI HR assistant! "
68
+ "I'll guide you through a series of interview questions to learn more about you. "
69
+ "Take your time and answer each question thoughtfully.")
70
+
71
+ def interview_step(user_input, history):
72
+ if user_input.lower() in ["exit", "quit"]:
73
+ history.append({"role": "assistant", "content": "The interview has ended at your request. Thank you for your time!"})
74
+ return history, ""
75
+
76
+ question_text = questions[current_question_index[0]]
77
+ history_content = "\n".join([f"Q: {entry['question']}\nA: {entry['answer']}" for entry in conversation_history])
78
+ combined_prompt = (f"{system_prompt}\n\nPrevious conversation history:\n{history_content}\n\n"
79
+ f"Current question: {question_text}\nUser's input: {user_input}\n\n"
80
+ "Respond in a warm and conversational way, offering natural follow-ups if needed.")
81
+
82
+ messages = [
83
+ SystemMessage(content=system_prompt),
84
+ HumanMessage(content=combined_prompt)
85
+ ]
86
+
87
+ response = chat.invoke(messages)
88
+ response_content = response.content.strip()
89
+
90
+ conversation_history.append({"question": question_text, "answer": user_input})
91
+ interview_data.append({"question": question_text, "answer": user_input})
92
+ history.append({"role": "user", "content": user_input})
93
+ history.append({"role": "assistant", "content": response_content})
94
+
95
+ if current_question_index[0] + 1 < len(questions):
96
+ current_question_index[0] += 1
97
+ next_question = f"Alright, let's move on. {questions[current_question_index[0]]}"
98
+ history.append({"role": "assistant", "content": next_question})
99
+ return history, ""
100
+ else:
101
+ history.append({"role": "assistant", "content": "That wraps up our interview. Thank you so much for your responses—it's been great learning more about you!"})
102
+ return history, ""
103
+
104
+ return interview_step, initial_message
105
+
106
+ def launch_candidate_app():
107
+ QUESTIONS_FILE_PATH = "questions.json"
108
+ try:
109
+ questions = read_questions_from_json(QUESTIONS_FILE_PATH)
110
+ interview_func, initial_message = conduct_interview(questions)
111
+
112
+ def start_interview_ui():
113
+ history = [{"role": "assistant", "content": initial_message}]
114
+ history.append({"role": "assistant", "content": "Let's begin! Here's your first question: " + questions[0]})
115
+ return history, ""
116
+
117
+ def clear_interview_ui():
118
+ return [], ""
119
+
120
+ def on_enter_submit_ui(history, user_response):
121
+ if not user_response.strip():
122
+ return history, ""
123
+ history, _ = interview_func(user_response, history)
124
+ return history, ""
125
+
126
+ with gr.Blocks(title="AI HR Interview Assistant") as candidate_app:
127
+ gr.Markdown("<h1 style='text-align: center;'>👋 Welcome to Your AI HR Interview Assistant</h1>")
128
+ start_btn = gr.Button("Start Interview", variant="primary")
129
+ chatbot = gr.Chatbot(label="Interview Chat", height=650, type="messages")
130
+ user_input = gr.Textbox(label="Your Response", placeholder="Type your answer here...", lines=1)
131
+ with gr.Row():
132
+ submit_btn = gr.Button("Submit")
133
+ clear_btn = gr.Button("Clear Chat")
134
+
135
+ start_btn.click(start_interview_ui, inputs=[], outputs=[chatbot, user_input])
136
+ submit_btn.click(on_enter_submit_ui, inputs=[chatbot, user_input], outputs=[chatbot, user_input])
137
+ user_input.submit(on_enter_submit_ui, inputs=[chatbot, user_input], outputs=[chatbot, user_input])
138
+ clear_btn.click(clear_interview_ui, inputs=[], outputs=[chatbot, user_input])
139
+
140
+ return candidate_app
141
+
142
+ except Exception as e:
143
+ print(f"Error: {e}")
144
+ return None
145
+
146
+ def create_manager_app():
147
+ with gr.Blocks(title="AI HR Interviewer Manager") as manager_app:
148
+ gr.HTML("<h1 style='text-align: center;'>AI HR Interviewer Manager</h1>")
149
+ user_role = gr.Dropdown(choices=["Admin", "Candidate"], label="Select User Role", value="Candidate")
150
+ proceed_button = gr.Button("👉 Proceed")
151
+
152
+ candidate_ui = gr.Column(visible=False)
153
+ admin_ui = gr.Column(visible=False)
154
+
155
+ with candidate_ui:
156
+ gr.Markdown("## 🚀 Candidate Interview")
157
+ candidate_app = launch_candidate_app()
158
+
159
+ with admin_ui:
160
+ gr.Markdown("## 🔒 Admin Panel")
161
+ gr.Markdown("Admin operations and question generation will go here.")
162
+
163
+ def show_selected_ui(role):
164
+ if role == "Candidate":
165
+ return gr.update(visible=True), gr.update(visible=False)
166
+ elif role == "Admin":
167
+ return gr.update(visible=False), gr.update(visible=True)
168
+ else:
169
+ return gr.update(visible=False), gr.update(visible=False)
170
+
171
+ proceed_button.click(show_selected_ui, inputs=[user_role], outputs=[candidate_ui, admin_ui])
172
+
173
+ return manager_app
174
+
175
+ def cleanup():
176
+ for audio_file in interview_state.temp_audio_files:
177
+ if os.path.exists(audio_file):
178
+ os.unlink(audio_file)
179
+
180
+ if __name__ == "__main__":
181
+ manager_app = create_manager_app()
182
+ try:
183
+ manager_app.launch(server_name="0.0.0.0", server_port=7860, debug=True)
184
+ finally:
185
+ cleanup()
gptgr.py ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ from collections import deque
4
+ from dotenv import load_dotenv
5
+ import gradio as gr
6
+ from langchain_openai import ChatOpenAI
7
+ from langchain.schema import HumanMessage, SystemMessage
8
+
9
+ # Load environment variables
10
+ load_dotenv()
11
+
12
+ # Function to read questions from JSON
13
+ def read_questions_from_json(file_path):
14
+ if not os.path.exists(file_path):
15
+ raise FileNotFoundError(f"The file '{file_path}' does not exist.")
16
+
17
+ with open(file_path, 'r') as f:
18
+ questions_list = json.load(f)
19
+
20
+ if not questions_list:
21
+ raise ValueError("The JSON file is empty or has invalid content.")
22
+
23
+ return questions_list
24
+
25
+ # Conduct interview and handle user input
26
+ def conduct_interview(questions, language="English", history_limit=5):
27
+ openai_api_key = os.getenv("OPENAI_API_KEY")
28
+ if not openai_api_key:
29
+ raise RuntimeError("OpenAI API key not found. Please add it to your .env file as OPENAI_API_KEY.")
30
+
31
+ chat = ChatOpenAI(
32
+ openai_api_key=openai_api_key, model="gpt-4", temperature=0.7, max_tokens=750
33
+ )
34
+
35
+ conversation_history = deque(maxlen=history_limit)
36
+ system_prompt = (f"You are Sarah, an empathetic HR interviewer conducting a technical interview in {language}. "
37
+ "Respond to user follow-up questions politely and concisely. If the user is confused, provide clear clarification.")
38
+
39
+ interview_data = []
40
+ current_question_index = [0] # Use a list to hold the index
41
+
42
+ initial_message = ("👋 Hi there, I'm Sarah, your friendly AI HR assistant! "
43
+ "I'll guide you through a series of interview questions to learn more about you. "
44
+ "Take your time and answer each question thoughtfully.")
45
+
46
+ def interview_step(user_input, history):
47
+ if user_input.lower() in ["exit", "quit"]:
48
+ history.append((None, "The interview has ended at your request. Thank you for your time!"))
49
+ return history, ""
50
+
51
+ question_text = questions[current_question_index[0]]
52
+ history_content = "\n".join([f"Q: {entry['question']}\nA: {entry['answer']}" for entry in conversation_history])
53
+ combined_prompt = (f"{system_prompt}\n\nPrevious conversation history:\n{history_content}\n\n"
54
+ f"Current question: {question_text}\nUser's input: {user_input}\n\n"
55
+ "Respond in a warm and conversational way, offering natural follow-ups if needed.")
56
+
57
+ messages = [
58
+ SystemMessage(content=system_prompt),
59
+ HumanMessage(content=combined_prompt)
60
+ ]
61
+
62
+ response = chat.invoke(messages)
63
+ response_content = response.content.strip()
64
+
65
+ conversation_history.append({"question": question_text, "answer": user_input})
66
+ interview_data.append({"question": question_text, "answer": user_input})
67
+ history.append((user_input, None))
68
+ history.append((None, response_content))
69
+
70
+ if current_question_index[0] + 1 < len(questions):
71
+ current_question_index[0] += 1
72
+ next_question = f"Alright, let's move on. {questions[current_question_index[0]]}"
73
+ history.append((None, next_question))
74
+ return history, ""
75
+ else:
76
+ history.append((None, "That wraps up our interview. Thank you so much for your responses—it's been great learning more about you!"))
77
+ return history, ""
78
+
79
+ return interview_step, initial_message
80
+
81
+ # Gradio interface
82
+ def main():
83
+ QUESTIONS_FILE_PATH = "questions.json" # Ensure you have a questions.json file with your interview questions
84
+
85
+ try:
86
+ questions = read_questions_from_json(QUESTIONS_FILE_PATH)
87
+ interview_func, initial_message = conduct_interview(questions)
88
+
89
+ css = """
90
+ .contain { display: flex; flex-direction: column; }
91
+ .gradio-container { height: 100vh !important; }
92
+ #component-0 { height: 100%; }
93
+ .chatbot { flex-grow: 1; overflow: auto; height: 100px; }
94
+ .chatbot .wrap.svelte-1275q59.wrap.svelte-1275q59 {flex-wrap : nowrap !important}
95
+ .user > div > .message {background-color : #dcf8c6 !important}
96
+ .bot > div > .message {background-color : #f7f7f8 !important}
97
+ """
98
+
99
+ with gr.Blocks(css=css) as demo:
100
+ gr.Markdown("""
101
+ <h1 style='text-align: center; margin-bottom: 1rem'>👋 Welcome to Your AI HR Interview Assistant</h1>
102
+ """)
103
+
104
+ start_btn = gr.Button("Start Interview", variant="primary")
105
+
106
+ gr.Markdown("""
107
+ <p style='text-align: center; margin-bottom: 1rem'>I will ask you a series of questions. Please answer honestly and thoughtfully. When you are ready, click "Start Interview" to begin.</p>
108
+ """)
109
+
110
+ chatbot = gr.Chatbot(label="Interview Chat", elem_id="chatbot", height=650)
111
+ user_input = gr.Textbox(label="Your Response", placeholder="Type your answer here...", lines=1)
112
+
113
+ with gr.Row():
114
+ submit_btn = gr.Button("Submit", variant="primary")
115
+ clear_btn = gr.Button("Clear Chat")
116
+
117
+ def start_interview():
118
+ history = []
119
+ history.append((None, initial_message))
120
+ history.append((None, "Let's begin! Here's your first question: " + questions[0]))
121
+ return history, ""
122
+
123
+ def clear_interview():
124
+ return [], ""
125
+
126
+ def interview_step(user_response, history):
127
+ return interview_func(user_response, history)
128
+
129
+ def on_enter_submit(history, user_response):
130
+ if not user_response.strip():
131
+ return history, ""
132
+ return interview_step(user_response, history)
133
+
134
+ start_btn.click(start_interview, inputs=[], outputs=[chatbot, user_input])
135
+ submit_btn.click(interview_step, inputs=[user_input, chatbot], outputs=[chatbot, user_input])
136
+ user_input.submit(on_enter_submit, inputs=[chatbot, user_input], outputs=[chatbot, user_input])
137
+ clear_btn.click(clear_interview, inputs=[], outputs=[chatbot, user_input])
138
+
139
+ demo.launch()
140
+
141
+ except Exception as e:
142
+ print(f"Error: {e}")
143
+
144
+ if __name__ == "__main__":
145
+ main()
grad.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import gradio as gr
4
+ from collections import deque
5
+ from dotenv import load_dotenv
6
+ from langchain_openai import ChatOpenAI
7
+ from langchain.schema import HumanMessage, SystemMessage
8
+
9
+ # Load environment variables
10
+ load_dotenv()
11
+
12
+ # Function to read questions from JSON
13
+ def read_questions_from_json(file_path):
14
+ if not os.path.exists(file_path):
15
+ raise FileNotFoundError(f"The file '{file_path}' does not exist.")
16
+ with open(file_path, 'r') as f:
17
+ questions_list = json.load(f)
18
+ if not questions_list:
19
+ raise ValueError("The JSON file is empty or has invalid content.")
20
+ return questions_list
21
+
22
+ # Function to handle user input and LLM's response
23
+ def handle_user_input(chat, system_prompt, conversation_history, question_text, user_input):
24
+ history_content = "\n".join([f"Q: {entry['question']}\nA: {entry['answer']}" for entry in conversation_history])
25
+ combined_prompt = (f"{system_prompt}\n\nPrevious conversation history:\n{history_content}\n\n"
26
+ f"Current question: {question_text}\nUser's input: {user_input}\n\n"
27
+ "Respond naturally to any follow-up questions or requests for clarification."
28
+ " Provide the next question or end the interview when appropriate.")
29
+
30
+ messages = [SystemMessage(content=system_prompt), HumanMessage(content=combined_prompt)]
31
+ response = chat.invoke(messages)
32
+ return response.content.strip()
33
+
34
+ # Function to conduct the interview dynamically
35
+ def conduct_interview(questions, language="English", history_limit=5):
36
+ openai_api_key = os.getenv("OPENAI_API_KEY")
37
+ if not openai_api_key:
38
+ raise RuntimeError("OpenAI API key not found. Please add it to your .env file as OPENAI_API_KEY.")
39
+
40
+ chat = ChatOpenAI(openai_api_key=openai_api_key, model="gpt-4", temperature=0.7, max_tokens=750)
41
+ conversation_history = deque(maxlen=history_limit)
42
+ system_prompt = f"You are Sarah, an empathetic HR interviewer conducting an interview in {language}."
43
+
44
+ def gradio_interview(user_input, history):
45
+ if not history:
46
+ # Initial greeting and first question
47
+ initial_message = (f"👋 Hello, I'm your AI HR assistant!\n"
48
+ f"I will ask you {len(questions)} questions.\n"
49
+ "Please answer honestly and to the best of your ability.")
50
+ history = [{"role": "assistant", "content": initial_message}]
51
+ current_question = questions[0]
52
+ history.append({"role": "assistant", "content": f"First question: {current_question}"})
53
+ return history, ""
54
+
55
+ current_question_index = (len(history) - 2) // 2 # Adjust for assistant's intro
56
+ if current_question_index < len(questions):
57
+ current_question = questions[current_question_index]
58
+ response = handle_user_input(chat, system_prompt, conversation_history, current_question, user_input)
59
+ conversation_history.append({"question": current_question, "answer": user_input})
60
+ history.append({"role": "user", "content": user_input})
61
+ history.append({"role": "assistant", "content": response})
62
+
63
+ if current_question_index + 1 < len(questions):
64
+ next_question = questions[current_question_index + 1]
65
+ history.append({"role": "assistant", "content": f"Next question: {next_question}"})
66
+ else:
67
+ history.append({"role": "assistant", "content": "Thank you for your time. This concludes the interview."})
68
+
69
+ return history, ""
70
+
71
+ return gradio_interview
72
+
73
+ # Load questions and start Gradio app
74
+ def start_hr_chatbot():
75
+ QUESTIONS_FILE_PATH = "questions.json"
76
+ try:
77
+ questions = read_questions_from_json(QUESTIONS_FILE_PATH)
78
+ except Exception as e:
79
+ print(f"Error: {e}")
80
+ return
81
+
82
+ interview_fn = conduct_interview(questions)
83
+
84
+ with gr.Blocks(css=".gradio-container { font-family: Arial, sans-serif; max-width: 700px; margin: auto; }") as demo:
85
+ gr.Markdown("## 🤖 HR Interview Chatbot")
86
+ chatbot = gr.Chatbot(label="HR Chatbot", type="messages")
87
+ user_input = gr.Textbox(label="💬 Your answer:", placeholder="Type your answer here and press Enter...", interactive=True)
88
+ start_button = gr.Button("Start Interview")
89
+ state = gr.State([])
90
+
91
+ def on_start(history):
92
+ return interview_fn("", history)
93
+
94
+ def on_submit(user_input, history):
95
+ history, new_input = interview_fn(user_input, history)
96
+ return history, ""
97
+
98
+ start_button.click(fn=on_start, inputs=[state], outputs=[chatbot, state])
99
+ user_input.submit(fn=on_submit, inputs=[user_input, state], outputs=[chatbot, state])
100
+
101
+ demo.launch(server_name="0.0.0.0", server_port=7860, debug=True)
102
+
103
+ if __name__ == "__main__":
104
+ start_hr_chatbot()
interview.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+
4
+ def read_questions_from_json(file_path):
5
+ """
6
+ Reads questions from a JSON file.
7
+ """
8
+ if not os.path.exists(file_path):
9
+ raise FileNotFoundError(f"The file '{file_path}' does not exist.")
10
+
11
+ with open(file_path, 'r') as f:
12
+ questions = json.load(f)
13
+
14
+ if not questions:
15
+ raise ValueError("The JSON file is empty or has invalid content.")
16
+
17
+ return questions
18
+
19
+ def conduct_interview(questions):
20
+ """
21
+ Conducts an interview by printing each question, taking input for the answer,
22
+ and storing the questions and answers in a list.
23
+ """
24
+ interview_data = []
25
+ print("\n--- Interview Started ---\n")
26
+
27
+ for question in questions:
28
+ print(f"{question}")
29
+ answer = input("Your answer: ").strip()
30
+ interview_data.append({"question": question, "answer": answer})
31
+
32
+ print("\n--- Interview Completed ---\n")
33
+ return interview_data
34
+
35
+ def save_interview_to_file(interview_data, file_path):
36
+ """
37
+ Saves the questions and answers to a text file.
38
+ """
39
+ with open(file_path, 'w') as f:
40
+ for entry in interview_data:
41
+ f.write(f"Q: {entry['question']}\n")
42
+ f.write(f"A: {entry['answer']}\n\n")
43
+
44
+ print(f"Interview saved to {file_path}")
45
+
46
+ if __name__ == "__main__":
47
+ QUESTIONS_FILE_PATH = "questions.json"
48
+ INTERVIEW_FILE_PATH = "interview.txt"
49
+
50
+ try:
51
+ # Read questions from JSON file
52
+ questions = read_questions_from_json(QUESTIONS_FILE_PATH)
53
+
54
+ # Conduct the interview
55
+ interview_data = conduct_interview(questions)
56
+
57
+ # Save the interview to a text file
58
+ save_interview_to_file(interview_data, INTERVIEW_FILE_PATH)
59
+
60
+ except Exception as e:
61
+ print(f"Error: {e}")
interview.txt ADDED
File without changes
knowledge_retrieval.py ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import fitz # PyMuPDF for PDF handling
3
+ from langchain_community.vectorstores import FAISS
4
+ from langchain_openai import OpenAIEmbeddings
5
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
6
+ from langchain.prompts import ChatPromptTemplate, PromptTemplate
7
+ from langchain.schema import Document, StrOutputParser
8
+ from langchain.chains.combine_documents.stuff import StuffDocumentsChain
9
+ from langchain.chains import RetrievalQA
10
+ from langchain.chains.llm import LLMChain
11
+ from langchain_core.runnables import RunnablePassthrough
12
+ from prompt_instructions import get_interview_prompt_hr, get_report_prompt_hr
13
+
14
+ # Function to load documents based on file type
15
+ def load_document(file_path):
16
+ ext = os.path.splitext(file_path)[1].lower()
17
+ if ext == ".txt":
18
+ with open(file_path, "r", encoding="utf-8") as f:
19
+ text = f.read()
20
+ return [Document(page_content=text, metadata={"source": file_path})]
21
+ elif ext == ".pdf":
22
+ try:
23
+ with fitz.open(file_path) as pdf:
24
+ text = ""
25
+ for page in pdf:
26
+ text += page.get_text()
27
+ return [Document(page_content=text, metadata={"source": file_path})]
28
+ except Exception as e:
29
+ raise RuntimeError(f"Error loading PDF file: {e}")
30
+ else:
31
+ raise RuntimeError(f"Unsupported file format: {ext}")
32
+
33
+ # Function to set up knowledge retrieval
34
+ def setup_knowledge_retrieval(llm, language='english', file_path=None):
35
+ embedding_model = OpenAIEmbeddings()
36
+
37
+ if file_path:
38
+ # Load and split the document
39
+ documents = load_document(file_path)
40
+ text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
41
+ texts = text_splitter.split_documents(documents)
42
+
43
+ # Create a new FAISS index from the document
44
+ faiss_index_path = "knowledge/faiss_index_hr_documents"
45
+ try:
46
+ documents_faiss_index = FAISS.from_documents(texts, embedding_model)
47
+ documents_faiss_index.save_local(faiss_index_path)
48
+ print(f"New FAISS vector store created and saved at {faiss_index_path}")
49
+ except Exception as e:
50
+ raise RuntimeError(f"Error during FAISS index creation: {e}")
51
+ else:
52
+ raise RuntimeError("No document provided for knowledge retrieval setup.")
53
+
54
+ documents_retriever = documents_faiss_index.as_retriever()
55
+
56
+ # Prompt template for the interview
57
+ interview_prompt_template = """
58
+ Use the following pieces of context to answer the question at the end.
59
+ If you don't know the answer, just say that you don't know, don't try to make up an answer.
60
+ Keep the answer as concise as possible.
61
+ {context}
62
+ Question: {question}
63
+ Helpful Answer:"""
64
+ interview_prompt = PromptTemplate.from_template(interview_prompt_template)
65
+
66
+ # Prompt template for the report
67
+ report_prompt_template = """
68
+ Use the following pieces of context to generate a report at the end.
69
+ If you don't know the answer, just say that you don't know, don't try to make up an answer.
70
+ Keep the answer as concise as possible.
71
+ {context}
72
+ Question: {question}
73
+ Helpful Answer:"""
74
+ report_prompt = PromptTemplate.from_template(report_prompt_template)
75
+
76
+ # Create RetrievalQA chains
77
+ interview_chain = RetrievalQA.from_chain_type(
78
+ llm=llm,
79
+ chain_type="stuff",
80
+ retriever=documents_retriever,
81
+ chain_type_kwargs={"prompt": interview_prompt}
82
+ )
83
+
84
+ report_chain = RetrievalQA.from_chain_type(
85
+ llm=llm,
86
+ chain_type="stuff",
87
+ retriever=documents_retriever,
88
+ chain_type_kwargs={"prompt": report_prompt}
89
+ )
90
+
91
+ return interview_chain, report_chain, documents_retriever
92
+
93
+ def get_next_response(interview_chain, message, history, question_count):
94
+ if question_count >= 5:
95
+ return "Thank you for your responses. I will now prepare a report."
96
+
97
+ if not interview_chain:
98
+ return "Error: Knowledge base not loaded. Please contact an admin."
99
+
100
+ # Generate the next question using RetrievalQA
101
+ response = interview_chain.invoke({"query": message})
102
+ next_question = response.get("result", "Could you provide more details on that?")
103
+
104
+ return next_question
105
+
106
+ def generate_report(report_chain, history, language):
107
+ combined_history = "\n".join(history)
108
+
109
+ # If report_chain is not available, return a fallback report
110
+ if not report_chain:
111
+ print("[DEBUG] Report chain not available. Generating a fallback HR report.")
112
+ fallback_report = f"""
113
+ HR Report in {language}:
114
+ Interview Summary:
115
+ {combined_history}
116
+
117
+ Assessment:
118
+ Based on the responses, the candidate's strengths, areas for improvement, and overall fit for the role have been noted. No additional knowledge-based insights due to missing vector database.
119
+ """
120
+ return fallback_report
121
+
122
+ # Generate report using the retrieval chain
123
+ result = report_chain.invoke({"query": f"Please provide an HR report based on the interview in {language}. Interview history: {combined_history}"})
124
+
125
+ return result.get("result", "Unable to generate report due to insufficient information.")
126
+
127
+ def get_initial_question(interview_chain):
128
+ if not interview_chain:
129
+ return "Please introduce yourself and tell me a little bit about your professional background."
130
+
131
+ result = interview_chain.invoke({"query": "What should be the first question in an HR interview?"})
132
+ return result.get("result", "Could you tell me a little bit about yourself and your professional background?")
133
+
134
+
135
+
m6.py ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ from collections import deque
4
+ from dotenv import load_dotenv
5
+ import gradio as gr
6
+ from langchain_openai import ChatOpenAI
7
+ from langchain.schema import HumanMessage, SystemMessage
8
+ from openai import OpenAI
9
+ import tempfile
10
+ import time
11
+
12
+ # Load environment variables
13
+ load_dotenv()
14
+
15
+ # Function to read questions from JSON
16
+ def read_questions_from_json(file_path):
17
+ if not os.path.exists(file_path):
18
+ raise FileNotFoundError(f"The file '{file_path}' does not exist.")
19
+
20
+ with open(file_path, 'r') as f:
21
+ questions_list = json.load(f)
22
+
23
+ if not questions_list:
24
+ raise ValueError("The JSON file is empty or has invalid content.")
25
+
26
+ return questions_list
27
+
28
+ # Function to convert text to speech
29
+ def convert_text_to_speech(text):
30
+ start_time = time.time()
31
+ try:
32
+ client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
33
+ response = client.audio.speech.create(model="tts-1", voice="alloy", input=text)
34
+
35
+ # Save the audio stream to a temporary file
36
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_file:
37
+ for chunk in response.iter_bytes():
38
+ tmp_file.write(chunk)
39
+ temp_audio_path = tmp_file.name
40
+
41
+ print(f"DEBUG - Text-to-speech conversion time: {time.time() - start_time:.2f} seconds")
42
+ return temp_audio_path
43
+
44
+ except Exception as e:
45
+ print(f"Error during text-to-speech conversion: {e}")
46
+ return None
47
+
48
+ # Function to transcribe audio
49
+ def transcribe_audio(audio_file_path):
50
+ start_time = time.time()
51
+ try:
52
+ client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
53
+ with open(audio_file_path, "rb") as audio_file:
54
+ transcription = client.audio.transcriptions.create(
55
+ model="whisper-1",
56
+ file=audio_file
57
+ )
58
+ print(f"DEBUG - Audio transcription time: {time.time() - start_time:.2f} seconds")
59
+ return transcription.text
60
+ except Exception as e:
61
+ print(f"Error during audio transcription: {e}")
62
+ return None
63
+
64
+ # Conduct interview and handle user input
65
+ def conduct_interview(questions, language="English", history_limit=5):
66
+ start_time = time.time()
67
+ openai_api_key = os.getenv("OPENAI_API_KEY")
68
+ if not openai_api_key:
69
+ raise RuntimeError("OpenAI API key not found. Please add it to your .env file as OPENAI_API_KEY.")
70
+
71
+ chat = ChatOpenAI(
72
+ openai_api_key=openai_api_key, model="gpt-4o", temperature=0.7, max_tokens=750
73
+ )
74
+
75
+ conversation_history = deque(maxlen=history_limit)
76
+ system_prompt = (f"You are Sarah, an empathetic HR interviewer conducting a technical interview in {language}. "
77
+ "Respond to user follow-up questions politely and concisely. If the user is confused, provide clear clarification.")
78
+
79
+ interview_data = []
80
+ current_question_index = [0] # Use a list to hold the index
81
+ is_interview_finished = False
82
+
83
+ initial_message = ("👋 Hi there, I'm Sarah, your friendly AI HR assistant! "
84
+ "I'll guide you through a series of interview questions to learn more about you. "
85
+ "Take your time and answer each question thoughtfully.")
86
+ final_message = "That wraps up our interview. Thank you so much for your responses—it's been great learning more about you!"
87
+ print(f"DEBUG - conduct_interview setup time: {time.time() - start_time:.2f} seconds")
88
+
89
+ def interview_step(user_input, audio_input, history):
90
+ nonlocal current_question_index
91
+ nonlocal is_interview_finished
92
+
93
+ step_start_time = time.time()
94
+
95
+ # Transcribe audio input if provided
96
+ if audio_input:
97
+ user_input = transcribe_audio(audio_input)
98
+ print("Transcription:", user_input)
99
+
100
+ if user_input.lower() in ["exit", "quit"]:
101
+ history.append({"role": "assistant", "content": "The interview has ended at your request. Thank you for your time!"})
102
+ is_interview_finished = True
103
+ return history, "", None
104
+
105
+ # If interview is finished, do nothing
106
+ if is_interview_finished:
107
+ return history, "", None
108
+
109
+ question_text = questions[current_question_index[0]]
110
+ history_content = "\n".join([f"Q: {entry['question']}\nA: {entry['answer']}" for entry in conversation_history])
111
+ combined_prompt = (f"{system_prompt}\n\nPrevious conversation history:\n{history_content}\n\n"
112
+ f"Current question: {question_text}\nUser's input: {user_input}\n\n"
113
+ "Respond in a warm and conversational way, offering natural follow-ups if needed.")
114
+
115
+ messages = [
116
+ SystemMessage(content=system_prompt),
117
+ HumanMessage(content=combined_prompt)
118
+ ]
119
+
120
+ chat_start_time = time.time()
121
+ response = chat.invoke(messages)
122
+ print(f"DEBUG - Chat response time: {time.time() - chat_start_time:.2f} seconds")
123
+ response_content = response.content.strip()
124
+
125
+ # Convert response to speech
126
+ audio_file_path = convert_text_to_speech(response_content)
127
+
128
+ conversation_history.append({"question": question_text, "answer": user_input})
129
+ interview_data.append({"question": question_text, "answer": user_input})
130
+
131
+ # Use the correct format for messages
132
+ history.append({"role": "user", "content": user_input})
133
+ history.append({"role": "assistant", "content": response_content})
134
+
135
+ if current_question_index[0] + 1 < len(questions):
136
+ current_question_index[0] += 1
137
+ next_question = f"Alright, let's move on. {questions[current_question_index[0]]}"
138
+ next_question_audio_path = convert_text_to_speech(next_question)
139
+ history.append({"role": "assistant", "content": next_question})
140
+ print(f"DEBUG - Interview step time: {time.time() - step_start_time:.2f} seconds")
141
+ return history, "", next_question_audio_path
142
+
143
+ else:
144
+ # Convert final message to speech and play it
145
+ final_message_audio_path = convert_text_to_speech(final_message)
146
+ history.append({"role": "assistant", "content": final_message})
147
+
148
+ # Convert the last question to speech
149
+ last_question_audio_path = convert_text_to_speech(questions[current_question_index[0]])
150
+ is_interview_finished = True
151
+ print(f"DEBUG - Interview step time: {time.time() - step_start_time:.2f} seconds")
152
+ return history, "", last_question_audio_path
153
+
154
+ return interview_step, initial_message, final_message
155
+
156
+ # Gradio interface
157
+ def main():
158
+ QUESTIONS_FILE_PATH = "questions.json" # Ensure you have a questions.json file with your interview questions
159
+
160
+ try:
161
+ questions = read_questions_from_json(QUESTIONS_FILE_PATH)
162
+ interview_func, initial_message, final_message = conduct_interview(questions)
163
+
164
+ css = """
165
+ .contain { display: flex; flex-direction: column; }
166
+ .gradio-container { height: 100vh !important; }
167
+ #component-0 { height: 100%; }
168
+ .chatbot { flex-grow: 1; overflow: auto; height: 100px; }
169
+ .chatbot .wrap.svelte-1275q59.wrap.svelte-1275q59 {flex-wrap : nowrap !important}
170
+ .user > div > .message {background-color : #dcf8c6 !important}
171
+ .bot > div > .message {background-color : #f7f7f8 !important}
172
+ """
173
+
174
+ with gr.Blocks(css=css) as demo:
175
+ gr.Markdown("""
176
+ <h1 style='text-align: center; margin-bottom: 1rem'>👋 Welcome to Your AI HR Interview Assistant</h1>
177
+ """)
178
+
179
+ start_btn = gr.Button("Start Interview", variant="primary")
180
+
181
+ gr.Markdown("""
182
+ <p style='text-align: center; margin-bottom: 1rem'>I will ask you a series of questions. Please answer honestly and thoughtfully. When you are ready, click "Start Interview" to begin.</p>
183
+ """)
184
+
185
+ chatbot = gr.Chatbot(label="Interview Chat", elem_id="chatbot", height=650, type='messages')
186
+ audio_input = gr.Audio(sources=["microphone"], type="filepath", label="Record Your Answer")
187
+ user_input = gr.Textbox(label="Your Response", placeholder="Type your answer here or use the microphone...", lines=1)
188
+
189
+ audio_output = gr.Audio(label="Response Audio", autoplay=True)
190
+
191
+ with gr.Row():
192
+ submit_btn = gr.Button("Submit", variant="primary")
193
+ clear_btn = gr.Button("Clear Chat")
194
+
195
+ def start_interview():
196
+ history = []
197
+
198
+ # Convert and play initial message
199
+ start_time = time.time()
200
+ initial_audio_path = convert_text_to_speech(initial_message)
201
+
202
+ # Combine initial message and first question
203
+ first_question = "Let's begin! Here's your first question: " + questions[0]
204
+ combined_message = initial_message + " " + first_question
205
+
206
+ # Convert combined message to speech
207
+ combined_audio_path = convert_text_to_speech(combined_message)
208
+
209
+ history.append({"role": "assistant", "content": combined_message})
210
+
211
+ print(f"DEBUG - Initial message audio time: {time.time() - start_time:.2f} seconds")
212
+
213
+ return history, "", combined_audio_path
214
+
215
+ def clear_interview():
216
+ # Reset the interview state
217
+ interview_func, initial_message, final_message = conduct_interview(questions)
218
+
219
+ return [], "", None
220
+
221
+ def interview_step_wrapper(user_response, audio_response, history):
222
+ history, _, audio_path = interview_func(user_response, audio_response, history)
223
+ time.sleep(0.1) # Reduced delay
224
+ return history, "", audio_path
225
+
226
+ def on_enter_submit(history, user_response):
227
+ if not user_response.strip():
228
+ return history, "", None
229
+ history, _, audio_path = interview_step_wrapper(user_response, None, history)
230
+ time.sleep(0.1) # Reduced delay
231
+ return history, "", audio_path
232
+
233
+ audio_input.stop_recording(interview_step_wrapper, inputs=[user_input, audio_input, chatbot], outputs=[chatbot, user_input, audio_output])
234
+ start_btn.click(start_interview, inputs=[], outputs=[chatbot, user_input, audio_output])
235
+ submit_btn.click(interview_step_wrapper, inputs=[user_input, audio_input, chatbot], outputs=[chatbot, user_input, audio_output])
236
+ user_input.submit(on_enter_submit, inputs=[chatbot, user_input], outputs=[chatbot, user_input, audio_output])
237
+ clear_btn.click(clear_interview, inputs=[], outputs=[chatbot, user_input, audio_output])
238
+
239
+ demo.launch()
240
+
241
+ except Exception as e:
242
+ print(f"Error: {e}")
243
+
244
+ if __name__ == "__main__":
245
+ main()
professional_machine_learning_engineer_exam_guide_english.pdf ADDED
Binary file (217 kB). View file
 
prompt_instructions.py ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime
2
+
3
+ current_datetime = datetime.now()
4
+ current_date = current_datetime.strftime("%Y-%m-%d")
5
+
6
+ # Initial Interview Messages
7
+ def get_interview_initial_message_hr(n_of_questions):
8
+ return f"""Hello, I'm an AI HR assistant. I'll be conducting this interview.
9
+ I will ask you about {n_of_questions} questions.
10
+ Please answer truthfully and to the best of your ability.
11
+ Could you please tell me which language you prefer to use for this interview?"""
12
+
13
+ def get_interview_initial_message_sarah(n_of_questions):
14
+ return f"""Hello, I'm Sarah, an AI assistant for technical interviews.
15
+ I will guide you through the process and ask you around {n_of_questions} questions.
16
+ Please feel free to share as much or as little as you're comfortable with."""
17
+
18
+ def get_interview_initial_message_aaron(n_of_questions):
19
+ return f"""Hello, I'm Aaron, an AI interviewer for behavioral and leadership assessments.
20
+ I will be asking you approximately {n_of_questions} questions. Be concise and direct in your responses.
21
+ Let's begin!"""
22
+
23
+ # HR Interview Prompts
24
+ def get_interview_prompt_hr(language, n_of_questions):
25
+ return f"""You are an AI HR interviewer, conducting an interview in {language}.
26
+ Use the following context and interview history to guide your response:
27
+
28
+ Context from knowledge base: {{context}}
29
+
30
+ Previous interview history:
31
+ {{history}}
32
+
33
+ Current question number: {{question_number}}/{n_of_questions}
34
+
35
+ Respond to the candidate's input briefly and directly in {language}.
36
+ Ask specific, detailed questions relevant to the job and the candidate's experience.
37
+ Remember all the previous answers given by the candidate.
38
+ If the candidate asks about a previous question, answer like an HR professional and then continue with the next question.
39
+ Keep in mind that you have a total of {n_of_questions} questions.
40
+ After {n_of_questions} interactions, indicate that you will prepare a report based on the gathered information and the provided document.
41
+ """
42
+
43
+ def get_interview_prompt_sarah_v3(language, index, n_of_questions):
44
+ return f"""You are Sarah, an empathic and compassionate HR interviewer conducting an interview in {language}.
45
+ Use the following context and interview history to guide your response:
46
+
47
+ Previous interview history:
48
+ {{history}}
49
+
50
+ Current question number: {index + 1}/{n_of_questions}
51
+
52
+ Respond directly in {language}. Ask a specific, professional HR-related question.
53
+ You must remember all the previous answers given by the candidate, and use this information if necessary.
54
+ Keep the tone professional but approachable.
55
+ Here's your question: {{question}}
56
+ """
57
+
58
+ def get_interview_prompt_aaron(language, n_of_questions):
59
+ return f"""You are Aaron, a direct, results-oriented interviewer conducting a professional interview in {language}.
60
+ Use the following context and interview history to guide your response:
61
+
62
+ Previous interview history:
63
+ {{history}}
64
+
65
+ Current question number: {{question_number}}/{n_of_questions}
66
+
67
+ Respond directly in {language}. Ask a precise, results-focused question that helps evaluate the candidate's suitability for the role.
68
+ Remember all the previous answers given by the candidate.
69
+ Keep the tone professional and efficient.
70
+ """
71
+
72
+ # Default HR Questions for Non-Technical Interviews
73
+ def get_default_hr_questions(index):
74
+ default_questions = [
75
+ "Can you please introduce yourself and share a bit about your professional background?",
76
+ "What are your career goals for the next few years?",
77
+ "Why did you apply for this position, and what excites you about this role?",
78
+ "Can you describe a challenging situation you’ve faced at work and how you handled it?",
79
+ "How do you prioritize tasks when you have multiple deadlines to meet?",
80
+ "Can you provide an example of a time when you worked in a team to achieve a common goal?",
81
+ "What is your preferred style of communication when working with your team or manager?",
82
+ "How do you handle constructive feedback and what’s a time you’ve grown from it?",
83
+ "What do you consider your greatest strengths and areas for improvement?",
84
+ "Is there anything you'd like to ask us or share that wasn’t covered in the interview?"
85
+ ]
86
+ if 0 <= index - 1 < len(default_questions):
87
+ return default_questions[index - 1]
88
+ return "That's all for now. Thank you for your time!"
89
+
90
+ # Report Prompts
91
+ def get_report_prompt_hr(language):
92
+ return f"""You are an HR professional preparing a report in {language}.
93
+ Use the following context and interview history to create your report:
94
+
95
+ Context from knowledge base: {{context}}
96
+
97
+ Complete interview history:
98
+ {{history}}
99
+
100
+ Prepare a brief report in {language} based strictly on the information gathered during the interview and the provided document.
101
+ Date: {current_date}
102
+
103
+ Report Structure:
104
+
105
+ Candidate Overview:
106
+ - Name (if provided)
107
+ - Position applied for (if discernible from context)
108
+
109
+ Assessment Summary:
110
+ - Key strengths based on the interview
111
+ - Areas of concern or further development
112
+ - Overall suitability for the role based on responses and provided document
113
+
114
+ Candidate's Experience and Skills:
115
+ - Relevant experience highlighted by the candidate
116
+ - Skills demonstrated during the interview
117
+ - Alignment with job requirements (based on the provided document)
118
+
119
+ Candidate's Responses:
120
+ - Communication skills
121
+ - Problem-solving abilities
122
+ - Behavioral traits observed
123
+
124
+ Recommendations:
125
+ - Next steps in the hiring process (e.g., further interviews, assessments)
126
+ - Any specific training or development if the candidate were to be hired
127
+
128
+ Concluding Remarks:
129
+ - Overall impression of the candidate
130
+ - Potential fit within the company culture
131
+
132
+ Ensure all sections are concise, focused, and evidence-based.
133
+ Avoid making assumptions and base any conclusions on the facts derived from the candidate's interview and the provided document.
134
+ """
135
+
136
+ def get_report_prompt(language):
137
+ return f"""You are a technical interviewer preparing a report in {language}.
138
+ Use the following context and interview history to create your report:
139
+
140
+ Complete interview history:
141
+ {{history}}
142
+
143
+ Prepare a concise technical report based on the gathered information, including:
144
+ - Summary of the candidate’s technical knowledge
145
+ - Strengths and areas of improvement
146
+ - Recommendations for next steps in the hiring process
147
+ Date: {current_date}
148
+
149
+ Keep the report objective, fact-based, and focused on technical evaluation.
150
+ """
151
+
152
+
153
+ # prompt_instructions.py
154
+
155
+ # HR Interview Prompts
156
+ def get_interview_prompt_technical(language, n_of_questions, question):
157
+ return f"""You are an AI Technical Interviewer conducting an interview in {language}.
158
+ Please follow these guidelines:
159
+
160
+ Current question number: {{question_number}}/{n_of_questions}
161
+
162
+ Respond to the candidate's input briefly and directly in {language}.
163
+ Pose the following technical question to the candidate:
164
+ {question}
165
+ """
166
+
167
+
168
+ def get_interview_initial_message_hr(n_of_questions):
169
+ return f"""Hello, I'm your AI assistant. I'll be conducting this interview.
170
+ I will ask you {n_of_questions} questions to learn more about you.
171
+ Take your time and answer each question thoughtfully."""
172
+
173
+
174
+ # Example usage for testing:
175
+ if __name__ == "__main__":
176
+ print(get_interview_initial_message_hr(5))
177
+ print(get_default_hr_questions(1))
178
+
179
+
180
+
181
+
questions.py ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ from dotenv import load_dotenv
4
+ import fitz # PyMuPDF
5
+ from langchain_openai import ChatOpenAI # Correct import from langchain-openai
6
+ from langchain.schema import HumanMessage, SystemMessage # For creating structured chat messages
7
+
8
+ QUESTIONS_PATH = "questions.json"
9
+
10
+ # Load environment variables
11
+ load_dotenv()
12
+
13
+ def split_text_into_chunks(text: str, chunk_size: int) -> list:
14
+ """
15
+ Splits the text into chunks of a specified maximum size.
16
+ """
17
+ # Trim the text to remove leading/trailing whitespace and reduce multiple spaces to a single space
18
+ cleaned_text = " ".join(text.split())
19
+ words = cleaned_text.split(" ")
20
+
21
+ chunks = []
22
+ current_chunk = []
23
+ current_length = 0
24
+
25
+ for word in words:
26
+ if current_length + len(word) + 1 > chunk_size:
27
+ chunks.append(" ".join(current_chunk))
28
+ current_chunk = [word]
29
+ current_length = len(word)
30
+ else:
31
+ current_chunk.append(word)
32
+ current_length += len(word) + 1
33
+
34
+ if current_chunk:
35
+ chunks.append(" ".join(current_chunk))
36
+
37
+ return chunks
38
+
39
+
40
+ def distribute_questions_across_chunks(n_chunks: int, n_questions: int) -> list:
41
+ """
42
+ Distributes a specified number of questions across a specified number of chunks.
43
+ """
44
+ questions_per_chunk = [1] * min(n_chunks, n_questions)
45
+ remaining_questions = n_questions - len(questions_per_chunk)
46
+
47
+ if remaining_questions > 0:
48
+ for i in range(len(questions_per_chunk)):
49
+ if remaining_questions == 0:
50
+ break
51
+ questions_per_chunk[i] += 1
52
+ remaining_questions -= 1
53
+
54
+ while len(questions_per_chunk) < n_chunks:
55
+ questions_per_chunk.append(0)
56
+
57
+ return questions_per_chunk
58
+
59
+
60
+ def extract_text_from_pdf(pdf_path):
61
+ text = ""
62
+ try:
63
+ print(f"[DEBUG] Opening PDF: {pdf_path}")
64
+ with fitz.open(pdf_path) as pdf:
65
+ print(f"[DEBUG] Extracting text from PDF: {pdf_path}")
66
+ for page in pdf:
67
+ text += page.get_text()
68
+ except Exception as e:
69
+ print(f"Error reading PDF: {e}")
70
+ raise RuntimeError("Unable to extract text from PDF.")
71
+ return text
72
+
73
+
74
+ def generate_questions_from_text(text, n_questions=5):
75
+ openai_api_key = os.getenv("OPENAI_API_KEY")
76
+
77
+ if not openai_api_key:
78
+ raise RuntimeError(
79
+ "OpenAI API key not found. Please add it to your .env file as OPENAI_API_KEY."
80
+ )
81
+
82
+ chat = ChatOpenAI(
83
+ openai_api_key=openai_api_key, model="gpt-4", temperature=0.7, max_tokens=750
84
+ )
85
+
86
+ messages = [
87
+ SystemMessage(
88
+ content="You are an expert interviewer who generates concise technical interview questions. Do not enumerate the questions. Answer only with questions."
89
+ ),
90
+ HumanMessage(
91
+ content=f"Based on the following content, generate {n_questions} technical interview questions:\n{text}"
92
+ ),
93
+ ]
94
+
95
+ try:
96
+ print(f"[DEBUG] Sending request to OpenAI with {n_questions} questions.")
97
+ response = chat.invoke(messages)
98
+ questions = response.content.strip().split("\n\n")
99
+ questions = [q.strip() for q in questions if q.strip()]
100
+ except Exception as e:
101
+ print(f"[ERROR] Failed to generate questions: {e}")
102
+ questions = ["An error occurred while generating questions."]
103
+
104
+ return questions
105
+
106
+
107
+ def save_questions(questions):
108
+ with open(QUESTIONS_PATH, "w") as f:
109
+ json.dump(questions, f, indent=4)
110
+
111
+
112
+ def generate_and_save_questions_from_pdf(pdf_path, total_questions=5):
113
+ print(f"[INFO] Generating questions from PDF: {pdf_path}")
114
+ pdf_text = extract_text_from_pdf(pdf_path)
115
+
116
+ if not pdf_text.strip():
117
+ raise RuntimeError("The PDF content is empty or could not be read.")
118
+
119
+ chunk_size = 2000
120
+ chunks = split_text_into_chunks(pdf_text, chunk_size)
121
+ n_chunks = len(chunks)
122
+
123
+ questions_distribution = distribute_questions_across_chunks(n_chunks, total_questions)
124
+ combined_questions = []
125
+
126
+ for i, (chunk, n_questions) in enumerate(zip(chunks, questions_distribution)):
127
+ print(f"[DEBUG] Processing chunk {i + 1} of {n_chunks}")
128
+ if n_questions > 0:
129
+ questions = generate_questions_from_text(chunk, n_questions=n_questions)
130
+ combined_questions.extend(questions)
131
+
132
+ print(f"[INFO] Total questions generated: {len(combined_questions)}")
133
+ save_questions(combined_questions)
134
+ print(f"[INFO] Questions saved to {QUESTIONS_PATH}")
135
+ return combined_questions
136
+
137
+
138
+ if __name__ == "__main__":
139
+ pdf_path = "professional_machine_learning_engineer_exam_guide_english.pdf"
140
+
141
+ try:
142
+ generated_questions = generate_and_save_questions_from_pdf(
143
+ pdf_path, total_questions=5
144
+ )
145
+ print(f"Generated Questions:\n{json.dumps(generated_questions, indent=2)}")
146
+ except Exception as e:
147
+ print(f"Failed to generate questions: {e}")
questionsgr.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from questions import generate_and_save_questions_from_pdf
3
+
4
+ def generate_questions(pdf_file, num_questions):
5
+ """
6
+ Generates questions from a PDF file using the questions.py script.
7
+
8
+ Args:
9
+ pdf_file: The PDF file to generate questions from.
10
+ num_questions: The number of questions to generate.
11
+
12
+ Returns:
13
+ A string indicating success or failure, and a list of generated questions.
14
+ """
15
+ try:
16
+ questions = generate_and_save_questions_from_pdf(pdf_file.name, total_questions=int(num_questions))
17
+ return f"✅ {len(questions)} questions generated and saved.", questions
18
+ except Exception as e:
19
+ return f"❌ Error: {e}", None
20
+
21
+
22
+ with gr.Blocks() as demo:
23
+ gr.Markdown("## 📄 PDF Question Generator")
24
+ with gr.Row():
25
+ pdf_input = gr.File(label="Upload PDF File", type="filepath") # Changed type to "filepath"
26
+ num_questions_input = gr.Number(label="Number of Questions", value=5)
27
+ generate_button = gr.Button("Generate Questions")
28
+ output_text = gr.Textbox(label="Output")
29
+ question_output = gr.JSON(label="Generated Questions")
30
+
31
+ generate_button.click(
32
+ generate_questions,
33
+ inputs=[pdf_input, num_questions_input],
34
+ outputs=[output_text, question_output]
35
+ )
36
+
37
+ if __name__ == "__main__":
38
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ python-dotenv==1.0.1
2
+ pandas==2.1.4
3
+ langchain==0.2.6
4
+ langchain-openai==0.1.14
5
+ langchain-core==0.2.11
6
+ langchain-ibm==0.1.8
7
+ langchain-community==0.2.6
8
+ ibm-watson-machine-learning==1.0.359
9
+ ipykernel
10
+ notebook
11
+ urllib3
12
+ requests==2.32.0
13
+ gradio
14
+ PyPDF2
15
+ python-docx
16
+ reportlab
17
+ openai
18
+ faiss-cpu
19
+ cryptography
20
+ pymysql
21
+ scikit-learn
22
+ pymupdf
23
+
requirements_dev.txt ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ aiofiles==23.2.1
2
+ aiohappyeyeballs==2.4.4
3
+ aiohttp==3.11.11
4
+ aiosignal==1.3.2
5
+ annotated-types==0.7.0
6
+ anyio==4.7.0
7
+ argon2-cffi==23.1.0
8
+ argon2-cffi-bindings==21.2.0
9
+ arrow==1.3.0
10
+ asttokens==2.4.1
11
+ async-lru==2.0.4
12
+ attrs==24.3.0
13
+ babel==2.16.0
14
+ beautifulsoup4==4.12.3
15
+ bleach==6.2.0
16
+ certifi==2024.12.14
17
+ cffi==1.17.1
18
+ chardet==5.2.0
19
+ charset-normalizer==3.4.1
20
+ click==8.1.8
21
+ colorama==0.4.6
22
+ comm==0.2.2
23
+ cryptography==44.0.0
24
+ dataclasses-json==0.6.7
25
+ debugpy==1.8.1
26
+ decorator==5.1.1
27
+ defusedxml==0.7.1
28
+ distro==1.9.0
29
+ executing==2.0.1
30
+ faiss-cpu==1.9.0.post1
31
+ fastapi==0.115.6
32
+ fastjsonschema==2.21.1
33
+ ffmpy==0.5.0
34
+ filelock==3.16.1
35
+ fqdn==1.5.1
36
+ frozenlist==1.5.0
37
+ fsspec==2024.12.0
38
+ gradio==5.9.1
39
+ gradio_client==1.5.2
40
+ greenlet==3.1.1
41
+ h11==0.14.0
42
+ httpcore==1.0.7
43
+ httpx==0.28.1
44
+ huggingface-hub==0.27.0
45
+ ibm-cos-sdk==2.13.6
46
+ ibm-cos-sdk-core==2.13.6
47
+ ibm-cos-sdk-s3transfer==2.13.6
48
+ ibm_watson_machine_learning==1.0.359
49
+ ibm_watsonx_ai==1.1.26
50
+ idna==3.10
51
+ importlib_metadata==8.5.0
52
+ ipykernel==6.29.4
53
+ ipython==8.25.0
54
+ isoduration==20.11.0
55
+ jedi==0.19.1
56
+ Jinja2==3.1.5
57
+ jiter==0.8.2
58
+ jmespath==1.0.1
59
+ joblib==1.4.2
60
+ json5==0.10.0
61
+ jsonpatch==1.33
62
+ jsonpointer==3.0.0
63
+ jsonschema==4.23.0
64
+ jsonschema-specifications==2024.10.1
65
+ jupyter-events==0.11.0
66
+ jupyter-lsp==2.2.5
67
+ jupyter_client==8.6.2
68
+ jupyter_core==5.7.2
69
+ jupyter_server==2.15.0
70
+ jupyter_server_terminals==0.5.3
71
+ jupyterlab==4.3.4
72
+ jupyterlab_pygments==0.3.0
73
+ jupyterlab_server==2.27.3
74
+ langchain==0.2.6
75
+ langchain-community==0.2.6
76
+ langchain-core==0.2.11
77
+ langchain-ibm==0.1.8
78
+ langchain-openai==0.1.14
79
+ langchain-text-splitters==0.2.2
80
+ langsmith==0.1.147
81
+ lomond==0.3.3
82
+ lxml==5.3.0
83
+ markdown-it-py==3.0.0
84
+ MarkupSafe==2.1.5
85
+ marshmallow==3.23.3
86
+ matplotlib-inline==0.1.7
87
+ mdurl==0.1.2
88
+ mistune==3.1.0
89
+ multidict==6.1.0
90
+ mypy-extensions==1.0.0
91
+ nbclient==0.10.2
92
+ nbconvert==7.16.5
93
+ nbformat==5.10.4
94
+ nest-asyncio==1.6.0
95
+ notebook==7.3.2
96
+ notebook_shim==0.2.4
97
+ numpy==1.26.4
98
+ openai==1.59.2
99
+ orjson==3.10.13
100
+ overrides==7.7.0
101
+ packaging==24.1
102
+ pandas==2.1.4
103
+ pandocfilters==1.5.1
104
+ parso==0.8.4
105
+ pillow==11.1.0
106
+ platformdirs==4.2.2
107
+ prometheus_client==0.21.1
108
+ prompt_toolkit==3.0.47
109
+ propcache==0.2.1
110
+ psutil==6.0.0
111
+ pure-eval==0.2.2
112
+ pycparser==2.22
113
+ pydantic==2.10.4
114
+ pydantic_core==2.27.2
115
+ pydub==0.25.1
116
+ Pygments==2.18.0
117
+ PyMuPDF==1.25.1
118
+ PyMySQL==1.1.1
119
+ PyPDF2==3.0.1
120
+ python-dateutil==2.9.0.post0
121
+ python-docx==1.1.2
122
+ python-dotenv==1.0.1
123
+ python-json-logger==3.2.1
124
+ python-multipart==0.0.20
125
+ pytz==2024.2
126
+ pywin32==306
127
+ pywinpty==2.0.14
128
+ PyYAML==6.0.2
129
+ pyzmq==26.0.3
130
+ referencing==0.35.1
131
+ regex==2024.11.6
132
+ reportlab==4.2.5
133
+ requests==2.32.0
134
+ requests-toolbelt==1.0.0
135
+ rfc3339-validator==0.1.4
136
+ rfc3986-validator==0.1.1
137
+ rich==13.9.4
138
+ rpds-py==0.22.3
139
+ ruff==0.8.5
140
+ safehttpx==0.1.6
141
+ scikit-learn==1.6.0
142
+ scipy==1.15.0
143
+ semantic-version==2.10.0
144
+ Send2Trash==1.8.3
145
+ setuptools==75.1.0
146
+ shellingham==1.5.4
147
+ six==1.16.0
148
+ sniffio==1.3.1
149
+ soupsieve==2.6
150
+ SQLAlchemy==2.0.36
151
+ stack-data==0.6.3
152
+ starlette==0.41.3
153
+ tabulate==0.9.0
154
+ tenacity==8.5.0
155
+ terminado==0.18.1
156
+ threadpoolctl==3.5.0
157
+ tiktoken==0.8.0
158
+ tinycss2==1.4.0
159
+ tomlkit==0.13.2
160
+ tornado==6.4.1
161
+ tqdm==4.67.1
162
+ traitlets==5.14.3
163
+ typer==0.15.1
164
+ types-python-dateutil==2.9.0.20241206
165
+ typing-inspect==0.9.0
166
+ typing_extensions==4.12.2
167
+ tzdata==2024.2
168
+ uri-template==1.3.0
169
+ urllib3==2.3.0
170
+ uvicorn==0.34.0
171
+ wcwidth==0.2.13
172
+ webcolors==24.11.1
173
+ webencodings==0.5.1
174
+ websocket-client==1.8.0
175
+ websockets==14.1
176
+ wheel==0.44.0
177
+ yarl==1.18.3
178
+ zipp==3.21.0
response.py ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def bot_response(history):
2
+ if not interview_state.interview_history:
3
+
4
+ reset_interview_action(interview_state.selected_interviewer)
5
+
6
+ if interview_state.interview_history[-1]["role"] == "user":
7
+ interview_state.question_count += 1
8
+
9
+
10
+ voice = interview_state.get_voice_setting()
11
+
12
+ if interview_state.question_count > interview_state.n_of_questions:
13
+ response = "That's all for now. Thank you for your time!"
14
+ interview_state.interview_finished = True
15
+
16
+ else:
17
+ # Select prompts based on interview type
18
+ if interview_state.interview_type == "hr":
19
+ if not interview_state.knowledge_retrieval_setup:
20
+ response = get_default_hr_questions(
21
+ interview_state.question_count
22
+ )
23
+ else:
24
+ if interview_state.question_count == 1:
25
+ response = get_initial_question(
26
+ interview_state.interview_chain
27
+ )
28
+ else:
29
+ response = get_next_response(
30
+ interview_state.interview_chain,
31
+ interview_state.interview_history[-1]["content"] if interview_state.interview_history[-1]["role"] == "user" else "",
32
+ [
33
+ msg["content"]
34
+ for msg in interview_state.interview_history
35
+ if msg.get("role") == "user"
36
+ ],
37
+ interview_state.question_count,
38
+ )
39
+ elif interview_state.interview_type == "sarah":
40
+ response = get_next_response(
41
+ interview_state.interview_chain,
42
+ interview_state.interview_history[-1]["content"] if interview_state.interview_history[-1]["role"] == "user" else "",
43
+ [
44
+ msg["content"]
45
+ for msg in interview_state.interview_history
46
+ if msg.get("role") == "user"
47
+ ],
48
+ interview_state.question_count,
49
+ )
50
+ elif interview_state.interview_type == "aaron":
51
+ response = get_next_response(
52
+ interview_state.interview_chain,
53
+ interview_state.interview_history[-1]["content"] if interview_state.interview_history[-1]["role"] == "user" else "",
54
+ [
55
+ msg["content"]
56
+ for msg in interview_state.interview_history
57
+ if msg.get("role") == "user"
58
+ ],
59
+ interview_state.question_count,
60
+ )
61
+
62
+ else:
63
+ response = "Invalid interview type."
64
+
65
+ audio_buffer = BytesIO()
66
+ convert_text_to_speech(response, audio_buffer, voice)
67
+ audio_buffer.seek(0)
68
+ with tempfile.NamedTemporaryFile(
69
+ suffix=".mp3", delete=False
70
+ ) as temp_file:
71
+ temp_audio_path = temp_file.name
72
+ temp_file.write(audio_buffer.getvalue())
73
+ interview_state.temp_audio_files.append(temp_audio_path)
74
+
75
+
76
+ history.append({"role": "assistant", "content": response})
77
+ interview_state.interview_history.append({"role": "assistant", "content": response})
78
+
79
+ if interview_state.interview_finished:
80
+
81
+ conclusion_message = "Thank you for being here. We will review your responses and provide feedback soon."
82
+ history.append(
83
+ {"role": "system", "content": conclusion_message}
84
+ )
85
+ interview_state.interview_history.append({"role": "system", "content": conclusion_message})
86
+
87
+ txt_path = save_interview_history(
88
+ [msg["content"] for msg in history if msg["role"] != "system"], interview_state.language
89
+ )
90
+ if txt_path:
91
+ return (
92
+ history,
93
+ gr.Audio(
94
+ value=temp_audio_path,
95
+ autoplay=True,
96
+ visible=True,
97
+ ),
98
+ gr.File(visible=True, value=txt_path),
99
+ gr.Textbox(interactive=False)
100
+ )
101
+ else:
102
+ return (
103
+ history,
104
+ gr.Audio(
105
+ value=temp_audio_path,
106
+ autoplay=True,
107
+ visible=True,
108
+ ),
109
+ None,
110
+ gr.Textbox(interactive=False)
111
+ )
112
+
113
+ return (
114
+ history,
115
+ gr.Audio(
116
+ value=temp_audio_path, autoplay=True, visible=True
117
+ ),
118
+ None,
119
+ gr.Textbox(interactive=True)
120
+ )
settings.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # Interview settings
2
+ language = "english" # Default language
3
+ n_of_questions = 5 # Default number of questions
split.py ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Tuple
2
+ import math
3
+
4
+ def split_text_into_chunks(text: str, chunk_size: int) -> List[str]:
5
+ """
6
+ Splits the text into chunks of a specified maximum size.
7
+ """
8
+ # Trim the text to remove leading/trailing whitespace and reduce multiple spaces to a single space
9
+ cleaned_text = " ".join(text.split())
10
+ words = cleaned_text.split(" ")
11
+
12
+ chunks = []
13
+ current_chunk = []
14
+ current_length = 0
15
+
16
+ for word in words:
17
+ if current_length + len(word) + 1 > chunk_size:
18
+ chunks.append(" ".join(current_chunk))
19
+ current_chunk = [word]
20
+ current_length = len(word)
21
+ else:
22
+ current_chunk.append(word)
23
+ current_length += len(word) + 1
24
+
25
+ if current_chunk:
26
+ chunks.append(" ".join(current_chunk))
27
+
28
+ return chunks
29
+
30
+
31
+ def distribute_questions_across_chunks(n_chunks: int, n_questions: int) -> List[int]:
32
+ """
33
+ Distributes a specified number of questions across a specified number of chunks.
34
+ """
35
+ # Initial allocation of at least one question to early chunks if possible
36
+ questions_per_chunk = [1] * min(n_chunks, n_questions)
37
+
38
+ remaining_questions = n_questions - len(questions_per_chunk)
39
+
40
+ # Distribute remaining questions evenly across chunks
41
+ if remaining_questions > 0:
42
+ for i in range(len(questions_per_chunk)):
43
+ if remaining_questions == 0:
44
+ break
45
+ questions_per_chunk[i] += 1
46
+ remaining_questions -= 1
47
+
48
+ # If chunks remain, add zeros to match the total chunks.
49
+ while len(questions_per_chunk) < n_chunks:
50
+ questions_per_chunk.append(0)
51
+
52
+ return questions_per_chunk
53
+
54
+
55
+ def generate_questions_for_text(text: str, chunk_size: int, n_questions: int) -> List[Tuple[str, int]]:
56
+ """
57
+ Splits the text into chunks, distributes questions across them, and returns a list of
58
+ (chunk, number of questions).
59
+ """
60
+ chunks = split_text_into_chunks(text, chunk_size)
61
+ n_chunks = len(chunks)
62
+
63
+ questions_distribution = distribute_questions_across_chunks(n_chunks, n_questions)
64
+
65
+ return list(zip(chunks, questions_distribution))
66
+
67
+
68
+ # Example usage
69
+ text = (
70
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin hendrerit urna "
71
+ "vel erat bibendum, eget condimentum ipsum interdum. Nulla facilisi. Quisque dictum "
72
+ "eros eu velit varius, eget faucibus mauris euismod. Etiam placerat nisi at urna maximus "
73
+ "viverra. Integer ut odio nec justo volutpat varius ut quis quam. Suspendisse potenti. "
74
+ "Donec vulputate quam quis metus sagittis, sed commodo justo ultricies. Nam ut velit "
75
+ "finibus, venenatis eros vel, consectetur arcu. Praesent vulputate at ligula non elementum. "
76
+ "Nulla varius condimentum justo, non placerat nisl ullamcorper eu."
77
+ )
78
+
79
+ chunk_size = 100 # Max length of each chunk in characters
80
+ n_questions = 5 # Total number of questions to be asked
81
+
82
+ result = generate_questions_for_text(text, chunk_size, n_questions)
83
+
84
+ for i, (chunk, num_questions) in enumerate(result):
85
+ print(f"Chunk {i + 1} ({len(chunk.split())} words):")
86
+ print(f"Questions: {num_questions}")
87
+ print(chunk)
88
+ print("-" * 40)
splitgpt.py ADDED
@@ -0,0 +1,331 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ from dotenv import load_dotenv
4
+ import fitz # PyMuPDF
5
+ from langchain_openai import ChatOpenAI # Correct import from langchain-openai
6
+ from langchain.schema import HumanMessage, SystemMessage # For creating structured chat messages
7
+
8
+ QUESTIONS_PATH = "questions.json"
9
+
10
+ # Load environment variables
11
+ load_dotenv()
12
+
13
+ def split_text_into_chunks(text: str, chunk_size: int) -> list:
14
+ """
15
+ Splits the text into chunks of a specified maximum size.
16
+ """
17
+ # Trim the text to remove leading/trailing whitespace and reduce multiple spaces to a single space
18
+ cleaned_text = " ".join(text.split())
19
+ words = cleaned_text.split(" ")
20
+
21
+ chunks = []
22
+ current_chunk = []
23
+ current_length = 0
24
+
25
+ for word in words:
26
+ if current_length + len(word) + 1 > chunk_size:
27
+ chunks.append(" ".join(current_chunk))
28
+ current_chunk = [word]
29
+ current_length = len(word)
30
+ else:
31
+ current_chunk.append(word)
32
+ current_length += len(word) + 1
33
+
34
+ if current_chunk:
35
+ chunks.append(" ".join(current_chunk))
36
+
37
+ return chunks
38
+
39
+
40
+ def distribute_questions_across_chunks(n_chunks: int, n_questions: int) -> list:
41
+ """
42
+ Distributes a specified number of questions across a specified number of chunks.
43
+ """
44
+ questions_per_chunk = [1] * min(n_chunks, n_questions)
45
+ remaining_questions = n_questions - len(questions_per_chunk)
46
+
47
+ if remaining_questions > 0:
48
+ for i in range(len(questions_per_chunk)):
49
+ if remaining_questions == 0:
50
+ break
51
+ questions_per_chunk[i] += 1
52
+ remaining_questions -= 1
53
+
54
+ while len(questions_per_chunk) < n_chunks:
55
+ questions_per_chunk.append(0)
56
+
57
+ return questions_per_chunk
58
+
59
+
60
+ def extract_text_from_pdf(pdf_path):
61
+ text = ""
62
+ try:
63
+ print(f"[DEBUG] Opening PDF: {pdf_path}")
64
+ with fitz.open(pdf_path) as pdf:
65
+ print(f"[DEBUG] Extracting text from PDF: {pdf_path}")
66
+ for page in pdf:
67
+ text += page.get_text()
68
+ except Exception as e:
69
+ print(f"Error reading PDF: {e}")
70
+ raise RuntimeError("Unable to extract text from PDF.")
71
+ return text
72
+
73
+
74
+ def generate_questions_from_text(text, n_questions=5):
75
+ openai_api_key = os.getenv("OPENAI_API_KEY")
76
+
77
+ if not openai_api_key:
78
+ raise RuntimeError(
79
+ "OpenAI API key not found. Please add it to your .env file as OPENAI_API_KEY."
80
+ )
81
+
82
+ chat = ChatOpenAI(
83
+ openai_api_key=openai_api_key, model="gpt-4", temperature=0.7, max_tokens=750
84
+ )
85
+
86
+ messages = [
87
+ SystemMessage(
88
+ content="You are an expert interviewer who generates concise technical interview questions. Do not enumerate the questions. Answer only with questions."
89
+ ),
90
+ HumanMessage(
91
+ content=f"Based on the following content, generate {n_questions} technical interview questions:\n{text}"
92
+ ),
93
+ ]
94
+
95
+ try:
96
+ print(f"[DEBUG] Sending request to OpenAI with {n_questions} questions.")
97
+ response = chat.invoke(messages)
98
+ questions = response.content.strip().split("\n\n")
99
+ questions = [q.strip() for q in questions if q.strip()]
100
+ except Exception as e:
101
+ print(f"[ERROR] Failed to generate questions: {e}")
102
+ questions = ["An error occurred while generating questions."]
103
+
104
+ return questions
105
+
106
+
107
+ def save_questions(questions):
108
+ with open(QUESTIONS_PATH, "w") as f:
109
+ json.dump(questions, f, indent=4)
110
+
111
+
112
+ def generate_and_save_questions_from_pdf(pdf_path, total_questions=5):
113
+ print(f"[INFO] Generating questions from PDF: {pdf_path}")
114
+
115
+ try:
116
+ pdf_text = extract_text_from_pdf(pdf_path)
117
+
118
+ if not pdf_text.strip():
119
+ raise RuntimeError("The PDF content is empty or could not be read.")
120
+
121
+ chunk_size = 2000
122
+ chunks = split_text_into_chunks(pdf_text, chunk_size)
123
+ n_chunks = len(chunks)
124
+
125
+ questions_distribution = distribute_questions_across_chunks(n_chunks, total_questions)
126
+ combined_questions = []
127
+
128
+ for i, (chunk, n_questions) in enumerate(zip(chunks, questions_distribution)):
129
+ print(f"[DEBUG] Processing chunk {i + 1} of {n_chunks}")
130
+ if n_questions > 0:
131
+ questions = generate_questions_from_text(chunk, n_questions=n_questions)
132
+ combined_questions.extend(questions)
133
+
134
+ if not combined_questions:
135
+ raise RuntimeError("No questions generated from the PDF content.")
136
+
137
+ print(f"[INFO] Total questions generated: {len(combined_questions)}")
138
+ save_questions(combined_questions)
139
+ print(f"[INFO] Questions saved to {QUESTIONS_PATH}")
140
+
141
+ # Return a status message and the JSON object
142
+ return "Questions generated successfully.", {"questions": combined_questions}
143
+
144
+ except Exception as e:
145
+ # Handle exceptions and return meaningful error messages
146
+ error_message = f"Error during question generation: {str(e)}"
147
+ print(f"[ERROR] {error_message}")
148
+ return error_message, {"questions": []}
149
+
150
+
151
+
152
+
153
+
154
+
155
+ import gradio as gr
156
+ import json
157
+ import os
158
+ import time
159
+
160
+ def generate_and_save_questions_from_pdf3_mock(pdf_path, total_questions=5):
161
+ print(f"[INFO] Generating questions from PDF: {pdf_path}")
162
+
163
+ if not os.path.exists(pdf_path):
164
+ yield "❌ Error: PDF file not found.", {}
165
+ return
166
+
167
+ yield "📄 PDF uploaded successfully. Processing started...", {}
168
+
169
+ try:
170
+ # Simulate PDF text extraction and processing
171
+ time.sleep(1)
172
+ pdf_text = "This is some mock PDF text for testing purposes."
173
+
174
+ if not pdf_text.strip():
175
+ yield "❌ Error: The PDF content is empty or could not be read.", {}
176
+ return
177
+
178
+ chunk_size = 2000
179
+ chunks = [pdf_text[i:i + chunk_size] for i in range(0, len(pdf_text), chunk_size)]
180
+ n_chunks = len(chunks)
181
+
182
+ yield f"🔄 Splitting text into {n_chunks} chunks...", {}
183
+
184
+ questions_distribution = [total_questions // n_chunks] * n_chunks
185
+ combined_questions = []
186
+
187
+ for i, (chunk, n_questions) in enumerate(zip(chunks, questions_distribution)):
188
+ yield f"🔄 Processing chunk {i + 1} of {n_chunks}...", {}
189
+ time.sleep(1) # Simulating processing time
190
+ combined_questions.append(f"Sample Question from Chunk {i + 1}")
191
+
192
+ if not combined_questions:
193
+ yield "❌ Error: No questions generated from the PDF content.", {}
194
+ return
195
+
196
+ yield f"✅ Total {len(combined_questions)} questions generated. Saving questions...", {}
197
+ save_path = "generated_questions_from_pdf.json"
198
+ with open(save_path, "w") as f:
199
+ json.dump({"questions": combined_questions}, f)
200
+
201
+ yield "✅ PDF processing complete. Questions saved successfully!", {"questions": combined_questions}
202
+
203
+ except Exception as e:
204
+ yield f"❌ Error during question generation: {str(e)}", {}
205
+
206
+ def generate_and_save_questions_from_pdf3_v1(pdf_path, total_questions=5):
207
+ print(f"[INFO] Generating questions from PDF: {pdf_path}")
208
+
209
+ if not os.path.exists(pdf_path):
210
+ yield "❌ Error: PDF file not found.", {}
211
+ return
212
+
213
+ yield "📄 PDF uploaded successfully. Processing started...", {}
214
+
215
+ try:
216
+ # Extract text from the PDF file
217
+ pdf_text = extract_text_from_pdf(pdf_path)
218
+
219
+ if not pdf_text.strip():
220
+ yield "❌ Error: The PDF content is empty or could not be read.", {}
221
+ return
222
+
223
+ # Split the PDF content into chunks
224
+ chunk_size = 2000 # Adjust this as necessary
225
+ chunks = split_text_into_chunks(pdf_text, chunk_size)
226
+ n_chunks = len(chunks)
227
+
228
+ yield f"🔄 Splitting text into {n_chunks} chunks...", {}
229
+
230
+ # Distribute the total number of questions across chunks
231
+ questions_distribution = distribute_questions_across_chunks(n_chunks, total_questions)
232
+ combined_questions = []
233
+
234
+ # Process each chunk and generate questions
235
+ for i, (chunk, n_questions) in enumerate(zip(chunks, questions_distribution)):
236
+ yield f"🔄 Processing chunk {i + 1} of {n_chunks}...", {}
237
+ if n_questions > 0:
238
+ questions = generate_questions_from_text(chunk, n_questions=n_questions)
239
+ combined_questions.extend(questions)
240
+
241
+ if not combined_questions:
242
+ yield "❌ Error: No questions generated from the PDF content.", {}
243
+ return
244
+
245
+ yield f"✅ Total {len(combined_questions)} questions generated. Saving questions...", {}
246
+
247
+ # Save generated questions to a file
248
+ save_path = "generated_questions_from_pdf.json"
249
+ with open(save_path, "w") as f:
250
+ json.dump({"questions": combined_questions}, f)
251
+
252
+ yield "✅ PDF processing complete. Questions saved successfully!", {"questions": combined_questions}
253
+
254
+ except Exception as e:
255
+ error_message = f"❌ Error during question generation: {str(e)}"
256
+ print(f"[ERROR] {error_message}")
257
+ yield error_message, {}
258
+
259
+ import json
260
+ import os
261
+
262
+ def generate_and_save_questions_from_pdf3(pdf_path, total_questions=5):
263
+ print(f"[INFO] Generating questions from PDF: {pdf_path}")
264
+
265
+ if not os.path.exists(pdf_path):
266
+ yield "❌ Error: PDF file not found.", {}
267
+ return
268
+
269
+ yield "📄 PDF uploaded successfully. Processing started...", {}
270
+
271
+ try:
272
+ # Extract text from the PDF file
273
+ pdf_text = extract_text_from_pdf(pdf_path)
274
+
275
+ if not pdf_text.strip():
276
+ yield "❌ Error: The PDF content is empty or could not be read.", {}
277
+ return
278
+
279
+ # Split the PDF content into chunks
280
+ chunk_size = 2000 # Adjust this as necessary
281
+ chunks = split_text_into_chunks(pdf_text, chunk_size)
282
+ n_chunks = len(chunks)
283
+
284
+ yield f"🔄 Splitting text into {n_chunks} chunks...", {}
285
+
286
+ # Distribute the total number of questions across chunks
287
+ questions_distribution = distribute_questions_across_chunks(n_chunks, total_questions)
288
+ combined_questions = []
289
+
290
+ # Process each chunk and generate questions
291
+ for i, (chunk, n_questions) in enumerate(zip(chunks, questions_distribution)):
292
+ yield f"🔄 Processing chunk {i + 1} of {n_chunks}...", {}
293
+ if n_questions > 0:
294
+ questions = generate_questions_from_text(chunk, n_questions=n_questions)
295
+ combined_questions.extend(questions)
296
+
297
+ if not combined_questions:
298
+ yield "❌ Error: No questions generated from the PDF content.", {}
299
+ return
300
+
301
+ yield f"✅ Total {len(combined_questions)} questions generated. Saving questions...", {}
302
+
303
+ # Save the combined questions in `generated_questions_from_pdf.json` (detailed version)
304
+ detailed_save_path = "generated_questions_from_pdf.json"
305
+ with open(detailed_save_path, "w") as f:
306
+ json.dump({"questions": combined_questions}, f)
307
+
308
+ # Save only the questions (overwrite `questions.json` if it already exists)
309
+ simple_save_path = "questions.json"
310
+ with open(simple_save_path, "w") as f:
311
+ json.dump(combined_questions, f)
312
+
313
+ yield "✅ PDF processing complete. Questions saved successfully!", {"questions": combined_questions}
314
+
315
+ except Exception as e:
316
+ error_message = f"❌ Error during question generation: {str(e)}"
317
+ print(f"[ERROR] {error_message}")
318
+ yield error_message, {}
319
+
320
+
321
+
322
+ if __name__ == "__main__":
323
+ pdf_path = "professional_machine_learning_engineer_exam_guide_english.pdf"
324
+
325
+ try:
326
+ generated_questions = generate_and_save_questions_from_pdf(
327
+ pdf_path, total_questions=5
328
+ )
329
+ print(f"Generated Questions:\n{json.dumps(generated_questions, indent=2)}")
330
+ except Exception as e:
331
+ print(f"Failed to generate questions: {e}")
utils.py ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime
2
+ from docx import Document
3
+ from docx.shared import Pt
4
+ from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
5
+ from pathlib import Path
6
+
7
+ def save_interview_history_old(history, language):
8
+ """Saves the interview history to a TXT file."""
9
+ file_name = f"interview_history_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
10
+ file_path = Path("hr_interviewer") / file_name
11
+
12
+ with open(file_path, "w", encoding="utf-8") as file:
13
+ for item in history:
14
+ file.write(f"{item}\n")
15
+
16
+ return file_path
17
+
18
+
19
+ import os
20
+ from datetime import datetime
21
+
22
+ def save_interview_history_fix(interview_history, language, folder_path="hr_interviewer"):
23
+ """
24
+ Saves the interview history to a file in the specified folder.
25
+
26
+ Args:
27
+ interview_history: The content of the interview history as a string.
28
+ language: Language of the report.
29
+ folder_path: Folder path where the history file will be saved.
30
+
31
+ Returns:
32
+ The file path of the saved interview history.
33
+ """
34
+ # Ensure the directory exists
35
+ os.makedirs(folder_path, exist_ok=True)
36
+
37
+ # Generate the filename with the current date and time
38
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
39
+ file_path = os.path.join(folder_path, f"interview_history_{timestamp}.txt")
40
+
41
+ try:
42
+ with open(file_path, "w", encoding="utf-8") as file:
43
+ file.write("\n".join(interview_history))
44
+ print(f"[DEBUG] Interview history saved at {file_path}")
45
+ return file_path
46
+ except Exception as e:
47
+ print(f"[ERROR] Failed to save interview history: {e}")
48
+ return None
49
+
50
+ import os
51
+ from datetime import datetime
52
+
53
+ def save_interview_history(interview_history, language, folder_path="hr_interviewer"):
54
+ os.makedirs(folder_path, exist_ok=True)
55
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
56
+ file_path = os.path.join(folder_path, f"interview_history_{timestamp}.txt")
57
+
58
+ try:
59
+ with open(file_path, "w", encoding="utf-8") as file:
60
+ file.write("\n".join(interview_history))
61
+ print(f"[DEBUG] Interview history saved at {file_path}")
62
+ return file_path
63
+ except Exception as e:
64
+ print(f"[ERROR] Failed to save interview history: {e}")
65
+ return None
66
+
67
+
68
+
69
+ def generate_interview_report(interview_history, language):
70
+ """
71
+ Generates a report in DOCX format based on the interview history.
72
+ Args:
73
+ interview_history: A list of strings representing the interview history.
74
+ language: The language of the report.
75
+ Returns:
76
+ A tuple containing the report content as a string and the path to the generated DOCX file.
77
+ """
78
+ doc = Document()
79
+
80
+ # Add title
81
+ title = doc.add_heading('Interview Report', level=1)
82
+ title.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
83
+ title_run = title.runs[0]
84
+ title_run.font.name = 'Arial'
85
+ title_run.font.size = Pt(16)
86
+ title_run.bold = True
87
+
88
+ # Add date
89
+ date_para = doc.add_paragraph(f"Date: {datetime.now().strftime('%Y-%m-%d')}")
90
+ date_para.alignment = WD_PARAGRAPH_ALIGNMENT.RIGHT
91
+ date_run = date_para.runs[0]
92
+ date_run.font.name = 'Arial'
93
+ date_run.font.size = Pt(11)
94
+
95
+ # Add interview history
96
+ doc.add_heading('Interview History', level=2)
97
+ for item in interview_history:
98
+ para = doc.add_paragraph(item)
99
+ para_run = para.runs[0]
100
+ para_run.font.name = 'Arial'
101
+ para_run.font.size = Pt(12)
102
+
103
+ # Save the document
104
+ file_name = f"interview_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.docx"
105
+ file_path = Path("hr_interviewer") / file_name
106
+ doc.save(file_path)
107
+
108
+ # Convert DOCX to string (for display in Gradio, etc.)
109
+ report_content = ""
110
+ for para in doc.paragraphs:
111
+ report_content += para.text + "\n"
112
+
113
+ return report_content, file_path
114
+
115
+
116
+ import json
117
+ from datetime import datetime
118
+ from docx import Document
119
+ from docx.shared import Pt
120
+ from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
121
+ from pathlib import Path
122
+
123
+ # ... (other functions remain the same)
124
+
125
+ def load_config(config_path="hr_interviewer/config.json"):
126
+ """Loads the configuration from a JSON file."""
127
+ try:
128
+ with open(config_path, "r") as f:
129
+ config = json.load(f)
130
+ except FileNotFoundError:
131
+ print(f"[WARNING] Config file not found at {config_path}. Using default settings.")
132
+ config = {} # Return empty dict to use defaults
133
+ except json.JSONDecodeError:
134
+ print(f"[ERROR] Error decoding JSON in {config_path}. Using default settings.")
135
+ config = {}
136
+ return config
137
+
138
+ def save_config(config, config_path="hr_interviewer/config.json"):
139
+ """Saves the configuration to a JSON file."""
140
+ try:
141
+ with open(config_path, "w") as f:
142
+ json.dump(config, f, indent=4)
143
+ print(f"[INFO] Configuration saved to {config_path}")
144
+ return True
145
+ except Exception as e:
146
+ print(f"[ERROR] Failed to save configuration: {e}")
147
+ return False