Skip to content

feat: BYO-LLM conversational voice mode — OpenAI-compatible endpoint + voice agent loop - #940

Open
andreolf wants to merge 1 commit into
jamiepine:mainfrom
andreolf:feat/byo-llm-conversation-clean
Open

feat: BYO-LLM conversational voice mode — OpenAI-compatible endpoint + voice agent loop#940
andreolf wants to merge 1 commit into
jamiepine:mainfrom
andreolf:feat/byo-llm-conversation-clean

Conversation

@andreolf

Copy link
Copy Markdown

Summary

Turns Voicebox from a TTS studio into a local voice agent: connect any OpenAI-compatible LLM (Ollama, LM Studio, OpenAI, Anthropic, etc.), speak or type to it, and hear replies in a cloned voice — running entirely on your machine.

This is the text-LLM bridge that the roadmap lists as the stepping stone before end-to-end speech LLMs (Moshi, GLM-4-Voice). It reuses the entire existing generation pipeline unchanged.

What's new

Backend

  • ConversationSettings ORM singleton (same pattern as CaptureSettings) — enabled, llm_endpoint, llm_api_key, llm_model, system_prompt_prefix
  • backend/backends/openai_compat_backend.pyOpenAICompatLLMBackend via httpx (already a dependency); implements both generate() and generate_with_history() for multi-turn chat
  • POST /conversation/turn — validates settings → calls external LLM with the profile's personality as system prompt + conversation history → enqueues TTS via the existing background job pipeline → returns assistant_text + generation_id immediately (non-blocking, same pattern as /generate)
  • GET /PUT /settings/conversation — settings CRUD, same pattern as captures/generation settings

Frontend

  • ConversationPanel.tsx — chat UI with user/assistant bubbles, Enter-to-send, polls SSE endpoint and auto-plays audio via the existing audio player store
  • ConversationPage.tsx — settings page: enable toggle, endpoint URL, API key (password field), model name, system-prompt textarea, provider compatibility sidebar (Ollama · LM Studio · OpenAI · any compatible API)
  • "Conversation" entry added to settings nav + TanStack Router
  • "Voice Chat" tab in VoiceInspector — visible only when conversation mode is enabled

Compatibility

Works out of the box with any server that speaks /v1/chat/completions:

  • Ollamahttp://localhost:11434, no API key
  • LM Studiohttp://localhost:1234, no API key
  • OpenAIhttps://api.openai.com, API key required
  • Anthropic — via compatible proxy

Test plan

  • Enable conversation mode in Settings → Conversation
  • Set endpoint to a running Ollama instance (ollama run llama3)
  • Open a voice profile → Voice Chat tab → send a message
  • Verify reply text appears and audio plays in the cloned voice
  • Disable conversation mode → Voice Chat tab disappears
  • Test with missing endpoint → 400 error surfaced in UI

🤖 Generated with Claude Code

- Add ConversationSettings ORM model (singleton pattern, same as
  CaptureSettings/GenerationSettings) with fields: enabled,
  llm_endpoint, llm_api_key, llm_model, system_prompt_prefix
- Export ConversationSettings from database package
- Add Pydantic models: ConversationSettingsResponse/Update,
  ConversationMessage, ConversationTurnRequest/Response
- Add OpenAI-compatible LLM backend (backends/openai_compat_backend.py)
  using httpx; supports single-turn generate() and multi-turn
  generate_with_history()
- Add voice agent loop (POST /conversation/turn): user message + history
  → external LLM with profile personality as system prompt → TTS via
  existing run_generation pipeline → returns assistant text + generation_id
- Add settings endpoints (GET/PUT /settings/conversation)
- Add useConversationSettings hook (optimistic updates, same pattern as
  useGenerationSettings)
- Add ConversationPanel React component with chat bubbles, Send button,
  auto-play TTS via SSE polling, keyboard shortcut (Enter to send)
- Add ConversationPage settings UI with endpoint/key/model/system-prompt
  fields and provider compatibility sidebar
- Wire Conversation tab into settings nav and TanStack Router
- Add Voice Chat tab to VoiceInspector (visible when conversation enabled)

Compatible with: Ollama, LM Studio, OpenAI, any OpenAI-compatible API.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ext-sakamoro

Copy link
Copy Markdown

Hi @andreolf — really nice work on this. I only spotted it after opening #947 / #948 today, which have some real overlap with the backend layer here:

  • We both create backend/backends/openai_compat_backend.py with an OpenAICompatLLMBackend on the LLMBackend protocol using httpx. That's a merge conflict if both PRs land as-is.
  • Feature scope diverges cleanly on top of it: this PR builds a new conversational voice chat UX (agent loop, ConversationSettings, ConversationPanel), while Add custom OpenAI-compatible LLM endpoint for refinement and personality #947 wires the same class into the existing refinement + personality-speak paths (extends CaptureSettings in place). Different DB tables, different frontend surfaces.

Whichever one @jamiepine wants first, happy to rebase mine onto the shared backend rather than duplicating it. If this PR lands first I'll pick up your OpenAICompatLLMBackend and drop mine; if #947 lands first, this one gets the same treatment.

Also — I noticed you're using the multi-turn generate_with_history() variant that isn't in the LLMBackend protocol yet. Probably worth adding it to the protocol so it stays available across backends, but that's a follow-up either way.

No rush from my side; happy to coordinate on whichever direction the maintainer prefers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants