feats: better group setting - #2812
Conversation
…-i2v Gemini Veo3.1[AI Studio]增加图生视频支持
Ensure image file is closed using defer after opening.
…edit Gemini Image系列支持图像编辑
…d-oai feat: 视频下载和界面预览统一使用OAI标准接口
…-err-code fix(aws): extract HTTP status code from AWS SDK errors
…na-err fix: nano-banana not compatible imageSize
…nce-playground-debugging feat(playground): enhance SSE debugging and add image paste support with i18n
fix: nano banana pro 4k(StreamScannerMaxBufferMB env)
feat: glm coding plan && kimi coding plan
…field fix: claude request missing field
fix(i18n): fill missing translations in i18n.
…ix-gemini-ImageConfig Revert "fix: gemini image correct generationConfig"
…emini-veo3.1-i2v Revert "Gemini Veo3.1[AI Studio]增加图生视频支持"
…emini-image-edit Revert "Gemini Image系列支持图像编辑"
…ix-nano-banana-err Revert "fix: nano-banana not compatible imageSize"
问题描述: - 使用 auto 分组的令牌调用 /v1/videos 等 Task 接口时,虽然任务能成功创建, 但使用日志不显示记录,且不会扣费 根本原因: - Distribute 中间件在选择渠道后,会将实际选中的分组存储在 ContextKeyAutoGroup 中 - 但 RelayTaskSubmit 函数没有从 context 中读取这个值来更新 info.UsingGroup - 导致 info.UsingGroup 始终是 "auto" 而不是实际选中的分组(如 "sora2逆") - 当 auto 分组的倍率配置为 0 时,quota 计算结果为 0 - 日志记录条件 "if quota != 0" 不满足,导致日志不记录、不扣费 修复方案: - 在 RelayTaskSubmit 函数中计算分组倍率之前,添加从 ContextKeyAutoGroup 获取实际分组的逻辑 - 使用安全的类型断言,避免潜在的 panic 风险 影响范围: - 仅影响 Task Relay 流程(/v1/videos, /suno, /kling 等接口) - 不影响使用具体分组令牌的调用 - 不影响其他 Relay 类型(chat/completions 等已有类似处理逻辑)
…task-logging fix(task): 修复使用 auto 分组时 Task Relay 不记录日志和不扣费的问题
fix: 设置默认max req body 为128MB
Use the native Gemini Models API (/v1beta/models) instead of the OpenAI-compatible path when listing models for Gemini channels, improving compatibility with third-party Gemini-format providers that don't implement OpenAI routes. - Add paginated model listing with timeout and optional proxy support - Select an enabled key for multi-key Gemini channels
…etchModels fix: remove Minimax from FETCHABLE channels
…tchModels fix(gemini): fetch model list via native v1beta/models endpoint
* feat: /v1/chat/completion -> /v1/response
…-auto-disable feat: status code auto-disable configuration
* fix: setting ui * fix: rm global.chat_completions_to_responses_policy * fix: rm global.chat_completions_to_responses_policy
…Names fix: clean propertyNames for gemini function
…t-param-override feat: channel testing supports parameter overriding
* feat: codex channel * feat: codex channel * feat: codex oauth flow * feat: codex refresh cred * feat: codex usage * fix: codex err message detail * fix: codex setting ui * feat: codex refresh cred task * fix: import err * fix: codex store must be false * fix: chat -> responses tool call * fix: chat -> responses tool call
…sing Claude's rendering logs, the two approaches handle input rendering differently.
WalkthroughThis change introduces per-model group configuration for channels. The backend adds a Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
model/ability.go (1)
146-176:⚠️ Potential issue | 🟡 MinorTrim and skip empty group names when generating abilities.
Whitespace or empty entries in default/custom groups can create invalid group records and mismatched routing.
🧹 Suggested normalization in both AddAbilities and UpdateAbilities
- for _, group := range groups { + for _, group := range groups { + group = strings.TrimSpace(group) + if group == "" { + continue + } key := group + "|" + model if _, exists := abilitySet[key]; exists { continue }- for _, group := range groups { + for _, group := range groups { + group = strings.TrimSpace(group) + if group == "" { + continue + } key := group + "|" + model if _, exists := abilitySet[key]; exists { continue }Also applies to: 226-256
🤖 Fix all issues with AI agents
In `@model/channel.go`:
- Around line 106-122: GetModelGroupMapping currently swallows JSON unmarshal
errors; update it to log the invalid JSON and the error before returning nil so
misconfigurations are visible. In the GetModelGroupMapping method, when
common.Unmarshal returns an error, emit a log entry (e.g., using the standard
log package or your package logger) that includes the error and the raw
*channel.ModelGroupMapping string for context, then continue to return nil; keep
the rest of the function (cleaning empty keys and returning mapping) unchanged.
In `@web/src/components/table/channels/modals/EditChannelModal.jsx`:
- Around line 1392-1395: The model_group_mapping currently serializes all
modelGroupConfig.specificRules, including stale entries for removed models;
update the logic around localInputs.model_group_mapping (in
EditChannelModal.jsx) to first prune specificRules by keeping only keys present
in normalizedModels (i.e., filter modelGroupConfig.specificRules by
normalizedModels set), then JSON.stringify that pruned object and omit (delete
or set to undefined/empty) model_group_mapping when the pruned object is empty
so no empty/stale overrides are submitted.
🧹 Nitpick comments (1)
web/src/components/table/channels/modals/EditChannelModal.jsx (1)
566-582: Normalize parsed model_group_mapping before storing.If persisted data contains arrays or non-array values, the editor state can become inconsistent. Consider filtering to a plain object of string arrays.
♻️ Suggested normalization
- if (mgMap && typeof mgMap === 'object') { - // 直接使用 model_group_mapping 的内容作为 specificRules,不做过滤 - mgConfig.specificRules = mgMap; - } + if (mgMap && typeof mgMap === 'object' && !Array.isArray(mgMap)) { + const cleanedRules = {}; + Object.entries(mgMap).forEach(([model, groups]) => { + if (Array.isArray(groups)) { + cleanedRules[model] = groups + .map((g) => String(g).trim()) + .filter(Boolean); + } + }); + mgConfig.specificRules = cleanedRules; + }
| // GetModelGroupMapping 解析模型分组映射,返回 map[model][]groups | ||
| func (channel *Channel) GetModelGroupMapping() map[string][]string { | ||
| if channel.ModelGroupMapping == nil || *channel.ModelGroupMapping == "" { | ||
| return nil | ||
| } | ||
| var mapping map[string][]string | ||
| if err := common.Unmarshal([]byte(*channel.ModelGroupMapping), &mapping); err != nil { | ||
| return nil | ||
| } | ||
| // 清理空值 | ||
| for model, groups := range mapping { | ||
| if model == "" || len(groups) == 0 { | ||
| delete(mapping, model) | ||
| } | ||
| } | ||
| return mapping | ||
| } |
There was a problem hiding this comment.
Log invalid model_group_mapping JSON to avoid silent fallback.
Right now a malformed JSON silently disables the feature. Emitting a log makes misconfigurations actionable.
🛠️ Proposed fix
- if err := common.Unmarshal([]byte(*channel.ModelGroupMapping), &mapping); err != nil {
- return nil
- }
+ if err := common.Unmarshal([]byte(*channel.ModelGroupMapping), &mapping); err != nil {
+ common.SysLog(fmt.Sprintf("failed to unmarshal model_group_mapping: channel_id=%d, error=%v", channel.Id, err))
+ return nil
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // GetModelGroupMapping 解析模型分组映射,返回 map[model][]groups | |
| func (channel *Channel) GetModelGroupMapping() map[string][]string { | |
| if channel.ModelGroupMapping == nil || *channel.ModelGroupMapping == "" { | |
| return nil | |
| } | |
| var mapping map[string][]string | |
| if err := common.Unmarshal([]byte(*channel.ModelGroupMapping), &mapping); err != nil { | |
| return nil | |
| } | |
| // 清理空值 | |
| for model, groups := range mapping { | |
| if model == "" || len(groups) == 0 { | |
| delete(mapping, model) | |
| } | |
| } | |
| return mapping | |
| } | |
| // GetModelGroupMapping 解析模型分组映射,返回 map[model][]groups | |
| func (channel *Channel) GetModelGroupMapping() map[string][]string { | |
| if channel.ModelGroupMapping == nil || *channel.ModelGroupMapping == "" { | |
| return nil | |
| } | |
| var mapping map[string][]string | |
| if err := common.Unmarshal([]byte(*channel.ModelGroupMapping), &mapping); err != nil { | |
| common.SysLog(fmt.Sprintf("failed to unmarshal model_group_mapping: channel_id=%d, error=%v", channel.Id, err)) | |
| return nil | |
| } | |
| // 清理空值 | |
| for model, groups := range mapping { | |
| if model == "" || len(groups) == 0 { | |
| delete(mapping, model) | |
| } | |
| } | |
| return mapping | |
| } |
🤖 Prompt for AI Agents
In `@model/channel.go` around lines 106 - 122, GetModelGroupMapping currently
swallows JSON unmarshal errors; update it to log the invalid JSON and the error
before returning nil so misconfigurations are visible. In the
GetModelGroupMapping method, when common.Unmarshal returns an error, emit a log
entry (e.g., using the standard log package or your package logger) that
includes the error and the raw *channel.ModelGroupMapping string for context,
then continue to return nil; keep the rest of the function (cleaning empty keys
and returning mapping) unchanged.
| // Build model_group_mapping from modelGroupConfig.specificRules only | ||
| // model_group_mapping 只存储有单独配置的模型 | ||
| localInputs.model_group_mapping = JSON.stringify(modelGroupConfig.specificRules || {}); | ||
|
|
There was a problem hiding this comment.
Prune mappings for models that were removed.
Stale overrides remain hidden in the editor but still get serialized, so they can reappear if a model is re-added. Filter to normalizedModels before stringify and omit when empty.
🧹 Suggested pruning before submit
- localInputs.model_group_mapping = JSON.stringify(modelGroupConfig.specificRules || {});
+ const modelGroupMapping = Object.fromEntries(
+ Object.entries(modelGroupConfig.specificRules || {}).filter(([model]) =>
+ normalizedModels.includes(model),
+ ),
+ );
+ localInputs.model_group_mapping =
+ Object.keys(modelGroupMapping).length > 0
+ ? JSON.stringify(modelGroupMapping)
+ : '';🤖 Prompt for AI Agents
In `@web/src/components/table/channels/modals/EditChannelModal.jsx` around lines
1392 - 1395, The model_group_mapping currently serializes all
modelGroupConfig.specificRules, including stale entries for removed models;
update the logic around localInputs.model_group_mapping (in
EditChannelModal.jsx) to first prune specificRules by keeping only keys present
in normalizedModels (i.e., filter modelGroupConfig.specificRules by
normalizedModels set), then JSON.stringify that pruned object and omit (delete
or set to undefined/empty) model_group_mapping when the pruned object is empty
so no empty/stale overrides are submitted.
Summary by CodeRabbit
Release Notes