Conversation
- Use fixed pixel height instead of percentage for ResponsiveContainer - Add minHeight guard to ChartContainer's ResponsiveContainer - Upgrade bytedance/sonic v1.14.2 → v1.15.0 for Go 1.26.0 compat - Add keepPreviousData to usage stats query Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Closes awsl-project#204 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
📝 WalkthroughWalkthrough此 PR 在后端和前端添加了 Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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.tsx、codex-provider-view.tsx、kiro-provider-view.tsx和custom-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函数通过短路求值正确处理了provider为nil的情况(第 233-235 行):return provider != nil && provider.Config != nil && provider.Config.DisableErrorCooldown当
provider为nil时返回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/button、Input从@/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-hooks的exhaustive-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 展开冗余存在两个关联问题:
提前返回守卫过严:
disableErrorCooldown是顶层ProviderConfig字段,与kiro子配置无关。若kiroConfig为空(虽然极少见),开关将静默失效,用户不会收到任何反馈。冗余的显式 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
📒 Files selected for processing (18)
internal/domain/model.gointernal/executor/executor.gointernal/executor/middleware_dispatch.goweb/src/components/layout/app-sidebar/nav-user.tsxweb/src/components/ui/chart.tsxweb/src/hooks/queries/use-usage-stats.tsweb/src/lib/transport/types.tsweb/src/locales/en.jsonweb/src/locales/zh.jsonweb/src/pages/projects/index.tsxweb/src/pages/providers/components/antigravity-provider-view.tsxweb/src/pages/providers/components/codex-provider-view.tsxweb/src/pages/providers/components/custom-config-step.tsxweb/src/pages/providers/components/kiro-provider-view.tsxweb/src/pages/providers/components/provider-edit-flow.tsxweb/src/pages/providers/context/provider-form-context.tsxweb/src/pages/providers/types.tsweb/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的警告可以移除。
ChartContainer的children类型已通过 TypeScript 类型定义约束为React.ComponentProps<typeof RechartsPrimitive.ResponsiveContainer>['children'],即ResponsiveContainer直接接受的图表元素(如AreaChart、BarChart),而不是ResponsiveContainer本身。这个类型约束在编译时就会防止任何尝试将ResponsiveContainer作为 children 传入的代码。实际的调用处(如overview.tsx)均按正确方式使用:直接传入图表组件而不是包装它们。无需担心双重嵌套问题。Likely an incorrect or invalid review comment.
web/src/locales/zh.json (1)
929-932:andinternal/executor/middleware_dispatch.go (1)
55-83: 请求转换的错误回退逻辑改进良好。转换失败时优雅地回退到原始格式(重置
needsConversion和currentClientType),而不是直接传播错误,提升了容错性。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)。
| func shouldSkipErrorCooldown(provider *domain.Provider) bool { | ||
| return provider != nil && provider.Config != nil && provider.Config.DisableErrorCooldown | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find shouldSkipErrorCooldown usage in middleware_dispatch.go
find . -type f -name "middleware_dispatch.go" | head -5Repository: 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 3Repository: 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.goRepository: 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 30Repository: awsl-project/maxx
Length of output: 2407
应分离显式 API 冷冻与策略类冷冻的处理逻辑。
当前实现在 middleware_dispatch.go 第 333-334 行通过 shouldSkipErrorCooldown() 跳过整个 handleCooldown() 调用,这导致来自上游的显式冷冻时间(CooldownUntil、RetryAfter、RateLimitInfo.QuotaResetTime)也被忽略,与需求矛盾。
handleCooldown() 内部同时处理两类情况:
- 显式时间(第 192-207 行):
CooldownUntil、QuotaResetTime、RetryAfter - 策略类(第 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.
| <Switch | ||
| checked={!!formData.disableErrorCooldown} | ||
| onCheckedChange={(checked) => | ||
| setFormData((prev) => ({ ...prev, disableErrorCooldown: checked })) | ||
| } | ||
| /> |
There was a problem hiding this comment.
保存进行中时 Switch 未被禁用
saving 为 true 时保存按钮已禁用,但 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.
Summary
Testing
Closes #239
Summary by CodeRabbit
发布说明
新功能
改进