File size: 7,584 Bytes
8b61324 e0ba152 8b61324 e0ba152 8b61324 1ff632a 8b61324 e0ba152 8b61324 6c6eb30 1ff632a 8b61324 05465db 8b61324 1ff632a 8b61324 1ff632a 8b61324 6c6eb30 8b61324 1ff632a 05465db 1ff632a 05465db 1ff632a 05465db 1ff632a 05465db 1ff632a 05465db 1ff632a 05465db 1ff632a 05465db 1ff632a 05465db 1ff632a 05465db 1ff632a 05465db 1ff632a 8b61324 1ff632a 05465db 8b61324 1ff632a 8b61324 1ff632a 8b61324 1ff632a 8b61324 1ff632a 0aab9be 1ff632a 8b61324 1ff632a 8b61324 05465db 8b61324 1ff632a 8b61324 1ff632a 8b61324 1ff632a 8b61324 1ff632a 8b61324 1ff632a 8b61324 1ff632a 8b61324 1ff632a |
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 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
import os
from app.models import User
import uvicorn
from fastapi import APIRouter, HTTPException, Request, Depends
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, Field
from app.config import settings
from vapi import Vapi
from dotenv import load_dotenv
from app.api.deps import get_db, get_current_user, get_chroma_collection
load_dotenv()
router = APIRouter()
# --- CONFIGURATION ---
VAPI_PRIVATE_KEY = os.getenv("VAPI_PRIVATE_KEY")
VAPI_ASSISTANT_ID = os.getenv("VAPI_ASSISTANT_ID")
SERVER_URL = os.getenv("SERVER_URL", "http://localhost:8000")
# Initialize Vapi Server SDK
try:
vapi_server = Vapi(token=VAPI_PRIVATE_KEY)
except Exception as e:
print(f"Vapi SDK Initialization Error: {e}")
print("Ensure VAPI_PRIVATE_KEY is set in .env")
# --- SCHEMAS ---
class ConfigRequest(BaseModel):
name: str
job_role: str
experience: str
level: str = "Medium"
# Optional customizable fields
model_name: str = "gpt-4o"
voice_provider: str = "11labs"
voice_id: str = "burt"
# --- ENDPOINTS ---
@router.post("/api/get-vapi-config")
async def get_vapi_config(data: ConfigRequest):
"""
Endpoint called by the Frontend to get the dynamically generated Assistant configuration.
"""
if not VAPI_ASSISTANT_ID:
raise HTTPException(
status_code=503,
detail="VAPI_ASSISTANT_ID not configured in .env."
)
try:
print(f"\n--- New Interview Request ---")
print(f"👤 User: {data.name}, Role: {data.job_role}, Exp: {data.experience}, Level: {data.level}")
system_prompt = (
f"You are the hiring manager at a tech company. You are conducting a strict 5-minute screening interview "
f"with {data.name} for a {data.job_role} role. The candidate has {data.experience} years of experience. "
f"The difficulty level is {data.level}. Your style is clear, concise, and professional. Do not lecture. "
"All questions must be relevant to the provided job role.\n\n"
"You MUST strictly follow this interview flow:\n\n"
"1. **Introduction**: You have already welcomed them. Wait for their confirmation to begin.\n\n"
"2. **Background Snapshot**: Ask: "
"'Give me a brief 30–40 second overview of your background and the type of work you've done related to this role.'\n\n"
"3. **Technical Depth**: Ask the candidate to choose one project relevant to the job role. "
"Ask: 'Pick one project you're proud of that aligns with this role. In under a minute, explain the problem, your approach, "
"tools or techniques used, and the business impact.'\n\n"
"4. **Core Skills Check**: Inform them you will ask 3 rapid questions tailored to the job. "
"Generate **three crisp, role-specific skill checks** following this logic:\n"
" - Question 1: A foundational concept essential to the role.\n"
" - Question 2: A practical troubleshooting or decision-making question.\n"
" - Question 3: A tool, framework, or technology familiarity question.\n"
"Questions must be specific to the given job role.\n\n"
"5. **Practical Scenario**: Generate **one short applied scenario** relevant to the role. "
"Ask the candidate to describe their high-level approach to solve it.\n\n"
"6. **Role & Communication Fit**: Ask a communication-focused question, such as: "
"'This role requires cross-team collaboration. Can you give an example where you explained something complex "
"to a non-technical or differently-skilled stakeholder?'\n\n"
"7. **Wrap-Up**: Say: 'Thank you. Any questions for me?' Then conclude: 'We’ll get back to you with next steps.'\n\n"
"**CRITICAL RULES:**\n"
"- Do NOT exceed the boundaries of each segment.\n"
"- If their answers run long, politely interrupt and move forward.\n"
"- Keep your phrasing tight and professional.\n"
"- All generated questions MUST be directly relevant to the specified job role."
)
webhook_url = f"{SERVER_URL}/api/webhook"
assistant_overrides = {
"model": {
"provider": "openai",
"model": data.model_name,
"temperature": 0.3,
"maxTokens": 150,
"emotionRecognitionEnabled": True,
"messages": [
{"role": "system", "content": system_prompt}
]
},
"voice": {
"provider": data.voice_provider,
"voiceId": data.voice_id,
"speed": 1.1,
"stability": 0.5
},
"firstMessage": f"Hi {data.name}, thanks for joining. I’m the hiring manager. This is a quick 5-minute screening to understand your background and fit. Shall we begin?",
"maxDurationSeconds": 360,
"silenceTimeoutSeconds": 40,
"backgroundSound": "office",
"backgroundDenoisingEnabled": True,
"endCallPhrases": [
"Goodbye",
"Have a great day",
"We’ll get back to you with next steps",
"Thank you for your time"
],
# --- SERVER ---
"server": {
"url": webhook_url
},
"metadata": {
"user_name": data.name,
"job_role": data.job_role,
"environment": "production_screening"
}
}
return {
"assistantId": VAPI_ASSISTANT_ID,
"overrides": assistant_overrides
}
except Exception as e:
print(f"❌ Vapi Configuration Error: {e}")
raise HTTPException(status_code=500, detail=f"Failed to configure agent: {str(e)}")
@router.post("/api/webhook")
async def vapi_webhook_receiver(request: Request):
"""
Endpoint that receives asynchronous events from Vapi's servers.
Saves transcripts to a local file.
"""
payload = await request.json()
message = payload.get("message", {})
call_id = payload.get("call", {}).get("id", "unknown_call")
os.makedirs("transcripts", exist_ok=True)
if message.get("type") == "transcript" and message.get("transcriptType") == "final":
transcript_text = message.get('transcript')
role = message.get('role', 'unknown')
try:
with open(f"transcripts/{call_id}.txt", "a", encoding="utf-8") as f:
f.write(f"{role.upper()}: {transcript_text}\n")
print(f"🗣️ [Saved] {role.upper()}: {transcript_text}")
except Exception as e:
print(f"❌ Error saving transcript: {e}")
elif message.get("type") == "end-of-call-report":
metadata = payload.get("assistant", {}).get("metadata", {})
summary = message.get('summary', 'N/A')
# Save summary to the same file
try:
with open(f"transcripts/{call_id}.txt", "a", encoding="utf-8") as f:
f.write(f"\n--- SUMMARY ---\n{summary}\n")
except Exception as e:
print(f"❌ Error saving summary: {e}")
print(f"\n--- 🏁 Call Ended Report ---")
print(f" User: {metadata.get('user_name')}")
print(f" Summary: {summary}")
print(f"---------------------------\n")
return {"status": "ok"} |