fix(styles): providers access token
This commit is contained in:
@@ -77,7 +77,10 @@ const OptionNodes = (props: {
|
||||
|
||||
const SealCascader: React.FC<
|
||||
CascaderAutoProps &
|
||||
SealFormItemProps & { optionNode?: React.FC<{ data: any }> }
|
||||
SealFormItemProps & {
|
||||
alwaysFocus?: boolean;
|
||||
optionNode?: React.FC<{ data: any }>;
|
||||
}
|
||||
> = (props) => {
|
||||
const {
|
||||
label,
|
||||
@@ -88,6 +91,7 @@ const SealCascader: React.FC<
|
||||
options,
|
||||
allowNull,
|
||||
isInFormItems = true,
|
||||
alwaysFocus = false,
|
||||
optionNode,
|
||||
notFoundContent,
|
||||
tagRender,
|
||||
@@ -167,7 +171,7 @@ const SealCascader: React.FC<
|
||||
classList={visible ? 'dropdown-visible' : ''}
|
||||
status={status}
|
||||
label={label}
|
||||
isFocus={isFocus}
|
||||
isFocus={alwaysFocus || isFocus}
|
||||
required={required}
|
||||
description={description}
|
||||
disabled={props.disabled}
|
||||
|
||||
@@ -255,6 +255,8 @@ export default {
|
||||
'models.catalog.precision': 'Precision',
|
||||
'models.form.gpuPerReplica.tips': 'Enter a custom number',
|
||||
'models.form.generic_proxy': 'Enable Generic Proxy',
|
||||
'models.form.enable_model_access': 'Enable Model Access',
|
||||
'models.form.enable_model_access.tips': 'Enable Model Access',
|
||||
'models.form.generic_proxy.tips':
|
||||
'After enabling the generic proxy, you can access URI paths that do not follow the OpenAI API standard.',
|
||||
'models.form.generic_proxy.button': 'Generic Proxy',
|
||||
|
||||
@@ -4,13 +4,15 @@ export default {
|
||||
'providers.table.providerName': 'Provider',
|
||||
'providers.table.models': 'Models',
|
||||
'providers.table.proxy': 'Proxy',
|
||||
'providers.form.tokens.title': ' API Tokens',
|
||||
'providers.form.tokens.add': 'Add API Token',
|
||||
'providers.form.tokens.title': ' API Key',
|
||||
'providers.form.tokens.add': 'Add API Key',
|
||||
'providers.form.models.add': 'Add Model',
|
||||
'providers.form.proxy.enable': 'Enable Proxy',
|
||||
'providers.form.proxy.url': 'Proxy URL',
|
||||
'providers.form.proxy.timeout': 'Proxy Timeout (seconds)',
|
||||
'providers.form.customConfig': 'Custom Configuration',
|
||||
'providers.form.model.test': 'Test',
|
||||
'providers.form.model.test.tips': 'Test Connection'
|
||||
'providers.form.model.test.tips': 'Test Connection',
|
||||
'providers.form.endpoint.placeholder': 'provider/models',
|
||||
'providers.form.fallback.token': 'Fallback API Keys'
|
||||
};
|
||||
|
||||
@@ -255,6 +255,8 @@ export default {
|
||||
'models.catalog.precision': 'Precision',
|
||||
'models.form.gpuPerReplica.tips': 'Enter a custom number',
|
||||
'models.form.generic_proxy': 'Enable Generic Proxy',
|
||||
'models.form.enable_model_access': 'Enable Model Access',
|
||||
'models.form.enable_model_access.tips': 'Enable Model Access',
|
||||
'models.form.generic_proxy.tips':
|
||||
'After enabling the generic proxy, you can access URI paths that do not follow the OpenAI API standard.',
|
||||
'models.form.generic_proxy.button': 'Generic Proxy',
|
||||
@@ -375,4 +377,6 @@ export default {
|
||||
// 76. 'models.form.maxContextLength': 'Maximum Context Length',
|
||||
// 77. 'models.form.backend.helperText': 'Not enabled yet. Will be enabled after deployment. ',
|
||||
// 78. 'models.table.instance.benchmark': 'Run Benchmark'
|
||||
// 77. 'models.form.enable_model_access': 'Enable Model Access',
|
||||
// 78. 'models.form.enable_model_access.tips': 'Enable Model Access',
|
||||
// ========== End of To-Do List ==========
|
||||
|
||||
@@ -4,13 +4,15 @@ export default {
|
||||
'providers.table.providerName': 'Provider',
|
||||
'providers.table.models': 'Models',
|
||||
'providers.table.proxy': 'Proxy',
|
||||
'providers.form.tokens.title': ' API Tokens',
|
||||
'providers.form.tokens.add': 'Add API Token',
|
||||
'providers.form.tokens.title': ' API Key',
|
||||
'providers.form.tokens.add': 'Add API Key',
|
||||
'providers.form.models.add': 'Add Model',
|
||||
'providers.form.proxy.enable': 'Enable Proxy',
|
||||
'providers.form.proxy.url': 'Proxy URL',
|
||||
'providers.form.proxy.timeout': 'Proxy Timeout (seconds)',
|
||||
'providers.form.customConfig': 'Custom Configuration',
|
||||
'providers.form.model.test': 'Test',
|
||||
'providers.form.model.test.tips': 'Test Connection'
|
||||
'providers.form.model.test.tips': 'Test Connection',
|
||||
'providers.form.endpoint.placeholder': 'provider/models',
|
||||
'providers.form.fallback.token': 'Fallback API Keys'
|
||||
};
|
||||
|
||||
@@ -260,6 +260,8 @@ export default {
|
||||
'models.catalog.precision': 'Точность',
|
||||
'models.form.gpuPerReplica.tips': 'Введите произвольное число',
|
||||
'models.form.generic_proxy': 'Включить универсальный прокси',
|
||||
'models.form.enable_model_access': 'Enable Model Access',
|
||||
'models.form.enable_model_access.tips': 'Enable Model Access',
|
||||
'models.form.generic_proxy.tips':
|
||||
'После включения универсального прокси вы можете получать доступ к URI-путям, которые не следуют стандарту OpenAI API.',
|
||||
'models.form.generic_proxy.button': 'Универсальный прокси',
|
||||
@@ -287,4 +289,6 @@ export default {
|
||||
// 1. 'models.form.maxContextLength': 'Maximum Context Length',
|
||||
// 2. 'models.form.backend.helperText': 'Not enabled yet. Will be enabled after deployment. ',
|
||||
// 3. 'models.table.instance.benchmark': 'Run Benchmark'
|
||||
// 2. 'models.form.enable_model_access': 'Enable Model Access',
|
||||
// 3. 'models.form.enable_model_access.tips': 'Enable Model Access',
|
||||
// ========== End of To-Do List ==========
|
||||
|
||||
@@ -4,13 +4,15 @@ export default {
|
||||
'providers.table.providerName': 'Provider',
|
||||
'providers.table.models': 'Models',
|
||||
'providers.table.proxy': 'Proxy',
|
||||
'providers.form.tokens.title': ' API Tokens',
|
||||
'providers.form.tokens.add': 'Add API Token',
|
||||
'providers.form.tokens.title': ' API Key',
|
||||
'providers.form.tokens.add': 'Add API Key',
|
||||
'providers.form.models.add': 'Add Model',
|
||||
'providers.form.proxy.enable': 'Enable Proxy',
|
||||
'providers.form.proxy.url': 'Proxy URL',
|
||||
'providers.form.proxy.timeout': 'Proxy Timeout (seconds)',
|
||||
'providers.form.customConfig': 'Custom Configuration',
|
||||
'providers.form.model.test': 'Test',
|
||||
'providers.form.model.test.tips': 'Test Connection'
|
||||
'providers.form.model.test.tips': 'Test Connection',
|
||||
'providers.form.endpoint.placeholder': 'provider/models',
|
||||
'providers.form.fallback.token': 'Fallback API Keys'
|
||||
};
|
||||
|
||||
@@ -242,6 +242,8 @@ export default {
|
||||
'models.catalog.precision': '精度',
|
||||
'models.form.gpuPerReplica.tips': '输入自定义数值',
|
||||
'models.form.generic_proxy': '启用通用代理',
|
||||
'models.form.enable_model_access': '启用模型接入',
|
||||
'models.form.enable_model_access.tips': '启用模型接入',
|
||||
'models.form.generic_proxy.tips':
|
||||
'启用通用代理后可支持访问非 OpenAI-API 标准的 URI 路径。',
|
||||
'models.form.generic_proxy.button': '通用代理',
|
||||
|
||||
@@ -4,13 +4,15 @@ export default {
|
||||
'providers.table.providerName': '提供商',
|
||||
'providers.table.models': '模型',
|
||||
'providers.table.proxy': '代理',
|
||||
'providers.form.tokens.title': 'API 令牌',
|
||||
'providers.form.tokens.add': '添加 API 令牌',
|
||||
'providers.form.tokens.title': 'API Key',
|
||||
'providers.form.tokens.add': '添加 API Key',
|
||||
'providers.form.models.add': '添加模型',
|
||||
'providers.form.proxy.enable': '启用代理',
|
||||
'providers.form.proxy.url': '代理地址',
|
||||
'providers.form.proxy.timeout': '代理超时(秒)',
|
||||
'providers.form.customConfig': '自定义配置',
|
||||
'providers.form.model.test': '测试',
|
||||
'providers.form.model.test.tips': '测试连接'
|
||||
'providers.form.model.test.tips': '测试连接',
|
||||
'providers.form.endpoint.placeholder': '提供商/模型',
|
||||
'providers.form.fallback.token': '备用 API Key'
|
||||
};
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import SealSelect from '@/components/seal-form/seal-select';
|
||||
import React from 'react';
|
||||
import { categoryConfig } from './model-tag';
|
||||
|
||||
const CategorySelect = (props: any) => {
|
||||
const { options, ...rest } = props;
|
||||
|
||||
const optionRenderer = (option: any) => {
|
||||
const config = categoryConfig[option.value] || {};
|
||||
console.log('option:', option, 'config:', config);
|
||||
return (
|
||||
<span className="flex-center">
|
||||
<span style={{ color: config.color }}>
|
||||
{React.cloneElement(config.icon)}
|
||||
</span>
|
||||
<span style={{ marginLeft: 8 }}>{option.label}</span>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<SealSelect
|
||||
options={options}
|
||||
optionRenderer={optionRenderer}
|
||||
labelRender={optionRenderer}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default CategorySelect;
|
||||
@@ -3,6 +3,7 @@ import { downloadFile, listFiles, listModels } from '@huggingface/hub';
|
||||
import { PipelineType } from '@huggingface/tasks';
|
||||
import { request } from '@umijs/max';
|
||||
import qs from 'query-string';
|
||||
import { ACCESS_API } from '../../model-access/apis';
|
||||
import {
|
||||
AccessControlFormData,
|
||||
BackendItem,
|
||||
@@ -418,7 +419,7 @@ export async function queryBackendList(params?: { cluster_id: number }) {
|
||||
}
|
||||
|
||||
export async function queryModelAccessUserList(id: number) {
|
||||
return request<{ items: UserListItem[] }>(`${MODELS_API}/${id}/access`, {
|
||||
return request<{ items: UserListItem[] }>(`${ACCESS_API}/${id}/access`, {
|
||||
method: 'GET'
|
||||
});
|
||||
}
|
||||
@@ -427,7 +428,7 @@ export async function updateModelAccessUser(params: {
|
||||
id: number;
|
||||
data: AccessControlFormData;
|
||||
}) {
|
||||
return request(`${MODELS_API}/${params.id}/access`, {
|
||||
return request(`${ACCESS_API}/${params.id}/access`, {
|
||||
method: 'POST',
|
||||
data: params.data
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import AlertBlockInfo from '@/components/alert-info/block';
|
||||
import ModalFooter from '@/components/modal-footer';
|
||||
import ScrollerModal from '@/components/scroller-modal';
|
||||
import { PageActionType } from '@/config/types';
|
||||
import FormDrawer from '@/pages/_components/form-drawer';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { message } from 'antd';
|
||||
import _ from 'lodash';
|
||||
@@ -75,19 +75,13 @@ const AccessControlModal: React.FC<
|
||||
}, [open, currentData]);
|
||||
|
||||
return (
|
||||
<ScrollerModal
|
||||
<FormDrawer
|
||||
title={title}
|
||||
open={open}
|
||||
centered={true}
|
||||
onCancel={onCancel}
|
||||
destroyOnHidden={true}
|
||||
closeIcon={true}
|
||||
maskClosable={false}
|
||||
keyboard={false}
|
||||
width={700}
|
||||
height={660}
|
||||
footer={
|
||||
<>
|
||||
<div style={{ paddingInline: 24, paddingBottom: 8 }}>
|
||||
{isChanged && (
|
||||
<AlertBlockInfo
|
||||
type="warning"
|
||||
@@ -99,7 +93,7 @@ const AccessControlModal: React.FC<
|
||||
></AlertBlockInfo>
|
||||
)}
|
||||
<ModalFooter onOk={handleSumit} onCancel={onCancel}></ModalFooter>
|
||||
</>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<AccessControlForm
|
||||
@@ -109,7 +103,7 @@ const AccessControlModal: React.FC<
|
||||
onFinish={handleOnFinish}
|
||||
onValuesChange={handleOnValuesChange}
|
||||
/>
|
||||
</ScrollerModal>
|
||||
</FormDrawer>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
import IconFont from '@/components/icon-font';
|
||||
import { StatusMaps } from '@/config';
|
||||
import {
|
||||
AudioOutlined,
|
||||
EditOutlined,
|
||||
PictureOutlined,
|
||||
WechatWorkOutlined
|
||||
} from '@ant-design/icons';
|
||||
import React from 'react';
|
||||
import { EditOutlined } from '@ant-design/icons';
|
||||
import { backendOptionsMap } from './backend-parameters';
|
||||
|
||||
export const backendTipsList = [
|
||||
@@ -299,42 +292,27 @@ export const modelCategoriesMap = {
|
||||
export const categoryOptions = [
|
||||
{
|
||||
label: 'LLM',
|
||||
value: modelCategoriesMap.llm,
|
||||
icon: React.createElement(WechatWorkOutlined, { style: { color: 'green' } })
|
||||
value: modelCategoriesMap.llm
|
||||
},
|
||||
{
|
||||
label: 'Embedding',
|
||||
value: modelCategoriesMap.embedding,
|
||||
icon: React.createElement(IconFont, {
|
||||
type: 'icon-cube',
|
||||
style: { color: 'magenta' }
|
||||
})
|
||||
value: modelCategoriesMap.embedding
|
||||
},
|
||||
{
|
||||
label: 'Reranker',
|
||||
value: modelCategoriesMap.reranker,
|
||||
icon: React.createElement(IconFont, {
|
||||
type: 'icon-rank1',
|
||||
style: { color: 'cyan' }
|
||||
})
|
||||
value: modelCategoriesMap.reranker
|
||||
},
|
||||
{
|
||||
label: 'Image',
|
||||
value: modelCategoriesMap.image,
|
||||
icon: React.createElement(PictureOutlined, { style: { color: 'orange' } })
|
||||
value: modelCategoriesMap.image
|
||||
},
|
||||
{
|
||||
label: 'Text-to-Speech',
|
||||
value: modelCategoriesMap.text_to_speech,
|
||||
icon: React.createElement(IconFont, {
|
||||
type: 'icon-sound-wave',
|
||||
style: { color: 'geekblue' }
|
||||
})
|
||||
value: modelCategoriesMap.text_to_speech
|
||||
},
|
||||
{
|
||||
label: 'Speech-to-Text',
|
||||
value: modelCategoriesMap.speech_to_text,
|
||||
icon: React.createElement(AudioOutlined, { style: { color: 'processing' } })
|
||||
value: modelCategoriesMap.speech_to_text
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ export type SourceType =
|
||||
export interface FormData {
|
||||
image_name?: string;
|
||||
run_command?: string;
|
||||
enable_model_access?: boolean;
|
||||
backend: string;
|
||||
restart_on_error?: boolean;
|
||||
env?: Record<string, any>;
|
||||
|
||||
@@ -16,8 +16,14 @@ const AdvanceConfig = () => {
|
||||
const form = Form.useFormInstance();
|
||||
const EnviromentVars = Form.useWatch('env', form);
|
||||
const backend = Form.useWatch('backend', form);
|
||||
const { onValuesChange, isGGUF, modelContextData, flatBackendOptions } =
|
||||
useFormContext();
|
||||
const modelAccessEnable = Form.useWatch('enable_model_access', form);
|
||||
const {
|
||||
onValuesChange,
|
||||
backendOptions,
|
||||
flatBackendOptions,
|
||||
isGGUF,
|
||||
modelContextData
|
||||
} = useFormContext();
|
||||
|
||||
const currentBackendOptions = useMemo(() => {
|
||||
return flatBackendOptions?.find((item) => item.value === backend);
|
||||
@@ -128,19 +134,35 @@ const AdvanceConfig = () => {
|
||||
></CheckboxField>
|
||||
</Form.Item>
|
||||
<Form.Item<FormData>
|
||||
name="generic_proxy"
|
||||
name="enable_model_access"
|
||||
valuePropName="checked"
|
||||
style={{ marginBottom: 8 }}
|
||||
>
|
||||
<CheckboxField
|
||||
description={intl.formatMessage({
|
||||
id: 'models.form.generic_proxy.tips'
|
||||
id: 'models.form.enable_model_access.tips'
|
||||
})}
|
||||
label={intl.formatMessage({
|
||||
id: 'models.form.generic_proxy'
|
||||
id: 'models.form.enable_model_access'
|
||||
})}
|
||||
></CheckboxField>
|
||||
</Form.Item>
|
||||
{modelAccessEnable && (
|
||||
<Form.Item<FormData>
|
||||
name="generic_proxy"
|
||||
valuePropName="checked"
|
||||
style={{ marginBottom: 8 }}
|
||||
>
|
||||
<CheckboxField
|
||||
description={intl.formatMessage({
|
||||
id: 'models.form.generic_proxy.tips'
|
||||
})}
|
||||
label={intl.formatMessage({
|
||||
id: 'models.form.generic_proxy'
|
||||
})}
|
||||
></CheckboxField>
|
||||
</Form.Item>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@ export interface FormData {
|
||||
description: string;
|
||||
api_tokens: string[];
|
||||
models: ProviderModel[];
|
||||
api_key: string;
|
||||
config: {
|
||||
type: maasProviderType;
|
||||
[key: string]: any;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import ListInput from '@/components/list-input';
|
||||
import MetadataList from '@/components/metadata-list';
|
||||
import SealInput from '@/components/seal-form/seal-input';
|
||||
import useAppUtils from '@/hooks/use-app-utils';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
@@ -10,17 +11,37 @@ const AccessToken = () => {
|
||||
const form = Form.useFormInstance<FormData>();
|
||||
const tokenList = Form.useWatch('api_tokens', form) || [];
|
||||
|
||||
const handleOnChange = (values: string[]) => {
|
||||
form.setFieldValue('api_tokens', values);
|
||||
const onAdd = () => {
|
||||
const newList = [...tokenList];
|
||||
newList.push('');
|
||||
form.setFieldValue('api_tokens', newList);
|
||||
};
|
||||
|
||||
const onDelete = (index: number) => {
|
||||
const newList = [...tokenList];
|
||||
newList.splice(index, 1);
|
||||
form.setFieldValue('api_tokens', newList);
|
||||
};
|
||||
|
||||
const handleInputChange = (
|
||||
index: number,
|
||||
e: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
const newList = [...tokenList];
|
||||
newList[index] = e.target.value;
|
||||
form.setFieldValue('api_tokens', newList);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
name="api_tokens"
|
||||
style={{
|
||||
marginBottom: 8
|
||||
}}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
required: false,
|
||||
message: getRuleMessage(
|
||||
'input',
|
||||
intl.formatMessage({ id: 'providers.form.tokens.title' })
|
||||
@@ -28,13 +49,22 @@ const AccessToken = () => {
|
||||
}
|
||||
]}
|
||||
>
|
||||
<ListInput
|
||||
required={true}
|
||||
btnText={intl.formatMessage({ id: 'providers.form.tokens.add' })}
|
||||
label={intl.formatMessage({ id: 'providers.form.tokens.title' })}
|
||||
<MetadataList
|
||||
dataList={tokenList}
|
||||
onChange={handleOnChange}
|
||||
></ListInput>
|
||||
btnText={intl.formatMessage({ id: 'providers.form.tokens.add' })}
|
||||
label={intl.formatMessage({ id: 'providers.form.fallback.token' })}
|
||||
onAdd={onAdd}
|
||||
onDelete={onDelete}
|
||||
>
|
||||
{(item, index) => (
|
||||
<div style={{ width: '100%' }} key={index}>
|
||||
<SealInput.Password
|
||||
value={item}
|
||||
onChange={(e) => handleInputChange(index, e)}
|
||||
></SealInput.Password>
|
||||
</div>
|
||||
)}
|
||||
</MetadataList>
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -4,6 +4,7 @@ import YamlEditor from '@/pages/_components/yaml-editor';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import React, { forwardRef, useImperativeHandle } from 'react';
|
||||
import AccessToken from './access-token';
|
||||
import ProxyConfig from './proxy-config';
|
||||
|
||||
const AdvanceConfig: React.FC<{
|
||||
@@ -28,6 +29,7 @@ const AdvanceConfig: React.FC<{
|
||||
return (
|
||||
<>
|
||||
<div data-field="advanceConfig"></div>
|
||||
<AccessToken></AccessToken>
|
||||
<ProxyConfig></ProxyConfig>
|
||||
<Form.Item
|
||||
hidden
|
||||
|
||||
@@ -54,6 +54,22 @@ const Basic = () => {
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item<FormData>
|
||||
name="api_key"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: getRuleMessage('input', 'providers.form.tokens.title')
|
||||
}
|
||||
]}
|
||||
>
|
||||
<SealInput.Password
|
||||
required
|
||||
label={intl.formatMessage({
|
||||
id: 'providers.form.tokens.title'
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item<FormData> name="description">
|
||||
<SealInput.TextArea
|
||||
scaleSize={true}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useWrapperContext } from '@/pages/_components/column-wrapper/use-wrappe
|
||||
import ScrollSpyTabs from '@/pages/_components/scroll-spy-tabs';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import {
|
||||
forwardRef,
|
||||
useEffect,
|
||||
@@ -15,7 +16,6 @@ import {
|
||||
} from 'react';
|
||||
import FormContext from '../config/form-context';
|
||||
import { FormData, MaasProviderItem as ListItem } from '../config/types';
|
||||
import AccessToken from './access-token';
|
||||
import AdvanceConfig from './advance-config';
|
||||
import Basic from './basic';
|
||||
import SupportedModels from './supported-models';
|
||||
@@ -63,6 +63,17 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
||||
}
|
||||
];
|
||||
|
||||
const handleOnFinish = (values: FormData) => {
|
||||
const apiTokens = values.api_tokens?.filter?.(
|
||||
(item) => item && item.trim() !== ''
|
||||
);
|
||||
const data = {
|
||||
..._.omit(values, ['api_key']),
|
||||
api_tokens: _.concat([], values.api_key, apiTokens || [])
|
||||
};
|
||||
onFinish(data);
|
||||
};
|
||||
|
||||
const handleActiveChange = (key: string[]) => {
|
||||
setActiveKey(key);
|
||||
};
|
||||
@@ -87,6 +98,8 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
||||
) {
|
||||
form.setFieldsValue({
|
||||
...currentData,
|
||||
api_key: currentData.api_tokens?.[0] || '',
|
||||
api_tokens: currentData.api_tokens?.slice(1) || [],
|
||||
proxy_enabled: !!currentData.proxy_url
|
||||
});
|
||||
}
|
||||
@@ -108,7 +121,7 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
||||
<FormContext.Provider value={{ action }}>
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={onFinish}
|
||||
onFinish={handleOnFinish}
|
||||
initialValues={{
|
||||
proxy_enabled: false,
|
||||
proxy_url: '',
|
||||
@@ -116,7 +129,6 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
||||
}}
|
||||
>
|
||||
<Basic />
|
||||
<AccessToken />
|
||||
<CollapsePanel
|
||||
activeKey={activeKey}
|
||||
accordion={false}
|
||||
|
||||
@@ -48,7 +48,7 @@ const ModelItem: React.FC<ModelItemProps> = ({
|
||||
const res = await runTestModel({
|
||||
data: {
|
||||
model_name: item.name,
|
||||
api_token: form.getFieldValue('api_tokens')?.[0] || '',
|
||||
api_token: form.getFieldValue('api_key') || '',
|
||||
config: {
|
||||
type: form.getFieldValue(['config', 'type']) || ''
|
||||
}
|
||||
|
||||
@@ -14,12 +14,12 @@ const SupportedModels = () => {
|
||||
|
||||
const handleOpenChange = async (open: boolean) => {
|
||||
try {
|
||||
await form.validateFields(['api_tokens', ['config', 'type']]);
|
||||
await form.validateFields(['api_key']);
|
||||
|
||||
if (open && providerModelList.length === 0) {
|
||||
fetchProviderModels({
|
||||
data: {
|
||||
api_token: form.getFieldValue('api_tokens')?.[0] || '',
|
||||
api_token: form.getFieldValue('api_key') || '',
|
||||
config: {
|
||||
type: form.getFieldValue(['config', 'type']) || ''
|
||||
}
|
||||
|
||||
@@ -20,11 +20,13 @@ const CellContent = styled.div`
|
||||
interface ProviderModelProps {
|
||||
dataList: AccessPointItem[];
|
||||
onSelect: (val: any, record: any) => void;
|
||||
sourceModels: any[];
|
||||
}
|
||||
|
||||
interface AccessItemProps {
|
||||
onSelect: (val: any, record: any) => void;
|
||||
data: any;
|
||||
sourceModels: any[];
|
||||
}
|
||||
|
||||
export const childActionList = [
|
||||
@@ -48,13 +50,24 @@ export const childActionList = [
|
||||
}
|
||||
];
|
||||
|
||||
const AccessItem: React.FC<AccessItemProps> = ({ onSelect, data }) => {
|
||||
const AccessItem: React.FC<AccessItemProps> = ({
|
||||
onSelect,
|
||||
data,
|
||||
sourceModels
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const renderProviderSource = () => {
|
||||
const model = sourceModels.find(
|
||||
(item: any) => item.value === data.provider_id || item.id === data.model
|
||||
);
|
||||
return model.label || '-';
|
||||
};
|
||||
return (
|
||||
<div style={{ borderRadius: 'var(--ant-table-header-border-radius)' }}>
|
||||
<RowChildren>
|
||||
<Row gutter={16} style={{ width: '100%' }}>
|
||||
<Col span={6}>
|
||||
<Col span={4}>
|
||||
<CellContent
|
||||
style={{
|
||||
paddingInline: 'var(--ant-table-cell-padding-inline)'
|
||||
@@ -64,7 +77,7 @@ const AccessItem: React.FC<AccessItemProps> = ({ onSelect, data }) => {
|
||||
</CellContent>
|
||||
</Col>
|
||||
<Col span={3}>
|
||||
<CellContent>-</CellContent>
|
||||
<CellContent>{renderProviderSource()}</CellContent>
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<CellContent>
|
||||
@@ -88,7 +101,7 @@ const AccessItem: React.FC<AccessItemProps> = ({ onSelect, data }) => {
|
||||
)}
|
||||
</CellContent>
|
||||
</Col>
|
||||
<Col span={3}>
|
||||
<Col span={4}>
|
||||
<CellContent>
|
||||
<AutoTooltip ghost>
|
||||
<StatusTag
|
||||
@@ -102,16 +115,16 @@ const AccessItem: React.FC<AccessItemProps> = ({ onSelect, data }) => {
|
||||
</CellContent>
|
||||
</Col>
|
||||
<Col span={5}>
|
||||
<CellContent style={{ marginLeft: -6 }}>
|
||||
<CellContent style={{ paddingLeft: 45 }}>
|
||||
<AutoTooltip ghost minWidth={20}>
|
||||
{dayjs(data.created_at).format('YYYY-MM-DD HH:mm:ss')}
|
||||
</AutoTooltip>
|
||||
</CellContent>
|
||||
</Col>
|
||||
<Col span={3}>
|
||||
<Col span={4}>
|
||||
<CellContent
|
||||
style={{
|
||||
marginLeft: -12
|
||||
paddingLeft: 38
|
||||
}}
|
||||
>
|
||||
<DropdownButtons
|
||||
@@ -126,12 +139,21 @@ const AccessItem: React.FC<AccessItemProps> = ({ onSelect, data }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const AccessPoints: React.FC<ProviderModelProps> = ({ dataList, onSelect }) => {
|
||||
const AccessPoints: React.FC<ProviderModelProps> = ({
|
||||
dataList,
|
||||
onSelect,
|
||||
sourceModels
|
||||
}) => {
|
||||
console.log('AccessPoints dataList:', dataList);
|
||||
return (
|
||||
<div>
|
||||
{dataList.map((item, index) => (
|
||||
<AccessItem data={item} key={index} onSelect={onSelect}></AccessItem>
|
||||
<AccessItem
|
||||
data={item}
|
||||
key={index}
|
||||
onSelect={onSelect}
|
||||
sourceModels={sourceModels}
|
||||
></AccessItem>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@ export interface FormData {
|
||||
description: string;
|
||||
categories: any[];
|
||||
meta: Record<string, any>;
|
||||
generic_proxy: boolean;
|
||||
fallback_endpoint: {
|
||||
provider_model_name?: string;
|
||||
model_id?: number;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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 useAppUtils from '@/hooks/use-app-utils';
|
||||
import CategorySelect from '@/pages/_components/category-select';
|
||||
import { categoryOptions } from '@/pages/llmodels/config';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
@@ -31,13 +32,13 @@ const Basic = () => {
|
||||
}
|
||||
]}
|
||||
>
|
||||
<SealSelect
|
||||
<CategorySelect
|
||||
required
|
||||
options={categoryOptions}
|
||||
label={intl.formatMessage({ id: 'models.form.categories' })}
|
||||
></SealSelect>
|
||||
></CategorySelect>
|
||||
</Form.Item>
|
||||
<Form.Item name="description">
|
||||
<Form.Item name="description" style={{ marginBottom: 8 }}>
|
||||
<SealInput.TextArea
|
||||
scaleSize={true}
|
||||
label={intl.formatMessage({
|
||||
@@ -45,6 +46,20 @@ const Basic = () => {
|
||||
})}
|
||||
></SealInput.TextArea>
|
||||
</Form.Item>
|
||||
<Form.Item<FormData>
|
||||
name="generic_proxy"
|
||||
valuePropName="checked"
|
||||
style={{ marginBottom: 8 }}
|
||||
>
|
||||
<CheckboxField
|
||||
description={intl.formatMessage({
|
||||
id: 'models.form.generic_proxy.tips'
|
||||
})}
|
||||
label={intl.formatMessage({
|
||||
id: 'models.form.generic_proxy'
|
||||
})}
|
||||
></CheckboxField>
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,9 +5,29 @@ import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { FormData } from '../config/types';
|
||||
import useEndpointSourceModels from '../hooks/use-endpoint-source-models';
|
||||
|
||||
const Inner = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
ul.ant-cascader-menu:first-child {
|
||||
li[data-path-key='deployments'] {
|
||||
position: relative;
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border-bottom: 1px solid var(--ant-color-split);
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const EndpointsForm = forwardRef((props, ref) => {
|
||||
const intl = useIntl();
|
||||
const { sourceModels, loading, fetchSourceModels } =
|
||||
@@ -164,6 +184,7 @@ const EndpointsForm = forwardRef((props, ref) => {
|
||||
showSearch
|
||||
expandTrigger="hover"
|
||||
multiple={false}
|
||||
alwaysFocus={true}
|
||||
onChange={(value, options) =>
|
||||
handleEndpointsChange(value, index, options)
|
||||
}
|
||||
@@ -174,7 +195,7 @@ const EndpointsForm = forwardRef((props, ref) => {
|
||||
}}
|
||||
maxTagCount={1}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'accesses.form.endpoint.model'
|
||||
id: 'providers.form.endpoint.placeholder'
|
||||
})}
|
||||
value={item.value}
|
||||
options={sourceModels}
|
||||
@@ -201,6 +222,7 @@ const EndpointsForm = forwardRef((props, ref) => {
|
||||
showSearch
|
||||
expandTrigger="hover"
|
||||
multiple={false}
|
||||
alwaysFocus={true}
|
||||
classNames={{
|
||||
popup: {
|
||||
root: 'cascader-popup-wrapper gpu-selector'
|
||||
@@ -209,6 +231,9 @@ const EndpointsForm = forwardRef((props, ref) => {
|
||||
label={intl.formatMessage({
|
||||
id: 'accesses.form.endpoint.fallback'
|
||||
})}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'providers.form.endpoint.placeholder'
|
||||
})}
|
||||
maxTagCount={1}
|
||||
value={fallbackValues.value}
|
||||
options={sourceModels}
|
||||
|
||||
@@ -18,7 +18,6 @@ import { FormData, AccessItem as ListItem } from '../config/types';
|
||||
import useEditEndpoints from '../hooks/use-edit-endpoints';
|
||||
import Basic from './basic';
|
||||
import Endpoints from './endpoints';
|
||||
import MetaData from './meta-data';
|
||||
|
||||
interface ProviderFormProps {
|
||||
ref?: any;
|
||||
@@ -50,12 +49,6 @@ const AccessForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
||||
icon: <IconFont type="icon-basic" />,
|
||||
field: 'name'
|
||||
},
|
||||
{
|
||||
value: TABKeysMap.METADATA,
|
||||
label: intl.formatMessage({ id: 'accesses.form.metadata.title' }),
|
||||
icon: <IconFont type="icon-database" />,
|
||||
field: 'metaSize'
|
||||
},
|
||||
{
|
||||
value: TABKeysMap.ENDPOINTS,
|
||||
label: intl.formatMessage({ id: 'accesses.form.endpoint.title' }),
|
||||
@@ -202,12 +195,6 @@ const AccessForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
||||
accordion={false}
|
||||
onChange={handleOnCollapseChange}
|
||||
items={[
|
||||
{
|
||||
key: TABKeysMap.METADATA,
|
||||
label: intl.formatMessage({ id: 'accesses.form.metadata.title' }),
|
||||
forceRender: true,
|
||||
children: <MetaData></MetaData>
|
||||
},
|
||||
{
|
||||
key: TABKeysMap.ENDPOINTS,
|
||||
label: intl.formatMessage({ id: 'accesses.form.endpoint.title' }),
|
||||
|
||||
+15
-1
@@ -2,6 +2,7 @@ import { queryModelsList } from '@/pages/llmodels/apis';
|
||||
import { ListItem as ModelListItem } from '@/pages/llmodels/config/types';
|
||||
import { queryMaasProviders } from '@/pages/maas-provider/apis';
|
||||
import { MaasProviderItem } from '@/pages/maas-provider/config/types';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { useState } from 'react';
|
||||
|
||||
type EmptyObject = Record<never, never>;
|
||||
@@ -15,6 +16,7 @@ type CascaderOption<T extends object = EmptyObject> = {
|
||||
} & Partial<T>;
|
||||
|
||||
const useEndpointSourceModels = () => {
|
||||
const intl = useIntl();
|
||||
const [sourceModels, setSourceModels] = useState<CascaderOption[]>([]);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
|
||||
@@ -28,7 +30,19 @@ const useEndpointSourceModels = () => {
|
||||
|
||||
const modelsList = [
|
||||
{
|
||||
label: 'Deployments',
|
||||
label: (
|
||||
<span>
|
||||
{intl.formatMessage({ id: 'menu.models.deployment' })}
|
||||
<span
|
||||
style={{
|
||||
color: 'var(--ant-color-text-tertiary)',
|
||||
marginLeft: 4
|
||||
}}
|
||||
>
|
||||
[GPUStack]
|
||||
</span>
|
||||
</span>
|
||||
),
|
||||
value: 'deployments',
|
||||
parent: true,
|
||||
children: models.items?.map?.((model: ModelListItem) => ({
|
||||
@@ -15,6 +15,7 @@ import { useMemoizedFn } from 'ahooks';
|
||||
import { message } from 'antd';
|
||||
import { useAtom } from 'jotai';
|
||||
import _ from 'lodash';
|
||||
import { useEffect } from 'react';
|
||||
import NoResult from '../_components/no-result';
|
||||
import PageBox from '../_components/page-box';
|
||||
import AccessControlModal from '../llmodels/components/access-control-modal';
|
||||
@@ -35,6 +36,7 @@ import { FormData, AccessItem as ListItem } from './config/types';
|
||||
import useAccessColumns from './hooks/use-access-columns';
|
||||
import useAccessControl from './hooks/use-access-control';
|
||||
import useCreateAccess from './hooks/use-create-access';
|
||||
import useEndpointSourceModels from './hooks/use-endpoint-source-models';
|
||||
|
||||
const Accesses: React.FC = () => {
|
||||
const {
|
||||
@@ -69,6 +71,7 @@ const Accesses: React.FC = () => {
|
||||
closeAccessControlModal,
|
||||
openAccessControlModalStatus
|
||||
} = useAccessControl();
|
||||
const { sourceModels, fetchSourceModels } = useEndpointSourceModels();
|
||||
|
||||
const handleClickDropdown = () => {
|
||||
openAccessModal(
|
||||
@@ -188,9 +191,19 @@ const Accesses: React.FC = () => {
|
||||
list: any,
|
||||
options: { parent?: any; [key: string]: any }
|
||||
) => {
|
||||
return <AccessPoints dataList={list} onSelect={onChildSelect} />;
|
||||
return (
|
||||
<AccessPoints
|
||||
dataList={list}
|
||||
onSelect={onChildSelect}
|
||||
sourceModels={sourceModels}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchSourceModels();
|
||||
}, []);
|
||||
|
||||
const columns = useAccessColumns(handleSelect);
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user