Chris233
5be28b1dbd
feat(asr): 支持讯飞实时语音转写(RTASR)提供商(#842) ( #894 )
...
- 新增 asr/xfyun.rs RTASR 流式客户端:signa = Base64(HmacSHA1(MD5(appid+ts), apiKey))
鉴权,1280B/40ms 分块发送,{"end": true} 收尾,seg_id 去重拼接,断连 partial 兜底,
鉴权/限流/超时错误分类可读。
- 全链路接入:dictation 主流程、QA 面板、重新转录、取消、设置页凭据预检与
"验证"探活;凭据 xfyun.app_id / xfyun.api_key 存系统凭据库,不落日志。
- 设置页新增「讯飞实时语音转写」选项(AppID + API Key),5 种语言 i18n。
- 文档:docs/xfyun-asr.md + README/README.zh/USAGE 同步。
已知限制:标准版 RTASR 无请求级热词参数(在讯飞控制台配置),语种默认中文普通话。
2026-08-03 21:37:10 +08:00
TRIP and sim
a3d014b687
chore(release): prepare 1.3.15(正式版版本号)+ 修复 bump 脚本误改依赖版本 ( #893 )
...
* fix(scripts): bump-version.sh 锚定 [package] 段,避免误改依赖版本
* chore(release): prepare 1.3.15
---------
Co-authored-by: sim <sim@local>
2026-08-03 21:27:10 +08:00
Chris233
9bfec8aedd
fix(dictation): 转写中按热键改为静默丢弃,移除排队接力(#856) ( #891 )
...
- 删除「排队接力」grace 窗口(HOTKEY_QUEUE_GRACE_MS / is_queued_chain_press),
handle_pressed 的 Toggle/Auto 冷却检查恢复为纯 #545 拦截:识别中按下热键的
Pressed 在会话收尾后一律静默丢弃,不再放行开录下一条
- fail_dictation / 空转写收尾回 Idle 时补设冷却,失败/空结果场景排队按下同样丢弃
- 更新 session_cooldown_until 契约注释;新增回归测试 toggle_press_within_cooldown_is_dropped
2026-08-03 20:29:08 +08:00
f5fc5fd0c1
feat(android): expose advanced debug tools on mobile ( #890 )
...
* feat(android): expose advanced debug tools on mobile
Show Advanced debug tools on Android with mobile-friendly layout. Backend SAF log export already landed in #877 .
Co-authored-by: Cursor <cursoragent@cursor.com >
* fix(settings): 调试工具保留条数说明仅移动端展示,导出成功消息可换行并显示完整路径
---------
Co-authored-by: Cursor <cursoragent@cursor.com >
Co-authored-by: Chris233 <h-chris233@outlook.com >
2026-08-03 19:02:38 +08:00
Chris233
26cd012433
fix(llm): 官方 OpenAI gpt-5 系列不下发 temperature,修复验证 400(#857) ( #888 )
...
* fix(llm): omit temperature for OpenAI gpt-5 family (fixes #857 )
OpenAI 官方 gpt-5 系列在 Chat Completions 只接受默认 temperature=1,
传 0.3(内置默认)会被 400 拒绝,导致验证/润色全部失败。
官方渠道的 gpt-5* 不再下发 temperature,由服务端用默认值;
gpt-4 等其余模型保持原行为。custom 预设显式温度不受影响。
* fix(android): skip set_ignore_cursor_events on Android capsule path
Android 胶囊走原生 overlay,tauri::WebviewWindow 无
set_ignore_cursor_events(桌面专用),导致 Android cargo check
编译失败(E0599)。cfg 门控跳过,触摸交互由系统处理。
2026-08-03 16:21:14 +08:00
TRIP and sim
73e65a4077
chore(release): prepare 1.3.15-Beta.4 ( #889 )
...
Co-authored-by: sim <sim@local>
2026-08-03 16:05:44 +08:00
cd48a037a4
fix(less-computer): restore OpenCode model discovery and execution ( #840 )
...
* fix(less-computer): restore OpenCode model execution
* fix(capsule): gate set_ignore_cursor_events behind #[cfg(not(mobile))]
#887 introduced desktop-only capsule cursor-passthrough logic into the
unconditionally-compiled capsule_focus module, breaking Android cargo check
(E0599: no method set_ignore_cursor_events on tauri::WebviewWindow). Gate the
block to desktop; variables it consumes (classic_style, selection_polish,
visible) are still used on mobile outside the block.
---------
Co-authored-by: sim <sim@local>
Co-authored-by: Chris233 <h-chris233@outlook.com >
2026-08-03 16:03:41 +08:00
leaion and leaion
0063497c6e
fix(linux): fcitx5 plugin crash on empty triggerKeyList with raw hotkey ( #883 )
...
When the raw hotkey path (SetHotkeyRaw) matches and triggerRawStates==0,
the code unconditionally indexes triggerKeyList_[0]. With an empty
KeyList (common in raw mode), this is undefined behavior and segfaults
fcitx5 on every hotkey press. Only access the list when the KeyList
path matched; use raw values for the raw path.
Co-authored-by: leaion <leaion@sohu.com >
2026-08-03 14:54:30 +08:00
TRIP and sim
006cb78a93
feat(capsule): 录音胶囊样式可选(流光 Siri / Openless 默认)+ 修复 beta 构建破坏 ( #887 )
...
* feat(capsule): 录音胶囊样式可选——流光 Siri 风格 / Openless 默认风格
设置 → 录音与输入新增「胶囊样式」下拉(保留显示开关):
- UserPreferences.capsule_style('siri' 默认 / 'classic')随 capsule:state
payload 下发,切换后下一次录音即生效
- Capsule.tsx 双样式分支:siri 保留 SiriGL 光效舞台;classic 恢复 1.3.14
经典毛玻璃药丸(音量条 + 取消/确认按钮 + thinking 扫光 + 翻译徽章),
取消/确认改走 lib/ipc/dictation.ts,退出动画时长按样式区分
- 预览支持 ?style=classic;i18n 五语言新增胶囊样式文案
* fix(settings): 选区润色行适配重构后的 ShortcutRecorder props
beta HEAD 在快捷键录入交互重构(e12c8dcc)合并后遗留的构建破坏:
SelectionPolishSection 仍传已删除的 alignRecordButton / modifierPresets,
tsc 直接报错。改为新 API(value 可空 + onReset 恢复默认快捷键,
等价于旧「启用」按钮),恢复 beta 可构建状态。
* fix(capsule): 评审修复——经典药丸按钮可点 + 音频线程零偏好锁
- 经典样式(Openless 默认)下胶囊窗口长期 set_ignore_cursor_events(true)
(纯光效舞台时代遗留),✕/✓ 按钮实际不可点。现在主线程闭包按
「classic && visible && 非选区润色 && 录音/转写/润色中」关闭鼠标穿透,
终态 toast / 隐藏 / Siri / 选区润色保持穿透;值不变时不重复调用
set_ignore_cursor_events
- emit_capsule 在音频回调线程 ~30Hz 被调,原先直接读 prefs(整份克隆 +
偏好锁被 set() 跨磁盘 I/O 持有)。改为 Inner 上 AtomicU8 样式缓存,
主线程闭包每帧从 prefs 同步(与 show_capsule 同源读取),音频线程零开销
- 恢复「录音胶囊」行的 desc 文案(beta 原有,重构时被误删)
- Capsule.tsx 退出动画注释同步为按样式区分的常量名
* docs(capsule): 注明入场帧 payload 可能滞后一帧样式(防误改回音频线程读 prefs)
---------
Co-authored-by: sim <sim@local>
2026-08-03 14:17:52 +08:00
Shuo Zhang and Chris233
8a2febf59d
feat(windows): add selected-text polish workflow ( #851 )
...
* feat(selection-polish): add configurable selected-text workflow
* fix(mobile): 选区润色工作流仅桌面编译——修复 Android cargo check
PR #851 新增的 coordinator/selection_polish 模块无条件编译,但 selection
模块在 mobile 走 mobile_stubs/selection.rs stub,缺少 SelectionInsertionTarget
等桌面 API,导致 CI 的 Android cargo check 失败(E0432/E0425)。
选区润色是桌面(Windows-first)工作流,mobile 不注册全局热键、不做跨应用
替换。将相关代码统一加 #[cfg(not(mobile))] 门控:
- coordinator.rs: selection_polish 模块、Inner 字段、热键绑定方法
- hotkey_loops.rs: 选区润色热键 supervisor/bridge 函数与事件分支
- mobile_stubs/hotkey.rs: 移除桌面专属事件变体
- commands: set_selection_polish_hotkey 与 SettingsWriter 实现
本地验证:cargo check (Windows) 与 cargo check --target
aarch64-linux-android 均通过。
* chore: 剥离无关格式化改动与本地开发产物
按 review 反馈清理 PR diff 噪音,不改变任何行为:
- .gitignore: 移除作者本机诊断产物(/.codegraph/、/artifacts/、/baseline-*.log、
/cargo-*.log、/docs/notes/、+/),恢复 beta 原样
- package.json / scripts/tauri-dev-visible.mjs: 移除开发验收专用 npm script 与脚本
- 恢复被重排的既有代码格式(import 分组、match arm、函数签名、空行、
测试断言排版、serde 属性、mod 声明顺序等),仅保留功能改动
本地验证:cargo check (Windows) 与 --target aarch64-linux-android 通过,
cargo test --lib 885 passed(1 个环境相关的符号链接权限测试除外)。
* chore: 修正选区润色默认键注释与 dev 注入缩进
- types.rs: selection_polish_hotkey 注释从「默认右 Control」改为实际默认
Right Alt(与 default_selection_polish_hotkey 及前端保持一致)
- coordinator.rs: inject_hotkey_click_for_dev 修正 beta 遗留的多余缩进
* fix(selection-polish): 非 Windows 平台 fail-closed 禁用选区润色
macOS/Linux 尚未实现等效的前台窗口/焦点控件校验,无法保证云端等待
期间结果不会落到用户切换后的应用或控件上。此前非 Windows 分支的
selection_insertion_target_is_captured 直接返回 true,用户一旦在 macOS
手动启用该功能,会执行无校验替换,与「安全替换」的承诺相悖。
- selection.rs: 非 Windows 一律返回 false,阻止选区文本发给 provider
- SelectionPolishSection: 设置入口仅 Windows 显示(macOS/Linux 隐藏)
* refactor(style-pack): 移除过渡版本字符串启发式迁移
is_non_asr_selection_prompt / is_asr_style_prompt / is_generic_selection_prompt
是为从未合入 beta 的中间构建写的字符串匹配恢复逻辑('short-lived
pre-separation build'),属于 YAGNI 违反且匹配脆弱。删除恢复逻辑与
三个 helper,保留 v1 风格包 selection_prompt 为空的默认值填充;
对应测试同步为 migration_fills_empty_selection_prompts_with_style_defaults。
* feat(i18n): 选区润色 UI 文案国际化(5 语言)
新增 settings.selectionPolish / style.pack.selection* / capsule.selectionPolish /
selectionPolishPreview 四组 key,覆盖 en/ja/ko/zh-CN/zh-TW。SelectionPolishSection、
SelectionPolishPreview、Capsule 提示、Style 工作流切换等 UI 文案全部改用 t(),
消除新组件与项目其余部分的 i18n 混用。风格包模板默认值(数据)保留中文。
* fix(style): 恢复内置风格包编辑禁用(与 beta 行为一致)
PR 原改动把内置包的编辑按钮从禁用改为可编辑(行为变更未在描述中声明)。
按接手 review 意见恢复 beta 原样:内置包仅可查看/重置,不进入编辑器。
* fix(i18n): 补齐 Style 编辑器提示与「当前」徽章国际化
审查发现的收尾问题(子代理复核):
- 内联编辑器提示改用已存在的 selectionPromptHint / dictationPromptHint
- 编辑器头部改用独立 dictationPromptEditorDesc / selectionPromptEditorDesc
(保留「当前编辑…输入对象是…」原意,避免与内联提示同屏重复)
- 风格卡片「当前」徽章改用 style.pack.current
- ja: selectionPromptHint 语感修正(ASRは通らず → ASRは経由せず)
---------
Co-authored-by: Chris233 <h-chris233@outlook.com >
2026-08-03 13:37:41 +08:00
sim
dd90c13ae3
chore(release): prepare 1.3.15-Beta.3
2026-08-03 12:36:02 +08:00
sim
e6d3779e5d
fix(style-pack): 对账机制收窄为仅推进 prompt/version,补单元测试
...
- reconcile 不再整包替换:只推进官方 prompt 与 version,用户的名称/描述/示例/
tags/enabled 全部保留(审核意见)
- pack_version_newer 正确处理 pre-release 后缀(-beta 视为与正式版同级)
- 补 3 个单测:版本比较 / prompt-only 推进保留用户字段 / 等版本跳过 + 缺失补入
- 默认包 fallback(get_or_default_active)改为优先清晰结构;更新过期注释
2026-08-03 12:06:15 +08:00
sim
65d7438a77
feat(style-pack): 内置包版本对账机制——提示词升级随 app 更新推向所有用户
...
- load 时对内置包做版本对账:代码内置包 version 更高则用官方副本覆盖本地
(保留用户 enabled 设置),本地缺失的内置包自动补入
- 老用户的「清晰结构」包在升级后自动拿到 v3.0 Beta 提示词,无需手动重置
2026-08-03 12:06:15 +08:00
sim
23bfc4f1e5
feat(style-pack): 清晰结构 v3.0 Beta 提示词上线——人格化「语修」角色 + 场景优先级分型
...
- STRUCTURED_BUILTIN_PROMPT 替换为 Beta 版:语修人格(性格/身体/习惯/梦想/职责/能力/规则)
+ 场景优先级分型(操作指引/编程任务/模型资讯)+ ASR 术语纠错词表 + 反 AI 自述式表达约束
- 内置包版本 2.0.0 → 3.0.0;默认风格包由 light 改为 structured(与 UI 层一致)
- 同步 TS mock 的 structured prompt;polish 契约测试更新为新 Beta 锚点
2026-08-03 12:06:15 +08:00
sim
3244df1957
chore(i18n): 快捷键录入 / 本地模型 / 模型预设新增文案(zh-CN/en/ja/ko/zh-TW)
...
- 快捷键:comboResetBtn / comboMenuToggle / comboDisableHint
- 本地模型:sherpaModelWhisperLargeV3(+Desc)、modelSelect*、groupDownload/Other
- 模型预设:customModelLabel / presetListLabel
2026-08-03 11:15:30 +08:00
sim
9e3d017da4
feat(local-asr): 本地模型三板块重构 + 下拉选择 + Whisper Large V3
...
- 新增「模型选择」板块:SelectLite 下拉直接选用已下载模型(选择即激活)
- 板块重排:模型选择 / 下载与管理 / 其他(存储位置、Apple 语音)
- 新增 sherpa-onnx Whisper Large V3 多语模型(Rust 下载配置 + TS 目录 + mock)
- 同步 static_catalog_preserves_ui_order 期望列表
2026-08-03 11:15:30 +08:00
sim
d679c320b8
feat(asr-models): 云端 ASR 模型预设下拉——加入千问 qwen3-asr-flash 系列
...
- 百炼:qwen3-asr-flash(-realtime)、fun-asr 系列新版本、paraformer 预设
(filetrans 官方只接受公网 URL、后端显式拒绝,不放预设)
- Whisper 兼容厂商(whisper/groq/siliconflow/openrouter):通用预设下拉
- CredentialField 支持 options 下拉,含「自定义模型…」逃生口(切回输入框),
与既有 unifiedBailian 协议提示/onValueChange 机制融合
2026-08-03 11:15:30 +08:00
sim
a55ec3e330
refactor(coding-agent): Less Computer 后端/权限模式/模型选择改用官方 SelectLite
...
统一三平台下拉观感,替换原生 <select>(issue #418 同类问题);模型下拉保留
「默认」选项,可选回 CLI 默认模型
2026-08-03 11:15:30 +08:00
sim
c4290071b0
fix(update-button): 检查更新按钮旋转方向与尺寸稳定
...
- ol-spin 改为逆时针,与 refresh(ccw)图标方向一致
- 按钮固定宽度 + 内容居中;图标固定 14x14 容器,check/refresh 切换与旋转时不再跳动
2026-08-03 11:15:30 +08:00
sim
e12c8dcc20
feat(hotkey-recorder): 快捷键录入交互重构——主行按钮移除、箭头右对齐、动画优化
...
- 未展开主行只留键帽 + 下拉箭头,录入入口统一在展开菜单里(录制快捷键/重置/停用)
- 箭头恒靠最右;菜单展开/收起高度改为固定值动画,多次切换不再卡顿
- ShortcutRecorder 支持 value=null(问答/切风格/唤起 App 未启用时直接录入即启用)
- 菜单支持 Esc / 点击外部收起;滑动方向统一向右
- 新增 defaultDictationHotkey()(RightControl,与 mock/Rust 默认一致)
2026-08-03 11:15:30 +08:00
bigsong and Claude Fable 5
e484cd11cf
fix(less-computer): 「hotkey disabled」只在状态翻转时打一次——日志不再被它刷爆 ( #873 )
...
coding_agent_hotkey_supervisor_loop 每 5s 轮询一次绑定。没配 Less Computer
语音键的用户,每一轮都会落到同一条禁用分支并无条件打一行
「[less-computer] hotkey disabled」,稳定产出 720 行/小时。
实测一份连续跑了六天的 openless.log 里,这一行占了全部日志的 95% 以上
(132416 行中约 12.7 万行),文件涨到 11MB:真正有用的会话/错误日志被冲得
很难翻,日志轮转也被它提前触发,把更早的历史挤掉——排查线上问题时相当致命。
讽刺的是它刷得最凶的恰恰是没在用这个功能的人:一旦配上语音键,函数会走到
安装分支,反而一行都不打。
改成只在状态真正翻转成「禁用」时打一次,重新装上语音键时清掉标志位,
下次禁用仍会再打。禁用/启用的行为本身完全不变,只动日志。
Co-authored-by: Claude Fable 5 <noreply@anthropic.com >
2026-08-02 20:56:44 +08:00
浩彬(^-^)
2409036ca9
fix(android): use app cache for Marketplace installs ( #879 )
2026-08-02 20:22:22 +08:00
浩彬(^-^) and Cursor
7ad24d3e94
fix(android): persist prefs/logs under app filesDir ( #877 )
...
* fix(android): use app filesDir for persistence and SAF log export
Honor devices reject writes under /data/local/tmp, which caused PreferencesStore to fall back to openless_prefs_fallback.json and settings saves to fail. Initialize JNI storage roots before Coordinator and file logging, route logs to files/logs/openless.log, and export via ContentResolver for SAF content:// URIs.
* fix(android): guard StylePack empty asset_root and Android-only coord logs
Reject persisting style pack icons when asset_root is empty to avoid writing under cwd; no-op cleanup on empty root. Limit Coordinator persistence degrade log suffix to Android only. Add unit test.
Co-authored-by: Cursor <cursoragent@cursor.com >
---------
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-08-02 20:04:18 +08:00
浩彬(^-^) and Cursor
761e6c5c80
fix(android): sync mobile style group nav with desktop sidebar ( #864 )
...
Expose polish mode and marketplace via a bottom-sheet under the Style tab, matching desktop NAV_TREE grouping for issue #862 .
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-08-02 19:30:01 +08:00
Chris233
cfd99be1a9
fix(windows): 主窗口改为不透明——消除录音时 DWM/GPU 饱和导致的系统卡顿 ( #882 )
...
主窗口 transparent:true 使 WebView2 内容整页走 per-pixel alpha 分层合成,
但 UI 所有大面积层(winchrome/shell/sidebar/内容区)都是不透明背景,
Mica 磨砂实际被完全盖住,透明是纯 GPU 成本零视觉收益。
录音期间叠加胶囊 60fps WebGL + 30Hz 事件,DWM 被拖到饱和时:
- 其他软件合成帧送不出去 → 全局无响应
- 任务栏右键触发缩略图/重合成 → GPU 飙升
新增 tauri.windows.conf.json 平台覆盖(json_patch 数组整体替换,
必须完整列出窗口),main 窗口 transparent:false,capsule 保留透明。
macOS/Linux 不受影响。参考 tauri.linux.conf.json 既有先例。
2026-08-02 19:08:53 +08:00
浩彬(^-^) and Cursor
c498746599
fix(android): hide overview activity heatmap on mobile layout ( #863 )
...
Skip getActivityStats IPC and ActivityHeatmapCard when useMobileLayout()
is true so narrow screens avoid horizontal overflow and unnecessary work.
Closes #861
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-08-02 19:05:44 +08:00
8f49971874
feat(asr): support additional Bailian speech models ( #866 )
...
* feat(asr): support more Bailian speech models
* test(asr): cover final 8k downsample phase
* fix(asr): address review findings on Bailian model routing (#866 )
- route paraformer-8k-realtime / sensevoice-8k-realtime to classic WebSocket,
matching the 8k downsample naming space in bailian.rs
- downsample 16k->8k with pairwise averaging (first-order low-pass) instead of
pure decimation to reduce aliasing; drop the lone tail sample on flush
- cap async provider validation at 120s (60s poll) instead of 660s so the
validate button cannot block for nearly 11 minutes
- join async transcript segments with a space so latin-script words do not
get glued together
* fix(asr): drop qwen3-asr-flash-filetrans and harden async polling (#866 )
- qwen3-asr-flash-filetrans only accepts public URLs per DashScope docs; it
is incompatible with the local-recording temporary OSS + oss:// path, so
reject it in protocol_for_model / routing / picker / docs instead of
shipping a model that would silently fail on real recordings
- simplify async_transcription_body / extract_async_result_url to the
single fun-asr results shape
- add bounded retry (exponential backoff, deadline-capped) to async task
polling and result download so one transient network blip does not
discard an entire recording
* docs(asr): list qwen-audio-3.0-asr-flash as a synchronous model (#866 )
Merge with beta #876 brought qwen-audio-3.0-asr-flash into the model
picker and protocol routing; keep the model reference doc in sync and
note that -streaming variants are not supported.
---------
Co-authored-by: LauraGPT <lauragpt@users.noreply.github.com >
Co-authored-by: Chris233 <h-chris233@outlook.com >
2026-08-02 18:55:03 +08:00
Murphy
10ff058626
feat(asr): support qwen audio batch transcription ( #876 )
2026-08-02 11:53:53 +08:00
bigsong and Claude Fable 5
f0ffa8a873
fix(capsule): 入场帧强制重注册 Space 贴附——胶囊不再被 macOS 26 钉死在单个桌面 ( #875 )
...
实测(2026-07-31):macOS 26 会在运行中把胶囊窗口从「全 Space 贴附」剥离,
之后它表现为普通受管窗口:只在某一个桌面可见,其它桌面上听写全程无任何
UI 指示(后端 30Hz orderFrontRegardless 全部无效,窗口 onscreen=0,最长观测
到 78 秒无提示录音)。用户侧症状即「微信里不弹胶囊、Claude 里又有」——
实际按桌面分,不按 App 分。
CGS 取证 + 体外复现(CGSRemoveWindowsFromSpaces 人工制造同款坏状态)跑遍
四种救法,结论:
· 重写同值 273:无效(AppKit 同值写入是 no-op,这正是坏状态救不回的原因)
· 隐藏时翻转 0→273 再显示:无效(隐藏期间的值变化不触发重注册)
· 同一 tick 连写 0、273:无效(被合并)
· 窗口可见时发生 CanJoinAllSpaces 位 0→1 的转变:有效,完整恢复贴附
· 跨显示器移动:有效(与野外观察到的「外接屏上意外自愈」一致)
修法:入场帧(隐藏→可见)先以 272(保留 Stationary|FullScreenAuxiliary,仅去
CanJoinAllSpaces 位)上屏,30ms 后的下一个主线程 tick 写回 273——可见状态下
的位翻转强制 WindowServer 重新注册贴附。30Hz level 帧不参与。写前读当前值,
发现漂移打 warn 留证据。glow 窗口同款写法一并加固。
Co-authored-by: Claude Fable 5 <noreply@anthropic.com >
2026-08-02 11:53:17 +08:00
bigsong and Claude Fable 5
95edf8943b
fix(capsule): 润色/插入失败改走 error 态——失败提示不再被前端丢掉 ( #874 )
...
胶囊只在 error 态渲染 message,done 态按设计是「冻结光效淡出、不带文字」
(Capsule.tsx 的 VoiceOrbStage:`state === 'error' && <span>{message}</span>`)。
但 end_session 无论成败都发 CapsuleState::Done。于是 default_done_message
辛辛苦苦拼出来的失败文案,全部在前端被丢弃——算了、传了、不显示。
最典型的受害者是润色失败。它会静默回退成未润色的原文,而胶囊照常显示成功态,
界面上没有任何痕迹。实测后果:LLM 凭证失效后连着十几个小时每一句都在拿原文,
用户只能靠「今天出来的字怎么变笨了」察觉,而日志里其实每一句都 ERROR 了。
default_done_message 的注释写着「polish 失败优先告知用户」——意图是对的,
只是这句话从来没能显示出来。
改成按结果选状态:润色失败 / 插入失败 / TSF 未上屏走 error 态,成功仍走 done。
文案生成逻辑一行没动,只改用哪个状态发出去。
Co-authored-by: Claude Fable 5 <noreply@anthropic.com >
2026-08-02 11:53:01 +08:00
LeiYang and Chris233
83c4f7f4e9
feat(asr): 火山引擎 ASR 新增方舟 API Key 鉴权模式 ( #867 )
...
* feat(asr): add Ark API Key auth mode for Volcengine ASR
* refactor(asr): isolate Ark API Key slot and harden auth-mode handling
Review fixes for #867 :
- P2-1: store Ark API Key in a dedicated volcengine.api_key slot so the
two auth modes never pollute each other's credentials
- P2-2: unify per-mode credential validation in
VolcengineAuthMode::auth_ok (single trim semantics across open_session /
overview configured status / dictation preflight)
- P2-3: await auth-mode persistence on switch, roll back with a failure
toast when the write fails
- P2-4: add unit tests for endpoint/header selection per mode and
per-mode credential validation
---------
Co-authored-by: Chris233 <h-chris233@outlook.com >
2026-08-02 00:58:46 +08:00
5fd353de93
fix(asr): WebSocket 建连补超时——握手挂死不再让热键彻底失灵 ( #871 )
...
三个实时 ASR 供应商的 `connect_async` 没有任何超时:握手打到一半断网、公司网关 /
酒店门户静默丢包、服务端黑洞掉连接,这个 await 就永远不返回。
后果不止是「这次听写失败」。`open_session` 是在 hotkey bridge 线程上 `block_on` 等的
(那条 bridge 为修 #468/#475 的 latch 竞态改成了串行,一次只处理一个事件),所以它
一挂,后续的按下 / 松开全都排在 channel 里没人取:
- 按热键开不了录音,也停不了
- 没有任何提示,胶囊不出现,用户只会以为「热键坏了」
- 只能退出 app 重开
真机复现(dogfood 构建,StepFun 实时):9 秒内连续开 6 次会话,第 6 次的建连没返回,
之后热键完全无响应。抓栈确认 `openless-hotkey-bridge` 停在 `__psynch_cvwait`,而
主线程与 tap 线程都正常 —— tap 仍在记录按键,只是没人处理。
四个 WS 供应商里只有 volcengine 当初写对了(CONNECT_TIMEOUT + 有限重试),另外三个
一直是裸 connect_async:
- asr/stepfun_realtime.rs
- asr/qwen_realtime.rs
- asr/bailian.rs
各自补 `CONNECT_TIMEOUT`(5s,与 volcengine 取值一致),超时报
`ConnectionFailed("连接超时(N ms)")`,走既有的错误路径弹胶囊,热键继续可用。
注意与 SESSION_READY_TIMEOUT 的区别:那个管「连上之后等 session.updated 回应」,
本次补的是「连上」本身,之前完全没人管。
只改「连不上」这条已经坏掉的路径,连得上时行为不变。加了一个用例:服务端收下 TCP
但永不完成握手时,open_session 必须有界返回而不是永远挂着。
深层问题(不在本 PR):把 `begin_session` 从串行 hotkey bridge 上挪开,这样任何一个
慢 IO 都不该有能力冻住热键。那处改动风险高(串行正是为修 latch 竞态而来),单独立项。
Co-authored-by: Claude Opus 5 <noreply@anthropic.com >
Co-authored-by: Chris233 <h-chris233@outlook.com >
2026-08-01 23:39:51 +08:00
1ab2d52484
fix(asr): StepFun 实时转写不再随机白等 12 秒(13% 的听写中招) ( #870 )
...
* fix(asr): StepFun 实时转写不再随机白等 12 秒
真机日志(1059 次听写)统计「松手 → 出结果」耗时:中位数 0.87s,但有 117 次
精确落在 12.00~12.97s,8~12s 之间一次都没有 —— 是硬超时兜底,不是网络抖动。
按 provider 拆分后只有 StepFun 中招:117/887 = 13.2%,bailian / qwen3-realtime
/ siliconflow 合计 0/172。三条语音链路(听写、语音问答、重新转录)共用同一个
send_last_frame,全都受影响,且全程不打任何日志。
根因在收尾状态机:
1. contains_non_silent_pcm 用「任意字节 != 0」判静音。麦克风底噪(实测
RMS≈0.0008)每个采样都非零,于是尾帧永远被判成「有真实语音」,把
last_non_silent_audio_written_at 刷到最后一个 completed 之后 ——
has_audio_after_last_completed 从此恒为真。
2. 宽限任务只在 FINISH_GRACE 到点查一次。这一次因上述原因不通过,就再没有第二
次机会;而尾帧写出后客户端不再发音频,服务端也不会再吐事件(松手前停顿
≥VAD 阈值时,最后一段的 completed 早在尾帧之前就到了),于是一路干等到
FINAL_RESULT_TIMEOUT。12s 后带着 partial 成功返回,所以文字是对的、也没报错。
对应三处修改:
- contains_non_silent_pcm 改成按振幅判(>1% 满量程、且至少 8 个采样,避开单点
尖峰),并且只对尾帧的真实录音部分求值,补的静音不参与。
- 宽限任务改成有界轮询(100ms 一次)。收尾前提保持不变 —— 「无未关句段 + 最后
一个 completed 之后没有未确认的真实音频」那条保护是对的,不为修卡顿而丢;
新增 FINISH_HARD_DEADLINE=3s 兜住服务端始终不关句段的情况。
- 12s 兜底分支补 log::warn!,这种事不该在日志里完全隐身。
行为学佐证:卡顿会话录音末段峰值 RMS<0.005(已静音)的占 52%,正常会话仅 16%;
卡顿会话原始转写 66% 以「嗯。」结尾,正常会话仅 8% —— 都指向「说完停顿一下再
松手」这个触发条件。
测试:三条新增测试在修复前均失败(两条重现 12.00s 卡死),修复后 19/19 通过。
既有的 non_silent_audio_waits_for_delayed_vad_before_finishing 保持通过,只换了
夹具([1u8; N] 在新判据下属于底噪,改用语音级振幅)。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
* fix(asr): avoid empty StepFun hard-deadline success
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com >
Co-authored-by: Chris233 <h-chris233@outlook.com >
2026-08-01 23:01:39 +08:00
93c6f6f448
fix(hotkey): 修饰键热键被当组合键用时不再误唤起听写(150ms 仲裁 + 即时撤销,胶囊不再赖着) ( #858 )
...
* fix(hotkey): 修饰键热键被当组合键用时不再唤起听写
modifier-only 触发键(如 Option)按住期间只要按下普通键,就说明用户在打
Option+任意字母/数字键这类组合键、不是想说话。此前 tap/hook 只看修饰键自身的边
沿:按下即 Pressed → 开录音,Auto 模式下快速松手还会被判成「短按锁存」,录音一直
开着停不下来。
- hotkey.rs:macOS CGEventTap / Windows WH_KEYBOARD_LL 在触发键按住期间收到普通
键 KEY_DOWN 时发一次新的 TriggerCombined 边沿(每次按住只发一次;修饰键叠加不
算,Shift 翻译修饰键行为不变),之后仍照常发 Released。
- coordinator:TriggerCombined 清掉按住态(随后的 Released 被 was_held 吞掉,不
再走 Hold 松手 / Auto 短按锁存),并只取消「这一次按下开出来的」会话 ——
toggle 停止 / 被冷却拦下 / 路由给 QA 的按下什么都不动,绝不误杀正在转写的上一
条。同时清掉冷却与防抖时间戳,紧接着那次真想说话的按下不会被静默吞掉。
- Less Computer 的 modifier 触发键走同一条撤销路径(顺带熄灭整屏描边)。
* feat(hotkey): 修饰键热键加 150ms 组合键仲裁窗口——组合键不再闪一下胶囊
上一提交是「事后撤销」:Option+任意字母/数字键仍会真的开麦、弹胶囊、烧一次 ASR
建连,然后被取消——功能对了,观感上闪一下。
改为按下后先等 150ms 再开会话,期间监听器若已报告叠加了普通键,这次按下整条作废:
麦克风不开、胶囊不弹。等待只加在 modifier-only 触发键的「开录」分支上:
- 自定义组合键(Cmd+Shift+D)本身没有歧义,不等;
- toggle 停止 / QA 路由 / 防抖与冷却判定都在窗口之前,时序不变(尤其不挤占
is_queued_chain_press 的 120ms 排队接力窗口);
- 窗口没盖住的慢速组合键(按住 Option 半秒再按 Tab)仍由 TriggerCombined 事后撤销
兜底,两条路径互补。
150ms 覆盖绝大多数组合键的「修饰键→普通键」间隔,又低于人从按键到开口的反应时间,
不吃首字。
* fix(hotkey): 补上移动端 hotkey stub 的 TriggerCombined / trigger_combined_since_press
Android CI(cargo check --target android)红:`mobile_stubs/hotkey.rs` 是移动端的
HotkeyEvent / HotkeyMonitor 替身,桌面端新增的枚举变体与方法必须同步补上,否则
coordinator 里那两条 match 臂和仲裁窗口的调用在移动端找不到符号。
移动端没有键盘监听器,`trigger_combined_since_press` 恒为 false(仲裁窗口等价于
直接放行)。
* fix(hotkey): 组合键撤销改走独立通道——按下第二个键的那一帧胶囊就消失
150ms 仲裁窗口只盖住了「修饰键→普通键」间隔够快的组合键;慢一点的(实测日志里
press→combined 中位数 428ms)仍然走事后撤销兜底,本该只是「闪一下」,实际却晚了
几百毫秒才收——观感上像录音真的起来了。
原因不是手速,是排队:TriggerCombined 和 Pressed/Released 挤同一条 channel,而
bridge 为修 #468/#475 的 latch 竞态改成了串行 block_on —— 这次按下自己的
begin_session(开麦 + ASR 握手)正卡在 bridge 线程上,撤销只能在队列里等它跑完。
日志里那个 428ms 因此是上界,真实键间隔被 begin_session 的耗时撑大了。
修法同 #853(Esc 取消):把撤销从 HotkeyEvent 枚举拆出,tap/hook 回调改发独立的
Sender<()>,由专用 combo-abort-bridge 线程消费。macOS CGEventTap 与 Windows
WH_KEYBOARD_LL 同步修改;Less Computer 的修饰键 monitor 走同一条通道,只是 handler
换成 cancel_less_computer_press。
并发面两处:
- 撤销与 Released 之间不再有先后保证。改为只看 hotkey_press_began_session
(每个 Pressed 边沿都会重置)判断要不要撤销,不再看 hotkey_trigger_held ——
否则 Released 抢先跑完清了按住态,撤销会认不出这条会话是自己开的、放着不管,
Auto 模式下就是一条停不下来的录音。清 hotkey_trigger_held 只为吞掉后面的
Released,与撤销与否无关。加了回归测试覆盖这个倒序。
- 撤销落在 begin_session 还在 await 的中途,由既有的
startup_race_status_for_starting / CancelRaced 检查点接住(audit HIGH #1),
与 Esc 取消同一条路径。
至此两条路径分工:仲裁窗口内的快组合键胶囊根本不出现;窗口没盖住的慢组合键胶囊
冒个头,按下第二个键即刻消失。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
* fix(hotkey): 取消先收胶囊再拆麦克风 + watchdog 碎觉——撤销不再拖着胶囊一秒
上一提交把撤销决策做到了「按下第二个键的那一帧」(tap → handle_trigger_combined
实测 0.14ms),但真机日志显示胶囊仍然晚 0.8~1 秒才消失。11 次组合键测试分成干净的
两组:这次按下已经把 recorder 开起来的 5 次是 769/900/931/965/986ms,还没开起来的
6 次是 0ms。
链路:cancel_session 先 stop_recorder_for_session 才 emit_capsule。
Recorder::stop() 要 join 音频线程,音频线程退出前要 join liveness watchdog,
watchdog 睡在 1000ms 的检查间隔里 —— 所以停采要等它当前那一觉睡完,胶囊排在后面。
按 Esc 取消走同一个函数,同样拖一秒。
两处一起改(缺一不可,见下):
- coordinator/dictation.rs:cancel_session 把 UI 收尾(finish_cancel_session_state
→ emit_capsule → schedule_capsule_idle → 熄描边)整块提到三个拆资源调用之前。
emit_capsule 仍排在 finish_cancel_session_state 之后 —— 它要读 state.voice_agent /
phase 拼 payload,再提前会发出「还在进行中」的那一帧。拆资源都按 session_id 取,
不依赖 phase,挪到后面语义不变。
- recorder.rs:watchdog 的一个检查间隔切成 50ms 的碎觉来睡,每觉醒来重看 stop_flag。
停采的等待从最坏 1000ms 降到 50ms。判据(回调静默 3s / 首帧 5s)用的是真实时间差
而非醒来次数,灵敏度不变;代价是录音期间线程多醒几次,醒来只读一个时间戳。
为什么必须成对:UI 先收之后,胶囊消失到麦克风真正释放之间有一段窗口,其间旧
recorder 还占着设备。`Recorder` 没有 Drop 停采,recorder 槽被下一条会话覆盖后旧音频
线程会继续跑、抓着麦克风不放。只做第一处会把这段窗口留在 ~1 秒,紧接着那次真想说话
的按下(最快也要 250ms 防抖 + 150ms 仲裁)就可能撞上;watchdog 切碎后窗口降到几十
毫秒,够不着。
副作用:正常说完话松手结束(end_session 也走 Recorder::stop())同样少等将近一秒。
可观察的代价:胶囊消失后系统菜单栏的录音小圆点会多亮几十毫秒。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
* fix(hotkey): resolve modifier combo abort after beta merge
* test(hotkey): initialize mac companion press id
* fix(hotkey): clone Linux fcitx combo channel
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com >
Co-authored-by: Chris233 <h-chris233@outlook.com >
2026-08-01 18:01:11 +08:00
52e6ef46fc
feat(hotkey): 会话激活期间 Esc 由 OpenLess 独占——不再透传宿主应用 ( #855 )
...
* feat(hotkey): 会话激活期间 Esc 由 OpenLess 独占——不再透传宿主应用
胶囊转圈时按 Esc,取消会话的同时宿主应用也收到了这个键——一次按键双重
生效(如顺带取消了 Claude 正在生成的回复)。参照输入法的交互模型:组合窗
激活时 Esc 只取消候选词、宿主应用收不到;胶囊显示进行中的会话时同理,
Esc 的语义就是「取消它」,应当被独占消费。
实现:hotkey.rs 新增进程级 ESC_EXCLUSIVE 旗标,由 emit_capsule(所有会话
状态变化的单一出口)维护——胶囊为 Recording/Transcribing/Polishing 且
dictation phase 非 Idle 时置位,终止帧清除。phase 条件排除 QA 会话:QA
的 Esc 由聚焦的浮窗窗口处理,吞键反而会挡掉。置位期间 macOS active tap
对 Esc keydown 返回 null 删除事件,Windows 底层钩子返回 LRESULT(1)——
Windows 本就吞听写触发键,Esc 是补齐同一语义。keyup 不吞:宿主应用几乎
都在 keydown 响应 Esc,孤儿 keyup 无害。
依赖 #853(独立取消通道):没有它,Processing 期间吞掉的 Esc 无法真正
触发取消,会变成「吃掉按键但什么都不做」。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
* test(hotkey): esc-exclusive 判定提取纯函数并补表格测试
- 提取 esc_exclusive_for_capsule(state, phase) 纯函数,emit_capsule 调用
- 表格测试覆盖:进行中胶囊非 Idle 独占、终止帧清除、QA(phase=Idle)不独占
* fix(hotkey): 修复 voice agent 审批挂起 Esc 失效与胶囊双 emit 竞争
- maybe_request_approval 增加取消感知:会话取消(Esc)时按 Deny 处理并清理
审批注册表,避免审批挂起期间 Esc 被独占吞掉却毫无效果(PR #855 场景)
- run_voice_agent_transcript 审批后若会话已取消,强制结果为 Cancelled,
避免把第一轮拦截文本当 Done 收尾
- schedule_capsule_idle 记录触发时胶囊状态,到点时若期间有更新的 emit 则
跳过隐藏(旧 schedule 不再覆盖新状态,消除取消路径双 emit 竞争)
- 补测试:审批取消感知、schedule 新旧状态竞争
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com >
Co-authored-by: Chris233 <h-chris233@outlook.com >
2026-08-01 12:30:10 +08:00
b16950d13e
fix(hotkey): Esc 取消改走独立通道——修复转写/润色期间按 Esc 停不下来 ( #853 )
...
* fix(hotkey): Esc 取消改走独立通道——修复转写/润色期间按 Esc 停不下来
#798 给 end_session 加了「转写 vs 取消」的 select! 赛跑,但取消信号本身
送不进来:hotkey bridge 线程为修 #468/#475 的 latch 竞态改成了串行
block_on,Hold 松手后 end_session 在 bridge 线程上同步跑完整段转写 +
润色,期间 Esc 的 HotkeyEvent::Cancelled 只能在同一条 channel 里排队。
流程跑完后才被取出,此时 phase 已回 Idle,cancel 变 no-op —— 用户按
Esc 毫无反应,必须干等转写完成。听写键为修饰键(默认 fn 等)时必现;
组合键用户不受影响(按键事件走 ComboHotkeyMonitor 的独立线程),这也是
#798 验证时没暴露的原因。
修法:把 Cancelled 从 HotkeyEvent 枚举拆出,Esc 在 tap/hook 回调里改发
独立的 Sender<()>,由专用 esc-cancel-bridge 线程消费并调 cancel_session
(纯同步快路径,不 await)。cancelled 旗标即时置位,end_session 的
select! 赛跑与润色流的 should_cancel 立即命中。macOS CGEventTap 与
Windows WH_KEYBOARD_LL 同步修改;Less Computer 修饰键 monitor 的 Esc
转发同样迁移。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
* fix(hotkey): esc-cancel-bridge 启动失败留 error 日志并补单元测试
- spawn_esc_cancel_bridge 线程创建失败时 log::error,避免 Esc 取消静默失效
- 新增 esc_cancel_bridge_loop 单元测试:Processing 阶段置 cancelled、
录制快捷键期间跳过、重复信号幂等
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com >
Co-authored-by: Chris233 <h-chris233@outlook.com >
2026-07-31 21:47:59 +08:00
Duyi-Wang and Chris233
f4bb056fe6
fix(llm): make custom provider temperature configurable ( #849 )
...
* fix(llm): make custom temperature configurable
Allow custom OpenAI-compatible LLM providers to omit or override the temperature parameter instead of always sending the default 0.3.
- Send temperature only when configured in OpenAI-compatible chat bodies
- Keep built-in OpenAI-compatible providers on the existing 0.3 default
- Store active custom LLM temperature via ark.temperature
- Expose a Temperature field for custom LLM providers in Settings
- Reuse the same temperature config for provider validation and real polish
- Add tests for omitted/configured temperature and input validation
* docs(settings): show custom temperature range
* fix(llm): honor temperature for legacy custom providers
* fix(llm): harden custom temperature handling
---------
Co-authored-by: Chris233 <h-chris233@outlook.com >
2026-07-22 08:52:09 +08:00
Shawn Shen
8263f6f46f
fix(asr): resolve Bailian ASR validation deadlock and surface error details ( #848 )
2026-07-21 22:26:28 +08:00
nb213 and binyangzhu000-sudo
ef66246623
Add Atlas Cloud LLM preset ( #844 )
...
Co-authored-by: binyangzhu000-sudo <224954946+binyangzhu000-sudo@users.noreply.github.com >
2026-07-21 21:56:40 +08:00
TRIP
857ea6dc21
Merge pull request #839 from Open-Less/release/1.3.15
...
fix(updater): prepare 1.3.15-Beta.2 after repository transfer
2026-07-21 18:43:09 +08:00
aeoform and Chris233
a1fe38730d
fix(history): 音频播放改用 Blob URL + 导出走 Tauri dialog save ( #841 )
...
* fix(history): 音频播放改用 Blob URL + 导出走 Tauri dialog save
* fix(history): 给 export_audio_recording 加最小目标路径校验
* fix(history): export 对话框移到后端,消除 IPC 路径注入风险
* fix(history): 将阻塞 dialog 调用移入 spawn_blocking 避免阻塞异步线程池
* fix(history): support mobile export destinations and blob cleanup
* fix(history): use mobile fs handles for recording export
* fix(build): sync mobile fs dependency lockfile
---------
Co-authored-by: Chris233 <h-chris233@outlook.com >
2026-07-20 15:51:41 +08:00
sim
6b33ba21e0
fix(updater): prepare 1.3.15-Beta.2 after repository transfer
2026-07-19 00:11:34 +08:00
75918e0338
fix(android): protect credentials with Android Keystore ( #838 )
...
* fix(android): protect credentials with Keystore
* fix(android): harden credential migration
* fix(android): recover sanitized credential migration
* fix(ci): target Android x86_64 test variant
* fix(ci): prepare Tauri Android test project
* fix(ci): set Tauri Android build metadata
* fix(ci): isolate Android Keystore test build
* fix(ci): generate Tauri Android Kotlin bridge
* fix(android): harden credential persistence
* fix(android): preserve credential migration roots
* fix(android): recover verified credential writes
* fix(android): clear unrecoverable credential recovery
* fix(ci): accept CRLF in Android credential contract
---------
Co-authored-by: Codex <codex@openai.com >
Co-authored-by: baiqing <lbx12309@icloud.com >
Co-authored-by: Chris233 <h-chris233@outlook.com >
2026-07-18 18:13:34 +08:00
bigsong and Claude Fable 5
ce3fd8d847
feat(llm): 新增 StepFun 供应商预设;修复 Codex OAuth 默认模型不可用 ( #836 )
...
StepFun 预设:
- 供应商下拉新增 StepFun(阶跃星辰),预填 https://api.stepfun.com/v1 ,
默认模型 step-1o-turbo-vision(无思考,流式首字 ~0.3s;step-3.x-flash
系列为推理模型且思考无法关闭,正式内容需等隐藏思考结束,不适合听写润色)
- thinking 控制表新增 stepfun → reasoning_effort(开=medium / 关=low),
base_url 兜底识别同步支持"自定义"preset 接入 StepFun 的场景
- 补默认 endpoint 映射与两个单元测试
Codex OAuth 默认模型:
- gpt-5.3-codex-spark 对 ChatGPT 账号会被后端 400 拒绝
("model is not supported when using Codex with a ChatGPT account"),
导致每次润色静默失败回退原文;默认与占位改为实测可用的 gpt-5.5
Co-authored-by: Claude Fable 5 <noreply@anthropic.com >
2026-07-18 15:43:39 +08:00
17fe67c59b
feat(asr): 接入阶跃星辰 StepAudio 2.5 批式转写,词典走一等 hotwords ( #835 )
...
* feat(asr): 接入阶跃星辰 StepAudio 2.5 批式转写,词典走一等 hotwords
StepFun 的 /v1/audio/transcriptions 是标准 OpenAI multipart(实测 2026-07),
归入 Whisper 兼容路由即可,无需新协议客户端:
- 前端新增 stepfun preset(https://api.stepfun.com/v1 + stepaudio-2.5-asr),
i18n 五语言补齐;
- StepFun 静默忽略 prompt(实测返回 200 但不参与偏置),词汇偏置走专门的
hotwords 参数(JSON 数组字符串)——WhisperBatchASR 新增 with_hotwords,
wiring 按 whisper_uses_hotwords 在 prompt / hotwords 间二选一路由,
用户词典在 StepFun 下才真正生效;
- response_format 只认 json/text → verbose_json 幻听过滤保持关闭;
- 修「验证」按钮对静音敏感厂商的假阴性:探针是 250ms 纯静音,StepFun 返回
400 "no speech found",但走到内容拒收说明鉴权(错 key=401)与模型名
(错模型=404)均已通过——判为验证成功而非凭据错误。
实测矩阵(真实 key 逐项打点):真实语音+json/缺省/带 prompt/带 hotwords 均
200 且转写准确(5.5s 音频约 0.95s 往返);verbose_json 400;静音 400
no speech;错 key 401;错模型 404。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
* feat(asr): StepFun 流式实时转写——同一入口按模型名自动切协议
照百炼「一个入口、按模型二次路由」的先例:stepfun preset 下模型填
`stepaudio-2.5-asr-stream`(或任意 `*-stream`)即走新的实时 WS 客户端
(asr/stepfun_realtime.rs),其余模型维持批式 Whisper 兼容路径。凭据与
「已配置」判定仍看真实 active `stepfun`,一把 key、同一个 https base
(wss URL 由 client 自动派生)。
协议与 qwen_realtime 同为 OpenAI Realtime 风格,但四处关键差异
(2026-07-16 真实接口逐项实测确认):
- 模型在 session.update 里传(session.audio.input.transcription.model),
session 配置是 audio.input.{format,transcription,turn_detection} 嵌套形状;
- delta 的 text/stash 是「已确定前缀 + 未定尾巴」的拼接关系(Qwen 是互斥取一);
- 没有服务端结束事件:session.finish 回 transcript.response.error,server_vad
下 commit 被静默忽略。收尾靠客户端状态机:松手时冲刷尾音频 + 补送 700ms
静音帧逼 VAD 关段,finishing && open_segments==0 即完成;纯静音会话由
1.2s 宽限期兜底空文本成功(验证按钮复用该路径,全程 ~2s);
- 实时协议词汇偏置走 transcription.prompt(批式恰相反只认 hotwords),
词典按 effective 协议自动选通道。
真机端到端(真实 key、模拟 recorder 时序):session 建立 186ms,
松手→最终结果 251ms(批式 ~950ms),5.5s 中文语音转写一字不差,
多句段 VAD 断句由 join_segments 复用 qwen 拼接规则。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
* fix(asr): wait for StepFun tail frame before finishing
* fix(asr): guard StepFun completion before tail ack
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com >
Co-authored-by: Chris233 <h-chris233@outlook.com >
2026-07-18 15:43:24 +08:00
Chris233
07f394f5a2
fix: include selected text in follow-up context ( #830 )
...
* fix: include selected text in follow-up context
* fix: address selection follow-up review
* chore: preserve beta CI workflow
* fix: invalidate closed QA turns
* fix: guard voice QA sessions
* fix: reset QA session tokens on reopen
* fix: guard stale QA end cleanup
2026-07-16 13:27:12 +08:00
842ae5fd64
feat(history): 详情页展示 ASR/LLM 模型与分步耗时,可观测的模型表现对比 ( #826 )
...
* feat(history): 详情页展示 ASR/LLM 模型与分步耗时,复制按钮归位润色卡片
- DictationSession 新增 asr_provider/asr_model/llm_provider/llm_model/asr_ms/polish_ms
六个可选字段(旧 JSON 兼容);听写收尾实测写入,Raw 直通不记 LLM 字段
- asr_ms 量松键后等待转写结果的时间(流式=收尾延迟,批式=完整转写);
polish_ms 量 LLM 润色/翻译调用耗时
- 失败/空转写历史也记 ASR provider/model,方便模型对比排查;
手动重转用当前 ASR 覆盖并清掉旧润色信息
- 详情页底部改为 识别/润色/插入 三行流水线明细;录音时长加「录音」前缀
并给识别行加悬停说明,避免与分步耗时混淆
- 润色卡片复制按钮移入卡片头部,与原文卡片对称
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
* fix(history): 模型归因改为构建时快照 + 亚秒毫秒精度 + volc resource id(review 反馈)
按 #826 review 逐条修复:
1. ASR/LLM 归因改为构建时快照。store_asr_for_session 新增 AsrCallLabel 参数
(编译期强制 11 个构建分支交出快照),记录实际构建的具体协议 id(含统一百炼
重定向后的 effective id)与归一化后的模型/alias;LLM 侧 polish 链路新增
llm_call 出参,在成功构建 provider、即将发起真实调用时才填充。会话中途改
设置不再污染归因。
2. preflight 失败不再产生伪数据。凭据缺失等构建失败时 llm_call 保持 None,
不落 llm_*/polish_ms;polish_ms 也只在真实调用发生时记录。
3. 步骤耗时 <1s 显示整数毫秒(durationMillis,5 语言包),流式收尾几十毫秒
不再被 0.1s 精度拍平。
4. Volcengine 经 allowlist(volc. 命名空间、ASCII 安全字符集、≤64)脱敏后
把 resource id 落入 asr_model,支持 issue #373 的引擎对比排障;异常值不落盘。
5. 识别行提示改用可聚焦 Tooltip 组件(hover/键盘 focus/触摸三通道 + 虚线下划线)。
6. 重转录命令改用 retranscribe_pcm 返回的构建时快照;抽出 apply_retranscription
纯函数。新增测试:旧 JSON 缺字段反序列化、camelCase 序列化、volc allowlist、
重转覆盖归因并清 LLM 字段、Raw 直通不产生调用快照。
UI 验收:深/浅色 × 桌面/移动 × 超长 provider/model 换行 × 键盘聚焦均通过
(mock 数据已扩成对应压力画像)。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
* fix(history): preserve actual model timing attribution
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com >
Co-authored-by: Chris233 <h-chris233@outlook.com >
2026-07-16 11:56:00 +08:00
TRIP and sim
033eb5e731
chore(release): prepare 1.3.15-Beta.1 ( #834 )
...
Co-authored-by: sim <sim@local>
2026-07-15 17:22:02 +08:00
TRIP and sim
3143892daa
fix(updater): discover modern Beta release tags ( #833 )
...
* fix(updater): discover modern Beta release tags
Recognize the current -Beta.N-tauri convention while preserving historical lowercase beta tags so desktop and Android resolve the newest prerelease manifest.
* fix(updater): validate complete modern Beta tags
Reject malformed modern feed tags before they can preempt a valid release, while preserving the historical lowercase beta suffix.
---------
Co-authored-by: sim <sim@local>
2026-07-15 16:51:02 +08:00
664633f32d
fix(asr): Apple Speech 保留停顿分段,修复停顿前内容丢失 ( #824 )
...
* fix(asr): Apple Speech 保留停顿分段,修复停顿前内容丢失
Apple 设备端识别会在语音停顿处把音频切成多个话段(utterance),每个话段
的回调只携带该话段的文本,随后 partial 从空重新累计;isFinal 通常只跟着
最后一个话段出现。原实现「拿到第一个 isFinal 就返回」,导致停顿之前的所有
话段被整段丢弃——用户表现为「说话中途停顿思考,前面说的全没了」。
- 新增 SegmentAccumulator:以 speechRecognitionMetadata 非空作为话段边界
信号,逐话段落袋,结束后按 CJK / 空格语言规则拼接返回。
- 结束判定由「见到第一个 final 就收工」改为轮询 SFSpeechRecognitionTask
.state == completed(辅以 isFinal 后静默的后备条件),兼容部分系统按话段
多次发 isFinal;completed 后留 250ms 收尾窗口,避免竞态截掉尾段。
- 三重防丢 / 防重:partial 骤缩时判定静默话段重置并抢救上一段;引擎重放
累计全文时按前缀 / 空白不敏感规则去重;识别中途报错时返回已累积话段而非
整段作废。
- 等待预算由固定 60s 硬顶改为 max(60s, 音频时长 + 30s),长录音不再被
「等待语音识别结果超时」截断。
- 新增 12 个单元测试覆盖多话段累积、逐话段 final、静默重置抢救、去重、
拼接规则与等待预算。
单话段(无停顿)场景输出与修复前逐字节相同,云端识别路径不受影响,因此不
存在回归风险。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com >
* fix(asr): harden Apple Speech segment lifecycle
* fix(asr): preserve repeated final-only utterances
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com >
Co-authored-by: baiqing <lbx12309@icloud.com >
2026-07-15 16:01:02 +08:00