fix: auto expand the fields group
This commit is contained in:
@@ -229,6 +229,10 @@
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.m-t-0 {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.m-b-16 {
|
.m-b-16 {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
@@ -278,10 +282,6 @@
|
|||||||
gap: 16px;
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gap-4 {
|
|
||||||
gap: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-white-tertiary {
|
.color-white-tertiary {
|
||||||
color: var(--color-white-tertiary);
|
color: var(--color-white-tertiary);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ import IconFont from '@/components/icon-font';
|
|||||||
import type { SelectProps } from 'antd';
|
import type { SelectProps } from 'antd';
|
||||||
import { Select } from 'antd';
|
import { Select } from 'antd';
|
||||||
import React, { forwardRef, useImperativeHandle } from 'react';
|
import React, { forwardRef, useImperativeHandle } from 'react';
|
||||||
|
import NotFoundContent from '../components/not-found-content';
|
||||||
|
|
||||||
const BaseSelect: React.FC<SelectProps & { ref?: any }> = forwardRef(
|
const BaseSelect: React.FC<SelectProps & { ref?: any }> = forwardRef(
|
||||||
(props, ref) => {
|
(props, ref) => {
|
||||||
|
const { notFoundContent, loading, ...restProps } = props;
|
||||||
const [isFocus, setIsFocus] = React.useState(false);
|
const [isFocus, setIsFocus] = React.useState(false);
|
||||||
const inputRef = React.useRef<any>(null);
|
const inputRef = React.useRef<any>(null);
|
||||||
|
|
||||||
@@ -32,7 +34,13 @@ const BaseSelect: React.FC<SelectProps & { ref?: any }> = forwardRef(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
{...props}
|
{...restProps}
|
||||||
|
notFoundContent={
|
||||||
|
<NotFoundContent
|
||||||
|
loading={loading}
|
||||||
|
notFoundContent={notFoundContent}
|
||||||
|
/>
|
||||||
|
}
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
onFocus={handleFocus}
|
onFocus={handleFocus}
|
||||||
onBlur={handleBlur}
|
onBlur={handleBlur}
|
||||||
|
|||||||
@@ -34,7 +34,10 @@ const NotFoundContent: React.FC<{
|
|||||||
return (
|
return (
|
||||||
<NoContent>
|
<NoContent>
|
||||||
{notFoundContent || (
|
{notFoundContent || (
|
||||||
<Empty description={intl.formatMessage({ id: 'common.data.empty' })} />
|
<Empty
|
||||||
|
description={intl.formatMessage({ id: 'common.data.empty' })}
|
||||||
|
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</NoContent>
|
</NoContent>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -62,3 +62,12 @@ export const specialCharacterReg = /(?=.*[\W_])/;
|
|||||||
export const noSpaceReg = /(?=\S+$)/;
|
export const noSpaceReg = /(?=\S+$)/;
|
||||||
|
|
||||||
export const lengthReg = /^.{6,12}$/;
|
export const lengthReg = /^.{6,12}$/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model name rules:
|
||||||
|
* 1. no more than 63 characters
|
||||||
|
* 2. contain only alphanumeric characters, '-', '_', and '.'
|
||||||
|
* 3. start and end with an alphanumeric character
|
||||||
|
*/
|
||||||
|
export const modelNameReg =
|
||||||
|
/^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,61}[A-Za-z0-9])?$/g;
|
||||||
|
|||||||
@@ -7,6 +7,13 @@ const useAppUtils = () => {
|
|||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [messageApi, contextHolder] = message.useMessage();
|
const [messageApi, contextHolder] = message.useMessage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param type Array<'input' | 'select'>
|
||||||
|
* @param name
|
||||||
|
* @param locale boolean
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
const getRuleMessage = (
|
const getRuleMessage = (
|
||||||
type: MessageType | MessageType[],
|
type: MessageType | MessageType[],
|
||||||
name: string,
|
name: string,
|
||||||
|
|||||||
@@ -238,5 +238,7 @@ export default {
|
|||||||
'models.form.draftModel.tips':
|
'models.form.draftModel.tips':
|
||||||
'You can enter a local path (e.g., /path/to/model) or select a model from Hugging Face or ModelScope (e.g., Tengyunw/qwen3_8b_eagle3). The system will automatically match based on the primary model source.',
|
'You can enter a local path (e.g., /path/to/model) or select a model from Hugging Face or ModelScope (e.g., Tengyunw/qwen3_8b_eagle3). The system will automatically match based on the primary model source.',
|
||||||
'models.form.quantization': 'Quantization',
|
'models.form.quantization': 'Quantization',
|
||||||
'models.form.backend.custom': 'User-defined'
|
'models.form.backend.custom': 'User-defined',
|
||||||
|
'models.form.rules.name':
|
||||||
|
'Up to 63 characters; letters, numbers, dots (.), underscores (_), and hyphens (-) only; must start and end with an alphanumeric character.'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -238,7 +238,9 @@ export default {
|
|||||||
'models.form.draftModel.tips':
|
'models.form.draftModel.tips':
|
||||||
'You can enter a local path (e.g., /path/to/model) or select a model from Hugging Face or ModelScope (e.g., Tengyunw/qwen3_8b_eagle3). The system will automatically match based on the primary model source.',
|
'You can enter a local path (e.g., /path/to/model) or select a model from Hugging Face or ModelScope (e.g., Tengyunw/qwen3_8b_eagle3). The system will automatically match based on the primary model source.',
|
||||||
'models.form.quantization': 'Quantization',
|
'models.form.quantization': 'Quantization',
|
||||||
'models.form.backend.custom': 'User Defined'
|
'models.form.backend.custom': 'User Defined',
|
||||||
|
'models.form.rules.name':
|
||||||
|
'Up to 63 characters; letters, numbers, dots (.), underscores (_), and hyphens (-) only; must start and end with an alphanumeric character.'
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
@@ -306,5 +308,6 @@ export default {
|
|||||||
// 45. 'models.form.draftModel.placeholder': 'Please select or enter a draft model',
|
// 45. 'models.form.draftModel.placeholder': 'Please select or enter a draft model',
|
||||||
// 46. 'models.form.draftModel.tips': 'You can enter a local path (e.g., /path/to/model) or select a model from Hugging Face or ModelScope (e.g., Tengyunw/qwen3_8b_eagle3). The system will automatically match based on the primary model source.'
|
// 46. 'models.form.draftModel.tips': 'You can enter a local path (e.g., /path/to/model) or select a model from Hugging Face or ModelScope (e.g., Tengyunw/qwen3_8b_eagle3). The system will automatically match based on the primary model source.'
|
||||||
// 47. 'models.form.quantization': 'Quantization',
|
// 47. 'models.form.quantization': 'Quantization',
|
||||||
// 48. 'models.form.backend.custom': 'User Defined'
|
// 48. 'models.form.backend.custom': 'User Defined',
|
||||||
|
// 49. 'models.form.rules.name': 'Up to 63 characters; letters, numbers, dots (.), underscores (_), and hyphens (-) only; must start and end with an alphanumeric character.'
|
||||||
// ========== End of To-Do List ==========
|
// ========== End of To-Do List ==========
|
||||||
|
|||||||
+20
-10
@@ -208,13 +208,17 @@ export default {
|
|||||||
'models.mymodels.status.inactive': 'Остановлен',
|
'models.mymodels.status.inactive': 'Остановлен',
|
||||||
'models.mymodels.status.degrade': 'Аномальный',
|
'models.mymodels.status.degrade': 'Аномальный',
|
||||||
'models.mymodels.status.active': 'Активен',
|
'models.mymodels.status.active': 'Активен',
|
||||||
'models.form.kvCache.tips': 'Расширенный KV-кэш и спекулятивное декодирование доступны только со встроенными бэкендами (vLLM / SGLang). Переключите бэкенд в <span class="bold-text">Расширенных</span> настройках, чтобы включить их.',
|
'models.form.kvCache.tips':
|
||||||
'models.form.kvCache.tips2': 'Поддерживается только при использовании встроенных бэкендов вывода (vLLM или SGLang).',
|
'Расширенный KV-кэш и спекулятивное декодирование доступны только со встроенными бэкендами (vLLM / SGLang). Переключите бэкенд в <span class="bold-text">Расширенных</span> настройках, чтобы включить их.',
|
||||||
|
'models.form.kvCache.tips2':
|
||||||
|
'Поддерживается только при использовании встроенных бэкендов вывода (vLLM или SGLang).',
|
||||||
'models.form.scheduling': 'Планирование',
|
'models.form.scheduling': 'Планирование',
|
||||||
'models.form.ramRatio': 'Соотношение ОЗУ к VRAM',
|
'models.form.ramRatio': 'Соотношение ОЗУ к VRAM',
|
||||||
'models.form.ramSize': 'Максимальный размер ОЗУ (ГиБ)',
|
'models.form.ramSize': 'Максимальный размер ОЗУ (ГиБ)',
|
||||||
'models.form.ramRatio.tips': 'Соотношение системной оперативной памяти к видеопамяти GPU, используемое для KV-кэша. Например, 2.0 означает, что кэш в ОЗУ может быть в два раза больше, чем в видеопамяти GPU.',
|
'models.form.ramRatio.tips':
|
||||||
'models.form.ramSize.tips': 'Максимальный размер KV-кэша, хранящегося в системной памяти (ГиБ). Если задано, это значение переопределяет "{content}".',
|
'Соотношение системной оперативной памяти к видеопамяти GPU, используемое для KV-кэша. Например, 2.0 означает, что кэш в ОЗУ может быть в два раза больше, чем в видеопамяти GPU.',
|
||||||
|
'models.form.ramSize.tips':
|
||||||
|
'Максимальный размер KV-кэша, хранящегося в системной памяти (ГиБ). Если задано, это значение переопределяет "{content}".',
|
||||||
'models.form.chunkSize.tips': 'Количество токенов на чанк KV-кэша.',
|
'models.form.chunkSize.tips': 'Количество токенов на чанк KV-кэша.',
|
||||||
'models.form.mode': 'Режим',
|
'models.form.mode': 'Режим',
|
||||||
'models.form.algorithm': 'Алгоритм',
|
'models.form.algorithm': 'Алгоритм',
|
||||||
@@ -225,15 +229,21 @@ export default {
|
|||||||
'models.form.mode.throughput': 'Пропускная способность',
|
'models.form.mode.throughput': 'Пропускная способность',
|
||||||
'models.form.mode.latency': 'Задержка',
|
'models.form.mode.latency': 'Задержка',
|
||||||
'models.form.mode.baseline': 'Стандартный',
|
'models.form.mode.baseline': 'Стандартный',
|
||||||
'models.form.mode.throughput.tips': 'оптимизировано для высокой пропускной способности при высокой конкурентности запросов.',
|
'models.form.mode.throughput.tips':
|
||||||
'models.form.mode.latency.tips': 'оптимизировано для низкой задержки при низкой конкурентности запросов.',
|
'оптимизировано для высокой пропускной способности при высокой конкурентности запросов.',
|
||||||
'models.form.mode.baseline.tips': 'наиболее совместимый вариант с полной точностью.',
|
'models.form.mode.latency.tips':
|
||||||
|
'оптимизировано для низкой задержки при низкой конкурентности запросов.',
|
||||||
|
'models.form.mode.baseline.tips':
|
||||||
|
'наиболее совместимый вариант с полной точностью.',
|
||||||
'models.form.draftModel.placeholder': 'Выберите или введите черновую модель',
|
'models.form.draftModel.placeholder': 'Выберите или введите черновую модель',
|
||||||
'models.form.draftModel.tips': 'Вы можете ввести локальный путь (например, /path/to/model) или выбрать модель из Hugging Face или ModelScope (например, Tengyunw/qwen3_8b_eagle3). Система автоматически сопоставит модель на основе источника основной модели.',
|
'models.form.draftModel.tips':
|
||||||
|
'Вы можете ввести локальный путь (например, /path/to/model) или выбрать модель из Hugging Face или ModelScope (например, Tengyunw/qwen3_8b_eagle3). Система автоматически сопоставит модель на основе источника основной модели.',
|
||||||
'models.form.quantization': 'Квантование',
|
'models.form.quantization': 'Квантование',
|
||||||
'models.form.backend.custom': 'Пользовательский'
|
'models.form.backend.custom': 'Пользовательский',
|
||||||
|
'models.form.rules.name':
|
||||||
|
'Up to 63 characters; letters, numbers, dots (.), underscores (_), and hyphens (-) only; must start and end with an alphanumeric character.'
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
|
// 1. 'models.form.rules.name': 'Up to 63 characters; letters, numbers, dots (.), underscores (_), and hyphens (-) only; must start and end with an alphanumeric character.'
|
||||||
// ========== End of To-Do List ==========
|
// ========== End of To-Do List ==========
|
||||||
|
|||||||
@@ -225,5 +225,7 @@ export default {
|
|||||||
'models.form.draftModel.tips':
|
'models.form.draftModel.tips':
|
||||||
'可填写本地路径(如 /path/to/model),或从 Hugging Face、ModelScope 选择模型(如 Tengyunw/qwen3_8b_eagle3)。系统将根据主模型来源自动匹配。',
|
'可填写本地路径(如 /path/to/model),或从 Hugging Face、ModelScope 选择模型(如 Tengyunw/qwen3_8b_eagle3)。系统将根据主模型来源自动匹配。',
|
||||||
'models.form.quantization': '量化',
|
'models.form.quantization': '量化',
|
||||||
'models.form.backend.custom': '用户定义'
|
'models.form.backend.custom': '用户定义',
|
||||||
|
'models.form.rules.name':
|
||||||
|
'长度不超过 63 个字符,只能包含字母、数字、点(.)、下划线(_)和连字符(-),且必须以字母或数字开头和结尾。'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ import _ from 'lodash';
|
|||||||
import React, { useEffect, useId, useRef, useState } from 'react';
|
import React, { useEffect, useId, useRef, useState } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import ColumnWrapper from '../../_components/column-wrapper';
|
import ColumnWrapper from '../../_components/column-wrapper';
|
||||||
import { backendFields, json2Yaml } from '../config';
|
import {
|
||||||
|
builtInBackendFields,
|
||||||
|
customBackendFields,
|
||||||
|
json2Yaml
|
||||||
|
} from '../config';
|
||||||
import { FormData, ListItem } from '../config/types';
|
import { FormData, ListItem } from '../config/types';
|
||||||
import BackendForm from '../forms';
|
import BackendForm from '../forms';
|
||||||
import ImportYAML from './import-yaml';
|
import ImportYAML from './import-yaml';
|
||||||
@@ -151,10 +155,10 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (currentData?.is_built_in) {
|
if (currentData?.is_built_in) {
|
||||||
return json2Yaml(_.pick(copyValues, backendFields));
|
return json2Yaml(_.pick(copyValues, builtInBackendFields));
|
||||||
}
|
}
|
||||||
return json2Yaml(
|
return json2Yaml(
|
||||||
_.pick(copyValues, [...backendFields, 'default_version'])
|
_.pick(copyValues, [...customBackendFields, 'default_version'])
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -109,13 +109,26 @@ export const customIcons = [
|
|||||||
'≥'
|
'≥'
|
||||||
];
|
];
|
||||||
|
|
||||||
export const backendFields = [
|
/**
|
||||||
|
* custom backend fields for config yaml
|
||||||
|
*/
|
||||||
|
export const customBackendFields = [
|
||||||
'description',
|
'description',
|
||||||
'health_check_path',
|
'health_check_path',
|
||||||
'default_run_command',
|
'default_run_command',
|
||||||
'version_configs',
|
'version_configs',
|
||||||
'default_backend_param'
|
'default_backend_param'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* built-in backend fields for config yaml
|
||||||
|
*/
|
||||||
|
export const builtInBackendFields = [
|
||||||
|
'description',
|
||||||
|
'version_configs',
|
||||||
|
'default_backend_param'
|
||||||
|
];
|
||||||
|
|
||||||
export const frameworks = [
|
export const frameworks = [
|
||||||
{
|
{
|
||||||
label: 'CUDA',
|
label: 'CUDA',
|
||||||
|
|||||||
@@ -37,38 +37,40 @@ const BasicForm: React.FC<AddModalProps> = ({ action, currentData }) => {
|
|||||||
required
|
required
|
||||||
></SealInput.Input>
|
></SealInput.Input>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item<FormData>
|
{!currentData?.is_built_in && (
|
||||||
name="health_check_path"
|
<>
|
||||||
rules={[{ required: false }]}
|
<Form.Item<FormData>
|
||||||
>
|
name="health_check_path"
|
||||||
<SealInput.Input
|
rules={[{ required: false }]}
|
||||||
trim
|
>
|
||||||
placeholder={intl.formatMessage(
|
<SealInput.Input
|
||||||
{ id: 'common.help.default' },
|
trim
|
||||||
{ content: '/v1/models' }
|
placeholder={intl.formatMessage(
|
||||||
)}
|
{ id: 'common.help.default' },
|
||||||
label={intl.formatMessage({ id: 'backend.form.healthCheckPath' })}
|
{ content: '/v1/models' }
|
||||||
></SealInput.Input>
|
)}
|
||||||
</Form.Item>
|
label={intl.formatMessage({ id: 'backend.form.healthCheckPath' })}
|
||||||
|
></SealInput.Input>
|
||||||
<Form.Item name="default_run_command">
|
</Form.Item>
|
||||||
<SealTextArea
|
<Form.Item name="default_run_command">
|
||||||
scaleSize={true}
|
<SealTextArea
|
||||||
alwaysFocus={true}
|
scaleSize={true}
|
||||||
description={intl.formatMessage({
|
alwaysFocus={true}
|
||||||
id: 'backend.form.defaultExecuteCommand.tips'
|
description={intl.formatMessage({
|
||||||
})}
|
id: 'backend.form.defaultExecuteCommand.tips'
|
||||||
placeholder={intl.formatMessage(
|
})}
|
||||||
{ id: 'common.help.eg' },
|
placeholder={intl.formatMessage(
|
||||||
{ content: 'vllm serve {{model_path}} --port {{port}}' }
|
{ id: 'common.help.eg' },
|
||||||
)}
|
{ content: 'vllm serve {{model_path}} --port {{port}}' }
|
||||||
autoSize={{ minRows: 2, maxRows: 4 }}
|
)}
|
||||||
label={intl.formatMessage({
|
autoSize={{ minRows: 2, maxRows: 4 }}
|
||||||
id: 'backend.form.defaultExecuteCommand'
|
label={intl.formatMessage({
|
||||||
})}
|
id: 'backend.form.defaultExecuteCommand'
|
||||||
></SealTextArea>
|
})}
|
||||||
</Form.Item>
|
></SealTextArea>
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<Form.Item<FormData>
|
<Form.Item<FormData>
|
||||||
name="default_backend_param"
|
name="default_backend_param"
|
||||||
rules={[{ required: false }]}
|
rules={[{ required: false }]}
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ const BackendForm: React.FC<AddModalProps> = forwardRef(
|
|||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
|
|
||||||
onFinish(data);
|
onFinish(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import SealInput from '@/components/seal-form/seal-input';
|
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 { modelNameReg } from '@/config';
|
||||||
import useAppUtils from '@/hooks/use-app-utils';
|
import useAppUtils from '@/hooks/use-app-utils';
|
||||||
import {
|
import {
|
||||||
ClusterStatusLabelMap,
|
ClusterStatusLabelMap,
|
||||||
@@ -65,10 +66,15 @@ const BasicForm: React.FC<BasicFormProps> = (props) => {
|
|||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: getRuleMessage('input', 'common.table.name')
|
message: getRuleMessage('input', 'common.table.name')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: modelNameReg,
|
||||||
|
message: intl.formatMessage({ id: 'models.form.rules.name' })
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<SealInput.Input
|
<SealInput.Input
|
||||||
|
description={intl.formatMessage({ id: 'models.form.rules.name' })}
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: 'common.table.name'
|
id: 'common.table.name'
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -30,12 +30,10 @@ import BasicForm from './basic';
|
|||||||
import Performance from './performance';
|
import Performance from './performance';
|
||||||
import ScheduleTypeForm from './schedule-type';
|
import ScheduleTypeForm from './schedule-type';
|
||||||
|
|
||||||
const advancedRequiredFields = [
|
const advancedRequiredFields = ['backend', 'image_name', 'run_command'];
|
||||||
'gpu_selector',
|
|
||||||
'backend',
|
const scheduleRequiredFields = ['gpu_selector'];
|
||||||
'image_name',
|
|
||||||
'run_command'
|
|
||||||
];
|
|
||||||
const performanceRequiredFields = ['speculative_config'];
|
const performanceRequiredFields = ['speculative_config'];
|
||||||
|
|
||||||
const SegmentedInner = styled(Segmented)`
|
const SegmentedInner = styled(Segmented)`
|
||||||
@@ -281,6 +279,14 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
|||||||
performanceRequiredFields.includes(name)
|
performanceRequiredFields.includes(name)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isScheduleRequired = names.some((name: string) =>
|
||||||
|
scheduleRequiredFields.includes(name)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isScheduleRequired) {
|
||||||
|
collapseKeys.push('scheduling');
|
||||||
|
}
|
||||||
|
|
||||||
if (isPerformanceRequired) {
|
if (isPerformanceRequired) {
|
||||||
collapseKeys.push('performance');
|
collapseKeys.push('performance');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ const SpeculativeDecode = () => {
|
|||||||
const onValuesChangeDebounced = _.debounce(() => {
|
const onValuesChangeDebounced = _.debounce(() => {
|
||||||
const allValues = form.getFieldsValue();
|
const allValues = form.getFieldsValue();
|
||||||
onValuesChange?.({}, allValues);
|
onValuesChange?.({}, allValues);
|
||||||
}, 200);
|
}, 150);
|
||||||
|
|
||||||
const handleSpeculativeEnabledChange = (e: any) => {
|
const handleSpeculativeEnabledChange = (e: any) => {
|
||||||
if (e.target.checked) {
|
if (e.target.checked) {
|
||||||
@@ -57,8 +57,8 @@ const SpeculativeDecode = () => {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
speculativeConfigRef.current = form.getFieldValue('speculative_config');
|
speculativeConfigRef.current = form.getFieldValue('speculative_config');
|
||||||
onValuesChangeDebounced();
|
|
||||||
}
|
}
|
||||||
|
onValuesChangeDebounced();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAlgorithemChange = (value: string) => {
|
const handleAlgorithemChange = (value: string) => {
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ import useSetChunkRequest from '@/hooks/use-chunk-request';
|
|||||||
import useUpdateChunkedList from '@/hooks/use-update-chunk-list';
|
import useUpdateChunkedList from '@/hooks/use-update-chunk-list';
|
||||||
import { queryWorkersList } from '@/pages/resources/apis';
|
import { queryWorkersList } from '@/pages/resources/apis';
|
||||||
import { ListItem as WokerListItem } from '@/pages/resources/config/types';
|
import { ListItem as WokerListItem } from '@/pages/resources/config/types';
|
||||||
|
import { useMemoizedFn } from 'ahooks';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import qs from 'query-string';
|
import qs from 'query-string';
|
||||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
MODELS_API,
|
MODELS_API,
|
||||||
MODEL_INSTANCE_API,
|
MODEL_INSTANCE_API,
|
||||||
@@ -73,7 +74,7 @@ const Models: React.FC = () => {
|
|||||||
setDataList: setModelInstances
|
setDataList: setModelInstances
|
||||||
});
|
});
|
||||||
|
|
||||||
const getAllModelInstances = useCallback(async () => {
|
const getAllModelInstances = useMemoizedFn(async () => {
|
||||||
try {
|
try {
|
||||||
instancesToken.current?.cancel?.();
|
instancesToken.current?.cancel?.();
|
||||||
instancesToken.current = createAxiosToken();
|
instancesToken.current = createAxiosToken();
|
||||||
@@ -89,9 +90,9 @@ const Models: React.FC = () => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
}, []);
|
});
|
||||||
|
|
||||||
const fetchData = useCallback(
|
const fetchData = useMemoizedFn(
|
||||||
async (params?: {
|
async (params?: {
|
||||||
loadingVal?: boolean;
|
loadingVal?: boolean;
|
||||||
query?: {
|
query?: {
|
||||||
@@ -156,8 +157,7 @@ const Models: React.FC = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
[queryParams]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleQueryChange = (params: any) => {
|
const handleQueryChange = (params: any) => {
|
||||||
@@ -168,14 +168,13 @@ const Models: React.FC = () => {
|
|||||||
fetchData({ query: { ...queryParams, ...params } });
|
fetchData({ query: { ...queryParams, ...params } });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePageChange = useCallback(
|
const handlePageChange = useMemoizedFn(
|
||||||
(page: number, pageSize: number | undefined) => {
|
(page: number, pageSize: number | undefined) => {
|
||||||
handleQueryChange({
|
handleQueryChange({
|
||||||
page: page,
|
page: page,
|
||||||
perPage: pageSize || 10
|
perPage: pageSize || 10
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
[queryParams]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const updateHandler = (list: any) => {
|
const updateHandler = (list: any) => {
|
||||||
@@ -191,7 +190,7 @@ const Models: React.FC = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const createModelsChunkRequest = useCallback(
|
const createModelsChunkRequest = useMemoizedFn(
|
||||||
async (params?: {
|
async (params?: {
|
||||||
search: string;
|
search: string;
|
||||||
categories: any[];
|
categories: any[];
|
||||||
@@ -212,11 +211,10 @@ const Models: React.FC = () => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
[queryParams.categories, queryParams.search]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const createModelsInstanceChunkRequest = useCallback(async () => {
|
const createModelsInstanceChunkRequest = useMemoizedFn(async () => {
|
||||||
chunkInstanceRequedtRef.current?.current?.cancel?.();
|
chunkInstanceRequedtRef.current?.current?.cancel?.();
|
||||||
try {
|
try {
|
||||||
chunkInstanceRequedtRef.current = setModelInstanceChunkRequest({
|
chunkInstanceRequedtRef.current = setModelInstanceChunkRequest({
|
||||||
@@ -227,18 +225,18 @@ const Models: React.FC = () => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
}, [updateInstanceHandler]);
|
});
|
||||||
|
|
||||||
const handleOnViewLogs = useCallback(() => {
|
const handleOnViewLogs = useMemoizedFn(() => {
|
||||||
isPageHidden.current = true;
|
isPageHidden.current = true;
|
||||||
chunkRequedtRef.current?.current?.cancel?.();
|
chunkRequedtRef.current?.current?.cancel?.();
|
||||||
cacheDataListRef.current = [];
|
cacheDataListRef.current = [];
|
||||||
cacheInsDataListRef.current = [];
|
cacheInsDataListRef.current = [];
|
||||||
chunkInstanceRequedtRef.current?.current?.cancel?.();
|
chunkInstanceRequedtRef.current?.current?.cancel?.();
|
||||||
instancesToken.current?.cancel?.();
|
instancesToken.current?.cancel?.();
|
||||||
}, []);
|
});
|
||||||
|
|
||||||
const handleOnCancelViewLogs = useCallback(async () => {
|
const handleOnCancelViewLogs = useMemoizedFn(async () => {
|
||||||
isPageHidden.current = false;
|
isPageHidden.current = false;
|
||||||
await getAllModelInstances();
|
await getAllModelInstances();
|
||||||
await createModelsInstanceChunkRequest();
|
await createModelsInstanceChunkRequest();
|
||||||
@@ -246,23 +244,20 @@ const Models: React.FC = () => {
|
|||||||
fetchData({
|
fetchData({
|
||||||
loadingVal: false
|
loadingVal: false
|
||||||
});
|
});
|
||||||
}, [fetchData, createModelsChunkRequest, createModelsInstanceChunkRequest]);
|
});
|
||||||
|
|
||||||
const handleSearchBySilent = useCallback(async () => {
|
const handleSearchBySilent = useMemoizedFn(async () => {
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
setTimeout(resolve, 300);
|
setTimeout(resolve, 300);
|
||||||
});
|
});
|
||||||
fetchData({
|
fetchData({
|
||||||
loadingVal: false
|
loadingVal: false
|
||||||
});
|
});
|
||||||
}, [fetchData]);
|
});
|
||||||
|
|
||||||
const handleSearch = useCallback(
|
const handleSearch = useMemoizedFn(async (params?: any) => {
|
||||||
async (params?: any) => {
|
await fetchData(params);
|
||||||
await fetchData(params);
|
});
|
||||||
},
|
|
||||||
[fetchData]
|
|
||||||
);
|
|
||||||
|
|
||||||
const debounceUpdateFilter = _.debounce((e: any) => {
|
const debounceUpdateFilter = _.debounce((e: any) => {
|
||||||
handleQueryChange({
|
handleQueryChange({
|
||||||
@@ -276,7 +271,7 @@ const Models: React.FC = () => {
|
|||||||
});
|
});
|
||||||
}, 350);
|
}, 350);
|
||||||
|
|
||||||
const handleNameChange = useCallback(debounceUpdateFilter, [queryParams]);
|
const handleNameChange = useMemoizedFn(debounceUpdateFilter);
|
||||||
|
|
||||||
const handleCategoryChange = async (value: any) => {
|
const handleCategoryChange = async (value: any) => {
|
||||||
handleQueryChange({
|
handleQueryChange({
|
||||||
@@ -369,9 +364,9 @@ const Models: React.FC = () => {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const setDisableExpand = useCallback((record: any) => {
|
const setDisableExpand = useMemoizedFn((record: any) => {
|
||||||
return !record?.replicas;
|
return !record?.replicas;
|
||||||
}, []);
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleVisibilityChange = async () => {
|
const handleVisibilityChange = async () => {
|
||||||
|
|||||||
@@ -259,11 +259,6 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleStopConversation = () => {
|
|
||||||
requestToken.current?.cancel?.();
|
|
||||||
setLoading(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const submitMessage = async (query: string) => {
|
const submitMessage = async (query: string) => {
|
||||||
try {
|
try {
|
||||||
setIsEmptyQuery(!queryValue);
|
setIsEmptyQuery(!queryValue);
|
||||||
@@ -507,9 +502,12 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
|
|||||||
)}
|
)}
|
||||||
</SearchInputWrapper>
|
</SearchInputWrapper>
|
||||||
</div>
|
</div>
|
||||||
<div className="center" ref={scroller}>
|
<div className="center" ref={scroller} style={{ marginTop: 16 }}>
|
||||||
<div className="documents">
|
<div className="documents">
|
||||||
<div className="flex-between m-b-8 doc-header">
|
<div
|
||||||
|
className="flex-between m-b-8 doc-header"
|
||||||
|
style={{ marginTop: 0 }}
|
||||||
|
>
|
||||||
<h3 className="m-l-10 flex-between flex-center font-size-14 line-24 m-b-0">
|
<h3 className="m-l-10 flex-between flex-center font-size-14 line-24 m-b-0">
|
||||||
<span>
|
<span>
|
||||||
{intl.formatMessage({ id: 'playground.embedding.documents' })}
|
{intl.formatMessage({ id: 'playground.embedding.documents' })}
|
||||||
|
|||||||
Reference in New Issue
Block a user