Skip to content

Configuration

Saco Song edited this page Aug 14, 2026 · 8 revisions

Configuration Reference

简体中文 · Home

The user configuration is TOML at ${XDG_CONFIG_HOME:-$HOME/.config}/voice-input/config.toml. The installed public sample is ~/.local/share/voice-input/config.toml.

Inspect the effective deserialized configuration without credentials:

voice-input config
voice-input config --format json

After editing TOML, restart the daemon:

systemctl --user restart voice-input.service

The on-demand Quickshell Settings window exposes the supported configuration and saves a complete, validated configuration through Rust. Its Overview summarizes the local service and current configuration. Speech, Refinement, Output, Appearance, and Hotkey & state group everyday controls, while each relevant page exposes its own expandable Advanced section for technical tuning. Alibaba Realtime and experimental Audio3 options appear inside Speech when the corresponding provider is selected. QML does not parse or write TOML.

Settings save model

voice-input settings

Settings loads the config through a dedicated voice-input settings-backend --stdio child using versioned NDJSON. The same child reports a narrowly allowlisted runtime summary for Overview; it never returns transcript text, output targets, arbitrary runtime errors, or credentials. Rust returns an opaque revision tied to the exact config source it read. Save includes that revision and every supported field; if another editor changed the source, Rust rejects the stale save and asks the user to reload instead of silently overwriting it.

Rust creates the config directory with mode 0700, writes config.toml with mode 0600, and atomically replaces the file. Save can also restart voice-input.service; restart failure is reported separately when persistence has already succeeded. Credential fields support keep or replace only. They are cleared after submission; QML/JavaScript memory clearing is best effort.

Top level and hotkey

Field Default Meaning
state_file "auto" auto uses the runtime state file. A custom path receives an additional copy. disabled suppresses that optional copy; the daemon still maintains $XDG_RUNTIME_DIR/voice-input/state.json for status/HUD.
hotkey.accelerator ", F9" Hyprland accelerator text used when generating the primary start/stop binding and when deciding output settle behavior. The daemon does not register a global shortcut itself.
hotkey.mode "toggle" hold or toggle. Determines the generated F9-style start/stop binding. In toggle mode, an accelerator containing a modifier forces at least 500 ms of pre-output settling.

The installed static Hyprland snippet also binds F8 to cancel and F10 to discard and restart, while leaving Omarchy's stock Super+Ctrl+X Voxtype shortcut unchanged. Use voice-input setup hyprland to generate bindings from the current config.

[audio]

Field Default Meaning
device "default" PipeWire target. default omits pw-record --target; any other value is passed as the target.
sample_rate 16000 Capture sample rate and WAV/ASR rate in Hz. Qwen receives this rate in session metadata.
max_duration_secs 300 Maximum recording length for both dedicated and shared pre-roll capture paths. Reaching the limit automatically stops capture and starts finalization instead of leaving the session recording.
partial_interval_ms 1500 Sleep interval for repeated local-CLI partial transcription. Realtime Qwen partials are event-driven.
pre_roll_enabled false Keeps pw-record open while the daemon runs and seeds a new session from a ring buffer. This has microphone-privacy implications.
pre_roll_ms 500 Requested pre-roll window. The ring also retains at least the 320 ms capture warm-up.

[asr]

Field Default Values / behavior
provider "local-cli" local-cli, alibaba-qwen-realtime, or the explicitly gated experimental alibaba-qwen-audio3.
backend_command "/usr/bin/voxtype" Executable for local final/partial ASR and remote fallback. It receives optional --engine, optional --model, --language CODE transcribe WAV.
engine "sensevoice" Local backend engine. An empty value omits --engine.
model "" Local backend model. Empty means backend default and omits --model.
language "simplified-chinese" english, simplified-chinese, traditional-chinese, japanese, or korean. ASR codes are en, zh, ja, and ko; Chinese variants then use OpenCC.
connect_timeout_ms 5000 Realtime TCP/WebSocket connection timeout; also the HTTP connect timeout for full-audio passes. Valid range: 100–120,000 ms. Audio3 replacement connections cap this value at 5,000 ms.
finalize_timeout_ms 8000 Deadline after requesting the realtime provider to finish. Valid range: 100–120,000 ms.
fallback_to_local true Allows Qwen failure, empty output, or an incomplete realtime audio stream to call the local backend with the complete recording. A successful realtime transcript can still be used after some worker errors, but never after realtime delivery backpressure made the remote stream incomplete.

