Skip to content

Development.zh CN

Saco Song edited this page Jul 24, 2026 · 4 revisions

开发指南

English · 首页

Toolchain 与构建

Crate 使用 Rust edition 2024,并提交 Cargo.lock。先获取 lockfile 指定的 dependency,之后 Makefile 可以离线构建:

cargo fetch --locked
cargo build --release --locked
# Dependency 已缓存后,可以使用项目 target:
make build

make build 会运行 cargo build --release --offline。Cargo cache 为空的全新 checkout 必须先执行 cargo fetch --locked

从源代码运行 daemon 前,请先停止已安装的 service,因为两者会绑定相同的 runtime socket:

systemctl --user stop voice-input.service
VOICE_INPUT_ASSET_DIR="$PWD/assets" cargo run --offline -- daemon

源代码 daemon 仍会读取常规的用户 config 和 runtime directory。开发完成后请重新启动已安装的 service。

仓库布局

src/
  main.rs                 初始化 TLS provider 并处理顶层退出
  args.rs                 手写的 CLI parser/help
  app.rs                  command dispatch,以及 status/config/settings client
  daemon.rs               control socket、capture service 和 session pipeline
  config.rs               公开 TOML schema、默认值和 legacy migration
  credentials.rs          解析 systemd/environment credential
  agent_context.rs        验证和脱敏当前 Pi/Codex session
  backend.rs              ASR trait、control 和 event
  backend/
    local_cli.rs           /usr/bin/voxtype adapter
    qwen_realtime.rs       Alibaba WebSocket streaming adapter
    qwen_batch.rs          Qwen 全音频 compatible-HTTP pass
    text.rs                提取 transcript 并执行 OpenCC 转换
  llm.rs                  refinement prompt、共享 deadline 和 fail-open 行为
  output.rs               检测 Wayland/XWayland target 并输出文本
  state.rs                state machine snapshot 和原子持久化
  waveform.rs             PCM 分析、ASR packetizer 和 Unix socket publisher
  wav.rs                   临时 PCM16 WAV writer
  paths.rs, setup.rs       安装路径和 setup helper
assets/
  config.toml              公开示例与默认值参考
  voice-input*.service    systemd 用户 unit template
  quickshell/              主 QML HUD
  hud.py                   旧版 GTK4 layer-shell HUD
  settings.py              可选 GTK/libadwaita 设置界面
  pi/                      Pi session-registry extension
  omarchy-*.conf/jsonc     Hyprland 和 Waybar snippet
.github/workflows/ci.yml   CI check
packaging/                 当前为空的 placeholder
contrib/omarchy/           当前为空的 Hyprland/Waybar placeholder

修改公开字段时,应同步更新 assets/config.toml、Rust 默认值和 Settings 默认值。主 Quickshell HUD 与旧版 Python HUD 是两个独立实现,也需要分别考虑。

测试

运行与 CI 相同的 Rust check:

cargo fmt --all -- --check
cargo check --locked --all-targets
cargo test --locked

检查 Python syntax,同时避免把 cache file 写入 worktree:

PYTHONPYCACHEPREFIX=/tmp/voice-input-pycache \
  python3 -m py_compile assets/hud.py assets/settings.py

当前 unit test 直接写在各 module 中,覆盖内容包括:

  • 当前和 legacy TOML parsing;
  • transcript extraction 和 OpenCC 路径选择;
  • Qwen event parsing 和 transcript assembly;
  • LLM response validation、retry policy、OpenRouter sort 和共享的 1–5 秒预算;这些测试使用本地 mock HTTP server;
  • Pi/Codex JSONL extraction、redaction 和 truncation;
  • waveform 的 chunk independence、对称性、NDJSON framing,以及 ASR packet preservation;
  • Wayland/XWayland effective output mode 和 120 字符 paste threshold;
  • credential 的换行清理与 NUL 拒绝,以及 CLI parsing。

测试不会访问真实麦克风、compositor、Alibaba/OpenAI-compatible account、Kitty/Pi/Codex process 或 Quickshell render,也不会实际验证 clipboard restoration。这些部分需要手动集成测试。

手动集成测试清单

请使用不敏感的测试文本和可丢弃的 clipboard 内容。

  1. 启动两个用户 service,确认三个 runtime file/socket 都存在。
  2. 在 terminal 中测试 record startstoptogglecancel
  3. 测试计划使用的 Hyprland binding,并确认 modifier release。
  4. 检查 realtime partial text、Server VAD 波形显示和静音取消。
  5. 如果启用相关功能,分别验证 final-pass replacement 和 /usr/bin/voxtype fallback。
  6. 分别测试短 Wayland direct type、长 Wayland paste 和 XWayland target。
  7. 确认 clipboard 和 Fcitx5 状态能够恢复。
  8. 移动或重置 HUD,并测试 monitor focus change/hotplug。
  9. 安装 extension 后重启 Pi,再分别测试 Pi 和 Codex 上下文。
  10. 把 timing log 附加到 report 前,检查其中是否意外包含内容。

需要保持的设计规则

  • 失败时保留识别文本: LLM failure 不能丢弃有效的 ASR output。
  • 单一 refinement deadline: 上下文请求与 fallback 共用最多五秒预算。
  • 视觉更新不能产生反压: waveform/HUD client 不能阻塞 capture 或 ASR。
  • 明确 backend identity: 本地 fallback 始终是配置的可执行文件,默认 /usr/bin/voxtype
  • Secret 不进入 config/argv/log: 新 credential 应通过 runtime resolver 和 systemd credential ID 读取。
  • 验证外部上下文: 保留 process/session/path 检查、size limit、redaction 和 prompt isolation。
  • 原子 state: 并发 writer 必须继续通过 StateHandle 串行更新,并以临时文件替换目标。

打包

packaging/ 当前没有发行版 package recipe;安装由 Makefile 实现。make install 会:

  • 把一个 release binary 安装到 $(PREFIX)/bin,默认 ~/.local/bin
  • 把 asset 安装到 $(PREFIX)/share/voice-input
  • 把 Pi extension 安装到 ~/.pi/agent/extensions
  • 把 unit template 渲染到 ~/.config/systemd/user
  • 创建私有 config 与加密 credential directory;
  • 仅在 config 不存在时创建示例;
  • 可能迁移程序明确识别的旧版 Voxtype config/credential file。

PREFIX 只会改变 binary/share 目标。Service、Pi extension、config 和 credential 位置有各自的 Make variable,并且默认使用用户 home。Packager 应设置所有相关变量,或者明确 staging 每个文件;不能假设 PREFIX 会重定位全部内容。

Service template 包含 @VOICE_INPUT_BIN@@VOICE_INPUT_ASSET_DIR@@VOICE_INPUT_QUICKSHELL_DIR@;打包时需要替换为最终绝对路径。Package metadata 应明确列出 /usr/bin/qs 和各项可选外部命令依赖。不要打包本地 config、加密 blob、runtime state、录音、JSONL session 或 log。

贡献流程

  1. 如果行为会影响隐私、provider protocol 或桌面前提,请先创建 issue 或范围明确的 proposal。
  2. 创建较小的 topic branch,不要把无关的本地配置加入 patch。
  3. 为纯逻辑增加或更新 unit test。对于桌面或网络行为,请提供可复现的手动测试步骤。
  4. 公开行为变化时,同步更新 assets/config.toml、Settings、Rust 默认值、README 和两种 Wiki 语言。
  5. 运行格式检查、all-target check、测试和 Python syntax validation。
  6. 检查 diff 中是否存在 credential、用户专属 absolute path、transcript、session file、录音或生成产物。
  7. 提交 pull request,并说明修改原因、兼容性影响、隐私或数据流变化,以及测试证据。

CI 会在 push 和 pull request 时运行,并且只获得仓库内容的只读权限。它检查 Rust formatting、all target、test 和 Python syntax。CI 通过后,仍然需要执行上面的 compositor、音频和 provider 手动检查。

另请参阅:架构 · 安全与隐私

Clone this wiki locally