feat: integrate JV Voice Profile cloning, Voicebox research, and ensure session voice/markdown prompt nudge

This commit is contained in:
Adolfo Reyna
2026-08-12 19:57:03 -04:00
parent 811a40f2cc
commit 70da5d857a
11 changed files with 661 additions and 50 deletions
+28 -1
View File
@@ -50,6 +50,17 @@ SESSION_ID_REGEX = re.compile(r"\bsession_id:\s*([^\s]+)", re.IGNORECASE)
VOICE_PROMPT_NUDGE = """[System Instruction / Voice & UI Context:
You are communicating with the user in a real-time voice conversation over microphone and TTS, while displaying formatted responses in the Companion Web UI.
- Keep spoken responses natural, concise, and conversational (1-2 sentences per turn unless details are requested).
- Use clean Markdown formatting (bolding, code blocks, bullet points) for readability in the Web UI.
- Write text meant to be read aloud using clear, natural phrasing and conversational contractions.
- Avoid repetitive filler openers like "Certainly!", "Absolutely!", or "Great question!".
- Perform any required tools or file operations silently without narrating step-by-step internal execution.]
"""
def _strip_ansi(text: str) -> str:
if not text:
return ""
@@ -220,6 +231,8 @@ class HermesLLM(FrameProcessor):
# Hermes conversations.
self._session_state_file = self._cwd / ".hermes-voice-session.json"
self._nudge_sent = False
# Persisted session ID
self._session_id: str | None = self._load_session_id()
if self._session_id:
@@ -230,6 +243,7 @@ class HermesLLM(FrameProcessor):
logger.info("Resetting active Hermes session state...")
self._session_id = None
self._session_renamed = False
self._nudge_sent = False
self._history.clear()
if self._proc:
asyncio.create_task(self._stop_persistent_proc())
@@ -246,6 +260,7 @@ class HermesLLM(FrameProcessor):
logger.info(f"Hermes session state updated from disk: {self._session_id} -> {disk_sid}")
self._session_id = disk_sid
self._session_renamed = False
self._nudge_sent = False
self._history.clear()
if self._proc:
asyncio.create_task(self._stop_persistent_proc())
@@ -522,6 +537,11 @@ class HermesLLM(FrameProcessor):
if not messages or messages[-1].get("content") != utterance:
messages.append({"role": "user", "content": utterance})
if not self._nudge_sent and messages:
messages[0]["content"] = VOICE_PROMPT_NUDGE + messages[0]["content"]
self._nudge_sent = True
logger.info("Injecting voice & markdown interaction context nudge into Hermes session.")
payload = {
"model": "hermes-agent" if not self._model or self._model.lower() in ("default", "none", "") else self._model,
"messages": messages,
@@ -675,7 +695,14 @@ class HermesLLM(FrameProcessor):
"""Run turn via Hermes CLI using persistent session tracking."""
if spoken_chunks is None:
spoken_chunks = chunks
cmd = [self._cli_path, "chat", "-q", utterance, "-Q", "--source", "voice", "--reasoning", "none"]
prompt_to_send = utterance
if not self._nudge_sent:
prompt_to_send = VOICE_PROMPT_NUDGE + utterance
self._nudge_sent = True
logger.info("Initializing Hermes turn with voice & markdown interaction context nudge.")
cmd = [self._cli_path, "chat", "-q", prompt_to_send, "-Q", "--source", "voice"]
if self._session_id:
cmd.extend(["-r", self._session_id])
if self._model and self._model.lower() not in ("default", "none", ""):