part 3:
)
# Set up metrics collection
from livekit.agents.voice import MetricsCollectedEvent
from livekit.agents import metrics
usage_collector = metrics.UsageCollector()
# Track metrics for latency calculation
eou_metrics = {}
llm_metrics = {}
tts_metrics = {}
@session.on("metrics_collected")
def _on_metrics_collected(ev: MetricsCollectedEvent):
# Use LiveKit's recommended log_metrics helper
metrics.log_metrics(ev.metrics)
# Track metrics by type for latency calculation
metrics_type = getattr(ev.metrics, 'type', 'unknown')
speech_id = getattr(ev.metrics, 'speech_id', None)
if metrics_type == 'eou_metrics' and speech_id:
eou_metrics[speech_id] = ev.metrics
logger.info(f"šÆ EOU METRICS CAPTURED: {ev.metrics}")
elif metrics_type == 'llm_metrics' and speech_id:
llm_metrics[speech_id] = ev.metrics
logger.info(f"š§ LLM METRICS CAPTURED: {ev.metrics}")
elif metrics_type == 'tts_metrics' and speech_id:
tts_metrics[speech_id] = ev.metrics
logger.info(f"š TTS METRICS CAPTURED: {ev.metrics}")
# Calculate total latency when we have all three metrics for a speech_id
if speech_id in eou_metrics and speech_id in llm_metrics and speech_id in tts_metrics:
eou = eou_metrics[speech_id]
llm = llm_metrics[speech_id]
tts = tts_metrics[speech_id]
# Calculate total latency according to LiveKit formula
total_latency = (
getattr(eou, 'end_of_utterance_delay', 0) +
getattr(llm, 'ttft', 0) +
getattr(tts, 'ttfb', 0)
)
logger.info(f"ā” TOTAL LATENCY CALCULATION for {speech_id}:")
logger.info(f" EOU delay: {getattr(eou, 'end_of_utterance_delay', 0):.3f}s")
logger.info(f" LLM ttft: {getattr(llm, 'ttft', 0):.3f}s")
logger.info(f" TTS ttfb: {getattr(tts, 'ttfb', 0):.3f}s")
logger.info(f" TOTAL: {total_latency:.3f}s")
# Log detailed metrics for analysis
agent.interview_logger.log_turn_detection_metrics(
metrics={
"type": "latency_analysis",
"speech_id": speech_id,
"total_latency": total_latency,
"eou_metrics": {
"end_of_utterance_delay": getattr(eou, 'end_of_utterance_delay', 0),
"transcription_delay": getattr(eou, 'transcription_delay', 0),
"on_user_turn_completed_delay": getattr(eou, 'on_user_turn_completed_delay', 0)
},
"llm_metrics": {
"duration": getattr(llm, 'duration', 0),
"ttft": getattr(llm, 'ttft', 0),
"completion_tokens": getattr(llm, 'completion_tokens', 0),
"tokens_per_second": getattr(llm, 'tokens_per_second', 0)
},
"tts_metrics": {
"duration": getattr(tts, 'duration', 0),
"ttfb": getattr(tts, 'ttfb', 0),
"audio_duration": getattr(tts, 'audio_duration', 0),
"characters_count": getattr(tts, 'characters_count', 0)
}
}
)
# Enhanced turn detection logging for other metrics
metrics_str = str(ev.metrics).lower()
if 'turn' in metrics_str or 'detection' in metrics_str:
logger.info(f"šÆ TURN DETECTION METRIC: {ev.metrics}")
# Log detailed turn detection metrics
agent.interview_logger.log_turn_detection_metrics(
metrics={
"type": getattr(ev.metrics, 'type', 'unknown'),
"label": getattr(ev.metrics, 'label', 'unknown'),
"timestamp": getattr(ev.metrics, 'timestamp', time.time()),
"raw_metrics": str(ev.metrics)
}
)
# Extract confidence if available
if hasattr(ev.metrics, 'confidence'):
agent.last_turn_detection_confidence = ev.metrics.confidence
elif hasattr(ev.metrics, 'value') and isinstance(ev.metrics.value, (int, float)):
agent.last_turn_detection_confidence = float(ev.metrics.value)
usage_collector.collect(ev.metrics)
# Add turn detection event handlers
logger.info("š§ Registering turn_detected event handler")
@session.on("turn_detected")
def _on_turn_detected(ev):
"""Handle turn detection events."""
logger.info(f"šÆ TURN DETECTED: {ev}")
print(f"šÆ TURN DETECTED: {ev}") # Immediate console feedback
# Record turn detection start time
agent.turn_detection_start_time = time.time()
# Log successful turn detection
current_question = agent.interview_questions.get_current_question()
question_number = current_question.number if current_question else 0
agent.interview_logger.log_turn_detection_attempt(
question_number=question_number,
attempt_type="turn_detected",
confidence=getattr(ev, 'confidence', 0.9),
success=True,
details=f"Turn detected: {ev}"
)
@session.on("turn_detection_failed")
def _on_turn_detection_failed(ev):
"""Handle turn detection failure events."""
logger.warning(f"ā TURN DETECTION FAILED: {ev}")
current_question = agent.interview_questions.get_current_question()
question_number = current_question.number if current_question else 0
agent.interview_logger.log_turn_detection_attempt(
question_number=question_number,
attempt_type="turn_detection_failed",
confidence=getattr(ev, 'confidence', 0.0),
success=False,
details=f"Turn detection failed: {ev}"
)
@session.on("turn_detection_timeout")
def _on_turn_detection_timeout(ev):
"""Handle turn detection timeout events."""
logger.warning(f"ā° TURN DETECTION TIMEOUT: {ev}")
current_question = agent.interview_questions.get_current_question()
question_number = current_question.number if current_question else 0
timeout_duration = getattr(ev, 'timeout_duration', 0.0)
agent.interview_logger.log_turn_detection_timeout(
question_number=question_number,
timeout_duration=timeout_duration,
last_confidence=agent.last_turn_detection_confidence
)
# Add session event handlers for turn detection model status
logger.info("š§ Registering user_state_changed event handler")
@session.on("user_state_changed")
def _on_user_state_changed(ev):
"""Handle user state change events."""
logger.info(f"š¤ USER STATE CHANGED: {ev}")
print(f"š¤ USER STATE CHANGED: {ev}") # Immediate console feedback
current_question = agent.interview_questions.get_current_question()
question_number = current_question.number if current_question else 0
agent.interview_logger.log_turn_detection_attempt(
question_number=question_number,
attempt_type="user_state_changed",
confidence=0.9,
success=True,
details=f"User state changed: {ev}"
)
@session.on("session_started")
def _on_session_started(ev):
"""Handle session start events."""
logger.info(f"š Session started: {ev}")
agent.interview_logger.log_turn_detection_model_event(
"session_started",
{"event": str(ev), "status": "active"}
)
@session.on("session_ended")
def _on_session_ended(ev):
"""Handle session end events."""
logger.info(f"š Session ended: {ev}")
agent.interview_logger.log_turn_detection_model_event(
"session_ended",
{"event": str(ev), "status": "inactive"}
)
# Add error handling for turn detection model
@session.on("error")
def _on_error(ev):
"""Handle error events."""
error_msg = str(ev)
logger.error(f"ā Session error: {error_msg}")
# Check if it's a turn detection related error
if 'turn' in error_msg.lower() or 'detection' in error_msg.lower():
agent.interview_logger.log_turn_detection_model_event(
"error",
{"error_message": error_msg, "event": str(ev)}
)
else:
agent.interview_logger.log_error(
"session_error",
error_msg
)
async def log_final_usage():
summary = usage_collector.get_summary()
logger.info(f"Final usage summary: {summary}")
ctx.add_shutdown_callback(log_final_usage)
# Start the session
await session.start(
agent=agent,
room=ctx.room
)
if
name == "__main__":
cli.run_app(
WorkerOptions(
entrypoint_fnc=entrypoint,
prewarm_fnc=prewarm
)
)