Skip to content

mtmd, server : add Voxtral Realtime 4B support with /v1/audio/transcriptions endpoint - #20638

Closed
didlawowo wants to merge 6 commits into
ggml-org:masterfrom
didlawowo:feat/voxtral-realtime-clean
Closed

didlawowo wants to merge 6 commits into
ggml-org:masterfrom
didlawowo:feat/voxtral-realtime-clean

Conversation

@didlawowo

@didlawowo didlawowo commented Mar 16, 2026 •

Copy link
Copy Markdown

Summary

Full support for Voxtral Mini 4B Realtime (February 2026), Mistral's streaming speech-to-text model, including a new OpenAI-compatible /v1/audio/transcriptions endpoint.

Pre-quantized GGUFs: https://huggingface.co/chris0173/Voxtral-Mini-4B-Realtime-2602-GGUF

What's included

1. Audio encoder + GGUF conversion (convert_hf_to_gguf.py, mtmd/)

  • VoxtralRealtimeEncoderModel for Mistral-format mmproj conversion
  • Causal transformer encoder (sliding window 750, RoPE, SwiGLU, RMSNorm)
  • Precomputed ada_rms_norm_t_cond at conversion time (zero runtime overhead)
  • PROJECTOR_TYPE_VOXTRAL_REALTIME with Whisper-compatible mel params

2. Dual-stream transcription (mtmd-helper.cpp, mtmd-cli.cpp)

  • mtmd_helper_eval_voxtral_realtime() implements the dual-stream protocol
  • Audio + text embeddings summed at each position (not prepended like other models)
  • Token embedding table loaded from GGUF for the summation
  • Full autoregressive decode with greedy sampling
  • CLI support: llama-mtmd-cli --image audio.wav -p "Transcribe"

3. Server endpoint (tools/server/)

Usage

# 1. Convert from Mistral format
python3 convert_hf_to_gguf.py /path/to/Voxtral-Mini-4B-Realtime-2602 \
    --outfile voxtral-4b-rt-Q8_0.gguf --outtype q8_0 --mistral-format
python3 convert_hf_to_gguf.py /path/to/Voxtral-Mini-4B-Realtime-2602 \
    --outfile mmproj-voxtral-4b-rt-F16.gguf --outtype f16 --mmproj --mistral-format

# 2a. CLI transcription
llama-mtmd-cli \
    -m voxtral-4b-rt-Q8_0.gguf \
    --mmproj mmproj-voxtral-4b-rt-F16.gguf \
    -ngl 99 \
    -c 8192 \
    --image audio.flac \
    -p "Transcribe this audio." \
    -n 512

# 2b. Server mode
llama-server \
    -m voxtral-4b-rt-Q8_0.gguf \
    --mmproj mmproj-voxtral-4b-rt-F16.gguf \
    -ngl 99 \
    -c 8192 \
    --host 0.0.0.0 \
    --port 8080

# 3. Transcription API (OpenAI Whisper-compatible)
# multipart/form-data (standard)
curl -X POST http://localhost:8080/v1/audio/transcriptions \
    -F file=@audio.flac \
    -F language=en

# JSON with base64 (alternative)
curl -X POST http://localhost:8080/v1/audio/transcriptions \
    -H "Content-Type: application/json" \
    -d '{"file":"<base64-encoded-audio>","format":"wav","language":"en"}'

Response format:

{"task":"transcribe","language":"en","duration":0.0,"text":"Let him come in then, said the king..."}

Resource requirements

  • VRAM: ~5.5 GB (3.5 GB text Q8_0 + 2 GB mmproj F16)
  • RAM: 8 GB recommended (model loading + audio processing)
  • GPU: any CUDA-capable GPU (tested on RTX 3090)

Testing

Tested on RTX 3090 with LibriSpeech (7127-75946-0002.flac):

Metric Value
Audio encode 169 ms
Decode speed 145 tok/s
Total latency ~6.6 s (for 15s audio)

Deployed and verified on a Kubernetes cluster with ArgoCD GitOps.

Dual-stream protocol (architecture note)

