mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-28 17:29:44 +00:00
4.8 test (#1394)
* fix: chat variable sync * feat: chat save variable config * fix: target handle hidden * adapt v1 chat init * adapt v1 chat init * adapt v1 chat init * adapt v1 chat init
This commit is contained in:
@@ -12,7 +12,7 @@ import { ChatSiteItemType } from '@fastgpt/global/core/chat/type';
|
||||
|
||||
type useChatStoreType = OutLinkChatAuthProps & {
|
||||
welcomeText: string;
|
||||
variableModules: VariableItemType[];
|
||||
variableNodes: VariableItemType[];
|
||||
questionGuide: boolean;
|
||||
ttsConfig: AppTTSConfigType;
|
||||
whisperConfig: AppWhisperConfigType;
|
||||
@@ -41,7 +41,7 @@ type useChatStoreType = OutLinkChatAuthProps & {
|
||||
};
|
||||
const StateContext = createContext<useChatStoreType>({
|
||||
welcomeText: '',
|
||||
variableModules: [],
|
||||
variableNodes: [],
|
||||
questionGuide: false,
|
||||
ttsConfig: {
|
||||
type: 'none',
|
||||
@@ -110,7 +110,7 @@ const Provider = ({
|
||||
}: ChatProviderProps) => {
|
||||
const [chatHistories, setChatHistories] = useState<ChatSiteItemType[]>([]);
|
||||
|
||||
const { welcomeText, variableModules, questionGuide, ttsConfig, whisperConfig } = useMemo(
|
||||
const { welcomeText, variableNodes, questionGuide, ttsConfig, whisperConfig } = useMemo(
|
||||
() => splitGuideModule(userGuideModule),
|
||||
[userGuideModule]
|
||||
);
|
||||
@@ -150,7 +150,7 @@ const Provider = ({
|
||||
teamId,
|
||||
teamToken,
|
||||
welcomeText,
|
||||
variableModules,
|
||||
variableNodes,
|
||||
questionGuide,
|
||||
ttsConfig,
|
||||
whisperConfig,
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { VariableItemType } from '@fastgpt/global/core/app/type.d';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React from 'react';
|
||||
import { UseFormReturn } from 'react-hook-form';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { Box, Button, Card, Input, Textarea } from '@chakra-ui/react';
|
||||
@@ -12,34 +12,19 @@ import { ChatBoxInputFormType } from '../type.d';
|
||||
|
||||
const VariableInput = ({
|
||||
appAvatar,
|
||||
variableModules,
|
||||
variableIsFinish,
|
||||
variableNodes,
|
||||
chatForm,
|
||||
onSubmitVariables
|
||||
}: {
|
||||
appAvatar?: string;
|
||||
variableModules: VariableItemType[];
|
||||
variableIsFinish: boolean;
|
||||
variableNodes: VariableItemType[];
|
||||
onSubmitVariables: (e: Record<string, any>) => void;
|
||||
chatForm: UseFormReturn<ChatBoxInputFormType>;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { register, unregister, setValue, handleSubmit: handleSubmitChat, watch } = chatForm;
|
||||
const { register, setValue, handleSubmit: handleSubmitChat, watch } = chatForm;
|
||||
const variables = watch('variables');
|
||||
|
||||
useEffect(() => {
|
||||
// 重新注册所有字段
|
||||
variableModules.forEach((item) => {
|
||||
register(`variables.${item.key}`, { required: item.required });
|
||||
});
|
||||
|
||||
return () => {
|
||||
// 组件卸载时注销所有字段
|
||||
variableModules.forEach((item) => {
|
||||
unregister(`variables.${item.key}`);
|
||||
});
|
||||
};
|
||||
}, [register, unregister, variableModules]);
|
||||
const chatStarted = watch('chatStarted');
|
||||
|
||||
return (
|
||||
<Box py={3}>
|
||||
@@ -55,7 +40,7 @@ const VariableInput = ({
|
||||
bg={'white'}
|
||||
boxShadow={'0 0 8px rgba(0,0,0,0.15)'}
|
||||
>
|
||||
{variableModules.map((item) => (
|
||||
{variableNodes.map((item) => (
|
||||
<Box key={item.id} mb={4}>
|
||||
<Box as={'label'} display={'inline-block'} position={'relative'} mb={1}>
|
||||
{item.label}
|
||||
@@ -73,7 +58,6 @@ const VariableInput = ({
|
||||
</Box>
|
||||
{item.type === VariableInputEnum.input && (
|
||||
<Input
|
||||
isDisabled={variableIsFinish}
|
||||
bg={'myWhite.400'}
|
||||
{...register(`variables.${item.key}`, {
|
||||
required: item.required
|
||||
@@ -82,7 +66,6 @@ const VariableInput = ({
|
||||
)}
|
||||
{item.type === VariableInputEnum.textarea && (
|
||||
<Textarea
|
||||
isDisabled={variableIsFinish}
|
||||
bg={'myWhite.400'}
|
||||
{...register(`variables.${item.key}`, {
|
||||
required: item.required
|
||||
@@ -94,7 +77,6 @@ const VariableInput = ({
|
||||
{item.type === VariableInputEnum.select && (
|
||||
<MySelect
|
||||
width={'100%'}
|
||||
isDisabled={variableIsFinish}
|
||||
list={(item.enums || []).map((item) => ({
|
||||
label: item.value,
|
||||
value: item.value
|
||||
@@ -110,7 +92,7 @@ const VariableInput = ({
|
||||
)}
|
||||
</Box>
|
||||
))}
|
||||
{!variableIsFinish && (
|
||||
{!chatStarted && (
|
||||
<Button
|
||||
leftIcon={<MyIcon name={'core/chat/chatFill'} w={'16px'} />}
|
||||
size={'sm'}
|
||||
|
@@ -58,6 +58,8 @@ import ChatProvider, { useChatProviderStore } from './Provider';
|
||||
import ChatItem from './components/ChatItem';
|
||||
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useCreation, useUpdateEffect } from 'ahooks';
|
||||
|
||||
const ResponseTags = dynamic(() => import('./ResponseTags'));
|
||||
const FeedbackModal = dynamic(() => import('./FeedbackModal'));
|
||||
const ReadFeedbackModal = dynamic(() => import('./ReadFeedbackModal'));
|
||||
@@ -147,7 +149,7 @@ const ChatBox = (
|
||||
|
||||
const {
|
||||
welcomeText,
|
||||
variableModules,
|
||||
variableNodes,
|
||||
questionGuide,
|
||||
startSegmentedAudio,
|
||||
finishSegmentedAudio,
|
||||
@@ -171,24 +173,10 @@ const ChatBox = (
|
||||
const chatStarted = watch('chatStarted');
|
||||
|
||||
/* variable */
|
||||
const variables = watch('variables');
|
||||
const filterVariableModules = useMemo(
|
||||
() => variableModules.filter((item) => item.type !== VariableInputEnum.custom),
|
||||
[variableModules]
|
||||
const filterVariableNodes = useCreation(
|
||||
() => variableNodes.filter((item) => item.type !== VariableInputEnum.custom),
|
||||
[variableNodes]
|
||||
);
|
||||
const variableIsFinish = (() => {
|
||||
if (!filterVariableModules || filterVariableModules.length === 0 || chatHistories.length > 0)
|
||||
return true;
|
||||
|
||||
for (let i = 0; i < filterVariableModules.length; i++) {
|
||||
const item = filterVariableModules[i];
|
||||
if (item.required && !variables[item.key]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return chatStarted;
|
||||
})();
|
||||
|
||||
// 滚动到底部
|
||||
const scrollToBottom = (behavior: 'smooth' | 'auto' = 'smooth') => {
|
||||
@@ -379,174 +367,185 @@ const ChatBox = (
|
||||
autoTTSResponse?: boolean;
|
||||
history?: ChatSiteItemType[];
|
||||
}) => {
|
||||
handleSubmit(async ({ variables }) => {
|
||||
if (!onStartChat) return;
|
||||
if (isChatting) {
|
||||
toast({
|
||||
title: '正在聊天中...请等待结束',
|
||||
status: 'warning'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
abortRequest();
|
||||
|
||||
text = text.trim();
|
||||
|
||||
if (!text && files.length === 0) {
|
||||
toast({
|
||||
title: '内容为空',
|
||||
status: 'warning'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const responseChatId = getNanoid(24);
|
||||
questionGuideController.current?.abort('stop');
|
||||
|
||||
// set auto audio playing
|
||||
if (autoTTSResponse) {
|
||||
await startSegmentedAudio();
|
||||
setAudioPlayingChatId(responseChatId);
|
||||
}
|
||||
|
||||
const newChatList: ChatSiteItemType[] = [
|
||||
...history,
|
||||
{
|
||||
dataId: getNanoid(24),
|
||||
obj: ChatRoleEnum.Human,
|
||||
value: [
|
||||
...files.map((file) => ({
|
||||
type: ChatItemValueTypeEnum.file,
|
||||
file: {
|
||||
type: file.type,
|
||||
name: file.name,
|
||||
url: file.url || ''
|
||||
}
|
||||
})),
|
||||
...(text
|
||||
? [
|
||||
{
|
||||
type: ChatItemValueTypeEnum.text,
|
||||
text: {
|
||||
content: text
|
||||
}
|
||||
}
|
||||
]
|
||||
: [])
|
||||
] as UserChatItemValueItemType[],
|
||||
status: 'finish'
|
||||
},
|
||||
{
|
||||
dataId: responseChatId,
|
||||
obj: ChatRoleEnum.AI,
|
||||
value: [
|
||||
{
|
||||
type: ChatItemValueTypeEnum.text,
|
||||
text: {
|
||||
content: ''
|
||||
}
|
||||
}
|
||||
],
|
||||
status: 'loading'
|
||||
}
|
||||
];
|
||||
|
||||
// 插入内容
|
||||
setChatHistories(newChatList);
|
||||
|
||||
// 清空输入内容
|
||||
resetInputVal({});
|
||||
setQuestionGuide([]);
|
||||
setTimeout(() => {
|
||||
scrollToBottom();
|
||||
}, 100);
|
||||
try {
|
||||
// create abort obj
|
||||
const abortSignal = new AbortController();
|
||||
chatController.current = abortSignal;
|
||||
|
||||
const messages = chats2GPTMessages({ messages: newChatList, reserveId: true });
|
||||
|
||||
const {
|
||||
responseData,
|
||||
responseText,
|
||||
newVariables,
|
||||
isNewChat = false
|
||||
} = await onStartChat({
|
||||
chatList: newChatList,
|
||||
messages,
|
||||
controller: abortSignal,
|
||||
generatingMessage: (e) => generatingMessage({ ...e, autoTTSResponse }),
|
||||
variables
|
||||
});
|
||||
|
||||
newVariables && setValue('variables', newVariables);
|
||||
|
||||
isNewChatReplace.current = isNewChat;
|
||||
|
||||
// set finish status
|
||||
setChatHistories((state) =>
|
||||
state.map((item, index) => {
|
||||
if (index !== state.length - 1) return item;
|
||||
return {
|
||||
...item,
|
||||
status: 'finish',
|
||||
responseData
|
||||
};
|
||||
})
|
||||
);
|
||||
setTimeout(() => {
|
||||
createQuestionGuide({
|
||||
history: newChatList.map((item, i) =>
|
||||
i === newChatList.length - 1
|
||||
? {
|
||||
...item,
|
||||
value: [
|
||||
{
|
||||
type: ChatItemValueTypeEnum.text,
|
||||
text: {
|
||||
content: responseText
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
: item
|
||||
)
|
||||
handleSubmit(
|
||||
async ({ variables }) => {
|
||||
if (!onStartChat) return;
|
||||
if (isChatting) {
|
||||
toast({
|
||||
title: '正在聊天中...请等待结束',
|
||||
status: 'warning'
|
||||
});
|
||||
generatingScroll();
|
||||
isPc && TextareaDom.current?.focus();
|
||||
}, 100);
|
||||
|
||||
// tts audio
|
||||
autoTTSResponse && splitText2Audio(responseText, true);
|
||||
} catch (err: any) {
|
||||
toast({
|
||||
title: t(getErrText(err, 'core.chat.error.Chat error')),
|
||||
status: 'error',
|
||||
duration: 5000,
|
||||
isClosable: true
|
||||
});
|
||||
|
||||
if (!err?.responseText) {
|
||||
resetInputVal({ text, files });
|
||||
setChatHistories(newChatList.slice(0, newChatList.length - 2));
|
||||
return;
|
||||
}
|
||||
|
||||
// set finish status
|
||||
setChatHistories((state) =>
|
||||
state.map((item, index) => {
|
||||
if (index !== state.length - 1) return item;
|
||||
return {
|
||||
...item,
|
||||
status: 'finish'
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
abortRequest();
|
||||
|
||||
autoTTSResponse && finishSegmentedAudio();
|
||||
})();
|
||||
text = text.trim();
|
||||
|
||||
if (!text && files.length === 0) {
|
||||
toast({
|
||||
title: '内容为空',
|
||||
status: 'warning'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// delete invalid variables, 只保留在 variableNodes 中的变量
|
||||
const requestVariables: Record<string, any> = {};
|
||||
variableNodes?.forEach((item) => {
|
||||
requestVariables[item.key] = variables[item.key] || '';
|
||||
});
|
||||
|
||||
const responseChatId = getNanoid(24);
|
||||
questionGuideController.current?.abort('stop');
|
||||
|
||||
// set auto audio playing
|
||||
if (autoTTSResponse) {
|
||||
await startSegmentedAudio();
|
||||
setAudioPlayingChatId(responseChatId);
|
||||
}
|
||||
|
||||
const newChatList: ChatSiteItemType[] = [
|
||||
...history,
|
||||
{
|
||||
dataId: getNanoid(24),
|
||||
obj: ChatRoleEnum.Human,
|
||||
value: [
|
||||
...files.map((file) => ({
|
||||
type: ChatItemValueTypeEnum.file,
|
||||
file: {
|
||||
type: file.type,
|
||||
name: file.name,
|
||||
url: file.url || ''
|
||||
}
|
||||
})),
|
||||
...(text
|
||||
? [
|
||||
{
|
||||
type: ChatItemValueTypeEnum.text,
|
||||
text: {
|
||||
content: text
|
||||
}
|
||||
}
|
||||
]
|
||||
: [])
|
||||
] as UserChatItemValueItemType[],
|
||||
status: 'finish'
|
||||
},
|
||||
{
|
||||
dataId: responseChatId,
|
||||
obj: ChatRoleEnum.AI,
|
||||
value: [
|
||||
{
|
||||
type: ChatItemValueTypeEnum.text,
|
||||
text: {
|
||||
content: ''
|
||||
}
|
||||
}
|
||||
],
|
||||
status: 'loading'
|
||||
}
|
||||
];
|
||||
|
||||
// 插入内容
|
||||
setChatHistories(newChatList);
|
||||
|
||||
// 清空输入内容
|
||||
resetInputVal({});
|
||||
setQuestionGuide([]);
|
||||
setTimeout(() => {
|
||||
scrollToBottom();
|
||||
}, 100);
|
||||
try {
|
||||
// create abort obj
|
||||
const abortSignal = new AbortController();
|
||||
chatController.current = abortSignal;
|
||||
|
||||
const messages = chats2GPTMessages({ messages: newChatList, reserveId: true });
|
||||
|
||||
const {
|
||||
responseData,
|
||||
responseText,
|
||||
newVariables,
|
||||
isNewChat = false
|
||||
} = await onStartChat({
|
||||
chatList: newChatList,
|
||||
messages,
|
||||
controller: abortSignal,
|
||||
generatingMessage: (e) => generatingMessage({ ...e, autoTTSResponse }),
|
||||
variables: requestVariables
|
||||
});
|
||||
|
||||
newVariables && setValue('variables', newVariables);
|
||||
|
||||
isNewChatReplace.current = isNewChat;
|
||||
|
||||
// set finish status
|
||||
setChatHistories((state) =>
|
||||
state.map((item, index) => {
|
||||
if (index !== state.length - 1) return item;
|
||||
return {
|
||||
...item,
|
||||
status: 'finish',
|
||||
responseData
|
||||
};
|
||||
})
|
||||
);
|
||||
setTimeout(() => {
|
||||
createQuestionGuide({
|
||||
history: newChatList.map((item, i) =>
|
||||
i === newChatList.length - 1
|
||||
? {
|
||||
...item,
|
||||
value: [
|
||||
{
|
||||
type: ChatItemValueTypeEnum.text,
|
||||
text: {
|
||||
content: responseText
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
: item
|
||||
)
|
||||
});
|
||||
generatingScroll();
|
||||
isPc && TextareaDom.current?.focus();
|
||||
}, 100);
|
||||
|
||||
// tts audio
|
||||
autoTTSResponse && splitText2Audio(responseText, true);
|
||||
} catch (err: any) {
|
||||
toast({
|
||||
title: t(getErrText(err, 'core.chat.error.Chat error')),
|
||||
status: 'error',
|
||||
duration: 5000,
|
||||
isClosable: true
|
||||
});
|
||||
|
||||
if (!err?.responseText) {
|
||||
resetInputVal({ text, files });
|
||||
setChatHistories(newChatList.slice(0, newChatList.length - 2));
|
||||
}
|
||||
|
||||
// set finish status
|
||||
setChatHistories((state) =>
|
||||
state.map((item, index) => {
|
||||
if (index !== state.length - 1) return item;
|
||||
return {
|
||||
...item,
|
||||
status: 'finish'
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
autoTTSResponse && finishSegmentedAudio();
|
||||
},
|
||||
(err) => {
|
||||
console.log(err?.variables);
|
||||
}
|
||||
)();
|
||||
},
|
||||
[
|
||||
abortRequest,
|
||||
@@ -566,7 +565,8 @@ const ChatBox = (
|
||||
splitText2Audio,
|
||||
startSegmentedAudio,
|
||||
t,
|
||||
toast
|
||||
toast,
|
||||
variableNodes
|
||||
]
|
||||
);
|
||||
|
||||
@@ -630,7 +630,7 @@ const ChatBox = (
|
||||
});
|
||||
};
|
||||
},
|
||||
[onDelMessage]
|
||||
[onDelMessage, setChatHistories]
|
||||
);
|
||||
// admin mark
|
||||
const onMark = useCallback(
|
||||
@@ -796,7 +796,19 @@ const ChatBox = (
|
||||
}
|
||||
};
|
||||
},
|
||||
[appId, chatId]
|
||||
[appId, chatId, setChatHistories]
|
||||
);
|
||||
|
||||
const resetVariables = useCallback(
|
||||
(e: Record<string, any> = {}) => {
|
||||
const value: Record<string, any> = { ...e };
|
||||
filterVariableNodes?.forEach((item) => {
|
||||
value[item.key] = e[item.key] || '';
|
||||
});
|
||||
|
||||
setValue('variables', value);
|
||||
},
|
||||
[filterVariableNodes, setValue]
|
||||
);
|
||||
|
||||
const showEmpty = useMemo(
|
||||
@@ -804,13 +816,13 @@ const ChatBox = (
|
||||
feConfigs?.show_emptyChat &&
|
||||
showEmptyIntro &&
|
||||
chatHistories.length === 0 &&
|
||||
!filterVariableModules?.length &&
|
||||
!filterVariableNodes?.length &&
|
||||
!welcomeText,
|
||||
[
|
||||
chatHistories.length,
|
||||
feConfigs?.show_emptyChat,
|
||||
showEmptyIntro,
|
||||
filterVariableModules?.length,
|
||||
filterVariableNodes?.length,
|
||||
welcomeText
|
||||
]
|
||||
);
|
||||
@@ -869,14 +881,7 @@ const ChatBox = (
|
||||
// output data
|
||||
useImperativeHandle(ref, () => ({
|
||||
getChatHistories: () => chatHistories,
|
||||
resetVariables(e) {
|
||||
const defaultVal: Record<string, any> = {};
|
||||
filterVariableModules?.forEach((item) => {
|
||||
defaultVal[item.key] = '';
|
||||
});
|
||||
|
||||
setValue('variables', e || defaultVal);
|
||||
},
|
||||
resetVariables,
|
||||
resetHistory(e) {
|
||||
abortRequest();
|
||||
setValue('chatStarted', e.length > 0);
|
||||
@@ -891,7 +896,7 @@ const ChatBox = (
|
||||
}));
|
||||
|
||||
return (
|
||||
<Flex flexDirection={'column'} h={'100%'}>
|
||||
<Flex flexDirection={'column'} h={'100%'} position={'relative'}>
|
||||
<Script src="/js/html2pdf.bundle.min.js" strategy="lazyOnload"></Script>
|
||||
{/* chat box container */}
|
||||
<Box ref={ChatBoxRef} flex={'1 0 0'} h={0} w={'100%'} overflow={'overlay'} px={[4, 0]} pb={3}>
|
||||
@@ -899,11 +904,10 @@ const ChatBox = (
|
||||
{showEmpty && <Empty />}
|
||||
{!!welcomeText && <WelcomeBox appAvatar={appAvatar} welcomeText={welcomeText} />}
|
||||
{/* variable input */}
|
||||
{!!filterVariableModules?.length && (
|
||||
{!!filterVariableNodes?.length && (
|
||||
<VariableInput
|
||||
appAvatar={appAvatar}
|
||||
variableModules={filterVariableModules}
|
||||
variableIsFinish={variableIsFinish}
|
||||
variableNodes={filterVariableNodes}
|
||||
chatForm={chatForm}
|
||||
onSubmitVariables={(data) => {
|
||||
setValue('chatStarted', true);
|
||||
@@ -995,7 +999,7 @@ const ChatBox = (
|
||||
</Box>
|
||||
</Box>
|
||||
{/* message input */}
|
||||
{onStartChat && variableIsFinish && active && (
|
||||
{onStartChat && (chatStarted || filterVariableNodes.length === 0) && active && (
|
||||
<MessageInput
|
||||
onSendMessage={sendPrompt}
|
||||
onStop={() => chatController.current?.abort('stop')}
|
||||
|
@@ -34,11 +34,13 @@ export type ChatTestComponentRef = {
|
||||
const ChatTest = (
|
||||
{
|
||||
app,
|
||||
isOpen,
|
||||
nodes = [],
|
||||
edges = [],
|
||||
onClose
|
||||
}: {
|
||||
app: AppSchema;
|
||||
isOpen: boolean;
|
||||
nodes?: StoreNodeItemType[];
|
||||
edges?: StoreEdgeItemType[];
|
||||
onClose: () => void;
|
||||
@@ -48,7 +50,6 @@ const ChatTest = (
|
||||
const { t } = useTranslation();
|
||||
const ChatBoxRef = useRef<ComponentRef>(null);
|
||||
const { userInfo } = useUserStore();
|
||||
const isOpen = useMemo(() => nodes && nodes.length > 0, [nodes]);
|
||||
|
||||
const startChat = useCallback(
|
||||
async ({ chatList, controller, generatingMessage, variables }: StartChatFnProps) => {
|
||||
|
@@ -28,7 +28,7 @@ export const ToolTargetHandle = ({ nodeId }: ToolHandleProps) => {
|
||||
edges.some((edge) => edge.targetHandle === getHandleId(nodeId, 'target', 'top')));
|
||||
|
||||
const Render = useMemo(() => {
|
||||
return (
|
||||
return hidden ? null : (
|
||||
<MyTooltip label={t('core.workflow.tool.Handle')} shouldWrapChildren={false}>
|
||||
<Handle
|
||||
style={{
|
||||
@@ -49,7 +49,7 @@ export const ToolTargetHandle = ({ nodeId }: ToolHandleProps) => {
|
||||
border={'4px solid #8774EE'}
|
||||
transform={'translate(0,-30%) rotate(45deg)'}
|
||||
pointerEvents={'none'}
|
||||
visibility={hidden ? 'hidden' : 'visible'}
|
||||
visibility={'visible'}
|
||||
/>
|
||||
</Handle>
|
||||
</MyTooltip>
|
||||
|
@@ -200,14 +200,19 @@ const MyTargetHandle = React.memo(function MyTargetHandle({
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (connectingEdge?.handleId && !connectingEdge.handleId?.includes('source')) return false;
|
||||
|
||||
// Same source node
|
||||
if (connectedEdges.some((item) => item.target === nodeId && item.targetHandle !== handleId))
|
||||
// From same source node
|
||||
if (
|
||||
connectedEdges.some(
|
||||
(item) => item.source === connectingEdge?.nodeId && item.target === nodeId
|
||||
)
|
||||
)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}, [connectedEdges, connectingEdge?.handleId, edges, handleId, node, nodeId]);
|
||||
}, [connectedEdges, connectingEdge?.handleId, connectingEdge?.nodeId, edges, node, nodeId]);
|
||||
|
||||
const RenderHandle = useMemo(() => {
|
||||
return (
|
||||
|
@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
import { getGuideModule } from '@fastgpt/global/core/workflow/utils';
|
||||
import { getGuideModule, replaceAppChatConfig } from '@fastgpt/global/core/workflow/utils';
|
||||
import { getChatModelNameListByModules } from '@/service/core/app/workflow';
|
||||
import type { InitChatProps, InitChatResponse } from '@/global/core/chat/api.d';
|
||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
@@ -62,7 +62,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
variables: chat?.variables || {},
|
||||
history,
|
||||
app: {
|
||||
userGuideModule: getGuideModule(nodes),
|
||||
userGuideModule: replaceAppChatConfig({
|
||||
node: getGuideModule(nodes),
|
||||
variableList: chat?.variableList,
|
||||
welcomeText: chat?.welcomeText
|
||||
}),
|
||||
chatModels: getChatModelNameListByModules(nodes),
|
||||
name: app.name,
|
||||
avatar: app.avatar,
|
||||
|
@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import type { InitChatResponse, InitOutLinkChatProps } from '@/global/core/chat/api.d';
|
||||
import { getGuideModule } from '@fastgpt/global/core/workflow/utils';
|
||||
import { getGuideModule, replaceAppChatConfig } from '@fastgpt/global/core/workflow/utils';
|
||||
import { getChatModelNameListByModules } from '@/service/core/app/workflow';
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { getChatItems } from '@fastgpt/service/core/chat/controller';
|
||||
@@ -72,7 +72,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
variables: chat?.variables || {},
|
||||
history,
|
||||
app: {
|
||||
userGuideModule: getGuideModule(nodes),
|
||||
userGuideModule: replaceAppChatConfig({
|
||||
node: getGuideModule(nodes),
|
||||
variableList: chat?.variableList,
|
||||
welcomeText: chat?.welcomeText
|
||||
}),
|
||||
chatModels: getChatModelNameListByModules(nodes),
|
||||
name: app.name,
|
||||
avatar: app.avatar,
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { getGuideModule } from '@fastgpt/global/core/workflow/utils';
|
||||
import { getGuideModule, replaceAppChatConfig } from '@fastgpt/global/core/workflow/utils';
|
||||
import { getChatModelNameListByModules } from '@/service/core/app/workflow';
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import type { InitChatResponse, InitTeamChatProps } from '@/global/core/chat/api.d';
|
||||
@@ -73,7 +73,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
variables: chat?.variables || {},
|
||||
history,
|
||||
app: {
|
||||
userGuideModule: getGuideModule(nodes),
|
||||
userGuideModule: replaceAppChatConfig({
|
||||
node: getGuideModule(nodes),
|
||||
variableList: chat?.variableList,
|
||||
welcomeText: chat?.welcomeText
|
||||
}),
|
||||
chatModels: getChatModelNameListByModules(nodes),
|
||||
name: app.name,
|
||||
avatar: app.avatar,
|
||||
|
@@ -246,6 +246,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
appId: app._id,
|
||||
teamId,
|
||||
tmbId: tmbId,
|
||||
nodes,
|
||||
variables: newVariables,
|
||||
isUpdateUseTime: isOwnerUse && source === ChatSourceEnum.online, // owner update use time
|
||||
shareId,
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Box, Flex, IconButton, useTheme, useDisclosure, Button } from '@chakra-ui/react';
|
||||
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/index.d';
|
||||
import { AppSchema } from '@fastgpt/global/core/app/type.d';
|
||||
@@ -25,7 +25,7 @@ import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import { formatTime2HM } from '@fastgpt/global/common/string/time';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext, getWorkflowStore } from '@/components/core/workflow/context';
|
||||
import { useInterval } from 'ahooks';
|
||||
import { useInterval, useUpdateEffect } from 'ahooks';
|
||||
|
||||
const ImportSettings = dynamic(() => import('@/components/core/workflow/Flow/ImportSettings'));
|
||||
const PublishHistories = dynamic(
|
||||
@@ -341,6 +341,11 @@ const Header = (props: Props) => {
|
||||
nodes: StoreNodeItemType[];
|
||||
edges: StoreEdgeItemType[];
|
||||
}>();
|
||||
const { isOpen: isOpenTest, onOpen: onOpenTest, onClose: onCloseTest } = useDisclosure();
|
||||
|
||||
useUpdateEffect(() => {
|
||||
onOpenTest();
|
||||
}, [workflowTestData]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -351,9 +356,10 @@ const Header = (props: Props) => {
|
||||
/>
|
||||
<ChatTest
|
||||
ref={ChatTestRef}
|
||||
isOpen={isOpenTest}
|
||||
{...workflowTestData}
|
||||
app={app}
|
||||
onClose={() => setWorkflowTestData(undefined)}
|
||||
onClose={onCloseTest}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@@ -99,6 +99,7 @@ const EditForm = ({
|
||||
const selectLLMModel = watch('aiSettings.model');
|
||||
const datasetSearchSetting = watch('dataset');
|
||||
const variables = watch('userGuide.variables');
|
||||
|
||||
const formatVariables = useMemo(
|
||||
() => formatEditorVariablePickerIcon([...getSystemVariables(t), ...variables]),
|
||||
[t, variables]
|
||||
|
@@ -12,6 +12,7 @@ import ChatTest from './ChatTest';
|
||||
import AppCard from './AppCard';
|
||||
import EditForm from './EditForm';
|
||||
import { AppSimpleEditFormType } from '@fastgpt/global/core/app/type';
|
||||
import { v1Workflow2V2 } from '@/web/core/workflow/adapt';
|
||||
|
||||
const SimpleEdit = ({ appId }: { appId: string }) => {
|
||||
const { isPc } = useSystemStore();
|
||||
@@ -28,6 +29,14 @@ const SimpleEdit = ({ appId }: { appId: string }) => {
|
||||
// show selected dataset
|
||||
useMount(() => {
|
||||
loadAllDatasets();
|
||||
|
||||
if (appDetail.version !== 'v2') {
|
||||
editForm.reset(
|
||||
appWorkflow2Form({
|
||||
nodes: v1Workflow2V2((appDetail.modules || []) as any)?.nodes
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
|
@@ -1,8 +1,4 @@
|
||||
import type {
|
||||
AIChatItemType,
|
||||
ChatItemType,
|
||||
UserChatItemType
|
||||
} from '@fastgpt/global/core/chat/type.d';
|
||||
import type { AIChatItemType, UserChatItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||
@@ -10,12 +6,15 @@ import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils';
|
||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type';
|
||||
import { getGuideModule, splitGuideModule } from '@fastgpt/global/core/workflow/utils';
|
||||
|
||||
type Props = {
|
||||
chatId: string;
|
||||
appId: string;
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
nodes: StoreNodeItemType[];
|
||||
variables?: Record<string, any>;
|
||||
isUpdateUseTime: boolean;
|
||||
source: `${ChatSourceEnum}`;
|
||||
@@ -30,6 +29,7 @@ export async function saveChat({
|
||||
appId,
|
||||
teamId,
|
||||
tmbId,
|
||||
nodes,
|
||||
variables,
|
||||
isUpdateUseTime,
|
||||
source,
|
||||
@@ -72,6 +72,8 @@ export async function saveChat({
|
||||
chat.variables = variables || {};
|
||||
await chat.save({ session });
|
||||
} else {
|
||||
const { welcomeText, variableNodes } = splitGuideModule(getGuideModule(nodes));
|
||||
|
||||
await MongoChat.create(
|
||||
[
|
||||
{
|
||||
@@ -79,6 +81,8 @@ export async function saveChat({
|
||||
teamId,
|
||||
tmbId,
|
||||
appId,
|
||||
variableList: variableNodes,
|
||||
welcomeText,
|
||||
variables,
|
||||
title,
|
||||
source,
|
||||
|
@@ -288,7 +288,7 @@ export const getWorkflowGlobalVariables = (
|
||||
t: TFunction
|
||||
): EditorVariablePickerType[] => {
|
||||
const globalVariables = formatEditorVariablePickerIcon(
|
||||
splitGuideModule(getGuideModule(nodes))?.variableModules || []
|
||||
splitGuideModule(getGuideModule(nodes))?.variableNodes || []
|
||||
).map((item) => ({
|
||||
...item,
|
||||
valueType: WorkflowIOValueTypeEnum.any
|
||||
|
Reference in New Issue
Block a user