Route qwen-asr through the organization fork

Keep the submodule pinned to the reviewed commit while moving the fetch URL to Open-Less/qwen-asr, then document the review checklist future upgrades must follow.\n\nConstraint: #301 identified the personal upstream submodule URL as a supply-chain risk.\nRejected: Updating the submodule commit together with the URL change | would mix provenance hardening with unreviewed upstream code changes.\nConfidence: high\nScope-risk: narrow\nDirective: Do not point .gitmodules back to antirez/qwen-asr; sync upstream into the organization fork and review the diff first.\nTested: gh repo view Open-Less/qwen-asr; git submodule update --init --recursive openless-all/app/src-tauri/vendor/qwen-asr; git diff --check; npm run build; cargo check --manifest-path src-tauri/Cargo.toml\nNot-tested: macOS INSTALL=0 ./scripts/build-mac.sh C-link verification.
This commit is contained in:
H-Chris233 committed 2026-05-12 18:48:03 +08:00
1 parent ea1b18ee08
commit 4d9b63aab3
13 files changed
+66 -17

No files matched your search

+1 -1
View File
@@ -59,7 +59,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
# vendor/qwen-asr 是 macOS 上 build.rs 必须的 git submodule(cc-rs 编译 # vendor/qwen-asr 是 macOS 上 build.rs 必须的 git submodule(cc-rs 编译
# antirez/qwen-asr 的 C 源),不拉就会在 mac 端 cargo build 阶段挂掉。 # Open-Less/qwen-asr fork 的 C 源),不拉就会在 mac 端 cargo build 阶段挂掉。
submodules: recursive submodules: recursive
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
+1 -1
View File
@@ -1,3 +1,3 @@
[submodule "openless-all/app/src-tauri/vendor/qwen-asr"] [submodule "openless-all/app/src-tauri/vendor/qwen-asr"]
path = openless-all/app/src-tauri/vendor/qwen-asr path = openless-all/app/src-tauri/vendor/qwen-asr
url = https://github.com/antirez/qwen-asr.git url = https://github.com/Open-Less/qwen-asr.git
+2 -2
View File
@@ -141,7 +141,7 @@ OpenLess does one thing: **turn speech into usable written text (especially AI p
- Tauri 2 + Rust backend + React/TS frontend. macOS 12+, Windows 10+. - Tauri 2 + Rust backend + React/TS frontend. macOS 12+, Windows 10+.
- **Toggle and push-to-talk** recording modes. `Esc` cancels at any phase, including polish/insert. - **Toggle and push-to-talk** recording modes. `Esc` cancels at any phase, including polish/insert.
- **Cloud ASR**: Volcengine streaming ASR, OpenAI Whisper-compatible batch ASR, Apple Speech (macOS). - **Cloud ASR**: Volcengine streaming ASR, OpenAI Whisper-compatible batch ASR, Apple Speech (macOS).
- **Local ASR**: bundled Qwen3-ASR (0.6B / 1.7B) via vendored `antirez/qwen-asr`; Windows Foundry Local Whisper variants. - **Local ASR**: bundled Qwen3-ASR (0.6B / 1.7B) via vendored `Open-Less/qwen-asr`; Windows Foundry Local Whisper variants.
- **Polish providers**: Ark / DeepSeek / OpenAI / Doubao / Anthropic-compatible chat-completions, plus any OpenAI-compatible endpoint you bring. - **Polish providers**: Ark / DeepSeek / OpenAI / Doubao / Anthropic-compatible chat-completions, plus any OpenAI-compatible endpoint you bring.
- 4 output modes: raw, light polish, structured (**AI prompt mode**), formal. Plus a **translation hotkey** that converts speech directly into the configured target language ([#43](../../issues/43)). - 4 output modes: raw, light polish, structured (**AI prompt mode**), formal. Plus a **translation hotkey** that converts speech directly into the configured target language ([#43](../../issues/43)).
- **Selection-ask QA panel** — separate hotkey opens a floating panel that runs voice Q&A against the highlighted text in any app ([#118](../../issues/118)). - **Selection-ask QA panel** — separate hotkey opens a floating panel that runs voice Q&A against the highlighted text in any app ([#118](../../issues/118)).
@@ -189,7 +189,7 @@ Full end-user walkthrough: [USAGE.md](USAGE.md).
## Build from source (developers) ## Build from source (developers)
The active codebase is in `openless-all/app/` (Tauri 2 + Rust + React/TS). The macOS build links a vendored C ASR engine ([`antirez/qwen-asr`](https://github.com/antirez/qwen-asr)) pulled in as a git submodule under `src-tauri/vendor/qwen-asr/`, so initialize submodules on first clone. The active codebase is in `openless-all/app/` (Tauri 2 + Rust + React/TS). The macOS build links a vendored C ASR engine ([`Open-Less/qwen-asr`](https://github.com/Open-Less/qwen-asr), forked from `antirez/qwen-asr`) pulled in as a git submodule under `src-tauri/vendor/qwen-asr/`, so initialize submodules on first clone.
```bash ```bash
# First clone only — pull in vendored submodules # First clone only — pull in vendored submodules
+2 -2
View File
@@ -141,7 +141,7 @@ OpenLess 只做一件事:**把语音变成可用的书面文字(尤其是 AI
- Tauri 2 + Rust 后端 + React/TS 前端;macOS 12+,Windows 10+。 - Tauri 2 + Rust 后端 + React/TS 前端;macOS 12+,Windows 10+。
- **切换式 + 按住说话** 双模式录音;任意阶段按 `Esc` 都能取消(包括润色 / 插入中)。 - **切换式 + 按住说话** 双模式录音;任意阶段按 `Esc` 都能取消(包括润色 / 插入中)。
- **云端 ASR**:火山引擎流式 ASR、OpenAI Whisper 兼容批式 ASR、Apple Speech(macOS)。 - **云端 ASR**:火山引擎流式 ASR、OpenAI Whisper 兼容批式 ASR、Apple Speech(macOS)。
- **本地 ASR**:内置 Qwen3-ASR(0.6B / 1.7B),通过 vendored `antirez/qwen-asr` 链接;Windows 端支持 Foundry Local Whisper。 - **本地 ASR**:内置 Qwen3-ASR(0.6B / 1.7B),通过 vendored `Open-Less/qwen-asr` 链接;Windows 端支持 Foundry Local Whisper。
- **润色 Provider**:Ark / DeepSeek / OpenAI / Doubao / Anthropic 兼容的 Chat Completions,以及任意 OpenAI 兼容的自定义 endpoint。 - **润色 Provider**:Ark / DeepSeek / OpenAI / Doubao / Anthropic 兼容的 Chat Completions,以及任意 OpenAI 兼容的自定义 endpoint。
- 4 种输出模式:原文、轻度润色、清晰结构(**AI prompt 模式**)、正式表达。另含**翻译热键**——按下后说一段话直接转成目标语言插入([#43](../../issues/43))。 - 4 种输出模式:原文、轻度润色、清晰结构(**AI prompt 模式**)、正式表达。另含**翻译热键**——按下后说一段话直接转成目标语言插入([#43](../../issues/43))。
- **划词语音问答(QA)面板** — 独立热键打开浮窗,对当前选中文本发起语音 Q&A([#118](../../issues/118))。 - **划词语音问答(QA)面板** — 独立热键打开浮窗,对当前选中文本发起语音 Q&A([#118](../../issues/118))。
@@ -192,7 +192,7 @@ OpenLess 只做一件事:**把语音变成可用的书面文字(尤其是 AI
## 从源码构建(开发者) ## 从源码构建(开发者)
当前活跃代码库在 `openless-all/app/`(Tauri 2 + Rust + React/TS)。macOS 构建会链接一份 vendored 的本地 ASR 引擎([`antirez/qwen-asr`](https://github.com/antirez/qwen-asr)),以 git submodule 形式挂在 `src-tauri/vendor/qwen-asr/`,首次 clone 后必须先拉子模块。 当前活跃代码库在 `openless-all/app/`(Tauri 2 + Rust + React/TS)。macOS 构建会链接一份 vendored 的本地 ASR 引擎([`Open-Less/qwen-asr`](https://github.com/Open-Less/qwen-asr),fork 自 `antirez/qwen-asr`),以 git submodule 形式挂在 `src-tauri/vendor/qwen-asr/`,首次 clone 后必须先拉子模块。
```bash ```bash
# 首次 clone 后拉取子模块 # 首次 clone 后拉取子模块
@@ -0,0 +1,49 @@
# qwen-asr submodule 升级检查清单
`openless-all/app/src-tauri/vendor/qwen-asr` 是 macOS 本地 Qwen3-ASR 的 C 引擎来源。OpenLess 只从组织 fork `https://github.com/Open-Less/qwen-asr.git` 拉取 submodule;`antirez/qwen-asr` 只作为上游同步来源,不直接进入主仓构建链路。
## 升级原则
- 不直接把 `.gitmodules` 改回个人上游仓库。
- 不在未审查 upstream diff 的情况下推进 submodule commit。
- 每次升级只推进 submodule 指针;除非编译或 FFI 必需,不混入 OpenLess 主项目逻辑改动。
- 保留当前锁定 commit 的可回滚性,必要时能 `git checkout <old-commit> -- openless-all/app/src-tauri/vendor/qwen-asr` 回退。
## 操作步骤
1. 在 `Open-Less/qwen-asr` fork 中同步 upstream。
2. 审查 fork 中待引入 commit:
- C 源码:`qwen_asr*.c`、`qwen_asr*.h`
- 构建脚本 / 模型下载脚本
- 新增二进制、大文件、网络下载地址或 shell 命令
- FFI API 是否改变:`qwen_load`、`qwen_free`、`qwen_set_token_callback`、`qwen_transcribe_audio`、`qwen_transcribe_stream`
3. 在 OpenLess 主仓更新 submodule:
```bash
git submodule sync --recursive openless-all/app/src-tauri/vendor/qwen-asr
git submodule update --init --recursive openless-all/app/src-tauri/vendor/qwen-asr
cd openless-all/app/src-tauri/vendor/qwen-asr
git fetch origin
git checkout <reviewed-commit>
cd -
```
4. 验证 submodule 来源仍是组织 fork:
```bash
git config --file .gitmodules --get submodule.openless-all/app/src-tauri/vendor/qwen-asr.url
git -C openless-all/app/src-tauri/vendor/qwen-asr remote get-url origin
git submodule status openless-all/app/src-tauri/vendor/qwen-asr
git diff --submodule=log -- openless-all/app/src-tauri/vendor/qwen-asr
```
5. 至少运行:
```bash
cd openless-all/app
npm run build
cargo check --manifest-path src-tauri/Cargo.toml
```
macOS 发布前还要用 `INSTALL=0 ./scripts/build-mac.sh` 验证 C 源编译和链接。
## PR / commit 说明必须包含
- 旧 submodule commit 和新 submodule commit。
- 已审查的 upstream commit 范围。
- 是否有 FFI API、模型文件结构、下载脚本或构建参数变化。
- 已运行的验证命令和未覆盖的平台。
+1 -1
View File
@@ -4,7 +4,7 @@ This is the current cross-platform OpenLess workspace.
## App Directory ## App Directory
The runnable Tauri app lives in `app/`. The macOS build links a vendored C ASR engine (`antirez/qwen-asr`) tracked as a git submodule under `app/src-tauri/vendor/qwen-asr/`, so initialize submodules on first clone. The runnable Tauri app lives in `app/`. The macOS build links a vendored C ASR engine (`Open-Less/qwen-asr`, forked from `antirez/qwen-asr`) tracked as a git submodule under `app/src-tauri/vendor/qwen-asr/`, so initialize submodules on first clone.
```bash ```bash
# First clone only — pull in vendored submodules # First clone only — pull in vendored submodules
+2 -2
View File
@@ -12,7 +12,7 @@ crate-type = ["staticlib", "cdylib", "rlib"]
[build-dependencies] [build-dependencies]
tauri-build = { version = "2", features = [] } tauri-build = { version = "2", features = [] }
# Compile antirez/qwen-asr C sources for the local ASR engine on macOS. # Compile vendored Open-Less/qwen-asr C sources for the local ASR engine on macOS.
cc = "1.1" cc = "1.1"
[dependencies] [dependencies]
@@ -74,7 +74,7 @@ core-graphics = "0.24"
objc2 = "0.5" objc2 = "0.5"
objc2-foundation = "0.2" objc2-foundation = "0.2"
objc2-app-kit = "0.2" objc2-app-kit = "0.2"
# antirez/qwen-asr 用 malloc 分配返回字符串,必须用 C `free()` 释放。 # qwen-asr 用 malloc 分配返回字符串,必须用 C `free()` 释放。
libc = "0.2" libc = "0.2"
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
+2 -2
View File
@@ -5,7 +5,7 @@ fn main() {
tauri_build::build(); tauri_build::build();
} }
/// 编译 antirez/qwen-asr 的 C 源(仅 macOS)。 /// 编译 vendored Open-Less/qwen-asr 的 C 源(仅 macOS)。
/// ///
/// 上游 Makefile `make blas` 等价配置:BLAS 加速通过 Accelerate framework, /// 上游 Makefile `make blas` 等价配置:BLAS 加速通过 Accelerate framework,
/// `USE_BLAS` + `ACCELERATE_NEW_LAPACK` 是必要宏。 /// `USE_BLAS` + `ACCELERATE_NEW_LAPACK` 是必要宏。
@@ -34,7 +34,7 @@ fn build_qwen_asr_macos() {
.define("ACCELERATE_NEW_LAPACK", None) .define("ACCELERATE_NEW_LAPACK", None)
.flag("-O3") .flag("-O3")
.flag("-ffast-math") .flag("-ffast-math")
// 上游开 `-Wall -Wextra`;我们把 antirez 的代码当三方依赖,把无关警告压成静默 // 上游开 `-Wall -Wextra`;我们把 qwen-asr 的代码当三方依赖,把无关警告压成静默
// 避免 build log 噪音淹没我们自己的告警。 // 避免 build log 噪音淹没我们自己的告警。
.flag("-Wno-unused-parameter") .flag("-Wno-unused-parameter")
.flag("-Wno-unused-variable") .flag("-Wno-unused-variable")
@@ -1,6 +1,6 @@
//! 本地 ASR 引擎入口。 //! 本地 ASR 引擎入口。
//! //!
//! 当前只在 macOS 编入 antirez/qwen-asr (纯 C + Accelerate);Windows 端 //! 当前只在 macOS 编入 vendored Open-Less/qwen-asr (纯 C + Accelerate);Windows 端
//! 的本地推理路径见 issue #256,本期不实现。 //! 的本地推理路径见 issue #256,本期不实现。
pub mod cache; pub mod cache;
@@ -1,4 +1,4 @@
//! antirez/qwen-asr 的安全 Rust 包装。 //! vendored Open-Less/qwen-asr 的安全 Rust 包装。
//! //!
//! 当前只暴露**最小可用面**:`load` / `transcribe_audio` / `transcribe_stream` //! 当前只暴露**最小可用面**:`load` / `transcribe_audio` / `transcribe_stream`
//! + token 回调。后续接 coordinator 时再扩 prompt/language 设置。 //! + token 回调。后续接 coordinator 时再扩 prompt/language 设置。
@@ -35,7 +35,7 @@ unsafe impl Sync for QwenAsrEngine {}
impl QwenAsrEngine { impl QwenAsrEngine {
/// 从模型目录加载(目录里需含 `config.json` / `model.safetensors*` / /// 从模型目录加载(目录里需含 `config.json` / `model.safetensors*` /
/// `vocab.json` / `merges.txt`,结构见 antirez `download_model.sh`)。 /// `vocab.json` / `merges.txt`,结构见 qwen-asr `download_model.sh`)。
pub fn load(model_dir: &Path) -> Result<Self> { pub fn load(model_dir: &Path) -> Result<Self> {
let dir_str = model_dir let dir_str = model_dir
.to_str() .to_str()
@@ -1,4 +1,4 @@
//! 对 antirez/qwen-asr 公共 C API 的最小 FFI 声明。 //! 对 vendored Open-Less/qwen-asr 公共 C API 的最小 FFI 声明。
//! //!
//! 头文件见 `vendor/qwen-asr/qwen_asr.h`。这里**不**复刻 `qwen_ctx_t` //! 头文件见 `vendor/qwen-asr/qwen_asr.h`。这里**不**复刻 `qwen_ctx_t`
//! 内部布局——保持不透明指针即可,避免 pthread/对齐相关的脆弱假设。 //! 内部布局——保持不透明指针即可,避免 pthread/对齐相关的脆弱假设。
@@ -125,7 +125,7 @@ fn ensure_dir(dir: &Path) -> Result<()> {
} }
/// 本地 ASR 模型根目录:`<data_dir>/models/qwen3-asr/`。 /// 本地 ASR 模型根目录:`<data_dir>/models/qwen3-asr/`。
/// 子目录 = 模型 id(如 `qwen3-asr-0.6b`),存 antirez `download_model.sh` /// 子目录 = 模型 id(如 `qwen3-asr-0.6b`),存 qwen-asr `download_model.sh`
/// 列出的 5–7 个文件。 /// 列出的 5–7 个文件。
pub fn local_models_root() -> Result<PathBuf> { pub fn local_models_root() -> Result<PathBuf> {
let dir = data_dir()?.join("models").join("qwen3-asr"); let dir = data_dir()?.join("models").join("qwen3-asr");
+1 -1
View File
@@ -14,7 +14,7 @@ export interface LocalAsrSettings {
providerId: string; providerId: string;
activeModel: string; activeModel: string;
mirror: string; mirror: string;
/** macOS 才编入 antirez/qwen-asr 引擎;Win 端 UI 据此把"开始"按钮灰掉。 */ /** macOS 才编入 vendored Open-Less/qwen-asr 引擎;Win 端 UI 据此把"开始"按钮灰掉。 */
engineAvailable: boolean; engineAvailable: boolean;
} }