From b753528e44a283857d8bd92a5bebdfc5bbc65a9f Mon Sep 17 00:00:00 2001 From: jialin Date: Fri, 5 Jul 2024 18:50:09 +0800 Subject: [PATCH] chore: models list re-render --- src/components/echarts/bar-chart.tsx | 20 +- src/components/echarts/h-bar.tsx | 19 +- src/components/echarts/line-chart.tsx | 8 +- .../empty-data.tsx => empty-data/index.tsx} | 6 +- src/components/logs-viewer/index.tsx | 4 +- src/components/progress-bar/index.tsx | 1 + src/components/seal-form/auto-complete.tsx | 18 +- src/components/seal-form/types.ts | 1 + .../seal-table/components/table-row.tsx | 8 +- src/components/seal-table/index.tsx | 11 +- src/components/seal-table/types.ts | 1 + src/hooks/use-table-row-selection.ts | 5 + src/hooks/use-update-chunk-list.ts | 39 +- src/locales/en-US/models.ts | 3 +- src/locales/en-US/resources.ts | 4 +- src/locales/zh-CN/models.ts | 3 +- src/locales/zh-CN/playground.ts | 2 +- src/locales/zh-CN/resources.ts | 4 +- src/pages/api-keys/index.tsx | 1 + .../components/resource-utilization.tsx | 51 +- .../dashboard/components/system-load.tsx | 9 +- .../components/usage-inner/index.tsx | 42 +- src/pages/llmodels/apis/index.ts | 4 +- src/pages/llmodels/components/add-modal.tsx | 14 +- .../llmodels/components/instance-item.tsx | 104 ++++ src/pages/llmodels/components/table-list.tsx | 399 +++++++++++++ .../llmodels/components/view-logs-modal.tsx | 14 +- src/pages/llmodels/config/index.ts | 17 +- src/pages/llmodels/config/types.ts | 1 + src/pages/llmodels/index.tsx | 529 ++---------------- .../components/reference-params.tsx | 42 +- src/pages/playground/style/chat-footer.less | 2 + src/pages/playground/style/ground-left.less | 9 +- .../playground/style/reference-params.less | 5 +- src/pages/resources/components/gpus.tsx | 9 +- src/pages/resources/components/workers.tsx | 53 +- src/pages/resources/index.tsx | 3 +- src/pages/users/index.tsx | 2 + 38 files changed, 810 insertions(+), 657 deletions(-) rename src/components/{charts/empty-data.tsx => empty-data/index.tsx} (78%) create mode 100644 src/pages/llmodels/components/instance-item.tsx create mode 100644 src/pages/llmodels/components/table-list.tsx diff --git a/src/components/echarts/bar-chart.tsx b/src/components/echarts/bar-chart.tsx index 78a72520..21bd6e57 100644 --- a/src/components/echarts/bar-chart.tsx +++ b/src/components/echarts/bar-chart.tsx @@ -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 = (props) => { @@ -22,7 +23,10 @@ const BarChart: React.FC = (props) => { legendData, title } = props; - const [dataOptions, setDataOptions] = useState({}); + + if (!seriesData.length) { + return ; + } const options = { title: { @@ -51,8 +55,7 @@ const BarChart: React.FC = (props) => { series: [] }; - - useEffect(() => { + const setDataOptions = () => { const data = _.map(seriesData, (item: any) => { return { ...item, @@ -63,7 +66,7 @@ const BarChart: React.FC = (props) => { } }; }); - const optionsConfig = { + return { ...options, animation: false, title: { @@ -79,9 +82,10 @@ const BarChart: React.FC = (props) => { }, series: data }; - console.log('optionsConfig=========', optionsConfig); - setDataOptions(optionsConfig); - }, [seriesData, xAxisData, title]); + }; + + const dataOptions: any = setDataOptions(); + return ( = (props) => { @@ -21,7 +22,9 @@ const BarChart: React.FC = (props) => { legendData, title } = props; - const [dataOptions, setDataOptions] = useState({}); + if (!seriesData.length) { + return ; + } const options = { title: titleConfig, @@ -45,7 +48,7 @@ const BarChart: React.FC = (props) => { series: [] }; - useEffect(() => { + const setDataOptions = () => { const data = _.map(seriesData, (item: any) => { return { ...item, @@ -60,9 +63,8 @@ const BarChart: React.FC = (props) => { color: item.color } }; - return item; }); - const optionsConfig = { + return { ...options, animation: false, title: { @@ -101,9 +103,10 @@ const BarChart: React.FC = (props) => { }, series: data }; - console.log('optionsConfig========h=', optionsConfig); - setDataOptions(optionsConfig); - }, [seriesData, xAxisData, title]); + }; + + const dataOptions: any = setDataOptions(); + return ( = (props) => { height, width, labelFormatter, - legendData, + legendData = [], smooth, title } = props; + if (!seriesData.length) { + return ; + } const options = { title: { @@ -42,7 +46,7 @@ const LineChart: React.FC = (props) => { yAxis, legend: { ...legend, - data: [] + data: legendData }, series: [] diff --git a/src/components/charts/empty-data.tsx b/src/components/empty-data/index.tsx similarity index 78% rename from src/components/charts/empty-data.tsx rename to src/components/empty-data/index.tsx index e268bc2e..c38c0490 100644 --- a/src/components/charts/empty-data.tsx +++ b/src/components/empty-data/index.tsx @@ -15,8 +15,8 @@ const EmptyData: React.FC<{ > {title && (

{title}

@@ -25,7 +25,7 @@ const EmptyData: React.FC<{ className="flex-center justify-center flex-column" style={{ height: '100%' }} > - + ); diff --git a/src/components/logs-viewer/index.tsx b/src/components/logs-viewer/index.tsx index 7a27d881..29fe9c78 100644 --- a/src/components/logs-viewer/index.tsx +++ b/src/components/logs-viewer/index.tsx @@ -8,14 +8,14 @@ import './index.less'; interface LogsViewerProps { height: number; - content: string; + content?: string; url: string; params?: object; } const LogsViewer: React.FC = (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(null); const scroller = useRef(null); diff --git a/src/components/progress-bar/index.tsx b/src/components/progress-bar/index.tsx index e30b2d83..8bea617e 100644 --- a/src/components/progress-bar/index.tsx +++ b/src/components/progress-bar/index.tsx @@ -14,6 +14,7 @@ const RenderProgress = memo( if (download) { return 'var(--ant-color-primary)'; } + if (percent <= 50) { return 'var(--color-progress-green)'; } diff --git a/src/components/seal-form/auto-complete.tsx b/src/components/seal-form/auto-complete.tsx index 9c3863cd..cded4e42 100644 --- a/src/components/seal-form/auto-complete.tsx +++ b/src/components/seal-form/auto-complete.tsx @@ -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 = ( extra, style, addAfter, + loading, ...rest } = props; const [isFocus, setIsFocus] = useState(false); @@ -63,6 +64,12 @@ const SealAutoComplete: React.FC = ( const handleOnSelect = (value: any, option: any) => { onSelect?.(value, option); }; + const renderAfter = () => { + if (loading) { + return ; + } + return addAfter; + }; return ( = ( required={required} description={description} disabled={props.disabled} - addAfter={addAfter} + addAfter={renderAfter()} onClick={handleClickWrapper} > {placeholder} + ) : ( + '' + ) + } onSelect={handleOnSelect} onFocus={handleOnFocus} onBlur={handleOnBlur} diff --git a/src/components/seal-form/types.ts b/src/components/seal-form/types.ts index 78e0f299..d6ad2ad0 100644 --- a/src/components/seal-form/types.ts +++ b/src/components/seal-form/types.ts @@ -7,4 +7,5 @@ export interface SealFormItemProps { description?: React.ReactNode; extra?: React.ReactNode; addAfter?: React.ReactNode; + loading?: React.ReactNode; } diff --git a/src/components/seal-table/components/table-row.tsx b/src/components/seal-table/components/table-row.tsx index b454235a..33ec30f7 100644 --- a/src/components/seal-table/components/table-row.tsx +++ b/src/components/seal-table/components/table-row.tsx @@ -33,8 +33,8 @@ const TableRow: React.FC< const [loading, setLoading] = useState(false); const pollTimer = useRef(null); const chunkRequestRef = useRef(null); - const childrenRendered = useRef(null); + console.log('table row===='); const { updateChunkedList } = useUpdateChunkedList(childrenData, { setDataList: setChildrenData }); @@ -228,7 +228,11 @@ const TableRow: React.FC< {expanded && (
- {childrenData.length ? renderChildrenData() : } + {childrenData.length ? ( + renderChildrenData() + ) : ( + + )}
)} diff --git a/src/components/seal-table/index.tsx b/src/components/seal-table/index.tsx index 01d83020..f707d80c 100644 --- a/src/components/seal-table/index.tsx +++ b/src/components/seal-table/index.tsx @@ -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 = (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 = (props) => { if (!props.dataSource.length) { return (
- +
); } return ( -
+
{props.dataSource.map((item, index) => { return ( = (props) => { ); }; -export default SealTable; +export default React.memo(SealTable); diff --git a/src/components/seal-table/types.ts b/src/components/seal-table/types.ts index 80cea931..1bab135e 100644 --- a/src/components/seal-table/types.ts +++ b/src/components/seal-table/types.ts @@ -36,6 +36,7 @@ export interface SealTableProps { renderChildren?: (data: any) => React.ReactNode; loadChildren?: (record: any) => Promise; loadChildrenAPI?: (record: any) => string; + contentRendered?: () => void; rowKey: string; } diff --git a/src/hooks/use-table-row-selection.ts b/src/hooks/use-table-row-selection.ts index 1a87c04c..8ef3d9cc 100644 --- a/src/hooks/use-table-row-selection.ts +++ b/src/hooks/use-table-row-selection.ts @@ -7,8 +7,13 @@ export default function useTableRowSelection() { setSelectedRowKeys(selectedRowKeys); }; + const clearSelections = () => { + setSelectedRowKeys([]); + }; + const rowSelection = { selectedRowKeys, + clearSelections, onChange: onSelectChange }; diff --git a/src/hooks/use-update-chunk-list.ts b/src/hooks/use-update-chunk-list.ts index 0cc476cc..1afeb095 100644 --- a/src/hooks/use-update-chunk-list.ts +++ b/src/hooks/use-update-chunk-list.ts @@ -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) { diff --git a/src/locales/en-US/models.ts b/src/locales/en-US/models.ts index 7a6363e6..42618af4 100644 --- a/src/locales/en-US/models.ts +++ b/src/locales/en-US/models.ts @@ -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' }; diff --git a/src/locales/en-US/resources.ts b/src/locales/en-US/resources.ts index 43b624e0..03f63789 100644 --- a/src/locales/en-US/resources.ts +++ b/src/locales/en-US/resources.ts @@ -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' }; diff --git a/src/locales/zh-CN/models.ts b/src/locales/zh-CN/models.ts index c6beec9d..a817a5a0 100644 --- a/src/locales/zh-CN/models.ts +++ b/src/locales/zh-CN/models.ts @@ -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': '请选择或输入模型名称' }; diff --git a/src/locales/zh-CN/playground.ts b/src/locales/zh-CN/playground.ts index a340e1a1..28e295aa 100644 --- a/src/locales/zh-CN/playground.ts +++ b/src/locales/zh-CN/playground.ts @@ -13,6 +13,6 @@ export default { 'playground.completion': '补全', 'playground.prompt': '提示', 'playground.tokenusage': 'Token 使用量', - 'models.openinplayground': '试验场打开', + 'models.openinplayground': '打开试验场', 'playground.tokenoutput': '输出' }; diff --git a/src/locales/zh-CN/resources.ts b/src/locales/zh-CN/resources.ts index 68c25d0c..b020eed8 100644 --- a/src/locales/zh-CN/resources.ts +++ b/src/locales/zh-CN/resources.ts @@ -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': '已用' }; diff --git a/src/pages/api-keys/index.tsx b/src/pages/api-keys/index.tsx index 594ead60..86a4848d 100644 --- a/src/pages/api-keys/index.tsx +++ b/src/pages/api-keys/index.tsx @@ -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() { diff --git a/src/pages/dashboard/components/resource-utilization.tsx b/src/pages/dashboard/components/resource-utilization.tsx index 5a7f52f5..81f079b8 100644 --- a/src/pages/dashboard/components/resource-utilization.tsx +++ b/src/pages/dashboard/components/resource-utilization.tsx @@ -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 ( <> - + ); }; diff --git a/src/pages/dashboard/components/system-load.tsx b/src/pages/dashboard/components/system-load.tsx index 7b80d938..7e3fde0e 100644 --- a/src/pages/dashboard/components/system-load.tsx +++ b/src/pages/dashboard/components/system-load.tsx @@ -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={ {intl.formatMessage({ id: 'dashboard.systemload' })} } - right={ - - } /> = ({ 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 }) => { {intl.formatMessage({ id: 'dashboard.usage' })}} - right={ - - } /> >(`${MODELS_API}`, { methos: 'GET', - params, - ...options + ...options, + params }); } diff --git a/src/pages/llmodels/components/add-modal.tsx b/src/pages/llmodels/components/add-modal.tsx index b1d274ed..e8142e1a 100644 --- a/src/pages/llmodels/components/add-modal.tsx +++ b/src/pages/llmodels/components/add-modal.tsx @@ -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 = (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 = (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 = (props) => { }; }); setFileOptions(list); + setLoading(false); } catch (error) { setFileOptions([]); + setLoading(false); } }; @@ -143,6 +148,7 @@ const AddModal: React.FC = (props) => { required showSearch onBlur={handleRepoOnBlur} + onSelect={handleRepoOnBlur} onChange={handleInputRepoChange} onSearch={debounceSearch} options={repoOptions} @@ -176,6 +182,7 @@ const AddModal: React.FC = (props) => { label={intl.formatMessage({ id: 'models.form.filename' })} required options={fileOptions} + loading={loading} onFocus={handleRepoOnBlur} disabled={action === PageAction.EDIT} > @@ -233,6 +240,7 @@ const AddModal: React.FC = (props) => { filterOption disabled={action === PageAction.EDIT} label={intl.formatMessage({ id: 'model.form.ollama.model' })} + placeholder={intl.formatMessage({ id: 'model.form.ollamaholder' })} required options={ollamaModelOptions} > @@ -362,4 +370,4 @@ const AddModal: React.FC = (props) => { ); }; -export default AddModal; +export default memo(AddModal); diff --git a/src/pages/llmodels/components/instance-item.tsx b/src/pages/llmodels/components/instance-item.tsx new file mode 100644 index 00000000..a798d6b6 --- /dev/null +++ b/src/pages/llmodels/components/instance-item.tsx @@ -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 = ({ + list, + handleChildSelect +}) => { + const intl = useIntl(); + const childActionList = [ + { + label: intl.formatMessage({ id: 'common.button.viewlog' }), + key: 'viewlog', + icon: + }, + { + label: intl.formatMessage({ id: 'common.button.delete' }), + key: 'delete', + danger: true, + icon: + } + ]; + return ( + + {_.map(list, (item: ModelInstanceListItem, index: number) => { + return ( +
+ + + {item.name} + + {item.worker_ip ? `${item.worker_ip}:${item.port}` : '-'} + + + + {item.source === 'huggingface' + ? item.huggingface_filename + : item.ollama_library_model_name} + + + + + + {item.state && ( + + )} + + + + + {dayjs(item.updated_at).format('YYYY-MM-DD HH:mm:ss')} + + + +
+ handleChildSelect(val, item)} + > +
+ +
+
+
+ ); + })} +
+ ); +}; +export default React.memo(InstanceItem); diff --git a/src/pages/llmodels/components/table-list.tsx b/src/pages/llmodels/components/table-list.tsx new file mode 100644 index 00000000..f3d906eb --- /dev/null +++ b/src/pages/llmodels/components/table-list.tsx @@ -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; + 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 = ({ + 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(PageAction.CREATE); + const [title, setTitle] = useState(''); + const [currentData, setCurrentData] = useState( + undefined + ); + const [currentInstanceUrl, setCurrentInstanceUrl] = useState(''); + + const ActionList = [ + { + label: intl.formatMessage({ id: 'common.button.edit' }), + key: 'edit', + icon: + }, + { + label: intl.formatMessage({ id: 'models.openinplayground' }), + key: 'chat', + icon: + }, + { + label: intl.formatMessage({ id: 'common.button.delete' }), + key: 'delete', + danger: true, + icon: + } + ]; + + 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 ( + + ); + }; + + useEffect(() => { + createModelsChunkRequest(); + }, []); + + return ( + <> + + + + + + } + right={ + + + + + + + } + > + + + { + return modelSourceMap[text] || '-'; + }} + /> + + { + return dayjs(val).format('YYYY-MM-DD HH:mm:ss'); + }} + /> + { + return !record.transition ? ( + handleSelect(val, record)} + > + ) : null; + }} + /> + + + + + + ); +}; + +export default memo(Models); diff --git a/src/pages/llmodels/components/view-logs-modal.tsx b/src/pages/llmodels/components/view-logs-modal.tsx index f21175f3..9fa1dc5c 100644 --- a/src/pages/llmodels/components/view-logs-modal.tsx +++ b/src/pages/llmodels/components/view-logs-modal.tsx @@ -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 = (props) => { - const { title, open, url, onCancel, content = '' } = props || {}; + console.log('viewlogs======'); + const { open, url, onCancel } = props || {}; + const intl = useIntl(); if (!open) { return null; } return ( = (props) => { footer={null} > ); }; -export default ViewCodeModal; +export default React.memo(ViewCodeModal); diff --git a/src/pages/llmodels/config/index.ts b/src/pages/llmodels/config/index.ts index 013d156f..4e4f5e45 100644 --- a/src/pages/llmodels/config/index.ts +++ b/src/pages/llmodels/config/index.ts @@ -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 = { + 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' +}; diff --git a/src/pages/llmodels/config/types.ts b/src/pages/llmodels/config/types.ts index 4013cb65..dd60ea6f 100644 --- a/src/pages/llmodels/config/types.ts +++ b/src/pages/llmodels/config/types.ts @@ -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; diff --git a/src/pages/llmodels/index.tsx b/src/pages/llmodels/index.tsx index adbfd423..1aa4d949 100644 --- a/src/pages/llmodels/index.tsx +++ b/src/pages/llmodels/index.tsx @@ -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(-1); + const [total, setTotal] = useState(100); - const [openAddModal, setOpenAddModal] = useState(false); const [loading, setLoading] = useState(false); - const [action, setAction] = useState(PageAction.CREATE); - const [title, setTitle] = useState(''); const [dataSource, setDataSource] = useState([]); - const [currentData, setCurrentData] = useState( - undefined - ); - const [currentInstanceUrl, setCurrentInstanceUrl] = useState(''); - - const ActionList = [ - { - label: intl.formatMessage({ id: 'common.button.edit' }), - key: 'edit', - icon: - }, - { - label: intl.formatMessage({ id: 'models.openinplayground' }), - key: 'chat', - icon: - }, - { - label: intl.formatMessage({ id: 'common.button.delete' }), - key: 'delete', - danger: true, - icon: - } - ]; - - const childActionList = [ - { - label: intl.formatMessage({ id: 'common.button.viewlog' }), - key: 'viewlog', - icon: - }, - { - label: intl.formatMessage({ id: 'common.button.delete' }), - key: 'delete', - danger: true, - icon: - } - ]; const chunkRequedtRef = useRef(); - const timer = useRef(); 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 ( - - {_.map(list, (item: ModelInstanceListItem, index: number) => { - return ( -
handleOnMouseEnter(item.id, index)} - onMouseLeave={handleOnMouseLeave} - style={{ borderRadius: 'var(--ant-table-header-border-radius)' }} - className={ - item.download_progress !== 100 && item.state !== 'Running' - ? 'skeleton-loading' - : '' - } - > - - - {item.name} - - {item.huggingface_filename} - - - - - {item.state && ( - - )} - - - - - {dayjs(item.updated_at).format('YYYY-MM-DD HH:mm:ss')} - - - -
- handleChildSelect(val, item)} - > -
- -
-
-
- ); - })} -
- ); - }; - return ( - <> - - - - - - } - right={ - - - - - - - } - > - - - - - { - return dayjs(val).format('YYYY-MM-DD HH:mm:ss'); - }} - /> - { - return !record.transition ? ( - handleSelect(val, record)} - > - ) : null; - }} - /> - - - - - + ); }; -export default Models; +export default memo(Models); diff --git a/src/pages/playground/components/reference-params.tsx b/src/pages/playground/components/reference-params.tsx index d1a4b28f..68925035 100644 --- a/src/pages/playground/components/reference-params.tsx +++ b/src/pages/playground/components/reference-params.tsx @@ -19,29 +19,31 @@ const ReferenceParams = (props: ReferenceParamsProps) => { } return (
- - - {intl.formatMessage({ id: 'playground.completion' })}:{' '} - {usage.completion_tokens} - - - {intl.formatMessage({ id: 'playground.prompt' })}:{' '} - {usage.prompt_tokens} - - - } - > - - {intl.formatMessage({ id: 'playground.tokenusage' })}:{' '} - {usage.total_tokens} - - + + + + {intl.formatMessage({ id: 'playground.completion' })}:{' '} + {usage.completion_tokens} + + + {intl.formatMessage({ id: 'playground.prompt' })}:{' '} + {usage.prompt_tokens} + + + } + > + + {intl.formatMessage({ id: 'playground.tokenusage' })}:{' '} + {usage.total_tokens} + + + {intl.formatMessage({ id: 'playground.tokenoutput' })}:{' '} - {usage.time_per_output_token_ms} token/ms + {usage.time_per_output_token_ms * 1000} Tokens/s
); diff --git a/src/pages/playground/style/chat-footer.less b/src/pages/playground/style/chat-footer.less index 6985a21a..4c1363d7 100644 --- a/src/pages/playground/style/chat-footer.less +++ b/src/pages/playground/style/chat-footer.less @@ -1,4 +1,6 @@ .chat-footer { display: flex; align-items: center; + padding-block: 10px; + padding-right: 32px; } diff --git a/src/pages/playground/style/ground-left.less b/src/pages/playground/style/ground-left.less index 46373c98..cc3c48e7 100644 --- a/src/pages/playground/style/ground-left.less +++ b/src/pages/playground/style/ground-left.less @@ -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; } } diff --git a/src/pages/playground/style/reference-params.less b/src/pages/playground/style/reference-params.less index 1a02a9b4..2476c45c 100644 --- a/src/pages/playground/style/reference-params.less +++ b/src/pages/playground/style/reference-params.less @@ -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; + } } diff --git a/src/pages/resources/components/gpus.tsx b/src/pages/resources/components/gpus.tsx index 21d1a5a3..a84930c8 100644 --- a/src/pages/resources/components/gpus.tsx +++ b/src/pages/resources/components/gpus.tsx @@ -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); diff --git a/src/pages/resources/components/workers.tsx b/src/pages/resources/components/workers.tsx index b7f508d3..1a5d4657 100644 --- a/src/pages/resources/components/workers.tsx +++ b/src/pages/resources/components/workers.tsx @@ -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 ? ( - Total: {convertFileSize(mountRoot?.total, 0)} - Used: {convertFileSize(mountRoot?.used, 0)} + + {intl.formatMessage({ id: 'resources.table.total' })}:{' '} + {convertFileSize(mountRoot?.total, 0)} + + + {intl.formatMessage({ id: 'resources.table.used' })}:{' '} + {convertFileSize(mountRoot?.used, 0)} + ) : ( 0 @@ -195,10 +202,12 @@ const Models: React.FC = () => { label={ - Total: {convertFileSize(record?.status?.memory.total, 0)} + {intl.formatMessage({ id: 'resources.table.total' })}:{' '} + {convertFileSize(record?.status?.memory.total, 0)} - Used: {convertFileSize(record?.status?.memory.used, 0)} + {intl.formatMessage({ id: 'resources.table.used' })}:{' '} + {convertFileSize(record?.status?.memory.used, 0)} } @@ -216,7 +225,12 @@ const Models: React.FC = () => { {record?.status?.gpu_devices.map((item, index) => { return ( - [{index}] + + [{index}] + { 'Unified Memory' ) : ( - [{index}] + + [{index}] + - Total:{' '} - {convertFileSize(item.memory?.total, 0)} + {intl.formatMessage({ + id: 'resources.table.total' + })} + : {convertFileSize(item.memory?.total, 0)} - Used:{' '} - {convertFileSize(item.memory?.used, 0)} + {intl.formatMessage({ + id: 'resources.table.used' + })} + : {convertFileSize(item.memory?.used, 0)} } @@ -289,4 +312,4 @@ const Models: React.FC = () => { ); }; -export default Models; +export default memo(Resources); diff --git a/src/pages/resources/index.tsx b/src/pages/resources/index.tsx index b79a90e1..5102f4f7 100644 --- a/src/pages/resources/index.tsx +++ b/src/pages/resources/index.tsx @@ -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(); diff --git a/src/pages/users/index.tsx b/src/pages/users/index.tsx index 6b9d8ba4..781ec888 100644 --- a/src/pages/users/index.tsx +++ b/src/pages/users/index.tsx @@ -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() {