backend_command defaults explicitly to /usr/bin/voxtype. Keep the absolute path to avoid invoking Voice Input recursively.

[asr.alibaba]

Field Default Meaning
endpoint "wss://dashscope.aliyuncs.com/api-ws/v1/realtime" Realtime WebSocket endpoint. The model query parameter is appended if absent.
model "qwen3-asr-flash-realtime-2026-02-10" Realtime Qwen model ID.
turn_mode "server-vad" server-vad sends VAD settings; manual disables server turn detection and commits on stop.
vad_threshold 0.2 Server VAD threshold passed unchanged to Qwen.
silence_duration_ms 400 Server VAD silence duration passed to Qwen.
final_pass_enabled false Re-recognizes the complete WAV through the compatible HTTP chat-completions endpoint.
final_pass_base_url "" Base URL without /chat/completions. Empty derives the compatible-mode URL for known China, international, or US DashScope realtime hosts. Custom realtime hosts require an explicit value.
final_pass_model "qwen3-asr-flash-2026-02-10" Full-audio final model ID.
final_pass_timeout_ms 20000 Overall HTTP request timeout for the final pass.
final_pass_enable_itn false Sends Alibaba asr_options.enable_itn; ITN means inverse text normalization.

The Alibaba key is not a public TOML field in assets/config.toml. Store credential ID alibaba-api-key as described in Security and Privacy.

[asr.alibaba_audio3] — experimental

Qwen-Audio-3 is opt-in and experimental. It is disabled in the public sample, is not offered by voice-input setup model, and must not be treated as a stable default. Select alibaba-qwen-audio3 in Settings and acknowledge the separate experimental gate before saving. It shares credential ID alibaba-api-key with Alibaba Realtime.

Field Default Meaning
experimental_enabled false Must be true while asr.provider = "alibaba-qwen-audio3"; selecting the provider does not set the acknowledgement automatically.
endpoint_mode "regional" regional uses the reviewed canonical pair for region; custom uses endpoint and native_endpoint exactly.
region "beijing" beijing or singapore. In Regional mode this chooses both endpoints.
endpoint "wss://dashscope.aliyuncs.com/api-ws/v1/inference" Streaming WebSocket URL. Active only in Custom mode; preserved as dormant data in Regional mode.
model "qwen-audio-3.0-asr-flash-streaming" Streaming model ID.
language_hints_enabled false Sends hints derived from [asr].language: en; zh,en; ja,en; or ko,en. Disabled preserves provider auto-detection.
heartbeat_enabled false Keeps a long silent streaming push-to-talk session alive while valid audio frames continue. It is not sent to Native.
recognition_preset "standard" standard, low-latency-dictation, long-form, or custom; see the effective mappings below.
max_sentence_silence_ms 800 Custom maximum sentence silence. The effective value must be 200–6,000 ms. Named presets use their fixed values and preserve this raw field as dormant data.
semantic_punctuation_enabled false Custom semantic-punctuation switch. Named presets use their fixed mapping.
multi_threshold_mode_enabled false Custom multi-threshold switch. Named presets use their fixed mapping.
speech_noise_threshold omitted Optional Custom finite threshold from -1 through 1. Omission leaves provider behavior unchanged. Named presets do not send it.
vocabulary [] Global dynamic vocabulary as { term, weight } entries. A nonempty list is sent to both Streaming and Native.
native_endpoint "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation" Native HTTP URL. Active only in Custom mode; preserved as dormant data in Regional mode.
native_model "qwen-audio-3.0-asr-flash" Native full-audio model ID.
native_final_pass_mode "streaming-only" streaming-only, adaptive, or always; see the policy below.
native_timeout_ms 20000 Native overall request timeout. Operational range: 100–120,000 ms; validation applies whenever the configured policy can run Native automatically.

Regional mode resolves to these fixed pairs; it does not combine a Streaming endpoint from one region with a Native endpoint from another:

Region Streaming Native
beijing wss://dashscope.aliyuncs.com/api-ws/v1/inference https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation
singapore wss://dashscope-intl.aliyuncs.com/api-ws/v1/inference https://dashscope-intl.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation

