Skip to content

feat: 供应商可禁用错误冷冻#243

Merged
SurviveM merged 12 commits intoawsl-project:mainfrom
ymkiux:feat/disable-error-cooldown
Feb 22, 2026
Merged

feat: 供应商可禁用错误冷冻#243
SurviveM merged 12 commits intoawsl-project:mainfrom
ymkiux:feat/disable-error-cooldown

Conversation

@ymkiux
Copy link
Contributor

@ymkiux ymkiux commented Feb 22, 2026

Summary

  • 新增 provider 级开关,可禁用错误触发的自动冷冻
  • 前端提供配置开关(创建/编辑/详情)并同步类型与文案

Testing

  • 未运行自动化测试

Closes #239

Summary by CodeRabbit

发布说明

  • 新功能

    • 为所有提供商添加了禁用错误冷却的开关,允许用户禁用自动错误冷却机制。
    • 改进了项目列表排序展示。
    • 优化了数据加载体验,在获取新数据时保留之前的数据。
  • 改进

    • 改进了图表容器的响应式设计。

@coderabbitai
Copy link

coderabbitai bot commented Feb 22, 2026

📝 Walkthrough

Walkthrough

此 PR 在后端和前端添加了 disableErrorCooldown 配置开关,允许提供商禁用自动错误冻结。后端在中间件中添加了守卫逻辑,前端在多个提供商类型中添加了 UI 控件和国际化支持。

Changes

Cohort / File(s) Summary
后端提供商配置
internal/domain/model.go, web/src/lib/transport/types.ts, web/src/pages/providers/types.ts
添加了公共的 disableErrorCooldown 布尔字段到 ProviderConfig 接口和相关类型定义,支持 JSON 序列化和表单数据映射。
执行器错误冷冻逻辑
internal/executor/executor.go, internal/executor/middleware_dispatch.go
引入 shouldSkipErrorCooldown() 辅助函数,在中间件中添加了守卫条件,当标志启用时跳过冷冻处理逻辑。
自定义提供商 UI
web/src/pages/providers/components/custom-config-step.tsx, web/src/pages/providers/components/provider-edit-flow.tsx
在配置表单中添加了错误冷冻禁用开关,包括状态管理和表单数据绑定。
Antigravity 提供商 UI
web/src/pages/providers/components/antigravity-provider-view.tsx
添加了本地状态、同步效果和切换处理器,渲染禁用错误冷冻的 Switch 控件。
Codex 提供商 UI
web/src/pages/providers/components/codex-provider-view.tsx
实现了错误冷冻禁用开关,包括状态初始化、同步逻辑和配置更新处理。
Kiro 提供商 UI
web/src/pages/providers/components/kiro-provider-view.tsx
添加了本地状态管理、异步更新处理和 UI 控件,支持乐观更新和失败回滚。
表单上下文
web/src/pages/providers/context/provider-form-context.tsx
在初始表单数据中添加了 disableErrorCooldown: false 默认值。
国际化
web/src/locales/en.json, web/src/locales/zh.json
为错误冷冻功能添加了英文和中文标题、标签及描述文本。
数据查询优化
web/src/hooks/queries/use-usage-stats.ts
添加了 keepPreviousData 以在加载新数据时保留前一页数据。
项目列表排序
web/src/pages/projects/index.tsx
引入 useMemo 实现项目列表按 createdAt 排序和 ID 辅助排序。
图表布局
web/src/components/ui/chart.tsx, web/src/pages/stats/index.tsx
更新了图表容器的响应式布局,使用 Recharts ResponsiveContainer 和 Tailwind 高度类。
UI 组件
web/src/components/layout/app-sidebar/nav-user.tsx
仅添加了空行。

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • awsl233777
  • Bowl42

Poem

