فهرست منبع

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
[email protected] 10 ماه پیش
والد
کامیت
7dbb6b017c

+ 5 - 4
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 {

+ 2 - 3
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,

+ 9 - 6
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":

+ 4 - 3
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)

+ 7 - 3
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)

+ 4 - 4
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 {

+ 1 - 1
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

+ 2 - 2
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 {

+ 2 - 2
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 {

+ 10 - 10
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

+ 2 - 1
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") {

+ 9 - 8
common/model-ratio.go → 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)
 }

+ 1 - 0
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",

+ 1 - 0
web/src/components/OperationSetting.js

@@ -60,6 +60,7 @@ const OperationSetting = () => {
     RetryTimes: 0,
     Chats: "[]",
     DemoSiteEnabled: false,
+    SelfUseModeEnabled: false,
     AutomaticDisableKeywords: '',
   });
 

+ 17 - 0
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) {
                   }
                 />
               </Col>
+              <Col span={8}>
+                <Form.Switch
+                  field={'SelfUseModeEnabled'}
+                  label={t('自用模式')}
+                  extraText={t('开启后不限制:必须设置模型倍率')}
+                  size='default'
+                  checkedText='|'
+                  uncheckedText='〇'
+                  onChange={(value) =>
+                    setInputs({
+                      ...inputs,
+                      SelfUseModeEnabled: value
+                    })
+                  }
+                />
+              </Col>
             </Row>
             <Row>
               <Button size='default' onClick={onSubmit}>