Skip to content

feat(group): add lowest-ratio AutoOpt with token filters#6088

Open
gao-gao-zai wants to merge 3 commits into
QuantumNous:mainfrom
gao-gao-zai:codex/autoopt-group-filters-upstream
Open

feat(group): add lowest-ratio AutoOpt with token filters#6088
gao-gao-zai wants to merge 3 commits into
QuantumNous:mainfrom
gao-gao-zai:codex/autoopt-group-filters-upstream

Conversation

@gao-gao-zai

@gao-gao-zai gao-gao-zai commented Jul 10, 2026

Copy link
Copy Markdown

⚠️ 提交说明 / PR Notice

Important

  • 请提供人工撰写的简洁摘要,避免直接粘贴未经整理的 AI 输出。

📝 变更描述 / Description

新增 AutoOpt 虚拟分组,在用户当前有权使用且已配置倍率的分组中,按用户实际分组倍率从低到高选择能够服务当前模型的渠道。相同倍率使用分组名稳定排序,渠道选择复用现有缓存、优先级和重试逻辑。

令牌可以为 AutoOpt 配置白名单或黑名单。创建和更新令牌时,服务端会校验模式和分组权限,限制过滤字段长度,并对分组去重、排序和归一化。请求执行时会再次与用户的当前可用分组取交集,防止历史令牌配置扩大权限;无效配置会拒绝或 fail closed。

web/default 的令牌编辑界面增加白名单/黑名单切换和多分组选择,并补齐 7 个 locale 的文案。本 PR 不包含 Classic 前端改动。

AI 辅助声明:本次代码在 Codex 辅助下实现、审查和验证,提交者将在提交 PR 前人工检查最终差异和本描述。

🚀 变更类型 / Type of change

  • 🐛 Bug 修复 (Bug fix) - 请关联对应 Issue,避免将设计取舍、理解偏差或预期不一致直接归类为 bug
  • ✨ 新功能 (New feature) - 重大特性建议先通过 Issue 沟通
  • ⚡ 性能优化 / 重构 (Refactor)
  • 📝 文档更新 (Documentation)

🔗 关联任务 / Related Issue

✅ 提交前检查项 / Checklist

  • 人工确认: 我已亲自整理并撰写此描述,没有直接粘贴未经处理的 AI 输出。
  • 非重复提交: 我已搜索现有的 IssuesPRs,确认不是重复提交。
  • Bug fix 说明: 若此 PR 标记为 Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。
  • 变更理解: 我已理解这些更改的工作原理及可能影响。
  • 范围聚焦: 本 PR 未包含任何与当前任务无关的代码改动。
  • 本地验证: 已在本地运行并通过测试或手动验证,维护者可以据此复核结果。
  • 安全合规: 代码中无敏感凭据,且符合项目代码规范。

📸 运行证明 / Proof of Work

go test ./controller ./service ./middleware ./model
ok  github.com/QuantumNous/new-api/controller
ok  github.com/QuantumNous/new-api/service
ok  github.com/QuantumNous/new-api/middleware
ok  github.com/QuantumNous/new-api/model

bun run typecheck
$ tsgo -b

bun x oxlint -c .oxlintrc.json <8 changed TS/TSX files>
0 errors

bun x oxfmt --check <8 changed TS/TSX files>
All matched files use the correct format.

bun run build
Rsbuild v2.1.4
ready built

Summary by CodeRabbit

  • New Features

    • Added AutoOpt support for model, group, and channel selection using lowest-ratio available groups.
    • Extended tokens and API keys with AutoOpt policy (whitelist/blacklist) and selectable group lists.
    • Added AutoOpt configuration UI (filters, badges/tooltips, and form validation).
  • Bug Fixes

    • Improved handling for empty model lists and safer group enablement resolution.
    • Hardened AutoOpt validation/normalization for token add/update flows.
  • Tests

    • Added coverage for AutoOpt group resolution, permissions, and AutoOpt token policy behavior.
  • Documentation

    • Updated AutoOpt UI translations in supported languages.

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Adds AutoOpt group policies across token storage, authorization, model and channel selection, API key management, frontend display, tests, and localized UI text.