🐰 开关一启闭,冻结逻辑巧调整,
提供商安稳行,前后端齐发力,
国际化文本全,用户体验更上层!✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning 发现部分超出范围的变更:在web/src/pages/projects/index.tsx中添加了项目排序功能,在web/src/pages/stats/index.tsx中修改了图表容器尺寸,这些均不在Issue #239的需求范围内 移除projects/index.tsx中的sortedProjects排序逻辑和stats/index.tsx中的图表高度修改,将其作为独立的PR提交。
Docstring Coverage ⚠️ Warning Docstring coverage is 15.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR标题"feat: 供应商可禁用错误冷冻"准确地概括了变更的主要内容:在供应商级别新增禁用错误冷冻的功能。
Linked Issues check ✅ Passed 代码变更完整实现了Issue #239的所有需求:后端新增disableErrorCooldown字段和shouldSkipErrorCooldown检查函数,前端提供了配置开关UI,开关默认关闭,开启时禁止错误冻结。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@SurviveM SurviveM merged commit 05e5d85 into awsl-project:main Feb 22, 2026
1 of 2 checks passed
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (7)
web/src/pages/projects/index.tsx (1)

44-60: 排序逻辑正确,但每次比较重复创建了 Date 对象。

在比较器中,new Date(a.createdAt) 被调用了两次(一次用于 Number.isFinite 检查,一次用于取值)。虽然对少量项目性能影响可忽略,但可以简化为一次构造:

