Skip to content

feat: smart channel routing based on request API type#5017

Open
xemxx wants to merge 4 commits into
QuantumNous:mainfrom
xemxx:feat/smart-channel-routing
Open

feat: smart channel routing based on request API type#5017
xemxx wants to merge 4 commits into
QuantumNous:mainfrom
xemxx:feat/smart-channel-routing

Conversation

@xemxx
Copy link
Copy Markdown

@xemxx xemxx commented May 21, 2026

⚠️ 提交说明 / PR Notice

Important

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

📝 变更描述 / Description

当前 new-api 支持同一模型配置多个渠道(如 Claude、OpenAI、Gemini 等),但渠道选择仅按优先级 + 权重随机,未考虑客户端请求的 API 格式。这导致:

  • 用户用原生 Claude API 请求 claude-sonnet-4.5 时,可能被路由到 OpenAI 格式的渠道
  • 触发不必要的请求/响应格式转换,增加延迟和出错概率

当请求模型例如 mimo-v2.5-pro 的时候,小米官方提供两种接口,但是 new-api 这块默认渠道只能配置 openai 或者 claude,如果我同时需要两种接口访问,当前只能配置一种然后做转换,但是转换有时候在 claude code 或者 hermes agent 会失败

这时需要一个策略来自动选择,比如配置两个小米官方的渠道,分别是 openai 和 claude,当用户访问的时候,自动选择对应的官方接口去直接访问,这样避免转换带来的误差以及问题出现,但是也要考虑现有逻辑,所以需要当两个渠道同优先级的时候再进入这种策略,否则依旧保证高优先级的先访问,走默认的转换逻辑

兼容性:

  • 单渠道或优先级不相同的场景,行为完全不变
  • 无匹配渠道时自动回退,不破坏现有路由逻辑
  • RelayFormat 时跳过智能路由,保持原有行为

🚀 变更类型 / Type of change

  • 🐛 Bug 修复 (Bug fix)
  • ✨ 新功能 (New feature)
  • ⚡ 性能优化 / 重构 (Refactor)
  • 📝 文档更新 (Documentation)

🔗 关联任务 / Related Issue

✅ 提交前检查项 / Checklist

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

📸 运行证明 / Proof of Work

image image image image

Summary by CodeRabbit

  • New Features

    • Improved routing: the system now infers request format from the URL and prefers channels that match that format for better compatibility and selection when multiple channels are available.
  • Refactor

    • Channel selection and retry initialization updated to carry request-format information through selection logic, enabling the smarter routing above.

Review Change Stack

xemxx added 3 commits May 21, 2026 15:29
When multiple channels share the same priority for a model, prefer channels
whose native API type matches the client's request format (OpenAI/Claude/Gemini).
This avoids unnecessary request/response conversion and improves relay performance.

Changes:
- Add RelayFormatToAPIType() mapping in types/relay_format.go
- Pass RelayFormat through RetryParam to channel selection
- Smart routing in model/ability.go (DB query path, memory cache disabled)
- Smart routing in model/channel_cache.go (memory cache enabled)
- Default to OpenAI format in middleware/distributor.go (pre-detection phase)

Fixes QuantumNous#4982
When the smart channel routing query joins abilities with channels,
both tables expose 'group' and 'weight' columns, causing SQLite to
fail with 'ambiguous column name' errors.

- Qualify the group filter with abilities. prefix in JOINed queries
- Wrap the priority subquery in parentheses so the generated SQL is
  valid (priority = (SELECT MAX(priority) ...) instead of priority =
  SELECT ...)
- Order by abilities.weight DESC explicitly, and select abilities.*
  on the filtered query to avoid column collision when scanning rows
The Distribute middleware runs before the per-route handler that
sets the actual RelayFormat, so smart channel routing was always
seeing RelayFormatOpenAI and could never prefer a Claude- or
Gemini-native channel for /v1/messages or Gemini paths.

Add InferRelayFormatFromPath, which mirrors the routing table in
router/relay-router.go, and use it in the distributor so requests
hitting /v1/messages get RelayFormatClaude (and so on) before
channel selection runs.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 277303d3-fdbf-4e66-a0b5-2837d556a21c

📥 Commits

Reviewing files that changed from the base of the PR and between e0fe3e4 and 59dd388.

📒 Files selected for processing (1)
  • types/relay_format.go

Walkthrough

This PR infers a request's relay format from its HTTP path, threads that format through service RetryParam into model selection, and uses it to prefer channels whose native API type matches the inferred relay format during channel selection.

Changes

Relay Format-Based Smart Channel Routing

Layer / File(s) Summary
Relay Format Utilities
types/relay_format.go
Adds RelayFormatToAPIType (maps RelayFormat to provider API type id) and InferRelayFormatFromPath (infers RelayFormat from HTTP paths via prefix/suffix checks).
Service Contract Extension
service/channel_select.go
Adds RelayFormat field to RetryParam and forwards it to model.GetRandomSatisfiedChannel in both auto and non-auto flows.
Ability Layer Smart Routing
model/ability.go
Adds imports and helpers (getChannelQueryWithAPIType, getMatchingChannelTypes) and updates GetChannel signature to accept relayFormat; when mappable, probes and prefers channels whose API type matches the relay format.
Channel Cache Smart Routing
model/channel_cache.go
GetRandomSatisfiedChannel accepts relayFormat and, for same-priority candidates, partitions candidates by native API type match and optionally filters to preferred subset before weighted selection.
Request Flow Integration
middleware/distributor.go, controller/relay.go
Middleware infers relay format from the request path and includes it in the RetryParam passed into channel selection; controller RetryParam struct literals updated to include RelayFormat.

Sequence Diagram

sequenceDiagram
  participant Client
  participant Middleware as middleware/distributor
  participant Service as service/channel_select
  participant Model as model/ability & model/channel_cache
  participant DB as Database

  Client->>Middleware: HTTP request (path)
  Middleware->>Middleware: InferRelayFormatFromPath(path)
  Middleware->>Service: CacheGetRandomSatisfiedChannel(retryParam{RelayFormat})
  Service->>Model: GetRandomSatisfiedChannel(relayFormat)
  Model->>Model: RelayFormatToAPIType(relayFormat)
  Model->>DB: Query candidate channels by (group, model)
  DB-->>Model: candidate channels
  Model->>Model: Partition/filter by matching API type
  Model-->>Service: selected channel
  Service-->>Middleware: channel
  Middleware-->>Client: route to selected backend
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly Related PRs

  • QuantumNous/new-api#2889: Related work around propagating/using RelayFormat and adapting relay handlers and URL/body handling based on format.

Suggested Reviewers

  • creamlike1024
  • xyfacai

Poem

🐰
I sniff the path, I hop and see,
Which format asks for OpenAI or Claude for me.
I nudge the Router, point the way,
Happy channels match the call today.
Hooray for smart hops!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: smart channel routing based on request API type' directly reflects the main change: implementing intelligent channel selection based on the client request's API format (RelayFormat).
Linked Issues check ✅ Passed The PR implementation fulfills all core coding requirements from issue #4891: automatic API-type-aware routing for multi-backend scenarios, protocol preservation without parameter conversion, and fallback to priority-based logic when no match exists.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the smart routing feature: relay format inference, channel selection logic, and struct extensions to support API-type matching. No unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@types/relay_format.go`:
- Around line 70-71: The case in types/relay_format.go that matches routes with
strings.HasPrefix(path, "/v1/engines/") && strings.HasSuffix(path,
"/embeddings") currently returns RelayFormatGemini which misclassifies legacy
OpenAI-style embeddings; update that branch to return the OpenAI embeddings
relay format constant (e.g., RelayFormatOpenAIEmbeddings or the project’s
embeddings-specific RelayFormat) instead of RelayFormatGemini so
/v1/engines/*/embeddings is routed to the embeddings backend.
- Around line 50-76: The InferRelayFormatFromPath function currently returns
RelayFormatOpenAI in the default case which biases routing for unknown paths;
change the default return to RelayFormatUnknown to preserve neutral behavior,
and add a new case branch so paths starting with "/v1beta/models/" return
RelayFormatGemini (similar to the existing "/v1/models/" handling); update the
switch in InferRelayFormatFromPath accordingly and ensure RelayFormatUnknown is
used downstream where absence of an API-type hint is intended.
🪄 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: a466504f-b7f0-43c6-98eb-6743b74a7024

📥 Commits

Reviewing files that changed from the base of the PR and between 006e801 and e0fe3e4.

📒 Files selected for processing (6)
  • controller/relay.go
  • middleware/distributor.go
  • model/ability.go
  • model/channel_cache.go
  • service/channel_select.go
  • types/relay_format.go

Comment thread types/relay_format.go Outdated
Comment thread types/relay_format.go Outdated
…d unknown paths

Address review feedback on smart channel routing:

- /v1/engines/*/embeddings is OpenAI's legacy embeddings endpoint, not
  Gemini. Return RelayFormatEmbedding so it routes to the correct backend.
- /v1beta/models/ is the Gemini API path used by upstream clients (see
  middleware/distributor.go handling); add it alongside /v1/models/ so
  Gemini routes are inferred consistently.
- Default branch now returns an empty RelayFormat instead of
  RelayFormatOpenAI. Downstream GetChannel treats empty as 'no API-type
  hint' and falls back to the original priority/weight-based selection,
  which is the truly neutral behaviour for unknown paths.
@xemxx
Copy link
Copy Markdown
Author

xemxx commented May 22, 2026

关于 #4759

@seefs001#4759 提了同主题的修复(早本 PR 10 天),思路一致:同模型多渠道时优先选原生格式渠道。两者实现路径不同,简单对比:

维度 #4759 #5017
过滤层 Go 内存 lo.Filter SQL WHERE api_type = ?
抽象 EndpointType(细粒度) api_type(int)
改动 +95 / 3 文件 +209 / 6 文件
路径覆盖 7 类 13 类(含 responses / realtime / audio / embeddings / rerank)
DB 查询 候选集 + 二次查 channel 单次 SQL

另:#4759 引用了 common.GetEndpointTypesByChannelType,我在当前 main 没找到实现,可能依赖未合入的分支,请维护者确认。

我都可以配合 — 关闭本 PR、或在 #4759 基础上补齐路径覆盖、或在本 PR 引入 EndpointType 替换 api_type。听维护者安排。

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.

建议:支持统一接口多后端自动路由能力(如 Claude/OpenAI 同模型自动分流)

1 participant