fix: backend selection
This commit is contained in:
@@ -60,7 +60,9 @@ const icons = {
|
||||
LockOpen: React.createElement(IconFont, { type: 'icon-lock_open' }),
|
||||
Permission: React.createElement(IconFont, { type: 'icon-permission' }),
|
||||
CaptivePortal: React.createElement(IconFont, { type: 'icon-captive_portal' }),
|
||||
StarOutlined: React.createElement(StarOutlined)
|
||||
StarOutlined: React.createElement(StarOutlined),
|
||||
Charger: React.createElement(IconFont, { type: 'icon-charger' }),
|
||||
Disabled: React.createElement(IconFont, { type: 'icon-disabled' })
|
||||
};
|
||||
|
||||
export default icons;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createFromIconfontCN } from '@ant-design/icons';
|
||||
// import './iconfont/iconfont.js';
|
||||
|
||||
const IconFont = createFromIconfontCN({
|
||||
scriptUrl: '//at.alicdn.com/t/c/font_4613488_2h8628ohalv.js'
|
||||
scriptUrl: '//at.alicdn.com/t/c/font_4613488_339f1qeidmn.js'
|
||||
});
|
||||
|
||||
export default IconFont;
|
||||
|
||||
@@ -38,5 +38,7 @@ export default {
|
||||
'backend.replaceEntrypoint': 'Override Image Entrypoint',
|
||||
'backend.entrypoint': 'Image Entrypoint',
|
||||
'backend.entrypoint.tips':
|
||||
'If specified, the ENTRYPOINT defined in the image will be ignored, and the command below will be used as the container startup entrypoint.'
|
||||
'If specified, the ENTRYPOINT defined in the image will be ignored, and the command below will be used as the container startup entrypoint.',
|
||||
'backend.form.defaultEnvironment': 'Default Environment Variables',
|
||||
'backend.recommendModels': 'Recommended Models'
|
||||
};
|
||||
|
||||
@@ -273,5 +273,7 @@ export default {
|
||||
'No compatible GPUs are available in the selected cluster for this model.',
|
||||
'models.form.modelfile.notfound': `The model file path you specified does not exist on the GPUStack server. It's recommended to place the model file at the same path on both the GPUStack server and GPUStack workers. This helps GPUStack make better decisions.`,
|
||||
'models.form.readyWorkers': 'workers ready',
|
||||
'models.form.maxContextLength': 'Maximum Context Length'
|
||||
'models.form.maxContextLength': 'Maximum Context Length',
|
||||
'models.form.backend.helperText':
|
||||
'Not enabled yet. Will be enabled after deployment. '
|
||||
};
|
||||
|
||||
@@ -38,5 +38,7 @@ export default {
|
||||
'backend.replaceEntrypoint': 'Override Image Entrypoint',
|
||||
'backend.entrypoint': 'Image Entrypoint',
|
||||
'backend.entrypoint.tips':
|
||||
'If specified, the ENTRYPOINT defined in the image will be ignored, and the command below will be used as the container startup entrypoint.'
|
||||
'If specified, the ENTRYPOINT defined in the image will be ignored, and the command below will be used as the container startup entrypoint.',
|
||||
'backend.form.defaultEnvironment': 'Default Environment Variables',
|
||||
'backend.recommendModels': 'Recommended Models'
|
||||
};
|
||||
|
||||
@@ -273,7 +273,9 @@ export default {
|
||||
'No compatible GPUs are available in the selected cluster for this model.',
|
||||
'models.form.modelfile.notfound': `The model file path you specified does not exist on the GPUStack server. It's recommended to place the model file at the same path on both the GPUStack server and GPUStack workers. This helps GPUStack make better decisions.`,
|
||||
'models.form.readyWorkers': 'workers ready',
|
||||
'models.form.maxContextLength': 'Maximum Context Length'
|
||||
'models.form.maxContextLength': 'Maximum Context Length',
|
||||
'models.form.backend.helperText':
|
||||
'Not enabled yet. Will be enabled after deployment. '
|
||||
};
|
||||
|
||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||
@@ -369,5 +371,6 @@ export default {
|
||||
// 73. 'models.catalog.nogpus.tips': 'No compatible GPUs are available in the selected cluster for this model.',
|
||||
// 74. 'models.form.modelfile.notfound': `The model file path you specified does not exist on the GPUStack server. It's recommended to place the model file at the same path on both the GPUStack server and GPUStack workers. This helps GPUStack make better decisions.`,
|
||||
// 75. 'models.form.readyWorkers': 'workers ready',
|
||||
// 76. 'models.form.maxContextLength': 'Maximum Context Length'
|
||||
// 76. 'models.form.maxContextLength': 'Maximum Context Length',
|
||||
// 77. 'models.form.backend.helperText': 'Not enabled yet. Will be enabled after deployment. ',
|
||||
// ========== End of To-Do List ==========
|
||||
|
||||
@@ -38,9 +38,12 @@ export default {
|
||||
'backend.replaceEntrypoint': 'Переопределить точку входа образа',
|
||||
'backend.entrypoint': 'Точка входа образа',
|
||||
'backend.entrypoint.tips':
|
||||
'Если указано, ENTRYPOINT, заданный в образе, будет проигнорирован, а вместо него для запуска контейнера будет использована команда ниже.'
|
||||
'Если указано, ENTRYPOINT, заданный в образе, будет проигнорирован, а вместо него для запуска контейнера будет использована команда ниже.',
|
||||
'backend.form.defaultEnvironment': 'Default Environment Variables',
|
||||
'backend.recommendModels': 'Recommended Models'
|
||||
};
|
||||
|
||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||
|
||||
// 1. 'backend.form.defaultEnvironment': 'Default Environment Variables',
|
||||
// 2. 'backend.recommendModels': 'Recommended Models'
|
||||
// ========== End of To-Do List ==========
|
||||
|
||||
@@ -277,9 +277,12 @@ export default {
|
||||
'В выбранном кластере нет доступных GPU, совместимых с этой моделью.',
|
||||
'models.form.modelfile.notfound': `Указанный путь к файлу модели не существует на сервере GPUStack. Рекомендуется размещать файл модели по одному и тому же пути как на сервере GPUStack, так и на воркерах GPUStack. Это поможет системе принимать лучшие решения по распределению ресурсов.`,
|
||||
'models.form.readyWorkers': 'воркеров готово',
|
||||
'models.form.maxContextLength': 'Maximum Context Length'
|
||||
'models.form.maxContextLength': 'Maximum Context Length',
|
||||
'models.form.backend.helperText':
|
||||
'Not enabled yet. Will be enabled after deployment. '
|
||||
};
|
||||
|
||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||
// 1. 'models.form.maxContextLength': 'Maximum Context Length'
|
||||
// 1. 'models.form.maxContextLength': 'Maximum Context Length',
|
||||
// 2. 'models.form.backend.helperText': 'Not enabled yet. Will be enabled after deployment. ',
|
||||
// ========== End of To-Do List ==========
|
||||
|
||||
@@ -35,5 +35,7 @@ export default {
|
||||
'backend.replaceEntrypoint': '覆盖镜像入口命令',
|
||||
'backend.entrypoint': '镜像入口命令',
|
||||
'backend.entrypoint.tips':
|
||||
'如指定,镜像中定义的 ENTRYPOINT 将被忽略,下面的命令将作为容器启动入口命令使用。'
|
||||
'如指定,镜像中定义的 ENTRYPOINT 将被忽略,下面的命令将作为容器启动入口命令使用。',
|
||||
'backend.form.defaultEnvironment': '默认环境变量',
|
||||
'backend.recommendModels': '推荐模型'
|
||||
};
|
||||
|
||||
@@ -257,5 +257,6 @@ export default {
|
||||
'models.form.modelfile.notfound':
|
||||
'你指定的模型文件路径在 GPUStack 服务器上不存在。建议在 GPUStack 服务器和 GPUStack 节点上使用相同的模型文件路径,这有助于 GPUStack 做出更优的调度与决策。',
|
||||
'models.form.readyWorkers': '节点就绪',
|
||||
'models.form.maxContextLength': '最大上下文长度'
|
||||
'models.form.maxContextLength': '最大上下文长度',
|
||||
'models.form.backend.helperText': '该社区后端暂未启用,部署后将自动启用'
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ import React, { useEffect, useId, useRef, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import ColumnWrapper from '../../_components/column-wrapper';
|
||||
import {
|
||||
BackendSourceValueMap,
|
||||
builtInBackendFields,
|
||||
customBackendFields,
|
||||
json2Yaml
|
||||
@@ -55,7 +56,8 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
'image_name',
|
||||
'run_command',
|
||||
'custom_framework',
|
||||
'entrypoint'
|
||||
'entrypoint',
|
||||
'environment'
|
||||
];
|
||||
|
||||
// remove '-custom' suffix from version_no in currentData, when action is EDIT
|
||||
@@ -64,7 +66,7 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
data.version_configs = Object.entries(data.version_configs || {}).reduce(
|
||||
(acc, [key, value]) => {
|
||||
const version = key.replace(/-custom$/, '');
|
||||
acc[version] = { ...value };
|
||||
acc[version] = { ...value, backend_source: data.backend_source };
|
||||
return acc;
|
||||
},
|
||||
{} as any
|
||||
@@ -128,7 +130,9 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
||||
is_default: key === values.default_version,
|
||||
built_in_frameworks:
|
||||
values.built_in_version_configs?.[key]?.built_in_frameworks || [],
|
||||
is_built_in: true,
|
||||
is_built_in:
|
||||
data.is_built_in &&
|
||||
data.backend_source === BackendSourceValueMap.BUILTIN,
|
||||
..._.pick(values.built_in_version_configs?.[key], [
|
||||
'image_name',
|
||||
'run_command',
|
||||
|
||||
@@ -5,7 +5,7 @@ import TagWrapper from '@/components/tags-wrapper';
|
||||
import ThemeTag from '@/components/tags-wrapper/theme-tag';
|
||||
import Card from '@/components/templates/card';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button, Tag } from 'antd';
|
||||
import { Button, Flex, Tag, Tooltip } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import { useMemo } from 'react';
|
||||
import semverCoerce from 'semver/functions/coerce';
|
||||
@@ -18,7 +18,8 @@ import {
|
||||
builtInBackendLogos,
|
||||
customColors,
|
||||
customIcons,
|
||||
getGpuColor
|
||||
getGpuColor,
|
||||
TagColorMap
|
||||
} from '../config';
|
||||
import { ListItem } from '../config/types';
|
||||
|
||||
@@ -142,12 +143,6 @@ interface BackendCardProps {
|
||||
data: ListItem;
|
||||
}
|
||||
|
||||
const TagColorMap: Record<string, string> = {
|
||||
[BackendSourceValueMap.CUSTOM]: 'purple',
|
||||
[BackendSourceValueMap.BUILTIN]: 'geekblue',
|
||||
[BackendSourceValueMap.COMMUNITY]: 'cyan'
|
||||
};
|
||||
|
||||
const BackendCard: React.FC<BackendCardProps> = ({ data, onSelect }) => {
|
||||
const intl = useIntl();
|
||||
|
||||
@@ -254,22 +249,65 @@ const BackendCard: React.FC<BackendCardProps> = ({ data, onSelect }) => {
|
||||
};
|
||||
|
||||
const renderRecommendModels = () => {
|
||||
const recommnadedModels = data.recommend_models || [];
|
||||
if (recommnadedModels.length === 0) {
|
||||
const recommendedModels = data.recommend_models || [];
|
||||
if (recommendedModels.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className="flex-center">
|
||||
<span className="dot"></span>
|
||||
<TagWrapper
|
||||
gap={8}
|
||||
dataList={recommnadedModels}
|
||||
renderTag={renderModel}
|
||||
></TagWrapper>
|
||||
<Tooltip
|
||||
title={
|
||||
<Flex gap={4} wrap="wrap">
|
||||
{recommendedModels.map((item) => (
|
||||
<Tag style={{ margin: 0 }} key={item}>
|
||||
{item}
|
||||
</Tag>
|
||||
))}
|
||||
</Flex>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
<ThemeTag color="default">
|
||||
{intl.formatMessage({ id: 'backend.recommendModels' })}
|
||||
</ThemeTag>
|
||||
</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const renderSource = () => {
|
||||
const source = data.is_built_in
|
||||
? BackendSourceLabelMap[BackendSourceValueMap.BUILTIN] || ''
|
||||
: BackendSourceLabelMap[data.backend_source] || '';
|
||||
if (!source) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<ThemeTag
|
||||
color={
|
||||
TagColorMap[
|
||||
data.is_built_in
|
||||
? BackendSourceValueMap.BUILTIN
|
||||
: data.backend_source
|
||||
]
|
||||
}
|
||||
className="font-400"
|
||||
variant="outlined"
|
||||
style={{
|
||||
borderRadius: 'var(--ant-border-radius)',
|
||||
margin: 0,
|
||||
width: 'max-content'
|
||||
}}
|
||||
>
|
||||
{intl.formatMessage({
|
||||
id: source
|
||||
})}
|
||||
</ThemeTag>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledCard
|
||||
onClick={handleClick}
|
||||
@@ -308,28 +346,7 @@ const BackendCard: React.FC<BackendCardProps> = ({ data, onSelect }) => {
|
||||
<IconFont type="icon-source" className="icon" />
|
||||
<span>Source:</span>
|
||||
</span>
|
||||
<ThemeTag
|
||||
color={
|
||||
TagColorMap[
|
||||
data.is_built_in
|
||||
? BackendSourceValueMap.BUILTIN
|
||||
: data.backend_source
|
||||
]
|
||||
}
|
||||
className="font-400"
|
||||
variant="outlined"
|
||||
style={{
|
||||
borderRadius: 'var(--ant-border-radius)',
|
||||
margin: 0,
|
||||
width: 'max-content'
|
||||
}}
|
||||
>
|
||||
{intl.formatMessage({
|
||||
id: data.is_built_in
|
||||
? BackendSourceLabelMap[BackendSourceValueMap.BUILTIN]
|
||||
: BackendSourceLabelMap[data.backend_source]
|
||||
})}
|
||||
</ThemeTag>
|
||||
{renderSource()}
|
||||
{data.backend_source === BackendSourceValueMap.COMMUNITY && (
|
||||
<>
|
||||
<ThemeTag
|
||||
@@ -349,7 +366,7 @@ const BackendCard: React.FC<BackendCardProps> = ({ data, onSelect }) => {
|
||||
</>
|
||||
)}
|
||||
</InfoItem>
|
||||
{/* {renderRecommendModels()} */}
|
||||
{renderRecommendModels()}
|
||||
</SourceWrapper>
|
||||
{renderFrameworks()}
|
||||
</Content>
|
||||
|
||||
@@ -2,7 +2,9 @@ import ScrollerModal from '@/components/scroller-modal';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { BackendSourceValueMap } from '../config';
|
||||
import { VersionListItem } from '../config/types';
|
||||
import VersionInfo from '../forms/version-info';
|
||||
|
||||
@@ -26,10 +28,11 @@ const VersionInfoModal: React.FC<VersionInfoModalProps> = ({
|
||||
if (open && currentData) {
|
||||
// add is_built_in field to built_in_version_configs
|
||||
const builtInVersions = currentData.built_in_version_configs || {};
|
||||
|
||||
for (const key in builtInVersions) {
|
||||
if (builtInVersions?.hasOwnProperty(key)) {
|
||||
builtInVersions[key].is_built_in = true;
|
||||
builtInVersions[key].is_built_in =
|
||||
currentData.backend_source === BackendSourceValueMap.BUILTIN &&
|
||||
currentData.is_built_in;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,9 +44,13 @@ const VersionInfoModal: React.FC<VersionInfoModalProps> = ({
|
||||
const versionList: VersionListItem[] = Object.entries(versions).map(
|
||||
([key, value]: [string, any]) => ({
|
||||
version_no: key,
|
||||
image_name: value.image_name,
|
||||
run_command: value.run_command,
|
||||
entrypoint: value.entrypoint,
|
||||
..._.pick(value, [
|
||||
'image_name',
|
||||
'run_command',
|
||||
'entrypoint',
|
||||
'environment',
|
||||
'backend_source'
|
||||
]),
|
||||
is_default: key === currentData.default_version,
|
||||
availableFrameworks: [
|
||||
...(value.built_in_frameworks || []),
|
||||
|
||||
@@ -43,6 +43,12 @@ export const BackendSourceLabelMap: Record<string, string> = {
|
||||
[BackendSourceValueMap.USER_DEFINED]: 'models.form.backend.custom'
|
||||
};
|
||||
|
||||
export const TagColorMap: Record<string, string> = {
|
||||
[BackendSourceValueMap.CUSTOM]: 'purple',
|
||||
[BackendSourceValueMap.BUILTIN]: 'geekblue',
|
||||
[BackendSourceValueMap.COMMUNITY]: 'cyan'
|
||||
};
|
||||
|
||||
export const backendActions = [
|
||||
{
|
||||
label: 'common.button.edit',
|
||||
@@ -63,7 +69,7 @@ export const backendActions = [
|
||||
value: 'enable',
|
||||
key: 'enable',
|
||||
locale: true,
|
||||
icon: icons.Yaml,
|
||||
icon: icons.Charger,
|
||||
show: (record: any) =>
|
||||
!record.enabled &&
|
||||
record.backend_source === BackendSourceValueMap.COMMUNITY
|
||||
@@ -73,7 +79,7 @@ export const backendActions = [
|
||||
value: 'disable',
|
||||
key: 'disable',
|
||||
locale: true,
|
||||
icon: icons.Yaml,
|
||||
icon: icons.Disabled,
|
||||
show: (record: any) =>
|
||||
record.enabled &&
|
||||
record.backend_source === BackendSourceValueMap.COMMUNITY
|
||||
|
||||
@@ -7,6 +7,8 @@ export interface VersionConfigs {
|
||||
entrypoint?: string;
|
||||
version_no?: string;
|
||||
is_built_in?: boolean;
|
||||
backend_source?: string;
|
||||
environment: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface VersionListItem extends VersionConfigs {
|
||||
@@ -24,6 +26,8 @@ export interface FormData {
|
||||
allowed_proxy_uris?: string[];
|
||||
content?: string;
|
||||
enabled?: boolean;
|
||||
backend_source?: string;
|
||||
default_environment?: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface ListItem extends FormData {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import LabelSelector from '@/components/label-selector';
|
||||
import ListInput from '@/components/list-input';
|
||||
import SealInput from '@/components/seal-form/seal-input';
|
||||
import SealTextArea from '@/components/seal-form/seal-textarea';
|
||||
@@ -6,7 +7,8 @@ import { PageActionType } from '@/config/types';
|
||||
import useAppUtils from '@/hooks/use-app-utils';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { BackendSourceValueMap } from '../config';
|
||||
import { FormData, ListItem } from '../config/types';
|
||||
|
||||
type AddModalProps = {
|
||||
@@ -17,6 +19,17 @@ const BasicForm: React.FC<AddModalProps> = ({ action, currentData }) => {
|
||||
const form = Form.useFormInstance();
|
||||
const intl = useIntl();
|
||||
const { getRuleMessage } = useAppUtils();
|
||||
const defaultEnvs = Form.useWatch('default_environment', form);
|
||||
|
||||
const handleEnviromentVarsChange = (labels: Record<string, any>) => {
|
||||
form.setFieldValue('env', labels);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (action === PageAction.CREATE) {
|
||||
form.setFieldValue('backend_source', BackendSourceValueMap.CUSTOM);
|
||||
}
|
||||
}, [action]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -37,6 +50,11 @@ const BasicForm: React.FC<AddModalProps> = ({ action, currentData }) => {
|
||||
required
|
||||
></SealInput.Input>
|
||||
</Form.Item>
|
||||
{action === PageAction.CREATE && (
|
||||
<Form.Item<FormData> hidden name="backend_source">
|
||||
<SealInput.Input></SealInput.Input>
|
||||
</Form.Item>
|
||||
)}
|
||||
{!currentData?.is_built_in && (
|
||||
<>
|
||||
<Form.Item<FormData>
|
||||
@@ -86,6 +104,16 @@ const BasicForm: React.FC<AddModalProps> = ({ action, currentData }) => {
|
||||
})}
|
||||
></ListInput>
|
||||
</Form.Item>
|
||||
<Form.Item<FormData> name="default_environment">
|
||||
<LabelSelector
|
||||
label={intl.formatMessage({
|
||||
id: 'backend.form.defaultEnvironment'
|
||||
})}
|
||||
labels={defaultEnvs}
|
||||
btnText={intl.formatMessage({ id: 'common.button.vars' })}
|
||||
onChange={handleEnviromentVarsChange}
|
||||
></LabelSelector>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item<FormData> name="description" rules={[{ required: false }]}>
|
||||
<SealInput.TextArea
|
||||
|
||||
@@ -5,7 +5,13 @@ import { useIntl } from '@umijs/max';
|
||||
import { Empty } from 'antd';
|
||||
import { useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { frameworks, getGpuColor } from '../config';
|
||||
import {
|
||||
BackendSourceLabelMap,
|
||||
BackendSourceValueMap,
|
||||
frameworks,
|
||||
getGpuColor,
|
||||
TagColorMap
|
||||
} from '../config';
|
||||
import { VersionListItem } from '../config/types';
|
||||
|
||||
const ItemWrapper = styled.div`
|
||||
@@ -76,19 +82,43 @@ interface VersionItemProps {
|
||||
|
||||
export const VersionItem: React.FC<VersionItemProps> = ({ data }) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const renderSource = () => {
|
||||
console.log('data.is_built_in', data.is_built_in);
|
||||
const source = data.is_built_in
|
||||
? BackendSourceLabelMap[BackendSourceValueMap.BUILTIN] || ''
|
||||
: BackendSourceLabelMap[data.backend_source || ''] || '';
|
||||
if (!source) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<ThemeTag
|
||||
color={
|
||||
TagColorMap[
|
||||
data.is_built_in
|
||||
? BackendSourceValueMap.BUILTIN
|
||||
: data.backend_source || ''
|
||||
]
|
||||
}
|
||||
className="font-400"
|
||||
variant="outlined"
|
||||
style={{
|
||||
borderRadius: 'var(--ant-border-radius)',
|
||||
margin: 0,
|
||||
width: 'max-content'
|
||||
}}
|
||||
>
|
||||
{intl.formatMessage({
|
||||
id: source
|
||||
})}
|
||||
</ThemeTag>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<ItemWrapper>
|
||||
<div className="title">
|
||||
<span>{data.version_no}</span>
|
||||
{data.is_built_in && (
|
||||
<ThemeTag
|
||||
color="geekblue"
|
||||
className="font-400"
|
||||
style={{ marginRight: 0 }}
|
||||
>
|
||||
{intl.formatMessage({ id: 'backend.builtin' })}
|
||||
</ThemeTag>
|
||||
)}
|
||||
{renderSource()}
|
||||
{!data.is_built_in && data.is_default && (
|
||||
<ThemeTag
|
||||
color="geekblue"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import CollapsibleContainer from '@/components/collapse-container';
|
||||
import LabelSelector from '@/components/label-selector';
|
||||
import BaseSelect from '@/components/seal-form/base/select';
|
||||
import SealInput from '@/components/seal-form/seal-input';
|
||||
import SealSelect from '@/components/seal-form/seal-select';
|
||||
@@ -75,7 +76,8 @@ const VersionsForm: React.FC<AddModalProps> = ({
|
||||
run_command: '',
|
||||
entrypoint: '',
|
||||
isBuiltin: false,
|
||||
is_default: false
|
||||
is_default: false,
|
||||
environment: {}
|
||||
}
|
||||
];
|
||||
|
||||
@@ -102,7 +104,8 @@ const VersionsForm: React.FC<AddModalProps> = ({
|
||||
run_command: '',
|
||||
entrypoint: '',
|
||||
isBuiltin: false,
|
||||
is_default: false
|
||||
is_default: false,
|
||||
environment: {}
|
||||
};
|
||||
form.setFieldValue('version_configs', [...versions, newVersion]);
|
||||
};
|
||||
@@ -136,6 +139,25 @@ const VersionsForm: React.FC<AddModalProps> = ({
|
||||
setDefaultVersion(value);
|
||||
};
|
||||
|
||||
const handleEnviromentVarsChange = (
|
||||
envs: Record<string, any>,
|
||||
name: number
|
||||
) => {
|
||||
const versions = form.getFieldValue('version_configs') || [];
|
||||
const updatedVersions = versions.map((version: any, idx: number) => {
|
||||
if (idx === name) {
|
||||
return {
|
||||
...version,
|
||||
environment: {
|
||||
...envs
|
||||
}
|
||||
};
|
||||
}
|
||||
return version;
|
||||
});
|
||||
form.setFieldValue('version_configs', updatedVersions);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const versions = form.getFieldValue('version_configs') || [];
|
||||
|
||||
@@ -227,6 +249,7 @@ const VersionsForm: React.FC<AddModalProps> = ({
|
||||
{ add, remove }
|
||||
) => {
|
||||
const versionConfigs = form.getFieldValue('version_configs');
|
||||
console.log('versionConfigs', versionConfigs);
|
||||
return fields?.map(({ key, name }) => (
|
||||
<div
|
||||
key={name}
|
||||
@@ -356,10 +379,7 @@ const VersionsForm: React.FC<AddModalProps> = ({
|
||||
})}
|
||||
></SealInput.TextArea>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={[name, 'run_command']}
|
||||
style={{ marginBottom: 0 }}
|
||||
>
|
||||
<Form.Item name={[name, 'run_command']}>
|
||||
<SealTextArea
|
||||
allowClear
|
||||
alwaysFocus={true}
|
||||
@@ -376,6 +396,18 @@ const VersionsForm: React.FC<AddModalProps> = ({
|
||||
label={intl.formatMessage({ id: 'backend.runCommand' })}
|
||||
></SealTextArea>
|
||||
</Form.Item>
|
||||
<Form.Item name={[name, 'environment']}>
|
||||
<LabelSelector
|
||||
label={intl.formatMessage({
|
||||
id: 'models.form.env'
|
||||
})}
|
||||
labels={versionConfigs.environment}
|
||||
btnText={intl.formatMessage({ id: 'common.button.vars' })}
|
||||
onChange={(envs) =>
|
||||
handleEnviromentVarsChange(envs, name)
|
||||
}
|
||||
></LabelSelector>
|
||||
</Form.Item>
|
||||
</CollapsibleContainer>
|
||||
</div>
|
||||
));
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import DeleteModal from '@/components/delete-modal';
|
||||
import IconFont from '@/components/icon-font';
|
||||
import { FilterBar } from '@/components/page-tools';
|
||||
import { PageAction } from '@/config';
|
||||
import { PageActionType } from '@/config/types';
|
||||
import useTableFetch from '@/hooks/use-table-fetch';
|
||||
import { useIntl } from '@umijs/max';
|
||||
@@ -122,7 +123,7 @@ const BackendList = () => {
|
||||
const handleAddBackend = () => {
|
||||
setOpenModalStatus({
|
||||
open: true,
|
||||
action: 'create'
|
||||
action: PageAction.CREATE
|
||||
});
|
||||
};
|
||||
|
||||
@@ -161,6 +162,7 @@ const BackendList = () => {
|
||||
enabled: item.action === 'enable'
|
||||
}
|
||||
});
|
||||
handleSearch();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -20,7 +20,13 @@ interface FormContextProps {
|
||||
action: PageActionType;
|
||||
gpuOptions: CascaderOption[];
|
||||
workerLabelOptions: CascaderOption[];
|
||||
backendOptions: BackendOption[];
|
||||
backendOptions: {
|
||||
label: string;
|
||||
value: string;
|
||||
title?: string;
|
||||
children: BackendOption[];
|
||||
}[];
|
||||
flatBackendOptions: BackendOption[];
|
||||
initialValues?: FormData; // for editing model
|
||||
modelContextData?: Record<string, any>;
|
||||
clearCacheFormValues?: () => void;
|
||||
|
||||
@@ -16,12 +16,12 @@ const AdvanceConfig = () => {
|
||||
const form = Form.useFormInstance();
|
||||
const EnviromentVars = Form.useWatch('env', form);
|
||||
const backend = Form.useWatch('backend', form);
|
||||
const { onValuesChange, backendOptions, isGGUF, modelContextData } =
|
||||
const { onValuesChange, isGGUF, modelContextData, flatBackendOptions } =
|
||||
useFormContext();
|
||||
|
||||
const currentBackendOptions = useMemo(() => {
|
||||
return backendOptions?.find((item) => item.value === backend);
|
||||
}, [backend, backendOptions]);
|
||||
return flatBackendOptions?.find((item) => item.value === backend);
|
||||
}, [backend, flatBackendOptions]);
|
||||
|
||||
const handleEnviromentVarsChange = (labels: Record<string, any>) => {
|
||||
form.setFieldValue('env', labels);
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import SealCascader from '@/components/seal-form/seal-cascader';
|
||||
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 useAppUtils from '@/hooks/use-app-utils';
|
||||
import { BackendSourceValueMap } from '@/pages/backends/config';
|
||||
import { CaretDownOutlined, InfoCircleOutlined } from '@ant-design/icons';
|
||||
import { useIntl, useNavigate } from '@umijs/max';
|
||||
import { Form, Select } from 'antd';
|
||||
import React, { useMemo } from 'react';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { backendTipsList } from '../config';
|
||||
import { backendOptionsMap } from '../config/backend-parameters';
|
||||
import { useFormContext } from '../config/form-context';
|
||||
import { BackendOption } from '../config/types';
|
||||
@@ -29,7 +32,13 @@ const BackendFields: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const { getRuleMessage } = useAppUtils();
|
||||
const form = Form.useFormInstance();
|
||||
const { onValuesChange, backendOptions, onBackendChange } = useFormContext();
|
||||
const {
|
||||
action,
|
||||
onValuesChange,
|
||||
backendOptions,
|
||||
flatBackendOptions,
|
||||
onBackendChange
|
||||
} = useFormContext();
|
||||
const backend = Form.useWatch('backend', form);
|
||||
const [showDeprecated, setShowDeprecated] = React.useState<boolean>(false);
|
||||
const [selectedBackend, setSelectedBackend] =
|
||||
@@ -39,31 +48,21 @@ const BackendFields: React.FC = () => {
|
||||
onValuesChange?.({}, form.getFieldsValue());
|
||||
};
|
||||
|
||||
const backendGroupedOptions = useMemo(() => {
|
||||
const builtInBackends = backendOptions?.filter(
|
||||
(item) => item.isBuiltIn || item.value === backendOptionsMap.custom
|
||||
);
|
||||
const customBackends = backendOptions?.filter(
|
||||
(item) => !item.isBuiltIn && item.value !== backendOptionsMap.custom
|
||||
);
|
||||
|
||||
const options = [];
|
||||
|
||||
if (builtInBackends && builtInBackends.length > 0) {
|
||||
options.push({
|
||||
label: intl.formatMessage({ id: 'backend.builtin' }),
|
||||
options: builtInBackends
|
||||
});
|
||||
const backendHelperText = useMemo(() => {
|
||||
const selected = flatBackendOptions?.find((item) => item.value === backend);
|
||||
if (
|
||||
selected &&
|
||||
!selected.enabled &&
|
||||
selected.backend_source === BackendSourceValueMap.COMMUNITY
|
||||
) {
|
||||
return (
|
||||
<span style={{ color: 'var(--ant-color-error)' }}>
|
||||
{intl.formatMessage({ id: 'models.form.backend.helperText' })}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
if (customBackends && customBackends.length > 0) {
|
||||
options.push({
|
||||
label: intl.formatMessage({ id: 'models.form.backend.custom' }),
|
||||
options: customBackends
|
||||
});
|
||||
}
|
||||
return options;
|
||||
}, [backendOptions, intl]);
|
||||
return null;
|
||||
}, [backend, flatBackendOptions, intl]);
|
||||
|
||||
const backendVersions = useMemo((): {
|
||||
builtIn: any[];
|
||||
@@ -111,14 +110,6 @@ const BackendFields: React.FC = () => {
|
||||
};
|
||||
}, [backend, selectedBackend, intl]);
|
||||
|
||||
const optionRender = (option: any) => {
|
||||
return option.data.title;
|
||||
};
|
||||
|
||||
const labelRender = (option: any) => {
|
||||
return option.title;
|
||||
};
|
||||
|
||||
const backendVersionLabelRender = (option: any) => {
|
||||
console.log('backendVersionLabelRender option:', option);
|
||||
return option.title;
|
||||
@@ -140,10 +131,21 @@ const BackendFields: React.FC = () => {
|
||||
};
|
||||
|
||||
const handleOnBackendChange = (value: any[], option: any) => {
|
||||
console.log('handleOnBackendChange value, option:', value, option);
|
||||
form.setFieldsValue({ backend: value[0] });
|
||||
onBackendChange?.(value[0], option[1]);
|
||||
setSelectedBackend(option[1]);
|
||||
form.setFieldValue('backend', value?.[1]);
|
||||
onBackendChange?.(value?.[1], option?.[1] || {});
|
||||
setSelectedBackend(option?.[1] || {});
|
||||
};
|
||||
|
||||
const displayRender = (labels: any[], selectedOptions?: any[]) => {
|
||||
const groupTitle = selectedOptions?.[0]?.title;
|
||||
if (!groupTitle) {
|
||||
return <span>{labels?.[0]}</span>;
|
||||
}
|
||||
return (
|
||||
<span className="flex-center">
|
||||
{intl.formatMessage({ id: groupTitle })} / {labels?.[1]}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
const renderDeprecatedVersionOptions = (values: any[]) => {
|
||||
@@ -192,6 +194,20 @@ const BackendFields: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (action === PageAction.EDIT) {
|
||||
const selected = flatBackendOptions?.find(
|
||||
(item) => item.value === backend
|
||||
);
|
||||
if (selected) {
|
||||
form.setFieldValue('backend_selection', [
|
||||
selected.backend_source,
|
||||
backend
|
||||
]);
|
||||
}
|
||||
}
|
||||
}, [backend, flatBackendOptions, action]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item name="backend" hidden>
|
||||
@@ -205,19 +221,12 @@ const BackendFields: React.FC = () => {
|
||||
message: getRuleMessage('select', 'models.form.backend')
|
||||
}
|
||||
]}
|
||||
help={backendHelperText}
|
||||
>
|
||||
{/* <SealSelect
|
||||
required
|
||||
onChange={onBackendChange}
|
||||
label={intl.formatMessage({ id: 'models.form.backend' })}
|
||||
description={<TooltipList list={backendTipsList}></TooltipList>}
|
||||
options={backendGroupedOptions}
|
||||
optionRender={optionRender}
|
||||
labelRender={labelRender}
|
||||
></SealSelect> */}
|
||||
<SealCascader
|
||||
required
|
||||
showSearch
|
||||
allowClear={false}
|
||||
changeOnSelect={false}
|
||||
expandTrigger="hover"
|
||||
multiple={false}
|
||||
@@ -231,6 +240,7 @@ const BackendFields: React.FC = () => {
|
||||
options={backendOptions}
|
||||
getPopupContainer={(triggerNode) => triggerNode.parentNode}
|
||||
optionNode={BackendNode}
|
||||
displayRender={displayRender}
|
||||
onChange={handleOnBackendChange}
|
||||
></SealCascader>
|
||||
</Form.Item>
|
||||
@@ -244,6 +254,7 @@ const BackendFields: React.FC = () => {
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'models.form.backendVersion.holder'
|
||||
})}
|
||||
description={<TooltipList list={backendTipsList}></TooltipList>}
|
||||
onChange={handleBackendVersionOnChange}
|
||||
label={intl.formatMessage({ id: 'models.form.backendVersion' })}
|
||||
footer={
|
||||
|
||||
@@ -90,7 +90,8 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
onOk
|
||||
} = props;
|
||||
const { getScrollElementScrollableHeight } = useWrapperContext();
|
||||
const { backendOptions, getBackendOptions } = useQueryBackends();
|
||||
const { backendOptions, flatBackendOptions, getBackendOptions } =
|
||||
useQueryBackends();
|
||||
const { getGPUOptionList, gpuOptions, workerLabelOptions } =
|
||||
useGenerateGPUOptions();
|
||||
const [form] = Form.useForm();
|
||||
@@ -394,6 +395,7 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
||||
action: action,
|
||||
gpuOptions: gpuOptions,
|
||||
backendOptions: backendOptions,
|
||||
flatBackendOptions: flatBackendOptions,
|
||||
workerLabelOptions: workerLabelOptions,
|
||||
initialValues: initialValues,
|
||||
modelContextData: modelContextData,
|
||||
|
||||
@@ -11,7 +11,7 @@ import { FormData } from '../config/types';
|
||||
const KVCacheForm = () => {
|
||||
const intl = useIntl();
|
||||
const form = Form.useFormInstance();
|
||||
const { onValuesChange, backendOptions, formKey } = useFormContext();
|
||||
const { onValuesChange, flatBackendOptions, formKey } = useFormContext();
|
||||
const kvCacheEnabled = Form.useWatch(['extended_kv_cache', 'enabled'], form);
|
||||
const backend = Form.useWatch('backend', form);
|
||||
const configCacheRef = useRef<any>({});
|
||||
@@ -68,7 +68,7 @@ const KVCacheForm = () => {
|
||||
};
|
||||
|
||||
const builtInBackend = useMemo(() => {
|
||||
const currentBackend = backendOptions.find(
|
||||
const currentBackend = flatBackendOptions.find(
|
||||
(item) => item.value === backend
|
||||
);
|
||||
|
||||
@@ -78,7 +78,7 @@ const KVCacheForm = () => {
|
||||
backend as string
|
||||
)
|
||||
);
|
||||
}, [backend, backendOptions]);
|
||||
}, [backend, flatBackendOptions]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -18,7 +18,7 @@ const LocalPathForm: React.FC = () => {
|
||||
const {
|
||||
formKey,
|
||||
gpuOptions,
|
||||
backendOptions,
|
||||
flatBackendOptions,
|
||||
onValuesChange,
|
||||
onBackendChange
|
||||
} = useFormContext();
|
||||
@@ -68,7 +68,7 @@ const LocalPathForm: React.FC = () => {
|
||||
});
|
||||
|
||||
if (oldBackend !== backend) {
|
||||
const option = backendOptions.find((item) => item.value === backend);
|
||||
const option = flatBackendOptions.find((item) => item.value === backend);
|
||||
onBackendChange?.(backend, option);
|
||||
} else {
|
||||
onValuesChange?.({ local_path: value }, form.getFieldsValue());
|
||||
|
||||
@@ -21,7 +21,7 @@ const AlgorithmMap = {
|
||||
|
||||
const SpeculativeDecode = () => {
|
||||
const intl = useIntl();
|
||||
const { source, backendOptions, onValuesChange } = useFormContext();
|
||||
const { source, flatBackendOptions, onValuesChange } = useFormContext();
|
||||
const { getRuleMessage } = useAppUtils();
|
||||
const form = Form.useFormInstance();
|
||||
const backend = Form.useWatch('backend', form);
|
||||
@@ -76,7 +76,7 @@ const SpeculativeDecode = () => {
|
||||
};
|
||||
|
||||
const builtInBackend = useMemo(() => {
|
||||
const currentBackend = backendOptions.find(
|
||||
const currentBackend = flatBackendOptions.find(
|
||||
(item) => item.value === backend
|
||||
);
|
||||
|
||||
@@ -86,7 +86,7 @@ const SpeculativeDecode = () => {
|
||||
backend as string
|
||||
)
|
||||
);
|
||||
}, [backend, backendOptions]);
|
||||
}, [backend, flatBackendOptions]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { useAtom } from 'jotai';
|
||||
import _ from 'lodash';
|
||||
import { useState } from 'react';
|
||||
import { queryBackendList } from '../apis';
|
||||
import { backendOptionsMap } from '../config/backend-parameters';
|
||||
import { BackendOption } from '../config/types';
|
||||
@@ -43,6 +44,9 @@ const groupByBackendSource = (list: BackendOption[]): BackendGroup[] => {
|
||||
|
||||
export default function useQueryBackends() {
|
||||
const [backendOptions, setBackendOptions] = useAtom(backendOptionsAtom);
|
||||
const [flatBackendOptions, setFlatBackendOptions] = useState<BackendOption[]>(
|
||||
[]
|
||||
);
|
||||
const intl = useIntl();
|
||||
|
||||
const getBackendOptions = async (params?: { cluster_id: number }) => {
|
||||
@@ -57,6 +61,7 @@ export default function useQueryBackends() {
|
||||
'is_built_in',
|
||||
'default_backend_param'
|
||||
]),
|
||||
backend_source: item.backend_source || BackendSourceValueMap.CUSTOM,
|
||||
value: item.backend_name,
|
||||
label:
|
||||
item.backend_name === backendOptionsMap.custom
|
||||
@@ -78,22 +83,23 @@ export default function useQueryBackends() {
|
||||
});
|
||||
|
||||
const groupList = groupByBackendSource(list);
|
||||
setFlatBackendOptions(list);
|
||||
setBackendOptions(groupList);
|
||||
|
||||
console.log('Fetched backend options:', list, groupList);
|
||||
|
||||
if (res?.items) {
|
||||
setBackendOptions(groupList);
|
||||
}
|
||||
return groupList || [];
|
||||
} catch (error) {
|
||||
// ignore
|
||||
setBackendOptions([]);
|
||||
setFlatBackendOptions([]);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
backendOptions,
|
||||
flatBackendOptions,
|
||||
getBackendOptions
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user