Skip to content

Development.zh CN

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

开发指南

English · 首页

Toolchain 与构建

Crate 使用 Rust edition 2024,并提交 Cargo.lock。项目的构建和验证 target 都会强制使用 lockfile:

cargo build --release --locked
# 等价的项目 target:
make build

从源码运行 daemon 前必须先停止已安装的 service,因为两者会使用相同的 runtime socket:

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

源码 daemon 仍会读取常规用户配置和 runtime directory。测试结束后请重新启动已安装的 service。

仓库结构

src/
  main.rs                   TLS 设置与顶层退出处理
  args.rs                   手写 CLI 解析与帮助信息
  app.rs                    命令分发、diagnostics、Settings 启动
  daemon.rs                 control socket、采集、session/finalization pipeline
  config.rs                 TOML schema、默认值、验证与迁移
  diagnostics.rs            schema 4 隐私安全支持数据
  credentials.rs            systemd/environment credential 解析与加密
  focused_window.rs         有边界的 Hyprland/Kitty 目标探测
  agent_context.rs          开始时创建的 Pi/Codex 术语快照
  backend.rs                ASR trait、control 与 event
  backend/
    local_cli.rs             /usr/bin/voxtype adapter 与 process 边界
    qwen_realtime.rs         Qwen Realtime WebSocket adapter
    qwen_batch.rs            compatible 完整音频 HTTP pass
    qwen_audio3/
      streaming.rs           实验性 Streaming protocol 与重连
      native.rs              实验性 Native 完整音频 pass
    text.rs                  transcript 提取与 OpenCC 转换
  http_client.rs            有边界且清理错误的 HTTP helper
  llm.rs                    目标感知 refinement 与共享 deadline
  output.rs                 Wayland/XWayland clipboard 投递及 helper
  settings_backend.rs       带版本的 Settings NDJSON 与持久化
  state.rs                  状态机和私有原子持久化
  waveform.rs               PCM 分析与 Unix socket publisher
  wav.rs                    有边界的临时 PCM16 WAV 处理
  paths.rs, setup.rs         安装路径与 setup helper
assets/
  config.toml                规范的公开 sample/default reference
  voice-input*.service      systemd 用户 unit template
  quickshell/                常驻 HUD QML 与 shader source
  quickshell-settings/       按需启动的 Settings QML
  pi/                        Pi session-registry extension
  omarchy-*.conf/jsonc       Hyprland 与 Waybar snippet
docs/                        Audio3 决策/评估与实验记录
.github/workflows/ci.yml     CI 验证

公开行为变化时,应同步更新 assets/config.toml、Rust 默认值和迁移、Settings 默认值/UI、两份 README,以及两种 Wiki 语言。常驻 HUD 与按需启动的 Settings 是生命周期不同的两套 Quickshell 配置。

验证

运行全部标准本地检查:

make validate
make hud-shaders QSB=/usr/lib/qt6/bin/qsb
/usr/lib/qt6/bin/qsb --dump target/quickshell/shaders/wavy-halo.frag.qsb
git diff --check

make validate 会运行 import-aware qmllint、Rust 格式检查、all-target check、测试,以及将 warning 视为错误的默认 Clippy lint。Qt 工具位于其他路径时,请设置 QMLLINTQSB

CI 还会使用 Qt 6.8.3 qmlformat 解析每个 QML asset;该语法检查是本地 qmllint 的补充,不能替代后者。Shader job 要求 QSB 精确包含六个 target:SPIR-V 100、GLSL 100 es、GLSL 120、GLSL 150、HLSL 50 和 MSL 12;同时验证 reflection 中 uniform block buf 位于 binding 0,qt_Matrix 是 offset 0、大小 64 byte 的 mat4qt_Opacity 是 offset 64、大小 4 byte 的 float

自动化覆盖

Rust 测试覆盖的边界包括:

  • 当前/旧版 TOML、Audio3 endpoint 与 final-pass 迁移、preset、vocabulary 限制和字段验证;
  • CLI 解析、control 命令/响应上限、连接准入、过期 idle generation 和 restart 语义;
  • Qwen Realtime 与 Audio3 event 解析、transcript assembly、重连/重放 policy、timestamp metadata、Native 选择和结果优先级;
  • schema 4 diagnostics allowlist、聚合上限和已清理的 provider identifier;
  • LLM response 验证、目标 prompt、去除术语的重试、OpenRouter sorting,以及共享的 1–30 秒 deadline;
  • Pi/Codex session 验证、脱敏、分词、低频排序、consumer 上限和本地技术术语归一化;
  • PCM16 fragmentation(包括奇数字节读取)、波形 chunk independence、可复用输出 buffer、对称性、NDJSON framing 和 publisher backpressure;
  • 本地 ASR 与输出 child process 的 timeout、process-group termination、并行 stdout/stderr 排空、输出上限、可执行文件探测、clipboard-only 路由和错误清理;
  • 私有/原子 state 持久化,以及带版本 Settings request、精确源 revision conflict、完整字段验证、credential keep/replace 和 restart failure 报告。

测试不会使用真实麦克风、compositor、远程 provider account、Kitty/Pi/Codex process、Quickshell rendering、clipboard manager 或完整 systemd credential lifecycle。QML 会经过 lint 和 parse,但 malformed-frame recovery、多显示器 rendering 等行为仍需要人工集成验证。

人工集成检查清单

