mirror of
https://github.com/linux-do/new-api.git
synced 2025-11-09 07:33:41 +08:00
Merge branch 'main' into refactor-settings-operation
This commit is contained in:
@@ -208,6 +208,8 @@ const (
|
|||||||
ChannelTypeLingYiWanWu = 31
|
ChannelTypeLingYiWanWu = 31
|
||||||
ChannelTypeAws = 33
|
ChannelTypeAws = 33
|
||||||
ChannelTypeCohere = 34
|
ChannelTypeCohere = 34
|
||||||
|
|
||||||
|
ChannelTypeDummy // this one is only for count, do not add any channel after this
|
||||||
)
|
)
|
||||||
|
|
||||||
var ChannelBaseURLs = []string{
|
var ChannelBaseURLs = []string{
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ var DefaultModelRatio = map[string]float64{
|
|||||||
"gpt-4-vision-preview": 5, // $0.01 / 1K tokens
|
"gpt-4-vision-preview": 5, // $0.01 / 1K tokens
|
||||||
"gpt-4-1106-vision-preview": 5, // $0.01 / 1K tokens
|
"gpt-4-1106-vision-preview": 5, // $0.01 / 1K tokens
|
||||||
"gpt-4-turbo": 5, // $0.01 / 1K tokens
|
"gpt-4-turbo": 5, // $0.01 / 1K tokens
|
||||||
|
"gpt-4-turbo-2024-04-09": 5, // $0.01 / 1K tokens
|
||||||
"gpt-3.5-turbo": 0.25, // $0.0015 / 1K tokens
|
"gpt-3.5-turbo": 0.25, // $0.0015 / 1K tokens
|
||||||
//"gpt-3.5-turbo-0301": 0.75, //deprecated
|
//"gpt-3.5-turbo-0301": 0.75, //deprecated
|
||||||
"gpt-3.5-turbo-0613": 0.75,
|
"gpt-3.5-turbo-0613": 0.75,
|
||||||
@@ -111,6 +112,8 @@ var DefaultModelRatio = map[string]float64{
|
|||||||
"command-light-nightly": 0.5,
|
"command-light-nightly": 0.5,
|
||||||
"command-r": 0.25,
|
"command-r": 0.25,
|
||||||
"command-r-plus ": 1.5,
|
"command-r-plus ": 1.5,
|
||||||
|
"deepseek-chat": 0.07,
|
||||||
|
"deepseek-coder": 0.07,
|
||||||
}
|
}
|
||||||
|
|
||||||
var DefaultModelPrice = map[string]float64{
|
var DefaultModelPrice = map[string]float64{
|
||||||
@@ -135,6 +138,12 @@ var DefaultModelPrice = map[string]float64{
|
|||||||
var modelPrice map[string]float64 = nil
|
var modelPrice map[string]float64 = nil
|
||||||
var modelRatio map[string]float64 = nil
|
var modelRatio map[string]float64 = nil
|
||||||
|
|
||||||
|
var CompletionRatio map[string]float64 = nil
|
||||||
|
var DefaultCompletionRatio = map[string]float64{
|
||||||
|
"gpt-4-gizmo-*": 2,
|
||||||
|
"gpt-4-all": 2,
|
||||||
|
}
|
||||||
|
|
||||||
func ModelPrice2JSONString() string {
|
func ModelPrice2JSONString() string {
|
||||||
if modelPrice == nil {
|
if modelPrice == nil {
|
||||||
modelPrice = DefaultModelPrice
|
modelPrice = DefaultModelPrice
|
||||||
@@ -199,6 +208,22 @@ func GetModelRatio(name string) float64 {
|
|||||||
return ratio
|
return ratio
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CompletionRatio2JSONString() string {
|
||||||
|
if CompletionRatio == nil {
|
||||||
|
CompletionRatio = DefaultCompletionRatio
|
||||||
|
}
|
||||||
|
jsonBytes, err := json.Marshal(CompletionRatio)
|
||||||
|
if err != nil {
|
||||||
|
SysError("error marshalling completion ratio: " + err.Error())
|
||||||
|
}
|
||||||
|
return string(jsonBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateCompletionRatioByJSONString(jsonStr string) error {
|
||||||
|
CompletionRatio = make(map[string]float64)
|
||||||
|
return json.Unmarshal([]byte(jsonStr), &CompletionRatio)
|
||||||
|
}
|
||||||
|
|
||||||
func GetCompletionRatio(name string) float64 {
|
func GetCompletionRatio(name string) float64 {
|
||||||
if strings.HasPrefix(name, "gpt-3.5") {
|
if strings.HasPrefix(name, "gpt-3.5") {
|
||||||
if name == "gpt-3.5-turbo" || strings.HasSuffix(name, "0125") {
|
if name == "gpt-3.5-turbo" || strings.HasSuffix(name, "0125") {
|
||||||
@@ -211,7 +236,7 @@ func GetCompletionRatio(name string) float64 {
|
|||||||
}
|
}
|
||||||
return 4.0 / 3.0
|
return 4.0 / 3.0
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(name, "gpt-4") {
|
if strings.HasPrefix(name, "gpt-4") && name != "gpt-4-all" && !strings.HasPrefix(name, "gpt-4-gizmo") {
|
||||||
if strings.HasPrefix(name, "gpt-4-turbo") || strings.HasSuffix(name, "preview") {
|
if strings.HasPrefix(name, "gpt-4-turbo") || strings.HasSuffix(name, "preview") {
|
||||||
return 3
|
return 3
|
||||||
}
|
}
|
||||||
@@ -240,9 +265,19 @@ func GetCompletionRatio(name string) float64 {
|
|||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if strings.HasPrefix(name, "deepseek") {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
switch name {
|
switch name {
|
||||||
case "llama2-70b-4096":
|
case "llama2-70b-4096":
|
||||||
return 0.8 / 0.7
|
return 0.8 / 0.64
|
||||||
|
case "llama3-8b-8192":
|
||||||
|
return 2
|
||||||
|
case "llama3-70b-8192":
|
||||||
|
return 0.79 / 0.59
|
||||||
|
}
|
||||||
|
if ratio, ok := CompletionRatio[name]; ok {
|
||||||
|
return ratio
|
||||||
}
|
}
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"html/template"
|
"html/template"
|
||||||
@@ -241,3 +242,11 @@ func RandomSleep() {
|
|||||||
// Sleep for 0-3000 ms
|
// Sleep for 0-3000 ms
|
||||||
time.Sleep(time.Duration(rand.Intn(3000)) * time.Millisecond)
|
time.Sleep(time.Duration(rand.Intn(3000)) * time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MapToJsonStr(m map[string]interface{}) string {
|
||||||
|
bytes, err := json.Marshal(m)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(bytes)
|
||||||
|
}
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ func testAllChannels(notify bool) error {
|
|||||||
if isChannelEnabled && service.ShouldDisableChannel(openaiErr, -1) && ban {
|
if isChannelEnabled && service.ShouldDisableChannel(openaiErr, -1) && ban {
|
||||||
service.DisableChannel(channel.Id, channel.Name, err.Error())
|
service.DisableChannel(channel.Id, channel.Name, err.Error())
|
||||||
}
|
}
|
||||||
if !isChannelEnabled && service.ShouldEnableChannel(err, openaiErr) {
|
if !isChannelEnabled && service.ShouldEnableChannel(err, openaiErr, channel.Status) {
|
||||||
service.EnableChannel(channel.Id, channel.Name)
|
service.EnableChannel(channel.Id, channel.Name)
|
||||||
}
|
}
|
||||||
channel.UpdateResponseTime(milliseconds)
|
channel.UpdateResponseTime(milliseconds)
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ func SendEmailVerification(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if common.EmailAliasRestrictionEnabled {
|
if common.EmailAliasRestrictionEnabled {
|
||||||
containsSpecialSymbols := strings.Contains(localPart, "+") || strings.Count(localPart, ".") > 1
|
containsSpecialSymbols := strings.Contains(localPart, "+") || strings.Contains(localPart, ".")
|
||||||
if containsSpecialSymbols {
|
if containsSpecialSymbols {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"success": false,
|
"success": false,
|
||||||
|
|||||||
@@ -4,13 +4,15 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"one-api/common"
|
||||||
"one-api/constant"
|
"one-api/constant"
|
||||||
"one-api/dto"
|
"one-api/dto"
|
||||||
"one-api/model"
|
"one-api/model"
|
||||||
"one-api/relay"
|
"one-api/relay"
|
||||||
"one-api/relay/channel/ai360"
|
"one-api/relay/channel/ai360"
|
||||||
"one-api/relay/channel/moonshot"
|
|
||||||
"one-api/relay/channel/lingyiwanwu"
|
"one-api/relay/channel/lingyiwanwu"
|
||||||
|
"one-api/relay/channel/moonshot"
|
||||||
|
relaycommon "one-api/relay/common"
|
||||||
relayconstant "one-api/relay/constant"
|
relayconstant "one-api/relay/constant"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -43,8 +45,9 @@ type OpenAIModels struct {
|
|||||||
|
|
||||||
var openAIModels []OpenAIModels
|
var openAIModels []OpenAIModels
|
||||||
var openAIModelsMap map[string]OpenAIModels
|
var openAIModelsMap map[string]OpenAIModels
|
||||||
|
var channelId2Models map[int][]string
|
||||||
|
|
||||||
func init() {
|
func getPermission() []OpenAIModelPermission {
|
||||||
var permission []OpenAIModelPermission
|
var permission []OpenAIModelPermission
|
||||||
permission = append(permission, OpenAIModelPermission{
|
permission = append(permission, OpenAIModelPermission{
|
||||||
Id: "modelperm-LwHkVFn8AcMItP432fKKDIKJ",
|
Id: "modelperm-LwHkVFn8AcMItP432fKKDIKJ",
|
||||||
@@ -60,7 +63,12 @@ func init() {
|
|||||||
Group: nil,
|
Group: nil,
|
||||||
IsBlocking: false,
|
IsBlocking: false,
|
||||||
})
|
})
|
||||||
|
return permission
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
// https://platform.openai.com/docs/models/model-endpoint-compatibility
|
// https://platform.openai.com/docs/models/model-endpoint-compatibility
|
||||||
|
permission := getPermission()
|
||||||
for i := 0; i < relayconstant.APITypeDummy; i++ {
|
for i := 0; i < relayconstant.APITypeDummy; i++ {
|
||||||
if i == relayconstant.APITypeAIProxyLibrary {
|
if i == relayconstant.APITypeAIProxyLibrary {
|
||||||
continue
|
continue
|
||||||
@@ -85,7 +93,7 @@ func init() {
|
|||||||
Id: modelName,
|
Id: modelName,
|
||||||
Object: "model",
|
Object: "model",
|
||||||
Created: 1626777600,
|
Created: 1626777600,
|
||||||
OwnedBy: "360",
|
OwnedBy: ai360.ChannelName,
|
||||||
Permission: permission,
|
Permission: permission,
|
||||||
Root: modelName,
|
Root: modelName,
|
||||||
Parent: nil,
|
Parent: nil,
|
||||||
@@ -128,6 +136,17 @@ func init() {
|
|||||||
for _, model := range openAIModels {
|
for _, model := range openAIModels {
|
||||||
openAIModelsMap[model.Id] = model
|
openAIModelsMap[model.Id] = model
|
||||||
}
|
}
|
||||||
|
channelId2Models = make(map[int][]string)
|
||||||
|
for i := 1; i <= common.ChannelTypeDummy; i++ {
|
||||||
|
apiType := relayconstant.ChannelType2APIType(i)
|
||||||
|
if apiType == -1 || apiType == relayconstant.APITypeAIProxyLibrary {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
meta := &relaycommon.RelayInfo{ChannelType: i}
|
||||||
|
adaptor := relay.GetAdaptor(apiType)
|
||||||
|
adaptor.Init(meta, dto.GeneralOpenAIRequest{})
|
||||||
|
channelId2Models[i] = adaptor.GetModelList()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListModels(c *gin.Context) {
|
func ListModels(c *gin.Context) {
|
||||||
@@ -142,21 +161,39 @@ func ListModels(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
models := model.GetGroupModels(user.Group)
|
models := model.GetGroupModels(user.Group)
|
||||||
userOpenAiModels := make([]OpenAIModels, 0)
|
userOpenAiModels := make([]OpenAIModels, 0)
|
||||||
|
permission := getPermission()
|
||||||
for _, s := range models {
|
for _, s := range models {
|
||||||
if _, ok := openAIModelsMap[s]; ok {
|
if _, ok := openAIModelsMap[s]; ok {
|
||||||
userOpenAiModels = append(userOpenAiModels, openAIModelsMap[s])
|
userOpenAiModels = append(userOpenAiModels, openAIModelsMap[s])
|
||||||
|
} else {
|
||||||
|
userOpenAiModels = append(userOpenAiModels, OpenAIModels{
|
||||||
|
Id: s,
|
||||||
|
Object: "model",
|
||||||
|
Created: 1626777600,
|
||||||
|
OwnedBy: "openai",
|
||||||
|
Permission: permission,
|
||||||
|
Root: s,
|
||||||
|
Parent: nil,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.JSON(200, gin.H{
|
c.JSON(200, gin.H{
|
||||||
"object": "list",
|
"success": true,
|
||||||
"data": userOpenAiModels,
|
"data": userOpenAiModels,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func ChannelListModels(c *gin.Context) {
|
func ChannelListModels(c *gin.Context) {
|
||||||
c.JSON(200, gin.H{
|
c.JSON(200, gin.H{
|
||||||
"object": "list",
|
"success": true,
|
||||||
"data": openAIModels,
|
"data": openAIModels,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func DashboardListModels(c *gin.Context) {
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"success": true,
|
||||||
|
"data": channelId2Models,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ type Log struct {
|
|||||||
IsStream bool `json:"is_stream" gorm:"default:false"`
|
IsStream bool `json:"is_stream" gorm:"default:false"`
|
||||||
ChannelId int `json:"channel" gorm:"index"`
|
ChannelId int `json:"channel" gorm:"index"`
|
||||||
TokenId int `json:"token_id" gorm:"default:0;index"`
|
TokenId int `json:"token_id" gorm:"default:0;index"`
|
||||||
|
Other string `json:"other"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -57,12 +58,13 @@ func RecordLog(userId int, logType int, content string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptTokens int, completionTokens int, modelName string, tokenName string, quota int, content string, tokenId int, userQuota int, useTimeSeconds int, isStream bool) {
|
func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptTokens int, completionTokens int, modelName string, tokenName string, quota int, content string, tokenId int, userQuota int, useTimeSeconds int, isStream bool, other map[string]interface{}) {
|
||||||
common.LogInfo(ctx, fmt.Sprintf("record consume log: userId=%d, 用户调用前余额=%d, channelId=%d, promptTokens=%d, completionTokens=%d, modelName=%s, tokenName=%s, quota=%d, content=%s", userId, userQuota, channelId, promptTokens, completionTokens, modelName, tokenName, quota, content))
|
common.LogInfo(ctx, fmt.Sprintf("record consume log: userId=%d, 用户调用前余额=%d, channelId=%d, promptTokens=%d, completionTokens=%d, modelName=%s, tokenName=%s, quota=%d, content=%s", userId, userQuota, channelId, promptTokens, completionTokens, modelName, tokenName, quota, content))
|
||||||
if !common.LogConsumeEnabled {
|
if !common.LogConsumeEnabled {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
username, _ := CacheGetUsername(userId)
|
username, _ := CacheGetUsername(userId)
|
||||||
|
otherStr := common.MapToJsonStr(other)
|
||||||
log := &Log{
|
log := &Log{
|
||||||
UserId: userId,
|
UserId: userId,
|
||||||
Username: username,
|
Username: username,
|
||||||
@@ -78,6 +80,7 @@ func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptToke
|
|||||||
TokenId: tokenId,
|
TokenId: tokenId,
|
||||||
UseTime: useTimeSeconds,
|
UseTime: useTimeSeconds,
|
||||||
IsStream: isStream,
|
IsStream: isStream,
|
||||||
|
Other: otherStr,
|
||||||
}
|
}
|
||||||
err := DB.Create(log).Error
|
err := DB.Create(log).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ func InitOptionMap() {
|
|||||||
common.OptionMap["ModelRatio"] = common.ModelRatio2JSONString()
|
common.OptionMap["ModelRatio"] = common.ModelRatio2JSONString()
|
||||||
common.OptionMap["ModelPrice"] = common.ModelPrice2JSONString()
|
common.OptionMap["ModelPrice"] = common.ModelPrice2JSONString()
|
||||||
common.OptionMap["GroupRatio"] = common.GroupRatio2JSONString()
|
common.OptionMap["GroupRatio"] = common.GroupRatio2JSONString()
|
||||||
|
common.OptionMap["CompletionRatio"] = common.CompletionRatio2JSONString()
|
||||||
common.OptionMap["TopUpLink"] = common.TopUpLink
|
common.OptionMap["TopUpLink"] = common.TopUpLink
|
||||||
common.OptionMap["ChatLink"] = common.ChatLink
|
common.OptionMap["ChatLink"] = common.ChatLink
|
||||||
common.OptionMap["ChatLink2"] = common.ChatLink2
|
common.OptionMap["ChatLink2"] = common.ChatLink2
|
||||||
@@ -290,6 +291,8 @@ func updateOptionMap(key string, value string) (err error) {
|
|||||||
err = common.UpdateModelRatioByJSONString(value)
|
err = common.UpdateModelRatioByJSONString(value)
|
||||||
case "GroupRatio":
|
case "GroupRatio":
|
||||||
err = common.UpdateGroupRatioByJSONString(value)
|
err = common.UpdateGroupRatioByJSONString(value)
|
||||||
|
case "CompletionRatio":
|
||||||
|
err = common.UpdateCompletionRatioByJSONString(value)
|
||||||
case "ModelPrice":
|
case "ModelPrice":
|
||||||
err = common.UpdateModelPriceByJSONString(value)
|
err = common.UpdateModelPriceByJSONString(value)
|
||||||
case "TopUpLink":
|
case "TopUpLink":
|
||||||
|
|||||||
@@ -253,14 +253,17 @@ func (user *User) Edit(updatePassword bool) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
newUser := *user
|
newUser := *user
|
||||||
DB.First(&user, user.Id)
|
updates := map[string]interface{}{
|
||||||
err = DB.Model(user).Updates(map[string]interface{}{
|
|
||||||
"username": newUser.Username,
|
"username": newUser.Username,
|
||||||
"password": newUser.Password,
|
|
||||||
"display_name": newUser.DisplayName,
|
"display_name": newUser.DisplayName,
|
||||||
"group": newUser.Group,
|
"group": newUser.Group,
|
||||||
"quota": newUser.Quota,
|
"quota": newUser.Quota,
|
||||||
}).Error
|
}
|
||||||
|
if updatePassword {
|
||||||
|
updates["password"] = newUser.Password
|
||||||
|
}
|
||||||
|
DB.First(&user, user.Id)
|
||||||
|
err = DB.Model(user).Updates(updates).Error
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if common.RedisEnabled {
|
if common.RedisEnabled {
|
||||||
_ = common.RedisSet(fmt.Sprintf("user_group:%d", user.Id), user.Group, time.Duration(UserId2GroupCacheSeconds)*time.Second)
|
_ = common.RedisSet(fmt.Sprintf("user_group:%d", user.Id), user.Group, time.Duration(UserId2GroupCacheSeconds)*time.Second)
|
||||||
|
|||||||
@@ -6,3 +6,5 @@ var ModelList = []string{
|
|||||||
"embedding_s1_v1",
|
"embedding_s1_v1",
|
||||||
"semantic_similarity_s1_v1",
|
"semantic_similarity_s1_v1",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ChannelName = "ai360"
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package ollama
|
package ollama
|
||||||
|
|
||||||
var ModelList []string
|
var ModelList = []string{
|
||||||
|
"llama3-7b",
|
||||||
|
}
|
||||||
|
|
||||||
var ChannelName = "ollama"
|
var ChannelName = "ollama"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ var ModelList = []string{
|
|||||||
"gpt-3.5-turbo-instruct",
|
"gpt-3.5-turbo-instruct",
|
||||||
"gpt-4", "gpt-4-0314", "gpt-4-0613", "gpt-4-1106-preview", "gpt-4-0125-preview",
|
"gpt-4", "gpt-4-0314", "gpt-4-0613", "gpt-4-1106-preview", "gpt-4-0125-preview",
|
||||||
"gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613",
|
"gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613",
|
||||||
"gpt-4-turbo-preview",
|
"gpt-4-turbo-preview", "gpt-4-turbo", "gpt-4-turbo-2024-04-09",
|
||||||
"gpt-4-vision-preview",
|
"gpt-4-vision-preview",
|
||||||
"text-embedding-ada-002", "text-embedding-3-small", "text-embedding-3-large",
|
"text-embedding-ada-002", "text-embedding-3-small", "text-embedding-3-large",
|
||||||
"text-curie-001", "text-babbage-001", "text-ada-001", "text-davinci-002", "text-davinci-003",
|
"text-curie-001", "text-babbage-001", "text-ada-001", "text-davinci-002", "text-davinci-003",
|
||||||
|
|||||||
@@ -25,8 +25,18 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func ChannelType2APIType(channelType int) int {
|
func ChannelType2APIType(channelType int) int {
|
||||||
apiType := APITypeOpenAI
|
apiType := -1
|
||||||
switch channelType {
|
switch channelType {
|
||||||
|
case common.ChannelTypeOpenAI:
|
||||||
|
apiType = APITypeOpenAI
|
||||||
|
case common.ChannelTypeAzure:
|
||||||
|
apiType = APITypeOpenAI
|
||||||
|
case common.ChannelTypeMoonshot:
|
||||||
|
apiType = APITypeOpenAI
|
||||||
|
case common.ChannelTypeLingYiWanWu:
|
||||||
|
apiType = APITypeOpenAI
|
||||||
|
case common.ChannelType360:
|
||||||
|
apiType = APITypeOpenAI
|
||||||
case common.ChannelTypeAnthropic:
|
case common.ChannelTypeAnthropic:
|
||||||
apiType = APITypeAnthropic
|
apiType = APITypeAnthropic
|
||||||
case common.ChannelTypeBaidu:
|
case common.ChannelTypeBaidu:
|
||||||
|
|||||||
@@ -196,7 +196,10 @@ func AudioHelper(c *gin.Context, relayMode int) *dto.OpenAIErrorWithStatusCode {
|
|||||||
if quota != 0 {
|
if quota != 0 {
|
||||||
tokenName := c.GetString("token_name")
|
tokenName := c.GetString("token_name")
|
||||||
logContent := fmt.Sprintf("模型倍率 %.2f,分组倍率 %.2f", modelRatio, groupRatio)
|
logContent := fmt.Sprintf("模型倍率 %.2f,分组倍率 %.2f", modelRatio, groupRatio)
|
||||||
model.RecordConsumeLog(ctx, userId, channelId, promptTokens, 0, audioRequest.Model, tokenName, quota, logContent, tokenId, userQuota, int(useTimeSeconds), false)
|
other := make(map[string]interface{})
|
||||||
|
other["model_ratio"] = modelRatio
|
||||||
|
other["group_ratio"] = groupRatio
|
||||||
|
model.RecordConsumeLog(ctx, userId, channelId, promptTokens, 0, audioRequest.Model, tokenName, quota, logContent, tokenId, userQuota, int(useTimeSeconds), false, other)
|
||||||
model.UpdateUserUsedQuotaAndRequestCount(userId, quota)
|
model.UpdateUserUsedQuotaAndRequestCount(userId, quota)
|
||||||
channelId := c.GetInt("channel_id")
|
channelId := c.GetInt("channel_id")
|
||||||
model.UpdateChannelUsedQuota(channelId, quota)
|
model.UpdateChannelUsedQuota(channelId, quota)
|
||||||
|
|||||||
@@ -191,7 +191,10 @@ func RelayImageHelper(c *gin.Context, relayMode int) *dto.OpenAIErrorWithStatusC
|
|||||||
quality = "hd"
|
quality = "hd"
|
||||||
}
|
}
|
||||||
logContent := fmt.Sprintf("模型倍率 %.2f,分组倍率 %.2f, 大小 %s, 品质 %s", modelRatio, groupRatio, imageRequest.Size, quality)
|
logContent := fmt.Sprintf("模型倍率 %.2f,分组倍率 %.2f, 大小 %s, 品质 %s", modelRatio, groupRatio, imageRequest.Size, quality)
|
||||||
model.RecordConsumeLog(ctx, userId, channelId, 0, 0, imageRequest.Model, tokenName, quota, logContent, tokenId, userQuota, int(useTimeSeconds), false)
|
other := make(map[string]interface{})
|
||||||
|
other["model_ratio"] = modelRatio
|
||||||
|
other["group_ratio"] = groupRatio
|
||||||
|
model.RecordConsumeLog(ctx, userId, channelId, 0, 0, imageRequest.Model, tokenName, quota, logContent, tokenId, userQuota, int(useTimeSeconds), false, other)
|
||||||
model.UpdateUserUsedQuotaAndRequestCount(userId, quota)
|
model.UpdateUserUsedQuotaAndRequestCount(userId, quota)
|
||||||
channelId := c.GetInt("channel_id")
|
channelId := c.GetInt("channel_id")
|
||||||
model.UpdateChannelUsedQuota(channelId, quota)
|
model.UpdateChannelUsedQuota(channelId, quota)
|
||||||
|
|||||||
@@ -202,7 +202,10 @@ func RelaySwapFace(c *gin.Context) *dto.MidjourneyResponse {
|
|||||||
if quota != 0 {
|
if quota != 0 {
|
||||||
tokenName := c.GetString("token_name")
|
tokenName := c.GetString("token_name")
|
||||||
logContent := fmt.Sprintf("模型固定价格 %.2f,分组倍率 %.2f,操作 %s", modelPrice, groupRatio, constant.MjActionSwapFace)
|
logContent := fmt.Sprintf("模型固定价格 %.2f,分组倍率 %.2f,操作 %s", modelPrice, groupRatio, constant.MjActionSwapFace)
|
||||||
model.RecordConsumeLog(ctx, userId, channelId, 0, 0, modelName, tokenName, quota, logContent, tokenId, userQuota, 0, false)
|
other := make(map[string]interface{})
|
||||||
|
other["model_price"] = modelPrice
|
||||||
|
other["group_ratio"] = groupRatio
|
||||||
|
model.RecordConsumeLog(ctx, userId, channelId, 0, 0, modelName, tokenName, quota, logContent, tokenId, userQuota, 0, false, other)
|
||||||
model.UpdateUserUsedQuotaAndRequestCount(userId, quota)
|
model.UpdateUserUsedQuotaAndRequestCount(userId, quota)
|
||||||
channelId := c.GetInt("channel_id")
|
channelId := c.GetInt("channel_id")
|
||||||
model.UpdateChannelUsedQuota(channelId, quota)
|
model.UpdateChannelUsedQuota(channelId, quota)
|
||||||
@@ -498,7 +501,10 @@ func RelayMidjourneySubmit(c *gin.Context, relayMode int) *dto.MidjourneyRespons
|
|||||||
if quota != 0 {
|
if quota != 0 {
|
||||||
tokenName := c.GetString("token_name")
|
tokenName := c.GetString("token_name")
|
||||||
logContent := fmt.Sprintf("模型固定价格 %.2f,分组倍率 %.2f,操作 %s", modelPrice, groupRatio, midjRequest.Action)
|
logContent := fmt.Sprintf("模型固定价格 %.2f,分组倍率 %.2f,操作 %s", modelPrice, groupRatio, midjRequest.Action)
|
||||||
model.RecordConsumeLog(ctx, userId, channelId, 0, 0, modelName, tokenName, quota, logContent, tokenId, userQuota, 0, false)
|
other := make(map[string]interface{})
|
||||||
|
other["model_price"] = modelPrice
|
||||||
|
other["group_ratio"] = groupRatio
|
||||||
|
model.RecordConsumeLog(ctx, userId, channelId, 0, 0, modelName, tokenName, quota, logContent, tokenId, userQuota, 0, false, other)
|
||||||
model.UpdateUserUsedQuotaAndRequestCount(userId, quota)
|
model.UpdateUserUsedQuotaAndRequestCount(userId, quota)
|
||||||
channelId := c.GetInt("channel_id")
|
channelId := c.GetInt("channel_id")
|
||||||
model.UpdateChannelUsedQuota(channelId, quota)
|
model.UpdateChannelUsedQuota(channelId, quota)
|
||||||
|
|||||||
@@ -315,7 +315,12 @@ func postConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, textRe
|
|||||||
logModel = "gpt-4-gizmo-*"
|
logModel = "gpt-4-gizmo-*"
|
||||||
logContent += fmt.Sprintf(",模型 %s", textRequest.Model)
|
logContent += fmt.Sprintf(",模型 %s", textRequest.Model)
|
||||||
}
|
}
|
||||||
model.RecordConsumeLog(ctx, relayInfo.UserId, relayInfo.ChannelId, promptTokens, completionTokens, logModel, tokenName, quota, logContent, relayInfo.TokenId, userQuota, int(useTimeSeconds), relayInfo.IsStream)
|
other := make(map[string]interface{})
|
||||||
|
other["model_ratio"] = modelRatio
|
||||||
|
other["group_ratio"] = groupRatio
|
||||||
|
other["completion_ratio"] = completionRatio
|
||||||
|
other["model_price"] = modelPrice
|
||||||
|
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 {
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ func SetApiRouter(router *gin.Engine) {
|
|||||||
apiRouter.Use(middleware.GlobalAPIRateLimit())
|
apiRouter.Use(middleware.GlobalAPIRateLimit())
|
||||||
{
|
{
|
||||||
apiRouter.GET("/status", controller.GetStatus)
|
apiRouter.GET("/status", controller.GetStatus)
|
||||||
|
apiRouter.GET("/models", middleware.UserAuth(), controller.DashboardListModels)
|
||||||
apiRouter.GET("/status/test", middleware.AdminAuth(), controller.TestStatus)
|
apiRouter.GET("/status/test", middleware.AdminAuth(), controller.TestStatus)
|
||||||
apiRouter.GET("/notice", controller.GetNotice)
|
apiRouter.GET("/notice", controller.GetNotice)
|
||||||
apiRouter.GET("/about", controller.GetAbout)
|
apiRouter.GET("/about", controller.GetAbout)
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ func ShouldDisableChannel(err *relaymodel.OpenAIError, statusCode int) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func ShouldEnableChannel(err error, openAIErr *relaymodel.OpenAIError) bool {
|
func ShouldEnableChannel(err error, openAIErr *relaymodel.OpenAIError, status int) bool {
|
||||||
if !common.AutomaticEnableChannelEnabled {
|
if !common.AutomaticEnableChannelEnabled {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -73,5 +73,8 @@ func ShouldEnableChannel(err error, openAIErr *relaymodel.OpenAIError) bool {
|
|||||||
if openAIErr != nil {
|
if openAIErr != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if status != common.ChannelStatusAutoDisabled {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import {
|
|||||||
} from '@douyinfe/semi-ui';
|
} from '@douyinfe/semi-ui';
|
||||||
import EditChannel from '../pages/Channel/EditChannel';
|
import EditChannel from '../pages/Channel/EditChannel';
|
||||||
import { IconTreeTriangleDown } from '@douyinfe/semi-icons';
|
import { IconTreeTriangleDown } from '@douyinfe/semi-icons';
|
||||||
|
import { loadChannelModels } from './utils.js';
|
||||||
|
|
||||||
function renderTimestamp(timestamp) {
|
function renderTimestamp(timestamp) {
|
||||||
return <>{timestamp2string(timestamp)}</>;
|
return <>{timestamp2string(timestamp)}</>;
|
||||||
@@ -354,27 +355,29 @@ const ChannelsTable = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const copySelectedChannel = async (id) => {
|
const copySelectedChannel = async (id) => {
|
||||||
const channelToCopy = channels.find(channel => String(channel.id) === String(id));
|
const channelToCopy = channels.find(
|
||||||
console.log(channelToCopy)
|
(channel) => String(channel.id) === String(id),
|
||||||
|
);
|
||||||
|
console.log(channelToCopy);
|
||||||
channelToCopy.name += '_复制';
|
channelToCopy.name += '_复制';
|
||||||
channelToCopy.created_time = null;
|
channelToCopy.created_time = null;
|
||||||
channelToCopy.balance = 0;
|
channelToCopy.balance = 0;
|
||||||
channelToCopy.used_quota = 0;
|
channelToCopy.used_quota = 0;
|
||||||
if (!channelToCopy) {
|
if (!channelToCopy) {
|
||||||
showError("渠道未找到,请刷新页面后重试。");
|
showError('渠道未找到,请刷新页面后重试。');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const newChannel = {...channelToCopy, id: undefined};
|
const newChannel = { ...channelToCopy, id: undefined };
|
||||||
const response = await API.post('/api/channel/', newChannel);
|
const response = await API.post('/api/channel/', newChannel);
|
||||||
if (response.data.success) {
|
if (response.data.success) {
|
||||||
showSuccess("渠道复制成功");
|
showSuccess('渠道复制成功');
|
||||||
await refresh();
|
await refresh();
|
||||||
} else {
|
} else {
|
||||||
showError(response.data.message);
|
showError(response.data.message);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showError("渠道复制失败: " + error.message);
|
showError('渠道复制失败: ' + error.message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -395,6 +398,7 @@ const ChannelsTable = () => {
|
|||||||
showError(reason);
|
showError(reason);
|
||||||
});
|
});
|
||||||
fetchGroups().then();
|
fetchGroups().then();
|
||||||
|
loadChannelModels().then();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const manageChannel = async (id, action, record, value) => {
|
const manageChannel = async (id, action, record, value) => {
|
||||||
|
|||||||
@@ -19,9 +19,15 @@ import {
|
|||||||
Spin,
|
Spin,
|
||||||
Table,
|
Table,
|
||||||
Tag,
|
Tag,
|
||||||
|
Tooltip,
|
||||||
} from '@douyinfe/semi-ui';
|
} from '@douyinfe/semi-ui';
|
||||||
import { ITEMS_PER_PAGE } from '../constants';
|
import { ITEMS_PER_PAGE } from '../constants';
|
||||||
import { renderNumber, renderQuota, stringToColor } from '../helpers/render';
|
import {
|
||||||
|
renderModelPrice,
|
||||||
|
renderNumber,
|
||||||
|
renderQuota,
|
||||||
|
stringToColor,
|
||||||
|
} from '../helpers/render';
|
||||||
import Paragraph from '@douyinfe/semi-ui/lib/es/typography/paragraph';
|
import Paragraph from '@douyinfe/semi-ui/lib/es/typography/paragraph';
|
||||||
|
|
||||||
const { Header } = Layout;
|
const { Header } = Layout;
|
||||||
@@ -292,16 +298,44 @@ const LogsTable = () => {
|
|||||||
title: '详情',
|
title: '详情',
|
||||||
dataIndex: 'content',
|
dataIndex: 'content',
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
|
if (record.other === '') {
|
||||||
|
return (
|
||||||
|
<Paragraph
|
||||||
|
ellipsis={{
|
||||||
|
rows: 2,
|
||||||
|
showTooltip: {
|
||||||
|
type: 'popover',
|
||||||
|
opts: { style: { width: 240 } },
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
style={{ maxWidth: 240 }}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</Paragraph>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let other = JSON.parse(record.other);
|
||||||
|
let content = renderModelPrice(
|
||||||
|
other.model_ratio,
|
||||||
|
other.model_price,
|
||||||
|
other.completion_ratio,
|
||||||
|
other.group_ratio,
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<Paragraph
|
<Tooltip content={content}>
|
||||||
ellipsis={{
|
<Paragraph
|
||||||
rows: 2,
|
ellipsis={{
|
||||||
showTooltip: { type: 'popover', opts: { style: { width: 240 } } },
|
rows: 2,
|
||||||
}}
|
showTooltip: {
|
||||||
style={{ maxWidth: 240 }}
|
type: 'popover',
|
||||||
>
|
opts: { style: { width: 240 } },
|
||||||
{text}
|
},
|
||||||
</Paragraph>
|
}}
|
||||||
|
style={{ maxWidth: 240 }}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</Paragraph>
|
||||||
|
</Tooltip>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -236,6 +236,31 @@ const renderTimestamp = (timestampInSeconds) => {
|
|||||||
|
|
||||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; // 格式化输出
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; // 格式化输出
|
||||||
};
|
};
|
||||||
|
// 修改renderDuration函数以包含颜色逻辑
|
||||||
|
function renderDuration(submit_time, finishTime) {
|
||||||
|
// 确保startTime和finishTime都是有效的时间戳
|
||||||
|
if (!submit_time || !finishTime) return 'N/A';
|
||||||
|
|
||||||
|
// 将时间戳转换为Date对象
|
||||||
|
const start = new Date(submit_time);
|
||||||
|
const finish = new Date(finishTime);
|
||||||
|
|
||||||
|
// 计算时间差(毫秒)
|
||||||
|
const durationMs = finish - start;
|
||||||
|
|
||||||
|
// 将时间差转换为秒,并保留一位小数
|
||||||
|
const durationSec = (durationMs / 1000).toFixed(1);
|
||||||
|
|
||||||
|
// 设置颜色:大于60秒则为红色,小于等于60秒则为绿色
|
||||||
|
const color = durationSec > 60 ? 'red' : 'green';
|
||||||
|
|
||||||
|
// 返回带有样式的颜色标签
|
||||||
|
return (
|
||||||
|
<Tag color={color} size="large">
|
||||||
|
{durationSec} 秒
|
||||||
|
</Tag>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const LogsTable = () => {
|
const LogsTable = () => {
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
@@ -248,6 +273,15 @@ const LogsTable = () => {
|
|||||||
return <div>{renderTimestamp(text / 1000)}</div>;
|
return <div>{renderTimestamp(text / 1000)}</div>;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '花费时间',
|
||||||
|
dataIndex: 'finish_time', // 以finish_time作为dataIndex
|
||||||
|
key: 'finish_time',
|
||||||
|
render: (finish, record) => {
|
||||||
|
// 假设record.start_time是存在的,并且finish是完成时间的时间戳
|
||||||
|
return renderDuration(record.submit_time, finish);
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '渠道',
|
title: '渠道',
|
||||||
dataIndex: 'channel_id',
|
dataIndex: 'channel_id',
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ const OperationSetting = () => {
|
|||||||
PreConsumedQuota: 0,
|
PreConsumedQuota: 0,
|
||||||
StreamCacheQueueLength: 0,
|
StreamCacheQueueLength: 0,
|
||||||
ModelRatio: '',
|
ModelRatio: '',
|
||||||
|
CompletionRatio: '',
|
||||||
ModelPrice: '',
|
ModelPrice: '',
|
||||||
GroupRatio: '',
|
GroupRatio: '',
|
||||||
TopUpLink: '',
|
TopUpLink: '',
|
||||||
@@ -69,6 +70,7 @@ const OperationSetting = () => {
|
|||||||
if (
|
if (
|
||||||
item.key === 'ModelRatio' ||
|
item.key === 'ModelRatio' ||
|
||||||
item.key === 'GroupRatio' ||
|
item.key === 'GroupRatio' ||
|
||||||
|
item.key === 'CompletionRatio' ||
|
||||||
item.key === 'ModelPrice'
|
item.key === 'ModelPrice'
|
||||||
) {
|
) {
|
||||||
item.value = JSON.stringify(JSON.parse(item.value), null, 2);
|
item.value = JSON.stringify(JSON.parse(item.value), null, 2);
|
||||||
@@ -166,6 +168,13 @@ const OperationSetting = () => {
|
|||||||
}
|
}
|
||||||
await updateOption('ModelRatio', inputs.ModelRatio);
|
await updateOption('ModelRatio', inputs.ModelRatio);
|
||||||
}
|
}
|
||||||
|
if (originInputs['CompletionRatio'] !== inputs.CompletionRatio) {
|
||||||
|
if (!verifyJSON(inputs.CompletionRatio)) {
|
||||||
|
showError('模型补全倍率不是合法的 JSON 字符串');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await updateOption('CompletionRatio', inputs.CompletionRatio);
|
||||||
|
}
|
||||||
if (originInputs['GroupRatio'] !== inputs.GroupRatio) {
|
if (originInputs['GroupRatio'] !== inputs.GroupRatio) {
|
||||||
if (!verifyJSON(inputs.GroupRatio)) {
|
if (!verifyJSON(inputs.GroupRatio)) {
|
||||||
showError('分组倍率不是合法的 JSON 字符串');
|
showError('分组倍率不是合法的 JSON 字符串');
|
||||||
@@ -304,6 +313,20 @@ const OperationSetting = () => {
|
|||||||
placeholder='为一个 JSON 文本,键为模型名称,值为倍率'
|
placeholder='为一个 JSON 文本,键为模型名称,值为倍率'
|
||||||
/>
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
|
<Form.Group widths='equal'>
|
||||||
|
<Form.TextArea
|
||||||
|
label='模型补全倍率(仅对自定义模型有效)'
|
||||||
|
name='CompletionRatio'
|
||||||
|
onChange={handleInputChange}
|
||||||
|
style={{
|
||||||
|
minHeight: 250,
|
||||||
|
fontFamily: 'JetBrains Mono, Consolas',
|
||||||
|
}}
|
||||||
|
autoComplete='new-password'
|
||||||
|
value={inputs.CompletionRatio}
|
||||||
|
placeholder='为一个 JSON 文本,键为分组名称,值为倍率'
|
||||||
|
/>
|
||||||
|
</Form.Group>
|
||||||
<Form.Group widths='equal'>
|
<Form.Group widths='equal'>
|
||||||
<Form.TextArea
|
<Form.TextArea
|
||||||
label='分组倍率'
|
label='分组倍率'
|
||||||
|
|||||||
@@ -18,3 +18,32 @@ export async function onGitHubOAuthClicked(github_client_id) {
|
|||||||
`https://github.com/login/oauth/authorize?client_id=${github_client_id}&state=${state}&scope=user:email`,
|
`https://github.com/login/oauth/authorize?client_id=${github_client_id}&state=${state}&scope=user:email`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let channelModels = undefined;
|
||||||
|
export async function loadChannelModels() {
|
||||||
|
const res = await API.get('/api/models');
|
||||||
|
const { success, data } = res.data;
|
||||||
|
if (!success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
channelModels = data;
|
||||||
|
localStorage.setItem('channel_models', JSON.stringify(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getChannelModels(type) {
|
||||||
|
if (channelModels !== undefined && type in channelModels) {
|
||||||
|
if (!channelModels[type]) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return channelModels[type];
|
||||||
|
}
|
||||||
|
let models = localStorage.getItem('channel_models');
|
||||||
|
if (!models) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
channelModels = JSON.parse(models);
|
||||||
|
if (type in channelModels) {
|
||||||
|
return channelModels[type];
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|||||||
@@ -86,13 +86,13 @@ export const CHANNEL_OPTIONS = [
|
|||||||
label: '智谱 ChatGLM',
|
label: '智谱 ChatGLM',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 16,
|
key: 26,
|
||||||
text: '智谱 GLM-4V',
|
text: '智谱 GLM-4V',
|
||||||
value: 26,
|
value: 26,
|
||||||
color: 'purple',
|
color: 'purple',
|
||||||
label: '智谱 GLM-4V',
|
label: '智谱 GLM-4V',
|
||||||
},
|
},
|
||||||
{ key: 16, text: 'Moonshot', value: 25, color: 'green', label: 'Moonshot' },
|
{ key: 25, text: 'Moonshot', value: 25, color: 'green', label: 'Moonshot' },
|
||||||
{ key: 19, text: '360 智脑', value: 19, color: 'blue', label: '360 智脑' },
|
{ key: 19, text: '360 智脑', value: 19, color: 'blue', label: '360 智脑' },
|
||||||
{ key: 23, text: '腾讯混元', value: 23, color: 'teal', label: '腾讯混元' },
|
{ key: 23, text: '腾讯混元', value: 23, color: 'teal', label: '腾讯混元' },
|
||||||
{ key: 31, text: '零一万物', value: 31, color: 'green', label: '零一万物' },
|
{ key: 31, text: '零一万物', value: 31, color: 'green', label: '零一万物' },
|
||||||
|
|||||||
@@ -135,6 +135,32 @@ export function renderQuota(quota, digits = 2) {
|
|||||||
return renderNumber(quota);
|
return renderNumber(quota);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function renderModelPrice(
|
||||||
|
modelRatio,
|
||||||
|
modelPrice = -1,
|
||||||
|
completionRatio,
|
||||||
|
groupRatio,
|
||||||
|
) {
|
||||||
|
// 1 ratio = $0.002 / 1K tokens
|
||||||
|
if (modelPrice !== -1) {
|
||||||
|
return '模型价格:$' + modelPrice * groupRatio;
|
||||||
|
} else {
|
||||||
|
if (completionRatio === undefined) {
|
||||||
|
completionRatio = 0;
|
||||||
|
}
|
||||||
|
let inputRatioPrice = modelRatio * 0.002 * groupRatio;
|
||||||
|
let completionRatioPrice =
|
||||||
|
modelRatio * completionRatio * 0.002 * groupRatio;
|
||||||
|
return (
|
||||||
|
'输入:$' +
|
||||||
|
inputRatioPrice.toFixed(3) +
|
||||||
|
'/1K tokens,补全:$' +
|
||||||
|
completionRatioPrice.toFixed(3) +
|
||||||
|
'/1K tokens'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function renderQuotaWithPrompt(quota, digits) {
|
export function renderQuotaWithPrompt(quota, digits) {
|
||||||
let displayInCurrency = localStorage.getItem('display_in_currency');
|
let displayInCurrency = localStorage.getItem('display_in_currency');
|
||||||
displayInCurrency = displayInCurrency === 'true';
|
displayInCurrency = displayInCurrency === 'true';
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
Banner,
|
Banner,
|
||||||
} from '@douyinfe/semi-ui';
|
} from '@douyinfe/semi-ui';
|
||||||
import { Divider } from 'semantic-ui-react';
|
import { Divider } from 'semantic-ui-react';
|
||||||
|
import { getChannelModels, loadChannelModels } from '../../components/utils.js';
|
||||||
|
|
||||||
const MODEL_MAPPING_EXAMPLE = {
|
const MODEL_MAPPING_EXAMPLE = {
|
||||||
'gpt-3.5-turbo-0301': 'gpt-3.5-turbo',
|
'gpt-3.5-turbo-0301': 'gpt-3.5-turbo',
|
||||||
@@ -87,97 +88,9 @@ const EditChannel = (props) => {
|
|||||||
const [customModel, setCustomModel] = useState('');
|
const [customModel, setCustomModel] = useState('');
|
||||||
const handleInputChange = (name, value) => {
|
const handleInputChange = (name, value) => {
|
||||||
setInputs((inputs) => ({ ...inputs, [name]: value }));
|
setInputs((inputs) => ({ ...inputs, [name]: value }));
|
||||||
if (name === 'type' && inputs.models.length === 0) {
|
if (name === 'type') {
|
||||||
let localModels = [];
|
let localModels = [];
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case 33:
|
|
||||||
case 14:
|
|
||||||
localModels = [
|
|
||||||
'claude-instant-1.2',
|
|
||||||
'claude-2',
|
|
||||||
'claude-2.0',
|
|
||||||
'claude-2.1',
|
|
||||||
'claude-3-opus-20240229',
|
|
||||||
'claude-3-sonnet-20240229',
|
|
||||||
'claude-3-haiku-20240307',
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
case 11:
|
|
||||||
localModels = ['PaLM-2'];
|
|
||||||
break;
|
|
||||||
case 15:
|
|
||||||
localModels = [
|
|
||||||
'ERNIE-Bot',
|
|
||||||
'ERNIE-Bot-turbo',
|
|
||||||
'ERNIE-Bot-4',
|
|
||||||
'Embedding-V1',
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
case 17:
|
|
||||||
localModels = [
|
|
||||||
'qwen-turbo',
|
|
||||||
'qwen-plus',
|
|
||||||
'qwen-max',
|
|
||||||
'qwen-max-longcontext',
|
|
||||||
'text-embedding-v1',
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
case 16:
|
|
||||||
localModels = ['chatglm_pro', 'chatglm_std', 'chatglm_lite'];
|
|
||||||
break;
|
|
||||||
case 18:
|
|
||||||
localModels = [
|
|
||||||
'SparkDesk',
|
|
||||||
'SparkDesk-v1.1',
|
|
||||||
'SparkDesk-v2.1',
|
|
||||||
'SparkDesk-v3.1',
|
|
||||||
'SparkDesk-v3.5',
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
case 19:
|
|
||||||
localModels = [
|
|
||||||
'360GPT_S2_V9',
|
|
||||||
'embedding-bert-512-v1',
|
|
||||||
'embedding_s1_v1',
|
|
||||||
'semantic_similarity_s1_v1',
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
case 23:
|
|
||||||
localModels = ['hunyuan'];
|
|
||||||
break;
|
|
||||||
case 24:
|
|
||||||
localModels = [
|
|
||||||
'gemini-1.0-pro-001',
|
|
||||||
'gemini-1.0-pro-vision-001',
|
|
||||||
'gemini-1.5-pro',
|
|
||||||
'gemini-1.5-pro-latest',
|
|
||||||
'gemini-pro',
|
|
||||||
'gemini-pro-vision',
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
case 34:
|
|
||||||
localModels = [
|
|
||||||
'command-r',
|
|
||||||
'command-r-plus',
|
|
||||||
'command-light',
|
|
||||||
'command-light-nightly',
|
|
||||||
'command',
|
|
||||||
'command-nightly',
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
case 25:
|
|
||||||
localModels = [
|
|
||||||
'moonshot-v1-8k',
|
|
||||||
'moonshot-v1-32k',
|
|
||||||
'moonshot-v1-128k',
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
case 26:
|
|
||||||
localModels = ['glm-4', 'glm-4v', 'glm-3-turbo'];
|
|
||||||
break;
|
|
||||||
case 31:
|
|
||||||
localModels = ['yi-34b-chat-0205', 'yi-34b-chat-200k', 'yi-vl-plus'];
|
|
||||||
break;
|
|
||||||
case 2:
|
case 2:
|
||||||
localModels = [
|
localModels = [
|
||||||
'mj_imagine',
|
'mj_imagine',
|
||||||
@@ -207,8 +120,14 @@ const EditChannel = (props) => {
|
|||||||
'mj_pan',
|
'mj_pan',
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
localModels = getChannelModels(value);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
setInputs((inputs) => ({ ...inputs, models: localModels }));
|
if (inputs.models.length === 0) {
|
||||||
|
setInputs((inputs) => ({ ...inputs, models: localModels }));
|
||||||
|
}
|
||||||
|
setBasicModels(localModels);
|
||||||
}
|
}
|
||||||
//setAutoBan
|
//setAutoBan
|
||||||
};
|
};
|
||||||
@@ -244,6 +163,7 @@ const EditChannel = (props) => {
|
|||||||
} else {
|
} else {
|
||||||
setAutoBan(true);
|
setAutoBan(true);
|
||||||
}
|
}
|
||||||
|
setBasicModels(getChannelModels(data.type));
|
||||||
// console.log(data);
|
// console.log(data);
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
@@ -312,6 +232,9 @@ const EditChannel = (props) => {
|
|||||||
loadChannel().then(() => {});
|
loadChannel().then(() => {});
|
||||||
} else {
|
} else {
|
||||||
setInputs(originInputs);
|
setInputs(originInputs);
|
||||||
|
let localModels = getChannelModels(inputs.type);
|
||||||
|
setBasicModels(localModels);
|
||||||
|
setInputs((inputs) => ({ ...inputs, models: localModels }));
|
||||||
}
|
}
|
||||||
}, [props.editingChannel.id]);
|
}, [props.editingChannel.id]);
|
||||||
|
|
||||||
@@ -596,7 +519,7 @@ const EditChannel = (props) => {
|
|||||||
handleInputChange('models', basicModels);
|
handleInputChange('models', basicModels);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
填入基础模型
|
填入相关模型
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type='secondary'
|
type='secondary'
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { API, isMobile, showError, showSuccess } from '../../helpers';
|
import { API, isMobile, showError, showSuccess } from '../../helpers';
|
||||||
import { renderQuotaWithPrompt } from '../../helpers/render';
|
import { renderQuota, renderQuotaWithPrompt } from '../../helpers/render';
|
||||||
import Title from '@douyinfe/semi-ui/lib/es/typography/title';
|
import Title from '@douyinfe/semi-ui/lib/es/typography/title';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Divider,
|
Divider,
|
||||||
Input,
|
Input,
|
||||||
|
Modal,
|
||||||
Select,
|
Select,
|
||||||
SideSheet,
|
SideSheet,
|
||||||
Space,
|
Space,
|
||||||
@@ -17,6 +18,8 @@ import {
|
|||||||
const EditUser = (props) => {
|
const EditUser = (props) => {
|
||||||
const userId = props.editingUser.id;
|
const userId = props.editingUser.id;
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [addQuotaModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
const [addQuotaLocal, setAddQuotaLocal] = useState('');
|
||||||
const [inputs, setInputs] = useState({
|
const [inputs, setInputs] = useState({
|
||||||
username: '',
|
username: '',
|
||||||
display_name: '',
|
display_name: '',
|
||||||
@@ -107,6 +110,16 @@ const EditUser = (props) => {
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const addLocalQuota = () => {
|
||||||
|
let newQuota = parseInt(quota) + parseInt(addQuotaLocal);
|
||||||
|
setInputs((inputs) => ({ ...inputs, quota: newQuota }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const openAddQuotaModal = () => {
|
||||||
|
setAddQuotaLocal('0');
|
||||||
|
setIsModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SideSheet
|
<SideSheet
|
||||||
@@ -192,14 +205,17 @@ const EditUser = (props) => {
|
|||||||
<div style={{ marginTop: 20 }}>
|
<div style={{ marginTop: 20 }}>
|
||||||
<Typography.Text>{`剩余额度${renderQuotaWithPrompt(quota)}`}</Typography.Text>
|
<Typography.Text>{`剩余额度${renderQuotaWithPrompt(quota)}`}</Typography.Text>
|
||||||
</div>
|
</div>
|
||||||
<Input
|
<Space>
|
||||||
name='quota'
|
<Input
|
||||||
placeholder={'请输入新的剩余额度'}
|
name='quota'
|
||||||
onChange={(value) => handleInputChange('quota', value)}
|
placeholder={'请输入新的剩余额度'}
|
||||||
value={quota}
|
onChange={(value) => handleInputChange('quota', value)}
|
||||||
type={'number'}
|
value={quota}
|
||||||
autoComplete='new-password'
|
type={'number'}
|
||||||
/>
|
autoComplete='new-password'
|
||||||
|
/>
|
||||||
|
<Button onClick={openAddQuotaModal}>添加额度</Button>
|
||||||
|
</Space>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Divider style={{ marginTop: 20 }}>以下信息不可修改</Divider>
|
<Divider style={{ marginTop: 20 }}>以下信息不可修改</Divider>
|
||||||
@@ -245,6 +261,30 @@ const EditUser = (props) => {
|
|||||||
/>
|
/>
|
||||||
</Spin>
|
</Spin>
|
||||||
</SideSheet>
|
</SideSheet>
|
||||||
|
<Modal
|
||||||
|
centered={true}
|
||||||
|
visible={addQuotaModalOpen}
|
||||||
|
onOk={() => {
|
||||||
|
addLocalQuota();
|
||||||
|
setIsModalOpen(false);
|
||||||
|
}}
|
||||||
|
onCancel={() => setIsModalOpen(false)}
|
||||||
|
closable={null}
|
||||||
|
>
|
||||||
|
<div style={{ marginTop: 20 }}>
|
||||||
|
<Typography.Text>{`新额度${renderQuota(quota)} + ${renderQuota(addQuotaLocal)} = ${renderQuota(quota + parseInt(addQuotaLocal))}`}</Typography.Text>
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
name='addQuotaLocal'
|
||||||
|
placeholder={'需要添加的额度(支持负数)'}
|
||||||
|
onChange={(value) => {
|
||||||
|
setAddQuotaLocal(value);
|
||||||
|
}}
|
||||||
|
value={addQuotaLocal}
|
||||||
|
type={'number'}
|
||||||
|
autoComplete='new-password'
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user