|
|
import json |
|
|
from datetime import datetime |
|
|
from typing import Optional |
|
|
from uuid import UUID |
|
|
|
|
|
from langchain_openai import ChatOpenAI |
|
|
from langchain.messages import SystemMessage, HumanMessage |
|
|
from langchain.tools import tool |
|
|
from sqlalchemy import select |
|
|
|
|
|
from src.database.candidates.client import SessionLocal |
|
|
from src.database.candidates.models import Candidate, VoiceScreeningResult, CVScreeningResult |
|
|
from src.state.candidate import CandidateStatus |
|
|
from src.agents.voice_screening.schemas.output_schema import VoiceScreeningOutput |
|
|
from src.prompts import get_prompt |
|
|
|
|
|
import base64 |
|
|
import os |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SYSTEM_PROMPT = get_prompt( |
|
|
template_name="voice_screening_judge", |
|
|
latest_version=True |
|
|
) |
|
|
|
|
|
|
|
|
@tool |
|
|
def evaluate_voice_screening(candidate_id: str) -> str: |
|
|
""" |
|
|
Evaluates a completed voice screening session for a candidate. |
|
|
|
|
|
Args: |
|
|
candidate_id (str): The UUID of the candidate to evaluate. |
|
|
|
|
|
Returns: |
|
|
str: A summary of the evaluation result. |
|
|
""" |
|
|
try: |
|
|
with SessionLocal() as session: |
|
|
|
|
|
candidate = session.execute( |
|
|
select(Candidate).where(Candidate.id == UUID(candidate_id)) |
|
|
).scalar_one_or_none() |
|
|
|
|
|
if not candidate: |
|
|
return f"β Candidate {candidate_id} not found." |
|
|
|
|
|
|
|
|
voice_result = session.execute( |
|
|
select(VoiceScreeningResult) |
|
|
.where(VoiceScreeningResult.candidate_id == UUID(candidate_id)) |
|
|
.order_by(VoiceScreeningResult.timestamp.desc()) |
|
|
).scalar_one_or_none() |
|
|
|
|
|
if not voice_result or not voice_result.transcript_text: |
|
|
return f"β No voice screening transcript found for candidate {candidate.full_name}." |
|
|
|
|
|
|
|
|
cv_result = session.execute( |
|
|
select(CVScreeningResult) |
|
|
.where(CVScreeningResult.candidate_id == UUID(candidate_id)) |
|
|
.order_by(CVScreeningResult.timestamp.desc()) |
|
|
).scalar_one_or_none() |
|
|
|
|
|
job_title = cv_result.job_title if cv_result else "the position" |
|
|
|
|
|
|
|
|
messages = [] |
|
|
|
|
|
messages.append(SystemMessage(content=SYSTEM_PROMPT)) |
|
|
|
|
|
user_content = [] |
|
|
user_content.append({"type": "text", "text": f"Candidate: {candidate.full_name}\nPosition: {job_title}\n"}) |
|
|
|
|
|
|
|
|
audio_loaded = False |
|
|
if voice_result.audio_url and os.path.exists(voice_result.audio_url): |
|
|
try: |
|
|
with open(voice_result.audio_url, "rb") as audio_file: |
|
|
audio_data = base64.b64encode(audio_file.read()).decode("utf-8") |
|
|
user_content.append({ |
|
|
"type": "input_audio", |
|
|
"input_audio": { |
|
|
"data": audio_data, |
|
|
"format": "wav" |
|
|
} |
|
|
}) |
|
|
audio_loaded = True |
|
|
except Exception as e: |
|
|
print(f"β οΈ Failed to load audio file: {e}") |
|
|
|
|
|
|
|
|
user_content.append({"type": "text", "text": f"Transcript:\n{voice_result.transcript_text}\n"}) |
|
|
|
|
|
messages.append(HumanMessage(content=user_content)) |
|
|
|
|
|
|
|
|
|
|
|
model_name = "gpt-4o-audio-preview" if audio_loaded else "gpt-4o" |
|
|
llm = ChatOpenAI(model=model_name, temperature=0) |
|
|
|
|
|
|
|
|
method = "function_calling" if audio_loaded else "function_calling" |
|
|
|
|
|
structured_llm = llm.with_structured_output(VoiceScreeningOutput, method=method) |
|
|
evaluation: VoiceScreeningOutput = structured_llm.invoke(messages) |
|
|
|
|
|
|
|
|
voice_result.sentiment_score = evaluation.sentiment_score |
|
|
voice_result.confidence_score = evaluation.confidence_score |
|
|
voice_result.communication_score = evaluation.communication_score |
|
|
voice_result.proficiency_score = evaluation.proficiency_score |
|
|
voice_result.llm_summary = evaluation.llm_summary |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
avg_score = ( |
|
|
evaluation.sentiment_score + |
|
|
evaluation.confidence_score + |
|
|
evaluation.communication_score + |
|
|
evaluation.proficiency_score |
|
|
) / 4.0 * 100 |
|
|
|
|
|
if avg_score >= 75: |
|
|
candidate.status = CandidateStatus.voice_passed |
|
|
result_msg = "PASSED" |
|
|
else: |
|
|
candidate.status = CandidateStatus.voice_rejected |
|
|
result_msg = "REJECTED" |
|
|
|
|
|
candidate.updated_at = datetime.utcnow() |
|
|
session.commit() |
|
|
|
|
|
return ( |
|
|
f"β
Evaluation complete for {candidate.full_name} using {model_name}.\n" |
|
|
f"Result: {result_msg} (Score: {avg_score:.1f}/100)\n" |
|
|
f"Summary: {evaluation.llm_summary}" |
|
|
) |
|
|
|
|
|
except Exception as e: |
|
|
import traceback |
|
|
return f"β Error evaluating voice screening: {str(e)}\n{traceback.format_exc()}" |
|
|
|
|
|
|
|
|
voice_judge = evaluate_voice_screening |
|
|
|