fix: table list action

This commit is contained in:
jialin
2024-07-01 15:35:48 +08:00
parent 018c8cd208
commit 0f9123ee4f
21 changed files with 316 additions and 169 deletions
+13 -16
View File
@@ -1,8 +1,7 @@
import PageTools from '@/components/page-tools';
import ProgressBar from '@/components/progress-bar';
import { convertFileSize } from '@/utils';
import { useIntl } from '@umijs/max';
import { Col, Row, Table } from 'antd';
import _ from 'lodash';
import { useContext } from 'react';
import { DashboardContext } from '../config/dashboard-context';
@@ -77,21 +76,19 @@ const ActiveTable = () => {
dataIndex: 'name',
key: 'name'
},
// {
// title: intl.formatMessage({ id: 'dashboard.gpuutilization' }),
// dataIndex: 'gpu_utilization',
// key: 'gpu_utilization',
// render: (text: any, record: any) => (
// <ProgressBar percent={_.round(text, 0)}></ProgressBar>
// )
// },
{
title: intl.formatMessage({ id: 'dashboard.gpuutilization' }),
dataIndex: 'gpu_utilization',
key: 'gpu_utilization',
render: (text: any, record: any) => (
<ProgressBar percent={_.round(text, 0)}></ProgressBar>
)
},
{
title: intl.formatMessage({ id: 'dashboard.vramutilization' }),
dataIndex: 'gpu_memory_utilization',
title: intl.formatMessage({ id: 'dashboard.allocatevram' }),
dataIndex: 'allocate_gpu_memory_utilization',
key: 'gpu_memory_utilization',
render: (text: any, record: any) => (
<ProgressBar percent={_.round(text, 0)}></ProgressBar>
)
render: (text: any, record: any) => <span>{convertFileSize(text)}</span>
},
{
title: intl.formatMessage({ id: 'dashboard.runninginstances' }),
@@ -99,7 +96,7 @@ const ActiveTable = () => {
key: 'instance_count'
},
{
title: 'Tokens',
title: intl.formatMessage({ id: 'dashboard.tokens' }),
dataIndex: 'token_count',
key: 'token_count'
}
+3 -3
View File
@@ -57,9 +57,9 @@ const Overview: React.FC = () => {
<Col
xs={{ flex: '100%' }}
sm={{ flex: '50%' }}
md={{ flex: '30%' }}
lg={{ flex: '20%' }}
xl={{ flex: '20%' }}
md={{ flex: '50%' }}
lg={{ flex: '25%' }}
xl={{ flex: '25%' }}
key={config.key}
>
{renderCardItem({
@@ -67,7 +67,7 @@ const UtilizationOvertime: React.FC = () => {
const list: { value: number; time: string; type: string }[] = [];
_.each(typeList, (type: any) => {
const dataList = _.map(_.get(data, type, []), (item: any) => {
const value = _.get(item, 'value', 0);
const value = _.round(_.get(item, 'value', 0), 1);
const time = dayjs(item.timestamp * 1000).format('HH:mm:ss');
const itemtype = _.get(TypeKeyMap, [type, 'intl'], false)
? intl.formatMessage({
+72 -15
View File
@@ -92,6 +92,19 @@ const tokenUsage = TokensData.map((val, i) => {
};
});
const getCurrentMonthDays = () => {
const now = dayjs();
const firstDayOfMonth = now.startOf('month');
const dateRange = [];
let currentDate = firstDayOfMonth;
while (currentDate.isBefore(now) || currentDate.isSame(now, 'day')) {
dateRange.push(currentDate.format('YYYY-MM-DD'));
currentDate = currentDate.add(1, 'day');
}
return dateRange;
};
const Usage = () => {
const intl = useIntl();
const { size } = useWindowResize();
@@ -105,8 +118,11 @@ const Usage = () => {
const [userData, setUserData] = useState<{ name: string; value: number }[]>(
[]
);
const [dateRange, setDateRange] = useState<string[]>(getCurrentMonthDays());
const data = useContext(DashboardContext)?.model_usage || {};
console.log('dateRange', dateRange);
const handleSelectDate = (dateString: string) => {};
const generateData = () => {
@@ -126,26 +142,67 @@ const Usage = () => {
_.each(data.api_request_history, (item: any) => {
requestList.push({
time: dayjs(item.timestamp * 1000).format('YYYY-MM'),
time: dayjs(item.timestamp * 1000).format('YYYY-MM-DD'),
value: item.value
});
});
_.each(data.completion_token_history, (item: any) => {
tokenList.push({
time: dayjs(item.timestamp * 1000).format('YYYY-MM'),
name: 'completion_token',
color: 'rgba(84, 204, 152,0.8)',
value: item.value
_.each(dateRange, (date: string) => {
// tokens data
const item = _.find(data.completion_token_history, (item: any) => {
return dayjs(item.timestamp * 1000).format('YYYY-MM-DD') === date;
});
});
_.each(data.prompt_token_history, (item: any) => {
tokenList.push({
time: dayjs(item.timestamp * 1000).format('YYYY-MM'),
name: 'prompt_token',
color: 'rgba(0, 170, 173, 0.8)',
value: item.value
if (!item) {
tokenList.push({
time: date,
name: 'completion_token',
color: 'rgba(84, 204, 152,0.8)',
value: 0
});
} else {
tokenList.push({
time: dayjs(item.timestamp * 1000).format('YYYY-MM-DD'),
name: 'completion_token',
color: 'rgba(84, 204, 152,0.8)',
value: item.value
});
}
const promptItem = _.find(data.prompt_token_history, (item: any) => {
return dayjs(item.timestamp * 1000).format('YYYY-MM-DD') === date;
});
if (!promptItem) {
tokenList.push({
time: date,
name: 'prompt_token',
color: 'rgba(0, 170, 173, 0.8)',
value: 0
});
} else {
tokenList.push({
time: dayjs(promptItem.timestamp * 1000).format('YYYY-MM-DD'),
name: 'prompt_token',
color: 'rgba(0, 170, 173, 0.8)',
value: promptItem.value
});
}
// api request data
const requestItem = _.find(data.api_request_history, (item: any) => {
return dayjs(item.timestamp * 1000).format('YYYY-MM-DD') === date;
});
if (!requestItem) {
requestList.push({
time: date,
value: 0
});
} else {
requestList.push({
time: dayjs(requestItem.timestamp * 1000).format('YYYY-MM-DD'),
value: requestItem.value
});
}
});
_.each(data.top_users, (item: any) => {
@@ -190,7 +247,7 @@ const Usage = () => {
</span>
}
right={
<RangePicker
<DatePicker
onChange={handleSelectDate}
style={{ width: 300 }}
picker="month"
+6 -6
View File
@@ -11,12 +11,12 @@ export const overviewConfigs = [
backgroundColor: 'var(--color-white-1)'
// backgroundColor: 'linear-gradient(135deg, #ffffff, rgb(232 249 240 / 60%))'
},
{
key: 'allocatedGpus',
label: 'dashboard.allocategpus',
backgroundColor: 'var(--color-white-1)'
// backgroundColor: 'linear-gradient(135deg, #ffffff, rgb(232 249 240 / 60%))'
},
// {
// key: 'allocatedGpus',
// label: 'dashboard.allocategpus',
// backgroundColor: 'var(--color-white-1)'
// // backgroundColor: 'linear-gradient(135deg, #ffffff, rgb(232 249 240 / 60%))'
// },
{
key: 'model_count',
label: 'dashboard.models',
+19
View File
@@ -1,4 +1,5 @@
import { StatusMaps } from '@/config';
import { EditOutlined } from '@ant-design/icons';
export const ollamaModelOptions = [
{ label: 'llama3', value: 'llama3' },
@@ -20,3 +21,21 @@ export const modelSourceMap = {
export const status: any = {
Running: StatusMaps.success
};
export const ActionList = [
{
label: 'common.button.edit',
key: 'edit',
icon: EditOutlined
},
{
label: 'models.openinplayground',
key: 'chat',
icon: EditOutlined
},
{
label: 'common.button.delete',
key: 'delete',
icon: EditOutlined
}
];
+1
View File
@@ -31,6 +31,7 @@ export interface ModelInstanceListItem {
gpu_index: number;
pid: number;
port: number;
name: string;
state: string;
download_progress: number;
model_id: number;
+64 -79
View File
@@ -1,3 +1,4 @@
import DropdownButtons from '@/components/drop-down-buttons';
import PageTools from '@/components/page-tools';
import SealTable from '@/components/seal-table';
import RowChildren from '@/components/seal-table/components/row-children';
@@ -24,17 +25,7 @@ import {
} from '@ant-design/icons';
import { PageContainer } from '@ant-design/pro-components';
import { Access, useAccess, useIntl, useNavigate } from '@umijs/max';
import {
Button,
Col,
Input,
Modal,
Row,
Space,
Tag,
Tooltip,
message
} from 'antd';
import { Button, Col, Input, Modal, Row, Space, message } from 'antd';
import dayjs from 'dayjs';
import _ from 'lodash';
import { useCallback, useEffect, useRef, useState } from 'react';
@@ -79,6 +70,38 @@ const Models: React.FC = () => {
);
const [currentInstanceUrl, setCurrentInstanceUrl] = useState<string>('');
const ActionList = [
{
label: intl.formatMessage({ id: 'common.button.edit' }),
key: 'edit',
icon: <EditOutlined />
},
{
label: intl.formatMessage({ id: 'models.openinplayground' }),
key: 'chat',
icon: <WechatWorkOutlined />
},
{
label: intl.formatMessage({ id: 'common.button.delete' }),
key: 'delete',
danger: true,
icon: <DeleteOutlined />
}
];
const childActionList = [
{
label: intl.formatMessage({ id: 'common.button.viewlog' }),
key: 'viewlog',
icon: <FieldTimeOutlined />
},
{
label: intl.formatMessage({ id: 'common.button.delete' }),
key: 'delete',
danger: true,
icon: <DeleteOutlined />
}
];
const chunkRequedtRef = useRef<any>();
const timer = useRef<any>();
let axiosToken = createAxiosToken();
@@ -370,6 +393,27 @@ const Models: React.FC = () => {
setTitle(intl.formatMessage({ id: 'models.title.edit' }));
};
const handleSelect = (val: any, row: ListItem) => {
if (val === 'edit') {
handleEdit(row);
}
if (val === 'chat') {
handleOpenPlayGround(row);
}
if (val === 'delete') {
handleDelete(row);
}
};
const handleChildSelect = (val: any, row: ModelInstanceListItem) => {
if (val === 'delete') {
handleDeleteInstace(row);
}
if (val === 'viewlog') {
handleViewLogs(row);
}
};
useEffect(() => {
// fetchData();
createModelsChunkRequest();
@@ -394,10 +438,7 @@ const Models: React.FC = () => {
>
<RowChildren key={`${item.id}_row`}>
<Row style={{ width: '100%' }} align="middle">
<Col span={6}>
<Tag>{item.gpu_index}</Tag>
{item.worker_ip}:{item.port}
</Col>
<Col span={6}>{item.name}</Col>
<Col span={4}>
<span>{item.huggingface_filename}</span>
</Col>
@@ -423,33 +464,10 @@ const Models: React.FC = () => {
</span>
</Col>
<Col span={5}>
{hoverChildIndex === `${item.id}-${index}` && (
<Space size={20}>
<Tooltip
title={intl.formatMessage({
id: 'common.button.delete'
})}
>
<Button
size="small"
danger
onClick={() => handleDeleteInstace(item)}
icon={<DeleteOutlined></DeleteOutlined>}
></Button>
</Tooltip>
<Tooltip
title={intl.formatMessage({
id: 'common.button.viewlog'
})}
>
<Button
size="small"
onClick={() => handleViewLogs(item)}
icon={<FieldTimeOutlined />}
></Button>
</Tooltip>
</Space>
)}
<DropdownButtons
items={childActionList}
onSelect={(val) => handleChildSelect(val, item)}
></DropdownButtons>
</Col>
</Row>
</RowChildren>
@@ -570,43 +588,10 @@ const Models: React.FC = () => {
key="operation"
render={(text, record) => {
return !record.transition ? (
<Space size={20}>
<Tooltip
title={intl.formatMessage({
id: 'common.button.edit'
})}
>
<Button
size="small"
type="primary"
onClick={() => handleEdit(record)}
icon={<EditOutlined></EditOutlined>}
></Button>
</Tooltip>
<Tooltip
title={intl.formatMessage({
id: 'models.openinplayground'
})}
>
<Button
size="small"
type="primary"
onClick={() => handleOpenPlayGround(record)}
icon={<WechatWorkOutlined />}
></Button>
</Tooltip>
<Tooltip
title={intl.formatMessage({ id: 'common.button.delete' })}
>
<Button
size="small"
type="primary"
danger
onClick={() => handleDelete(record)}
icon={<DeleteOutlined></DeleteOutlined>}
></Button>
</Tooltip>
</Space>
<DropdownButtons
items={ActionList}
onSelect={(val) => handleSelect(val, record)}
></DropdownButtons>
) : null;
}}
/>
+3 -1
View File
@@ -1,5 +1,5 @@
import LogoIcon from '@/assets/images/logo.png';
import { userAtom } from '@/atoms/user';
import { initialPasswordAtom, userAtom } from '@/atoms/user';
import SealInput from '@/components/seal-form/seal-input';
import { GlobalOutlined, LockOutlined, UserOutlined } from '@ant-design/icons';
import { SelectLang, history, useIntl, useModel } from '@umijs/max';
@@ -26,6 +26,7 @@ const renderLogo = () => {
};
const LoginForm = () => {
const [userInfo, setUserInfo] = useAtom(userAtom);
const [initialPassword, setInitialPassword] = useAtom(initialPasswordAtom);
const { initialState, setInitialState } = useModel('@@initialState');
const { globalState, setGlobalState } = useModel('global');
const intl = useIntl();
@@ -62,6 +63,7 @@ const LoginForm = () => {
userInfo
});
setUserInfo(userInfo);
setInitialPassword(values.password);
if (!userInfo?.require_password_change) {
gotoDefaultPage(userInfo);
}
+6 -4
View File
@@ -1,4 +1,4 @@
import { userAtom } from '@/atoms/user';
import { initialPasswordAtom, userAtom } from '@/atoms/user';
import SealInput from '@/components/seal-form/seal-input';
import { PasswordReg } from '@/config';
import { GlobalOutlined, LockOutlined } from '@ant-design/icons';
@@ -12,6 +12,7 @@ const PasswordForm: React.FC = () => {
const [form] = Form.useForm();
const [userInfo, setUserInfo] = useAtom(userAtom);
const [initialPassword, setInitialPassword] = useAtom(initialPasswordAtom);
const gotoDefaultPage = (userInfo: any) => {
const pathname =
userInfo && userInfo?.is_admin ? '/dashboard' : '/playground';
@@ -23,13 +24,14 @@ const PasswordForm: React.FC = () => {
try {
await updatePassword({
new_password: values.new_password,
current_password: values.current_password
current_password: initialPassword
});
await setUserInfo({
...userInfo,
require_password_change: false
});
setInitialPassword('');
gotoDefaultPage(userInfo);
message.success(intl.formatMessage({ id: 'common.message.success' }));
} catch (error) {
@@ -63,7 +65,7 @@ const PasswordForm: React.FC = () => {
{intl.formatMessage({ id: 'users.password.modify.description' })}
</span>
</h2>
{/*
<Form.Item
name="current_password"
rules={[
@@ -82,7 +84,7 @@ const PasswordForm: React.FC = () => {
prefix={<LockOutlined />}
label={intl.formatMessage({ id: 'users.form.currentpassword' })}
/>
</Form.Item>
</Form.Item> */}
<Form.Item
name="new_password"
rules={[
+27 -24
View File
@@ -1,3 +1,4 @@
import DropdownButtons from '@/components/drop-down-buttons';
import PageTools from '@/components/page-tools';
import { PageAction } from '@/config';
import type { PageActionType } from '@/config/types';
@@ -14,7 +15,7 @@ import {
} from '@ant-design/icons';
import { PageContainer } from '@ant-design/pro-components';
import { useIntl } from '@umijs/max';
import { Button, Input, Modal, Space, Table, Tooltip, message } from 'antd';
import { Button, Input, Modal, Space, Table, message } from 'antd';
import dayjs from 'dayjs';
import _ from 'lodash';
import { useEffect, useState } from 'react';
@@ -44,6 +45,19 @@ const Models: React.FC = () => {
query: ''
});
const ActionList = [
{
key: 'edit',
label: intl.formatMessage({ id: 'common.button.edit' }),
icon: <EditOutlined></EditOutlined>
},
{
key: 'delete',
danger: true,
label: intl.formatMessage({ id: 'common.button.delete' }),
icon: <DeleteOutlined></DeleteOutlined>
}
];
const fetchData = async () => {
setLoading(true);
try {
@@ -172,6 +186,14 @@ const Models: React.FC = () => {
setTitle(intl.formatMessage({ id: 'users.form.edit' }));
};
const handleSelect = (val: any, row: ListItem) => {
if (val === 'edit') {
handleEditUser(row);
} else if (val === 'delete') {
handleDelete(row);
}
};
useEffect(() => {
fetchData();
}, [queryParams]);
@@ -296,29 +318,10 @@ const Models: React.FC = () => {
width={200}
render={(text, record: ListItem) => {
return (
<Space size={20}>
<Tooltip
title={intl.formatMessage({ id: 'common.button.edit' })}
>
<Button
size="small"
type="primary"
onClick={() => handleEditUser(record)}
icon={<EditOutlined></EditOutlined>}
></Button>
</Tooltip>
<Tooltip
title={intl.formatMessage({ id: 'common.button.delete' })}
>
<Button
size="small"
type="primary"
danger
onClick={() => handleDelete(record)}
icon={<DeleteOutlined></DeleteOutlined>}
></Button>
</Tooltip>
</Space>
<DropdownButtons
items={ActionList}
onSelect={(val) => handleSelect(val, record)}
></DropdownButtons>
);
}}
/>