Skip to content

feat(asr): 新增「自定义 OpenAI 兼容」ASR 预设与高级参数(#854) - #898

Merged
H-Chris233 merged 2 commits into
Open-Less:betafrom
H-Chris233:codex/feat-openai-compatible-asr
Aug 3, 2026
Merged

feat(asr): 新增「自定义 OpenAI 兼容」ASR 预设与高级参数(#854)#898
H-Chris233 merged 2 commits into
Open-Less:betafrom
H-Chris233:codex/feat-openai-compatible-asr

Conversation

@H-Chris233

@H-Chris233 H-Chris233 commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

User description

背景

issue #854:Windows 用户无法稳定把 ASR 指到局域网自建 OpenAI 兼容端点(llama.cpp 等)。现有 Whisper 兼容预设都是命名厂商(whisper / siliconflow / zhipu / groq / stepfun / openrouter),各自带着厂商特异的硬编码开关,对“任意 OpenAI 兼容端点”不够通用。

改动

  • 新增 ASR 预设 openai-compatible(UI 名「自定义 OpenAI 兼容」):endpoint / model / api key 全部由用户填写,无默认值。
  • 行为默认最保守:multipart、不发 response_format、不分片,与服务端能力对齐。
  • 该预设提供两个高级选项(设置页,仅对通用预设显示):
    • verbose_json 开关(配合幻听过滤;自建服务不支持时保持关闭);
    • 分片时长(0 = 不分片,适合长录音或服务端单次请求时长受限)。
  • 高级配置按 provider 存于系统凭据 vault(账号 asr.advanced_config,JSON {"verboseJson": bool, "chunkDurationMs": number|null}),缺失/非法回落保守默认。
  • 命名厂商预设行为保持不变(whisper/groq 仍发 verbose_json,zhipu/openrouter 仍 30s 分片等)。

验证

  • cargo check --lib 通过;新增 4 个单测(默认保守行为 / 高级配置切换 / JSON 解析容错 / vault 写读往返)通过;whisper 相关 36 个回归用例通过。
  • 前端 tscvite build 通过;advancedAsrConfig.test.tsProvidersSection.test.ts 通过。
  • 说明:本 PR 只含上述改动,不含同一工作区中其它并行任务的未提交改动。

PR Type

Enhancement, Bug fix


Description

  • Add openai-compatible ASR preset for arbitrary OpenAI-compatible endpoints.

  • Allow empty API key, omitting Authorization to avoid 401.

  • Require endpoint/model for new preset with clear errors.

  • Add advanced verbose_json/chunking config with frontend UI and tests.


Diagram Walkthrough

flowchart LR
  ASR["Custom OpenAI-compatible ASR"] --> ADV["Advanced options (verbose_json, chunking)"]
  ASR --> NOAUTH["Optional API key, omit Authorization header"]
  ADV --> VAULT["Vault per-provider config"]
  ADV --> UI["Settings UI + i18n"]
  NOAUTH --> LAN["LAN llama.cpp transcription"]
Loading

File Walkthrough

Relevant files
Enhancement
7 files
whisper.rs
Optional Authorization header for empty API key                   
+62/-12 
providers.rs
Make ASR API key optional for custom preset                           
+11/-8   
coordinator.rs
Add advanced ASR config parsing and chunking                         
+166/-1 
asr_wiring.rs
Gate openai-compatible credentials and verbose_json           
+63/-2   
advancedAsrConfig.ts
Create advanced config parse/serialize helpers                     
+41/-0   
ProvidersSection.tsx
Add advanced options UI for custom ASR                                     
+115/-1 
shared.tsx
Add openai-compatible preset to ASR presets                           
+4/-0     
Configuration changes
2 files
credentials.rs
Register AsrAdvancedConfig credential account                       
+2/-0     
credentials.rs
Persist advancedConfig in ASR provider entries                     
+43/-2   
I18n
5 files
en.ts
Add English translations for custom ASR preset                     
+6/-0     
ja.ts
Add Japanese translations for custom ASR preset                   
+6/-0     
ko.ts
Add Korean translations for custom ASR preset                       
+6/-0     
zh-CN.ts
Add Simplified Chinese translations for custom ASR preset
+6/-0     
zh-TW.ts
Add Traditional Chinese translations for custom ASR preset
+6/-0     
Tests
2 files
advancedAsrConfig.test.ts
Test advanced config parsing behavior                                       
+64/-0   
ProvidersSection.test.ts
Test custom preset has no defaults                                             
+13/-0   

新增 openai-compatible 预设,可把 ASR 指向任意 OpenAI 兼容 /audio/transcriptions 端点(自建 / LAN llama.cpp 等),默认行为最保守(multipart、不发 response_format、不分片)。verbose_json 与分片时长按 provider 存于凭据 vault(asr.advanced_config),在设置页提供高级选项;命名厂商预设行为保持不变。附带后端解析/门控与前端解析纯函数单测。
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

(Review updated until commit 67fbb12)

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis 🔶

854 - Partially compliant

Compliant requirements:

  • Added a generic openai-compatible ASR preset with user-supplied endpoint/model/api key.
  • Empty API key is allowed and the Authorization header is omitted to support unauthenticated LAN endpoints.
  • Default request behavior is conservative: multipart, no response_format, no chunking.
  • Added per-provider advanced options for verbose_json and chunk duration, stored in the credentials vault.
  • Added Rust and frontend tests for parsing, serialization, and header omission behavior.

Non-compliant requirements:

  • The Foundry long-recording "only recorder callbacks, never transcribe" bug is not addressed.
  • The overwriting of externally edited preferences.json / forced active provider behavior is not investigated or documented.
  • The PR does not explicitly state whether LAN llama-server ASR is supported in the product docs, nor provide a roadmap.
  • No new handling or test was added for the llama.cpp transcript.text.done response shape; compatibility with that shape is not explicitly verified.

Requires further human verification:

  • End-to-end verification on Windows against a LAN llama-server/Qwen3-ASR endpoint.
  • UI configuration flow for the new preset, including save/commit order and whether the active provider is persisted reliably.
  • Long-recording behavior with and without chunking against the LAN server.
  • Whether the response parser handles {"type":"transcript.text.done","text":"..."} in practice.
⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Possible Issue

parse_advanced_asr_config accepts positive fractional chunkDurationMs values below 1 and floors them to Some(0). If the vault JSON contains {"chunkDurationMs":0.9} (for example via direct editing or a future client that does not pre-normalize), batch_asr_chunk_limit_ms returns Some(0), which is passed to split_pcm_by_duration and can cause a divide-by-zero or invalid chunking. The parser should reject values whose floored result is 0, e.g., by requiring ms >= 1.0 or checking ms.floor() > 0. The current UI normalizes before saving, so this only manifests with out-of-band JSON values.

chunk_duration_ms: value.get("chunkDurationMs").and_then(|v| {
    v.as_u64().filter(|ms| *ms > 0).or_else(|| {
        v.as_f64()
            .filter(|ms| ms.is_finite() && *ms > 0.0 && *ms <= u64::MAX as f64)
            .map(|ms| ms.floor() as u64)
    })
}),

@H-Chris233
H-Chris233 marked this pull request as ready for review August 3, 2026 14:39
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit f95baf4

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit 67fbb12

@H-Chris233
H-Chris233 merged commit b541018 into Open-Less:beta Aug 3, 2026
8 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant