From 818bd824da99a014528ca36858afcd1f61cd4f89 Mon Sep 17 00:00:00 2001 From: CaIon <1808837298@qq.com> Date: Thu, 18 Apr 2024 17:52:18 +0800 Subject: [PATCH 01/29] =?UTF-8?q?feat:=20=E5=89=8D=E7=AB=AF=E4=B8=8D?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E6=95=8F=E6=84=9F=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/constants.go | 9 -- common/model-ratio.go | 172 ++++++++++++++-------------- constant/payment.go | 8 ++ controller/misc.go | 6 +- controller/option.go | 2 +- controller/topup.go | 13 ++- model/option.go | 16 +-- service/epay.go | 9 +- web/src/components/SystemSetting.js | 14 ++- 9 files changed, 129 insertions(+), 120 deletions(-) create mode 100644 constant/payment.go diff --git a/common/constants.go b/common/constants.go index 54c3e37..78935ec 100644 --- a/common/constants.go +++ b/common/constants.go @@ -9,15 +9,6 @@ import ( "github.com/google/uuid" ) -// Pay Settings - -var PayAddress = "" -var CustomCallbackAddress = "" -var EpayId = "" -var EpayKey = "" -var Price = 7.3 -var MinTopUp = 1 - var StartTime = time.Now().Unix() // unit: second var Version = "v0.0.0" // this hard coding will be replaced automatically when building, no need to manually change var SystemName = "New API" diff --git a/common/model-ratio.go b/common/model-ratio.go index 2d6ae6f..e7ffe42 100644 --- a/common/model-ratio.go +++ b/common/model-ratio.go @@ -14,91 +14,91 @@ import ( // 1 === ¥0.014 / 1k tokens var DefaultModelRatio = map[string]float64{ //"midjourney": 50, - "gpt-4-gizmo-*": 15, - "gpt-4": 15, - "gpt-4-0314": 15, - "gpt-4-0613": 15, - "gpt-4-32k": 30, - "gpt-4-32k-0314": 30, - "gpt-4-32k-0613": 30, - "gpt-4-1106-preview": 5, // $0.01 / 1K tokens - "gpt-4-0125-preview": 5, // $0.01 / 1K tokens - "gpt-4-turbo-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-turbo": 5, // $0.01 / 1K tokens - "gpt-3.5-turbo": 0.25, // $0.0015 / 1K tokens - "gpt-3.5-turbo-0301": 0.75, - "gpt-3.5-turbo-0613": 0.75, - "gpt-3.5-turbo-16k": 1.5, // $0.003 / 1K tokens - "gpt-3.5-turbo-16k-0613": 1.5, - "gpt-3.5-turbo-instruct": 0.75, // $0.0015 / 1K tokens - "gpt-3.5-turbo-1106": 0.5, // $0.001 / 1K tokens - "gpt-3.5-turbo-0125": 0.25, - "babbage-002": 0.2, // $0.0004 / 1K tokens - "davinci-002": 1, // $0.002 / 1K tokens - "text-ada-001": 0.2, - "text-babbage-001": 0.25, - "text-curie-001": 1, - "text-davinci-002": 10, - "text-davinci-003": 10, - "text-davinci-edit-001": 10, - "code-davinci-edit-001": 10, - "whisper-1": 15, // $0.006 / minute -> $0.006 / 150 words -> $0.006 / 200 tokens -> $0.03 / 1k tokens - "tts-1": 7.5, // 1k characters -> $0.015 - "tts-1-1106": 7.5, // 1k characters -> $0.015 - "tts-1-hd": 15, // 1k characters -> $0.03 - "tts-1-hd-1106": 15, // 1k characters -> $0.03 - "davinci": 10, - "curie": 10, - "babbage": 10, - "ada": 10, - "text-embedding-3-small": 0.01, - "text-embedding-3-large": 0.065, - "text-embedding-ada-002": 0.05, - "text-search-ada-doc-001": 10, - "text-moderation-stable": 0.1, - "text-moderation-latest": 0.1, - "dall-e-2": 8, - "dall-e-3": 16, - "claude-instant-1": 0.4, // $0.8 / 1M tokens - "claude-2.0": 4, // $8 / 1M tokens - "claude-2.1": 4, // $8 / 1M tokens - "claude-3-haiku-20240307": 0.125, // $0.25 / 1M tokens - "claude-3-sonnet-20240229": 1.5, // $3 / 1M tokens - "claude-3-opus-20240229": 7.5, // $15 / 1M tokens - "ERNIE-Bot": 0.8572, // ¥0.012 / 1k tokens - "ERNIE-Bot-turbo": 0.5715, // ¥0.008 / 1k tokens - "ERNIE-Bot-4": 8.572, // ¥0.12 / 1k tokens - "Embedding-V1": 0.1429, // ¥0.002 / 1k tokens - "PaLM-2": 1, - "gemini-pro": 1, // $0.00025 / 1k characters -> $0.001 / 1k tokens - "gemini-pro-vision": 1, // $0.00025 / 1k characters -> $0.001 / 1k tokens - "gemini-1.0-pro-vision-001": 1, - "gemini-1.0-pro-001": 1, - "gemini-1.5-pro-latest": 1, - "gemini-1.0-pro-latest": 1, - "gemini-1.0-pro-vision-latest": 1, - "gemini-ultra": 1, - "chatglm_turbo": 0.3572, // ¥0.005 / 1k tokens - "chatglm_pro": 0.7143, // ¥0.01 / 1k tokens - "chatglm_std": 0.3572, // ¥0.005 / 1k tokens - "chatglm_lite": 0.1429, // ¥0.002 / 1k tokens - "glm-4": 7.143, // ¥0.1 / 1k tokens - "glm-4v": 7.143, // ¥0.1 / 1k tokens - "glm-3-turbo": 0.3572, - "qwen-turbo": 0.8572, // ¥0.012 / 1k tokens - "qwen-plus": 10, // ¥0.14 / 1k tokens - "text-embedding-v1": 0.05, // ¥0.0007 / 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.5": 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 + "gpt-4-gizmo-*": 15, + "gpt-4": 15, + "gpt-4-0314": 15, + "gpt-4-0613": 15, + "gpt-4-32k": 30, + "gpt-4-32k-0314": 30, + "gpt-4-32k-0613": 30, + "gpt-4-1106-preview": 5, // $0.01 / 1K tokens + "gpt-4-0125-preview": 5, // $0.01 / 1K tokens + "gpt-4-turbo-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-turbo": 5, // $0.01 / 1K tokens + "gpt-3.5-turbo": 0.25, // $0.0015 / 1K tokens + "gpt-3.5-turbo-0301": 0.75, + "gpt-3.5-turbo-0613": 0.75, + "gpt-3.5-turbo-16k": 1.5, // $0.003 / 1K tokens + "gpt-3.5-turbo-16k-0613": 1.5, + "gpt-3.5-turbo-instruct": 0.75, // $0.0015 / 1K tokens + "gpt-3.5-turbo-1106": 0.5, // $0.001 / 1K tokens + "gpt-3.5-turbo-0125": 0.25, + "babbage-002": 0.2, // $0.0004 / 1K tokens + "davinci-002": 1, // $0.002 / 1K tokens + "text-ada-001": 0.2, + "text-babbage-001": 0.25, + "text-curie-001": 1, + "text-davinci-002": 10, + "text-davinci-003": 10, + "text-davinci-edit-001": 10, + "code-davinci-edit-001": 10, + "whisper-1": 15, // $0.006 / minute -> $0.006 / 150 words -> $0.006 / 200 tokens -> $0.03 / 1k tokens + "tts-1": 7.5, // 1k characters -> $0.015 + "tts-1-1106": 7.5, // 1k characters -> $0.015 + "tts-1-hd": 15, // 1k characters -> $0.03 + "tts-1-hd-1106": 15, // 1k characters -> $0.03 + "davinci": 10, + "curie": 10, + "babbage": 10, + "ada": 10, + "text-embedding-3-small": 0.01, + "text-embedding-3-large": 0.065, + "text-embedding-ada-002": 0.05, + "text-search-ada-doc-001": 10, + "text-moderation-stable": 0.1, + "text-moderation-latest": 0.1, + "dall-e-2": 8, + "dall-e-3": 16, + "claude-instant-1": 0.4, // $0.8 / 1M tokens + "claude-2.0": 4, // $8 / 1M tokens + "claude-2.1": 4, // $8 / 1M tokens + "claude-3-haiku-20240307": 0.125, // $0.25 / 1M tokens + "claude-3-sonnet-20240229": 1.5, // $3 / 1M tokens + "claude-3-opus-20240229": 7.5, // $15 / 1M tokens + "ERNIE-Bot": 0.8572, // ¥0.012 / 1k tokens + "ERNIE-Bot-turbo": 0.5715, // ¥0.008 / 1k tokens + "ERNIE-Bot-4": 8.572, // ¥0.12 / 1k tokens + "Embedding-V1": 0.1429, // ¥0.002 / 1k tokens + "PaLM-2": 1, + "gemini-pro": 1, // $0.00025 / 1k characters -> $0.001 / 1k tokens + "gemini-pro-vision": 1, // $0.00025 / 1k characters -> $0.001 / 1k tokens + "gemini-1.0-pro-vision-001": 1, + "gemini-1.0-pro-001": 1, + "gemini-1.5-pro-latest": 1, + "gemini-1.0-pro-latest": 1, + "gemini-1.0-pro-vision-latest": 1, + "gemini-ultra": 1, + "chatglm_turbo": 0.3572, // ¥0.005 / 1k tokens + "chatglm_pro": 0.7143, // ¥0.01 / 1k tokens + "chatglm_std": 0.3572, // ¥0.005 / 1k tokens + "chatglm_lite": 0.1429, // ¥0.002 / 1k tokens + "glm-4": 7.143, // ¥0.1 / 1k tokens + "glm-4v": 7.143, // ¥0.1 / 1k tokens + "glm-3-turbo": 0.3572, + "qwen-turbo": 0.8572, // ¥0.012 / 1k tokens + "qwen-plus": 10, // ¥0.14 / 1k tokens + "text-embedding-v1": 0.05, // ¥0.0007 / 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.5": 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 // https://platform.lingyiwanwu.com/docs#-计费单元 // 已经按照 7.2 来换算美元价格 "yi-34b-chat-0205": 0.018, @@ -205,7 +205,7 @@ func GetCompletionRatio(name string) float64 { return 4.0 / 3.0 } if strings.HasPrefix(name, "gpt-4") { - 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 2 diff --git a/constant/payment.go b/constant/payment.go new file mode 100644 index 0000000..da1e0dd --- /dev/null +++ b/constant/payment.go @@ -0,0 +1,8 @@ +package constant + +var PayAddress = "" +var CustomCallbackAddress = "" +var EpayId = "" +var EpayKey = "" +var Price = 7.3 +var MinTopUp = 1 diff --git a/controller/misc.go b/controller/misc.go index 416538d..273a618 100644 --- a/controller/misc.go +++ b/controller/misc.go @@ -46,8 +46,8 @@ func GetStatus(c *gin.Context) { "wechat_qrcode": common.WeChatAccountQRCodeImageURL, "wechat_login": common.WeChatAuthEnabled, "server_address": common.ServerAddress, - "price": common.Price, - "min_topup": common.MinTopUp, + "price": constant.Price, + "min_topup": constant.MinTopUp, "turnstile_check": common.TurnstileCheckEnabled, "turnstile_site_key": common.TurnstileSiteKey, "top_up_link": common.TopUpLink, @@ -60,7 +60,7 @@ func GetStatus(c *gin.Context) { "enable_data_export": common.DataExportEnabled, "data_export_default_time": common.DataExportDefaultTime, "default_collapse_sidebar": common.DefaultCollapseSidebar, - "enable_online_topup": common.PayAddress != "" && common.EpayId != "" && common.EpayKey != "", + "enable_online_topup": constant.PayAddress != "" && constant.EpayId != "" && constant.EpayKey != "", "mj_notify_enabled": constant.MjNotifyEnabled, }, }) diff --git a/controller/option.go b/controller/option.go index bbf8357..7c4db41 100644 --- a/controller/option.go +++ b/controller/option.go @@ -14,7 +14,7 @@ func GetOptions(c *gin.Context) { var options []*model.Option common.OptionMapRWMutex.Lock() for k, v := range common.OptionMap { - if strings.HasSuffix(k, "Token") || strings.HasSuffix(k, "Secret") { + if strings.HasSuffix(k, "Token") || strings.HasSuffix(k, "Secret") || strings.HasSuffix(k, "Key") { continue } options = append(options, &model.Option{ diff --git a/controller/topup.go b/controller/topup.go index 08493f9..ebb24a9 100644 --- a/controller/topup.go +++ b/controller/topup.go @@ -5,6 +5,7 @@ import ( "github.com/Calcium-Ion/go-epay/epay" "github.com/gin-gonic/gin" "github.com/samber/lo" + "one-api/constant" "log" "net/url" @@ -28,13 +29,13 @@ type AmountRequest struct { } func GetEpayClient() *epay.Client { - if common.PayAddress == "" || common.EpayId == "" || common.EpayKey == "" { + if constant.PayAddress == "" || constant.EpayId == "" || constant.EpayKey == "" { return nil } withUrl, err := epay.NewClient(&epay.Config{ - PartnerID: common.EpayId, - Key: common.EpayKey, - }, common.PayAddress) + PartnerID: constant.EpayId, + Key: constant.EpayKey, + }, constant.PayAddress) if err != nil { return nil } @@ -50,12 +51,12 @@ func getPayMoney(amount float64, user model.User) float64 { if topupGroupRatio == 0 { topupGroupRatio = 1 } - payMoney := amount * common.Price * topupGroupRatio + payMoney := amount * constant.Price * topupGroupRatio return payMoney } func getMinTopup() int { - minTopup := common.MinTopUp + minTopup := constant.MinTopUp if !common.DisplayInCurrencyEnabled { minTopup = minTopup * int(common.QuotaPerUnit) } diff --git a/model/option.go b/model/option.go index 8432141..91a7ea7 100644 --- a/model/option.go +++ b/model/option.go @@ -63,8 +63,8 @@ func InitOptionMap() { common.OptionMap["CustomCallbackAddress"] = "" common.OptionMap["EpayId"] = "" common.OptionMap["EpayKey"] = "" - common.OptionMap["Price"] = strconv.FormatFloat(common.Price, 'f', -1, 64) - common.OptionMap["MinTopUp"] = strconv.Itoa(common.MinTopUp) + common.OptionMap["Price"] = strconv.FormatFloat(constant.Price, 'f', -1, 64) + common.OptionMap["MinTopUp"] = strconv.Itoa(constant.MinTopUp) common.OptionMap["TopupGroupRatio"] = common.TopupGroupRatio2JSONString() common.OptionMap["GitHubClientId"] = "" common.OptionMap["GitHubClientSecret"] = "" @@ -227,17 +227,17 @@ func updateOptionMap(key string, value string) (err error) { case "ServerAddress": common.ServerAddress = value case "PayAddress": - common.PayAddress = value + constant.PayAddress = value case "CustomCallbackAddress": - common.CustomCallbackAddress = value + constant.CustomCallbackAddress = value case "EpayId": - common.EpayId = value + constant.EpayId = value case "EpayKey": - common.EpayKey = value + constant.EpayKey = value case "Price": - common.Price, _ = strconv.ParseFloat(value, 64) + constant.Price, _ = strconv.ParseFloat(value, 64) case "MinTopUp": - common.MinTopUp, _ = strconv.Atoi(value) + constant.MinTopUp, _ = strconv.Atoi(value) case "TopupGroupRatio": err = common.UpdateTopupGroupRatioByJSONString(value) case "GitHubClientId": diff --git a/service/epay.go b/service/epay.go index 7ce4aad..4678157 100644 --- a/service/epay.go +++ b/service/epay.go @@ -1,10 +1,13 @@ package service -import "one-api/common" +import ( + "one-api/common" + "one-api/constant" +) func GetCallbackAddress() string { - if common.CustomCallbackAddress == "" { + if constant.CustomCallbackAddress == "" { return common.ServerAddress } - return common.CustomCallbackAddress + return constant.CustomCallbackAddress } diff --git a/web/src/components/SystemSetting.js b/web/src/components/SystemSetting.js index b07c0dd..5906896 100644 --- a/web/src/components/SystemSetting.js +++ b/web/src/components/SystemSetting.js @@ -312,7 +312,9 @@ const SystemSetting = () => {
-
通用设置
+
+ 通用设置 +
{ /> { 更新支付设置 -
配置登录注册
+
+ 配置登录注册 +
{ 保存 WeChat Server 设置 -
配置 Telegram 登录
+
+ 配置 Telegram 登录 +
Date: Thu, 18 Apr 2024 18:02:09 +0800 Subject: [PATCH 02/29] =?UTF-8?q?feat:=20=E5=8F=AF=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E6=98=AF=E5=90=A6=E8=BD=AC=E5=8F=91=E4=B8=8A=E6=B8=B8mj?= =?UTF-8?q?=E5=9B=BE=E7=89=87=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- constant/midjourney.go | 2 +- controller/midjourney.go | 10 ++++++++-- model/option.go | 3 +++ relay/relay-mj.go | 4 +++- web/src/components/OperationSetting.js | 7 +++++++ 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/constant/midjourney.go b/constant/midjourney.go index 6d0b5ac..cd38d5f 100644 --- a/constant/midjourney.go +++ b/constant/midjourney.go @@ -1,8 +1,8 @@ package constant var MjNotifyEnabled = false - var MjModeClearEnabled = false +var MjForwardUrlEnabled = true const ( MjErrorUnknown = 5 diff --git a/controller/midjourney.go b/controller/midjourney.go index b5b832b..3d779c8 100644 --- a/controller/midjourney.go +++ b/controller/midjourney.go @@ -10,11 +10,11 @@ import ( "log" "net/http" "one-api/common" + "one-api/constant" "one-api/dto" "one-api/model" "one-api/service" "strconv" - "strings" "time" ) @@ -233,6 +233,12 @@ func GetAllMidjourney(c *gin.Context) { if logs == nil { logs = make([]*model.Midjourney, 0) } + if constant.MjForwardUrlEnabled { + for i, midjourney := range logs { + midjourney.ImageUrl = common.ServerAddress + "/mj/image/" + midjourney.MjId + logs[i] = midjourney + } + } c.JSON(200, gin.H{ "success": true, "message": "", @@ -259,7 +265,7 @@ func GetUserMidjourney(c *gin.Context) { if logs == nil { logs = make([]*model.Midjourney, 0) } - if !strings.Contains(common.ServerAddress, "localhost") { + if constant.MjForwardUrlEnabled { for i, midjourney := range logs { midjourney.ImageUrl = common.ServerAddress + "/mj/image/" + midjourney.MjId logs[i] = midjourney diff --git a/model/option.go b/model/option.go index 91a7ea7..57bbc38 100644 --- a/model/option.go +++ b/model/option.go @@ -93,6 +93,7 @@ func InitOptionMap() { common.OptionMap["DefaultCollapseSidebar"] = strconv.FormatBool(common.DefaultCollapseSidebar) common.OptionMap["MjNotifyEnabled"] = strconv.FormatBool(constant.MjNotifyEnabled) common.OptionMap["MjModeClearEnabled"] = strconv.FormatBool(constant.MjModeClearEnabled) + common.OptionMap["MjForwardUrlEnabled"] = strconv.FormatBool(constant.MjForwardUrlEnabled) common.OptionMap["CheckSensitiveEnabled"] = strconv.FormatBool(constant.CheckSensitiveEnabled) common.OptionMap["CheckSensitiveOnPromptEnabled"] = strconv.FormatBool(constant.CheckSensitiveOnPromptEnabled) //common.OptionMap["CheckSensitiveOnCompletionEnabled"] = strconv.FormatBool(constant.CheckSensitiveOnCompletionEnabled) @@ -198,6 +199,8 @@ func updateOptionMap(key string, value string) (err error) { constant.MjNotifyEnabled = boolValue case "MjModeClearEnabled": constant.MjModeClearEnabled = boolValue + case "MjForwardUrlEnabled": + constant.MjForwardUrlEnabled = boolValue case "CheckSensitiveEnabled": constant.CheckSensitiveEnabled = boolValue case "CheckSensitiveOnPromptEnabled": diff --git a/relay/relay-mj.go b/relay/relay-mj.go index 7b3a4e2..27b4c6d 100644 --- a/relay/relay-mj.go +++ b/relay/relay-mj.go @@ -110,11 +110,13 @@ func coverMidjourneyTaskDto(c *gin.Context, originTask *model.Midjourney) (midjo midjourneyTask.StartTime = originTask.StartTime midjourneyTask.FinishTime = originTask.FinishTime midjourneyTask.ImageUrl = "" - if originTask.ImageUrl != "" { + if originTask.ImageUrl != "" && constant.MjForwardUrlEnabled { midjourneyTask.ImageUrl = common.ServerAddress + "/mj/image/" + originTask.MjId if originTask.Status != "SUCCESS" { midjourneyTask.ImageUrl += "?rand=" + strconv.FormatInt(time.Now().UnixNano(), 10) } + } else { + midjourneyTask.ImageUrl = originTask.ImageUrl } midjourneyTask.Status = originTask.Status midjourneyTask.FailReason = originTask.FailReason diff --git a/web/src/components/OperationSetting.js b/web/src/components/OperationSetting.js index 7a112e5..44b6214 100644 --- a/web/src/components/OperationSetting.js +++ b/web/src/components/OperationSetting.js @@ -39,6 +39,7 @@ const OperationSetting = () => { SensitiveWords: '', MjNotifyEnabled: '', MjModeClearEnabled: '', + MjForwardUrlEnabled: '', DrawingEnabled: '', DataExportEnabled: '', DataExportDefaultTime: 'hour', @@ -322,6 +323,12 @@ const OperationSetting = () => { name='MjNotifyEnabled' onChange={handleInputChange} /> + Date: Thu, 18 Apr 2024 19:37:52 +0800 Subject: [PATCH 03/29] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E7=9C=8B=E6=9D=BF=20#190?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/pages/Detail/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/src/pages/Detail/index.js b/web/src/pages/Detail/index.js index ccc07c1..f334dd3 100644 --- a/web/src/pages/Detail/index.js +++ b/web/src/pages/Detail/index.js @@ -72,6 +72,7 @@ const Detail = (props) => { stack: true, legends: { visible: true, + selectMode: 'single', }, title: { visible: true, @@ -216,6 +217,8 @@ const Detail = (props) => { } else if (dataExportDefaultTime === 'week') { timeGranularity = 604800; } + // sort created_at + data.sort((a, b) => a.created_at - b.created_at); data.forEach((item) => { item['created_at'] = Math.floor(item['created_at'] / timeGranularity) * timeGranularity; From 65b85377c62531fc4d0753546cc5714840fa0302 Mon Sep 17 00:00:00 2001 From: CaIon <1808837298@qq.com> Date: Thu, 18 Apr 2024 20:23:06 +0800 Subject: [PATCH 04/29] feat: update cache #204 --- model/cache.go | 6 +++++- model/token.go | 3 +++ model/user.go | 5 +++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/model/cache.go b/model/cache.go index dc2ed3b..2080292 100644 --- a/model/cache.go +++ b/model/cache.go @@ -168,7 +168,11 @@ func CacheUpdateUserQuota(id int) error { if err != nil { return err } - err = common.RedisSet(fmt.Sprintf("user_quota:%d", id), fmt.Sprintf("%d", quota), time.Duration(UserId2QuotaCacheSeconds)*time.Second) + return CacheSetUserQuota(id, quota) +} + +func CacheSetUserQuota(id int, quota int) error { + err := common.RedisSet(fmt.Sprintf("user_quota:%d", id), fmt.Sprintf("%d", quota), time.Duration(UserId2QuotaCacheSeconds)*time.Second) return err } diff --git a/model/token.go b/model/token.go index 08909cb..51805bc 100644 --- a/model/token.go +++ b/model/token.go @@ -102,6 +102,9 @@ func GetTokenById(id int) (*Token, error) { token := Token{Id: id} var err error = nil err = DB.First(&token, "id = ?", id).Error + if err != nil { + go cacheSetToken(&token) + } return &token, err } diff --git a/model/user.go b/model/user.go index 8aaaf52..ac1f8d3 100644 --- a/model/user.go +++ b/model/user.go @@ -410,6 +410,11 @@ func ValidateAccessToken(token string) (user *User) { func GetUserQuota(id int) (quota int, err error) { err = DB.Model(&User{}).Where("id = ?", id).Select("quota").Find("a).Error + if err != nil { + if common.RedisEnabled { + go CacheSetUserQuota(id, quota) + } + } return quota, err } From 4ef98ba7eb0f81bea3315444a0fbccc04a03f544 Mon Sep 17 00:00:00 2001 From: CaIon <1808837298@qq.com> Date: Thu, 18 Apr 2024 20:26:38 +0800 Subject: [PATCH 05/29] feat: update cache --- model/token.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/model/token.go b/model/token.go index 51805bc..1bbf6c4 100644 --- a/model/token.go +++ b/model/token.go @@ -103,7 +103,9 @@ func GetTokenById(id int) (*Token, error) { var err error = nil err = DB.First(&token, "id = ?", id).Error if err != nil { - go cacheSetToken(&token) + if common.RedisEnabled { + go cacheSetToken(&token) + } } return &token, err } From e6765ef32db8dec0e6a88f86e137e0acfcba580f Mon Sep 17 00:00:00 2001 From: CaIon <1808837298@qq.com> Date: Thu, 18 Apr 2024 20:30:17 +0800 Subject: [PATCH 06/29] feat: update cache --- model/cache.go | 7 ++----- model/user.go | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/model/cache.go b/model/cache.go index 2080292..330e09b 100644 --- a/model/cache.go +++ b/model/cache.go @@ -25,9 +25,6 @@ var token2UserId = make(map[string]int) var token2UserIdLock sync.RWMutex func cacheSetToken(token *Token) error { - if !common.RedisEnabled { - return token.SelectUpdate() - } jsonBytes, err := json.Marshal(token) if err != nil { return err @@ -168,10 +165,10 @@ func CacheUpdateUserQuota(id int) error { if err != nil { return err } - return CacheSetUserQuota(id, quota) + return cacheSetUserQuota(id, quota) } -func CacheSetUserQuota(id int, quota int) error { +func cacheSetUserQuota(id int, quota int) error { err := common.RedisSet(fmt.Sprintf("user_quota:%d", id), fmt.Sprintf("%d", quota), time.Duration(UserId2QuotaCacheSeconds)*time.Second) return err } diff --git a/model/user.go b/model/user.go index ac1f8d3..f3ce5ee 100644 --- a/model/user.go +++ b/model/user.go @@ -412,7 +412,7 @@ func GetUserQuota(id int) (quota int, err error) { err = DB.Model(&User{}).Where("id = ?", id).Select("quota").Find("a).Error if err != nil { if common.RedisEnabled { - go CacheSetUserQuota(id, quota) + go cacheSetUserQuota(id, quota) } } return quota, err From 79cf70683fa6a736bed76d1591b6e952e8c0b7a5 Mon Sep 17 00:00:00 2001 From: iszcz <74706321+iszcz@users.noreply.github.com> Date: Sat, 20 Apr 2024 01:25:47 +0800 Subject: [PATCH 07/29] =?UTF-8?q?=E7=94=A8=E6=88=B7=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E9=A1=B5=E6=96=B0=E5=A2=9E=E5=88=86=E7=BB=84=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller/user.go | 3 +- model/user.go | 45 +++++++++++-------- web/src/components/UsersTable.js | 77 ++++++++++++++++++++++++++------ 3 files changed, 92 insertions(+), 33 deletions(-) diff --git a/controller/user.go b/controller/user.go index 309ce49..fe596a0 100644 --- a/controller/user.go +++ b/controller/user.go @@ -216,7 +216,8 @@ func GetAllUsers(c *gin.Context) { func SearchUsers(c *gin.Context) { keyword := c.Query("keyword") - users, err := model.SearchUsers(keyword) + group := c.Query("group") + users, err := model.SearchUsers(keyword, group) if err != nil { c.JSON(http.StatusOK, gin.H{ "success": false, diff --git a/model/user.go b/model/user.go index f3ce5ee..f943c17 100644 --- a/model/user.go +++ b/model/user.go @@ -73,27 +73,36 @@ func GetAllUsers(startIdx int, num int) (users []*User, err error) { return users, err } -func SearchUsers(keyword string) ([]*User, error) { - var users []*User - var err error +func SearchUsers(keyword string, group string) ([]*User, error) { + var users []*User + var err error - // 尝试将关键字转换为整数ID - keywordInt, err := strconv.Atoi(keyword) - if err == nil { - // 如果转换成功,按照ID搜索用户 - err = DB.Unscoped().Omit("password").Where("id = ?", keywordInt).Find(&users).Error - if err != nil || len(users) > 0 { - // 如果依据ID找到用户或者发生错误,返回结果或错误 - return users, err - } - } + // 尝试将关键字转换为整数ID + keywordInt, err := strconv.Atoi(keyword) + if err == nil { + // 如果转换成功,按照ID和可选的组别搜索用户 + query := DB.Unscoped().Omit("password").Where("`id` = ?", keywordInt) + if group != "" { + query = query.Where("`group` = ?", group) // 使用反引号包围group + } + err = query.Find(&users).Error + if err != nil || len(users) > 0 { + return users, err + } + } - // 如果ID转换失败或者没有找到用户,依据其他字段进行模糊搜索 - err = DB.Unscoped().Omit("password"). - Where("username LIKE ? OR email LIKE ? OR display_name LIKE ?", keyword+"%", keyword+"%", keyword+"%"). - Find(&users).Error + err = nil - return users, err + query := DB.Unscoped().Omit("password") + likeCondition := "`username` LIKE ? OR `email` LIKE ? OR `display_name` LIKE ?" + if group != "" { + query = query.Where("("+likeCondition+") AND `group` = ?", "%"+keyword+"%", "%"+keyword+"%", "%"+keyword+"%", group) + } else { + query = query.Where(likeCondition, "%"+keyword+"%", "%"+keyword+"%", "%"+keyword+"%") + } + err = query.Find(&users).Error + + return users, err } func GetUserById(id int, selectAll bool) (*User, error) { diff --git a/web/src/components/UsersTable.js b/web/src/components/UsersTable.js index 50fe7a0..209148a 100644 --- a/web/src/components/UsersTable.js +++ b/web/src/components/UsersTable.js @@ -235,6 +235,8 @@ const UsersTable = () => { const [activePage, setActivePage] = useState(1); const [searchKeyword, setSearchKeyword] = useState(''); const [searching, setSearching] = useState(false); + const [searchGroup, setSearchGroup] = useState(''); + const [groupOptions, setGroupOptions] = useState([]); const [userCount, setUserCount] = useState(ITEMS_PER_PAGE); const [showAddUser, setShowAddUser] = useState(false); const [showEditUser, setShowEditUser] = useState(false); @@ -298,6 +300,7 @@ const UsersTable = () => { .catch((reason) => { showError(reason); }); + fetchGroups().then(); }, []); const manageUser = async (username, action, record) => { @@ -340,15 +343,15 @@ const UsersTable = () => { } }; - const searchUsers = async () => { - if (searchKeyword === '') { + const searchUsers = async (searchKeyword, searchGroup) => { + if (searchKeyword === '' && searchGroup === '') { // if keyword is blank, load files instead. await loadUsers(0); setActivePage(1); return; } setSearching(true); - const res = await API.get(`/api/user/search?keyword=${searchKeyword}`); + const res = await API.get(`/api/user/search?keyword=${searchKeyword}&group=${searchGroup}`); const { success, message, data } = res.data; if (success) { setUsers(data); @@ -409,6 +412,25 @@ const UsersTable = () => { } }; + const fetchGroups = async () => { + try { + let res = await API.get(`/api/group/`); + // add 'all' option + // res.data.data.unshift('all'); + if (res === undefined) { + return; + } + setGroupOptions( + res.data.data.map((group) => ({ + label: group, + value: group, + })), + ); + } catch (error) { + showError(error.message); + } + }; + return ( <> { handleClose={closeEditUser} editingUser={editingUser} > - - handleKeywordChange(value)} - /> + { + searchUsers(searchKeyword, searchGroup); + }} + labelPosition='left' + > +
+ + handleKeywordChange(value)} + /> + { + setSearchGroup(value); + searchUsers(searchKeyword, value); + }} + /> + + +
Date: Sat, 20 Apr 2024 17:18:14 +0800 Subject: [PATCH 08/29] =?UTF-8?q?feat:=20=E5=9C=A8=E9=87=8D=E8=AF=95?= =?UTF-8?q?=E6=97=B6=E6=89=93=E5=8D=B0=E9=87=8D=E8=AF=95=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller/relay.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/controller/relay.go b/controller/relay.go index 0fd9d7a..089bde2 100644 --- a/controller/relay.go +++ b/controller/relay.go @@ -15,6 +15,7 @@ import ( "one-api/relay/constant" relayconstant "one-api/relay/constant" "one-api/service" + "strings" ) func relayHandler(c *gin.Context, relayMode int) *dto.OpenAIErrorWithStatusCode { @@ -42,7 +43,7 @@ func Relay(c *gin.Context) { group := c.GetString("group") originalModel := c.GetString("original_model") openaiErr := relayHandler(c, relayMode) - retryLogStr := fmt.Sprintf("重试:%d", channelId) + useChannel := []int{channelId} if openaiErr != nil { go processChannelError(c, channelId, openaiErr) } else { @@ -55,7 +56,7 @@ func Relay(c *gin.Context) { break } channelId = channel.Id - retryLogStr += fmt.Sprintf("->%d", channel.Id) + useChannel = append(useChannel, channelId) common.LogInfo(c.Request.Context(), fmt.Sprintf("using channel #%d to retry (remain times %d)", channel.Id, i)) middleware.SetupContextForSelectedChannel(c, channel, originalModel) @@ -66,7 +67,10 @@ func Relay(c *gin.Context) { go processChannelError(c, channelId, openaiErr) } } - common.LogInfo(c.Request.Context(), retryLogStr) + if len(useChannel) > 1 { + retryLogStr := fmt.Sprintf("重试:%s", strings.Trim(strings.Join(strings.Fields(fmt.Sprint(useChannel)), "->"), "[]")) + common.LogInfo(c.Request.Context(), retryLogStr) + } if openaiErr != nil { if openaiErr.StatusCode == http.StatusTooManyRequests { From 6b71db7ce2584d1066477dba002be2d6c498322d Mon Sep 17 00:00:00 2001 From: CaIon <1808837298@qq.com> Date: Sat, 20 Apr 2024 21:05:23 +0800 Subject: [PATCH 09/29] =?UTF-8?q?feat:=20=E7=8A=B6=E6=80=81=E7=A0=81?= =?UTF-8?q?=E5=A4=8D=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- middleware/distributor.go | 1 + model/channel.go | 8 +++++++ relay/relay-text.go | 8 ++++++- service/error.go | 19 +++++++++++++++ web/src/pages/Channel/EditChannel.js | 35 ++++++++++++++++++++++++++++ 5 files changed, 70 insertions(+), 1 deletion(-) diff --git a/middleware/distributor.go b/middleware/distributor.go index 108c783..ae5707f 100644 --- a/middleware/distributor.go +++ b/middleware/distributor.go @@ -177,6 +177,7 @@ func SetupContextForSelectedChannel(c *gin.Context, channel *model.Channel, mode } c.Set("auto_ban", ban) c.Set("model_mapping", channel.GetModelMapping()) + c.Set("status_code_mapping", channel.GetStatusCodeMapping()) c.Request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", channel.Key)) c.Set("base_url", channel.GetBaseURL()) // TODO: api_version统一 diff --git a/model/channel.go b/model/channel.go index 3e30ad4..c0c21c0 100644 --- a/model/channel.go +++ b/model/channel.go @@ -25,6 +25,7 @@ type Channel struct { Group string `json:"group" gorm:"type:varchar(64);default:'default'"` UsedQuota int64 `json:"used_quota" gorm:"bigint;default:0"` ModelMapping *string `json:"model_mapping" gorm:"type:varchar(1024);default:''"` + StatusCodeMapping *string `json:"status_code_mapping" gorm:"type:varchar(1024);default:''"` Priority *int64 `json:"priority" gorm:"bigint;default:0"` AutoBan *int `json:"auto_ban" gorm:"default:1"` } @@ -153,6 +154,13 @@ func (channel *Channel) GetModelMapping() string { return *channel.ModelMapping } +func (channel *Channel) GetStatusCodeMapping() string { + if channel.StatusCodeMapping == nil { + return "" + } + return *channel.StatusCodeMapping +} + func (channel *Channel) Insert() error { var err error err = DB.Create(channel).Error diff --git a/relay/relay-text.go b/relay/relay-text.go index 71a47c2..6026560 100644 --- a/relay/relay-text.go +++ b/relay/relay-text.go @@ -154,6 +154,7 @@ func TextHelper(c *gin.Context) *dto.OpenAIErrorWithStatusCode { requestBody = bytes.NewBuffer(jsonData) } + statusCodeMappingStr := c.GetString("status_code_mapping") resp, err := adaptor.DoRequest(c, relayInfo, requestBody) if err != nil { return service.OpenAIErrorWrapper(err, "do_request_failed", http.StatusInternalServerError) @@ -162,12 +163,17 @@ func TextHelper(c *gin.Context) *dto.OpenAIErrorWithStatusCode { if resp.StatusCode != http.StatusOK { returnPreConsumedQuota(c, relayInfo.TokenId, userQuota, preConsumedQuota) - return service.RelayErrorHandler(resp) + openaiErr := service.RelayErrorHandler(resp) + // reset status code 重置状态码 + service.ResetStatusCode(openaiErr, statusCodeMappingStr) + return openaiErr } usage, openaiErr := adaptor.DoResponse(c, resp, relayInfo) if openaiErr != nil { returnPreConsumedQuota(c, relayInfo.TokenId, userQuota, preConsumedQuota) + // reset status code 重置状态码 + service.ResetStatusCode(openaiErr, statusCodeMappingStr) return openaiErr } postConsumeQuota(c, relayInfo, *textRequest, usage, ratio, preConsumedQuota, userQuota, modelRatio, groupRatio, modelPrice) diff --git a/service/error.go b/service/error.go index 39eb0f9..4b00f37 100644 --- a/service/error.go +++ b/service/error.go @@ -86,3 +86,22 @@ func RelayErrorHandler(resp *http.Response) (errWithStatusCode *dto.OpenAIErrorW } return } + +func ResetStatusCode(openaiErr *dto.OpenAIErrorWithStatusCode, statusCodeMappingStr string) { + if statusCodeMappingStr == "" || statusCodeMappingStr == "{}" { + return + } + statusCodeMapping := make(map[string]string) + err := json.Unmarshal([]byte(statusCodeMappingStr), &statusCodeMapping) + if err != nil { + return + } + if openaiErr.StatusCode == http.StatusOK { + return + } + codeStr := strconv.Itoa(openaiErr.StatusCode) + if _, ok := statusCodeMapping[codeStr]; ok { + intCode, _ := strconv.Atoi(statusCodeMapping[codeStr]) + openaiErr.StatusCode = intCode + } +} diff --git a/web/src/pages/Channel/EditChannel.js b/web/src/pages/Channel/EditChannel.js index 0fe6e2b..de00d89 100644 --- a/web/src/pages/Channel/EditChannel.js +++ b/web/src/pages/Channel/EditChannel.js @@ -29,6 +29,10 @@ const MODEL_MAPPING_EXAMPLE = { 'gpt-4-32k-0314': 'gpt-4-32k', }; +const STATUS_CODE_MAPPING_EXAMPLE = { + 400: '500', +}; + function type2secretPrompt(type) { // inputs.type === 15 ? '按照如下格式输入:APIKey|SecretKey' : (inputs.type === 18 ? '按照如下格式输入:APPID|APISecret|APIKey' : '请输入渠道对应的鉴权密钥') switch (type) { @@ -61,6 +65,7 @@ const EditChannel = (props) => { base_url: '', other: '', model_mapping: '', + status_code_mapping: '', models: [], auto_ban: 1, test_model: '', @@ -629,6 +634,36 @@ const EditChannel = (props) => { > 填入模板 +
+ + 状态码复写(仅影响本地判断,不修改返回到上游的状态码): + +
+