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()