M6 + M0: hardening, deploy docs, PoC scripts
- Pipeline-down fallback: dialplan plays gogo-fallback after a failed AudioSocket (§15) + script to synthesize the Bosnian prompt via TTS - Voice heartbeat file → /health/voice endpoint + live status in admin panel - Retention job also purges stale call registrations; chat sessions metered; 100%-usage super-admin alert email (GOGO_ADMIN_ALERT_EMAIL) - .env.example, docs/DEPLOY.md (compose stack, GPU node, backups pg_dump, update procedure, security notes) - §16 Definition of Done as an automated test: softphone-style call → fake partner availability → request pushed → webhook confirm → console SMS - M0 PoC scripts: STT accuracy harness (CER/WER + spoken-digit check; dry-run verified with espeak-ng samples + faster-whisper small on CPU — phone number extracted exactly), TTS bake-off (Azure vs ElevenLabs, latency+cost), and end-to-end latency smoke test speaking real AudioSocket to the live pipeline Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -94,14 +94,38 @@ async def handle_connection(reader: asyncio.StreamReader, writer: asyncio.Stream
|
||||
await session.run()
|
||||
|
||||
|
||||
async def _heartbeat() -> None:
|
||||
"""Touch a heartbeat file every 15 s; /health/voice + admin panel read its age (M6)."""
|
||||
import os
|
||||
|
||||
path = heartbeat_path()
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
while True:
|
||||
try:
|
||||
with open(path, "w") as f:
|
||||
f.write(str(int(asyncio.get_running_loop().time())))
|
||||
except OSError:
|
||||
log.exception("cannot write heartbeat %s", path)
|
||||
await asyncio.sleep(15)
|
||||
|
||||
|
||||
def heartbeat_path() -> str:
|
||||
import os
|
||||
|
||||
return os.path.join(get_settings().recordings_dir, ".voice-heartbeat")
|
||||
|
||||
|
||||
async def serve() -> None:
|
||||
s = get_settings()
|
||||
server = await asyncio.start_server(handle_connection, s.voice_host, s.voice_port)
|
||||
addrs = ", ".join(str(sock.getsockname()) for sock in server.sockets)
|
||||
log.info("AudioSocket server listening on %s", addrs)
|
||||
# health heartbeat file for the admin panel / monitoring (M6)
|
||||
async with server:
|
||||
await server.serve_forever()
|
||||
heartbeat = asyncio.create_task(_heartbeat())
|
||||
try:
|
||||
async with server:
|
||||
await server.serve_forever()
|
||||
finally:
|
||||
heartbeat.cancel()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
|
||||
Reference in New Issue
Block a user