| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | import React, { useState, useRef, useEffect } from 'react'; |
| | import JSONEditor from '../../../common/ui/JSONEditor'; |
| | import { |
| | SideSheet, |
| | Button, |
| | Form, |
| | Typography, |
| | Space, |
| | Tag, |
| | Row, |
| | Col, |
| | Card, |
| | Avatar, |
| | Spin, |
| | } from '@douyinfe/semi-ui'; |
| | import { IconLayers, IconSave, IconClose } from '@douyinfe/semi-icons'; |
| | import { API, showError, showSuccess } from '../../../../helpers'; |
| | import { useTranslation } from 'react-i18next'; |
| | import { useIsMobile } from '../../../../hooks/common/useIsMobile'; |
| |
|
| | const { Text, Title } = Typography; |
| |
|
| | |
| | const ENDPOINT_TEMPLATE = { |
| | openai: { path: '/v1/chat/completions', method: 'POST' }, |
| | 'openai-response': { path: '/v1/responses', method: 'POST' }, |
| | anthropic: { path: '/v1/messages', method: 'POST' }, |
| | gemini: { path: '/v1beta/models/{model}:generateContent', method: 'POST' }, |
| | 'jina-rerank': { path: '/rerank', method: 'POST' }, |
| | 'image-generation': { path: '/v1/images/generations', method: 'POST' }, |
| | }; |
| |
|
| | const EditPrefillGroupModal = ({ |
| | visible, |
| | onClose, |
| | editingGroup, |
| | onSuccess, |
| | }) => { |
| | const { t } = useTranslation(); |
| | const isMobile = useIsMobile(); |
| | const [loading, setLoading] = useState(false); |
| | const formRef = useRef(null); |
| | const isEdit = editingGroup && editingGroup.id !== undefined; |
| |
|
| | const [selectedType, setSelectedType] = useState(editingGroup?.type || 'tag'); |
| |
|
| | |
| | useEffect(() => { |
| | setSelectedType(editingGroup?.type || 'tag'); |
| | }, [editingGroup?.type]); |
| |
|
| | const typeOptions = [ |
| | { label: t('模型组'), value: 'model' }, |
| | { label: t('标签组'), value: 'tag' }, |
| | { label: t('端点组'), value: 'endpoint' }, |
| | ]; |
| |
|
| | |
| | const handleSubmit = async (values) => { |
| | setLoading(true); |
| | try { |
| | const submitData = { |
| | ...values, |
| | }; |
| | if (values.type === 'endpoint') { |
| | submitData.items = values.items || ''; |
| | } else { |
| | submitData.items = Array.isArray(values.items) ? values.items : []; |
| | } |
| |
|
| | if (editingGroup.id) { |
| | submitData.id = editingGroup.id; |
| | const res = await API.put('/api/prefill_group', submitData); |
| | if (res.data.success) { |
| | showSuccess(t('更新成功')); |
| | onSuccess(); |
| | } else { |
| | showError(res.data.message || t('更新失败')); |
| | } |
| | } else { |
| | const res = await API.post('/api/prefill_group', submitData); |
| | if (res.data.success) { |
| | showSuccess(t('创建成功')); |
| | onSuccess(); |
| | } else { |
| | showError(res.data.message || t('创建失败')); |
| | } |
| | } |
| | } catch (error) { |
| | showError(t('操作失败')); |
| | } |
| | setLoading(false); |
| | }; |
| |
|
| | return ( |
| | <SideSheet |
| | placement='left' |
| | title={ |
| | <Space> |
| | {isEdit ? ( |
| | <Tag color='blue' shape='circle'> |
| | {t('更新')} |
| | </Tag> |
| | ) : ( |
| | <Tag color='green' shape='circle'> |
| | {t('新建')} |
| | </Tag> |
| | )} |
| | <Title heading={4} className='m-0'> |
| | {isEdit ? t('更新预填组') : t('创建新的预填组')} |
| | </Title> |
| | </Space> |
| | } |
| | visible={visible} |
| | onCancel={onClose} |
| | width={isMobile ? '100%' : 600} |
| | bodyStyle={{ padding: '0' }} |
| | footer={ |
| | <div className='flex justify-end bg-white'> |
| | <Space> |
| | <Button |
| | theme='solid' |
| | className='!rounded-lg' |
| | onClick={() => formRef.current?.submitForm()} |
| | icon={<IconSave />} |
| | loading={loading} |
| | > |
| | {t('提交')} |
| | </Button> |
| | <Button |
| | theme='light' |
| | className='!rounded-lg' |
| | type='primary' |
| | onClick={onClose} |
| | icon={<IconClose />} |
| | > |
| | {t('取消')} |
| | </Button> |
| | </Space> |
| | </div> |
| | } |
| | closeIcon={null} |
| | > |
| | <Spin spinning={loading}> |
| | <Form |
| | getFormApi={(api) => (formRef.current = api)} |
| | initValues={{ |
| | name: editingGroup?.name || '', |
| | type: editingGroup?.type || 'tag', |
| | description: editingGroup?.description || '', |
| | items: (() => { |
| | try { |
| | if (editingGroup?.type === 'endpoint') { |
| | // 保持原始字符串 |
| | return typeof editingGroup?.items === 'string' |
| | ? editingGroup.items |
| | : JSON.stringify(editingGroup.items || {}, null, 2); |
| | } |
| | return Array.isArray(editingGroup?.items) |
| | ? editingGroup.items |
| | : []; |
| | } catch { |
| | return editingGroup?.type === 'endpoint' ? '' : []; |
| | } |
| | })(), |
| | }} |
| | onSubmit={handleSubmit} |
| | > |
| | <div className='p-2'> |
| | {/* 基本信息 */} |
| | <Card className='!rounded-2xl shadow-sm border-0'> |
| | <div className='flex items-center mb-2'> |
| | <Avatar size='small' color='green' className='mr-2 shadow-md'> |
| | <IconLayers size={16} /> |
| | </Avatar> |
| | <div> |
| | <Text className='text-lg font-medium'>{t('基本信息')}</Text> |
| | <div className='text-xs text-gray-600'> |
| | {t('设置预填组的基本信息')} |
| | </div> |
| | </div> |
| | </div> |
| | <Row gutter={12}> |
| | <Col span={24}> |
| | <Form.Input |
| | field='name' |
| | label={t('组名')} |
| | placeholder={t('请输入组名')} |
| | rules={[{ required: true, message: t('请输入组名') }]} |
| | showClear |
| | /> |
| | </Col> |
| | <Col span={24}> |
| | <Form.Select |
| | field='type' |
| | label={t('类型')} |
| | placeholder={t('选择组类型')} |
| | optionList={typeOptions} |
| | rules={[{ required: true, message: t('请选择组类型') }]} |
| | style={{ width: '100%' }} |
| | onChange={(val) => setSelectedType(val)} |
| | /> |
| | </Col> |
| | <Col span={24}> |
| | <Form.TextArea |
| | field='description' |
| | label={t('描述')} |
| | placeholder={t('请输入组描述')} |
| | rows={3} |
| | showClear |
| | /> |
| | </Col> |
| | <Col span={24}> |
| | {selectedType === 'endpoint' ? ( |
| | <JSONEditor |
| | field='items' |
| | label={t('端点映射')} |
| | value={ |
| | formRef.current?.getValue('items') ?? |
| | (typeof editingGroup?.items === 'string' |
| | ? editingGroup.items |
| | : JSON.stringify(editingGroup.items || {}, null, 2)) |
| | } |
| | onChange={(val) => |
| | formRef.current?.setValue('items', val) |
| | } |
| | editorType='object' |
| | placeholder={ |
| | '{\n "openai": {"path": "/v1/chat/completions", "method": "POST"}\n}' |
| | } |
| | template={ENDPOINT_TEMPLATE} |
| | templateLabel={t('填入模板')} |
| | extraText={t('键为端点类型,值为路径和方法对象')} |
| | /> |
| | ) : ( |
| | <Form.TagInput |
| | field='items' |
| | label={t('项目')} |
| | placeholder={t('输入项目名称,按回车添加')} |
| | addOnBlur |
| | showClear |
| | style={{ width: '100%' }} |
| | /> |
| | )} |
| | </Col> |
| | </Row> |
| | </Card> |
| | </div> |
| | </Form> |
| | </Spin> |
| | </SideSheet> |
| | ); |
| | }; |
| |
|
| | export default EditPrefillGroupModal; |
| |
|