owenkaplinsky
Clean initial commit for HuggingFace
363cda9
"""
Session service for voice screening.
Handles session configuration, screening questions, and database operations.
"""
import logging
from typing import Optional, Dict
from uuid import UUID
from datetime import datetime
from sqlalchemy import select, desc
from src.database.candidates.client import SessionLocal
from src.database.candidates.models import Candidate, CVScreeningResult, VoiceScreeningResult
from src.state.candidate import CandidateStatus
from src.agents.voice_screening.utils.questions import get_screening_questions
logger = logging.getLogger(__name__)
def get_session_config(candidate_id: str) -> Dict:
"""
Generate session configuration for a candidate.
Args:
candidate_id: UUID of the candidate
Returns:
Dict with session configuration including instructions and questions
"""
with SessionLocal() as db:
# Fetch candidate
candidate = db.execute(
select(Candidate).where(Candidate.id == UUID(candidate_id))
).scalar_one_or_none()
if not candidate:
raise ValueError(f"Candidate {candidate_id} not found")
# Fetch latest CV screening result for job title
cv_result = db.execute(
select(CVScreeningResult)
.where(CVScreeningResult.candidate_id == UUID(candidate_id))
.order_by(desc(CVScreeningResult.timestamp))
.limit(1)
).scalar_one_or_none()
job_title = cv_result.job_title if cv_result else "the position"
questions = get_screening_questions(job_title)
# Build instructions
instructions = (
f"You are a friendly HR assistant conducting a phone screening interview with {candidate.full_name} "
f"for the position of {job_title}. "
f"Greet the candidate warmly by name. "
f"Your goal is to ask the following main questions to assess their fit:\n\n"
)
for i, q in enumerate(questions, 1):
instructions += f"{i}. {q}\n"
instructions += (
"\nAsk one question at a time. Wait for their response before moving to the next. "
"Keep the conversations brief and to the point, ask only one follow-up question per main question. "
"If they ask clarifying questions, answer them briefly."
)
return {
"candidate_name": candidate.full_name,
"job_title": job_title,
"instructions": instructions,
"questions": questions,
"config": {
"modalities": ["audio", "text"],
"instructions": instructions,
"voice": "alloy",
"input_audio_format": "pcm16",
"output_audio_format": "pcm16",
"turn_detection": {
"type": "server_vad",
"threshold": 0.5,
"prefix_padding_ms": 300,
"silence_duration_ms": 10000
}
}
}
def save_voice_screening_session(
candidate_id: str,
session_id: str,
transcript_text: str,
audio_url: Optional[str] = None
) -> None:
"""
Save voice screening session to database.
Args:
candidate_id: UUID of the candidate
session_id: Session identifier
transcript_text: Full conversation transcript
audio_url: Path to saved audio file
"""
with SessionLocal() as db:
candidate = db.execute(
select(Candidate).where(Candidate.id == UUID(candidate_id))
).scalar_one_or_none()
if not candidate:
raise ValueError(f"Candidate {candidate_id} not found")
# Create new voice screening result entry
screening_entry = VoiceScreeningResult(
candidate_id=candidate.id,
call_sid=session_id, # Using session_id instead of Twilio call_sid
transcript_text=transcript_text,
audio_url=audio_url,
timestamp=datetime.utcnow(),
# Scores will be filled by judge later
sentiment_score=None,
confidence_score=None,
communication_score=None,
llm_summary=None,
)
# Add and commit
db.add(screening_entry)
candidate.status = CandidateStatus.voice_done
candidate.updated_at = datetime.utcnow()
db.commit()
logger.info(f"Voice screening session saved for candidate {candidate_id}")