Voice dictation widgets¶
Two widgets add speech-to-text dictation to a form field:
VoiceDictation(djgentelella.widgets.core) — a textarea with a microphone button.VoiceEditorTinymce(djgentelella.widgets.tinymce) — a TinyMCE rich-text editor with a microphone button in its own toolbar.
Both capture audio in the browser (Web Audio + voice-activity detection). The
dictation data-mode chooses how it is transcribed:
segments(default) — each phrase (cut at a natural pause) is transcribed and appended live as you speak.single— nothing is sent until Stop, then the whole recording is transcribed in a single request.hybrid— segments appear live, then on Stop the whole recording is re-transcribed and replaces them, trading the live per-segment result for the broader ASR context of a single whole-file pass.
Usage
from django import forms
from django.urls import reverse_lazy
from djgentelella.forms.forms import GTForm
from djgentelella.widgets import core as genwidgets
from djgentelella.widgets import tinymce as tinymce_widget
class DictationForm(GTForm, forms.Form):
notes = forms.CharField(
required=False,
widget=genwidgets.VoiceDictation(
url=reverse_lazy('voice_transcribe'), language='es'),
)
body = forms.CharField(
required=False,
widget=tinymce_widget.VoiceEditorTinymce(
url=reverse_lazy('voice_transcribe'), language='es'),
)
url is the transcription endpoint and language the spoken language.
Note
getUserMedia requires a secure context — the page must be served
over HTTPS or from localhost.
Transcription endpoint
djgentelella ships a ready-to-use endpoint, voice_transcribe
(VoiceTranscribeView), that accepts a POST with an audio file (plus
optional language/hotwords/initial_prompt) and returns
{"text": "..."}. It has two interchangeable backends selected by settings:
localRuns NVIDIA Parakeet-v3 in-process (CPU) via the optional extra
pip install "djgentelella[asr]". The model (~670 MB) downloads from Hugging Face on first use.remoteForwards the audio to an external ASR HTTP API (
GENTELELLA_ASR_REMOTE_URL) viapip install "djgentelella[asr-remote]", which pulls an HTTP client and nothing else, presentingAuthorization: Bearer <GENTELELLA_ASR_REMOTE_TOKEN>when configured. Use this when the host can’t run the model in-process.The request defaults to the OpenAI
POST /v1/audio/transcriptionsshape (file+model/language/prompt, response{"text": ...}), so a stock config talks to OpenAI, Groq, vLLM or faster-whisper-server unchanged. The threeGENTELELLA_ASR_REMOTE_*_PARAM/_MODELsettings below remap the field names for other servers — e.g. Deepgram (HOTWORDS_PARAM=keyterm).modelis only sent whenGENTELELLA_ASR_REMOTE_MODELis set; otherwise the server uses its own default model.
If GENTELELLA_ASR_BACKEND is unset it defaults to remote when a remote
URL is configured, otherwise local. Any other value raises
ImproperlyConfigured rather than silently falling back.
Warning
Transcription is expensive in both backends — cpu in-process, billable API
calls remotely — and every request holds a worker for up to
GENTELELLA_ASR_TIMEOUT seconds. Uploads above
GENTELELLA_ASR_MAX_UPLOAD_BYTES are rejected with a 413 for that reason;
the endpoint applies no rate limiting of its own, so put the usual per-user
throttling in front of it if it is exposed to untrusted users.
Settings
Setting |
Purpose |
Default |
|---|---|---|
|
|
auto (remote if a URL is set) |
|
External ASR endpoint (remote backend) |
(none) |
|
Bearer token for the external ASR |
|
|
Remote request timeout (seconds) |
|
|
Largest audio accepted; above it the endpoint answers 413. Set to
|
|
|
Model id sent to the remote ASR (omitted when unset → server default) |
(none) |
|
Remote field name for the biasing prompt (from |
|
|
Remote field name for keyword biasing (from |
(none) |
|
Local model id |
|
|
Local model quantization |
|
|
Default language (local backend) |
|
|
Punctuation & capitalization (local) |
|
Per-widget data-* options
Beyond url and language (kwargs), the widgets accept tuning through
attrs:
data-hotwords/data-initial-prompt— biasing context forwarded to the endpoint (used by Whisper-style backends; Parakeet-v3 ignores them).data-vad-silence-ms(default 600),data-vad-min-speech-ms(500),data-vad-max-segment-ms(10000),data-rms-threshold(0.008),data-pool-size(3) — voice-activity-detection / concurrency tuning. The defaults cut at short ~0.6 s pauses so segments appear live as you speak; raisedata-vad-silence-ms/data-vad-min-speech-msfor longer segments (more ASR context per segment).data-insert-mode="paragraph"— (TinyMCE) wrap each dictated fragment in its own<p>instead of inline.data-mode— dictation mode:segments(default),singleorhybrid(see the top of this section).
genwidgets.VoiceDictation(
url=reverse_lazy('voice_transcribe'), language='es',
attrs={'data-hotwords': 'Django, gentelella', 'data-vad-silence-ms': 1000},
)
Note
Parakeet-v3 cannot be biased toward specific vocabulary, so proper
nouns and acronyms may be mis-transcribed. A Whisper backend (which accepts
hotwords/initial_prompt) handles those better.