Vai al contenuto principale

Voxtral di Mistral: guida con progetto demo

Scopri come eseguire il modello Voxtral Mini 3B di Mistral con vLLM, configurare un'API e creare un'app Streamlit per riepilogo e Q&A audio.
· 12 min leggi

Scopri con l'IA

Mistral ha recentemente lanciato i suoi primi modelli audio multimodali open-source, Voxtral Small e Voxtral Mini, basati sull’architettura Mistral 3B e ottimizzati specificamente per attività di comprensione e trascrizione audio.

In questo blog mi concentrerò su Voxtral Mini 3B, un modello compatto e open-source progettato per compiti audio‑to‑text in tempo reale come trascrizione, riassunto e Q&A. Con le sue dimensioni efficienti e il supporto per il ragionamento su contesti lunghi, Voxtral Mini è particolarmente potente se abbinato a framework di inferenza ad alto throughput come vLLM, risultando ideale per creare applicazioni audio veloci e offline.

In questo tutorial ti mostrerò come:

  • Eseguire Voxtral Mini 3B con vLLM su Colab Pro
  • Distribuire un endpoint API tramite tunnel ngrok per accedere al modello da ovunque
  • Creare un assistente audio con Streamlit che esegue trascrizione, riassunto e Q&A usando input audio grezzi

Teniamo aggiornatɜ lɜ nostrɜ lettorɜ sulle ultime novità dell’AI inviando The Median, la nostra newsletter gratuita del venerdì che riassume le notizie chiave della settimana. Iscriviti e resta sul pezzo in pochi minuti a settimana:

Che cos’è Voxtral di Mistral?

Voxtral è la famiglia di modelli audio completamente open source di Mistral progettata per una potente comprensione del parlato. Voxtral è disponibile in due dimensioni:

  • Voxtral Small: 24 B di parametri, ideale per l’uso enterprise in produzione
  • Voxtral Mini: 3 B di parametri, ottimizzato per l’esecuzione in locale su dispositivi edge

mistral's voxtral benchmarks

Fonte: Mistral

Voxtral accetta input audio grezzi (come .wav o .mp3) ed è addestrato per generare in modo efficiente trascrizioni e riassunti da contenuti parlati. Voxtral è una variante più piccola ottimizzata per inferenza rapida e deployment offline.

Segue il formato dei modelli Mistral ed è compatibile con framework di inferenza ad alto throughput come vLLM, il che lo rende un’ottima scelta per trascrizioni in tempo reale o applicazioni vocali leggere.

Come eseguire Voxtral Mini 3B con vLLM

In questa sezione ti spiegherò come servire Voxtral Mini 3B usando vLLM su un Colab Pro con GPU T4 abilitata. vLLM è stato scelto per le sue capacità di serving ad alto throughput e bassa latenza, ideali per modelli come Voxtral che richiedono risposte in streaming rapide con supporto audio.

Inoltre, useremo PyNGrok per esporre il server vLLM tramite un endpoint pubblico, rendendolo accessibile alle applicazioni in esecuzione sulla tua macchina locale.

Passaggio 1: Installa le dipendenze

Iniziamo installando le dipendenze nel nostro ambiente Colab. Esegui il seguente comando nella cella di Colab:

!uv pip install -U transformers accelerate "vllm[audio]" --torch-backend=auto --extra-index-url https://wheels.vllm.ai/nightly

Iniziamo installando le librerie richieste usando uv, un installer di pacchetti Python più veloce. Questo scarica la versione nightly di vllm[audio] che include il supporto sperimentale per modelli audio‑linguistici come Voxtral. Se non hai uv installato nel tuo ambiente, installalo prima eseguendo il seguente codice:

!pip install uv

Passaggio 2: Installa Mistral-Common

Successivamente, installiamo il pacchetto mistral-common, che fornisce utility essenziali per interagire con Voxtral e altri modelli della famiglia Mistral. Questo pacchetto include tokenizer allineati alle implementazioni ufficiali e validazione basata su Pydantic per le strutture dei messaggi.

!pip install mistral-common --upgrade
!python -c "import mistral_common; print(mistral_common.__version__)"

Il pacchetto mistral-common contiene moduli di utilità per Voxtral e altri modelli della famiglia Mistral. Include tokenizer, messaggi tipizzati in formati Pydantic e funzioni di supporto audio. Questo passaggio garantisce la compatibilità con la gestione interna di Voxtral per input audio e messaggi strutturati.

Passaggio 3: Configura VLLM

Ora che abbiamo le dipendenze base di Mistral pronte, configuriamo vLLM clonandone il repository principale.

Passaggio 3.1: Clona il repository vLLM

Cloniamo l’repository GitHub ufficiale di vLLM, che ci dà accesso a esempi audio integrati e funge da base per avviare inferenza offline o hosted.

!git clone https://github.com/vllm-project/vllm && cd vllm

Passaggio 3.2: Esegui un’inferenza audio di esempio 

Facciamo un rapido sanity check per assicurarci che la configurazione sia corretta.

!python vllm/examples/offline_inference/audio_language.py --num-audios 2 --model-type voxtral

Ho eseguito l’esempio offline audio_language.py, che esegue inferenza audio‑linguistica su due campioni predefiniti usando il modello Voxtral e verifica che la decodifica e la generazione audio funzionino correttamente.

Passaggio 4: Configura PyGrok

Ora che vLLM è configurato, impostiamo PyNGrok per esporre il server vLLM su Colab tramite un indirizzo internet pubblicamente accessibile.

  • Inizia creando un account gratuito su https://ngrok.com/
  • Scorri verso il basso nella scheda Connect, copia il tag di autenticazione (nascosto nell’immagine sotto) e salvalo come secret nei Colab Secrets.

NGrok Token

Poi, installiamo PyNGrok nell’ambiente Colab e impostiamo il token di autenticazione usando il token NGROK copiato in precedenza. Se stai lavorando a un prototipo, puoi passare qui direttamente il token.

!pip install pyngrok -q
from pyngrok import ngrok
ngrok.set_auth_token("NGROK_TOKEN")

Passaggio 5: Metti in serving il modello

Questo è il passaggio chiave, in cui avviamo il modello Voxtral Mini 3B tramite vLLM. 

!vllm serve mistralai/Voxtral-Mini-3B-2507 --tokenizer_mode mistral --config_format mistral --load_format mistral --max-model-len 4864

Lo snippet include diverse flag importanti:

  • --tokenizer_mode mistral: usa il tokenizer specifico di Mistral per una tokenizzazione accurata.
  • --config_format mistral e --load_format mistral: queste flag assicurano che sia la configurazione del modello sia i pesi vengano caricati nel formato personalizzato di Mistral, mantenendo la compatibilità.
  • --max-model-len 4864: imposta la massima lunghezza del contesto di input a 4864 token.

Nota: lascia in esecuzione questa cella perché è un server di modello attivo.

Passaggio 6: Ottieni l’endpoint pubblico NGrok

Una volta che il tuo server vLLM è in esecuzione, esegui il seguente codice riga per riga in un terminale Python:

from pyngrok import ngrok
ngrok.set_auth_token(NGROK_TOKEN)
public_url = ngrok.connect(8000)
print("Public endpoint:", public_url)

Il codice qui sopra esporrà il modello su internet a un URL pubblico temporaneo come:

https://80xxxxxxxxxx.ngrok-free.app

Salva questo URL, che verrà usato nel config.py locale per collegarsi al modello remoto. Al termine dell’esecuzione, termina la connessione eseguendo:

ngrok.kill()

Creare una demo di trascrizione e riassunto audio

In questa sezione costruiremo un’interfaccia Streamlit che:

  1. Riceve audio in input (upload dell’utente)
  2. Lo invia al modello tramite il server vLLM
  3. Mostra la trascrizione, il riassunto e permette di fare domande

Passaggio 1: Configurare le dipendenze

Prima di creare l’app, definiamo tutte le dipendenze richieste in un file requirements.txt. Questo assicura ambienti consistenti tra esecuzioni locali o notebook Colab.

streamlit>=1.28.0
openai>=1.0.0
mistral-common>=0.0.12
huggingface-hub>=0.19.0
pyngrok>=6.0.0
requests>=2.28.0
pydub>=0.25.1 

Ecco a cosa servono:

  • streamlit: libreria per costruire l’interfaccia audio interattiva.
  • openai: interfaccia il server vLLM compatibile con Voxtral usando l’SDK di OpenAI.
  • mistral-common: fornisce utility specifiche di Voxtral come AudioChunk e TextChunk.
  • pyngrok: espone il nostro server vLLM locale a internet, permettendo all’app Streamlit di accedervi.
  • Pydub: libreria per convertire file audio per l’inferenza.
  • huggingface-hub e requests: usate per recuperare modelli e configurazioni (se necessario).

Passaggio 2: Impostare la variabile d’ambiente

Per mantenere una configurazione pulita e portabile, salviamo i dettagli dell’API come variabili d’ambiente. Puoi definirle in un file .env:

VOXTRAL_API_KEY=dummy-key
VOXTRAL_API_BASE=NGROK_TOKEN/v1
VOXTRAL_MODEL_NAME=mistralai/Voxtral-Mini-3B-2507

VOXTRAL_API_BASE deve puntare alla tua istanza vLLM in esecuzione, che può essere localhost (http://localhost:8000) o un endpoint pubblico ngrok del tipo https://80xxxxxxxxxx.ngrok-free.app.

Nota: Assicurati di aggiungere /v1 alla fine della base dell’API per la compatibilità con OpenAI.

Passaggio 3: Configurare il file di config

Per rendere la tua app modulare, facilmente regolabile e pronta per la produzione, definiremo una classe di configurazione centrale in config.py. Questo file controllerà impostazioni del modello, accesso all’API, formati audio supportati, lingue e preferenze della UI.

Passaggio 3.1: Caricare le variabili d’ambiente

Prima di definire la classe di configurazione, carichiamo eventuali variabili d’ambiente salvate in un file .env. Questo mantiene separate dal codice le informazioni sensibili.

import os
from typing import Optional
from pathlib import Path
# Load environment variables from .env file
env_file = Path(".env")
if env_file.exists():
    with open(env_file, 'r') as f:
        for line in f:
            if line.strip() and not line.startswith('#'):
                key, value = line.strip().split('=', 1)
                os.environ[key] = value

Questo snippet controlla se esiste un file .env nella directory root. Se trovato, legge le coppie chiave‑valore e le imposta come variabili d’ambiente. Questi valori sono poi accessibili tramite os.getenv(), evitando che i secret vengano hardcodati nell’app.

Passaggio 3.2: Definire la classe Config

Ora raccogliamo tutte le impostazioni in una chiara classe Config per un accesso e un riutilizzo semplici.

class Config:
    # API Configuration
    VOXTRAL_API_KEY: str = os.getenv("VOXTRAL_API_KEY", "EMPTY")
    VOXTRAL_API_BASE: str = os.getenv("VOXTRAL_API_BASE", "http://localhost:8000/v1")
    # Model Configuration
    MODEL_NAME: str = os.getenv("VOXTRAL_MODEL_NAME", "voxtral-mini-3b-2507")
    # Default Parameters
    DEFAULT_TEMPERATURE: float = 0.2
    DEFAULT_TOP_P: float = 0.95
    # Audio Configuration
    MAX_AUDIO_SIZE_MB: int = 100
    SUPPORTED_AUDIO_FORMATS: list = ['mp3', 'wav', 'm4a', 'flac', 'ogg']
    SUPPORTED_LANGUAGES: list = [
        "English", "Spanish", "French", "German", "Italian", "Portuguese", 
        "Russian", "Chinese", "Japanese", "Korean", "Arabic", "Hindi"
    ]
    # UI Configuration
    STREAMLIT_THEME: dict = {
        "primaryColor": "#1f77b4",
        "backgroundColor": "#ffffff",
        "secondaryBackgroundColor": "#f0f2f6",
        "textColor": "#262730",
        "font": "sans serif"
    }
    @classmethod
    def get_api_config(cls) -> dict:
        return {
            "api_key": cls.VOXTRAL_API_KEY,
            "base_url": cls.VOXTRAL_API_BASE,
            "model_name": cls.MODEL_NAME
        }    
    @classmethod
    def validate_config(cls) -> bool:
        if not cls.VOXTRAL_API_BASE:
            return False
        return True    
    @classmethod
    def get_language_code(cls, language_name: str) -> Optional[str]:
        language_mapping = {
            "English": "en",
            "Spanish": "es", 
            "French": "fr",
            "German": "de",
            "Italian": "it",
            "Portuguese": "pt",
            "Russian": "ru",
            "Chinese": "zh",
            "Japanese": "ja",
            "Korean": "ko",
            "Arabic": "ar",
            "Hindi": "hi"
        }
        return language_mapping.get(language_name) 

Insieme, questa classe Config funge da centro di controllo centralizzato per la tua app basata su Voxtral. Che tu stia configurando i parametri del modello, gestendo i formati audio o impostando il tema dell’app, questa struttura garantisce astrazione pulita, riusabilità del codice e manutenibilità.

La classe include:

  • Metodo get_api_config(): centralizza tutte le credenziali legate all’API. Ovunque l’app effettui una chiamata al modello Voxtral, puoi semplicemente usare Config.get_api_config() per recuperare tutto il necessario in un colpo solo.
  • Metodo validate_config(): prima di iniziare l’inferenza, permette di validare che i valori essenziali di configurazione (come la base URL) siano definiti correttamente. In caso contrario, puoi intercettare il problema in anticipo e avvisare l’utente.
  • Metodo get_language_code(): abilita un Q&A multilingue fluido mappando i nomi delle lingue dall’interfaccia (ad es. “French”) ai codici ISO standard (ad es. “fr”). Se viene passata una lingua non supportata, restituisce in modo sicuro None.

Passaggio 4: Costruisci l’app Streamlit

Vediamo passo passo i sotto-passaggi che compongono l’interfaccia e la logica dell’app.

Passaggio 4.1: Imposta il layout e lo stile

In questo passaggio inizializziamo il frontend Streamlit, carichiamo le dipendenze e impostiamo il CSS. Questo costituisce la base visiva e funzionale dell’app.

import streamlit as st
import tempfile
import os
from mistral_common.protocol.instruct.messages import TextChunk, AudioChunk, UserMessage
from mistral_common.audio import Audio
from openai import OpenAI
import time
from config import Config
# Page configuration
st.set_page_config(
    page_title="Voxtral Audio Assistant",
    page_icon="🎵",
    layout="wide",
    initial_sidebar_state="expanded"
)
# Custom CSS 
st.markdown("""
<style>
    .main-header {
        
        font-weight: bold;
        
        text-align: center;
        
    }
    .section-header {
        
        font-weight: bold;
        
        
    }
    .info-box {
        background-
        
        border-radius: 0.5rem;
        border-left: 4px solid #1f77b4;
        
    }
    .success-box {
        background-
        
        border-radius: 0.5rem;
        border-left: 4px solid #28a745;
        
    }
    .chat-message {
        
        border-radius: 0.5rem;
        
    }
    .user-message {
        background-
        border-left: 4px solid #2196f3;
    }
    .assistant-message {
        background-
        border-left: 4px solid #9c27b0;
    }
</style>
""", unsafe_allow_html=True)

Nel codice Python e CSS sopra:

  • Importiamo le librerie essenziali per UI, gestione file e formattazione dei messaggi audio/testo usando il pacchetto mistral_common di Voxtral.
  • Configuriamo poi il layout dell’app Streamlit, un’interfaccia a due pannelli con una sidebar espansa di default.
  • Infine, iniettiamo CSS personalizzato per separare visivamente le varie parti della UI, come intestazioni, sezioni audio, messaggi in chat e riquadri di riepilogo. Questo migliora la gerarchia visiva dell’app con titoli stilizzati, messaggi informativi e balloon distinti per utente e assistente.

Passaggio 4.2: Inizializza sessione e client

Ora garantiamo un comportamento coerente dell’app tra i reload inizializzando variabili di sessione e client.

def init_session_state():
    defaults = {
        'transcription': "",
        'summary': "",
        'chat_history': [],
        'audio_file_path': None
    }
    for key, default_value in defaults.items():
        if key not in st.session_state:
            st.session_state[key] = default_value
def initialize_client():
    config = Config.get_api_config() 
    client = OpenAI(
        api_key=config["api_key"],
        base_url=config["base_url"],
    ) 
    # Test connection
    try:
        models = client.models.list()
        return client
    except Exception as e:
        st.error(f"Failed to connect to Voxtral API: {str(e)}")
        st.info("Make sure your ngrok tunnel is running in Google Colab")
        return None

Questo snippet imposta lo stato interno dell’app e gestisce la connessione al server API di Voxtral. Include due componenti chiave:

  1. Inizializzazione della sessione: assicura che dati utente come trascrizioni, riassunti e file audio caricati esistano correttamente tra interazioni e ricariche dell’app.
  2. Inizializzazione del client: si connette al modello Voxtral Mini 3B in hosting locale tramite l’endpoint compatibile con OpenAI esposto da vLLM (servito tramite Ngrok). In caso di errore, viene mostrato un messaggio nella UI.

Passaggio 4.3: Suddivisione audio e trascrizione in tempo reale

Questo passaggio gestisce la logica principale di preparazione del file audio e generazione di una trascrizione usando Voxtral Mini 3B.

def file_to_chunk(file_path: str) -> AudioChunk:
    audio = Audio.from_file(file_path, strict=False)
    return AudioChunk.from_audio(audio)
def transcribe_audio(client, audio_file_path):
    try:
        with open(audio_file_path, "rb") as f:
            response = client.audio.transcriptions.create(
                file=f,
                model=Config.MODEL_NAME,
                response_format="text",
                stream=True
            )       
            transcription = ""
            progress_bar = st.progress(0)
            status_text = st.empty()
            # Collect all chunks first to get total count
            chunks = list(response)
            total_chunks = len(chunks)
            for i, chunk in enumerate(chunks):
                delta = chunk.choices[0].get("delta", {}).get("content")
                if delta:
                    transcription += delta
                    progress = min((i + 1) / max(total_chunks, 1), 1.0)
                    progress_bar.progress(progress)
                    status_text.text(f"Transcribing... {len(transcription)} characters")
            progress_bar.empty()
            status_text.empty()
            return transcription
    except Exception as e:
        st.error(f"Error during transcription: {str(e)}")
        return None

Qui ci sono due funzioni chiave:

  1. Conversione in AudioChunk: prima convertiamo il file audio caricato nel formato AudioChunk, requisito di input per l’API di Voxtral.
  2. Trascrizione in streaming con avanzamento: inviamo l’audio al modello in streaming (stream=True) e lo decodifichiamo chunk per chunk. Una barra di avanzamento si aggiorna dinamicamente man mano che arrivano i token, offrendo un feedback in tempo reale sull’avanzamento della trascrizione.

Passaggio 4.4: Generare un riassunto dall’audio

Questo passaggio invia l’input audio insieme a un prompt testuale al modello Voxtral, che restituisce un riassunto conciso e strutturato del contenuto audio.

def generate_summary(client, audio_file_path):
    try:
        audio_chunk = file_to_chunk(audio_file_path)
        text_chunk = TextChunk(text="Please provide a comprehensive summary of this audio content, highlighting the key points and main themes discussed.")
        user_msg = UserMessage(content=[audio_chunk, text_chunk]).to_openai()    
        response = client.chat.completions.create(
            model=Config.MODEL_NAME,
            messages=[user_msg],
            temperature=Config.DEFAULT_TEMPERATURE,
            top_p=Config.DEFAULT_TOP_P,
        )
        return response.choices[0].message.content
    except Exception as e:
        st.error(f"Error generating summary: {str(e)}")
        return None

Il codice sopra usa diversi metodi e funzioni per generare un riassunto dall’audio caricato:

  • audio_chunk: per prima cosa convertiamo il file audio caricato in un AudioChunk per l’elaborazione audio di Voxtral.
  • text_chunk: poi, un prompt testuale istruisce il modello a riassumere il contenuto in modo chiaro e completo.
  • UserMessage: combina audio_chunk e text_chunk in un unico messaggio multimodale in formato compatibile con OpenAI.
  • Response.choices[0].message.content: questo oggetto estrae il testo del riassunto dalla risposta del modello.

Passaggio 4.5: Q&A multilingue sull’audio

Ora che abbiamo il riassunto, abilitiamo l’utente a porre domande in linguaggio naturale sul contenuto audio caricato. Questo sfrutta le capacità multimodali di Voxtral combinando audio e prompt testuali per generare risposte contestuali.

La seguente funzione supporta anche risposte multilingue modificando dinamicamente il prompt in base alla lingua selezionata.

def ask_question(client, audio_file_path, question, language="English"):
    try:
        audio_chunk = file_to_chunk(audio_file_path)
        if language != "English":
            question = f"Please answer the following question in {language}: {question}"
        text_chunk = TextChunk(text=question)
        user_msg = UserMessage(content=[audio_chunk, text_chunk]).to_openai()        
        response = client.chat.completions.create(
            model=Config.MODEL_NAME,
            messages=[user_msg],
            temperature=Config.DEFAULT_TEMPERATURE,
            top_p=Config.DEFAULT_TOP_P,
        )        
        return response.choices[0].message.content
    except Exception as e:
        st.error(f"Error asking question: {str(e)}")
        return None

Ecco cosa succede nello snippet qui sopra:

  • La funzione ask_question() consente agli utenti di porre qualsiasi domanda sull’audio caricato.
  • Inizia convertendo il file caricato in un AudioChunk usando la funzione riutilizzabile file_to_chunk(), che prepara l’audio nel formato atteso dall’API multimodale di Voxtral.
  • Poi controlla la lingua selezionata. Se non è l’inglese, la domanda dell’utente viene prefissata con un’istruzione che chiede al modello di rispondere nella lingua desiderata (ad es. hindi o spagnolo).
  • La domanda dell’utente viene racchiusa in un TextChunk e sia l’audio che il testo vengono inseriti in un UserMessage multimodale.
  • Inviano quindi questo messaggio al modello, insieme a temperature e top‑p dalla configurazione per controllare la qualità della generazione.
  • Il modello restituisce una risposta single‑turn.

Passaggio 4.6: Configurare la sidebar

La sidebar funge da pannello di controllo, permettendo di gestire le impostazioni di sessione, selezionare la lingua di output, testare la connessione all’API di Voxtral e personalizzare il comportamento del modello con slider intuitivi.

def render_sidebar():
    with st.sidebar:
        st.markdown('<h3 class="section-header">Configuration</h3>', unsafe_allow_html=True)        
        # Connection status
        st.markdown('<h4>Connection Status</h4>', unsafe_allow_html=True)
        if st.button("Test Connection"):
            client = initialize_client()
            if client:
                st.success("Connected to Voxtral API")
            else:
                st.error("Connection failed")        
        # Language selection
        selected_language = st.selectbox("Select language for Q&A:", Config.SUPPORTED_LANGUAGES)        
        # Model configuration
        st.markdown('<h4>Model Settings</h4>', unsafe_allow_html=True)
        temperature = st.slider("Temperature", 0.0, 1.0, Config.DEFAULT_TEMPERATURE, 0.1)
        top_p = st.slider("Top P", 0.0, 1.0, Config.DEFAULT_TOP_P, 0.05)
        if st.button("Clear Session"):
            for key in ['transcription', 'summary', 'chat_history', 'audio_file_path']:
                st.session_state[key] = "" if key in ['transcription', 'summary'] else [] if key == 'chat_history' else None
            st.rerun()        
        return selected_language, temperature, top_p

Ecco una panoramica del codice sopra:

  • Sidebar: l’oggetto st.sidebar apre una barra laterale comprimibile dove inseriamo gli strumenti di configurazione dell’app.
  • Test di connessione: un pulsante "Test Connection" richiama initialize_client(), che prova a connettersi all’API di Voxtral e segnala esito positivo o negativo tramite avvisi di Streamlit.
  • Selezione lingua: un menu a tendina consente di scegliere una delle 12 lingue supportate. Questa selezione informa in seguito il modello su quale lingua usare per rispondere.
  • Parametri del modello: degli slider permettono di controllare parametri come:
    • temperature: controlla la creatività. Valori più bassi rendono le risposte più deterministiche, quelli più alti più varie.
    • top_p: controlla il nucleus sampling, che limita la generazione ai token più probabili.
  • Reset della sessione: un pulsante "Clear Session" azzera tutte le variabili di stato e riavvia l’app per ripartire da zero.

Passaggio 4.7: Sezione di upload e processamento audio

Questa sezione permette agli utenti di caricare file audio (come .mp3, .wav, ecc.), che vengono salvati temporaneamente. Una volta caricato, l’utente può scegliere di trascrivere l’audio o generare un riassunto di alto livello. 

def render_audio_processing():
    st.markdown('<h3 class="section-header">Audio Upload & Processing</h3>', unsafe_allow_html=True)    
    uploaded_file = st.file_uploader(
        "Choose an audio file",
        type=Config.SUPPORTED_AUDIO_FORMATS,
        help="Upload an audio file to transcribe and analyze"
    )
    if uploaded_file is not None:
        with tempfile.NamedTemporaryFile(delete=False, suffix=f".{uploaded_file.name.split('.')[-1]}") as tmp_file:
            tmp_file.write(uploaded_file.getvalue())
            st.session_state.audio_file_path = tmp_file.name
        st.success(f"File uploaded: {uploaded_file.name}")
        # Initialize client
        client = initialize_client()
        col1, col2 = st.columns(2) 
        with col1:
            if st.button("Generate Summary", type="primary"):
                with st.spinner("Generating summary..."):
                    summary = generate_summary(client, st.session_state.audio_file_path)
                    if summary:
                        st.session_state.summary = summary
        with col2:
            if st.button("Transcribe Audio", type="secondary"):
                with st.spinner("Transcribing audio..."):
                    transcription = transcribe_audio(client, st.session_state.audio_file_path)
                    if transcription:
                        st.session_state.transcription = transcription
        if st.session_state.summary:
            st.markdown('<h4>Summary</h4>', unsafe_allow_html=True)
            st.markdown(f'<div class="success-box">{st.session_state.summary}</div>', unsafe_allow_html=True)
        if st.session_state.transcription:
            st.markdown('<h4>Audio Transcription</h4>', unsafe_allow_html=True)
            st.text_area("Transcription", st.session_state.transcription, height=200, label_visibility="collapsed")

Serve una struttura modulare per facilitare upload audio, processamento e memorizzazione persistente dei risultati tra le interazioni. Ecco come ho fatto:

  • Uploader di file: prima, un uploader drag‑and‑drop che accetta i formati definiti in Config.SUPPORTED_AUDIO_FORMATS. Una volta caricato, il file viene scritto in un file temporaneo e salvato in st.session_state.audio_file_path.
  • Inizializzazione client: viene inizializzato un client compatibile con Voxtral per consentire chiamate di riassunto e trascrizione.
  • Pulsanti per riassunto e trascrizione: definiamo due pulsanti:
    • Generate summary: quando cliccato, invia l’audio al modello con un prompt di riepilogo. Viene mostrato uno spinner e, se ha successo, l’output è salvato in st.session_state.summary.
    • Transcribe audio: invia il file audio all’endpoint di trascrizione. Lo stato di avanzamento è mostrato con uno spinner e il risultato viene salvato in st.session_state.transcription.
  • Infine, entrambi i risultati sono mostrati in un’area di testo.

Passaggio 4.8: Q&A interattivo multilingue

Questo passaggio consente di porre domande sul file audio caricato in più lingue (ad es. inglese, hindi, spagnolo). La lingua selezionata viene utilizzata per formattare la risposta del modello e ogni coppia domanda‑risposta è memorizzata nella cronologia conversazionale gestita dalla sessione e mostrata con balloon in stile chat.

def render_qa_section(selected_language):
    st.markdown('<h3 class="section-header">Multilingual Q&A</h3>', unsafe_allow_html=True) 
    if st.session_state.audio_file_path:
        st.markdown(f'<div class="info-box">Selected language: <strong>{selected_language}</strong></div>', unsafe_allow_html=True)
        question = st.text_input(
            f"Ask a question about the audio (in {selected_language}):",
            placeholder="e.g., What is the main topic discussed?"
        )    
        if st.button("Ask Question", type="primary") and question:
            client = initialize_client()
            with st.spinner("Processing your question..."):
                answer = ask_question(client, st.session_state.audio_file_path, question, selected_language)
                if answer:
                    # Chat history
                    st.session_state.chat_history.append({
                        "question": question,
                        "answer": answer,
                        "language": selected_language,
                        "timestamp": time.strftime("%H:%M:%S")
                    })
                    st.success("Question answered!")
                else:
                    st.error("Failed to get answer. Please try again.")
                if st.session_state.chat_history:
            st.markdown('<h4>Conversation History</h4>', unsafe_allow_html=True)
            for chat in reversed(st.session_state.chat_history):
                st.markdown(f'<div class="chat-message user-message"><strong>Question:</strong> {chat["question"]}</div>', unsafe_allow_html=True)
                st.markdown(f'<div class="chat-message assistant-message"><strong>Answer:</strong> {chat["answer"]}</div>', unsafe_allow_html=True)
                st.markdown("---")
    else:
        st.markdown('<div class="info-box">Please upload an audio file to start asking questions.</div>', unsafe_allow_html=True)

Ecco una spiegazione dello snippet qui sopra:

  • Ask Question button: un campo di testo consente di digitare una domanda nella lingua selezionata e di cliccare il pulsante Ask Question. Una volta cliccato, il sistema:
    • Inizializza il client compatibile con Voxtral.
    • Invia la domanda insieme all’audio caricato al modello.
    • Mostra uno spinner mentre attende la risposta del modello.
  • Gestione della risposta: se viene ricevuta una risposta, essa viene:
    • Salvata in st.session_state.chat_history con un timestamp.
    • Visualizzata con formattazione specifica per ruolo usando il CSS personalizzato (balloon utente vs assistente).
    • Se il modello fallisce, viene mostrato un messaggio di errore chiaro.
  • Cronologia della conversazione: se esistono coppie Q&A precedenti, vengono mostrate in ordine cronologico inverso con un separatore.

Passaggio 4.9: Avvio dell’app

Questo passaggio finale riunisce tutti i componenti definiti in precedenza, inclusa l’inizializzazione dello stato di sessione, il rendering della sidebar, la gestione dell’upload audio, trascrizioni, riassunti e Q&A usando un layout a due colonne.

Una volta che il server è in esecuzione e tutte le dipendenze sono installate, questo passaggio assicura che l’app venga servita al browser tramite Streamlit ed è pronta all’uso.

def main():
    st.markdown('<h1 class="main-header">🎵 Voxtral Audio Assistant</h1>', unsafe_allow_html=True)
    # Initialize session state
    init_session_state()
    # Render sidebar and get configuration
    selected_language, temperature, top_p = render_sidebar()
    col1, col2 = st.columns([1, 1]) 
    with col1:
        render_audio_processing()
    with col2:
        render_qa_section(selected_language)
    st.markdown("---")
    st.markdown("""
    <div style="text-align: center;">
        <p>Powered by <strong>Voxtral Mini 3B</strong> | Built with Streamlit</p>
        <p>Supports multiple languages for audio analysis and Q&A</p>
    </div>
    """, unsafe_allow_html=True)
if __name__ == "__main__":
    main() 

La funzione main() funge da punto di ingresso dell’app. Controlla il layout, orchestra il rendering e assicura che tutte le variabili di sessione e i componenti UI siano inizializzati correttamente. Ecco un riepilogo delle funzioni definite sopra:

  • init_session_state(): prepara valori di default come transcription, summary e chat_history per mantenere lo stato dell’app tra le interazioni.
  • render_sidebar(): carica i widget di configurazione inclusi selettore della lingua, test di connessione e parametri del modello.
  • render_audio_processing(): consente di caricare e processare file audio per trascrizione e generazione del riassunto.
  • render_qa_section(): abilita infine il question answering multilingue sull’audio caricato.

Quando tutto è pronto, avvia l’app con:

pip install -r requirements.txt

Assicurati che sia accessibile tramite l’URL specificato in VOXTRAL_API_BASE nel tuo config.py. Una volta installate tutte le dipendenze, esegui l’applicazione Streamlit con il seguente comando nel terminale:

streamlit run app.py

Il browser aprirà automaticamente l’interfaccia. L’app è ora pienamente funzionante con upload audio, trascrizione in tempo reale, riassunti e Q&A multilingue, il tutto alimentato da Voxtral Mini 3B.

Puoi trovare tutto il codice trattato qui in questo repository GitHub che ho preparato.

Conclusione

Voxtral Mini 3B è un modello efficiente e open‑weight per la trascrizione e la comprensione del parlato. In questo tutorial, lo abbiamo eseguito con vLLM, impostato un’API e creato un’app di riepilogo audio con Streamlit.

Che tu stia costruendo un riassuntore di podcast, un assistente per meeting o un’app di comandi vocali, Voxtral Mini può essere la base per un ragionamento audio veloce e locale.

Aashi Dutt

Autore

Aashi Dutt

Sono una Google Developers Expert in ML (Gen AI), una Kaggle 3x Expert e una Women Techmakers Ambassador con oltre 3 anni di esperienza nel tech. Ho co-fondato una startup health-tech nel 2020 e sto conseguendo un master in informatica al Georgia Tech, con specializzazione in machine learning.

Argomenti

Correlato