Custom endpoints must be valid ws/wss and http/https URLs respectively. Non-loopback hosts require encrypted wss/https, embedded credentials are rejected, and the configured strings—including path, port, and query—remain the selected route. Regional mode ignores and preserves dormant Custom URLs.

Recognition presets

Value Effective controls
standard max_sentence_silence_ms = 800; semantic punctuation off; multi-threshold mode off; no speech/noise threshold. This remains the default.
low-latency-dictation 400 ms; semantic punctuation off; multi-threshold mode on; no threshold. It remains an experimental evaluation candidate, not a general recommendation.
long-form 1300 ms; semantic punctuation on; multi-threshold mode off; no threshold. It remains an experimental evaluation candidate.
custom Uses all four raw controls. Silence must be 200–6,000 ms; an optional threshold must be finite and within -1..=1; semantic punctuation and multi-threshold mode are mutually exclusive.

Dynamic vocabulary

vocabulary is optional. Streaming puts the same trimmed term/weight map in run-task; Native puts it in the full-audio request. Local validation enforces all of the following:

  • at most 2,000 entries;
  • at most 256 KiB total configured term bytes;
  • no control characters and no term that is empty after trimming;
  • an all-ASCII trimmed term has at most 7 whitespace-separated segments; any term containing non-ASCII text has at most 15 Unicode characters;
  • weight is 15 or exactly 50;
  • at most 50 entries have weight 50;
  • terms must be unique after trimming (exact string comparison).

Routine diagnostics report only the entry count, never the terms.

Native final-pass policy

  • streaming-only never sends the complete recording automatically.
  • adaptive runs Native when streaming is empty, failed, degraded, interrupted, overloaded, not explicitly finished by the provider, or the recording reaches 30 seconds. It skips only a usable, non-overloaded, explicitly finished stream shorter than 30 seconds. If a healthy completed stream actually sent Audio3 Session Context, duration alone does not replace it with Native; the other recovery reasons still apply.
  • always runs Native for every non-cancelled, nonempty recording.

A successful Native transcript takes precedence. On Native failure or timeout, usable Streaming text remains available, followed by local fallback only when needed. Cancellation never launches Native. Native accepts at most 10 MiB of raw WAV bytes.

Configuration migration and region credentials

Migration distinguishes a missing field from an explicitly configured value:

  • Without endpoint_mode, only the exact canonical Beijing or Singapore URL pair enters Regional mode. The matching pair supplies the inferred region unless an explicit region overrides it. Every other pair becomes Custom, with both original URL strings preserved byte-for-byte; a missing region then defaults to Beijing.
  • The removed workspace_id field is ignored on load and omitted on save. It has no request or routing effect.
  • Legacy native_final_pass_enabled = true becomes native_final_pass_mode = "always"; false becomes "streaming-only". If both old and new fields are present, only matching combinations load; conflicting combinations are rejected as ambiguous.
  • If recognition_preset is missing, the legacy raw controls become standard only for the exact default combination (800, both switches false, no threshold). Every other combination becomes custom and retains the raw values.

Alibaba API keys are region-scoped. Voice Input does not migrate a key when the region changes and never probes another region. Replace credential alibaba-api-key yourself when the selected region requires another key. Singapore selection does not imply that every model/control scenario has been live-validated.

The standalone commands below require Audio3 to be selected and explicitly enabled. They upload the WAV to the resolved Regional route or exact Custom endpoint and may incur API charges:

voice-input asr stream-test --file sample.wav
voice-input asr test --file sample.wav

The streaming test requires a 16 kHz mono PCM16 WAV. The Native command applies the 10 MiB raw-WAV limit.

[output]

Field Default Meaning
mode "paste" Text delivery always uses clipboard paste. Legacy type and clipboard values remain parseable for existing config files but no longer change runtime behavior.
fallback_to_clipboard true Legacy compatibility field; retained when loading and saving older configurations but no longer changes runtime behavior.
type_delay_ms 0 Legacy compatibility field; per-character synthetic typing is no longer used.
pre_type_delay_ms 140 Delay before clipboard output. Toggle shortcuts containing modifiers raise it to at least 500 ms.
paste_keys "shift+Insert" Wayland paste chord sent to the active window by Hyprland dispatch sendshortcut. Supported modifiers are Ctrl, Shift, Alt, and Super.
prefer_paste_for_xwayland true Legacy compatibility field; XWayland always uses the paste route.
xwayland_paste_keys "shift+Insert" XWayland chord sent through xdotool; empty falls back to paste_keys.

