mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-30 02:12:38 +00:00
Fixed the duplicate data check problem, history filter and add tts stream (#477)
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||
import { GetChatSpeechProps } from '@/global/core/chat/api.d';
|
||||
import { text2Speech } from '@fastgpt/service/core/ai/audio/speech';
|
||||
import { pushAudioSpeechBill } from '@/service/support/wallet/bill/push';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { authType2BillSource } from '@/service/support/wallet/bill/utils';
|
||||
import { getAudioSpeechModel } from '@/service/core/ai/model';
|
||||
import { MongoTTSBuffer } from '@fastgpt/service/common/buffer/tts/schema';
|
||||
|
||||
/*
|
||||
1. get tts from chatItem store
|
||||
@@ -18,50 +19,66 @@ import { authType2BillSource } from '@/service/support/wallet/bill/utils';
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { chatItemId, ttsConfig, input } = req.body as GetChatSpeechProps;
|
||||
const { ttsConfig, input } = req.body as GetChatSpeechProps;
|
||||
|
||||
if (!ttsConfig.model || !ttsConfig.voice) {
|
||||
throw new Error('model or voice not found');
|
||||
}
|
||||
|
||||
const { teamId, tmbId, authType } = await authCert({ req, authToken: true });
|
||||
|
||||
const chatItem = await (async () => {
|
||||
if (!chatItemId) return null;
|
||||
return await MongoChatItem.findOne(
|
||||
{
|
||||
dataId: chatItemId
|
||||
},
|
||||
'tts'
|
||||
);
|
||||
})();
|
||||
const ttsModel = getAudioSpeechModel(ttsConfig.model);
|
||||
const voiceData = ttsModel.voices.find((item) => item.value === ttsConfig.voice);
|
||||
|
||||
if (chatItem?.tts) {
|
||||
return jsonRes(res, {
|
||||
data: chatItem.tts
|
||||
});
|
||||
if (!voiceData) {
|
||||
throw new Error('voice not found');
|
||||
}
|
||||
|
||||
const { tts, model } = await text2Speech({
|
||||
const ttsBuffer = await MongoTTSBuffer.findOne(
|
||||
{
|
||||
bufferId: voiceData.bufferId,
|
||||
text: input
|
||||
},
|
||||
'buffer'
|
||||
);
|
||||
|
||||
if (ttsBuffer?.buffer) {
|
||||
return res.end(new Uint8Array(ttsBuffer.buffer.buffer));
|
||||
}
|
||||
|
||||
await text2Speech({
|
||||
res,
|
||||
input,
|
||||
model: ttsConfig.model,
|
||||
voice: ttsConfig.voice,
|
||||
input
|
||||
});
|
||||
props: {
|
||||
// temp code
|
||||
baseUrl: ttsModel.baseUrl || '',
|
||||
key: ttsModel.key || ''
|
||||
},
|
||||
onSuccess: async ({ model, buffer }) => {
|
||||
try {
|
||||
pushAudioSpeechBill({
|
||||
model: model,
|
||||
textLength: input.length,
|
||||
tmbId,
|
||||
teamId,
|
||||
source: authType2BillSource({ authType })
|
||||
});
|
||||
|
||||
(async () => {
|
||||
if (!chatItem) return;
|
||||
try {
|
||||
chatItem.tts = tts;
|
||||
await chatItem.save();
|
||||
} catch (error) {}
|
||||
})();
|
||||
|
||||
jsonRes(res, {
|
||||
data: tts
|
||||
});
|
||||
|
||||
pushAudioSpeechBill({
|
||||
model: model,
|
||||
textLength: input.length,
|
||||
tmbId,
|
||||
teamId,
|
||||
source: authType2BillSource({ authType })
|
||||
await MongoTTSBuffer.create({
|
||||
bufferId: voiceData.bufferId,
|
||||
text: input,
|
||||
buffer
|
||||
});
|
||||
} catch (error) {}
|
||||
},
|
||||
onError: (err) => {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
|
@@ -34,8 +34,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
qaModels: global.qaModels,
|
||||
cqModels: global.cqModels,
|
||||
extractModels: global.extractModels,
|
||||
qgModels: global.qgModels,
|
||||
vectorModels: global.vectorModels,
|
||||
audioSpeechModels: global.audioSpeechModels.map((item) => ({
|
||||
...item,
|
||||
baseUrl: undefined,
|
||||
key: undefined
|
||||
})),
|
||||
priceMd: global.priceMd,
|
||||
systemVersion: global.systemVersion || '0.0.0'
|
||||
}
|
||||
|
@@ -86,6 +86,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
if (chatMessages[chatMessages.length - 1].obj !== ChatRoleEnum.Human) {
|
||||
chatMessages.pop();
|
||||
}
|
||||
|
||||
// user question
|
||||
const question = chatMessages.pop();
|
||||
if (!question) {
|
||||
@@ -173,15 +174,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
const isAppOwner = !shareId && String(user.team.tmbId) === String(app.tmbId);
|
||||
|
||||
/* format prompts */
|
||||
const prompts = history.concat(gptMessage2ChatType(messages));
|
||||
|
||||
// set sse response headers
|
||||
if (stream) {
|
||||
res.setHeader('Content-Type', 'text/event-stream;charset=utf-8');
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
res.setHeader('X-Accel-Buffering', 'no');
|
||||
res.setHeader('Cache-Control', 'no-cache, no-transform');
|
||||
}
|
||||
const concatHistory = history.concat(chatMessages);
|
||||
|
||||
/* start flow controller */
|
||||
const { responseData, answerText } = await dispatchModules({
|
||||
@@ -193,7 +186,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
tmbId: user.team.tmbId,
|
||||
variables,
|
||||
params: {
|
||||
history: prompts,
|
||||
history: concatHistory,
|
||||
userChatInput: question.value
|
||||
},
|
||||
stream,
|
||||
|
@@ -6,10 +6,10 @@ import React, { useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MySelect from '@/components/Select';
|
||||
import { TTSTypeEnum } from '@/constants/app';
|
||||
import { Text2SpeechVoiceEnum, openaiTTSModel } from '@fastgpt/global/core/ai/speech/constant';
|
||||
import { AppTTSConfigType } from '@/types/app';
|
||||
import { useAudioPlay } from '@/web/common/utils/voice';
|
||||
import { useLoading } from '@/web/common/hooks/useLoading';
|
||||
import { audioSpeechModels } from '@/web/common/system/staticData';
|
||||
|
||||
const TTSSelect = ({
|
||||
value,
|
||||
@@ -37,10 +37,16 @@ const TTSSelect = ({
|
||||
if (e === TTSTypeEnum.none || e === TTSTypeEnum.web) {
|
||||
onChange({ type: e as `${TTSTypeEnum}` });
|
||||
} else {
|
||||
const audioModel = audioSpeechModels.find((item) =>
|
||||
item.voices.find((voice) => voice.value === e)
|
||||
);
|
||||
if (!audioModel) {
|
||||
return;
|
||||
}
|
||||
onChange({
|
||||
type: TTSTypeEnum.model,
|
||||
model: openaiTTSModel,
|
||||
voice: e as `${Text2SpeechVoiceEnum}`,
|
||||
model: audioModel.model,
|
||||
voice: e,
|
||||
speed: 1
|
||||
});
|
||||
}
|
||||
@@ -77,12 +83,7 @@ const TTSSelect = ({
|
||||
list={[
|
||||
{ label: t('core.app.tts.Close'), value: TTSTypeEnum.none },
|
||||
{ label: t('core.app.tts.Web'), value: TTSTypeEnum.web },
|
||||
{ label: 'Alloy', value: Text2SpeechVoiceEnum.alloy },
|
||||
{ label: 'Echo', value: Text2SpeechVoiceEnum.echo },
|
||||
{ label: 'Fable', value: Text2SpeechVoiceEnum.fable },
|
||||
{ label: 'Onyx', value: Text2SpeechVoiceEnum.onyx },
|
||||
{ label: 'Nova', value: Text2SpeechVoiceEnum.nova },
|
||||
{ label: 'Shimmer', value: Text2SpeechVoiceEnum.shimmer }
|
||||
...audioSpeechModels.map((item) => item.voices).flat()
|
||||
]}
|
||||
onchange={onclickChange}
|
||||
/>
|
||||
|
@@ -2,11 +2,13 @@ import React from 'react';
|
||||
import { Box, Flex, Button } from '@chakra-ui/react';
|
||||
import { useConfirm } from '@/web/common/hooks/useConfirm';
|
||||
import { useImportStore, SelectorContainer, PreviewFileOrChunk } from './Provider';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
const fileExtension = '.csv';
|
||||
const csvTemplate = `index,content\n"被索引的内容","对应的答案。CSV 中请注意内容不能包含双引号,双引号是列分割符号"\n"什么是 laf","laf 是一个云函数开发平台……",""\n"什么是 sealos","Sealos 是以 kubernetes 为内核的云操作系统发行版,可以……"`;
|
||||
|
||||
const CsvImport = () => {
|
||||
const { t } = useTranslation();
|
||||
const { successChunks, totalChunks, isUnselectedFile, onclickUpload, uploading } =
|
||||
useImportStore();
|
||||
|
||||
@@ -24,6 +26,7 @@ const CsvImport = () => {
|
||||
value: csvTemplate,
|
||||
type: 'text/csv'
|
||||
}}
|
||||
tip={t('dataset.import csv tip')}
|
||||
>
|
||||
<Flex mt={3}>
|
||||
<Button isDisabled={uploading} onClick={openConfirm(onclickUpload)}>
|
||||
|
@@ -55,6 +55,7 @@ export interface Props extends BoxProps {
|
||||
};
|
||||
showUrlFetch?: boolean;
|
||||
showCreateFile?: boolean;
|
||||
tip?: string;
|
||||
}
|
||||
|
||||
const FileSelect = ({
|
||||
@@ -65,6 +66,7 @@ const FileSelect = ({
|
||||
fileTemplate,
|
||||
showUrlFetch = true,
|
||||
showCreateFile = true,
|
||||
tip,
|
||||
...props
|
||||
}: Props) => {
|
||||
const { datasetDetail } = useDatasetStore();
|
||||
@@ -423,6 +425,7 @@ const FileSelect = ({
|
||||
{t('file.Click to download file template', { name: fileTemplate.filename })}
|
||||
</Box>
|
||||
)}
|
||||
{!!tip && <Box color={'myGray.500'}>{tip}</Box>}
|
||||
{selectingText !== undefined && (
|
||||
<FileSelectLoading loading text={selectingText} fixed={false} />
|
||||
)}
|
||||
|
@@ -403,12 +403,14 @@ export const SelectorContainer = ({
|
||||
showUrlFetch,
|
||||
showCreateFile,
|
||||
fileTemplate,
|
||||
tip,
|
||||
children
|
||||
}: {
|
||||
fileExtension: string;
|
||||
showUrlFetch?: boolean;
|
||||
showCreateFile?: boolean;
|
||||
fileTemplate?: FileSelectProps['fileTemplate'];
|
||||
tip?: string;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const { files, setPreviewFile, isUnselectedFile, setFiles, chunkLen } = useImportStore();
|
||||
@@ -433,6 +435,7 @@ export const SelectorContainer = ({
|
||||
showUrlFetch={showUrlFetch}
|
||||
showCreateFile={showCreateFile}
|
||||
fileTemplate={fileTemplate}
|
||||
tip={tip}
|
||||
py={isUnselectedFile ? '100px' : 5}
|
||||
/>
|
||||
{!isUnselectedFile && (
|
||||
|
@@ -117,10 +117,8 @@ const InputDataModal = ({
|
||||
const { mutate: sureImportData, isLoading: isImporting } = useRequest({
|
||||
mutationFn: async (e: InputDataType) => {
|
||||
if (!e.q) {
|
||||
return toast({
|
||||
title: '匹配的知识点不能为空',
|
||||
status: 'warning'
|
||||
});
|
||||
setCurrentTab(TabEnum.content);
|
||||
return Promise.reject(t('dataset.data.input is empty'));
|
||||
}
|
||||
if (countPromptTokens(e.q) >= maxToken) {
|
||||
return toast({
|
||||
|
Reference in New Issue
Block a user