Skip to main content

Module dictate

Module dictate 

Source
Expand description

Dictation - the [dictate] section in forge.toml, and the preflight pass that makes the models usable before forge starts.

The costs are what shape this. A first run fetches 3.07 GB; every later one re-hashes what is on disk, about 0.8 s for the pair now the two models are prepared concurrently, then loads the weights, another second warm. The hash is skipped for a model whose size and modification time still match the digest recorded for it, which is every boot but the one after a file changed. None of that can happen while somebody is dictating, so all of it happens once, at boot, on the preflight screen.

Re-exports§

use std::collections::HashMap; 🔒
use std::path::PathBuf; 🔒
use std::sync::Arc; 🔒
use std::sync::atomic::AtomicBool; 🔒
use std::sync::atomic::Ordering; 🔒
use std::time::Duration; 🔒
use std::time::Instant; 🔒
use parking_lot::Mutex; 🔒
use serde::Deserialize; 🔒
use serde::Deserialize; 🔒
use tokio::time::MissedTickBehavior; 🔒
use crate::SessionSlot; 🔒
use crate::protocol::DictateOutcome; 🔒
use crate::protocol::SessionUpdate; 🔒

Structs§

DictateAxes
The three axes decided, as a client that captures sends them: the values it read from the greeting with its own edits applied, so every axis is a value rather than a maybe. The crate defaults stand in where nothing was configured, which is what the client’s own panel draws as the unset state.
DictateModel
One model’s row in preflight.
DictateOverrides
What a session has overridden on the normalizer’s prompt axes. None on an axis means “the crate default”; the /dictate dialog derives each row’s in-force marker from this plus the defaults.
DictateRuntime 🔒
Dictation state across every session. Every take is fed by a client’s own frames and holds no device, so several seats can dictate at once; several takes can also be finishing, because a new recording never waits for the previous transcript.
DictateSettings
The [dictate] section. Every field has a default, so an absent section is exactly enabled = false.
DictateSnapshot
Everything preflight knows about dictation right now.
DictateState 🔒
Preflight’s own handle on the work: the snapshot a view renders, the cancellation flag, and the active models the resolution landed.
FinishingTake 🔒
A submitted take still awaiting its transcript. The recording is already stopped; the entry stays only so a stop can still abandon it.
LiveRecording 🔒
The live side of one dictation: the channel its task polls for the stop decision (true on the channel means submit, false means abandon), and - for a take the client captures - the sink the socket pushes frames into. The seat is the map key, so the entry does not carry it again.
RegisteredTake 🔒
A take the client will feed with frames, registered and ready to run: what the socket pushes into, what the runner finishes, and what the take normalizes by.
SetRecording 🔒
The read-aloud set being recorded, when one is: the audio a client’s own frames have fed so far, held until the stop that keeps or drops it. It belongs to no session - the words are known, so nothing is transcribed and nothing is routed - but it is a live capture like any other, and the guards that wait on captures count it.
TransferProgress 🔒
Per-file transfer bookkeeping, kept across the progress events of one prepare call.

Enums§

DictateBind
The push-to-talk key. Right Cmd is the default; on Linux and Windows there is no Cmd key, so the cmd equivalent is the right Control key.
DictateFailure
Why preflight stopped.
DictateMode
How a press/release pair maps onto starting and stopping a take. Auto infers from timing; the forced modes bypass the tap window entirely.
DictateModelState
How far one model has got.
DictateOverrideUpdate
One edit the /dictate dialog asks for: set a single axis, or clear them all. Enter on an already-set row re-sets the same value; there is no per-axis clear, so the reset row is the only way back.
DictateRole
What a model is for. Preflight labels the row by role and prints the file underneath, because the role is what a reader is scanning for and the file is the detail they want only when something is wrong.
TakeResolution 🔒
How waiting on a submitted take ended.

Constants§

DEFAULT_MAX_CAPTURE_MINUTES 🔒
Cap on one recording when [dictate] max_capture_minutes is absent. Matches the crate’s own default; a capture reserves 4 bytes a sample eagerly, so this is about 110 MiB held for the run.
METER_INTERVAL 🔒
How often the live recording is polled for its level. A deliberate fourth clock: coarser than the repaint gate, so every level event lands on a repaint without driving it.
PROGRESS_POLL 🔒
How often a submitted take’s per-window progress stream is polled. A window takes seconds to decode; this cannot be felt.
STOP_PARK_WINDOW 🔒
How long after parking a stop is still treated as racing its start’s registration. The gap it covers is the scheduler’s delay between two spawned tasks, so anything older is a stop whose take is already gone.

Functions§

clear_recording_if_ours 🔒
Clear the seat’s live entry when the take that owns it has resolved - a teardown may already have removed it, which this tolerates.
default_max_capture_minutes 🔒
enabled_by_default 🔒
failing_file 🔒
The file an error is about, so the row that failed is the one that goes red.
failure_for 🔒
forward_take_progress 🔒
Forward a take’s progress steps as updates while it runs. Steps arrive while the recording is still open (segments settling, no total) and again after the stop (total known); the task ends when the take’s segmenter closes the stream.
handle_dictate_stop 🔒
Command::DictateStop: submit (true) or abandon the take that key started. A stop that arrives before its start has registered is parked in stop_pending for the registration to honour, rather than dropped: dropping it would orphan a take until the take’s own cap ended it. A stop whose take already resolved parks too - the park cannot tell the two apart on arrival - but it is only honoured while fresh, and every take-resolution path clears it, so it never poisons the session’s next attempt.
map_outcome 🔒
Map the crate’s answer onto the wire outcome. A normalisation that produced nothing is a valid answer, not a failure.
move_to_finishing 🔒
Move this take’s stop channel from the live map to the finishing list, so a stop during transcription still routes to it.
preflight_config 🔒
The config preflight builds the engine from: the [dictate] settings, the resolved active models, and the always-on stores under forge’s app-support dir - per-take diagnostics and the verified-digest record - machine-local and never synced.
prepare 🔒
The fetch-and-verify leg. Err(()) means state already carries the reason.
record_until_stopped 🔒
Stream level events until a stop decision or the capture cap stops itself. Returns whether the take should be submitted.
register_stream_take 🔒
Command::DictateStream: register a take the client will feed with frames.
remove_finishing 🔒
Drop this take’s finishing entry, whatever took it out of routing.
run_dictate_preflight 🔒
Fetch, verify and load the active models, reporting progress into state as it goes. The caller resolves the models and builds the config; a dictation-off run never gets here.
run_recording 🔒
Own one take from first sample to delivered transcript: stream level events while recording, then submit and wait, honouring the stop channel at every phase. Segments of a long take transcribe while the recording is still running; the transcript lands once the take’s join has been normalized.
run_stream_take 🔒
Own a stream-fed take from its registration to its delivered transcript: the recording lifecycle, with the axes the client sent.
spec_size 🔒
Byte length the config records for the model at path, or zero when the error is about a file no spec names.
teardown_all 🔒
Release every session’s dictation at once, for workspace shutdown.
teardown_for_closed_session 🔒
Abandon everything key has in flight: a live take goes back (dropping the entry closes the stop channel, which is what makes its runner abandon it) and a submitted take loses its cancel route. Called when the session closes.
wait_for_take 🔒
Wait for a submitted take’s answer or a stop decision, whichever comes first. A submit decision is a no-op (the take is already in), and ANY other stop - Some(false) from Esc, or the channel closing at teardown - fires this take’s own cancel token and abandons the wait. The abandon must end the wait rather than re-select: a closed channel answers immediately and forever, and a loop that keeps polling it busy-spins a core until the inference runs out on its own.