mirror of
https://github.com/labring/FastGPT.git
synced 2025-10-14 15:11:13 +00:00
fix: node copy, debug variables, auto-execution (#5664)
* fix debug variables * auto execute condition * fix autoTTSResponse * node copy * memory debug nodes * doc * yuque doc * fix: debug * img * i18n --------- Co-authored-by: archer <545436317@qq.com>
This commit is contained in:
@@ -53,7 +53,7 @@ const LabelAndFormRender = ({
|
||||
return (
|
||||
<Box _notLast={{ mb: 4 }}>
|
||||
<Flex alignItems={'center'} mb={1}>
|
||||
{typeof label === 'string' ? <FormLabel required={required}>{label}</FormLabel> : label}
|
||||
{typeof label === 'string' ? <FormLabel required={required}>{t(label)}</FormLabel> : label}
|
||||
{placeholder && <QuestionTip ml={1} label={placeholder} />}
|
||||
</Flex>
|
||||
|
||||
|
@@ -64,6 +64,7 @@ const ChatInput = ({
|
||||
const chatInputGuide = useContextSelector(ChatBoxContext, (v) => v.chatInputGuide);
|
||||
const fileSelectConfig = useContextSelector(ChatBoxContext, (v) => v.fileSelectConfig);
|
||||
const dialogTips = useContextSelector(ChatBoxContext, (v) => v.dialogTips);
|
||||
const autoTTSResponse = useContextSelector(ChatBoxContext, (v) => v.autoTTSResponse);
|
||||
|
||||
const fileCtrl = useFieldArray({
|
||||
control,
|
||||
@@ -448,7 +449,8 @@ const ChatInput = ({
|
||||
handleSend={(text) => {
|
||||
onSendMessage({
|
||||
text: text.trim(),
|
||||
files: fileList
|
||||
files: fileList,
|
||||
autoTTSResponse
|
||||
});
|
||||
replaceFiles([]);
|
||||
}}
|
||||
|
@@ -46,7 +46,6 @@ export type ChatProviderProps = {
|
||||
type useChatStoreType = ChatProviderProps & {
|
||||
welcomeText: string;
|
||||
variableList: VariableItemType[];
|
||||
allVariableList: VariableItemType[];
|
||||
questionGuide: AppQGConfigType;
|
||||
ttsConfig: AppTTSConfigType;
|
||||
whisperConfig: AppWhisperConfigType;
|
||||
@@ -238,10 +237,7 @@ const Provider = ({
|
||||
const value: useChatStoreType = {
|
||||
...props,
|
||||
welcomeText,
|
||||
variableList: variables.filter(
|
||||
(item) => item.type !== VariableInputEnum.custom && item.type !== VariableInputEnum.internal
|
||||
),
|
||||
allVariableList: variables,
|
||||
variableList: variables,
|
||||
questionGuide,
|
||||
ttsConfig,
|
||||
fileSelectConfig,
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { type UseFormReturn } from 'react-hook-form';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { Box, Button, Card, Flex } from '@chakra-ui/react';
|
||||
@@ -152,6 +152,19 @@ const VariableInput = ({
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{!chatStarted && commonVariableList.length === 0 && (
|
||||
<Button
|
||||
leftIcon={<MyIcon name={'core/chat/chatFill'} w={'16px'} />}
|
||||
size={'sm'}
|
||||
maxW={'100px'}
|
||||
mt={4}
|
||||
onClick={variablesForm.handleSubmit(() => {
|
||||
chatForm.setValue('chatStarted', true);
|
||||
})}
|
||||
>
|
||||
{t('chat:start_chat')}
|
||||
</Button>
|
||||
)}
|
||||
</Card>
|
||||
</Box>
|
||||
)}
|
||||
|
@@ -20,7 +20,12 @@ const ChatHomeVariablesForm = ({ chatForm }: Props) => {
|
||||
const variablesForm = useContextSelector(ChatItemContext, (v) => v.variablesForm);
|
||||
|
||||
const variableList = useContextSelector(ChatBoxContext, (v) => v.variableList);
|
||||
const allVariableList = useContextSelector(ChatBoxContext, (v) => v.allVariableList);
|
||||
const externalVariableList = variableList.filter(
|
||||
(item) => item.type === VariableInputEnum.custom
|
||||
);
|
||||
const commonVariableList = variableList.filter(
|
||||
(item) => item.type !== VariableInputEnum.custom && item.type !== VariableInputEnum.internal
|
||||
);
|
||||
|
||||
return (
|
||||
<Card
|
||||
@@ -31,27 +36,25 @@ const ChatHomeVariablesForm = ({ chatForm }: Props) => {
|
||||
>
|
||||
<Box p={3}>
|
||||
{/* custom variables */}
|
||||
{allVariableList.filter((i) => i.type === VariableInputEnum.custom).length > 0 && (
|
||||
{externalVariableList.length > 0 && (
|
||||
<>
|
||||
{allVariableList
|
||||
.filter((i) => i.type === VariableInputEnum.custom)
|
||||
.map((item) => (
|
||||
<LabelAndFormRender
|
||||
{...item}
|
||||
key={item.key}
|
||||
fieldName={`variables.${item.key}`}
|
||||
placeholder={item.description}
|
||||
inputType={variableInputTypeToInputType(item.type, item.valueType)}
|
||||
form={variablesForm}
|
||||
bg={'myGray.50'}
|
||||
/>
|
||||
))}
|
||||
{externalVariableList.map((item) => (
|
||||
<LabelAndFormRender
|
||||
{...item}
|
||||
key={item.key}
|
||||
fieldName={`variables.${item.key}`}
|
||||
placeholder={item.description}
|
||||
inputType={variableInputTypeToInputType(item.type, item.valueType)}
|
||||
form={variablesForm}
|
||||
bg={'myGray.50'}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
{/* normal variables */}
|
||||
{variableList.length > 0 && (
|
||||
{commonVariableList.length > 0 && (
|
||||
<>
|
||||
{variableList.map((item) => (
|
||||
{commonVariableList.map((item) => (
|
||||
<LabelAndFormRender
|
||||
{...item}
|
||||
key={item.key}
|
||||
|
@@ -144,7 +144,6 @@ const ChatBox = ({
|
||||
const outLinkAuthData = useContextSelector(ChatBoxContext, (v) => v.outLinkAuthData);
|
||||
const welcomeText = useContextSelector(ChatBoxContext, (v) => v.welcomeText);
|
||||
const variableList = useContextSelector(ChatBoxContext, (v) => v.variableList);
|
||||
const allVariableList = useContextSelector(ChatBoxContext, (v) => v.allVariableList);
|
||||
const questionGuide = useContextSelector(ChatBoxContext, (v) => v.questionGuide);
|
||||
const startSegmentedAudio = useContextSelector(ChatBoxContext, (v) => v.startSegmentedAudio);
|
||||
const finishSegmentedAudio = useContextSelector(ChatBoxContext, (v) => v.finishSegmentedAudio);
|
||||
@@ -156,11 +155,14 @@ const ChatBox = ({
|
||||
const isInteractive = useMemo(() => checkIsInteractiveByHistories(chatRecords), [chatRecords]);
|
||||
|
||||
const showExternalVariable = useMemo(() => {
|
||||
return (
|
||||
[ChatTypeEnum.log, ChatTypeEnum.test, ChatTypeEnum.chat].includes(chatType) &&
|
||||
allVariableList.some((item) => item.type === VariableInputEnum.custom)
|
||||
);
|
||||
}, [allVariableList, chatType]);
|
||||
const map: Record<string, boolean> = {
|
||||
[ChatTypeEnum.log]: true,
|
||||
[ChatTypeEnum.test]: true,
|
||||
[ChatTypeEnum.chat]: true,
|
||||
[ChatTypeEnum.home]: true
|
||||
};
|
||||
return map[chatType] && variableList.some((item) => item.type === VariableInputEnum.custom);
|
||||
}, [variableList, chatType]);
|
||||
|
||||
// compute variable input is finish.
|
||||
const chatForm = useForm<ChatBoxInputFormType>({
|
||||
@@ -173,10 +175,20 @@ const ChatBox = ({
|
||||
const { setValue, watch } = chatForm;
|
||||
const chatStartedWatch = watch('chatStarted');
|
||||
|
||||
// 可以进入对话框对话
|
||||
const commonVariableList = variableList.filter(
|
||||
(item) => item.type !== VariableInputEnum.custom && item.type !== VariableInputEnum.internal
|
||||
);
|
||||
|
||||
/*
|
||||
对话已经开始的标记:
|
||||
1. 保证 appId 一致。
|
||||
2. 有对话记录/手动点了开始/默认没有需要填写的变量。
|
||||
*/
|
||||
const chatStarted =
|
||||
chatBoxData?.appId === appId &&
|
||||
(chatRecords.length > 0 || chatStartedWatch || variableList.length === 0);
|
||||
(chatRecords.length > 0 ||
|
||||
chatStartedWatch ||
|
||||
(commonVariableList.length === 0 && !showExternalVariable));
|
||||
|
||||
// 滚动到底部
|
||||
const scrollToBottom = useMemoizedFn((behavior: 'smooth' | 'auto' = 'smooth', delay = 0) => {
|
||||
@@ -449,7 +461,7 @@ const ChatBox = ({
|
||||
|
||||
// Only declared variables are kept
|
||||
const requestVariables: Record<string, any> = {};
|
||||
allVariableList?.forEach((item) => {
|
||||
variableList?.forEach((item) => {
|
||||
const val =
|
||||
variables[item.key] === '' ||
|
||||
variables[item.key] === undefined ||
|
||||
@@ -827,7 +839,7 @@ const ChatBox = ({
|
||||
feConfigs?.show_emptyChat &&
|
||||
showEmptyIntro &&
|
||||
chatRecords.length === 0 &&
|
||||
!variableList?.length &&
|
||||
!commonVariableList?.length &&
|
||||
!showExternalVariable &&
|
||||
!welcomeText,
|
||||
[
|
||||
@@ -835,7 +847,7 @@ const ChatBox = ({
|
||||
feConfigs?.show_emptyChat,
|
||||
showEmptyIntro,
|
||||
chatRecords.length,
|
||||
variableList?.length,
|
||||
commonVariableList?.length,
|
||||
showExternalVariable,
|
||||
welcomeText
|
||||
]
|
||||
@@ -1126,8 +1138,7 @@ const ChatBox = ({
|
||||
>
|
||||
<Flex h={'100%'} flexDir={'column'} justifyContent={'center'} w={'100%'}>
|
||||
{HomeChatRenderBox}
|
||||
{allVariableList.filter((item) => item.type !== VariableInputEnum.internal).length >
|
||||
0 ? (
|
||||
{variableList.filter((item) => item.type !== VariableInputEnum.internal).length > 0 ? (
|
||||
<Box w={'100%'}>
|
||||
<ChatHomeVariablesForm chatForm={chatForm} />
|
||||
</Box>
|
||||
|
@@ -30,6 +30,7 @@ import {
|
||||
nodeInputTypeToInputType,
|
||||
variableInputTypeToInputType
|
||||
} from '@/components/core/app/formRender/utils';
|
||||
import { useSafeTranslation } from '@fastgpt/web/hooks/useSafeTranslation';
|
||||
|
||||
const MyRightDrawer = dynamic(
|
||||
() => import('@fastgpt/web/components/common/MyDrawer/MyRightDrawer')
|
||||
@@ -41,7 +42,7 @@ enum TabEnum {
|
||||
}
|
||||
|
||||
export const useDebug = () => {
|
||||
const { t } = useTranslation();
|
||||
const { t } = useSafeTranslation();
|
||||
const { toast } = useToast();
|
||||
|
||||
const setNodes = useContextSelector(WorkflowNodeEdgeContext, (v) => v.setNodes);
|
||||
@@ -241,8 +242,8 @@ export const useDebug = () => {
|
||||
<MyRightDrawer
|
||||
onClose={onClose}
|
||||
iconSrc="core/workflow/debugBlue"
|
||||
title={t('common:core.workflow.Debug Node')}
|
||||
maxW={['90vw', '35vw']}
|
||||
title={t('workflow:debug_test')}
|
||||
maxW={['90vw', '40vw']}
|
||||
px={0}
|
||||
>
|
||||
<Box flex={'1 0 0'} overflow={'auto'} px={6}>
|
||||
@@ -260,12 +261,27 @@ export const useDebug = () => {
|
||||
onChange={setCurrentTab}
|
||||
/>
|
||||
)}
|
||||
<Box display={currentTab === TabEnum.node ? 'block' : 'none'}>
|
||||
{renderInputs.map((item) => (
|
||||
<LabelAndFormRender
|
||||
key={item.key}
|
||||
label={item.label}
|
||||
required={item.required}
|
||||
placeholder={t(item.placeholder || item.description)}
|
||||
inputType={nodeInputTypeToInputType(item.renderTypeList)}
|
||||
form={variablesForm}
|
||||
fieldName={`nodeVariables.${item.key}`}
|
||||
bg={'myGray.50'}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
<Box display={currentTab === TabEnum.global ? 'block' : 'none'}>
|
||||
{customVar.map((item) => (
|
||||
<LabelAndFormRender
|
||||
{...item}
|
||||
key={item.key}
|
||||
placeholder={item.description}
|
||||
label={item.label}
|
||||
required={item.required}
|
||||
placeholder={t(item.description)}
|
||||
inputType={variableInputTypeToInputType(item.type)}
|
||||
form={variablesForm}
|
||||
fieldName={`variables.${item.key}`}
|
||||
@@ -274,9 +290,10 @@ export const useDebug = () => {
|
||||
))}
|
||||
{internalVar.map((item) => (
|
||||
<LabelAndFormRender
|
||||
{...item}
|
||||
key={item.key}
|
||||
placeholder={item.description}
|
||||
label={item.label}
|
||||
required={item.required}
|
||||
placeholder={t(item.description)}
|
||||
inputType={variableInputTypeToInputType(item.type)}
|
||||
form={variablesForm}
|
||||
fieldName={`variables.${item.key}`}
|
||||
@@ -285,8 +302,9 @@ export const useDebug = () => {
|
||||
))}
|
||||
{filteredVar.map((item) => (
|
||||
<LabelAndFormRender
|
||||
{...item}
|
||||
key={item.key}
|
||||
label={item.label}
|
||||
required={item.required}
|
||||
placeholder={item.description}
|
||||
inputType={variableInputTypeToInputType(item.type)}
|
||||
form={variablesForm}
|
||||
@@ -295,19 +313,6 @@ export const useDebug = () => {
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
<Box display={currentTab === TabEnum.node ? 'block' : 'none'}>
|
||||
{renderInputs.map((item) => (
|
||||
<LabelAndFormRender
|
||||
{...item}
|
||||
key={item.key}
|
||||
placeholder={item.placeholder || item.description}
|
||||
inputType={nodeInputTypeToInputType(item.renderTypeList)}
|
||||
form={variablesForm}
|
||||
fieldName={`nodeVariables.${item.key}`}
|
||||
bg={'myGray.50'}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
<Flex py={2} justifyContent={'flex-end'} px={6}>
|
||||
<Button onClick={handleSubmit(onClickRun, onCheckRunError)}>{t('common:Run')}</Button>
|
||||
|
@@ -514,7 +514,8 @@ const MenuRender = React.memo(function MenuRender({
|
||||
version: template.version,
|
||||
versionLabel: template.versionLabel,
|
||||
isLatestVersion: template.isLatestVersion,
|
||||
toolConfig: template.toolConfig
|
||||
toolConfig: template.toolConfig,
|
||||
catchError: template.catchError
|
||||
},
|
||||
selected: true,
|
||||
parentNodeId: undefined,
|
||||
|
@@ -707,25 +707,31 @@ const WorkflowContextProvider = ({
|
||||
|
||||
try {
|
||||
// 3. Run one step
|
||||
const { memoryEdges, entryNodeIds, skipNodeQueue, nodeResponses, newVariables } =
|
||||
await postWorkflowDebug({
|
||||
nodes: runtimeNodes,
|
||||
edges: debugData.runtimeEdges,
|
||||
skipNodeQueue: debugData.skipNodeQueue,
|
||||
variables: {
|
||||
appId,
|
||||
cTime: formatTime2YMDHMW(),
|
||||
...debugData.variables
|
||||
},
|
||||
query: debugData.query, // 添加 query 参数
|
||||
history: debugData.history,
|
||||
const {
|
||||
memoryEdges,
|
||||
memoryNodes,
|
||||
entryNodeIds,
|
||||
skipNodeQueue,
|
||||
nodeResponses,
|
||||
newVariables
|
||||
} = await postWorkflowDebug({
|
||||
nodes: runtimeNodes,
|
||||
edges: debugData.runtimeEdges,
|
||||
skipNodeQueue: debugData.skipNodeQueue,
|
||||
variables: {
|
||||
appId,
|
||||
chatConfig: appDetail.chatConfig
|
||||
});
|
||||
cTime: formatTime2YMDHMW(),
|
||||
...debugData.variables
|
||||
},
|
||||
query: debugData.query, // 添加 query 参数
|
||||
history: debugData.history,
|
||||
appId,
|
||||
chatConfig: appDetail.chatConfig
|
||||
});
|
||||
|
||||
// 4. Store debug result
|
||||
setWorkflowDebugData({
|
||||
runtimeNodes: debugData.runtimeNodes,
|
||||
runtimeNodes: memoryNodes,
|
||||
runtimeEdges: memoryEdges,
|
||||
entryNodeIds,
|
||||
skipNodeQueue,
|
||||
@@ -776,7 +782,7 @@ const WorkflowContextProvider = ({
|
||||
console.log(error);
|
||||
}
|
||||
},
|
||||
[appId, onChangeNode, setNodes]
|
||||
[appId, onChangeNode, setNodes, appDetail.chatConfig]
|
||||
);
|
||||
const onStopNodeDebug = useMemoizedFn(() => {
|
||||
setWorkflowDebugData(undefined);
|
||||
|
Reference in New Issue
Block a user