Skip to content

feat: support qiniu ai provider#4251

Closed
liangchaoboy wants to merge 2 commits intoQuantumNous:mainfrom
liangchaoboy:main
Closed

feat: support qiniu ai provider#4251
liangchaoboy wants to merge 2 commits intoQuantumNous:mainfrom
liangchaoboy:main

Conversation

@liangchaoboy
Copy link
Copy Markdown

@liangchaoboy liangchaoboy commented Apr 14, 2026

📝 变更描述 / Description

本 PR 为 Qiniu(七牛) 增加了一个独立的渠道类型(type=58),并将其按 OpenAI 兼容供应商接入现有转发链路:后端将 ChannelTypeQiniu 映射到 APITypeOpenAI,因此请求转发与鉴权逻辑复用 OpenAI adaptor(Authorization: Bearer <key>),无需额外的请求体/响应体转换。

同时,补齐了“从上游拉取模型列表”的能力:前端将 type=58 加入可拉取模型列表的白名单集合,使渠道编辑弹窗可以调用现有的 /v1/models 拉取流程;后端沿用统一的 /v1/models 解析逻辑,返回 data[].id 作为模型列表。新增单元测试使用 httptest 模拟上游 /v1/models,验证请求路径与 Bearer 鉴权头正确,并能正确解析模型 ID。

🚀 变更类型 / 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

本地测试通过(只跑本次新增覆盖项):

go test ./controller -run Qiniu
ok  	github.com/QuantumNous/new-api/controller	0.456s

Summary by CodeRabbit

  • New Features
    • Added Qiniu as a new supported channel provider with complete integration across backend and frontend systems.
    • Enabled automatic upstream model list fetching and mapping capabilities for Qiniu channels, allowing dynamic model synchronization.
    • Implemented full streaming protocol support for Qiniu channels alongside request compatibility handling.
    • Enhanced the channel management interface to display Qiniu as an available provider option with dedicated UI controls for model management and configuration.

Vincent and others added 2 commits April 14, 2026 16:57
Introduce Qiniu (OpenAI-compatible) channel type, enable upstream /v1/models fetching in UI, and add a unit test for model listing.

Made-with: Cursor
Replace hardcoded OpenAI/Azure channel type check with the
SupportStreamOptions flag so all channels registered in
streamSupportedChannels (including Qiniu) correctly preserve
StreamOptions in upstream requests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 14, 2026

Walkthrough

This pull request introduces support for a new Qiniu channel type (ID: 58). Changes include backend constant definitions mapping Qiniu to the OpenAI API type, stream options support configuration, upstream model fetching capability, and frontend UI enhancements enabling Qiniu as an OpenAI-compatible channel option.

Changes

