Skip to content

feat(history): 详情页展示 ASR/LLM 模型与分步耗时,可观测的模型表现对比 - #826

Merged
H-Chris233 merged 3 commits into
Open-Less:betafrom
bigsongeth:feat/history-model-timings
Jul 16, 2026
Merged

feat(history): 详情页展示 ASR/LLM 模型与分步耗时,可观测的模型表现对比#826
H-Chris233 merged 3 commits into
Open-Less:betafrom
bigsongeth:feat/history-model-timings

Conversation

@bigsongeth

@bigsongeth bigsongeth commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

User description

初衷

现在换 ASR 供应商、换润色模型都很方便,但换完之后「到底哪个模型更快、哪个更准」没有任何可以回看的地方——历史页只记了润色模式、时间和录音时长。想做模型能力对比时,只能靠当场的体感。

这个 PR 把每次听写实际用了什么模型、各步花了多久记进历史,让历史页成为一个可以监测转写模型与 LLM 表现的地方:连续用两个供应商各说几段话,翻历史就能直观对比收尾延迟、润色耗时和输出质量。

改动

历史详情页底部改为「识别 / 润色 / 插入」三行流水线明细:

步骤 内容 右列
识别 ASR provider · 模型(如 bailian · fun-asr-realtime 识别耗时
润色 LLM provider · 模型(如 codex_oauth · gpt-5.5 润色耗时
插入 目标 App · 字数 · 热词命中(原有信息归位到这一行) 插入状态

后端DictationSession 新增 6 个可选字段 asr_provider / asr_model / llm_provider / llm_model / asr_ms / polish_ms,全部 #[serde(default)],旧 history.json 完全兼容(旧条目前端自动隐藏对应行,信息量与改版前一致)。

耗时语义(也是几个字段设计上想清楚的点):

  • asr_ms 量的是松键后等待转写结果的时间:流式 ASR 边录边转,这里是用户感知的收尾延迟;批式 ASR 则是完整转写耗时。含自动静默重试的时间(那也是用户在等的时间)。
  • polish_ms 只在 LLM 真正被调用时记录(翻译 / 非 Raw / Raw 但风格包带 LLM),Raw 直通为 None,前端隐藏润色行。
  • 模型标签与凭据读取器同源(含默认值回退),volcengine / Apple Speech 这类无模型 id 的只记 provider。
  • 失败与空转写的历史也记 ASR 模型——「哪个模型转不出来」正是对比时要看的信息;手动重新转录会用当前 ASR 覆盖模型/耗时,并清掉旧润色字段避免张冠李戴。

顺手的两个 UI 修正:

  • 润色卡片的复制按钮从外层工具栏移入卡片头部,与原文卡片对称(此前「轻度润色」的复制按钮孤悬在外)。
  • 标题行时长加「录音」前缀,并给「识别」行加悬停说明——录音时长发生在松键前,和识别/润色耗时不该被加总,避免「时间对不上」的误读(实测反馈)。

五个语言包(en / zh-CN / zh-TW / ja / ko)同步新增文案;移除已无引用的 insertedTo

测试

  • cargo check + cargo check --tests 通过(含新增 DashScope 多模态 / ElevenLabs 供应商分支的模型标签)
  • tsc --noEmit + vite build 通过
  • macOS 本机 release 构建实测:流式(bailian fun-asr-realtime,识别 0.2s)与润色(gpt-5.5,1.4s)耗时与模型均正确落库并展示;旧历史条目正常降级只显示插入行

效果

image image

PR Type

Enhancement, Bug fix


Description

  • 详情页流水线展示 ASR/LLM 模型与分步耗时

  • 构建时快照确保归因准确,旧数据 Serde 缺省兼容

  • 自动静默重试和失败条目也记录模型信息

  • 前端 UI 组件重排并添加多语言 i18n 支持


Diagram Walkthrough

flowchart LR
  A["begin_session"] --> B["store_asr_for_session (with build-time label)"]
  B --> C["transcribe + measure asr_ms"]
  C --> D["polish flow: capture llm_call + llm_elapsed_ms"]
  D --> E["save DictationSession with asr/llm/model/ms fields"]
  E --> F["frontend History: pipeline display"]
Loading

File Walkthrough

Relevant files
Enhancement
11 files
history.rs
重转录逻辑更新,添加 apply_retranscription 与测试                                         
+86/-6   
coordinator.rs
添加 AsrCallLabel、volc_resource_history_label,集成快照                 
+92/-7   
asr_wiring.rs
build_qa_asr_start 返回标签,各分支提供构建时快照                                             
+69/-29 
dictation.rs
端到端会话捕获 ASR/LLM 快照和耗时,更新失败处理                                                         
+204/-30
polish_flow.rs
添加 LLM 调用标签和耗时记录,增加 Raw 直通测试                                                         
+109/-11
qa_session.rs
更新 QA 会话 DictationSession 构造填充新字段                                               
+15/-1   
resources.rs
新增 take_asr_label_for_session 函数                                                 
+20/-2   
polish.rs
添加 LlmCallLabel 定义和 call_label 方法                                               
+25/-0   
types.rs
DictationSession 新增 6 个可选字段,添加上序列化测试                                         
+80/-0   
types.ts
TypeScript 类型添加 asrProvider 等 6 个字段                                           
+12/-0   
History.tsx
详情底部改为流水线网格,添加悬停提示                                                                             
+57/-9   
Documentation
5 files
en.ts
添加录音前缀和分步标签的英文 i18n                                                                           
+6/-1     
ja.ts
同步新增日语 i18n 键                                                                                       
+6/-1     
ko.ts
同步新增韩语 i18n 键                                                                                       
+6/-1     
zh-CN.ts
同步新增简体中文 i18n 键                                                                                   
+6/-1     
zh-TW.ts
同步新增繁体中文 i18n 键                                                                                   
+6/-1     
Tests
1 files
mock-data.ts
更新 mock 数据包含新字段(轮换画像)                                                                       
+18/-0   

- 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>
@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

(Review updated until commit c4d5318)

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ No major issues detected

@H-Chris233

Copy link
Copy Markdown
Collaborator

@appergb 给决策

@appergb appergb left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Senior review — exact head a2ab3537b251ef6a7700f7e8543fdc322676c06a

Strengths

  • Rust Option<T> + #[serde(default)] 与 camelCase TS 镜像使旧 history.json 的读取兼容路径合理;旧版本 serde 默认也会忽略新增未知字段,不需要一次性迁移。
  • 重转录成功后覆盖 ASR 字段并清空旧 llm_* / polish_ms,避免新转写挂着旧润色元数据,这个契约方向正确。
  • 新增持久化字段只有 provider/model 标识与耗时,没有把 API key、token、endpoint 或 extra headers 写入历史;未发现新的凭据泄露。
  • exact-head CI 已核对:macOS、Windows、Linux、Android cargo check 及 PR-Agent 均成功,所有 check run 的 head_sha 都是该 SHA。
  • PR 没有 GitHub 正式关联的 closing issue;我同时阅读了最贴近动机的 #373(处理速度异常慢)。该 issue 也暴露了 Volcengine 资源标识的可观测性缺口,见下方。

Critical

  • 无。

Important

  1. 模型归因不是调用快照,可能把结果记到错误 provider/model。 coordinator/dictation.rs:2882 在 ASR 完成后调用 asr_history_label(&inner.prefs.get()),润色也是请求返回后重新读 vault;会话中切换 provider/model 时,历史记录与实际已构建客户端不一致。Foundry/Sherpa 的构建路径还会把未知 alias 回退到默认模型,而 label helper 直接记录未归一化 pref。请在构建实际 ASR/LLM provider 时捕获规范化 label,并随 session/result 传到持久化点,而不是事后重读全局设置。

  2. “LLM 真正被调用才记录”的契约目前由模式推断,失败路径会产生伪数据。 coordinator/dictation.rs:3136llm_used 只看 translate/mode/style pack;例如 Gemini 缺 key 时在 read_gemini_credentials 就返回,HTTP/模型没有被调用,但仍写入 llmProviderllmModel 和很短的 polishMs。应让 polish dispatch 返回真实调用元数据(或至少明确 attempted vs completed),再决定是否持久化。

  3. 亚秒延迟的展示精度不足,直接削弱模型比较目标。 History.tsx:443 / :454 复用了录音时长 formatter,只保留 0.1 秒;截图中已经出现 0.0 秒。快速流式收尾常在几十毫秒量级,这会把多个有差异的结果显示成同一个值。建议 step latency 小于 1 秒时显示整数毫秒,较大值再显示秒。

  4. Volcengine 的可比较标识被丢弃。 #373 的 ASR 配置明确是 volc.seedasr.sauc.duration,代码里对应 VolcengineResourceIdcoordinator.rs:2444 当前固定返回 model=None,因此不同 Volc resource/engine 的历史全都只显示 volcengine,无法支持这个最接近动机的排障场景。请评估把非密钥的规范化 resource/engine 标识写入 model 字段(若某类 resource id 可能包含租户敏感信息,则先定义脱敏/allowlist)。

  5. 没有新增行为/契约测试。 这 13 个文件只更新了 mock 数据,没有测试旧 JSON 缺字段反序列化、camelCase 序列化、各 provider 默认/alias 映射、Raw 直通不落 LLM 字段、LLM preflight failure、重转清理旧字段,以及精细耗时格式。现有 CI 全绿只能证明编译与原测试不回归,不能保护这次新增的数据语义。

Minor

  • stepAsrHint 使用不可聚焦的 span title=...:桌面鼠标可 hover,但键盘和 Android 触屏无法可靠发现/读取。应使用可聚焦 tooltip/popover trigger,并补 aria 语义。
  • 现有视觉证据只有桌面深色模式。三列 grid 在窄屏、长 provider/model、长本地化状态文本下的换行/溢出,以及浅色/高对比度状态尚无验证证据。

Ready to merge verdict

  • 逻辑质量:Not ready。 上述归因、调用语义、显示精度与测试问题会让“模型表现对比”的数据不可靠。
  • UI / 发布门禁:Not ready,禁止合并。 这是明确的用户可见 History UI 改动;即使逻辑修完,也需完成移动端/桌面、触屏/键盘、窄宽度、浅/深色的 UI 验收再进入发布。按本次门禁要求,本 review 不会批准或合并。

Open-Less#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 Open-Less#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>
@bigsongeth

Copy link
Copy Markdown
Contributor Author

@appergb 感谢这轮 review——五条 Important 全部成立,尤其 1/2 指出的归因时机问题正中要害。已在 0fcb8fc 逐条修复:

1. 归因改为构建时快照 ✅

  • ASR:store_asr_for_session 新增 AsrCallLabel 参数,签名强制 11 个构建分支在构建现场交出 (provider, model) 快照——provider 是实际构建用的具体协议 id(统一百炼入口先经 resolve_effective_asr_provider 重定向后的 effective id),model 是实际传给客户端的值。Foundry/Sherpa 记录的是归一化后的 model_alias(未知 alias 回退默认后的值),本地 Qwen3 由 build_local_qwen3 返回 ModelId 校验归一后的 id。快照随会话资源存放,end_session 取走落库;转写失败路径(fail_dictationwrite_transcribe_failed_history)也改用同一份快照,不再重读设置。
  • LLM:polish 调用链(polish_text / translate_text / polish_or_passthrough* / streaming)新增 llm_call 出参,在成功构建 provider、即将发起真实调用时填充 LlmCallLabel(从已构建的 config 读 provider_id/model,Codex 的 model 已经过 normalize_codex_model)。

2. preflight 失败不再产生伪数据 ✅
凭据缺失等构建失败路径(如 read_gemini_credentials? 提前返回、build_active_llm_provider 失败)不会填充 llm_call,此时 llm_provider / llm_model / polish_ms 全部不落库——polish_ms 现在以 llm_call.is_some() 为唯一判据,不再由 mode 推断。语义:落库的 llm 字段 = 「本次实际尝试发起的调用」;调用发出但 HTTP 失败仍记录(有真实延迟与归因价值),errorCode: polishFailed 已表达失败。

3. 亚秒毫秒精度 ✅
新增 formatStepDuration:<1s 显示整数毫秒(durationMillis,5 个语言包),≥1s 沿用 0.1s。mock 数据里 64ms/120ms 的流式收尾现在显示「64 毫秒 / 120 毫秒」,不再被拍成 0.1 秒档。

4. Volcengine resource id ✅
新增 volc_resource_history_label allowlist:仅放行 volc. 命名空间、ASCII 字母数字与 ._-、≤64 字符的产品标识(覆盖 #373volc.seedasr.sauc.durationvolc.bigasr.*);自定义/异常值可能携带租户信息,一律不落盘。通过校验的写入 asr_model,历史里可直接对比不同 Volc 引擎。

5. 测试 ✅
新增 6 个行为/契约测试(全部通过):

  • 旧 history.json 缺全部新字段的反序列化兼容(dictation_session_deserializes_legacy_json_without_model_fields
  • 新字段 camelCase 序列化契约(前端镜像依赖)
  • volc allowlist 放行/拒绝两组(含 trim、非 ASCII、超长、空格)
  • 重转录覆盖 ASR 归因并清除旧 llm_*/polish_ms(抽出 apply_retranscription 纯函数)
  • Raw 直通不产生 LLM 调用快照(raw_passthrough_leaves_llm_call_snapshot_empty——这条测试在开发中还真拦下过一次语义误解)

Minor A(a11y)✅ 识别行提示改用仓库统一的 Tooltip 组件(focusable:hover / 键盘 focus / 触摸三通道,portal 定位),并带虚线下划线视觉暗示,与设置页 #777 的 hint 模式一致。

Minor B(UI 验收)✅ mock 数据扩成四种压力画像(亚秒流式收尾、volc resource id、超长 provider/model、Raw 无润色行/旧条目缺字段),实测通过:深色/浅色 × 桌面 1280 / 移动 375 宽;openai/whisper-large-v3-turbo-preview-2026-01-31 在 375 宽下 overflowWrap 折行 3 行、右列耗时不挤压、无横向滚动;旧条目仅显示插入行(与改版前信息量一致)。

另外重转录命令现在使用 retranscribe_pcm 返回的构建时快照(build_qa_asr_start 同步返回 label),与主听写路径同一套归因机制。

CI 绿后请再过目。

🤖 Generated with Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit 0fcb8fc

@github-actions

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit c4d5318

@H-Chris233
H-Chris233 merged commit 842ae5f into Open-Less:beta Jul 16, 2026
5 checks passed
@bigsongeth
bigsongeth deleted the feat/history-model-timings branch July 22, 2026 10:55
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.

3 participants