Skip to content

feat: 新增节点优先级配置,支持多候选模型按优先级路由 - #697

Open
ghdefe wants to merge 1 commit into
justlovemaki:mainfrom
ghdefe:feat_candicate_provider
Open

feat: 新增节点优先级配置,支持多候选模型按优先级路由#697
ghdefe wants to merge 1 commit into
justlovemaki:mainfrom
ghdefe:feat_candicate_provider

Conversation

@ghdefe

@ghdefe ghdefe commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

功能:新增节点优先级配置,支持多候选模型按优先级路由

背景

customModels 中配置了多个 modelId/alias 相同的模型映射(例如多个供应商提供同一个模型),或同一模型既在 customModels 中配置又被当前 Provider 直接支持时,原先只能取第一个匹配的配置,无法按节点健康状态和优先级智能选择最优路径。

变更内容

1. 多候选模型路由引擎

  • src/utils/common.js - handleContentGenerationRequest 重写模型选择逻辑:收集所有匹配的 customModel 候选 + 当前 Provider 直接支持,按节点优先级排序,选最优路径;所有候选不可用时兜底到直接支持
  • src/providers/provider-models.js - 新增 getAllCustomModelConfigs() 返回所有匹配的自定义模型配置(而非仅第一个)

2. 节点优先级评分

  • src/providers/provider-pool-manager.js - _calculateNodeScore() 引入 priority 作为首要排序因子(priority * 1e16),确保低优先级节点不会因其他因子(使用次数、负载等)被误选
  • 新增 hasHealthyProviderForModel() - 只读检查指定 Provider 是否有健康节点支持某模型
  • 新增 getBestPriorityForModel() - 获取指定 Provider 下支持某模型的健康节点中最高优先级

3. 配置与 UI

  • configs/provider_pools.json.example - 示例配置添加 priority 字段
  • static/app/modal.js - 新增/编辑 Provider 表单增加「优先级」输入框,可选填,留空视为最低优先级
  • static/app/utils.js - 注册 priority 字段标签
  • static/app/i18n.js - 中英文翻译添加 modal.provider.priority 条目

使用方式

provider_pools.json 的节点配置中添加 priority 字段,数值越小优先级越高(如 priority: 1 高于 priority: 10)。未配置的节点默认最低优先级。

@justlovemaki

Copy link
Copy Markdown
Owner

@codex

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 431d96a8eb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


// 0. 优先级分层:priority 越小越优先,未配置默认 Infinity
// 使用 1e16 作为每个优先级层的间隔,确保低优先级节点不会因其他因子超越高优先级节点
const priorityScore = (config.priority ?? Infinity) * 1e16;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Use a finite score for nodes without priority

Existing provider-pool configurations, and new nodes whose optional priority field is left blank, have no priority, so this expression makes every healthy node's score Infinity. In _doSelectProvider, all such scores compare equal and the UUID tie-breaker repeatedly selects the same node, disabling the existing LRU/load balancing behavior for the default configuration. Use a finite lowest-priority tier so the remaining score components can still order nodes within that tier.

Useful? React with 👍 / 👎.

Comment on lines +62 to +64
return CONFIG.customModels.filter(m =>
// m.provider === targetProvider &&
(m.id === targetModelId || m.alias === targetModelId)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve the explicit provider prefix when collecting routes

When a request uses an explicit model such as atlascloud:gpt-4o, the prefix is parsed into targetProvider but then ignored by this filter. If several custom models share the gpt-4o alias—as in the supplied example—the priority comparison can route that explicitly prefixed request through qiniu, fenno, or another provider instead of atlascloud. Keep the m.provider === targetProvider constraint for prefixed model IDs.

Useful? React with 👍 / 👎.

Comment on lines +693 to +695
const healthyProviders = availableProviders.filter(p =>
p.config.isHealthy && !p.config.isDisabled && !p.config.needsRefresh
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Recover scheduled nodes before evaluating custom routes

For a custom-model candidate whose target node was marked unhealthy with scheduledRecoveryTime, this method filters the node out without invoking _checkAndRecoverScheduledProviders, unlike both selectProvider and the new hasHealthyProviderForModel. Once the cooldown expires, alias requests still skip that target and never reach selection, so it may remain unusable indefinitely when scheduled health checks are disabled and no unrelated traffic selects that provider type. Run scheduled recovery before constructing healthyProviders.

Useful? React with 👍 / 👎.

Comment on lines +597 to +599
// 0. 优先级分层:priority 越小越优先,未配置默认 Infinity
// 使用 1e16 作为每个优先级层的间隔,确保低优先级节点不会因其他因子超越高优先级节点
const priorityScore = (config.priority ?? Infinity) * 1e16;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Skip queue-full nodes before applying priority tiers

With configured tiers, the fixed 1e17 score returned for a queue-full node can beat an idle lower-priority node: for example, a full priority-1 node scores 1e17, while an idle priority-10 node scores slightly above 1e17 after its base score is added. acquireSlot therefore selects the full node and throws 429 even though the lower-priority node has capacity, regressing the prior concurrency fallback behavior. Exclude full nodes or ensure their sentinel score sorts after every usable priority tier.

Useful? React with 👍 / 👎.

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