feat: dynamic backends
This commit is contained in:
@@ -23,3 +23,13 @@ export const clusterListAtom = atom<
|
|||||||
value: number;
|
value: number;
|
||||||
}[]
|
}[]
|
||||||
>([]);
|
>([]);
|
||||||
|
|
||||||
|
export const backendOptionsAtom = atom<
|
||||||
|
{
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
default_backend_param: string[];
|
||||||
|
default_version: string;
|
||||||
|
versions: { label: string; value: string }[];
|
||||||
|
}[]
|
||||||
|
>([]);
|
||||||
|
|||||||
@@ -12,7 +12,12 @@ import { SealFormItemProps } from './types';
|
|||||||
import Wrapper from './wrapper';
|
import Wrapper from './wrapper';
|
||||||
import InputWrapper from './wrapper/input';
|
import InputWrapper from './wrapper/input';
|
||||||
|
|
||||||
const LabelWrapper = styled.div`
|
const LabelWrapper = styled.div.attrs({
|
||||||
|
className: 'seal-textarea-label'
|
||||||
|
})`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
background-color: var(--ant-color-bg-container);
|
background-color: var(--ant-color-bg-container);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -110,7 +115,7 @@ const SealTextArea: React.FC<InputTextareaProps & SealFormItemProps> = (
|
|||||||
<InputWrapper>
|
<InputWrapper>
|
||||||
<Wrapper
|
<Wrapper
|
||||||
status={status}
|
status={status}
|
||||||
label={<LabelWrapper>{label}</LabelWrapper>}
|
label={label}
|
||||||
isFocus={isFocus}
|
isFocus={isFocus}
|
||||||
required={required}
|
required={required}
|
||||||
description={description}
|
description={description}
|
||||||
|
|||||||
@@ -154,7 +154,9 @@ export const Label = styled.div.attrs<{
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
// inner
|
// inner
|
||||||
const Inner = styled.div`
|
const Inner = styled.div.attrs({
|
||||||
|
className: '__inner__'
|
||||||
|
})`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -163,6 +163,9 @@ const InputWrapper = styled.div`
|
|||||||
height: auto;
|
height: auto;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
|
.ant-input-textarea-allow-clear.ant-input-affix-wrapper {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default InputWrapper;
|
export default InputWrapper;
|
||||||
|
|||||||
@@ -97,10 +97,10 @@ const SelectWrapper = styled.div`
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
height: 54px;
|
height: 54px;
|
||||||
|
|
||||||
.ant-select-selection-search {
|
.ant-select-selection-wrap {
|
||||||
// top: 20px !important;
|
height: 100%;
|
||||||
// inset-inline-start: ${INPUT_INNER_PADDING}px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.ant-select-auto-complete {
|
&.ant-select-auto-complete {
|
||||||
.ant-select-selection-search {
|
.ant-select-selection-search {
|
||||||
padding-inline-start: ${INPUT_INNER_PADDING}px;
|
padding-inline-start: ${INPUT_INNER_PADDING}px;
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
const MODEL_EVALUATIONS = '/model-evaluations';
|
|
||||||
|
|
||||||
let controller = new AbortController();
|
|
||||||
let signal = controller.signal;
|
|
||||||
|
|
||||||
self.onmessage = async (event) => {
|
|
||||||
const { list, modelSource, modelSourceMap } = event.data;
|
|
||||||
|
|
||||||
const repoList = list.map((item: any) => ({
|
|
||||||
source: modelSource,
|
|
||||||
...(modelSource === modelSourceMap.huggingface_value
|
|
||||||
? { huggingface_repo_id: item.name }
|
|
||||||
: { model_scope_model_id: item.name })
|
|
||||||
}));
|
|
||||||
|
|
||||||
try {
|
|
||||||
controller?.abort();
|
|
||||||
controller = new AbortController();
|
|
||||||
signal = controller.signal;
|
|
||||||
const response = await fetch(`v1/${MODEL_EVALUATIONS}`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
signal,
|
|
||||||
body: JSON.stringify({ model_specs: repoList })
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const evaluations = await response.json();
|
|
||||||
const { results } = evaluations;
|
|
||||||
const resultList = list.map((item: any, index: number) => {
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
evaluateResult: results[index] || null
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
self.postMessage({ success: true, resultList });
|
|
||||||
} catch (error) {
|
|
||||||
self.postMessage({ success: false, resultList: list });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.onmessage = (event) => {
|
|
||||||
if (event.data === 'abort') {
|
|
||||||
controller.abort();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -20,6 +20,8 @@ export const MODEL_INSTANCE_API = '/model-instances';
|
|||||||
|
|
||||||
export const MODEL_EVALUATIONS = '/model-evaluations';
|
export const MODEL_EVALUATIONS = '/model-evaluations';
|
||||||
|
|
||||||
|
export const BACKEND_LIST_API = '/inference-backend/list';
|
||||||
|
|
||||||
const setProxyUrl = (url: string) => {
|
const setProxyUrl = (url: string) => {
|
||||||
return `/proxy?url=${encodeURIComponent(url)}`;
|
return `/proxy?url=${encodeURIComponent(url)}`;
|
||||||
};
|
};
|
||||||
@@ -396,3 +398,54 @@ export async function evaluationsModelSpec(
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function queryBackendList() {
|
||||||
|
// return request<{
|
||||||
|
// items: {
|
||||||
|
// backend_name: string;
|
||||||
|
// backend_show_name: string;
|
||||||
|
// from_config: boolean;
|
||||||
|
// default_version: string;
|
||||||
|
// default_backend_param: string[];
|
||||||
|
// versions: string[];
|
||||||
|
// }[];
|
||||||
|
// }>(BACKEND_LIST_API, {
|
||||||
|
// method: 'GET'
|
||||||
|
// });
|
||||||
|
return {
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
backend_name: 'vllm',
|
||||||
|
backend_show_name: 'vLLM',
|
||||||
|
from_config: false,
|
||||||
|
default_version: '0.10.1.1',
|
||||||
|
default_backend_param: null,
|
||||||
|
versions: ['0.10.1.1', '0.10.0', '0.9.2', '0.8.5', '0.8.3']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backend_name: 'ascend-mindie',
|
||||||
|
backend_show_name: 'Ascend MindIE',
|
||||||
|
from_config: false,
|
||||||
|
default_version: null,
|
||||||
|
default_backend_param: null,
|
||||||
|
versions: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backend_name: 'custom',
|
||||||
|
backend_show_name: 'Custom',
|
||||||
|
from_config: false,
|
||||||
|
default_version: null,
|
||||||
|
default_backend_param: null,
|
||||||
|
versions: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backend_name: 'test',
|
||||||
|
backend_show_name: null,
|
||||||
|
from_config: true,
|
||||||
|
default_version: 'v1',
|
||||||
|
default_backend_param: ['--host=0.0.0.0'],
|
||||||
|
versions: ['v1']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,22 +2,17 @@ import IconFont from '@/components/icon-font';
|
|||||||
import LabelSelector from '@/components/label-selector';
|
import LabelSelector from '@/components/label-selector';
|
||||||
import ListInput from '@/components/list-input';
|
import ListInput from '@/components/list-input';
|
||||||
import CheckboxField from '@/components/seal-form/checkbox-field';
|
import CheckboxField from '@/components/seal-form/checkbox-field';
|
||||||
import SealInput from '@/components/seal-form/seal-input';
|
|
||||||
import SealSelect from '@/components/seal-form/seal-select';
|
import SealSelect from '@/components/seal-form/seal-select';
|
||||||
import TooltipList from '@/components/tooltip-list';
|
import TooltipList from '@/components/tooltip-list';
|
||||||
import { PageAction } from '@/config';
|
|
||||||
import { PageActionType } from '@/config/types';
|
import { PageActionType } from '@/config/types';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Collapse, Form, FormInstance, Typography } from 'antd';
|
import { Collapse, Form, FormInstance, Typography } from 'antd';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { useCallback, useMemo } from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
backendLabelMap,
|
|
||||||
backendParamsHolderTips,
|
backendParamsHolderTips,
|
||||||
backendTipsList,
|
|
||||||
getBackendParamsTips,
|
getBackendParamsTips,
|
||||||
modelCategories,
|
modelCategories,
|
||||||
modelSourceMap,
|
|
||||||
placementStrategyOptions,
|
placementStrategyOptions,
|
||||||
ScheduleValueMap
|
ScheduleValueMap
|
||||||
} from '../config';
|
} from '../config';
|
||||||
@@ -26,6 +21,7 @@ import BackendParameters, {
|
|||||||
} from '../config/backend-parameters';
|
} from '../config/backend-parameters';
|
||||||
import { useFormContext } from '../config/form-context';
|
import { useFormContext } from '../config/form-context';
|
||||||
import { FormData } from '../config/types';
|
import { FormData } from '../config/types';
|
||||||
|
import BackendFields from '../forms/backend-fields';
|
||||||
import dataformStyles from '../style/data-form.less';
|
import dataformStyles from '../style/data-form.less';
|
||||||
import Performance from './performance';
|
import Performance from './performance';
|
||||||
|
|
||||||
@@ -35,8 +31,6 @@ interface AdvanceConfigProps {
|
|||||||
gpuOptions: Array<any>;
|
gpuOptions: Array<any>;
|
||||||
action: PageActionType;
|
action: PageActionType;
|
||||||
source: string;
|
source: string;
|
||||||
backendOptions?: Global.BaseOption<string>[];
|
|
||||||
handleBackendChange?: (value: string) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const placementStrategyTips = [
|
const placementStrategyTips = [
|
||||||
@@ -51,7 +45,7 @@ const placementStrategyTips = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const AdvanceConfig: React.FC<AdvanceConfigProps> = (props) => {
|
const AdvanceConfig: React.FC<AdvanceConfigProps> = (props) => {
|
||||||
const { form, isGGUF, gpuOptions, source, backendOptions, action } = props;
|
const { form, isGGUF, gpuOptions, source, action } = props;
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const wokerSelector = Form.useWatch('worker_selector', form);
|
const wokerSelector = Form.useWatch('worker_selector', form);
|
||||||
const EnviromentVars = Form.useWatch('env', form);
|
const EnviromentVars = Form.useWatch('env', form);
|
||||||
@@ -63,7 +57,7 @@ const AdvanceConfig: React.FC<AdvanceConfigProps> = (props) => {
|
|||||||
const placement_strategy = Form.useWatch('placement_strategy', form);
|
const placement_strategy = Form.useWatch('placement_strategy', form);
|
||||||
const gpuSelectorIds = Form.useWatch(['gpu_selector', 'gpu_ids'], form);
|
const gpuSelectorIds = Form.useWatch(['gpu_selector', 'gpu_ids'], form);
|
||||||
const worker_selector = Form.useWatch('worker_selector', form);
|
const worker_selector = Form.useWatch('worker_selector', form);
|
||||||
const { onValuesChange } = useFormContext();
|
const { onValuesChange, onBackendChange, backendOptions } = useFormContext();
|
||||||
|
|
||||||
const paramsConfig = useMemo(() => {
|
const paramsConfig = useMemo(() => {
|
||||||
return _.get(BackendParameters, backend, []);
|
return _.get(BackendParameters, backend, []);
|
||||||
@@ -154,47 +148,7 @@ const AdvanceConfig: React.FC<AdvanceConfigProps> = (props) => {
|
|||||||
options={modelCategories}
|
options={modelCategories}
|
||||||
></SealSelect>
|
></SealSelect>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name="backend" rules={[{ required: true }]}>
|
<BackendFields></BackendFields>
|
||||||
<SealSelect
|
|
||||||
required
|
|
||||||
onChange={props.handleBackendChange}
|
|
||||||
label={intl.formatMessage({ id: 'models.form.backend' })}
|
|
||||||
description={<TooltipList list={backendTipsList}></TooltipList>}
|
|
||||||
options={
|
|
||||||
backendOptions ?? [
|
|
||||||
{
|
|
||||||
label: backendLabelMap[backendOptionsMap.vllm],
|
|
||||||
value: backendOptionsMap.vllm,
|
|
||||||
disabled:
|
|
||||||
props.source === modelSourceMap.local_path_value
|
|
||||||
? false
|
|
||||||
: isGGUF
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: backendLabelMap[backendOptionsMap.ascendMindie],
|
|
||||||
value: backendOptionsMap.ascendMindie,
|
|
||||||
disabled:
|
|
||||||
props.source === modelSourceMap.local_path_value
|
|
||||||
? false
|
|
||||||
: isGGUF
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: backendLabelMap[backendOptionsMap.voxBox],
|
|
||||||
value: backendOptionsMap.voxBox,
|
|
||||||
disabled:
|
|
||||||
props.source === modelSourceMap.local_path_value
|
|
||||||
? false
|
|
||||||
: props.source === modelSourceMap.ollama_library_value ||
|
|
||||||
isGGUF
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
disabled={
|
|
||||||
action === PageAction.EDIT &&
|
|
||||||
props.source !== modelSourceMap.local_path_value
|
|
||||||
}
|
|
||||||
></SealSelect>
|
|
||||||
</Form.Item>
|
|
||||||
{scheduleType === ScheduleValueMap.Auto && (
|
{scheduleType === ScheduleValueMap.Auto && (
|
||||||
<>
|
<>
|
||||||
<Form.Item<FormData> name="placement_strategy">
|
<Form.Item<FormData> name="placement_strategy">
|
||||||
@@ -257,51 +211,6 @@ const AdvanceConfig: React.FC<AdvanceConfigProps> = (props) => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Form.Item name="backend_version">
|
|
||||||
<SealInput.Input
|
|
||||||
placeholder={
|
|
||||||
backendParamsTips?.version
|
|
||||||
? `${intl.formatMessage({ id: 'common.help.eg' })} ${backendParamsTips?.version}`
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
onBlur={handleBackendVersionOnBlur}
|
|
||||||
label={intl.formatMessage({ id: 'models.form.backendVersion' })}
|
|
||||||
description={intl.formatMessage(
|
|
||||||
{
|
|
||||||
id: 'models.form.backendVersion.tips'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
backend: backendLabelMap[backend],
|
|
||||||
version: backendParamsTips?.version
|
|
||||||
? `(${intl.formatMessage({ id: 'common.help.eg' })} ${backendParamsTips?.version})`
|
|
||||||
: '',
|
|
||||||
link: backendParamsTips?.releases && (
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
marginLeft: 5
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography.Link
|
|
||||||
className="flex-center"
|
|
||||||
style={{ display: 'inline' }}
|
|
||||||
href={backendParamsTips?.releases}
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
{intl.formatMessage({ id: 'models.form.releases' })}
|
|
||||||
</span>
|
|
||||||
<IconFont
|
|
||||||
type="icon-external-link"
|
|
||||||
className="font-size-14 m-l-4"
|
|
||||||
></IconFont>
|
|
||||||
</Typography.Link>
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
></SealInput.Input>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item<FormData> name="backend_parameters">
|
<Form.Item<FormData> name="backend_parameters">
|
||||||
<ListInput
|
<ListInput
|
||||||
placeholder={
|
placeholder={
|
||||||
|
|||||||
@@ -9,7 +9,12 @@ import React, { forwardRef, useImperativeHandle } from 'react';
|
|||||||
import { excludeFields, ScheduleValueMap, sourceOptions } from '../config';
|
import { excludeFields, ScheduleValueMap, sourceOptions } from '../config';
|
||||||
import { backendOptionsMap } from '../config/backend-parameters';
|
import { backendOptionsMap } from '../config/backend-parameters';
|
||||||
import { FormContext } from '../config/form-context';
|
import { FormContext } from '../config/form-context';
|
||||||
import { DeployFormKey, FormData, SourceType } from '../config/types';
|
import {
|
||||||
|
BackendOption,
|
||||||
|
DeployFormKey,
|
||||||
|
FormData,
|
||||||
|
SourceType
|
||||||
|
} from '../config/types';
|
||||||
import CatalogFrom from '../forms/catalog';
|
import CatalogFrom from '../forms/catalog';
|
||||||
import HuggingFaceForm from '../forms/hugging-face';
|
import HuggingFaceForm from '../forms/hugging-face';
|
||||||
import LocalPathForm from '../forms/local-path';
|
import LocalPathForm from '../forms/local-path';
|
||||||
@@ -24,7 +29,7 @@ interface DataFormProps {
|
|||||||
isGGUF: boolean;
|
isGGUF: boolean;
|
||||||
formKey: DeployFormKey;
|
formKey: DeployFormKey;
|
||||||
sourceDisable?: boolean;
|
sourceDisable?: boolean;
|
||||||
backendOptions?: Global.BaseOption<string>[];
|
backendOptions: BackendOption[];
|
||||||
sourceList?: Global.BaseOption<string>[];
|
sourceList?: Global.BaseOption<string>[];
|
||||||
clusterList: Global.BaseOption<number>[];
|
clusterList: Global.BaseOption<number>[];
|
||||||
fields?: string[];
|
fields?: string[];
|
||||||
@@ -41,7 +46,7 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
|||||||
formKey,
|
formKey,
|
||||||
initialValues,
|
initialValues,
|
||||||
sourceDisable = true,
|
sourceDisable = true,
|
||||||
backendOptions,
|
backendOptions = [],
|
||||||
sourceList,
|
sourceList,
|
||||||
clusterList = [],
|
clusterList = [],
|
||||||
fields = ['source'],
|
fields = ['source'],
|
||||||
@@ -174,8 +179,10 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
|||||||
value={{
|
value={{
|
||||||
isGGUF: isGGUF,
|
isGGUF: isGGUF,
|
||||||
formKey: formKey,
|
formKey: formKey,
|
||||||
|
source: props.source,
|
||||||
pageAction: action,
|
pageAction: action,
|
||||||
gpuOptions: gpuOptions,
|
gpuOptions: gpuOptions,
|
||||||
|
backendOptions: backendOptions,
|
||||||
onValuesChange: onValuesChange,
|
onValuesChange: onValuesChange,
|
||||||
onBackendChange: handleBackendChange
|
onBackendChange: handleBackendChange
|
||||||
}}
|
}}
|
||||||
@@ -276,8 +283,6 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
|||||||
isGGUF={isGGUF}
|
isGGUF={isGGUF}
|
||||||
action={action}
|
action={action}
|
||||||
source={props.source}
|
source={props.source}
|
||||||
backendOptions={backendOptions}
|
|
||||||
handleBackendChange={handleBackendChange}
|
|
||||||
></AdvanceConfig>
|
></AdvanceConfig>
|
||||||
</Form>
|
</Form>
|
||||||
</FormContext.Provider>
|
</FormContext.Provider>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { defaultFormValues, deployFormKeyMap, modelSourceMap } from '../config';
|
import { defaultFormValues, deployFormKeyMap, modelSourceMap } from '../config';
|
||||||
import { backendOptionsMap } from '../config/backend-parameters';
|
import { backendOptionsMap } from '../config/backend-parameters';
|
||||||
import { FormData, SourceType } from '../config/types';
|
import { BackendOption, FormData, SourceType } from '../config/types';
|
||||||
import {
|
import {
|
||||||
MessageStatus,
|
MessageStatus,
|
||||||
WarningStausOptions,
|
WarningStausOptions,
|
||||||
@@ -58,6 +58,7 @@ type AddModalProps = {
|
|||||||
width?: string | number;
|
width?: string | number;
|
||||||
initialValues?: any;
|
initialValues?: any;
|
||||||
deploymentType?: 'modelList' | 'modelFiles';
|
deploymentType?: 'modelList' | 'modelFiles';
|
||||||
|
backendOptions: BackendOption[];
|
||||||
clusterList: Global.BaseOption<
|
clusterList: Global.BaseOption<
|
||||||
number,
|
number,
|
||||||
{ provider: string; state: string | number }
|
{ provider: string; state: string | number }
|
||||||
@@ -541,6 +542,7 @@ const AddModal: FC<AddModalProps> = (props) => {
|
|||||||
onOk={handleOnOk}
|
onOk={handleOnOk}
|
||||||
ref={form}
|
ref={form}
|
||||||
isGGUF={isGGUF}
|
isGGUF={isGGUF}
|
||||||
|
backendOptions={props.backendOptions}
|
||||||
onBackendChange={handleBackendChange}
|
onBackendChange={handleBackendChange}
|
||||||
onValuesChange={onValuesChange}
|
onValuesChange={onValuesChange}
|
||||||
></DataForm>
|
></DataForm>
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ import {
|
|||||||
} from '../config/types';
|
} from '../config/types';
|
||||||
import useFormInitialValues from '../hooks/use-form-initial-values';
|
import useFormInitialValues from '../hooks/use-form-initial-values';
|
||||||
import useModelsColumns from '../hooks/use-models-columns';
|
import useModelsColumns from '../hooks/use-models-columns';
|
||||||
|
import useQueryBackends from '../hooks/use-query-backends';
|
||||||
import APIAccessInfoModal from './api-access-info';
|
import APIAccessInfoModal from './api-access-info';
|
||||||
import DeployModal from './deploy-modal';
|
import DeployModal from './deploy-modal';
|
||||||
import Instances from './instances';
|
import Instances from './instances';
|
||||||
@@ -123,6 +124,7 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
loadend,
|
loadend,
|
||||||
total
|
total
|
||||||
}) => {
|
}) => {
|
||||||
|
const { backendOptions } = useQueryBackends();
|
||||||
const { generateFormValues, clusterList, getClusterList } =
|
const { generateFormValues, clusterList, getClusterList } =
|
||||||
useFormInitialValues();
|
useFormInitialValues();
|
||||||
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
|
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
|
||||||
@@ -686,6 +688,7 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
clusterList={clusterList}
|
clusterList={clusterList}
|
||||||
onCancel={handleModalCancel}
|
onCancel={handleModalCancel}
|
||||||
onOk={handleModalOk}
|
onOk={handleModalOk}
|
||||||
|
backendOptions={backendOptions}
|
||||||
></UpdateModel>
|
></UpdateModel>
|
||||||
<DeployModal
|
<DeployModal
|
||||||
open={openDeployModal.show}
|
open={openDeployModal.show}
|
||||||
@@ -696,6 +699,7 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
isGGUF={openDeployModal.isGGUF}
|
isGGUF={openDeployModal.isGGUF}
|
||||||
hasLinuxWorker={openDeployModal.hasLinuxWorker}
|
hasLinuxWorker={openDeployModal.hasLinuxWorker}
|
||||||
clusterList={clusterList}
|
clusterList={clusterList}
|
||||||
|
backendOptions={backendOptions}
|
||||||
onCancel={handleDeployModalCancel}
|
onCancel={handleDeployModalCancel}
|
||||||
onOk={handleCreateModel}
|
onOk={handleCreateModel}
|
||||||
></DeployModal>
|
></DeployModal>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
updateIgnoreFields
|
updateIgnoreFields
|
||||||
} from '../config';
|
} from '../config';
|
||||||
import { backendOptionsMap } from '../config/backend-parameters';
|
import { backendOptionsMap } from '../config/backend-parameters';
|
||||||
import { FormData } from '../config/types';
|
import { BackendOption, FormData } from '../config/types';
|
||||||
import { generateGPUSelector } from '../config/utils';
|
import { generateGPUSelector } from '../config/utils';
|
||||||
import { useCheckCompatibility } from '../hooks';
|
import { useCheckCompatibility } from '../hooks';
|
||||||
import ColumnWrapper from './column-wrapper';
|
import ColumnWrapper from './column-wrapper';
|
||||||
@@ -26,6 +26,7 @@ type AddModalProps = {
|
|||||||
data: FormData;
|
data: FormData;
|
||||||
isGGUF: boolean;
|
isGGUF: boolean;
|
||||||
};
|
};
|
||||||
|
backendOptions: BackendOption[];
|
||||||
clusterList: Global.BaseOption<
|
clusterList: Global.BaseOption<
|
||||||
number,
|
number,
|
||||||
{ provider: string; state: string | number }
|
{ provider: string; state: string | number }
|
||||||
@@ -325,6 +326,7 @@ const UpdateModal: React.FC<AddModalProps> = (props) => {
|
|||||||
onOk={handleOk}
|
onOk={handleOk}
|
||||||
ref={formRef}
|
ref={formRef}
|
||||||
isGGUF={isGGUF}
|
isGGUF={isGGUF}
|
||||||
|
backendOptions={props.backendOptions}
|
||||||
onBackendChange={handleAsyncBackendChange}
|
onBackendChange={handleAsyncBackendChange}
|
||||||
onValuesChange={handleManulOnValuesChange}
|
onValuesChange={handleManulOnValuesChange}
|
||||||
></DataForm>
|
></DataForm>
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import { PageActionType } from '@/config/types';
|
import { PageActionType } from '@/config/types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { DeployFormKey } from './types';
|
import { BackendOption, DeployFormKey } from './types';
|
||||||
|
|
||||||
interface FormContextProps {
|
interface FormContextProps {
|
||||||
isGGUF?: boolean;
|
isGGUF?: boolean;
|
||||||
formKey: DeployFormKey;
|
formKey: DeployFormKey;
|
||||||
source?: string;
|
source: string;
|
||||||
pageAction: PageActionType;
|
pageAction: PageActionType;
|
||||||
gpuOptions?: any[];
|
gpuOptions?: any[];
|
||||||
|
backendOptions: BackendOption[];
|
||||||
onValuesChange?: (changedValues: any, allValues: any) => void;
|
onValuesChange?: (changedValues: any, allValues: any) => void;
|
||||||
onBackendChange?: (backend: string) => void;
|
onBackendChange: (backend: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CatalogFormContextProps {
|
interface CatalogFormContextProps {
|
||||||
@@ -19,6 +20,10 @@ interface CatalogFormContextProps {
|
|||||||
onQuantizationChange: (val: string) => void;
|
onQuantizationChange: (val: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface FormOuterContextProps {
|
||||||
|
sourceList?: Global.BaseOption<string>[];
|
||||||
|
}
|
||||||
|
|
||||||
export const FormContext = React.createContext<FormContextProps>(
|
export const FormContext = React.createContext<FormContextProps>(
|
||||||
{} as FormContextProps
|
{} as FormContextProps
|
||||||
);
|
);
|
||||||
@@ -27,6 +32,10 @@ export const CatalogFormContext = React.createContext<CatalogFormContextProps>(
|
|||||||
{} as CatalogFormContextProps
|
{} as CatalogFormContextProps
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const FormOuterContext = React.createContext<FormOuterContextProps>(
|
||||||
|
{} as FormOuterContextProps
|
||||||
|
);
|
||||||
|
|
||||||
export const useFormContext = () => {
|
export const useFormContext = () => {
|
||||||
const context = React.useContext(FormContext);
|
const context = React.useContext(FormContext);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
@@ -44,3 +53,13 @@ export const useCatalogFormContext = () => {
|
|||||||
}
|
}
|
||||||
return context;
|
return context;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useFormOuterContext = () => {
|
||||||
|
const context = React.useContext(FormOuterContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error(
|
||||||
|
'useFormOuterContext must be used within a FormOuterProvider'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|||||||
@@ -324,7 +324,9 @@ export const excludeFields = [
|
|||||||
'scheduleType',
|
'scheduleType',
|
||||||
'placement_strategy',
|
'placement_strategy',
|
||||||
'backend',
|
'backend',
|
||||||
'gpu_selector'
|
'gpu_selector',
|
||||||
|
'run_command',
|
||||||
|
'image_name'
|
||||||
];
|
];
|
||||||
|
|
||||||
// ingore fields when compare old and new data
|
// ingore fields when compare old and new data
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ export type SourceType =
|
|||||||
| 'ollama_library';
|
| 'ollama_library';
|
||||||
|
|
||||||
export interface FormData {
|
export interface FormData {
|
||||||
|
image_name?: string;
|
||||||
|
run_command?: string;
|
||||||
backend: string;
|
backend: string;
|
||||||
restart_on_error?: boolean;
|
restart_on_error?: boolean;
|
||||||
env?: Record<string, any>;
|
env?: Record<string, any>;
|
||||||
@@ -242,3 +244,11 @@ export interface EvaluateResult {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BackendOption {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
default_backend_param: string[];
|
||||||
|
default_version: string;
|
||||||
|
versions: { label: string; value: string }[];
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,134 @@
|
|||||||
|
import IconFont from '@/components/icon-font';
|
||||||
|
import AutoComplete from '@/components/seal-form/auto-complete';
|
||||||
|
import SealInput from '@/components/seal-form/seal-input';
|
||||||
|
import SealSelect from '@/components/seal-form/seal-select';
|
||||||
|
import TooltipList from '@/components/tooltip-list';
|
||||||
|
import { PageAction } from '@/config';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { Form, Typography } from 'antd';
|
||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import {
|
||||||
|
backendLabelMap,
|
||||||
|
backendTipsList,
|
||||||
|
getBackendParamsTips,
|
||||||
|
modelSourceMap
|
||||||
|
} from '../config';
|
||||||
|
import { backendOptionsMap } from '../config/backend-parameters';
|
||||||
|
import { useFormContext } from '../config/form-context';
|
||||||
|
import { FormData } from '../config/types';
|
||||||
|
|
||||||
|
const BackendFields: React.FC = () => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const form = Form.useFormInstance();
|
||||||
|
const {
|
||||||
|
isGGUF,
|
||||||
|
formKey,
|
||||||
|
pageAction: action,
|
||||||
|
source,
|
||||||
|
gpuOptions,
|
||||||
|
onValuesChange,
|
||||||
|
backendOptions,
|
||||||
|
onBackendChange
|
||||||
|
} = useFormContext();
|
||||||
|
const backend = Form.useWatch('backend', form);
|
||||||
|
|
||||||
|
const handleBackendVersionOnBlur = () => {
|
||||||
|
onValuesChange?.({}, form.getFieldsValue());
|
||||||
|
};
|
||||||
|
|
||||||
|
const backendParamsTips = useMemo(() => {
|
||||||
|
return getBackendParamsTips(backend);
|
||||||
|
}, [backend]);
|
||||||
|
|
||||||
|
const backendVersions = useMemo(() => {
|
||||||
|
if (!backend || backend === backendOptionsMap.custom) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
backendOptions.find((item) => item.value === backend)?.versions || []
|
||||||
|
);
|
||||||
|
}, [backend]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Item name="backend" rules={[{ required: true }]}>
|
||||||
|
<SealSelect
|
||||||
|
required
|
||||||
|
onChange={onBackendChange}
|
||||||
|
label={intl.formatMessage({ id: 'models.form.backend' })}
|
||||||
|
description={<TooltipList list={backendTipsList}></TooltipList>}
|
||||||
|
options={backendOptions}
|
||||||
|
disabled={
|
||||||
|
action === PageAction.EDIT &&
|
||||||
|
source !== modelSourceMap.local_path_value
|
||||||
|
}
|
||||||
|
></SealSelect>
|
||||||
|
</Form.Item>
|
||||||
|
{backendOptionsMap.custom !== backend && (
|
||||||
|
<Form.Item name="backend_version">
|
||||||
|
<AutoComplete
|
||||||
|
options={backendVersions}
|
||||||
|
placeholder="enter or select a version"
|
||||||
|
onBlur={handleBackendVersionOnBlur}
|
||||||
|
label={intl.formatMessage({ id: 'models.form.backendVersion' })}
|
||||||
|
description={intl.formatMessage(
|
||||||
|
{
|
||||||
|
id: 'models.form.backendVersion.tips'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backend: backendLabelMap[backend],
|
||||||
|
version: backendParamsTips?.version
|
||||||
|
? `(${intl.formatMessage({ id: 'common.help.eg' })} ${backendParamsTips?.version})`
|
||||||
|
: '',
|
||||||
|
link: backendParamsTips?.releases && (
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
marginLeft: 5
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography.Link
|
||||||
|
className="flex-center"
|
||||||
|
style={{ display: 'inline' }}
|
||||||
|
href={backendParamsTips?.releases}
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{intl.formatMessage({ id: 'models.form.releases' })}
|
||||||
|
</span>
|
||||||
|
<IconFont
|
||||||
|
type="icon-external-link"
|
||||||
|
className="font-size-14 m-l-4"
|
||||||
|
></IconFont>
|
||||||
|
</Typography.Link>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
></AutoComplete>
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
{backend === backendOptionsMap.custom && (
|
||||||
|
<>
|
||||||
|
<Form.Item<FormData> name="image_name" rules={[{ required: true }]}>
|
||||||
|
<SealInput.Input
|
||||||
|
required
|
||||||
|
allowClear
|
||||||
|
scaleSize={true}
|
||||||
|
label="Image Name"
|
||||||
|
></SealInput.Input>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item<FormData> name="run_command" rules={[{ required: true }]}>
|
||||||
|
<SealInput.TextArea
|
||||||
|
required
|
||||||
|
scaleSize={true}
|
||||||
|
allowClear
|
||||||
|
label="Execution Command"
|
||||||
|
></SealInput.TextArea>
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BackendFields;
|
||||||
@@ -128,10 +128,6 @@ export const checkCurrentbackend = (data: {
|
|||||||
return backendOptionsMap.voxBox;
|
return backendOptionsMap.voxBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isGGUF) {
|
|
||||||
return backendOptionsMap.llamaBox;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checkOnlyAscendNPU(gpuOptions)) {
|
if (checkOnlyAscendNPU(gpuOptions)) {
|
||||||
return backendOptionsMap.ascendMindie;
|
return backendOptionsMap.ascendMindie;
|
||||||
}
|
}
|
||||||
@@ -229,11 +225,6 @@ export const useCheckCompatibility = () => {
|
|||||||
return clusterNames.join(', ');
|
return clusterNames.join(', ');
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCurrentCluster = (id: number) => {
|
|
||||||
const cluster = clusterList.find?.((item) => item.value === id);
|
|
||||||
return cluster?.label || '';
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCheckCompatibility = (
|
const handleCheckCompatibility = (
|
||||||
evaluateResult: EvaluateResult | null
|
evaluateResult: EvaluateResult | null
|
||||||
): MessageStatus => {
|
): MessageStatus => {
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { backendOptionsAtom } from '@/atoms/models';
|
||||||
|
import { useAtom } from 'jotai';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { queryBackendList } from '../apis';
|
||||||
|
|
||||||
|
export default function useQueryBackends() {
|
||||||
|
const [backendOptions, setBackendOptions] = useAtom(backendOptionsAtom);
|
||||||
|
|
||||||
|
const getBackendOptions = async () => {
|
||||||
|
try {
|
||||||
|
const res = await queryBackendList();
|
||||||
|
const list = res?.items?.map((item) => {
|
||||||
|
return {
|
||||||
|
value: item.backend_name,
|
||||||
|
label: item.backend_show_name,
|
||||||
|
default_backend_param: item.default_backend_param || [],
|
||||||
|
default_version: item.default_version,
|
||||||
|
versions: (item.versions || []).map((version) => ({
|
||||||
|
label: version,
|
||||||
|
value: version
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
});
|
||||||
|
if (res?.items) {
|
||||||
|
setBackendOptions(list || []);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// ignore
|
||||||
|
setBackendOptions([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getBackendOptions();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
backendOptions,
|
||||||
|
getBackendOptions
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
import { SourceType } from '@/pages/llmodels/config/types';
|
import { SourceType } from '@/pages/llmodels/config/types';
|
||||||
import DownloadModal from '@/pages/llmodels/download';
|
import DownloadModal from '@/pages/llmodels/download';
|
||||||
import { useGenerateWorkerOptions } from '@/pages/llmodels/hooks/use-form-initial-values';
|
import { useGenerateWorkerOptions } from '@/pages/llmodels/hooks/use-form-initial-values';
|
||||||
|
import useQueryBackends from '@/pages/llmodels/hooks/use-query-backends';
|
||||||
import { PageContainer } from '@ant-design/pro-components';
|
import { PageContainer } from '@ant-design/pro-components';
|
||||||
import { useIntl, useNavigate } from '@umijs/max';
|
import { useIntl, useNavigate } from '@umijs/max';
|
||||||
import { useMemoizedFn } from 'ahooks';
|
import { useMemoizedFn } from 'ahooks';
|
||||||
@@ -64,7 +65,7 @@ const ModelFiles = () => {
|
|||||||
watch: true,
|
watch: true,
|
||||||
contentForDelete: 'resources.modelfiles.modelfile'
|
contentForDelete: 'resources.modelfiles.modelfile'
|
||||||
});
|
});
|
||||||
|
const { backendOptions } = useQueryBackends();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { showSuccess } = useAppUtils();
|
const { showSuccess } = useAppUtils();
|
||||||
const [downloadModalStatus, setDownlaodMoalStatus] = useState<{
|
const [downloadModalStatus, setDownlaodMoalStatus] = useState<{
|
||||||
@@ -106,9 +107,15 @@ const ModelFiles = () => {
|
|||||||
worker_id: value
|
worker_id: value
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const generateInitialValues = (record: ListItem, gpuOptions: any[]) => {
|
|
||||||
|
const checkIsGGUF = (record: ListItem) => {
|
||||||
const isGGUF = _.includes(record.resolved_paths?.[0], 'gguf');
|
const isGGUF = _.includes(record.resolved_paths?.[0], 'gguf');
|
||||||
const isOllama = !!record.ollama_library_model_name;
|
const isOllama = !!record.ollama_library_model_name;
|
||||||
|
return isGGUF || isOllama;
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateInitialValues = (record: ListItem, gpuOptions: any[]) => {
|
||||||
|
const isGGUF = checkIsGGUF(record);
|
||||||
const audioModelTag = identifyModelTask(
|
const audioModelTag = identifyModelTask(
|
||||||
record.source,
|
record.source,
|
||||||
record.resolved_paths?.[0]
|
record.resolved_paths?.[0]
|
||||||
@@ -140,12 +147,12 @@ const ModelFiles = () => {
|
|||||||
: {},
|
: {},
|
||||||
name: extractFileName(name),
|
name: extractFileName(name),
|
||||||
backend: checkCurrentbackend({
|
backend: checkCurrentbackend({
|
||||||
isGGUF: !audioModelTag && (isGGUF || isOllama),
|
isGGUF: !audioModelTag && isGGUF,
|
||||||
isAudio: !!audioModelTag,
|
isAudio: !!audioModelTag,
|
||||||
gpuOptions: gpuOptions,
|
gpuOptions: gpuOptions,
|
||||||
defaultBackend: backendOptionsMap.vllm
|
defaultBackend: backendOptionsMap.vllm
|
||||||
}),
|
}),
|
||||||
isGGUF: !audioModelTag && (isGGUF || isOllama)
|
isGGUF: !audioModelTag && isGGUF
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -338,6 +345,7 @@ const ModelFiles = () => {
|
|||||||
workerOptions={readyWorkers}
|
workerOptions={readyWorkers}
|
||||||
></DownloadModal>
|
></DownloadModal>
|
||||||
<DeployModal
|
<DeployModal
|
||||||
|
backendOptions={backendOptions}
|
||||||
deploymentType="modelFiles"
|
deploymentType="modelFiles"
|
||||||
title={intl.formatMessage({ id: 'models.button.deploy' })}
|
title={intl.formatMessage({ id: 'models.button.deploy' })}
|
||||||
onCancel={handleDeployModalCancel}
|
onCancel={handleDeployModalCancel}
|
||||||
|
|||||||
Reference in New Issue
Block a user