Skip to content

Add real-time voice chat with Deepgram Flux - #8

Open
dg-edcharbeneau wants to merge 6 commits into
danroth27:mainfrom
dg-edcharbeneau:main
Open

dg-edcharbeneau wants to merge 6 commits into
danroth27:mainfrom
dg-edcharbeneau:main

Conversation

@dg-edcharbeneau

Copy link
Copy Markdown

Summary

Adds a Real-time voice chat scenario (/voice-chat). You talk to an agent and it talks back: Deepgram Flux handles speech-to-text and Flux TTS handles text-to-speech. The scenario wraps the existing AG-UI pipeline without changing it. The only server-side addition is a dedicated /voice_chat agent.

  • Speech in: Flux (/v2/listen, flux-general-en) decides when you've finished speaking. The transcript is then sent with AgentContext.SendMessageAsync.
  • Speech out: SpeechTapChatClient is a DelegatingChatClient that sits inside FormattedChatClient. It splits the streamed reply into sentences, strips Markdown and skips code blocks. Each reply is spoken as one Flux TTS turn (/v2/speak, flux-haley-en, expressivity 1).
  • Auth: the browser opens both Deepgram WebSockets itself. It authenticates with a 30-second JWT from the new /api/deepgram/token endpoint, passed as the bearer subprotocol. The API key stays on the server, and no audio goes through the Blazor circuit.
  • Optional key: the key comes from the Parameters:deepgram-api-key AppHost secret or a DEEPGRAM_API_KEY environment variable. Without a key, the app runs normally and the page says how to enable voice.

The voice agent

/voice_chat is a separate agent, so the Agentic chat prompt is untouched.

  • Instructions written for speech:
    • one to three short, plain sentences
    • no Markdown, lists, code, emoji or URLs
    • say symbols and numbers the way a person would
    • adapt to the user's tone as read from the transcript
    • treat [interrupted by the user] as the point where the user stopped listening
  • Lower latency:
    • With gpt-5-mini, the model reasoned silently for 2.3 to 4 s before its first token. The voice agent requests minimal reasoning effort, which brought the first token to about 1.3 s; Agentic chat takes 3.5 to 4.1 s for the same question.
    • Microsoft.Extensions.AI's ReasoningEffort has no minimal level, so it's set with ChatReasoningEffortLevel.Minimal through ChatOptions.RawRepresentationFactory.
    • It's sent only to reasoning deployments (Foundry.SupportsReasoningEffort), because gpt-4o-mini rejects reasoning_effort with HTTP 400.

Voice UI best practices

This follows dg-edcharbeneau/voice-best-practices:

dg-edcharbeneau and others added 6 commits October 5, 2026 12:48
Add a /voice-chat scenario that layers Deepgram voice around the
unchanged AG-UI pipeline: Flux speech-to-text (/v2/listen) in, Flux TTS
(/v2/speak) out, with barge-in. The browser connects to Deepgram
directly using short-lived JWTs minted by a new /api/deepgram/token
endpoint, so the API key stays on the server and no audio crosses the
Blazor circuit.

The voice UI follows the Deepgram voice UI best practices: an explicit
state machine shown as an always-visible status, a mic meter driven in
JS, AudioWorklet capture, gap-free playback, one TTS socket per session
kept alive with a no-op Configure, preflight checks, friendly errors,
and full teardown on stop. On barge-in the agent run is cancelled and
SpeechTapChatClient rewrites the agent's history to what the user
actually heard (SpeechInterrupted.text_spoken).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Give the real-time voice chat its own /voice_chat agent instead of
sharing /agentic_chat. Its instructions are written for speech: short
plain sentences with no Markdown, adapting to the user's tone as read
from the transcript, and treating "[interrupted by the user]" as the
point where the user stopped listening.

Cut response latency for voice. With gpt-5-mini, silent reasoning took
2.3 to 4 seconds before the first token. The voice agent now requests
minimal reasoning effort, which brought the first token to about 1.3
seconds. Microsoft.Extensions.AI has no minimal level, so it is set
through ChatOptions.RawRepresentationFactory, and only for reasoning
deployments (Foundry.SupportsReasoningEffort), because models such as
gpt-4o-mini reject reasoning_effort with HTTP 400.

Also:
- Set Flux TTS expressivity to 1 for a slightly more animated voice.
- Rename the scenario to "Real-time voice chat" in the UI and docs.
- Add a Real-time voice chat card to the Home page.
- Update the README and findings for the new endpoint and latency fix.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Remove the UserSecretsId and byte order mark that were committed to
AgenticUI.AgentServer.csproj by mistake. The project file now matches main.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Add real-time voice chat with Deepgram Flux
Add docs/images/deepgram-voice-architecture.svg, in the style of the
existing architecture diagram, showing how the browser, Deepgram Flux
STT and TTS, the Blazor web app, the AG-UI agent server, and Microsoft
Foundry connect. Reference it from a new "Real-time voice" section of
the README's architecture overview.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.

1 participant