fix: filter non-normal gguf files

This commit is contained in:
jialin
2025-08-14 11:03:55 +08:00
parent ccfdb99348
commit d6dce4021d
12 changed files with 107 additions and 51 deletions
+19 -1
View File
@@ -2,9 +2,27 @@ import langConfigMap from '@/locales/lang-config-map';
import { GlobalOutlined } from '@ant-design/icons'; import { GlobalOutlined } from '@ant-design/icons';
import { getAllLocales, setLocale } from '@umijs/max'; import { getAllLocales, setLocale } from '@umijs/max';
import { Dropdown } from 'antd'; import { Dropdown } from 'antd';
import { createStyles } from 'antd-style';
import { get } from 'lodash'; import { get } from 'lodash';
const useStyles = createStyles(({ token, css }) => ({
button: css`
color: ${token.colorText};
padding: 0 12px;
cursor: pointer;
.anticon {
color: ${token.colorText};
}
&:hover {
.anticon {
color: ${token.colorTextTertiary};
}
}
`
}));
const LangSelect = () => { const LangSelect = () => {
const { styles } = useStyles();
const allLocals = getAllLocales(); const allLocals = getAllLocales();
const items = allLocals.map((key) => { const items = allLocals.map((key) => {
return { return {
@@ -25,7 +43,7 @@ const LangSelect = () => {
return ( return (
<Dropdown menu={{ items }}> <Dropdown menu={{ items }}>
<span style={{ padding: '0 12px', cursor: 'pointer' }}> <span className={styles.button}>
<GlobalOutlined /> <GlobalOutlined />
</span> </span>
</Dropdown> </Dropdown>
@@ -15,9 +15,13 @@ const useStyles = createStyles(({ token, css }) => ({
padding: 0 8px; padding: 0 8px;
&:hover { &:hover {
color: ${token.colorTextTertiary}; color: ${token.colorTextTertiary};
.anticon {
color: ${token.colorTextTertiary};
}
} }
.anticon { .anticon {
font-size: 16px; font-size: 16px;
color: ${token.colorText};
} }
.icon-auto { .icon-auto {
font-size: 20px; font-size: 20px;
-1
View File
@@ -570,7 +570,6 @@ body {
.ant-message-notice-content { .ant-message-notice-content {
border-radius: var(--border-radius-base) !important; border-radius: var(--border-radius-base) !important;
line-height: 2;
max-width: 300px; max-width: 300px;
text-align: left; text-align: left;
word-break: break-word; word-break: break-word;
+3 -1
View File
@@ -255,5 +255,7 @@ export default {
'common.login.auth': 'Authenticating...', 'common.login.auth': 'Authenticating...',
'common.login.auth.failed': 'Authentication failed', 'common.login.auth.failed': 'Authentication failed',
'common.login.password': 'Log in with Password', 'common.login.password': 'Log in with Password',
'common.external.login': 'Log in with {type}' 'common.external.login': 'Log in with {type}',
'common.sso.noConfig':
'Single sign-on is not enabled on this system. Please contact your administrator.'
}; };
+4 -1
View File
@@ -255,7 +255,9 @@ export default {
'common.login.auth': 'Authenticating...', 'common.login.auth': 'Authenticating...',
'common.login.auth.failed': 'Authentication failed', 'common.login.auth.failed': 'Authentication failed',
'common.login.password': 'Log in with Password', 'common.login.password': 'Log in with Password',
'common.external.login': 'Log in with {type}' 'common.external.login': 'Log in with {type}',
'common.sso.noConfig':
'Single sign-on is not enabled on this system. Please contact your administrator.'
}; };
// ========== To-Do: Translate Keys (Remove After Translation) ========== // ========== To-Do: Translate Keys (Remove After Translation) ==========
@@ -278,4 +280,5 @@ export default {
// 17. 'common.login.auth.failed': 'Authentication failed', // 17. 'common.login.auth.failed': 'Authentication failed',
// 18. 'common.external.login': 'Log in with {type}' // 18. 'common.external.login': 'Log in with {type}'
// 19. 'common.login.password': 'Log in with Password', // 19. 'common.login.password': 'Log in with Password',
// 20. 'common.sso.noConfig': 'Single sign-on is not enabled on this system. Please contact your administrator.'
// ========== End of To-Do List ========== // ========== End of To-Do List ==========
+4 -1
View File
@@ -254,10 +254,13 @@ export default {
'common.login.auth': 'Аутентификация...', 'common.login.auth': 'Аутентификация...',
'common.login.auth.failed': 'Ошибка аутентификации', 'common.login.auth.failed': 'Ошибка аутентификации',
'common.login.password': 'Log in with Password', 'common.login.password': 'Log in with Password',
'common.external.login': 'Log in with {type}' 'common.external.login': 'Log in with {type}',
'common.sso.noConfig':
'Single sign-on is not enabled on this system. Please contact your administrator.'
}; };
// ========== To-Do: Translate Keys (Remove After Translation) ========== // ========== To-Do: Translate Keys (Remove After Translation) ==========
// 1. 'common.external.login': 'Log in with {type}' // 1. 'common.external.login': 'Log in with {type}'
// 2. 'common.login.password': 'Log in with Password' // 2. 'common.login.password': 'Log in with Password'
// 3. 'common.sso.noConfig': 'Single sign-on is not enabled on this system. Please contact your administrator.'
// ========== End of To-Do List ========== // ========== End of To-Do List ==========
+2 -1
View File
@@ -248,5 +248,6 @@ export default {
'common.login.auth': '认证中...', 'common.login.auth': '认证中...',
'common.login.auth.failed': '认证失败', 'common.login.auth.failed': '认证失败',
'common.login.password': '使用密码登录', 'common.login.password': '使用密码登录',
'common.external.login': '使用 {type} 登录' 'common.external.login': '使用 {type} 登录',
'common.sso.noConfig': '该系统未启用单点登录,请联系管理员。'
}; };
@@ -161,6 +161,11 @@ const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => {
return filterRegGGUF.test(file.path) || _.includes(file.path, '.gguf'); return filterRegGGUF.test(file.path) || _.includes(file.path, '.gguf');
}; };
const isNormalGGUFModelFile = (filename: string) => {
const file = filename?.toLowerCase() ?? '';
return file.indexOf('mmproj') === -1 && file.indexOf('imatrix') === -1;
};
// hugging face files // hugging face files
const getHuggingfaceFiles = async () => { const getHuggingfaceFiles = async () => {
try { try {
@@ -175,7 +180,7 @@ const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => {
}); });
const list = _.filter(fileList, (file: any) => { const list = _.filter(fileList, (file: any) => {
return hfFileFilter(file) && file.path.indexOf('mmproj') === -1; return hfFileFilter(file) && isNormalGGUFModelFile(file.path);
}); });
return list; return list;
@@ -188,7 +193,7 @@ const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => {
return ( return (
filterRegGGUF.test(file.Path) && filterRegGGUF.test(file.Path) &&
file.Type === 'blob' && file.Type === 'blob' &&
file.Path.indexOf('mmproj') === -1 isNormalGGUFModelFile(file.Path)
); );
}; };
+11 -28
View File
@@ -1,7 +1,5 @@
import LogoIcon from '@/assets/images/gpustack-logo.png'; import LogoIcon from '@/assets/images/gpustack-logo.png';
import { userAtom } from '@/atoms/user'; import { userAtom } from '@/atoms/user';
import LangSelect from '@/components/lang-select';
import ThemeDropActions from '@/components/theme-toggle/theme-drop-actions';
import { useIntl, useModel } from '@umijs/max'; import { useIntl, useModel } from '@umijs/max';
import { Button, Divider, Form, Spin, message } from 'antd'; import { Button, Divider, Form, Spin, message } from 'antd';
import { createStyles } from 'antd-style'; import { createStyles } from 'antd-style';
@@ -52,22 +50,6 @@ const DividerWrapper = styled(Divider)`
`; `;
const useStyles = createStyles(({ token, css }) => ({ const useStyles = createStyles(({ token, css }) => ({
header: css`
display: flex;
align-items: center;
gap: 8px;
position: fixed;
right: 0;
top: 0;
height: 60px;
padding: 20px;
.anticon-global {
color: ${token.colorText};
}
.anticon:hover {
color: ${token.colorTextTertiary};
}
`,
errorMessage: css` errorMessage: css`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -99,6 +81,7 @@ const LoginForm = () => {
const intl = useIntl(); const intl = useIntl();
const [form] = Form.useForm(); const [form] = Form.useForm();
const [isPassword, setIsPassword] = useState(false); const [isPassword, setIsPassword] = useState(false);
const [loading, setLoading] = useState(false);
const renderWelCome = () => { const renderWelCome = () => {
return ( return (
@@ -161,6 +144,7 @@ const LoginForm = () => {
gotoDefaultPage(userInfo); gotoDefaultPage(userInfo);
} }
}, },
onError: (error) => { onError: (error) => {
// gpustack handle in the interceptor // gpustack handle in the interceptor
} }
@@ -173,6 +157,9 @@ const LoginForm = () => {
setUserInfo(userInfo); setUserInfo(userInfo);
gotoDefaultPage({}); gotoDefaultPage({});
}, },
onLoading: (loading) => {
setLoading(loading);
},
onError: handleOnError onError: handleOnError
}); });
@@ -186,6 +173,8 @@ const LoginForm = () => {
} else if (SSOAuth.options.saml) { } else if (SSOAuth.options.saml) {
SSOAuth.loginWithSAML(); SSOAuth.loginWithSAML();
} }
setLoading(true);
setAuthError(null);
}; };
const hasThirdPartyLogin = useMemo(() => { const hasThirdPartyLogin = useMemo(() => {
@@ -228,22 +217,16 @@ const LoginForm = () => {
}; };
const isThirdPartyAuthHandling = useMemo(() => { const isThirdPartyAuthHandling = useMemo(() => {
return SSOAuth.isSSOLogin && !authError; return loading && !authError;
}, [SSOAuth.isSSOLogin, authError]); }, [loading, authError]);
return ( return (
<div> <div>
{contextHolder} {contextHolder}
<div className={styles.header}>
<ThemeDropActions></ThemeDropActions>
<LangSelect />
</div>
<div> <div>
{isThirdPartyAuthHandling ? ( {isThirdPartyAuthHandling ? (
<Spin> <Spin tip={intl.formatMessage({ id: 'common.login.auth' })}>
<span style={{ color: 'var(--ant-color-text)' }}> <div style={{ width: 300 }}></div>
{intl.formatMessage({ id: 'common.login.auth' })}
</span>
</Spin> </Spin>
) : ( ) : (
<> <>
+2 -5
View File
@@ -2,8 +2,8 @@ import { initialPasswordAtom, userAtom } from '@/atoms/user';
import SealInput from '@/components/seal-form/seal-input'; import SealInput from '@/components/seal-form/seal-input';
import { PasswordReg } from '@/config'; import { PasswordReg } from '@/config';
import { CRYPT_TEXT } from '@/utils/localstore/index'; import { CRYPT_TEXT } from '@/utils/localstore/index';
import { GlobalOutlined, LockOutlined } from '@ant-design/icons'; import { LockOutlined } from '@ant-design/icons';
import { SelectLang, useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Button, Form, message } from 'antd'; import { Button, Form, message } from 'antd';
import CryptoJS from 'crypto-js'; import CryptoJS from 'crypto-js';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
@@ -47,9 +47,6 @@ const PasswordForm: React.FC = () => {
return ( return (
<div> <div>
<div style={{ position: 'fixed', right: 0, top: 0, padding: '0 20px' }}>
<SelectLang icon={<GlobalOutlined />} reload={false} />
</div>
<Form <Form
form={form} form={form}
style={{ width: '360px', margin: '0 auto' }} style={{ width: '360px', margin: '0 auto' }}
+10 -2
View File
@@ -1,5 +1,5 @@
// hooks/useSSOAuth.ts // hooks/useSSOAuth.ts
import { history } from '@umijs/max'; import { history, useIntl } from '@umijs/max';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
type LoginOption = { type LoginOption = {
@@ -22,17 +22,21 @@ async function fetchAuthConfig(url: string) {
export function useSSOAuth({ export function useSSOAuth({
fetchUserInfo, fetchUserInfo,
onSuccess, onSuccess,
onLoading,
onError onError
}: { }: {
fetchUserInfo: () => Promise<any>; fetchUserInfo: () => Promise<any>;
onSuccess?: (userInfo: any) => void; onSuccess?: (userInfo: any) => void;
onError?: (err: Error) => void; onError?: (err: Error) => void;
onLoading?: (loading: boolean) => void;
}) { }) {
const [loginOption, setLoginOption] = useState<LoginOption>({ const [loginOption, setLoginOption] = useState<LoginOption>({
saml: false, saml: false,
oidc: false oidc: false
}); });
const intl = useIntl();
const { location } = history; const { location } = history;
const params = new URLSearchParams(location.search); const params = new URLSearchParams(location.search);
const sso = params.get('sso'); const sso = params.get('sso');
@@ -53,17 +57,21 @@ export function useSSOAuth({
saml: !!authConfig.is_saml saml: !!authConfig.is_saml
}); });
if (sso) { if (sso) {
onLoading?.(true);
if (authConfig.is_oidc) { if (authConfig.is_oidc) {
oidcLogin(); oidcLogin();
} else if (authConfig.is_saml) { } else if (authConfig.is_saml) {
samlLogin(); samlLogin();
} else { } else {
onError?.(new Error('No SSO configuration found')); onError?.(
new Error(intl.formatMessage({ id: 'common.sso.noConfig' }))
);
} }
} }
} catch (error: any) { } catch (error: any) {
setLoginOption({ oidc: false, saml: false }); setLoginOption({ oidc: false, saml: false });
onError?.(error); onError?.(error);
onLoading?.(false);
} }
}; };
+41 -8
View File
@@ -2,9 +2,12 @@ import Bg2 from '@/assets/images/bg-2.png';
import { userAtom } from '@/atoms/user'; import { userAtom } from '@/atoms/user';
import DarkMask from '@/components/dark-mask'; import DarkMask from '@/components/dark-mask';
import Footer from '@/components/footer'; import Footer from '@/components/footer';
import LangSelect from '@/components/lang-select';
import ThemeDropActions from '@/components/theme-toggle/theme-drop-actions';
import useUserSettings from '@/hooks/use-user-settings'; import useUserSettings from '@/hooks/use-user-settings';
import { useModel } from '@umijs/max'; import { useModel } from '@umijs/max';
import { ConfigProvider, theme } from 'antd'; import { ConfigProvider, theme } from 'antd';
import { createStyles } from 'antd-style';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
import { useEffect } from 'react'; import { useEffect } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
@@ -12,6 +15,29 @@ import LoginForm from './components/login-form';
import PasswordForm from './components/password-form'; import PasswordForm from './components/password-form';
import { checkDefaultPage } from './utils'; import { checkDefaultPage } from './utils';
const useStyles = createStyles(({ token, css }) => ({
header: css`
display: flex;
align-items: center;
gap: 8px;
position: fixed;
right: 0;
top: 0;
height: 60px;
padding: 20px;
.anticon-global {
color: ${token.colorText};
}
.anticon:hover {
color: ${token.colorTextTertiary};
}
`,
formContainer: css`
height: calc(100vh - 64px);
width: 100%;
`
}));
const Wrapper = styled.div<{ $isDarkTheme: boolean }>` const Wrapper = styled.div<{ $isDarkTheme: boolean }>`
position: fixed; position: fixed;
top: 0; top: 0;
@@ -30,7 +56,6 @@ const Wrapper = styled.div<{ $isDarkTheme: boolean }>`
`; `;
const Box = styled.div` const Box = styled.div`
padding-top: 10%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between; justify-content: space-between;
@@ -40,6 +65,7 @@ const Box = styled.div`
const FormWrapper = styled.div` const FormWrapper = styled.div`
position: relative; position: relative;
margin: 0 auto; margin: 0 auto;
z-index: 999;
border-radius: var(--border-radius-modal); border-radius: var(--border-radius-modal);
width: max-content; width: max-content;
height: max-content; height: max-content;
@@ -48,6 +74,8 @@ const FormWrapper = styled.div`
padding: 40px; padding: 40px;
background-color: var(--color-modal-content-bg); background-color: var(--color-modal-content-bg);
box-shadow: var(--color-modal-box-shadow); box-shadow: var(--color-modal-box-shadow);
margin-top: 50vh;
transform: translateY(-50%);
.field-wrapper { .field-wrapper {
background-color: transparent !important; background-color: transparent !important;
} }
@@ -58,6 +86,7 @@ const FormWrapper = styled.div`
`; `;
const Login = () => { const Login = () => {
const { styles } = useStyles();
const { themeData, userSettings, isDarkTheme } = useUserSettings(); const { themeData, userSettings, isDarkTheme } = useUserSettings();
const [userInfo, setUserInfo] = useAtom(userAtom); const [userInfo, setUserInfo] = useAtom(userAtom);
const { initialState, setInitialState } = useModel('@@initialState') || {}; const { initialState, setInitialState } = useModel('@@initialState') || {};
@@ -95,10 +124,14 @@ const Login = () => {
...themeData ...themeData
}} }}
> >
<div> <div className={styles.header}>
<DarkMask></DarkMask> <ThemeDropActions></ThemeDropActions>
<Wrapper $isDarkTheme={isDarkTheme}></Wrapper> <LangSelect />
<Box> </div>
<DarkMask></DarkMask>
<Wrapper $isDarkTheme={isDarkTheme}></Wrapper>
<Box>
<div className={styles.formContainer}>
<FormWrapper> <FormWrapper>
{userInfo?.require_password_change ? ( {userInfo?.require_password_change ? (
<PasswordForm /> <PasswordForm />
@@ -106,9 +139,9 @@ const Login = () => {
<LoginForm /> <LoginForm />
)} )}
</FormWrapper> </FormWrapper>
<Footer /> </div>
</Box> <Footer />
</div> </Box>
</ConfigProvider> </ConfigProvider>
); );
}; };