diff --git a/src/assets/styles/menu.less b/src/assets/styles/menu.less index 8bacfe1c..d3081ce3 100644 --- a/src/assets/styles/menu.less +++ b/src/assets/styles/menu.less @@ -3,8 +3,7 @@ .ant-layout-sider-children { border-inline: none; border-radius: 0; - // padding-inline: 16px; - padding-block-end: 12px; + padding-block-end: 8px; } .umi-plugin-layout-right { diff --git a/src/global.less b/src/global.less index 50aeb7b5..c009e11f 100644 --- a/src/global.less +++ b/src/global.less @@ -218,6 +218,11 @@ body { background-color: var(--color-fill-1); height: 100%; + .ant-pro-sider-footer { + padding-block: 2px 0; + padding-left: 6px; + } + .ant-pro-sider-actions-list-item { padding: 0; @@ -228,7 +233,6 @@ body { .ant-pro-sider-logo { position: relative; - padding-left: 18px; padding-block: 12px; border-block-end: none; } diff --git a/src/layouts/index.tsx b/src/layouts/index.tsx index 75b124a9..eb94b87e 100644 --- a/src/layouts/index.tsx +++ b/src/layouts/index.tsx @@ -1,7 +1,7 @@ // @ts-nocheck import { routeCacheAtom, setRouteCache } from '@/atoms/route-cache'; -import { userAtom } from '@/atoms/user'; +import { GPUStackVersionAtom, userAtom } from '@/atoms/user'; import DarkMask from '@/components/dark-mask'; import IconFont from '@/components/icon-font'; import routeCachekey from '@/config/route-cachekey'; @@ -112,6 +112,7 @@ export default (props: any) => { const navigate = useNavigate(); const intl = useIntl(); const { clientRoutes } = useAppData(); + const [version] = useAtom(GPUStackVersionAtom); const initialInfo = (useModel && useModel('@@initialState')) || { initialState: undefined, @@ -311,6 +312,9 @@ export default (props: any) => { onCollapse={onCollapse} onMenuHeaderClick={onMenuHeaderClick} menuHeaderRender={renderMenuHeader} + menuFooterRender={() => ( + {version?.version} + )} extra={[ = forwardRef( const jsonData = yaml2Json(content); const exsistingVersions = Object.keys(jsonData.version_configs || {}); + // Check backend version rules if ( actionStatus.isBuiltIn && - exsistingVersions.find((v) => !v.endsWith('-custom')) + exsistingVersions.find((v) => !v?.endsWith('-custom')) ) { setError(intl.formatMessage({ id: 'backend.version.no.tips' })); return false; } + + // Check custom backend name rule if ( !actionStatus.isBuiltIn && actionStatus.action === PageAction.CREATE && - !jsonData.backend_name.endsWith('-custom') + !jsonData.backend_name?.endsWith('-custom') ) { setError(intl.formatMessage({ id: 'backend.backend.rules.custom' })); return false; } + // Check default version exists + if ( + jsonData.default_version && + !exsistingVersions.includes(jsonData.default_version) && + !actionStatus.isBuiltIn + ) { + // the default_version must be in [existingVersions] + setError( + intl.formatMessage( + { id: 'backend.version.default.not.exists' }, + { versions: exsistingVersions.join(', ') } + ) + ); + return false; + } + return content; } catch (error) { + setError((error as Error).message); return false; } }; diff --git a/src/pages/backends/components/yaml-editor.tsx b/src/pages/backends/components/yaml-editor.tsx index a08f15d3..3bdcb365 100644 --- a/src/pages/backends/components/yaml-editor.tsx +++ b/src/pages/backends/components/yaml-editor.tsx @@ -88,12 +88,12 @@ const ViewerEditor: React.FC = forwardRef((props, ref) => { } }; + // Currently, do not use this function, but keep it for future validation needs const getMarkers = () => { const uri = editorRef.current?.getModel()?.uri; const markers = monacoRef.current?.editor.getModelMarkers({ resource: uri }); - console.log('Validation markers:', uri, markers); return markers; }; diff --git a/src/pages/llmodels/apis/index.ts b/src/pages/llmodels/apis/index.ts index 24b7ad9a..f9665c9c 100644 --- a/src/pages/llmodels/apis/index.ts +++ b/src/pages/llmodels/apis/index.ts @@ -367,7 +367,7 @@ export async function queryCatalogList( } export async function queryCatalogItemSpec( - params: { id: number; cluster_id: number }, + params: { id: number; cluster_id: number | null }, options?: any ) { return request>( diff --git a/src/pages/llmodels/catalog.tsx b/src/pages/llmodels/catalog.tsx index 180a74da..22e5d12e 100644 --- a/src/pages/llmodels/catalog.tsx +++ b/src/pages/llmodels/catalog.tsx @@ -198,7 +198,10 @@ const Catalog: React.FC = () => { const getCatalogSource = async () => { try { const id = dataSource.dataList?.[0]?.id; - const res: any = await queryCatalogItemSpec({ id, cluster_id: 0 }); + const res: any = await queryCatalogItemSpec({ + id, + cluster_id: null + }); sourceRef.current = res?.items?.[0]?.source; } catch (error) {} }; diff --git a/src/pages/llmodels/components/deploy-builtin-modal.tsx b/src/pages/llmodels/components/deploy-builtin-modal.tsx index 4c7a207c..5c4b0227 100644 --- a/src/pages/llmodels/components/deploy-builtin-modal.tsx +++ b/src/pages/llmodels/components/deploy-builtin-modal.tsx @@ -250,6 +250,16 @@ const AddModal: React.FC = (props) => { cluster_id: clusterId, name }); + + // If no avaliable gpus for the model, show warning message + if (!res.items.length) { + setWarningStatus({ + show: true, + type: 'warning', + message: intl.formatMessage({ id: 'models.catalog.nogpus.tips' }) + }); + return; + } handleCheckCompatibility(allValues); } catch (error) { // ignore diff --git a/src/pages/llmodels/forms/index.tsx b/src/pages/llmodels/forms/index.tsx index 16f4bf99..6c6e0d01 100644 --- a/src/pages/llmodels/forms/index.tsx +++ b/src/pages/llmodels/forms/index.tsx @@ -103,7 +103,6 @@ const DataForm: React.FC = forwardRef((props, ref) => { const [form] = Form.useForm(); const intl = useIntl(); const [activeKey, setActiveKey] = React.useState([]); - const scheduleType = Form.useWatch('scheduleType', form); const [target, setTarget] = React.useState(TABKeysMap.BASIC); console.log('scroller instance in form:', osInstance, scrollEventElement); diff --git a/src/pages/llmodels/hooks/index.ts b/src/pages/llmodels/hooks/index.ts index dca99982..4c2953f4 100644 --- a/src/pages/llmodels/hooks/index.ts +++ b/src/pages/llmodels/hooks/index.ts @@ -296,7 +296,12 @@ export const useCheckCompatibility = () => { title: intl.formatMessage({ id: 'models.form.check.passed' }), message: intl.formatMessage({ id: messageId }, { ram, vram }) }; - } else if (othersAvailable) { + } else if ( + othersAvailable && + !scheduling_messages?.length && + !compatibility_messages?.length + ) { + // no specific messages, but other clusters are available msgData = { title: intl.formatMessage({ id: 'models.form.check.clusterUnavailable' diff --git a/src/pages/login/apis/index.ts b/src/pages/login/apis/index.ts index 13ab7789..b4821ad4 100644 --- a/src/pages/login/apis/index.ts +++ b/src/pages/login/apis/index.ts @@ -24,11 +24,14 @@ export const login = async ( }; export const logout = async (userInfo?: any) => { - await request(`${AUTH_API}/logout`, { + const res = await request(`${AUTH_API}/logout`, { method: 'POST' }); clearStorageUserSettings(); clearAtomStorage(userAtom); + if (res.logout_url) { + window.location.href = res.logout_url; + } return; };