chore: models list re-render
This commit is contained in:
@@ -8,8 +8,9 @@ import {
|
||||
xAxis,
|
||||
yAxis
|
||||
} from '@/components/echarts/config';
|
||||
import EmptyData from '@/components/empty-data';
|
||||
import _ from 'lodash';
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
import { memo } from 'react';
|
||||
import { ChartProps } from './types';
|
||||
|
||||
const BarChart: React.FC<ChartProps> = (props) => {
|
||||
@@ -22,7 +23,10 @@ const BarChart: React.FC<ChartProps> = (props) => {
|
||||
legendData,
|
||||
title
|
||||
} = props;
|
||||
const [dataOptions, setDataOptions] = useState({});
|
||||
|
||||
if (!seriesData.length) {
|
||||
return <EmptyData height={height} title={title}></EmptyData>;
|
||||
}
|
||||
|
||||
const options = {
|
||||
title: {
|
||||
@@ -51,8 +55,7 @@ const BarChart: React.FC<ChartProps> = (props) => {
|
||||
|
||||
series: []
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const setDataOptions = () => {
|
||||
const data = _.map(seriesData, (item: any) => {
|
||||
return {
|
||||
...item,
|
||||
@@ -63,7 +66,7 @@ const BarChart: React.FC<ChartProps> = (props) => {
|
||||
}
|
||||
};
|
||||
});
|
||||
const optionsConfig = {
|
||||
return {
|
||||
...options,
|
||||
animation: false,
|
||||
title: {
|
||||
@@ -79,9 +82,10 @@ const BarChart: React.FC<ChartProps> = (props) => {
|
||||
},
|
||||
series: data
|
||||
};
|
||||
console.log('optionsConfig=========', optionsConfig);
|
||||
setDataOptions(optionsConfig);
|
||||
}, [seriesData, xAxisData, title]);
|
||||
};
|
||||
|
||||
const dataOptions: any = setDataOptions();
|
||||
|
||||
return (
|
||||
<Chart
|
||||
height={height}
|
||||
|
||||
@@ -7,8 +7,9 @@ import {
|
||||
xAxis,
|
||||
yAxis
|
||||
} from '@/components/echarts/config';
|
||||
import EmptyData from '@/components/empty-data';
|
||||
import _ from 'lodash';
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
import { memo } from 'react';
|
||||
import { ChartProps } from './types';
|
||||
|
||||
const BarChart: React.FC<ChartProps> = (props) => {
|
||||
@@ -21,7 +22,9 @@ const BarChart: React.FC<ChartProps> = (props) => {
|
||||
legendData,
|
||||
title
|
||||
} = props;
|
||||
const [dataOptions, setDataOptions] = useState({});
|
||||
if (!seriesData.length) {
|
||||
return <EmptyData height={height} title={title}></EmptyData>;
|
||||
}
|
||||
|
||||
const options = {
|
||||
title: titleConfig,
|
||||
@@ -45,7 +48,7 @@ const BarChart: React.FC<ChartProps> = (props) => {
|
||||
series: []
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const setDataOptions = () => {
|
||||
const data = _.map(seriesData, (item: any) => {
|
||||
return {
|
||||
...item,
|
||||
@@ -60,9 +63,8 @@ const BarChart: React.FC<ChartProps> = (props) => {
|
||||
color: item.color
|
||||
}
|
||||
};
|
||||
return item;
|
||||
});
|
||||
const optionsConfig = {
|
||||
return {
|
||||
...options,
|
||||
animation: false,
|
||||
title: {
|
||||
@@ -101,9 +103,10 @@ const BarChart: React.FC<ChartProps> = (props) => {
|
||||
},
|
||||
series: data
|
||||
};
|
||||
console.log('optionsConfig========h=', optionsConfig);
|
||||
setDataOptions(optionsConfig);
|
||||
}, [seriesData, xAxisData, title]);
|
||||
};
|
||||
|
||||
const dataOptions: any = setDataOptions();
|
||||
|
||||
return (
|
||||
<Chart
|
||||
height={height}
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
xAxis,
|
||||
yAxis
|
||||
} from '@/components/echarts/config';
|
||||
import EmptyData from '@/components/empty-data';
|
||||
import _ from 'lodash';
|
||||
import { memo } from 'react';
|
||||
import { ChartProps } from './types';
|
||||
@@ -19,10 +20,13 @@ const LineChart: React.FC<ChartProps> = (props) => {
|
||||
height,
|
||||
width,
|
||||
labelFormatter,
|
||||
legendData,
|
||||
legendData = [],
|
||||
smooth,
|
||||
title
|
||||
} = props;
|
||||
if (!seriesData.length) {
|
||||
return <EmptyData height={height} title={title}></EmptyData>;
|
||||
}
|
||||
|
||||
const options = {
|
||||
title: {
|
||||
@@ -42,7 +46,7 @@ const LineChart: React.FC<ChartProps> = (props) => {
|
||||
yAxis,
|
||||
legend: {
|
||||
...legend,
|
||||
data: []
|
||||
data: legendData
|
||||
},
|
||||
|
||||
series: []
|
||||
|
||||
@@ -15,8 +15,8 @@ const EmptyData: React.FC<{
|
||||
>
|
||||
{title && (
|
||||
<h3
|
||||
className="justify-center"
|
||||
style={{ padding: '16px 0', marginBottom: 0 }}
|
||||
className="justify-center font-size-12"
|
||||
style={{ padding: '4px 0', marginBottom: 0 }}
|
||||
>
|
||||
{title}
|
||||
</h3>
|
||||
@@ -25,7 +25,7 @@ const EmptyData: React.FC<{
|
||||
className="flex-center justify-center flex-column"
|
||||
style={{ height: '100%' }}
|
||||
>
|
||||
<Empty />
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -8,14 +8,14 @@ import './index.less';
|
||||
|
||||
interface LogsViewerProps {
|
||||
height: number;
|
||||
content: string;
|
||||
content?: string;
|
||||
url: string;
|
||||
params?: object;
|
||||
}
|
||||
const LogsViewer: React.FC<LogsViewerProps> = (props) => {
|
||||
const { height, content, url } = props;
|
||||
const [nowrap, setNowrap] = useState(false);
|
||||
const [logsContent, setLogsContent] = useState(content);
|
||||
const [logsContent, setLogsContent] = useState(content || '');
|
||||
const { setChunkRequest } = useSetChunkRequest();
|
||||
const chunkRequedtRef = useRef<any>(null);
|
||||
const scroller = useRef<any>(null);
|
||||
|
||||
@@ -14,6 +14,7 @@ const RenderProgress = memo(
|
||||
if (download) {
|
||||
return 'var(--ant-color-primary)';
|
||||
}
|
||||
|
||||
if (percent <= 50) {
|
||||
return 'var(--color-progress-green)';
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AutoComplete, Form } from 'antd';
|
||||
import { AutoComplete, Form, Spin } from 'antd';
|
||||
import type { AutoCompleteProps } from 'antd/lib';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import Wrapper from './components/wrapper';
|
||||
@@ -17,6 +17,7 @@ const SealAutoComplete: React.FC<AutoCompleteProps & SealFormItemProps> = (
|
||||
extra,
|
||||
style,
|
||||
addAfter,
|
||||
loading,
|
||||
...rest
|
||||
} = props;
|
||||
const [isFocus, setIsFocus] = useState(false);
|
||||
@@ -63,6 +64,12 @@ const SealAutoComplete: React.FC<AutoCompleteProps & SealFormItemProps> = (
|
||||
const handleOnSelect = (value: any, option: any) => {
|
||||
onSelect?.(value, option);
|
||||
};
|
||||
const renderAfter = () => {
|
||||
if (loading) {
|
||||
return <Spin size="small"></Spin>;
|
||||
}
|
||||
return addAfter;
|
||||
};
|
||||
|
||||
return (
|
||||
<Wrapper
|
||||
@@ -73,12 +80,19 @@ const SealAutoComplete: React.FC<AutoCompleteProps & SealFormItemProps> = (
|
||||
required={required}
|
||||
description={description}
|
||||
disabled={props.disabled}
|
||||
addAfter={addAfter}
|
||||
addAfter={renderAfter()}
|
||||
onClick={handleClickWrapper}
|
||||
>
|
||||
<AutoComplete
|
||||
{...rest}
|
||||
ref={inputRef}
|
||||
placeholder={
|
||||
isFocus ? (
|
||||
<span style={{ paddingLeft: '12px' }}>{placeholder}</span>
|
||||
) : (
|
||||
''
|
||||
)
|
||||
}
|
||||
onSelect={handleOnSelect}
|
||||
onFocus={handleOnFocus}
|
||||
onBlur={handleOnBlur}
|
||||
|
||||
@@ -7,4 +7,5 @@ export interface SealFormItemProps {
|
||||
description?: React.ReactNode;
|
||||
extra?: React.ReactNode;
|
||||
addAfter?: React.ReactNode;
|
||||
loading?: React.ReactNode;
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ const TableRow: React.FC<
|
||||
const [loading, setLoading] = useState(false);
|
||||
const pollTimer = useRef<any>(null);
|
||||
const chunkRequestRef = useRef<any>(null);
|
||||
const childrenRendered = useRef<any>(null);
|
||||
|
||||
console.log('table row====');
|
||||
const { updateChunkedList } = useUpdateChunkedList(childrenData, {
|
||||
setDataList: setChildrenData
|
||||
});
|
||||
@@ -228,7 +228,11 @@ const TableRow: React.FC<
|
||||
{expanded && (
|
||||
<div className="expanded-row">
|
||||
<Spin spinning={loading}>
|
||||
{childrenData.length ? renderChildrenData() : <Empty></Empty>}
|
||||
{childrenData.length ? (
|
||||
renderChildrenData()
|
||||
) : (
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE}></Empty>
|
||||
)}
|
||||
</Spin>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { RightOutlined } from '@ant-design/icons';
|
||||
import { Button, Checkbox, Col, Empty, Row, Spin } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import TableHeader from './components/table-header';
|
||||
import TableRow from './components/table-row';
|
||||
import './styles/index.less';
|
||||
@@ -21,9 +21,10 @@ const SealTable: React.FC<SealTableProps> = (props) => {
|
||||
loadChildren,
|
||||
loadChildrenAPI
|
||||
} = props;
|
||||
|
||||
console.log('sealtable====');
|
||||
const [selectAll, setSelectAll] = useState(false);
|
||||
const [indeterminate, setIndeterminate] = useState(false);
|
||||
const tableContent = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (rowSelection) {
|
||||
@@ -121,12 +122,12 @@ const SealTable: React.FC<SealTableProps> = (props) => {
|
||||
if (!props.dataSource.length) {
|
||||
return (
|
||||
<div className="empty-wrapper">
|
||||
<Empty></Empty>
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE}></Empty>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="seal-table-content">
|
||||
<div className="seal-table-content" ref={tableContent}>
|
||||
{props.dataSource.map((item, index) => {
|
||||
return (
|
||||
<TableRow
|
||||
@@ -163,4 +164,4 @@ const SealTable: React.FC<SealTableProps> = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default SealTable;
|
||||
export default React.memo(SealTable);
|
||||
|
||||
@@ -36,6 +36,7 @@ export interface SealTableProps {
|
||||
renderChildren?: (data: any) => React.ReactNode;
|
||||
loadChildren?: (record: any) => Promise<any[]>;
|
||||
loadChildrenAPI?: (record: any) => string;
|
||||
contentRendered?: () => void;
|
||||
rowKey: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,13 @@ export default function useTableRowSelection() {
|
||||
setSelectedRowKeys(selectedRowKeys);
|
||||
};
|
||||
|
||||
const clearSelections = () => {
|
||||
setSelectedRowKeys([]);
|
||||
};
|
||||
|
||||
const rowSelection = {
|
||||
selectedRowKeys,
|
||||
clearSelections,
|
||||
onChange: onSelectChange
|
||||
};
|
||||
|
||||
|
||||
@@ -42,31 +42,38 @@ export function useUpdateChunkedList(
|
||||
);
|
||||
if (updateIndex === -1) {
|
||||
const updateItem = _.cloneDeep(item);
|
||||
const list = _.concat(updateItem, dataList);
|
||||
options.setDataList(list);
|
||||
options.setDataList((preDataList: any) => {
|
||||
return _.concat(updateItem, preDataList);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
// DELETE
|
||||
if (data?.type === WatchEventType.DELETE) {
|
||||
const list = _.filter(dataList, (item: any) => {
|
||||
return !_.find(ids, (id: any) => id === item.id);
|
||||
options.setDataList((prevDataList: any) => {
|
||||
const updatedList = _.filter(prevDataList, (item: any) => {
|
||||
return !_.find(ids, (id: any) => id === item.id);
|
||||
});
|
||||
return updatedList;
|
||||
});
|
||||
options.setDataList(list);
|
||||
}
|
||||
// UPDATE
|
||||
if (data?.type === WatchEventType.UPDATE) {
|
||||
_.each(collections, (item: any) => {
|
||||
const updateIndex = _.findIndex(
|
||||
dataList,
|
||||
(sItem: any) => sItem.id === item.id
|
||||
);
|
||||
console.log('updateIndex===========', dataList, updateIndex, data);
|
||||
if (updateIndex > -1) {
|
||||
const updateItem = _.cloneDeep(item);
|
||||
dataList[updateIndex] = updateItem;
|
||||
}
|
||||
options.setDataList(dataList);
|
||||
options.setDataList((prevDataList: any[]) => {
|
||||
const updatedDataList = _.cloneDeep(prevDataList);
|
||||
|
||||
_.each(collections, (item: any) => {
|
||||
const updateIndex = _.findIndex(
|
||||
updatedDataList,
|
||||
(sItem: any) => sItem.id === item.id
|
||||
);
|
||||
if (updateIndex > -1) {
|
||||
const updateItem = _.cloneDeep(item);
|
||||
updatedDataList[updateIndex] = updateItem;
|
||||
}
|
||||
});
|
||||
|
||||
return updatedDataList;
|
||||
});
|
||||
}
|
||||
if (options?.callback) {
|
||||
|
||||
@@ -12,5 +12,6 @@ export default {
|
||||
'models.form.s3address': 'S3 Address',
|
||||
'models.openinplayground': 'Open in Playground',
|
||||
'models.instances': 'instances',
|
||||
'model.form.ollama.model': 'Ollama Model'
|
||||
'model.form.ollama.model': 'Ollama Model',
|
||||
'model.form.ollamaholder': 'Please select or input model name'
|
||||
};
|
||||
|
||||
@@ -14,5 +14,7 @@ export default {
|
||||
'resources.table.temperature': 'Temperature',
|
||||
'resources.table.core': 'Core',
|
||||
'resources.table.gpuutilization': 'GPU Utilization',
|
||||
'resources.table.vramutilization': 'VRAM Utilization'
|
||||
'resources.table.vramutilization': 'VRAM Utilization',
|
||||
'resources.table.total': 'Total',
|
||||
'resources.table.used': 'Used'
|
||||
};
|
||||
|
||||
@@ -12,5 +12,6 @@ export default {
|
||||
'models.form.s3address': 'S3 地址',
|
||||
'models.openinplayground': '在 Playground 中打开',
|
||||
'models.instances': '实例',
|
||||
'model.form.ollama.model': 'Ollama 模型'
|
||||
'model.form.ollama.model': 'Ollama 模型',
|
||||
'model.form.ollamaholder': '请选择或输入模型名称'
|
||||
};
|
||||
|
||||
@@ -13,6 +13,6 @@ export default {
|
||||
'playground.completion': '补全',
|
||||
'playground.prompt': '提示',
|
||||
'playground.tokenusage': 'Token 使用量',
|
||||
'models.openinplayground': '试验场打开',
|
||||
'models.openinplayground': '打开试验场',
|
||||
'playground.tokenoutput': '输出'
|
||||
};
|
||||
|
||||
@@ -14,5 +14,7 @@ export default {
|
||||
'resources.table.temperature': '温度',
|
||||
'resources.table.core': '核数',
|
||||
'resources.table.gpuutilization': 'GPU 利用率',
|
||||
'resources.table.vramutilization': '显存利用率'
|
||||
'resources.table.vramutilization': '显存利用率',
|
||||
'resources.table.total': '总量',
|
||||
'resources.table.used': '已用'
|
||||
};
|
||||
|
||||
@@ -142,6 +142,7 @@ const Models: React.FC = () => {
|
||||
async onOk() {
|
||||
await handleBatchRequest(rowSelection.selectedRowKeys, deleteApisKey);
|
||||
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
||||
rowSelection.clearSelections();
|
||||
fetchData();
|
||||
},
|
||||
onCancel() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Chart from '@/components/echarts/chart';
|
||||
import { getLocale, useIntl } from '@umijs/max';
|
||||
import LineChart from '@/components/echarts/line-chart';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import dayjs from 'dayjs';
|
||||
import _ from 'lodash';
|
||||
import { memo, useContext } from 'react';
|
||||
@@ -112,8 +112,6 @@ const option = {
|
||||
const UtilizationOvertime: React.FC = () => {
|
||||
console.log('systemload=====================');
|
||||
const intl = useIntl();
|
||||
const locale = getLocale();
|
||||
let dataOptions: any = {};
|
||||
const data = useContext(DashboardContext)?.system_load?.history || {};
|
||||
|
||||
const typeList = ['gpu', 'cpu', 'memory', 'gpu_memory'];
|
||||
@@ -121,9 +119,8 @@ const UtilizationOvertime: React.FC = () => {
|
||||
const generateData = () => {
|
||||
const legendData: string[] = [];
|
||||
const xAxisData: string[] = [];
|
||||
let list: { value: number; time: string; type: string }[] = [];
|
||||
|
||||
list = _.map(typeList, (item: string) => {
|
||||
let seriesData: { value: number; time: string; type: string }[] = [];
|
||||
seriesData = _.map(typeList, (item: string) => {
|
||||
const itemConfig = _.get(TypeKeyMap, item, {});
|
||||
const name = itemConfig.intl
|
||||
? intl.formatMessage({ id: itemConfig.label })
|
||||
@@ -131,18 +128,8 @@ const UtilizationOvertime: React.FC = () => {
|
||||
legendData.push(name);
|
||||
const itemDataList = _.get(data, item, []);
|
||||
return {
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
showSymbol: false,
|
||||
itemStyle: {
|
||||
color: itemConfig.color
|
||||
},
|
||||
lineStyle: {
|
||||
width: 1.5,
|
||||
opacity: 0.7,
|
||||
color: itemConfig.color
|
||||
},
|
||||
name: name,
|
||||
color: itemConfig.color,
|
||||
data: _.map(itemDataList, (item: any) => {
|
||||
xAxisData.push(dayjs(item.timestamp * 1000).format('HH:mm:ss'));
|
||||
return {
|
||||
@@ -152,26 +139,24 @@ const UtilizationOvertime: React.FC = () => {
|
||||
})
|
||||
};
|
||||
});
|
||||
|
||||
dataOptions = {
|
||||
...option,
|
||||
animation: false,
|
||||
legend: {
|
||||
...option.legend,
|
||||
data: legendData
|
||||
},
|
||||
xAxis: {
|
||||
...option.xAxis,
|
||||
data: _.uniq(xAxisData)
|
||||
},
|
||||
series: list
|
||||
return {
|
||||
seriesData,
|
||||
legendData,
|
||||
xAxisData: _.uniq(xAxisData)
|
||||
};
|
||||
};
|
||||
generateData();
|
||||
const { seriesData, legendData, xAxisData } = generateData();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Chart height={390} options={dataOptions} width="100%"></Chart>
|
||||
<LineChart
|
||||
height={390}
|
||||
seriesData={seriesData}
|
||||
legendData={legendData}
|
||||
xAxisData={xAxisData}
|
||||
smooth={true}
|
||||
width="100%"
|
||||
></LineChart>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import PageTools from '@/components/page-tools';
|
||||
import breakpoints from '@/config/breakpoints';
|
||||
import useWindowResize from '@/hooks/use-window-resize';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Col, DatePicker, Row } from 'antd';
|
||||
import { Col, Row } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import _ from 'lodash';
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
@@ -40,13 +40,6 @@ const SystemLoad = () => {
|
||||
left={
|
||||
<span>{intl.formatMessage({ id: 'dashboard.systemload' })}</span>
|
||||
}
|
||||
right={
|
||||
<DatePicker
|
||||
onChange={handleSelectDate}
|
||||
style={{ width: 300 }}
|
||||
defaultValue={dayjs()}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Row style={{ width: '100%' }} gutter={[0, 20]}>
|
||||
<Col
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import PageTools from '@/components/page-tools';
|
||||
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Col, DatePicker, Row } from 'antd';
|
||||
import { Col, Row } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import _ from 'lodash';
|
||||
import { memo, useCallback, useContext } from 'react';
|
||||
@@ -87,7 +87,6 @@ const UsageInner: React.FC<{ paddingRight: string }> = ({ paddingRight }) => {
|
||||
color: baseColorMap.base,
|
||||
data: []
|
||||
};
|
||||
const users: string[] = [];
|
||||
|
||||
_.each(xAxisData, (date: string) => {
|
||||
// tokens data
|
||||
@@ -140,22 +139,27 @@ const UsageInner: React.FC<{ paddingRight: string }> = ({ paddingRight }) => {
|
||||
});
|
||||
|
||||
// ========== top users ============
|
||||
|
||||
_.each(data.top_users, (item: any) => {
|
||||
users.push(item.username);
|
||||
topUserPrompt.data.push({
|
||||
name: item.username,
|
||||
value: item.prompt_token_count
|
||||
if (!data.top_users?.length) {
|
||||
userData = [];
|
||||
topUserList = [];
|
||||
} else {
|
||||
const users: string[] = [];
|
||||
_.each(data.top_users, (item: any) => {
|
||||
users.push(item.username);
|
||||
topUserPrompt.data.push({
|
||||
name: item.username,
|
||||
value: item.prompt_token_count
|
||||
});
|
||||
topUserCompletion.data.push({
|
||||
name: item.username,
|
||||
value: item.completion_token_count
|
||||
});
|
||||
});
|
||||
topUserCompletion.data.push({
|
||||
name: item.username,
|
||||
value: item.completion_token_count
|
||||
});
|
||||
});
|
||||
topUserList = _.uniq(users);
|
||||
userData = [topUserCompletion, topUserPrompt];
|
||||
}
|
||||
|
||||
requestData = [requestList];
|
||||
userData = [topUserCompletion, topUserPrompt];
|
||||
topUserList = users;
|
||||
tokenData = [completionData, prompData];
|
||||
}, [data, xAxisData]);
|
||||
|
||||
@@ -166,14 +170,6 @@ const UsageInner: React.FC<{ paddingRight: string }> = ({ paddingRight }) => {
|
||||
<PageTools
|
||||
style={{ margin: '26px 0px' }}
|
||||
left={<span>{intl.formatMessage({ id: 'dashboard.usage' })}</span>}
|
||||
right={
|
||||
<DatePicker
|
||||
onChange={handleSelectDate}
|
||||
style={{ width: 300 }}
|
||||
picker="month"
|
||||
defaultValue={currentDate}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Row style={{ width: '100%' }} gutter={[0, 20]}>
|
||||
<Col
|
||||
|
||||
@@ -18,8 +18,8 @@ export async function queryModelsList(
|
||||
) {
|
||||
return request<Global.PageResponse<ListItem>>(`${MODELS_API}`, {
|
||||
methos: 'GET',
|
||||
params,
|
||||
...options
|
||||
...options,
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { SearchOutlined } from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form, Input, Modal } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
import {
|
||||
callHuggingfaceQuickSearch,
|
||||
queryHuggingfaceModelFiles
|
||||
@@ -27,14 +27,16 @@ type AddModalProps = {
|
||||
};
|
||||
|
||||
const sourceOptions = [
|
||||
{ label: 'Huggingface', value: 'huggingface', key: 'huggingface' },
|
||||
{ label: 'Hugging Face', value: 'huggingface', key: 'huggingface' },
|
||||
{ label: 'Ollama Library', value: 'ollama_library', key: 'ollama_library' }
|
||||
];
|
||||
|
||||
const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
console.log('addmodel====');
|
||||
const { title, action, open, onOk, onCancel } = props || {};
|
||||
const [form] = Form.useForm();
|
||||
const intl = useIntl();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const modelSource = Form.useWatch('source', form);
|
||||
const [repoOptions, setRepoOptions] = useState<
|
||||
{ label: string; value: string }[]
|
||||
@@ -77,6 +79,7 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
};
|
||||
const handleFetchModelFiles = async (repo: string) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const res = await queryHuggingfaceModelFiles({ repo });
|
||||
const list = _.filter(res, (file: any) => {
|
||||
return _.endsWith(file.path, '.gguf');
|
||||
@@ -88,8 +91,10 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
};
|
||||
});
|
||||
setFileOptions(list);
|
||||
setLoading(false);
|
||||
} catch (error) {
|
||||
setFileOptions([]);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -143,6 +148,7 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
required
|
||||
showSearch
|
||||
onBlur={handleRepoOnBlur}
|
||||
onSelect={handleRepoOnBlur}
|
||||
onChange={handleInputRepoChange}
|
||||
onSearch={debounceSearch}
|
||||
options={repoOptions}
|
||||
@@ -176,6 +182,7 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
label={intl.formatMessage({ id: 'models.form.filename' })}
|
||||
required
|
||||
options={fileOptions}
|
||||
loading={loading}
|
||||
onFocus={handleRepoOnBlur}
|
||||
disabled={action === PageAction.EDIT}
|
||||
></SealAutoComplete>
|
||||
@@ -233,6 +240,7 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
filterOption
|
||||
disabled={action === PageAction.EDIT}
|
||||
label={intl.formatMessage({ id: 'model.form.ollama.model' })}
|
||||
placeholder={intl.formatMessage({ id: 'model.form.ollamaholder' })}
|
||||
required
|
||||
options={ollamaModelOptions}
|
||||
></SealAutoComplete>
|
||||
@@ -362,4 +370,4 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default AddModal;
|
||||
export default memo(AddModal);
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
import DropdownButtons from '@/components/drop-down-buttons';
|
||||
import RowChildren from '@/components/seal-table/components/row-children';
|
||||
import StatusTag from '@/components/status-tag';
|
||||
import { DeleteOutlined, FieldTimeOutlined } from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Col, Row, Space } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import _ from 'lodash';
|
||||
import React from 'react';
|
||||
import { status } from '../config';
|
||||
import { ModelInstanceListItem } from '../config/types';
|
||||
|
||||
interface InstanceItemProps {
|
||||
list: ModelInstanceListItem[];
|
||||
handleChildSelect: (val: string, item: ModelInstanceListItem) => void;
|
||||
}
|
||||
|
||||
const InstanceItem: React.FC<InstanceItemProps> = ({
|
||||
list,
|
||||
handleChildSelect
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
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 />
|
||||
}
|
||||
];
|
||||
return (
|
||||
<Space size={16} direction="vertical" style={{ width: '100%' }}>
|
||||
{_.map(list, (item: ModelInstanceListItem, index: number) => {
|
||||
return (
|
||||
<div
|
||||
key={`${item.id}`}
|
||||
style={{ borderRadius: 'var(--ant-table-header-border-radius)' }}
|
||||
className={
|
||||
item.download_progress !== 100 && item.state !== 'Running'
|
||||
? 'skeleton-loading'
|
||||
: ''
|
||||
}
|
||||
>
|
||||
<RowChildren key={`${item.id}_row`}>
|
||||
<Row style={{ width: '100%' }} align="middle">
|
||||
<Col span={4}>{item.name}</Col>
|
||||
<Col span={3}>
|
||||
{item.worker_ip ? `${item.worker_ip}:${item.port}` : '-'}
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<span>
|
||||
{item.source === 'huggingface'
|
||||
? item.huggingface_filename
|
||||
: item.ollama_library_model_name}
|
||||
</span>
|
||||
</Col>
|
||||
|
||||
<Col span={3}>
|
||||
<span
|
||||
style={{ paddingLeft: '0px' }}
|
||||
className="flex justify-center"
|
||||
>
|
||||
{item.state && (
|
||||
<StatusTag
|
||||
download={
|
||||
item.state !== 'Running'
|
||||
? { percent: item.download_progress }
|
||||
: undefined
|
||||
}
|
||||
statusValue={{
|
||||
status: status[item.state] as any,
|
||||
text: item.state
|
||||
}}
|
||||
></StatusTag>
|
||||
)}
|
||||
</span>
|
||||
</Col>
|
||||
<Col span={5}>
|
||||
<span style={{ paddingLeft: 36 }}>
|
||||
{dayjs(item.updated_at).format('YYYY-MM-DD HH:mm:ss')}
|
||||
</span>
|
||||
</Col>
|
||||
<Col span={5}>
|
||||
<div style={{ paddingLeft: 39 }}>
|
||||
<DropdownButtons
|
||||
items={childActionList}
|
||||
onSelect={(val: string) => handleChildSelect(val, item)}
|
||||
></DropdownButtons>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</RowChildren>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
export default React.memo(InstanceItem);
|
||||
@@ -0,0 +1,399 @@
|
||||
import DropdownButtons from '@/components/drop-down-buttons';
|
||||
import PageTools from '@/components/page-tools';
|
||||
import SealTable from '@/components/seal-table';
|
||||
import SealColumn from '@/components/seal-table/components/seal-column';
|
||||
import { PageAction } from '@/config';
|
||||
import type { PageActionType } from '@/config/types';
|
||||
import useTableRowSelection from '@/hooks/use-table-row-selection';
|
||||
import useTableSort from '@/hooks/use-table-sort';
|
||||
import { handleBatchRequest } from '@/utils';
|
||||
import {
|
||||
DeleteOutlined,
|
||||
EditOutlined,
|
||||
PlusOutlined,
|
||||
SyncOutlined,
|
||||
WechatWorkOutlined
|
||||
} from '@ant-design/icons';
|
||||
import { PageContainer } from '@ant-design/pro-components';
|
||||
import { Access, useAccess, useIntl, useNavigate } from '@umijs/max';
|
||||
import { Button, Input, Modal, Space, message } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import { memo, useCallback, useEffect, useState } from 'react';
|
||||
import {
|
||||
MODELS_API,
|
||||
MODEL_INSTANCE_API,
|
||||
createModel,
|
||||
deleteModel,
|
||||
deleteModelInstance,
|
||||
queryModelInstancesList,
|
||||
updateModel
|
||||
} from '../apis';
|
||||
import { modelSourceMap } from '../config';
|
||||
import { FormData, ListItem, ModelInstanceListItem } from '../config/types';
|
||||
import AddModal from './add-modal';
|
||||
import InstanceItem from './instance-item';
|
||||
import ViewLogsModal from './view-logs-modal';
|
||||
|
||||
interface ModelsProps {
|
||||
handleSearch: (e: any) => void;
|
||||
handleNameChange: (e: any) => void;
|
||||
fetchData: () => Promise<any>;
|
||||
handleShowSizeChange: (page: number, size: number) => void;
|
||||
handlePageChange: (page: number, pageSize: number | undefined) => void;
|
||||
createModelsChunkRequest: () => void;
|
||||
queryParams: {
|
||||
page: number;
|
||||
perPage: number;
|
||||
query?: string;
|
||||
};
|
||||
dataSource: ListItem[];
|
||||
loading: boolean;
|
||||
total: number;
|
||||
}
|
||||
|
||||
const Models: React.FC<ModelsProps> = ({
|
||||
dataSource,
|
||||
handleNameChange,
|
||||
handleSearch,
|
||||
handleShowSizeChange,
|
||||
handlePageChange,
|
||||
createModelsChunkRequest,
|
||||
queryParams,
|
||||
loading,
|
||||
total,
|
||||
fetchData
|
||||
}) => {
|
||||
// const { modal } = App.useApp();
|
||||
console.log('model list====2');
|
||||
const access = useAccess();
|
||||
const intl = useIntl();
|
||||
const navigate = useNavigate();
|
||||
const rowSelection = useTableRowSelection();
|
||||
const { sortOrder, setSortOrder } = useTableSort({
|
||||
defaultSortOrder: 'descend'
|
||||
});
|
||||
const [openLogModal, setOpenLogModal] = useState(false);
|
||||
const [openAddModal, setOpenAddModal] = useState(false);
|
||||
const [action, setAction] = useState<PageActionType>(PageAction.CREATE);
|
||||
const [title, setTitle] = useState<string>('');
|
||||
const [currentData, setCurrentData] = useState<ListItem | undefined>(
|
||||
undefined
|
||||
);
|
||||
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 handleTableChange = (pagination: any, filters: any, sorter: any) => {
|
||||
console.log('handleTableChange=======', pagination, filters, sorter);
|
||||
setSortOrder(sorter.order);
|
||||
};
|
||||
|
||||
const handleAddModal = () => {
|
||||
setOpenAddModal(true);
|
||||
setAction(PageAction.CREATE);
|
||||
setTitle(intl.formatMessage({ id: 'models.button.deploy' }));
|
||||
};
|
||||
|
||||
const handleModalOk = useCallback(
|
||||
async (data: FormData) => {
|
||||
try {
|
||||
if (action === PageAction.CREATE) {
|
||||
await createModel({ data });
|
||||
}
|
||||
if (action === PageAction.EDIT) {
|
||||
await updateModel({ data, id: currentData?.id as number });
|
||||
}
|
||||
setOpenAddModal(false);
|
||||
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
||||
} catch (error) {}
|
||||
},
|
||||
[action, currentData]
|
||||
);
|
||||
|
||||
const handleModalCancel = useCallback(() => {
|
||||
console.log('handleModalCancel');
|
||||
setOpenAddModal(false);
|
||||
}, []);
|
||||
|
||||
const handleLogModalCancel = useCallback(() => {
|
||||
setOpenLogModal(false);
|
||||
}, []);
|
||||
const handleDelete = async (row: any) => {
|
||||
Modal.confirm({
|
||||
title: '',
|
||||
content: intl.formatMessage(
|
||||
{ id: 'common.delete.confirm' },
|
||||
{ type: intl.formatMessage({ id: 'models.table.models' }) }
|
||||
),
|
||||
async onOk() {
|
||||
await deleteModel(row.id);
|
||||
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
||||
},
|
||||
onCancel() {
|
||||
console.log('Cancel');
|
||||
}
|
||||
});
|
||||
};
|
||||
const handleDeleteBatch = () => {
|
||||
Modal.confirm({
|
||||
title: '',
|
||||
content: intl.formatMessage(
|
||||
{ id: 'common.delete.confirm' },
|
||||
{ type: intl.formatMessage({ id: 'models.table.models' }) }
|
||||
),
|
||||
async onOk() {
|
||||
await handleBatchRequest(rowSelection.selectedRowKeys, deleteModel);
|
||||
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
||||
rowSelection.clearSelections();
|
||||
},
|
||||
onCancel() {
|
||||
console.log('Cancel');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleOpenPlayGround = (row: any) => {
|
||||
navigate(`/playground?model=${row.name}`);
|
||||
};
|
||||
|
||||
const handleViewLogs = async (row: any) => {
|
||||
try {
|
||||
setCurrentInstanceUrl(`${MODEL_INSTANCE_API}/${row.id}/logs`);
|
||||
setOpenLogModal(true);
|
||||
} catch (error) {
|
||||
console.log('error:', error);
|
||||
}
|
||||
};
|
||||
const handleDeleteInstace = (row: any) => {
|
||||
Modal.confirm({
|
||||
title: '',
|
||||
content: intl.formatMessage(
|
||||
{ id: 'common.delete.confirm' },
|
||||
{ type: intl.formatMessage({ id: 'models.instances' }) }
|
||||
),
|
||||
async onOk() {
|
||||
await deleteModelInstance(row.id);
|
||||
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
||||
},
|
||||
onCancel() {
|
||||
console.log('Cancel');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getModelInstances = useCallback(async (row: any) => {
|
||||
const params = {
|
||||
id: row.id,
|
||||
page: 1,
|
||||
perPage: 100
|
||||
};
|
||||
const data = await queryModelInstancesList(params);
|
||||
return data.items || [];
|
||||
}, []);
|
||||
|
||||
const generateChildrenRequestAPI = (params: any) => {
|
||||
return `${MODELS_API}/${params.id}/instances`;
|
||||
};
|
||||
|
||||
const handleEdit = (row: ListItem) => {
|
||||
setCurrentData(row);
|
||||
setOpenAddModal(true);
|
||||
setAction(PageAction.EDIT);
|
||||
setTitle(intl.formatMessage({ id: 'models.title.edit' }));
|
||||
};
|
||||
|
||||
const handleSelect = useCallback((val: any, row: ListItem) => {
|
||||
if (val === 'edit') {
|
||||
handleEdit(row);
|
||||
}
|
||||
if (val === 'chat') {
|
||||
handleOpenPlayGround(row);
|
||||
}
|
||||
if (val === 'delete') {
|
||||
handleDelete(row);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleChildSelect = useCallback(
|
||||
(val: any, row: ModelInstanceListItem) => {
|
||||
if (val === 'delete') {
|
||||
handleDeleteInstace(row);
|
||||
}
|
||||
if (val === 'viewlog') {
|
||||
handleViewLogs(row);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const renderChildren = (list: any) => {
|
||||
return (
|
||||
<InstanceItem
|
||||
list={list}
|
||||
handleChildSelect={handleChildSelect}
|
||||
></InstanceItem>
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
createModelsChunkRequest();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageContainer
|
||||
ghost
|
||||
header={{
|
||||
title: intl.formatMessage({ id: 'models.title' })
|
||||
}}
|
||||
extra={[]}
|
||||
>
|
||||
<PageTools
|
||||
marginBottom={22}
|
||||
left={
|
||||
<Space>
|
||||
<Input
|
||||
placeholder={intl.formatMessage({ id: 'common.filter.name' })}
|
||||
style={{ width: 300 }}
|
||||
size="large"
|
||||
allowClear
|
||||
onChange={handleNameChange}
|
||||
></Input>
|
||||
<Button
|
||||
type="text"
|
||||
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
||||
onClick={handleSearch}
|
||||
icon={<SyncOutlined></SyncOutlined>}
|
||||
></Button>
|
||||
</Space>
|
||||
}
|
||||
right={
|
||||
<Space size={20}>
|
||||
<Button
|
||||
icon={<PlusOutlined></PlusOutlined>}
|
||||
type="primary"
|
||||
onClick={handleAddModal}
|
||||
>
|
||||
{intl?.formatMessage?.({ id: 'models.button.deploy' })}
|
||||
</Button>
|
||||
<Access accessible={access.canDelete}>
|
||||
<Button
|
||||
icon={<DeleteOutlined />}
|
||||
danger
|
||||
onClick={handleDeleteBatch}
|
||||
disabled={!rowSelection.selectedRowKeys.length}
|
||||
>
|
||||
{intl?.formatMessage?.({ id: 'common.button.delete' })}
|
||||
</Button>
|
||||
</Access>
|
||||
</Space>
|
||||
}
|
||||
></PageTools>
|
||||
<SealTable
|
||||
dataSource={dataSource}
|
||||
rowSelection={rowSelection}
|
||||
loading={loading}
|
||||
rowKey="id"
|
||||
expandable={true}
|
||||
onChange={handleTableChange}
|
||||
pollingChildren={false}
|
||||
watchChildren={true}
|
||||
loadChildren={getModelInstances}
|
||||
loadChildrenAPI={generateChildrenRequestAPI}
|
||||
renderChildren={renderChildren}
|
||||
pagination={{
|
||||
showSizeChanger: true,
|
||||
pageSize: queryParams.perPage,
|
||||
current: queryParams.page,
|
||||
total: total,
|
||||
hideOnSinglePage: true,
|
||||
onShowSizeChange: handleShowSizeChange,
|
||||
onChange: handlePageChange
|
||||
}}
|
||||
>
|
||||
<SealColumn
|
||||
title={intl.formatMessage({ id: 'common.table.name' })}
|
||||
dataIndex="name"
|
||||
key="name"
|
||||
width={400}
|
||||
span={6}
|
||||
/>
|
||||
<SealColumn
|
||||
title={intl.formatMessage({ id: 'models.form.source' })}
|
||||
dataIndex="source"
|
||||
key="source"
|
||||
span={4}
|
||||
render={(text) => {
|
||||
return modelSourceMap[text] || '-';
|
||||
}}
|
||||
/>
|
||||
<SealColumn
|
||||
title={intl.formatMessage({ id: 'models.form.replicas' })}
|
||||
dataIndex="replicas"
|
||||
key="replicas"
|
||||
align="center"
|
||||
span={4}
|
||||
/>
|
||||
<SealColumn
|
||||
span={5}
|
||||
title={intl.formatMessage({ id: 'common.table.createTime' })}
|
||||
dataIndex="created_at"
|
||||
key="createTime"
|
||||
defaultSortOrder="descend"
|
||||
sortOrder={sortOrder}
|
||||
showSorterTooltip={false}
|
||||
sorter={true}
|
||||
render={(val, row) => {
|
||||
return dayjs(val).format('YYYY-MM-DD HH:mm:ss');
|
||||
}}
|
||||
/>
|
||||
<SealColumn
|
||||
span={5}
|
||||
title={intl.formatMessage({ id: 'common.table.operation' })}
|
||||
key="operation"
|
||||
render={(text, record) => {
|
||||
return !record.transition ? (
|
||||
<DropdownButtons
|
||||
items={ActionList}
|
||||
onSelect={(val) => handleSelect(val, record)}
|
||||
></DropdownButtons>
|
||||
) : null;
|
||||
}}
|
||||
/>
|
||||
</SealTable>
|
||||
</PageContainer>
|
||||
<AddModal
|
||||
open={openAddModal}
|
||||
action={action}
|
||||
title={title}
|
||||
data={currentData}
|
||||
onCancel={handleModalCancel}
|
||||
onOk={handleModalOk}
|
||||
></AddModal>
|
||||
<ViewLogsModal
|
||||
url={currentInstanceUrl}
|
||||
open={openLogModal}
|
||||
onCancel={handleLogModalCancel}
|
||||
></ViewLogsModal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(Models);
|
||||
@@ -1,24 +1,25 @@
|
||||
import LogsViewer from '@/components/logs-viewer';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Modal } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
type ViewModalProps = {
|
||||
content: string;
|
||||
title: string;
|
||||
open: boolean;
|
||||
url: string;
|
||||
onCancel: () => void;
|
||||
};
|
||||
|
||||
const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
|
||||
const { title, open, url, onCancel, content = '' } = props || {};
|
||||
console.log('viewlogs======');
|
||||
const { open, url, onCancel } = props || {};
|
||||
const intl = useIntl();
|
||||
if (!open) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={title}
|
||||
title={intl.formatMessage({ id: 'common.button.viewlog' })}
|
||||
open={open}
|
||||
onCancel={onCancel}
|
||||
destroyOnClose={true}
|
||||
@@ -30,15 +31,14 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
|
||||
footer={null}
|
||||
>
|
||||
<LogsViewer
|
||||
content={content}
|
||||
height={500}
|
||||
url={url}
|
||||
params={{
|
||||
follow: false
|
||||
follow: true
|
||||
}}
|
||||
></LogsViewer>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ViewCodeModal;
|
||||
export default React.memo(ViewCodeModal);
|
||||
|
||||
@@ -12,10 +12,10 @@ export const ollamaModelOptions = [
|
||||
{ label: 'deepseek-coder', value: 'deepseek-coder' }
|
||||
];
|
||||
|
||||
export const modelSourceMap = {
|
||||
huggingface: 'huggingface',
|
||||
ollama_library: 'ollama_library',
|
||||
s3: 's3'
|
||||
export const modelSourceMap: Record<string, string> = {
|
||||
huggingface: 'Hugging Face',
|
||||
ollama_library: 'Ollama Library',
|
||||
s3: 'S3'
|
||||
};
|
||||
|
||||
export const status: any = {
|
||||
@@ -40,3 +40,12 @@ export const ActionList = [
|
||||
icon: EditOutlined
|
||||
}
|
||||
];
|
||||
|
||||
export const InstanceStatusMap = {
|
||||
Initializing: 'Initializing',
|
||||
Pending: 'Pending',
|
||||
Running: 'Running',
|
||||
Scheduled: 'Scheduled',
|
||||
Error: 'Error',
|
||||
Downloading: 'Downloading'
|
||||
};
|
||||
|
||||
@@ -25,6 +25,7 @@ export interface ModelInstanceListItem {
|
||||
source: string;
|
||||
huggingface_repo_id: string;
|
||||
huggingface_filename: string;
|
||||
ollama_library_model_name: string;
|
||||
s3_address: string;
|
||||
worker_id: number;
|
||||
worker_ip: string;
|
||||
|
||||
+55
-474
@@ -1,105 +1,23 @@
|
||||
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';
|
||||
import SealColumn from '@/components/seal-table/components/seal-column';
|
||||
import StatusTag from '@/components/status-tag';
|
||||
import { PageAction } from '@/config';
|
||||
import type { PageActionType } from '@/config/types';
|
||||
import useSetChunkRequest, {
|
||||
createAxiosToken
|
||||
} from '@/hooks/use-chunk-request';
|
||||
import useTableRowSelection from '@/hooks/use-table-row-selection';
|
||||
import useTableSort from '@/hooks/use-table-sort';
|
||||
import useUpdateChunkedList from '@/hooks/use-update-chunk-list';
|
||||
import { handleBatchRequest } from '@/utils';
|
||||
import {
|
||||
DeleteOutlined,
|
||||
EditOutlined,
|
||||
FieldTimeOutlined,
|
||||
PlusOutlined,
|
||||
SyncOutlined,
|
||||
WechatWorkOutlined
|
||||
} 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, message } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import _ from 'lodash';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
MODELS_API,
|
||||
MODEL_INSTANCE_API,
|
||||
createModel,
|
||||
deleteModel,
|
||||
deleteModelInstance,
|
||||
queryModelInstancesList,
|
||||
queryModelsList,
|
||||
updateModel
|
||||
} from './apis';
|
||||
import AddModal from './components/add-modal';
|
||||
import ViewLogsModal from './components/view-logs-modal';
|
||||
import { status } from './config';
|
||||
import { FormData, ListItem, ModelInstanceListItem } from './config/types';
|
||||
import { memo, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { MODELS_API, queryModelsList } from './apis';
|
||||
import TableList from './components/table-list';
|
||||
import { ListItem } from './config/types';
|
||||
|
||||
const Models: React.FC = () => {
|
||||
// const { modal } = App.useApp();
|
||||
const access = useAccess();
|
||||
const intl = useIntl();
|
||||
const navigate = useNavigate();
|
||||
console.log('model list====1');
|
||||
|
||||
const { setChunkRequest } = useSetChunkRequest();
|
||||
const rowSelection = useTableRowSelection();
|
||||
const { sortOrder, setSortOrder } = useTableSort({
|
||||
defaultSortOrder: 'descend'
|
||||
});
|
||||
const [logContent, setLogContent] = useState('');
|
||||
const [openLogModal, setOpenLogModal] = useState(false);
|
||||
const [hoverChildIndex, setHoverChildIndex] = useState<string | number>(-1);
|
||||
|
||||
const [total, setTotal] = useState(100);
|
||||
const [openAddModal, setOpenAddModal] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [action, setAction] = useState<PageActionType>(PageAction.CREATE);
|
||||
const [title, setTitle] = useState<string>('');
|
||||
const [dataSource, setDataSource] = useState<ListItem[]>([]);
|
||||
const [currentData, setCurrentData] = useState<ListItem | undefined>(
|
||||
undefined
|
||||
);
|
||||
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();
|
||||
const [queryParams, setQueryParams] = useState({
|
||||
page: 1,
|
||||
@@ -112,10 +30,10 @@ const Models: React.FC = () => {
|
||||
setDataList: setDataSource
|
||||
});
|
||||
|
||||
const fetchData = async (polling?: boolean) => {
|
||||
const fetchData = useCallback(async () => {
|
||||
axiosToken?.cancel?.();
|
||||
axiosToken = createAxiosToken();
|
||||
setLoading(!polling);
|
||||
setLoading(true);
|
||||
try {
|
||||
const params = {
|
||||
..._.pickBy(queryParams, (val: any) => !!val)
|
||||
@@ -127,44 +45,39 @@ const Models: React.FC = () => {
|
||||
setDataSource(res.items);
|
||||
setTotal(res.pagination.total);
|
||||
} catch (error) {
|
||||
setDataSource([]);
|
||||
console.log('error', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
}, [queryParams]);
|
||||
|
||||
const handleShowSizeChange = (page: number, size: number) => {
|
||||
console.log(page, size);
|
||||
setQueryParams({
|
||||
...queryParams,
|
||||
perPage: size
|
||||
});
|
||||
};
|
||||
const handleShowSizeChange = useCallback(
|
||||
(page: number, size: number) => {
|
||||
console.log(page, size);
|
||||
setQueryParams({
|
||||
...queryParams,
|
||||
perPage: size
|
||||
});
|
||||
},
|
||||
[queryParams]
|
||||
);
|
||||
|
||||
const handlePageChange = (page: number, pageSize: number | undefined) => {
|
||||
console.log(page, pageSize);
|
||||
setQueryParams({
|
||||
...queryParams,
|
||||
page: page
|
||||
});
|
||||
};
|
||||
|
||||
const handleTableChange = (pagination: any, filters: any, sorter: any) => {
|
||||
console.log('handleTableChange=======', pagination, filters, sorter);
|
||||
setSortOrder(sorter.order);
|
||||
};
|
||||
|
||||
const handleFilter = () => {
|
||||
fetchData();
|
||||
};
|
||||
const handlePageChange = useCallback(
|
||||
(page: number, pageSize: number | undefined) => {
|
||||
console.log(page, pageSize);
|
||||
setQueryParams({
|
||||
...queryParams,
|
||||
page: page
|
||||
});
|
||||
},
|
||||
[queryParams]
|
||||
);
|
||||
|
||||
const updateHandler = (list: any) => {
|
||||
_.each(list, (data: any) => {
|
||||
updateChunkedList(data);
|
||||
});
|
||||
if (!dataSource.length) {
|
||||
fetchData();
|
||||
}
|
||||
};
|
||||
|
||||
const createModelsChunkRequest = () => {
|
||||
@@ -182,374 +95,42 @@ const Models: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearch = (e: any) => {
|
||||
const handleSearch = useCallback((e: any) => {
|
||||
fetchData();
|
||||
};
|
||||
|
||||
const handleNameChange = (e: any) => {
|
||||
setQueryParams({
|
||||
...queryParams,
|
||||
query: e.target.value
|
||||
});
|
||||
};
|
||||
|
||||
const handleAddModal = () => {
|
||||
setOpenAddModal(true);
|
||||
setAction(PageAction.CREATE);
|
||||
setTitle(intl.formatMessage({ id: 'models.button.deploy' }));
|
||||
};
|
||||
|
||||
const handleModalOk = async (data: FormData) => {
|
||||
try {
|
||||
if (action === PageAction.CREATE) {
|
||||
await createModel({ data });
|
||||
}
|
||||
if (action === PageAction.EDIT) {
|
||||
await updateModel({ data, id: currentData?.id as number });
|
||||
}
|
||||
setOpenAddModal(false);
|
||||
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
const handleModalCancel = () => {
|
||||
console.log('handleModalCancel');
|
||||
setOpenAddModal(false);
|
||||
};
|
||||
|
||||
const handleLogModalCancel = () => {
|
||||
setOpenLogModal(false);
|
||||
};
|
||||
const handleDelete = async (row: any) => {
|
||||
Modal.confirm({
|
||||
title: '',
|
||||
content: intl.formatMessage(
|
||||
{ id: 'common.delete.confirm' },
|
||||
{ type: intl.formatMessage({ id: 'models.table.models' }) }
|
||||
),
|
||||
async onOk() {
|
||||
await deleteModel(row.id);
|
||||
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
||||
fetchData();
|
||||
},
|
||||
onCancel() {
|
||||
console.log('Cancel');
|
||||
}
|
||||
});
|
||||
};
|
||||
const handleDeleteBatch = () => {
|
||||
Modal.confirm({
|
||||
title: '',
|
||||
content: intl.formatMessage(
|
||||
{ id: 'common.delete.confirm' },
|
||||
{ type: intl.formatMessage({ id: 'models.table.models' }) }
|
||||
),
|
||||
async onOk() {
|
||||
await handleBatchRequest(rowSelection.selectedRowKeys, deleteModel);
|
||||
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
||||
fetchData();
|
||||
},
|
||||
onCancel() {
|
||||
console.log('Cancel');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleOpenPlayGround = (row: any) => {
|
||||
navigate(`/playground?model=${row.name}`);
|
||||
};
|
||||
|
||||
const handleViewLogs = async (row: any) => {
|
||||
try {
|
||||
setCurrentInstanceUrl(`${MODEL_INSTANCE_API}/${row.id}/logs`);
|
||||
setOpenLogModal(true);
|
||||
} catch (error) {
|
||||
console.log('error:', error);
|
||||
}
|
||||
};
|
||||
const handleDeleteInstace = (row: any) => {
|
||||
Modal.confirm({
|
||||
title: '',
|
||||
content: intl.formatMessage(
|
||||
{ id: 'common.delete.confirm' },
|
||||
{ type: intl.formatMessage({ id: 'models.instances' }) }
|
||||
),
|
||||
async onOk() {
|
||||
await deleteModelInstance(row.id);
|
||||
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
||||
fetchData();
|
||||
},
|
||||
onCancel() {
|
||||
console.log('Cancel');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleOnMouseEnter = (id: number, index: number) => {
|
||||
setHoverChildIndex(`${id}-${index}`);
|
||||
};
|
||||
|
||||
const handleOnMouseLeave = () => {
|
||||
setHoverChildIndex(-1);
|
||||
};
|
||||
|
||||
const getModelInstances = useCallback(async (row: any) => {
|
||||
const params = {
|
||||
id: row.id,
|
||||
page: 1,
|
||||
perPage: 100
|
||||
};
|
||||
const data = await queryModelInstancesList(params);
|
||||
return data.items || [];
|
||||
}, []);
|
||||
|
||||
const generateChildrenRequestAPI = (params: any) => {
|
||||
return `${MODELS_API}/${params.id}/instances`;
|
||||
};
|
||||
const handleNameChange = useCallback(
|
||||
(e: any) => {
|
||||
setQueryParams({
|
||||
...queryParams,
|
||||
query: e.target.value
|
||||
});
|
||||
},
|
||||
[queryParams]
|
||||
);
|
||||
|
||||
const handleEdit = (row: ListItem) => {
|
||||
setCurrentData(row);
|
||||
setOpenAddModal(true);
|
||||
setAction(PageAction.EDIT);
|
||||
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();
|
||||
return () => {
|
||||
chunkRequedtRef.current?.current?.cancel?.();
|
||||
axiosToken?.cancel?.();
|
||||
};
|
||||
}, [queryParams]);
|
||||
|
||||
useEffect(() => {
|
||||
createModelsChunkRequest();
|
||||
return () => {
|
||||
chunkRequedtRef.current?.current?.cancel?.();
|
||||
};
|
||||
}, [queryParams]);
|
||||
|
||||
const renderChildren = (list: any) => {
|
||||
return (
|
||||
<Space size={16} direction="vertical" style={{ width: '100%' }}>
|
||||
{_.map(list, (item: ModelInstanceListItem, index: number) => {
|
||||
return (
|
||||
<div
|
||||
key={`${item.id}`}
|
||||
onMouseEnter={() => handleOnMouseEnter(item.id, index)}
|
||||
onMouseLeave={handleOnMouseLeave}
|
||||
style={{ borderRadius: 'var(--ant-table-header-border-radius)' }}
|
||||
className={
|
||||
item.download_progress !== 100 && item.state !== 'Running'
|
||||
? 'skeleton-loading'
|
||||
: ''
|
||||
}
|
||||
>
|
||||
<RowChildren key={`${item.id}_row`}>
|
||||
<Row style={{ width: '100%' }} align="middle">
|
||||
<Col span={6}>{item.name}</Col>
|
||||
<Col span={4}>
|
||||
<span>{item.huggingface_filename}</span>
|
||||
</Col>
|
||||
|
||||
<Col span={4}>
|
||||
<span
|
||||
style={{ paddingLeft: '68px' }}
|
||||
className="flex justify-center"
|
||||
>
|
||||
{item.state && (
|
||||
<StatusTag
|
||||
download={
|
||||
item.state !== 'Running'
|
||||
? { percent: item.download_progress }
|
||||
: undefined
|
||||
}
|
||||
statusValue={{
|
||||
status: status[item.state] as any,
|
||||
text: item.state
|
||||
}}
|
||||
></StatusTag>
|
||||
)}
|
||||
</span>
|
||||
</Col>
|
||||
<Col span={5}>
|
||||
<span style={{ paddingLeft: 36 }}>
|
||||
{dayjs(item.updated_at).format('YYYY-MM-DD HH:mm:ss')}
|
||||
</span>
|
||||
</Col>
|
||||
<Col span={5}>
|
||||
<div style={{ paddingLeft: 39 }}>
|
||||
<DropdownButtons
|
||||
items={childActionList}
|
||||
onSelect={(val) => handleChildSelect(val, item)}
|
||||
></DropdownButtons>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</RowChildren>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageContainer
|
||||
ghost
|
||||
header={{
|
||||
title: intl.formatMessage({ id: 'models.title' })
|
||||
}}
|
||||
extra={[]}
|
||||
>
|
||||
<PageTools
|
||||
marginBottom={22}
|
||||
left={
|
||||
<Space>
|
||||
<Input
|
||||
placeholder={intl.formatMessage({ id: 'common.filter.name' })}
|
||||
style={{ width: 300 }}
|
||||
size="large"
|
||||
allowClear
|
||||
onChange={handleNameChange}
|
||||
></Input>
|
||||
<Button
|
||||
type="text"
|
||||
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
||||
onClick={handleSearch}
|
||||
icon={<SyncOutlined></SyncOutlined>}
|
||||
></Button>
|
||||
</Space>
|
||||
}
|
||||
right={
|
||||
<Space size={20}>
|
||||
<Button
|
||||
icon={<PlusOutlined></PlusOutlined>}
|
||||
type="primary"
|
||||
onClick={handleAddModal}
|
||||
>
|
||||
{intl?.formatMessage?.({ id: 'models.button.deploy' })}
|
||||
</Button>
|
||||
<Access accessible={access.canDelete}>
|
||||
<Button
|
||||
icon={<DeleteOutlined />}
|
||||
danger
|
||||
onClick={handleDeleteBatch}
|
||||
disabled={!rowSelection.selectedRowKeys.length}
|
||||
>
|
||||
{intl?.formatMessage?.({ id: 'common.button.delete' })}
|
||||
</Button>
|
||||
</Access>
|
||||
</Space>
|
||||
}
|
||||
></PageTools>
|
||||
<SealTable
|
||||
dataSource={dataSource}
|
||||
rowSelection={rowSelection}
|
||||
loading={loading}
|
||||
rowKey="id"
|
||||
expandable={true}
|
||||
onChange={handleTableChange}
|
||||
pollingChildren={false}
|
||||
watchChildren={true}
|
||||
loadChildren={getModelInstances}
|
||||
loadChildrenAPI={generateChildrenRequestAPI}
|
||||
renderChildren={renderChildren}
|
||||
pagination={{
|
||||
showSizeChanger: true,
|
||||
pageSize: queryParams.perPage,
|
||||
current: queryParams.page,
|
||||
total: total,
|
||||
hideOnSinglePage: true,
|
||||
onShowSizeChange: handleShowSizeChange,
|
||||
onChange: handlePageChange
|
||||
}}
|
||||
>
|
||||
<SealColumn
|
||||
title={intl.formatMessage({ id: 'common.table.name' })}
|
||||
dataIndex="name"
|
||||
key="name"
|
||||
width={400}
|
||||
span={6}
|
||||
/>
|
||||
<SealColumn
|
||||
title={intl.formatMessage({ id: 'models.form.source' })}
|
||||
dataIndex="source"
|
||||
key="source"
|
||||
span={4}
|
||||
/>
|
||||
<SealColumn
|
||||
title={intl.formatMessage({ id: 'models.form.replicas' })}
|
||||
dataIndex="replicas"
|
||||
key="replicas"
|
||||
align="center"
|
||||
span={4}
|
||||
/>
|
||||
<SealColumn
|
||||
span={5}
|
||||
title={intl.formatMessage({ id: 'common.table.createTime' })}
|
||||
dataIndex="created_at"
|
||||
key="createTime"
|
||||
defaultSortOrder="descend"
|
||||
sortOrder={sortOrder}
|
||||
showSorterTooltip={false}
|
||||
sorter={true}
|
||||
render={(val, row) => {
|
||||
return dayjs(val).format('YYYY-MM-DD HH:mm:ss');
|
||||
}}
|
||||
/>
|
||||
<SealColumn
|
||||
span={5}
|
||||
title={intl.formatMessage({ id: 'common.table.operation' })}
|
||||
key="operation"
|
||||
render={(text, record) => {
|
||||
return !record.transition ? (
|
||||
<DropdownButtons
|
||||
items={ActionList}
|
||||
onSelect={(val) => handleSelect(val, record)}
|
||||
></DropdownButtons>
|
||||
) : null;
|
||||
}}
|
||||
/>
|
||||
</SealTable>
|
||||
</PageContainer>
|
||||
<AddModal
|
||||
open={openAddModal}
|
||||
action={action}
|
||||
title={title}
|
||||
data={currentData}
|
||||
onCancel={handleModalCancel}
|
||||
onOk={handleModalOk}
|
||||
></AddModal>
|
||||
<ViewLogsModal
|
||||
url={currentInstanceUrl}
|
||||
title={intl.formatMessage({ id: 'common.button.viewlog' })}
|
||||
open={openLogModal}
|
||||
content={logContent}
|
||||
onCancel={handleLogModalCancel}
|
||||
></ViewLogsModal>
|
||||
</>
|
||||
<TableList
|
||||
dataSource={dataSource}
|
||||
handleNameChange={handleNameChange}
|
||||
handleSearch={handleSearch}
|
||||
handleShowSizeChange={handleShowSizeChange}
|
||||
handlePageChange={handlePageChange}
|
||||
createModelsChunkRequest={createModelsChunkRequest}
|
||||
queryParams={queryParams}
|
||||
loading={loading}
|
||||
total={total}
|
||||
fetchData={fetchData}
|
||||
></TableList>
|
||||
);
|
||||
};
|
||||
|
||||
export default Models;
|
||||
export default memo(Models);
|
||||
|
||||
@@ -19,29 +19,31 @@ const ReferenceParams = (props: ReferenceParamsProps) => {
|
||||
}
|
||||
return (
|
||||
<div className="reference-params">
|
||||
<Tooltip
|
||||
title={
|
||||
<Space>
|
||||
<span>
|
||||
{intl.formatMessage({ id: 'playground.completion' })}:{' '}
|
||||
{usage.completion_tokens}
|
||||
</span>
|
||||
<span>
|
||||
{intl.formatMessage({ id: 'playground.prompt' })}:{' '}
|
||||
{usage.prompt_tokens}
|
||||
</span>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
{intl.formatMessage({ id: 'playground.tokenusage' })}:{' '}
|
||||
{usage.total_tokens}
|
||||
</span>
|
||||
</Tooltip>
|
||||
<span className="usage">
|
||||
<Tooltip
|
||||
title={
|
||||
<Space>
|
||||
<span>
|
||||
{intl.formatMessage({ id: 'playground.completion' })}:{' '}
|
||||
{usage.completion_tokens}
|
||||
</span>
|
||||
<span>
|
||||
{intl.formatMessage({ id: 'playground.prompt' })}:{' '}
|
||||
{usage.prompt_tokens}
|
||||
</span>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
{intl.formatMessage({ id: 'playground.tokenusage' })}:{' '}
|
||||
{usage.total_tokens}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</span>
|
||||
|
||||
<span>
|
||||
{intl.formatMessage({ id: 'playground.tokenoutput' })}:{' '}
|
||||
{usage.time_per_output_token_ms} token/ms
|
||||
{usage.time_per_output_token_ms * 1000} Tokens/s
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
.chat-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-block: 10px;
|
||||
padding-right: 32px;
|
||||
}
|
||||
|
||||
@@ -4,17 +4,10 @@
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
height: calc(100vh - 136px);
|
||||
// padding-bottom: 120px;
|
||||
padding-right: 32px;
|
||||
|
||||
.message-list-wrap {
|
||||
max-height: calc(100vh - 152px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.ground-left-footer {
|
||||
// position: absolute;
|
||||
// bottom: 20px;
|
||||
// width: 100%;
|
||||
padding-right: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
.reference-params {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
height: 40px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--ant-orange);
|
||||
gap: 20px;
|
||||
|
||||
.usage {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
import PageTools from '@/components/page-tools';
|
||||
import ProgressBar from '@/components/progress-bar';
|
||||
import useTableRowSelection from '@/hooks/use-table-row-selection';
|
||||
import useTableSort from '@/hooks/use-table-sort';
|
||||
import { convertFileSize } from '@/utils';
|
||||
import { SyncOutlined } from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button, Input, Space, Table } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
import { queryGpuDevicesList } from '../apis';
|
||||
import { GPUDeviceItem } from '../config/types';
|
||||
const { Column } = Table;
|
||||
|
||||
const Models: React.FC = () => {
|
||||
const GPUList: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const rowSelection = useTableRowSelection();
|
||||
const { sortOrder, setSortOrder } = useTableSort({
|
||||
defaultSortOrder: 'descend'
|
||||
});
|
||||
@@ -56,6 +54,7 @@ const Models: React.FC = () => {
|
||||
setDataSource(res.items);
|
||||
setTotal(res.pagination.total);
|
||||
} catch (error) {
|
||||
setDataSource([]);
|
||||
console.log('error', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -193,4 +192,4 @@ const Models: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default Models;
|
||||
export default memo(GPUList);
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import PageTools from '@/components/page-tools';
|
||||
import ProgressBar from '@/components/progress-bar';
|
||||
import StatusTag from '@/components/status-tag';
|
||||
import useTableRowSelection from '@/hooks/use-table-row-selection';
|
||||
import useTableSort from '@/hooks/use-table-sort';
|
||||
import { convertFileSize } from '@/utils';
|
||||
import { SyncOutlined } from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button, Input, Space, Table } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
import { queryWorkersList } from '../apis';
|
||||
import { Filesystem, GPUDeviceItem, ListItem } from '../config/types';
|
||||
const { Column } = Table;
|
||||
|
||||
const Models: React.FC = () => {
|
||||
const rowSelection = useTableRowSelection();
|
||||
const Resources: React.FC = () => {
|
||||
console.log('resources======workers');
|
||||
|
||||
const { sortOrder, setSortOrder } = useTableSort({
|
||||
defaultSortOrder: 'descend'
|
||||
});
|
||||
@@ -39,6 +39,7 @@ const Models: React.FC = () => {
|
||||
setDataSource(res.items);
|
||||
setTotal(res.pagination.total);
|
||||
} catch (error) {
|
||||
setDataSource([]);
|
||||
console.log('error', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -96,8 +97,14 @@ const Models: React.FC = () => {
|
||||
);
|
||||
return mountRoot ? (
|
||||
<span className="flex-column">
|
||||
<span>Total: {convertFileSize(mountRoot?.total, 0)}</span>
|
||||
<span>Used: {convertFileSize(mountRoot?.used, 0)}</span>
|
||||
<span>
|
||||
{intl.formatMessage({ id: 'resources.table.total' })}:{' '}
|
||||
{convertFileSize(mountRoot?.total, 0)}
|
||||
</span>
|
||||
<span>
|
||||
{intl.formatMessage({ id: 'resources.table.used' })}:{' '}
|
||||
{convertFileSize(mountRoot?.used, 0)}
|
||||
</span>
|
||||
</span>
|
||||
) : (
|
||||
0
|
||||
@@ -195,10 +202,12 @@ const Models: React.FC = () => {
|
||||
label={
|
||||
<span className="flex-column">
|
||||
<span>
|
||||
Total: {convertFileSize(record?.status?.memory.total, 0)}
|
||||
{intl.formatMessage({ id: 'resources.table.total' })}:{' '}
|
||||
{convertFileSize(record?.status?.memory.total, 0)}
|
||||
</span>
|
||||
<span>
|
||||
Used: {convertFileSize(record?.status?.memory.used, 0)}
|
||||
{intl.formatMessage({ id: 'resources.table.used' })}:{' '}
|
||||
{convertFileSize(record?.status?.memory.used, 0)}
|
||||
</span>
|
||||
</span>
|
||||
}
|
||||
@@ -216,7 +225,12 @@ const Models: React.FC = () => {
|
||||
{record?.status?.gpu_devices.map((item, index) => {
|
||||
return (
|
||||
<span className="flex-center" key={index}>
|
||||
<span className="m-r-5">[{index}]</span>
|
||||
<span
|
||||
className="m-r-5"
|
||||
style={{ display: 'flex', width: 25 }}
|
||||
>
|
||||
[{index}]
|
||||
</span>
|
||||
<ProgressBar
|
||||
key={index}
|
||||
percent={_.round(item.core.utilization_rate, 0)}
|
||||
@@ -244,19 +258,28 @@ const Models: React.FC = () => {
|
||||
'Unified Memory'
|
||||
) : (
|
||||
<span className="flex-center">
|
||||
<span className="m-r-5">[{index}]</span>
|
||||
<span
|
||||
className="m-r-5"
|
||||
style={{ display: 'flex', width: 25 }}
|
||||
>
|
||||
[{index}]
|
||||
</span>
|
||||
<ProgressBar
|
||||
key={index}
|
||||
percent={_.round(item.memory.utilization_rate, 0)}
|
||||
label={
|
||||
<span className="flex-column">
|
||||
<span>
|
||||
Total:{' '}
|
||||
{convertFileSize(item.memory?.total, 0)}
|
||||
{intl.formatMessage({
|
||||
id: 'resources.table.total'
|
||||
})}
|
||||
: {convertFileSize(item.memory?.total, 0)}
|
||||
</span>
|
||||
<span>
|
||||
Used:{' '}
|
||||
{convertFileSize(item.memory?.used, 0)}
|
||||
{intl.formatMessage({
|
||||
id: 'resources.table.used'
|
||||
})}
|
||||
: {convertFileSize(item.memory?.used, 0)}
|
||||
</span>
|
||||
</span>
|
||||
}
|
||||
@@ -289,4 +312,4 @@ const Models: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default Models;
|
||||
export default memo(Resources);
|
||||
|
||||
@@ -19,7 +19,8 @@ const items: TabsProps['items'] = [
|
||||
}
|
||||
];
|
||||
const Resources = () => {
|
||||
const [activeKey, setActiveKey] = useState('test');
|
||||
console.log('resources======');
|
||||
const [activeKey, setActiveKey] = useState('workers');
|
||||
|
||||
const intl = useIntl();
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ const Models: React.FC = () => {
|
||||
setDataSource(res.items);
|
||||
setTotal(res.pagination.total);
|
||||
} catch (error) {
|
||||
setDataSource([]);
|
||||
console.log('error', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -170,6 +171,7 @@ const Models: React.FC = () => {
|
||||
async onOk() {
|
||||
await handleBatchRequest(rowSelection.selectedRowKeys, deleteUser);
|
||||
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
||||
rowSelection.clearSelections();
|
||||
fetchData();
|
||||
},
|
||||
onCancel() {
|
||||
|
||||
Reference in New Issue
Block a user