Skip to content

feat(settings): OpenAI 403 临时冷却参数改为后台可配置 - #6347

Open
ryanchou1994 wants to merge 1 commit into
Wei-Shaw:mainfrom
ryanchou1994:fix/openai-403-cooldown-configurable
Open

feat(settings): OpenAI 403 临时冷却参数改为后台可配置#6347
ryanchou1994 wants to merge 1 commit into
Wei-Shaw:mainfrom
ryanchou1994:fix/openai-403-cooldown-configurable

Conversation

@ryanchou1994

Copy link
Copy Markdown
Contributor

问题

#5280:OpenAI 账号只要收到 403 就被临时冷却 10 分钟,窗口内累计 3 次直接转永久错误。这三个数字全部硬编码在 backend/internal/service/ratelimit_service.go

openAI403CooldownMinutesDefault = 10
openAI403DisableThreshold       = 3
openAI403CounterWindowMinutes   = 180

不少部署里 403 是 IP / 代理出口被上游拒绝,和账号本身无关;管理员想把冷却调短、把阈值调高,或干脆只换号不罚号,目前都只能改代码重新构建。

修复

把这三个参数(外加一个总开关)做成系统设置,接线完全照仓库里已有的 529 过载冷却OverloadCooldownSettings)和 429 默认回避RateLimit429CooldownSettings)两条范本复制,没有引入新的模式:

后端

  • settings_view.go:新增 OpenAI403CooldownSettings { enabled, cooldown_minutes, disable_threshold, window_minutes }DefaultOpenAI403CooldownSettings()
  • domain_constants.go:设置键 openai_403_cooldown_settings
  • setting_features.goGet 缺值 / 空值 / 坏 JSON 回默认,读到的数值 clamp 到范围;Set 启用状态下越界直接报错(错误信息带字段名),停用状态下越界归一化为默认值——与 529 的处理一致
  • ratelimit_service.gohandleOpenAI403 在既有 HTML 403 守卫之后读取设置;enabled=false 时只换号不罚号(不递增计数、不临时停调、不写永久错误,和 HTML 403 分支同一口径);冷却时长 / 阈值 / 计数窗口三处全部改走配置。读取失败或未注入 SettingService 时回落默认值
  • setting_handler_runtime.go + routes/admin.goGET / PUT /api/v1/admin/settings/openai-403-cooldown

前端

  • 设置页「网关」分组内、429 卡片之后新增「OpenAI 403 临时冷却」卡片:开关 + 三个数字输入(带 min / max),中英文案

范围与默认值

  • 默认值锁定为原常量:启用 / 10 分钟 / 3 次 / 180 分钟。没有改过设置的部署升级后行为逐字不变。
  • 三个原常量保留、名字不改,作为唯一默认值来源;ratelimit_cn_providers.go 里 Kimi 并发限制借用 openAI403CooldownMinutesDefault 的路径不在本 PR 范围内,行为不变。
  • 数值范围:分钟类 1-1440,阈值 1-100。

测试

新增 backend/internal/service/openai_403_cooldown_test.go-tags=unit),10 个用例:

用例 断言
TestGetOpenAI403CooldownSettings_DefaultsWhenNotSet 未设置 ⇒ 启用 / 10 / 3 / 180
TestGetOpenAI403CooldownSettings_ClampsOutOfRange 存 99999 / 0 / -5 ⇒ 读回 1440 / 1 / 1
TestSetOpenAI403CooldownSettings_RejectsOutOfRangeWhenEnabled 三个字段各自越界 ⇒ 报错且信息含字段名
TestSetOpenAI403CooldownSettings_NormalizesWhenDisabled 停用 + 越界 ⇒ 不报错、存回默认
TestHandleOpenAI403_UsesConfiguredCooldownMinutes 配 2 分钟 ⇒ SetTempUnschedulableuntil 落在 now+2min,不是 +10min
TestHandleOpenAI403_UsesConfiguredThreshold 配阈值 2:count=2 ⇒ 永久错误;count=1 ⇒ 临时停调
TestHandleOpenAI403_PassesConfiguredWindowToCounter 配 30 分钟窗口 ⇒ IncrementOpenAI403Count 收到 30
TestHandleOpenAI403_DisabledSkipsAccountPenalty 停用 ⇒ 返回 false,零计数 / 零停调 / 零永久错误 / 零阻断通知
TestHandleOpenAI403_FallsBackToDefaultsWithoutSettingService 未注入 SettingService ⇒ 10 分钟 + 180 窗口
TestHandleOpenAI403_HTMLBodyStillSkipsPenaltyWhenEnabled 启用 + HTML 403 ⇒ 仍无处罚(守卫顺序没被破坏)

