ai-interview-caller / src /call_manager.py
ranjithkumar111's picture
Upload 8 files
b253d7e verified
Raw
History Blame Contribute Delete
3.97 kB
# File: src/call_manager.py
# Purpose: Initiate outbound AI calls via Bland.ai — no webhooks needed,
# Bland.ai handles the full conversation loop internally.
import httpx
from typing import Dict, Any, List
from config import (
BLAND_API_KEY, BLAND_API_URL,
INTRO_MESSAGE, MAX_QUESTIONS,
)
# In-memory session store keyed by Bland call_id
_sessions: Dict[str, Dict[str, Any]] = {}
HEADERS = {
"authorization": BLAND_API_KEY,
"Content-Type": "application/json",
}
def initiate_call(
candidate_phone: str,
session_id: int,
candidate_name: str,
resume_skills: List[str],
) -> str:
"""
Place an outbound AI call via Bland.ai.
Bland handles STT, LLM, TTS and conversation loop internally.
Returns the Bland call_id.
"""
# Build personalised prompt from resume skills
skills_context = ""
if resume_skills:
skills_str = ", ".join(resume_skills[:5])
skills_context = (
f"The candidate has experience with: {skills_str}. "
f"Ask relevant questions about these skills during the interview. "
)
prompt = (
f"You are an expert AI technical recruiter conducting a phone screening interview with {candidate_name}. "
f"{skills_context}"
"Your job is to: "
"1. Introduce yourself as an AI recruitment assistant. "
"2. Ask one clear question at a time. "
"3. Listen carefully and ask follow-up questions when answers are vague. "
"4. Cover these topics: background and experience, most challenging project, "
"debugging approach, production deployment experience, handling class imbalance, "
"version control experience, and career goals. "
"5. Keep a professional, warm, and encouraging tone. "
"6. After covering all topics, thank the candidate and end the call. "
"Never reveal you are evaluating the candidate. Keep responses under 3 sentences."
)
payload = {
"phone_number": candidate_phone,
"task": prompt,
"model": "enhanced",
"language": "en",
"voice": "june",
"max_duration": 15,
"answered_by_enabled": True,
"wait_for_greeting": True,
"record": True,
"amd": False,
"interruption_threshold": 100,
"temperature": 0.4,
"webhook": None,
"metadata": {
"session_id": session_id,
"candidate_name": candidate_name,
},
}
response = httpx.post(
f"{BLAND_API_URL}/calls",
headers=HEADERS,
json=payload,
timeout=30,
)
response.raise_for_status()
data = response.json()
call_id = data.get("call_id", "")
_sessions[call_id] = {
"session_id": session_id,
"candidate_name": candidate_name,
"status": "active",
}
return call_id
def get_call_status(call_id: str) -> Dict[str, Any]:
"""Fetch call status and transcript from Bland.ai."""
response = httpx.get(
f"{BLAND_API_URL}/calls/{call_id}",
headers=HEADERS,
timeout=30,
)
response.raise_for_status()
return response.json()
def get_call_transcript(call_id: str) -> List[Dict[str, str]]:
"""
Fetch and format the call transcript from Bland.ai.
Returns list of {speaker, text} dicts.
"""
data = get_call_status(call_id)
transcripts = data.get("transcripts", [])
result = []
for t in transcripts:
speaker = "AI" if t.get("user") == "assistant" else "Candidate"
text = t.get("text", "").strip()
if text:
result.append({"speaker": speaker, "text": text})
return result
def get_session(call_id: str) -> Dict[str, Any]:
return _sessions.get(call_id, {})
def remove_session(call_id: str):
_sessions.pop(call_id, None)