The Proctoring ML Service communicates via LiveKit Data Channel. It publishes alerts that can be consumed by the Node.js backend or any LiveKit client.
All proctoring alerts are published to this topic.
// Subscribe in Node.js backend
room.on('dataReceived', (payload, participant, topic) => {
if (topic === 'proctoring') {
const alert = JSON.parse(payload.toString());
handleProctoringAlert(alert);
}
});{
"participant_id": "user_123",
"timestamp": "2024-12-08T10:30:00.000Z",
"type": "phone_detected",
"severity": "high",
"cheating_probability": 0.85,
"source": "video",
"details": {
// Alert-specific details
}
}| Field | Type | Description |
|---|---|---|
participant_id |
string | LiveKit participant identity |
timestamp |
string | ISO 8601 timestamp |
type |
AlertType | Type of alert (see below) |
severity |
AlertSeverity | low, medium, high, critical |
cheating_probability |
float | 0.0 - 1.0 probability score |
source |
string | "video" or "audio" |
details |
object | Alert-specific data |
Phone visible in the frame.
{
"type": "phone_detected",
"severity": "high",
"details": {
"phone_confidence": 0.92,
"phone_bbox": [100, 200, 150, 280],
"phone_position": "hand"
}
}More than one face detected in frame.
{
"type": "multiple_faces",
"severity": "high",
"details": {
"face_count": 2,
"primary_face_bbox": [120, 80, 320, 350],
"other_faces": [
{"bbox": [400, 100, 500, 250], "confidence": 0.88}
]
}
}Candidate consistently looking away from screen.
{
"type": "looking_away",
"severity": "medium",
"details": {
"gaze_away_ratio": 0.65,
"gaze_direction": {"x": -0.8, "y": 0.2},
"duration_seconds": 4.2
}
}No face detected in frame.
{
"type": "face_not_visible",
"severity": "medium",
"details": {
"face_visible_ratio": 0.15,
"last_face_seen": "2024-12-08T10:29:55.000Z",
"duration_seconds": 5.0
}
}High cheating probability without specific trigger.
{
"type": "suspicious_behavior",
"severity": "medium",
"details": {
"top_factors": ["gaze_variance", "head_movement_freq"],
"feature_scores": {
"gaze_variance": 0.85,
"head_movement_freq": 0.72
}
}
}Multiple distinct speakers detected.
{
"type": "multiple_voices",
"severity": "high",
"source": "audio",
"details": {
"speaker_count": 2,
"primary_speaker_confidence": 0.95,
"duration_seconds": 3.5
}
}Speech detected from off-screen source.
{
"type": "background_speech",
"severity": "medium",
"source": "audio",
"details": {
"background_voice_confidence": 0.78,
"primary_voice_active": true
}
}Low-volume speech detected (potential coaching).
{
"type": "whispering",
"severity": "medium",
"source": "audio",
"details": {
"audio_level_db": -32.5,
"voice_confidence": 0.88,
"duration_seconds": 2.1
}
}Unusual audio patterns detected.
{
"type": "suspicious_audio",
"severity": "medium",
"source": "audio",
"details": {
"pattern_type": "text_to_speech",
"confidence": 0.72
}
}No audio received for extended period.
{
"type": "microphone_issue",
"severity": "low",
"source": "audio",
"details": {
"silence_duration_seconds": 45,
"last_audio_received": "2024-12-08T10:29:15.000Z"
}
}| Level | Description | Response |
|---|---|---|
low |
Minor anomaly, may be false positive | Log only |
medium |
Noticeable violation, gentle warning | AI issues gentle reminder |
high |
Significant violation | AI issues serious warning |
critical |
Major violation, likely cheating | Consider termination |
The Node.js backend can use these pre-defined messages for the AI interviewer:
const PHONE_MESSAGES = {
gentle: "I noticed something in your hands. Please make sure your desk is clear and focus on the interview.",
serious: "I need to remind you that using external devices is not permitted during this interview.",
final: "This is a final warning. Any further use of phones or devices will terminate this interview."
};const MULTIPLE_FACES_MESSAGES = {
gentle: "I'm seeing multiple faces in the frame. Please ensure you're alone in the room.",
serious: "This interview requires you to be alone. Please ensure no one else is visible.",
final: "I've detected multiple people several times. This is your final warning."
};const LOOKING_AWAY_MESSAGES = {
gentle: "I've noticed you looking away from the screen quite a bit. Please try to maintain focus on the interview.",
serious: "Please keep your attention on the screen. Looking away frequently affects the interview.",
final: "Excessive looking away may indicate outside assistance. Please focus on the screen."
};const AUDIO_MESSAGES = {
multiple_voices: {
gentle: "I'm picking up some background voices. Please ensure you're in a quiet environment.",
serious: "I'm detecting another person speaking. Please confirm you're alone."
},
whispering: {
gentle: "I noticed some quiet sounds. Please speak clearly so I can hear you well.",
serious: "Please speak at a normal volume so we can continue the interview properly."
}
};At the end of an interview, request a session summary:
{
"action": "get_summary",
"participant_id": "user_123"
}{
"participant_id": "user_123",
"session_duration_seconds": 1800,
"frames_processed": 18000,
"audio_chunks_processed": 3600,
"integrity_score": 85,
"alerts_summary": {
"total": 5,
"by_type": {
"looking_away": 3,
"phone_detected": 1,
"background_speech": 1
},
"by_severity": {
"low": 1,
"medium": 3,
"high": 1
}
},
"feature_averages": {
"gaze_away_ratio": 0.15,
"face_visible_ratio": 0.95,
"phone_detected_ratio": 0.02,
"multiple_faces_ratio": 0.00
},
"timeline": [
{
"timestamp": "2024-12-08T10:05:00.000Z",
"type": "looking_away",
"severity": "medium"
},
{
"timestamp": "2024-12-08T10:12:30.000Z",
"type": "phone_detected",
"severity": "high"
}
]
}{
"error": true,
"code": "MODEL_LOAD_FAILED",
"message": "Failed to load YOLO11 model",
"timestamp": "2024-12-08T10:30:00.000Z"
}| Code | Description |
|---|---|
MODEL_LOAD_FAILED |
Failed to load ML model |
FRAME_DECODE_ERROR |
Could not decode video frame |
AUDIO_DECODE_ERROR |
Could not decode audio chunk |
PROCESSING_TIMEOUT |
Processing took too long |
GPU_OOM |
GPU out of memory |
| Alert Type | Cooldown (seconds) |
|---|---|
phone_detected |
10 |
multiple_faces |
15 |
looking_away |
20 |
face_not_visible |
10 |
multiple_voices |
15 |
background_speech |
20 |
whispering |
20 |
Alerts of the same type for the same participant will not be published more frequently than the cooldown period.