feat: add seamless text and voice chat
Rebuild & Deploy / rebuild (push) Successful in 49s

This commit is contained in:
snowspeeder committed 2026-08-29 07:49:29 -04:00
1 parent 321421e37f
commit 120ab192f1
7 files changed
+558 -79

No files matched your search

+29 -16
View File
@@ -80,7 +80,8 @@ SYSTEM_PROMPT = textwrap.dedent("""\
no markdown, and no formatting except the Azure delivery markers below.
- Spell out numbers when it sounds more natural ("twenty twenty-six"
instead of "2026").
- If you need to ask a question, ask exactly one.
- Make reasonable assumptions and answer directly. Ask a follow-up only
when it is genuinely impossible to proceed without one.
- Be warm and direct. Don't be sycophantic or overly formal.
- If you don't know something, say so briefly and move on.
@@ -144,7 +145,7 @@ SYSTEM_PROMPT = textwrap.dedent("""\
- User says "look up the price of a PS5" → CALL dispatch_task with
description="Find the current retail price of a PlayStation 5"
After calling dispatch_task, tell the user "I'll get on that for you"
and keep the conversation going. The result comes back automatically — when
without asking for more permission or details. The result comes back automatically — when
you receive it, share the findings naturally in your conversational style.
You can have multiple tasks running at once.
@@ -612,29 +613,41 @@ async def handle_job(ctx: JobContext) -> None:
),
)
# Listen for data messages (voice switching) from the web UI.
# Listen for conversation controls (voice selection and voice/text mode).
# Defined here so it captures this job's session via closure.
def _on_room_data(packet) -> None:
topic = getattr(packet, "topic", None)
if topic and topic != "voice-control":
if topic and topic not in ("voice-control", "conversation-control"):
return
try:
msg = json.loads(packet.data.decode("utf-8"))
except (json.JSONDecodeError, UnicodeDecodeError):
return
if msg.get("type") != "set_voice":
if msg.get("type") == "set_mode":
mode = msg.get("mode")
if mode not in ("voice", "text"):
return
voice_enabled = mode == "voice"
logger.info("Switching conversation mode to %s", mode)
if not voice_enabled:
asyncio.create_task(session.interrupt())
session.input.set_audio_enabled(voice_enabled)
session.output.set_audio_enabled(voice_enabled)
return
voice = msg.get("voice", "")
if not voice:
return
logger.info("Switching TTS voice to %s", voice)
try:
tts = session.tts
if hasattr(tts, "update_options"):
tts.update_options(voice=voice)
logger.info("Voice updated to %s", voice)
except Exception as e: # noqa: BLE001
logger.warning("Failed to update voice: %s", e)
if msg.get("type") == "set_voice":
voice = msg.get("voice", "")
if not voice:
return
logger.info("Switching TTS voice to %s", voice)
try:
active_tts = session.tts
if hasattr(active_tts, "update_options"):
active_tts.update_options(voice=voice)
logger.info("Voice updated to %s", voice)
except Exception as e: # noqa: BLE001
logger.warning("Failed to update voice: %s", e)
ctx.room.on("data_received", _on_room_data)