File size: 5,424 Bytes
ba72f62
2ae57cb
22b00f2
 
 
2ae57cb
 
 
22b00f2
8e4e001
2ae57cb
 
 
 
 
 
22b00f2
 
2ae57cb
22b00f2
 
 
 
 
 
 
2ae57cb
22b00f2
 
2ae57cb
22b00f2
 
 
 
 
 
 
 
 
 
 
 
 
2ae57cb
22b00f2
 
2ae57cb
22b00f2
 
2ae57cb
8e4e001
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22b00f2
 
 
 
 
 
8e4e001
 
 
 
 
 
22b00f2
 
8e4e001
 
 
 
 
 
 
2ae57cb
22b00f2
 
2ae57cb
22b00f2
 
2ae57cb
22b00f2
8e4e001
 
 
 
 
 
22b00f2
 
 
 
 
 
2ae57cb
22b00f2
 
2ae57cb
22b00f2
 
2ae57cb
22b00f2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2ae57cb
8e4e001
 
 
 
 
2ae57cb
22b00f2
2ae57cb
22b00f2
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import os
import json
import asyncio
import edge_tts
from faster_whisper import WhisperModel
from langchain_groq import ChatGroq
import logging

# Initialize models
chat_groq_api = os.getenv("GROQ_API_KEY", "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")
groq_llm = ChatGroq(
    temperature=0.7,
    model_name="llama-3.3-70b-versatile",
    api_key=chat_groq_api
)

# Initialize Whisper model
whisper_model = None

def load_whisper_model():
    global whisper_model
    if whisper_model is None:
        device = "cuda" if os.system("nvidia-smi") == 0 else "cpu"
        compute_type = "float16" if device == "cuda" else "int8"
        whisper_model = WhisperModel("base", device=device, compute_type=compute_type)
    return whisper_model

def generate_first_question(profile, job):
    """Generate the first interview question based on profile and job"""
    try:
        prompt = f"""
        You are conducting an interview for a {job.role} position at {job.company}.
        The candidate's profile shows:
        - Skills: {profile.get('skills', [])}
        - Experience: {profile.get('experience', [])}
        - Education: {profile.get('education', [])}
        
        Generate an appropriate opening interview question that is professional and relevant.
        Keep it concise and clear.
        """
        
        response = groq_llm.predict(prompt)
        return response.strip()
    except Exception as e:
        logging.error(f"Error generating first question: {e}")
        return "Tell me about yourself and why you're interested in this position."

def edge_tts_to_file_sync(text, output_path, voice="en-US-AriaNeural"):
    """Synchronous wrapper for edge-tts"""
    try:
        # Ensure the directory exists and is writable
        directory = os.path.dirname(output_path)
        if not directory:
            directory = "/tmp"  # Fallback to /tmp if no directory specified
            output_path = os.path.join(directory, os.path.basename(output_path))
        
        os.makedirs(directory, exist_ok=True)
        
        # Test write permissions
        test_file = os.path.join(directory, f"test_{os.getpid()}.tmp")
        try:
            with open(test_file, 'w') as f:
                f.write("test")
            os.remove(test_file)
        except (PermissionError, OSError) as e:
            logging.error(f"Directory {directory} is not writable: {e}")
            # Fallback to /tmp
            directory = "/tmp"
            output_path = os.path.join(directory, os.path.basename(output_path))
            os.makedirs(directory, exist_ok=True)
        
        async def generate_audio():
            communicate = edge_tts.Communicate(text, voice)
            await communicate.save(output_path)
        
        # Run async function in sync context
        try:
            loop = asyncio.get_event_loop()
        except RuntimeError:
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
        
        loop.run_until_complete(generate_audio())
        
        # Verify file was created and has content
        if os.path.exists(output_path) and os.path.getsize(output_path) > 0:
            return output_path
        else:
            logging.error(f"Audio file was not created or is empty: {output_path}")
            return None
            
    except Exception as e:
        logging.error(f"Error in TTS generation: {e}")
        return None

def whisper_stt(audio_path):
    """Speech-to-text using Faster-Whisper"""
    try:
        if not audio_path or not os.path.exists(audio_path):
            logging.error(f"Audio file does not exist: {audio_path}")
            return ""
        
        # Check if file has content
        if os.path.getsize(audio_path) == 0:
            logging.error(f"Audio file is empty: {audio_path}")
            return ""
        
        model = load_whisper_model()
        segments, _ = model.transcribe(audio_path)
        transcript = " ".join(segment.text for segment in segments)
        return transcript.strip()
    except Exception as e:
        logging.error(f"Error in STT: {e}")
        return ""

def evaluate_answer(question, answer, ref_answer, job_role, seniority):
    """Evaluate candidate's answer"""
    try:
        prompt = f"""
        You are evaluating a candidate's answer for a {seniority} {job_role} position.
        
        Question: {question}
        Candidate Answer: {answer}
        Reference Answer: {ref_answer}
        
        Evaluate based on technical correctness, clarity, and relevance.
        Respond with JSON format:
        {{
            "Score": "Poor|Medium|Good|Excellent",
            "Reasoning": "brief explanation",
            "Improvements": ["suggestion1", "suggestion2"]
        }}
        """
        
        response = groq_llm.predict(prompt)
        # Extract JSON from response
        start_idx = response.find("{")
        end_idx = response.rfind("}") + 1
        if start_idx >= 0 and end_idx > start_idx:
            json_str = response[start_idx:end_idx]
            return json.loads(json_str)
        else:
            raise ValueError("No valid JSON found in response")
    except Exception as e:
        logging.error(f"Error evaluating answer: {e}")
        return {
            "Score": "Medium",
            "Reasoning": "Evaluation failed",
            "Improvements": ["Please be more specific"]
        }