Merge remote-tracking branch 'upstream/main'

This commit is contained in:
Laisky.Cai
2023-10-19 09:10:23 +00:00
32 changed files with 651 additions and 139 deletions

View File

@@ -283,7 +283,9 @@ function App() {
</Suspense>
}
/>
<Route path='*' element={NotFound} />
<Route path='*' element={
<NotFound />
} />
</Routes>
);
}

View File

@@ -1,7 +1,7 @@
import React, { useEffect, useState } from 'react';
import {Button, Form, Input, Label, Pagination, Popup, Table} from 'semantic-ui-react';
import { Button, Form, Input, Label, Message, Pagination, Popup, Table } from 'semantic-ui-react';
import { Link } from 'react-router-dom';
import { API, showError, showInfo, showNotice, showSuccess, timestamp2string } from '../helpers';
import { API, setPromptShown, shouldShowPrompt, showError, showInfo, showSuccess, timestamp2string } from '../helpers';
import { CHANNEL_OPTIONS, ITEMS_PER_PAGE } from '../constants';
import { renderGroup, renderNumber } from '../helpers/render';
@@ -55,6 +55,7 @@ const ChannelsTable = () => {
const [searchKeyword, setSearchKeyword] = useState('');
const [searching, setSearching] = useState(false);
const [updatingBalance, setUpdatingBalance] = useState(false);
const [showPrompt, setShowPrompt] = useState(shouldShowPrompt("channel-test"));
const loadChannels = async (startIdx) => {
const res = await API.get(`/api/channel/?p=${startIdx}`);
@@ -96,7 +97,7 @@ const ChannelsTable = () => {
});
}, []);
const manageChannel = async (id, action, idx, priority) => {
const manageChannel = async (id, action, idx, value) => {
let data = { id };
let res;
switch (action) {
@@ -112,10 +113,20 @@ const ChannelsTable = () => {
res = await API.put('/api/channel/', data);
break;
case 'priority':
if (priority === '') {
if (value === '') {
return;
}
data.priority = parseInt(priority);
data.priority = parseInt(value);
res = await API.put('/api/channel/', data);
break;
case 'weight':
if (value === '') {
return;
}
data.weight = parseInt(value);
if (data.weight < 0) {
data.weight = 0;
}
res = await API.put('/api/channel/', data);
break;
}
@@ -142,9 +153,23 @@ const ChannelsTable = () => {
return <Label basic color='green'>已启用</Label>;
case 2:
return (
<Label basic color='red'>
已禁用
</Label>
<Popup
trigger={<Label basic color='red'>
已禁用
</Label>}
content='本渠道被手动禁用'
basic
/>
);
case 3:
return (
<Popup
trigger={<Label basic color='yellow'>
已禁用
</Label>}
content='本渠道被程序自动禁用'
basic
/>
);
default:
return (
@@ -202,7 +227,6 @@ const ChannelsTable = () => {
showInfo(`通道 ${name} 测试成功,耗时 ${time.toFixed(2)} 秒。`);
} else {
showError(message);
showNotice("当前版本测试是通过按照 OpenAI API 格式使用 gpt-3.5-turbo 模型进行非流式请求实现的,因此测试报错并不一定代表通道不可用,该功能后续会修复。")
}
};
@@ -216,6 +240,17 @@ const ChannelsTable = () => {
}
};
const deleteAllDisabledChannels = async () => {
const res = await API.delete(`/api/channel/disabled`);
const { success, message, data } = res.data;
if (success) {
showSuccess(`已删除所有禁用渠道,共计 ${data}`);
await refresh();
} else {
showError(message);
}
};
const updateChannelBalance = async (id, name, idx) => {
const res = await API.get(`/api/channel/update_balance/${id}/`);
const { success, message, balance } = res.data;
@@ -282,7 +317,19 @@ const ChannelsTable = () => {
onChange={handleKeywordChange}
/>
</Form>
{
showPrompt && (
<Message onDismiss={() => {
setShowPrompt(false);
setPromptShown("channel-test");
}}>
当前版本测试是通过按照 OpenAI API 格式使用 gpt-3.5-turbo
模型进行非流式请求实现的因此测试报错并不一定代表通道不可用该功能后续会修复
另外OpenAI 渠道已经不再支持通过 key 获取余额因此余额显示为 0对于支持的渠道类型请点击余额进行刷新
</Message>
)
}
<Table basic compact size='small'>
<Table.Header>
<Table.Row>
@@ -343,10 +390,10 @@ const ChannelsTable = () => {
余额
</Table.HeaderCell>
<Table.HeaderCell
style={{ cursor: 'pointer' }}
onClick={() => {
sortChannel('priority');
}}
style={{ cursor: 'pointer' }}
onClick={() => {
sortChannel('priority');
}}
>
优先级
</Table.HeaderCell>
@@ -390,18 +437,18 @@ const ChannelsTable = () => {
</Table.Cell>
<Table.Cell>
<Popup
trigger={<Input type="number" defaultValue={channel.priority} onBlur={(event) => {
manageChannel(
channel.id,
'priority',
idx,
event.target.value,
);
}}>
<input style={{maxWidth:'60px'}} />
</Input>}
content='渠道选择优先级,越高越优先'
basic
trigger={<Input type='number' defaultValue={channel.priority} onBlur={(event) => {
manageChannel(
channel.id,
'priority',
idx,
event.target.value
);
}}>
<input style={{ maxWidth: '60px' }} />
</Input>}
content='渠道选择优先级,越高越优先'
basic
/>
</Table.Cell>
<Table.Cell>
@@ -481,6 +528,20 @@ const ChannelsTable = () => {
</Button>
<Button size='small' onClick={updateAllChannelsBalance}
loading={loading || updatingBalance}>更新所有已启用通道余额</Button>
<Popup
trigger={
<Button size='small' loading={loading}>
删除禁用渠道
</Button>
}
on='click'
flowing
hoverable
>
<Button size='small' loading={loading} negative onClick={deleteAllDisabledChannels}>
确认删除
</Button>
</Popup>
<Pagination
floated='right'
activePage={activePage}

View File

@@ -2,8 +2,8 @@ import React, { useContext, useEffect, useState } from 'react';
import { Button, Divider, Form, Grid, Header, Image, Message, Modal, Segment } from 'semantic-ui-react';
import { Link, useNavigate, useSearchParams } from 'react-router-dom';
import { UserContext } from '../context/User';
import { API, getLogo, showError, showSuccess } from '../helpers';
import { getOAuthState, onGitHubOAuthClicked } from './utils';
import { API, getLogo, showError, showSuccess, showWarning } from '../helpers';
import { onGitHubOAuthClicked } from './utils';
const LoginForm = () => {
const [inputs, setInputs] = useState({
@@ -68,8 +68,14 @@ const LoginForm = () => {
if (success) {
userDispatch({ type: 'login', payload: data });
localStorage.setItem('user', JSON.stringify(data));
navigate('/');
showSuccess('登录成功!');
if (username === 'root' && password === '123456') {
navigate('/user/edit');
showSuccess('登录成功!');
showWarning('请立刻修改默认密码!');
} else {
navigate('/token');
showSuccess('登录成功!');
}
} else {
showError(message);
}
@@ -126,7 +132,7 @@ const LoginForm = () => {
circular
color='black'
icon='github'
onClick={()=>onGitHubOAuthClicked(status.github_client_id)}
onClick={() => onGitHubOAuthClicked(status.github_client_id)}
/>
) : (
<></>

View File

@@ -138,7 +138,7 @@ const TokensTable = () => {
let defaultUrl;
if (chatLink) {
defaultUrl = chatLink + `/#/?settings={"key":"laisky-${key}"}`;
defaultUrl = chatLink + `/#/?settings={"key":"laisky-${key}","url":"${serverAddress}"}`;
} else {
defaultUrl = `https://chat.oneapi.pro/#/?settings={"key":"laisky-${key}","url":"${serverAddress}"}`;
}

View File

@@ -8,6 +8,7 @@ export const CHANNEL_OPTIONS = [
{ key: 18, text: '讯飞星火认知', value: 18, color: 'blue' },
{ key: 16, text: '智谱 ChatGLM', value: 16, color: 'violet' },
{ key: 19, text: '360 智脑', value: 19, color: 'blue' },
{ key: 23, text: '腾讯混元', value: 23, color: 'teal' },
{ key: 8, text: '自定义渠道', value: 8, color: 'pink' },
{ key: 22, text: '知识库FastGPT', value: 22, color: 'blue' },
{ key: 21, text: '知识库AI Proxy', value: 21, color: 'purple' },

View File

@@ -186,4 +186,14 @@ export const verifyJSON = (str) => {
return false;
}
return true;
};
};
export function shouldShowPrompt(id) {
let prompt = localStorage.getItem(`prompt-${id}`);
return !prompt;
}
export function setPromptShown(id) {
localStorage.setItem(`prompt-${id}`, 'true');
}

View File

@@ -19,6 +19,8 @@ function type2secretPrompt(type) {
return '按照如下格式输入APPID|APISecret|APIKey';
case 22:
return '按照如下格式输入APIKey-AppId例如fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041';
case 23:
return '按照如下格式输入AppId|SecretId|SecretKey';
default:
return '请输入渠道对应的鉴权密钥';
}
@@ -76,7 +78,10 @@ const EditChannel = () => {
localModels = ['SparkDesk'];
break;
case 19:
localModels = ['360GPT_S2_V9', 'embedding-bert-512-v1', 'embedding_s1_v1', 'semantic_similarity_s1_v1', '360GPT_S2_V9.4'];
localModels = ['360GPT_S2_V9', 'embedding-bert-512-v1', 'embedding_s1_v1', 'semantic_similarity_s1_v1'];
break;
case 23:
localModels = ['hunyuan'];
break;
}
setInputs((inputs) => ({ ...inputs, models: localModels }));

View File

@@ -1,19 +1,12 @@
import React from 'react';
import { Segment, Header } from 'semantic-ui-react';
import { Message } from 'semantic-ui-react';
const NotFound = () => (
<>
<Header
block
as="h4"
content="404"
attached="top"
icon="info"
className="small-icon"
/>
<Segment attached="bottom">
未找到所请求的页面
</Segment>
<Message negative>
<Message.Header>页面不存在</Message.Header>
<p>请检查你的浏览器地址是否正确</p>
</Message>
</>
);

View File

@@ -102,7 +102,7 @@ const EditUser = () => {
label='密码'
name='password'
type={'password'}
placeholder={'请输入新的密码'}
placeholder={'请输入新的密码,最短 8 位'}
onChange={handleInputChange}
value={password}
autoComplete='new-password'