Skip to main content
When the user dictates into a field that already has text, the words have to fit where the cursor is. Dictating “tomorrow works” into I think| we should go should give I think tomorrow works we should go. By default, the rewrite returns a complete sentence, with a capital letter at the start and a full stop at the end. Insert that at the cursor as it is, and you get I thinkTomorrow works. we should go. To get it right, split the job in two:
  • The rewrite fixes the words. Tell it what comes before the cursor, and it writes the dictation as a continuation: lowercase for ordinary words, capitals kept for names. Only a model can tell those apart.
  • Your code fixes the joins. Spacing and end punctuation follow fixed rules from the text on either side of the cursor, so code gets them right every time. The rewrite output is trimmed, so it can’t return a leading space anyway.
The four steps below explain each part. For working code, go to the complete example.

1. Read the text around the cursor

Take the text before the cursor and the text after it, each up to a few hundred characters. How you read them depends on the platform: Some fields don’t expose their text, such as password fields and some canvas-based editors. Treat those as empty, and add no spacing.

2. Set rewrite_mode to smart

Send every insertion with "rewrite_mode": "smart". The instruction in the next step only works on smart. On the default fast mode, the rewrite usually ignores it and returns the whole sentence, so the text before the cursor appears twice: I think I think tomorrow works. smart adds some latency to the rewrite. See Choosing a rewrite mode for the trade-off.

3. Tell the rewrite where the words land

Decide in code whether the cursor sits mid-sentence:
  • At the start of a sentence, when the field is empty or the text before the cursor ends with ., !, ? or a line break: send no instruction. The default cleanup writes a normal sentence.
  • Mid-sentence: send an llm_instruction that says the dictated words continue the text before them.
In both cases, send the text before the cursor as stt_prompt too. It’s context for the transcription itself, so spellings, names and casing carry over from what the user already wrote. Keep it as plain text, with no instructions in it. See Prompting and keyterms. For I think| we should go, the config is:
The instruction describes the shape you want, a mid-sentence continuation, instead of listing rules such as “lowercase unless it’s a name” or “no final punctuation”. The model leans toward complete sentences, and a rule against that has to fight it on every request, so it’s followed less reliably.

4. Join the result in code

When the response comes back:
  • Spacing. Add a space before the words when the text before the cursor doesn’t end in whitespace, and after them when the text after the cursor doesn’t start with whitespace or punctuation.
  • End punctuation. Remove a trailing ., ! or ? when the text after the cursor carries on the sentence, such as a lowercase word or a comma.
  • A failed rewrite. If the rewrite fails, llm_response is null. Apply the same joins to text, the verbatim transcript. In the SDKs, final_text already falls back to it.

Complete example

Each tab is a complete program. Add your API key, save a recording as clip.wav, and run it. The four helpers are the same in every tab, so you can copy them into your app as they are:

What to expect

These are results from the complete example, run against Dictation: The rewrite lowercases ordinary words mid-sentence but keeps names such as “London” and “Friday” capitalized, drops filler words and repeats, and ends with a full stop only when the sentence ends there.