Skip to content

feat(model): auto create vendor and bind model metadata on channel insert/update - #6742

Closed
ggdayup wants to merge 1 commit into
QuantumNous:mainfrom
ggdayup:feat/auto-vendor-creation
Closed

feat(model): auto create vendor and bind model metadata on channel insert/update#6742
ggdayup wants to merge 1 commit into
QuantumNous:mainfrom
ggdayup:feat/auto-vendor-creation

Conversation

@ggdayup

@ggdayup ggdayup commented Aug 10, 2026

Copy link
Copy Markdown

⚠️ 提交说明 / PR Notice

Important

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

📝 变更描述 / Description

当添加或更新新渠道(尤其是高级自定义渠道 OmniRoute、SiliconFlow 等)时,系统原先仅更新渠道表和路由能力表,导致 /models/metadata 模型广场中缺少对应供应商实体,且模型元数据脱节。

本 PR 实现了渠道到供应商与模型元数据的全自动联动:

  1. 自动供应商创建 (EnsureVendorForChannel):渠道保存或更新时,自动识别并推导供应商名称,不存在时自动创建。
  2. 自动模型元数据绑定 (AutoBindChannelModelsToVendor):自动遍历渠道拥有的模型,缺失元数据时自动生成并绑定该供应商 ID。
  3. 全生命周期 Hook:已注入到 Channel.Insert()Channel.Update()BatchInsertChannels() 以及上游巡检自动发现模型 Hook 中。

注:本 PR 包含 AI 辅助开发/生成 (AI-assisted)

🚀 变更类型 / Type of change

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

🔗 关联任务 / Related Issue

  • Closes #

✅ 提交前检查项 / Checklist

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

📸 运行证明 / Proof of Work

  • 单元测试通过证明:go test ./model -v -run TestEnsureVendorAndAutoBindModelsForNewChannel (PASS)
  • relaykit 模块独立构建验证:cd relaykit && GOWORK=off go build ./... (OK)

Summary by CodeRabbit

  • New Features

    • Added support for Gemini 3.1 Flash Lite, Gemini 3.5 Flash Lite, Gemini 3.6, and Gemini 3.6 Flash.
    • New channels now automatically receive associated vendor and model metadata.
    • Existing channel workflows now synchronize vendor and model associations.
  • Bug Fixes

    • Added default cache, model, and completion ratio settings for the new Gemini models.
  • Tests

    • Added coverage validating automatic metadata creation and model ratio settings.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The change automatically synchronizes channel vendors and models during channel operations. It also adds four Gemini models to supported listings and configures their cache, model, and completion ratios with tests.

Changes

Channel vendor synchronization

Layer / File(s) Summary
Vendor and model synchronization
model/vendor_meta.go
Channel-based vendor resolution, vendor creation or reuse, and model metadata synchronization were added.
Channel flow integration and validation
model/channel.go, controller/channel_upstream_update.go, model/vendor_channel_auto_test.go
Channel creation, updates, and automatic or manual apply flows now trigger synchronization. Integration tests verify enabled vendor and vendor-linked model records.

Gemini model support

