Verbara.Sdk.VoiceAi
2.9.0
dotnet add package Verbara.Sdk.VoiceAi --version 2.9.0
NuGet\Install-Package Verbara.Sdk.VoiceAi -Version 2.9.0
<PackageReference Include="Verbara.Sdk.VoiceAi" Version="2.9.0" />
<PackageVersion Include="Verbara.Sdk.VoiceAi" Version="2.9.0" />
<PackageReference Include="Verbara.Sdk.VoiceAi" />
paket add Verbara.Sdk.VoiceAi --version 2.9.0
#r "nuget: Verbara.Sdk.VoiceAi, 2.9.0"
#:package Verbara.Sdk.VoiceAi@2.9.0
#addin nuget:?package=Verbara.Sdk.VoiceAi&version=2.9.0
#tool nuget:?package=Verbara.Sdk.VoiceAi&version=2.9.0
Verbara.Sdk.VoiceAi
Voice AI pipeline for Verbara.Sdk — orchestration layer for STT, TTS, and conversation with turn-taking and barge-in detection.
Installation
dotnet add package Verbara.Sdk.VoiceAi
Quick Start
// Implement your conversation handler (called once per user utterance)
public class MyHandler : IConversationHandler
{
public ValueTask<string> HandleAsync(
string transcript, ConversationContext context, CancellationToken ct)
{
return ValueTask.FromResult($"You said: {transcript}");
}
}
// Register in DI
services.AddAudioSocketServer(opts => opts.Port = 9092);
services.AddVoiceAiPipeline<MyHandler>(opts =>
{
opts.InputFormat = AudioFormat.Slin16Mono8kHz;
opts.OutputFormat = AudioFormat.Slin16Mono8kHz;
opts.EndOfUtteranceSilence = TimeSpan.FromMilliseconds(600);
});
// Subscribe to pipeline events
var pipeline = app.Services.GetRequiredService<VoiceAiPipeline>();
pipeline.Events.Subscribe(evt => Console.WriteLine(evt));
Features
VoiceAiPipeline— full VAD → STT →IConversationHandler→ TTS loop per AudioSocket session- Barge-in detection: cancels TTS playback when the caller speaks
IConversationHandler— scoped per session; implement to plug in any LLM or business logicISessionHandler— low-level interface; implement for fully custom session handlingVoiceAiSessionBroker— hosted service that hands eachAudioSocketSessionto the activeISessionHandler, once- The session ends when its handler does. As soon as
HandleSessionAsyncreturns, throws or is cancelled, the broker calls the session'sHangupAsync: one hangup frame if the line is still live, then the close. On Asterisk 20 and later the call goes on in the dialplan afterAudioSocket(); an ARIexternalMediachannel leaves Stasis with the caller still in your bridge. A handler keeps its line exactly as long as it runs, so await the conversation rather than leaving it to a background task. A session the caller or the handler already ended gets nothing more, and one the broker ends while still live is logged once at Information. - A graceful stop ends the calls, then waits. While the token passed to
StopAsyncis not cancelled, the stop ends every live session it handed out with a hangup frame and waits for the handlers to return; it does not cancel their token. Once that token is cancelled (the host's shutdown budget ran out), the stop cancels the handlers' token and returns without waiting further. A directStopAsync(CancellationToken.None)therefore waits until every handler has returned: pass a token you cancel after a bound of your own. - Disposal does not wait.
Disposecancels the handlers' token and returns. - The handler's
CancellationTokenbelongs to the broker: only a stop that is no longer graceful, orDispose, cancels it. Once its stop has been called, or once it is disposed, the broker hands no new session on; the AudioSocket server keeps such a session and releases it when it stops. The broker is not restartable: a start after a stop subscribes nothing, a start afterDisposethrowsObjectDisposedException, and a stop afterDisposedoes nothing. - Register
AddAudioSocketServerbeforeAddVoiceAiPipeline, as above: the host stops services in reverse order, so the broker then stops first and every call ends with a hangup frame. A server stopped first, or at the same time (HostOptions.ServicesStopConcurrently), now also ends each live session with one hangup frame (a session that cannot take its frame within about a second is closed without one), so the far end reads exactly one hangup frame whatever the order.
- The session ends when its handler does. As soon as
- Observable
Eventsstream (SpeechStartedEvent,TranscriptReceivedEvent,BargInDetectedEvent, etc.) - Native AOT compatible
Custom STT / TTS Providers
When writing your own SpeechRecognizer or SpeechSynthesizer subclass, override ProviderName with a stable literal to avoid the per-utterance GetType().Name allocation on the pipeline hot path (used as a tag on STT/TTS activities):
public sealed class MyCustomRecognizer : SpeechRecognizer
{
public override string ProviderName => "MyCustom";
public override IAsyncEnumerable<SpeechRecognitionResult> StreamAsync(
IAsyncEnumerable<ReadOnlyMemory<byte>> audioFrames,
AudioFormat format,
CancellationToken ct = default)
{
// ...
}
}
If you don't override ProviderName the default falls back to GetType().Name — functional, but incurs one reflection call per utterance.
Observability
- Metrics:
VoiceAiMetrics(sessions started/completed/failed/cancelled, session duration),SpeechRecognitionMetrics(transcriptions started/completed/failed/cancelled, latency),SpeechSynthesisMetrics(syntheses started/completed/failed/cancelled/silent, latency, characters). Every session, recognition and synthesis the pipeline starts ends in exactly one ofcompleted,failedorcancelled;cancelledis taggedvoiceai.endingwith what cut it short (session-cancelled, and for syntheses alsobarge-in,disposal,far-end), andtts.syntheses.completedcounts only a synthesis whose audio was all written. A session iscancelledonly when the token handed toHandleSessionAsyncwas cancelled; one the far end or a graceful broker stop ended iscompleted. The latency sample of a cancelled turn (stt.transcription.latency_ms,tts.synthesis.latency_ms) and the duration sample of a cancelled session (voiceai.session.duration_ms) carry the samevoiceai.endingvalue; samples of completed and failed turns and sessions carry none, and a query that aggregates over tags reads every sample, as before. - Tracing:
VoiceAiActivitySource— session / recognition / synthesis spans. - Health:
VoiceAiHealthCheck,SttHealthCheck,TtsHealthCheckauto-registered byAddVoiceAiPipeline<THandler>(). - Discover names via
VerbaraTelemetry.ActivitySourceNames/MeterNamesfromVerbara.Sdk.Hosting.
Documentation
See the main README for full documentation. Upgrading from 2.6.1: voice-session-ending-migration.md.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Diagnostics.HealthChecks (>= 10.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Options (>= 10.0.0)
- System.Reactive (>= 7.0.0)
- Verbara.Sdk.Audio (>= 2.9.0)
- Verbara.Sdk.VoiceAi.AudioSocket (>= 2.9.0)
NuGet packages (5)
Showing the top 5 NuGet packages that depend on Verbara.Sdk.VoiceAi:
| Package | Downloads |
|---|---|
|
Verbara.Sdk.VoiceAi.Stt
STT providers for Verbara.Sdk.VoiceAi — Deepgram, Cartesia, AssemblyAI (WebSocket streaming), OpenAI Whisper, Azure Whisper, Google Speech (REST batch). Zero third-party dependencies. |
|
|
Verbara.Sdk.VoiceAi.Tts
TTS providers for Verbara.Sdk.VoiceAi — ElevenLabs (WebSocket streaming) and Azure TTS (REST). Zero third-party dependencies. |
|
|
Verbara.Sdk.VoiceAi.Testing
Test fakes for Verbara.Sdk.VoiceAi -- FakeSpeechRecognizer, FakeSpeechSynthesizer, FakeConversationHandler for testing Voice AI apps without API keys. |
|
|
Verbara.Sdk.VoiceAi.OpenAiRealtime
OpenAI Realtime API bridge for Verbara.Sdk.VoiceAi — connects Asterisk AudioSocket directly to an OpenAI Realtime model, with function calling and observability. Zero third-party dependencies. |
|
|
Verbara.Sdk.VoiceAi.TurnDetection
ML-based turn detection for Verbara.Sdk VoiceAi pipeline using the Pipecat smart-turn-v3.2-cpu ONNX model. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.9.0 | 515 | 10/29/2026 |
| 2.8.0 | 2,962 | 10/9/2026 |
| 2.7.0 | 783 | 10/3/2026 |
| 2.6.1 | 510 | 9/30/2026 |
| 2.6.0 | 755 | 9/24/2026 |
| 2.5.3 | 638 | 9/13/2026 |
| 2.5.2 | 309 | 9/13/2026 |
| 2.5.1 | 319 | 9/12/2026 |
| 2.5.0 | 388 | 8/25/2026 |
| 2.4.0 | 1,986 | 7/27/2026 |
| 2.3.2 | 687 | 7/20/2026 |
| 2.3.1 | 313 | 7/14/2026 |
| 2.3.0 | 436 | 7/6/2026 |
| 2.2.1 | 888 | 5/23/2026 |
| 2.2.0 | 196 | 5/20/2026 |
| 2.1.2 | 200 | 5/8/2026 |
| 2.1.1 | 180 | 5/7/2026 |
| 2.1.0 | 3,454 | 5/7/2026 |
| 2.0.0 | 178 | 5/6/2026 |