From 497813eced18b85b1f63e203d31664c507fe1e0e Mon Sep 17 00:00:00 2001 From: jialin Date: Mon, 26 Jan 2026 12:45:54 +0800 Subject: [PATCH] fix(styles): providers access token --- src/components/seal-form/seal-cascader.tsx | 8 ++- src/locales/en-US/models.ts | 2 + src/locales/en-US/provider.ts | 8 +-- src/locales/ja-JP/models.ts | 4 ++ src/locales/ja-JP/provider.ts | 8 +-- src/locales/ru-RU/models.ts | 4 ++ src/locales/ru-RU/provider.ts | 8 +-- src/locales/zh-CN/models.ts | 2 + src/locales/zh-CN/provider.ts | 8 +-- src/pages/_components/category-select.tsx | 30 +++++++++++ src/pages/llmodels/apis/index.ts | 5 +- .../components/access-control-modal/index.tsx | 16 ++---- src/pages/llmodels/config/index.ts | 36 +++---------- src/pages/llmodels/config/types.ts | 1 + src/pages/llmodels/forms/advance-config.tsx | 32 ++++++++++-- src/pages/maas-provider/config/types.ts | 1 + .../maas-provider/forms/access-token.tsx | 50 +++++++++++++++---- .../maas-provider/forms/advance-config.tsx | 2 + src/pages/maas-provider/forms/basic.tsx | 16 ++++++ src/pages/maas-provider/forms/index.tsx | 18 +++++-- src/pages/maas-provider/forms/model-item.tsx | 2 +- .../maas-provider/forms/supported-models.tsx | 4 +- .../model-access/components/access-points.tsx | 40 +++++++++++---- src/pages/model-access/config/types.ts | 1 + src/pages/model-access/forms/basic.tsx | 23 +++++++-- src/pages/model-access/forms/endpoints.tsx | 27 +++++++++- src/pages/model-access/forms/index.tsx | 13 ----- ...dels.ts => use-endpoint-source-models.tsx} | 16 +++++- src/pages/model-access/index.tsx | 15 +++++- 29 files changed, 294 insertions(+), 106 deletions(-) create mode 100644 src/pages/_components/category-select.tsx rename src/pages/model-access/hooks/{use-endpoint-source-models.ts => use-endpoint-source-models.tsx} (84%) diff --git a/src/components/seal-form/seal-cascader.tsx b/src/components/seal-form/seal-cascader.tsx index a6c4f9e9..3b2330a9 100644 --- a/src/components/seal-form/seal-cascader.tsx +++ b/src/components/seal-form/seal-cascader.tsx @@ -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} diff --git a/src/locales/en-US/models.ts b/src/locales/en-US/models.ts index b7002f7e..8fd7c2ef 100644 --- a/src/locales/en-US/models.ts +++ b/src/locales/en-US/models.ts @@ -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', diff --git a/src/locales/en-US/provider.ts b/src/locales/en-US/provider.ts index 62104134..5b53f4e1 100644 --- a/src/locales/en-US/provider.ts +++ b/src/locales/en-US/provider.ts @@ -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' }; diff --git a/src/locales/ja-JP/models.ts b/src/locales/ja-JP/models.ts index e1aab431..1adb80c6 100644 --- a/src/locales/ja-JP/models.ts +++ b/src/locales/ja-JP/models.ts @@ -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 ========== diff --git a/src/locales/ja-JP/provider.ts b/src/locales/ja-JP/provider.ts index 62104134..5b53f4e1 100644 --- a/src/locales/ja-JP/provider.ts +++ b/src/locales/ja-JP/provider.ts @@ -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' }; diff --git a/src/locales/ru-RU/models.ts b/src/locales/ru-RU/models.ts index 458c9fb4..538c026b 100644 --- a/src/locales/ru-RU/models.ts +++ b/src/locales/ru-RU/models.ts @@ -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 ========== diff --git a/src/locales/ru-RU/provider.ts b/src/locales/ru-RU/provider.ts index 62104134..5b53f4e1 100644 --- a/src/locales/ru-RU/provider.ts +++ b/src/locales/ru-RU/provider.ts @@ -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' }; diff --git a/src/locales/zh-CN/models.ts b/src/locales/zh-CN/models.ts index 1bd75379..203f08fb 100644 --- a/src/locales/zh-CN/models.ts +++ b/src/locales/zh-CN/models.ts @@ -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': '通用代理', diff --git a/src/locales/zh-CN/provider.ts b/src/locales/zh-CN/provider.ts index e6336b3c..31ea219f 100644 --- a/src/locales/zh-CN/provider.ts +++ b/src/locales/zh-CN/provider.ts @@ -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' }; diff --git a/src/pages/_components/category-select.tsx b/src/pages/_components/category-select.tsx new file mode 100644 index 00000000..d858331b --- /dev/null +++ b/src/pages/_components/category-select.tsx @@ -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 ( + + + {React.cloneElement(config.icon)} + + {option.label} + + ); + }; + return ( + + ); +}; + +export default CategorySelect; diff --git a/src/pages/llmodels/apis/index.ts b/src/pages/llmodels/apis/index.ts index e3a1866e..b4a52956 100644 --- a/src/pages/llmodels/apis/index.ts +++ b/src/pages/llmodels/apis/index.ts @@ -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 }); diff --git a/src/pages/llmodels/components/access-control-modal/index.tsx b/src/pages/llmodels/components/access-control-modal/index.tsx index 0f281dd8..849b208c 100644 --- a/src/pages/llmodels/components/access-control-modal/index.tsx +++ b/src/pages/llmodels/components/access-control-modal/index.tsx @@ -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 ( - +
{isChanged && ( )} - +
} > -
+ ); }; diff --git a/src/pages/llmodels/config/index.ts b/src/pages/llmodels/config/index.ts index 23198bd9..25d2d08d 100644 --- a/src/pages/llmodels/config/index.ts +++ b/src/pages/llmodels/config/index.ts @@ -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 } ]; diff --git a/src/pages/llmodels/config/types.ts b/src/pages/llmodels/config/types.ts index 3e102a34..f655fced 100644 --- a/src/pages/llmodels/config/types.ts +++ b/src/pages/llmodels/config/types.ts @@ -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; diff --git a/src/pages/llmodels/forms/advance-config.tsx b/src/pages/llmodels/forms/advance-config.tsx index 62ed79b7..90767564 100644 --- a/src/pages/llmodels/forms/advance-config.tsx +++ b/src/pages/llmodels/forms/advance-config.tsx @@ -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 = () => { > - name="generic_proxy" + name="enable_model_access" valuePropName="checked" style={{ marginBottom: 8 }} > + {modelAccessEnable && ( + + name="generic_proxy" + valuePropName="checked" + style={{ marginBottom: 8 }} + > + + + )} ); }; diff --git a/src/pages/maas-provider/config/types.ts b/src/pages/maas-provider/config/types.ts index da05eead..f5d70096 100644 --- a/src/pages/maas-provider/config/types.ts +++ b/src/pages/maas-provider/config/types.ts @@ -9,6 +9,7 @@ export interface FormData { description: string; api_tokens: string[]; models: ProviderModel[]; + api_key: string; config: { type: maasProviderType; [key: string]: any; diff --git a/src/pages/maas-provider/forms/access-token.tsx b/src/pages/maas-provider/forms/access-token.tsx index 25637e8d..3356686e 100644 --- a/src/pages/maas-provider/forms/access-token.tsx +++ b/src/pages/maas-provider/forms/access-token.tsx @@ -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(); 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 + ) => { + const newList = [...tokenList]; + newList[index] = e.target.value; + form.setFieldValue('api_tokens', newList); }; return ( <> { } ]} > - + btnText={intl.formatMessage({ id: 'providers.form.tokens.add' })} + label={intl.formatMessage({ id: 'providers.form.fallback.token' })} + onAdd={onAdd} + onDelete={onDelete} + > + {(item, index) => ( +
+ handleInputChange(index, e)} + > +
+ )} +
); diff --git a/src/pages/maas-provider/forms/advance-config.tsx b/src/pages/maas-provider/forms/advance-config.tsx index 3d8c993c..6dc27b09 100644 --- a/src/pages/maas-provider/forms/advance-config.tsx +++ b/src/pages/maas-provider/forms/advance-config.tsx @@ -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 ( <>
+ + + name="api_key" + rules={[ + { + required: true, + message: getRuleMessage('input', 'providers.form.tokens.title') + } + ]} + > + + name="description"> = 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 = 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 = forwardRef((props, ref) => {
= forwardRef((props, ref) => { }} > - = ({ 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']) || '' } diff --git a/src/pages/maas-provider/forms/supported-models.tsx b/src/pages/maas-provider/forms/supported-models.tsx index 607082fe..51291110 100644 --- a/src/pages/maas-provider/forms/supported-models.tsx +++ b/src/pages/maas-provider/forms/supported-models.tsx @@ -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']) || '' } diff --git a/src/pages/model-access/components/access-points.tsx b/src/pages/model-access/components/access-points.tsx index b1b889ba..2e545a1a 100644 --- a/src/pages/model-access/components/access-points.tsx +++ b/src/pages/model-access/components/access-points.tsx @@ -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 = ({ onSelect, data }) => { +const AccessItem: React.FC = ({ + 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 (
- + = ({ onSelect, data }) => { - - + {renderProviderSource()} @@ -88,7 +101,7 @@ const AccessItem: React.FC = ({ onSelect, data }) => { )} - + = ({ onSelect, data }) => { - + {dayjs(data.created_at).format('YYYY-MM-DD HH:mm:ss')} - + = ({ onSelect, data }) => { ); }; -const AccessPoints: React.FC = ({ dataList, onSelect }) => { +const AccessPoints: React.FC = ({ + dataList, + onSelect, + sourceModels +}) => { console.log('AccessPoints dataList:', dataList); return (
{dataList.map((item, index) => ( - + ))}
); diff --git a/src/pages/model-access/config/types.ts b/src/pages/model-access/config/types.ts index c33f7145..ed470e7d 100644 --- a/src/pages/model-access/config/types.ts +++ b/src/pages/model-access/config/types.ts @@ -3,6 +3,7 @@ export interface FormData { description: string; categories: any[]; meta: Record; + generic_proxy: boolean; fallback_endpoint: { provider_model_name?: string; model_id?: number; diff --git a/src/pages/model-access/forms/basic.tsx b/src/pages/model-access/forms/basic.tsx index 98ba32cf..ee1520b5 100644 --- a/src/pages/model-access/forms/basic.tsx +++ b/src/pages/model-access/forms/basic.tsx @@ -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 = () => { } ]} > - + > - + { })} > + + name="generic_proxy" + valuePropName="checked" + style={{ marginBottom: 8 }} + > + + ); }; diff --git a/src/pages/model-access/forms/endpoints.tsx b/src/pages/model-access/forms/endpoints.tsx index 3d5207d6..7bd94026 100644 --- a/src/pages/model-access/forms/endpoints.tsx +++ b/src/pages/model-access/forms/endpoints.tsx @@ -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} diff --git a/src/pages/model-access/forms/index.tsx b/src/pages/model-access/forms/index.tsx index 57684584..90d9b293 100644 --- a/src/pages/model-access/forms/index.tsx +++ b/src/pages/model-access/forms/index.tsx @@ -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 = forwardRef((props, ref) => { icon: , field: 'name' }, - { - value: TABKeysMap.METADATA, - label: intl.formatMessage({ id: 'accesses.form.metadata.title' }), - icon: , - field: 'metaSize' - }, { value: TABKeysMap.ENDPOINTS, label: intl.formatMessage({ id: 'accesses.form.endpoint.title' }), @@ -202,12 +195,6 @@ const AccessForm: React.FC = forwardRef((props, ref) => { accordion={false} onChange={handleOnCollapseChange} items={[ - { - key: TABKeysMap.METADATA, - label: intl.formatMessage({ id: 'accesses.form.metadata.title' }), - forceRender: true, - children: - }, { key: TABKeysMap.ENDPOINTS, label: intl.formatMessage({ id: 'accesses.form.endpoint.title' }), diff --git a/src/pages/model-access/hooks/use-endpoint-source-models.ts b/src/pages/model-access/hooks/use-endpoint-source-models.tsx similarity index 84% rename from src/pages/model-access/hooks/use-endpoint-source-models.ts rename to src/pages/model-access/hooks/use-endpoint-source-models.tsx index c995b958..b7691981 100644 --- a/src/pages/model-access/hooks/use-endpoint-source-models.ts +++ b/src/pages/model-access/hooks/use-endpoint-source-models.tsx @@ -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; @@ -15,6 +16,7 @@ type CascaderOption = { } & Partial; const useEndpointSourceModels = () => { + const intl = useIntl(); const [sourceModels, setSourceModels] = useState([]); const [loading, setLoading] = useState(false); @@ -28,7 +30,19 @@ const useEndpointSourceModels = () => { const modelsList = [ { - label: 'Deployments', + label: ( + + {intl.formatMessage({ id: 'menu.models.deployment' })} + + [GPUStack] + + + ), value: 'deployments', parent: true, children: models.items?.map?.((model: ModelListItem) => ({ diff --git a/src/pages/model-access/index.tsx b/src/pages/model-access/index.tsx index 09a312e1..997e91da 100644 --- a/src/pages/model-access/index.tsx +++ b/src/pages/model-access/index.tsx @@ -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 ; + return ( + + ); }; + useEffect(() => { + fetchSourceModels(); + }, []); + const columns = useAccessColumns(handleSelect); return (