请使用不敏感的音频/文本、可丢弃的剪贴板内容,以及允许产生远程测试费用的账号。

  1. 启动两个用户 service;只检查 control.sockstate.jsonwaveform.sock 的 metadata,不打印 state,然后运行 voice-input diagnostics
  2. 测试 record startstoptogglecancelrestart;确认 idle 时 restart 被忽略,只有 active session 会启动 replacement。
  3. 测试随附的 F8/F9/F10 Hyprland binding,以及 hold mode 的 bind/bindr 配对,包括 modifier release 和 finalization 期间排队的 control。
  4. 验证 arming、实时 partial text、waveform 可见性、350 ms no-speech grace、最终空结果,以及达到 audio.max_duration_secs 后自动停止。
  5. 分别验证 Qwen Realtime 正常完成、一次受控重建、delivery overload、完整音频 final pass 和本地 fallback。
  6. 对实验性 Audio3,验证 Regional/Custom 路由、区域 credential、preset、language hint、heartbeat、vocabulary、一次 4× 重放重连,以及 Streaming-only/Adaptive/Always Native policy。
  7. 使用 voice-input asr stream-test --file …voice-input asr test --file … 隔离 Audio3 API;这些命令会发送远程请求,并可能产生费用。
  8. 验证原生 Wayland 与 XWayland 粘贴目标、兼容 clipboard manager 下的 sensitive Wayland payload、可丢弃剪贴板内容的恢复、helper timeout 行为和 Fcitx5 恢复。
  9. 移动/重置 HUD;测试焦点变化、多显示器、热插拔/重连、只重启 daemon、只重启 HUD,以及 malformed/stale state 或 waveform input 不会替换上一份有效 UI state。
  10. 安装 extension 后重启/reload Pi。在 Pi/Codex 中开始 dictation,并在停止前改变焦点,确认开始时术语与停止时目标风格相互独立。
  11. 连续打开两次 Settings,确认只激活一个窗口。测试只保存配置、credential keep/replace、Test LLM、过期 revision conflict、部分 restart failure、request timeout、backend crash、malformed/oversized/wrong-ID response、有限自动重启和手动 Reload 恢复。
  12. 确认 voice-input diagnostics --format json 不包含 transcript、endpoint、model、credential、vocabulary term、Agent source 或 provider message。绝不附加 status 输出或未经检查的 journal。

必须保留的设计规则

  • 失败时保留识别文本: LLM failure 不得丢弃有效 ASR 输出。
  • 一份 refinement deadline: prompt/context 工作与纯 transcript 重试共用一份最多 30 秒的预算;预算至少为 10 秒时,为恢复保留五秒。
  • 采集不承受 backpressure: capture 不得等待远程 ASR 或 HUD client。音频 control queue 和 waveform queue 有界且非阻塞;overload 转向完整缓冲音频恢复。ASR event channel 当前仍是标准无界 Rust mpsc,在单独完成架构重构前,不得把它记录或假设为有界。
  • 所有外部边界都有限制: command、response、file、network message、process output、recording、retry 和 wait 都需要明确上限,并定义 timeout、disconnect 与 partial-data 行为。
  • 清理错误内容: 面向用户的 error 和常规日志不得暴露 provider body、child stderr、transcript、clipboard 内容、credential、术语或 session 数据。
  • 冻结单次操作上下文: 开始时术语不可变,并与停止时目标分类相互独立。保留验证、脱敏、各 consumer 上限和 prompt isolation。
  • Secret 只通过 stdin: Settings replacement 采用 QML stdin → Rust → systemd-creds stdin,不进入 TOML/argv/environment/log/response。QML 托管 string 的清理仍只能尽力执行。
  • 持久化由 Rust 负责: schema 验证、私有权限、原子替换、locking、完整字段保留和精确源 conflict 不得移入 QML。
  • 把 QML 输入视为不可信: 共享 Process/Socket/Timer 位于 screen variant 之外;完整 protocol/state/waveform frame 通过验证后才能一次原子赋值;保留上一份有效值,并限制 backend restart。
  • 保留 clipboard 隐私语义: 所有输出继续使用 clipboard paste;Wayland transcript 与恢复 payload 使用 sensitive hint,XWayland 不提供同等保证。
  • 保留 Qt 6 shader ABI: 编译 .qsb,遵循 Qt uniform block/reflection layout,输出 premultiplied alpha,并保留全部 CI target。

打包

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

  • 把 release binary 安装到 $(PREFIX)/bin
  • 编译并安装 HUD shader、QML asset、Settings 和内置 font 到 $(PREFIX)/share/voice-input
  • 把 Pi extension 安装到 ~/.pi/agent/extensions
  • 生成用户 service template 并安装桌面 snippet;
  • 创建私有 config 和加密 credential directory;
  • 仅在用户配置不存在时创建配置,并可能迁移已识别的旧文件;
  • 删除旧版 GTK/Python 安装 asset。

PREFIX 不会重定位所有用户路径。Packager 必须设置 SYSTEMD_USER_DIRPI_EXTENSIONS_DIRCONFIG_HOME 等变量,或者明确 staging 文件。不要打包本地 config、credential、runtime state、recording、clipboard backup、session JSONL、log 或生成的 build output。

贡献流程

仓库贡献指南定义了 Rust、QML、Quickshell、shader、资源边界和验证标准。

  1. 使用范围明确的 topic branch,不要把无关本地配置加入 patch。
  2. 为解析和不变量增加 unit test;对于 compositor、audio、provider 或 QML 行为,记录可复现的人工检查。
  3. 公开行为变化时,同步更新全部公开 schema surface 和两种文档语言。
  4. 运行上文完整验证与 shader 命令;检查 QSB target/reflection,并运行 git diff --check
  5. 检查 patch 中是否包含 credential、用户绝对路径、transcript、术语、clipboard backup、session file、recording 或生成 artifact。
  6. 在 pull request 中说明兼容性、迁移、隐私/data flow、资源限制和测试证据。

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

Clone this wiki locally