️ improve: Allows selection of test models (#121)

This commit is contained in:
Martial BE 2024-03-25 14:57:30 +08:00
parent 376d54f56a
commit 646cb74154
No known key found for this signature in database
GPG Key ID: D06C32DF0EDB9084
3 changed files with 121 additions and 28 deletions

View File

@ -18,7 +18,7 @@ import (
"github.com/gin-gonic/gin"
)
func testChannel(channel *model.Channel, request types.ChatCompletionRequest) (err error, openaiErr *types.OpenAIError) {
func testChannel(channel *model.Channel, testModel string) (err error, openaiErr *types.OpenAIError) {
if channel.TestModel == "" {
return errors.New("请填写测速模型后再试"), nil
}
@ -33,7 +33,13 @@ func testChannel(channel *model.Channel, request types.ChatCompletionRequest) (e
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request = req
request.Model = channel.TestModel
request := buildTestRequest()
if testModel != "" {
request.Model = testModel
} else {
request.Model = channel.TestModel
}
provider := providers.GetProvider(channel, c)
if provider == nil {
@ -54,21 +60,15 @@ func testChannel(channel *model.Channel, request types.ChatCompletionRequest) (e
chatProvider.SetUsage(&types.Usage{})
response, openAIErrorWithStatusCode := chatProvider.CreateChatCompletion(&request)
response, openAIErrorWithStatusCode := chatProvider.CreateChatCompletion(request)
if openAIErrorWithStatusCode != nil {
return errors.New(openAIErrorWithStatusCode.Message), &openAIErrorWithStatusCode.OpenAIError
}
usage := chatProvider.GetUsage()
if usage.CompletionTokens == 0 {
return fmt.Errorf("channel %s, message 补全 tokens 非预期返回 0", channel.Name), nil
}
// 转换为JSON字符串
jsonBytes, _ := json.Marshal(response)
common.SysLog(fmt.Sprintf("测试模型 %s 返回内容为:%s", channel.Name, string(jsonBytes)))
common.SysLog(fmt.Sprintf("测试渠道 %s : %s 返回内容为:%s", channel.Name, request.Model, string(jsonBytes)))
return nil, nil
}
@ -81,9 +81,9 @@ func buildTestRequest() *types.ChatCompletionRequest {
Content: "You just need to output 'hi' next.",
},
},
Model: "",
// MaxTokens: 1,
Stream: false,
Model: "",
MaxTokens: 2,
Stream: false,
}
return testRequest
}
@ -105,9 +105,9 @@ func TestChannel(c *gin.Context) {
})
return
}
testRequest := buildTestRequest()
testModel := c.Query("model")
tik := time.Now()
err, _ = testChannel(channel, *testRequest)
err, _ = testChannel(channel, testModel)
tok := time.Now()
milliseconds := tok.Sub(tik).Milliseconds()
go channel.UpdateResponseTime(milliseconds)
@ -163,7 +163,6 @@ func testAllChannels(notify bool) error {
if err != nil {
return err
}
testRequest := buildTestRequest()
var disableThreshold = int64(common.ChannelDisableThreshold * 1000)
if disableThreshold == 0 {
disableThreshold = 10000000 // a impossible value
@ -172,7 +171,7 @@ func testAllChannels(notify bool) error {
for _, channel := range channels {
isChannelEnabled := channel.Status == common.ChannelStatusEnabled
tik := time.Now()
err, openaiErr := testChannel(channel, *testRequest)
err, openaiErr := testChannel(channel, "")
tok := time.Now()
milliseconds := tok.Sub(tik).Milliseconds()
if milliseconds > disableThreshold {

View File

@ -22,6 +22,8 @@ import {
Collapse,
Typography,
TextField,
Stack,
Menu,
Box
} from '@mui/material';
@ -32,12 +34,51 @@ import ResponseTimeLabel from './ResponseTimeLabel';
import GroupLabel from './GroupLabel';
import { IconDotsVertical, IconEdit, IconTrash, IconCopy, IconWorldWww } from '@tabler/icons-react';
import { styled, alpha } from '@mui/material/styles';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
import { copy } from 'utils/common';
const StyledMenu = styled((props) => (
<Menu
elevation={0}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'right'
}}
transformOrigin={{
vertical: 'top',
horizontal: 'right'
}}
{...props}
/>
))(({ theme }) => ({
'& .MuiPaper-root': {
borderRadius: 6,
marginTop: theme.spacing(1),
minWidth: 180,
color: theme.palette.mode === 'light' ? 'rgb(55, 65, 81)' : theme.palette.grey[300],
boxShadow:
'rgb(255, 255, 255) 0px 0px 0px 0px, rgba(0, 0, 0, 0.05) 0px 0px 0px 1px, rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px',
'& .MuiMenu-list': {
padding: '4px 0'
},
'& .MuiMenuItem-root': {
'& .MuiSvgIcon-root': {
fontSize: 18,
color: theme.palette.text.secondary,
marginRight: theme.spacing(1.5)
},
'&:active': {
backgroundColor: alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity)
}
}
}
}));
export default function ChannelTableRow({ item, manageChannel, handleOpenModal, setModalChannelId }) {
const [open, setOpen] = useState(null);
const [openTest, setOpenTest] = useState(false);
const [openDelete, setOpenDelete] = useState(false);
const [statusSwitch, setStatusSwitch] = useState(item.status);
const [priorityValve, setPriority] = useState(item.priority);
@ -63,6 +104,10 @@ export default function ChannelTableRow({ item, manageChannel, handleOpenModal,
setOpen(event.currentTarget);
};
const handleTestModel = (event) => {
setOpenTest(event.currentTarget);
};
const handleCloseMenu = () => {
setOpen(null);
};
@ -105,11 +150,21 @@ export default function ChannelTableRow({ item, manageChannel, handleOpenModal,
setWeight(currentValue);
};
const handleResponseTime = async () => {
const { success, time } = await manageChannel(item.id, 'test', '');
const handleResponseTime = async (modelName) => {
setOpenTest(null);
if (typeof modelName !== 'string') {
modelName = item.test_model;
}
if (modelName == '') {
showError('请先设置测试模型');
return;
}
const { success, time } = await manageChannel(item.id, 'test', modelName);
if (success) {
setResponseTimeData({ test_time: Date.now() / 1000, response_time: time * 1000 });
showInfo(`通道 ${item.name} 测试成功,耗时 ${time.toFixed(2)} 秒。`);
showInfo(`通道 ${item.name}: ${modelName} 测试成功,耗时 ${time.toFixed(2)} 秒。`);
}
};
@ -202,9 +257,24 @@ export default function ChannelTableRow({ item, manageChannel, handleOpenModal,
</TableCell>
<TableCell>
<IconButton onClick={handleOpenMenu} sx={{ color: 'rgb(99, 115, 129)' }}>
<IconDotsVertical />
</IconButton>
<Stack direction="row" spacing={1}>
<Button
id="test-model-button"
aria-controls={openTest ? 'test-model-menu' : undefined}
aria-haspopup="true"
aria-expanded={openTest ? 'true' : undefined}
variant="outlined"
disableElevation
onClick={handleTestModel}
endIcon={<KeyboardArrowDownIcon />}
>
测试
</Button>
<IconButton onClick={handleOpenMenu} sx={{ color: 'rgb(99, 115, 129)' }}>
<IconDotsVertical />
</IconButton>
</Stack>
</TableCell>
</TableRow>
@ -256,6 +326,28 @@ export default function ChannelTableRow({ item, manageChannel, handleOpenModal,
</MenuItem>
</Popover>
<StyledMenu
id="test-model-menu"
MenuListProps={{
'aria-labelledby': 'test-model-button'
}}
anchorEl={openTest}
open={!!openTest}
onClose={() => {
setOpenTest(null);
}}
>
{modelMap.map((model) => (
<MenuItem
key={'test_model-' + model}
onClick={() => {
handleResponseTime(model);
}}
>
{model}
</MenuItem>
))}
</StyledMenu>
<TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0, textAlign: 'left' }} colSpan={10}>
<Collapse in={openRow} timeout="auto" unmountOnExit>

View File

@ -158,7 +158,9 @@ export default function ChannelPage() {
});
break;
case 'test':
res = await API.get(url + `test/${id}`);
res = await API.get(url + `test/${id}`, {
params: { model: value }
});
break;
}
const { success, message } = res.data;
@ -377,16 +379,16 @@ export default function ChannelPage() {
orderBy={orderBy}
onRequestSort={handleSort}
headLabel={[
{ id: 'collapse', label: '', disableSort: true },
{ id: 'id', label: 'ID', disableSort: false },
{ id: 'collapse', label: '', disableSort: true, width: '50px' },
{ id: 'id', label: 'ID', disableSort: false, width: '80px' },
{ id: 'name', label: '名称', disableSort: false },
{ id: 'group', label: '分组', disableSort: true },
{ id: 'type', label: '类型', disableSort: false },
{ id: 'status', label: '状态', disableSort: false },
{ id: 'response_time', label: '响应时间', disableSort: false },
{ id: 'balance', label: '余额', disableSort: false },
{ id: 'priority', label: '优先级', disableSort: false },
{ id: 'weight', label: '权重', disableSort: false },
{ id: 'priority', label: '优先级', disableSort: false, width: '80px' },
{ id: 'weight', label: '权重', disableSort: false, width: '80px' },
{ id: 'action', label: '操作', disableSort: true }
]}
/>