Skip to content

fix(settings): 切换 LLM/ASR preset UI 不刷新 (#219)#220

Merged
H-Chris233 merged 2 commits into
mainfrom
fix/preset-switch-race-219
May 4, 2026
Merged

fix(settings): 切换 LLM/ASR preset UI 不刷新 (#219)#220
H-Chris233 merged 2 commits into
mainfrom
fix/preset-switch-race-219

Conversation

@appergb
Copy link
Copy Markdown
Collaborator

@appergb appergb commented May 4, 2026

User description

Closes #219

Summary

设置页切换 LLM / ASR 供应商时,凭据输入框显示的还是旧 active provider 的 key/model/endpoint —— 用户以为切换没生效,实际是前端 race。底层 providers HashMap 已经是 per-provider,不需要改后端

根因(两条 bug 在同一函数)

  1. Race:setLlmProvider(id) / setAsrProvider(id) 是同步 React state 改动,立刻让 CredentialField 重挂载读 ark.api_key —— 但此时 await setActive*Provider 还没跑,后端 root.active.llm 还是旧值,lookup_account 落到旧 entry。
  2. 覆盖:setCredential('ark.endpoint', preset.baseUrl) 无条件写默认值,每次切回该 provider 都把用户自定义的 baseUrl 覆盖回出厂默认。

改动

Settings.tsx 两个 handler,共 +20/-8:

  • 把 await setActive*Provider(id) 挪到 setLlm/AsrProvider(id) 之前——后端 active 切到位再让 React 重挂载
  • endpoint/model 默认值改成「仅当该 provider entry 该字段为空」才填,不再覆盖用户自定义

不动 persistence schema、不动 IPC、不动 coordinator。

Test plan

  • Ark 填 key A → 切 DeepSeek 填 key D → 切回 Ark 看到 key A(保留)
  • Ark 自定义 baseUrl → 切 DeepSeek 再切回 → Ark 自定义 baseUrl 还在
  • ASR:SiliconFlow ↔ GLM ↔ Groq ↔ OpenAI 切换同款验收
  • 首次切到一个还没填过的 provider,endpoint/model 显示该 preset 默认值

PR Type

Bug fix


Description

  • Fix race: switch backend active before UI remounts

  • Preserve user overrides: default endpoint/model only if empty

  • Handle rapid switches: split state and sequence ref to abort stale

  • Credential field keys now use committed provider for correct reads


Diagram Walkthrough

flowchart LR
  A["User selects new provider in <select>"]
  B["Immediate setLlmProvider → UI reflects choice"]
  C["Increment sequence ref (seq = ++ref)"]
  D["await setActiveLlmProvider(id)"]
  E{"seq matches?"}
  F["await updatePrefs(next)"]
  G{"endpoint/field empty?"}
  H["setCredential(default) only if empty"]
  I["setCommittedLlmProvider(id)"]
  J["CredentialField remounts with correct key"]
  K["Abort: stale request, do not commit"]

  A --> B
  B --> C
  C --> D
  D --> E
  E -- yes --> F
  E -- no --> K
  F --> E
  E -- yes --> G
  G -- yes --> H
  G -- no --> E
  H --> E
  E -- yes --> I
  I --> J
Loading

File Walkthrough

Relevant files
Bug fix
Settings.tsx
Fix stale credential display and overwrite protection in provider
switch handlers

openless-all/app/src/pages/Settings.tsx

  • Introduce committedLlmProvider / committedAsrProvider state to defer
    credential remount until backend active provider is set
  • Add llmSwitchSeqRef / asrSwitchSeqRef to abort stale async flows on
    rapid switches
  • Reorder: setActive*Provider and updatePrefs before commit; default
    endpoint/model only written if current value is empty
  • Update all CredentialField keys, ProviderTools key, and volcengine
    conditional to use committed*Provider instead of immediate state
  • preset lookups and placeholder logic now derive from committed
    provider, preventing placeholder/credential mismatch
+68/-21 

底层 providers HashMap 已是 per-provider,bug 完全在前端两个 onChange:

1) Race
setLlmProvider(id) / setAsrProvider(id) 是同步 React state 改动,
CredentialField 用 `key={`${provider}:api_key`}` 强制重挂载、读后端
ark.api_key —— 但此时 await setActive*Provider 还没跑,root.active.llm
还是旧值,lookup_account 落到旧 entry。把 await setActive 移到
setLlm/AsrProvider 之前修。

2) 覆盖
旧实现无条件 setCredential('ark.endpoint', preset.baseUrl),把用户在
该 provider 上之前自定义的 baseUrl 覆盖掉。改成「仅在 entry 该字段
为空」才填默认值,保留用户自定义。ASR 端的 endpoint + model 同款修。