Unlike standard multimodal models that prepend media embeddings, Voxtral Realtime sums audio and text embeddings at each position:

Standard:   [audio_embeddings] [text_tokens] → decoder
Voxtral RT: audio_embd[i] + tok_embd[prompt[i]] → decoder (at every position)

This enables streaming with <500ms latency. The prefix consists of [BOS] + [PAD×32] + [DELAY×6] = 39 tokens.

Related

What this does NOT include

  • No new standalone binary
  • No microphone capture / real-time streaming
  • No changes to the generation loop for other models

@didlawowo
didlawowo requested review from a team and CISC as code owners March 16, 2026 12:27
@github-actions github-actions Bot added model Model specific examples python python script changes server labels Mar 16, 2026
@didlawowo
didlawowo force-pushed the feat/voxtral-realtime-clean branch 2 times, most recently from e461266 to b31e2c9 Compare March 16, 2026 21:18
@didlawowo

Copy link
Copy Markdown
Author

any reviewer ?

Add model support for Voxtral Mini 4B Realtime (mistralai/Voxtral-Mini-4B-Realtime-2602).

GGUF conversion:
- VoxtralRealtimeEncoderModel for Mistral-format mmproj conversion
- Precompute ada_rms_norm_t_cond adaptive normalization at conversion time

Decoder:
- Optional ffn_ada_norm_up tensor per layer (zero overhead for other models)

Audio encoder (voxtral-realtime-enc.cpp):
- Causal transformer with sliding window attention (750), RoPE, SwiGLU
- Causal conv1d with left-padding, frame stacking, adapter MLP

mtmd integration:
- PROJECTOR_TYPE_VOXTRAL_REALTIME with Whisper-compatible mel params
- Audio preprocessor with Voxtral-specific normalization

Based on work by @Acceldium (PR ggml-org#19698), refactored for Phase 1 scope.
Implement the Voxtral Realtime dual-stream inference protocol where
audio and text embeddings are summed at each position (not prepended).

mtmd-helper:
- mtmd_helper_eval_voxtral_realtime() handles full pipeline
- Token embedding table loader from GGUF
- Prefill: combined[i] = audio_embd[i] + tok_embd[prompt[i]]
- Autoregressive: continue dual-stream while within audio range

mtmd-cli:
- Detect mtmd_support_voxtral_realtime() in single-turn mode
- Route to dual-stream path, print transcription

Tested with LibriSpeech: perfect transcription, 169ms encode, 145 tok/s.
Add OpenAI-compatible audio transcription endpoint for speech-to-text.

New task type SERVER_TASK_TYPE_AUDIO_TRANSCRIPTION follows the EMBEDDING
pattern: process at DONE_PROMPT, run dual-stream transcription, release slot.

Endpoint accepts JSON with base64-encoded audio:
  POST /v1/audio/transcriptions
  {"file": "<base64>", "format": "wav", "language": "en"}

Response format:
  {"task": "transcribe", "language": "en", "text": "..."}

Resolves feature request ggml-org#15291 (audio transcription endpoint).
Support the standard OpenAI Whisper API format:
  curl -F file=@audio.wav -F language=en /v1/audio/transcriptions

Also keeps JSON+base64 as fallback. Multipart fields (file, language,
model, etc.) are extracted from httplib's form parser and passed
through server_http_req.
- Use llama_vocab_is_eog() instead of single eos_token check in helper
- Skip control tokens and streaming markers during detokenization
- Fixes trailing "ished." artifact in transcription output
- Error handling tests using tinygemma3 (vision-only, no audio)
- Verify endpoint exists (not 404)
- Verify multipart and JSON formats are accepted
- Verify graceful error when model doesn't support audio
- Integration test with real model (skipped by default, set LLAMA_TEST_AUDIO_MODEL=1)
@ngxson

ngxson commented Mar 23, 2026

Copy link
Copy Markdown
Collaborator

This PR contains many model-specific code paths, which are considered as anti-pattern in libmtmd design. Please refer to #20914 for a formal discussion before proceeding. For now, I'm closing the current PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

examples model Model specific python python script changes server

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants