Custom vocabulary
The problem. Speech models write what is most likely in general. Your product, your customers’ names and your jargon are rare words: “Gladia” comes out as “Gladya”, “Kubernetes” as “Cuban et ease”, “Dr Okafor” as “doctor oak a four”. Custom vocabulary tells the model which words to expect, so they come out right the first time.
Where it works. Every model: solaria-3, solaria-1, trantoria-1.
Step by step
Section titled “Step by step”1. List your words
Section titled “1. List your words”The option goes in realtime_processing of the session’s configuration. Both fields are required:
{ "realtime_processing": { "custom_vocabulary": true, "custom_vocabulary_config": { "vocabulary": ["Gladia", "Kubernetes", "Dr Okafor"] } }}2. Start a session with it
Section titled “2. Start a session with it”Each program streams a 16-bit WAV file and prints the final transcripts. Put your API key in the GLADIA_API_KEY environment variable, then run python custom_vocabulary.py meeting.wav.
# pip install gladiaio-sdkimport osimport sysimport threadingimport wave
from gladiaio_sdk import GladiaClient, LiveV2InitRequest
with wave.open(sys.argv[1]) as wav: # a 16-bit PCM WAV file rate, channels = wav.getframerate(), wav.getnchannels() audio = wav.readframes(wav.getnframes())
config = { "encoding": "wav/pcm", "sample_rate": rate, "bit_depth": 16, "channels": channels, "model": "solaria-3", "language_config": {"languages": ["en"]}, "realtime_processing": { "custom_vocabulary": True, "custom_vocabulary_config": { "vocabulary": [ "Gladia", {"value": "Kubernetes", "pronunciations": ["cuban et ease", "kubernetis"]}, {"value": "Dr Okafor", "intensity": 0.8}, ], "default_intensity": 0.5, }, },}
client = GladiaClient( api_key=os.environ["GLADIA_API_KEY"], api_url=os.environ.get("GLADIA_API_URL", "https://eu1.api.gladia.io"),)session = client.live_v2().start_session(LiveV2InitRequest.from_dict(config))ended, errors = threading.Event(), []
@session.on("message")def on_message(message): if message.type == "transcript" and message.data.is_final: print(message.data.utterance.text)
session.on("error", errors.append)session.once("ended", lambda _: ended.set())
step = rate * channels * 2 // 10 # 100 ms of audio per chunkfor start in range(0, len(audio), step): session.send_audio(audio[start : start + step]) # live audio: send each chunk as it is capturedsession.stop_recording()
ended.wait(timeout=60)sys.exit(1 if errors or not ended.is_set() else 0)// npm install @gladiaio/sdkimport { readFileSync } from "node:fs";import { GladiaClient } from "@gladiaio/sdk";
const { sampleRate, channels, pcm } = readWav(process.argv[2]); // a 16-bit PCM WAV file
const config = { encoding: "wav/pcm", sample_rate: sampleRate, bit_depth: 16, channels, model: "solaria-3", language_config: { languages: ["en"] }, realtime_processing: { custom_vocabulary: true, custom_vocabulary_config: { vocabulary: [ "Gladia", { value: "Kubernetes", pronunciations: ["cuban et ease", "kubernetis"] }, { value: "Dr Okafor", intensity: 0.8 }, ], default_intensity: 0.5, }, },};
const client = new GladiaClient({ apiKey: process.env.GLADIA_API_KEY, apiUrl: process.env.GLADIA_API_URL ?? "https://eu1.api.gladia.io",});const session = client.liveV2().startSession(config);
session.on("message", (message) => { if (message.type === "transcript" && message.data.is_final) console.log(message.data.utterance.text);});session.on("error", (error) => { console.error(error); process.exitCode = 1;});session.once("ended", () => process.exit());
const step = (sampleRate * channels * 2) / 10; // 100 ms of audio per chunkfor (let start = 0; start < pcm.length; start += step) { session.sendAudio(pcm.subarray(start, start + step)); // live audio: send each chunk as it is captured}session.stopRecording();
function readWav(path) { const file = readFileSync(path); let fmt, pcm; for (let pos = 12; pos < file.length; ) { const id = file.toString("ascii", pos, pos + 4); const size = file.readUInt32LE(pos + 4); if (id === "fmt ") fmt = { channels: file.readUInt16LE(pos + 10), sampleRate: file.readUInt32LE(pos + 12) }; if (id === "data") pcm = file.subarray(pos + 8, pos + 8 + size); pos += 8 + size + (size % 2); } return { ...fmt, pcm };}Create the session, then stream its audio to the returned url (Live WebSocket):
curl -X POST https://eu1.api.gladia.io/v2/live \ -H "x-gladia-key: $GLADIA_API_KEY" \ -H "content-type: application/json" \ -d '{ "encoding": "wav/pcm", "sample_rate": 16000, "bit_depth": 16, "channels": 1, "model": "solaria-3", "language_config": { "languages": ["en"] }, "realtime_processing": { "custom_vocabulary": true, "custom_vocabulary_config": { "vocabulary": [ "Gladia", { "value": "Kubernetes", "pronunciations": ["cuban et ease", "kubernetis"] }, { "value": "Dr Okafor", "intensity": 0.8 } ], "default_intensity": 0.5 } } }'3. Check the result
Section titled “3. Check the result”Nothing new arrives: the words simply come out spelled as you listed them, in partial and final transcripts.
{ "type": "transcript", "data": { "is_final": true, "utterance": { "text": "Dr Okafor deployed Gladia on Kubernetes." } } }Options
Section titled “Options”| field | what it does |
|---|---|
vocabulary |
The words or phrases: strings, or objects with their own options. |
value |
The word or phrase, written exactly as it must appear. |
pronunciations |
How it may be heard, written as it sounds. Whatever the model writes for one of them is rewritten to value. |
intensity |
How strongly the entry is favoured, from 0 to 1 (trantoria-1 only, see below). |
default_intensity |
The intensity of entries that set none. |
Limits and gotchas
Section titled “Limits and gotchas”- Fewer, precise entries work better. List what is specific to your audio. Hundreds of common words, or a whole dictionary, make the model see your words where they were not said.
- Intensity applies to
trantoria-1only.solaria-3andsolaria-1read the list as context: every entry counts the same andintensityis ignored. Ontrantoria-1, a high intensity replaces similar-sounding words with yours: raise it word by word, and only for the words still missed. - Size limit. At most 1,000 entries. The whole session configuration (vocabulary, custom spelling and
custom_metadatatogether) must also fit in its WebSocket URL: a few hundred average entries. Beyond,POST /v2/liveanswers400with “the session configuration is too large for its WebSocket URL”. - Numbers stay as listed. With numbers as digits, a vocabulary phrase made of number words (“Seven Eleven”) is kept as you wrote it.
- Not a spelling rule. Vocabulary helps the model hear a word. To change how a word is written (casing, an abbreviation), use custom spelling.
- One list per session. The vocabulary cannot be changed while the session runs: create a new session.
languageof an entry is not applied: it is accepted for compatibility with Gladia’s cloud API.
Troubleshooting
Section titled “Troubleshooting”| symptom | cause | fix |
|---|---|---|
400 “custom_vocabulary_config is required when custom_vocabulary is true” |
the flag without the list | send both fields |
400 “the session configuration is too large…” |
the list, the spelling and the metadata do not fit in the URL | shorten the list, keep the words that are actually missed |
| your word now appears where it was not said | the list is too long or the intensity too high | remove generic entries, lower intensity |
a word is still missed on solaria-3 |
the model hears it differently | add pronunciations as it is heard, or fix it with custom spelling |