Cohort / File(s) Summary
Backend Channel Constants
constant/channel.go
Added ChannelTypeQiniu = 58 constant with corresponding base URL (https://api.qnaigc.com) and display name ("Qiniu") mappings.
API Type Mapping
common/api_type.go
Added switch branch in ChannelType2APIType to map ChannelTypeQiniu to APITypeOpenAI.
Stream Support Configuration
relay/common/relay_info.go
Added ChannelTypeQiniu: true to streamSupportedChannels map to enable stream options support for Qiniu channel type.
OpenAI Adaptor Logic
relay/channel/openai/adaptor.go
Refactored stream options clearing logic in ConvertOpenAIRequest to use SupportStreamOptions flag instead of explicit channel type checks.
Channel Upstream Model Fetching Test
controller/channel_upstream_update_qiniu_test.go
Added TestFetchChannelUpstreamModelIDs_Qiniu test validating HTTP requests (GET /v1/models with Bearer token auth) and model ID extraction for Qiniu channel type.
Frontend Channel Constants
web/src/constants/channel.constants.js
Added Qiniu option ({ value: 58, color: 'green', label: 'Qiniu' }) to CHANNEL_OPTIONS and included 58 in MODEL_FETCHABLE_CHANNEL_TYPES set.
Frontend UI Components
web/src/components/table/channels/modals/EditChannelModal.jsx, web/src/helpers/render.jsx
Enabled upstream model management UI for Qiniu (added to MODEL_FETCHABLE_TYPES); added Qiniu icon rendering mapping to OpenAI icon in getChannelIcon helper.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

Suggested reviewers

  • seefs001
  • creamlike1024

Poem

🐰 A new channel hops into the fold,
Qiniu's configured, brave and bold,
From backend maps to frontend views,
Stream support and model fetches too—
The system grows with seamless grace! 🌟

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: support qiniu ai provider' directly and clearly summarizes the main change—adding Qiniu as a new AI provider channel type with OpenAI compatibility.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.42.1)
web/src/components/table/channels/modals/EditChannelModal.jsx

[]


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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@controller/channel_upstream_update_qiniu_test.go`:
- Around line 16-25: The handler goroutine inside srv :=
httptest.NewServer(http.HandlerFunc(...) ) uses t.Fatalf which must not be
called from spawned goroutines; instead return HTTP error responses (e.g.,
http.Error with 400/500) when checks on r.Method, r.URL.Path or
r.Header.Get("Authorization") fail, and capture the actual values (method, path,
auth header) into variables or send them on a channel so the main test goroutine
can perform the assertions after the request completes; replace each t.Fatalf in
the handler with an appropriate http.Error call and move the t.Fatalf/assert
checks into the main test goroutine using the captured values or channel result.
🪄 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: 94c1c68d-cf50-4119-bb6c-4b8e6916e4e6

📥 Commits

Reviewing files that changed from the base of the PR and between 8c8661d and 7e8bd73.

📒 Files selected for processing (8)
  • common/api_type.go
  • constant/channel.go
  • controller/channel_upstream_update_qiniu_test.go
  • relay/channel/openai/adaptor.go
  • relay/common/relay_info.go
  • web/src/components/table/channels/modals/EditChannelModal.jsx
  • web/src/constants/channel.constants.js
  • web/src/helpers/render.jsx

Comment on lines +16 to +25
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
t.Fatalf("unexpected method: %s", r.Method)
}
if r.URL.Path != "/v1/models" {
t.Fatalf("unexpected path: %s", r.URL.Path)
}
if got := r.Header.Get("Authorization"); got != "Bearer test-key" {
t.Fatalf("unexpected Authorization header: %q", got)
}
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.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Go testing package docs: can t.Fatalf (or t.FailNow) be called from a goroutine created by the test, such as an httptest server handler goroutine?

💡 Result:

No. In Go tests, t.Fatalf/t.FailNow (and t.Fatal/t.FailNow/t.SkipNow variants) must be called only from the goroutine running the test function; calling them from other goroutines created by the test (for example, an httptest server handler goroutine) is explicitly disallowed and can lead to race conditions and incorrect behavior. What the docs say: FailNow “stops its execution by calling runtime.Goexit” in the current goroutine, and “FailNow must be called from the goroutine running the test or benchmark function, not from other goroutines created during the test. Calling FailNow does not stop those other goroutines.” The general “test ends when … calls any of … T.FailNow, T.Fatal, T.Fatalf …” plus “must be called only from the goroutine running the Test function” language likewise applies. Safe patterns: - From goroutine(s) spawned during the test, use t.Errorf/t.Fail (non-fatal) or otherwise report via channels/WaitGroups and have the main test goroutine call Fatalf/FailNow. - For httptest handlers, avoid calling t.Fatal/Fatalf directly in the handler; instead signal failure back to the test goroutine (e.g., send an error on a channel), then fail in the test goroutine. If you need, I can show a small channel-based example for httptest handlers.

Citations:


Replace t.Fatalf calls in the handler goroutine with HTTP error responses.

Calling t.Fatalf from within the httptest handler goroutine violates Go testing semantics. Per the testing package documentation, t.Fatalf and t.FailNow must only be called from the test goroutine itself; calling them from spawned goroutines (including handlers) can cause race conditions and unreliable test failures. Return an HTTP error status from the handler and perform assertions in the main test goroutine instead.

Suggested adjustment
  srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodGet {
-     t.Fatalf("unexpected method: %s", r.Method)
+     http.Error(w, "unexpected method", http.StatusBadRequest)
+     return
    }
    if r.URL.Path != "/v1/models" {
-     t.Fatalf("unexpected path: %s", r.URL.Path)
+     http.Error(w, "unexpected path", http.StatusBadRequest)
+     return
    }
    if got := r.Header.Get("Authorization"); got != "Bearer test-key" {
-     t.Fatalf("unexpected Authorization header: %q", got)
+     http.Error(w, "unexpected Authorization header", http.StatusUnauthorized)
+     return
    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@controller/channel_upstream_update_qiniu_test.go` around lines 16 - 25, The
handler goroutine inside srv := httptest.NewServer(http.HandlerFunc(...) ) uses
t.Fatalf which must not be called from spawned goroutines; instead return HTTP
error responses (e.g., http.Error with 400/500) when checks on r.Method,
r.URL.Path or r.Header.Get("Authorization") fail, and capture the actual values
(method, path, auth header) into variables or send them on a channel so the main
test goroutine can perform the assertions after the request completes; replace
each t.Fatalf in the handler with an appropriate http.Error call and move the
t.Fatalf/assert checks into the main test goroutine using the captured values or
channel result.

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