merge upstream

Signed-off-by: wozulong <>
This commit is contained in:
wozulong 2024-05-16 19:00:58 +08:00
commit 9932962320
21 changed files with 218 additions and 71 deletions

View File

@ -70,9 +70,17 @@ var DefaultModelRatio = map[string]float64{
"claude-3-haiku-20240307": 0.125, // $0.25 / 1M tokens "claude-3-haiku-20240307": 0.125, // $0.25 / 1M tokens
"claude-3-sonnet-20240229": 1.5, // $3 / 1M tokens "claude-3-sonnet-20240229": 1.5, // $3 / 1M tokens
"claude-3-opus-20240229": 7.5, // $15 / 1M tokens "claude-3-opus-20240229": 7.5, // $15 / 1M tokens
"ERNIE-Bot": 0.8572, // ¥0.012 / 1k tokens "ERNIE-Bot": 0.8572, // ¥0.012 / 1k tokens //renamed to ERNIE-3.5-8K
"ERNIE-Bot-turbo": 0.5715, // ¥0.008 / 1k tokens "ERNIE-Bot-turbo": 0.5715, // ¥0.008 / 1k tokens //renamed to ERNIE-Lite-8K
"ERNIE-Bot-4": 8.572, // ¥0.12 / 1k tokens "ERNIE-Bot-4": 8.572, // ¥0.12 / 1k tokens //renamed to ERNIE-4.0-8K
"ERNIE-4.0-8K": 8.572, // ¥0.12 / 1k tokens
"ERNIE-3.5-8K": 0.8572, // ¥0.012 / 1k tokens
"ERNIE-Speed-8K": 0.2858, // ¥0.004 / 1k tokens
"ERNIE-Speed-128K": 0.2858, // ¥0.004 / 1k tokens
"ERNIE-Lite-8K": 0.2143, // ¥0.003 / 1k tokens
"ERNIE-Tiny-8K": 0.0715, // ¥0.001 / 1k tokens
"ERNIE-Character-8K": 0.2858, // ¥0.004 / 1k tokens
"ERNIE-Functions-8K": 0.2858, // ¥0.004 / 1k tokens
"Embedding-V1": 0.1429, // ¥0.002 / 1k tokens "Embedding-V1": 0.1429, // ¥0.002 / 1k tokens
"PaLM-2": 1, "PaLM-2": 1,
"gemini-pro": 1, // $0.00025 / 1k characters -> $0.001 / 1k tokens "gemini-pro": 1, // $0.00025 / 1k characters -> $0.001 / 1k tokens
@ -80,6 +88,7 @@ var DefaultModelRatio = map[string]float64{
"gemini-1.0-pro-vision-001": 1, "gemini-1.0-pro-vision-001": 1,
"gemini-1.0-pro-001": 1, "gemini-1.0-pro-001": 1,
"gemini-1.5-pro-latest": 1, "gemini-1.5-pro-latest": 1,
"gemini-1.5-flash-latest": 1,
"gemini-1.0-pro-latest": 1, "gemini-1.0-pro-latest": 1,
"gemini-1.0-pro-vision-latest": 1, "gemini-1.0-pro-vision-latest": 1,
"gemini-ultra": 1, "gemini-ultra": 1,
@ -98,6 +107,9 @@ var DefaultModelRatio = map[string]float64{
"SparkDesk-v3.1": 1.2858, // ¥0.018 / 1k tokens "SparkDesk-v3.1": 1.2858, // ¥0.018 / 1k tokens
"SparkDesk-v3.5": 1.2858, // ¥0.018 / 1k tokens "SparkDesk-v3.5": 1.2858, // ¥0.018 / 1k tokens
"360GPT_S2_V9": 0.8572, // ¥0.012 / 1k tokens "360GPT_S2_V9": 0.8572, // ¥0.012 / 1k tokens
"360gpt-turbo": 0.0858, // ¥0.0012 / 1k tokens
"360gpt-turbo-responsibility-8k": 0.8572, // ¥0.012 / 1k tokens
"360gpt-pro": 0.8572, // ¥0.012 / 1k tokens
"embedding-bert-512-v1": 0.0715, // ¥0.001 / 1k tokens "embedding-bert-512-v1": 0.0715, // ¥0.001 / 1k tokens
"embedding_s1_v1": 0.0715, // ¥0.001 / 1k tokens "embedding_s1_v1": 0.0715, // ¥0.001 / 1k tokens
"semantic_similarity_s1_v1": 0.0715, // ¥0.001 / 1k tokens "semantic_similarity_s1_v1": 0.0715, // ¥0.001 / 1k tokens
@ -299,6 +311,15 @@ func GetCompletionRatio(name string) float64 {
if strings.HasPrefix(name, "deepseek") { if strings.HasPrefix(name, "deepseek") {
return 2 return 2
} }
if strings.HasPrefix(name, "ERNIE-Speed-") {
return 2
} else if strings.HasPrefix(name, "ERNIE-Lite-") {
return 2
} else if strings.HasPrefix(name, "ERNIE-Character") {
return 2
} else if strings.HasPrefix(name, "ERNIE-Functions") {
return 2
}
switch name { switch name {
case "llama2-70b-4096": case "llama2-70b-4096":
return 0.8 / 0.64 return 0.8 / 0.64

View File

@ -258,3 +258,12 @@ func MapToJsonStrFloat(m map[string]float64) string {
} }
return string(bytes) return string(bytes)
} }
func StrToMap(str string) map[string]interface{} {
m := make(map[string]interface{})
err := json.Unmarshal([]byte(str), &m)
if err != nil {
return nil
}
return m
}

View File

@ -64,7 +64,21 @@ func testChannel(channel *model.Channel, testModel string) (err error, openaiErr
} else { } else {
testModel = adaptor.GetModelList()[0] testModel = adaptor.GetModelList()[0]
} }
} else {
modelMapping := *channel.ModelMapping
if modelMapping != "" && modelMapping != "{}" {
modelMap := make(map[string]string)
err := json.Unmarshal([]byte(modelMapping), &modelMap)
if err != nil {
openaiErr := service.OpenAIErrorWrapperLocal(err, "unmarshal_model_mapping_failed", http.StatusInternalServerError).Error
return err, &openaiErr
} }
if modelMap[testModel] != "" {
testModel = modelMap[testModel]
}
}
}
request := buildTestRequest() request := buildTestRequest()
request.Model = testModel request.Model = testModel
meta.UpstreamModelName = testModel meta.UpstreamModelName = testModel