Changes

AutoOpt policy and persistence

Layer / File(s) Summary
Policy and token persistence
service/group.go, model/token.go, model/ability.go, constant/context_key.go, controller/token.go, middleware/auth.go
Adds ratio-based AutoOpt group derivation, whitelist/blacklist filtering, token fields, normalization, persistence, and request-context propagation.
Model and channel routing
controller/group.go, controller/model.go, controller/user.go, middleware/distributor.go, service/channel_select.go, controller/*test.go
Resolves priced and AutoOpt groups for model listings and selects channels from policy-filtered candidates, with coverage for permitted, rejected, and filtered cases.
API key configuration and frontend support
web/default/src/features/keys/*, web/default/src/components/model-group-selector.tsx, web/default/src/features/playground/*
Adds AutoOpt form fields, validation, payload conversion, group badges, policy controls, and string-ratio handling.
Localized AutoOpt labels and guidance
web/default/src/i18n/locales/*
Adds or updates AutoOpt selection, filtering, whitelist, and exclusion translations.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant TokenAuth
  participant AutoOptPolicy
  participant ModelController
  participant ChannelSelector
  TokenAuth->>AutoOptPolicy: load token mode and configured groups
  AutoOptPolicy-->>ModelController: return ordered eligible groups
  ModelController->>ChannelSelector: try channels for candidate groups
  ChannelSelector-->>ModelController: return selected channel and group affinity
Loading

Possibly related PRs

Suggested labels: enhancement

Suggested reviewers: Calcium-Ion

Poem

I’m a rabbit with groups in a row,
Sorting by ratios wherever they go.
Tokens remember each policy there,
AutoOpt hops to channels fair.
Whitelist or blacklist, bright and light—
Translated labels make things right.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: AutoOpt group selection with token filtering.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
web/default/src/features/keys/lib/api-key-form.ts (1)

42-58: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider mirroring the backend's AutoOpt group-count/length limit on the frontend.

The PR objective states token creation/update "limit filter lengths" server-side, but this form schema has no upper bound on auto_opt_groups. Without a client-side max, users can select an arbitrarily large list and only find out it's rejected after a round-trip to the server.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/features/keys/lib/api-key-form.ts` around lines 42 - 58, The
api-key form schema’s auto_opt_groups field lacks the backend’s maximum
group-count validation. Update the schema near the auto_opt_groups definition
and its superRefine validation to enforce the same maximum length as the
backend, with a localized validation message, while preserving the existing
AutoOpt whitelist non-empty check.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@service/channel_select.go`:
- Around line 90-113: The AutoOpt selection path aborts on group errors and does
not preserve cross-group retry progress. In the AutoOpt branch of the
channel-selection function, log GetRandomSatisfiedChannel errors and continue
evaluating remaining groups, then add crossGroupRetry and
ContextKeyAutoGroupIndex handling analogous to the "auto" branch so retries
advance through candidates instead of restarting; if unsupported, document that
behavior explicitly.

---

Nitpick comments:
In `@web/default/src/features/keys/lib/api-key-form.ts`:
- Around line 42-58: The api-key form schema’s auto_opt_groups field lacks the
backend’s maximum group-count validation. Update the schema near the
auto_opt_groups definition and its superRefine validation to enforce the same
maximum length as the backend, with a localized validation message, while
preserving the existing AutoOpt whitelist non-empty check.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8100ec0b-5f91-43b0-91df-154594cdf071

📥 Commits

Reviewing files that changed from the base of the PR and between f2c7cd3 and 7e02e0b.

📒 Files selected for processing (30)
  • constant/context_key.go
  • controller/group.go
  • controller/model.go
  • controller/model_list_test.go
  • controller/model_owned_by_test.go
  • controller/token.go
  • controller/token_test.go
  • controller/user.go
  • middleware/auth.go
  • middleware/distributor.go
  • model/ability.go
  • model/token.go
  • service/channel_select.go
  • service/group.go
  • service/group_test.go
  • web/default/src/components/model-group-selector.tsx
  • web/default/src/features/keys/components/api-key-group-combobox.tsx
  • web/default/src/features/keys/components/api-keys-columns.tsx
  • web/default/src/features/keys/components/api-keys-mutate-drawer.tsx
  • web/default/src/features/keys/lib/api-key-form.ts
  • web/default/src/features/keys/types.ts
  • web/default/src/features/playground/api.ts
  • web/default/src/features/playground/types.ts
  • web/default/src/i18n/locales/en.json
  • web/default/src/i18n/locales/fr.json
  • web/default/src/i18n/locales/ja.json
  • web/default/src/i18n/locales/ru.json
  • web/default/src/i18n/locales/vi.json
  • web/default/src/i18n/locales/zh-TW.json
  • web/default/src/i18n/locales/zh.json

Comment thread service/channel_select.go
@gao-gao-zai gao-gao-zai force-pushed the codex/autoopt-group-filters-upstream branch from 7e02e0b to e5ebdc2 Compare July 10, 2026 17:33
@gao-gao-zai

Copy link
Copy Markdown
Author

Addressed the CodeRabbit feedback:

  • AutoOpt continues to later candidate groups after a selection error and returns the last error only when no candidate succeeds.
  • CrossGroupRetry is normalized off for AutoOpt because that stateful behavior is defined only for the legacy �uto group.
  • The default frontend now validates the serialized AutoOpt group filter against the backend's 4096-byte limit, with all seven locales updated.
  • Added regression coverage for the backend byte limit and CrossGroupRetry normalization.

Validation passed: targeted Go packages, TypeScript typecheck, oxlint, UTF-8 boundary check, i18n sync, and production build.

@gao-gao-zai gao-gao-zai force-pushed the codex/autoopt-group-filters-upstream branch from e5ebdc2 to 94a8e68 Compare July 10, 2026 18:29

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
middleware/distributor.go (1)

122-137: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Optional: de-duplicate the two affinity group-selection loops.

The new AutoOpt branch (Lines 122-137) is an exact copy of the auto branch loop body (Lines 112-121); only the candidate-group source differs. Consider extracting a small local closure so the two paths cannot drift apart. A local closure here is justified since it captures the shared selectGroup/channel/affinityUsable state and would have two callers.

♻️ Proposed refactor
+					selectFromGroups := func(groups []string) {
+						for _, g := range groups {
+							if model.IsChannelEnabledForGroupModel(g, modelRequest.Model, preferred.Id) {
+								selectGroup = g
+								common.SetContextKey(c, constant.ContextKeyAutoGroup, g)
+								channel = preferred
+								affinityUsable = true
+								service.MarkChannelAffinityUsed(c, g, preferred.Id)
+								break
+							}
+						}
+					}
 					if usingGroup == "auto" {
-						autoGroups := service.GetUserAutoGroup(userGroup)
-						for _, g := range autoGroups {
-							if model.IsChannelEnabledForGroupModel(g, modelRequest.Model, preferred.Id) {
-								selectGroup = g
-								common.SetContextKey(c, constant.ContextKeyAutoGroup, g)
-								channel = preferred
-								affinityUsable = true
-								service.MarkChannelAffinityUsed(c, g, preferred.Id)
-								break
-							}
-						}
+						selectFromGroups(service.GetUserAutoGroup(userGroup))
 					} else if service.IsAutoOptGroup(usingGroup) {
-						autoOptGroups := service.GetUserAutoOptGroupsWithPolicy(
+						selectFromGroups(service.GetUserAutoOptGroupsWithPolicy(
 							userGroup,
 							common.GetContextKeyString(c, constant.ContextKeyTokenAutoOptMode),
 							common.GetContextKeyStringSlice(c, constant.ContextKeyTokenAutoOptGroups),
-						)
-						for _, g := range autoOptGroups {
-							if model.IsChannelEnabledForGroupModel(g, modelRequest.Model, preferred.Id) {
-								selectGroup = g
-								common.SetContextKey(c, constant.ContextKeyAutoGroup, g)
-								channel = preferred
-								affinityUsable = true
-								service.MarkChannelAffinityUsed(c, g, preferred.Id)
-								break
-							}
-						}
+						))
 					} else if model.IsChannelEnabledForGroupModel(usingGroup, modelRequest.Model, preferred.Id) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@middleware/distributor.go` around lines 122 - 137, Deduplicate the repeated
affinity group-selection logic in the auto and AutoOpt branches of the
distributor flow. Extract the shared loop body into a local closure that accepts
the candidate groups, captures and updates selectGroup, channel, affinityUsable,
and affinity state, then invoke it from both branches while preserving the
existing selection and break behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@middleware/distributor.go`:
- Around line 122-137: Deduplicate the repeated affinity group-selection logic
in the auto and AutoOpt branches of the distributor flow. Extract the shared
loop body into a local closure that accepts the candidate groups, captures and
updates selectGroup, channel, affinityUsable, and affinity state, then invoke it
from both branches while preserving the existing selection and break behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f205f812-7da8-48c5-8f57-b30656ef9b42

📥 Commits

Reviewing files that changed from the base of the PR and between e5ebdc2 and 94a8e68.

📒 Files selected for processing (30)
  • constant/context_key.go
  • controller/group.go
  • controller/model.go
  • controller/model_list_test.go
  • controller/model_owned_by_test.go
  • controller/token.go
  • controller/token_test.go
  • controller/user.go
  • middleware/auth.go
  • middleware/distributor.go
  • model/ability.go
  • model/token.go
  • service/channel_select.go
  • service/group.go
  • service/group_test.go
  • web/default/src/components/model-group-selector.tsx
  • web/default/src/features/keys/components/api-key-group-combobox.tsx
  • web/default/src/features/keys/components/api-keys-columns.tsx
  • web/default/src/features/keys/components/api-keys-mutate-drawer.tsx
  • web/default/src/features/keys/lib/api-key-form.ts
  • web/default/src/features/keys/types.ts
  • web/default/src/features/playground/api.ts
  • web/default/src/features/playground/types.ts
  • web/default/src/i18n/locales/en.json
  • web/default/src/i18n/locales/fr.json
  • web/default/src/i18n/locales/ja.json
  • web/default/src/i18n/locales/ru.json
  • web/default/src/i18n/locales/vi.json
  • web/default/src/i18n/locales/zh-TW.json
  • web/default/src/i18n/locales/zh.json
✅ Files skipped from review due to trivial changes (6)
  • web/default/src/i18n/locales/fr.json
  • web/default/src/i18n/locales/zh.json
  • web/default/src/i18n/locales/vi.json
  • web/default/src/i18n/locales/ja.json
  • web/default/src/i18n/locales/zh-TW.json
  • web/default/src/i18n/locales/ru.json
🚧 Files skipped from review as they are similar to previous changes (23)
  • web/default/src/features/playground/api.ts
  • web/default/src/features/keys/components/api-key-group-combobox.tsx
  • web/default/src/components/model-group-selector.tsx
  • constant/context_key.go
  • web/default/src/features/keys/components/api-keys-columns.tsx
  • controller/model_list_test.go
  • controller/user.go
  • controller/token_test.go
  • model/ability.go
  • controller/model_owned_by_test.go
  • controller/group.go
  • service/channel_select.go
  • controller/token.go
  • controller/model.go
  • web/default/src/features/playground/types.ts
  • web/default/src/features/keys/lib/api-key-form.ts
  • middleware/auth.go
  • model/token.go
  • web/default/src/features/keys/components/api-keys-mutate-drawer.tsx
  • web/default/src/features/keys/types.ts
  • web/default/src/i18n/locales/en.json
  • service/group_test.go
  • service/group.go

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.

1 participant