| import logging |
| from typing import Any |
| from models.event import Event, EventType |
| from models.session_state import TurnState |
| from queues import queue_names |
|
|
| logger = logging.getLogger("s2s_server") |
|
|
| class TurnManager: |
| def __init__(self, context: Any): |
| self.context = context |
| self.state = TurnState.IDLE |
|
|
| |
| self.context.event_bus.subscribe(EventType.USER_SPEECH_START, self.on_user_speech_start) |
| self.context.event_bus.subscribe(EventType.USER_SPEECH_END, self.on_user_speech_end) |
| self.context.event_bus.subscribe(EventType.STABLE_TRANSCRIPT, self.on_stable_transcript) |
| self.context.event_bus.subscribe(EventType.LLM_STARTED, self.on_llm_started) |
| self.context.event_bus.subscribe(EventType.BARGE_IN, self.on_barge_in) |
| self.context.event_bus.subscribe(EventType.CANCEL_ALL, self.on_cancel_all) |
| self.context.event_bus.subscribe(EventType.TTS_FINISHED, self.on_tts_finished) |
| self.context.event_bus.subscribe(EventType.TTS_STARTED, self.on_tts_started) |
|
|
| async def on_user_speech_start(self, event: Event): |
| import uuid |
| self.context.turn_id = str(uuid.uuid4())[:8] |
| self.context.logger.info( |
| f"[{self.context.session_id}] TurnManager: User speech start detected (state: {self.state.name}, turn_id: {self.context.turn_id})" |
| ) |
|
|
| |
| if self.context.session_config.allow_interruptions and self.state in ( |
| TurnState.THINKING, |
| TurnState.SPEAKING, |
| ): |
| await self.context.event_bus.publish(EventType.BARGE_IN) |
|
|
| self.state = TurnState.USER_SPEAKING |
| await self.context.websocket.send_json({"type": "status", "message": "Listening..."}) |
|
|
| async def on_user_speech_end(self, event: Event): |
| self.context.logger.info(f"[{self.context.session_id}] TurnManager: User speech end detected.") |
| full_audio = event.payload |
| self.context.queue_registry.get(queue_names.TRANSCRIPT).put_nowait(full_audio) |
|
|
| async def on_stable_transcript(self, event: Event): |
| text = event.payload |
| self.context.logger.info( |
| f"[{self.context.session_id}] TurnManager: Stable transcript received: '{text}'" |
| ) |
| await self.context.websocket.send_json({"type": "transcription", "text": text}) |
|
|
| |
| self.context.queue_registry.get(queue_names.LLM).put_nowait(text) |
|
|
| async def on_llm_started(self, event: Event): |
| self.state = TurnState.THINKING |
| await self.context.websocket.send_json({"type": "status", "message": "Thinking..."}) |
|
|
| async def on_tts_started(self, event: Event): |
| self.state = TurnState.SPEAKING |
|
|
| async def on_barge_in(self, event: Event): |
| self.context.logger.info(f"[{self.context.session_id}] TurnManager: Barge-in triggered.") |
| self.state = TurnState.INTERRUPTED |
| await self.context.websocket.send_json({"type": "interrupt"}) |
| await self.context.event_bus.publish(EventType.CANCEL_ALL) |
|
|
| async def on_cancel_all(self, event: Event): |
| self.context.logger.info(f"[{self.context.session_id}] TurnManager: Cancelling active response.") |
| |
| await self.context.task_supervisor.cancel_tasks("llm_generation", "tts_generation") |
| |
| self.context.cleanup_manager.perform_cleanup() |
|
|
| async def on_tts_finished(self, event: Event): |
| tts_queue = self.context.queue_registry.get(queue_names.TTS) |
| if tts_queue.empty() and self.state != TurnState.USER_SPEAKING: |
| self.context.logger.info(f"[{self.context.session_id}] TurnManager: Turn complete. Returning to IDLE.") |
| self.state = TurnState.IDLE |
| await self.context.websocket.send_json({"type": "done"}) |
| await self.context.event_bus.publish(EventType.TURN_COMPLETED) |
|
|