62 lines
1.8 KiB
Python
62 lines
1.8 KiB
Python
import logging
|
|
import threading
|
|
from threading import Event
|
|
|
|
import uvicorn
|
|
|
|
from speech_to_speech.api.openai_realtime.pipeline_unit import PipelineUnit
|
|
from speech_to_speech.api.openai_realtime.websocket_router import create_app
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class RealtimeServer:
|
|
"""
|
|
Pipeline handler for the OpenAI Realtime API mode.
|
|
|
|
Owns a pool of isolated PipelineUnits and a single uvicorn server.
|
|
The websocket route claims the next free unit on each accept and releases
|
|
it on disconnect; once all units are in use, further connections are rejected.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
stop_event: Event,
|
|
pool: list[PipelineUnit],
|
|
host: str = "0.0.0.0",
|
|
port: int = 8765,
|
|
) -> None:
|
|
if not pool:
|
|
raise ValueError("RealtimeServer requires at least one PipelineUnit in the pool")
|
|
self.stop_event = stop_event
|
|
self.pool = pool
|
|
self.host = host
|
|
self.port = port
|
|
|
|
def run(self) -> None:
|
|
"""Start the FastAPI/uvicorn server (called from a ThreadManager thread)."""
|
|
app = create_app(pool=self.pool, stop_event=self.stop_event)
|
|
|
|
logger.info(
|
|
f"OpenAI Realtime API starting on ws://{self.host}:{self.port}/v1/realtime (pool size {len(self.pool)})"
|
|
)
|
|
|
|
config = uvicorn.Config(
|
|
app,
|
|
host=self.host,
|
|
port=self.port,
|
|
log_level="info",
|
|
)
|
|
server = uvicorn.Server(config)
|
|
|
|
server.install_signal_handlers = lambda: None # type: ignore[attr-defined]
|
|
|
|
def _watch_stop() -> None:
|
|
self.stop_event.wait()
|
|
server.should_exit = True
|
|
|
|
watcher = threading.Thread(target=_watch_stop, daemon=True)
|
|
watcher.start()
|
|
|
|
server.run()
|