chore: version info modal
This commit is contained in:
+8
-3
@@ -1,9 +1,13 @@
|
|||||||
import { defineConfig } from '@umijs/max';
|
import { defineConfig } from '@umijs/max';
|
||||||
import theme from './theme';
|
|
||||||
const CompressionWebpackPlugin = require('compression-webpack-plugin');
|
|
||||||
|
|
||||||
import proxy from './proxy';
|
import proxy from './proxy';
|
||||||
import routes from './routes';
|
import routes from './routes';
|
||||||
|
import theme from './theme';
|
||||||
|
import { getBranchInfo } from './utils';
|
||||||
|
const CompressionWebpackPlugin = require('compression-webpack-plugin');
|
||||||
|
|
||||||
|
const versionInfo = getBranchInfo();
|
||||||
|
process.env.VERSION = JSON.stringify(versionInfo);
|
||||||
|
|
||||||
const env = process.env.NODE_ENV;
|
const env = process.env.NODE_ENV;
|
||||||
const isProduction = env === 'production';
|
const isProduction = env === 'production';
|
||||||
|
|
||||||
@@ -15,6 +19,7 @@ export default defineConfig({
|
|||||||
history: {
|
history: {
|
||||||
type: 'hash'
|
type: 'hash'
|
||||||
},
|
},
|
||||||
|
|
||||||
base: process.env.npm_config_base || '/',
|
base: process.env.npm_config_base || '/',
|
||||||
...(isProduction
|
...(isProduction
|
||||||
? {
|
? {
|
||||||
|
|||||||
+3
-3
@@ -1,16 +1,16 @@
|
|||||||
const proxyTableList = ['cli', 'v1', 'auth', 'v1-openai'];
|
const proxyTableList = ['cli', 'v1', 'auth', 'v1-openai', 'version'];
|
||||||
|
|
||||||
// @ts-ingore
|
// @ts-ingore
|
||||||
export default function createProxyTable(target?: string) {
|
export default function createProxyTable(target?: string) {
|
||||||
const proxyTable = proxyTableList.reduce(
|
const proxyTable = proxyTableList.reduce(
|
||||||
(obj: Record<string, object>, api) => {
|
(obj: Record<string, object>, api) => {
|
||||||
const newTarget = target || 'http://localhost';
|
const newTarget = target || 'http://localhost';
|
||||||
obj[`/${api}/`] = {
|
obj[`/${api}`] = {
|
||||||
target: newTarget,
|
target: newTarget,
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
secure: false,
|
secure: false,
|
||||||
ws: true,
|
ws: true,
|
||||||
pathRewrite: (pth: string) => pth.replace(`/^/${api}/`, `/${api}`),
|
pathRewrite: (pth: string) => pth.replace(`/^/${api}`, `/${api}`),
|
||||||
// onProxyRes: (proxyRes: any, req: any, res: any) => {
|
// onProxyRes: (proxyRes: any, req: any, res: any) => {
|
||||||
// if (req.headers.accept === 'text/event-stream') {
|
// if (req.headers.accept === 'text/event-stream') {
|
||||||
// res.writeHead(res.statusCode, {
|
// res.writeHead(res.statusCode, {
|
||||||
|
|||||||
+1
-1
@@ -9,5 +9,5 @@ export const getBranchInfo = () => {
|
|||||||
.execSync(`git tag --contains ${latestCommit}`)
|
.execSync(`git tag --contains ${latestCommit}`)
|
||||||
.toString()
|
.toString()
|
||||||
.trim();
|
.trim();
|
||||||
return { version: versionTag, commitId: latestCommit };
|
return { version: versionTag || 'dev', commitId: latestCommit.slice(0, 7) };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,24 +2,15 @@ import { IApi } from '@umijs/max';
|
|||||||
|
|
||||||
export default (api: IApi) => {
|
export default (api: IApi) => {
|
||||||
api.modifyHTML(($) => {
|
api.modifyHTML(($) => {
|
||||||
console.log('pllugins=========modifyHTML', $);
|
const info = JSON.parse(process.env.VERSION || '{}');
|
||||||
|
const env = process.env.NODE_ENV;
|
||||||
|
$('html').attr(
|
||||||
|
'data-version',
|
||||||
|
env === 'production' ? info.version : `${info.version}-${info.commitId}`
|
||||||
|
);
|
||||||
return $;
|
return $;
|
||||||
});
|
});
|
||||||
api.onStart(() => {
|
api.onStart(() => {
|
||||||
console.log('pllugins=========start');
|
console.log('start');
|
||||||
});
|
});
|
||||||
|
|
||||||
// api.modifyConfig((memo: any) => {
|
|
||||||
// // some beautiful code
|
|
||||||
// console.log('pllugins=========memo', memo);
|
|
||||||
// return memo;
|
|
||||||
// });
|
|
||||||
// api.addLayouts(() => {
|
|
||||||
// return [
|
|
||||||
// {
|
|
||||||
// id: 'layout',
|
|
||||||
// file: require.resolve('./src/global-layouts/index.tsx')
|
|
||||||
// }
|
|
||||||
// ];
|
|
||||||
// });
|
|
||||||
};
|
};
|
||||||
|
|||||||
+20
-1
@@ -1,6 +1,11 @@
|
|||||||
|
import { GPUStackVersionAtom } from '@/atoms/user';
|
||||||
|
import { setAtomStorage } from '@/atoms/utils';
|
||||||
import { RequestConfig, history } from '@umijs/max';
|
import { RequestConfig, history } from '@umijs/max';
|
||||||
import { requestConfig } from './request-config';
|
import { requestConfig } from './request-config';
|
||||||
import { queryCurrentUserState } from './services/profile/apis';
|
import {
|
||||||
|
queryCurrentUserState,
|
||||||
|
queryVersionInfo
|
||||||
|
} from './services/profile/apis';
|
||||||
|
|
||||||
const loginPath = '/login';
|
const loginPath = '/login';
|
||||||
let currentUserInfo: any = {};
|
let currentUserInfo: any = {};
|
||||||
@@ -28,6 +33,18 @@ export async function getInitialState(): Promise<{
|
|||||||
return {} as Global.UserInfo;
|
return {} as Global.UserInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getAppVersionInfo = async () => {
|
||||||
|
try {
|
||||||
|
const data = await queryVersionInfo();
|
||||||
|
console.log('versioninfo=========', data);
|
||||||
|
setAtomStorage(GPUStackVersionAtom, data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('queryVersionInfo error', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getAppVersionInfo();
|
||||||
|
|
||||||
if (![loginPath].includes(location.pathname)) {
|
if (![loginPath].includes(location.pathname)) {
|
||||||
const userInfo = await fetchUserInfo();
|
const userInfo = await fetchUserInfo();
|
||||||
currentUserInfo = {
|
currentUserInfo = {
|
||||||
@@ -43,6 +60,8 @@ export async function getInitialState(): Promise<{
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('app.tsx');
|
||||||
|
|
||||||
export const request: RequestConfig = {
|
export const request: RequestConfig = {
|
||||||
baseURL: ' /v1',
|
baseURL: ' /v1',
|
||||||
...requestConfig
|
...requestConfig
|
||||||
|
|||||||
@@ -1,7 +1,16 @@
|
|||||||
|
import { atom } from 'jotai';
|
||||||
import { atomWithStorage } from 'jotai/utils';
|
import { atomWithStorage } from 'jotai/utils';
|
||||||
|
|
||||||
export const userAtom = atomWithStorage<any>('userInfo', null);
|
export const userAtom = atomWithStorage<any>('userInfo', null);
|
||||||
|
|
||||||
|
export const GPUStackVersionAtom = atom<{
|
||||||
|
version: string;
|
||||||
|
git_commit: string;
|
||||||
|
}>({
|
||||||
|
version: '',
|
||||||
|
git_commit: ''
|
||||||
|
});
|
||||||
|
|
||||||
export const initialPasswordAtom = atomWithStorage<string>(
|
export const initialPasswordAtom = atomWithStorage<string>(
|
||||||
'initialPassword',
|
'initialPassword',
|
||||||
''
|
''
|
||||||
|
|||||||
@@ -8,7 +8,14 @@ export const clearAtomStorage = (atom: any) => {
|
|||||||
store.set(atom, null);
|
store.set(atom, null);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAtomStorage = (atom: any) => {
|
export const setAtomStorage = (atom: any, value: any) => {
|
||||||
|
if (!atom) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const store = getDefaultStore();
|
||||||
|
store.set(atom, value);
|
||||||
|
};
|
||||||
|
export const getAtomStorage = (atom: any): any => {
|
||||||
if (!atom) {
|
if (!atom) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,20 @@ import { Button } from 'antd';
|
|||||||
type CopyButtonProps = {
|
type CopyButtonProps = {
|
||||||
text: string;
|
text: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
fontSize?: string;
|
||||||
type?: 'text' | 'primary' | 'dashed' | 'link' | 'default';
|
type?: 'text' | 'primary' | 'dashed' | 'link' | 'default';
|
||||||
size?: 'small' | 'middle' | 'large';
|
size?: 'small' | 'middle' | 'large';
|
||||||
|
shape?: 'circle' | 'round' | 'default';
|
||||||
|
style?: React.CSSProperties;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CopyButton: React.FC<CopyButtonProps> = ({
|
const CopyButton: React.FC<CopyButtonProps> = ({
|
||||||
text,
|
text,
|
||||||
disabled,
|
disabled,
|
||||||
type = 'text',
|
type = 'text',
|
||||||
|
shape = 'circle',
|
||||||
|
fontSize = '14px',
|
||||||
|
style,
|
||||||
size = 'middle'
|
size = 'middle'
|
||||||
}) => {
|
}) => {
|
||||||
const { copied, copyToClipboard } = useCopyToClipboard();
|
const { copied, copyToClipboard } = useCopyToClipboard();
|
||||||
@@ -24,21 +30,20 @@ const CopyButton: React.FC<CopyButtonProps> = ({
|
|||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
type={type}
|
type={type}
|
||||||
shape="circle"
|
shape={shape}
|
||||||
size={size}
|
size={size}
|
||||||
onClick={handleCopy}
|
onClick={handleCopy}
|
||||||
disabled={!!disabled}
|
disabled={!!disabled}
|
||||||
>
|
icon={
|
||||||
{copied ? (
|
copied ? (
|
||||||
<CheckCircleFilled
|
<CheckCircleFilled
|
||||||
style={{ color: 'var(--ant-color-success)', fontSize: '14px' }}
|
style={{ color: 'var(--ant-color-success)', fontSize: fontSize }}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<CopyOutlined
|
<CopyOutlined style={{ fontSize: fontSize, ...style }} />
|
||||||
style={{ color: 'var(--ant-color-primary)', fontSize: '14px' }}
|
)
|
||||||
/>
|
}
|
||||||
)}
|
></Button>
|
||||||
</Button>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const EditorWrap: React.FC<EditorwrapProps> = ({
|
|||||||
showHeader = true
|
showHeader = true
|
||||||
}) => {
|
}) => {
|
||||||
const handleChangeLang = (value: string) => {
|
const handleChangeLang = (value: string) => {
|
||||||
onChangeLang && onChangeLang(value);
|
onChangeLang?.(value);
|
||||||
};
|
};
|
||||||
const renderHeader = () => {
|
const renderHeader = () => {
|
||||||
if (header) {
|
if (header) {
|
||||||
@@ -39,7 +39,13 @@ const EditorWrap: React.FC<EditorwrapProps> = ({
|
|||||||
options={langOptions}
|
options={langOptions}
|
||||||
onChange={handleChangeLang}
|
onChange={handleChangeLang}
|
||||||
></Select>
|
></Select>
|
||||||
<CopyButton text={copyText} />
|
<CopyButton
|
||||||
|
text={copyText}
|
||||||
|
size="small"
|
||||||
|
style={{
|
||||||
|
color: 'rgba(255,255,255,.7)'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,4 +7,10 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: var(--font-size-middle);
|
font-size: var(--font-size-middle);
|
||||||
color: var(--color-text-2);
|
color: var(--color-text-2);
|
||||||
|
|
||||||
|
.footer-content-left-text {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,23 @@
|
|||||||
|
import { GPUStackVersionAtom } from '@/atoms/user';
|
||||||
|
import { getAtomStorage } from '@/atoms/utils';
|
||||||
|
import VersionInfo from '@/components/version-info';
|
||||||
|
import externalLinks from '@/config/external-links';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Space } from 'antd';
|
import { Button, Modal, Space } from 'antd';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
|
||||||
const Footer: React.FC = () => {
|
const Footer: React.FC = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const showVersion = () => {
|
||||||
|
Modal.info({
|
||||||
|
icon: null,
|
||||||
|
centered: false,
|
||||||
|
width: 500,
|
||||||
|
content: <VersionInfo intl={intl} />
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="footer">
|
<div className="footer">
|
||||||
<div className="footer-content">
|
<div className="footer-content">
|
||||||
@@ -14,6 +28,19 @@ const Footer: React.FC = () => {
|
|||||||
<span> {new Date().getFullYear()}</span>
|
<span> {new Date().getFullYear()}</span>
|
||||||
<span> {intl.formatMessage({ id: 'settings.company' })}</span>
|
<span> {intl.formatMessage({ id: 'settings.company' })}</span>
|
||||||
</Space>
|
</Space>
|
||||||
|
<Space size={8} style={{ marginLeft: 18 }}>
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
size="small"
|
||||||
|
href={externalLinks.documentation}
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{intl.formatMessage({ id: 'common.button.help' })}
|
||||||
|
</Button>
|
||||||
|
<Button type="link" size="small" onClick={showVersion}>
|
||||||
|
{getAtomStorage(GPUStackVersionAtom)?.version}
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,12 +20,17 @@ type StatusTagProps = {
|
|||||||
text: string;
|
text: string;
|
||||||
message?: string;
|
message?: string;
|
||||||
};
|
};
|
||||||
|
type?: 'tag' | 'circle';
|
||||||
download?: {
|
download?: {
|
||||||
percent: number;
|
percent: number;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const StatusTag: React.FC<StatusTagProps> = ({ statusValue, download }) => {
|
const StatusTag: React.FC<StatusTagProps> = ({
|
||||||
|
statusValue,
|
||||||
|
download,
|
||||||
|
type = 'tag'
|
||||||
|
}) => {
|
||||||
const { text, status } = statusValue;
|
const { text, status } = statusValue;
|
||||||
const [statusColor, setStatusColor] = useState<{
|
const [statusColor, setStatusColor] = useState<{
|
||||||
text: string;
|
text: string;
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
.version-box {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.img {
|
||||||
|
margin-top: 16px;
|
||||||
|
text-align: center;
|
||||||
|
height: 30px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
text-align: center;
|
||||||
|
font-size: var(--font-size-middle);
|
||||||
|
margin-block: 30px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ver {
|
||||||
|
line-height: 32px;
|
||||||
|
display: flex;
|
||||||
|
font-size: var(--font-size-middle);
|
||||||
|
|
||||||
|
.label {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
width: 60px;
|
||||||
|
font-size: var(--font-size-middle);
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
.val {
|
||||||
|
color: var(--ant-color-text-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import Logo from '@/assets/images/gpustack-logo.png';
|
||||||
|
import { GPUStackVersionAtom } from '@/atoms/user';
|
||||||
|
import { getAtomStorage } from '@/atoms/utils';
|
||||||
|
import React from 'react';
|
||||||
|
import './index.less';
|
||||||
|
|
||||||
|
const VersionInfo: React.FC<{ intl: any }> = ({ intl }) => {
|
||||||
|
// get the data attr from html
|
||||||
|
const version = document.documentElement.getAttribute('data-version');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="version-box">
|
||||||
|
<div className="img">
|
||||||
|
<img src={Logo} alt="logo" />
|
||||||
|
</div>
|
||||||
|
<div className="title">
|
||||||
|
{intl.formatMessage({ id: 'common.footer.version.title' })}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="ver">
|
||||||
|
<span className="label">
|
||||||
|
{' '}
|
||||||
|
{intl.formatMessage({ id: 'common.footer.version.server' })}
|
||||||
|
</span>
|
||||||
|
<span className="val">
|
||||||
|
{getAtomStorage(GPUStackVersionAtom)?.version ||
|
||||||
|
getAtomStorage(GPUStackVersionAtom)?.git_commit}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="ver">
|
||||||
|
<span className="label">UI </span>
|
||||||
|
<span className="val"> {version}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default VersionInfo;
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export default {
|
||||||
|
documentation: 'https://docs.gpustack.ai/',
|
||||||
|
github: 'https://github.com/gpustack/gpustack',
|
||||||
|
discord: 'https://discord.gg/2ZvXuaYq',
|
||||||
|
site: 'https://seal.io/'
|
||||||
|
};
|
||||||
Vendored
+2
@@ -21,4 +21,6 @@ declare namespace Global {
|
|||||||
require_password_change: boolean;
|
require_password_change: boolean;
|
||||||
id: number;
|
id: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SearchParams = Pagination & { search?: string };
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
// 应用前置、全局运行的逻辑时 会在这里执行
|
// 应用前置、全局运行的逻辑时 会在这里执行
|
||||||
|
|||||||
+14
-1
@@ -1,10 +1,10 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
import { userAtom } from '@/atoms/user';
|
import { userAtom } from '@/atoms/user';
|
||||||
|
import VersionInfo from '@/components/version-info';
|
||||||
import { logout } from '@/pages/login/apis';
|
import { logout } from '@/pages/login/apis';
|
||||||
import { useAccessMarkedRoutes } from '@@/plugin-access';
|
import { useAccessMarkedRoutes } from '@@/plugin-access';
|
||||||
import { useModel } from '@@/plugin-model';
|
import { useModel } from '@@/plugin-model';
|
||||||
|
|
||||||
import { ProLayout } from '@ant-design/pro-components';
|
import { ProLayout } from '@ant-design/pro-components';
|
||||||
import {
|
import {
|
||||||
Link,
|
Link,
|
||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
useNavigate,
|
useNavigate,
|
||||||
type IRoute
|
type IRoute
|
||||||
} from '@umijs/max';
|
} from '@umijs/max';
|
||||||
|
import { Modal } from 'antd';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import Exception from './Exception';
|
import Exception from './Exception';
|
||||||
@@ -102,6 +103,15 @@ export default (props: any) => {
|
|||||||
return intl.formatMessage({ id: args.id });
|
return intl.formatMessage({ id: args.id });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const showVersion = () => {
|
||||||
|
Modal.info({
|
||||||
|
icon: null,
|
||||||
|
centered: false,
|
||||||
|
width: 500,
|
||||||
|
content: <VersionInfo intl={intl} />
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const runtimeConfig = {
|
const runtimeConfig = {
|
||||||
...initialInfo,
|
...initialInfo,
|
||||||
logout: async (userInfo) => {
|
logout: async (userInfo) => {
|
||||||
@@ -109,6 +119,9 @@ export default (props: any) => {
|
|||||||
await logout();
|
await logout();
|
||||||
navigate(loginPath);
|
navigate(loginPath);
|
||||||
},
|
},
|
||||||
|
showVersion: () => {
|
||||||
|
return showVersion();
|
||||||
|
},
|
||||||
notFound: <span>404 not found</span>
|
notFound: <span>404 not found</span>
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
import avatarImg from '@/assets/images/avatar.png';
|
import avatarImg from '@/assets/images/avatar.png';
|
||||||
|
import externalLinks from '@/config/external-links';
|
||||||
import langConfigMap from '@/locales/lang-config-map';
|
import langConfigMap from '@/locales/lang-config-map';
|
||||||
import {
|
import {
|
||||||
DiscordOutlined,
|
DiscordOutlined,
|
||||||
@@ -27,6 +28,7 @@ export function getRightRenderContent(opts: {
|
|||||||
intl: any;
|
intl: any;
|
||||||
}) {
|
}) {
|
||||||
const { intl, collapsed, siderWidth } = opts;
|
const { intl, collapsed, siderWidth } = opts;
|
||||||
|
|
||||||
const allLocals = getAllLocales();
|
const allLocals = getAllLocales();
|
||||||
if (opts.runtimeConfig.rightRender) {
|
if (opts.runtimeConfig.rightRender) {
|
||||||
return opts.runtimeConfig.rightRender(
|
return opts.runtimeConfig.rightRender(
|
||||||
@@ -73,22 +75,25 @@ export function getRightRenderContent(opts: {
|
|||||||
key: 'site',
|
key: 'site',
|
||||||
icon: <HomeOutlined />,
|
icon: <HomeOutlined />,
|
||||||
label: 'GPUStack',
|
label: 'GPUStack',
|
||||||
url: 'https://gpustack.ai/'
|
url: externalLinks.site
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'github',
|
key: 'github',
|
||||||
icon: <GithubOutlined />,
|
icon: <GithubOutlined />,
|
||||||
label: 'GitHub'
|
label: 'GitHub',
|
||||||
|
url: externalLinks.github
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'Discord',
|
key: 'Discord',
|
||||||
icon: <DiscordOutlined />,
|
icon: <DiscordOutlined />,
|
||||||
label: 'Discord'
|
label: 'Discord',
|
||||||
|
url: externalLinks.discord
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'docs',
|
key: 'docs',
|
||||||
icon: <ReadOutlined />,
|
icon: <ReadOutlined />,
|
||||||
label: intl.formatMessage({ id: 'common.button.docs' })
|
label: intl.formatMessage({ id: 'common.button.docs' }),
|
||||||
|
url: externalLinks.documentation
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'version',
|
key: 'version',
|
||||||
@@ -118,11 +123,26 @@ export function getRightRenderContent(opts: {
|
|||||||
label: (
|
label: (
|
||||||
<span className="flex flex-center">
|
<span className="flex flex-center">
|
||||||
{item.icon}
|
{item.icon}
|
||||||
<a className="m-l-8" href="#" target="_blank">
|
{item.key === 'version' ? (
|
||||||
{item.label}
|
<a className="m-l-8">{item.label}</a>
|
||||||
</a>
|
) : (
|
||||||
|
<a
|
||||||
|
className="m-l-8"
|
||||||
|
href={item.url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
)
|
),
|
||||||
|
onClick() {
|
||||||
|
if (item.key === 'version') {
|
||||||
|
// opts.runtimeConfig.showVersion();
|
||||||
|
opts.runtimeConfig.showVersion();
|
||||||
|
}
|
||||||
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export default {
|
|||||||
'playground.params.topp.tips':
|
'playground.params.topp.tips':
|
||||||
'Controls diversity via nucleus sampling: 0.5 means half of all likelihood-weighted options are considered.',
|
'Controls diversity via nucleus sampling: 0.5 means half of all likelihood-weighted options are considered.',
|
||||||
'playground.params.seed.tips':
|
'playground.params.seed.tips':
|
||||||
'Specify a random seed to ensure deterministic sampling. Using the same seed and parameters will produce the same results for repeated requests.',
|
'If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same seed and parameters should return the same result.',
|
||||||
'playground.params.stop.tips':
|
'playground.params.stop.tips':
|
||||||
'A stop sequence is a predefined or user-specified text string that signals the AI to stop generating further tokens when these sequences appear.'
|
'A stop sequence is a predefined or user-specified text string that signals the AI to stop generating further tokens when these sequences appear.'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ export default {
|
|||||||
'playground.params.maxtokens.tips':
|
'playground.params.maxtokens.tips':
|
||||||
'生成的最大 token 数。输入标记和生成的标记的总长度受模型上下文长度的限制。',
|
'生成的最大 token 数。输入标记和生成的标记的总长度受模型上下文长度的限制。',
|
||||||
'playground.params.topp.tips':
|
'playground.params.topp.tips':
|
||||||
'通过核采样控制多样性:0.5 表示考虑所有基于概率权重选项的一半。',
|
'通过核心采样控制多样性:0.5 表示考虑所有基于概率权重选项的一半。',
|
||||||
'playground.params.seed.tips':
|
'playground.params.seed.tips':
|
||||||
'指定随机种子以确保确定性采样。使用相同的种子和参数对于重复请求将产生相同的结果。',
|
'如果指定,我们的系统将尽最大努力进行确定性采样,以便使用相同种子和参数的重复请求应返回相同的结果。',
|
||||||
'playground.params.stop.tips':
|
'playground.params.stop.tips':
|
||||||
'停止序列是一个预定义或用户指定的文本字符串,当这些序列出现时,它会提示 AI 停止生成后续的标记。'
|
'停止序列是一个预定义或用户指定的文本字符串,当这些序列出现时,它会提示 AI 停止生成后续的标记。'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ import { FormData, ListItem } from '../config/types';
|
|||||||
|
|
||||||
export const APIS_KEYS_API = '/api-keys';
|
export const APIS_KEYS_API = '/api-keys';
|
||||||
|
|
||||||
export async function queryApisKeysList(
|
export async function queryApisKeysList(params: Global.SearchParams) {
|
||||||
params: Global.Pagination & { query?: string }
|
|
||||||
) {
|
|
||||||
return request<Global.PageResponse<ListItem>>(`${APIS_KEYS_API}`, {
|
return request<Global.PageResponse<ListItem>>(`${APIS_KEYS_API}`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params
|
params
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ const Models: React.FC = () => {
|
|||||||
const [queryParams, setQueryParams] = useState({
|
const [queryParams, setQueryParams] = useState({
|
||||||
page: 1,
|
page: 1,
|
||||||
perPage: 10,
|
perPage: 10,
|
||||||
query: ''
|
search: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleShowSizeChange = (page: number, size: number) => {
|
const handleShowSizeChange = (page: number, size: number) => {
|
||||||
@@ -90,7 +90,7 @@ const Models: React.FC = () => {
|
|||||||
const handleNameChange = (e: any) => {
|
const handleNameChange = (e: any) => {
|
||||||
setQueryParams({
|
setQueryParams({
|
||||||
...queryParams,
|
...queryParams,
|
||||||
query: e.target.value
|
search: e.target.value
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const MODEL_INSTANCE_API = '/model-instances';
|
|||||||
|
|
||||||
// ===================== Models =====================
|
// ===================== Models =====================
|
||||||
export async function queryModelsList(
|
export async function queryModelsList(
|
||||||
params: Global.Pagination & { query?: string },
|
params: Global.SearchParams,
|
||||||
options?: any
|
options?: any
|
||||||
) {
|
) {
|
||||||
return request<Global.PageResponse<ListItem>>(`${MODELS_API}`, {
|
return request<Global.PageResponse<ListItem>>(`${MODELS_API}`, {
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
import DropdownButtons from '@/components/drop-down-buttons';
|
import DropdownButtons from '@/components/drop-down-buttons';
|
||||||
import RowChildren from '@/components/seal-table/components/row-children';
|
import RowChildren from '@/components/seal-table/components/row-children';
|
||||||
import StatusTag from '@/components/status-tag';
|
import StatusTag from '@/components/status-tag';
|
||||||
import { DeleteOutlined, FieldTimeOutlined } from '@ant-design/icons';
|
import {
|
||||||
|
DeleteOutlined,
|
||||||
|
FieldTimeOutlined,
|
||||||
|
InfoCircleOutlined
|
||||||
|
} from '@ant-design/icons';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Col, Row, Space } from 'antd';
|
import { Col, Row, Space, Tooltip } from 'antd';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@@ -59,35 +63,44 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getWorkerIp = (item: ModelInstanceListItem) => {
|
const renderWorkerInfo = (item: ModelInstanceListItem) => {
|
||||||
|
let workerIp = '-';
|
||||||
if (item.worker_ip) {
|
if (item.worker_ip) {
|
||||||
return item.port ? `${item.worker_ip}:${item.port}` : item.worker_ip;
|
workerIp = item.port ? `${item.worker_ip}:${item.port}` : item.worker_ip;
|
||||||
}
|
}
|
||||||
return '-';
|
return (
|
||||||
|
<div>
|
||||||
|
<div>{item.worker_name}</div>
|
||||||
|
<div>{workerIp}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Space size={16} direction="vertical" style={{ width: '100%' }}>
|
<Space size={16} direction="vertical" style={{ width: '100%' }}>
|
||||||
{_.map(list, (item: ModelInstanceListItem, index: number) => {
|
{_.map(list, (item: ModelInstanceListItem, index: number) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
className="_2Q2Yw"
|
||||||
key={`${item.id}`}
|
key={`${item.id}`}
|
||||||
style={{ borderRadius: 'var(--ant-table-header-border-radius)' }}
|
style={{ borderRadius: 'var(--ant-table-header-border-radius)' }}
|
||||||
>
|
>
|
||||||
<RowChildren key={`${item.id}_row`}>
|
<RowChildren key={`${item.id}_row`}>
|
||||||
<Row style={{ width: '100%' }} align="middle">
|
<Row style={{ width: '100%' }} align="middle">
|
||||||
<Col span={4}>{item.name}</Col>
|
<Col
|
||||||
<Col span={3}>{item.worker_name || '-'}</Col>
|
span={5}
|
||||||
<Col span={4}>
|
style={{
|
||||||
<span>
|
paddingInline: 'var(--ant-table-cell-padding-inline)'
|
||||||
{item.source === 'huggingface'
|
}}
|
||||||
? item.huggingface_filename
|
>
|
||||||
: item.ollama_library_model_name}
|
<Tooltip title={renderWorkerInfo(item)}>
|
||||||
</span>
|
<span className="m-r-5">{item.name}</span>
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</Tooltip>
|
||||||
</Col>
|
</Col>
|
||||||
|
<Col span={6}></Col>
|
||||||
<Col span={3}>
|
<Col span={4}>
|
||||||
<span
|
<span
|
||||||
style={{ paddingLeft: '0px' }}
|
style={{ paddingLeft: '56px' }}
|
||||||
className="flex justify-center"
|
className="flex justify-center"
|
||||||
>
|
>
|
||||||
{item.state && (
|
{item.state && (
|
||||||
@@ -107,11 +120,11 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
|
|||||||
</span>
|
</span>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={5}>
|
<Col span={5}>
|
||||||
<span style={{ paddingLeft: 36 }}>
|
<span style={{ paddingLeft: 38 }}>
|
||||||
{dayjs(item.updated_at).format('YYYY-MM-DD HH:mm:ss')}
|
{dayjs(item.updated_at).format('YYYY-MM-DD HH:mm:ss')}
|
||||||
</span>
|
</span>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={5}>
|
<Col span={4}>
|
||||||
<div style={{ paddingLeft: 39 }}>
|
<div style={{ paddingLeft: 39 }}>
|
||||||
<DropdownButtons
|
<DropdownButtons
|
||||||
items={setChildActionList(item)}
|
items={setChildActionList(item)}
|
||||||
|
|||||||
@@ -337,15 +337,21 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
dataIndex="name"
|
dataIndex="name"
|
||||||
key="name"
|
key="name"
|
||||||
width={400}
|
width={400}
|
||||||
span={6}
|
span={5}
|
||||||
/>
|
/>
|
||||||
<SealColumn
|
<SealColumn
|
||||||
title={intl.formatMessage({ id: 'models.form.source' })}
|
title={intl.formatMessage({ id: 'models.form.source' })}
|
||||||
dataIndex="source"
|
dataIndex="source"
|
||||||
key="source"
|
key="source"
|
||||||
span={4}
|
span={6}
|
||||||
render={(text) => {
|
render={(text, record: ListItem) => {
|
||||||
return modelSourceMap[text] || '-';
|
return (
|
||||||
|
<span>
|
||||||
|
{record.source === modelSourceMap.huggingface_value
|
||||||
|
? `${modelSourceMap.huggingface} / ${record.huggingface_filename}`
|
||||||
|
: `${modelSourceMap.ollama_library} / ${record.ollama_library_model_name}`}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<SealColumn
|
<SealColumn
|
||||||
@@ -369,7 +375,7 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<SealColumn
|
<SealColumn
|
||||||
span={5}
|
span={4}
|
||||||
title={intl.formatMessage({ id: 'common.table.operation' })}
|
title={intl.formatMessage({ id: 'common.table.operation' })}
|
||||||
key="operation"
|
key="operation"
|
||||||
render={(text, record) => {
|
render={(text, record) => {
|
||||||
|
|||||||
@@ -15,7 +15,10 @@ export const ollamaModelOptions = [
|
|||||||
export const modelSourceMap: Record<string, string> = {
|
export const modelSourceMap: Record<string, string> = {
|
||||||
huggingface: 'Hugging Face',
|
huggingface: 'Hugging Face',
|
||||||
ollama_library: 'Ollama Library',
|
ollama_library: 'Ollama Library',
|
||||||
s3: 'S3'
|
s3: 'S3',
|
||||||
|
huggingface_value: 'huggingface',
|
||||||
|
ollama_library_value: 'ollama_library',
|
||||||
|
s3_value: 's3'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const InstanceStatusMap = {
|
export const InstanceStatusMap = {
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ export interface ListItem {
|
|||||||
source: string;
|
source: string;
|
||||||
huggingface_repo_id: string;
|
huggingface_repo_id: string;
|
||||||
huggingface_file_name: string;
|
huggingface_file_name: string;
|
||||||
|
huggingface_filename: string;
|
||||||
|
ollama_library_model_name: string;
|
||||||
s3Address: string;
|
s3Address: string;
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const Models: React.FC = () => {
|
|||||||
const [queryParams, setQueryParams] = useState({
|
const [queryParams, setQueryParams] = useState({
|
||||||
page: 1,
|
page: 1,
|
||||||
perPage: 10,
|
perPage: 10,
|
||||||
query: ''
|
search: ''
|
||||||
});
|
});
|
||||||
// request data
|
// request data
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ const Models: React.FC = () => {
|
|||||||
(e: any) => {
|
(e: any) => {
|
||||||
setQueryParams({
|
setQueryParams({
|
||||||
...queryParams,
|
...queryParams,
|
||||||
query: e.target.value
|
search: e.target.value
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[queryParams]
|
[queryParams]
|
||||||
|
|||||||
@@ -122,13 +122,16 @@ const MessageItem: React.FC<{
|
|||||||
<div className="delete-btn">
|
<div className="delete-btn">
|
||||||
<Space size={5}>
|
<Space size={5}>
|
||||||
{message.content && (
|
{message.content && (
|
||||||
<CopyButton text={message.content} size="small"></CopyButton>
|
<CopyButton
|
||||||
|
text={message.content}
|
||||||
|
size="small"
|
||||||
|
shape="default"
|
||||||
|
type="default"
|
||||||
|
fontSize="12px"
|
||||||
|
></CopyButton>
|
||||||
)}
|
)}
|
||||||
<Button
|
<Button
|
||||||
type="text"
|
|
||||||
shape="circle"
|
|
||||||
size="small"
|
size="small"
|
||||||
style={{ color: 'var(--ant-color-primary)' }}
|
|
||||||
onClick={handleDelete}
|
onClick={handleDelete}
|
||||||
icon={<MinusCircleOutlined />}
|
icon={<MinusCircleOutlined />}
|
||||||
></Button>
|
></Button>
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
|
|||||||
const systemList = systemMessage
|
const systemList = systemMessage
|
||||||
? [{ role: 'system', content: systemMessage }]
|
? [{ role: 'system', content: systemMessage }]
|
||||||
: [];
|
: [];
|
||||||
const code = `import OpenAI from "openai";\nconst openai = new OpenAI({\n"base_url": "/v1-openai", \n "gpustack_api_key": "$\{GPUSTACK_API_KEY}"\n });\n\nasync function main(){\nconst params = ${JSON.stringify(
|
const code = `import OpenAI from "openai";\nconst openai = new OpenAI();\n\nasync function main(){\nconst params = ${JSON.stringify(
|
||||||
{
|
{
|
||||||
...parameters,
|
...parameters,
|
||||||
messages: [...systemList, ...messageList]
|
messages: [...systemList, ...messageList]
|
||||||
@@ -92,7 +92,7 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
|
|||||||
const systemList = systemMessage
|
const systemList = systemMessage
|
||||||
? [{ role: 'system', content: systemMessage }]
|
? [{ role: 'system', content: systemMessage }]
|
||||||
: [];
|
: [];
|
||||||
const code = `from openai import OpenAI\nclient = OpenAI({\n "base_url": "/v1-openai", \n "gpustack_api_key": "$\{GPUSTACK_API_KEY}"\n })\n\ncompletion = client.chat.completions.create(\n${formattedParams} messages=${JSON.stringify([...systemList, ...messageList], null, 2)})\nprint(completion.choices[0].message)`;
|
const code = `from openai import OpenAI\nclient = OpenAI()\n\ncompletion = client.chat.completions.create(\n${formattedParams} messages=${JSON.stringify([...systemList, ...messageList], null, 2)})\nprint(completion.choices[0].message)`;
|
||||||
setCodeValue(code);
|
setCodeValue(code);
|
||||||
}
|
}
|
||||||
formatCode();
|
formatCode();
|
||||||
|
|||||||
@@ -4,18 +4,14 @@ import { GPUDeviceItem, ListItem } from '../config/types';
|
|||||||
export const WORKERS_API = '/workers';
|
export const WORKERS_API = '/workers';
|
||||||
export const GPU_DEVICES_API = '/gpu-devices';
|
export const GPU_DEVICES_API = '/gpu-devices';
|
||||||
|
|
||||||
export async function queryWorkersList(
|
export async function queryWorkersList(params: Global.SearchParams) {
|
||||||
params: Global.Pagination & { query?: string }
|
|
||||||
) {
|
|
||||||
return request<Global.PageResponse<ListItem>>(`${WORKERS_API}`, {
|
return request<Global.PageResponse<ListItem>>(`${WORKERS_API}`, {
|
||||||
methos: 'GET',
|
methos: 'GET',
|
||||||
params
|
params
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function queryGpuDevicesList(
|
export async function queryGpuDevicesList(params: Global.SearchParams) {
|
||||||
params: Global.Pagination & { query?: string }
|
|
||||||
) {
|
|
||||||
return request<Global.PageResponse<GPUDeviceItem>>(`${GPU_DEVICES_API}`, {
|
return request<Global.PageResponse<GPUDeviceItem>>(`${GPU_DEVICES_API}`, {
|
||||||
methos: 'GET',
|
methos: 'GET',
|
||||||
params
|
params
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const GPUList: React.FC = () => {
|
|||||||
const [queryParams, setQueryParams] = useState({
|
const [queryParams, setQueryParams] = useState({
|
||||||
page: 1,
|
page: 1,
|
||||||
perPage: 10,
|
perPage: 10,
|
||||||
query: ''
|
search: ''
|
||||||
});
|
});
|
||||||
const handleShowSizeChange = (current: number, size: number) => {
|
const handleShowSizeChange = (current: number, size: number) => {
|
||||||
setQueryParams({
|
setQueryParams({
|
||||||
@@ -67,7 +67,7 @@ const GPUList: React.FC = () => {
|
|||||||
const handleNameChange = (e: any) => {
|
const handleNameChange = (e: any) => {
|
||||||
setQueryParams({
|
setQueryParams({
|
||||||
...queryParams,
|
...queryParams,
|
||||||
query: e.target.value
|
search: e.target.value
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const Resources: React.FC = () => {
|
|||||||
const [queryParams, setQueryParams] = useState({
|
const [queryParams, setQueryParams] = useState({
|
||||||
page: 1,
|
page: 1,
|
||||||
perPage: 10,
|
perPage: 10,
|
||||||
query: ''
|
search: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
@@ -73,7 +73,7 @@ const Resources: React.FC = () => {
|
|||||||
const handleNameChange = (e: any) => {
|
const handleNameChange = (e: any) => {
|
||||||
setQueryParams({
|
setQueryParams({
|
||||||
...queryParams,
|
...queryParams,
|
||||||
query: e.target.value
|
search: e.target.value
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ import { FormData, ListItem } from '../config/types';
|
|||||||
|
|
||||||
export const USERS_API = '/users';
|
export const USERS_API = '/users';
|
||||||
|
|
||||||
export async function queryUsersList(
|
export async function queryUsersList(params: Global.SearchParams) {
|
||||||
params: Global.Pagination & { query?: string }
|
|
||||||
) {
|
|
||||||
return request<Global.PageResponse<ListItem>>(`${USERS_API}`, {
|
return request<Global.PageResponse<ListItem>>(`${USERS_API}`, {
|
||||||
methos: 'GET',
|
methos: 'GET',
|
||||||
params
|
params
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ const Users: React.FC = () => {
|
|||||||
const [queryParams, setQueryParams] = useState({
|
const [queryParams, setQueryParams] = useState({
|
||||||
page: 1,
|
page: 1,
|
||||||
perPage: 10,
|
perPage: 10,
|
||||||
query: ''
|
search: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
const ActionList = [
|
const ActionList = [
|
||||||
@@ -104,7 +104,7 @@ const Users: React.FC = () => {
|
|||||||
const handleNameChange = (e: any) => {
|
const handleNameChange = (e: any) => {
|
||||||
setQueryParams({
|
setQueryParams({
|
||||||
...queryParams,
|
...queryParams,
|
||||||
query: e.target.value
|
search: e.target.value
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { clearAtomStorage } from '@/atoms/utils';
|
|||||||
import { RequestConfig, history } from '@umijs/max';
|
import { RequestConfig, history } from '@umijs/max';
|
||||||
import { message } from 'antd';
|
import { message } from 'antd';
|
||||||
|
|
||||||
const NoBaseURLAPIs = ['/auth', '/v1-openai'];
|
const NoBaseURLAPIs = ['/auth', '/v1-openai', '/version'];
|
||||||
|
|
||||||
export const requestConfig: RequestConfig = {
|
export const requestConfig: RequestConfig = {
|
||||||
errorConfig: {
|
errorConfig: {
|
||||||
|
|||||||
@@ -6,3 +6,9 @@ export async function queryCurrentUserState(opts?: Record<string, any>) {
|
|||||||
...opts
|
...opts
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function queryVersionInfo() {
|
||||||
|
return request(`/version`, {
|
||||||
|
method: 'GET'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user