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:
@@ -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",
|
||||
|
||||
@@ -109,6 +109,7 @@ func New(cfg *config.Config, auth *service.AuthService, handlers Handlers) *gin.
|
||||
authed.POST("/tokens/import-krea-cookie", handlers.ProviderAdmin.ImportKreaCookie)
|
||||
authed.POST("/tokens/import-imagine-token", handlers.ProviderAdmin.ImportImagineToken)
|
||||
authed.POST("/tokens/import-grok-token", handlers.ProviderAdmin.ImportGrokToken)
|
||||
authed.POST("/tokens/import-custom-account", handlers.ProviderAdmin.ImportCustomAccount)
|
||||
authed.POST("/tokens/delete-bulk", handlers.ProviderAdmin.TokenDeleteBulk)
|
||||
authed.PATCH("/tokens/:pool/:id", handlers.ProviderAdmin.TokenUpdate)
|
||||
authed.DELETE("/tokens/:pool/:id", handlers.ProviderAdmin.TokenDelete)
|
||||
|
||||
Reference in New Issue
Block a user