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