View File

@ -108,8 +108,8 @@ func init() {
}) })
} }
openAIModelsMap = make(map[string]dto.OpenAIModels) openAIModelsMap = make(map[string]dto.OpenAIModels)
for _, model := range openAIModels { for _, aiModel := range openAIModels {
openAIModelsMap[model.Id] = model openAIModelsMap[aiModel.Id] = aiModel
} }
channelId2Models = make(map[int][]string) channelId2Models = make(map[int][]string)
for i := 1; i <= common.ChannelTypeDummy; i++ { for i := 1; i <= common.ChannelTypeDummy; i++ {
@ -174,8 +174,8 @@ func DashboardListModels(c *gin.Context) {
func RetrieveModel(c *gin.Context) { func RetrieveModel(c *gin.Context) {
modelId := c.Param("model") modelId := c.Param("model")
if model, ok := openAIModelsMap[modelId]; ok { if aiModel, ok := openAIModelsMap[modelId]; ok {
c.JSON(200, model) c.JSON(200, aiModel)
} else { } else {
openAIError := dto.OpenAIError{ openAIError := dto.OpenAIError{
Message: fmt.Sprintf("The model '%s' does not exist", modelId), Message: fmt.Sprintf("The model '%s' does not exist", modelId),
@ -191,12 +191,12 @@ func RetrieveModel(c *gin.Context) {
func GetPricing(c *gin.Context) { func GetPricing(c *gin.Context) {
userId := c.GetInt("id") userId := c.GetInt("id")
user, _ := model.GetUserById(userId, true) group, err := model.CacheGetUserGroup(userId)
groupRatio := common.GetGroupRatio("default") groupRatio := common.GetGroupRatio("default")
if user != nil { if err != nil {
groupRatio = common.GetGroupRatio(user.Group) groupRatio = common.GetGroupRatio(group)
} }
pricing := model.GetPricing(user, openAIModels) pricing := model.GetPricing(group)
c.JSON(200, gin.H{ c.JSON(200, gin.H{
"success": true, "success": true,
"data": pricing, "data": pricing,

View File

@ -43,7 +43,7 @@ func Relay(c *gin.Context) {
group := c.GetString("group") group := c.GetString("group")
originalModel := c.GetString("original_model") originalModel := c.GetString("original_model")
openaiErr := relayHandler(c, relayMode) openaiErr := relayHandler(c, relayMode)
useChannel := []int{channelId} c.Set("use_channel", []string{fmt.Sprintf("%d", channelId)})
if openaiErr != nil { if openaiErr != nil {
go processChannelError(c, channelId, openaiErr) go processChannelError(c, channelId, openaiErr)
} else { } else {
@ -56,7 +56,9 @@ func Relay(c *gin.Context) {
break break
} }
channelId = channel.Id channelId = channel.Id
useChannel = append(useChannel, channelId) useChannel := c.GetStringSlice("use_channel")
useChannel = append(useChannel, fmt.Sprintf("%d", channelId))
c.Set("use_channel", useChannel)
common.LogInfo(c.Request.Context(), fmt.Sprintf("using channel #%d to retry (remain times %d)", channel.Id, i)) common.LogInfo(c.Request.Context(), fmt.Sprintf("using channel #%d to retry (remain times %d)", channel.Id, i))
middleware.SetupContextForSelectedChannel(c, channel, originalModel) middleware.SetupContextForSelectedChannel(c, channel, originalModel)
@ -67,6 +69,7 @@ func Relay(c *gin.Context) {
go processChannelError(c, channelId, openaiErr) go processChannelError(c, channelId, openaiErr)
} }
} }
useChannel := c.GetStringSlice("use_channel")
if len(useChannel) > 1 { if len(useChannel) > 1 {
retryLogStr := fmt.Sprintf("重试:%s", strings.Trim(strings.Join(strings.Fields(fmt.Sprint(useChannel)), "->"), "[]")) retryLogStr := fmt.Sprintf("重试:%s", strings.Trim(strings.Join(strings.Fields(fmt.Sprint(useChannel)), "->"), "[]"))
common.LogInfo(c.Request.Context(), retryLogStr) common.LogInfo(c.Request.Context(), retryLogStr)

View File

@ -155,6 +155,16 @@ func GetUserLogs(userId int, logType int, startTimestamp int64, endTimestamp int
err = tx.Order("id desc").Limit(num).Offset(startIdx).Omit("id").Find(&logs).Error err = tx.Order("id desc").Limit(num).Offset(startIdx).Omit("id").Find(&logs).Error
return logs, total, err return logs, total, err
for i := range logs {
var otherMap map[string]interface{}
otherMap = common.StrToMap(logs[i].Other)
if otherMap != nil {
// delete admin
delete(otherMap, "admin_info")
}
logs[i].Other = common.MapToJsonStr(otherMap)
}
return logs, total, err
} }
func SearchAllLogs(keyword string) (logs []*Log, err error) { func SearchAllLogs(keyword string) (logs []*Log, err error) {

View File

@ -93,12 +93,12 @@ func InitDB() (err error) {
if !common.IsMasterNode { if !common.IsMasterNode {
return nil return nil
} }
if common.UsingMySQL { //if common.UsingMySQL {
_, _ = sqlDB.Exec("DROP INDEX idx_channels_key ON channels;") // TODO: delete this line when most users have upgraded // _, _ = sqlDB.Exec("DROP INDEX idx_channels_key ON channels;") // TODO: delete this line when most users have upgraded
_, _ = sqlDB.Exec("ALTER TABLE midjourneys MODIFY action VARCHAR(40);") // TODO: delete this line when most users have upgraded // _, _ = sqlDB.Exec("ALTER TABLE midjourneys MODIFY action VARCHAR(40);") // TODO: delete this line when most users have upgraded
_, _ = sqlDB.Exec("ALTER TABLE midjourneys MODIFY progress VARCHAR(30);") // TODO: delete this line when most users have upgraded // _, _ = sqlDB.Exec("ALTER TABLE midjourneys MODIFY progress VARCHAR(30);") // TODO: delete this line when most users have upgraded
_, _ = sqlDB.Exec("ALTER TABLE midjourneys MODIFY status VARCHAR(20);") // TODO: delete this line when most users have upgraded // _, _ = sqlDB.Exec("ALTER TABLE midjourneys MODIFY status VARCHAR(20);") // TODO: delete this line when most users have upgraded
} //}
common.SysLog("database migration started") common.SysLog("database migration started")
err = db.AutoMigrate(&Channel{}) err = db.AutoMigrate(&Channel{})
if err != nil { if err != nil {

View File

@ -13,16 +13,16 @@ var (
updatePricingLock sync.Mutex updatePricingLock sync.Mutex
) )
func GetPricing(user *User, openAIModels []dto.OpenAIModels) []dto.ModelPricing { func GetPricing(group string) []dto.ModelPricing {
updatePricingLock.Lock() updatePricingLock.Lock()
defer updatePricingLock.Unlock() defer updatePricingLock.Unlock()
if time.Since(lastGetPricingTime) > time.Minute*1 || len(pricingMap) == 0 { if time.Since(lastGetPricingTime) > time.Minute*1 || len(pricingMap) == 0 {
updatePricing(openAIModels) updatePricing()
} }
if user != nil { if group != "" {
userPricingMap := make([]dto.ModelPricing, 0) userPricingMap := make([]dto.ModelPricing, 0)
models := GetGroupModels(user.Group) models := GetGroupModels(group)
for _, pricing := range pricingMap { for _, pricing := range pricingMap {
if !common.StringsContains(models, pricing.ModelName) { if !common.StringsContains(models, pricing.ModelName) {
pricing.Available = false pricing.Available = false
@ -34,28 +34,19 @@ func GetPricing(user *User, openAIModels []dto.OpenAIModels) []dto.ModelPricing
return pricingMap return pricingMap
} }
func updatePricing(openAIModels []dto.OpenAIModels) { func updatePricing() {
modelRatios := common.GetModelRatios() //modelRatios := common.GetModelRatios()
enabledModels := GetEnabledModels() enabledModels := GetEnabledModels()
allModels := make(map[string]string) allModels := make(map[string]int)
for _, openAIModel := range openAIModels { for i, model := range enabledModels {
if common.StringsContains(enabledModels, openAIModel.Id) { allModels[model] = i
allModels[openAIModel.Id] = openAIModel.OwnedBy
}
}
for model, _ := range modelRatios {
if common.StringsContains(enabledModels, model) {
if _, ok := allModels[model]; !ok {
allModels[model] = "custom"
}
}
} }
pricingMap = make([]dto.ModelPricing, 0) pricingMap = make([]dto.ModelPricing, 0)
for model, ownerBy := range allModels { for model, _ := range allModels {
pricing := dto.ModelPricing{ pricing := dto.ModelPricing{
Available: true, Available: true,
ModelName: model, ModelName: model,
OwnerBy: ownerBy,
} }
modelPrice, findPrice := common.GetModelPrice(model, false) modelPrice, findPrice := common.GetModelPrice(model, false)
if findPrice { if findPrice {

View File

@ -11,7 +11,7 @@ import (
type Token struct { type Token struct {
Id int `json:"id"` Id int `json:"id"`
UserId int `json:"user_id"` UserId int `json:"user_id" gorm:"index"`
Key string `json:"key" gorm:"type:char(48);uniqueIndex"` Key string `json:"key" gorm:"type:char(48);uniqueIndex"`
Status int `json:"status" gorm:"default:1"` Status int `json:"status" gorm:"default:1"`
Name string `json:"name" gorm:"index" ` Name string `json:"name" gorm:"index" `

View File

@ -1,6 +1,9 @@
package ai360 package ai360
var ModelList = []string{ var ModelList = []string{
"360gpt-turbo",
"360gpt-turbo-responsibility-8k",
"360gpt-pro",
"360GPT_S2_V9", "360GPT_S2_V9",
"embedding-bert-512-v1", "embedding-bert-512-v1",
"embedding_s1_v1", "embedding_s1_v1",

View File

@ -9,6 +9,7 @@ import (
"one-api/relay/channel" "one-api/relay/channel"
relaycommon "one-api/relay/common" relaycommon "one-api/relay/common"
"one-api/relay/constant" "one-api/relay/constant"
"strings"
) )
type Adaptor struct { type Adaptor struct {
@ -33,8 +34,24 @@ func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant" fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant"
case "BLOOMZ-7B": case "BLOOMZ-7B":
fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/bloomz_7b1" fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/bloomz_7b1"
case "ERNIE-4.0-8K":
fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions_pro"
case "ERNIE-3.5-8K":
fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions"
case "ERNIE-Speed-8K":
fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie_speed"
case "ERNIE-Character-8K":
fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-char-8k"
case "ERNIE-Functions-8K":
fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-func-8k"
case "ERNIE-Lite-8K-0922":
fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant"
case "Yi-34B-Chat":
fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/yi_34b_chat"
case "Embedding-V1": case "Embedding-V1":
fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/embeddings/embedding-v1" fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/embeddings/embedding-v1"
default:
fullRequestURL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/" + strings.ToLower(info.UpstreamModelName)
} }
var accessToken string var accessToken string
var err error var err error

View File

@ -1,11 +1,19 @@
package baidu package baidu
var ModelList = []string{ var ModelList = []string{
"ERNIE-Bot-4", "ERNIE-3.5-8K",
"ERNIE-Bot-8K", "ERNIE-4.0-8K",
"ERNIE-Bot", "ERNIE-Speed-8K",
"ERNIE-Speed", "ERNIE-Speed-128K",
"ERNIE-Bot-turbo", "ERNIE-Lite-8K",
"ERNIE-Tiny-8K",
"ERNIE-Character-8K",
"ERNIE-Functions-8K",
//"ERNIE-Bot-4",
//"ERNIE-Bot-8K",
//"ERNIE-Bot",
//"ERNIE-Speed",
//"ERNIE-Bot-turbo",
"Embedding-V1", "Embedding-V1",
} }

View File

@ -21,6 +21,7 @@ func (a *Adaptor) Init(info *relaycommon.RelayInfo, request dto.GeneralOpenAIReq
// 定义一个映射,存储模型名称和对应的版本 // 定义一个映射,存储模型名称和对应的版本
var modelVersionMap = map[string]string{ var modelVersionMap = map[string]string{
"gemini-1.5-pro-latest": "v1beta", "gemini-1.5-pro-latest": "v1beta",
"gemini-1.5-flash-latest": "v1beta",
"gemini-ultra": "v1beta", "gemini-ultra": "v1beta",
} }

View File

@ -5,7 +5,7 @@ const (
) )
var ModelList = []string{ var ModelList = []string{
"gemini-1.0-pro-latest", "gemini-1.0-pro-001", "gemini-1.5-pro-latest", "gemini-ultra", "gemini-1.0-pro-latest", "gemini-1.0-pro-001", "gemini-1.5-pro-latest", "gemini-1.5-flash-latest", "gemini-ultra",
"gemini-1.0-pro-vision-latest", "gemini-1.0-pro-vision-001", "gemini-1.0-pro-vision-latest", "gemini-1.0-pro-vision-001",
} }

View File

@ -15,7 +15,7 @@ const (
APITypeAIProxyLibrary APITypeAIProxyLibrary
APITypeTencent APITypeTencent
APITypeGemini APITypeGemini
APITypeZhipu_v4 APITypeZhipuV4
APITypeOllama APITypeOllama
APITypePerplexity APITypePerplexity
APITypeAws APITypeAws
@ -48,7 +48,7 @@ func ChannelType2APIType(channelType int) (int, bool) {
case common.ChannelTypeGemini: case common.ChannelTypeGemini:
apiType = APITypeGemini apiType = APITypeGemini
case common.ChannelTypeZhipu_v4: case common.ChannelTypeZhipu_v4:
apiType = APITypeZhipu_v4 apiType = APITypeZhipuV4
case common.ChannelTypeOllama: case common.ChannelTypeOllama:
apiType = APITypeOllama apiType = APITypeOllama
case common.ChannelTypePerplexity: case common.ChannelTypePerplexity:

View File

@ -323,6 +323,9 @@ func postConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, textRe
other["group_ratio"] = groupRatio other["group_ratio"] = groupRatio
other["completion_ratio"] = completionRatio other["completion_ratio"] = completionRatio
other["model_price"] = modelPrice other["model_price"] = modelPrice
adminInfo := make(map[string]interface{})
adminInfo["use_channel"] = ctx.GetStringSlice("use_channel")
other["admin_info"] = adminInfo
model.RecordConsumeLog(ctx, relayInfo.UserId, relayInfo.ChannelId, promptTokens, completionTokens, logModel, tokenName, quota, logContent, relayInfo.TokenId, userQuota, int(useTimeSeconds), relayInfo.IsStream, other) model.RecordConsumeLog(ctx, relayInfo.UserId, relayInfo.ChannelId, promptTokens, completionTokens, logModel, tokenName, quota, logContent, relayInfo.TokenId, userQuota, int(useTimeSeconds), relayInfo.IsStream, other)
//if quota != 0 { //if quota != 0 {

View File

@ -41,7 +41,7 @@ func GetAdaptor(apiType int) channel.Adaptor {
return &xunfei.Adaptor{} return &xunfei.Adaptor{}
case constant.APITypeZhipu: case constant.APITypeZhipu:
return &zhipu.Adaptor{} return &zhipu.Adaptor{}
case constant.APITypeZhipu_v4: case constant.APITypeZhipuV4:
return &zhipu_4v.Adaptor{} return &zhipu_4v.Adaptor{}
case constant.APITypeOllama: case constant.APITypeOllama:
return &ollama.Adaptor{} return &ollama.Adaptor{}

View File

@ -20,7 +20,7 @@ func SetApiRouter(router *gin.Engine) {
apiRouter.GET("/about", controller.GetAbout) apiRouter.GET("/about", controller.GetAbout)
apiRouter.GET("/midjourney", controller.GetMidjourney) apiRouter.GET("/midjourney", controller.GetMidjourney)
apiRouter.GET("/home_page_content", controller.GetHomePageContent) apiRouter.GET("/home_page_content", controller.GetHomePageContent)
apiRouter.GET("/pricing", middleware.CriticalRateLimit(), middleware.TryUserAuth(), controller.GetPricing) apiRouter.GET("/pricing", middleware.TryUserAuth(), controller.GetPricing)
apiRouter.GET("/verification", middleware.CriticalRateLimit(), middleware.TurnstileCheck(), controller.SendEmailVerification) apiRouter.GET("/verification", middleware.CriticalRateLimit(), middleware.TurnstileCheck(), controller.SendEmailVerification)
apiRouter.GET("/reset_password", middleware.CriticalRateLimit(), middleware.TurnstileCheck(), controller.SendPasswordResetEmail) apiRouter.GET("/reset_password", middleware.CriticalRateLimit(), middleware.TurnstileCheck(), controller.SendPasswordResetEmail)
apiRouter.POST("/user/reset", middleware.CriticalRateLimit(), controller.ResetPassword) apiRouter.POST("/user/reset", middleware.CriticalRateLimit(), controller.ResetPassword)

View File

@ -6,6 +6,7 @@ import {
showError, showError,
showInfo, showInfo,
showSuccess, showSuccess,
showWarning,
timestamp2string, timestamp2string,
} from '../helpers'; } from '../helpers';
@ -309,6 +310,12 @@ const ChannelsTable = () => {
const setChannelFormat = (channels) => { const setChannelFormat = (channels) => {
for (let i = 0; i < channels.length; i++) { for (let i = 0; i < channels.length; i++) {
if (channels[i].type === 8) {
showWarning(
'检测到您使用了“自定义渠道”类型请更换为“OpenAI”渠道类型',
);
showWarning('下个版本将不再支持“自定义渠道”类型!');
}
channels[i].key = '' + channels[i].id; channels[i].key = '' + channels[i].id;
let test_models = []; let test_models = [];
channels[i].models.split(',').forEach((item, index) => { channels[i].models.split(',').forEach((item, index) => {

View File

@ -294,6 +294,30 @@ const LogsTable = () => {
); );
}, },
}, },
{
title: '重试',
dataIndex: 'retry',
className: isAdmin() ? 'tableShow' : 'tableHiddle',
render: (text, record, index) => {
let content = '渠道:' + record.channel;
if (record.other !== '') {
let other = JSON.parse(record.other);
if (other.admin_info !== undefined) {
if (
other.admin_info.use_channel !== null &&
other.admin_info.use_channel !== undefined &&
other.admin_info.use_channel !== ''
) {
// channel id array
let useChannel = other.admin_info.use_channel;
let useChannelStr = useChannel.join('->');
content = `渠道:${useChannelStr}`;
}
}
}
return isAdminUser ? <div>{content}</div> : <></>;
},
},
{ {
title: '详情', title: '详情',
dataIndex: 'content', dataIndex: 'content',

View File

@ -1,7 +1,16 @@
import React, { useContext, useEffect, useState } from 'react'; import React, { useContext, useEffect, useRef, useState } from 'react';
import { API, copy, showError, showSuccess } from '../helpers'; import { API, copy, showError, showSuccess } from '../helpers';
import { Banner, Layout, Modal, Table, Tag, Tooltip } from '@douyinfe/semi-ui'; import {
Banner,
Input,
Layout,
Modal,
Space,
Table,
Tag,
Tooltip,
} from '@douyinfe/semi-ui';
import { stringToColor } from '../helpers/render.js'; import { stringToColor } from '../helpers/render.js';
import { UserContext } from '../context/User/index.js'; import { UserContext } from '../context/User/index.js';
import Text from '@douyinfe/semi-ui/lib/es/typography/text'; import Text from '@douyinfe/semi-ui/lib/es/typography/text';
@ -45,6 +54,27 @@ function renderAvailable(available) {
} }
const ModelPricing = () => { const ModelPricing = () => {
const [filteredValue, setFilteredValue] = useState([]);
const compositionRef = useRef({ isComposition: false });
const handleChange = (value) => {
if (compositionRef.current.isComposition) {
return;
}
const newFilteredValue = value ? [value] : [];
setFilteredValue(newFilteredValue);
};
const handleCompositionStart = () => {
compositionRef.current.isComposition = true;
};
const handleCompositionEnd = (event) => {
compositionRef.current.isComposition = false;
const value = event.target.value;
const newFilteredValue = value ? [value] : [];
setFilteredValue(newFilteredValue);
};
const columns = [ const columns = [
{ {
title: '可用性', title: '可用性',
@ -52,28 +82,28 @@ const ModelPricing = () => {
render: (text, record, index) => { render: (text, record, index) => {
return renderAvailable(text); return renderAvailable(text);
}, },
sorter: (a, b) => a.available - b.available,
}, },
{ {
title: '提供者', title: (
dataIndex: 'owner_by', <Space>
render: (text, record, index) => { <span>模型名称</span>
return ( <Input
<> placeholder='模糊搜索'
<Tag color={stringToColor(text)} size='large'> style={{ width: 200 }}
{text} onCompositionStart={handleCompositionStart}
</Tag> onCompositionEnd={handleCompositionEnd}
</> onChange={handleChange}
); showClear
}, />
}, </Space>
{ ),
title: '模型名称',
dataIndex: 'model_name', // 以finish_time作为dataIndex dataIndex: 'model_name', // 以finish_time作为dataIndex
render: (text, record, index) => { render: (text, record, index) => {
return ( return (
<> <>
<Tag <Tag
color={stringToColor(record.owner_by)} color={stringToColor(text)}
size='large' size='large'
onClick={() => { onClick={() => {
copyText(text); copyText(text);
@ -84,6 +114,8 @@ const ModelPricing = () => {
</> </>
); );
}, },
onFilter: (value, record) => record.model_name.includes(value),
filteredValue,
}, },
{ {
title: '计费类型', title: '计费类型',
@ -91,6 +123,7 @@ const ModelPricing = () => {
render: (text, record, index) => { render: (text, record, index) => {
return renderQuotaType(parseInt(text)); return renderQuotaType(parseInt(text));
}, },
sorter: (a, b) => a.quota_type - b.quota_type,
}, },
{ {
title: '模型倍率', title: '模型倍率',
@ -150,14 +183,17 @@ const ModelPricing = () => {
return a.quota_type - b.quota_type; return a.quota_type - b.quota_type;
}); });
// sort by owner_by, openai is max, other use localeCompare // sort by model_name, start with gpt is max, other use localeCompare
models.sort((a, b) => { models.sort((a, b) => {
if (a.owner_by === 'openai') { if (a.model_name.startsWith('gpt') && !b.model_name.startsWith('gpt')) {
return -1; return -1;
} else if (b.owner_by === 'openai') { } else if (
!a.model_name.startsWith('gpt') &&
b.model_name.startsWith('gpt')
) {
return 1; return 1;
} else { } else {
return a.owner_by.localeCompare(b.owner_by); return a.model_name.localeCompare(b.model_name);
} }
}); });