feat: 自定义 OpenAI 兼容上游聚合 + 权重/并发调度

- custom provider:把任意 v1 端点当账号(base_url+key),按 model id 路由,
  直连不走代理;支持 images/generations、images/edits、Sora 式异步视频
- 调度器全局权重优先 + 每账号并发感知(上游账号可配并发,其余系统固定)
- 后台:添加/编辑上游、自定义模型表单(每档普通/代理价,留空=不支持)、
  账号权重/并发列与导入权重
- grok-video / nano-banana-2(runway) 写入硬编码目录(开源自带);
  去掉 540p,Adobe 视频最低 720p
- UI:SelectMenu 与输入框等高、测试耗时改用秒

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-28 21:59:46 +08:00
co-authored by Claude Opus 4.8
parent d289ec378d
commit 3ef84bb9b3
17 changed files with 1149 additions and 64 deletions
@@ -132,6 +132,38 @@ func (h *ProviderAdminHandler) ImportGrokToken(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"ok": true, "id": item.ID, "status": item.Status, "pending": item.Status == "pending"})
}
func (h *ProviderAdminHandler) ImportCustomAccount(c *gin.Context) {
var body struct {
BaseURL string `json:"base_url"`
URL string `json:"url"`
Key string `json:"key"`
APIKey string `json:"api_key"`
Models string `json:"models"`
Name string `json:"name"`
Weight int `json:"weight"`
Concurrency int `json:"concurrency"`
ID string `json:"id"`
}
if err := c.ShouldBindJSON(&body); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"detail": "invalid request body"})
return
}
baseURL := body.BaseURL
if baseURL == "" {
baseURL = body.URL
}
key := body.Key
if key == "" {
key = body.APIKey
}
item, err := h.tokens.ImportCustomAccount(c.Request.Context(), baseURL, key, body.Models, body.Name, body.Weight, body.Concurrency, body.ID)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"ok": true, "id": item.ID, "status": item.Status})
}
func (h *ProviderAdminHandler) ImportKreaCookie(c *gin.Context) {
var body struct {
Cookie string `json:"cookie"`
@@ -370,13 +370,15 @@ func (h *UserGenerationHandler) catalogEntries(c *gin.Context) ([]gin.H, error)
"description": "Adobe Flux Kontext Max",
},
{
"id": "nano-banana-2",
"provider": "adobe",
"type": "image",
"ratios": []string{"1:1", "16:9", "9:16", "4:3", "3:4"},
"resolutions": []string{"1K"},
"image_to_image": true,
"description": "Adobe Gemini Flash Nano Banana",
"id": "nano-banana-2",
"provider": "runway",
"type": "image",
"ratios": []string{"16:9", "9:16", "1:1", "4:3", "3:4", "21:9", "3:2", "5:4", "4:5", "2:3"},
"resolutions": []string{"1K", "2K", "4K"},
"image_to_image": true,
"max_reference_images": 6,
"reference_mode": "asset",
"description": "Runway Nano Banana 2 (图/参考图)",
},
{
"id": "gemini-veo31",
@@ -405,7 +407,7 @@ func (h *UserGenerationHandler) catalogEntries(c *gin.Context) ([]gin.H, error)
"provider": "adobe",
"type": "video",
"ratios": []string{"16:9", "1:1", "9:16"},
"resolutions": []string{"540p", "720p", "1080p"},
"resolutions": []string{"720p", "1080p"},
"durations": []string{"5s"},
"max_reference_images": 2,
"reference_mode": "frame",
@@ -422,6 +424,17 @@ func (h *UserGenerationHandler) catalogEntries(c *gin.Context) ([]gin.H, error)
"reference_mode": "frame",
"description": "Runway Gen-4 Turbo video (图生视频)",
},
{
"id": "grok-video",
"provider": "grok",
"type": "video",
"ratios": []string{"2:3", "3:2", "1:1", "9:16", "16:9"},
"resolutions": []string{"720p"},
"durations": []string{"6s", "10s"},
"max_reference_images": 6,
"reference_mode": "asset",
"description": "Grok Imagine video (文/图生视频)",
},
{
"id": "seedream-4.5",
"provider": "leonardo",
@@ -519,11 +532,11 @@ func (h *UserGenerationHandler) publicModels() ([]gin.H, error) {
},
{
"id": "nano-banana-2",
"provider": "adobe",
"provider": "runway",
"kind": "image",
"ratios": []string{"1:1", "16:9", "9:16", "4:3", "3:4"},
"resolutions": []string{"1K"},
"description": "Adobe Gemini Flash Nano Banana",
"ratios": []string{"16:9", "9:16", "1:1", "4:3", "3:4", "21:9", "3:2", "5:4", "4:5", "2:3"},
"resolutions": []string{"1K", "2K", "4K"},
"description": "Runway Nano Banana 2",
"stub": false,
},
{
@@ -549,7 +562,7 @@ func (h *UserGenerationHandler) publicModels() ([]gin.H, error) {
"provider": "adobe",
"kind": "video",
"ratios": []string{"16:9", "1:1", "9:16"},
"resolutions": []string{"540p", "720p", "1080p"},
"resolutions": []string{"720p", "1080p"},
"description": "Adobe Firefly Video",
"stub": false,
},
@@ -562,6 +575,15 @@ func (h *UserGenerationHandler) publicModels() ([]gin.H, error) {
"description": "Runway Gen-4 Turbo video",
"stub": false,
},
{
"id": "grok-video",
"provider": "grok",
"kind": "video",
"ratios": []string{"2:3", "3:2", "1:1", "9:16", "16:9"},
"resolutions": []string{"720p"},
"description": "Grok Imagine video",
"stub": false,
},
{
"id": "seedream-4.5",
"provider": "leonardo",