265 lines
7.3 KiB
TypeScript
265 lines
7.3 KiB
TypeScript
import { PageAction } from '@/config';
|
|
import { PageActionType } from '@/config/types';
|
|
import { json2Yaml, yaml2Json } from '@/pages/backends/config';
|
|
import {
|
|
CollapsePanel,
|
|
IconFont,
|
|
ScrollSpyTabs,
|
|
useFinishFailed,
|
|
useScrollActiveChange,
|
|
useWrapperContext
|
|
} from '@gpustack/core-ui';
|
|
import { useIntl } from '@umijs/max';
|
|
import { Form } from 'antd';
|
|
import _ from 'lodash';
|
|
import {
|
|
forwardRef,
|
|
useEffect,
|
|
useImperativeHandle,
|
|
useMemo,
|
|
useRef
|
|
} from 'react';
|
|
import FormContext from '../config/form-context';
|
|
import { FormData, MaasProviderItem as ListItem } from '../config/types';
|
|
import useProviderRequiredFields from '../hooks/use-provider-required-fields';
|
|
import AdvanceConfig from './advance-config';
|
|
import Basic from './basic';
|
|
import SupportedModels from './supported-models';
|
|
|
|
interface ProviderFormProps {
|
|
ref?: any;
|
|
action: PageActionType;
|
|
currentData?: ListItem; // Used when action is EDIT
|
|
onFinish: (values: FormData) => Promise<void>;
|
|
onFinishFailed?: (errorInfo: any) => void;
|
|
}
|
|
|
|
const TABKeysMap = {
|
|
BASIC: 'basic',
|
|
SUPPORTEDMODELS: 'supportedModels',
|
|
ADVANCED: 'advanced'
|
|
};
|
|
|
|
const requiredFields = {
|
|
[TABKeysMap.BASIC]: {
|
|
sort: 1,
|
|
fields: ['name', 'config', 'api_key']
|
|
},
|
|
[TABKeysMap.SUPPORTEDMODELS]: {
|
|
sort: 2,
|
|
fields: ['models']
|
|
}
|
|
};
|
|
|
|
const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
|
|
const { action, currentData, onFinish, onFinishFailed } = props;
|
|
const intl = useIntl();
|
|
const providerRequiredFieldsMap = useProviderRequiredFields();
|
|
const [form] = Form.useForm();
|
|
const { getScrollElementScrollableHeight } = useWrapperContext();
|
|
const configType = Form.useWatch(['config', 'type'], form);
|
|
const scrollTabsRef = useRef<any>(null);
|
|
const advanceRef = useRef<any>(null);
|
|
const {
|
|
activeKey,
|
|
collapseKeys,
|
|
handleActiveChange,
|
|
handleOnCollapseChange,
|
|
updateActiveKey
|
|
} = useScrollActiveChange({
|
|
initalActiveKeys: [TABKeysMap.BASIC],
|
|
initialCollapseKeys: [TABKeysMap.SUPPORTEDMODELS]
|
|
});
|
|
|
|
const segmentOptions = [
|
|
{
|
|
value: TABKeysMap.BASIC,
|
|
label: intl.formatMessage({ id: 'common.title.basicInfo' }),
|
|
icon: <IconFont type="icon-basic" />,
|
|
field: 'name'
|
|
},
|
|
{
|
|
value: TABKeysMap.SUPPORTEDMODELS,
|
|
label: intl.formatMessage({ id: 'providers.table.models' }),
|
|
icon: <IconFont type="icon-models" />,
|
|
field: 'supportedModels'
|
|
},
|
|
{
|
|
value: TABKeysMap.ADVANCED,
|
|
label: intl.formatMessage({ id: 'resources.form.advanced' }),
|
|
icon: <IconFont type="icon-settings" />,
|
|
field: 'advanceConfig'
|
|
}
|
|
];
|
|
|
|
const providerFields = useMemo(() => {
|
|
const currentProviderFields = providerRequiredFieldsMap[configType] || [];
|
|
return currentProviderFields;
|
|
}, [providerRequiredFieldsMap, configType]);
|
|
|
|
const formatAPIKeys = (values: FormData) => {
|
|
const apiTokens = values.api_tokens?.filter?.(
|
|
(item) => item && item.trim() !== ''
|
|
);
|
|
const apiTokenList = _.concat([], values.api_key, apiTokens || []);
|
|
if (action === PageAction.CREATE) {
|
|
return apiTokenList.map((item: string) => ({ input: item }));
|
|
}
|
|
|
|
const existingTokens = new Set(
|
|
(currentData?.api_tokens || []).map((item: { hash: string }) => item.hash)
|
|
);
|
|
return apiTokenList.map((item: string) =>
|
|
existingTokens.has(item) ? { hash: item } : { input: item }
|
|
);
|
|
};
|
|
|
|
const getCustomConfig = () => {
|
|
const customConfig = yaml2Json(advanceRef.current?.getYamlValue() || '');
|
|
const config = form.getFieldValue('config') || {};
|
|
return {
|
|
...config,
|
|
...customConfig
|
|
};
|
|
};
|
|
|
|
const handleOnFinish = (values: FormData) => {
|
|
const data = {
|
|
..._.omit(values, ['api_key']),
|
|
api_tokens: formatAPIKeys(values),
|
|
config: {
|
|
...values.config,
|
|
...yaml2Json(advanceRef.current?.getYamlValue() || '')
|
|
},
|
|
models: _.uniqBy(values.models, 'name'),
|
|
clone_from_id: action === PageAction.COPY ? currentData?.id : undefined
|
|
};
|
|
onFinish(data);
|
|
};
|
|
|
|
const onTargetChange = (key: string) => {
|
|
scrollTabsRef.current?.handleTargetChange(key);
|
|
};
|
|
|
|
const { handleOnFinishFailed } = useFinishFailed({
|
|
requiredFields,
|
|
onTargetChange,
|
|
updateActiveKey
|
|
});
|
|
|
|
const handleFinishFailed = (errorInfo: any) => {
|
|
handleOnFinishFailed(errorInfo);
|
|
onFinishFailed?.(errorInfo);
|
|
};
|
|
|
|
useImperativeHandle(ref, () => ({
|
|
submit: () => {
|
|
form.submit();
|
|
},
|
|
resetFields: () => {
|
|
form.resetFields();
|
|
}
|
|
}));
|
|
|
|
useEffect(() => {
|
|
if (
|
|
(action === PageAction.EDIT || action === PageAction.COPY) &&
|
|
currentData
|
|
) {
|
|
const apiTokensList = _.get(currentData, 'api_tokens', []).map(
|
|
(item: any) => item.hash || ''
|
|
);
|
|
const currentProvider = _.get(
|
|
providerRequiredFieldsMap,
|
|
[currentData.config.type],
|
|
[]
|
|
);
|
|
|
|
const currentRequiredFields = currentProvider.map(
|
|
(item: { name: string }) => item.name
|
|
);
|
|
|
|
const customConfigYaml = json2Yaml(
|
|
_.omit(currentData.config, ['type', ...currentRequiredFields]) || {}
|
|
);
|
|
|
|
form.setFieldsValue({
|
|
...currentData,
|
|
models: (currentData.models || []).map((item) => ({
|
|
...item,
|
|
category: item.category || null
|
|
})),
|
|
api_key: apiTokensList?.[0] || '',
|
|
api_tokens: apiTokensList?.slice(1) || [],
|
|
proxy_enabled: !!currentData.proxy_url,
|
|
custom_config: customConfigYaml
|
|
});
|
|
advanceRef.current?.setYamlValue?.(customConfigYaml);
|
|
}
|
|
}, [form, currentData, action]);
|
|
|
|
return (
|
|
<ScrollSpyTabs
|
|
ref={scrollTabsRef}
|
|
defaultTarget="basic"
|
|
segmentOptions={segmentOptions}
|
|
activeKey={activeKey}
|
|
setActiveKey={handleActiveChange}
|
|
segmentedTop={{
|
|
top: 0,
|
|
offsetTop: 96
|
|
}}
|
|
getScrollElementScrollableHeight={getScrollElementScrollableHeight}
|
|
>
|
|
<FormContext.Provider
|
|
value={{
|
|
action,
|
|
id: currentData?.id,
|
|
currentData,
|
|
providerFields,
|
|
getCustomConfig: getCustomConfig
|
|
}}
|
|
>
|
|
<Form
|
|
form={form}
|
|
onFinish={handleOnFinish}
|
|
onFinishFailed={handleFinishFailed}
|
|
initialValues={{
|
|
proxy_enabled: false,
|
|
proxy_url: '',
|
|
proxy_timeout: 30
|
|
}}
|
|
>
|
|
<Basic />
|
|
<CollapsePanel
|
|
activeKey={collapseKeys}
|
|
accordion={false}
|
|
onChange={handleOnCollapseChange}
|
|
items={[
|
|
{
|
|
key: TABKeysMap.SUPPORTEDMODELS,
|
|
label: intl.formatMessage({ id: 'providers.table.models' }),
|
|
forceRender: true,
|
|
children: <SupportedModels></SupportedModels>
|
|
},
|
|
{
|
|
key: TABKeysMap.ADVANCED,
|
|
label: intl.formatMessage({ id: 'resources.form.advanced' }),
|
|
forceRender: true,
|
|
children: (
|
|
<AdvanceConfig
|
|
action={action}
|
|
ref={advanceRef}
|
|
></AdvanceConfig>
|
|
)
|
|
}
|
|
]}
|
|
></CollapsePanel>
|
|
</Form>
|
|
</FormContext.Provider>
|
|
</ScrollSpyTabs>
|
|
);
|
|
});
|
|
|
|
export default ProviderForm;
|