Merge branch 'dev2.4' of https://github.com/c121914yu/FastGPT into dev2.4

This commit is contained in:
archer
2023-04-04 21:59:38 +08:00
5 changed files with 99 additions and 70 deletions

View File

@@ -57,7 +57,7 @@ export const postModelDataJsonData = (
jsonData: { prompt: string; completion: string; vector?: number[] }[]
) => POST(`/model/data/pushModelDataJson`, { modelId, data: jsonData });
export const putModelDataById = (data: { dataId: string; text: string }) =>
export const putModelDataById = (data: { dataId: string; text: string; q?: string }) =>
PUT('/model/data/putModelData', data);
export const delOneModelData = (dataId: string) =>
DELETE(`/model/data/delModelDataById?dataId=${dataId}`);

View File

@@ -65,20 +65,16 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
messages: [
{
role: 'system',
content: `服务端逻辑生成器。根据用户输入的需求,拆解成代码实现的步骤,并按下面格式返回:
1.
2.
3.
....
content: `服务端逻辑生成器。根据用户输入的需求,拆解成代码实现的步骤,并按格式返回: 1.\n2.\n3.\n ......
下面是一些例子:
实现一个手机号注册账号的方法
发送手机验证码函数:
实现一个手机号注册账号的方法,包含两个函数
* 发送手机验证码函数:
1. 从 query 中获取 phone
2. 校验手机号格式是否正确,不正确返回{error: "手机号格式错误"}
3. 给 phone 发送一个短信验证码验证码长度为6位字符串内容为你正在注册laf, 验证码为code
4. 数据库添加数据,表为"codes",内容为 {phone, code}
注册函数
* 注册函数
1. 从 body 中获取 phone 和 code
2. 校验手机号格式是否正确,不正确返回{error: "手机号格式错误"}
2. 获取数据库数据,表为"codes",查找是否有符合 phone, code 等于body参数的记录没有的话返回 {error:"验证码不正确"}

View File

@@ -2,13 +2,12 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { authToken } from '@/service/utils/tools';
import { connectRedis } from '@/service/redis';
import { ModelDataStatusEnum } from '@/constants/redis';
import { generateVector } from '@/service/events/generateVector';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
let { dataId, text } = req.body as {
dataId: string;
text: string;
};
const { dataId, text, q } = req.body as { dataId: string; text: string; q?: string };
const { authorization } = req.headers;
if (!authorization) {
@@ -31,7 +30,17 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
}
// 更新
await redis.hSet(dataId, 'text', text);
await redis.sendCommand([
'HMSET',
dataId,
...(q ? ['q', q, 'status', ModelDataStatusEnum.waiting] : []),
'text',
text
]);
if (q) {
generateVector();
}
jsonRes(res);
} catch (err) {

View File

@@ -1,7 +1,6 @@
import React, { useState, useCallback } from 'react';
import {
Box,
IconButton,
Flex,
Button,
Modal,
@@ -9,37 +8,40 @@ import {
ModalContent,
ModalHeader,
ModalCloseButton,
Input,
Textarea
} from '@chakra-ui/react';
import { useForm, useFieldArray } from 'react-hook-form';
import { postModelDataInput } from '@/api/model';
import { useForm } from 'react-hook-form';
import { postModelDataInput, putModelDataById } from '@/api/model';
import { useToast } from '@/hooks/useToast';
import { DeleteIcon } from '@chakra-ui/icons';
import { customAlphabet } from 'nanoid';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
type FormData = { text: string; q: string };
export type FormData = { dataId?: string; text: string; q: string };
const InputDataModal = ({
onClose,
onSuccess,
modelId
modelId,
defaultValues = {
text: '',
q: ''
}
}: {
onClose: () => void;
onSuccess: () => void;
modelId: string;
defaultValues?: FormData;
}) => {
const [importing, setImporting] = useState(false);
const { toast } = useToast();
const { register, handleSubmit, control } = useForm<FormData>({
defaultValues: {
text: '',
q: ''
}
const { register, handleSubmit } = useForm<FormData>({
defaultValues
});
/**
* 确认导入新数据
*/
const sureImportData = useCallback(
async (e: FormData) => {
setImporting(true);
@@ -72,6 +74,26 @@ const InputDataModal = ({
[modelId, onClose, onSuccess, toast]
);
const updateData = useCallback(
async (e: FormData) => {
if (!e.dataId) return;
if (e.text === defaultValues.text && e.q === defaultValues.q) return;
await putModelDataById({
dataId: e.dataId,
text: e.text,
q: e.q === defaultValues.q ? '' : e.q
});
toast({
title: '修改回答成功',
status: 'success'
});
onClose();
onSuccess();
},
[defaultValues.q, onClose, onSuccess, toast]
);
return (
<Modal isOpen={true} onClose={onClose} isCentered>
<ModalOverlay />
@@ -125,7 +147,10 @@ const InputDataModal = ({
<Button variant={'outline'} mr={3} onClick={onClose}>
</Button>
<Button isLoading={importing} onClick={handleSubmit(sureImportData)}>
<Button
isLoading={importing}
onClick={handleSubmit(defaultValues.dataId ? updateData : sureImportData)}
>
</Button>
</Flex>

View File

@@ -1,4 +1,4 @@
import React, { useCallback } from 'react';
import React, { useCallback, useState } from 'react';
import {
Box,
TableContainer,
@@ -12,7 +12,6 @@ import {
Flex,
Button,
useDisclosure,
Textarea,
Menu,
MenuButton,
MenuList,
@@ -25,22 +24,21 @@ import { usePagination } from '@/hooks/usePagination';
import {
getModelDataList,
delOneModelData,
putModelDataById,
getModelSplitDataList,
getExportDataList
} from '@/api/model';
import { DeleteIcon, RepeatIcon } from '@chakra-ui/icons';
import { DeleteIcon, RepeatIcon, EditIcon } from '@chakra-ui/icons';
import { useToast } from '@/hooks/useToast';
import { useLoading } from '@/hooks/useLoading';
import dynamic from 'next/dynamic';
import { useMutation, useQuery } from '@tanstack/react-query';
import type { FormData as InputDataType } from './InputDataModal';
const InputModel = dynamic(() => import('./InputDataModal'));
const SelectFileModel = dynamic(() => import('./SelectFileModal'));
const SelectJsonModel = dynamic(() => import('./SelectJsonModal'));
const ModelDataCard = ({ model }: { model: ModelSchema }) => {
const { toast } = useToast();
const { Loading } = useLoading();
const {
@@ -58,25 +56,8 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
}
});
const updateAnswer = useCallback(
async (dataId: string, text: string) => {
await putModelDataById({
dataId,
text
});
toast({
title: '修改回答成功',
status: 'success'
});
},
[toast]
);
const [editInputData, setEditInputData] = useState<InputDataType>();
const {
isOpen: isOpenInputModal,
onOpen: onOpenInputModal,
onClose: onCloseInputModal
} = useDisclosure();
const {
isOpen: isOpenSelectFileModal,
onOpen: onOpenSelectFileModal,
@@ -151,7 +132,16 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
</MenuButton>
<MenuList>
<MenuItem onClick={onOpenInputModal}></MenuItem>
<MenuItem
onClick={() =>
setEditInputData({
text: '',
q: ''
})
}
>
</MenuItem>
<MenuItem onClick={onOpenSelectFileModal}></MenuItem>
<MenuItem onClick={onOpenSelectJsonModal}>JSON导入</MenuItem>
</MenuList>
@@ -170,34 +160,38 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
<Th>Question</Th>
<Th>Text</Th>
<Th>Status</Th>
<Th></Th>
<Th></Th>
</Tr>
</Thead>
<Tbody>
{modelDataList.map((item) => (
<Tr key={item.id}>
<Td w={'350px'}>
<Box fontSize={'xs'} w={'100%'} whiteSpace={'pre-wrap'} _notLast={{ mb: 1 }}>
<Td>
<Box fontSize={'xs'} w={'100%'} whiteSpace={'pre-wrap'}>
{item.q}
</Box>
</Td>
<Td minW={'200px'}>
<Textarea
w={'100%'}
h={'100%'}
defaultValue={item.text}
fontSize={'xs'}
resize={'both'}
onBlur={(e) => {
const oldVal = modelDataList.find((data) => item.id === data.id)?.text;
if (oldVal !== e.target.value) {
updateAnswer(item.id, e.target.value);
}
}}
></Textarea>
<Box w={'100%'} fontSize={'xs'} whiteSpace={'pre-wrap'}>
{item.text}
</Box>
</Td>
<Td w={'100px'}>{ModelDataStatusMap[item.status]}</Td>
<Td>{ModelDataStatusMap[item.status]}</Td>
<Td>
<IconButton
mr={5}
icon={<EditIcon />}
variant={'outline'}
aria-label={'delete'}
size={'sm'}
onClick={() =>
setEditInputData({
dataId: item.id,
q: item.q,
text: item.text
})
}
/>
<IconButton
icon={<DeleteIcon />}
variant={'outline'}
@@ -221,8 +215,13 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
</Box>
<Loading loading={isLoading} fixed={false} />
{isOpenInputModal && (
<InputModel modelId={model._id} onClose={onCloseInputModal} onSuccess={refetchData} />
{editInputData !== undefined && (
<InputModel
modelId={model._id}
defaultValues={editInputData}
onClose={() => setEditInputData(undefined)}
onSuccess={refetchData}
/>
)}
{isOpenSelectFileModal && (
<SelectFileModel