From dea99ff70ace97e0c94a7564044891f6eb11091f Mon Sep 17 00:00:00 2001 From: jialin Date: Thu, 8 Aug 2024 11:56:31 +0800 Subject: [PATCH] chore: list state merge --- src/components/icon-font/index.tsx | 2 +- src/locales/en-US/models.ts | 2 + src/locales/zh-CN/models.ts | 2 + src/pages/api-keys/index.tsx | 41 +++++++++++----- .../llmodels/components/hf-model-file.tsx | 11 ++--- .../llmodels/components/hf-model-item.tsx | 6 ++- src/pages/llmodels/components/model-card.tsx | 35 +++++++++----- .../llmodels/components/search-input.tsx | 6 ++- .../llmodels/components/search-model.tsx | 11 ++--- .../llmodels/components/search-result.tsx | 2 +- src/pages/llmodels/components/table-list.tsx | 4 +- src/pages/llmodels/config/file-type.ts | 35 ++++++++++++++ src/pages/llmodels/config/index.ts | 14 ++++++ src/pages/llmodels/index.tsx | 47 ++++++++++++++----- src/pages/llmodels/style/model-card.less | 1 + src/pages/resources/components/gpus.tsx | 39 ++++++++++----- src/pages/resources/components/workers.tsx | 38 ++++++++++----- src/pages/resources/index.tsx | 7 ++- src/pages/users/index.tsx | 39 ++++++++++----- 19 files changed, 246 insertions(+), 96 deletions(-) create mode 100644 src/pages/llmodels/config/file-type.ts diff --git a/src/components/icon-font/index.tsx b/src/components/icon-font/index.tsx index 7d62d72e..3e7923ee 100644 --- a/src/components/icon-font/index.tsx +++ b/src/components/icon-font/index.tsx @@ -1,7 +1,7 @@ import { createFromIconfontCN } from '@ant-design/icons'; const IconFont = createFromIconfontCN({ - scriptUrl: '//at.alicdn.com/t/c/font_4613488_rsdrbzw4fyd.js' + scriptUrl: '//at.alicdn.com/t/c/font_4613488_orlwzwe9x4k.js' }); export default IconFont; diff --git a/src/locales/en-US/models.ts b/src/locales/en-US/models.ts index fe3ceebb..66cad915 100644 --- a/src/locales/en-US/models.ts +++ b/src/locales/en-US/models.ts @@ -14,6 +14,8 @@ export default { 'models.instances': 'instances', 'model.form.ollama.model': 'Ollama Model', 'model.form.ollamaholder': 'Please select or input model name', + 'model.deploy.sort': 'Sort', + 'model.deploy.search.placeholder': 'Search models from Hugging Face', 'model.form.ollamatips': 'Tip: The following are the preconfigured Ollama models in GPUStack. Please select the model you want, or directly enter the model you wish to deploy in the 【{name}】 input box on the right.' }; diff --git a/src/locales/zh-CN/models.ts b/src/locales/zh-CN/models.ts index 8d276b49..f297aec9 100644 --- a/src/locales/zh-CN/models.ts +++ b/src/locales/zh-CN/models.ts @@ -14,6 +14,8 @@ export default { 'models.instances': '实例', 'model.form.ollama.model': 'Ollama 模型', 'model.form.ollamaholder': '请选择或输入模型名称', + 'model.deploy.sort': '排序', + 'model.deploy.search.placeholder': '从 Hugging Face 搜索模型', 'model.form.ollamatips': '提示:以下为 GPUStack 预设的 Ollama 模型,请选择你想要的模型或者直接在右侧表单 【{name}】 输入框中输入你要部署的模型。' }; diff --git a/src/pages/api-keys/index.tsx b/src/pages/api-keys/index.tsx index ee40f340..b0cc71c5 100644 --- a/src/pages/api-keys/index.tsx +++ b/src/pages/api-keys/index.tsx @@ -19,16 +19,23 @@ import { ListItem } from './config/types'; const { Column } = Table; const APIKeys: React.FC = () => { + console.log('APIKeys========'); const rowSelection = useTableRowSelection(); const { sortOrder, setSortOrder } = useTableSort({ defaultSortOrder: 'descend' }); const intl = useIntl(); const modalRef = useRef(null); - const [dataSource, setDataSource] = useState([]); - const [total, setTotal] = useState(0); + const [dataSource, setDataSource] = useState<{ + dataList: ListItem[]; + loading: boolean; + total: number; + }>({ + dataList: [], + loading: false, + total: 0 + }); const [openAddModal, setOpenAddModal] = useState(false); - const [loading, setLoading] = useState(false); const [action, setAction] = useState(PageAction.CREATE); const [queryParams, setQueryParams] = useState({ page: 1, @@ -51,20 +58,28 @@ const APIKeys: React.FC = () => { }; const fetchData = async () => { - setLoading(true); + setDataSource((pre) => { + pre.loading = true; + return pre; + }); try { const params = { ..._.pickBy(queryParams, (val: any) => !!val) }; const res = await queryApisKeysList(params); - console.log('res=======', res); - setDataSource(res.items || []); - setTotal(res.pagination.total); + + setDataSource({ + dataList: res.items || [], + loading: false, + total: res.pagination.total + }); } catch (error) { console.log('error', error); - setDataSource([]); - } finally { - setLoading(false); + setDataSource({ + dataList: [], + loading: false, + total: dataSource.total + }); } }; const handleSearch = (e: any) => { @@ -173,16 +188,16 @@ const APIKeys: React.FC = () => { } > = (props) => { const handleFetchModelFiles = async () => { if (!props.repo) { setDataSource({ fileList: [], loading: false }); + handleSelectModelFile({}); return; } setDataSource({ ...dataSource, loading: true }); @@ -54,9 +52,10 @@ const HFModelFile: React.FC = (props) => { const name = _.split(item.path, '.').slice(0, -1).join('.'); let quanType = _.toUpper(name.split('-').slice(-1)[0]); if (quanType.indexOf('.') > -1) { - quanType = _.split(quanType, '.')[1]; + quanType = _.split(quanType, '.').pop(); } - if (_.get(GGUF_QUANT_DESCRIPTIONS, GGMLQuantizationType[quanType])) { + console.log('quanType', quanType, FileType[quanType]); + if (FileType[quanType] !== undefined) { return {quanType}; } return null; diff --git a/src/pages/llmodels/components/hf-model-item.tsx b/src/pages/llmodels/components/hf-model-item.tsx index 2dc90617..b610af16 100644 --- a/src/pages/llmodels/components/hf-model-item.tsx +++ b/src/pages/llmodels/components/hf-model-item.tsx @@ -25,6 +25,7 @@ interface HFModelItemProps { const HFModelItem: React.FC = (props) => { return (
= (props) => { {props.task && ( @@ -68,9 +69,10 @@ const HFModelItem: React.FC = (props) => { ) : (
- {_.map(props.tags, (tag: string) => { + {_.map(props.tags, (tag: string, index: string) => { return ( = (props) => { const getModelCardData = async () => { if (!repo) { + setModelData(null); return; } try { const res = await queryHuggingfaceModelDetail({ repo }); - console.log('modelcarddata==========', res); setModelData(res); } catch (error) { setModelData({}); @@ -31,15 +32,27 @@ const ModelCard: React.FC<{ repo: string }> = (props) => { Model Card
-
-
{modelData.id}
- - - Architecture: - {modelData.config?.model_type} - - -
+ {modelData ? ( +
+
{modelData.id}
+
+ + Architecture: + {modelData.config?.model_type} + + +
+
+ ) : ( + + )}
); diff --git a/src/pages/llmodels/components/search-input.tsx b/src/pages/llmodels/components/search-input.tsx index fb8b6a19..09c11843 100644 --- a/src/pages/llmodels/components/search-input.tsx +++ b/src/pages/llmodels/components/search-input.tsx @@ -2,6 +2,7 @@ import IconFont from '@/components/icon-font'; import hotkeys from '@/config/hotkeys'; import { platformCall } from '@/utils'; import { SearchOutlined } from '@ant-design/icons'; +import { useIntl } from '@umijs/max'; import { Input, Tag } from 'antd'; import React, { useRef, useState } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; @@ -10,6 +11,7 @@ const SearchInput: React.FC<{ onSearch: (e: any) => void; }> = (props) => { const { onSearch } = props; + const intl = useIntl(); const [isFocus, setIsFocus] = useState(false); const inputRef = useRef(null); const platform = platformCall(); @@ -26,7 +28,9 @@ const SearchInput: React.FC<{ onFocus={() => setIsFocus(true)} onBlur={() => setIsFocus(false)} allowClear - placeholder="Search models from Hugging Face" + placeholder={intl.formatMessage({ + id: 'model.deploy.search.placeholder' + })} suffix={ !isFocus && ( diff --git a/src/pages/llmodels/components/search-model.tsx b/src/pages/llmodels/components/search-model.tsx index e443f0f2..f341301e 100644 --- a/src/pages/llmodels/components/search-model.tsx +++ b/src/pages/llmodels/components/search-model.tsx @@ -165,7 +165,11 @@ const SearchModel: React.FC = (props) => { value={sortType} onChange={handleSortChange} labelRender={({ label }) => { - return Sort: {label}; + return ( + + {intl.formatMessage({ id: 'model.deploy.sort' })}: {label} + + ); }} options={modelFilesSortOptions} size="middle" @@ -203,11 +207,6 @@ const SearchModel: React.FC = (props) => { return (
- {/* */} {modelSource === modelSourceMap.huggingface_value ? ( renderHFSearch() ) : ( diff --git a/src/pages/llmodels/components/search-result.tsx b/src/pages/llmodels/components/search-result.tsx index fff3641c..40178461 100644 --- a/src/pages/llmodels/components/search-result.tsx +++ b/src/pages/llmodels/components/search-result.tsx @@ -27,7 +27,7 @@ const SearchResult: React.FC = (props) => { {resultList.map((item, index) => (
-
handleSelect(e, item)} tabIndex={0}> +
handleSelect(e, item)}> = ({ ); }; -export default Models; +export default memo(Models); diff --git a/src/pages/llmodels/config/file-type.ts b/src/pages/llmodels/config/file-type.ts new file mode 100644 index 00000000..e65ddbc3 --- /dev/null +++ b/src/pages/llmodels/config/file-type.ts @@ -0,0 +1,35 @@ +enum FileType { + F32 = 0, + F16 = 1, + Q4_0 = 2, + Q4_1 = 3, + Q4_1_SOME_F16 = 4, + Q8_0 = 7, + Q5_0 = 8, + Q5_1 = 9, + Q2_K = 10, + Q3_K_S = 11, + Q3_K_M = 12, + Q3_K_L = 13, + Q4_K_S = 14, + Q4_K_M = 15, + Q5_K_S = 16, + Q5_K_M = 17, + Q6_K = 18, + IQ2_XXS = 19, + IQ2_XS = 20, + Q2_K_S = 21, + IQ3_XS = 22, + IQ3_XXS = 23, + IQ1_S = 24, + IQ4_NL = 25, + IQ3_S = 26, + IQ3_M = 27, + IQ2_S = 28, + IQ2_M = 29, + IQ4_XS = 30, + IQ1_M = 31, + BF16 = 32 +} + +export default FileType; diff --git a/src/pages/llmodels/config/index.ts b/src/pages/llmodels/config/index.ts index 944ba4b5..14ed34be 100644 --- a/src/pages/llmodels/config/index.ts +++ b/src/pages/llmodels/config/index.ts @@ -23,6 +23,20 @@ export const ollamaModelOptions = [ tags: ['9B', '27B'], id: 'gemma2' }, + { + label: 'mistral-nemo', + value: 'mistral-nemo', + name: 'mistral-nemo', + tags: ['12B'], + id: 'mistral-nemo' + }, + { + label: 'mistral-large', + value: 'mistral-large', + name: 'mistral-large', + tags: ['123B'], + id: 'mistral-large' + }, { label: 'mistral', value: 'mistral', diff --git a/src/pages/llmodels/index.tsx b/src/pages/llmodels/index.tsx index 6bfe2008..d6610544 100644 --- a/src/pages/llmodels/index.tsx +++ b/src/pages/llmodels/index.tsx @@ -16,9 +16,17 @@ const Models: React.FC = () => { const { setChunkRequest: setModelInstanceChunkRequest } = useSetChunkRequest(); const [total, setTotal] = useState(0); - const [modelInstances, setModelInstances] = useState([]); const [loading, setLoading] = useState(false); - const [dataSource, setDataSource] = useState([]); + const [modelInstances, setModelInstances] = useState([]); + const [dataSource, setDataSource] = useState<{ + dataList: ListItem[]; + loading: boolean; + total: number; + }>({ + dataList: [], + loading: false, + total: 0 + }); const [firstLoad, setFirstLoad] = useState(true); const chunkRequedtRef = useRef(); const chunkInstanceRequedtRef = useRef(); @@ -30,14 +38,23 @@ const Models: React.FC = () => { }); const { updateChunkedList, cacheDataListRef } = useUpdateChunkedList({ - dataList: dataSource, - setDataList: setDataSource + dataList: dataSource.dataList, + setDataList(list) { + setDataSource({ + total: dataSource.total, + loading: false, + dataList: list + }); + } }); const fetchData = useCallback(async () => { axiosToken?.cancel?.(); axiosToken = createAxiosToken(); - setLoading(true); + setDataSource((pre) => { + pre.loading = true; + return pre; + }); try { const params = { ..._.pickBy(queryParams, (val: any) => !!val) @@ -46,14 +63,20 @@ const Models: React.FC = () => { cancelToken: axiosToken.token }); if (!firstLoad) { - setDataSource(res.items); + setDataSource({ + dataList: res.items || [], + loading: false, + total: res.pagination.total + }); } - setTotal(res.pagination.total); } catch (error) { - setDataSource([]); + setDataSource({ + dataList: [], + loading: false, + total: dataSource.total + }); console.log('error', error); } finally { - setLoading(false); setFirstLoad(false); } }, [queryParams]); @@ -146,13 +169,13 @@ const Models: React.FC = () => { }} > ); diff --git a/src/pages/llmodels/style/model-card.less b/src/pages/llmodels/style/model-card.less index 00806485..9f3ecb09 100644 --- a/src/pages/llmodels/style/model-card.less +++ b/src/pages/llmodels/style/model-card.less @@ -1,6 +1,7 @@ .model-card-wrap { display: flex; flex-direction: column; + min-height: 72px; padding: 10px; border: 1px solid var(--ant-color-border); border-radius: var(--border-radius-base); diff --git a/src/pages/resources/components/gpus.tsx b/src/pages/resources/components/gpus.tsx index 5e22a3b6..1a4c5aeb 100644 --- a/src/pages/resources/components/gpus.tsx +++ b/src/pages/resources/components/gpus.tsx @@ -12,13 +12,20 @@ import { GPUDeviceItem } from '../config/types'; const { Column } = Table; const GPUList: React.FC = () => { + console.log('GPUList======'); const intl = useIntl(); const { sortOrder, setSortOrder } = useTableSort({ defaultSortOrder: 'descend' }); - const [dataSource, setDataSource] = useState([]); - const [total, setTotal] = useState(10); - const [loading, setLoading] = useState(false); + const [dataSource, setDataSource] = useState<{ + dataList: GPUDeviceItem[]; + loading: boolean; + total: number; + }>({ + dataList: [], + loading: false, + total: 0 + }); const [queryParams, setQueryParams] = useState({ page: 1, perPage: 10, @@ -39,20 +46,28 @@ const GPUList: React.FC = () => { }; const fetchData = async () => { - setLoading(true); + setDataSource((pre) => { + pre.loading = true; + return pre; + }); try { const params = { ..._.pickBy(queryParams, (val: any) => !!val) }; const res = await queryGpuDevicesList(params); - setDataSource(res.items); - setTotal(res.pagination.total); + setDataSource({ + dataList: res.items || [], + loading: false, + total: res.pagination.total + }); } catch (error) { - setDataSource([]); + setDataSource({ + dataList: [], + loading: false, + total: dataSource.total + }); console.log('error', error); - } finally { - setLoading(false); } }; const handleSearch = (e: any) => { @@ -95,15 +110,15 @@ const GPUList: React.FC = () => { } >
{ const modalRef = useRef(null); const rowSelection = useTableRowSelection(); const intl = useIntl(); - const [total, setTotal] = useState(0); - const [loading, setLoading] = useState(false); const [open, setOpen] = useState(false); - const [dataSource, setDataSource] = useState([]); + const [dataSource, setDataSource] = useState<{ + dataList: ListItem[]; + loading: boolean; + total: number; + }>({ + dataList: [], + loading: false, + total: 0 + }); const [queryParams, setQueryParams] = useState({ page: 1, perPage: 10, @@ -41,20 +47,28 @@ const Resources: React.FC = () => { }); const fetchData = async () => { - setLoading(true); + setDataSource((pre) => { + pre.loading = true; + return pre; + }); try { const params = { ..._.pickBy(queryParams, (val: any) => !!val) }; const res = await queryWorkersList(params); - setDataSource(res.items); - setTotal(res.pagination.total); + setDataSource({ + dataList: res.items, + loading: false, + total: res.pagination.total + }); } catch (error) { - setDataSource([]); + setDataSource({ + dataList: [], + loading: false, + total: dataSource.total + }); console.log('error', error); - } finally { - setLoading(false); } }; @@ -193,8 +207,8 @@ const Resources: React.FC = () => { } >
{ showSizeChanger: true, pageSize: queryParams.perPage, current: queryParams.page, - total: total, + total: dataSource.total, hideOnSinglePage: queryParams.perPage === 10, onChange: handlePageChange }} diff --git a/src/pages/resources/index.tsx b/src/pages/resources/index.tsx index e5ff6fb6..139cd165 100644 --- a/src/pages/resources/index.tsx +++ b/src/pages/resources/index.tsx @@ -2,7 +2,7 @@ import { PageContainer } from '@ant-design/pro-components'; import { useIntl } from '@umijs/max'; import type { TabsProps } from 'antd'; import { Tabs } from 'antd'; -import { useState } from 'react'; +import { useCallback, useState } from 'react'; import GPUs from './components/gpus'; import Workers from './components/workers'; @@ -19,14 +19,13 @@ const items: TabsProps['items'] = [ } ]; const Resources = () => { - console.log('resources======'); const [activeKey, setActiveKey] = useState('workers'); const intl = useIntl(); - const handleChangeTab = (key: string) => { + const handleChangeTab = useCallback((key: string) => { setActiveKey(key); - }; + }, []); return ( <> diff --git a/src/pages/users/index.tsx b/src/pages/users/index.tsx index 04c2e213..68bb31a0 100644 --- a/src/pages/users/index.tsx +++ b/src/pages/users/index.tsx @@ -33,10 +33,16 @@ const Users: React.FC = () => { }); const intl = useIntl(); const modalRef = useRef(null); - const [total, setTotal] = useState(0); const [openAddModal, setOpenAddModal] = useState(false); - const [loading, setLoading] = useState(false); - const [dataSource, setDataSource] = useState([]); + const [dataSource, setDataSource] = useState<{ + dataList: ListItem[]; + loading: boolean; + total: number; + }>({ + dataList: [], + loading: false, + total: 0 + }); const [action, setAction] = useState(PageAction.CREATE); const [title, setTitle] = useState(''); const [currentData, setCurrentData] = useState( @@ -64,20 +70,27 @@ const Users: React.FC = () => { } ]; const fetchData = async () => { - setLoading(true); + setDataSource((pre) => { + pre.loading = true; + return pre; + }); try { const params = { ..._.pickBy(queryParams, (val: any) => !!val) }; const res = await queryUsersList(params); - console.log('res=======', res); - setDataSource(res.items); - setTotal(res.pagination.total); + setDataSource({ + dataList: res.items || [], + loading: false, + total: res.pagination.total + }); } catch (error) { - setDataSource([]); + setDataSource({ + dataList: [], + loading: false, + total: dataSource.total + }); console.log('error', error); - } finally { - setLoading(false); } }; @@ -232,16 +245,16 @@ const Users: React.FC = () => { } >