Every output temporarily places recognized text on the target clipboard, sends the paste chord, waits 220 ms, and restores the previous content. On native Wayland, both the transcript and restored payload use the wl-copy --sensitive hint; only compatible clipboard managers guarantee that these items are excluded from history and not reordered. XWayland has no equivalent hint. Backup/restoration is best effort when no readable clipboard content exists.

[ime]

Field Default Meaning
manage_fcitx5 true Enables the Fcitx5 guard.
force_ascii_before_output true If both flags are true and fcitx5-remote reports state 2, run fcitx5-remote -c before output and -o afterward.

[llm]

Field Default Meaning
enabled false Enables conservative transcript refinement. Failure always keeps the ASR text.
api_base_url "https://api.openai.com/v1" OpenAI-compatible base URL; /chat/completions is appended.
model "" Required model ID when enabled.
timeout_ms 15000 Shared refinement budget, clamped to 1,000–30,000 ms. Contextual and transcript-only attempts share it; budgets of at least 10,000 ms reserve the final 5,000 ms for transcript-only recovery.
provider_sort "" If non-empty and the URL host is openrouter.ai or a subdomain, sends provider.sort. Ignored for other hosts.
agent_context_enabled false Captures terminology once from the validated Pi/Codex session focused when dictation starts. It is effective when either Audio3 or LLM refinement will consume the snapshot; it does nothing when both consumers are absent.
agent_context_max_chars 6000 Local source-message budget, valid from 500–12,000 characters. Local redaction/truncation happens before consumer-specific selection; this is not the number of characters sent remotely.

One immutable start-time terminology snapshot is shared by enabled consumers. Audio3 Session Context receives a newline-separated view of at most 400 characters. Refine receives at most 96 terms and 1,500 total term characters. Each consumer therefore applies a smaller bound than agent_context_max_chars; the local source message, term frequencies, window titles, and process metadata are not sent.

The LLM credential uses ID openrouter-api-key, including when api_base_url points to another OpenAI-compatible provider. No key appears in voice-input config output.

[hud]

Field Default Meaning
enabled true Controls whether the resident Quickshell HUD surface is shown. The voice-input-hud.service process may remain running while the surface is disabled.
margin_bottom 72 Bottom margin used by the Quickshell HUD, in logical pixels.
height 56 Base/minimum capsule height used by the Quickshell HUD, in logical pixels.
position "bottom-center" bottom-center, bottom-left, or bottom-right; reflected in state and honored by Quickshell.
offset_x 0 Horizontal offset in logical pixels. Positive moves right.
offset_y 0 Vertical offset added to the bottom margin. Positive moves upward.
nudge_step 24 Default amount for voice-input hud move …; runtime forces at least 1.

The daemon includes hud_enabled, hud_margin_bottom, hud_height, hud_position, hud_offset_x, and hud_offset_y in every HUD snapshot. Quickshell honors all six values.

Move commands update TOML and runtime state immediately:

voice-input hud move left
voice-input hud move up 10
voice-input hud position bottom-right
voice-input hud center   # bottom-center and x=0; preserves y
voice-input hud reset    # bottom-center and x=y=0

Credentials and environment fallback

At daemon startup, each secret resolves in this order:

  1. systemd credential from $CREDENTIALS_DIRECTORY (alibaba-api-key or openrouter-api-key);
  2. VOICE_INPUT_ALIBABA_API_KEY or VOICE_INPUT_OPENROUTER_API_KEY;
  3. a legacy in-memory TOML value, if present in an older config.

Use encrypted systemd credentials for the supplied service. Environment variables are mainly useful for a manually launched daemon and may be exposed to same-user process inspection.

Settings credential replacement and Test LLM requests send entered secrets only over inherited stdin. Keys are never included in config, process arguments, environment variables, logs, or backend responses. Test LLM can use either a just-entered key or the existing encrypted-store credential.

See also: Security and Privacy · Troubleshooting

Clone this wiki locally