♻️ 减少重复的 Date 构造
     return projects.slice().sort((a, b) => {
-      const timeA = Number.isFinite(new Date(a.createdAt).getTime())
-        ? new Date(a.createdAt).getTime()
-        : 0;
-      const timeB = Number.isFinite(new Date(b.createdAt).getTime())
-        ? new Date(b.createdAt).getTime()
-        : 0;
+      const rawA = new Date(a.createdAt).getTime();
+      const timeA = Number.isFinite(rawA) ? rawA : 0;
+      const rawB = new Date(b.createdAt).getTime();
+      const timeB = Number.isFinite(rawB) ? rawB : 0;
       if (timeA !== timeB) {
         return timeA - timeB;
       }
       return a.id - b.id;
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/projects/index.tsx` around lines 44 - 60, The comparator in the
useMemo for sortedProjects constructs the same Date twice per item; refactor the
comparator inside sortedProjects to create each Date once (e.g., compute const
dateA = new Date(a.createdAt).getTime() and const dateB = new
Date(b.createdAt).getTime()), then use Number.isFinite checks against those
cached values (or default to 0) and compare dateA/dateB before falling back to
a.id - b.id; update variables timeA/timeB (or replace them) accordingly to avoid
duplicate Date construction.
web/src/pages/providers/components/antigravity-provider-view.tsx (1)

471-488: 考虑将 disableErrorCooldown 开关 UI 抽取为共享组件。

该开关的 UI 结构(标签 + 描述 + Switch)在 antigravity-provider-view.tsxcodex-provider-view.tsxkiro-provider-view.tsxcustom-config-step.tsx 中完全重复。可以像 CLIProxyAPISwitch 一样抽取为独立组件(例如 DisableErrorCooldownSwitch),减少重复代码。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/providers/components/antigravity-provider-view.tsx` around
lines 471 - 488, Extract the repeated label+description+Switch UI into a
reusable React component (e.g., DisableErrorCooldownSwitch) to replace
duplicated blocks in antigravity-provider-view, codex-provider-view,
kiro-provider-view and custom-config-step; the new component should accept
props: checked (boolean) mapped to disableErrorCooldown, onChange mapped to
handleToggleDisableErrorCooldown, and disabled mapped to
updateProvider.isPending (or equivalent pending state), render the same text via
i18n keys 'provider.disableErrorCooldown' and
'provider.disableErrorCooldownDesc', and then update each file to import and use
<DisableErrorCooldownSwitch checked={disableErrorCooldown}
onChange={handleToggleDisableErrorCooldown} disabled={updateProvider.isPending}
/> instead of the inline markup.
internal/executor/middleware_dispatch.go (1)

333-340: shouldSkipErrorCooldown 实现正确且支持空指针安全。

shouldSkipErrorCooldown 函数通过短路求值正确处理了 providernil 的情况(第 233-235 行):

return provider != nil && provider.Config != nil && provider.Config.DisableErrorCooldown

providernil 时返回 false,使得第 333 行的守卫条件 !shouldSkipErrorCooldown(...) 评估为 true。然而,代码随后在守卫块内访问 matchedRoute.Provider.ID 而未进行额外的 nil 检查。尽管路由器在创建 MatchedRoute 时已验证 provider 存在(通过 prov, ok := providers[route.ProviderID] 并在未找到时跳过),但为了防御编程,建议在守卫条件中添加显式的非空检查:

if matchedRoute.Provider != nil && !shouldSkipErrorCooldown(matchedRoute.Provider) {

这样可以避免任何可能的空指针解引用。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/executor/middleware_dispatch.go` around lines 333 - 340, The guard
currently calls shouldSkipErrorCooldown(matchedRoute.Provider) but then
dereferences matchedRoute.Provider.ID inside the block; update the condition to
explicitly ensure matchedRoute.Provider is non-nil (e.g., change the if to check
matchedRoute.Provider != nil && !shouldSkipErrorCooldown(matchedRoute.Provider))
so that uses of matchedRoute.Provider.ID, e.handleCooldown and
e.broadcaster.BroadcastMessage("cooldown_update", ...) are nil-safe and avoid
potential nil pointer dereference.
web/src/pages/providers/components/provider-edit-flow.tsx (2)

44-44: Switch 的导入路径与文件内其他 UI 组件不一致

Button@/components/ui/buttonInput@/components/ui/input 单独导入,而 Switch 使用了桶导入 @/components/ui。建议统一为直接导入路径以保持一致性。

♻️ 建议修改
-import { Switch } from '@/components/ui';
+import { Switch } from '@/components/ui/switch';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/providers/components/provider-edit-flow.tsx` at line 44, The
import for Switch is using the barrel module while other UI components use
specific paths; update the Switch import in provider-edit-flow.tsx to match the
direct import style used for Button and Input (i.e., import Switch from the
specific component file instead of from '@/components/ui') so all UI imports are
consistent; locate the Switch symbol in the file and change its import to the
corresponding direct path.

356-356: !! 双重否定冗余

formData.disableErrorCooldown 在初始化时已经通过 ?? false 确保是 boolean 类型,不需要 !! 强制转换。

♻️ 建议修改
-          disableErrorCooldown: !!formData.disableErrorCooldown,
+          disableErrorCooldown: formData.disableErrorCooldown ?? false,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/providers/components/provider-edit-flow.tsx` at line 356, 在
provider-edit-flow.tsx 中为对象属性 disableErrorCooldown
赋值时不需要使用双重否定转换;formData.disableErrorCooldown 已在初始化时用 `?? false` 保证为 boolean,直接使用
`disableErrorCooldown: formData.disableErrorCooldown` 即可,删去 `!!`
强制转换以简化代码(定位符:formData.disableErrorCooldown / disableErrorCooldown)。
web/src/pages/providers/components/kiro-provider-view.tsx (2)

276-278: useEffect 依赖项中使用了可选链,可能触发 ESLint 警告

[provider.config?.disableErrorCooldown] 在运行时功能正确,但 eslint-plugin-react-hooksexhaustive-deps 规则会将其识别为"复杂表达式"并发出警告,建议先将值提取为局部变量再放入依赖数组。

♻️ 建议修改
+  const serverDisableErrorCooldown = provider.config?.disableErrorCooldown;
+
   useEffect(() => {
-    setDisableErrorCooldown(provider.config?.disableErrorCooldown ?? false);
-  }, [provider.config?.disableErrorCooldown]);
+    setDisableErrorCooldown(serverDisableErrorCooldown ?? false);
+  }, [serverDisableErrorCooldown]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/providers/components/kiro-provider-view.tsx` around lines 276 -
278, The useEffect currently uses a complex optional-chained expression in its
dependency array (provider.config?.disableErrorCooldown) which can trigger
eslint-plugin-react-hooks warnings; fix it by extracting the value to a local
constant (e.g. const currentDisableErrorCooldown =
provider.config?.disableErrorCooldown ?? false) and then update the effect to
call setDisableErrorCooldown(currentDisableErrorCooldown) with
[currentDisableErrorCooldown] as the dependency; reference useEffect and
setDisableErrorCooldown to locate and update the code.

280-302: kiroConfig 守卫会无声地阻断 disableErrorCooldown 的更新,且 config 展开冗余

存在两个关联问题:

  1. 提前返回守卫过严disableErrorCooldown 是顶层 ProviderConfig 字段,与 kiro 子配置无关。若 kiroConfig 为空(虽然极少见),开关将静默失效,用户不会收到任何反馈。

  2. 冗余的显式 kiro 展开...provider.config 已经包含了 kiro 字段,在其后再写 kiro: { ...kiroConfig } 是重复操作。可以直接依赖展开保留 kiro 配置,同时去掉不必要的守卫。

♻️ 建议简化
 const handleToggleDisableErrorCooldown = async (checked: boolean) => {
-  const kiroConfig = provider.config?.kiro;
-  if (!kiroConfig) return;
   const prev = disableErrorCooldown;
   setDisableErrorCooldown(checked);
   try {
     await updateProvider.mutateAsync({
       id: provider.id,
       data: {
         ...provider,
         config: {
           ...provider.config,
           disableErrorCooldown: checked,
-          kiro: {
-            ...kiroConfig,
-          },
         },
       },
     });
   } catch {
     setDisableErrorCooldown(prev);
   }
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/providers/components/kiro-provider-view.tsx` around lines 280 -
302, The guard in handleToggleDisableErrorCooldown incorrectly returns when
provider.config.kiro is falsy and also redundantly reassigns kiro; remove the
early return and the explicit kiro: { ...kiroConfig } override, and instead call
updateProvider.mutateAsync with data: { ...provider, config: {
...provider.config, disableErrorCooldown: checked } } so the top-level
disableErrorCooldown is updated regardless of kiro presence while preserving
existing config and kiro; keep the optimistic setDisableErrorCooldown(checked)
and restore the prev value in the catch as currently implemented.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c28434c and 581374f.

📒 Files selected for processing (18)
  • internal/domain/model.go
  • internal/executor/executor.go
  • internal/executor/middleware_dispatch.go
  • web/src/components/layout/app-sidebar/nav-user.tsx
  • web/src/components/ui/chart.tsx
  • web/src/hooks/queries/use-usage-stats.ts
  • web/src/lib/transport/types.ts
  • web/src/locales/en.json
  • web/src/locales/zh.json
  • web/src/pages/projects/index.tsx
  • web/src/pages/providers/components/antigravity-provider-view.tsx
  • web/src/pages/providers/components/codex-provider-view.tsx
  • web/src/pages/providers/components/custom-config-step.tsx
  • web/src/pages/providers/components/kiro-provider-view.tsx
  • web/src/pages/providers/components/provider-edit-flow.tsx
  • web/src/pages/providers/context/provider-form-context.tsx
  • web/src/pages/providers/types.ts
  • web/src/pages/stats/index.tsx
🧰 Additional context used
🧬 Code graph analysis (5)
web/src/pages/providers/components/provider-edit-flow.tsx (2)
web/src/components/ui/index.ts (1)
  • Switch (51-51)
web/src/components/ui/switch.tsx (1)
  • Switch (30-30)
web/src/pages/providers/components/codex-provider-view.tsx (2)
launcher/script.js (1)
  • config (276-276)
web/src/lib/transport/http-transport.ts (1)
  • updateProvider (115-118)
web/src/pages/providers/components/kiro-provider-view.tsx (4)
web/src/lib/transport/http-transport.ts (1)
  • updateProvider (115-118)
web/src/hooks/queries/index.ts (1)
  • useUpdateProvider (11-11)
web/src/hooks/queries/use-providers.ts (1)
  • useUpdateProvider (50-70)
web/src/components/ui/index.ts (1)
  • Switch (51-51)
web/src/pages/providers/components/custom-config-step.tsx (1)
web/src/components/ui/index.ts (1)
  • Switch (51-51)
internal/executor/executor.go (4)
internal/domain/model.go (1)
  • Provider (178-206)
web/src/lib/transport/types.ts (1)
  • Provider (68-78)
internal/repository/sqlite/models.go (2)
  • Provider (63-71)
  • Provider (73-73)
web/src/lib/transport/index.ts (1)
  • Provider (9-9)
🔇 Additional comments (15)
web/src/hooks/queries/use-usage-stats.ts (1)

6-6: keepPreviousData 用法符合 TanStack Query v5 规范,代码正确。

keepPreviousData 选项已在 v5 中移除,改为通过 placeholderData: keepPreviousData 实现相同效果,此处用法完全正确,可有效避免切换筛选条件时图表出现闪烁。

Also applies to: 120-120

internal/domain/model.go (1)

165-175: DisableErrorCooldown 字段定义正确,omitempty 确保默认关闭语义。

bool 零值(false)配合 omitempty 在序列化时被省略,后端读取时同样得到 false,即默认启用冷冻逻辑,与需求中"开关默认关闭"一致。

web/src/lib/transport/types.ts (1)

60-66: disableErrorCooldown?: boolean 与后端 Go 结构体定义完全一致,代码正确。

web/src/pages/stats/index.tsx (1)

901-902: 图表高度修复正确,解决了 height="100%" 在 flex 布局下可能导致高度为 0 的问题。

外层容器使用 h-[400px] min-h-[400px]ResponsiveContainer 使用 height={400},两者保持一致,同时 width="100% 保留了水平方向的响应式能力。

web/src/components/ui/chart.tsx (1)

66-68: 关于双重嵌套 ResponsiveContainer 的警告可以移除。

ChartContainerchildren 类型已通过 TypeScript 类型定义约束为 React.ComponentProps<typeof RechartsPrimitive.ResponsiveContainer>['children'],即 ResponsiveContainer 直接接受的图表元素(如 AreaChartBarChart),而不是 ResponsiveContainer 本身。这个类型约束在编译时就会防止任何尝试将 ResponsiveContainer 作为 children 传入的代码。实际的调用处(如 overview.tsx)均按正确方式使用:直接传入图表组件而不是包装它们。无需担心双重嵌套问题。

Likely an incorrect or invalid review comment.

web/src/locales/zh.json (1)

929-932: and

internal/executor/middleware_dispatch.go (1)

55-83: 请求转换的错误回退逻辑改进良好。

转换失败时优雅地回退到原始格式(重置 needsConversioncurrentClientType),而不是直接传播错误,提升了容错性。URI 转换也正确地只在转换成功时执行。

web/src/pages/providers/context/provider-form-context.tsx (1)

43-43: LGTM!

默认值 false 与需求一致(开关默认关闭),resetFormData 也会通过 initialFormData 正确重置该字段。

web/src/pages/providers/types.ts (1)

227-227: LGTM!

类型定义与后端 ProviderConfig.DisableErrorCooldown 和传输层 ProviderConfig.disableErrorCooldown 保持一致。

web/src/pages/providers/components/custom-config-step.tsx (2)

56-81: LGTM!

disableErrorCooldown 正确放置在 config 顶层(而非 config.custom 内部),与后端 ProviderConfig 结构一致。!! 双重否定确保值为严格布尔类型。


211-229: UI 部分实现清晰,与其他提供商视图保持一致。

Switch 绑定和 i18n key 使用正确。

web/src/pages/providers/components/antigravity-provider-view.tsx (1)

319-342: 实现模式正确,乐观更新 + 失败回滚处理得当。

handleToggleDisableErrorCooldown 遵循了与 handleToggleCLIProxyAPI 一致的乐观更新模式,disabled={updateProvider.isPending} 防止了两个开关之间的并发更新竞态。

web/src/pages/providers/components/codex-provider-view.tsx (1)

391-413: 实现与 Antigravity 视图保持一致,逻辑正确。

乐观更新、错误回滚和并发保护均与其他提供商视图一致。

web/src/locales/en.json (1)

930-930: 删除未使用的 cloakTitle 键或将其改为非编号形式。

cloakTitle 在代码库中从未被引用(仅存在于 en.json 和 zh.json 的第 899 行),而 errorCooldownTitle 是实际显示的第三个部分,编号为 "3. Error Cooldown" 是正确的。建议将 cloakTitle 从两个 JSON 文件中移除,或改为非编号形式以避免混淆。

Likely an incorrect or invalid review comment.

web/src/pages/providers/components/kiro-provider-view.tsx (1)

287-287: 无需修改,代码类型正确

Provider.id 已在类型定义中明确声明为 number 类型(web/src/lib/transport/types.ts 第 69 行),useUpdateProvider 的签名也期望接收 { id: number }。kiro-provider-view.tsx 第 287 行直接传递 id: provider.id 的写法完全正确,不存在类型不匹配问题。

provider-edit-flow.tsx 第 379 行使用 Number(provider.id) 是冗余的(将已经是 number 的值再次转换),但这不能作为 provider.id 不是 number 类型的证据。

Likely an incorrect or invalid review comment.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/executor/executor.go`:
- Around line 233-235: The current shouldSkipErrorCooldown() causes
handleCooldown() to be skipped entirely, which ignores explicit upstream
cooldowns; change the logic so explicit cooldown fields (CooldownUntil,
RetryAfter, RateLimitInfo.QuotaResetTime) are always processed before consulting
provider.Config.DisableErrorCooldown, and only the policy-based branches
(server/network/unknown error handling) are gated by DisableErrorCooldown.
Concretely: in middleware_dispatch.go call a refactored handler that first
applies explicit-time handling from handleCooldown (or a new
extractExplicitCooldowns() helper) unconditionally, then only run the
policy-based portion of handleCooldown if shouldSkipErrorCooldown() is false (or
rename shouldSkipErrorCooldown to shouldSkipPolicyCooldown and use it to guard
the policy branch inside handleCooldown or at the call site). Ensure references
to shouldSkipErrorCooldown, handleCooldown and the explicit fields
(CooldownUntil, RetryAfter, RateLimitInfo.QuotaResetTime) are updated
accordingly.

In `@web/src/pages/providers/components/provider-edit-flow.tsx`:
- Around line 592-597: The Switch for formData.disableErrorCooldown can still be
toggled while saving is true, causing inconsistent state; update the Switch (the
component rendering checked={!!formData.disableErrorCooldown} and using
onCheckedChange which calls setFormData) to respect the saving flag by adding a
disabled prop tied to saving and guard the onCheckedChange handler (no-op when
saving) so state cannot change during submission.

---

Duplicate comments:
In `@web/src/pages/providers/components/codex-provider-view.tsx`:
- Around line 578-595: This UI block is duplicated in codex-provider-view.tsx
and antigravity-provider-view.tsx; extract it into a shared React component
(e.g., ProviderDisableErrorCooldown) that accepts props: checked (boolean),
onChange (handler), disabled (boolean) and optional translation keys or uses the
existing i18n `t` in its parent; replace the duplicated JSX in both
CodexProviderView and AntigravityProviderView with this new component, wiring
checked -> disableErrorCooldown, onChange -> handleToggleDisableErrorCooldown,
and disabled -> updateProvider.isPending to remove the duplication.

---

Nitpick comments:
In `@internal/executor/middleware_dispatch.go`:
- Around line 333-340: The guard currently calls
shouldSkipErrorCooldown(matchedRoute.Provider) but then dereferences
matchedRoute.Provider.ID inside the block; update the condition to explicitly
ensure matchedRoute.Provider is non-nil (e.g., change the if to check
matchedRoute.Provider != nil && !shouldSkipErrorCooldown(matchedRoute.Provider))
so that uses of matchedRoute.Provider.ID, e.handleCooldown and
e.broadcaster.BroadcastMessage("cooldown_update", ...) are nil-safe and avoid
potential nil pointer dereference.

In `@web/src/pages/projects/index.tsx`:
- Around line 44-60: The comparator in the useMemo for sortedProjects constructs
the same Date twice per item; refactor the comparator inside sortedProjects to
create each Date once (e.g., compute const dateA = new
Date(a.createdAt).getTime() and const dateB = new Date(b.createdAt).getTime()),
then use Number.isFinite checks against those cached values (or default to 0)
and compare dateA/dateB before falling back to a.id - b.id; update variables
timeA/timeB (or replace them) accordingly to avoid duplicate Date construction.

In `@web/src/pages/providers/components/antigravity-provider-view.tsx`:
- Around line 471-488: Extract the repeated label+description+Switch UI into a
reusable React component (e.g., DisableErrorCooldownSwitch) to replace
duplicated blocks in antigravity-provider-view, codex-provider-view,
kiro-provider-view and custom-config-step; the new component should accept
props: checked (boolean) mapped to disableErrorCooldown, onChange mapped to
handleToggleDisableErrorCooldown, and disabled mapped to
updateProvider.isPending (or equivalent pending state), render the same text via
i18n keys 'provider.disableErrorCooldown' and
'provider.disableErrorCooldownDesc', and then update each file to import and use
<DisableErrorCooldownSwitch checked={disableErrorCooldown}
onChange={handleToggleDisableErrorCooldown} disabled={updateProvider.isPending}
/> instead of the inline markup.

In `@web/src/pages/providers/components/kiro-provider-view.tsx`:
- Around line 276-278: The useEffect currently uses a complex optional-chained
expression in its dependency array (provider.config?.disableErrorCooldown) which
can trigger eslint-plugin-react-hooks warnings; fix it by extracting the value
to a local constant (e.g. const currentDisableErrorCooldown =
provider.config?.disableErrorCooldown ?? false) and then update the effect to
call setDisableErrorCooldown(currentDisableErrorCooldown) with
[currentDisableErrorCooldown] as the dependency; reference useEffect and
setDisableErrorCooldown to locate and update the code.
- Around line 280-302: The guard in handleToggleDisableErrorCooldown incorrectly
returns when provider.config.kiro is falsy and also redundantly reassigns kiro;
remove the early return and the explicit kiro: { ...kiroConfig } override, and
instead call updateProvider.mutateAsync with data: { ...provider, config: {
...provider.config, disableErrorCooldown: checked } } so the top-level
disableErrorCooldown is updated regardless of kiro presence while preserving
existing config and kiro; keep the optimistic setDisableErrorCooldown(checked)
and restore the prev value in the catch as currently implemented.

In `@web/src/pages/providers/components/provider-edit-flow.tsx`:
- Line 44: The import for Switch is using the barrel module while other UI
components use specific paths; update the Switch import in
provider-edit-flow.tsx to match the direct import style used for Button and
Input (i.e., import Switch from the specific component file instead of from
'@/components/ui') so all UI imports are consistent; locate the Switch symbol in
the file and change its import to the corresponding direct path.
- Line 356: 在 provider-edit-flow.tsx 中为对象属性 disableErrorCooldown
赋值时不需要使用双重否定转换;formData.disableErrorCooldown 已在初始化时用 `?? false` 保证为 boolean,直接使用
`disableErrorCooldown: formData.disableErrorCooldown` 即可,删去 `!!`
强制转换以简化代码(定位符:formData.disableErrorCooldown / disableErrorCooldown)。

Comment on lines +233 to +235
func shouldSkipErrorCooldown(provider *domain.Provider) bool {
return provider != nil && provider.Config != nil && provider.Config.DisableErrorCooldown
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find shouldSkipErrorCooldown usage in middleware_dispatch.go
find . -type f -name "middleware_dispatch.go" | head -5

Repository: awsl-project/maxx

Length of output: 103


🏁 Script executed:

#!/bin/bash
# Search for shouldSkipErrorCooldown and handleCooldown usage patterns
rg -n "shouldSkipErrorCooldown|handleCooldown" --type=go -A 3 -B 3

Repository: awsl-project/maxx

Length of output: 3163


🏁 Script executed:

#!/bin/bash
# Check the executor.go file to see the full context of shouldSkipErrorCooldown and nearby functions
cat -n internal/executor/executor.go | sed -n '225,250p'

Repository: awsl-project/maxx

Length of output: 1127


🏁 Script executed:

#!/bin/bash
# Get the full handleCooldown implementation
cat -n internal/executor/executor.go | sed -n '172,231p'

Repository: awsl-project/maxx

Length of output: 3012


🏁 Script executed:

#!/bin/bash
# Look for explicit cooldown handling (CooldownUntil, RetryAfter, QuotaResetTime)
rg -n "CooldownUntil|RetryAfter|QuotaResetTime" --type=go -B 2 -A 2 internal/executor/executor.go

Repository: awsl-project/maxx

Length of output: 969


🏁 Script executed:

#!/bin/bash
# Check the ProxyError struct and its fields related to cooldown
rg -n "type ProxyError" --type=go -A 30

Repository: awsl-project/maxx

Length of output: 2407


应分离显式 API 冷冻与策略类冷冻的处理逻辑。

当前实现在 middleware_dispatch.go 第 333-334 行通过 shouldSkipErrorCooldown() 跳过整个 handleCooldown() 调用,这导致来自上游的显式冷冻时间(CooldownUntilRetryAfterRateLimitInfo.QuotaResetTime)也被忽略,与需求矛盾。

handleCooldown() 内部同时处理两类情况:

  • 显式时间(第 192-207 行):CooldownUntilQuotaResetTimeRetryAfter
  • 策略类(第 208-220 行):服务器错误、网络错误、未知错误

需要将显式冷冻逻辑提前至 shouldSkipErrorCooldown() 检查之外,仅对策略类冷冻分支应用开关控制。这样当 DisableErrorCooldown=true 时,显式 API 冷冻时间仍被尊重,而错误触发的 policy-based 冷冻被禁用。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/executor/executor.go` around lines 233 - 235, The current
shouldSkipErrorCooldown() causes handleCooldown() to be skipped entirely, which
ignores explicit upstream cooldowns; change the logic so explicit cooldown
fields (CooldownUntil, RetryAfter, RateLimitInfo.QuotaResetTime) are always
processed before consulting provider.Config.DisableErrorCooldown, and only the
policy-based branches (server/network/unknown error handling) are gated by
DisableErrorCooldown. Concretely: in middleware_dispatch.go call a refactored
handler that first applies explicit-time handling from handleCooldown (or a new
extractExplicitCooldowns() helper) unconditionally, then only run the
policy-based portion of handleCooldown if shouldSkipErrorCooldown() is false (or
rename shouldSkipErrorCooldown to shouldSkipPolicyCooldown and use it to guard
the policy branch inside handleCooldown or at the call site). Ensure references
to shouldSkipErrorCooldown, handleCooldown and the explicit fields
(CooldownUntil, RetryAfter, RateLimitInfo.QuotaResetTime) are updated
accordingly.

Comment on lines +592 to +597
<Switch
checked={!!formData.disableErrorCooldown}
onCheckedChange={(checked) =>
setFormData((prev) => ({ ...prev, disableErrorCooldown: checked }))
}
/>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

保存进行中时 Switch 未被禁用

savingtrue 时保存按钮已禁用,但 Switch 没有对应的 disabled 守卫,用户在提交飞行中仍可切换开关,可能引起状态不一致。

🛡️ 建议修改
               <Switch
                 checked={!!formData.disableErrorCooldown}
                 onCheckedChange={(checked) =>
                   setFormData((prev) => ({ ...prev, disableErrorCooldown: checked }))
                 }
+                disabled={saving}
               />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/providers/components/provider-edit-flow.tsx` around lines 592 -
597, The Switch for formData.disableErrorCooldown can still be toggled while
saving is true, causing inconsistent state; update the Switch (the component
rendering checked={!!formData.disableErrorCooldown} and using onCheckedChange
which calls setFormData) to respect the saving flag by adding a disabled prop
tied to saving and guard the onCheckedChange handler (no-op when saving) so
state cannot change during submission.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

新增禁止冻结开关,仅调整错误冻结逻辑(提供商使用方式不变)

2 participants