chore: get start from deploying
This commit is contained in:
@@ -9,11 +9,21 @@ interface HeaderPrefixProps {
|
||||
onSelectAll?: (e: any) => void;
|
||||
indeterminate?: boolean;
|
||||
selectAll?: boolean;
|
||||
hasColumns?: boolean;
|
||||
}
|
||||
|
||||
const HeaderPrefix: React.FC<HeaderPrefixProps> = (props) => {
|
||||
const { expandable, enableSelection, onSelectAll, indeterminate, selectAll } =
|
||||
props;
|
||||
const {
|
||||
hasColumns,
|
||||
expandable,
|
||||
enableSelection,
|
||||
onSelectAll,
|
||||
indeterminate,
|
||||
selectAll
|
||||
} = props;
|
||||
if (!hasColumns) {
|
||||
return null;
|
||||
}
|
||||
if (expandable && enableSelection) {
|
||||
return (
|
||||
<div className="header-row-prefix-wrapper">
|
||||
|
||||
@@ -18,6 +18,7 @@ interface TableBodyProps {
|
||||
loadChildren?: any;
|
||||
loadChildrenAPI?: any;
|
||||
onCell?: any;
|
||||
empty?: React.ReactNode;
|
||||
}
|
||||
|
||||
const TableBody: React.FC<TableBodyProps> = ({
|
||||
@@ -34,12 +35,13 @@ const TableBody: React.FC<TableBodyProps> = ({
|
||||
loadChildren,
|
||||
loadChildrenAPI,
|
||||
columns,
|
||||
onCell
|
||||
onCell,
|
||||
empty
|
||||
}) => {
|
||||
if (!dataSource.length) {
|
||||
return (
|
||||
<div className="empty-wrapper">
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
||||
{empty || <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ const SealTable: React.FC<SealTableProps & { pagination: PaginationProps }> = (
|
||||
watchChildren,
|
||||
rowSelection,
|
||||
pagination,
|
||||
empty,
|
||||
renderChildren,
|
||||
loadChildren,
|
||||
loadChildrenAPI
|
||||
@@ -116,11 +117,13 @@ const SealTable: React.FC<SealTableProps & { pagination: PaginationProps }> = (
|
||||
onSelectAll={handleSelectAllChange}
|
||||
expandable={expandable}
|
||||
enableSelection={rowSelection?.enableSelection}
|
||||
hasColumns={parsedColumns.length > 0}
|
||||
></HeaderPrefix>
|
||||
<Header onSort={onSort} columns={parsedColumns}></Header>
|
||||
</div>
|
||||
<Spin spinning={loading}>
|
||||
<TableBody
|
||||
empty={empty}
|
||||
dataSource={props.dataSource}
|
||||
columns={parsedColumns}
|
||||
rowSelection={rowSelection}
|
||||
|
||||
@@ -18,6 +18,11 @@ import {
|
||||
ListItem as WorkerListItem
|
||||
} from '@/pages/resources/config/types';
|
||||
import { handleBatchRequest } from '@/utils';
|
||||
import {
|
||||
IS_FIRST_LOGIN,
|
||||
readState,
|
||||
writeState
|
||||
} from '@/utils/localstore/index';
|
||||
import {
|
||||
DeleteOutlined,
|
||||
DownOutlined,
|
||||
@@ -28,7 +33,17 @@ import {
|
||||
} from '@ant-design/icons';
|
||||
import { PageContainer } from '@ant-design/pro-components';
|
||||
import { Access, useAccess, useIntl, useNavigate } from '@umijs/max';
|
||||
import { Button, Dropdown, Input, Select, Space, Tooltip, message } from 'antd';
|
||||
import {
|
||||
Button,
|
||||
Dropdown,
|
||||
Empty,
|
||||
Input,
|
||||
Select,
|
||||
Space,
|
||||
Tooltip,
|
||||
Typography,
|
||||
message
|
||||
} from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import { useAtom } from 'jotai';
|
||||
import _ from 'lodash';
|
||||
@@ -45,6 +60,7 @@ import {
|
||||
} from '../apis';
|
||||
import {
|
||||
InstanceRealtimeLogStatus,
|
||||
backendOptionsMap,
|
||||
getSourceRepoConfigValue,
|
||||
modelCategories,
|
||||
modelCategoriesMap,
|
||||
@@ -76,6 +92,7 @@ interface ModelsProps {
|
||||
deleteIds?: number[];
|
||||
gpuDeviceList: GPUDeviceItem[];
|
||||
workerList: WorkerListItem[];
|
||||
catalogList?: any[];
|
||||
dataSource: ListItem[];
|
||||
loading: boolean;
|
||||
loadend: boolean;
|
||||
@@ -158,12 +175,15 @@ const Models: React.FC<ModelsProps> = ({
|
||||
dataSource,
|
||||
gpuDeviceList,
|
||||
workerList,
|
||||
catalogList,
|
||||
queryParams,
|
||||
loading,
|
||||
loadend,
|
||||
total
|
||||
}) => {
|
||||
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
|
||||
const [isFirstLogin, setIsFirstLogin] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [expandAtom, setExpandAtom] = useAtom(modelsExpandKeysAtom);
|
||||
const access = useAccess();
|
||||
const intl = useIntl();
|
||||
@@ -199,6 +219,17 @@ const Models: React.FC<ModelsProps> = ({
|
||||
});
|
||||
const modalRef = useRef<any>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!catalogList?.length) {
|
||||
return;
|
||||
}
|
||||
const getFirstLoginState = async () => {
|
||||
const is_first_login = await readState(IS_FIRST_LOGIN);
|
||||
setIsFirstLogin(is_first_login);
|
||||
};
|
||||
getFirstLoginState();
|
||||
}, [catalogList?.length]);
|
||||
|
||||
useHotkeys(
|
||||
HotKeys.NEW1.join(','),
|
||||
() => {
|
||||
@@ -667,8 +698,11 @@ const Models: React.FC<ModelsProps> = ({
|
||||
});
|
||||
};
|
||||
|
||||
const columns: SealColumnProps[] = useMemo(
|
||||
() => [
|
||||
const columns: SealColumnProps[] = useMemo(() => {
|
||||
if (isFirstLogin) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
{
|
||||
title: intl.formatMessage({ id: 'common.table.name' }),
|
||||
dataIndex: 'name',
|
||||
@@ -744,13 +778,67 @@ const Models: React.FC<ModelsProps> = ({
|
||||
/>
|
||||
)
|
||||
}
|
||||
],
|
||||
[sortOrder, intl]
|
||||
);
|
||||
];
|
||||
}, [sortOrder, intl, isFirstLogin]);
|
||||
|
||||
const handleOnClick = async () => {
|
||||
if (isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = catalogList?.find(
|
||||
(item) => item.backend === backendOptionsMap.llamaBox && item.size === 1.5
|
||||
);
|
||||
console.log('catalogList=======', data);
|
||||
try {
|
||||
if (data) {
|
||||
setIsLoading(true);
|
||||
const modelData = await createModel({
|
||||
data: data
|
||||
});
|
||||
writeState(IS_FIRST_LOGIN, false);
|
||||
setIsFirstLogin(false);
|
||||
setTimeout(() => {
|
||||
updateExpandedRowKeys([modelData.id]);
|
||||
}, 300);
|
||||
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
||||
handleSearch?.();
|
||||
}
|
||||
} catch (error) {
|
||||
// ingore
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
const renderEmpty = useMemo(() => {
|
||||
if (dataSource.length || !isFirstLogin) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className="flex-column justify-center flex-center"
|
||||
style={{ height: 300 }}
|
||||
>
|
||||
<Empty description=""></Empty>
|
||||
<Typography.Title level={4} style={{ marginBottom: 30 }}>
|
||||
No Models yet!
|
||||
</Typography.Title>
|
||||
<div>
|
||||
<Button type="primary" onClick={handleOnClick} loading={isLoading}>
|
||||
<span style={{ fontSize: 13 }}>Get started with</span>
|
||||
<span style={{ fontSize: 14, fontWeight: 700 }}>
|
||||
DeepSeek-R1-Distill-Qwen-1.5B
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}, [dataSource.length, isFirstLogin, isLoading]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageContainer
|
||||
className="models-page-container"
|
||||
ghost
|
||||
header={{
|
||||
title: intl.formatMessage({ id: 'models.title' }),
|
||||
@@ -760,6 +848,7 @@ const Models: React.FC<ModelsProps> = ({
|
||||
>
|
||||
<PageTools
|
||||
marginBottom={22}
|
||||
style={{ display: isFirstLogin ? 'none' : 'flex' }}
|
||||
left={
|
||||
<Space>
|
||||
<Input
|
||||
@@ -856,7 +945,9 @@ const Models: React.FC<ModelsProps> = ({
|
||||
</Space>
|
||||
}
|
||||
></PageTools>
|
||||
|
||||
<SealTable
|
||||
empty={renderEmpty}
|
||||
columns={columns}
|
||||
dataSource={dataSource}
|
||||
rowSelection={rowSelection}
|
||||
|
||||
@@ -6,12 +6,15 @@ import {
|
||||
GPUDeviceItem,
|
||||
ListItem as WokerListItem
|
||||
} from '@/pages/resources/config/types';
|
||||
import { IS_FIRST_LOGIN, readState } from '@/utils/localstore';
|
||||
import _ from 'lodash';
|
||||
import qs from 'query-string';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
MODELS_API,
|
||||
MODEL_INSTANCE_API,
|
||||
queryCatalogItemSpec,
|
||||
queryCatalogList,
|
||||
queryModelsInstances,
|
||||
queryModelsList
|
||||
} from './apis';
|
||||
@@ -37,6 +40,7 @@ const Models: React.FC = () => {
|
||||
total: 0
|
||||
});
|
||||
|
||||
const [catalogList, setCatalogList] = useState<any[]>([]);
|
||||
const [gpuDeviceList, setGpuDeviceList] = useState<GPUDeviceItem[]>([]);
|
||||
const [workerList, setWorkerList] = useState<WokerListItem[]>([]);
|
||||
const chunkRequedtRef = useRef<any>();
|
||||
@@ -243,6 +247,33 @@ const Models: React.FC = () => {
|
||||
};
|
||||
}, [queryParams]);
|
||||
|
||||
useEffect(() => {
|
||||
const getCataLogList = async () => {
|
||||
const isFirstLogin = readState(IS_FIRST_LOGIN);
|
||||
if (!isFirstLogin) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res: any = await queryCatalogList({
|
||||
search: 'DeepSeek R1',
|
||||
page: 1
|
||||
});
|
||||
const name = _.toLower(res?.items[0]?.name).replace(/\s/g, '-') || '';
|
||||
const catalogSpecs: any = await queryCatalogItemSpec({
|
||||
id: res?.items[0]?.id
|
||||
});
|
||||
const list = catalogSpecs?.items?.map((item: any) => {
|
||||
item.name = name;
|
||||
return item;
|
||||
});
|
||||
setCatalogList(list || []);
|
||||
} catch (error) {
|
||||
// ignore
|
||||
}
|
||||
};
|
||||
getCataLogList();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
createModelsChunkRequest();
|
||||
}, [createModelsChunkRequest]);
|
||||
@@ -311,6 +342,7 @@ const Models: React.FC = () => {
|
||||
deleteIds={dataSource.deletedIds}
|
||||
gpuDeviceList={gpuDeviceList}
|
||||
workerList={workerList}
|
||||
catalogList={catalogList}
|
||||
></TableList>
|
||||
</TableContext.Provider>
|
||||
);
|
||||
|
||||
@@ -8,13 +8,14 @@ import {
|
||||
removeRememberMe
|
||||
} from '@/utils/localstore/index';
|
||||
import { LockOutlined, UserOutlined } from '@ant-design/icons';
|
||||
import { history, useIntl, useModel } from '@umijs/max';
|
||||
import { useIntl, useModel } from '@umijs/max';
|
||||
import { Button, Checkbox, Form } from 'antd';
|
||||
import CryptoJS from 'crypto-js';
|
||||
import { useAtom } from 'jotai';
|
||||
import { memo, useEffect, useMemo } from 'react';
|
||||
import { flushSync } from 'react-dom';
|
||||
import { login } from '../apis';
|
||||
import { checkDefaultPage } from '../utils';
|
||||
|
||||
const REMEMBER_ME_KEY = 'r_m';
|
||||
const CRYPT_TEXT = 'seal';
|
||||
@@ -50,11 +51,10 @@ const LoginForm = () => {
|
||||
);
|
||||
}, [intl]);
|
||||
|
||||
const gotoDefaultPage = (userInfo: any) => {
|
||||
const pathname =
|
||||
userInfo && userInfo?.is_admin ? '/dashboard' : '/playground';
|
||||
history.push(pathname);
|
||||
const gotoDefaultPage = async (userInfo: any) => {
|
||||
checkDefaultPage(userInfo, true);
|
||||
};
|
||||
|
||||
const fetchUserInfo = async () => {
|
||||
const userInfo = await initialState?.fetchUserInfo?.();
|
||||
|
||||
@@ -108,6 +108,7 @@ const LoginForm = () => {
|
||||
username: values.username,
|
||||
password: values.password
|
||||
});
|
||||
|
||||
const userInfo = await fetchUserInfo();
|
||||
setUserInfo(userInfo);
|
||||
if (values.autoLogin) {
|
||||
|
||||
@@ -2,11 +2,12 @@ import { initialPasswordAtom, userAtom } from '@/atoms/user';
|
||||
import SealInput from '@/components/seal-form/seal-input';
|
||||
import { PasswordReg } from '@/config';
|
||||
import { GlobalOutlined, LockOutlined } from '@ant-design/icons';
|
||||
import { SelectLang, history, useIntl } from '@umijs/max';
|
||||
import { SelectLang, useIntl } from '@umijs/max';
|
||||
import { Button, Form, message } from 'antd';
|
||||
import CryptoJS from 'crypto-js';
|
||||
import { useAtom } from 'jotai';
|
||||
import { updatePassword } from '../apis';
|
||||
import { checkDefaultPage } from '../utils';
|
||||
|
||||
const CRYPT_TEXT = 'seal';
|
||||
|
||||
@@ -16,10 +17,8 @@ const PasswordForm: React.FC = () => {
|
||||
|
||||
const [userInfo, setUserInfo] = useAtom(userAtom);
|
||||
const [initialPassword, setInitialPassword] = useAtom(initialPasswordAtom);
|
||||
const gotoDefaultPage = (userInfo: any) => {
|
||||
const pathname =
|
||||
userInfo && userInfo?.is_admin ? '/dashboard' : '/playground';
|
||||
history.push(pathname);
|
||||
const gotoDefaultPage = async (userInfo: any) => {
|
||||
checkDefaultPage(userInfo, false);
|
||||
};
|
||||
|
||||
const decryptPassword = (password: string) => {
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
import { userAtom } from '@/atoms/user';
|
||||
import Footer from '@/components/footer';
|
||||
import { history, useModel } from '@umijs/max';
|
||||
import { useModel } from '@umijs/max';
|
||||
import { useAtom } from 'jotai';
|
||||
import { useEffect } from 'react';
|
||||
import LoginForm from './components/login-form';
|
||||
import PasswordForm from './components/password-form';
|
||||
import styles from './components/styles.less';
|
||||
import { checkDefaultPage } from './utils';
|
||||
|
||||
const Login = () => {
|
||||
const [userInfo, setUserInfo] = useAtom(userAtom);
|
||||
const { initialState, setInitialState } = useModel('@@initialState') || {};
|
||||
|
||||
const gotoDefaultPage = (userInfo: any) => {
|
||||
const gotoDefaultPage = async (userInfo: any) => {
|
||||
if (!userInfo || userInfo?.require_password_change) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkDefaultPage(userInfo, true);
|
||||
if (!initialState?.currentUser) {
|
||||
setInitialState((s: any) => ({
|
||||
...s,
|
||||
currentUser: userInfo
|
||||
}));
|
||||
}
|
||||
const pathname = userInfo?.is_admin ? '/dashboard' : '/playground';
|
||||
|
||||
history.push(pathname, { replace: true });
|
||||
};
|
||||
useEffect(() => {
|
||||
gotoDefaultPage(userInfo);
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { isOnline } from '@/utils';
|
||||
import {
|
||||
IS_FIRST_LOGIN,
|
||||
readState,
|
||||
writeState
|
||||
} from '@/utils/localstore/index';
|
||||
import { history } from '@umijs/max';
|
||||
|
||||
export const checkDefaultPage = async (userInfo: any, replace: boolean) => {
|
||||
const isFirstLogin = await readState(IS_FIRST_LOGIN);
|
||||
|
||||
if (isFirstLogin === null && isOnline()) {
|
||||
writeState(IS_FIRST_LOGIN, true);
|
||||
const pathname =
|
||||
userInfo && userInfo?.is_admin ? '/models/list' : '/playground';
|
||||
history.push(pathname);
|
||||
return;
|
||||
}
|
||||
await writeState(IS_FIRST_LOGIN, false);
|
||||
|
||||
const pathname = userInfo?.is_admin ? '/dashboard' : '/playground';
|
||||
|
||||
history.push(pathname, { replace: replace });
|
||||
};
|
||||
@@ -192,3 +192,8 @@ export const base64ToFile = (base64String: string, fileName: string) => {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// check onlinestatus
|
||||
export const isOnline = () => {
|
||||
return window.navigator.onLine;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import localStore from './store';
|
||||
|
||||
const IS_FIRST_LOGIN = 'is_first_login';
|
||||
|
||||
const store = localStore.createInstance({ name: '_xWXJKJ_S1Sna_' });
|
||||
|
||||
const rememberMe = (key: string, data: any) => {
|
||||
@@ -20,6 +22,25 @@ const removeRememberMe = (key: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
export { getRememberMe, rememberMe, removeRememberMe };
|
||||
const readState = (key: string) => {
|
||||
if (store) {
|
||||
return store.getItem(key);
|
||||
}
|
||||
};
|
||||
|
||||
const writeState = (key: string, data: any) => {
|
||||
if (store) {
|
||||
store.setItem(key, data);
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
IS_FIRST_LOGIN,
|
||||
getRememberMe,
|
||||
readState,
|
||||
rememberMe,
|
||||
removeRememberMe,
|
||||
writeState
|
||||
};
|
||||
|
||||
export default store;
|
||||
|
||||
Reference in New Issue
Block a user