From 7dbb6b017c4efe9457683bcba56fe9d24b22e5d9 Mon Sep 17 00:00:00 2001 From: "1808837298@qq.com" <1808837298@qq.com> Date: Sat, 1 Mar 2025 21:13:48 +0800 Subject: [PATCH] feat: Add self-use mode for model ratio and price configuration - Introduce `SelfUseModeEnabled` setting to allow flexible model ratio configuration - Update error handling to provide more informative messages when model ratios are not set - Modify pricing and relay logic to support self-use mode - Add UI toggle for enabling self-use mode in operation settings - Implement fallback mechanism for model ratios when self-use mode is enabled --- controller/channel-test.go | 9 +++++---- controller/pricing.go | 5 ++--- model/option.go | 15 ++++++++------ model/pricing.go | 7 ++++--- relay/helper/price.go | 10 +++++++--- relay/relay-mj.go | 8 ++++---- relay/relay-text.go | 2 +- relay/relay_task.go | 4 ++-- relay/websocket.go | 4 ++-- service/quota.go | 20 +++++++++---------- service/token_counter.go | 3 ++- {common => setting}/model-ratio.go | 17 ++++++++-------- setting/operation_setting.go | 1 + web/src/components/OperationSetting.js | 1 + .../Setting/Operation/SettingsGeneral.js | 17 ++++++++++++++++ 15 files changed, 76 insertions(+), 47 deletions(-) rename {common => setting}/model-ratio.go (97%) diff --git a/controller/channel-test.go b/controller/channel-test.go index 23922073..68e4d939 100644 --- a/controller/channel-test.go +++ b/controller/channel-test.go @@ -18,6 +18,7 @@ import ( relaycommon "one-api/relay/common" "one-api/relay/constant" "one-api/service" + "one-api/setting" "strconv" "strings" "sync" @@ -145,12 +146,12 @@ func testChannel(channel *model.Channel, testModel string) (err error, openAIErr if err != nil { return err, nil } - modelPrice, usePrice := common.GetModelPrice(testModel, false) - modelRatio, success := common.GetModelRatio(testModel) + modelPrice, usePrice := setting.GetModelPrice(testModel, false) + modelRatio, success := setting.GetModelRatio(testModel) if !usePrice && !success { - return fmt.Errorf("模型 %s 倍率和价格均未设置", testModel), nil + return fmt.Errorf("模型 %s 倍率和价格均未设置,请设置或者开启自用模式", testModel), nil } - completionRatio := common.GetCompletionRatio(testModel) + completionRatio := setting.GetCompletionRatio(testModel) ratio := modelRatio quota := 0 if !usePrice { diff --git a/controller/pricing.go b/controller/pricing.go index d7af5a4c..97f27490 100644 --- a/controller/pricing.go +++ b/controller/pricing.go @@ -2,7 +2,6 @@ package controller import ( "github.com/gin-gonic/gin" - "one-api/common" "one-api/model" "one-api/setting" ) @@ -40,7 +39,7 @@ func GetPricing(c *gin.Context) { } func ResetModelRatio(c *gin.Context) { - defaultStr := common.DefaultModelRatio2JSONString() + defaultStr := setting.DefaultModelRatio2JSONString() err := model.UpdateOption("ModelRatio", defaultStr) if err != nil { c.JSON(200, gin.H{ @@ -49,7 +48,7 @@ func ResetModelRatio(c *gin.Context) { }) return } - err = common.UpdateModelRatioByJSONString(defaultStr) + err = setting.UpdateModelRatioByJSONString(defaultStr) if err != nil { c.JSON(200, gin.H{ "success": false, diff --git a/model/option.go b/model/option.go index 64d15ca8..5426c28d 100644 --- a/model/option.go +++ b/model/option.go @@ -91,11 +91,11 @@ func InitOptionMap() { common.OptionMap["ModelRequestRateLimitCount"] = strconv.Itoa(setting.ModelRequestRateLimitCount) common.OptionMap["ModelRequestRateLimitDurationMinutes"] = strconv.Itoa(setting.ModelRequestRateLimitDurationMinutes) common.OptionMap["ModelRequestRateLimitSuccessCount"] = strconv.Itoa(setting.ModelRequestRateLimitSuccessCount) - common.OptionMap["ModelRatio"] = common.ModelRatio2JSONString() - common.OptionMap["ModelPrice"] = common.ModelPrice2JSONString() + common.OptionMap["ModelRatio"] = setting.ModelRatio2JSONString() + common.OptionMap["ModelPrice"] = setting.ModelPrice2JSONString() common.OptionMap["GroupRatio"] = setting.GroupRatio2JSONString() common.OptionMap["UserUsableGroups"] = setting.UserUsableGroups2JSONString() - common.OptionMap["CompletionRatio"] = common.CompletionRatio2JSONString() + common.OptionMap["CompletionRatio"] = setting.CompletionRatio2JSONString() common.OptionMap["TopUpLink"] = common.TopUpLink common.OptionMap["ChatLink"] = common.ChatLink common.OptionMap["ChatLink2"] = common.ChatLink2 @@ -111,6 +111,7 @@ func InitOptionMap() { common.OptionMap["MjActionCheckSuccessEnabled"] = strconv.FormatBool(setting.MjActionCheckSuccessEnabled) common.OptionMap["CheckSensitiveEnabled"] = strconv.FormatBool(setting.CheckSensitiveEnabled) common.OptionMap["DemoSiteEnabled"] = strconv.FormatBool(setting.DemoSiteEnabled) + common.OptionMap["SelfUseModeEnabled"] = strconv.FormatBool(setting.SelfUseModeEnabled) common.OptionMap["ModelRequestRateLimitEnabled"] = strconv.FormatBool(setting.ModelRequestRateLimitEnabled) common.OptionMap["CheckSensitiveOnPromptEnabled"] = strconv.FormatBool(setting.CheckSensitiveOnPromptEnabled) common.OptionMap["StopOnSensitiveEnabled"] = strconv.FormatBool(setting.StopOnSensitiveEnabled) @@ -243,6 +244,8 @@ func updateOptionMap(key string, value string) (err error) { setting.CheckSensitiveEnabled = boolValue case "DemoSiteEnabled": setting.DemoSiteEnabled = boolValue + case "SelfUseModeEnabled": + setting.SelfUseModeEnabled = boolValue case "CheckSensitiveOnPromptEnabled": setting.CheckSensitiveOnPromptEnabled = boolValue case "ModelRequestRateLimitEnabled": @@ -340,15 +343,15 @@ func updateOptionMap(key string, value string) (err error) { case "DataExportDefaultTime": common.DataExportDefaultTime = value case "ModelRatio": - err = common.UpdateModelRatioByJSONString(value) + err = setting.UpdateModelRatioByJSONString(value) case "GroupRatio": err = setting.UpdateGroupRatioByJSONString(value) case "UserUsableGroups": err = setting.UpdateUserUsableGroupsByJSONString(value) case "CompletionRatio": - err = common.UpdateCompletionRatioByJSONString(value) + err = setting.UpdateCompletionRatioByJSONString(value) case "ModelPrice": - err = common.UpdateModelPriceByJSONString(value) + err = setting.UpdateModelPriceByJSONString(value) case "TopUpLink": common.TopUpLink = value case "ChatLink": diff --git a/model/pricing.go b/model/pricing.go index fc709ce4..2d0aa1b7 100644 --- a/model/pricing.go +++ b/model/pricing.go @@ -2,6 +2,7 @@ package model import ( "one-api/common" + "one-api/setting" "sync" "time" ) @@ -64,14 +65,14 @@ func updatePricing() { ModelName: model, EnableGroup: groups, } - modelPrice, findPrice := common.GetModelPrice(model, false) + modelPrice, findPrice := setting.GetModelPrice(model, false) if findPrice { pricing.ModelPrice = modelPrice pricing.QuotaType = 1 } else { - modelRatio, _ := common.GetModelRatio(model) + modelRatio, _ := setting.GetModelRatio(model) pricing.ModelRatio = modelRatio - pricing.CompletionRatio = common.GetCompletionRatio(model) + pricing.CompletionRatio = setting.GetCompletionRatio(model) pricing.QuotaType = 0 } pricingMap = append(pricingMap, pricing) diff --git a/relay/helper/price.go b/relay/helper/price.go index 1f4a5b3c..97cbf162 100644 --- a/relay/helper/price.go +++ b/relay/helper/price.go @@ -17,7 +17,7 @@ type PriceData struct { } func ModelPriceHelper(c *gin.Context, info *relaycommon.RelayInfo, promptTokens int, maxTokens int) (PriceData, error) { - modelPrice, usePrice := common.GetModelPrice(info.OriginModelName, false) + modelPrice, usePrice := setting.GetModelPrice(info.OriginModelName, false) groupRatio := setting.GetGroupRatio(info.Group) var preConsumedQuota int var modelRatio float64 @@ -27,9 +27,13 @@ func ModelPriceHelper(c *gin.Context, info *relaycommon.RelayInfo, promptTokens preConsumedTokens = promptTokens + maxTokens } var success bool - modelRatio, success = common.GetModelRatio(info.OriginModelName) + modelRatio, success = setting.GetModelRatio(info.OriginModelName) if !success { - return PriceData{}, fmt.Errorf("模型 %s 倍率或价格未配置, 请联系管理员设置;Model %s ratio or price not set, please contact administrator to set", info.OriginModelName, info.OriginModelName) + if info.UserId == 1 { + return PriceData{}, fmt.Errorf("模型 %s 倍率或价格未配置,请设置或开始自用模式;Model %s ratio or price not set, please set or start self-use mode", info.OriginModelName, info.OriginModelName) + } else { + return PriceData{}, fmt.Errorf("模型 %s 倍率或价格未配置, 请联系管理员设置;Model %s ratio or price not set, please contact administrator to set", info.OriginModelName, info.OriginModelName) + } } ratio := modelRatio * groupRatio preConsumedQuota = int(float64(preConsumedTokens) * ratio) diff --git a/relay/relay-mj.go b/relay/relay-mj.go index 57de8d10..8baf033a 100644 --- a/relay/relay-mj.go +++ b/relay/relay-mj.go @@ -157,10 +157,10 @@ func RelaySwapFace(c *gin.Context) *dto.MidjourneyResponse { return service.MidjourneyErrorWrapper(constant.MjRequestError, "sour_base64_and_target_base64_is_required") } modelName := service.CoverActionToModelName(constant.MjActionSwapFace) - modelPrice, success := common.GetModelPrice(modelName, true) + modelPrice, success := setting.GetModelPrice(modelName, true) // 如果没有配置价格,则使用默认价格 if !success { - defaultPrice, ok := common.GetDefaultModelRatioMap()[modelName] + defaultPrice, ok := setting.GetDefaultModelRatioMap()[modelName] if !ok { modelPrice = 0.1 } else { @@ -463,10 +463,10 @@ func RelayMidjourneySubmit(c *gin.Context, relayMode int) *dto.MidjourneyRespons fullRequestURL := fmt.Sprintf("%s%s", baseURL, requestURL) modelName := service.CoverActionToModelName(midjRequest.Action) - modelPrice, success := common.GetModelPrice(modelName, true) + modelPrice, success := setting.GetModelPrice(modelName, true) // 如果没有配置价格,则使用默认价格 if !success { - defaultPrice, ok := common.GetDefaultModelRatioMap()[modelName] + defaultPrice, ok := setting.GetDefaultModelRatioMap()[modelName] if !ok { modelPrice = 0.1 } else { diff --git a/relay/relay-text.go b/relay/relay-text.go index eb331e25..bf6c5fd3 100644 --- a/relay/relay-text.go +++ b/relay/relay-text.go @@ -311,7 +311,7 @@ func postConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, modelName := relayInfo.OriginModelName tokenName := ctx.GetString("token_name") - completionRatio := common.GetCompletionRatio(modelName) + completionRatio := setting.GetCompletionRatio(modelName) ratio := priceData.ModelRatio * priceData.GroupRatio modelRatio := priceData.ModelRatio groupRatio := priceData.GroupRatio diff --git a/relay/relay_task.go b/relay/relay_task.go index 591ad3bb..ab35d3e8 100644 --- a/relay/relay_task.go +++ b/relay/relay_task.go @@ -37,9 +37,9 @@ func RelayTaskSubmit(c *gin.Context, relayMode int) (taskErr *dto.TaskError) { } modelName := service.CoverTaskActionToModelName(platform, relayInfo.Action) - modelPrice, success := common.GetModelPrice(modelName, true) + modelPrice, success := setting.GetModelPrice(modelName, true) if !success { - defaultPrice, ok := common.GetDefaultModelRatioMap()[modelName] + defaultPrice, ok := setting.GetDefaultModelRatioMap()[modelName] if !ok { modelPrice = 0.1 } else { diff --git a/relay/websocket.go b/relay/websocket.go index 2dac60af..b0636057 100644 --- a/relay/websocket.go +++ b/relay/websocket.go @@ -39,7 +39,7 @@ func WssHelper(c *gin.Context, ws *websocket.Conn) (openaiErr *dto.OpenAIErrorWi } } //relayInfo.UpstreamModelName = textRequest.Model - modelPrice, getModelPriceSuccess := common.GetModelPrice(relayInfo.UpstreamModelName, false) + modelPrice, getModelPriceSuccess := setting.GetModelPrice(relayInfo.UpstreamModelName, false) groupRatio := setting.GetGroupRatio(relayInfo.Group) var preConsumedQuota int @@ -65,7 +65,7 @@ func WssHelper(c *gin.Context, ws *websocket.Conn) (openaiErr *dto.OpenAIErrorWi //if realtimeEvent.Session.MaxResponseOutputTokens != 0 { // preConsumedTokens = promptTokens + int(realtimeEvent.Session.MaxResponseOutputTokens) //} - modelRatio, _ = common.GetModelRatio(relayInfo.UpstreamModelName) + modelRatio, _ = setting.GetModelRatio(relayInfo.UpstreamModelName) ratio = modelRatio * groupRatio preConsumedQuota = int(float64(preConsumedTokens) * ratio) } else { diff --git a/service/quota.go b/service/quota.go index 9ce2858d..b3412c1e 100644 --- a/service/quota.go +++ b/service/quota.go @@ -38,9 +38,9 @@ func calculateAudioQuota(info QuotaInfo) int { return int(info.ModelPrice * common.QuotaPerUnit * info.GroupRatio) } - completionRatio := common.GetCompletionRatio(info.ModelName) - audioRatio := common.GetAudioRatio(info.ModelName) - audioCompletionRatio := common.GetAudioCompletionRatio(info.ModelName) + completionRatio := setting.GetCompletionRatio(info.ModelName) + audioRatio := setting.GetAudioRatio(info.ModelName) + audioCompletionRatio := setting.GetAudioCompletionRatio(info.ModelName) ratio := info.GroupRatio * info.ModelRatio quota := info.InputDetails.TextTokens + int(math.Round(float64(info.OutputDetails.TextTokens)*completionRatio)) @@ -75,7 +75,7 @@ func PreWssConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, usag audioInputTokens := usage.InputTokenDetails.AudioTokens audioOutTokens := usage.OutputTokenDetails.AudioTokens groupRatio := setting.GetGroupRatio(relayInfo.Group) - modelRatio, _ := common.GetModelRatio(modelName) + modelRatio, _ := setting.GetModelRatio(modelName) quotaInfo := QuotaInfo{ InputDetails: TokenDetails{ @@ -122,9 +122,9 @@ func PostWssConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, mod audioOutTokens := usage.OutputTokenDetails.AudioTokens tokenName := ctx.GetString("token_name") - completionRatio := common.GetCompletionRatio(modelName) - audioRatio := common.GetAudioRatio(relayInfo.OriginModelName) - audioCompletionRatio := common.GetAudioCompletionRatio(modelName) + completionRatio := setting.GetCompletionRatio(modelName) + audioRatio := setting.GetAudioRatio(relayInfo.OriginModelName) + audioCompletionRatio := setting.GetAudioCompletionRatio(modelName) quotaInfo := QuotaInfo{ InputDetails: TokenDetails{ @@ -184,9 +184,9 @@ func PostAudioConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, audioOutTokens := usage.CompletionTokenDetails.AudioTokens tokenName := ctx.GetString("token_name") - completionRatio := common.GetCompletionRatio(relayInfo.OriginModelName) - audioRatio := common.GetAudioRatio(relayInfo.OriginModelName) - audioCompletionRatio := common.GetAudioCompletionRatio(relayInfo.OriginModelName) + completionRatio := setting.GetCompletionRatio(relayInfo.OriginModelName) + audioRatio := setting.GetAudioRatio(relayInfo.OriginModelName) + audioCompletionRatio := setting.GetAudioCompletionRatio(relayInfo.OriginModelName) modelRatio := priceData.ModelRatio groupRatio := priceData.GroupRatio diff --git a/service/token_counter.go b/service/token_counter.go index aa62bc6e..e868beb4 100644 --- a/service/token_counter.go +++ b/service/token_counter.go @@ -10,6 +10,7 @@ import ( "one-api/constant" "one-api/dto" relaycommon "one-api/relay/common" + "one-api/setting" "strings" "unicode/utf8" @@ -32,7 +33,7 @@ func InitTokenEncoders() { if err != nil { common.FatalLog(fmt.Sprintf("failed to get gpt-4o token encoder: %s", err.Error())) } - for model, _ := range common.GetDefaultModelRatioMap() { + for model, _ := range setting.GetDefaultModelRatioMap() { if strings.HasPrefix(model, "gpt-3.5") { tokenEncoderMap[model] = cl100TokenEncoder } else if strings.HasPrefix(model, "gpt-4") { diff --git a/common/model-ratio.go b/setting/model-ratio.go similarity index 97% rename from common/model-ratio.go rename to setting/model-ratio.go index 03681172..0606f107 100644 --- a/common/model-ratio.go +++ b/setting/model-ratio.go @@ -1,7 +1,8 @@ -package common +package setting import ( "encoding/json" + "one-api/common" "strings" "sync" ) @@ -261,7 +262,7 @@ func ModelPrice2JSONString() string { GetModelPriceMap() jsonBytes, err := json.Marshal(modelPriceMap) if err != nil { - SysError("error marshalling model price: " + err.Error()) + common.SysError("error marshalling model price: " + err.Error()) } return string(jsonBytes) } @@ -285,7 +286,7 @@ func GetModelPrice(name string, printErr bool) (float64, bool) { price, ok := modelPriceMap[name] if !ok { if printErr { - SysError("model price not found: " + name) + common.SysError("model price not found: " + name) } return -1, false } @@ -305,7 +306,7 @@ func ModelRatio2JSONString() string { GetModelRatioMap() jsonBytes, err := json.Marshal(modelRatioMap) if err != nil { - SysError("error marshalling model ratio: " + err.Error()) + common.SysError("error marshalling model ratio: " + err.Error()) } return string(jsonBytes) } @@ -324,8 +325,8 @@ func GetModelRatio(name string) (float64, bool) { } ratio, ok := modelRatioMap[name] if !ok { - SysError("model ratio not found: " + name) - return 37.5, false + common.SysError("model ratio not found: " + name) + return 37.5, SelfUseModeEnabled } return ratio, true } @@ -333,7 +334,7 @@ func GetModelRatio(name string) (float64, bool) { func DefaultModelRatio2JSONString() string { jsonBytes, err := json.Marshal(defaultModelRatio) if err != nil { - SysError("error marshalling model ratio: " + err.Error()) + common.SysError("error marshalling model ratio: " + err.Error()) } return string(jsonBytes) } @@ -355,7 +356,7 @@ func CompletionRatio2JSONString() string { GetCompletionRatioMap() jsonBytes, err := json.Marshal(CompletionRatio) if err != nil { - SysError("error marshalling completion ratio: " + err.Error()) + common.SysError("error marshalling completion ratio: " + err.Error()) } return string(jsonBytes) } diff --git a/setting/operation_setting.go b/setting/operation_setting.go index 4940d0fc..d4275168 100644 --- a/setting/operation_setting.go +++ b/setting/operation_setting.go @@ -3,6 +3,7 @@ package setting import "strings" var DemoSiteEnabled = false +var SelfUseModeEnabled = false var AutomaticDisableKeywords = []string{ "Your credit balance is too low", diff --git a/web/src/components/OperationSetting.js b/web/src/components/OperationSetting.js index 19f2dbe6..5c51c751 100644 --- a/web/src/components/OperationSetting.js +++ b/web/src/components/OperationSetting.js @@ -60,6 +60,7 @@ const OperationSetting = () => { RetryTimes: 0, Chats: "[]", DemoSiteEnabled: false, + SelfUseModeEnabled: false, AutomaticDisableKeywords: '', }); diff --git a/web/src/pages/Setting/Operation/SettingsGeneral.js b/web/src/pages/Setting/Operation/SettingsGeneral.js index 1c98d33e..e46e7db2 100644 --- a/web/src/pages/Setting/Operation/SettingsGeneral.js +++ b/web/src/pages/Setting/Operation/SettingsGeneral.js @@ -22,6 +22,7 @@ export default function GeneralSettings(props) { DisplayTokenStatEnabled: false, DefaultCollapseSidebar: false, DemoSiteEnabled: false, + SelfUseModeEnabled: false, }); const refForm = useRef(); const [inputsRow, setInputsRow] = useState(inputs); @@ -205,6 +206,22 @@ export default function GeneralSettings(props) { } /> + + + setInputs({ + ...inputs, + SelfUseModeEnabled: value + }) + } + /> +