chore: list state merge

main
jialin 2 years ago
parent 77f38394e6
commit dea99ff70a

@ -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;

@ -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.'
};

@ -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}】 输入框中输入你要部署的模型。'
};

@ -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<any>(null);
const [dataSource, setDataSource] = useState<ListItem[]>([]);
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<PageActionType>(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 = () => {
}
></PageTools>
<Table
dataSource={dataSource}
dataSource={dataSource.dataList}
rowSelection={rowSelection}
loading={loading}
loading={dataSource.loading}
rowKey="id"
onChange={handleTableChange}
pagination={{
showSizeChanger: true,
pageSize: queryParams.perPage,
current: queryParams.page,
total: total,
total: dataSource.total,
hideOnSinglePage: queryParams.perPage === 10,
onChange: handlePageChange
}}

@ -1,14 +1,11 @@
import { convertFileSize } from '@/utils';
import { SearchOutlined } from '@ant-design/icons';
import {
GGMLQuantizationType,
GGUF_QUANT_DESCRIPTIONS
} from '@huggingface/gguf';
import { Button, Col, Empty, Row, Space, Spin } from 'antd';
import classNames from 'classnames';
import _ from 'lodash';
import { useEffect, useState } from 'react';
import { queryHuggingfaceModelFiles } from '../apis';
import FileType from '../config/file-type';
import '../style/hf-model-file.less';
import TitleWrapper from './title-wrapper';
@ -32,6 +29,7 @@ const HFModelFile: React.FC<HFModelFileProps> = (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<HFModelFileProps> = (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 <span className="tag-item">{quanType}</span>;
}
return null;

@ -25,6 +25,7 @@ interface HFModelItemProps {
const HFModelItem: React.FC<HFModelItemProps> = (props) => {
return (
<div
tabIndex={0}
className={classNames('hf-model-item', {
active: props.active
})}
@ -41,8 +42,8 @@ const HFModelItem: React.FC<HFModelItemProps> = (props) => {
<Space size={16}>
{props.task && (
<Tag
color="rgb(236, 240, 242)"
style={{
backgroundColor: 'var(--color-white-1)',
marginRight: 0
}}
>
@ -68,9 +69,10 @@ const HFModelItem: React.FC<HFModelItemProps> = (props) => {
) : (
<div className="flex-between">
<Space size={10}>
{_.map(props.tags, (tag: string) => {
{_.map(props.tags, (tag: string, index: string) => {
return (
<Tag
key={index}
style={{
backgroundColor: 'var(--color-white-1)',
marginRight: 0

@ -1,4 +1,5 @@
import { Space, Tag } from 'antd';
import IconFont from '@/components/icon-font';
import { Button, Empty, Tag } from 'antd';
import React, { useEffect, useState } from 'react';
import { queryHuggingfaceModelDetail } from '../apis';
import '../style/model-card.less';
@ -10,11 +11,11 @@ const ModelCard: React.FC<{ repo: string }> = (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) => {
<span>Model Card</span>
</TitleWrapper>
<div className="wrapper">
<div className="model-card-wrap">
<div className="title">{modelData.id}</div>
<Space>
<Tag className="tag-item">
<span className="m-r-5">Architecture:</span>
{modelData.config?.model_type}
</Tag>
</Space>
</div>
{modelData ? (
<div className="model-card-wrap">
<div className="title">{modelData.id}</div>
<div className="flex-between flex-center">
<Tag className="tag-item">
<span className="m-r-5">Architecture:</span>
{modelData.config?.model_type}
</Tag>
<Button
type="link"
target="_blank"
href={`https://huggingface.co/${modelData.id}`}
>
View in Hugging Face
<IconFont type="icon-external-link"></IconFont>
</Button>
</div>
</div>
) : (
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE}></Empty>
)}
</div>
</>
);

@ -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<any>(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 && (
<Tag style={{ marginRight: 0 }}>

@ -165,7 +165,11 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
value={sortType}
onChange={handleSortChange}
labelRender={({ label }) => {
return <span>Sort: {label}</span>;
return (
<span>
{intl.formatMessage({ id: 'model.deploy.sort' })}: {label}
</span>
);
}}
options={modelFilesSortOptions}
size="middle"
@ -203,11 +207,6 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
return (
<div style={{ flex: 1 }}>
<div className={SearchStyle['search-bar']}>
{/* <RadioButtons
options={sourceList}
value={modelSource}
onChange={handleSourceChange}
></RadioButtons> */}
{modelSource === modelSourceMap.huggingface_value ? (
renderHFSearch()
) : (

@ -27,7 +27,7 @@ const SearchResult: React.FC<SearchResultProps> = (props) => {
<Row gutter={[16, 16]}>
{resultList.map((item, index) => (
<Col span={24} key={item.name}>
<div onClick={(e) => handleSelect(e, item)} tabIndex={0}>
<div onClick={(e) => handleSelect(e, item)}>
<HFModelItem
source={source}
tags={item.tags}

@ -22,7 +22,7 @@ import { Access, useAccess, useIntl, useNavigate } from '@umijs/max';
import { Button, Dropdown, Input, Space, Tag, message } from 'antd';
import dayjs from 'dayjs';
import _ from 'lodash';
import { useCallback, useRef, useState } from 'react';
import { memo, useCallback, useRef, useState } from 'react';
import {
MODELS_API,
MODEL_INSTANCE_API,
@ -502,4 +502,4 @@ const Models: React.FC<ModelsProps> = ({
);
};
export default Models;
export default memo(Models);

@ -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;

@ -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',

@ -16,9 +16,17 @@ const Models: React.FC = () => {
const { setChunkRequest: setModelInstanceChunkRequest } =
useSetChunkRequest();
const [total, setTotal] = useState(0);
const [modelInstances, setModelInstances] = useState<any[]>([]);
const [loading, setLoading] = useState(false);
const [dataSource, setDataSource] = useState<ListItem[]>([]);
const [modelInstances, setModelInstances] = useState<any[]>([]);
const [dataSource, setDataSource] = useState<{
dataList: ListItem[];
loading: boolean;
total: number;
}>({
dataList: [],
loading: false,
total: 0
});
const [firstLoad, setFirstLoad] = useState(true);
const chunkRequedtRef = useRef<any>();
const chunkInstanceRequedtRef = useRef<any>();
@ -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 = () => {
}}
>
<TableList
dataSource={dataSource}
dataSource={dataSource.dataList}
handleNameChange={handleNameChange}
handleSearch={handleSearch}
handlePageChange={handlePageChange}
queryParams={queryParams}
loading={loading}
total={total}
loading={dataSource.loading}
total={dataSource.total}
></TableList>
</TableContext.Provider>
);

@ -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);

@ -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<GPUDeviceItem[]>([]);
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 = () => {
}
></PageTools>
<Table
dataSource={dataSource}
loading={loading}
dataSource={dataSource.dataList}
loading={dataSource.loading}
rowKey="id"
onChange={handleTableChange}
pagination={{
showSizeChanger: true,
pageSize: queryParams.perPage,
current: queryParams.page,
total: total,
total: dataSource.total,
hideOnSinglePage: queryParams.perPage === 10,
onChange: handlePageChange
}}

@ -30,10 +30,16 @@ const Resources: React.FC = () => {
const modalRef = useRef<any>(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<ListItem[]>([]);
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 = () => {
}
></PageTools>
<Table
dataSource={dataSource}
loading={loading}
dataSource={dataSource.dataList}
loading={dataSource.loading}
rowKey="id"
onChange={handleTableChange}
rowSelection={rowSelection}
@ -202,7 +216,7 @@ const Resources: React.FC = () => {
showSizeChanger: true,
pageSize: queryParams.perPage,
current: queryParams.page,
total: total,
total: dataSource.total,
hideOnSinglePage: queryParams.perPage === 10,
onChange: handlePageChange
}}

@ -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 (
<>

@ -33,10 +33,16 @@ const Users: React.FC = () => {
});
const intl = useIntl();
const modalRef = useRef<any>(null);
const [total, setTotal] = useState(0);
const [openAddModal, setOpenAddModal] = useState(false);
const [loading, setLoading] = useState(false);
const [dataSource, setDataSource] = useState<ListItem[]>([]);
const [dataSource, setDataSource] = useState<{
dataList: ListItem[];
loading: boolean;
total: number;
}>({
dataList: [],
loading: false,
total: 0
});
const [action, setAction] = useState<PageActionType>(PageAction.CREATE);
const [title, setTitle] = useState<string>('');
const [currentData, setCurrentData] = useState<ListItem | undefined>(
@ -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 = () => {
}
></PageTools>
<Table
dataSource={dataSource}
dataSource={dataSource.dataList}
rowSelection={rowSelection}
loading={loading}
loading={dataSource.loading}
rowKey="id"
onChange={handleTableChange}
pagination={{
showSizeChanger: true,
pageSize: queryParams.perPage,
current: queryParams.page,
total: total,
total: dataSource.total,
hideOnSinglePage: queryParams.perPage === 10,
onChange: handlePageChange
}}

Loading…
Cancel
Save