红 → 绿验证(golang:1.27 容器):

  • handleOpenAI403 的三处读取改回常量 ⇒ 冷却时长 / 阈值 / 窗口三个用例失败,其余通过
  • 删掉 enabled=false 分支 ⇒ DisabledSkipsAccountPenalty 失败
  • 把设置分支挪到 HTML 守卫之前 ⇒ HTMLBodyStillSkipsPenaltyWhenEnabled 等失败
  • 还原后 10/10 通过;go test -tags=unit ./... 全绿;gofmt -l 空;go vet 通过
  • 前端 pnpm typecheck / pnpm lint:check 通过

既有引用这三个常量的测试(openai_gateway_cn_fixes_test.goratelimit_service_403_html_test.go 等)未改动,默认值不变所以原样通过。

查重与已知限制

  • 提交前搜过 open PR 与 issue 只要403就冷却10分钟苦不堪言 #5280 的引用,没有人在做同一件事。
  • enabled=false 意味着账号级 403(比如真的被封)也不会再自动进错误状态,只靠换号——这是把选择权交给管理员,默认仍是启用;文案里已写明。
  • 没有加 DB migration,设置走既有 key-value 存储,与 529 / 429 一样。

OpenAI 账号收到 403 后的处置策略此前完全硬编码在 ratelimit_service.go:
单次临时冷却 10 分钟、计数窗口 180 分钟、窗口内累计 3 次转永久错误。
403 在很多部署里是 IP / 代理出口被拒而非账号问题,管理员却只能改代码
重新构建才能调整,issue Wei-Shaw#5280 及其下多位用户对此反馈强烈。

本次把这三个参数(外加一个总开关)做成系统设置,接线完全照既有的
529 过载冷却 / 429 默认回避两条范本:

- 后端:新增 OpenAI403CooldownSettings(enabled / cooldown_minutes /
  disable_threshold / window_minutes),设置键 openai_403_cooldown_settings,
  读端 clamp、写端启用时校验范围(1-1440 分钟 / 1-100 次)、停用时归一化;
  handleOpenAI403 在 HTML 403 守卫之后读取设置,三个数值全部改走配置;
  新增 GET/PUT /api/v1/admin/settings/openai-403-cooldown。
- 前端:设置页「网关」分组内新增「OpenAI 403 临时冷却」卡片,中英文案。

默认值锁定为原常量(启用 / 10 / 3 / 180),未改设置的部署升级后行为
逐字不变;原三个常量保留作为唯一默认值来源,Kimi 并发限制路径借用
openAI403CooldownMinutesDefault 的逻辑不受影响。

enabled=false 的语义是「403 只换号、不罚号」:不递增计数、不临时停调、
不写永久错误,与既有 HTML 403(端点级错误)分支的处置一致。

附 10 个单元测试:设置读写默认 / clamp / 校验 / 归一化各一,处理链上
冷却时长 / 阈值 / 窗口三项分别断言走配置值,停用跳过处罚、无设置服务
时回落默认、HTML 守卫仍先于设置分支各一。撤掉读取点或删掉停用分支时
对应测试实测失败。

Fixes Wei-Shaw#5280
Copilot AI lite review requested due to automatic review settings August 28, 2026 13:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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