chore: providers ux
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import IconFont from '@/components/icon-font';
|
||||
import {
|
||||
ApiOutlined,
|
||||
CopyOutlined,
|
||||
DeleteOutlined,
|
||||
DockerOutlined,
|
||||
DownloadOutlined,
|
||||
@@ -62,7 +63,8 @@ const icons = {
|
||||
CaptivePortal: React.createElement(IconFont, { type: 'icon-captive_portal' }),
|
||||
StarOutlined: React.createElement(StarOutlined),
|
||||
Charger: React.createElement(IconFont, { type: 'icon-charger' }),
|
||||
Disabled: React.createElement(IconFont, { type: 'icon-disabled' })
|
||||
Disabled: React.createElement(IconFont, { type: 'icon-disabled' }),
|
||||
CopyOutlined: React.createElement(CopyOutlined)
|
||||
};
|
||||
|
||||
export default icons;
|
||||
|
||||
@@ -46,6 +46,9 @@ export default {
|
||||
'noresult.provider.title': 'No Providers',
|
||||
'noresult.provider.subTitle': 'No providers have been added yet.',
|
||||
'noresult.provider.nofound': 'No matching providers found.',
|
||||
'noresult.providers.title': 'No Providers',
|
||||
'noresult.providers.subTitle': 'No providers have been added yet.',
|
||||
'noresult.providers.nofound': 'No matching providers found.',
|
||||
'noresult.accesses.title': 'No Accesses',
|
||||
'noresult.accesses.subTitle': 'No accesses have been added yet.',
|
||||
'noresult.accesses.nofound': 'No matching accesses found.'
|
||||
|
||||
@@ -2,5 +2,6 @@ export default {
|
||||
'providers.title': 'Providers',
|
||||
'providers.button.add': 'Add Provider',
|
||||
'providers.table.providerName': 'Provider',
|
||||
'providers.table.modelsCounts': 'Models Count'
|
||||
'providers.table.modelsCounts': 'Models Count',
|
||||
'providers.table.proxy': 'Proxy'
|
||||
};
|
||||
|
||||
@@ -46,6 +46,9 @@ export default {
|
||||
'noresult.provider.title': 'No Providers',
|
||||
'noresult.provider.subTitle': 'No providers have been added yet.',
|
||||
'noresult.provider.nofound': 'No matching providers found.',
|
||||
'noresult.providers.title': 'No Providers',
|
||||
'noresult.providers.subTitle': 'No providers have been added yet.',
|
||||
'noresult.providers.nofound': 'No matching providers found.',
|
||||
'noresult.accesses.title': 'No Accesses',
|
||||
'noresult.accesses.subTitle': 'No accesses have been added yet.',
|
||||
'noresult.accesses.nofound': 'No matching accesses found.'
|
||||
|
||||
@@ -2,5 +2,6 @@ export default {
|
||||
'providers.title': 'Providers',
|
||||
'providers.button.add': 'Add Provider',
|
||||
'providers.table.providerName': 'Provider',
|
||||
'providers.table.modelsCounts': 'Models Count'
|
||||
'providers.table.modelsCounts': 'Models Count',
|
||||
'providers.table.proxy': 'Proxy'
|
||||
};
|
||||
|
||||
@@ -47,6 +47,9 @@ export default {
|
||||
'noresult.provider.title': 'No Providers',
|
||||
'noresult.provider.subTitle': 'No providers have been added yet.',
|
||||
'noresult.provider.nofound': 'No matching providers found.',
|
||||
'noresult.providers.title': 'No Providers',
|
||||
'noresult.providers.subTitle': 'No providers have been added yet.',
|
||||
'noresult.providers.nofound': 'No matching providers found.',
|
||||
'noresult.accesses.title': 'No Accesses',
|
||||
'noresult.accesses.subTitle': 'No accesses have been added yet.',
|
||||
'noresult.accesses.nofound': 'No matching accesses found.'
|
||||
@@ -63,6 +66,9 @@ export default {
|
||||
// 5. 'noresult.provider.title': 'No Providers',
|
||||
// 6. 'noresult.provider.subTitle': 'No providers have been added yet.',
|
||||
// 7. 'noresult.provider.nofound': 'No matching providers found.',
|
||||
// 5. 'noresult.providers.title': 'No Providers',
|
||||
// 6. 'noresult.providers.subTitle': 'No providers have been added yet.',
|
||||
// 7. 'noresult.providers.nofound': 'No matching providers found.',
|
||||
// 8. 'noresult.accesses.title': 'No Accesses',
|
||||
// 9. 'noresult.accesses.subTitle': 'No accesses have been added yet.',
|
||||
// 10. 'noresult.accesses.nofound': 'No matching accesses found.'
|
||||
|
||||
@@ -2,5 +2,6 @@ export default {
|
||||
'providers.title': 'Providers',
|
||||
'providers.button.add': 'Add Provider',
|
||||
'providers.table.providerName': 'Provider',
|
||||
'providers.table.modelsCounts': 'Models Count'
|
||||
'providers.table.modelsCounts': 'Models Count',
|
||||
'providers.table.proxy': 'Proxy'
|
||||
};
|
||||
|
||||
@@ -15,8 +15,7 @@ export default {
|
||||
'menu.models.userModels': '我的模型',
|
||||
'menu.models.benchmark': '基准测试',
|
||||
'menu.models.benchmarkDetail': '基准测试详情',
|
||||
'menu.models.provider': '渠道',
|
||||
'menu.models.providers': '渠道',
|
||||
'menu.models.providers': '提供商',
|
||||
'menu.models.accesses': '接入',
|
||||
'menu.modelCatalog': '模型库',
|
||||
'menu.models.catalog': '模型库',
|
||||
|
||||
@@ -41,9 +41,9 @@ export default {
|
||||
'noresult.benchmark.title': '暂无基准测试',
|
||||
'noresult.benchmark.subTitle': '尚未添加任何基准测试。',
|
||||
'noresult.benchmark.nofound': '未找到匹配的基准测试',
|
||||
'noresult.provider.title': '暂无渠道',
|
||||
'noresult.provider.subTitle': '尚未添加任何渠道。',
|
||||
'noresult.provider.nofound': '未找到匹配的渠道',
|
||||
'noresult.providers.title': '暂无提供商',
|
||||
'noresult.providers.subTitle': '尚未添加任何提供商。',
|
||||
'noresult.providers.nofound': '未找到匹配的提供商',
|
||||
'noresult.accesses.title': '暂无接入',
|
||||
'noresult.accesses.subTitle': '尚未添加任何接入。',
|
||||
'noresult.accesses.nofound': '未找到匹配的接入'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export default {
|
||||
'providers.title': '渠道',
|
||||
'providers.button.add': '添加渠道',
|
||||
'providers.table.providerName': '渠道',
|
||||
'providers.table.modelsCounts': '模型数量'
|
||||
'providers.title': '提供商',
|
||||
'providers.button.add': '添加提供商',
|
||||
'providers.table.providerName': '提供商',
|
||||
'providers.table.modelsCounts': '模型数量',
|
||||
'providers.table.proxy': '代理'
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
WechatWorkOutlined
|
||||
} from '@ant-design/icons';
|
||||
import { Tag } from 'antd';
|
||||
import { modelCategoriesMap } from '../config';
|
||||
import { modelCategoriesMap } from '../llmodels/config';
|
||||
|
||||
export const categoryConfig = {
|
||||
[modelCategoriesMap.reranker]: {
|
||||
@@ -53,7 +53,7 @@ const ModelTag: React.FC<ModelTagProps> = ({ categoryKey, size }) => {
|
||||
return (
|
||||
<Tag
|
||||
icon={config.icon}
|
||||
variant="outlined"
|
||||
variant={'outlined'}
|
||||
style={{
|
||||
height: size,
|
||||
margin: 0,
|
||||
@@ -7,10 +7,10 @@ import { Typography } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import _ from 'lodash';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { categoryConfig } from '../../_components/model-tag';
|
||||
import { modelCategories } from '../config';
|
||||
import { CatalogItem as CatalogItemType } from '../config/types';
|
||||
import '../style/catalog-item.less';
|
||||
import { categoryConfig } from './model-tag';
|
||||
|
||||
interface CatalogItemProps {
|
||||
activeId: number;
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Button } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import React, { useMemo } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { categoryConfig } from '../../_components/model-tag';
|
||||
import {
|
||||
modelCategories,
|
||||
modelCategoriesMap,
|
||||
@@ -17,7 +18,6 @@ import {
|
||||
MyModelsStatusValueMap
|
||||
} from '../config';
|
||||
import { categoryToPathMap } from '../config/button-actions';
|
||||
import { categoryConfig } from './model-tag';
|
||||
|
||||
const CardWrapper = styled.div`
|
||||
&:hover {
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Tooltip } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import _ from 'lodash';
|
||||
import { useMemo } from 'react';
|
||||
import ModelTag from '../components/model-tag';
|
||||
import ModelTag from '../../_components/model-tag';
|
||||
import { ActionList, generateSource } from '../config/button-actions';
|
||||
import { ListItem } from '../config/types';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { categoryOptions, modelCategoriesMap } from '@/pages/llmodels/config';
|
||||
import { modelCategoriesMap } from '@/pages/llmodels/config';
|
||||
import {
|
||||
CheckCircleOutlined,
|
||||
CloseCircleOutlined,
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
} from '@ant-design/icons';
|
||||
import { Flex, Tag } from 'antd';
|
||||
import React from 'react';
|
||||
import { categoryConfig } from '../../_components/model-tag';
|
||||
|
||||
interface ProviderModelProps {
|
||||
dataList: any[];
|
||||
@@ -61,8 +62,10 @@ const ProviderModels: React.FC<ProviderModelProps> = () => {
|
||||
: 'gray'
|
||||
}
|
||||
>
|
||||
{model.name} (
|
||||
{categoryOptions.find((item) => item.value === model.type)?.label})
|
||||
{model.name}
|
||||
<span style={{ marginLeft: 8 }}>
|
||||
{categoryConfig[model.type]?.icon}
|
||||
</span>
|
||||
</Tag>
|
||||
))}
|
||||
</Flex>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { PageActionType } from '@/config/types';
|
||||
import { createContext, useContext } from 'react';
|
||||
|
||||
interface FormContextProps {
|
||||
providerType?: string;
|
||||
action?: PageActionType;
|
||||
}
|
||||
|
||||
const FormContext = createContext<FormContextProps>({});
|
||||
|
||||
export const useFormContext = () => {
|
||||
const context = useContext(FormContext);
|
||||
if (!context) {
|
||||
throw new Error('useFormContext must be used within a FormProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
export default FormContext;
|
||||
@@ -44,8 +44,8 @@ export const maasProviderOptions = Object.entries(maasProviderValueMap).map(
|
||||
);
|
||||
|
||||
export const ProviderStatusValueMap: Record<string, string> = {
|
||||
Ready: 'Ready',
|
||||
InActive: 'Inactive'
|
||||
Ready: 'ready',
|
||||
InActive: 'inactive'
|
||||
};
|
||||
|
||||
export const ProviderStatusLabelMap = {
|
||||
@@ -65,6 +65,11 @@ export const rowActionList = [
|
||||
label: 'common.button.edit',
|
||||
icon: icons.EditOutlined
|
||||
},
|
||||
{
|
||||
key: 'copy',
|
||||
label: 'common.button.copy',
|
||||
icon: icons.CopyOutlined
|
||||
},
|
||||
{
|
||||
key: 'delete',
|
||||
label: 'common.button.delete',
|
||||
|
||||
@@ -9,23 +9,26 @@ export const mockDataList: MaasProviderItem[] = [
|
||||
models: 5,
|
||||
state_message: '',
|
||||
provider: 'doubao',
|
||||
proxy_config: {},
|
||||
builtIn: false
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
models: 3,
|
||||
name: 'Qwen',
|
||||
state: 'Inactive',
|
||||
state: 'inactive',
|
||||
state_message: 'Provider is inactive',
|
||||
provider: 'qwen',
|
||||
proxy_config: false,
|
||||
builtIn: true
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
models: 8,
|
||||
name: 'OpenAI',
|
||||
state: 'Ready',
|
||||
state: 'ready',
|
||||
state_message: '',
|
||||
proxy_config: null,
|
||||
provider: 'openai',
|
||||
builtIn: false
|
||||
},
|
||||
@@ -33,18 +36,20 @@ export const mockDataList: MaasProviderItem[] = [
|
||||
id: 4,
|
||||
models: 2,
|
||||
name: 'Deepseek',
|
||||
state: 'Ready',
|
||||
state: 'ready',
|
||||
state_message: '',
|
||||
provider: 'deepseek',
|
||||
proxy_config: null,
|
||||
builtIn: false
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
models: 4,
|
||||
name: 'Anthropic',
|
||||
state: 'Inactive',
|
||||
state: 'inactive',
|
||||
state_message: 'Provider is inactive',
|
||||
provider: 'anthropic',
|
||||
proxy_config: {},
|
||||
builtIn: true
|
||||
}
|
||||
];
|
||||
|
||||
@@ -7,6 +7,7 @@ export interface MaasProviderItem {
|
||||
state: string;
|
||||
state_message: string;
|
||||
provider: maasProviderType;
|
||||
proxy_config?: any;
|
||||
builtIn: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,13 +5,14 @@ import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import React, { forwardRef, useImperativeHandle } from 'react';
|
||||
import { maasProviderType } from '../config';
|
||||
import ProxyConfig from './proxy_config';
|
||||
|
||||
const AdvanceConfig: React.FC<{
|
||||
action: PageActionType;
|
||||
provider?: maasProviderType;
|
||||
ref?: any;
|
||||
}> = forwardRef(({ action, provider }, ref) => {
|
||||
const [form] = Form.useForm();
|
||||
const form = Form.useFormInstance();
|
||||
const intl = useIntl();
|
||||
const editorRef = React.useRef<any>(null);
|
||||
const [fileContent, setFileContent] = React.useState<string>('');
|
||||
@@ -28,7 +29,8 @@ const AdvanceConfig: React.FC<{
|
||||
|
||||
return (
|
||||
<>
|
||||
<div data-field="customConfig"></div>
|
||||
<div data-field="advanceConfig"></div>
|
||||
<ProxyConfig></ProxyConfig>
|
||||
<Form.Item
|
||||
hidden
|
||||
name="custom_config"
|
||||
@@ -43,14 +45,13 @@ const AdvanceConfig: React.FC<{
|
||||
</Form.Item>
|
||||
<YamlEditor
|
||||
ref={editorRef}
|
||||
title={'Yaml'}
|
||||
title={'Custom Config (YAML)'}
|
||||
value={fileContent}
|
||||
height={300}
|
||||
onUpload={(content) => {
|
||||
setFileContent(content);
|
||||
}}
|
||||
></YamlEditor>
|
||||
<div className="scroller-to-holder" style={{ height: 1 }}></div>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
import IconFont from '@/components/icon-font';
|
||||
import { PageAction } from '@/config';
|
||||
import { PageActionType } from '@/config/types';
|
||||
import CollapsePanel from '@/pages/_components/collapse-panel';
|
||||
import { useWrapperContext } from '@/pages/_components/column-wrapper/use-wrapper-context';
|
||||
import ScrollSpyTabs from '@/pages/_components/scroll-spy-tabs';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import { forwardRef, useImperativeHandle, useRef, useState } from 'react';
|
||||
import {
|
||||
forwardRef,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useRef,
|
||||
useState
|
||||
} from 'react';
|
||||
import { maasProviderType } from '../config';
|
||||
import FormContext from '../config/form-context';
|
||||
import { MaasProviderItem as ListItem } from '../config/types';
|
||||
import AccessToken from './access-token';
|
||||
import AdvanceConfig from './advance-config';
|
||||
@@ -31,9 +39,9 @@ const TABKeysMap = {
|
||||
const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
||||
const { action, provider, currentData, onFinish } = props;
|
||||
const intl = useIntl();
|
||||
const [form] = Form.useForm();
|
||||
const { getScrollElementScrollableHeight } = useWrapperContext();
|
||||
const [activeKey, setActiveKey] = useState<string[]>([TABKeysMap.BASIC]);
|
||||
const [form] = Form.useForm();
|
||||
const scrollTabsRef = useRef<any>(null);
|
||||
|
||||
const segmentOptions = [
|
||||
@@ -50,17 +58,11 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
||||
field: 'supportedModels'
|
||||
},
|
||||
{
|
||||
value: TABKeysMap.CUSTOMCONFIG,
|
||||
label: 'Custom Config',
|
||||
value: TABKeysMap.ADVANCED,
|
||||
label: intl.formatMessage({ id: 'resources.form.advanced' }),
|
||||
icon: <IconFont type="icon-settings" />,
|
||||
field: 'customConfig'
|
||||
field: 'advanceConfig'
|
||||
}
|
||||
// {
|
||||
// value: TABKeysMap.ADVANCED,
|
||||
// label: intl.formatMessage({ id: 'resources.form.advanced' }),
|
||||
// icon: <IconFont type="icon-settings" />,
|
||||
// field: 'categories'
|
||||
// }
|
||||
];
|
||||
|
||||
const handleActiveChange = (key: string[]) => {
|
||||
@@ -80,6 +82,15 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
||||
}
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
if (action === PageAction.EDIT && currentData) {
|
||||
form.setFieldsValue({
|
||||
...currentData,
|
||||
models: currentData.models || []
|
||||
});
|
||||
}
|
||||
}, [form, currentData, action]);
|
||||
|
||||
return (
|
||||
<ScrollSpyTabs
|
||||
ref={scrollTabsRef}
|
||||
@@ -93,34 +104,46 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
||||
}}
|
||||
getScrollElementScrollableHeight={getScrollElementScrollableHeight}
|
||||
>
|
||||
<Form form={form}>
|
||||
<Basic />
|
||||
<AccessToken />
|
||||
<CollapsePanel
|
||||
activeKey={activeKey}
|
||||
accordion={false}
|
||||
onChange={handleOnCollapseChange}
|
||||
items={[
|
||||
{
|
||||
key: TABKeysMap.SUPPORTEDMODELS,
|
||||
label: 'Supported Models',
|
||||
forceRender: true,
|
||||
children: <SupportedModels></SupportedModels>
|
||||
},
|
||||
{
|
||||
key: TABKeysMap.CUSTOMCONFIG,
|
||||
label: 'Custom Config',
|
||||
forceRender: true,
|
||||
children: (
|
||||
<AdvanceConfig
|
||||
action={action}
|
||||
provider={provider}
|
||||
></AdvanceConfig>
|
||||
)
|
||||
<FormContext.Provider value={{ providerType: provider, action }}>
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={onFinish}
|
||||
initialValues={{
|
||||
proxy_config: {
|
||||
enabled: false,
|
||||
url: '',
|
||||
timeout: 30
|
||||
}
|
||||
]}
|
||||
></CollapsePanel>
|
||||
</Form>
|
||||
}}
|
||||
>
|
||||
<Basic />
|
||||
<AccessToken />
|
||||
<CollapsePanel
|
||||
activeKey={activeKey}
|
||||
accordion={false}
|
||||
onChange={handleOnCollapseChange}
|
||||
items={[
|
||||
{
|
||||
key: TABKeysMap.SUPPORTEDMODELS,
|
||||
label: 'Supported Models',
|
||||
forceRender: true,
|
||||
children: <SupportedModels></SupportedModels>
|
||||
},
|
||||
{
|
||||
key: TABKeysMap.ADVANCED,
|
||||
label: 'Advanced',
|
||||
forceRender: true,
|
||||
children: (
|
||||
<AdvanceConfig
|
||||
action={action}
|
||||
provider={provider}
|
||||
></AdvanceConfig>
|
||||
)
|
||||
}
|
||||
]}
|
||||
></CollapsePanel>
|
||||
</Form>
|
||||
</FormContext.Provider>
|
||||
</ScrollSpyTabs>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import CheckboxField from '@/components/seal-form/checkbox-field';
|
||||
import SealInput from '@/components/seal-form/seal-input';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import React from 'react';
|
||||
import { useFormContext } from '../config/form-context';
|
||||
|
||||
const AdvanceConfig = () => {
|
||||
const form = Form.useFormInstance();
|
||||
const intl = useIntl();
|
||||
const { action } = useFormContext();
|
||||
const proxyConfigRef = React.useRef<any>({});
|
||||
const proxyConfigEnabled = Form.useWatch(['proxy_config', 'enabled'], form);
|
||||
|
||||
const handleSpeculativeEnabledChange = (e: any) => {
|
||||
if (e.target.checked) {
|
||||
form.setFieldValue('speculative_config', {
|
||||
enabled: true,
|
||||
url: proxyConfigRef.current?.url || '',
|
||||
timeout: proxyConfigRef.current?.timeout || 30
|
||||
});
|
||||
} else {
|
||||
proxyConfigRef.current = form.getFieldValue('proxy_config');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
name={['proxy_config', 'enabled']}
|
||||
valuePropName="checked"
|
||||
style={{ marginBottom: 8 }}
|
||||
>
|
||||
<CheckboxField
|
||||
label={'Enable Proxy'}
|
||||
onChange={handleSpeculativeEnabledChange}
|
||||
></CheckboxField>
|
||||
</Form.Item>
|
||||
{proxyConfigEnabled && (
|
||||
<>
|
||||
<Form.Item name={['proxy_config', 'url']}>
|
||||
<SealInput.Input
|
||||
label={'Proxy URL'}
|
||||
placeholder="http://proxy.example.com:8080"
|
||||
></SealInput.Input>
|
||||
</Form.Item>
|
||||
<Form.Item name={['proxy_config', 'timeout']}>
|
||||
<SealInput.Number
|
||||
label={'Proxy Timeout'}
|
||||
placeholder="30"
|
||||
></SealInput.Number>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AdvanceConfig;
|
||||
@@ -22,6 +22,13 @@ const SupportedModels = () => {
|
||||
const intl = useIntl();
|
||||
const form = Form.useFormInstance<FormData>();
|
||||
|
||||
const handleTestModel = (data: any) => {
|
||||
data.loading = true;
|
||||
setTimeout(() => {
|
||||
data.loading = false;
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const renderModelItem = (
|
||||
data: any,
|
||||
{
|
||||
@@ -32,6 +39,11 @@ const SupportedModels = () => {
|
||||
return (
|
||||
<SelectWrapper>
|
||||
<SealSelect
|
||||
suffixIcon={
|
||||
<CheckCircleFilled
|
||||
style={{ color: 'var(--ant-color-success)', fontSize: 16 }}
|
||||
/>
|
||||
}
|
||||
alwaysFocus={true}
|
||||
value={data.value}
|
||||
onChange={onChange}
|
||||
@@ -42,12 +54,12 @@ const SupportedModels = () => {
|
||||
{ label: 'Model C', value: 'model_c' }
|
||||
]}
|
||||
/>
|
||||
<span className="icon-wrapper">
|
||||
<CheckCircleFilled
|
||||
style={{ color: 'var(--ant-color-success)', fontSize: 16 }}
|
||||
/>
|
||||
</span>
|
||||
<Button type="link" size="small">
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
loading={data.loading}
|
||||
onClick={() => handleTestModel(data)}
|
||||
>
|
||||
Test
|
||||
</Button>
|
||||
</SelectWrapper>
|
||||
|
||||
@@ -19,11 +19,11 @@ const useCreateProvider = (options: { refresh: () => void }) => {
|
||||
provider: null
|
||||
});
|
||||
|
||||
const openModal = (action: PageActionType, row?: ListItem) => {
|
||||
const openModal = (action: PageActionType, title: string, row?: ListItem) => {
|
||||
setOpenModalStatus({
|
||||
...openModalStatus,
|
||||
open: true,
|
||||
title: action === PageAction.CREATE ? 'Create Provider' : 'Edit Provider',
|
||||
title: title,
|
||||
action,
|
||||
currentData: row,
|
||||
provider: row ? row.provider : null
|
||||
@@ -31,8 +31,13 @@ const useCreateProvider = (options: { refresh: () => void }) => {
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
setOpenModalStatus({ ...openModalStatus, open: false });
|
||||
options.refresh();
|
||||
setOpenModalStatus({
|
||||
open: false,
|
||||
action: PageAction.CREATE,
|
||||
currentData: undefined,
|
||||
title: '',
|
||||
provider: null
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
maasProviderLabelMap,
|
||||
providerIconsMap,
|
||||
ProviderStatus,
|
||||
ProviderStatusLabelMap,
|
||||
rowActionList
|
||||
} from '../config';
|
||||
import { MaasProviderItem } from '../config/types';
|
||||
@@ -69,15 +68,21 @@ const useProviderColumns = (
|
||||
render: (value: number) => <span>{value}</span>
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'common.table.status' }),
|
||||
dataIndex: 'state',
|
||||
span: 4,
|
||||
title: intl.formatMessage({ id: 'providers.table.proxy' }),
|
||||
dataIndex: 'proxy_config',
|
||||
span: 3,
|
||||
render: (value: number) => <span>{value ? 'Configured' : 'None'}</span>
|
||||
},
|
||||
{
|
||||
title: 'Token Settings',
|
||||
dataIndex: 'tokens',
|
||||
span: 3,
|
||||
render: (value: string, record: MaasProviderItem) => (
|
||||
<StatusTag
|
||||
statusValue={{
|
||||
status: ProviderStatus[value],
|
||||
text: ProviderStatusLabelMap[value],
|
||||
message: record.state_message || undefined
|
||||
status: ProviderStatus['ready'],
|
||||
text: 'Configured',
|
||||
message: ''
|
||||
}}
|
||||
/>
|
||||
)
|
||||
@@ -86,7 +91,7 @@ const useProviderColumns = (
|
||||
title: intl.formatMessage({ id: 'common.table.createTime' }),
|
||||
dataIndex: 'created_at',
|
||||
sorter: tableSorter(6),
|
||||
span: 4,
|
||||
span: 3,
|
||||
render: (value: string) => (
|
||||
<AutoTooltip ghost minWidth={20}>
|
||||
{dayjs(value).format('YYYY-MM-DD HH:mm:ss')}
|
||||
|
||||
@@ -62,17 +62,16 @@ const MaasProvider: React.FC = () => {
|
||||
const { handleExpandChange, handleExpandAll, expandedRowKeys } =
|
||||
useExpandedRowKeys(expandAtom);
|
||||
const intl = useIntl();
|
||||
const {
|
||||
openProviderModalStatus,
|
||||
setOpenProviderModalStatus,
|
||||
openProviderModal,
|
||||
closeProviderModal
|
||||
} = useCreateProvider({
|
||||
refresh: handleSearch
|
||||
});
|
||||
const { openProviderModalStatus, openProviderModal, closeProviderModal } =
|
||||
useCreateProvider({
|
||||
refresh: handleSearch
|
||||
});
|
||||
|
||||
const handleClickDropdown = () => {
|
||||
openProviderModal('create');
|
||||
openProviderModal(
|
||||
'create',
|
||||
intl.formatMessage({ id: 'providers.button.add' })
|
||||
);
|
||||
};
|
||||
|
||||
const handleModalOk = async (data: FormData) => {
|
||||
@@ -87,39 +86,25 @@ const MaasProvider: React.FC = () => {
|
||||
});
|
||||
}
|
||||
fetchData();
|
||||
setOpenProviderModalStatus({
|
||||
open: false,
|
||||
action: PageAction.CREATE,
|
||||
currentData: undefined,
|
||||
title: '',
|
||||
provider: null
|
||||
});
|
||||
closeProviderModal();
|
||||
message.success(intl.formatMessage({ id: 'common.message.success' }));
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
const handleModalCancel = () => {
|
||||
console.log('handleModalCancel');
|
||||
setOpenProviderModalStatus({
|
||||
open: false,
|
||||
action: PageAction.CREATE,
|
||||
currentData: undefined,
|
||||
title: '',
|
||||
provider: null
|
||||
});
|
||||
closeProviderModal();
|
||||
};
|
||||
|
||||
const handleEditProvider = (row: ListItem) => {
|
||||
setOpenProviderModalStatus({
|
||||
open: true,
|
||||
action: PageAction.EDIT,
|
||||
currentData: row,
|
||||
title: intl.formatMessage(
|
||||
openProviderModal(
|
||||
PageAction.EDIT,
|
||||
intl.formatMessage(
|
||||
{ id: 'clusters.edit.cluster' },
|
||||
{ cluster: row.name }
|
||||
),
|
||||
provider: row.provider
|
||||
});
|
||||
row
|
||||
);
|
||||
};
|
||||
|
||||
const handleSelect = useMemoizedFn((val: any, row: ListItem) => {
|
||||
@@ -128,6 +113,9 @@ const MaasProvider: React.FC = () => {
|
||||
} else if (val === 'delete') {
|
||||
handleDelete({ ...row, name: row.name });
|
||||
}
|
||||
if (val === 'copy') {
|
||||
openProviderModal(PageAction.EDIT, 'Copy Provider', row);
|
||||
}
|
||||
});
|
||||
|
||||
const handleOnToggleExpandAll = () => {
|
||||
@@ -226,11 +214,11 @@ const MaasProvider: React.FC = () => {
|
||||
image={<IconFont type="icon-extension-outline" />}
|
||||
filters={_.omit(queryParams, ['sort_by'])}
|
||||
noFoundText={intl.formatMessage({
|
||||
id: 'noresult.provider.nofound'
|
||||
id: 'noresult.providers.nofound'
|
||||
})}
|
||||
title={intl.formatMessage({ id: 'noresult.provider.title' })}
|
||||
title={intl.formatMessage({ id: 'noresult.providers.title' })}
|
||||
subTitle={intl.formatMessage({
|
||||
id: 'noresult.provider.subTitle'
|
||||
id: 'noresult.providers.subTitle'
|
||||
})}
|
||||
onClick={handleClickDropdown}
|
||||
buttonText={intl.formatMessage({ id: 'noresult.button.add' })}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import SealInput from '@/components/seal-form/seal-input';
|
||||
import SealSelect from '@/components/seal-form/seal-select';
|
||||
import { categoryOptions } from '@/pages/llmodels/config';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import { FormData } from '../config/types';
|
||||
|
||||
const Basic = () => {
|
||||
const intl = useIntl();
|
||||
const form = Form.useFormInstance<FormData>();
|
||||
return (
|
||||
<>
|
||||
<Form.Item name="name" data-field="name">
|
||||
<SealInput.Input required label={'Model Name'} />
|
||||
</Form.Item>
|
||||
<Form.Item name="category">
|
||||
<SealSelect options={categoryOptions} label="Category"></SealSelect>
|
||||
</Form.Item>
|
||||
<Form.Item name="description">
|
||||
<SealInput.TextArea
|
||||
scaleSize={true}
|
||||
label={intl.formatMessage({
|
||||
id: 'common.table.description'
|
||||
})}
|
||||
></SealInput.TextArea>
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Basic;
|
||||
@@ -0,0 +1,38 @@
|
||||
import SealInput from '@/components/seal-form/seal-input';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import { FormData } from '../config/types';
|
||||
|
||||
const Endpoints = () => {
|
||||
const intl = useIntl();
|
||||
const form = Form.useFormInstance<FormData>();
|
||||
return (
|
||||
<>
|
||||
<Form.Item name="name" data-field="endpoints">
|
||||
<SealInput.Input
|
||||
required
|
||||
label={intl.formatMessage({
|
||||
id: 'common.table.name'
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="provider" hidden>
|
||||
<SealInput.Input
|
||||
label={intl.formatMessage({
|
||||
id: 'providers.table.providerName'
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="description">
|
||||
<SealInput.TextArea
|
||||
scaleSize={true}
|
||||
label={intl.formatMessage({
|
||||
id: 'common.table.description'
|
||||
})}
|
||||
></SealInput.TextArea>
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Endpoints;
|
||||
@@ -1,17 +1,68 @@
|
||||
import IconFont from '@/components/icon-font';
|
||||
import { PageActionType } from '@/config/types';
|
||||
import CollapsePanel from '@/pages/_components/collapse-panel';
|
||||
import { useWrapperContext } from '@/pages/_components/column-wrapper/use-wrapper-context';
|
||||
import ScrollSpyTabs from '@/pages/_components/scroll-spy-tabs';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import { forwardRef, useImperativeHandle } from 'react';
|
||||
import { MaasProviderItem as ListItem } from '../config/types';
|
||||
import { forwardRef, useImperativeHandle, useRef, useState } from 'react';
|
||||
import { maasProviderType } from '../config';
|
||||
import { AccessItem as ListItem } from '../config/types';
|
||||
import Basic from './basic';
|
||||
import Endpoints from './endpoints';
|
||||
import MetaData from './meta-data';
|
||||
|
||||
interface ProviderFormProps {
|
||||
ref?: any;
|
||||
action: PageActionType;
|
||||
provider?: maasProviderType;
|
||||
currentData?: ListItem; // Used when action is EDIT
|
||||
onFinish: (values: FormData) => void;
|
||||
}
|
||||
|
||||
const TABKeysMap = {
|
||||
BASIC: 'basic',
|
||||
METADATA: 'metadata',
|
||||
ENDPOINTS: 'endpoints',
|
||||
ADVANCED: 'advanced'
|
||||
};
|
||||
|
||||
const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
||||
const { action, provider, currentData, onFinish } = props;
|
||||
const intl = useIntl();
|
||||
const { getScrollElementScrollableHeight } = useWrapperContext();
|
||||
const [activeKey, setActiveKey] = useState<string[]>([TABKeysMap.BASIC]);
|
||||
const [form] = Form.useForm();
|
||||
const scrollTabsRef = useRef<any>(null);
|
||||
|
||||
const segmentOptions = [
|
||||
{
|
||||
value: TABKeysMap.BASIC,
|
||||
label: 'Basic',
|
||||
icon: <IconFont type="icon-basic" />,
|
||||
field: 'name'
|
||||
},
|
||||
{
|
||||
value: TABKeysMap.METADATA,
|
||||
label: 'Metadata',
|
||||
icon: <IconFont type="icon-speed" />,
|
||||
field: 'metadata'
|
||||
},
|
||||
{
|
||||
value: TABKeysMap.ENDPOINTS,
|
||||
label: 'Endpoints',
|
||||
icon: <IconFont type="icon-settings" />,
|
||||
field: 'endpoints'
|
||||
}
|
||||
];
|
||||
|
||||
const handleActiveChange = (key: string[]) => {
|
||||
setActiveKey(key);
|
||||
};
|
||||
|
||||
const handleOnCollapseChange = (keys: string | string[]) => {
|
||||
setActiveKey(Array.isArray(keys) ? keys : [keys]);
|
||||
};
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
submit: () => {
|
||||
@@ -23,9 +74,43 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
||||
}));
|
||||
|
||||
return (
|
||||
<Form form={form} layout="vertical">
|
||||
{/* Form fields go here */}
|
||||
</Form>
|
||||
<ScrollSpyTabs
|
||||
ref={scrollTabsRef}
|
||||
defaultTarget="basic"
|
||||
segmentOptions={segmentOptions}
|
||||
activeKey={activeKey}
|
||||
setActiveKey={handleActiveChange}
|
||||
segmentedTop={{
|
||||
top: 0,
|
||||
offsetTop: 96
|
||||
}}
|
||||
getScrollElementScrollableHeight={getScrollElementScrollableHeight}
|
||||
>
|
||||
<Form form={form}>
|
||||
<Basic />
|
||||
<CollapsePanel
|
||||
activeKey={activeKey}
|
||||
accordion={false}
|
||||
onChange={handleOnCollapseChange}
|
||||
items={[
|
||||
{
|
||||
key: TABKeysMap.METADATA,
|
||||
label: 'Metadata',
|
||||
forceRender: true,
|
||||
children: <MetaData></MetaData>
|
||||
},
|
||||
{
|
||||
key: TABKeysMap.ENDPOINTS,
|
||||
label: 'Endpoints',
|
||||
forceRender: true,
|
||||
children: (
|
||||
<Endpoints action={action} provider={provider}></Endpoints>
|
||||
)
|
||||
}
|
||||
]}
|
||||
></CollapsePanel>
|
||||
</Form>
|
||||
</ScrollSpyTabs>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import SealInput from '@/components/seal-form/seal-input';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import { FormData } from '../config/types';
|
||||
|
||||
const MetaData = () => {
|
||||
const intl = useIntl();
|
||||
const form = Form.useFormInstance<FormData>();
|
||||
return (
|
||||
<>
|
||||
<Form.Item name="name" data-field="metadata">
|
||||
<SealInput.Input
|
||||
required
|
||||
label={intl.formatMessage({
|
||||
id: 'common.table.name'
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="provider" hidden>
|
||||
<SealInput.Input
|
||||
label={intl.formatMessage({
|
||||
id: 'providers.table.providerName'
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="description">
|
||||
<SealInput.TextArea
|
||||
scaleSize={true}
|
||||
label={intl.formatMessage({
|
||||
id: 'common.table.description'
|
||||
})}
|
||||
></SealInput.TextArea>
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MetaData;
|
||||
@@ -19,8 +19,14 @@ const useCreateAccess = (options: { refresh: () => void }) => {
|
||||
provider: null
|
||||
});
|
||||
|
||||
const openModal = () => {
|
||||
setOpenModalStatus({ ...openModalStatus, open: true });
|
||||
const openModal = (action: PageActionType, currentData?: ListItem) => {
|
||||
setOpenModalStatus({
|
||||
...openModalStatus,
|
||||
open: true,
|
||||
action,
|
||||
currentData,
|
||||
title: action === PageAction.CREATE ? 'Create Access' : 'Edit Access'
|
||||
});
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
|
||||
@@ -67,7 +67,7 @@ const Accesses: React.FC = () => {
|
||||
});
|
||||
|
||||
const handleClickDropdown = () => {
|
||||
openAccessModal();
|
||||
openAccessModal('create');
|
||||
};
|
||||
|
||||
const handleModalOk = async (data: FormData) => {
|
||||
|
||||
Reference in New Issue
Block a user