Ver Fonte

feat(monitor_setting): implement automatic channel testing configuration

CaIon há 6 meses atrás
pai
commit
56fc3441da

+ 23 - 11
controller/channel-test.go

@@ -20,6 +20,7 @@ import (
 	relayconstant "one-api/relay/constant"
 	"one-api/relay/helper"
 	"one-api/service"
+	"one-api/setting/operation_setting"
 	"one-api/types"
 	"strconv"
 	"strings"
@@ -477,15 +478,26 @@ func TestAllChannels(c *gin.Context) {
 	return
 }
 
-func AutomaticallyTestChannels(frequency int) {
-	if frequency <= 0 {
-		common.SysLog("CHANNEL_TEST_FREQUENCY is not set or invalid, skipping automatic channel test")
-		return
-	}
-	for {
-		time.Sleep(time.Duration(frequency) * time.Minute)
-		common.SysLog("testing all channels")
-		_ = testAllChannels(false)
-		common.SysLog("channel test finished")
-	}
+var autoTestChannelsOnce sync.Once
+
+func AutomaticallyTestChannels() {
+	autoTestChannelsOnce.Do(func() {
+		for {
+			if !operation_setting.GetMonitorSetting().AutoTestChannelEnabled {
+				time.Sleep(10 * time.Minute)
+				continue
+			}
+			frequency := operation_setting.GetMonitorSetting().AutoTestChannelMinutes
+			common.SysLog(fmt.Sprintf("automatically test channels with interval %d minutes", frequency))
+			for {
+				time.Sleep(time.Duration(frequency) * time.Minute)
+				common.SysLog("automatically testing all channels")
+				_ = testAllChannels(false)
+				common.SysLog("automatically channel test finished")
+				if !operation_setting.GetMonitorSetting().AutoTestChannelEnabled {
+					break
+				}
+			}
+		}
+	})
 }

+ 3 - 7
main.go

@@ -94,13 +94,9 @@ func main() {
 		}
 		go controller.AutomaticallyUpdateChannels(frequency)
 	}
-	if os.Getenv("CHANNEL_TEST_FREQUENCY") != "" {
-		frequency, err := strconv.Atoi(os.Getenv("CHANNEL_TEST_FREQUENCY"))
-		if err != nil {
-			common.FatalLog("failed to parse CHANNEL_TEST_FREQUENCY: " + err.Error())
-		}
-		go controller.AutomaticallyTestChannels(frequency)
-	}
+
+	go controller.AutomaticallyTestChannels()
+
 	if common.IsMasterNode && constant.UpdateTask {
 		gopool.Go(func() {
 			controller.UpdateMidjourneyTaskBulk()

+ 2 - 4
relay/common/relay_utils.go

@@ -2,12 +2,10 @@ package common
 
 import (
 	"fmt"
-	"github.com/gin-gonic/gin"
-	_ "image/gif"
-	_ "image/jpeg"
-	_ "image/png"
 	"one-api/constant"
 	"strings"
+
+	"github.com/gin-gonic/gin"
 )
 
 func GetFullRequestURL(baseURL string, requestURL string, channelType int) string {

+ 3 - 0
service/file_decoder.go

@@ -5,6 +5,9 @@ import (
 	"encoding/base64"
 	"fmt"
 	"image"
+	_ "image/gif"
+	_ "image/jpeg"
+	_ "image/png"
 	"io"
 	"net/http"
 	"one-api/common"

+ 3 - 77
service/token_counter.go

@@ -5,6 +5,9 @@ import (
 	"errors"
 	"fmt"
 	"image"
+	_ "image/gif"
+	_ "image/jpeg"
+	_ "image/png"
 	"log"
 	"math"
 	"one-api/common"
@@ -357,33 +360,6 @@ func CountRequestToken(c *gin.Context, meta *types.TokenCountMeta, info *relayco
 	return tkm, nil
 }
 
-//func CountTokenChatRequest(info *relaycommon.RelayInfo, request dto.GeneralOpenAIRequest) (int, error) {
-//	tkm := 0
-//	msgTokens, err := CountTokenMessages(info, request.Messages, request.Model, request.Stream)
-//	if err != nil {
-//		return 0, err
-//	}
-//	tkm += msgTokens
-//	if request.Tools != nil {
-//		openaiTools := request.Tools
-//		countStr := ""
-//		for _, tool := range openaiTools {
-//			countStr = tool.Function.Name
-//			if tool.Function.Description != "" {
-//				countStr += tool.Function.Description
-//			}
-//			if tool.Function.Parameters != nil {
-//				countStr += fmt.Sprintf("%v", tool.Function.Parameters)
-//			}
-//		}
-//		toolTokens := CountTokenInput(countStr, request.Model)
-//		tkm += 8
-//		tkm += toolTokens
-//	}
-//
-//	return tkm, nil
-//}
-
 func CountTokenClaudeRequest(request dto.ClaudeRequest, model string) (int, error) {
 	tkm := 0
 
@@ -543,56 +519,6 @@ func CountTokenRealtime(info *relaycommon.RelayInfo, request dto.RealtimeEvent,
 	return textToken, audioToken, nil
 }
 
-//func CountTokenMessages(info *relaycommon.RelayInfo, messages []dto.Message, model string, stream bool) (int, error) {
-//	//recover when panic
-//	tokenEncoder := getTokenEncoder(model)
-//	// Reference:
-//	// https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb
-//	// https://github.com/pkoukk/tiktoken-go/issues/6
-//	//
-//	// Every message follows <|start|>{role/name}\n{content}<|end|>\n
-//	var tokensPerMessage int
-//	var tokensPerName int
-//
-//	tokensPerMessage = 3
-//	tokensPerName = 1
-//
-//	tokenNum := 0
-//	for _, message := range messages {
-//		tokenNum += tokensPerMessage
-//		tokenNum += getTokenNum(tokenEncoder, message.Role)
-//		if message.Content != nil {
-//			if message.Name != nil {
-//				tokenNum += tokensPerName
-//				tokenNum += getTokenNum(tokenEncoder, *message.Name)
-//			}
-//			arrayContent := message.ParseContent()
-//			for _, m := range arrayContent {
-//				if m.Type == dto.ContentTypeImageURL {
-//					imageUrl := m.GetImageMedia()
-//					imageTokenNum, err := getImageToken(info, imageUrl, model, stream)
-//					if err != nil {
-//						return 0, err
-//					}
-//					tokenNum += imageTokenNum
-//					log.Printf("image token num: %d", imageTokenNum)
-//				} else if m.Type == dto.ContentTypeInputAudio {
-//					// TODO: 音频token数量计算
-//					tokenNum += 100
-//				} else if m.Type == dto.ContentTypeFile {
-//					tokenNum += 5000
-//				} else if m.Type == dto.ContentTypeVideoUrl {
-//					tokenNum += 5000
-//				} else {
-//					tokenNum += getTokenNum(tokenEncoder, m.Text)
-//				}
-//			}
-//		}
-//	}
-//	tokenNum += 3 // Every reply is primed with <|start|>assistant<|message|>
-//	return tokenNum, nil
-//}
-
 func CountTokenInput(input any, model string) int {
 	switch v := input.(type) {
 	case string:

+ 34 - 0
setting/operation_setting/monitor_setting.go

@@ -0,0 +1,34 @@
+package operation_setting
+
+import (
+	"one-api/setting/config"
+	"os"
+	"strconv"
+)
+
+type MonitorSetting struct {
+	AutoTestChannelEnabled bool `json:"auto_test_channel_enabled"`
+	AutoTestChannelMinutes int  `json:"auto_test_channel_minutes"`
+}
+
+// 默认配置
+var monitorSetting = MonitorSetting{
+	AutoTestChannelEnabled: false,
+	AutoTestChannelMinutes: 10,
+}
+
+func init() {
+	// 注册到全局配置管理器
+	config.GlobalConfig.Register("monitor_setting", &monitorSetting)
+}
+
+func GetMonitorSetting() *MonitorSetting {
+	if os.Getenv("CHANNEL_TEST_FREQUENCY") != "" {
+		frequency, err := strconv.Atoi(os.Getenv("CHANNEL_TEST_FREQUENCY"))
+		if err == nil && frequency > 0 {
+			monitorSetting.AutoTestChannelEnabled = true
+			monitorSetting.AutoTestChannelMinutes = frequency
+		}
+	}
+	return &monitorSetting
+}

+ 3 - 4
web/src/components/settings/OperationSetting.jsx

@@ -68,6 +68,8 @@ const OperationSetting = () => {
     AutomaticDisableChannelEnabled: false,
     AutomaticEnableChannelEnabled: false,
     AutomaticDisableKeywords: '',
+    'monitor_setting.auto_test_channel_enabled': false,
+    'monitor_setting.auto_test_channel_minutes': 10,
   });
 
   let [loading, setLoading] = useState(false);
@@ -78,10 +80,7 @@ const OperationSetting = () => {
     if (success) {
       let newInputs = {};
       data.forEach((item) => {
-        if (
-          item.key.endsWith('Enabled') ||
-          ['DefaultCollapseSidebar'].includes(item.key)
-        ) {
+        if (typeof inputs[item.key] === 'boolean') {
           newInputs[item.key] = toBoolean(item.value);
         } else {
           newInputs[item.key] = item.value;

+ 36 - 0
web/src/pages/Setting/Operation/SettingsMonitoring.jsx

@@ -38,6 +38,8 @@ export default function SettingsMonitoring(props) {
     AutomaticDisableChannelEnabled: false,
     AutomaticEnableChannelEnabled: false,
     AutomaticDisableKeywords: '',
+    'monitor_setting.auto_test_channel_enabled': false,
+    'monitor_setting.auto_test_channel_minutes': 10,
   });
   const refForm = useRef();
   const [inputsRow, setInputsRow] = useState(inputs);
@@ -98,6 +100,40 @@ export default function SettingsMonitoring(props) {
           style={{ marginBottom: 15 }}
         >
           <Form.Section text={t('监控设置')}>
+            <Row gutter={16}>
+              <Col xs={24} sm={12} md={8} lg={8} xl={8}>
+                <Form.Switch
+                  field={'monitor_setting.auto_test_channel_enabled'}
+                  label={t('定时测试所有通道')}
+                  size='default'
+                  checkedText='|'
+                  uncheckedText='〇'
+                  onChange={(value) =>
+                    setInputs({
+                      ...inputs,
+                      'monitor_setting.auto_test_channel_enabled': value,
+                    })
+                  }
+                />
+              </Col>
+              <Col xs={24} sm={12} md={8} lg={8} xl={8}>
+                <Form.InputNumber
+                  label={t('自动测试所有通道间隔时间')}
+                  step={1}
+                  min={1}
+                  suffix={t('分钟')}
+                  extraText={t('每隔多少分钟测试一次所有通道')}
+                  placeholder={''}
+                  field={'monitor_setting.auto_test_channel_minutes'}
+                  onChange={(value) =>
+                    setInputs({
+                      ...inputs,
+                      'monitor_setting.auto_test_channel_minutes': parseInt(value),
+                    })
+                  }
+                />
+              </Col>
+            </Row>
             <Row gutter={16}>
               <Col xs={24} sm={12} md={8} lg={8} xl={8}>
                 <Form.InputNumber