diff --git a/client/public/imgs/module/extract.png b/client/public/imgs/module/extract.png new file mode 100644 index 000000000..8cd0d1e02 Binary files /dev/null and b/client/public/imgs/module/extract.png differ diff --git a/client/src/components/MyModal/index.tsx b/client/src/components/MyModal/index.tsx index 9c9cbfe5f..357ef1cc3 100644 --- a/client/src/components/MyModal/index.tsx +++ b/client/src/components/MyModal/index.tsx @@ -42,7 +42,7 @@ const MyModal = ({ {...props} > {!!title && {title}} - + {showCloseBtn && } {children} diff --git a/client/src/components/MyTooltip/index.tsx b/client/src/components/MyTooltip/index.tsx index 40aaee0b1..9dc0f3946 100644 --- a/client/src/components/MyTooltip/index.tsx +++ b/client/src/components/MyTooltip/index.tsx @@ -20,7 +20,7 @@ const MyTooltip = ({ children, forceShow = false, ...props }: Props) => { py={2} borderRadius={'8px'} whiteSpace={'pre-wrap'} - shouldWrapChildren + boxShadow={'1px 1px 10px rgba(0,0,0,0.2)'} {...props} > {children} diff --git a/client/src/constants/flow/ModuleTemplate.ts b/client/src/constants/flow/ModuleTemplate.ts index 9188121d4..b33256b74 100644 --- a/client/src/constants/flow/ModuleTemplate.ts +++ b/client/src/constants/flow/ModuleTemplate.ts @@ -60,7 +60,6 @@ export const UserInputModule: FlowModuleTemplateType = { name: '用户问题(对话入口)', intro: '用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。', flowType: FlowModuleTypeEnum.questionInput, - url: '/app/modules/init/userChatInput', inputs: [ { key: SystemInputEnum.userChatInput, @@ -83,7 +82,6 @@ export const HistoryModule: FlowModuleTemplateType = { name: '聊天记录', intro: '用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。', flowType: FlowModuleTypeEnum.historyNode, - url: '/app/modules/init/history', inputs: [ { key: 'maxContext', @@ -116,7 +114,6 @@ export const ChatModule: FlowModuleTemplateType = { name: 'AI 对话', intro: 'AI 大模型对话', flowType: FlowModuleTypeEnum.chatNode, - url: '/app/modules/chat/gpt', inputs: [ { key: 'model', @@ -206,7 +203,6 @@ export const KBSearchModule: FlowModuleTemplateType = { name: '知识库搜索', intro: '去知识库中搜索对应的答案。可作为 AI 对话引用参考。', flowType: FlowModuleTypeEnum.kbSearchNode, - url: '/app/modules/kb/search', inputs: [ { key: 'kbList', @@ -322,7 +318,6 @@ export const ClassifyQuestionModule: FlowModuleTemplateType = { intro: '可以判断用户问题属于哪方面问题,从而执行不同的操作。', description: '根据用户的历史记录和当前问题判断该次提问的类型。可以添加多组问题类型,下面是一个模板例子:\n类型1: 打招呼\n类型2: 关于 laf 通用问题\n类型3: 关于 laf 代码问题\n类型4: 其他问题', - url: '/app/modules/agent/classifyQuestion', flowType: FlowModuleTypeEnum.classifyQuestion, inputs: [ { @@ -378,6 +373,65 @@ export const ClassifyQuestionModule: FlowModuleTemplateType = { } ] }; +export const ContextExtractModule: FlowModuleTemplateType = { + logo: '/imgs/module/extract.png', + name: '内容提取', + intro: '从文本中提取出指定格式的数据', + description: '可从文本中提取指定的数据,例如:sql语句、搜索关键词、代码等', + flowType: FlowModuleTypeEnum.contentExtract, + inputs: [ + { + key: 'systemPrompt', + type: FlowInputItemTypeEnum.textarea, + valueType: FlowValueTypeEnum.string, + label: '提取内容描述', + description: '写一段提取要求,告诉 AI 需要提取哪些内容', + placeholder: '例如: \n1. 根据用户的\n2. Sealos 是一个集群操作系统', + value: '' + }, + Input_Template_History, + Input_Template_UserChatInput, + { + key: 'agents', + type: FlowInputItemTypeEnum.custom, + label: '', + value: [ + { + value: '打招呼', + key: 'fasw' + }, + { + value: '关于 xxx 的问题', + key: 'fqsw' + }, + { + value: '其他问题', + key: 'fesw' + } + ] + } + ], + outputs: [ + { + key: 'fasw', + label: '', + type: FlowOutputItemTypeEnum.hidden, + targets: [] + }, + { + key: 'fqsw', + label: '', + type: FlowOutputItemTypeEnum.hidden, + targets: [] + }, + { + key: 'fesw', + label: '', + type: FlowOutputItemTypeEnum.hidden, + targets: [] + } + ] +}; export const EmptyModule: FlowModuleTemplateType = { logo: '/imgs/module/cq.png', name: '该模块已被移除', diff --git a/client/src/constants/flow/index.ts b/client/src/constants/flow/index.ts index cd0ead49a..1fb92be77 100644 --- a/client/src/constants/flow/index.ts +++ b/client/src/constants/flow/index.ts @@ -30,7 +30,8 @@ export enum FlowModuleTypeEnum { kbSearchNode = 'kbSearchNode', tfSwitchNode = 'tfSwitchNode', answerNode = 'answerNode', - classifyQuestion = 'classifyQuestion' + classifyQuestion = 'classifyQuestion', + contentExtract = 'contentExtract' } export enum SpecialInputKeyEnum { diff --git a/client/src/constants/theme.ts b/client/src/constants/theme.ts index 071683bcb..016d6a396 100644 --- a/client/src/constants/theme.ts +++ b/client/src/constants/theme.ts @@ -199,16 +199,6 @@ const Select = selectMultiStyle({ } }); -const Tooltip = defineStyleConfig({ - baseStyle: { - p: 3, - bg: 'white', - color: 'blackAlpha.800', - borderRadius: '8px', - boxShadow: '1px 1px 10px rgba(0,0,0,0.2)' - } -}); - // 全局主题 export const theme = extendTheme({ styles: { @@ -309,7 +299,6 @@ export const theme = extendTheme({ Textarea, Switch, Select, - Tooltip, NumberInput } }); diff --git a/client/src/pages/api/openapi/v1/chat/completions.ts b/client/src/pages/api/openapi/v1/chat/completions.ts index 3c89a8a91..0d307b86e 100644 --- a/client/src/pages/api/openapi/v1/chat/completions.ts +++ b/client/src/pages/api/openapi/v1/chat/completions.ts @@ -113,6 +113,12 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex throw new Error('Question is empty'); } + // 创建响应流 + 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'); + /* start process */ const { responseData, answerText } = await dispatchModules({ res, diff --git a/client/src/service/moduleDispatch/agent/classifyQuestion.ts b/client/src/service/moduleDispatch/agent/classifyQuestion.ts index 303de760b..fb2cefb78 100644 --- a/client/src/service/moduleDispatch/agent/classifyQuestion.ts +++ b/client/src/service/moduleDispatch/agent/classifyQuestion.ts @@ -7,13 +7,14 @@ 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'; export type CQProps = { systemPrompt?: string; history?: ChatItemType[]; - userChatInput: string; - agents: ClassifyQuestionAgentItemType[]; + [SystemInputEnum.userChatInput]: string; userOpenaiAccount: UserModelSchema['openaiAccount']; + agents: ClassifyQuestionAgentItemType[]; }; export type CQResponse = { [TaskResponseKeyEnum.responseData]: ChatHistoryItemResType; @@ -22,7 +23,7 @@ export type CQResponse = { const agentModel = 'gpt-3.5-turbo'; const agentFunName = 'agent_user_question'; -const maxTokens = 2000; +const maxTokens = 3000; /* request openai chat */ export const dispatchClassifyQuestion = async (props: Record): Promise => { diff --git a/client/src/service/moduleDispatch/agent/extract.ts b/client/src/service/moduleDispatch/agent/extract.ts index c1bfa2c83..3fa3c4c20 100644 --- a/client/src/service/moduleDispatch/agent/extract.ts +++ b/client/src/service/moduleDispatch/agent/extract.ts @@ -7,40 +7,41 @@ import type { ChatItemType } from '@/types/chat'; import { ChatRoleEnum } from '@/constants/chat'; import { getAIChatApi, axiosConfig } from '@/service/ai/openai'; import type { ClassifyQuestionAgentItemType } from '@/types/app'; -import { authUser } from '@/service/utils/auth'; +import { SystemInputEnum } from '@/constants/app'; export type Props = { + systemPrompt?: string; history?: ChatItemType[]; - userChatInput: string; - agents: ClassifyQuestionAgentItemType[]; + [SystemInputEnum.userChatInput]: string; description: string; + agents: ClassifyQuestionAgentItemType[]; +}; +export type Response = { + arguments: Record; + deficiency: boolean; }; -export type Response = { history: ChatItemType[] }; -const agentModel = 'gpt-3.5-turbo-16k'; +const agentModel = 'gpt-3.5-turbo'; const agentFunName = 'agent_extract_data'; +const maxTokens = 3000; -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - try { - await authUser({ req, authRoot: true }); - - const response = await extract(req.body); - - jsonRes(res, { - data: response - }); - } catch (err) { - jsonRes(res, { - code: 500, - error: err - }); - } -} - -/* request openai chat */ -export async function extract({ agents, history = [], userChatInput, description }: Props) { +export async function extract({ + systemPrompt, + agents, + history = [], + userChatInput, + description +}: Props): Promise { const messages: ChatItemType[] = [ - ...history.slice(-4), + ...(systemPrompt + ? [ + { + obj: ChatRoleEnum.System, + value: systemPrompt + } + ] + : []), + ...history, { obj: ChatRoleEnum.Human, value: userChatInput @@ -50,7 +51,7 @@ export async function extract({ agents, history = [], userChatInput, description // @ts-ignore model: agentModel, prompts: messages, - maxTokens: 3000 + maxTokens }); const adaptMessages = adaptChatItem_openAI({ messages: filterMessages, reserveId: false }); @@ -94,7 +95,17 @@ export async function extract({ agents, history = [], userChatInput, description } ); - const arg = JSON.parse(response.data.choices?.[0]?.message?.function_call?.arguments || ''); + const arg = JSON.parse(response.data.choices?.[0]?.message?.function_call?.arguments || '{}'); + let deficiency = false; + for (const key in arg) { + if (arg[key] === '') { + deficiency = true; + break; + } + } - return arg; + return { + arguments: arg, + deficiency + }; } diff --git a/client/src/types/flow.d.ts b/client/src/types/flow.d.ts index 90edf567f..f4fb4d6e3 100644 --- a/client/src/types/flow.d.ts +++ b/client/src/types/flow.d.ts @@ -52,7 +52,6 @@ export type FlowModuleTemplateType = { description?: string; intro: string; flowType: `${FlowModuleTypeEnum}`; - url?: string; inputs: FlowInputItemType[]; outputs: FlowOutputItemType[]; };