Layer / File(s) Summary
Gemini model configuration and tests
relay/channel/gemini/constant.go, setting/ratio_setting/*
Four Gemini models were added to supported listings and ratio settings. Tests validate their model and completion ratios.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ChannelFlow
  participant EnsureChannelVendorAndModels
  participant VendorModelRecords
  ChannelFlow->>EnsureChannelVendorAndModels: synchronize channel metadata
  EnsureChannelVendorAndModels->>VendorModelRecords: ensure vendor
  EnsureChannelVendorAndModels->>VendorModelRecords: bind channel models
Loading

Possibly related PRs

Suggested reviewers: seefs001

Poem

I’m a rabbit with models to bind,
Vendors and channels now align.
Four Gemini names join the play,
Ratios guide them night and day.
Tests check each little hop—
Metadata lands in every spot.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% 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 and concisely describes the main change: automatic vendor creation and model metadata binding during channel lifecycle operations.
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.

Warning

⚠️ This pull request shows signs of AI-generated slop (redundant_comments, defensive_cruft). It has been flagged by CodeRabbit slop detection and should be reviewed carefully.

@ggdayup ggdayup closed this Aug 10, 2026

@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: 5

🤖 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 `@model/vendor_channel_auto_test.go`:
- Around line 13-14: Update TestEnsureVendorAndAutoBindModelsForNewChannel to
create an isolated database fixture before calling DB.AutoMigrate, assign that
fixture to the global DB, and restore the previous DB value via t.Cleanup.
Ensure the fixture’s database resources and migrated state are also cleaned up
through t.Cleanup so the test is independent of suite order and shared data.

In `@model/vendor_meta.go`:
- Around line 180-196: Update model/vendor_meta.go:180-196 in
AutoBindChannelModelsToVendor to return unexpected lookup errors and model
create or update errors instead of only logging them. Update
model/channel.go:450-450 to roll back and return the synchronization error
before committing the batch transaction; at model/channel.go:530-532 and
582-584, return the error or persist a durable retry state when atomicity is
unavailable. At controller/channel_upstream_update.go:519-519 and 966-966,
propagate or durably retry synchronization failures after automatic and manual
model application.
- Around line 136-153: Update EnsureVendorForChannel to enforce active
vendor-name uniqueness with a portable non-null key column populated only for
active vendors and a unique index on that key, compatible with SQLite, MySQL
5.7.8+, and PostgreSQL 9.6+. Populate or clear the key consistently when vendors
are created or soft-deleted, and when Create conflicts on the unique constraint,
reload and return the existing active Vendor instead of returning the error.

In `@relay/channel/gemini/constant.go`:
- Line 8: Remove the unsupported bare gemini-3.6 identifier while retaining
gemini-3.6-flash: update relay/channel/gemini/constant.go:8-8,
setting/ratio_setting/cache_ratio.go:13-13,
setting/ratio_setting/model_ratio.go:195-195, 339-339, and 592-593 to eliminate
its configuration entries, and update
setting/ratio_setting/model_ratio_test.go:30-34 to remove its test coverage.
Ensure no configuration or test path continues to reference gemini-3.6.

In `@setting/ratio_setting/model_ratio_test.go`:
- Around line 15-18: Update the model ratio test table and assertions to cover
the cache-ratio billing invariant: add an expected cache ratio for each
supported model, call GetCacheRatio for every case, and assert the returned
value alongside the existing model and completion ratio checks.
🪄 Autofix

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 Plus

Run ID: be469855-47df-4ff9-b685-a18b17b2333e

📥 Commits

Reviewing files that changed from the base of the PR and between 823e263 and 19aca2d.

📒 Files selected for processing (8)
  • controller/channel_upstream_update.go
  • model/channel.go
  • model/vendor_channel_auto_test.go
  • model/vendor_meta.go
  • relay/channel/gemini/constant.go
  • setting/ratio_setting/cache_ratio.go
  • setting/ratio_setting/model_ratio.go
  • setting/ratio_setting/model_ratio_test.go

Comment on lines +13 to +14
func TestEnsureVendorAndAutoBindModelsForNewChannel(t *testing.T) {
require.NoError(t, DB.AutoMigrate(&Channel{}, &Ability{}, &Vendor{}, &Model{}))

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.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Create an isolated database fixture.

This test uses the global DB without initializing or restoring it. It can depend on suite order and leave migrated tables and rows in a shared test database.

Initialize an isolated test database, assign and restore DB, and clean up through t.Cleanup. As per coding guidelines, backend tests must initialize database state explicitly in test fixtures.

🤖 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 `@model/vendor_channel_auto_test.go` around lines 13 - 14, Update
TestEnsureVendorAndAutoBindModelsForNewChannel to create an isolated database
fixture before calling DB.AutoMigrate, assign that fixture to the global DB, and
restore the previous DB value via t.Cleanup. Ensure the fixture’s database
resources and migrated state are also cleaned up through t.Cleanup so the test
is independent of suite order and shared data.

Source: Coding guidelines

Comment thread model/vendor_meta.go
Comment on lines +136 to +153
err := useDB.Where("name = ? AND deleted_at IS NULL", name).First(&vendor).Error
if err == nil {
return vendor.Id, nil
}

if errors.Is(err, gorm.ErrRecordNotFound) {
newVendor := Vendor{
Name: name,
Description: fmt.Sprintf("%s 渠道自动创建供应商", name),
Icon: icon,
Status: 1,
CreatedTime: common.GetTimestamp(),
UpdatedTime: common.GetTimestamp(),
}
if err := useDB.Create(&newVendor).Error; err != nil {
return 0, err
}
return newVendor.Id, nil

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.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Prevent duplicate active vendor rows.

EnsureVendorForChannel uses a read-then-create sequence. The (name, deleted_at) unique index does not enforce one active row because MySQL, PostgreSQL, and SQLite allow multiple NULL values in a unique key. Concurrent calls can both find no row and create vendors with deleted_at IS NULL.

Use a portable active-name uniqueness strategy. Then handle a conflicting create by loading the existing vendor. As per coding guidelines, database code must support SQLite, MySQL >= 5.7.8, and PostgreSQL >= 9.6.

🤖 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 `@model/vendor_meta.go` around lines 136 - 153, Update EnsureVendorForChannel
to enforce active vendor-name uniqueness with a portable non-null key column
populated only for active vendors and a unique index on that key, compatible
with SQLite, MySQL 5.7.8+, and PostgreSQL 9.6+. Populate or clear the key
consistently when vendors are created or soft-deleted, and when Create conflicts
on the unique constraint, reload and return the existing active Vendor instead
of returning the error.

Source: Coding guidelines

Comment thread model/vendor_meta.go
Comment on lines +180 to +196
err := useDB.Where("model_name = ? AND deleted_at IS NULL", modelName).First(&existing).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
newModel := Model{
ModelName: modelName,
VendorID: vendorID,
Status: 1,
SyncOfficial: 0,
Endpoints: `["openai"]`,
CreatedTime: now,
UpdatedTime: now,
}
if err := useDB.Create(&newModel).Error; err != nil {
common.SysError(fmt.Sprintf("auto bind model failed: %s, err: %v", modelName, err))
}
} else if err == nil && existing.VendorID == 0 {
useDB.Model(&existing).Update("vendor_id", vendorID)
}

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.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Do not report successful synchronization after a metadata write fails.

AutoBindChannelModelsToVendor drops database errors, and every caller discards the returned synchronization error. A channel operation can therefore succeed while vendor or model metadata is missing.

  • model/vendor_meta.go#L180-L196: return unexpected lookup errors and model create or update errors instead of only logging them.
  • model/channel.go#L450-L450: roll back and return the synchronization error before committing the batch transaction.
  • model/channel.go#L530-L532: return the synchronization error, or persist a durable retry state if channel insertion cannot be atomic.
  • model/channel.go#L582-L584: return the synchronization error, or persist a durable retry state if channel updates cannot be atomic.
  • controller/channel_upstream_update.go#L519-L519: propagate or durably retry the synchronization failure after automatic model application.
  • controller/channel_upstream_update.go#L966-L966: propagate or durably retry the synchronization failure after manual model application.
📍 Affects 3 files
  • model/vendor_meta.go#L180-L196 (this comment)
  • model/channel.go#L450-L450
  • model/channel.go#L530-L532
  • model/channel.go#L582-L584
  • controller/channel_upstream_update.go#L519-L519
  • controller/channel_upstream_update.go#L966-L966
🤖 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 `@model/vendor_meta.go` around lines 180 - 196, Update
model/vendor_meta.go:180-196 in AutoBindChannelModelsToVendor to return
unexpected lookup errors and model create or update errors instead of only
logging them. Update model/channel.go:450-450 to roll back and return the
synchronization error before committing the batch transaction; at
model/channel.go:530-532 and 582-584, return the error or persist a durable
retry state when atomicity is unavailable. At
controller/channel_upstream_update.go:519-519 and 966-966, propagate or durably
retry synchronization failures after automatic and manual model application.

"gemini-2.0-flash-001", "gemini-2.0-flash-lite-001", "gemini-2.0-flash-lite",
"gemini-2.5-flash-lite",
"gemini-2.5-flash-lite", "gemini-3.1-flash-lite", "gemini-3.5-flash-lite",
"gemini-3.6", "gemini-3.6-flash",

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verify whether an explicit Gemini upstream-name translation exists.
ast-grep outline relay/channel/gemini --items all
rg -n --type=go -C 5 'gemini-3\.6|UpstreamModelName|OriginModelName|ModelList' .

Repository: QuantumNous/new-api

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Gemini files:\n'
git ls-files 'relay/channel/gemini/*'

printf '\nGemini constants/adaptor outline/selected content:\n'
sed -n '1,80p' relay/channel/gemini/constant.go
printf '\n'
sed -n '1,260p' relay/channel/gemini/adaptor.go

printf '\nRelevant ratio settings:\n'
sed -n '1,45p' setting/ratio_setting/cache_ratio.go
sed -n '180,210p' setting/ratio_setting/model_ratio.go
sed -n '330,348p' setting/ratio_setting/model_ratio.go
sed -n '580,602p' setting/ratio_setting/model_ratio.go
sed -n '24,38p' setting/ratio_setting/model_ratio_test.go

printf '\nFocused upstream-name translation/search for Gemini 3 identifiers:\n'
rg -n --type=go -C 3 'gemini-3\.6|gemini-3\.6-flash|UpstreamModelName|OriginModelName|ConvertOpenAIRequest|RequestURL.*gemini|Info\.UpstreamModelName|body\.Model' relay setting/dto relay/channel/gemini | sed -n '1,240p'

Repository: QuantumNous/new-api

Length of output: 32367


🌐 Web query:

Google Gemini API pricing supported model versions gemini-3.6 gemini-3.6-flash

💡 Result:

As of August 10, 2026, Gemini 3.6 Flash is a generally available (GA) model in the Gemini API and the Gemini Enterprise Agent Platform [1][2][3]. Gemini API Pricing for Gemini 3.6 Flash (Paid Tier) The following pricing applies per 1 million tokens for the gemini-3.6-flash model [4]: Input Price: $1.50 per 1 million tokens [4]. Output Price: $7.50 per 1 million tokens (includes thinking tokens) [4]. Context Caching Storage Price: $1.00 per 1 million tokens per hour [4]. Grounding with Google Search/Maps: The first 5,000 requests per month are free (shared across the Gemini 3 model family); subsequent requests are $14.00 per 1,000 search queries [4]. Key Model Details Model ID: gemini-3.6-flash [1][2] Release Date: July 21, 2026 [2][3] Context Window: 1,048,576 tokens [1][2] Max Output Tokens: 65,536 tokens [1][2] Capabilities: Includes support for function calling, structured outputs, search grounding, context caching, and code execution [1][2]. Note: There is no Free Tier for gemini-3.6-flash in the Gemini API [4]. While a free tier exists for some models within the Gemini API, pricing and availability depend on the specific model selected [5]. You can verify the most current rates and specific model availability directly through the official Gemini API pricing documentation [4].

Citations:


Remove the unsupported upstream identifier gemini-3.6.

gemini-3.6-flash is the supported Gemini model ID; the bare gemini-3.6 entry can be sent upstream because the Gemini adaptor builds request URLs from info.UpstreamModelName without translating it. Remove all configuration and test paths for the bare identifier, and keep gemini-3.6-flash in the Gemini model list.

📍 Affects 4 files
  • relay/channel/gemini/constant.go#L8-L8 (this comment)
  • setting/ratio_setting/cache_ratio.go#L13-L13
  • setting/ratio_setting/model_ratio.go#L195-L195
  • setting/ratio_setting/model_ratio.go#L339-L339
  • setting/ratio_setting/model_ratio.go#L592-L593
  • setting/ratio_setting/model_ratio_test.go#L30-L34
🤖 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 `@relay/channel/gemini/constant.go` at line 8, Remove the unsupported bare
gemini-3.6 identifier while retaining gemini-3.6-flash: update
relay/channel/gemini/constant.go:8-8,
setting/ratio_setting/cache_ratio.go:13-13,
setting/ratio_setting/model_ratio.go:195-195, 339-339, and 592-593 to eliminate
its configuration entries, and update
setting/ratio_setting/model_ratio_test.go:30-34 to remove its test coverage.
Ensure no configuration or test path continues to reference gemini-3.6.

Comment on lines +15 to +18
tests := []struct {
model string
expectedModelRatio float64
expectedCompRatio float64

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Test the cache-ratio billing invariant.

These cases do not call GetCacheRatio. A missing or incorrect cache ratio can change cache-token billing without failing this test. Add the expected cache ratio and assert it for every supported model.

Proposed test update
 	tests := []struct {
 		model              string
 		expectedModelRatio float64
 		expectedCompRatio  float64
+		expectedCacheRatio float64
 	}{
 		{
 			model:              "gemini-3.1-flash-lite",
 			expectedModelRatio: 0.125,
 			expectedCompRatio:  6.0,
+			expectedCacheRatio: 0.1,
 		},
 			compRatio := ratio_setting.GetCompletionRatio(tt.model)
 			assert.InDelta(t, tt.expectedCompRatio, compRatio, 0.0001, "completion ratio mismatch for %s", tt.model)
+
+			cacheRatio, ok := ratio_setting.GetCacheRatio(tt.model)
+			require.True(t, ok, "cache ratio for %s should exist", tt.model)
+			assert.InDelta(t, tt.expectedCacheRatio, cacheRatio, 0.0001, "cache ratio mismatch for %s", tt.model)

As per coding guidelines, “Backend tests must protect … billing/accounting invariants.”

Also applies to: 42-49

🤖 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 `@setting/ratio_setting/model_ratio_test.go` around lines 15 - 18, Update the
model ratio test table and assertions to cover the cache-ratio billing
invariant: add an expected cache ratio for each supported model, call
GetCacheRatio for every case, and assert the returned value alongside the
existing model and completion ratio checks.

Source: Coding guidelines

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