Configuration
altgo reads a TOML configuration file. Every field has a default, so you only need to change what applies to you. Use Settings for day-to-day changes. The file is for fields the interface does not expose, or for changing many values at once.
Location
~/.config/altgo/altgo.toml
Transcription
[transcriber] backend selects the transcription engine. The default is local, which runs on this machine. Set it to online to use a cloud ASR service.
[transcriber]
backend = "local" # "local" (default) or "online"
language = "zh" # empty string means automatic detection, limited by the chosen model
threads = 0 # local engine threads, 0 = use all CPU parallelism
Local engine
[transcriber]
model = "sense-voice" # filled in after a download in Settings, or a model directory path
Available models:
| Model | Description | Download size |
|---|---|---|
sense-voice | SenseVoice, automatic detection of Chinese, English, Japanese, Korean and Cantonese, fast | about 230 MB |
sense-voice-yue | SenseVoice Cantonese-enhanced, more accurate on Cantonese | about 237 MB |
fire-red-asr2-ctc | FireRedASR2 CTC, stronger on mixed Chinese and English speech | about 740 MB |
The local engine embeds sherpa-onnx. The model loads once when the pipeline starts and stays in memory. A missing model file or a load failure is reported at startup. Recordings longer than 60 seconds are split at speech boundaries detected by silero-vad and transcribed segment by segment, with a target segment length of 25 seconds, so there is no practical length limit. Shorter recordings are transcribed in one pass. The silero-vad model ships with the app, and splitting falls back to silence detection if it cannot be loaded.
Online engine
[transcriber]
backend = "online"
[transcriber.online]
protocol = "mimo_chat" # mimo_chat, chat_input_audio or transcriptions
api_key = "sk-your-key"
api_base_url = "https://token-plan-cn.xiaomimimo.com/v1"
model = "mimo-v2.5-asr"
timeout = 60 # request timeout in seconds
Differences between the three protocols:
| Protocol | Request shape | Services |
|---|---|---|
mimo_chat | chat/completions with the WAV as base64 in a single input_audio block and asr_options | Xiaomi MiMo gateway, the default |
chat_input_audio | Same chat shape without asr_options, with the language as a top-level field when set | OpenAI-shaped input_audio endpoints |
transcriptions | OpenAI /audio/transcriptions multipart | whisper, Zhipu glm-asr, the hosted SiliconFlow SenseVoiceSmall and others |
An online transcription failure is reported directly and does not fall back to the local engine. The key can also be set with the ALTGO_TRANSCRIBER_API_KEY environment variable.
Polishing
Polishing is optional. With level none altgo skips it and calls no model. With any other level, [polisher] backend selects a remote API or a local model.
Engine selection
[polisher]
backend = "remote" # "remote" (default) or "local"
local_model = "qwen3-0.6b" # local: "qwen3-0.6b", "qwen3-1.7b" or a custom GGUF path
The local runtime and models are downloaded on demand from the polishing section in Settings and are not bundled with the installer. The runtime is llama.cpp's llama-server: the Windows x64 archive is about 19.5 MB and expands to about 47 MiB. Qwen3-0.6B is about 397 MB and Qwen3-1.7B about 1.1 GB. You can point local_model at your own GGUF file, in which case altgo downloads no weights and installs only the runtime.
Local settings are fixed at values measured in practice: temperature 0, output capped at 192 tokens and threads set to the number of physical cores, up to 16. The local engine has a single level. The level choice acts as a switch: none turns it off and any other level turns it on. Output also passes a fidelity check. Numbers, URLs or paths changed, the text reduced to a summary, content added, or formatting markers introduced all count as failures and fall back to the raw transcript.
Local latency: the first call waits for the runtime to start and the model to load, measured at about 1.3 to 2.7 seconds on a local machine. Later calls take about 0.15 to 0.3 seconds. The runtime exits after ten idle minutes to release memory and starts again on the next use.
Polishing level
| Level | Effect | Remote API key required |
|---|---|---|
none | No polishing, the transcript is used as is | No |
light | Fix punctuation and typos | Yes |
medium | Fix grammar and improve word order | Yes |
heavy | Structured rewrite for formal documents | Yes |
Remote configuration
[polisher]
level = "medium"
protocol = "openai" # "openai" or "anthropic"
api_key = "sk-your-key"
api_base_url = "https://api.deepseek.com"
model = "deepseek-v4-flash"
max_tokens = 1024
timeout = 60
temperature = 0.3
With level = "none", no key, URL or model name is needed. With any other level, all three are required.
api_base_url accepts three forms. altgo builds the request URL for you:
- Without a version path, such as
https://api.deepseek.comorhttps://api.anthropic.com: altgo appends/v1/chat/completionsor/v1/messages. - With a version path, such as
https://api.moonshot.cn/v1,https://open.bigmodel.cn/api/paas/v4orhttps://dashscope.aliyuncs.com/compatible-mode/v1: altgo appends only/chat/completionsor/messages. - A full endpoint ending in
/chat/completionsor/messages: used unchanged.
The polishing section in Settings has a Test connection button. After choosing a provider preset and entering a key, click it to send one minimal request and get a readable result (invalid key, wrong URL, timeout and so on), without saving or recording anything.
Glossary
The glossary fixes domain terms that speech recognition writes with homophone characters. Add terms one at a time in Settings, or set glossary in [polisher]:
[polisher]
glossary = [
"地月空间",
"平动点",
"深空",
"航天器",
"月球",
"近月空间",
"月面",
"探测器",
"地月转移轨道",
"拉格朗日点",
]
The list applies only to remote polishing at medium and heavy. When it is non-empty, altgo appends it to the system prompt and sends temperature 0 regardless of the configured value. The glossary is sent to the provider along with the transcript, so only list terms you are comfortable sending there. Keep it to 10 to 50 terms, with at most 32 characters per term. none, light and local polishing ignore the list. The model may normalize synonyms or replace a correct phrase with a glossary term. altgo checks the output and retries once without the glossary when it inserts a term that has no homophone source in the input. This phonetic check only covers Chinese terms. English terms can still be added to the list, but altgo does not run the check on them.
Common providers
The table lists configuration values for common services. All combinations go in the [polisher] section.
| Provider | protocol | api_base_url | Example model |
|---|---|---|---|
| OpenAI | openai | https://api.openai.com | gpt-3.5-turbo |
| DeepSeek | openai | https://api.deepseek.com | deepseek-v4-flash |
| Anthropic | anthropic | https://api.anthropic.com | claude-sonnet-4-20250514 |
| Volcengine Ark (Doubao) | openai | https://ark.cn-beijing.volces.com/api/v3 | doubao-seed-1.6-flash-250828 |
| Baidu Qianfan | openai | https://qianfan.baidubce.com/v2 | ernie-4.5-turbo-128k |
| MiniMax | openai | https://api.minimaxi.com/v1 | MiniMax-M3 |
| OpenRouter | openai | https://openrouter.ai/api/v1 | deepseek/deepseek-v4-flash |
| Google Gemini (OpenAI-compatible endpoint) | openai | https://generativelanguage.googleapis.com/v1beta/openai | gemini-2.5-flash-lite |
| Local Ollama | openai | http://localhost:11434 | qwen2.5:7b |
The api_key for a local Ollama server can be any non-empty value, for example ollama.
Settings includes presets for about a hundred providers (13 native, plus 86 migrated from cc-switch). Selecting one fills in the URL and protocol, so you only add the key. Entries suffixed with (Anthropic) or (OpenAI) are different protocol endpoints of the same provider.
Thinking level (off by default)
Voice polishing is a light text task. A reasoning model thinks before answering, which adds seconds of latency and token cost. The default is thinking_level = "off", and altgo adds the disable parameter automatically for providers that support it:
| Provider | Parameter added automatically |
|---|---|
| Dashscope (Qwen), SiliconFlow (.cn and .com) | enable_thinking: false |
| Zhipu (bigmodel.cn and z.ai), Volcengine Ark, MiniMax, DeepSeek, Moonshot, Kimi, Xiaomi MiMo | thinking: {"type": "disabled"} |
| OpenRouter | reasoning: {"enabled": false} |
| Providers that think by default behind the Anthropic protocol (DeepSeek, Xiaomi MiMo and others) | thinking: {"type": "disabled"} |
Set thinking_level to "low", "medium" or "high" when you want thinking. Settings has a dropdown with the same values. Requests enable thinking in each provider's own dialect:
| Provider | Parameter added when a level is set |
|---|---|
| Dashscope, SiliconFlow | enable_thinking: true (a switch only, no levels) |
| Zhipu, Volcengine Ark, MiniMax, DeepSeek, Moonshot, Kimi, Xiaomi MiMo | thinking: {"type": "enabled"} (a switch only, no levels) |
| OpenRouter | reasoning: {"effort": "low"}, "medium" or "high" |
| Other providers, including OpenAI | reasoning_effort: "low", "medium" or "high" |
| Anthropic protocol | thinking: {"type": "enabled", "budget_tokens": 1024}, 4096 or 16384, with a higher max_tokens and no temperature |
Relay endpoints not listed above have any <think> reasoning traces stripped from the response. The Anthropic protocol takes the first text block. Anthropic itself and unknown endpoints receive no disable parameter, because official thinking is opt-in and newer models return HTTP 400 for disabled.
For providers whose thinking is on by default, when the disable parameter is not sent, thinking and the answer share max_tokens. If thinking uses the whole budget, the visible text is empty and polishing appears to fail. Some models force thinking (such as Zhipu GLM-5.3) and reject the disable parameter. Choosing the model recommended by the preset avoids this. If a model does not support the chosen level, the provider returns an error.
Environment variable override:
export ALTGO_POLISHER_API_KEY="sk-your-key"
Key configuration
The default trigger key is right Alt. Using Press to set in Settings writes linux_evdev_code on Linux and windows_vk_code on Windows.
[key_listener]
key_name = "Alt_R" # trigger key, right Alt here; use Alt_L for left Alt
long_press_threshold = 200 # long press threshold in milliseconds
double_click_interval = 300 # double press interval in milliseconds
min_press_duration = 100 # minimum press duration, filters IME chatter
Recording
[recorder]
sample_rate = 16000 # transcription uses 16 kHz mono input; other values are rejected
Output
[output]
prefer_polished = true # clipboard and injection prefer polished text when it succeeded and is non-empty
inject_text = false # type the result into the focused window, Windows only
Linux needs xclip, xsel or wl-copy available, otherwise the automatic clipboard write fails. You can still copy from the overlay, or use the copy button in the history list on the home page.
Interface
[gui]
language = "" # "zh", "en" or an empty string to follow the system language
overlay_position = "bottom_center" # "bottom_center" or "top_center"
auto_check_update = true # check for updates at startup
Logging
Log levels are not part of the configuration file. Use an environment variable:
RUST_LOG=altgo=debug altgo
Precedence
Environment variables > configuration file > code defaults
| Environment variable | Overrides |
|---|---|
ALTGO_POLISHER_API_KEY | polisher.api_key |
ALTGO_TRANSCRIBER_API_KEY | transcriber.online.api_key |
ALTGO_MODEL_BASE_URL | transcription model download mirror (not a configuration field) |
ALTGO_POLISH_MODEL_BASE_URL | polishing model download mirror (not a configuration field) |