Aller au contenu principal

Cas d'Usage de la Transcription Temps Réel

Applications concrètes du streaming

La transcription temps réel ouvre des possibilités que le mode offline ne peut pas couvrir. Dans cette leçon, vous découvrirez quatre cas d’usage majeurs avec du code fonctionnel pour chacun.

Sous-titrage en direct

Le sous-titrage en direct est le cas le plus visible du temps réel. Que ce soit pour un webinaire, une conférence ou un cours en ligne, les sous-titres apparaissent au fur et à mesure que l’orateur parle.

import asyncio
import os
from mistralai import Mistral
from mistralai.models import (
    AudioFormat,
    TranscriptionStreamTextDelta,
    TranscriptionStreamDone
)


class SousTitrageDirect:
    """Système de sous-titrage en direct avec gestion des lignes."""

    def __init__(self, max_chars_par_ligne=50, max_lignes=2):
        self.max_chars = max_chars_par_ligne
        self.max_lignes = max_lignes
        self.ligne_courante = ""
        self.lignes = []

    async def lancer(self, source_audio):
        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=source_audio,
            model="voxtral-mini-transcribe-realtime-2602",
            audio_format=audio_format,
            target_streaming_delay_ms=800,
        ):
            if isinstance(event, TranscriptionStreamTextDelta):
                self._ajouter_texte(event.text)
            elif isinstance(event, TranscriptionStreamDone):
                self._afficher_final()

    def _ajouter_texte(self, texte):
        self.ligne_courante += texte

        if len(self.ligne_courante) >= self.max_chars:
            pos = self.ligne_courante.rfind(" ", 0, self.max_chars)
            if pos > 0:
                self.lignes.append(self.ligne_courante[:pos])
                self.ligne_courante = self.ligne_courante[pos + 1:]
            else:
                self.lignes.append(self.ligne_courante[:self.max_chars])
                self.ligne_courante = self.ligne_courante[self.max_chars:]

            if len(self.lignes) > self.max_lignes:
                self.lignes = self.lignes[-self.max_lignes:]

        self._afficher()

    def _afficher(self):
        for ligne in self.lignes:
            print(f"  {ligne}")
        print(f"  {self.ligne_courante}", end="", flush=True)

    def _afficher_final(self):
        print("\n\n[Sous-titrage terminé]")

Assistant vocal interactif

Un assistant vocal doit comprendre l’utilisateur pendant qu’il parle pour pouvoir répondre rapidement. La transcription temps réel est le premier maillon de la chaîne : Speech-to-Text, puis LLM, puis Text-to-Speech.

async def assistant_vocal(source_audio):
    """Transcrit puis répond via un LLM."""
    client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
    audio_format = AudioFormat(encoding="pcm_s16le", sample_rate=16000)

    # Étape 1 : Transcrire la question
    question_parts = []

    async for event in client.audio.realtime.transcribe_stream(
        audio_stream=source_audio,
        model="voxtral-mini-transcribe-realtime-2602",
        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"\rVous : {texte_en_cours}", end="", flush=True)
        elif isinstance(event, TranscriptionStreamDone):
            break

    question = "".join(question_parts).strip()
    print()

    if not question:
        print("Aucune question détectée.")
        return

    # Étape 2 : Envoyer au LLM pour obtenir une réponse
    print("Assistant : ", end="", flush=True)

    reponse_stream = client.chat.stream(
        model="mistral-large-latest",
        messages=[
            {
                "role": "system",
                "content": "Vous êtes un assistant vocal concis. "
                           "Répondez en 2-3 phrases 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")

Dictée vocale avec sauvegarde

La dictée vocale va au-delà de la simple transcription : elle met en forme le texte et le sauvegarde au fil de l’eau.

class DicteeVocale:
    """Dictée vocale avec sauvegarde automatique."""

    def __init__(self, fichier_sortie="dictee.txt"):
        self.fichier_sortie = fichier_sortie
        self.texte = []
        self.nb_mots = 0

    async def lancer(self, source_audio):
        client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
        audio_format = AudioFormat(encoding="pcm_s16le", sample_rate=16000)

        print("Dictée en cours... (Ctrl+C pour arrêter)\n")

        async for event in client.audio.realtime.transcribe_stream(
            audio_stream=source_audio,
            model="voxtral-mini-transcribe-realtime-2602",
            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)

                # Sauvegarde automatique tous les 100 mots
                if self.nb_mots % 100 < 5:
                    self._sauvegarder()

            elif isinstance(event, TranscriptionStreamDone):
                break

        self._sauvegarder()
        print(f"\n\nDictée terminée : {self.nb_mots} mots")
        print(f"Fichier : {self.fichier_sortie}")

    def _sauvegarder(self):
        contenu = "".join(self.texte)
        with open(self.fichier_sortie, "w", encoding="utf-8") as f:
            f.write(contenu)

Transcription de conférence avec métadonnées

Pour une conférence longue, vous ajoutez des métadonnées (orateur, horodatage) et exportez en JSON :

import json
from datetime import datetime


async def transcrire_conference(
    source_audio,
    nom_conference: str,
    orateur: str = "Inconnu"
):
    """Transcrit une session de conférence avec métadonnées."""
    client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
    audio_format = AudioFormat(encoding="pcm_s16le", sample_rate=16000)

    session = {
        "conference": nom_conference,
        "orateur": orateur,
        "debut": datetime.now().isoformat(),
        "texte": [],
    }

    print(f"Transcription : {nom_conference}")
    print(f"Orateur : {orateur}")
    print("-" * 50)

    async for event in client.audio.realtime.transcribe_stream(
        audio_stream=source_audio,
        model="voxtral-mini-transcribe-realtime-2602",
        audio_format=audio_format,
        target_streaming_delay_ms=1500,
    ):
        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())

    nom_fichier = f"session_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
    with open(nom_fichier, "w", encoding="utf-8") as f:
        json.dump(session, f, ensure_ascii=False, indent=2)

    print(f"\n\nSession sauvegardée : {nom_fichier}")
    return session

Points clés à retenir

  • Le sous-titrage en direct utilise une latence de 800 ms et gère l’affichage ligne par ligne
  • L’assistant vocal chaîne transcription temps réel et LLM pour répondre aux questions
  • La dictée vocale sauvegarde automatiquement le texte au fil de l’eau
  • La transcription de conférence ajoute des métadonnées et exporte en JSON
  • Chaque cas d’usage a sa latence optimale : 300 ms (assistant) à 1500 ms (conférence)