chore: display the direct command in add worker
This commit is contained in:
+3
-12
@@ -20,18 +20,9 @@ export default function createProxyTable(target?: string) {
|
|||||||
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) => {
|
||||||
// console.log('proxyRes=====', req);
|
// console.log('headers=========', {
|
||||||
|
// res: proxyRes.headers,
|
||||||
// proxyRes.on('data', (chunk: any) => {
|
// req: req.headers
|
||||||
// res.write(chunk);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// proxyRes.on('end', () => {
|
|
||||||
// res.end();
|
|
||||||
// });
|
|
||||||
|
|
||||||
// proxyRes.on('error', (err: any) => {
|
|
||||||
// res.status(500).end('Stream error');
|
|
||||||
// });
|
// });
|
||||||
// },
|
// },
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
+9
-2
@@ -20,7 +20,10 @@ export async function getInitialState(): Promise<{
|
|||||||
const getUpdateCheck = async () => {
|
const getUpdateCheck = async () => {
|
||||||
try {
|
try {
|
||||||
const data = await updateCheck();
|
const data = await updateCheck();
|
||||||
setAtomStorage(UpdateCheckAtom, data);
|
|
||||||
|
setAtomStorage(UpdateCheckAtom, {
|
||||||
|
...data
|
||||||
|
});
|
||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('updateCheck error', error);
|
console.error('updateCheck error', error);
|
||||||
@@ -45,7 +48,11 @@ export async function getInitialState(): Promise<{
|
|||||||
const getAppVersionInfo = async () => {
|
const getAppVersionInfo = async () => {
|
||||||
try {
|
try {
|
||||||
const data = await queryVersionInfo();
|
const data = await queryVersionInfo();
|
||||||
setAtomStorage(GPUStackVersionAtom, data);
|
const isProduction = data.version?.indexOf('0.0.0') === -1;
|
||||||
|
setAtomStorage(GPUStackVersionAtom, {
|
||||||
|
...data,
|
||||||
|
isProduction
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('queryVersionInfo error', error);
|
console.error('queryVersionInfo error', error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
.driver-popover {
|
||||||
|
color: var(--ant-color-text);
|
||||||
|
|
||||||
|
.driver-popover-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: var(--font-weight-bold);
|
||||||
|
color: var(--ant-color-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-1
@@ -6,9 +6,11 @@ export const userAtom = atomWithStorage<any>('userInfo', null);
|
|||||||
export const GPUStackVersionAtom = atom<{
|
export const GPUStackVersionAtom = atom<{
|
||||||
version: string;
|
version: string;
|
||||||
git_commit: string;
|
git_commit: string;
|
||||||
|
isProduction: boolean;
|
||||||
}>({
|
}>({
|
||||||
version: '',
|
version: '',
|
||||||
git_commit: ''
|
git_commit: '',
|
||||||
|
isProduction: false
|
||||||
});
|
});
|
||||||
|
|
||||||
export const UpdateCheckAtom = atom<{
|
export const UpdateCheckAtom = atom<{
|
||||||
|
|||||||
@@ -4,10 +4,9 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: var(--font-size-base);
|
font-size: var(--font-size-base);
|
||||||
padding: 2px;
|
|
||||||
border-radius: var(--border-radius-base);
|
border-radius: var(--border-radius-base);
|
||||||
height: 40px;
|
height: 32px;
|
||||||
width: 40px;
|
padding: 0 8px;
|
||||||
border: 1px solid var(--ant-color-border);
|
border: 1px solid var(--ant-color-border);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const RadioButtons: React.FC<RadioButtonsProps> = (props) => {
|
|||||||
{options.map((option) => (
|
{options.map((option) => (
|
||||||
<span
|
<span
|
||||||
key={option.value}
|
key={option.value}
|
||||||
onClick={() => onChange(option.value)}
|
onClick={() => onChange({ target: { value: option.value } } as any)}
|
||||||
className={classNames('item', { active: value === option.value })}
|
className={classNames('item', { active: value === option.value })}
|
||||||
>
|
>
|
||||||
{option.label}
|
{option.label}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
const driverIDMap = {
|
||||||
|
deployModel: 'deploy-model'
|
||||||
|
};
|
||||||
|
|
||||||
|
const driverConfig = {};
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
@import url('src/assets/styles/common.less');
|
@import url('src/assets/styles/common.less');
|
||||||
@import url('src/assets/styles/menu.less');
|
@import url('src/assets/styles/menu.less');
|
||||||
|
@import url('src/assets/styles/driver.less');
|
||||||
|
|
||||||
html {
|
html {
|
||||||
--font-family: 'noto sans', sans-serif;
|
--font-family: 'noto sans', sans-serif;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useIntl } from '@umijs/max';
|
|||||||
import { driver, type Config } from 'driver.js';
|
import { driver, type Config } from 'driver.js';
|
||||||
import { useEffect, useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
|
|
||||||
export const useDriver = (config?: Config) => {
|
export const useDriver = (config?: Config & { id: string }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const driverRef = useRef<any>(null);
|
const driverRef = useRef<any>(null);
|
||||||
|
|
||||||
|
|||||||
@@ -212,5 +212,6 @@ export default {
|
|||||||
'common.text.latest': 'Latest',
|
'common.text.latest': 'Latest',
|
||||||
'common.text.new': 'New',
|
'common.text.new': 'New',
|
||||||
'common.text.changelog': 'Release Notes',
|
'common.text.changelog': 'Release Notes',
|
||||||
'common.button.recreate': 'Recreate'
|
'common.button.recreate': 'Recreate',
|
||||||
|
'common.button.delrecreate': 'Delete (Recreate)'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -78,5 +78,13 @@ export default {
|
|||||||
'models.form.backendVersion': 'Backend Version',
|
'models.form.backendVersion': 'Backend Version',
|
||||||
'models.form.backendVersion.tips':
|
'models.form.backendVersion.tips':
|
||||||
'Pin a specific version to keep the backend stable across GPUStack upgrades.',
|
'Pin a specific version to keep the backend stable across GPUStack upgrades.',
|
||||||
'models.form.gpuselector': 'GPU Selector'
|
'models.form.gpuselector': 'GPU Selector',
|
||||||
|
'models.form.backend.llamabox':
|
||||||
|
'llama-box: For GGUF format models, supports Linux, macOS, and Windows.',
|
||||||
|
'models.form.backend.vllm':
|
||||||
|
'vLLM: For non-GGUF format models, supports x86 Linux only.',
|
||||||
|
'models.form.backend.voxbox': 'vox-box: For non-GGUF format audio models.',
|
||||||
|
'models.form.search.gguftips':
|
||||||
|
'GGUF format is required for Mac and Windows compatibility.',
|
||||||
|
'models.form.button.addlabel': 'Add Label'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -97,8 +97,8 @@ export default {
|
|||||||
'playground.audio.button.generate': 'Generate Text Content',
|
'playground.audio.button.generate': 'Generate Text Content',
|
||||||
'playground.multiple.on': 'Enable',
|
'playground.multiple.on': 'Enable',
|
||||||
'playground.multiple.off': 'Disable',
|
'playground.multiple.off': 'Disable',
|
||||||
'playground.image.params.sampler': 'Sampler',
|
'playground.image.params.sampler': 'Sample Method',
|
||||||
'playground.image.params.schedule': 'Scheduler',
|
'playground.image.params.schedule': 'Schedule Method',
|
||||||
'playground.image.params.samplerSteps': 'Sampling Steps',
|
'playground.image.params.samplerSteps': 'Sampling Steps',
|
||||||
'playground.image.params.seed': 'Seed',
|
'playground.image.params.seed': 'Seed',
|
||||||
'playground.image.params.randomseed': 'Random Seed',
|
'playground.image.params.randomseed': 'Random Seed',
|
||||||
|
|||||||
@@ -46,5 +46,9 @@ export default {
|
|||||||
'resources.worker.add.step2.tips':
|
'resources.worker.add.step2.tips':
|
||||||
'Note: <span style="color: #000;font-weight: 600">mytoken</span> is the token obtained in the first step.',
|
'Note: <span style="color: #000;font-weight: 600">mytoken</span> is the token obtained in the first step.',
|
||||||
'resources.worker.add.step3':
|
'resources.worker.add.step3':
|
||||||
'Refresh workers list, you will see the new worker.'
|
'After success, refresh the workers list to see the new worker.',
|
||||||
|
'resources.worker.container.supported': 'Do not support windows.',
|
||||||
|
'resources.worker.current.version': 'Current version is {version}.',
|
||||||
|
'resources.worker.select.command':
|
||||||
|
'Select a label to generate the command and copy it using the copy button.'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -205,5 +205,6 @@ export default {
|
|||||||
'common.text.latest': '最新',
|
'common.text.latest': '最新',
|
||||||
'common.text.new': '新',
|
'common.text.new': '新',
|
||||||
'common.text.changelog': '更新日志',
|
'common.text.changelog': '更新日志',
|
||||||
'common.button.recreate': '重新创建'
|
'common.button.recreate': '重新创建',
|
||||||
|
'common.button.delrecreate': '删除(重建)'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -75,5 +75,12 @@ export default {
|
|||||||
'models.form.backendVersion': '后端版本',
|
'models.form.backendVersion': '后端版本',
|
||||||
'models.form.backendVersion.tips':
|
'models.form.backendVersion.tips':
|
||||||
'固定指定版本以保持后端在 GPUStack 升级过程中的稳定性',
|
'固定指定版本以保持后端在 GPUStack 升级过程中的稳定性',
|
||||||
'models.form.gpuselector': 'GPU 选择器'
|
'models.form.gpuselector': 'GPU 选择器',
|
||||||
|
'models.form.backend.llamabox':
|
||||||
|
'llama-box: 用于 GGUF 格式模型,支持 Linux, macOS 和 Windows',
|
||||||
|
'models.form.backend.vllm': 'vLLM: 用于非 GGUF 格式模型,仅支持 x86 Linux',
|
||||||
|
'models.form.backend.voxbox': 'vox-box: 用于非 GGUF 格式的音频模型',
|
||||||
|
'models.form.search.gguftips':
|
||||||
|
'Mac 和 Windows 需要使用 GGUF 格式以确保兼容性',
|
||||||
|
'models.form.button.addlabel': '添加标签'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -95,8 +95,8 @@ export default {
|
|||||||
'playground.multiple.on': '开启',
|
'playground.multiple.on': '开启',
|
||||||
'playground.multiple.off': '关闭',
|
'playground.multiple.off': '关闭',
|
||||||
'playground.image.params.sampler': '采样方法',
|
'playground.image.params.sampler': '采样方法',
|
||||||
'playground.image.params.schedule': '调度器',
|
'playground.image.params.schedule': '调度方法',
|
||||||
'playground.image.params.samplerSteps': '迭代步数',
|
'playground.image.params.samplerSteps': '采样步数',
|
||||||
'playground.image.params.seed': '种子',
|
'playground.image.params.seed': '种子',
|
||||||
'playground.image.params.randomseed': '随机种子',
|
'playground.image.params.randomseed': '随机种子',
|
||||||
'playground.image.params.negativePrompt': '负向提示',
|
'playground.image.params.negativePrompt': '负向提示',
|
||||||
|
|||||||
@@ -45,5 +45,8 @@ export default {
|
|||||||
'resources.worker.add.step2': '注册 Worker',
|
'resources.worker.add.step2': '注册 Worker',
|
||||||
'resources.worker.add.step2.tips':
|
'resources.worker.add.step2.tips':
|
||||||
'注意:<span style="color: #000;font-weight: 600">mytoken</span> 为第一步获取到的 Token',
|
'注意:<span style="color: #000;font-weight: 600">mytoken</span> 为第一步获取到的 Token',
|
||||||
'resources.worker.add.step3': '刷新 workers 列表,可以看到新添加的 worker'
|
'resources.worker.add.step3': '成功后,刷新 worker 列表即可看到新的 worker',
|
||||||
|
'resources.worker.container.supported': '不支持 Windows',
|
||||||
|
'resources.worker.current.version': '当前版本为 {version}',
|
||||||
|
'resources.worker.select.command': '选择一个标签生成命令并使用复制按钮复制'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -153,6 +153,21 @@ const AdvanceConfig: React.FC<AdvanceConfigProps> = (props) => {
|
|||||||
const collapseItems = useMemo(() => {
|
const collapseItems = useMemo(() => {
|
||||||
const children = (
|
const children = (
|
||||||
<>
|
<>
|
||||||
|
{/* <Form.Item<FormData> name="labels">
|
||||||
|
<ListInput
|
||||||
|
placeholder={
|
||||||
|
backendParamsHolderTips[backend]
|
||||||
|
? intl.formatMessage({
|
||||||
|
id: backendParamsHolderTips[backend].holder
|
||||||
|
})
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
btnText="models.form.button.addlabel"
|
||||||
|
label="Labels"
|
||||||
|
dataList={form.getFieldValue('backend_parameters') || []}
|
||||||
|
options={modelLabels}
|
||||||
|
></ListInput>
|
||||||
|
</Form.Item> */}
|
||||||
<Form.Item name="scheduleType">
|
<Form.Item name="scheduleType">
|
||||||
<SealSelect
|
<SealSelect
|
||||||
label={intl.formatMessage({ id: 'models.form.scheduletype' })}
|
label={intl.formatMessage({ id: 'models.form.scheduletype' })}
|
||||||
|
|||||||
@@ -508,38 +508,54 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
|||||||
min={0}
|
min={0}
|
||||||
></SealInput.Number>
|
></SealInput.Number>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name="backend" id="backend-field">
|
<Form.Item name="backend">
|
||||||
<SealSelect
|
<div id="backend-field">
|
||||||
onChange={handleBackendChange}
|
<SealSelect
|
||||||
label={intl.formatMessage({ id: 'models.form.backend' })}
|
onChange={handleBackendChange}
|
||||||
options={[
|
label={intl.formatMessage({ id: 'models.form.backend' })}
|
||||||
{
|
description={
|
||||||
label: `llama-box`,
|
<div>
|
||||||
value: backendOptionsMap.llamaBox,
|
<div>
|
||||||
disabled:
|
1.{' '}
|
||||||
props.source === modelSourceMap.local_path_value
|
{intl.formatMessage({ id: 'models.form.backend.llamabox' })}
|
||||||
? false
|
</div>
|
||||||
: !isGGUF
|
<div>
|
||||||
},
|
2. {intl.formatMessage({ id: 'models.form.backend.vllm' })}
|
||||||
{
|
</div>
|
||||||
label: 'vLLM',
|
<div>
|
||||||
value: backendOptionsMap.vllm,
|
3. {intl.formatMessage({ id: 'models.form.backend.voxbox' })}
|
||||||
disabled:
|
</div>
|
||||||
props.source === modelSourceMap.local_path_value
|
</div>
|
||||||
? false
|
|
||||||
: isGGUF
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'vox-box',
|
|
||||||
value: backendOptionsMap.voxBox,
|
|
||||||
disabled: props.source === modelSourceMap.ollama_library_value
|
|
||||||
}
|
}
|
||||||
]}
|
options={[
|
||||||
disabled={
|
{
|
||||||
action === PageAction.EDIT &&
|
label: `llama-box`,
|
||||||
props.source !== modelSourceMap.local_path_value
|
value: backendOptionsMap.llamaBox,
|
||||||
}
|
disabled:
|
||||||
></SealSelect>
|
props.source === modelSourceMap.local_path_value
|
||||||
|
? false
|
||||||
|
: !isGGUF
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'vLLM',
|
||||||
|
value: backendOptionsMap.vllm,
|
||||||
|
disabled:
|
||||||
|
props.source === modelSourceMap.local_path_value
|
||||||
|
? false
|
||||||
|
: isGGUF
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'vox-box',
|
||||||
|
value: backendOptionsMap.voxBox,
|
||||||
|
disabled: props.source === modelSourceMap.ollama_library_value
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
disabled={
|
||||||
|
action === PageAction.EDIT &&
|
||||||
|
props.source !== modelSourceMap.local_path_value
|
||||||
|
}
|
||||||
|
></SealSelect>
|
||||||
|
</div>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item<FormData> name="description">
|
<Form.Item<FormData> name="description">
|
||||||
<SealInput.TextArea
|
<SealInput.TextArea
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import ModalFooter from '@/components/modal-footer';
|
import ModalFooter from '@/components/modal-footer';
|
||||||
import { PageActionType } from '@/config/types';
|
import { PageActionType } from '@/config/types';
|
||||||
import useDriver from '@/hooks/use-driver';
|
|
||||||
import { CloseOutlined } from '@ant-design/icons';
|
import { CloseOutlined } from '@ant-design/icons';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Button, Drawer } from 'antd';
|
import { Button, Drawer } from 'antd';
|
||||||
@@ -58,9 +57,10 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|||||||
modelSourceMap.huggingface_value,
|
modelSourceMap.huggingface_value,
|
||||||
modelSourceMap.modelscope_value
|
modelSourceMap.modelscope_value
|
||||||
];
|
];
|
||||||
const { start } = useDriver({
|
// const { start } = useDriver({
|
||||||
steps
|
// steps,
|
||||||
});
|
// id: 'deploy-model'
|
||||||
|
// });
|
||||||
const form = useRef<any>({});
|
const form = useRef<any>({});
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [selectedModel, setSelectedModel] = useState<any>({});
|
const [selectedModel, setSelectedModel] = useState<any>({});
|
||||||
@@ -128,7 +128,9 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|||||||
|
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
// if (open && loadfinish) {
|
// if (open && loadfinish) {
|
||||||
// start();
|
// setTimeout(() => {
|
||||||
|
// start();
|
||||||
|
// }, 1000);
|
||||||
// }
|
// }
|
||||||
// }, [loadfinish, open]);
|
// }, [loadfinish, open]);
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import {
|
|||||||
ListItem as WorkerListItem
|
ListItem as WorkerListItem
|
||||||
} from '@/pages/resources/config/types';
|
} from '@/pages/resources/config/types';
|
||||||
import {
|
import {
|
||||||
|
DeleteOutlined,
|
||||||
FieldTimeOutlined,
|
FieldTimeOutlined,
|
||||||
FileSyncOutlined,
|
|
||||||
InfoCircleOutlined
|
InfoCircleOutlined
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
@@ -71,12 +71,12 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
|
|||||||
icon: <FieldTimeOutlined />
|
icon: <FieldTimeOutlined />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'common.button.recreate',
|
label: 'common.button.delrecreate',
|
||||||
key: 'delete',
|
key: 'delete',
|
||||||
props: {
|
props: {
|
||||||
danger: false
|
danger: true
|
||||||
},
|
},
|
||||||
icon: <FileSyncOutlined />
|
icon: <DeleteOutlined />
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import { BulbOutlined, InfoCircleOutlined } from '@ant-design/icons';
|
import {
|
||||||
|
BulbOutlined,
|
||||||
|
QuestionCircleOutlined,
|
||||||
|
WarningOutlined
|
||||||
|
} from '@ant-design/icons';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Checkbox, Select, Tooltip } from 'antd';
|
import { Checkbox, Select, Tooltip } from 'antd';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
@@ -233,6 +237,12 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
|
|||||||
onChange={handleSearchInputChange}
|
onChange={handleSearchInputChange}
|
||||||
modelSource={modelSource}
|
modelSource={modelSource}
|
||||||
></SearchInput>
|
></SearchInput>
|
||||||
|
<div className="gguf-tips">
|
||||||
|
<WarningOutlined className="font-size-14 m-r-5 warning" />
|
||||||
|
<span>
|
||||||
|
{intl.formatMessage({ id: 'models.form.search.gguftips' })}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<div className={SearchStyle.filter}>
|
<div className={SearchStyle.filter}>
|
||||||
<span>
|
<span>
|
||||||
<span className="value">
|
<span className="value">
|
||||||
@@ -268,7 +278,7 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
GGUF
|
GGUF
|
||||||
<InfoCircleOutlined className="m-l-4" />
|
<QuestionCircleOutlined className="m-l-4" />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
</span>
|
</span>
|
||||||
@@ -318,6 +328,7 @@ const SearchModel: React.FC<SearchInputProps> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{
|
||||||
<SearchResult
|
<SearchResult
|
||||||
loading={dataSource.loading}
|
loading={dataSource.loading}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ const SearchResult: React.FC<SearchResultProps> = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<SimpleBar style={{ height: 'calc(100vh - 194px)' }}>
|
<SimpleBar style={{ height: 'calc(100vh - 224px)' }}>
|
||||||
<div style={{ ...props.style }} className="search-result-wrap">
|
<div style={{ ...props.style }} className="search-result-wrap">
|
||||||
<Spin spinning={props.loading}>
|
<Spin spinning={props.loading}>
|
||||||
<div style={{ minHeight: 200 }}>
|
<div style={{ minHeight: 200 }}>
|
||||||
|
|||||||
@@ -449,7 +449,7 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
const handleDeleteInstace = (row: any, list: ModelInstanceListItem[]) => {
|
const handleDeleteInstace = (row: any, list: ModelInstanceListItem[]) => {
|
||||||
modalRef.current.show({
|
modalRef.current.show({
|
||||||
content: 'models.instances',
|
content: 'models.instances',
|
||||||
okText: 'common.button.recreate',
|
okText: 'common.button.delrecreate',
|
||||||
name: row.name,
|
name: row.name,
|
||||||
async onOk() {
|
async onOk() {
|
||||||
await deleteModelInstance(row.id);
|
await deleteModelInstance(row.id);
|
||||||
|
|||||||
@@ -345,3 +345,11 @@ export const getVllmCliArgs = (inputString: string) => {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const modelLabels = [
|
||||||
|
{ label: 'Image', value: 'image_only' },
|
||||||
|
{ label: 'Text-to-speech', value: 'text_to_speech' },
|
||||||
|
{ label: 'Speech-to-text', value: 'speech_to_text' },
|
||||||
|
{ label: 'reranker', value: 'reranker' },
|
||||||
|
{ label: 'Embedding', value: 'embedding_only' }
|
||||||
|
];
|
||||||
|
|||||||
@@ -104,6 +104,10 @@ const options = [
|
|||||||
{
|
{
|
||||||
label: '--image-no-vae-tiling',
|
label: '--image-no-vae-tiling',
|
||||||
value: '--image-no-vae-tiling'
|
value: '--image-no-vae-tiling'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '--mmproj',
|
||||||
|
value: '--mmproj'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,17 @@
|
|||||||
// height: calc(100vh - 194px);
|
// height: calc(100vh - 194px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gguf-tips {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 0;
|
||||||
|
color: var(--ant-color-text-secondary);
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
color: var(--ant-color-warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.search-bar {
|
.search-bar {
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import HighlightCode from '@/components/highlight-code';
|
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Modal } from 'antd';
|
import { Modal, Tabs, TabsProps } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { addWorkerGuide } from '../config';
|
import ContainerInstall from './container-install';
|
||||||
|
import ScriptInstall from './script-install';
|
||||||
|
|
||||||
type ViewModalProps = {
|
type ViewModalProps = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -12,8 +12,21 @@ type ViewModalProps = {
|
|||||||
const AddWorker: React.FC<ViewModalProps> = (props) => {
|
const AddWorker: React.FC<ViewModalProps> = (props) => {
|
||||||
const { open, onCancel } = props || {};
|
const { open, onCancel } = props || {};
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
const [token, setToken] = React.useState('');
|
||||||
|
const [activeKey, setActiveKey] = React.useState('script');
|
||||||
|
|
||||||
const origin = window.location.origin;
|
const items: TabsProps['items'] = [
|
||||||
|
{
|
||||||
|
key: 'script',
|
||||||
|
label: 'Script Installation',
|
||||||
|
children: <ScriptInstall token={token}></ScriptInstall>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'container',
|
||||||
|
label: 'Container Installation',
|
||||||
|
children: <ContainerInstall token={token} />
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@@ -26,49 +39,19 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
|
|||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
keyboard={false}
|
keyboard={false}
|
||||||
width={600}
|
width={600}
|
||||||
|
styles={{
|
||||||
|
body: {
|
||||||
|
height: 310
|
||||||
|
}
|
||||||
|
}}
|
||||||
footer={null}
|
footer={null}
|
||||||
>
|
>
|
||||||
<div>
|
<Tabs
|
||||||
<h3>1. {intl.formatMessage({ id: 'resources.worker.add.step1' })}</h3>
|
items={items}
|
||||||
<h4>{intl.formatMessage({ id: 'resources.worker.linuxormaxos' })}</h4>
|
activeKey={activeKey}
|
||||||
<HighlightCode
|
type="card"
|
||||||
code={addWorkerGuide.mac.getToken}
|
onChange={(key) => setActiveKey(key)}
|
||||||
theme="dark"
|
></Tabs>
|
||||||
></HighlightCode>
|
|
||||||
<h4>Windows </h4>
|
|
||||||
<HighlightCode
|
|
||||||
code={addWorkerGuide.win.getToken}
|
|
||||||
theme="dark"
|
|
||||||
></HighlightCode>
|
|
||||||
<h3>
|
|
||||||
2. {intl.formatMessage({ id: 'resources.worker.add.step2' })}{' '}
|
|
||||||
<span
|
|
||||||
className="font-size-12"
|
|
||||||
style={{ color: 'var(--ant-color-text-tertiary)' }}
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: `(${intl.formatMessage({
|
|
||||||
id: 'resources.worker.add.step2.tips'
|
|
||||||
})})`
|
|
||||||
}}
|
|
||||||
></span>
|
|
||||||
</h3>
|
|
||||||
<h4>{intl.formatMessage({ id: 'resources.worker.linuxormaxos' })}</h4>
|
|
||||||
<HighlightCode
|
|
||||||
code={addWorkerGuide.mac.registerWorker(origin)}
|
|
||||||
theme="dark"
|
|
||||||
></HighlightCode>
|
|
||||||
<h4>Windows </h4>
|
|
||||||
<HighlightCode
|
|
||||||
theme="dark"
|
|
||||||
code={addWorkerGuide.win.registerWorker(origin)}
|
|
||||||
></HighlightCode>
|
|
||||||
<h4>Docker </h4>
|
|
||||||
<HighlightCode
|
|
||||||
theme="dark"
|
|
||||||
code={addWorkerGuide.docker.registerWorker(origin)}
|
|
||||||
></HighlightCode>
|
|
||||||
<h3>3. {intl.formatMessage({ id: 'resources.worker.add.step3' })}</h3>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
import { GPUStackVersionAtom } from '@/atoms/user';
|
||||||
|
import { getAtomStorage } from '@/atoms/utils';
|
||||||
|
import HighlightCode from '@/components/highlight-code';
|
||||||
|
import { BulbOutlined, WarningOutlined } from '@ant-design/icons';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { Radio } from 'antd';
|
||||||
|
import React from 'react';
|
||||||
|
import { addWorkerGuide, containerInstallOptions } from '../config';
|
||||||
|
import './styles/installation.less';
|
||||||
|
|
||||||
|
type ViewModalProps = {
|
||||||
|
token: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const AddWorker: React.FC<ViewModalProps> = (props) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const origin = window.location.origin;
|
||||||
|
const [activeKey, setActiveKey] = React.useState('cuda');
|
||||||
|
const versionInfo = getAtomStorage(GPUStackVersionAtom);
|
||||||
|
|
||||||
|
const code = React.useMemo(() => {
|
||||||
|
let version = versionInfo?.version;
|
||||||
|
if (!version || !versionInfo.isProduction) {
|
||||||
|
version = 'main';
|
||||||
|
}
|
||||||
|
if (activeKey === 'cuda') {
|
||||||
|
return addWorkerGuide.docker.registerWorker({
|
||||||
|
server: origin,
|
||||||
|
tag: version,
|
||||||
|
token: props.token
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return addWorkerGuide.docker.registerWorker({
|
||||||
|
server: origin,
|
||||||
|
tag: `${version}-${activeKey}`,
|
||||||
|
token: props.token
|
||||||
|
});
|
||||||
|
}, [versionInfo, activeKey, props.token, origin]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="container-install">
|
||||||
|
<div className="notes">
|
||||||
|
<span>
|
||||||
|
1.{' '}
|
||||||
|
<span>
|
||||||
|
{intl.formatMessage({ id: 'resources.worker.container.supported' })}
|
||||||
|
</span>
|
||||||
|
<WarningOutlined
|
||||||
|
style={{ color: 'var(--ant-color-warning)' }}
|
||||||
|
className="font-size-14 m-l-5"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
2.{' '}
|
||||||
|
{intl.formatMessage(
|
||||||
|
{ id: 'resources.worker.current.version' },
|
||||||
|
{ version: versionInfo.version }
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
3. {intl.formatMessage({ id: 'resources.worker.select.command' })}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="m-b-20">
|
||||||
|
<Radio.Group
|
||||||
|
block
|
||||||
|
options={containerInstallOptions}
|
||||||
|
defaultValue={activeKey}
|
||||||
|
value={activeKey}
|
||||||
|
optionType="button"
|
||||||
|
buttonStyle="solid"
|
||||||
|
onChange={(e) => setActiveKey(e.target.value)}
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<HighlightCode theme="dark" code={code}></HighlightCode>
|
||||||
|
<h3>
|
||||||
|
<BulbOutlined className="m-r-5"></BulbOutlined>
|
||||||
|
{intl.formatMessage({ id: 'resources.worker.add.step3' })}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(AddWorker);
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import HighlightCode from '@/components/highlight-code';
|
||||||
|
import { BulbOutlined } from '@ant-design/icons';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import React from 'react';
|
||||||
|
import { addWorkerGuide } from '../config';
|
||||||
|
|
||||||
|
type ViewModalProps = { token: string };
|
||||||
|
|
||||||
|
const AddWorker: React.FC<ViewModalProps> = (props) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const origin = window.location.origin;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h4>{intl.formatMessage({ id: 'resources.worker.linuxormaxos' })}</h4>
|
||||||
|
<HighlightCode
|
||||||
|
code={addWorkerGuide.mac.registerWorker({
|
||||||
|
server: origin,
|
||||||
|
token: props.token
|
||||||
|
})}
|
||||||
|
theme="dark"
|
||||||
|
></HighlightCode>
|
||||||
|
<h4>Windows </h4>
|
||||||
|
<HighlightCode
|
||||||
|
theme="dark"
|
||||||
|
code={addWorkerGuide.win.registerWorker({
|
||||||
|
server: origin,
|
||||||
|
token: props.token
|
||||||
|
})}
|
||||||
|
></HighlightCode>
|
||||||
|
<h3>
|
||||||
|
<BulbOutlined className="m-r-5"></BulbOutlined>
|
||||||
|
{intl.formatMessage({ id: 'resources.worker.add.step3' })}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(AddWorker);
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
.container-install {
|
||||||
|
.notes {
|
||||||
|
padding: 10px 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
border-left: 2px solid var(--ant-color-split);
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-left: 16px;
|
||||||
|
background: var(--color-fill-sider);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,22 +18,29 @@ export const status: any = {
|
|||||||
export const addWorkerGuide = {
|
export const addWorkerGuide = {
|
||||||
mac: {
|
mac: {
|
||||||
getToken: 'cat /var/lib/gpustack/token',
|
getToken: 'cat /var/lib/gpustack/token',
|
||||||
registerWorker(server: string) {
|
registerWorker(params: { server: string; token: string }) {
|
||||||
return `curl -sfL https://get.gpustack.ai | sh -s - --server-url ${server} --token mytoken`;
|
return `curl -sfL https://get.gpustack.ai | sh -s - --server-url ${params.server} --token ${params.token}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
win: {
|
win: {
|
||||||
getToken:
|
getToken:
|
||||||
'Get-Content -Path (Join-Path -Path $env:APPDATA -ChildPath "gpustack\\token") -Raw',
|
'Get-Content -Path (Join-Path -Path $env:APPDATA -ChildPath "gpustack\\token") -Raw',
|
||||||
registerWorker(server: string) {
|
registerWorker(params: { server: string; token: string }) {
|
||||||
return `Invoke-Expression "& { $((Invoke-WebRequest -Uri "https://get.gpustack.ai" -UseBasicParsing).Content) } --server-url ${server} --token mytoken"`;
|
return `Invoke-Expression "& { $((Invoke-WebRequest -Uri "https://get.gpustack.ai" -UseBasicParsing).Content) } --server-url ${params.server} --token ${params.token}"`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
docker: {
|
docker: {
|
||||||
getToken:
|
getToken:
|
||||||
'Get-Content -Path (Join-Path -Path $env:APPDATA -ChildPath "gpustack\\token") -Raw',
|
'Get-Content -Path (Join-Path -Path $env:APPDATA -ChildPath "gpustack\\token") -Raw',
|
||||||
registerWorker(server: string) {
|
registerWorker(params: { server: string; tag: string; token: string }) {
|
||||||
return `docker run -d --gpus all --ipc=host --network=host gpustack/gpustack --server-url ${server} --token mytoken`;
|
return `docker run -d --gpus all --ipc=host --network=host gpustack/gpustack:${params.tag} --server-url ${params.server} --token ${params.token}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const containerInstallOptions = [
|
||||||
|
{ label: 'CUDA', value: 'cuda' },
|
||||||
|
{ label: 'CANN', value: 'npu' },
|
||||||
|
{ label: 'MUSA', value: 'musa' },
|
||||||
|
{ label: 'CPU', value: 'cpu' }
|
||||||
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user