diff --git a/common/redis.go b/common/redis.go index f3205567..55d4931c 100644 --- a/common/redis.go +++ b/common/redis.go @@ -2,13 +2,15 @@ package common import ( "context" + "os" + "strings" + "time" + "github.com/go-redis/redis/v8" "github.com/songquanpeng/one-api/common/logger" - "os" - "time" ) -var RDB *redis.Client +var RDB redis.Cmdable var RedisEnabled = true // InitRedisClient This function is called after init() @@ -23,13 +25,23 @@ func InitRedisClient() (err error) { logger.SysLog("SYNC_FREQUENCY not set, Redis is disabled") return nil } - logger.SysLog("Redis is enabled") - opt, err := redis.ParseURL(os.Getenv("REDIS_CONN_STRING")) - if err != nil { - logger.FatalLog("failed to parse Redis connection string: " + err.Error()) + redisConnString := os.Getenv("REDIS_CONN_STRING") + if os.Getenv("REDIS_MASTER_NAME") == "" { + logger.SysLog("Redis is enabled") + opt, err := redis.ParseURL(redisConnString) + if err != nil { + logger.FatalLog("failed to parse Redis connection string: " + err.Error()) + } + RDB = redis.NewClient(opt) + } else { + // cluster mode + logger.SysLog("Redis cluster mode enabled") + RDB = redis.NewUniversalClient(&redis.UniversalOptions{ + Addrs: strings.Split(redisConnString, ","), + Password: os.Getenv("REDIS_PASSWORD"), + MasterName: os.Getenv("REDIS_MASTER_NAME"), + }) } - RDB = redis.NewClient(opt) - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() diff --git a/controller/channel-billing.go b/controller/channel-billing.go index 0bc905de..c7d2d240 100644 --- a/controller/channel-billing.go +++ b/controller/channel-billing.go @@ -101,6 +101,16 @@ type SiliconFlowUsageResponse struct { } `json:"data"` } +type DeepSeekUsageResponse struct { + IsAvailable bool `json:"is_available"` + BalanceInfos []struct { + Currency string `json:"currency"` + TotalBalance string `json:"total_balance"` + GrantedBalance string `json:"granted_balance"` + ToppedUpBalance string `json:"topped_up_balance"` + } `json:"balance_infos"` +} + // GetAuthHeader get auth header func GetAuthHeader(token string) http.Header { h := http.Header{} @@ -237,7 +247,36 @@ func updateChannelSiliconFlowBalance(channel *model.Channel) (float64, error) { if response.Code != 20000 { return 0, fmt.Errorf("code: %d, message: %s", response.Code, response.Message) } - balance, err := strconv.ParseFloat(response.Data.Balance, 64) + balance, err := strconv.ParseFloat(response.Data.TotalBalance, 64) + if err != nil { + return 0, err + } + channel.UpdateBalance(balance) + return balance, nil +} + +func updateChannelDeepSeekBalance(channel *model.Channel) (float64, error) { + url := "https://api.deepseek.com/user/balance" + body, err := GetResponseBody("GET", url, channel, GetAuthHeader(channel.Key)) + if err != nil { + return 0, err + } + response := DeepSeekUsageResponse{} + err = json.Unmarshal(body, &response) + if err != nil { + return 0, err + } + index := -1 + for i, balanceInfo := range response.BalanceInfos { + if balanceInfo.Currency == "CNY" { + index = i + break + } + } + if index == -1 { + return 0, errors.New("currency CNY not found") + } + balance, err := strconv.ParseFloat(response.BalanceInfos[index].TotalBalance, 64) if err != nil { return 0, err } @@ -271,6 +310,8 @@ func updateChannelBalance(channel *model.Channel) (float64, error) { return updateChannelAIGC2DBalance(channel) case channeltype.SiliconFlow: return updateChannelSiliconFlowBalance(channel) + case channeltype.DeepSeek: + return updateChannelDeepSeekBalance(channel) default: return 0, errors.New("尚未实现") } diff --git a/relay/adaptor/ali/constants.go b/relay/adaptor/ali/constants.go index 3f24ce2e..f3d99520 100644 --- a/relay/adaptor/ali/constants.go +++ b/relay/adaptor/ali/constants.go @@ -1,7 +1,23 @@ package ali var ModelList = []string{ - "qwen-turbo", "qwen-plus", "qwen-max", "qwen-max-longcontext", - "text-embedding-v1", + "qwen-turbo", "qwen-turbo-latest", + "qwen-plus", "qwen-plus-latest", + "qwen-max", "qwen-max-latest", + "qwen-max-longcontext", + "qwen-vl-max", "qwen-vl-max-latest", "qwen-vl-plus", "qwen-vl-plus-latest", + "qwen-vl-ocr", "qwen-vl-ocr-latest", + "qwen-audio-turbo", + "qwen-math-plus", "qwen-math-plus-latest", "qwen-math-turbo", "qwen-math-turbo-latest", + "qwen-coder-plus", "qwen-coder-plus-latest", "qwen-coder-turbo", "qwen-coder-turbo-latest", + "qwq-32b-preview", "qwen2.5-72b-instruct", "qwen2.5-32b-instruct", "qwen2.5-14b-instruct", "qwen2.5-7b-instruct", "qwen2.5-3b-instruct", "qwen2.5-1.5b-instruct", "qwen2.5-0.5b-instruct", + "qwen2-72b-instruct", "qwen2-57b-a14b-instruct", "qwen2-7b-instruct", "qwen2-1.5b-instruct", "qwen2-0.5b-instruct", + "qwen1.5-110b-chat", "qwen1.5-72b-chat", "qwen1.5-32b-chat", "qwen1.5-14b-chat", "qwen1.5-7b-chat", "qwen1.5-1.8b-chat", "qwen1.5-0.5b-chat", + "qwen-72b-chat", "qwen-14b-chat", "qwen-7b-chat", "qwen-1.8b-chat", "qwen-1.8b-longcontext-chat", + "qwen2-vl-7b-instruct", "qwen2-vl-2b-instruct", "qwen-vl-v1", "qwen-vl-chat-v1", + "qwen2-audio-instruct", "qwen-audio-chat", + "qwen2.5-math-72b-instruct", "qwen2.5-math-7b-instruct", "qwen2.5-math-1.5b-instruct", "qwen2-math-72b-instruct", "qwen2-math-7b-instruct", "qwen2-math-1.5b-instruct", + "qwen2.5-coder-32b-instruct", "qwen2.5-coder-14b-instruct", "qwen2.5-coder-7b-instruct", "qwen2.5-coder-3b-instruct", "qwen2.5-coder-1.5b-instruct", "qwen2.5-coder-0.5b-instruct", + "text-embedding-v1", "text-embedding-v3", "text-embedding-v2", "text-embedding-async-v2", "text-embedding-async-v1", "ali-stable-diffusion-xl", "ali-stable-diffusion-v1.5", "wanx-v1", } diff --git a/relay/adaptor/anthropic/constants.go b/relay/adaptor/anthropic/constants.go index cb574706..8ea7c4d8 100644 --- a/relay/adaptor/anthropic/constants.go +++ b/relay/adaptor/anthropic/constants.go @@ -9,5 +9,4 @@ var ModelList = []string{ "claude-3-5-sonnet-20240620", "claude-3-5-sonnet-20241022", "claude-3-5-sonnet-latest", - "claude-3-5-haiku-20241022", } diff --git a/relay/adaptor/gemini/constants.go b/relay/adaptor/gemini/constants.go index 57b14378..9d1cbc4a 100644 --- a/relay/adaptor/gemini/constants.go +++ b/relay/adaptor/gemini/constants.go @@ -6,5 +6,6 @@ var ModelList = []string{ "gemini-pro", "gemini-1.0-pro", "gemini-1.5-flash", "gemini-1.5-pro", "text-embedding-004", "aqa", - "gemini-2.0-flash-exp", "gemini-2.0-flash-thinking-exp", + "gemini-2.0-flash-exp", + "gemini-2.0-flash-thinking-exp", } diff --git a/relay/billing/ratio/model.go b/relay/billing/ratio/model.go index 712c710d..125dbb9a 100644 --- a/relay/billing/ratio/model.go +++ b/relay/billing/ratio/model.go @@ -130,29 +130,94 @@ var ModelRatio = map[string]float64{ "chatglm_lite": 0.1429, // ¥0.002 / 1k tokens "cogview-3": 0.25 * RMB, // https://help.aliyun.com/zh/dashscope/developer-reference/tongyi-thousand-questions-metering-and-billing - "qwen-turbo": 0.5715, // ¥0.008 / 1k tokens - "qwen-plus": 1.4286, // ¥0.02 / 1k tokens - "qwen-max": 1.4286, // ¥0.02 / 1k tokens - "qwen-max-longcontext": 1.4286, // ¥0.02 / 1k tokens - "text-embedding-v1": 0.05, // ¥0.0007 / 1k tokens - "ali-stable-diffusion-xl": 8, - "ali-stable-diffusion-v1.5": 8, - "wanx-v1": 8, - "SparkDesk": 1.2858, // ¥0.018 / 1k tokens - "SparkDesk-v1.1": 1.2858, // ¥0.018 / 1k tokens - "SparkDesk-v2.1": 1.2858, // ¥0.018 / 1k tokens - "SparkDesk-v3.1": 1.2858, // ¥0.018 / 1k tokens - "SparkDesk-v3.1-128K": 1.2858, // ¥0.018 / 1k tokens - "SparkDesk-v3.5": 1.2858, // ¥0.018 / 1k tokens - "SparkDesk-v3.5-32K": 1.2858, // ¥0.018 / 1k tokens - "SparkDesk-v4.0": 1.2858, // ¥0.018 / 1k tokens - "360GPT_S2_V9": 0.8572, // ¥0.012 / 1k tokens - "embedding-bert-512-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 - "hunyuan": 7.143, // ¥0.1 / 1k tokens // https://cloud.tencent.com/document/product/1729/97731#e0e6be58-60c8-469f-bdeb-6c264ce3b4d0 - "ChatStd": 0.01 * RMB, - "ChatPro": 0.1 * RMB, + "qwen-turbo": 1.4286, // ¥0.02 / 1k tokens + "qwen-turbo-latest": 1.4286, + "qwen-plus": 1.4286, + "qwen-plus-latest": 1.4286, + "qwen-max": 1.4286, + "qwen-max-latest": 1.4286, + "qwen-max-longcontext": 1.4286, + "qwen-vl-max": 1.4286, + "qwen-vl-max-latest": 1.4286, + "qwen-vl-plus": 1.4286, + "qwen-vl-plus-latest": 1.4286, + "qwen-vl-ocr": 1.4286, + "qwen-vl-ocr-latest": 1.4286, + "qwen-audio-turbo": 1.4286, + "qwen-math-plus": 1.4286, + "qwen-math-plus-latest": 1.4286, + "qwen-math-turbo": 1.4286, + "qwen-math-turbo-latest": 1.4286, + "qwen-coder-plus": 1.4286, + "qwen-coder-plus-latest": 1.4286, + "qwen-coder-turbo": 1.4286, + "qwen-coder-turbo-latest": 1.4286, + "qwq-32b-preview": 1.4286, + "qwen2.5-72b-instruct": 1.4286, + "qwen2.5-32b-instruct": 1.4286, + "qwen2.5-14b-instruct": 1.4286, + "qwen2.5-7b-instruct": 1.4286, + "qwen2.5-3b-instruct": 1.4286, + "qwen2.5-1.5b-instruct": 1.4286, + "qwen2.5-0.5b-instruct": 1.4286, + "qwen2-72b-instruct": 1.4286, + "qwen2-57b-a14b-instruct": 1.4286, + "qwen2-7b-instruct": 1.4286, + "qwen2-1.5b-instruct": 1.4286, + "qwen2-0.5b-instruct": 1.4286, + "qwen1.5-110b-chat": 1.4286, + "qwen1.5-72b-chat": 1.4286, + "qwen1.5-32b-chat": 1.4286, + "qwen1.5-14b-chat": 1.4286, + "qwen1.5-7b-chat": 1.4286, + "qwen1.5-1.8b-chat": 1.4286, + "qwen1.5-0.5b-chat": 1.4286, + "qwen-72b-chat": 1.4286, + "qwen-14b-chat": 1.4286, + "qwen-7b-chat": 1.4286, + "qwen-1.8b-chat": 1.4286, + "qwen-1.8b-longcontext-chat": 1.4286, + "qwen2-vl-7b-instruct": 1.4286, + "qwen2-vl-2b-instruct": 1.4286, + "qwen-vl-v1": 1.4286, + "qwen-vl-chat-v1": 1.4286, + "qwen2-audio-instruct": 1.4286, + "qwen-audio-chat": 1.4286, + "qwen2.5-math-72b-instruct": 1.4286, + "qwen2.5-math-7b-instruct": 1.4286, + "qwen2.5-math-1.5b-instruct": 1.4286, + "qwen2-math-72b-instruct": 1.4286, + "qwen2-math-7b-instruct": 1.4286, + "qwen2-math-1.5b-instruct": 1.4286, + "qwen2.5-coder-32b-instruct": 1.4286, + "qwen2.5-coder-14b-instruct": 1.4286, + "qwen2.5-coder-7b-instruct": 1.4286, + "qwen2.5-coder-3b-instruct": 1.4286, + "qwen2.5-coder-1.5b-instruct": 1.4286, + "qwen2.5-coder-0.5b-instruct": 1.4286, + "text-embedding-v1": 0.05, // ¥0.0007 / 1k tokens + "text-embedding-v3": 0.05, + "text-embedding-v2": 0.05, + "text-embedding-async-v2": 0.05, + "text-embedding-async-v1": 0.05, + "ali-stable-diffusion-xl": 8.00, + "ali-stable-diffusion-v1.5": 8.00, + "wanx-v1": 8.00, + "SparkDesk": 1.2858, // ¥0.018 / 1k tokens + "SparkDesk-v1.1": 1.2858, // ¥0.018 / 1k tokens + "SparkDesk-v2.1": 1.2858, // ¥0.018 / 1k tokens + "SparkDesk-v3.1": 1.2858, // ¥0.018 / 1k tokens + "SparkDesk-v3.1-128K": 1.2858, // ¥0.018 / 1k tokens + "SparkDesk-v3.5": 1.2858, // ¥0.018 / 1k tokens + "SparkDesk-v3.5-32K": 1.2858, // ¥0.018 / 1k tokens + "SparkDesk-v4.0": 1.2858, // ¥0.018 / 1k tokens + "360GPT_S2_V9": 0.8572, // ¥0.012 / 1k tokens + "embedding-bert-512-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 + "hunyuan": 7.143, // ¥0.1 / 1k tokens // https://cloud.tencent.com/document/product/1729/97731#e0e6be58-60c8-469f-bdeb-6c264ce3b4d0 + "ChatStd": 0.01 * RMB, + "ChatPro": 0.1 * RMB, // https://platform.moonshot.cn/pricing "moonshot-v1-8k": 0.012 * RMB, "moonshot-v1-32k": 0.024 * RMB, @@ -390,11 +455,13 @@ func GetCompletionRatio(name string, channelType int) float64 { return 4.0 / 3.0 } if strings.HasPrefix(name, "gpt-4") { - if strings.HasPrefix(name, "gpt-4o-mini") || name == "gpt-4o-2024-08-06" { + if strings.HasPrefix(name, "gpt-4o") { + if name == "gpt-4o-2024-05-13" { + return 3 + } return 4 } if strings.HasPrefix(name, "gpt-4-turbo") || - strings.HasPrefix(name, "gpt-4o") || strings.HasSuffix(name, "preview") { return 3 } diff --git a/relay/controller/audio.go b/relay/controller/audio.go index dafa5cc7..541ee1a5 100644 --- a/relay/controller/audio.go +++ b/relay/controller/audio.go @@ -111,16 +111,9 @@ func RelayAudioHelper(c *gin.Context, relayMode int) *relaymodel.ErrorWithStatus }() // map model name - modelMapping := c.GetString(ctxkey.ModelMapping) - if modelMapping != "" { - modelMap := make(map[string]string) - err := json.Unmarshal([]byte(modelMapping), &modelMap) - if err != nil { - return openai.ErrorWrapper(err, "unmarshal_model_mapping_failed", http.StatusInternalServerError) - } - if modelMap[audioModel] != "" { - audioModel = modelMap[audioModel] - } + modelMapping := c.GetStringMapString(ctxkey.ModelMapping) + if modelMapping != nil && modelMapping[audioModel] != "" { + audioModel = modelMapping[audioModel] } baseURL := channeltype.ChannelBaseURLs[channelType] diff --git a/web/berry/src/constants/ChannelConstants.js b/web/berry/src/constants/ChannelConstants.js index 375adcd9..b5a855a6 100644 --- a/web/berry/src/constants/ChannelConstants.js +++ b/web/berry/src/constants/ChannelConstants.js @@ -185,7 +185,7 @@ export const CHANNEL_OPTIONS = { value: 45, color: 'primary' }, - 45: { + 46: { key: 46, text: 'Replicate', value: 46, diff --git a/web/berry/src/views/Channel/component/TableRow.js b/web/berry/src/views/Channel/component/TableRow.js index 3114479d..525f9188 100644 --- a/web/berry/src/views/Channel/component/TableRow.js +++ b/web/berry/src/views/Channel/component/TableRow.js @@ -268,6 +268,8 @@ function renderBalance(type, balance) { return ¥{balance.toFixed(2)}; case 13: // AIGC2D return {renderNumber(balance)}; + case 36: // DeepSeek + return ¥{balance.toFixed(2)}; case 44: // SiliconFlow return ¥{balance.toFixed(2)}; default: diff --git a/web/default/src/components/ChannelsTable.js b/web/default/src/components/ChannelsTable.js index 6e0ec05d..e745814b 100644 --- a/web/default/src/components/ChannelsTable.js +++ b/web/default/src/components/ChannelsTable.js @@ -52,6 +52,8 @@ function renderBalance(type, balance) { return ¥{balance.toFixed(2)}; case 13: // AIGC2D return {renderNumber(balance)}; + case 36: // DeepSeek + return ¥{balance.toFixed(2)}; case 44: // SiliconFlow return ¥{balance.toFixed(2)}; default: