diff --git a/client/public/locales/en/common.json b/client/public/locales/en/common.json
index 21da3b864..6d24c388e 100644
--- a/client/public/locales/en/common.json
+++ b/client/public/locales/en/common.json
@@ -78,6 +78,28 @@
"share": "Share",
"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"
},
"common": {
diff --git a/client/public/locales/zh/common.json b/client/public/locales/zh/common.json
index b8606e96e..368aa5aa1 100644
--- a/client/public/locales/zh/common.json
+++ b/client/public/locales/zh/common.json
@@ -78,6 +78,28 @@
"share": "外部链接调用",
"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": "重新生成"
},
"common": {
@@ -228,7 +250,7 @@
"Help Document": "帮助文档"
},
"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}}"
},
"user": {
diff --git a/client/src/api/service/plugins.ts b/client/src/api/service/plugins.ts
index 1bae574ea..b646de021 100644
--- a/client/src/api/service/plugins.ts
+++ b/client/src/api/service/plugins.ts
@@ -1,12 +1,15 @@
-import { GET, POST } from './request';
+import { POST } from './request';
export const textCensor = (data: { text: string }) =>
POST<{ code?: number; message: string }>('/plugins/censor/text_baidu', data)
.then((res) => {
if (res?.code === 5000) {
- return Promise.reject(res.message);
+ return Promise.reject(res);
}
})
.catch((err) => {
+ if (err?.code === 5000) {
+ return Promise.reject(err.message);
+ }
return Promise.resolve('');
});
diff --git a/client/src/components/ChatBox/ResponseTags.tsx b/client/src/components/ChatBox/ResponseTags.tsx
index c61ddc568..a8404ca62 100644
--- a/client/src/components/ChatBox/ResponseTags.tsx
+++ b/client/src/components/ChatBox/ResponseTags.tsx
@@ -32,16 +32,18 @@ const ResponseTags = ({
} = useDisclosure();
const {
+ chatAccount,
quoteList = [],
historyPreview = [],
runningTime = 0
} = useMemo(() => {
const chatData = responseData.find((item) => item.moduleType === FlowModuleTypeEnum.chatNode);
- if (!chatData) return {};
return {
- quoteList: chatData.quoteList,
- historyPreview: chatData.historyPreview,
- runningTime: responseData.reduce((sum, item) => sum + (item.runningTime || 0), 0)
+ chatAccount: responseData.filter((item) => item.moduleType === FlowModuleTypeEnum.chatNode)
+ .length,
+ quoteList: chatData?.quoteList,
+ historyPreview: chatData?.historyPreview,
+ runningTime: responseData.reduce((sum, item) => sum + (item.runningTime || 0), 0).toFixed(2)
};
}, [responseData]);
@@ -54,36 +56,47 @@ const ResponseTags = ({
return responseData.length === 0 ? null : (
- {quoteList.length > 0 && (
-
- setQuoteModalData(quoteList)}
- >
- {quoteList.length}条引用
-
-
- )}
- {historyPreview.length > 0 && (
-
- setContextModalData(historyPreview)}
- >
- {historyPreview.length}条上下文
-
-
- )}
- {isPc && runningTime > 0 && (
-
- {runningTime}s
+ {chatAccount === 1 ? (
+ <>
+ {quoteList.length > 0 && (
+
+ setQuoteModalData(quoteList)}
+ >
+ {quoteList.length}条引用
+
+
+ )}
+ {historyPreview.length > 0 && (
+
+ setContextModalData(historyPreview)}
+ >
+ {historyPreview.length}条上下文
+
+
+ )}
+ >
+ ) : (
+
+ 多组 AI 对话
)}
-
+
+ {isPc && runningTime > 0 && (
+
+
+ {runningTime}s
+
+
+ )}
+
{t('chat.Complete Response')}
diff --git a/client/src/components/ChatBox/WholeResponseModal.tsx b/client/src/components/ChatBox/WholeResponseModal.tsx
index 7c12eff6b..6c8eb9639 100644
--- a/client/src/components/ChatBox/WholeResponseModal.tsx
+++ b/client/src/components/ChatBox/WholeResponseModal.tsx
@@ -1,11 +1,36 @@
-import React, { useMemo } from 'react';
-import { Box, ModalBody, useTheme, Flex } from '@chakra-ui/react';
+import React, { useMemo, useState } from 'react';
+import { Box, useTheme, Flex, Image } from '@chakra-ui/react';
import type { ChatHistoryItemResType } from '@/types/chat';
import { useTranslation } from 'react-i18next';
+import { ModuleTemplatesFlat } from '@/constants/flow/ModuleTemplate';
+import Tabs from '../Tabs';
import MyModal from '../MyModal';
import MyTooltip from '../MyTooltip';
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' ? (
+
+
+ {label}:
+
+
+ {value}
+
+
+ ) : null;
+}
const ResponseModal = ({
response,
@@ -14,56 +39,159 @@ const ResponseModal = ({
response: ChatHistoryItemResType[];
onClose: () => void;
}) => {
- const { t } = useTranslation();
const theme = useTheme();
+ const { t } = useTranslation();
- const formatResponse = useMemo(
+ const list = useMemo(
() =>
- response.map((item) => {
- const copy = { ...item };
- delete copy.historyPreview;
- delete copy.quoteList;
- return copy;
- }),
+ response.map((item, i) => ({
+ label: (
+
+ item.moduleType === template.flowType)?.logo
+ }
+ alt={''}
+ w={['14px', '16px']}
+ />
+ {item.moduleName}
+
+ ),
+ id: `${i}`
+ })),
[response]
);
+ const [currentTab, setCurrentTab] = useState(`0`);
+
+ const activeModule = useMemo(() => response[Number(currentTab)], [currentTab, response]);
+
return (
{t('chat.Complete Response')}
-
+
}
- isCentered
>
-
- {formatResponse.map((item, i) => (
-
- {JSON.stringify(item, null, 2)}
-
- ))}
-
+
+
+
+
+
+
+
+
+
+
+
+ {/* ai chat */}
+
+
+
+ {
+ try {
+ JSON.stringify(activeModule.quoteList, null, 2);
+ } catch (error) {
+ return '';
+ }
+ })()}
+ />
+ {
+ if (!activeModule?.historyPreview) return '';
+ return (
+ <>
+ {activeModule.historyPreview.map((item, i) => (
+
+ {item.obj}
+ {item.value}
+
+ ))}
+ >
+ );
+ })()}
+ />
+
+ {/* dataset search */}
+
+
+
+ {/* classify question */}
+ {
+ if (!activeModule?.cqList) return '';
+ return (
+
+ {activeModule.cqList.map((item) => (
+
+ {item.value}
+
+ ))}
+
+ );
+ })()}
+ />
+
+
+ {/* extract */}
+
+ {
+ try {
+ return JSON.stringify(activeModule?.extractResult, null, 2);
+ } catch (error) {
+ return '';
+ }
+ })()}
+ />
+
+ {/* http */}
+ {
+ try {
+ return JSON.stringify(activeModule?.body, null, 2);
+ } catch (error) {
+ return '';
+ }
+ })()}
+ />
+ {
+ try {
+ return JSON.stringify(activeModule?.httpResult, null, 2);
+ } catch (error) {
+ return '';
+ }
+ })()}
+ />
+
+
);
};
diff --git a/client/src/components/Markdown/chat/Quote.tsx b/client/src/components/Markdown/chat/Quote.tsx
new file mode 100644
index 000000000..9b4f16021
--- /dev/null
+++ b/client/src/components/Markdown/chat/Quote.tsx
@@ -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 (
+
+ {quoteList.length > 0 ? (
+ <>
+ 本次回答的引用:
+
+ {quoteList.map((item, i) => (
+ {
+ if (!item.file_id) return;
+ try {
+ await getFileAndOpen(item.file_id);
+ } catch (error) {
+ toast({
+ status: 'warning',
+ title: getErrText(error, '打开文件失败')
+ });
+ }
+ }}
+ >
+ {item.filename}
+
+ ))}
+
+ >
+ ) : (
+ 正在生成引用……
+ )}
+
+ );
+};
+
+export default QuoteBlock;
diff --git a/client/src/components/Markdown/index.tsx b/client/src/components/Markdown/index.tsx
index a2b481335..09c2cab11 100644
--- a/client/src/components/Markdown/index.tsx
+++ b/client/src/components/Markdown/index.tsx
@@ -15,21 +15,32 @@ const MermaidCodeBlock = dynamic(() => import('./img/MermaidCodeBlock'));
const MdImage = dynamic(() => import('./img/Image'));
const ChatGuide = dynamic(() => import('./chat/Guide'));
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) {
const match = /language-(\w+)/.exec(className || '');
const codeType = match?.[1];
- if (codeType === 'mermaid') {
+ if (codeType === CodeClassName.mermaid) {
return ;
}
- if (codeType === 'guide') {
+ if (codeType === CodeClassName.guide) {
return ;
}
- if (codeType === 'echarts') {
+ if (codeType === CodeClassName.echarts) {
return ;
}
+ if (codeType === CodeClassName.quote) {
+ return ;
+ }
return (
{children}
diff --git a/client/src/components/Tabs/index.tsx b/client/src/components/Tabs/index.tsx
index fd090b2e6..bba605a00 100644
--- a/client/src/components/Tabs/index.tsx
+++ b/client/src/components/Tabs/index.tsx
@@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next';
// @ts-ignore
interface Props extends GridProps {
- list: { id: string; label: string }[];
+ list: { id: string; label: string | React.ReactNode }[];
activeId: string;
size?: 'sm' | 'md' | 'lg';
onChange: (id: string) => void;
@@ -23,13 +23,13 @@ const Tabs = ({ list, size = 'md', activeId, onChange, ...props }: Props) => {
};
case 'md':
return {
- fontSize: 'md',
+ fontSize: ['sm', 'md'],
outP: '4px',
inlineP: 1
};
case 'lg':
return {
- fontSize: 'lg',
+ fontSize: ['md', 'lg'],
outP: '5px',
inlineP: 2
};
@@ -68,7 +68,7 @@ const Tabs = ({ list, size = 'md', activeId, onChange, ...props }: Props) => {
onChange(item.id);
}}
>
- {t(item.label) || item.label}
+ {typeof item.label === 'string' ? t(item.label) : item.label}
))}
diff --git a/client/src/pages/api/openapi/v1/chat/completions.ts b/client/src/pages/api/openapi/v1/chat/completions.ts
index f9553a276..694590017 100644
--- a/client/src/pages/api/openapi/v1/chat/completions.ts
+++ b/client/src/pages/api/openapi/v1/chat/completions.ts
@@ -32,6 +32,7 @@ import { getSystemTime } from '@/utils/user';
import { authOutLinkChat } from '@/service/support/outLink/auth';
import requestIp from 'request-ip';
import { replaceVariable } from '@/utils/common/tools/text';
+import { ModuleDispatchProps } from '@/types/core/modules';
export type MessageItemType = ChatCompletionRequestMessage & { dataId?: string };
type FastGptWebChatProps = {
@@ -365,13 +366,15 @@ export async function dispatchModules({
module.inputs.forEach((item: any) => {
params[item.key] = item.value;
});
- const props: Record = {
+ const props: ModuleDispatchProps> = {
res,
stream,
detail,
+ variables,
+ moduleName: module.name,
outputs: module.outputs,
userOpenaiAccount: user?.openaiAccount,
- ...params
+ inputs: params
};
const dispatchRes = await (async () => {
diff --git a/client/src/pages/api/support/file/readUrl.ts b/client/src/pages/api/support/file/readUrl.ts
index 21f81cb4a..77a33d4e9 100644
--- a/client/src/pages/api/support/file/readUrl.ts
+++ b/client/src/pages/api/support/file/readUrl.ts
@@ -4,6 +4,7 @@ import { connectToDatabase } from '@/service/mongo';
import { authUser } from '@/service/utils/auth';
import jwt from 'jsonwebtoken';
import { ERROR_ENUM } from '@/service/errorCode';
+import { GridFSStorage } from '@/service/lib/gridfs';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
@@ -17,6 +18,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
const { userId } = await authUser({ req });
+ // auth file
+ const gridFs = new GridFSStorage('dataset', userId);
+ await gridFs.findAndAuthFile(fileId);
+
const token = await createFileToken({
userId,
fileId
diff --git a/client/src/pages/kb/detail/components/InputDataModal.tsx b/client/src/pages/kb/detail/components/InputDataModal.tsx
index fbb9b1f6d..fd3f5536e 100644
--- a/client/src/pages/kb/detail/components/InputDataModal.tsx
+++ b/client/src/pages/kb/detail/components/InputDataModal.tsx
@@ -2,7 +2,6 @@ import React, { useState, useCallback } from 'react';
import { Box, Flex, Button, Textarea, IconButton, BoxProps } from '@chakra-ui/react';
import { useForm } from 'react-hook-form';
import { insertData2Kb, putKbDataById, delOneKbDataByDataId } from '@/api/plugins/kb';
-import { getFileViewUrl } from '@/api/support/file';
import { useToast } from '@/hooks/useToast';
import { getErrText } from '@/utils/tools';
import MyIcon from '@/components/Icon';
@@ -13,6 +12,7 @@ import { useQuery } from '@tanstack/react-query';
import { DatasetItemType } from '@/types/plugin';
import { useTranslation } from 'react-i18next';
import { useDatasetStore } from '@/store/dataset';
+import { getFileAndOpen } from '@/utils/common/file';
export type FormData = { dataId?: string } & DatasetItemType;
@@ -267,9 +267,7 @@ export function RawFileText({ fileId, filename = '', ...props }: RawFileTextProp
textDecoration: 'underline',
onClick: async () => {
try {
- const url = await getFileViewUrl(fileId);
- const asPath = `${location.origin}${url}`;
- window.open(asPath, '_blank');
+ await getFileAndOpen(fileId);
} catch (error) {
toast({
title: getErrText(error, '获取文件地址失败'),
diff --git a/client/src/service/moduleDispatch/agent/classifyQuestion.ts b/client/src/service/moduleDispatch/agent/classifyQuestion.ts
index 00d4712bc..daa3d85c7 100644
--- a/client/src/service/moduleDispatch/agent/classifyQuestion.ts
+++ b/client/src/service/moduleDispatch/agent/classifyQuestion.ts
@@ -5,19 +5,18 @@ import { ChatRoleEnum, TaskResponseKeyEnum } from '@/constants/chat';
import { getAIChatApi, axiosConfig } from '@/service/lib/openai';
import type { ClassifyQuestionAgentItemType } from '@/types/app';
import { countModelPrice } from '@/service/events/pushBill';
-import { UserModelSchema } from '@/types/mongoSchema';
import { getModel } from '@/service/utils/data';
import { SystemInputEnum } from '@/constants/app';
import { SpecialInputKeyEnum } from '@/constants/flow';
import { FlowModuleTypeEnum } from '@/constants/flow';
+import { ModuleDispatchProps } from '@/types/core/modules';
-export type CQProps = {
+export type CQProps = ModuleDispatchProps<{
systemPrompt?: string;
history?: ChatItemType[];
[SystemInputEnum.userChatInput]: string;
- userOpenaiAccount: UserModelSchema['openaiAccount'];
[SpecialInputKeyEnum.agents]: ClassifyQuestionAgentItemType[];
-};
+}>;
export type CQResponse = {
[TaskResponseKeyEnum.responseData]: ChatHistoryItemResType;
[key: string]: any;
@@ -29,7 +28,11 @@ const maxTokens = 3000;
/* request openai chat */
export const dispatchClassifyQuestion = async (props: Record): Promise => {
- const { agents, systemPrompt, history = [], userChatInput, userOpenaiAccount } = props as CQProps;
+ const {
+ moduleName,
+ userOpenaiAccount,
+ inputs: { agents, systemPrompt, history = [], userChatInput }
+ } = props as CQProps;
if (!userChatInput) {
return Promise.reject('Input is empty');
@@ -97,6 +100,7 @@ export const dispatchClassifyQuestion = async (props: Record): Prom
[result.key]: 1,
[TaskResponseKeyEnum.responseData]: {
moduleType: FlowModuleTypeEnum.classifyQuestion,
+ moduleName,
price: userOpenaiAccount?.key ? 0 : countModelPrice({ model: agentModel, tokens }),
model: getModel(agentModel)?.name || agentModel,
tokens,
diff --git a/client/src/service/moduleDispatch/agent/extract.ts b/client/src/service/moduleDispatch/agent/extract.ts
index 703b23469..2d84ae3f1 100644
--- a/client/src/service/moduleDispatch/agent/extract.ts
+++ b/client/src/service/moduleDispatch/agent/extract.ts
@@ -6,17 +6,16 @@ import { getAIChatApi, axiosConfig } from '@/service/lib/openai';
import type { ContextExtractAgentItemType } from '@/types/app';
import { ContextExtractEnum } from '@/constants/flow/flowField';
import { countModelPrice } from '@/service/events/pushBill';
-import { UserModelSchema } from '@/types/mongoSchema';
import { getModel } from '@/service/utils/data';
import { FlowModuleTypeEnum } from '@/constants/flow';
+import { ModuleDispatchProps } from '@/types/core/modules';
-export type Props = {
- userOpenaiAccount: UserModelSchema['openaiAccount'];
+export type Props = ModuleDispatchProps<{
history?: ChatItemType[];
[ContextExtractEnum.content]: string;
[ContextExtractEnum.extractKeys]: ContextExtractAgentItemType[];
[ContextExtractEnum.description]: string;
-};
+}>;
export type Response = {
[ContextExtractEnum.success]?: boolean;
[ContextExtractEnum.failed]?: boolean;
@@ -29,11 +28,9 @@ const agentFunName = 'agent_extract_data';
const maxTokens = 4000;
export async function dispatchContentExtract({
+ moduleName,
userOpenaiAccount,
- content,
- extractKeys,
- history = [],
- description
+ inputs: { content, extractKeys, history = [], description }
}: Props): Promise {
if (!content) {
return Promise.reject('Input is empty');
@@ -120,6 +117,7 @@ export async function dispatchContentExtract({
...arg,
[TaskResponseKeyEnum.responseData]: {
moduleType: FlowModuleTypeEnum.contentExtract,
+ moduleName,
price: userOpenaiAccount?.key ? 0 : countModelPrice({ model: agentModel, tokens }),
model: getModel(agentModel)?.name || agentModel,
tokens,
diff --git a/client/src/service/moduleDispatch/chat/oneapi.ts b/client/src/service/moduleDispatch/chat/oneapi.ts
index d3d831226..c21ab454a 100644
--- a/client/src/service/moduleDispatch/chat/oneapi.ts
+++ b/client/src/service/moduleDispatch/chat/oneapi.ts
@@ -11,7 +11,6 @@ import { TaskResponseKeyEnum } from '@/constants/chat';
import { getChatModel } from '@/service/utils/data';
import { countModelPrice } from '@/service/events/pushBill';
import { ChatModelItemType } from '@/types/model';
-import { UserModelSchema } from '@/types/mongoSchema';
import { textCensor } from '@/api/service/plugins';
import { ChatCompletionRequestMessageRoleEnum } from 'openai';
import { AppModuleItemType } from '@/types/app';
@@ -21,19 +20,16 @@ import { defaultQuotePrompt, defaultQuoteTemplate } from '@/prompts/core/AIChat'
import type { AIChatProps } from '@/types/core/aiChat';
import { replaceVariable } from '@/utils/common/tools/text';
import { FlowModuleTypeEnum } from '@/constants/flow';
+import { ModuleDispatchProps } from '@/types/core/modules';
-export type ChatProps = AIChatProps & {
- res: NextApiResponse;
- history?: ChatItemType[];
- userChatInput: string;
- stream?: boolean;
- detail?: boolean;
- quoteQA?: QuoteItemType[];
- systemPrompt?: string;
- limitPrompt?: string;
- userOpenaiAccount: UserModelSchema['openaiAccount'];
- outputs: AppModuleItemType['outputs'];
-};
+export type ChatProps = ModuleDispatchProps<
+ AIChatProps & {
+ userChatInput: string;
+ history?: ChatItemType[];
+ quoteQA?: QuoteItemType[];
+ limitPrompt?: string;
+ }
+>;
export type ChatResponse = {
[TaskResponseKeyEnum.answerText]: string;
[TaskResponseKeyEnum.responseData]: ChatHistoryItemResType;
@@ -41,24 +37,27 @@ export type ChatResponse = {
};
/* request openai chat */
-export const dispatchChatCompletion = async (props: Record): Promise => {
+export const dispatchChatCompletion = async (props: ChatProps): Promise => {
let {
res,
- model = global.chatModels[0]?.model,
- temperature = 0,
- maxToken = 4000,
+ moduleName,
stream = false,
detail = false,
- history = [],
- quoteQA = [],
- userChatInput,
- systemPrompt = '',
- limitPrompt,
- quoteTemplate,
- quotePrompt,
userOpenaiAccount,
- outputs
- } = props as ChatProps;
+ outputs,
+ inputs: {
+ model = global.chatModels[0]?.model,
+ temperature = 0,
+ maxToken = 4000,
+ history = [],
+ quoteQA = [],
+ userChatInput,
+ systemPrompt = '',
+ limitPrompt,
+ quoteTemplate,
+ quotePrompt
+ }
+ } = props;
if (!userChatInput) {
return Promise.reject('Question is empty');
}
@@ -177,6 +176,7 @@ export const dispatchChatCompletion = async (props: Record): Promis
[TaskResponseKeyEnum.answerText]: answerText,
[TaskResponseKeyEnum.responseData]: {
moduleType: FlowModuleTypeEnum.chatNode,
+ moduleName,
price: userOpenaiAccount?.key ? 0 : countModelPrice({ model, tokens: totalTokens }),
model: modelConstantsData.name,
tokens: totalTokens,
@@ -194,15 +194,18 @@ function filterQuote({
model,
quoteTemplate
}: {
- quoteQA: ChatProps['quoteQA'];
+ quoteQA: ChatProps['inputs']['quoteQA'];
model: ChatModelItemType;
quoteTemplate?: string;
}) {
const sliceResult = sliceMessagesTB({
maxTokens: model.quoteMaxToken,
- messages: quoteQA.map((item) => ({
+ messages: quoteQA.map((item, index) => ({
obj: ChatRoleEnum.System,
- value: replaceVariable(quoteTemplate || defaultQuoteTemplate, item)
+ value: replaceVariable(quoteTemplate || defaultQuoteTemplate, {
+ ...item,
+ index: `${index + 1}`
+ })
}))
});
@@ -212,7 +215,12 @@ function filterQuote({
const quoteText =
filterQuoteQA.length > 0
? `${filterQuoteQA
- .map((item) => replaceVariable(quoteTemplate || defaultQuoteTemplate, item))
+ .map((item, index) =>
+ replaceVariable(quoteTemplate || defaultQuoteTemplate, {
+ ...item,
+ index: `${index + 1}`
+ })
+ )
.join('\n')}`
: '';
@@ -232,7 +240,7 @@ function getChatMessages({
}: {
quotePrompt?: string;
quoteText: string;
- history: ChatProps['history'];
+ history: ChatProps['inputs']['history'];
systemPrompt: string;
limitPrompt?: string;
userChatInput: string;
@@ -288,7 +296,7 @@ function getMaxTokens({
}: {
maxToken: number;
model: ChatModelItemType;
- filterMessages: ChatProps['history'];
+ filterMessages: ChatProps['inputs']['history'];
}) {
const tokensLimit = model.contextMaxToken;
/* count response max token */
diff --git a/client/src/service/moduleDispatch/init/history.tsx b/client/src/service/moduleDispatch/init/history.tsx
index 03b44be9d..46bc10b2c 100644
--- a/client/src/service/moduleDispatch/init/history.tsx
+++ b/client/src/service/moduleDispatch/init/history.tsx
@@ -1,13 +1,16 @@
import { SystemInputEnum } from '@/constants/app';
import { ChatItemType } from '@/types/chat';
+import type { ModuleDispatchProps } from '@/types/core/modules';
-export type HistoryProps = {
+export type HistoryProps = ModuleDispatchProps<{
maxContext: number;
[SystemInputEnum.history]: ChatItemType[];
-};
+}>;
export const dispatchHistory = (props: Record) => {
- const { maxContext = 5, history = [] } = props as HistoryProps;
+ const {
+ inputs: { maxContext = 5, history = [] }
+ } = props as HistoryProps;
return {
history: maxContext > 0 ? history.slice(-maxContext) : []
diff --git a/client/src/service/moduleDispatch/init/userChatInput.tsx b/client/src/service/moduleDispatch/init/userChatInput.tsx
index 7743b9f24..c5bd5f0c2 100644
--- a/client/src/service/moduleDispatch/init/userChatInput.tsx
+++ b/client/src/service/moduleDispatch/init/userChatInput.tsx
@@ -1,11 +1,14 @@
import { SystemInputEnum } from '@/constants/app';
+import type { ModuleDispatchProps } from '@/types/core/modules';
-export type UserChatInputProps = {
+export type UserChatInputProps = ModuleDispatchProps<{
[SystemInputEnum.userChatInput]: string;
-};
+}>;
export const dispatchChatInput = (props: Record) => {
- const { userChatInput } = props as UserChatInputProps;
+ const {
+ inputs: { userChatInput }
+ } = props as UserChatInputProps;
return {
userChatInput
};
diff --git a/client/src/service/moduleDispatch/kb/search.ts b/client/src/service/moduleDispatch/kb/search.ts
index d384031c9..df7dd3f77 100644
--- a/client/src/service/moduleDispatch/kb/search.ts
+++ b/client/src/service/moduleDispatch/kb/search.ts
@@ -7,13 +7,14 @@ import type { SelectedKbType } from '@/types/plugin';
import type { QuoteItemType } from '@/types/chat';
import { PgDatasetTableName } from '@/constants/plugin';
import { FlowModuleTypeEnum } from '@/constants/flow';
+import { ModuleDispatchProps } from '@/types/core/modules';
-type KBSearchProps = {
+type KBSearchProps = ModuleDispatchProps<{
kbList: SelectedKbType;
similarity: number;
limit: number;
userChatInput: string;
-};
+}>;
export type KBSearchResponse = {
[TaskResponseKeyEnum.responseData]: ChatHistoryItemResType;
isEmpty?: boolean;
@@ -22,7 +23,10 @@ export type KBSearchResponse = {
};
export async function dispatchKBSearch(props: Record): Promise {
- 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) {
return Promise.reject("You didn't choose the knowledge base");
@@ -59,6 +63,7 @@ export async function dispatchKBSearch(props: Record): Promise;
export type AnswerResponse = {
[TaskResponseKeyEnum.answerText]: string;
finish: boolean;
};
export const dispatchAnswer = (props: Record): AnswerResponse => {
- const { res, detail, text = '', stream } = props as AnswerProps;
+ const {
+ res,
+ detail,
+ stream,
+ inputs: { text = '' }
+ } = props as AnswerProps;
if (stream) {
sseResponse({
diff --git a/client/src/service/moduleDispatch/tools/http.ts b/client/src/service/moduleDispatch/tools/http.ts
index 8b645829b..e7e612886 100644
--- a/client/src/service/moduleDispatch/tools/http.ts
+++ b/client/src/service/moduleDispatch/tools/http.ts
@@ -1,16 +1,13 @@
import { TaskResponseKeyEnum } from '@/constants/chat';
import { HttpPropsEnum } from '@/constants/flow/flowField';
import { ChatHistoryItemResType } from '@/types/chat';
-import type { NextApiResponse } from 'next';
import { FlowModuleTypeEnum } from '@/constants/flow';
+import { ModuleDispatchProps } from '@/types/core/modules';
-export type HttpRequestProps = {
- res: NextApiResponse;
- stream: boolean;
- userOpenaiAccount: any;
+export type HttpRequestProps = ModuleDispatchProps<{
[HttpPropsEnum.url]: string;
[key: string]: any;
-};
+}>;
export type HttpResponse = {
[HttpPropsEnum.finish]: boolean;
[HttpPropsEnum.failed]?: boolean;
@@ -19,16 +16,30 @@ export type HttpResponse = {
};
export const dispatchHttpRequest = async (props: Record): Promise => {
- const { res, stream, userOpenaiAccount, url, ...body } = props as HttpRequestProps;
+ const {
+ moduleName,
+ variables,
+ inputs: { url, ...body }
+ } = props as HttpRequestProps;
+
+ const requestBody = {
+ variables,
+ ...body
+ };
try {
- const response = await fetchData({ url, body });
+ const response = await fetchData({
+ url,
+ body: requestBody
+ });
return {
[HttpPropsEnum.finish]: true,
[TaskResponseKeyEnum.responseData]: {
moduleType: FlowModuleTypeEnum.httpRequest,
+ moduleName,
price: 0,
+ body: requestBody,
httpResult: response
},
...response
@@ -39,8 +50,10 @@ export const dispatchHttpRequest = async (props: Record): Promise;
// http
+ body?: Record;
httpResult?: Record;
};
diff --git a/client/src/types/core/aiChat.d.ts b/client/src/types/core/aiChat.d.ts
index 07f03f35f..9bb56a66b 100644
--- a/client/src/types/core/aiChat.d.ts
+++ b/client/src/types/core/aiChat.d.ts
@@ -1,6 +1,7 @@
+/* ai chat modules props */
export type AIChatProps = {
model: string;
- systemPrompt: string;
+ systemPrompt?: string;
temperature: number;
maxToken: number;
quoteTemplate?: string;
diff --git a/client/src/types/core/modules.d.ts b/client/src/types/core/modules.d.ts
new file mode 100644
index 000000000..ef17e400b
--- /dev/null
+++ b/client/src/types/core/modules.d.ts
@@ -0,0 +1,15 @@
+import type { NextApiResponse } from 'next';
+import { RunningModuleItemType } from '../app';
+import { UserModelSchema } from '../mongoSchema';
+
+// module dispatch props type
+export type ModuleDispatchProps = {
+ res: NextApiResponse;
+ moduleName: string;
+ stream: boolean;
+ detail: boolean;
+ variables: Record;
+ outputs: RunningModuleItemType['outputs'];
+ userOpenaiAccount?: UserModelSchema['openaiAccount'];
+ inputs: T;
+};
diff --git a/client/src/utils/app.ts b/client/src/utils/app.ts
index f6b1fa29f..ef98f2a99 100644
--- a/client/src/utils/app.ts
+++ b/client/src/utils/app.ts
@@ -31,12 +31,12 @@ export const getDefaultAppForm = (): EditFormType => {
return {
chatModel: {
- model: defaultChatModel.model,
+ model: defaultChatModel?.model,
systemPrompt: '',
temperature: 0,
quotePrompt: '',
quoteTemplate: '',
- maxToken: defaultChatModel.contextMaxToken / 2,
+ maxToken: defaultChatModel ? defaultChatModel.contextMaxToken / 2 : 4000,
frequency: 0.5,
presence: -0.5
},
diff --git a/client/src/utils/common/file/index.tsx b/client/src/utils/common/file/index.tsx
new file mode 100644
index 000000000..5809f5f93
--- /dev/null
+++ b/client/src/utils/common/file/index.tsx
@@ -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');
+}