feat(task): replace built-in task adaptors with a sandboxed JS plugin system (#7076)

This commit is contained in:
Calcium-Ion
2026-08-29 18:51:57 +08:00
committed by GitHub
parent 7037ac15bd
commit eb48396d5f
336 changed files with 52333 additions and 6369 deletions
+91 -2
View File
@@ -5,11 +5,36 @@ import (
"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/constant"
"github.com/QuantumNous/new-api/dto"
"github.com/QuantumNous/new-api/logger"
"github.com/QuantumNous/new-api/model"
"github.com/QuantumNous/new-api/pkg/jsplugin"
"github.com/gin-gonic/gin"
)
func GetChannelConstraints(c *gin.Context) *dto.ChannelConstraints {
if c == nil {
return &dto.ChannelConstraints{}
}
if existing, ok := common.GetContextKeyType[*dto.ChannelConstraints](c, constant.ContextKeyChannelConstraints); ok && existing != nil {
return existing
}
constraints := &dto.ChannelConstraints{}
common.SetContextKey(c, constant.ContextKeyChannelConstraints, constraints)
return constraints
}
func AppendTaskPluginIdentityFilter(c *gin.Context, pluginKey string) {
if c == nil {
return
}
GetChannelConstraints(c).AddFilter(dto.ChannelFilter{
Kind: dto.FilterTaskPluginIdentity,
TaskPluginKey: pluginKey,
TaskPluginChannelTypes: pinnedTaskPluginChannelTypes(c, pluginKey),
})
}
type RetryParam struct {
Ctx *gin.Context
TokenGroup string
@@ -85,6 +110,7 @@ func CacheGetRandomSatisfiedChannel(param *RetryParam) (*model.Channel, string,
var err error
selectGroup := param.TokenGroup
userGroup := common.GetContextKeyString(param.Ctx, constant.ContextKeyUserGroup)
filters := GetChannelConstraints(param.Ctx).Filters
if param.TokenGroup == "auto" {
autoGroups := GetRequestAutoGroups(param.Ctx, userGroup)
@@ -115,7 +141,12 @@ func CacheGetRandomSatisfiedChannel(param *RetryParam) (*model.Channel, string,
}
logger.LogDebug(param.Ctx, "Auto selecting group: %s, priorityRetry: %d", autoGroup, priorityRetry)
channel, _ = model.GetRandomSatisfiedChannel(autoGroup, param.ModelName, priorityRetry, param.RequestPath)
channel, _ = model.GetRandomSatisfiedChannel(
autoGroup,
param.ModelName,
priorityRetry,
filters,
)
if channel == nil {
// Current group has no available channel for this model, try next group
// 当前分组没有该模型的可用渠道,尝试下一个分组
@@ -153,10 +184,68 @@ func CacheGetRandomSatisfiedChannel(param *RetryParam) (*model.Channel, string,
break
}
} else {
channel, err = model.GetRandomSatisfiedChannel(param.TokenGroup, param.ModelName, param.GetRetry(), param.RequestPath)
channel, err = model.GetRandomSatisfiedChannel(
param.TokenGroup,
param.ModelName,
param.GetRetry(),
filters,
)
if err != nil {
return nil, param.TokenGroup, err
}
}
return channel, selectGroup, nil
}
func pinnedTaskPluginChannelTypes(c *gin.Context, expected string) []int {
if c == nil || expected == "" {
return nil
}
if value, exists := c.Get(jsplugin.ContextKeyPinnedEndpoint); exists {
pinned, ok := value.(jsplugin.PinnedEndpoint)
if ok && pinned.Generation != nil && len(pinned.Candidates) > 1 {
expectedFound := false
channelTypes := make([]int, 0, len(pinned.Candidates))
seen := make(map[int]struct{}, len(pinned.Candidates))
for _, candidate := range pinned.Candidates {
if candidate.Plugin == nil {
continue
}
if candidate.Plugin.Meta.Key == expected {
expectedFound = true
}
for _, channelType := range candidate.Plugin.Meta.ChannelTypes {
if channelType == 0 || channelType == constant.ChannelTypeTaskPlugin {
continue
}
if _, duplicate := seen[channelType]; duplicate {
continue
}
if plugin, indexed := pinned.Generation.GetByChannelType(channelType); indexed && plugin == candidate.Plugin {
seen[channelType] = struct{}{}
channelTypes = append(channelTypes, channelType)
}
}
}
if expectedFound {
return channelTypes
}
}
}
value, exists := c.Get(jsplugin.ContextKeyPinnedPlugin)
pinned, ok := value.(jsplugin.PinnedPlugin)
if !exists || !ok || pinned.Generation == nil || pinned.Plugin == nil || pinned.Plugin.Meta.Key != expected {
return nil
}
channelTypes := make([]int, 0, len(pinned.Plugin.Meta.ChannelTypes))
for _, channelType := range pinned.Plugin.Meta.ChannelTypes {
if channelType == 0 || channelType == constant.ChannelTypeTaskPlugin {
continue
}
channelTypes = append(channelTypes, channelType)
}
if len(channelTypes) == 0 {
return nil
}
return channelTypes
}