import PropTypes from 'prop-types'; import { useState, useEffect } from 'react'; import { CHANNEL_OPTIONS } from 'constants/ChannelConstants'; import { useTheme } from '@mui/material/styles'; import { API } from 'utils/api'; import { showError, showSuccess } from 'utils/common'; import { Dialog, DialogTitle, DialogContent, DialogActions, TextField, Button, Divider, Select, MenuItem, FormControl, InputLabel, OutlinedInput, ButtonGroup, Container, Autocomplete, FormHelperText } from '@mui/material'; import { Formik } from 'formik'; import * as Yup from 'yup'; import { defaultConfig, typeConfig } from '../type/Config'; //typeConfig import { createFilterOptions } from '@mui/material/Autocomplete'; const filter = createFilterOptions(); const validationSchema = Yup.object().shape({ is_edit: Yup.boolean(), name: Yup.string().required('名称 不能为空'), type: Yup.number().required('渠道 不能为空'), key: Yup.string().when('is_edit', { is: false, then: Yup.string().required('密钥 不能为空') }), other: Yup.string(), models: Yup.array().min(1, '模型 不能为空'), groups: Yup.array().min(1, '用户组 不能为空'), base_url: Yup.string().when('type', { is: (value) => [3, 24, 8].includes(value), then: Yup.string().required('渠道API地址 不能为空'), // base_url 是必需的 otherwise: Yup.string() // 在其他情况下,base_url 可以是任意字符串 }), model_mapping: Yup.string().test('is-json', '必须是有效的JSON字符串', function (value) { try { if (value === '' || value === null || value === undefined) { return true; } const parsedValue = JSON.parse(value); if (typeof parsedValue === 'object') { return true; } } catch (e) { return false; } return false; }) }); const EditModal = ({ open, channelId, onCancel, onOk }) => { const theme = useTheme(); // const [loading, setLoading] = useState(false); const [initialInput, setInitialInput] = useState(defaultConfig.input); const [inputLabel, setInputLabel] = useState(defaultConfig.inputLabel); // const [inputPrompt, setInputPrompt] = useState(defaultConfig.prompt); const [groupOptions, setGroupOptions] = useState([]); const [modelOptions, setModelOptions] = useState([]); const [basicModels, setBasicModels] = useState([]); const initChannel = (typeValue) => { if (typeConfig[typeValue]?.inputLabel) { setInputLabel({ ...defaultConfig.inputLabel, ...typeConfig[typeValue].inputLabel }); } else { setInputLabel(defaultConfig.inputLabel); } if (typeConfig[typeValue]?.prompt) { setInputPrompt({ ...defaultConfig.prompt, ...typeConfig[typeValue].prompt }); } else { setInputPrompt(defaultConfig.prompt); } return typeConfig[typeValue]?.input; }; const handleTypeChange = (setFieldValue, typeValue, values) => { const newInput = initChannel(typeValue); if (newInput) { Object.keys(newInput).forEach((key) => { if ( (!Array.isArray(values[key]) && values[key] !== null && values[key] !== undefined) || (Array.isArray(values[key]) && values[key].length > 0) ) { return; } setFieldValue(key, newInput[key]); }); } }; const fetchGroups = async () => { try { let res = await API.get(`/api/group/`); setGroupOptions(res.data.data); } catch (error) { showError(error.message); } }; const fetchModels = async () => { try { let res = await API.get(`/api/channel/models`); setModelOptions(res.data.data.map((model) => model.id)); setBasicModels( res.data.data .filter((model) => { return model.id.startsWith('gpt-3') || model.id.startsWith('gpt-4'); }) .map((model) => model.id) ); } catch (error) { showError(error.message); } }; const submit = async (values, { setErrors, setStatus, setSubmitting }) => { setSubmitting(true); if (values.base_url && values.base_url.endsWith('/')) { values.base_url = values.base_url.slice(0, values.base_url.length - 1); } if (values.type === 3 && values.other === '') { values.other = '2023-09-01-preview'; } if (values.type === 18 && values.other === '') { values.other = 'v2.1'; } let res; values.models = values.models.join(','); values.group = values.groups.join(','); if (channelId) { res = await API.put(`/api/channel/`, { ...values, id: parseInt(channelId) }); } else { res = await API.post(`/api/channel/`, values); } const { success, message } = res.data; if (success) { if (channelId) { showSuccess('渠道更新成功!'); } else { showSuccess('渠道创建成功!'); } setSubmitting(false); setStatus({ success: true }); onOk(true); } else { setStatus({ success: false }); // showError(message); setErrors({ submit: message }); } }; const loadChannel = async () => { let res = await API.get(`/api/channel/${channelId}`); const { success, message, data } = res.data; if (success) { if (data.models === '') { data.models = []; } else { data.models = data.models.split(','); } if (data.group === '') { data.groups = []; } else { data.groups = data.group.split(','); } if (data.model_mapping !== '') { data.model_mapping = JSON.stringify(JSON.parse(data.model_mapping), null, 2); } data.is_edit = true; initChannel(data.type); setInitialInput(data); } else { showError(message); } }; useEffect(() => { fetchGroups().then(); fetchModels().then(); if (channelId) { loadChannel().then(); } else { initChannel(1); setInitialInput({ ...defaultConfig.input, is_edit: false }); } }, [channelId]); return ( {channelId ? '编辑渠道' : '新建渠道'} {({ errors, handleBlur, handleChange, handleSubmit, isSubmitting, touched, values, setFieldValue }) => (
{inputLabel.type} {touched.type && errors.type ? ( {errors.type} ) : ( {inputPrompt.type} )} {inputLabel.name} {touched.name && errors.name ? ( {errors.name} ) : ( {inputPrompt.name} )} {inputLabel.base_url} {touched.base_url && errors.base_url ? ( {errors.base_url} ) : ( {inputPrompt.base_url} )} {inputPrompt.other && ( {inputLabel.other} {touched.other && errors.other ? ( {errors.other} ) : ( {inputPrompt.other} )} )} { const event = { target: { name: 'groups', value: value } }; handleChange(event); }} onBlur={handleBlur} filterSelectedOptions renderInput={(params) => } aria-describedby="helper-text-channel-groups-label" /> {errors.groups ? ( {errors.groups} ) : ( {inputPrompt.groups} )} { const event = { target: { name: 'models', value: value } }; handleChange(event); }} onBlur={handleBlur} filterSelectedOptions renderInput={(params) => } filterOptions={(options, params) => { const filtered = filter(options, params); const { inputValue } = params; const isExisting = options.some((option) => inputValue === option); if (inputValue !== '' && !isExisting) { filtered.push(inputValue); } return filtered; }} /> {errors.models ? ( {errors.models} ) : ( {inputPrompt.models} )} {inputLabel.key} {touched.key && errors.key ? ( {errors.key} ) : ( {inputPrompt.key} )} {/* {inputLabel.model_mapping} */} {touched.model_mapping && errors.model_mapping ? ( {errors.model_mapping} ) : ( {inputPrompt.model_mapping} )}
)}
); }; export default EditModal; EditModal.propTypes = { open: PropTypes.bool, channelId: PropTypes.number, onCancel: PropTypes.func, onOk: PropTypes.func };