Spaces:
Sleeping
Sleeping
import os | |
import json | |
from dotenv import load_dotenv | |
import uuid | |
from datetime import datetime | |
from groq import Groq | |
from constants import SYSTEM_PROMPT, INTERVIEW_INSTRUCTIONS, MOCK_INTERVIEW_PROMPT,EXPECTED_OUTPUT | |
from constants import JOB_MATCHING_INSTRUCTIONS,JOB_MATCHING_PROMPT,JOB_ROLES | |
from phi.agent import Agent | |
from phi.model.google import Gemini | |
from phi.tools.duckduckgo import DuckDuckGo | |
from elevenlabs.client import ElevenLabs | |
from elevenlabs import VoiceSettings | |
from gradio import ( | |
Blocks, Chatbot, Row, Column, Radio, Dropdown, | |
Button, Audio, Textbox, State, HTML | |
) | |
load_dotenv() | |
# Initialize clients | |
eleven_client = ElevenLabs(api_key=os.getenv("ELEVEN_API_KEY")) | |
groq_client = Groq(api_key=os.getenv("GROQ_API_KEY")) | |
def get_current_datetime() -> str: | |
return json.dumps({ | |
"current_datetime": datetime.now().strftime("%Y-%m-%d %H:%M:%S") | |
}) | |
def create_agent(mode): | |
base_config = { | |
"model": Gemini(id="gemini-2.0-flash-exp", api_key=os.getenv("GOOGLE_API_KEY")), | |
"show_tool_calls": True, | |
"tools": [DuckDuckGo(fixed_max_results=10), get_current_datetime], | |
"add_history_to_messages": True, | |
} | |
if mode == "Interview Guide": | |
return Agent( | |
**base_config, | |
system_prompt=SYSTEM_PROMPT, | |
instructions=INTERVIEW_INSTRUCTIONS, | |
num_history_responses=6, | |
expected_output=EXPECTED_OUTPUT | |
) | |
elif mode == "Job Matching": | |
return Agent( | |
**base_config, | |
system_prompt=JOB_MATCHING_PROMPT, | |
instructions=JOB_MATCHING_INSTRUCTIONS, | |
) | |
else: | |
return Agent( | |
**base_config, | |
system_prompt=MOCK_INTERVIEW_PROMPT, | |
instructions=["Conduct a technical mock interview.", "Ask one question at a time.", "Evaluate the candidate's responses in simple concise words"], | |
num_history_responses=3, | |
) | |
def text_to_speech_file(text: str, client: ElevenLabs) -> str: | |
""" | |
Converts text to speech using ElevenLabs API and saves as MP3. | |
Args: | |
text (str): Text to convert to speech | |
client (ElevenLabs): Initialized ElevenLabs client | |
Returns: | |
str: Path to saved audio file | |
""" | |
try: | |
response = client.text_to_speech.convert( | |
voice_id="pNInz6obpgDQGcFmaJgB", # Adam voice | |
optimize_streaming_latency="0", | |
output_format="mp3_22050_32", | |
text=text, | |
model_id="eleven_turbo_v2", | |
voice_settings=VoiceSettings( | |
stability=0.0, | |
similarity_boost=1.0, | |
style=0.0, | |
use_speaker_boost=True, | |
), | |
) | |
save_file_path = f"{uuid.uuid4()}.mp3" | |
with open(save_file_path, "wb") as f: | |
for chunk in response: | |
if chunk: | |
f.write(chunk) | |
return save_file_path | |
except Exception as e: | |
raise Exception(f"Text-to-speech conversion failed: {str(e)}") | |
def handle_mock_interview(audio_path, history, agent_state, voice_choice): | |
"""Handle audio for Mock Interview mode""" | |
if not audio_path: | |
return history, agent_state, None, "" | |
try: | |
# Transcribe audio | |
with open(audio_path, "rb") as audio_file: | |
transcription = groq_client.audio.transcriptions.create( | |
file=("recording.wav", audio_file.read(), "audio/wav"), | |
model="whisper-large-v3-turbo", | |
response_format="text" | |
) | |
# Initialize agent if needed | |
if "agent" not in agent_state or agent_state.get("mode") != "Mock Interview": | |
agent_state["agent"] = create_agent("Mock Interview") | |
agent_state["mode"] = "Mock Interview" | |
# Get agent response | |
agent = agent_state["agent"] | |
response = agent.run(transcription).content | |
try: | |
# Generate audio using the improved text-to-speech function | |
audio_output = text_to_speech_file(response, eleven_client) | |
# Update history | |
history.append({"role": "user", "content": f"[Audio]: {transcription}"}) | |
history.append({"role": "assistant", "content": response}) | |
return history, agent_state, audio_output, "" | |
except Exception as audio_error: | |
return history, agent_state, None, f"Audio generation error: {str(audio_error)}" | |
except Exception as e: | |
return history, agent_state, None, f"Error: {str(e)}" | |
def handle_text_input(message, history, mode, agent_state): | |
"""Handle text input for Interview Guide mode""" | |
if not message.strip(): | |
return history, agent_state, "", "" | |
if "agent" not in agent_state or agent_state.get("mode") != mode: | |
agent_state["agent"] = create_agent(mode) | |
agent_state["mode"] = mode | |
agent = agent_state["agent"] | |
history = history + [{"role": "user", "content": message}] | |
try: | |
# Using direct response instead of streaming | |
response = agent.run(message).content | |
history.append({"role": "assistant", "content": response}) | |
return history, agent_state, "", "" | |
except Exception as e: | |
return history, agent_state, "", f"Error: {str(e)}" | |
def handle_job_matching(role, experience, location, history, agent_state): | |
"""Handle job matching mode inputs""" | |
# Initialize history if None | |
if history is None: | |
history = [] | |
# Initialize agent_state if None | |
if agent_state is None: | |
agent_state = {} | |
if not all([role, experience, location]): | |
return history, agent_state, "Please fill in all fields" | |
# Initialize agent if needed | |
if "agent" not in agent_state or agent_state.get("mode") != "Job Matching": | |
agent_state["agent"] = create_agent("Job Matching") | |
agent_state["mode"] = "Job Matching" | |
query = f"""Find relevant jobs for: | |
Role: {role} | |
Experience: {experience} | |
Location: {location} | |
Please search for current job listings and provide details including: | |
1. Company name | |
2. Job title | |
3. Location | |
4. Key requirements | |
5. Application link or process | |
""" | |
try: | |
agent = agent_state["agent"] | |
history = history + [{"role": "user", "content": query}] | |
response = agent.run(query).content | |
history.append({"role": "assistant", "content": response}) | |
return history, agent_state, "" | |
except Exception as e: | |
return history, agent_state, f"Error: {str(e)}" | |
def clear_chat(): | |
return [], {}, None, "" | |
with Blocks(title="AI Interview Assistant") as demo: | |
# State | |
chat_history = State([]) | |
agent_state = State({}) | |
mode = Radio( | |
choices=["Interview Guide", "Mock Interview", "Job Matching"], | |
label="Mode", | |
value="Interview Guide" | |
) | |
chatbot = Chatbot(label="Conversation", height=500, type="messages") | |
error_msg = HTML() | |
with Row(): | |
with Column(visible=True) as text_col: | |
text_input = Textbox( | |
label="Type your message", | |
placeholder="Ask about interview preparation...", | |
lines=3 | |
) | |
submit_btn = Button("Send Message") | |
# Audio input for Mock Interview | |
with Column(visible=False) as audio_col: | |
voice_select = Dropdown( | |
choices=["Brian", "Rachel", "Sam"], | |
value="Brian", | |
label="Assistant Voice" | |
) | |
audio_input = Audio( | |
sources=["microphone"], | |
type="filepath", | |
label="Record your answer" | |
) | |
audio_output = Audio( | |
label="Assistant's Response", | |
visible=True | |
) | |
with Column(visible=False) as job_col: | |
role_select = Dropdown( | |
choices=JOB_ROLES, | |
label="Select Job Role", | |
value="Software Engineer" | |
) | |
experience = Dropdown( | |
choices=["0-2 years", "2-5 years", "5-8 years", "8+ years"], | |
label="Experience Level", | |
value="0-2 years" | |
) | |
location = Textbox( | |
label="Preferred Location", | |
placeholder="Enter city, state, or 'Remote'", | |
lines=1 | |
) | |
search_btn = Button("Search Jobs") | |
# Clear button | |
clear_btn = Button("Clear Chat") | |
# Event handlers | |
def update_mode(mode_value): | |
return ( | |
Column(visible=mode_value == "Interview Guide"), | |
Column(visible=mode_value == "Mock Interview"), | |
Column(visible=mode_value == "Job Matching") | |
) | |
mode.change( | |
update_mode, | |
inputs=[mode], | |
outputs=[text_col,audio_col,job_col] | |
) | |
submit_btn.click( | |
handle_text_input, | |
inputs=[text_input, chat_history, mode, agent_state], | |
outputs=[chatbot, agent_state, text_input, error_msg] | |
) | |
audio_input.change( | |
handle_mock_interview, | |
inputs=[audio_input, chat_history, agent_state, voice_select], | |
outputs=[chatbot, agent_state, audio_output, error_msg] | |
) | |
search_btn.click( | |
handle_job_matching, | |
inputs=[role_select, experience, location, chat_history, agent_state], | |
outputs=[chatbot, agent_state, error_msg] | |
) | |
clear_btn.click( | |
clear_chat, | |
outputs=[chat_history, agent_state, audio_output, error_msg] | |
) | |
if __name__ == "__main__": | |
demo.launch() |