mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-29 01:40:51 +00:00
Add modal to show completion response data (#324)
This commit is contained in:
@@ -78,6 +78,28 @@
|
|||||||
"share": "Share",
|
"share": "Share",
|
||||||
"test": "Test Chat "
|
"test": "Test Chat "
|
||||||
},
|
},
|
||||||
|
"response": {
|
||||||
|
"module cq": "Question classification list",
|
||||||
|
"module cq result": "Classification Result",
|
||||||
|
"module extract description": "Extract Description",
|
||||||
|
"module extract result": "Extract Result",
|
||||||
|
"module historyPreview": "Messages",
|
||||||
|
"module http body": "Body",
|
||||||
|
"module http result": "Response",
|
||||||
|
"module http url": "Request Url",
|
||||||
|
"module limit": "Count Limit",
|
||||||
|
"module maxToken": "MaxTokens",
|
||||||
|
"module model": "Model",
|
||||||
|
"module name": "Name",
|
||||||
|
"module price": "Price",
|
||||||
|
"module question": "Question",
|
||||||
|
"module quoteList": "Quotes",
|
||||||
|
"module runningTime": "Time",
|
||||||
|
"module similarity": "Similarity",
|
||||||
|
"module temperature": "Temperature",
|
||||||
|
"module time": "Running Time",
|
||||||
|
"module tokens": "Tokens"
|
||||||
|
},
|
||||||
"retry": "Retry"
|
"retry": "Retry"
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
|
@@ -78,6 +78,28 @@
|
|||||||
"share": "外部链接调用",
|
"share": "外部链接调用",
|
||||||
"test": "测试"
|
"test": "测试"
|
||||||
},
|
},
|
||||||
|
"response": {
|
||||||
|
"module cq": "问题分类列表",
|
||||||
|
"module cq result": "分类结果",
|
||||||
|
"module extract description": "提取要求描述",
|
||||||
|
"module extract result": "提取结果",
|
||||||
|
"module historyPreview": "完整记录",
|
||||||
|
"module http body": "请求体",
|
||||||
|
"module http result": "响应体",
|
||||||
|
"module http url": "请求地址",
|
||||||
|
"module limit": "单次搜索上限",
|
||||||
|
"module maxToken": "最大 Tokens",
|
||||||
|
"module model": "模型",
|
||||||
|
"module name": "模型名",
|
||||||
|
"module price": "计费",
|
||||||
|
"module question": "问题",
|
||||||
|
"module quoteList": "引用内容",
|
||||||
|
"module runningTime": "运行时长",
|
||||||
|
"module similarity": "相似度",
|
||||||
|
"module temperature": "温度",
|
||||||
|
"module time": "运行时长",
|
||||||
|
"module tokens": "Tokens"
|
||||||
|
},
|
||||||
"retry": "重新生成"
|
"retry": "重新生成"
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
@@ -228,7 +250,7 @@
|
|||||||
"Help Document": "帮助文档"
|
"Help Document": "帮助文档"
|
||||||
},
|
},
|
||||||
"template": {
|
"template": {
|
||||||
"Quote Content Tip": "该配置只有传入引用内容(知识库搜索)时生效。\n可以自定义引用内容的结构,以更好的适配不同场景。可以使用 {{q}}, {{a}}, {{source}} 来作为 “检索内容”、“预期内容”和“来源”,他们都是可选的,下面是默认值:\n{{default}}",
|
"Quote Content Tip": "该配置只有传入引用内容(知识库搜索)时生效。\n可以自定义引用内容的结构,以更好的适配不同场景。可以使用一些变量来进行模板配置:\n{{q}} - 检索内容, {{a}} - 预期内容, {{source}} - 来源,{{file_id}} - 来源文件名,{{index}} - 第n个引用,他们都是可选的,下面是默认值:\n{{default}}",
|
||||||
"Quote Prompt Tip": "该配置只有传入引用内容(知识库搜索)时生效。\n可以用 {{quote}} 来插入引用内容,使用 {{question}} 来插入问题。下面是默认值:\n{{default}}"
|
"Quote Prompt Tip": "该配置只有传入引用内容(知识库搜索)时生效。\n可以用 {{quote}} 来插入引用内容,使用 {{question}} 来插入问题。下面是默认值:\n{{default}}"
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
|
@@ -1,12 +1,15 @@
|
|||||||
import { GET, POST } from './request';
|
import { POST } from './request';
|
||||||
|
|
||||||
export const textCensor = (data: { text: string }) =>
|
export const textCensor = (data: { text: string }) =>
|
||||||
POST<{ code?: number; message: string }>('/plugins/censor/text_baidu', data)
|
POST<{ code?: number; message: string }>('/plugins/censor/text_baidu', data)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res?.code === 5000) {
|
if (res?.code === 5000) {
|
||||||
return Promise.reject(res.message);
|
return Promise.reject(res);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
if (err?.code === 5000) {
|
||||||
|
return Promise.reject(err.message);
|
||||||
|
}
|
||||||
return Promise.resolve('');
|
return Promise.resolve('');
|
||||||
});
|
});
|
||||||
|
@@ -32,16 +32,18 @@ const ResponseTags = ({
|
|||||||
} = useDisclosure();
|
} = useDisclosure();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
chatAccount,
|
||||||
quoteList = [],
|
quoteList = [],
|
||||||
historyPreview = [],
|
historyPreview = [],
|
||||||
runningTime = 0
|
runningTime = 0
|
||||||
} = useMemo(() => {
|
} = useMemo(() => {
|
||||||
const chatData = responseData.find((item) => item.moduleType === FlowModuleTypeEnum.chatNode);
|
const chatData = responseData.find((item) => item.moduleType === FlowModuleTypeEnum.chatNode);
|
||||||
if (!chatData) return {};
|
|
||||||
return {
|
return {
|
||||||
quoteList: chatData.quoteList,
|
chatAccount: responseData.filter((item) => item.moduleType === FlowModuleTypeEnum.chatNode)
|
||||||
historyPreview: chatData.historyPreview,
|
.length,
|
||||||
runningTime: responseData.reduce((sum, item) => sum + (item.runningTime || 0), 0)
|
quoteList: chatData?.quoteList,
|
||||||
|
historyPreview: chatData?.historyPreview,
|
||||||
|
runningTime: responseData.reduce((sum, item) => sum + (item.runningTime || 0), 0).toFixed(2)
|
||||||
};
|
};
|
||||||
}, [responseData]);
|
}, [responseData]);
|
||||||
|
|
||||||
@@ -54,36 +56,47 @@ const ResponseTags = ({
|
|||||||
|
|
||||||
return responseData.length === 0 ? null : (
|
return responseData.length === 0 ? null : (
|
||||||
<Flex alignItems={'center'} mt={2} flexWrap={'wrap'}>
|
<Flex alignItems={'center'} mt={2} flexWrap={'wrap'}>
|
||||||
{quoteList.length > 0 && (
|
{chatAccount === 1 ? (
|
||||||
<MyTooltip label="查看引用">
|
<>
|
||||||
<Tag
|
{quoteList.length > 0 && (
|
||||||
colorSchema="blue"
|
<MyTooltip label="查看引用">
|
||||||
cursor={'pointer'}
|
<Tag
|
||||||
{...TagStyles}
|
colorSchema="blue"
|
||||||
onClick={() => setQuoteModalData(quoteList)}
|
cursor={'pointer'}
|
||||||
>
|
{...TagStyles}
|
||||||
{quoteList.length}条引用
|
onClick={() => setQuoteModalData(quoteList)}
|
||||||
</Tag>
|
>
|
||||||
</MyTooltip>
|
{quoteList.length}条引用
|
||||||
)}
|
</Tag>
|
||||||
{historyPreview.length > 0 && (
|
</MyTooltip>
|
||||||
<MyTooltip label={'点击查看完整对话记录'}>
|
)}
|
||||||
<Tag
|
{historyPreview.length > 0 && (
|
||||||
colorSchema="green"
|
<MyTooltip label={'点击查看完整对话记录'}>
|
||||||
cursor={'pointer'}
|
<Tag
|
||||||
{...TagStyles}
|
colorSchema="green"
|
||||||
onClick={() => setContextModalData(historyPreview)}
|
cursor={'pointer'}
|
||||||
>
|
{...TagStyles}
|
||||||
{historyPreview.length}条上下文
|
onClick={() => setContextModalData(historyPreview)}
|
||||||
</Tag>
|
>
|
||||||
</MyTooltip>
|
{historyPreview.length}条上下文
|
||||||
)}
|
</Tag>
|
||||||
{isPc && runningTime > 0 && (
|
</MyTooltip>
|
||||||
<Tag colorSchema="purple" cursor={'default'} {...TagStyles}>
|
)}
|
||||||
{runningTime}s
|
</>
|
||||||
|
) : (
|
||||||
|
<Tag colorSchema="blue" {...TagStyles}>
|
||||||
|
多组 AI 对话
|
||||||
</Tag>
|
</Tag>
|
||||||
)}
|
)}
|
||||||
<MyTooltip label={'点击查看完整响应值'}>
|
|
||||||
|
{isPc && runningTime > 0 && (
|
||||||
|
<MyTooltip label={'模块运行时间和'}>
|
||||||
|
<Tag colorSchema="purple" cursor={'default'} {...TagStyles}>
|
||||||
|
{runningTime}s
|
||||||
|
</Tag>
|
||||||
|
</MyTooltip>
|
||||||
|
)}
|
||||||
|
<MyTooltip label={'点击查看完整响应'}>
|
||||||
<Tag colorSchema="gray" cursor={'pointer'} {...TagStyles} onClick={onOpenWholeModal}>
|
<Tag colorSchema="gray" cursor={'pointer'} {...TagStyles} onClick={onOpenWholeModal}>
|
||||||
{t('chat.Complete Response')}
|
{t('chat.Complete Response')}
|
||||||
</Tag>
|
</Tag>
|
||||||
|
@@ -1,11 +1,36 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import { Box, ModalBody, useTheme, Flex } from '@chakra-ui/react';
|
import { Box, useTheme, Flex, Image } from '@chakra-ui/react';
|
||||||
import type { ChatHistoryItemResType } from '@/types/chat';
|
import type { ChatHistoryItemResType } from '@/types/chat';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { ModuleTemplatesFlat } from '@/constants/flow/ModuleTemplate';
|
||||||
|
import Tabs from '../Tabs';
|
||||||
|
|
||||||
import MyModal from '../MyModal';
|
import MyModal from '../MyModal';
|
||||||
import MyTooltip from '../MyTooltip';
|
import MyTooltip from '../MyTooltip';
|
||||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||||
|
import { formatPrice } from '@/utils/user';
|
||||||
|
|
||||||
|
function Row({ label, value }: { label: string; value?: string | number | React.ReactNode }) {
|
||||||
|
const theme = useTheme();
|
||||||
|
return value !== undefined && value !== '' && value !== 'undefined' ? (
|
||||||
|
<Box mb={2}>
|
||||||
|
<Box fontSize={['sm', 'md']} mb={1} flex={'0 0 90px'}>
|
||||||
|
{label}:
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
borderRadius={'lg'}
|
||||||
|
border={theme.borders.base}
|
||||||
|
px={3}
|
||||||
|
py={1}
|
||||||
|
position={'relative'}
|
||||||
|
whiteSpace={'pre-wrap'}
|
||||||
|
fontSize={'sm'}
|
||||||
|
>
|
||||||
|
{value}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
) : null;
|
||||||
|
}
|
||||||
|
|
||||||
const ResponseModal = ({
|
const ResponseModal = ({
|
||||||
response,
|
response,
|
||||||
@@ -14,56 +39,159 @@ const ResponseModal = ({
|
|||||||
response: ChatHistoryItemResType[];
|
response: ChatHistoryItemResType[];
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const formatResponse = useMemo(
|
const list = useMemo(
|
||||||
() =>
|
() =>
|
||||||
response.map((item) => {
|
response.map((item, i) => ({
|
||||||
const copy = { ...item };
|
label: (
|
||||||
delete copy.historyPreview;
|
<Flex alignItems={'center'} justifyContent={'center'} px={2}>
|
||||||
delete copy.quoteList;
|
<Image
|
||||||
return copy;
|
mr={2}
|
||||||
}),
|
src={
|
||||||
|
ModuleTemplatesFlat.find((template) => item.moduleType === template.flowType)?.logo
|
||||||
|
}
|
||||||
|
alt={''}
|
||||||
|
w={['14px', '16px']}
|
||||||
|
/>
|
||||||
|
{item.moduleName}
|
||||||
|
</Flex>
|
||||||
|
),
|
||||||
|
id: `${i}`
|
||||||
|
})),
|
||||||
[response]
|
[response]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [currentTab, setCurrentTab] = useState(`0`);
|
||||||
|
|
||||||
|
const activeModule = useMemo(() => response[Number(currentTab)], [currentTab, response]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MyModal
|
<MyModal
|
||||||
|
isCentered
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
h={['90vh', '80vh']}
|
h={['90vh', '80vh']}
|
||||||
minW={['90vw', '600px']}
|
w={['90vw', '500px']}
|
||||||
title={
|
title={
|
||||||
<Flex alignItems={'center'}>
|
<Flex alignItems={'center'}>
|
||||||
{t('chat.Complete Response')}
|
{t('chat.Complete Response')}
|
||||||
<MyTooltip
|
<MyTooltip label={'从左往右,为各个模块的响应顺序'}>
|
||||||
label={
|
|
||||||
'moduleName: 模型名\nprice: 价格,倍率:100000\nmodel?: 模型名\ntokens?: token 消耗\n\nanswer?: 回答内容\nquestion?: 问题\ntemperature?: 温度\nmaxToken?: 最大 tokens\n\nsimilarity?: 相似度\nlimit?: 单次搜索结果\n\ncqList?: 问题分类列表\ncqResult?: 分类结果\n\nextractDescription?: 内容提取描述\nextractResult?: 提取结果'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<QuestionOutlineIcon ml={2} />
|
<QuestionOutlineIcon ml={2} />
|
||||||
</MyTooltip>
|
</MyTooltip>
|
||||||
</Flex>
|
</Flex>
|
||||||
}
|
}
|
||||||
isCentered
|
|
||||||
>
|
>
|
||||||
<ModalBody>
|
<Flex h={'100%'} flexDirection={'column'}>
|
||||||
{formatResponse.map((item, i) => (
|
<Box>
|
||||||
<Box
|
<Tabs list={list} activeId={currentTab} onChange={setCurrentTab} />
|
||||||
key={i}
|
</Box>
|
||||||
p={2}
|
<Box py={2} px={4} flex={'1 0 0'} overflow={'auto'}>
|
||||||
pt={[0, 2]}
|
<Row label={t('chat.response.module name')} value={activeModule?.moduleName} />
|
||||||
borderRadius={'lg'}
|
<Row
|
||||||
border={theme.borders.base}
|
label={t('chat.response.module price')}
|
||||||
_notLast={{ mb: 2 }}
|
value={`¥${formatPrice(activeModule?.price)}`}
|
||||||
position={'relative'}
|
/>
|
||||||
whiteSpace={'pre-wrap'}
|
<Row
|
||||||
>
|
label={t('chat.response.module time')}
|
||||||
{JSON.stringify(item, null, 2)}
|
value={`${activeModule?.runningTime || 0}s`}
|
||||||
</Box>
|
/>
|
||||||
))}
|
<Row label={t('chat.response.module tokens')} value={`${activeModule?.tokens}`} />
|
||||||
</ModalBody>
|
<Row label={t('chat.response.module model')} value={activeModule?.model} />
|
||||||
|
|
||||||
|
{/* ai chat */}
|
||||||
|
<Row label={t('chat.response.module question')} value={activeModule?.question} />
|
||||||
|
<Row label={t('chat.response.module temperature')} value={activeModule?.temperature} />
|
||||||
|
<Row label={t('chat.response.module maxToken')} value={activeModule?.maxToken} />
|
||||||
|
<Row
|
||||||
|
label={t('chat.response.module quoteList')}
|
||||||
|
value={(() => {
|
||||||
|
try {
|
||||||
|
JSON.stringify(activeModule.quoteList, null, 2);
|
||||||
|
} catch (error) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
})()}
|
||||||
|
/>
|
||||||
|
<Row
|
||||||
|
label={t('chat.response.module historyPreview')}
|
||||||
|
value={(() => {
|
||||||
|
if (!activeModule?.historyPreview) return '';
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{activeModule.historyPreview.map((item, i) => (
|
||||||
|
<Box key={i} _notLast={{ mb: 3, borderBottom: theme.borders.base }} pb={3}>
|
||||||
|
<Box fontWeight={'bold'}>{item.obj}</Box>
|
||||||
|
<Box>{item.value}</Box>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* dataset search */}
|
||||||
|
<Row label={t('chat.response.module similarity')} value={activeModule?.similarity} />
|
||||||
|
<Row label={t('chat.response.module limit')} value={activeModule?.limit} />
|
||||||
|
|
||||||
|
{/* classify question */}
|
||||||
|
<Row
|
||||||
|
label={t('chat.response.module cq')}
|
||||||
|
value={(() => {
|
||||||
|
if (!activeModule?.cqList) return '';
|
||||||
|
return (
|
||||||
|
<Box as={'ol'} px={3}>
|
||||||
|
{activeModule.cqList.map((item) => (
|
||||||
|
<Box key={item.key} as={'li'}>
|
||||||
|
{item.value}
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
/>
|
||||||
|
<Row label={t('chat.response.module cq result')} value={activeModule?.cqResult} />
|
||||||
|
|
||||||
|
{/* extract */}
|
||||||
|
<Row
|
||||||
|
label={t('chat.response.module extract description')}
|
||||||
|
value={activeModule?.extractDescription}
|
||||||
|
/>
|
||||||
|
<Row
|
||||||
|
label={t('chat.response.module extract result')}
|
||||||
|
value={(() => {
|
||||||
|
try {
|
||||||
|
return JSON.stringify(activeModule?.extractResult, null, 2);
|
||||||
|
} catch (error) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
})()}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* http */}
|
||||||
|
<Row
|
||||||
|
label={t('chat.response.module http body')}
|
||||||
|
value={(() => {
|
||||||
|
try {
|
||||||
|
return JSON.stringify(activeModule?.body, null, 2);
|
||||||
|
} catch (error) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
})()}
|
||||||
|
/>
|
||||||
|
<Row
|
||||||
|
label={t('chat.response.module http result')}
|
||||||
|
value={(() => {
|
||||||
|
try {
|
||||||
|
return JSON.stringify(activeModule?.httpResult, null, 2);
|
||||||
|
} catch (error) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
})()}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
</MyModal>
|
</MyModal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
64
client/src/components/Markdown/chat/Quote.tsx
Normal file
64
client/src/components/Markdown/chat/Quote.tsx
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import { Box, useTheme } from '@chakra-ui/react';
|
||||||
|
import { getFileAndOpen } from '@/utils/common/file';
|
||||||
|
import { useToast } from '@/hooks/useToast';
|
||||||
|
import { getErrText } from '@/utils/tools';
|
||||||
|
|
||||||
|
type QuoteItemType = {
|
||||||
|
file_id?: string;
|
||||||
|
filename: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const QuoteBlock = ({ code }: { code: string }) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
const { toast } = useToast();
|
||||||
|
const quoteList = useMemo(() => {
|
||||||
|
try {
|
||||||
|
return JSON.parse(code) as QuoteItemType[];
|
||||||
|
} catch (error) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}, [code]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box mt={3} pt={2} borderTop={theme.borders.base}>
|
||||||
|
{quoteList.length > 0 ? (
|
||||||
|
<>
|
||||||
|
<Box>本次回答的引用:</Box>
|
||||||
|
<Box as={'ol'}>
|
||||||
|
{quoteList.map((item, i) => (
|
||||||
|
<Box
|
||||||
|
key={i}
|
||||||
|
as={'li'}
|
||||||
|
{...(item.file_id
|
||||||
|
? {
|
||||||
|
textDecoration: 'underline',
|
||||||
|
color: 'myBlue.800',
|
||||||
|
cursor: 'pointer'
|
||||||
|
}
|
||||||
|
: {})}
|
||||||
|
onClick={async () => {
|
||||||
|
if (!item.file_id) return;
|
||||||
|
try {
|
||||||
|
await getFileAndOpen(item.file_id);
|
||||||
|
} catch (error) {
|
||||||
|
toast({
|
||||||
|
status: 'warning',
|
||||||
|
title: getErrText(error, '打开文件失败')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.filename}
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Box>正在生成引用……</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default QuoteBlock;
|
@@ -15,21 +15,32 @@ const MermaidCodeBlock = dynamic(() => import('./img/MermaidCodeBlock'));
|
|||||||
const MdImage = dynamic(() => import('./img/Image'));
|
const MdImage = dynamic(() => import('./img/Image'));
|
||||||
const ChatGuide = dynamic(() => import('./chat/Guide'));
|
const ChatGuide = dynamic(() => import('./chat/Guide'));
|
||||||
const EChartsCodeBlock = dynamic(() => import('./img/EChartsCodeBlock'));
|
const EChartsCodeBlock = dynamic(() => import('./img/EChartsCodeBlock'));
|
||||||
|
const QuoteBlock = dynamic(() => import('./chat/Quote'));
|
||||||
|
|
||||||
|
export enum CodeClassName {
|
||||||
|
guide = 'guide',
|
||||||
|
mermaid = 'mermaid',
|
||||||
|
echarts = 'echarts',
|
||||||
|
quote = 'quote'
|
||||||
|
}
|
||||||
|
|
||||||
function Code({ inline, className, children }: any) {
|
function Code({ inline, className, children }: any) {
|
||||||
const match = /language-(\w+)/.exec(className || '');
|
const match = /language-(\w+)/.exec(className || '');
|
||||||
const codeType = match?.[1];
|
const codeType = match?.[1];
|
||||||
|
|
||||||
if (codeType === 'mermaid') {
|
if (codeType === CodeClassName.mermaid) {
|
||||||
return <MermaidCodeBlock code={String(children)} />;
|
return <MermaidCodeBlock code={String(children)} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codeType === 'guide') {
|
if (codeType === CodeClassName.guide) {
|
||||||
return <ChatGuide text={String(children)} />;
|
return <ChatGuide text={String(children)} />;
|
||||||
}
|
}
|
||||||
if (codeType === 'echarts') {
|
if (codeType === CodeClassName.echarts) {
|
||||||
return <EChartsCodeBlock code={String(children)} />;
|
return <EChartsCodeBlock code={String(children)} />;
|
||||||
}
|
}
|
||||||
|
if (codeType === CodeClassName.quote) {
|
||||||
|
return <QuoteBlock code={String(children)} />;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<CodeLight className={className} inline={inline} match={match}>
|
<CodeLight className={className} inline={inline} match={match}>
|
||||||
{children}
|
{children}
|
||||||
|
@@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
interface Props extends GridProps {
|
interface Props extends GridProps {
|
||||||
list: { id: string; label: string }[];
|
list: { id: string; label: string | React.ReactNode }[];
|
||||||
activeId: string;
|
activeId: string;
|
||||||
size?: 'sm' | 'md' | 'lg';
|
size?: 'sm' | 'md' | 'lg';
|
||||||
onChange: (id: string) => void;
|
onChange: (id: string) => void;
|
||||||
@@ -23,13 +23,13 @@ const Tabs = ({ list, size = 'md', activeId, onChange, ...props }: Props) => {
|
|||||||
};
|
};
|
||||||
case 'md':
|
case 'md':
|
||||||
return {
|
return {
|
||||||
fontSize: 'md',
|
fontSize: ['sm', 'md'],
|
||||||
outP: '4px',
|
outP: '4px',
|
||||||
inlineP: 1
|
inlineP: 1
|
||||||
};
|
};
|
||||||
case 'lg':
|
case 'lg':
|
||||||
return {
|
return {
|
||||||
fontSize: 'lg',
|
fontSize: ['md', 'lg'],
|
||||||
outP: '5px',
|
outP: '5px',
|
||||||
inlineP: 2
|
inlineP: 2
|
||||||
};
|
};
|
||||||
@@ -68,7 +68,7 @@ const Tabs = ({ list, size = 'md', activeId, onChange, ...props }: Props) => {
|
|||||||
onChange(item.id);
|
onChange(item.id);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t(item.label) || item.label}
|
{typeof item.label === 'string' ? t(item.label) : item.label}
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@@ -32,6 +32,7 @@ import { getSystemTime } from '@/utils/user';
|
|||||||
import { authOutLinkChat } from '@/service/support/outLink/auth';
|
import { authOutLinkChat } from '@/service/support/outLink/auth';
|
||||||
import requestIp from 'request-ip';
|
import requestIp from 'request-ip';
|
||||||
import { replaceVariable } from '@/utils/common/tools/text';
|
import { replaceVariable } from '@/utils/common/tools/text';
|
||||||
|
import { ModuleDispatchProps } from '@/types/core/modules';
|
||||||
|
|
||||||
export type MessageItemType = ChatCompletionRequestMessage & { dataId?: string };
|
export type MessageItemType = ChatCompletionRequestMessage & { dataId?: string };
|
||||||
type FastGptWebChatProps = {
|
type FastGptWebChatProps = {
|
||||||
@@ -365,13 +366,15 @@ export async function dispatchModules({
|
|||||||
module.inputs.forEach((item: any) => {
|
module.inputs.forEach((item: any) => {
|
||||||
params[item.key] = item.value;
|
params[item.key] = item.value;
|
||||||
});
|
});
|
||||||
const props: Record<string, any> = {
|
const props: ModuleDispatchProps<Record<string, any>> = {
|
||||||
res,
|
res,
|
||||||
stream,
|
stream,
|
||||||
detail,
|
detail,
|
||||||
|
variables,
|
||||||
|
moduleName: module.name,
|
||||||
outputs: module.outputs,
|
outputs: module.outputs,
|
||||||
userOpenaiAccount: user?.openaiAccount,
|
userOpenaiAccount: user?.openaiAccount,
|
||||||
...params
|
inputs: params
|
||||||
};
|
};
|
||||||
|
|
||||||
const dispatchRes = await (async () => {
|
const dispatchRes = await (async () => {
|
||||||
|
@@ -4,6 +4,7 @@ import { connectToDatabase } from '@/service/mongo';
|
|||||||
import { authUser } from '@/service/utils/auth';
|
import { authUser } from '@/service/utils/auth';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import { ERROR_ENUM } from '@/service/errorCode';
|
import { ERROR_ENUM } from '@/service/errorCode';
|
||||||
|
import { GridFSStorage } from '@/service/lib/gridfs';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
try {
|
try {
|
||||||
@@ -17,6 +18,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
|
|
||||||
const { userId } = await authUser({ req });
|
const { userId } = await authUser({ req });
|
||||||
|
|
||||||
|
// auth file
|
||||||
|
const gridFs = new GridFSStorage('dataset', userId);
|
||||||
|
await gridFs.findAndAuthFile(fileId);
|
||||||
|
|
||||||
const token = await createFileToken({
|
const token = await createFileToken({
|
||||||
userId,
|
userId,
|
||||||
fileId
|
fileId
|
||||||
|
@@ -2,7 +2,6 @@ import React, { useState, useCallback } from 'react';
|
|||||||
import { Box, Flex, Button, Textarea, IconButton, BoxProps } from '@chakra-ui/react';
|
import { Box, Flex, Button, Textarea, IconButton, BoxProps } from '@chakra-ui/react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { insertData2Kb, putKbDataById, delOneKbDataByDataId } from '@/api/plugins/kb';
|
import { insertData2Kb, putKbDataById, delOneKbDataByDataId } from '@/api/plugins/kb';
|
||||||
import { getFileViewUrl } from '@/api/support/file';
|
|
||||||
import { useToast } from '@/hooks/useToast';
|
import { useToast } from '@/hooks/useToast';
|
||||||
import { getErrText } from '@/utils/tools';
|
import { getErrText } from '@/utils/tools';
|
||||||
import MyIcon from '@/components/Icon';
|
import MyIcon from '@/components/Icon';
|
||||||
@@ -13,6 +12,7 @@ import { useQuery } from '@tanstack/react-query';
|
|||||||
import { DatasetItemType } from '@/types/plugin';
|
import { DatasetItemType } from '@/types/plugin';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useDatasetStore } from '@/store/dataset';
|
import { useDatasetStore } from '@/store/dataset';
|
||||||
|
import { getFileAndOpen } from '@/utils/common/file';
|
||||||
|
|
||||||
export type FormData = { dataId?: string } & DatasetItemType;
|
export type FormData = { dataId?: string } & DatasetItemType;
|
||||||
|
|
||||||
@@ -267,9 +267,7 @@ export function RawFileText({ fileId, filename = '', ...props }: RawFileTextProp
|
|||||||
textDecoration: 'underline',
|
textDecoration: 'underline',
|
||||||
onClick: async () => {
|
onClick: async () => {
|
||||||
try {
|
try {
|
||||||
const url = await getFileViewUrl(fileId);
|
await getFileAndOpen(fileId);
|
||||||
const asPath = `${location.origin}${url}`;
|
|
||||||
window.open(asPath, '_blank');
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast({
|
toast({
|
||||||
title: getErrText(error, '获取文件地址失败'),
|
title: getErrText(error, '获取文件地址失败'),
|
||||||
|
@@ -5,19 +5,18 @@ import { ChatRoleEnum, TaskResponseKeyEnum } from '@/constants/chat';
|
|||||||
import { getAIChatApi, axiosConfig } from '@/service/lib/openai';
|
import { getAIChatApi, axiosConfig } from '@/service/lib/openai';
|
||||||
import type { ClassifyQuestionAgentItemType } from '@/types/app';
|
import type { ClassifyQuestionAgentItemType } from '@/types/app';
|
||||||
import { countModelPrice } from '@/service/events/pushBill';
|
import { countModelPrice } from '@/service/events/pushBill';
|
||||||
import { UserModelSchema } from '@/types/mongoSchema';
|
|
||||||
import { getModel } from '@/service/utils/data';
|
import { getModel } from '@/service/utils/data';
|
||||||
import { SystemInputEnum } from '@/constants/app';
|
import { SystemInputEnum } from '@/constants/app';
|
||||||
import { SpecialInputKeyEnum } from '@/constants/flow';
|
import { SpecialInputKeyEnum } from '@/constants/flow';
|
||||||
import { FlowModuleTypeEnum } from '@/constants/flow';
|
import { FlowModuleTypeEnum } from '@/constants/flow';
|
||||||
|
import { ModuleDispatchProps } from '@/types/core/modules';
|
||||||
|
|
||||||
export type CQProps = {
|
export type CQProps = ModuleDispatchProps<{
|
||||||
systemPrompt?: string;
|
systemPrompt?: string;
|
||||||
history?: ChatItemType[];
|
history?: ChatItemType[];
|
||||||
[SystemInputEnum.userChatInput]: string;
|
[SystemInputEnum.userChatInput]: string;
|
||||||
userOpenaiAccount: UserModelSchema['openaiAccount'];
|
|
||||||
[SpecialInputKeyEnum.agents]: ClassifyQuestionAgentItemType[];
|
[SpecialInputKeyEnum.agents]: ClassifyQuestionAgentItemType[];
|
||||||
};
|
}>;
|
||||||
export type CQResponse = {
|
export type CQResponse = {
|
||||||
[TaskResponseKeyEnum.responseData]: ChatHistoryItemResType;
|
[TaskResponseKeyEnum.responseData]: ChatHistoryItemResType;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
@@ -29,7 +28,11 @@ const maxTokens = 3000;
|
|||||||
|
|
||||||
/* request openai chat */
|
/* request openai chat */
|
||||||
export const dispatchClassifyQuestion = async (props: Record<string, any>): Promise<CQResponse> => {
|
export const dispatchClassifyQuestion = async (props: Record<string, any>): Promise<CQResponse> => {
|
||||||
const { agents, systemPrompt, history = [], userChatInput, userOpenaiAccount } = props as CQProps;
|
const {
|
||||||
|
moduleName,
|
||||||
|
userOpenaiAccount,
|
||||||
|
inputs: { agents, systemPrompt, history = [], userChatInput }
|
||||||
|
} = props as CQProps;
|
||||||
|
|
||||||
if (!userChatInput) {
|
if (!userChatInput) {
|
||||||
return Promise.reject('Input is empty');
|
return Promise.reject('Input is empty');
|
||||||
@@ -97,6 +100,7 @@ export const dispatchClassifyQuestion = async (props: Record<string, any>): Prom
|
|||||||
[result.key]: 1,
|
[result.key]: 1,
|
||||||
[TaskResponseKeyEnum.responseData]: {
|
[TaskResponseKeyEnum.responseData]: {
|
||||||
moduleType: FlowModuleTypeEnum.classifyQuestion,
|
moduleType: FlowModuleTypeEnum.classifyQuestion,
|
||||||
|
moduleName,
|
||||||
price: userOpenaiAccount?.key ? 0 : countModelPrice({ model: agentModel, tokens }),
|
price: userOpenaiAccount?.key ? 0 : countModelPrice({ model: agentModel, tokens }),
|
||||||
model: getModel(agentModel)?.name || agentModel,
|
model: getModel(agentModel)?.name || agentModel,
|
||||||
tokens,
|
tokens,
|
||||||
|
@@ -6,17 +6,16 @@ import { getAIChatApi, axiosConfig } from '@/service/lib/openai';
|
|||||||
import type { ContextExtractAgentItemType } from '@/types/app';
|
import type { ContextExtractAgentItemType } from '@/types/app';
|
||||||
import { ContextExtractEnum } from '@/constants/flow/flowField';
|
import { ContextExtractEnum } from '@/constants/flow/flowField';
|
||||||
import { countModelPrice } from '@/service/events/pushBill';
|
import { countModelPrice } from '@/service/events/pushBill';
|
||||||
import { UserModelSchema } from '@/types/mongoSchema';
|
|
||||||
import { getModel } from '@/service/utils/data';
|
import { getModel } from '@/service/utils/data';
|
||||||
import { FlowModuleTypeEnum } from '@/constants/flow';
|
import { FlowModuleTypeEnum } from '@/constants/flow';
|
||||||
|
import { ModuleDispatchProps } from '@/types/core/modules';
|
||||||
|
|
||||||
export type Props = {
|
export type Props = ModuleDispatchProps<{
|
||||||
userOpenaiAccount: UserModelSchema['openaiAccount'];
|
|
||||||
history?: ChatItemType[];
|
history?: ChatItemType[];
|
||||||
[ContextExtractEnum.content]: string;
|
[ContextExtractEnum.content]: string;
|
||||||
[ContextExtractEnum.extractKeys]: ContextExtractAgentItemType[];
|
[ContextExtractEnum.extractKeys]: ContextExtractAgentItemType[];
|
||||||
[ContextExtractEnum.description]: string;
|
[ContextExtractEnum.description]: string;
|
||||||
};
|
}>;
|
||||||
export type Response = {
|
export type Response = {
|
||||||
[ContextExtractEnum.success]?: boolean;
|
[ContextExtractEnum.success]?: boolean;
|
||||||
[ContextExtractEnum.failed]?: boolean;
|
[ContextExtractEnum.failed]?: boolean;
|
||||||
@@ -29,11 +28,9 @@ const agentFunName = 'agent_extract_data';
|
|||||||
const maxTokens = 4000;
|
const maxTokens = 4000;
|
||||||
|
|
||||||
export async function dispatchContentExtract({
|
export async function dispatchContentExtract({
|
||||||
|
moduleName,
|
||||||
userOpenaiAccount,
|
userOpenaiAccount,
|
||||||
content,
|
inputs: { content, extractKeys, history = [], description }
|
||||||
extractKeys,
|
|
||||||
history = [],
|
|
||||||
description
|
|
||||||
}: Props): Promise<Response> {
|
}: Props): Promise<Response> {
|
||||||
if (!content) {
|
if (!content) {
|
||||||
return Promise.reject('Input is empty');
|
return Promise.reject('Input is empty');
|
||||||
@@ -120,6 +117,7 @@ export async function dispatchContentExtract({
|
|||||||
...arg,
|
...arg,
|
||||||
[TaskResponseKeyEnum.responseData]: {
|
[TaskResponseKeyEnum.responseData]: {
|
||||||
moduleType: FlowModuleTypeEnum.contentExtract,
|
moduleType: FlowModuleTypeEnum.contentExtract,
|
||||||
|
moduleName,
|
||||||
price: userOpenaiAccount?.key ? 0 : countModelPrice({ model: agentModel, tokens }),
|
price: userOpenaiAccount?.key ? 0 : countModelPrice({ model: agentModel, tokens }),
|
||||||
model: getModel(agentModel)?.name || agentModel,
|
model: getModel(agentModel)?.name || agentModel,
|
||||||
tokens,
|
tokens,
|
||||||
|
@@ -11,7 +11,6 @@ import { TaskResponseKeyEnum } from '@/constants/chat';
|
|||||||
import { getChatModel } from '@/service/utils/data';
|
import { getChatModel } from '@/service/utils/data';
|
||||||
import { countModelPrice } from '@/service/events/pushBill';
|
import { countModelPrice } from '@/service/events/pushBill';
|
||||||
import { ChatModelItemType } from '@/types/model';
|
import { ChatModelItemType } from '@/types/model';
|
||||||
import { UserModelSchema } from '@/types/mongoSchema';
|
|
||||||
import { textCensor } from '@/api/service/plugins';
|
import { textCensor } from '@/api/service/plugins';
|
||||||
import { ChatCompletionRequestMessageRoleEnum } from 'openai';
|
import { ChatCompletionRequestMessageRoleEnum } from 'openai';
|
||||||
import { AppModuleItemType } from '@/types/app';
|
import { AppModuleItemType } from '@/types/app';
|
||||||
@@ -21,19 +20,16 @@ import { defaultQuotePrompt, defaultQuoteTemplate } from '@/prompts/core/AIChat'
|
|||||||
import type { AIChatProps } from '@/types/core/aiChat';
|
import type { AIChatProps } from '@/types/core/aiChat';
|
||||||
import { replaceVariable } from '@/utils/common/tools/text';
|
import { replaceVariable } from '@/utils/common/tools/text';
|
||||||
import { FlowModuleTypeEnum } from '@/constants/flow';
|
import { FlowModuleTypeEnum } from '@/constants/flow';
|
||||||
|
import { ModuleDispatchProps } from '@/types/core/modules';
|
||||||
|
|
||||||
export type ChatProps = AIChatProps & {
|
export type ChatProps = ModuleDispatchProps<
|
||||||
res: NextApiResponse;
|
AIChatProps & {
|
||||||
history?: ChatItemType[];
|
userChatInput: string;
|
||||||
userChatInput: string;
|
history?: ChatItemType[];
|
||||||
stream?: boolean;
|
quoteQA?: QuoteItemType[];
|
||||||
detail?: boolean;
|
limitPrompt?: string;
|
||||||
quoteQA?: QuoteItemType[];
|
}
|
||||||
systemPrompt?: string;
|
>;
|
||||||
limitPrompt?: string;
|
|
||||||
userOpenaiAccount: UserModelSchema['openaiAccount'];
|
|
||||||
outputs: AppModuleItemType['outputs'];
|
|
||||||
};
|
|
||||||
export type ChatResponse = {
|
export type ChatResponse = {
|
||||||
[TaskResponseKeyEnum.answerText]: string;
|
[TaskResponseKeyEnum.answerText]: string;
|
||||||
[TaskResponseKeyEnum.responseData]: ChatHistoryItemResType;
|
[TaskResponseKeyEnum.responseData]: ChatHistoryItemResType;
|
||||||
@@ -41,24 +37,27 @@ export type ChatResponse = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* request openai chat */
|
/* request openai chat */
|
||||||
export const dispatchChatCompletion = async (props: Record<string, any>): Promise<ChatResponse> => {
|
export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResponse> => {
|
||||||
let {
|
let {
|
||||||
res,
|
res,
|
||||||
model = global.chatModels[0]?.model,
|
moduleName,
|
||||||
temperature = 0,
|
|
||||||
maxToken = 4000,
|
|
||||||
stream = false,
|
stream = false,
|
||||||
detail = false,
|
detail = false,
|
||||||
history = [],
|
|
||||||
quoteQA = [],
|
|
||||||
userChatInput,
|
|
||||||
systemPrompt = '',
|
|
||||||
limitPrompt,
|
|
||||||
quoteTemplate,
|
|
||||||
quotePrompt,
|
|
||||||
userOpenaiAccount,
|
userOpenaiAccount,
|
||||||
outputs
|
outputs,
|
||||||
} = props as ChatProps;
|
inputs: {
|
||||||
|
model = global.chatModels[0]?.model,
|
||||||
|
temperature = 0,
|
||||||
|
maxToken = 4000,
|
||||||
|
history = [],
|
||||||
|
quoteQA = [],
|
||||||
|
userChatInput,
|
||||||
|
systemPrompt = '',
|
||||||
|
limitPrompt,
|
||||||
|
quoteTemplate,
|
||||||
|
quotePrompt
|
||||||
|
}
|
||||||
|
} = props;
|
||||||
if (!userChatInput) {
|
if (!userChatInput) {
|
||||||
return Promise.reject('Question is empty');
|
return Promise.reject('Question is empty');
|
||||||
}
|
}
|
||||||
@@ -177,6 +176,7 @@ export const dispatchChatCompletion = async (props: Record<string, any>): Promis
|
|||||||
[TaskResponseKeyEnum.answerText]: answerText,
|
[TaskResponseKeyEnum.answerText]: answerText,
|
||||||
[TaskResponseKeyEnum.responseData]: {
|
[TaskResponseKeyEnum.responseData]: {
|
||||||
moduleType: FlowModuleTypeEnum.chatNode,
|
moduleType: FlowModuleTypeEnum.chatNode,
|
||||||
|
moduleName,
|
||||||
price: userOpenaiAccount?.key ? 0 : countModelPrice({ model, tokens: totalTokens }),
|
price: userOpenaiAccount?.key ? 0 : countModelPrice({ model, tokens: totalTokens }),
|
||||||
model: modelConstantsData.name,
|
model: modelConstantsData.name,
|
||||||
tokens: totalTokens,
|
tokens: totalTokens,
|
||||||
@@ -194,15 +194,18 @@ function filterQuote({
|
|||||||
model,
|
model,
|
||||||
quoteTemplate
|
quoteTemplate
|
||||||
}: {
|
}: {
|
||||||
quoteQA: ChatProps['quoteQA'];
|
quoteQA: ChatProps['inputs']['quoteQA'];
|
||||||
model: ChatModelItemType;
|
model: ChatModelItemType;
|
||||||
quoteTemplate?: string;
|
quoteTemplate?: string;
|
||||||
}) {
|
}) {
|
||||||
const sliceResult = sliceMessagesTB({
|
const sliceResult = sliceMessagesTB({
|
||||||
maxTokens: model.quoteMaxToken,
|
maxTokens: model.quoteMaxToken,
|
||||||
messages: quoteQA.map((item) => ({
|
messages: quoteQA.map((item, index) => ({
|
||||||
obj: ChatRoleEnum.System,
|
obj: ChatRoleEnum.System,
|
||||||
value: replaceVariable(quoteTemplate || defaultQuoteTemplate, item)
|
value: replaceVariable(quoteTemplate || defaultQuoteTemplate, {
|
||||||
|
...item,
|
||||||
|
index: `${index + 1}`
|
||||||
|
})
|
||||||
}))
|
}))
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -212,7 +215,12 @@ function filterQuote({
|
|||||||
const quoteText =
|
const quoteText =
|
||||||
filterQuoteQA.length > 0
|
filterQuoteQA.length > 0
|
||||||
? `${filterQuoteQA
|
? `${filterQuoteQA
|
||||||
.map((item) => replaceVariable(quoteTemplate || defaultQuoteTemplate, item))
|
.map((item, index) =>
|
||||||
|
replaceVariable(quoteTemplate || defaultQuoteTemplate, {
|
||||||
|
...item,
|
||||||
|
index: `${index + 1}`
|
||||||
|
})
|
||||||
|
)
|
||||||
.join('\n')}`
|
.join('\n')}`
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
@@ -232,7 +240,7 @@ function getChatMessages({
|
|||||||
}: {
|
}: {
|
||||||
quotePrompt?: string;
|
quotePrompt?: string;
|
||||||
quoteText: string;
|
quoteText: string;
|
||||||
history: ChatProps['history'];
|
history: ChatProps['inputs']['history'];
|
||||||
systemPrompt: string;
|
systemPrompt: string;
|
||||||
limitPrompt?: string;
|
limitPrompt?: string;
|
||||||
userChatInput: string;
|
userChatInput: string;
|
||||||
@@ -288,7 +296,7 @@ function getMaxTokens({
|
|||||||
}: {
|
}: {
|
||||||
maxToken: number;
|
maxToken: number;
|
||||||
model: ChatModelItemType;
|
model: ChatModelItemType;
|
||||||
filterMessages: ChatProps['history'];
|
filterMessages: ChatProps['inputs']['history'];
|
||||||
}) {
|
}) {
|
||||||
const tokensLimit = model.contextMaxToken;
|
const tokensLimit = model.contextMaxToken;
|
||||||
/* count response max token */
|
/* count response max token */
|
||||||
|
@@ -1,13 +1,16 @@
|
|||||||
import { SystemInputEnum } from '@/constants/app';
|
import { SystemInputEnum } from '@/constants/app';
|
||||||
import { ChatItemType } from '@/types/chat';
|
import { ChatItemType } from '@/types/chat';
|
||||||
|
import type { ModuleDispatchProps } from '@/types/core/modules';
|
||||||
|
|
||||||
export type HistoryProps = {
|
export type HistoryProps = ModuleDispatchProps<{
|
||||||
maxContext: number;
|
maxContext: number;
|
||||||
[SystemInputEnum.history]: ChatItemType[];
|
[SystemInputEnum.history]: ChatItemType[];
|
||||||
};
|
}>;
|
||||||
|
|
||||||
export const dispatchHistory = (props: Record<string, any>) => {
|
export const dispatchHistory = (props: Record<string, any>) => {
|
||||||
const { maxContext = 5, history = [] } = props as HistoryProps;
|
const {
|
||||||
|
inputs: { maxContext = 5, history = [] }
|
||||||
|
} = props as HistoryProps;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
history: maxContext > 0 ? history.slice(-maxContext) : []
|
history: maxContext > 0 ? history.slice(-maxContext) : []
|
||||||
|
@@ -1,11 +1,14 @@
|
|||||||
import { SystemInputEnum } from '@/constants/app';
|
import { SystemInputEnum } from '@/constants/app';
|
||||||
|
import type { ModuleDispatchProps } from '@/types/core/modules';
|
||||||
|
|
||||||
export type UserChatInputProps = {
|
export type UserChatInputProps = ModuleDispatchProps<{
|
||||||
[SystemInputEnum.userChatInput]: string;
|
[SystemInputEnum.userChatInput]: string;
|
||||||
};
|
}>;
|
||||||
|
|
||||||
export const dispatchChatInput = (props: Record<string, any>) => {
|
export const dispatchChatInput = (props: Record<string, any>) => {
|
||||||
const { userChatInput } = props as UserChatInputProps;
|
const {
|
||||||
|
inputs: { userChatInput }
|
||||||
|
} = props as UserChatInputProps;
|
||||||
return {
|
return {
|
||||||
userChatInput
|
userChatInput
|
||||||
};
|
};
|
||||||
|
@@ -7,13 +7,14 @@ import type { SelectedKbType } from '@/types/plugin';
|
|||||||
import type { QuoteItemType } from '@/types/chat';
|
import type { QuoteItemType } from '@/types/chat';
|
||||||
import { PgDatasetTableName } from '@/constants/plugin';
|
import { PgDatasetTableName } from '@/constants/plugin';
|
||||||
import { FlowModuleTypeEnum } from '@/constants/flow';
|
import { FlowModuleTypeEnum } from '@/constants/flow';
|
||||||
|
import { ModuleDispatchProps } from '@/types/core/modules';
|
||||||
|
|
||||||
type KBSearchProps = {
|
type KBSearchProps = ModuleDispatchProps<{
|
||||||
kbList: SelectedKbType;
|
kbList: SelectedKbType;
|
||||||
similarity: number;
|
similarity: number;
|
||||||
limit: number;
|
limit: number;
|
||||||
userChatInput: string;
|
userChatInput: string;
|
||||||
};
|
}>;
|
||||||
export type KBSearchResponse = {
|
export type KBSearchResponse = {
|
||||||
[TaskResponseKeyEnum.responseData]: ChatHistoryItemResType;
|
[TaskResponseKeyEnum.responseData]: ChatHistoryItemResType;
|
||||||
isEmpty?: boolean;
|
isEmpty?: boolean;
|
||||||
@@ -22,7 +23,10 @@ export type KBSearchResponse = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export async function dispatchKBSearch(props: Record<string, any>): Promise<KBSearchResponse> {
|
export async function dispatchKBSearch(props: Record<string, any>): Promise<KBSearchResponse> {
|
||||||
const { kbList = [], similarity = 0.4, limit = 5, userChatInput } = props as KBSearchProps;
|
const {
|
||||||
|
moduleName,
|
||||||
|
inputs: { kbList = [], similarity = 0.4, limit = 5, userChatInput }
|
||||||
|
} = props as KBSearchProps;
|
||||||
|
|
||||||
if (kbList.length === 0) {
|
if (kbList.length === 0) {
|
||||||
return Promise.reject("You didn't choose the knowledge base");
|
return Promise.reject("You didn't choose the knowledge base");
|
||||||
@@ -59,6 +63,7 @@ export async function dispatchKBSearch(props: Record<string, any>): Promise<KBSe
|
|||||||
quoteQA: searchRes,
|
quoteQA: searchRes,
|
||||||
responseData: {
|
responseData: {
|
||||||
moduleType: FlowModuleTypeEnum.kbSearchNode,
|
moduleType: FlowModuleTypeEnum.kbSearchNode,
|
||||||
|
moduleName,
|
||||||
price: countModelPrice({ model: vectorModel.model, tokens: tokenLen }),
|
price: countModelPrice({ model: vectorModel.model, tokens: tokenLen }),
|
||||||
model: vectorModel.name,
|
model: vectorModel.name,
|
||||||
tokens: tokenLen,
|
tokens: tokenLen,
|
||||||
|
@@ -1,21 +1,23 @@
|
|||||||
import { sseResponseEventEnum, TaskResponseKeyEnum } from '@/constants/chat';
|
import { sseResponseEventEnum, TaskResponseKeyEnum } from '@/constants/chat';
|
||||||
import { sseResponse } from '@/service/utils/tools';
|
import { sseResponse } from '@/service/utils/tools';
|
||||||
import { textAdaptGptResponse } from '@/utils/adapt';
|
import { textAdaptGptResponse } from '@/utils/adapt';
|
||||||
import type { NextApiResponse } from 'next';
|
import type { ModuleDispatchProps } from '@/types/core/modules';
|
||||||
|
|
||||||
export type AnswerProps = {
|
export type AnswerProps = ModuleDispatchProps<{
|
||||||
res: NextApiResponse;
|
|
||||||
detail?: boolean;
|
|
||||||
text: string;
|
text: string;
|
||||||
stream: boolean;
|
}>;
|
||||||
};
|
|
||||||
export type AnswerResponse = {
|
export type AnswerResponse = {
|
||||||
[TaskResponseKeyEnum.answerText]: string;
|
[TaskResponseKeyEnum.answerText]: string;
|
||||||
finish: boolean;
|
finish: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const dispatchAnswer = (props: Record<string, any>): AnswerResponse => {
|
export const dispatchAnswer = (props: Record<string, any>): AnswerResponse => {
|
||||||
const { res, detail, text = '', stream } = props as AnswerProps;
|
const {
|
||||||
|
res,
|
||||||
|
detail,
|
||||||
|
stream,
|
||||||
|
inputs: { text = '' }
|
||||||
|
} = props as AnswerProps;
|
||||||
|
|
||||||
if (stream) {
|
if (stream) {
|
||||||
sseResponse({
|
sseResponse({
|
||||||
|
@@ -1,16 +1,13 @@
|
|||||||
import { TaskResponseKeyEnum } from '@/constants/chat';
|
import { TaskResponseKeyEnum } from '@/constants/chat';
|
||||||
import { HttpPropsEnum } from '@/constants/flow/flowField';
|
import { HttpPropsEnum } from '@/constants/flow/flowField';
|
||||||
import { ChatHistoryItemResType } from '@/types/chat';
|
import { ChatHistoryItemResType } from '@/types/chat';
|
||||||
import type { NextApiResponse } from 'next';
|
|
||||||
import { FlowModuleTypeEnum } from '@/constants/flow';
|
import { FlowModuleTypeEnum } from '@/constants/flow';
|
||||||
|
import { ModuleDispatchProps } from '@/types/core/modules';
|
||||||
|
|
||||||
export type HttpRequestProps = {
|
export type HttpRequestProps = ModuleDispatchProps<{
|
||||||
res: NextApiResponse;
|
|
||||||
stream: boolean;
|
|
||||||
userOpenaiAccount: any;
|
|
||||||
[HttpPropsEnum.url]: string;
|
[HttpPropsEnum.url]: string;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
};
|
}>;
|
||||||
export type HttpResponse = {
|
export type HttpResponse = {
|
||||||
[HttpPropsEnum.finish]: boolean;
|
[HttpPropsEnum.finish]: boolean;
|
||||||
[HttpPropsEnum.failed]?: boolean;
|
[HttpPropsEnum.failed]?: boolean;
|
||||||
@@ -19,16 +16,30 @@ export type HttpResponse = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const dispatchHttpRequest = async (props: Record<string, any>): Promise<HttpResponse> => {
|
export const dispatchHttpRequest = async (props: Record<string, any>): Promise<HttpResponse> => {
|
||||||
const { res, stream, userOpenaiAccount, url, ...body } = props as HttpRequestProps;
|
const {
|
||||||
|
moduleName,
|
||||||
|
variables,
|
||||||
|
inputs: { url, ...body }
|
||||||
|
} = props as HttpRequestProps;
|
||||||
|
|
||||||
|
const requestBody = {
|
||||||
|
variables,
|
||||||
|
...body
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetchData({ url, body });
|
const response = await fetchData({
|
||||||
|
url,
|
||||||
|
body: requestBody
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
[HttpPropsEnum.finish]: true,
|
[HttpPropsEnum.finish]: true,
|
||||||
[TaskResponseKeyEnum.responseData]: {
|
[TaskResponseKeyEnum.responseData]: {
|
||||||
moduleType: FlowModuleTypeEnum.httpRequest,
|
moduleType: FlowModuleTypeEnum.httpRequest,
|
||||||
|
moduleName,
|
||||||
price: 0,
|
price: 0,
|
||||||
|
body: requestBody,
|
||||||
httpResult: response
|
httpResult: response
|
||||||
},
|
},
|
||||||
...response
|
...response
|
||||||
@@ -39,8 +50,10 @@ export const dispatchHttpRequest = async (props: Record<string, any>): Promise<H
|
|||||||
[HttpPropsEnum.failed]: true,
|
[HttpPropsEnum.failed]: true,
|
||||||
[TaskResponseKeyEnum.responseData]: {
|
[TaskResponseKeyEnum.responseData]: {
|
||||||
moduleType: FlowModuleTypeEnum.httpRequest,
|
moduleType: FlowModuleTypeEnum.httpRequest,
|
||||||
|
moduleName,
|
||||||
price: 0,
|
price: 0,
|
||||||
httpResult: {}
|
body: requestBody,
|
||||||
|
httpResult: { error }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
6
client/src/types/chat.d.ts
vendored
6
client/src/types/chat.d.ts
vendored
@@ -50,10 +50,11 @@ export type QuoteItemType = KbDataItemType & {
|
|||||||
// response data
|
// response data
|
||||||
export type ChatHistoryItemResType = {
|
export type ChatHistoryItemResType = {
|
||||||
moduleType: `${FlowModuleTypeEnum}`;
|
moduleType: `${FlowModuleTypeEnum}`;
|
||||||
|
moduleName: string;
|
||||||
price: number;
|
price: number;
|
||||||
runningTime?: number;
|
runningTime?: number;
|
||||||
model?: string;
|
|
||||||
tokens?: number;
|
tokens?: number;
|
||||||
|
model?: string;
|
||||||
|
|
||||||
// chat
|
// chat
|
||||||
question?: string;
|
question?: string;
|
||||||
@@ -62,7 +63,7 @@ export type ChatHistoryItemResType = {
|
|||||||
quoteList?: QuoteItemType[];
|
quoteList?: QuoteItemType[];
|
||||||
historyPreview?: ChatItemType[]; // completion context array. history will slice
|
historyPreview?: ChatItemType[]; // completion context array. history will slice
|
||||||
|
|
||||||
// kb search
|
// dataset search
|
||||||
similarity?: number;
|
similarity?: number;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
|
|
||||||
@@ -75,5 +76,6 @@ export type ChatHistoryItemResType = {
|
|||||||
extractResult?: Record<string, any>;
|
extractResult?: Record<string, any>;
|
||||||
|
|
||||||
// http
|
// http
|
||||||
|
body?: Record<string, any>;
|
||||||
httpResult?: Record<string, any>;
|
httpResult?: Record<string, any>;
|
||||||
};
|
};
|
||||||
|
3
client/src/types/core/aiChat.d.ts
vendored
3
client/src/types/core/aiChat.d.ts
vendored
@@ -1,6 +1,7 @@
|
|||||||
|
/* ai chat modules props */
|
||||||
export type AIChatProps = {
|
export type AIChatProps = {
|
||||||
model: string;
|
model: string;
|
||||||
systemPrompt: string;
|
systemPrompt?: string;
|
||||||
temperature: number;
|
temperature: number;
|
||||||
maxToken: number;
|
maxToken: number;
|
||||||
quoteTemplate?: string;
|
quoteTemplate?: string;
|
||||||
|
15
client/src/types/core/modules.d.ts
vendored
Normal file
15
client/src/types/core/modules.d.ts
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import type { NextApiResponse } from 'next';
|
||||||
|
import { RunningModuleItemType } from '../app';
|
||||||
|
import { UserModelSchema } from '../mongoSchema';
|
||||||
|
|
||||||
|
// module dispatch props type
|
||||||
|
export type ModuleDispatchProps<T> = {
|
||||||
|
res: NextApiResponse;
|
||||||
|
moduleName: string;
|
||||||
|
stream: boolean;
|
||||||
|
detail: boolean;
|
||||||
|
variables: Record<string, any>;
|
||||||
|
outputs: RunningModuleItemType['outputs'];
|
||||||
|
userOpenaiAccount?: UserModelSchema['openaiAccount'];
|
||||||
|
inputs: T;
|
||||||
|
};
|
@@ -31,12 +31,12 @@ export const getDefaultAppForm = (): EditFormType => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
chatModel: {
|
chatModel: {
|
||||||
model: defaultChatModel.model,
|
model: defaultChatModel?.model,
|
||||||
systemPrompt: '',
|
systemPrompt: '',
|
||||||
temperature: 0,
|
temperature: 0,
|
||||||
quotePrompt: '',
|
quotePrompt: '',
|
||||||
quoteTemplate: '',
|
quoteTemplate: '',
|
||||||
maxToken: defaultChatModel.contextMaxToken / 2,
|
maxToken: defaultChatModel ? defaultChatModel.contextMaxToken / 2 : 4000,
|
||||||
frequency: 0.5,
|
frequency: 0.5,
|
||||||
presence: -0.5
|
presence: -0.5
|
||||||
},
|
},
|
||||||
|
7
client/src/utils/common/file/index.tsx
Normal file
7
client/src/utils/common/file/index.tsx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { getFileViewUrl } from '@/api/support/file';
|
||||||
|
|
||||||
|
export async function getFileAndOpen(fileId: string) {
|
||||||
|
const url = await getFileViewUrl(fileId);
|
||||||
|
const asPath = `${location.origin}${url}`;
|
||||||
|
window.open(asPath, '_blank');
|
||||||
|
}
|
Reference in New Issue
Block a user