feat: enable channel when test succeed (#771)
* 增加功能: 渠道 - 测试所有通道; 设置 - 运营设置 - 监控设置 - 成功时自动启用通道 * refactor: update implementation --------- Co-authored-by: liyujie <29959257@qq.com> Co-authored-by: JustSong <songquanpeng@foxmail.com>
This commit is contained in:
parent
8f5b83562b
commit
a3f80a3392
@ -78,6 +78,7 @@ var QuotaForInviter = 0
|
|||||||
var QuotaForInvitee = 0
|
var QuotaForInvitee = 0
|
||||||
var ChannelDisableThreshold = 5.0
|
var ChannelDisableThreshold = 5.0
|
||||||
var AutomaticDisableChannelEnabled = false
|
var AutomaticDisableChannelEnabled = false
|
||||||
|
var AutomaticEnableChannelEnabled = false
|
||||||
var QuotaRemindThreshold = 1000
|
var QuotaRemindThreshold = 1000
|
||||||
var PreConsumedQuota = 500
|
var PreConsumedQuota = 500
|
||||||
var ApproximateTokenEnabled = false
|
var ApproximateTokenEnabled = false
|
||||||
|
@ -81,6 +81,9 @@ func testChannel(channel *model.Channel, request ChatRequest) (err error, openai
|
|||||||
return fmt.Errorf("Error: %s\nResp body: %s", err, body), nil
|
return fmt.Errorf("Error: %s\nResp body: %s", err, body), nil
|
||||||
}
|
}
|
||||||
if response.Usage.CompletionTokens == 0 {
|
if response.Usage.CompletionTokens == 0 {
|
||||||
|
if response.Error.Message == "" {
|
||||||
|
response.Error.Message = "补全 tokens 非预期返回 0"
|
||||||
|
}
|
||||||
return errors.New(fmt.Sprintf("type %s, code %v, message %s", response.Error.Type, response.Error.Code, response.Error.Message)), &response.Error
|
return errors.New(fmt.Sprintf("type %s, code %v, message %s", response.Error.Type, response.Error.Code, response.Error.Message)), &response.Error
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@ -142,20 +145,32 @@ func TestChannel(c *gin.Context) {
|
|||||||
var testAllChannelsLock sync.Mutex
|
var testAllChannelsLock sync.Mutex
|
||||||
var testAllChannelsRunning bool = false
|
var testAllChannelsRunning bool = false
|
||||||
|
|
||||||
// disable & notify
|
func notifyRootUser(subject string, content string) {
|
||||||
func disableChannel(channelId int, channelName string, reason string) {
|
|
||||||
if common.RootUserEmail == "" {
|
if common.RootUserEmail == "" {
|
||||||
common.RootUserEmail = model.GetRootUserEmail()
|
common.RootUserEmail = model.GetRootUserEmail()
|
||||||
}
|
}
|
||||||
model.UpdateChannelStatusById(channelId, common.ChannelStatusAutoDisabled)
|
|
||||||
subject := fmt.Sprintf("通道「%s」(#%d)已被禁用", channelName, channelId)
|
|
||||||
content := fmt.Sprintf("通道「%s」(#%d)已被禁用,原因:%s", channelName, channelId, reason)
|
|
||||||
err := common.SendEmail(subject, common.RootUserEmail, content)
|
err := common.SendEmail(subject, common.RootUserEmail, content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.SysError(fmt.Sprintf("failed to send email: %s", err.Error()))
|
common.SysError(fmt.Sprintf("failed to send email: %s", err.Error()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// disable & notify
|
||||||
|
func disableChannel(channelId int, channelName string, reason string) {
|
||||||
|
model.UpdateChannelStatusById(channelId, common.ChannelStatusAutoDisabled)
|
||||||
|
subject := fmt.Sprintf("通道「%s」(#%d)已被禁用", channelName, channelId)
|
||||||
|
content := fmt.Sprintf("通道「%s」(#%d)已被禁用,原因:%s", channelName, channelId, reason)
|
||||||
|
notifyRootUser(subject, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
// enable & notify
|
||||||
|
func enableChannel(channelId int, channelName string) {
|
||||||
|
model.UpdateChannelStatusById(channelId, common.ChannelStatusEnabled)
|
||||||
|
subject := fmt.Sprintf("通道「%s」(#%d)已被启用", channelName, channelId)
|
||||||
|
content := fmt.Sprintf("通道「%s」(#%d)已被启用", channelName, channelId)
|
||||||
|
notifyRootUser(subject, content)
|
||||||
|
}
|
||||||
|
|
||||||
func testAllChannels(notify bool) error {
|
func testAllChannels(notify bool) error {
|
||||||
if common.RootUserEmail == "" {
|
if common.RootUserEmail == "" {
|
||||||
common.RootUserEmail = model.GetRootUserEmail()
|
common.RootUserEmail = model.GetRootUserEmail()
|
||||||
@ -178,20 +193,21 @@ func testAllChannels(notify bool) error {
|
|||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
for _, channel := range channels {
|
for _, channel := range channels {
|
||||||
if channel.Status != common.ChannelStatusEnabled {
|
isChannelEnabled := channel.Status == common.ChannelStatusEnabled
|
||||||
continue
|
|
||||||
}
|
|
||||||
tik := time.Now()
|
tik := time.Now()
|
||||||
err, openaiErr := testChannel(channel, *testRequest)
|
err, openaiErr := testChannel(channel, *testRequest)
|
||||||
tok := time.Now()
|
tok := time.Now()
|
||||||
milliseconds := tok.Sub(tik).Milliseconds()
|
milliseconds := tok.Sub(tik).Milliseconds()
|
||||||
if milliseconds > disableThreshold {
|
if isChannelEnabled && milliseconds > disableThreshold {
|
||||||
err = errors.New(fmt.Sprintf("响应时间 %.2fs 超过阈值 %.2fs", float64(milliseconds)/1000.0, float64(disableThreshold)/1000.0))
|
err = errors.New(fmt.Sprintf("响应时间 %.2fs 超过阈值 %.2fs", float64(milliseconds)/1000.0, float64(disableThreshold)/1000.0))
|
||||||
disableChannel(channel.Id, channel.Name, err.Error())
|
disableChannel(channel.Id, channel.Name, err.Error())
|
||||||
}
|
}
|
||||||
if shouldDisableChannel(openaiErr, -1) {
|
if isChannelEnabled && shouldDisableChannel(openaiErr, -1) {
|
||||||
disableChannel(channel.Id, channel.Name, err.Error())
|
disableChannel(channel.Id, channel.Name, err.Error())
|
||||||
}
|
}
|
||||||
|
if !isChannelEnabled && shouldEnableChannel(err, openaiErr) {
|
||||||
|
enableChannel(channel.Id, channel.Name)
|
||||||
|
}
|
||||||
channel.UpdateResponseTime(milliseconds)
|
channel.UpdateResponseTime(milliseconds)
|
||||||
time.Sleep(common.RequestInterval)
|
time.Sleep(common.RequestInterval)
|
||||||
}
|
}
|
||||||
|
@ -145,6 +145,19 @@ func shouldDisableChannel(err *OpenAIError, statusCode int) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func shouldEnableChannel(err error, openAIErr *OpenAIError) bool {
|
||||||
|
if !common.AutomaticEnableChannelEnabled {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if openAIErr != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func setEventStreamHeaders(c *gin.Context) {
|
func setEventStreamHeaders(c *gin.Context) {
|
||||||
c.Writer.Header().Set("Content-Type", "text/event-stream")
|
c.Writer.Header().Set("Content-Type", "text/event-stream")
|
||||||
c.Writer.Header().Set("Cache-Control", "no-cache")
|
c.Writer.Header().Set("Cache-Control", "no-cache")
|
||||||
|
@ -119,6 +119,7 @@
|
|||||||
" 年 ": " y ",
|
" 年 ": " y ",
|
||||||
"未测试": "Not tested",
|
"未测试": "Not tested",
|
||||||
"通道 ${name} 测试成功,耗时 ${time.toFixed(2)} 秒。": "Channel ${name} test succeeded, time consumed ${time.toFixed(2)} s.",
|
"通道 ${name} 测试成功,耗时 ${time.toFixed(2)} 秒。": "Channel ${name} test succeeded, time consumed ${time.toFixed(2)} s.",
|
||||||
|
"已成功开始测试所有通道,请刷新页面查看结果。": "All channels have been successfully tested, please refresh the page to view the results.",
|
||||||
"已成功开始测试所有已启用通道,请刷新页面查看结果。": "All enabled channels have been successfully tested, please refresh the page to view the results.",
|
"已成功开始测试所有已启用通道,请刷新页面查看结果。": "All enabled channels have been successfully tested, please refresh the page to view the results.",
|
||||||
"通道 ${name} 余额更新成功!": "Channel ${name} balance updated successfully!",
|
"通道 ${name} 余额更新成功!": "Channel ${name} balance updated successfully!",
|
||||||
"已更新完毕所有已启用通道余额!": "The balance of all enabled channels has been updated!",
|
"已更新完毕所有已启用通道余额!": "The balance of all enabled channels has been updated!",
|
||||||
@ -139,6 +140,7 @@
|
|||||||
"启用": "Enable",
|
"启用": "Enable",
|
||||||
"编辑": "Edit",
|
"编辑": "Edit",
|
||||||
"添加新的渠道": "Add a new channel",
|
"添加新的渠道": "Add a new channel",
|
||||||
|
"测试所有通道": "Test all channels",
|
||||||
"测试所有已启用通道": "Test all enabled channels",
|
"测试所有已启用通道": "Test all enabled channels",
|
||||||
"更新所有已启用通道余额": "Update the balance of all enabled channels",
|
"更新所有已启用通道余额": "Update the balance of all enabled channels",
|
||||||
"刷新": "Refresh",
|
"刷新": "Refresh",
|
||||||
|
@ -34,6 +34,7 @@ func InitOptionMap() {
|
|||||||
common.OptionMap["TurnstileCheckEnabled"] = strconv.FormatBool(common.TurnstileCheckEnabled)
|
common.OptionMap["TurnstileCheckEnabled"] = strconv.FormatBool(common.TurnstileCheckEnabled)
|
||||||
common.OptionMap["RegisterEnabled"] = strconv.FormatBool(common.RegisterEnabled)
|
common.OptionMap["RegisterEnabled"] = strconv.FormatBool(common.RegisterEnabled)
|
||||||
common.OptionMap["AutomaticDisableChannelEnabled"] = strconv.FormatBool(common.AutomaticDisableChannelEnabled)
|
common.OptionMap["AutomaticDisableChannelEnabled"] = strconv.FormatBool(common.AutomaticDisableChannelEnabled)
|
||||||
|
common.OptionMap["AutomaticEnableChannelEnabled"] = strconv.FormatBool(common.AutomaticEnableChannelEnabled)
|
||||||
common.OptionMap["ApproximateTokenEnabled"] = strconv.FormatBool(common.ApproximateTokenEnabled)
|
common.OptionMap["ApproximateTokenEnabled"] = strconv.FormatBool(common.ApproximateTokenEnabled)
|
||||||
common.OptionMap["LogConsumeEnabled"] = strconv.FormatBool(common.LogConsumeEnabled)
|
common.OptionMap["LogConsumeEnabled"] = strconv.FormatBool(common.LogConsumeEnabled)
|
||||||
common.OptionMap["DisplayInCurrencyEnabled"] = strconv.FormatBool(common.DisplayInCurrencyEnabled)
|
common.OptionMap["DisplayInCurrencyEnabled"] = strconv.FormatBool(common.DisplayInCurrencyEnabled)
|
||||||
@ -147,6 +148,8 @@ func updateOptionMap(key string, value string) (err error) {
|
|||||||
common.EmailDomainRestrictionEnabled = boolValue
|
common.EmailDomainRestrictionEnabled = boolValue
|
||||||
case "AutomaticDisableChannelEnabled":
|
case "AutomaticDisableChannelEnabled":
|
||||||
common.AutomaticDisableChannelEnabled = boolValue
|
common.AutomaticDisableChannelEnabled = boolValue
|
||||||
|
case "AutomaticEnableChannelEnabled":
|
||||||
|
common.AutomaticEnableChannelEnabled = boolValue
|
||||||
case "ApproximateTokenEnabled":
|
case "ApproximateTokenEnabled":
|
||||||
common.ApproximateTokenEnabled = boolValue
|
common.ApproximateTokenEnabled = boolValue
|
||||||
case "LogConsumeEnabled":
|
case "LogConsumeEnabled":
|
||||||
|
@ -234,7 +234,7 @@ const ChannelsTable = () => {
|
|||||||
const res = await API.get(`/api/channel/test`);
|
const res = await API.get(`/api/channel/test`);
|
||||||
const { success, message } = res.data;
|
const { success, message } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
showInfo('已成功开始测试所有已启用通道,请刷新页面查看结果。');
|
showInfo('已成功开始测试所有通道,请刷新页面查看结果。');
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ const OperationSetting = () => {
|
|||||||
ChatLink: '',
|
ChatLink: '',
|
||||||
QuotaPerUnit: 0,
|
QuotaPerUnit: 0,
|
||||||
AutomaticDisableChannelEnabled: '',
|
AutomaticDisableChannelEnabled: '',
|
||||||
|
AutomaticEnableChannelEnabled: '',
|
||||||
ChannelDisableThreshold: 0,
|
ChannelDisableThreshold: 0,
|
||||||
LogConsumeEnabled: '',
|
LogConsumeEnabled: '',
|
||||||
DisplayInCurrencyEnabled: '',
|
DisplayInCurrencyEnabled: '',
|
||||||
@ -269,6 +270,12 @@ const OperationSetting = () => {
|
|||||||
name='AutomaticDisableChannelEnabled'
|
name='AutomaticDisableChannelEnabled'
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
|
<Form.Checkbox
|
||||||
|
checked={inputs.AutomaticEnableChannelEnabled === 'true'}
|
||||||
|
label='成功时自动启用通道'
|
||||||
|
name='AutomaticEnableChannelEnabled'
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
<Form.Button onClick={() => {
|
<Form.Button onClick={() => {
|
||||||
submitConfig('monitor').then();
|
submitConfig('monitor').then();
|
||||||
|
Loading…
Reference in New Issue
Block a user