Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
1bbabda0
authored
Sep 03, 2025
by
CaIon
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat(monitor_setting): implement automatic channel testing configuration
parent
22b724ca
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
102 additions
and
98 deletions
+102
-98
controller/channel-test.go
+18
-6
main.go
+3
-7
relay/common/relay_utils.go
+2
-4
service/file_decoder.go
+3
-0
service/token_counter.go
+3
-77
setting/operation_setting/monitor_setting.go
+34
-0
web/src/components/settings/OperationSetting.jsx
+3
-4
web/src/pages/Setting/Operation/SettingsMonitoring.jsx
+36
-0
No files found.
controller/channel-test.go
View file @
1bbabda0
...
@@ -20,6 +20,7 @@ import (
...
@@ -20,6 +20,7 @@ import (
relayconstant
"one-api/relay/constant"
relayconstant
"one-api/relay/constant"
"one-api/relay/helper"
"one-api/relay/helper"
"one-api/service"
"one-api/service"
"one-api/setting/operation_setting"
"one-api/types"
"one-api/types"
"strconv"
"strconv"
"strings"
"strings"
...
@@ -477,15 +478,26 @@ func TestAllChannels(c *gin.Context) {
...
@@ -477,15 +478,26 @@ func TestAllChannels(c *gin.Context) {
return
return
}
}
func
AutomaticallyTestChannels
(
frequency
int
)
{
var
autoTestChannelsOnce
sync
.
Once
if
frequency
<=
0
{
common
.
SysLog
(
"CHANNEL_TEST_FREQUENCY is not set or invalid, skipping automatic channel test"
)
func
AutomaticallyTestChannels
()
{
return
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
{
for
{
time
.
Sleep
(
time
.
Duration
(
frequency
)
*
time
.
Minute
)
time
.
Sleep
(
time
.
Duration
(
frequency
)
*
time
.
Minute
)
common
.
SysLog
(
"
testing all channels"
)
common
.
SysLog
(
"automatically
testing all channels"
)
_
=
testAllChannels
(
false
)
_
=
testAllChannels
(
false
)
common
.
SysLog
(
"channel test finished"
)
common
.
SysLog
(
"automatically channel test finished"
)
if
!
operation_setting
.
GetMonitorSetting
()
.
AutoTestChannelEnabled
{
break
}
}
}
}
})
}
}
main.go
View file @
1bbabda0
...
@@ -94,13 +94,9 @@ func main() {
...
@@ -94,13 +94,9 @@ func main() {
}
}
go
controller
.
AutomaticallyUpdateChannels
(
frequency
)
go
controller
.
AutomaticallyUpdateChannels
(
frequency
)
}
}
if
os
.
Getenv
(
"CHANNEL_TEST_FREQUENCY"
)
!=
""
{
frequency
,
err
:=
strconv
.
Atoi
(
os
.
Getenv
(
"CHANNEL_TEST_FREQUENCY"
))
go
controller
.
AutomaticallyTestChannels
()
if
err
!=
nil
{
common
.
FatalLog
(
"failed to parse CHANNEL_TEST_FREQUENCY: "
+
err
.
Error
())
}
go
controller
.
AutomaticallyTestChannels
(
frequency
)
}
if
common
.
IsMasterNode
&&
constant
.
UpdateTask
{
if
common
.
IsMasterNode
&&
constant
.
UpdateTask
{
gopool
.
Go
(
func
()
{
gopool
.
Go
(
func
()
{
controller
.
UpdateMidjourneyTaskBulk
()
controller
.
UpdateMidjourneyTaskBulk
()
...
...
relay/common/relay_utils.go
View file @
1bbabda0
...
@@ -2,12 +2,10 @@ package common
...
@@ -2,12 +2,10 @@ package common
import
(
import
(
"fmt"
"fmt"
"github.com/gin-gonic/gin"
_
"image/gif"
_
"image/jpeg"
_
"image/png"
"one-api/constant"
"one-api/constant"
"strings"
"strings"
"github.com/gin-gonic/gin"
)
)
func
GetFullRequestURL
(
baseURL
string
,
requestURL
string
,
channelType
int
)
string
{
func
GetFullRequestURL
(
baseURL
string
,
requestURL
string
,
channelType
int
)
string
{
...
...
service/file_decoder.go
View file @
1bbabda0
...
@@ -5,6 +5,9 @@ import (
...
@@ -5,6 +5,9 @@ import (
"encoding/base64"
"encoding/base64"
"fmt"
"fmt"
"image"
"image"
_
"image/gif"
_
"image/jpeg"
_
"image/png"
"io"
"io"
"net/http"
"net/http"
"one-api/common"
"one-api/common"
...
...
service/token_counter.go
View file @
1bbabda0
...
@@ -5,6 +5,9 @@ import (
...
@@ -5,6 +5,9 @@ import (
"errors"
"errors"
"fmt"
"fmt"
"image"
"image"
_
"image/gif"
_
"image/jpeg"
_
"image/png"
"log"
"log"
"math"
"math"
"one-api/common"
"one-api/common"
...
@@ -357,33 +360,6 @@ func CountRequestToken(c *gin.Context, meta *types.TokenCountMeta, info *relayco
...
@@ -357,33 +360,6 @@ func CountRequestToken(c *gin.Context, meta *types.TokenCountMeta, info *relayco
return
tkm
,
nil
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
)
{
func
CountTokenClaudeRequest
(
request
dto
.
ClaudeRequest
,
model
string
)
(
int
,
error
)
{
tkm
:=
0
tkm
:=
0
...
@@ -543,56 +519,6 @@ func CountTokenRealtime(info *relaycommon.RelayInfo, request dto.RealtimeEvent,
...
@@ -543,56 +519,6 @@ func CountTokenRealtime(info *relaycommon.RelayInfo, request dto.RealtimeEvent,
return
textToken
,
audioToken
,
nil
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
{
func
CountTokenInput
(
input
any
,
model
string
)
int
{
switch
v
:=
input
.
(
type
)
{
switch
v
:=
input
.
(
type
)
{
case
string
:
case
string
:
...
...
setting/operation_setting/monitor_setting.go
0 → 100644
View file @
1bbabda0
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
}
web/src/components/settings/OperationSetting.jsx
View file @
1bbabda0
...
@@ -68,6 +68,8 @@ const OperationSetting = () => {
...
@@ -68,6 +68,8 @@ const OperationSetting = () => {
AutomaticDisableChannelEnabled
:
false
,
AutomaticDisableChannelEnabled
:
false
,
AutomaticEnableChannelEnabled
:
false
,
AutomaticEnableChannelEnabled
:
false
,
AutomaticDisableKeywords
:
''
,
AutomaticDisableKeywords
:
''
,
'monitor_setting.auto_test_channel_enabled'
:
false
,
'monitor_setting.auto_test_channel_minutes'
:
10
,
});
});
let
[
loading
,
setLoading
]
=
useState
(
false
);
let
[
loading
,
setLoading
]
=
useState
(
false
);
...
@@ -78,10 +80,7 @@ const OperationSetting = () => {
...
@@ -78,10 +80,7 @@ const OperationSetting = () => {
if
(
success
)
{
if
(
success
)
{
let
newInputs
=
{};
let
newInputs
=
{};
data
.
forEach
((
item
)
=>
{
data
.
forEach
((
item
)
=>
{
if
(
if
(
typeof
inputs
[
item
.
key
]
===
'boolean'
)
{
item
.
key
.
endsWith
(
'Enabled'
)
||
[
'DefaultCollapseSidebar'
].
includes
(
item
.
key
)
)
{
newInputs
[
item
.
key
]
=
toBoolean
(
item
.
value
);
newInputs
[
item
.
key
]
=
toBoolean
(
item
.
value
);
}
else
{
}
else
{
newInputs
[
item
.
key
]
=
item
.
value
;
newInputs
[
item
.
key
]
=
item
.
value
;
...
...
web/src/pages/Setting/Operation/SettingsMonitoring.jsx
View file @
1bbabda0
...
@@ -38,6 +38,8 @@ export default function SettingsMonitoring(props) {
...
@@ -38,6 +38,8 @@ export default function SettingsMonitoring(props) {
AutomaticDisableChannelEnabled
:
false
,
AutomaticDisableChannelEnabled
:
false
,
AutomaticEnableChannelEnabled
:
false
,
AutomaticEnableChannelEnabled
:
false
,
AutomaticDisableKeywords
:
''
,
AutomaticDisableKeywords
:
''
,
'monitor_setting.auto_test_channel_enabled'
:
false
,
'monitor_setting.auto_test_channel_minutes'
:
10
,
});
});
const
refForm
=
useRef
();
const
refForm
=
useRef
();
const
[
inputsRow
,
setInputsRow
]
=
useState
(
inputs
);
const
[
inputsRow
,
setInputsRow
]
=
useState
(
inputs
);
...
@@ -100,6 +102,40 @@ export default function SettingsMonitoring(props) {
...
@@ -100,6 +102,40 @@ export default function SettingsMonitoring(props) {
<
Form
.
Section
text=
{
t
(
'监控设置'
)
}
>
<
Form
.
Section
text=
{
t
(
'监控设置'
)
}
>
<
Row
gutter=
{
16
}
>
<
Row
gutter=
{
16
}
>
<
Col
xs=
{
24
}
sm=
{
12
}
md=
{
8
}
lg=
{
8
}
xl=
{
8
}
>
<
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
<
Form
.
InputNumber
label=
{
t
(
'测试所有渠道的最长响应时间'
)
}
label=
{
t
(
'测试所有渠道的最长响应时间'
)
}
step=
{
1
}
step=
{
1
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment