mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-27 00:17:31 +00:00
feat: 修改模型数据可修改问题
This commit is contained in:
@@ -57,7 +57,7 @@ export const postModelDataJsonData = (
|
|||||||
jsonData: { prompt: string; completion: string; vector?: number[] }[]
|
jsonData: { prompt: string; completion: string; vector?: number[] }[]
|
||||||
) => POST(`/model/data/pushModelDataJson`, { modelId, data: jsonData });
|
) => 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);
|
PUT('/model/data/putModelData', data);
|
||||||
export const delOneModelData = (dataId: string) =>
|
export const delOneModelData = (dataId: string) =>
|
||||||
DELETE(`/model/data/delModelDataById?dataId=${dataId}`);
|
DELETE(`/model/data/delModelDataById?dataId=${dataId}`);
|
||||||
|
@@ -65,27 +65,23 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
role: 'system',
|
role: 'system',
|
||||||
content: `服务端逻辑生成器。根据用户输入的需求,拆解成代码实现的步骤,并按下面格式返回:
|
content: `服务端逻辑生成器。根据用户输入的需求,拆解成代码实现的步骤,并按格式返回: 1.\n2.\n3.\n ......
|
||||||
1.
|
|
||||||
2.
|
|
||||||
3.
|
|
||||||
....
|
|
||||||
|
|
||||||
下面是一些例子:
|
下面是一些例子:
|
||||||
实现一个手机号注册账号的方法
|
实现一个手机号注册账号的方法,包含两个函数
|
||||||
发送手机验证码函数:
|
* 发送手机验证码函数:
|
||||||
1. 从 query 中获取 phone
|
1. 从 query 中获取 phone
|
||||||
2. 校验手机号格式是否正确,不正确返回{error: "手机号格式错误"}
|
2. 校验手机号格式是否正确,不正确返回{error: "手机号格式错误"}
|
||||||
3. 给 phone 发送一个短信验证码,验证码长度为6位字符串,内容为:你正在注册laf, 验证码为:code
|
3. 给 phone 发送一个短信验证码,验证码长度为6位字符串,内容为:你正在注册laf, 验证码为:code
|
||||||
4. 数据库添加数据,表为"codes",内容为 {phone, code}
|
4. 数据库添加数据,表为"codes",内容为 {phone, code}
|
||||||
注册函数
|
* 注册函数
|
||||||
1. 从 body 中获取 phone 和 code
|
1. 从 body 中获取 phone 和 code
|
||||||
2. 校验手机号格式是否正确,不正确返回{error: "手机号格式错误"}
|
2. 校验手机号格式是否正确,不正确返回{error: "手机号格式错误"}
|
||||||
2. 获取数据库数据,表为"codes",查找是否有符合 phone, code 等于body参数的记录,没有的话返回 {error:"验证码不正确"}
|
2. 获取数据库数据,表为"codes",查找是否有符合 phone, code 等于body参数的记录,没有的话返回 {error:"验证码不正确"}
|
||||||
4. 添加数据库数据,表为"users" ,内容为{phone, code, createTime}
|
4. 添加数据库数据,表为"users" ,内容为{phone, code, createTime}
|
||||||
5. 删除数据库数据,删除 code 记录
|
5. 删除数据库数据,删除 code 记录
|
||||||
---------------
|
---------------
|
||||||
更新播客记录。传入blogId,blogText,tags,还需要记录更新的时间
|
更新博客记录。传入blogId,blogText,tags,还需要记录更新的时间
|
||||||
1. 从 body 中获取 blogId,blogText 和 tags
|
1. 从 body 中获取 blogId,blogText 和 tags
|
||||||
2. 校验 blogId 是否为空,为空则返回 {error: "博客ID不能为空"}
|
2. 校验 blogId 是否为空,为空则返回 {error: "博客ID不能为空"}
|
||||||
3. 校验 blogText 是否为空,为空则返回 {error: "博客内容不能为空"}
|
3. 校验 blogText 是否为空,为空则返回 {error: "博客内容不能为空"}
|
||||||
|
@@ -2,13 +2,12 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { authToken } from '@/service/utils/tools';
|
import { authToken } from '@/service/utils/tools';
|
||||||
import { connectRedis } from '@/service/redis';
|
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>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
try {
|
try {
|
||||||
let { dataId, text } = req.body as {
|
const { dataId, text, q } = req.body as { dataId: string; text: string; q?: string };
|
||||||
dataId: string;
|
|
||||||
text: string;
|
|
||||||
};
|
|
||||||
const { authorization } = req.headers;
|
const { authorization } = req.headers;
|
||||||
|
|
||||||
if (!authorization) {
|
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);
|
jsonRes(res);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import React, { useState, useCallback } from 'react';
|
import React, { useState, useCallback } from 'react';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
IconButton,
|
|
||||||
Flex,
|
Flex,
|
||||||
Button,
|
Button,
|
||||||
Modal,
|
Modal,
|
||||||
@@ -9,37 +8,40 @@ import {
|
|||||||
ModalContent,
|
ModalContent,
|
||||||
ModalHeader,
|
ModalHeader,
|
||||||
ModalCloseButton,
|
ModalCloseButton,
|
||||||
Input,
|
|
||||||
Textarea
|
Textarea
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { useForm, useFieldArray } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { postModelDataInput } from '@/api/model';
|
import { postModelDataInput, putModelDataById } from '@/api/model';
|
||||||
import { useToast } from '@/hooks/useToast';
|
import { useToast } from '@/hooks/useToast';
|
||||||
import { DeleteIcon } from '@chakra-ui/icons';
|
|
||||||
import { customAlphabet } from 'nanoid';
|
import { customAlphabet } from 'nanoid';
|
||||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
|
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
|
||||||
|
|
||||||
type FormData = { text: string; q: string };
|
export type FormData = { dataId?: string; text: string; q: string };
|
||||||
|
|
||||||
const InputDataModal = ({
|
const InputDataModal = ({
|
||||||
onClose,
|
onClose,
|
||||||
onSuccess,
|
onSuccess,
|
||||||
modelId
|
modelId,
|
||||||
|
defaultValues = {
|
||||||
|
text: '',
|
||||||
|
q: ''
|
||||||
|
}
|
||||||
}: {
|
}: {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onSuccess: () => void;
|
onSuccess: () => void;
|
||||||
modelId: string;
|
modelId: string;
|
||||||
|
defaultValues?: FormData;
|
||||||
}) => {
|
}) => {
|
||||||
const [importing, setImporting] = useState(false);
|
const [importing, setImporting] = useState(false);
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
const { register, handleSubmit, control } = useForm<FormData>({
|
const { register, handleSubmit } = useForm<FormData>({
|
||||||
defaultValues: {
|
defaultValues
|
||||||
text: '',
|
|
||||||
q: ''
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确认导入新数据
|
||||||
|
*/
|
||||||
const sureImportData = useCallback(
|
const sureImportData = useCallback(
|
||||||
async (e: FormData) => {
|
async (e: FormData) => {
|
||||||
setImporting(true);
|
setImporting(true);
|
||||||
@@ -72,6 +74,26 @@ const InputDataModal = ({
|
|||||||
[modelId, onClose, onSuccess, toast]
|
[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 (
|
return (
|
||||||
<Modal isOpen={true} onClose={onClose} isCentered>
|
<Modal isOpen={true} onClose={onClose} isCentered>
|
||||||
<ModalOverlay />
|
<ModalOverlay />
|
||||||
@@ -125,7 +147,10 @@ const InputDataModal = ({
|
|||||||
<Button variant={'outline'} mr={3} onClick={onClose}>
|
<Button variant={'outline'} mr={3} onClick={onClose}>
|
||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
<Button isLoading={importing} onClick={handleSubmit(sureImportData)}>
|
<Button
|
||||||
|
isLoading={importing}
|
||||||
|
onClick={handleSubmit(defaultValues.dataId ? updateData : sureImportData)}
|
||||||
|
>
|
||||||
确认导入
|
确认导入
|
||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
TableContainer,
|
TableContainer,
|
||||||
@@ -12,7 +12,6 @@ import {
|
|||||||
Flex,
|
Flex,
|
||||||
Button,
|
Button,
|
||||||
useDisclosure,
|
useDisclosure,
|
||||||
Textarea,
|
|
||||||
Menu,
|
Menu,
|
||||||
MenuButton,
|
MenuButton,
|
||||||
MenuList,
|
MenuList,
|
||||||
@@ -25,22 +24,21 @@ import { usePagination } from '@/hooks/usePagination';
|
|||||||
import {
|
import {
|
||||||
getModelDataList,
|
getModelDataList,
|
||||||
delOneModelData,
|
delOneModelData,
|
||||||
putModelDataById,
|
|
||||||
getModelSplitDataList,
|
getModelSplitDataList,
|
||||||
getExportDataList
|
getExportDataList
|
||||||
} from '@/api/model';
|
} from '@/api/model';
|
||||||
import { DeleteIcon, RepeatIcon } from '@chakra-ui/icons';
|
import { DeleteIcon, RepeatIcon, EditIcon } from '@chakra-ui/icons';
|
||||||
import { useToast } from '@/hooks/useToast';
|
import { useToast } from '@/hooks/useToast';
|
||||||
import { useLoading } from '@/hooks/useLoading';
|
import { useLoading } from '@/hooks/useLoading';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||||
|
import type { FormData as InputDataType } from './InputDataModal';
|
||||||
|
|
||||||
const InputModel = dynamic(() => import('./InputDataModal'));
|
const InputModel = dynamic(() => import('./InputDataModal'));
|
||||||
const SelectFileModel = dynamic(() => import('./SelectFileModal'));
|
const SelectFileModel = dynamic(() => import('./SelectFileModal'));
|
||||||
const SelectJsonModel = dynamic(() => import('./SelectJsonModal'));
|
const SelectJsonModel = dynamic(() => import('./SelectJsonModal'));
|
||||||
|
|
||||||
const ModelDataCard = ({ model }: { model: ModelSchema }) => {
|
const ModelDataCard = ({ model }: { model: ModelSchema }) => {
|
||||||
const { toast } = useToast();
|
|
||||||
const { Loading } = useLoading();
|
const { Loading } = useLoading();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -58,25 +56,8 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateAnswer = useCallback(
|
const [editInputData, setEditInputData] = useState<InputDataType>();
|
||||||
async (dataId: string, text: string) => {
|
|
||||||
await putModelDataById({
|
|
||||||
dataId,
|
|
||||||
text
|
|
||||||
});
|
|
||||||
toast({
|
|
||||||
title: '修改回答成功',
|
|
||||||
status: 'success'
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[toast]
|
|
||||||
);
|
|
||||||
|
|
||||||
const {
|
|
||||||
isOpen: isOpenInputModal,
|
|
||||||
onOpen: onOpenInputModal,
|
|
||||||
onClose: onCloseInputModal
|
|
||||||
} = useDisclosure();
|
|
||||||
const {
|
const {
|
||||||
isOpen: isOpenSelectFileModal,
|
isOpen: isOpenSelectFileModal,
|
||||||
onOpen: onOpenSelectFileModal,
|
onOpen: onOpenSelectFileModal,
|
||||||
@@ -151,7 +132,16 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
|
|||||||
导入
|
导入
|
||||||
</MenuButton>
|
</MenuButton>
|
||||||
<MenuList>
|
<MenuList>
|
||||||
<MenuItem onClick={onOpenInputModal}>手动输入</MenuItem>
|
<MenuItem
|
||||||
|
onClick={() =>
|
||||||
|
setEditInputData({
|
||||||
|
text: '',
|
||||||
|
q: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
手动输入
|
||||||
|
</MenuItem>
|
||||||
<MenuItem onClick={onOpenSelectFileModal}>文件导入</MenuItem>
|
<MenuItem onClick={onOpenSelectFileModal}>文件导入</MenuItem>
|
||||||
<MenuItem onClick={onOpenSelectJsonModal}>JSON导入</MenuItem>
|
<MenuItem onClick={onOpenSelectJsonModal}>JSON导入</MenuItem>
|
||||||
</MenuList>
|
</MenuList>
|
||||||
@@ -170,34 +160,38 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
|
|||||||
<Th>Question</Th>
|
<Th>Question</Th>
|
||||||
<Th>Text</Th>
|
<Th>Text</Th>
|
||||||
<Th>Status</Th>
|
<Th>Status</Th>
|
||||||
<Th></Th>
|
<Th>操作</Th>
|
||||||
</Tr>
|
</Tr>
|
||||||
</Thead>
|
</Thead>
|
||||||
<Tbody>
|
<Tbody>
|
||||||
{modelDataList.map((item) => (
|
{modelDataList.map((item) => (
|
||||||
<Tr key={item.id}>
|
<Tr key={item.id}>
|
||||||
<Td w={'350px'}>
|
<Td>
|
||||||
<Box fontSize={'xs'} w={'100%'} whiteSpace={'pre-wrap'} _notLast={{ mb: 1 }}>
|
<Box fontSize={'xs'} w={'100%'} whiteSpace={'pre-wrap'}>
|
||||||
{item.q}
|
{item.q}
|
||||||
</Box>
|
</Box>
|
||||||
</Td>
|
</Td>
|
||||||
<Td minW={'200px'}>
|
<Td minW={'200px'}>
|
||||||
<Textarea
|
<Box w={'100%'} fontSize={'xs'} whiteSpace={'pre-wrap'}>
|
||||||
w={'100%'}
|
{item.text}
|
||||||
h={'100%'}
|
</Box>
|
||||||
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>
|
|
||||||
</Td>
|
</Td>
|
||||||
<Td w={'100px'}>{ModelDataStatusMap[item.status]}</Td>
|
<Td>{ModelDataStatusMap[item.status]}</Td>
|
||||||
<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
|
<IconButton
|
||||||
icon={<DeleteIcon />}
|
icon={<DeleteIcon />}
|
||||||
variant={'outline'}
|
variant={'outline'}
|
||||||
@@ -221,8 +215,13 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Loading loading={isLoading} fixed={false} />
|
<Loading loading={isLoading} fixed={false} />
|
||||||
{isOpenInputModal && (
|
{editInputData !== undefined && (
|
||||||
<InputModel modelId={model._id} onClose={onCloseInputModal} onSuccess={refetchData} />
|
<InputModel
|
||||||
|
modelId={model._id}
|
||||||
|
defaultValues={editInputData}
|
||||||
|
onClose={() => setEditInputData(undefined)}
|
||||||
|
onSuccess={refetchData}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{isOpenSelectFileModal && (
|
{isOpenSelectFileModal && (
|
||||||
<SelectFileModel
|
<SelectFileModel
|
||||||
|
Reference in New Issue
Block a user