不动 persistence schema、不动 IPC、不动 coordinator。

Closes #219
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1accecbd57

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// 不再无条件覆盖用户自定义值。
const onLlmProviderChange = async (id: LlmPresetId) => {
setLlmProvider(id);
await setActiveLlmProvider(id);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Update provider selection state before awaiting async work

onLlmProviderChange now waits on multiple async IPC calls before calling setLlmProvider. Because this is a controlled <select>, delaying the state update until after await makes the displayed selection depend on async completion order rather than the latest user input. If the user switches providers quickly, an earlier request that resolves later can overwrite a newer choice and leave the UI showing a stale provider.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

已修复 P2,commit 1eec2fa

按反馈把 provider state 拆成两个:

  • *Provider 立即跟随用户输入,驱动 `` —— 受控反馈始终是最新选择 committed*Provider 仅在所有 await 完成后 commit,驱动 `CredentialField` 的 key 与 placeholder/preset 渲染 加 *SwitchSeqRef 守卫:每次进 handler 自增,每段 await 后比对,发现已有更新的 switch 在飞就 abort 当前 commit 三类回归路径都堵上了: 单次切换:`` 立即响应;CredentialField 等后端 active 切完才 remount 读正确 entry(issue fix(settings): 切换 LLM/ASR preset 时凭据 UI 显示旧 active 的内容(race + 覆盖) #219 fix 保留)
  • 快速连点两次:只有最后一次的 commit 落地,旧请求晚到也不会覆盖
  • 渲染期间 placeholder:跟着 committed 走,避免「`` 已经是 DeepSeek 但 placeholder 还显示 ARK 默认 URL」的不一致 `tsc + vite build` 通过。

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

PR Reviewer Guide 🔍

(Review updated until commit 1eec2fa)

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis 🔶

219 - Partially compliant

Compliant requirements:

  • 先切换后端 active provider,再提交新的 committed provider 供 UI 重挂载。
  • 通过按字段存在性检查,避免覆盖已存在的 endpoint / model 自定义值。
  • 增加序列号守卫,降低快速连续切换时的 stale write 风险。
  • 未引入 persistence schema、IPC 或 coordinator 变更。

Non-compliant requirements:

[]

Requires further human verification:

  • 需要在真实界面里手动验证 LLM / ASR 切换时,输入框是否始终显示对应 provider 的实际值,而不是旧 active 的残留。
  • 需要验证“首次切换到空配置 provider 时自动预填默认值、但已配置 provider 不被覆盖”的实际交互效果。
⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ No major issues detected

Codex P2: 上一版把 setLlmProvider/setAsrProvider 挪到所有 await 之后,
受控 <select> 的 value 现在依赖 IPC 完成顺序——用户连点两次时,先发的
请求晚到会用旧 id 覆盖最新的 setProvider,UI 卡在错误选项上。

修法(state 二分):
- *Provider:立即跟随用户输入,驱动 <select>
- committed*Provider:仅在所有 await 完成后才 commit,驱动 CredentialField
  的 key 与 placeholder/preset 的渲染
- *SwitchSeqRef:每次进入 handler 自增,每段 await 后比对,发现已有更新的
  switch 在飞就 abort 当前 commit,stale write 保护

效果:
- 用户切换 <select> 立刻看到新选项(受控反馈正确)
- CredentialField 等到后端 active 真切完才 remount 读 entry(issue #219 修复保留)
- 100ms 内连点两次:只有最后那次的 commit 落地,UI 不会被旧请求覆盖
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

Persistent review updated to latest commit 1eec2fa

@H-Chris233 H-Chris233 merged commit 1a8f4aa into main May 4, 2026
2 checks passed
appergb pushed a commit that referenced this pull request May 4, 2026
包含自 1.2.13 以来的修复与新功能:
- feat(autostart): 跨端开机自启 (#194)
- feat(asr): SiliconFlow / GLM-ASR / Groq / OpenAI Whisper preset (#212)
- fix(qa): Windows / Linux 划词追问浮窗 Esc + 拖拽 + 文案 (#205 / #206)
- fix(settings): preset 切换 race + per-provider 凭据隔离 (#219 / #220)
- fix(overview): 概览整屏适配 + 嵌套 scroller 细滚动条 (#243 / #248)
- 一系列 Windows IME (TSF) 模块 (#233 / #240 等)
@appergb appergb deleted the fix/preset-switch-race-219 branch May 6, 2026 11:52
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.

fix(settings): 切换 LLM/ASR preset 时凭据 UI 显示旧 active 的内容(race + 覆盖)

2 participants