Skip to content

Voxtral realtime streaming transcription

Opens a Voxtral realtime WebSocket (wss://api.mistral.ai/v1/audio/transcriptions/realtime) via Meai.ISpeechToTextClient.GetStreamingTextAsync, streams ~1 s of synthesized PCM 16-bit LE / 16 kHz silence to drive the session, and verifies at least a SessionOpen + SessionClose update pair. A TranscriptionDone update with non-empty text is asserted only when the server returns one — silent input does not reliably trigger a transcription on Voxtral.

Skips when MISTRAL_API_KEY is unset.

This example assumes using Mistral; is in scope and apiKey contains your Mistral API key.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
using var client = new MistralClient(apiKey);

// 1 second of PCM 16-bit LE / 16 kHz silence (~16 000 samples × 2 B/sample).
var pcmSilence = new byte[16000 * 2];
using var audio = new MemoryStream(pcmSilence);

Meai.ISpeechToTextClient speechClient = client;

bool sessionOpened = false;
bool sessionClosed = false;
Meai.SpeechToTextResponseUpdate? finalUpdate = null;

using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(45));
try
{
    await foreach (var update in speechClient.GetStreamingTextAsync(audio, cancellationToken: timeoutCts.Token))
    {
        if (update.Kind == Meai.SpeechToTextResponseUpdateKind.SessionOpen)
        {
            sessionOpened = true;
        }
        else if (update.Kind == Meai.SpeechToTextResponseUpdateKind.SessionClose)
        {
            sessionClosed = true;
        }
        else if (update.Kind == Meai.SpeechToTextResponseUpdateKind.TextUpdated)
        {
            finalUpdate = update;
        }
    }
}
catch (System.Net.WebSockets.WebSocketException ex)
{
    throw new AssertInconclusiveException(
        $"Voxtral realtime endpoint unavailable (WebSocket error: {ex.Message}); skipping live streaming test.",
        ex);
}

// finalUpdate may be null when silent input doesn't trigger transcription —
// that's expected and not a failure of the streaming plumbing itself.
if (finalUpdate is not null)
{
}