Real-Time Transcription Use Cases
On this page
Course updated November 3, 2026 for Voxtral 3, based on Mistral’s Migrate from Voxtral 2 guide: Voxtral 2 models (
voxtral-mini-2602,voxtral-mini-transcribe-realtime-2602,voxtral-mini-tts-2603), deprecated October 31, 2026 and retired April 30, 2027, are replaced in examples byvoxtral-mini-transcribe-3,voxtral-mini-transcribe-realtime-3, andvoxtral-mini-tts-3-0. Key changes: timestamps are segment-only (wordreturns a 400 error), real-time diarization is supported, real-time session settings are locked after the first audio chunk, andcontext_biasinputs are free-form (phrases with spaces). Sources: Migrate from Voxtral 2 · Speech to Text.
Four concrete streaming applications
You now have all the building blocks: the stream, the microphone, and latency tuning. This lesson assembles them into four complete applications—live captioning, a voice assistant, dictation, and conference transcription—that you can use as-is as a starting point. Each illustrates a problem that offline mode cannot solve, and each uses a different latency setting, giving you a concrete feel for the tuning discussed earlier.
Live captioning
Captioning is the most visible real-time use case, and also the most demanding in terms of presentation. Transcribing isn’t enough: you must break the text into short lines, display only two at a time, and scroll the whole thing without ever splitting a word in half. That’s exactly what the following class does. When the current line reaches the maximum length, it finds the last space before that limit and breaks there; if no space is found—a very long word, a dictated URL—it cuts at the maximum length rather than letting the line overflow. The 800-millisecond setting reflects this use case’s priority: readable text is better than instant but broken text, and the audience won’t notice this slight delay.
import asyncio
import os
from mistralai.client import Mistral
from mistralai.client.models import (
AudioFormat,
TranscriptionStreamTextDelta,
TranscriptionStreamDone
)
class LiveCaptioning:
"""Live captioning system with line management."""
def __init__(self, max_chars_per_line=50, max_lines=2):
self.max_chars = max_chars_per_line
self.max_lines = max_lines
self.current_line = ""
self.lines = []
async def run(self, audio_source):
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
audio_format = AudioFormat(encoding="pcm_s16le", sample_rate=16000)
async for event in client.audio.realtime.transcribe_stream(
audio_stream=audio_source,
model="voxtral-mini-transcribe-realtime-3",
audio_format=audio_format,
target_streaming_delay_ms=800,
):
if isinstance(event, TranscriptionStreamTextDelta):
self._add_text(event.text)
elif isinstance(event, TranscriptionStreamDone):
self._afficher_final()
def _add_text(self, texte):
self.current_line += texte
if len(self.current_line) >= self.max_chars:
pos = self.current_line.rfind(" ", 0, self.max_chars)
if pos > 0:
self.lines.append(self.current_line[:pos])
self.current_line = self.current_line[pos + 1:]
else:
self.lines.append(self.current_line[:self.max_chars])
self.current_line = self.current_line[self.max_chars:]
if len(self.lines) > self.max_lines:
self.lines = self.lines[-self.max_lines:]
self._afficher()
def _afficher(self):
for ligne in self.lines:
print(f" {ligne}")
print(f" {self.current_line}", end="", flush=True)
def _afficher_final(self):
print("\n\n[Captioning finished]")
Interactive voice assistant
A voice assistant can’t afford silence. It must understand the user while they speak, or else leave a gap after every question that immediately gives away the machine. Real-time transcription is the first link in a three-step chain: Speech-to-Text, then LLM, then Text-to-Speech. Here, latency drops to 300 milliseconds, the most aggressive value in the entire lesson, and the text displays by overwriting the current line using a carriage return, creating the impression of a sentence completing itself before your eyes.
The second half of the function switches to the LLM. The response is itself requested as a stream, so the first words arrive before the model has finished its sentence: the responsiveness gained at the transcription stage isn’t wasted in the next step. The system prompt enforces conciseness, a decisive detail in spoken interaction where a ten-line response becomes unlistenable.
async def assistant_vocal(audio_source):
"""Transcribes then responds via an LLM."""
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
audio_format = AudioFormat(encoding="pcm_s16le", sample_rate=16000)
# Step 1: Transcribe the question
question_parts = []
async for event in client.audio.realtime.transcribe_stream(
audio_stream=audio_source,
model="voxtral-mini-transcribe-realtime-3",
audio_format=audio_format,
target_streaming_delay_ms=300,
):
if isinstance(event, TranscriptionStreamTextDelta):
question_parts.append(event.text)
texte_en_cours = "".join(question_parts)
print(f"\rYou: {texte_en_cours}", end="", flush=True)
elif isinstance(event, TranscriptionStreamDone):
break
question = "".join(question_parts).strip()
print()
if not question:
print("No question detected.")
return
# Step 2: Send to LLM to get a response
print("Assistant: ", end="", flush=True)
reponse_stream = client.chat.stream(
model="mistral-large-latest",
messages=[
{
"role": "system",
"content": "You are a concise voice assistant. "
"Respond in 2-3 sentences maximum."
},
{"role": "user", "content": question}
]
)
for chunk in reponse_stream:
texte = chunk.data.choices[0].delta.content
if texte:
print(texte, end="", flush=True)
print("\n")
Voice dictation with autosave
Dictation poses a problem the two previous cases ignore: duration. A session can stretch over an hour, and losing the text due to a crash or dropout would be unacceptable. Hence automatic saving, triggered roughly every hundred words, which rewrites the output file on the fly. The nb_mots counter also serves as user feedback: it tells you how much you’ve produced, information anyone dictating a report expects. The 600-millisecond latency places the cursor on the side of precision, since someone dictating reads back their text and prefers an accurate word to an instant one.
class DicteeVocale:
"""Voice dictation with automatic saving."""
def __init__(self, fichier_sortie="dictee.txt"):
self.fichier_sortie = fichier_sortie
self.texte = []
self.nb_mots = 0
async def run(self, audio_source):
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
audio_format = AudioFormat(encoding="pcm_s16le", sample_rate=16000)
print("Dictating... (Ctrl+C to stop)\n")
async for event in client.audio.realtime.transcribe_stream(
audio_stream=audio_source,
model="voxtral-mini-transcribe-realtime-3",
audio_format=audio_format,
target_streaming_delay_ms=600,
):
if isinstance(event, TranscriptionStreamTextDelta):
self.texte.append(event.text)
self.nb_mots += len(event.text.split())
print(event.text, end="", flush=True)
# Autosave every 100 words
if self.nb_mots % 100 < 5:
self._sauvegarder()
elif isinstance(event, TranscriptionStreamDone):
break
self._sauvegarder()
print(f"\n\nDictation finished: {self.nb_mots} words")
print(f"File: {self.fichier_sortie}")
def _sauvegarder(self):
contenu = "".join(self.texte)
with open(self.fichier_sortie, "w", encoding="utf-8") as f:
f.write(contenu)
Conference transcription with metadata
For a long conference, raw text isn’t worth much if it’s not identifiable. Six months later, no one will know which session or speaker a file named transcription.txt came from. The following function therefore wraps the transcription in a documented structure—conference name, speaker, start and end timestamps, word count—and exports it as JSON under a timestamped name, immediately usable by an internal search engine or an archiving pipeline. The 1,200-millisecond latency is perfectly comfortable here: the audience listens to the speaker, the display is just a supplement, and precision takes priority.
import json
from datetime import datetime
async def transcribe_conference(
audio_source,
conference_name: str,
speaker: str = "Inconnu"
):
"""Transcribes a conference session with metadata."""
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
audio_format = AudioFormat(encoding="pcm_s16le", sample_rate=16000)
session = {
"conference": conference_name,
"speaker": speaker,
"debut": datetime.now().isoformat(),
"texte": [],
}
print(f"Transcription: {conference_name}")
print(f"Speaker: {speaker}")
print("-" * 50)
async for event in client.audio.realtime.transcribe_stream(
audio_stream=audio_source,
model="voxtral-mini-transcribe-realtime-3",
audio_format=audio_format,
target_streaming_delay_ms=1200,
):
if isinstance(event, TranscriptionStreamTextDelta):
session["texte"].append(event.text)
print(event.text, end="", flush=True)
elif isinstance(event, TranscriptionStreamDone):
break
session["fin"] = datetime.now().isoformat()
session["texte"] = "".join(session["texte"])
session["nb_mots"] = len(session["texte"].split())
filename = f"session_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
with open(filename, "w", encoding="utf-8") as f:
json.dump(session, f, ensure_ascii=False, indent=2)
print(f"\n\nSession saved: {filename}")
return session
Review the four latencies in order—300, 600, 800, then 1,200 milliseconds—and note that they follow a single logic: the more the user expects a reaction from the machine, the shorter the delay must be; the more they expect a usable document, the longer it can be. Take one of these applications and plug it into the microphone generator from the previous lesson: you’ll have a truly usable tool in just a few minutes.
Key takeaways
- Live captioning uses 800 ms latency and manages display line by line
- The voice assistant chains real-time transcription and an LLM to answer questions
- Voice dictation automatically saves text on the fly
- Conference transcription adds metadata and exports to JSON
- Each use case has its optimal latency: 300 ms (assistant) to 1,200 ms (conference), within the accepted range of 80 to 1,200 ms or 2,400 ms