fix: models.dev 拉取失败后 UI 永久卡在 loading 态 (#145)#285
Conversation
根因:ai_page.rs:3405 的 EnsureModelsDevLoaded/RefreshModelsDev Err 分支 仅执行 log::warn!,既未调用 ctx.notify() 触发重绘,也未更新任何状态, 导致 cached() 始终返回 None,agent_providers_widget.rs 的 None 分支 永远渲染 "loading" 占位文字,用户无法感知失败也无法恢复。 修复: - models_dev.rs: 新增 FETCH_FAILED AtomicBool + last_fetch_failed() / set_fetch_failed() 公共接口,与现有 CHIPS_EXPANDED 模式一致 - ai_page.rs: EnsureModelsDevLoaded 与 RefreshModelsDev 的 Err 分支 改为调用 set_fetch_failed(true) + ctx.notify(),确保 UI 重绘 - agent_providers_widget.rs: None 分支检查 last_fetch_failed(), 失败时显示含重试提示的文字(catalog-empty),而非误导性的 loading 文字 (页首 Refresh 按钮始终可见,用户可直接点击重试) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017po385dpt8y1iuhpDixXbT
zerx-lab
left a comment
There was a problem hiding this comment.
(详见 inline comments 和下方总结 comment)
— 由 Claude Routine 自动生成;如需复评请 @ 维护者人工触发。
Generated by Claude Code
| @@ -3402,7 +3402,11 @@ impl TypedActionView for AISettingsPageView { | |||
| async move { models_dev::fetch_and_cache(client).await }, | |||
| |view, result, ctx| match result { | |||
| Ok(()) => view.rebuild_current_page(ctx), | |||
There was a problem hiding this comment.
成功路径没有调用 models_dev::set_fetch_failed(false)。PR body 的说明是"成功时 cached() 为 Some,无需重置"——正常流程下确实如此,但这个推断依赖一个隐式前提:catalog 一旦写入就不会被清空。若将来 catalog 因某种原因(内存回收、版本升级清缓存、State 重建等)被重置为 None,FETCH_FAILED 会停留在 true,下次进入 None 分支时会错误地显示失败态,即便实际上最近一次拉取已经成功。
建议在 Ok(()) 分支也加一行 models_dev::set_fetch_failed(false);,让标志严格反映最近一次拉取的实际结果,两个 handler(EnsureModelsDevLoaded 和 RefreshModelsDev)都需要同步修改。
Generated by Claude Code
|
|
||
| match models_dev::cached() { | ||
| None => { | ||
| let catalog_text = if models_dev::last_fetch_failed() { |
There was a problem hiding this comment.
Question:这里复用了 settings-agent-providers-catalog-empty,但这个 key 的语义是"目录为空",而此处的上下文是"网络拉取失败"。两种情形对用户的含义截然不同——前者暗示配置问题或 providers 未接入,后者是网络/CDN 故障。Issue #145 中明确要求渲染独立的错误态("设置后应该显示错误文字而不是 loading 文字")。
建议新增一条专用 i18n key(例如 settings-agent-providers-catalog-load-failed),措辞应说明拉取失败并引导用户点击页面顶部已存在的 Refresh 按钮重试,避免与"空目录"产生歧义。
Generated by Claude Code
Review 总结整体判断:建议修改后合并(Request Changes 倾向,但因同账号限制以 Comment 形式提交) 两处问题需要处理后才宜合并,逻辑方向是正确的,改动量小。 阻塞问题1.
2. 复用了语义不符的 i18n key( 用 建议(不阻塞)
看起来不错的地方
— 由 Claude Routine 自动生成;如需复评请 @ 维护者人工触发。 Generated by Claude Code |
- ai_page.rs: Ok(()) 分支同步调用 set_fetch_failed(false),让标志严格 反映最近一次拉取结果,避免 catalog 未来被重置为 None 时显示错误失败态 - i18n: 新增专用 key settings-agent-providers-catalog-load-failed(en/zh-CN/ja), 语义明确为"拉取失败",不再复用"目录为空"的 catalog-empty key - agent_providers_widget.rs: 失败态改用新 key,消除语义歧义 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017po385dpt8y1iuhpDixXbT
关联 Issue
Fixes #145
问题点(确定性描述)
根因:
app/src/settings_view/ai_page.rs:3403–3406EnsureModelsDevLoaded(及RefreshModelsDev)的Err分支仅执行log::warn!,既未调用ctx.notify()触发 UI 重绘,也未更新任何可观测状态。结果:models_dev::cached()(models_dev.rs:135)始终返回Noneagent_providers_widget.rs:1463的match models_dev::cached()的None分支永远渲染settings-agent-providers-loading-catalog("正在拉取…"占位)复现证据
规模声明
cached()调用方影响面修改文件清单
app/src/ai/agent_providers/models_dev.rsFETCH_FAILED: AtomicBool+last_fetch_failed()+set_fetch_failed()app/src/settings_view/ai_page.rsEnsureModelsDevLoaded与RefreshModelsDev的 Err 分支改为set_fetch_failed(true)+ctx.notify()app/src/settings_view/agent_providers_widget.rslast_fetch_failed(),失败时显示含重试提示的文字而非 loading 文字验证
网络层面
cargo check因远程环境无法拉取 git 依赖而失败(proxy 403)。已通过
rustc单独验证新增 AtomicBool 代码语法正确,逻辑改动均为对现有模式的最小扩展(与CHIPS_EXPANDED完全同构)。影响面
models_dev::cached()的 3 处调用方:agent_providers_widget.rs:1463(本 PR 修改)、ai_page.rs:3427、ai_page.rs:3458(均为let Some(...) else { return }模式,语义不变)agent_providers_widget.rs:1437-1442)始终渲染,失败后用户可直接点击重试🤖 Generated with Claude Code
https://claude.ai/code/session_017po385dpt8y1iuhpDixXbT
Generated by Claude Code