mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 13:03:50 +00:00
extract modules
This commit is contained in:
@@ -18,6 +18,13 @@
|
||||
"New Chat": "New Chat",
|
||||
"You need to a chat app": "You need to a chat app"
|
||||
},
|
||||
"common": {
|
||||
"Add": "Add",
|
||||
"Filed is repeat": "Filed is repeated",
|
||||
"Filed is repeated": "",
|
||||
"Input": "Input",
|
||||
"Output": "Output"
|
||||
},
|
||||
"home": {
|
||||
"Quickly build AI question and answer library": "Quickly build AI question and answer library",
|
||||
"Start Now": "Start Now",
|
||||
|
@@ -18,6 +18,13 @@
|
||||
"New Chat": "新对话",
|
||||
"You need to a chat app": "你需要创建一个应用"
|
||||
},
|
||||
"common": {
|
||||
"Add": "添加",
|
||||
"Filed is repeat": "",
|
||||
"Filed is repeated": "字段重复了",
|
||||
"Input": "输入",
|
||||
"Output": "输出"
|
||||
},
|
||||
"home": {
|
||||
"Quickly build AI question and answer library": "快速搭建 AI 问答系统",
|
||||
"Start Now": "立即开始",
|
||||
|
@@ -54,7 +54,8 @@ export const ChatSourceMap = {
|
||||
export enum ChatModuleEnum {
|
||||
'AIChat' = 'AI Chat',
|
||||
'KBSearch' = 'KB Search',
|
||||
'CQ' = 'Classify Question'
|
||||
'CQ' = 'Classify Question',
|
||||
'Extract' = 'Content Extract'
|
||||
}
|
||||
|
||||
export enum OutLinkTypeEnum {
|
||||
|
@@ -15,6 +15,7 @@ import {
|
||||
Input_Template_TFSwitch,
|
||||
Input_Template_UserChatInput
|
||||
} from './inputTemplate';
|
||||
import { ContextExtractEnum } from './flowField';
|
||||
|
||||
export const ChatModelSystemTip =
|
||||
'模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。可使用变量,例如 {{language}}';
|
||||
@@ -333,7 +334,7 @@ export const ClassifyQuestionModule: FlowModuleTemplateType = {
|
||||
Input_Template_History,
|
||||
Input_Template_UserChatInput,
|
||||
{
|
||||
key: 'agents',
|
||||
key: SpecialInputKeyEnum.agents,
|
||||
type: FlowInputItemTypeEnum.custom,
|
||||
label: '',
|
||||
value: [
|
||||
@@ -375,59 +376,70 @@ export const ClassifyQuestionModule: FlowModuleTemplateType = {
|
||||
};
|
||||
export const ContextExtractModule: FlowModuleTemplateType = {
|
||||
logo: '/imgs/module/extract.png',
|
||||
name: '内容提取',
|
||||
name: '文本内容提取',
|
||||
intro: '从文本中提取出指定格式的数据',
|
||||
description: '可从文本中提取指定的数据,例如:sql语句、搜索关键词、代码等',
|
||||
flowType: FlowModuleTypeEnum.contentExtract,
|
||||
inputs: [
|
||||
Input_Template_TFSwitch,
|
||||
{
|
||||
key: 'systemPrompt',
|
||||
key: ContextExtractEnum.description,
|
||||
type: FlowInputItemTypeEnum.textarea,
|
||||
valueType: FlowValueTypeEnum.string,
|
||||
label: '提取内容描述',
|
||||
label: '提取要求描述',
|
||||
description: '写一段提取要求,告诉 AI 需要提取哪些内容',
|
||||
placeholder: '例如: \n1. 根据用户的\n2. Sealos 是一个集群操作系统',
|
||||
placeholder:
|
||||
'例如: \n1. 你是一个实验室预约助手。根据用户问题,提取出姓名、实验室号和预约时间',
|
||||
value: ''
|
||||
},
|
||||
Input_Template_History,
|
||||
Input_Template_UserChatInput,
|
||||
{
|
||||
key: 'agents',
|
||||
key: ContextExtractEnum.content,
|
||||
type: FlowInputItemTypeEnum.target,
|
||||
label: '需要提取的文本',
|
||||
required: true,
|
||||
valueType: FlowValueTypeEnum.string
|
||||
},
|
||||
{
|
||||
key: ContextExtractEnum.extractKeys,
|
||||
type: FlowInputItemTypeEnum.custom,
|
||||
label: '',
|
||||
label: '目标字段',
|
||||
description: "由 '描述' 和 'key' 组成一个目标字段,可提取多个目标字段",
|
||||
value: [
|
||||
{
|
||||
value: '打招呼',
|
||||
key: 'fasw'
|
||||
key: 'a',
|
||||
desc: '描述',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
value: '关于 xxx 的问题',
|
||||
key: 'fqsw'
|
||||
},
|
||||
{
|
||||
value: '其他问题',
|
||||
key: 'fesw'
|
||||
key: 'n',
|
||||
desc: '描述',
|
||||
required: true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: 'fasw',
|
||||
label: '',
|
||||
type: FlowOutputItemTypeEnum.hidden,
|
||||
key: ContextExtractEnum.success,
|
||||
label: '字段完全提取',
|
||||
valueType: FlowValueTypeEnum.boolean,
|
||||
type: FlowOutputItemTypeEnum.source,
|
||||
targets: []
|
||||
},
|
||||
{
|
||||
key: 'fqsw',
|
||||
label: '',
|
||||
type: FlowOutputItemTypeEnum.hidden,
|
||||
key: ContextExtractEnum.failed,
|
||||
label: '提取字段缺失',
|
||||
valueType: FlowValueTypeEnum.boolean,
|
||||
type: FlowOutputItemTypeEnum.source,
|
||||
targets: []
|
||||
},
|
||||
{
|
||||
key: 'fesw',
|
||||
label: '',
|
||||
type: FlowOutputItemTypeEnum.hidden,
|
||||
key: ContextExtractEnum.fields,
|
||||
label: '提取结果',
|
||||
description: '一个 JSON 对象,例如 {"name:":"YY","Time":"2023/7/2 18:00"}',
|
||||
valueType: FlowValueTypeEnum.other,
|
||||
type: FlowOutputItemTypeEnum.source,
|
||||
targets: []
|
||||
}
|
||||
]
|
||||
@@ -461,7 +473,7 @@ export const ModuleTemplates = [
|
||||
},
|
||||
{
|
||||
label: 'Agent',
|
||||
list: [ClassifyQuestionModule]
|
||||
list: [ClassifyQuestionModule, ContextExtractModule]
|
||||
}
|
||||
];
|
||||
export const ModuleTemplatesFlat = ModuleTemplates.map((templates) => templates.list)?.flat();
|
||||
@@ -1070,7 +1082,7 @@ export const appTemplates: (AppItemType & { avatar: string; intro: string })[] =
|
||||
connected: true
|
||||
},
|
||||
{
|
||||
key: 'agents',
|
||||
key: SpecialInputKeyEnum.agents,
|
||||
value: [
|
||||
{
|
||||
value: '打招呼、问候等问题',
|
||||
|
8
client/src/constants/flow/flowField.ts
Normal file
8
client/src/constants/flow/flowField.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export enum ContextExtractEnum {
|
||||
extractKeys = 'extractKeys',
|
||||
content = 'content',
|
||||
description = 'description',
|
||||
success = 'success',
|
||||
failed = 'failed',
|
||||
fields = 'fields'
|
||||
}
|
@@ -35,7 +35,8 @@ export enum FlowModuleTypeEnum {
|
||||
}
|
||||
|
||||
export enum SpecialInputKeyEnum {
|
||||
'answerText' = 'text'
|
||||
'answerText' = 'text',
|
||||
'agents' = 'agents' // cq agent key
|
||||
}
|
||||
|
||||
export enum FlowValueTypeEnum {
|
||||
|
@@ -25,6 +25,7 @@ import { pushTaskBill } from '@/service/events/pushBill';
|
||||
import { BillSourceEnum } from '@/constants/user';
|
||||
import { ChatHistoryItemResType } from '@/types/chat';
|
||||
import { UserModelSchema } from '@/types/mongoSchema';
|
||||
import { dispatchContentExtract } from '@/service/moduleDispatch/agent/extract';
|
||||
|
||||
export type MessageItemType = ChatCompletionRequestMessage & { _id?: string };
|
||||
type FastGptWebChatProps = {
|
||||
@@ -337,7 +338,8 @@ export async function dispatchModules({
|
||||
[FlowModuleTypeEnum.answerNode]: dispatchAnswer,
|
||||
[FlowModuleTypeEnum.chatNode]: dispatchChatCompletion,
|
||||
[FlowModuleTypeEnum.kbSearchNode]: dispatchKBSearch,
|
||||
[FlowModuleTypeEnum.classifyQuestion]: dispatchClassifyQuestion
|
||||
[FlowModuleTypeEnum.classifyQuestion]: dispatchClassifyQuestion,
|
||||
[FlowModuleTypeEnum.contentExtract]: dispatchContentExtract
|
||||
};
|
||||
if (callbackMap[module.flowType]) {
|
||||
return callbackMap[module.flowType](props);
|
||||
|
@@ -10,7 +10,7 @@ import type { ClassifyQuestionAgentItemType } from '@/types/app';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 4);
|
||||
import MyIcon from '@/components/Icon';
|
||||
import { FlowOutputItemTypeEnum, FlowValueTypeEnum } from '@/constants/flow';
|
||||
import { FlowOutputItemTypeEnum, FlowValueTypeEnum, SpecialInputKeyEnum } from '@/constants/flow';
|
||||
import SourceHandle from '../render/SourceHandle';
|
||||
|
||||
const NodeCQNode = ({
|
||||
@@ -25,7 +25,7 @@ const NodeCQNode = ({
|
||||
onChangeNode={onChangeNode}
|
||||
flowInputList={inputs}
|
||||
CustomComponent={{
|
||||
agents: ({
|
||||
[SpecialInputKeyEnum.agents]: ({
|
||||
key: agentKey,
|
||||
value: agents = []
|
||||
}: {
|
||||
|
@@ -0,0 +1,146 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Box, Button, Table, Thead, Tbody, Tr, Th, Td, TableContainer } from '@chakra-ui/react';
|
||||
import { NodeProps } from 'reactflow';
|
||||
import { FlowModuleItemType } from '@/types/flow';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import NodeCard from '../modules/NodeCard';
|
||||
import Container from '../modules/Container';
|
||||
import { AddIcon } from '@chakra-ui/icons';
|
||||
import RenderInput from '../render/RenderInput';
|
||||
import Divider from '../modules/Divider';
|
||||
import { ContextExtractAgentItemType } from '@/types/app';
|
||||
import RenderOutput from '../render/RenderOutput';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import ExtractFieldModal from '../modules/ExtractFieldModal';
|
||||
import { ContextExtractEnum } from '@/constants/flow/flowField';
|
||||
|
||||
const NodeExtract = ({
|
||||
data: { inputs, outputs, moduleId, onChangeNode, ...props }
|
||||
}: NodeProps<FlowModuleItemType>) => {
|
||||
const { t } = useTranslation();
|
||||
const [editExtractFiled, setEditExtractField] = useState<ContextExtractAgentItemType>();
|
||||
|
||||
return (
|
||||
<NodeCard minW={'380px'} moduleId={moduleId} {...props}>
|
||||
<Divider text="Input" />
|
||||
<Container>
|
||||
<RenderInput
|
||||
moduleId={moduleId}
|
||||
onChangeNode={onChangeNode}
|
||||
flowInputList={inputs}
|
||||
CustomComponent={{
|
||||
[ContextExtractEnum.extractKeys]: ({
|
||||
key,
|
||||
value: extractKeys = []
|
||||
}: {
|
||||
key: string;
|
||||
value?: ContextExtractAgentItemType[];
|
||||
}) => (
|
||||
<Box>
|
||||
<TableContainer>
|
||||
<Table>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>字段 key</Th>
|
||||
<Th>字段描述</Th>
|
||||
<Th>必填</Th>
|
||||
<Th></Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{extractKeys.map((item, index) => (
|
||||
<Tr key={index}>
|
||||
<Td>{item.key}</Td>
|
||||
<Td whiteSpace={'pre-line'} wordBreak={'break-all'}>
|
||||
{item.desc}{' '}
|
||||
</Td>
|
||||
<Td>{item.required ? '✔' : ''}</Td>
|
||||
<Td whiteSpace={'nowrap'}>
|
||||
<MyIcon
|
||||
mr={3}
|
||||
name={'settingLight'}
|
||||
w={'16px'}
|
||||
cursor={'pointer'}
|
||||
onClick={() => {
|
||||
setEditExtractField(item);
|
||||
}}
|
||||
/>
|
||||
<MyIcon
|
||||
name={'delete'}
|
||||
w={'16px'}
|
||||
cursor={'pointer'}
|
||||
onClick={() => {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'inputs',
|
||||
key: ContextExtractEnum.extractKeys,
|
||||
value: extractKeys.filter((extract) => item.key !== extract.key)
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<Box mt={2} textAlign={'right'}>
|
||||
<Button
|
||||
variant={'base'}
|
||||
leftIcon={<AddIcon fontSize={'10px'} />}
|
||||
onClick={() =>
|
||||
setEditExtractField({
|
||||
desc: '',
|
||||
key: '',
|
||||
required: true
|
||||
})
|
||||
}
|
||||
>
|
||||
新增字段
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</Container>
|
||||
<Divider text="Output" />
|
||||
<Container>
|
||||
<RenderOutput flowOutputList={outputs} />
|
||||
</Container>
|
||||
|
||||
{!!editExtractFiled && (
|
||||
<ExtractFieldModal
|
||||
defaultField={editExtractFiled}
|
||||
onClose={() => setEditExtractField(undefined)}
|
||||
onSubmit={(data) => {
|
||||
const extracts: ContextExtractAgentItemType[] =
|
||||
inputs.find((item) => item.key === ContextExtractEnum.extractKeys)?.value || [];
|
||||
|
||||
const exists = extracts.find((item) => item.key === editExtractFiled.key);
|
||||
|
||||
if (exists) {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'inputs',
|
||||
key: ContextExtractEnum.extractKeys,
|
||||
value: extracts.map((item) => (item.key === editExtractFiled.key ? data : item))
|
||||
});
|
||||
} else {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'inputs',
|
||||
key: ContextExtractEnum.extractKeys,
|
||||
value: extracts.concat(data)
|
||||
});
|
||||
}
|
||||
|
||||
setEditExtractField(undefined);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</NodeCard>
|
||||
);
|
||||
};
|
||||
|
||||
export default NodeExtract;
|
@@ -1,8 +1,10 @@
|
||||
import React from 'react';
|
||||
import { Box, useTheme } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
const Divider = ({ text }: { text: 'Body' | 'Input' | 'Output' | string }) => {
|
||||
const Divider = ({ text }: { text: 'Input' | 'Output' | string }) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Box
|
||||
textAlign={'center'}
|
||||
@@ -12,7 +14,7 @@ const Divider = ({ text }: { text: 'Body' | 'Input' | 'Output' | string }) => {
|
||||
borderBottom={theme.borders.base}
|
||||
fontSize={'lg'}
|
||||
>
|
||||
{text}
|
||||
{t(`common.${text}`)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
@@ -0,0 +1,74 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
ModalHeader,
|
||||
ModalFooter,
|
||||
ModalBody,
|
||||
Flex,
|
||||
Switch,
|
||||
Input,
|
||||
FormControl
|
||||
} from '@chakra-ui/react';
|
||||
import type { ContextExtractAgentItemType } from '@/types/app';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
|
||||
import MyModal from '@/components/MyModal';
|
||||
import Avatar from '@/components/Avatar';
|
||||
|
||||
const ExtractFieldModal = ({
|
||||
defaultField = {
|
||||
desc: '',
|
||||
key: '',
|
||||
required: true
|
||||
},
|
||||
onClose,
|
||||
onSubmit
|
||||
}: {
|
||||
defaultField?: ContextExtractAgentItemType;
|
||||
onClose: () => void;
|
||||
onSubmit: (data: ContextExtractAgentItemType) => void;
|
||||
}) => {
|
||||
const { register, handleSubmit } = useForm<ContextExtractAgentItemType>({
|
||||
defaultValues: defaultField
|
||||
});
|
||||
|
||||
return (
|
||||
<MyModal isOpen={true} onClose={onClose}>
|
||||
<ModalHeader display={'flex'} alignItems={'center'}>
|
||||
<Avatar src={'/imgs/module/extract.png'} mr={2} w={'20px'} objectFit={'cover'} />
|
||||
提取字段配置
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
<Flex alignItems={'center'}>
|
||||
<Box w={'70px'}>必填</Box>
|
||||
<Switch {...register('required')} />
|
||||
</Flex>
|
||||
<Flex mt={5} alignItems={'center'}>
|
||||
<Box w={'80px'}>字段描述</Box>
|
||||
<Input
|
||||
placeholder="姓名/年龄/sql语句……"
|
||||
{...register('desc', { required: '字段描述不能为空' })}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex mt={5} alignItems={'center'}>
|
||||
<Box w={'80px'}>字段 key</Box>
|
||||
<Input
|
||||
placeholder="name/age/sql"
|
||||
{...register('key', { required: '字段 key 不能为空' })}
|
||||
/>
|
||||
</Flex>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button variant={'base'} mr={3} onClick={onClose}>
|
||||
取消
|
||||
</Button>
|
||||
<Button onClick={handleSubmit(onSubmit)}>确认</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(ExtractFieldModal);
|
@@ -29,7 +29,14 @@ const NodeCard = ({
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Box minW={minW} bg={'white'} border={theme.borders.md} borderRadius={'md'} boxShadow={'sm'}>
|
||||
<Box
|
||||
minW={minW}
|
||||
maxW={'430px'}
|
||||
bg={'white'}
|
||||
border={theme.borders.md}
|
||||
borderRadius={'md'}
|
||||
boxShadow={'sm'}
|
||||
>
|
||||
<Flex className="custom-drag-handle" px={4} py={3} alignItems={'center'}>
|
||||
<Avatar src={logo} borderRadius={'md'} objectFit={'contain'} w={'30px'} h={'30px'} />
|
||||
<Box ml={3} fontSize={'lg'} color={'myGray.600'}>
|
||||
@@ -39,7 +46,7 @@ const NodeCard = ({
|
||||
<MyTooltip label={description} forceShow>
|
||||
<QuestionOutlineIcon
|
||||
display={['none', 'inline']}
|
||||
transform={'translateY(-1px)'}
|
||||
transform={'translateY(1px)'}
|
||||
ml={1}
|
||||
/>
|
||||
</MyTooltip>
|
||||
|
@@ -68,6 +68,9 @@ const NodeVariable = dynamic(() => import('./components/Nodes/NodeVariable'), {
|
||||
const NodeUserGuide = dynamic(() => import('./components/Nodes/NodeUserGuide'), {
|
||||
ssr: false
|
||||
});
|
||||
const NodeExtract = dynamic(() => import('./components/Nodes/NodeExtract'), {
|
||||
ssr: false
|
||||
});
|
||||
|
||||
import 'reactflow/dist/style.css';
|
||||
import styles from './index.module.scss';
|
||||
@@ -83,7 +86,8 @@ const nodeTypes = {
|
||||
[FlowModuleTypeEnum.kbSearchNode]: NodeKbSearch,
|
||||
[FlowModuleTypeEnum.tfSwitchNode]: NodeTFSwitch,
|
||||
[FlowModuleTypeEnum.answerNode]: NodeAnswer,
|
||||
[FlowModuleTypeEnum.classifyQuestion]: NodeCQNode
|
||||
[FlowModuleTypeEnum.classifyQuestion]: NodeCQNode,
|
||||
[FlowModuleTypeEnum.contentExtract]: NodeExtract
|
||||
// [FlowModuleTypeEnum.empty]: EmptyModule
|
||||
};
|
||||
const edgeTypes = {
|
||||
|
@@ -8,13 +8,14 @@ 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';
|
||||
|
||||
export type CQProps = {
|
||||
systemPrompt?: string;
|
||||
history?: ChatItemType[];
|
||||
[SystemInputEnum.userChatInput]: string;
|
||||
userOpenaiAccount: UserModelSchema['openaiAccount'];
|
||||
agents: ClassifyQuestionAgentItemType[];
|
||||
[SpecialInputKeyEnum.agents]: ClassifyQuestionAgentItemType[];
|
||||
};
|
||||
export type CQResponse = {
|
||||
[TaskResponseKeyEnum.responseData]: ChatHistoryItemResType;
|
||||
|
@@ -1,50 +1,44 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { adaptChatItem_openAI } from '@/utils/plugin/openai';
|
||||
import { ChatContextFilter } from '@/service/utils/chat/index';
|
||||
import type { ChatItemType } from '@/types/chat';
|
||||
import { ChatRoleEnum } from '@/constants/chat';
|
||||
import type { ChatHistoryItemResType, ChatItemType } from '@/types/chat';
|
||||
import { ChatModuleEnum, ChatRoleEnum, TaskResponseKeyEnum } from '@/constants/chat';
|
||||
import { getAIChatApi, axiosConfig } from '@/service/ai/openai';
|
||||
import type { ClassifyQuestionAgentItemType } from '@/types/app';
|
||||
import { SystemInputEnum } from '@/constants/app';
|
||||
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';
|
||||
|
||||
export type Props = {
|
||||
systemPrompt?: string;
|
||||
userOpenaiAccount: UserModelSchema['openaiAccount'];
|
||||
history?: ChatItemType[];
|
||||
[SystemInputEnum.userChatInput]: string;
|
||||
description: string;
|
||||
agents: ClassifyQuestionAgentItemType[];
|
||||
[ContextExtractEnum.content]: string;
|
||||
[ContextExtractEnum.extractKeys]: ContextExtractAgentItemType[];
|
||||
[ContextExtractEnum.description]: string;
|
||||
};
|
||||
export type Response = {
|
||||
arguments: Record<string, any>;
|
||||
deficiency: boolean;
|
||||
[ContextExtractEnum.success]?: boolean;
|
||||
[ContextExtractEnum.failed]?: boolean;
|
||||
[ContextExtractEnum.fields]: Record<string, any>;
|
||||
[TaskResponseKeyEnum.responseData]: ChatHistoryItemResType;
|
||||
};
|
||||
|
||||
const agentModel = 'gpt-3.5-turbo';
|
||||
const agentFunName = 'agent_extract_data';
|
||||
const maxTokens = 3000;
|
||||
|
||||
export async function extract({
|
||||
systemPrompt,
|
||||
agents,
|
||||
export async function dispatchContentExtract({
|
||||
userOpenaiAccount,
|
||||
content,
|
||||
extractKeys,
|
||||
history = [],
|
||||
userChatInput,
|
||||
description
|
||||
}: Props): Promise<Response> {
|
||||
const messages: ChatItemType[] = [
|
||||
...(systemPrompt
|
||||
? [
|
||||
{
|
||||
obj: ChatRoleEnum.System,
|
||||
value: systemPrompt
|
||||
}
|
||||
]
|
||||
: []),
|
||||
...history,
|
||||
{
|
||||
obj: ChatRoleEnum.Human,
|
||||
value: userChatInput
|
||||
value: content
|
||||
}
|
||||
];
|
||||
const filterMessages = ChatContextFilter({
|
||||
@@ -62,25 +56,25 @@ export async function extract({
|
||||
description: string;
|
||||
}
|
||||
> = {};
|
||||
agents.forEach((item) => {
|
||||
extractKeys.forEach((item) => {
|
||||
properties[item.key] = {
|
||||
type: 'string',
|
||||
description: item.value
|
||||
description: item.desc
|
||||
};
|
||||
});
|
||||
|
||||
// function body
|
||||
const agentFunction = {
|
||||
name: agentFunName,
|
||||
description,
|
||||
description: `${description}\n如果内容不存在,返回空字符串。当前时间是2023/7/31 18:00`,
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties,
|
||||
required: agents.map((item) => item.key)
|
||||
required: extractKeys.map((item) => item.key)
|
||||
}
|
||||
};
|
||||
|
||||
const chatAPI = getAIChatApi();
|
||||
const chatAPI = getAIChatApi(userOpenaiAccount);
|
||||
|
||||
const response = await chatAPI.createChatCompletion(
|
||||
{
|
||||
@@ -91,21 +85,44 @@ export async function extract({
|
||||
functions: [agentFunction]
|
||||
},
|
||||
{
|
||||
...axiosConfig()
|
||||
...axiosConfig(userOpenaiAccount)
|
||||
}
|
||||
);
|
||||
|
||||
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;
|
||||
const arg: Record<string, any> = (() => {
|
||||
try {
|
||||
return JSON.parse(response.data.choices?.[0]?.message?.function_call?.arguments || '{}');
|
||||
} catch (error) {
|
||||
return {};
|
||||
}
|
||||
})();
|
||||
console.log(adaptMessages, arg);
|
||||
|
||||
// auth fields
|
||||
let success = !extractKeys.find((item) => !arg[item.key]);
|
||||
// auth empty value
|
||||
if (success) {
|
||||
for (const key in arg) {
|
||||
if (arg[key] === '') {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const tokens = response.data.usage?.total_tokens || 0;
|
||||
|
||||
return {
|
||||
arguments: arg,
|
||||
deficiency
|
||||
[ContextExtractEnum.success]: success ? true : undefined,
|
||||
[ContextExtractEnum.failed]: success ? undefined : true,
|
||||
[ContextExtractEnum.fields]: arg,
|
||||
[TaskResponseKeyEnum.responseData]: {
|
||||
moduleName: ChatModuleEnum.Extract,
|
||||
price: userOpenaiAccount?.key ? 0 : countModelPrice({ model: agentModel, tokens }),
|
||||
model: getModel(agentModel)?.name || agentModel,
|
||||
tokens,
|
||||
extractDescription: description,
|
||||
extractResult: arg
|
||||
}
|
||||
};
|
||||
}
|
||||
|
6
client/src/types/app.d.ts
vendored
6
client/src/types/app.d.ts
vendored
@@ -8,6 +8,7 @@ import {
|
||||
import type { FlowInputItemType, FlowOutputItemType, FlowOutputTargetItemType } from './flow';
|
||||
import type { AppSchema, kbSchema } from './mongoSchema';
|
||||
import { ChatModelType } from '@/constants/model';
|
||||
import { FlowValueTypeEnum } from '@/constants/flow';
|
||||
|
||||
export type AppListItemType = {
|
||||
_id: string;
|
||||
@@ -45,6 +46,11 @@ export type ClassifyQuestionAgentItemType = {
|
||||
value: string;
|
||||
key: string;
|
||||
};
|
||||
export type ContextExtractAgentItemType = {
|
||||
desc: string;
|
||||
key: string;
|
||||
required: boolean;
|
||||
};
|
||||
|
||||
export type VariableItemType = {
|
||||
id: string;
|
||||
|
4
client/src/types/chat.d.ts
vendored
4
client/src/types/chat.d.ts
vendored
@@ -66,4 +66,8 @@ export type ChatHistoryItemResType = {
|
||||
// cq
|
||||
cqList?: ClassifyQuestionAgentItemType[];
|
||||
cqResult?: string;
|
||||
|
||||
// content extract
|
||||
extractDescription?: string;
|
||||
extractResult?: Record<string, any>;
|
||||
};
|
||||
|
Reference in New Issue
Block a user