Commit 23642af6 by archer

feat: agent and ui

parent 46f20c7d
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1688632968712" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3386" xmlns:xlink="http://www.w3.org/1999/xlink" ><path d="M507.904 52.224q95.232 0 179.2 36.352t145.92 98.304 98.304 145.408 36.352 178.688-36.352 179.2-98.304 145.92-145.92 98.304-179.2 36.352-178.688-36.352-145.408-98.304-98.304-145.92-36.352-179.2 36.352-178.688 98.304-145.408 145.408-98.304 178.688-36.352zM736.256 573.44q30.72 0 55.296-15.872t24.576-47.616q0-30.72-24.576-45.568t-55.296-14.848l-452.608 0q-30.72 0-56.32 14.848t-25.6 45.568q0 31.744 25.6 47.616t56.32 15.872l452.608 0z" p-id="3387"></path></svg>
\ No newline at end of file
...@@ -35,7 +35,8 @@ const map = { ...@@ -35,7 +35,8 @@ const map = {
kbTest: require('./icons/kbTest.svg').default, kbTest: require('./icons/kbTest.svg').default,
date: require('./icons/date.svg').default, date: require('./icons/date.svg').default,
apikey: require('./icons/apikey.svg').default, apikey: require('./icons/apikey.svg').default,
save: require('./icons/save.svg').default save: require('./icons/save.svg').default,
minus: require('./icons/minus.svg').default
}; };
export type IconName = keyof typeof map; export type IconName = keyof typeof map;
......
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { Box, Flex, Tooltip, Link } from '@chakra-ui/react'; import { Box, Flex, Link } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import MyIcon from '../Icon'; import MyIcon from '../Icon';
import { useUserStore } from '@/store/user'; import { useUserStore } from '@/store/user';
......
import React, { memo } from 'react';
import { Box } from '@chakra-ui/react';
const Loading = ({ text }: { text?: string }) => {
return (
<Box>
<Box
minW={'100px'}
w={'100%'}
h={'80px'}
backgroundImage={'url("/imgs/loading.gif")'}
backgroundSize={'contain'}
backgroundRepeat={'no-repeat'}
backgroundPosition={'center'}
/>
{text && (
<Box mt={1} textAlign={'center'} fontSize={'sm'} color={'myGray.600'}>
{text}
</Box>
)}
</Box>
);
};
export default memo(Loading);
import React from 'react';
import { Tooltip, TooltipProps } from '@chakra-ui/react';
const MyTooltip = ({ children, ...props }: TooltipProps) => {
return (
<Tooltip
bg={'white'}
arrowShadowColor={' rgba(0,0,0,0.1)'}
hasArrow
arrowSize={12}
offset={[-15, 15]}
color={'myGray.800'}
px={4}
py={2}
borderRadius={'8px'}
whiteSpace={'pre-wrap'}
{...props}
>
{children}
</Tooltip>
);
};
export default MyTooltip;
...@@ -155,145 +155,145 @@ export const chatAppDemo: AppItemType = { ...@@ -155,145 +155,145 @@ export const chatAppDemo: AppItemType = {
modules: [chatModule({ id: 'chat' })] modules: [chatModule({ id: 'chat' })]
}; };
export const kbChatAppDemo: AppItemType = { // export const kbChatAppDemo: AppItemType = {
id: 'kbchat', // id: 'kbchat',
name: 'kbchat', // name: 'kbchat',
// 标记字段 // // 标记字段
modules: [ // modules: [
{ // {
moduleId: 'kbsearch', // moduleId: 'kbsearch',
flowType: FlowModuleTypeEnum.kbSearchNode, // flowType: FlowModuleTypeEnum.kbSearchNode,
type: AppModuleItemTypeEnum.http, // type: AppModuleItemTypeEnum.http,
url: '/openapi/modules/kb/search', // url: '/openapi/modules/kb/search',
position: { x: -500, y: 0 }, // position: { x: -500, y: 0 },
inputs: [ // inputs: [
{ // {
key: 'kb_ids', // key: 'kb_ids',
type: FlowInputItemTypeEnum.custom, // type: FlowInputItemTypeEnum.custom,
label: '关联的知识库', // label: '关联的知识库',
value: ['646627f4f7b896cfd8910e38'], // value: ['646627f4f7b896cfd8910e38'],
list: [] // list: []
}, // },
{ // {
key: 'similarity', // key: 'similarity',
type: FlowInputItemTypeEnum.slider, // type: FlowInputItemTypeEnum.slider,
label: '相似度', // label: '相似度',
value: 0.8, // value: 0.8,
min: 0, // min: 0,
max: 1, // max: 1,
step: 0.01, // step: 0.01,
markList: [ // markList: [
{ label: '0', value: 0 }, // { label: '0', value: 0 },
{ label: '1', value: 1 } // { label: '1', value: 1 }
] // ]
}, // },
{ // {
key: 'limit', // key: 'limit',
type: FlowInputItemTypeEnum.slider, // type: FlowInputItemTypeEnum.slider,
label: '单次搜索上限', // label: '单次搜索上限',
value: 5, // value: 5,
min: 1, // min: 1,
max: 20, // max: 20,
step: 1, // step: 1,
markList: [ // markList: [
{ label: '1', value: 1 }, // { label: '1', value: 1 },
{ label: '20', value: 20 } // { label: '20', value: 20 }
] // ]
}, // },
{ // {
key: SystemInputEnum.history, // key: SystemInputEnum.history,
type: FlowInputItemTypeEnum.hidden, // type: FlowInputItemTypeEnum.hidden,
label: '引用复用数量', // label: '引用复用数量',
value: 1 // value: 1
}, // },
{ // {
key: SystemInputEnum.userChatInput, // key: SystemInputEnum.userChatInput,
type: FlowInputItemTypeEnum.none, // type: FlowInputItemTypeEnum.none,
label: '用户输入(系统自动填写)', // label: '用户输入(系统自动填写)',
description: '' // description: ''
} // }
], // ],
outputs: [ // outputs: [
{ // {
key: 'rawSearch', // key: 'rawSearch',
label: '源搜索数据', // label: '源搜索数据',
type: FlowOutputItemTypeEnum.none, // type: FlowOutputItemTypeEnum.none,
response: true, // response: true,
targets: [] // targets: []
}, // },
{ // {
key: 'isEmpty', // key: 'isEmpty',
label: '无搜索结果', // label: '无搜索结果',
type: FlowOutputItemTypeEnum.source, // type: FlowOutputItemTypeEnum.source,
targets: [ // targets: [
{ // {
moduleId: 'tfswitch', // moduleId: 'tfswitch',
key: SystemInputEnum.switch // key: SystemInputEnum.switch
} // }
] // ]
}, // },
{ // {
key: 'quotePrompt', // key: 'quotePrompt',
label: '引用内容(字符串)', // label: '引用内容(字符串)',
type: FlowOutputItemTypeEnum.source, // type: FlowOutputItemTypeEnum.source,
targets: [ // targets: [
{ // {
moduleId: 'chat', // moduleId: 'chat',
key: 'quotePrompt' // key: 'quotePrompt'
} // }
] // ]
} // }
] // ]
}, // },
{ // {
moduleId: 'tfswitch', // moduleId: 'tfswitch',
type: AppModuleItemTypeEnum.switch, // type: AppModuleItemTypeEnum.switch,
flowType: FlowModuleTypeEnum.tfSwitchNode, // flowType: FlowModuleTypeEnum.tfSwitchNode,
position: { x: 0, y: 510 }, // position: { x: 0, y: 510 },
inputs: [ // inputs: [
{ // {
key: SystemInputEnum.switch, // key: SystemInputEnum.switch,
type: FlowInputItemTypeEnum.target, // type: FlowInputItemTypeEnum.target,
label: '触发器', // label: '触发器',
connected: true // connected: true
} // }
], // ],
outputs: [ // outputs: [
{ // {
key: 'true', // key: 'true',
label: '无搜索数据', // label: '无搜索数据',
type: FlowOutputItemTypeEnum.source, // type: FlowOutputItemTypeEnum.source,
targets: [ // targets: [
{ // {
moduleId: 'answer', // moduleId: 'answer',
key: SystemInputEnum.switch // key: SystemInputEnum.switch
} // }
] // ]
}, // },
{ // {
key: 'false', // key: 'false',
label: '有搜索数据', // label: '有搜索数据',
type: FlowOutputItemTypeEnum.source, // type: FlowOutputItemTypeEnum.source,
targets: [ // targets: [
{ // {
moduleId: 'chat', // moduleId: 'chat',
key: SystemInputEnum.switch // key: SystemInputEnum.switch
} // }
] // ]
} // }
] // ]
}, // },
{ // {
...chatModule({ id: 'chat', limitPrompt: '参考知识库内容进行回答', history: 5 }), // ...chatModule({ id: 'chat', limitPrompt: '参考知识库内容进行回答', history: 5 }),
position: { x: 300, y: 240 } // position: { x: 300, y: 240 }
}, // },
{ // {
...answerModule({ id: 'answer' }), // ...answerModule({ id: 'answer' }),
position: { x: 300, y: 0 } // position: { x: 300, y: 0 }
} // }
] // ]
}; // };
// export const classifyQuestionDemo: AppItemType = { // export const classifyQuestionDemo: AppItemType = {
// id: 'classifyQuestionDemo', // id: 'classifyQuestionDemo',
......
...@@ -25,4 +25,4 @@ export const ChatRoleMap = { ...@@ -25,4 +25,4 @@ export const ChatRoleMap = {
}; };
export const HUMAN_ICON = `https://fastgpt.run/icon/human.png`; export const HUMAN_ICON = `https://fastgpt.run/icon/human.png`;
export const LOGO_ICON = `https://fastgpt.run/imgs/modelAvatar.png`; export const LOGO_ICON = `https://fastgpt.run/icon/logo.png`;
import { AppModuleItemTypeEnum, SystemInputEnum } from '../app'; import { AppModuleItemTypeEnum, SystemInputEnum, SpecificInputEnum } from '../app';
import { FlowModuleTypeEnum, FlowInputItemTypeEnum, FlowOutputItemTypeEnum } from './index'; import { FlowModuleTypeEnum, FlowInputItemTypeEnum, FlowOutputItemTypeEnum } from './index';
import type { AppModuleTemplateItemType } from '@/types/app'; import type { AppModuleTemplateItemType } from '@/types/app';
import { chatModelList } from '../data'; import { chatModelList } from '../data';
...@@ -9,7 +9,7 @@ import { ...@@ -9,7 +9,7 @@ import {
} from './inputTemplate'; } from './inputTemplate';
export const UserInputModule: AppModuleTemplateItemType = { export const UserInputModule: AppModuleTemplateItemType = {
logo: '', logo: '/imgs/module/userChatInput.png',
name: '用户问题', name: '用户问题',
intro: '用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。', intro: '用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。',
type: AppModuleItemTypeEnum.initInput, type: AppModuleItemTypeEnum.initInput,
...@@ -32,7 +32,7 @@ export const UserInputModule: AppModuleTemplateItemType = { ...@@ -32,7 +32,7 @@ export const UserInputModule: AppModuleTemplateItemType = {
] ]
}; };
export const HistoryModule: AppModuleTemplateItemType = { export const HistoryModule: AppModuleTemplateItemType = {
logo: '', logo: '/imgs/module/history.png',
name: '聊天记录', name: '聊天记录',
intro: '用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。', intro: '用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。',
type: AppModuleItemTypeEnum.initInput, type: AppModuleItemTypeEnum.initInput,
...@@ -64,7 +64,7 @@ export const HistoryModule: AppModuleTemplateItemType = { ...@@ -64,7 +64,7 @@ export const HistoryModule: AppModuleTemplateItemType = {
}; };
export const ChatModule: AppModuleTemplateItemType = { export const ChatModule: AppModuleTemplateItemType = {
logo: '', logo: '/imgs/module/AI.png',
name: 'AI 对话', name: 'AI 对话',
intro: 'OpenAI GPT 大模型对话。', intro: 'OpenAI GPT 大模型对话。',
flowType: FlowModuleTypeEnum.chatNode, flowType: FlowModuleTypeEnum.chatNode,
...@@ -135,7 +135,7 @@ export const ChatModule: AppModuleTemplateItemType = { ...@@ -135,7 +135,7 @@ export const ChatModule: AppModuleTemplateItemType = {
], ],
outputs: [ outputs: [
{ {
key: 'answer', key: SpecificInputEnum.answerText,
label: '模型回复', label: '模型回复',
description: '直接响应,无需配置', description: '直接响应,无需配置',
type: FlowOutputItemTypeEnum.hidden, type: FlowOutputItemTypeEnum.hidden,
...@@ -145,7 +145,7 @@ export const ChatModule: AppModuleTemplateItemType = { ...@@ -145,7 +145,7 @@ export const ChatModule: AppModuleTemplateItemType = {
}; };
export const KBSearchModule: AppModuleTemplateItemType = { export const KBSearchModule: AppModuleTemplateItemType = {
logo: '', logo: '/imgs/module/db.png',
name: '知识库搜索', name: '知识库搜索',
intro: '去知识库中搜索对应的答案。可作为 AI 对话引用参考。', intro: '去知识库中搜索对应的答案。可作为 AI 对话引用参考。',
flowType: FlowModuleTypeEnum.kbSearchNode, flowType: FlowModuleTypeEnum.kbSearchNode,
...@@ -205,7 +205,7 @@ export const KBSearchModule: AppModuleTemplateItemType = { ...@@ -205,7 +205,7 @@ export const KBSearchModule: AppModuleTemplateItemType = {
{ {
key: 'quotePrompt', key: 'quotePrompt',
label: '引用内容', label: '引用内容',
description: '搜索结果为空时不触发', description: '搜索结果为空时不返回',
type: FlowOutputItemTypeEnum.source, type: FlowOutputItemTypeEnum.source,
targets: [] targets: []
} }
...@@ -213,7 +213,7 @@ export const KBSearchModule: AppModuleTemplateItemType = { ...@@ -213,7 +213,7 @@ export const KBSearchModule: AppModuleTemplateItemType = {
}; };
export const AnswerModule: AppModuleTemplateItemType = { export const AnswerModule: AppModuleTemplateItemType = {
logo: '', logo: '/imgs/module/reply.png',
name: '指定回复', name: '指定回复',
intro: '该模块可以直接回复一段指定的内容。常用于引导、提示。', intro: '该模块可以直接回复一段指定的内容。常用于引导、提示。',
type: AppModuleItemTypeEnum.answer, type: AppModuleItemTypeEnum.answer,
...@@ -221,7 +221,7 @@ export const AnswerModule: AppModuleTemplateItemType = { ...@@ -221,7 +221,7 @@ export const AnswerModule: AppModuleTemplateItemType = {
inputs: [ inputs: [
Input_Template_TFSwitch, Input_Template_TFSwitch,
{ {
key: 'answerText', key: SpecificInputEnum.answerText,
value: '', value: '',
type: FlowInputItemTypeEnum.input, type: FlowInputItemTypeEnum.input,
label: '回复的内容' label: '回复的内容'
...@@ -257,31 +257,52 @@ export const TFSwitchModule: AppModuleTemplateItemType = { ...@@ -257,31 +257,52 @@ export const TFSwitchModule: AppModuleTemplateItemType = {
} }
] ]
}; };
export const ClassifyQuestionModule: AppModuleTemplateItemType = { export const ClassifyQuestionModule: AppModuleTemplateItemType = {
logo: '', logo: '/imgs/module/cq.png',
name: '意图识别', name: '意图识别',
intro: '可以判断用户问题属于哪方面问题,从而执行不同的操作。', intro: '可以判断用户问题属于哪方面问题,从而执行不同的操作。',
type: AppModuleItemTypeEnum.switch, type: AppModuleItemTypeEnum.http,
flowType: FlowModuleTypeEnum.tfSwitchNode, url: '/openapi/modules/agent/classifyQuestion',
flowType: FlowModuleTypeEnum.classifyQuestionNode,
inputs: [ inputs: [
{ {
key: SystemInputEnum.switch, key: 'systemPrompt',
type: FlowInputItemTypeEnum.target, type: FlowInputItemTypeEnum.textarea,
label: '输入' label: '系统提示词',
description:
'你可以添加一些特定内容的介绍,从而更好的识别用户的问题类型。这个内容通常是给模型介绍一个它不知道的内容。',
placeholder: '例如: \n1. Laf 是一个云函数开发平台……\n2. Sealos 是一个集群操作系统',
value: ''
},
Input_Template_History,
Input_Template_UserChatInput,
{
key: 'agents',
type: FlowInputItemTypeEnum.custom,
label: '',
value: [
{
value: '',
key: 'a'
},
{
value: '',
key: 'b'
}
]
} }
], ],
outputs: [ outputs: [
{ {
key: 'true', key: 'a',
label: 'True', label: '',
type: FlowOutputItemTypeEnum.source, type: FlowOutputItemTypeEnum.hidden,
targets: [] targets: []
}, },
{ {
key: 'false', key: 'b',
label: 'False', label: '',
type: FlowOutputItemTypeEnum.source, type: FlowOutputItemTypeEnum.hidden,
targets: [] targets: []
} }
] ]
...@@ -302,7 +323,7 @@ export const ModuleTemplates = [ ...@@ -302,7 +323,7 @@ export const ModuleTemplates = [
}, },
{ {
label: '工具', label: '工具',
list: [AnswerModule, TFSwitchModule] list: [AnswerModule]
}, },
{ {
label: 'Agent', label: 'Agent',
......
...@@ -24,7 +24,8 @@ export enum FlowModuleTypeEnum { ...@@ -24,7 +24,8 @@ export enum FlowModuleTypeEnum {
chatNode = 'chatNode', chatNode = 'chatNode',
kbSearchNode = 'kbSearchNode', kbSearchNode = 'kbSearchNode',
tfSwitchNode = 'tfSwitchNode', tfSwitchNode = 'tfSwitchNode',
answerNode = 'answerNode' answerNode = 'answerNode',
classifyQuestionNode = 'classifyQuestionNode'
} }
export const edgeOptions = { export const edgeOptions = {
......
...@@ -202,6 +202,16 @@ const Select = selectMultiStyle({ ...@@ -202,6 +202,16 @@ 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({ export const theme = extendTheme({
styles: { styles: {
...@@ -297,6 +307,7 @@ export const theme = extendTheme({ ...@@ -297,6 +307,7 @@ export const theme = extendTheme({
Textarea, Textarea,
Switch, Switch,
Select, Select,
Tooltip,
NumberInput NumberInput
} }
}); });
...@@ -79,7 +79,7 @@ export async function classifyQuestion({ ...@@ -79,7 +79,7 @@ export async function classifyQuestion({
properties: { properties: {
type: { type: {
type: 'string', type: 'string',
description: agents.map((item) => `${item.desc},返回: '${item.key}'`).join('; '), description: agents.map((item) => `${item.value},返回: '${item.key}'`).join('; '),
enum: agents.map((item) => item.key) enum: agents.map((item) => item.key)
} }
}, },
...@@ -106,7 +106,10 @@ export async function classifyQuestion({ ...@@ -106,7 +106,10 @@ export async function classifyQuestion({
if (!arg.type) { if (!arg.type) {
throw new Error(''); throw new Error('');
} }
console.log(arg.type); console.log(
'意图结果',
agents.findIndex((item) => item.key === arg.type)
);
return { return {
[arg.type]: 1 [arg.type]: 1
......
...@@ -10,6 +10,7 @@ import type { ChatItemType } from '@/types/chat'; ...@@ -10,6 +10,7 @@ import type { ChatItemType } from '@/types/chat';
import { ChatRoleEnum, sseResponseEventEnum } from '@/constants/chat'; import { ChatRoleEnum, sseResponseEventEnum } from '@/constants/chat';
import { parseStreamChunk, textAdaptGptResponse } from '@/utils/adapt'; import { parseStreamChunk, textAdaptGptResponse } from '@/utils/adapt';
import { getOpenAIApi, axiosConfig } from '@/service/ai/openai'; import { getOpenAIApi, axiosConfig } from '@/service/ai/openai';
import { SpecificInputEnum } from '@/constants/app';
export type Props = { export type Props = {
model: `${OpenAiChatEnum}`; model: `${OpenAiChatEnum}`;
...@@ -22,7 +23,7 @@ export type Props = { ...@@ -22,7 +23,7 @@ export type Props = {
systemPrompt?: string; systemPrompt?: string;
limitPrompt?: string; limitPrompt?: string;
}; };
export type Response = { answer: string }; export type Response = { [SpecificInputEnum.answerText]: string };
export default async function handler(req: NextApiRequest, res: NextApiResponse) { export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try { try {
...@@ -132,7 +133,8 @@ export async function chatCompletion({ ...@@ -132,7 +133,8 @@ export async function chatCompletion({
const chatAPI = getOpenAIApi(); const chatAPI = getOpenAIApi();
/* count response max token */ /* count response max token */
const promptsToken = modelToolMap[model].countTokens({ const promptsToken = modelToolMap.countTokens({
model,
messages: filterMessages messages: filterMessages
}); });
maxToken = maxToken + promptsToken > modelTokenLimit ? modelTokenLimit - promptsToken : maxToken; maxToken = maxToken + promptsToken > modelTokenLimit ? modelTokenLimit - promptsToken : maxToken;
...@@ -143,8 +145,8 @@ export async function chatCompletion({ ...@@ -143,8 +145,8 @@ export async function chatCompletion({
temperature: Number(temperature || 0), temperature: Number(temperature || 0),
max_tokens: maxToken, max_tokens: maxToken,
messages: adaptMessages, messages: adaptMessages,
frequency_penalty: 0.5, // 越大,重复内容越少 // frequency_penalty: 0.5, // 越大,重复内容越少
presence_penalty: -0.5, // 越大,越容易出现新内容 // presence_penalty: -0.5, // 越大,越容易出现新内容
stream stream
}, },
{ {
...@@ -184,7 +186,7 @@ export async function chatCompletion({ ...@@ -184,7 +186,7 @@ export async function chatCompletion({
})(); })();
return { return {
answer answerText: answer
}; };
} }
......
...@@ -92,8 +92,9 @@ export async function kbSearch({ ...@@ -92,8 +92,9 @@ export async function kbSearch({
const searchRes: QuoteItemType[] = res?.[2]?.rows || []; const searchRes: QuoteItemType[] = res?.[2]?.rows || [];
// filter part quote by maxToken // filter part quote by maxToken
const sliceResult = modelToolMap['gpt-3.5-turbo'] const sliceResult = modelToolMap
.tokenSlice({ .tokenSlice({
model: 'gpt-3.5-turbo',
maxToken, maxToken,
messages: searchRes.map((item, i) => ({ messages: searchRes.map((item, i) => ({
obj: ChatRoleEnum.System, obj: ChatRoleEnum.System,
......
...@@ -10,12 +10,7 @@ import { getChatHistory } from './getHistory'; ...@@ -10,12 +10,7 @@ import { getChatHistory } from './getHistory';
import { saveChat } from '@/pages/api/chat/saveChat'; import { saveChat } from '@/pages/api/chat/saveChat';
import { sseResponse } from '@/service/utils/tools'; import { sseResponse } from '@/service/utils/tools';
import { type ChatCompletionRequestMessage } from 'openai'; import { type ChatCompletionRequestMessage } from 'openai';
import { import { SpecificInputEnum, AppModuleItemTypeEnum } from '@/constants/app';
kbChatAppDemo,
chatAppDemo,
SpecificInputEnum,
AppModuleItemTypeEnum
} from '@/constants/app';
import { model, Types } from 'mongoose'; import { model, Types } from 'mongoose';
import { moduleFetch } from '@/service/api/request'; import { moduleFetch } from '@/service/api/request';
import { AppModuleItemType, RunningModuleItemType } from '@/types/app'; import { AppModuleItemType, RunningModuleItemType } from '@/types/app';
...@@ -42,7 +37,6 @@ export type ChatResponseType = { ...@@ -42,7 +37,6 @@ export type ChatResponseType = {
quoteLen?: number; quoteLen?: number;
}; };
/* 发送提示词 */
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) { export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) {
res.on('close', () => { res.on('close', () => {
res.end(); res.end();
...@@ -117,7 +111,6 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex ...@@ -117,7 +111,6 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
}, },
stream stream
}); });
console.log(responseData, answerText);
// save chat // save chat
if (typeof chatId === 'string') { if (typeof chatId === 'string') {
...@@ -354,7 +347,7 @@ function loadModules(modules: AppModuleItemType[]): RunningModuleItemType[] { ...@@ -354,7 +347,7 @@ function loadModules(modules: AppModuleItemType[]): RunningModuleItemType[] {
})), })),
outputs: module.outputs.map((item) => ({ outputs: module.outputs.map((item) => ({
key: item.key, key: item.key,
answer: item.type === FlowOutputItemTypeEnum.answer, answer: item.key === SpecificInputEnum.answerText,
response: item.response, response: item.response,
value: undefined, value: undefined,
targets: item.targets targets: item.targets
......
...@@ -2,7 +2,6 @@ import React, { useCallback, useState } from 'react'; ...@@ -2,7 +2,6 @@ import React, { useCallback, useState } from 'react';
import { import {
Flex, Flex,
Box, Box,
Tooltip,
Button, Button,
TableContainer, TableContainer,
Table, Table,
...@@ -37,6 +36,7 @@ import { formatTimeToChatTime, useCopyData, getErrText } from '@/utils/tools'; ...@@ -37,6 +36,7 @@ import { formatTimeToChatTime, useCopyData, getErrText } from '@/utils/tools';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { defaultShareChat } from '@/constants/model'; import { defaultShareChat } from '@/constants/model';
import type { ShareChatEditType } from '@/types/app'; import type { ShareChatEditType } from '@/types/app';
import MyTooltip from '@/components/MyTooltip';
const Share = ({ modelId }: { modelId: string }) => { const Share = ({ modelId }: { modelId: string }) => {
const { toast } = useToast(); const { toast } = useToast();
...@@ -112,9 +112,9 @@ ${e.password ? `密码为: ${e.password}` : ''}`; ...@@ -112,9 +112,9 @@ ${e.password ? `密码为: ${e.password}` : ''}`;
<Flex justifyContent={'space-between'}> <Flex justifyContent={'space-between'}>
<Box fontWeight={'bold'}> <Box fontWeight={'bold'}>
免登录聊天窗口 免登录聊天窗口
<Tooltip label="可以直接分享该模型给其他用户去进行对话,对方无需登录即可直接进行对话。注意,这个功能会消耗你账号的tokens。请保管好链接和密码。"> <MyTooltip label="可以直接分享该模型给其他用户去进行对话,对方无需登录即可直接进行对话。注意,这个功能会消耗你账号的tokens。请保管好链接和密码。">
<QuestionOutlineIcon ml={1} /> <QuestionOutlineIcon ml={1} />
</Tooltip> </MyTooltip>
</Box> </Box>
<Button <Button
variant={'base'} variant={'base'}
......
...@@ -10,13 +10,7 @@ const NodeAnswer = ({ ...@@ -10,13 +10,7 @@ const NodeAnswer = ({
data: { moduleId, inputs, outputs, onChangeNode, ...props } data: { moduleId, inputs, outputs, onChangeNode, ...props }
}: NodeProps<FlowModuleItemType>) => { }: NodeProps<FlowModuleItemType>) => {
return ( return (
<NodeCard <NodeCard minW={'400px'} moduleId={moduleId} {...props}>
minW={'400px'}
logo={'/icon/logo.png'}
name={'SSE 响应'}
moduleId={moduleId}
{...props}
>
<Divider text="Input" /> <Divider text="Input" />
<Container> <Container>
<RenderInput moduleId={moduleId} onChangeNode={onChangeNode} flowInputList={inputs} /> <RenderInput moduleId={moduleId} onChangeNode={onChangeNode} flowInputList={inputs} />
......
import React from 'react';
import { NodeProps } from 'reactflow';
import { Box, Input, Button, Flex } from '@chakra-ui/react';
import NodeCard from './modules/NodeCard';
import { FlowModuleItemType } from '@/types/flow';
import Divider from './modules/Divider';
import Container from './modules/Container';
import RenderInput from './render/RenderInput';
import type { ClassifyQuestionAgentItemType } from '@/types/app';
import { Handle, Position } from 'reactflow';
import { customAlphabet } from 'nanoid';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 4);
import MyIcon from '@/components/Icon';
import { FlowOutputItemTypeEnum } from '@/constants/flow';
const NodeCQNode = ({
data: { moduleId, inputs, outputs, onChangeNode, ...props }
}: NodeProps<FlowModuleItemType>) => {
return (
<NodeCard minW={'400px'} moduleId={moduleId} {...props}>
<Divider text="Input" />
<Container>
<RenderInput
moduleId={moduleId}
onChangeNode={onChangeNode}
flowInputList={inputs}
CustomComponent={{
agents: ({
key: agentKey,
value: agents = []
}: {
key: string;
value?: ClassifyQuestionAgentItemType[];
}) => (
<Box>
{agents.map((item, i) => (
<Flex key={item.key} mb={4} alignItems={'center'}>
<MyIcon
mr={2}
name={'minus'}
w={'14px'}
cursor={'pointer'}
color={'myGray.600'}
_hover={{ color: 'myGray.900' }}
onClick={() => {
const newInputValue = agents.filter((input) => input.key !== item.key);
const newOutputVal = outputs.filter((output) => output.key !== item.key);
onChangeNode({
moduleId,
type: 'inputs',
key: agentKey,
value: newInputValue
});
onChangeNode({
moduleId,
type: 'outputs',
key: agentKey,
value: newOutputVal
});
}}
/>
<Box flex={1}>
<Box flex={1}>意图{i + 1}</Box>
<Box position={'relative'}>
<Input
mt={1}
defaultValue={item.value}
onChange={(e) => {
const newVal = agents.map((val) =>
val.key === item.key
? {
...val,
value: e.target.value
}
: val
);
onChangeNode({
moduleId,
key: agentKey,
value: newVal
});
}}
/>
<Handle
style={{
top: '50%',
right: '-14px',
transform: 'translate(50%,-50%)',
width: '12px',
height: '12px',
background: '#9CA2A8'
}}
type="source"
id={item.key}
position={Position.Right}
/>
</Box>
</Box>
</Flex>
))}
<Button
onClick={() => {
const key = nanoid();
const newInputValue = agents.concat({ value: '', key });
const newOutputValue = outputs.concat({
key,
label: '',
type: FlowOutputItemTypeEnum.hidden,
targets: []
});
onChangeNode({
moduleId,
type: 'inputs',
key: agentKey,
value: newInputValue
});
onChangeNode({
moduleId,
type: 'outputs',
key: agentKey,
value: newOutputValue
});
}}
>
添加意图
</Button>
</Box>
)
}}
/>
</Container>
</NodeCard>
);
};
export default React.memo(NodeCQNode);
...@@ -26,7 +26,7 @@ const NodeKbSearch = ({ ...@@ -26,7 +26,7 @@ const NodeKbSearch = ({
onChangeNode={onChangeNode} onChangeNode={onChangeNode}
flowInputList={inputs} flowInputList={inputs}
CustomComponent={{ CustomComponent={{
kb_ids: ({ key, value, onChangeNode }) => ( kb_ids: ({ key, value }) => (
<KBSelect <KBSelect
relatedKbs={value} relatedKbs={value}
onChange={(e) => { onChange={(e) => {
......
import React from 'react'; import React, { useRef } from 'react';
import { Box, Flex } from '@chakra-ui/react'; import { Box, Flex, useOutsideClick } from '@chakra-ui/react';
import { ModuleTemplates } from '@/constants/flow/ModuleTemplate'; import { ModuleTemplates } from '@/constants/flow/ModuleTemplate';
import type { AppModuleTemplateItemType } from '@/types/app'; import type { AppModuleTemplateItemType } from '@/types/app';
import type { XYPosition } from 'reactflow'; import type { XYPosition } from 'reactflow';
...@@ -7,13 +7,36 @@ import Avatar from '@/components/Avatar'; ...@@ -7,13 +7,36 @@ import Avatar from '@/components/Avatar';
const ModuleStoreList = ({ const ModuleStoreList = ({
isOpen, isOpen,
onAddNode onAddNode,
onClose
}: { }: {
isOpen: boolean; isOpen: boolean;
onAddNode: (e: { template: AppModuleTemplateItemType; position: XYPosition }) => void; onAddNode: (e: { template: AppModuleTemplateItemType; position: XYPosition }) => void;
onClose: () => void;
}) => { }) => {
const ContextMenuRef = useRef(null);
useOutsideClick({
ref: ContextMenuRef,
handler: () => {
onClose();
}
});
return ( return (
<>
<Box
zIndex={2}
display={isOpen ? 'block' : 'none'}
position={'fixed'}
top={0}
left={0}
right={0}
bottom={0}
></Box>
<Flex <Flex
zIndex={3}
ref={ContextMenuRef}
flexDirection={'column'} flexDirection={'column'}
position={'absolute'} position={'absolute'}
top={'65px'} top={'65px'}
...@@ -21,7 +44,6 @@ const ModuleStoreList = ({ ...@@ -21,7 +44,6 @@ const ModuleStoreList = ({
h={isOpen ? '90%' : '0'} h={isOpen ? '90%' : '0'}
w={isOpen ? '360px' : '0'} w={isOpen ? '360px' : '0'}
bg={'white'} bg={'white'}
zIndex={1}
boxShadow={'3px 0 20px rgba(0,0,0,0.2)'} boxShadow={'3px 0 20px rgba(0,0,0,0.2)'}
borderRadius={'20px'} borderRadius={'20px'}
overflow={'hidden'} overflow={'hidden'}
...@@ -44,14 +66,14 @@ const ModuleStoreList = ({ ...@@ -44,14 +66,14 @@ const ModuleStoreList = ({
borderRadius={'md'} borderRadius={'md'}
draggable draggable
onDragEnd={(e) => { onDragEnd={(e) => {
if (e.clientX < 400) return; // if (e.clientX < 400) return;
onAddNode({ onAddNode({
template: item, template: item,
position: { x: e.clientX, y: e.clientY } position: { x: e.clientX, y: e.clientY }
}); });
}} }}
> >
<Avatar src={item.logo} w={'34px'} /> <Avatar src={item.logo} w={'34px'} borderRadius={'0'} />
<Box ml={5} flex={'1 0 0'}> <Box ml={5} flex={'1 0 0'}>
<Box color={'black'}>{item.name}</Box> <Box color={'black'}>{item.name}</Box>
<Box color={'myGray.500'} fontSize={'sm'}> <Box color={'myGray.500'} fontSize={'sm'}>
...@@ -63,6 +85,7 @@ const ModuleStoreList = ({ ...@@ -63,6 +85,7 @@ const ModuleStoreList = ({
)} )}
</Box> </Box>
</Flex> </Flex>
</>
); );
}; };
......
import React from 'react'; import React from 'react';
import { Box, Tooltip } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import { QuestionOutlineIcon } from '@chakra-ui/icons'; import { QuestionOutlineIcon } from '@chakra-ui/icons';
import MyTooltip from '@/components/MyTooltip';
const Label = ({ const Label = ({
required = false, required = false,
...@@ -19,9 +20,9 @@ const Label = ({ ...@@ -19,9 +20,9 @@ const Label = ({
</Box> </Box>
)} )}
{description && ( {description && (
<Tooltip label={description}> <MyTooltip label={description}>
<QuestionOutlineIcon display={['none', 'inline']} fontSize={'12px'} mb={1} ml={1} /> <QuestionOutlineIcon display={['none', 'inline']} fontSize={'12px'} mb={1} ml={1} />
</Tooltip> </MyTooltip>
)} )}
</Box> </Box>
); );
......
...@@ -4,7 +4,6 @@ import { ...@@ -4,7 +4,6 @@ import {
Box, Box,
Textarea, Textarea,
Input, Input,
Tooltip,
NumberInput, NumberInput,
NumberInputField, NumberInputField,
NumberInputStepper, NumberInputStepper,
...@@ -16,6 +15,7 @@ import { QuestionOutlineIcon } from '@chakra-ui/icons'; ...@@ -16,6 +15,7 @@ import { QuestionOutlineIcon } from '@chakra-ui/icons';
import { Handle, Position } from 'reactflow'; import { Handle, Position } from 'reactflow';
import MySelect from '@/components/Select'; import MySelect from '@/components/Select';
import MySlider from '@/components/Slider'; import MySlider from '@/components/Slider';
import MyTooltip from '@/components/MyTooltip';
const Label = ({ const Label = ({
required = false, required = false,
...@@ -34,9 +34,9 @@ const Label = ({ ...@@ -34,9 +34,9 @@ const Label = ({
</Box> </Box>
)} )}
{description && ( {description && (
<Tooltip label={description}> <MyTooltip label={description}>
<QuestionOutlineIcon display={['none', 'inline']} ml={1} /> <QuestionOutlineIcon display={['none', 'inline']} ml={1} />
</Tooltip> </MyTooltip>
)} )}
</Box> </Box>
); );
...@@ -49,14 +49,7 @@ const RenderBody = ({ ...@@ -49,14 +49,7 @@ const RenderBody = ({
}: { }: {
flowInputList: FlowInputItemType[]; flowInputList: FlowInputItemType[];
moduleId: string; moduleId: string;
CustomComponent?: Record< CustomComponent?: Record<string, (e: FlowInputItemType) => React.ReactNode>;
string,
(e: {
key: string;
value: any;
onChangeNode: FlowModuleItemType['onChangeNode'];
}) => React.ReactNode
>;
onChangeNode: FlowModuleItemType['onChangeNode']; onChangeNode: FlowModuleItemType['onChangeNode'];
}) => { }) => {
return ( return (
...@@ -65,9 +58,11 @@ const RenderBody = ({ ...@@ -65,9 +58,11 @@ const RenderBody = ({
(item) => (item) =>
item.type !== FlowInputItemTypeEnum.hidden && ( item.type !== FlowInputItemTypeEnum.hidden && (
<Box key={item.key} _notLast={{ mb: 7 }} position={'relative'}> <Box key={item.key} _notLast={{ mb: 7 }} position={'relative'}>
{!!item.label && (
<Label required={item.required} description={item.description}> <Label required={item.required} description={item.description}>
{item.label} {item.label}
</Label> </Label>
)}
<Box mt={2} className={'nodrag'}> <Box mt={2} className={'nodrag'}>
{item.type === FlowInputItemTypeEnum.numberInput && ( {item.type === FlowInputItemTypeEnum.numberInput && (
<NumberInput <NumberInput
...@@ -151,9 +146,7 @@ const RenderBody = ({ ...@@ -151,9 +146,7 @@ const RenderBody = ({
</Box> </Box>
)} )}
{item.type === FlowInputItemTypeEnum.custom && CustomComponent[item.key] && ( {item.type === FlowInputItemTypeEnum.custom && CustomComponent[item.key] && (
<> <>{CustomComponent[item.key]({ ...item })}</>
{CustomComponent[item.key]({ key: item.key, value: item.value, onChangeNode })}
</>
)} )}
{item.type === FlowInputItemTypeEnum.target && ( {item.type === FlowInputItemTypeEnum.target && (
<Handle <Handle
......
import React from 'react'; import React from 'react';
import type { FlowOutputItemType } from '@/types/flow'; import type { FlowOutputItemType } from '@/types/flow';
import { Box, Tooltip, Flex } from '@chakra-ui/react'; import { Box, Flex } from '@chakra-ui/react';
import { FlowOutputItemTypeEnum } from '@/constants/flow'; import { FlowOutputItemTypeEnum } from '@/constants/flow';
import { QuestionOutlineIcon } from '@chakra-ui/icons'; import { QuestionOutlineIcon } from '@chakra-ui/icons';
import { Handle, Position } from 'reactflow'; import { Handle, Position } from 'reactflow';
import MyTooltip from '@/components/MyTooltip';
const Label = ({ const Label = ({
children, children,
...@@ -14,9 +15,9 @@ const Label = ({ ...@@ -14,9 +15,9 @@ const Label = ({
}) => ( }) => (
<Flex as={'label'} justifyContent={'right'} alignItems={'center'} position={'relative'}> <Flex as={'label'} justifyContent={'right'} alignItems={'center'} position={'relative'}>
{description && ( {description && (
<Tooltip label={description}> <MyTooltip label={description}>
<QuestionOutlineIcon display={['none', 'inline']} mr={1} /> <QuestionOutlineIcon display={['none', 'inline']} mr={1} />
</Tooltip> </MyTooltip>
)} )}
{children} {children}
</Flex> </Flex>
......
import React, { useCallback, useEffect } from 'react'; import React, { useCallback, useEffect, useRef } from 'react';
import ReactFlow, { import ReactFlow, {
Background, Background,
Controls, Controls,
...@@ -7,7 +7,8 @@ import ReactFlow, { ...@@ -7,7 +7,8 @@ import ReactFlow, {
useNodesState, useNodesState,
useEdgesState, useEdgesState,
XYPosition, XYPosition,
Connection Connection,
useViewport
} from 'reactflow'; } from 'reactflow';
import { Box, Flex, IconButton, useTheme, useDisclosure } from '@chakra-ui/react'; import { Box, Flex, IconButton, useTheme, useDisclosure } from '@chakra-ui/react';
import { SmallCloseIcon } from '@chakra-ui/icons'; import { SmallCloseIcon } from '@chakra-ui/icons';
...@@ -47,6 +48,9 @@ const NodeQuestionInput = dynamic(() => import('./components/NodeQuestionInput') ...@@ -47,6 +48,9 @@ const NodeQuestionInput = dynamic(() => import('./components/NodeQuestionInput')
const TemplateList = dynamic(() => import('./components/TemplateList'), { const TemplateList = dynamic(() => import('./components/TemplateList'), {
ssr: false ssr: false
}); });
const NodeCQNode = dynamic(() => import('./components/NodeCQNode'), {
ssr: false
});
import 'reactflow/dist/style.css'; import 'reactflow/dist/style.css';
import styles from './index.module.scss'; import styles from './index.module.scss';
...@@ -60,14 +64,18 @@ const nodeTypes = { ...@@ -60,14 +64,18 @@ const nodeTypes = {
[FlowModuleTypeEnum.chatNode]: NodeChat, [FlowModuleTypeEnum.chatNode]: NodeChat,
[FlowModuleTypeEnum.kbSearchNode]: NodeKbSearch, [FlowModuleTypeEnum.kbSearchNode]: NodeKbSearch,
[FlowModuleTypeEnum.tfSwitchNode]: NodeTFSwitch, [FlowModuleTypeEnum.tfSwitchNode]: NodeTFSwitch,
[FlowModuleTypeEnum.answerNode]: NodeAnswer [FlowModuleTypeEnum.answerNode]: NodeAnswer,
[FlowModuleTypeEnum.classifyQuestionNode]: NodeCQNode
}; };
const edgeTypes = { const edgeTypes = {
buttonedge: ButtonEdge buttonedge: ButtonEdge
}; };
type Props = { app: AppSchema; onBack: () => void };
const AppEdit = ({ app, onBack }: { app: AppSchema; onBack: () => void }) => { const AppEdit = ({ app, onBack }: Props) => {
const reactFlowWrapper = useRef<HTMLDivElement>(null);
const theme = useTheme(); const theme = useTheme();
const { x, y, zoom } = useViewport();
const [nodes, setNodes, onNodesChange] = useNodesState<FlowModuleItemType>([]); const [nodes, setNodes, onNodesChange] = useNodesState<FlowModuleItemType>([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]); const [edges, setEdges, onEdgesChange] = useEdgesState([]);
const { const {
...@@ -77,11 +85,11 @@ const AppEdit = ({ app, onBack }: { app: AppSchema; onBack: () => void }) => { ...@@ -77,11 +85,11 @@ const AppEdit = ({ app, onBack }: { app: AppSchema; onBack: () => void }) => {
} = useDisclosure(); } = useDisclosure();
const onChangeNode = useCallback( const onChangeNode = useCallback(
({ moduleId, key, value, valueKey = 'value' }: FlowModuleItemChangeProps) => { ({ moduleId, key, type = 'inputs', value, valueKey = 'value' }: FlowModuleItemChangeProps) => {
setNodes((nodes) => setNodes((nodes) =>
nodes.map((node) => { nodes.map((node) => {
if (node.id !== moduleId) return node; if (node.id !== moduleId) return node;
if (type === 'inputs') {
return { return {
...node, ...node,
data: { data: {
...@@ -97,6 +105,15 @@ const AppEdit = ({ app, onBack }: { app: AppSchema; onBack: () => void }) => { ...@@ -97,6 +105,15 @@ const AppEdit = ({ app, onBack }: { app: AppSchema; onBack: () => void }) => {
}) })
} }
}; };
}
return {
...node,
data: {
...node.data,
outputs: value
}
};
}) })
); );
}, },
...@@ -111,12 +128,17 @@ const AppEdit = ({ app, onBack }: { app: AppSchema; onBack: () => void }) => { ...@@ -111,12 +128,17 @@ const AppEdit = ({ app, onBack }: { app: AppSchema; onBack: () => void }) => {
); );
const onAddNode = useCallback( const onAddNode = useCallback(
({ template, position }: { template: AppModuleTemplateItemType; position: XYPosition }) => { ({ template, position }: { template: AppModuleTemplateItemType; position: XYPosition }) => {
if (!reactFlowWrapper.current) return;
const reactFlowBounds = reactFlowWrapper.current.getBoundingClientRect();
const mouseX = (position.x - reactFlowBounds.left - x) / zoom - 100;
const mouseY = (position.y - reactFlowBounds.top - y) / zoom;
setNodes((state) => setNodes((state) =>
state.concat( state.concat(
appModule2FlowNode({ appModule2FlowNode({
item: { item: {
...template, ...template,
position, position: { x: mouseX, y: mouseY },
moduleId: nanoid() moduleId: nanoid()
}, },
onChangeNode, onChangeNode,
...@@ -125,7 +147,7 @@ const AppEdit = ({ app, onBack }: { app: AppSchema; onBack: () => void }) => { ...@@ -125,7 +147,7 @@ const AppEdit = ({ app, onBack }: { app: AppSchema; onBack: () => void }) => {
) )
); );
}, },
[onChangeNode, onDelNode, setNodes] [onChangeNode, onDelNode, setNodes, x, y, zoom]
); );
const onDelConnect = useCallback( const onDelConnect = useCallback(
...@@ -245,6 +267,13 @@ const AppEdit = ({ app, onBack }: { app: AppSchema; onBack: () => void }) => { ...@@ -245,6 +267,13 @@ const AppEdit = ({ app, onBack }: { app: AppSchema; onBack: () => void }) => {
borderRadius={'lg'} borderRadius={'lg'}
isLoading={isLoading} isLoading={isLoading}
aria-label={'save'} aria-label={'save'}
bg={'myBlue.200'}
variant={'base'}
border={'none'}
color={'myGray.900'}
_hover={{
bg: 'myBlue.300'
}}
onClick={onclickSave} onClick={onclickSave}
/> />
</Flex> </Flex>
...@@ -270,11 +299,13 @@ const AppEdit = ({ app, onBack }: { app: AppSchema; onBack: () => void }) => { ...@@ -270,11 +299,13 @@ const AppEdit = ({ app, onBack }: { app: AppSchema; onBack: () => void }) => {
transition={'0.2s ease'} transition={'0.2s ease'}
aria-label={''} aria-label={''}
zIndex={1} zIndex={1}
boxShadow={'1px 1px 6px #4e83fd'} boxShadow={'2px 2px 6px #85b1ff'}
onClick={() => (isOpenTemplate ? onCloseTemplate() : onOpenTemplate())} onClick={() => {
isOpenTemplate ? onCloseTemplate() : onOpenTemplate();
}}
/> />
<ReactFlowProvider>
<ReactFlow <ReactFlow
ref={reactFlowWrapper}
className={styles.panel} className={styles.panel}
nodes={nodes} nodes={nodes}
edges={edges} edges={edges}
...@@ -302,11 +333,16 @@ const AppEdit = ({ app, onBack }: { app: AppSchema; onBack: () => void }) => { ...@@ -302,11 +333,16 @@ const AppEdit = ({ app, onBack }: { app: AppSchema; onBack: () => void }) => {
showInteractive={false} showInteractive={false}
/> />
</ReactFlow> </ReactFlow>
</ReactFlowProvider> <TemplateList isOpen={isOpenTemplate} onAddNode={onAddNode} onClose={onCloseTemplate} />
<TemplateList isOpen={isOpenTemplate} onAddNode={onAddNode} />
</Box> </Box>
</Flex> </Flex>
); );
}; };
export default AppEdit; const Flow = (data: Props) => (
<ReactFlowProvider>
<AppEdit {...data} />
</ReactFlowProvider>
);
export default Flow;
...@@ -173,7 +173,7 @@ const CreateModal = ({ onClose, onSuccess }: { onClose: () => void; onSuccess: ( ...@@ -173,7 +173,7 @@ const CreateModal = ({ onClose, onSuccess }: { onClose: () => void; onSuccess: (
boxShadow={'sm'} boxShadow={'sm'}
{...(getValues('templateId') === item.id {...(getValues('templateId') === item.id
? { ? {
bg: 'myBlue.300' bg: 'myWhite.600'
} }
: { : {
_hover: { _hover: {
......
...@@ -120,7 +120,13 @@ const MyApps = () => { ...@@ -120,7 +120,13 @@ const MyApps = () => {
}} }}
/> />
</Flex> </Flex>
<Box className={styles.intro} py={2} fontSize={'sm'} color={'myGray.600'}> <Box
className={styles.intro}
py={2}
wordBreak={'break-all'}
fontSize={'sm'}
color={'myGray.600'}
>
{app.intro || '这个应用还没写介绍~'} {app.intro || '这个应用还没写介绍~'}
</Box> </Box>
</Card> </Card>
......
import React from 'react'; import React from 'react';
import { Box, Flex, Button, Tooltip, Card } from '@chakra-ui/react'; import { Box, Flex, Button, Card } from '@chakra-ui/react';
import type { ShareAppItem } from '@/types/app'; import type { ShareAppItem } from '@/types/app';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import MyIcon from '@/components/Icon'; import MyIcon from '@/components/Icon';
import styles from '../index.module.scss'; import styles from '../index.module.scss';
import Avatar from '@/components/Avatar'; import Avatar from '@/components/Avatar';
import MyTooltip from '@/components/MyTooltip';
const ShareModelList = ({ const ShareModelList = ({
models = [], models = [],
...@@ -44,7 +45,7 @@ const ShareModelList = ({ ...@@ -44,7 +45,7 @@ const ShareModelList = ({
{model.name} {model.name}
</Box> </Box>
</Flex> </Flex>
<Tooltip label={model.intro}> <MyTooltip label={model.intro}>
<Box <Box
className={styles.intro} className={styles.intro}
flex={1} flex={1}
...@@ -55,7 +56,7 @@ const ShareModelList = ({ ...@@ -55,7 +56,7 @@ const ShareModelList = ({
> >
{model.intro || '这个应用还没有介绍~'} {model.intro || '这个应用还没有介绍~'}
</Box> </Box>
</Tooltip> </MyTooltip>
<Flex justifyContent={'space-between'}> <Flex justifyContent={'space-between'}>
<Flex <Flex
......
...@@ -23,7 +23,6 @@ import { ...@@ -23,7 +23,6 @@ import {
DrawerOverlay, DrawerOverlay,
DrawerContent, DrawerContent,
Card, Card,
Tooltip,
useOutsideClick, useOutsideClick,
useTheme useTheme
} from '@chakra-ui/react'; } from '@chakra-ui/react';
...@@ -48,6 +47,7 @@ import Avatar from '@/components/Avatar'; ...@@ -48,6 +47,7 @@ import Avatar from '@/components/Avatar';
import Empty from './components/Empty'; import Empty from './components/Empty';
import QuoteModal from './components/QuoteModal'; import QuoteModal from './components/QuoteModal';
import { HUMAN_ICON } from '@/constants/chat'; import { HUMAN_ICON } from '@/constants/chat';
import MyTooltip from '@/components/MyTooltip';
const Markdown = dynamic(async () => await import('@/components/Markdown')); const Markdown = dynamic(async () => await import('@/components/Markdown'));
const PhoneSliderBar = dynamic(() => import('./components/PhoneSliderBar'), { const PhoneSliderBar = dynamic(() => import('./components/PhoneSliderBar'), {
...@@ -701,7 +701,7 @@ const Chat = () => { ...@@ -701,7 +701,7 @@ const Chat = () => {
{item.obj === 'Human' && <Box flex={1} />} {item.obj === 'Human' && <Box flex={1} />}
{/* avatar */} {/* avatar */}
<Menu autoSelect={false} isLazy> <Menu autoSelect={false} isLazy>
<Tooltip label={item.obj === 'AI' ? '应用详情' : ''}> <MyTooltip label={item.obj === 'AI' ? '应用详情' : ''}>
<MenuButton <MenuButton
as={Box} as={Box}
{...(item.obj === 'AI' {...(item.obj === 'AI'
...@@ -730,7 +730,7 @@ const Chat = () => { ...@@ -730,7 +730,7 @@ const Chat = () => {
h={['20px', '34px']} h={['20px', '34px']}
/> />
</MenuButton> </MenuButton>
</Tooltip> </MyTooltip>
{!isPc && <RenderContextMenu history={item} index={index} AiDetail />} {!isPc && <RenderContextMenu history={item} index={index} AiDetail />}
</Menu> </Menu>
{/* message */} {/* message */}
......
...@@ -22,7 +22,6 @@ import { ...@@ -22,7 +22,6 @@ import {
DrawerOverlay, DrawerOverlay,
DrawerContent, DrawerContent,
Card, Card,
Tooltip,
useOutsideClick, useOutsideClick,
useTheme, useTheme,
Input, Input,
...@@ -49,6 +48,7 @@ import SideBar from '@/components/SideBar'; ...@@ -49,6 +48,7 @@ import SideBar from '@/components/SideBar';
import Avatar from '@/components/Avatar'; import Avatar from '@/components/Avatar';
import Empty from './components/Empty'; import Empty from './components/Empty';
import { HUMAN_ICON } from '@/constants/chat'; import { HUMAN_ICON } from '@/constants/chat';
import MyTooltip from '@/components/MyTooltip';
const ShareHistory = dynamic(() => import('./components/ShareHistory'), { const ShareHistory = dynamic(() => import('./components/ShareHistory'), {
loading: () => <Loading fixed={false} />, loading: () => <Loading fixed={false} />,
...@@ -619,7 +619,7 @@ const Chat = () => { ...@@ -619,7 +619,7 @@ const Chat = () => {
{item.obj === 'Human' && <Box flex={1} />} {item.obj === 'Human' && <Box flex={1} />}
{/* avatar */} {/* avatar */}
<Menu autoSelect={false} isLazy> <Menu autoSelect={false} isLazy>
<Tooltip label={item.obj === 'AI' ? '应用详情' : ''}> <MyTooltip label={item.obj === 'AI' ? '应用详情' : ''}>
<MenuButton <MenuButton
as={Box} as={Box}
{...(item.obj === 'AI' {...(item.obj === 'AI'
...@@ -642,7 +642,7 @@ const Chat = () => { ...@@ -642,7 +642,7 @@ const Chat = () => {
h={['20px', '34px']} h={['20px', '34px']}
/> />
</MenuButton> </MenuButton>
</Tooltip> </MyTooltip>
{!isPc && <RenderContextMenu history={item} index={index} />} {!isPc && <RenderContextMenu history={item} index={index} />}
</Menu> </Menu>
{/* message */} {/* message */}
......
...@@ -7,7 +7,7 @@ import React, { ...@@ -7,7 +7,7 @@ import React, {
ForwardedRef ForwardedRef
} from 'react'; } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { Box, Flex, Button, FormControl, IconButton, Tooltip, Input, Card } from '@chakra-ui/react'; import { Box, Flex, Button, FormControl, IconButton, Input, Card } from '@chakra-ui/react';
import { QuestionOutlineIcon, DeleteIcon } from '@chakra-ui/icons'; import { QuestionOutlineIcon, DeleteIcon } from '@chakra-ui/icons';
import { delKbById, putKbById } from '@/api/plugins/kb'; import { delKbById, putKbById } from '@/api/plugins/kb';
import { useSelectFile } from '@/hooks/useSelectFile'; import { useSelectFile } from '@/hooks/useSelectFile';
...@@ -19,6 +19,7 @@ import { compressImg } from '@/utils/file'; ...@@ -19,6 +19,7 @@ import { compressImg } from '@/utils/file';
import type { KbItemType } from '@/types/plugin'; import type { KbItemType } from '@/types/plugin';
import Avatar from '@/components/Avatar'; import Avatar from '@/components/Avatar';
import Tag from '@/components/Tag'; import Tag from '@/components/Tag';
import MyTooltip from '@/components/MyTooltip';
export interface ComponentRef { export interface ComponentRef {
initInput: (tags: string) => void; initInput: (tags: string) => void;
...@@ -173,9 +174,9 @@ const Info = ( ...@@ -173,9 +174,9 @@ const Info = (
<Flex mt={8} alignItems={'center'} w={'100%'} maxW={'350px'} flexWrap={'wrap'}> <Flex mt={8} alignItems={'center'} w={'100%'} maxW={'350px'} flexWrap={'wrap'}>
<Box flex={'0 0 90px'} w={0}> <Box flex={'0 0 90px'} w={0}>
分类标签 分类标签
<Tooltip label={'用空格隔开多个标签,便于搜索'}> <MyTooltip label={'用空格隔开多个标签,便于搜索'}>
<QuestionOutlineIcon ml={1} /> <QuestionOutlineIcon ml={1} />
</Tooltip> </MyTooltip>
</Box> </Box>
<Input <Input
flex={1} flex={1}
......
import React, { useCallback, useState, useMemo } from 'react'; import React, { useCallback, useState, useMemo } from 'react';
import { Box, Flex, useTheme, Input, IconButton, Tooltip } from '@chakra-ui/react'; import { Box, Flex, useTheme, Input, IconButton } from '@chakra-ui/react';
import { AddIcon } from '@chakra-ui/icons'; import { AddIcon } from '@chakra-ui/icons';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { postCreateKb } from '@/api/plugins/kb'; import { postCreateKb } from '@/api/plugins/kb';
...@@ -10,6 +10,7 @@ import { useUserStore } from '@/store/user'; ...@@ -10,6 +10,7 @@ import { useUserStore } from '@/store/user';
import MyIcon from '@/components/Icon'; import MyIcon from '@/components/Icon';
import Avatar from '@/components/Avatar'; import Avatar from '@/components/Avatar';
import Tag from '@/components/Tag'; import Tag from '@/components/Tag';
import MyTooltip from '@/components/MyTooltip';
const KbList = ({ kbId }: { kbId: string }) => { const KbList = ({ kbId }: { kbId: string }) => {
const theme = useTheme(); const theme = useTheme();
...@@ -78,7 +79,7 @@ const KbList = ({ kbId }: { kbId: string }) => { ...@@ -78,7 +79,7 @@ const KbList = ({ kbId }: { kbId: string }) => {
/> />
)} )}
</Flex> </Flex>
<Tooltip label={'新建一个知识库'}> <MyTooltip label={'新建一个知识库'}>
<IconButton <IconButton
h={'32px'} h={'32px'}
icon={<AddIcon />} icon={<AddIcon />}
...@@ -86,7 +87,7 @@ const KbList = ({ kbId }: { kbId: string }) => { ...@@ -86,7 +87,7 @@ const KbList = ({ kbId }: { kbId: string }) => {
variant={'base'} variant={'base'}
onClick={handleCreateModel} onClick={handleCreateModel}
/> />
</Tooltip> </MyTooltip>
</Flex> </Flex>
<Box flex={'1 0 0'} h={0} pl={[0, 2]} overflowY={'scroll'} userSelect={'none'}> <Box flex={'1 0 0'} h={0} pl={[0, 2]} overflowY={'scroll'} userSelect={'none'}>
{kbs.map((item) => ( {kbs.map((item) => (
......
...@@ -40,7 +40,7 @@ export type ShareChatEditType = { ...@@ -40,7 +40,7 @@ export type ShareChatEditType = {
/* agent */ /* agent */
/* question classify */ /* question classify */
export type ClassifyQuestionAgentItemType = { export type ClassifyQuestionAgentItemType = {
desc: string; value: string;
key: string; key: string;
}; };
......
...@@ -37,6 +37,7 @@ export type FlowOutputItemType = { ...@@ -37,6 +37,7 @@ export type FlowOutputItemType = {
export type FlowModuleItemChangeProps = { export type FlowModuleItemChangeProps = {
moduleId: string; moduleId: string;
type?: 'inputs' | 'outputs';
key: string; key: string;
value: any; value: any;
valueKey?: keyof FlowInputItemType & keyof FlowBodyItemType; valueKey?: keyof FlowInputItemType & keyof FlowBodyItemType;
......
...@@ -109,8 +109,6 @@ export const appModule2FlowEdge = ({ ...@@ -109,8 +109,6 @@ export const appModule2FlowEdge = ({
modules: AppModuleItemType[]; modules: AppModuleItemType[];
onDelete: (id: string) => void; onDelete: (id: string) => void;
}) => { }) => {
console.log(modules);
const edges: Edge[] = []; const edges: Edge[] = [];
modules.forEach((module) => modules.forEach((module) =>
module.outputs.forEach((output) => module.outputs.forEach((output) =>
......
import { OpenAiChatEnum } from '@/constants/model';
import type { ChatModelType } from '@/constants/model';
import type { ChatItemType } from '@/types/chat';
import { countOpenAIToken, openAiSliceTextByToken } from './openai'; import { countOpenAIToken, openAiSliceTextByToken } from './openai';
import { gpt_chatItemTokenSlice } from '@/pages/api/openapi/text/gptMessagesSlice'; import { gpt_chatItemTokenSlice } from '@/pages/api/openapi/text/gptMessagesSlice';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment