mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 13:03:50 +00:00
4.8.11 test fix (#2746)
* fix: refresh tool param * perf: load image message * fix: cannot remoce externalReadUrl * perf: variables update dom * perf: empty response tip * fix: conflict
This commit is contained in:
@@ -449,7 +449,11 @@ const ChatInput = ({
|
||||
border: 'none'
|
||||
}}
|
||||
placeholder={
|
||||
isSpeaking ? t('common:core.chat.Speaking') : t('common:core.chat.Type a message')
|
||||
isSpeaking
|
||||
? t('common:core.chat.Speaking')
|
||||
: isPc
|
||||
? t('common:core.chat.Type a message')
|
||||
: t('chat:input_placeholder_phone')
|
||||
}
|
||||
resize={'none'}
|
||||
rows={1}
|
||||
|
@@ -174,7 +174,7 @@ const ChatItem = (props: Props) => {
|
||||
|
||||
// Check last group is interactive, Auto add a empty text node(animation)
|
||||
const lastGroup = groupedValues[groupedValues.length - 1];
|
||||
if (isChatting) {
|
||||
if (isChatting || groupedValues.length === 0) {
|
||||
if (
|
||||
(lastGroup &&
|
||||
lastGroup[lastGroup.length - 1] &&
|
||||
|
@@ -72,7 +72,7 @@ async function handler(
|
||||
...(websiteConfig && { websiteConfig }),
|
||||
...(status && { status }),
|
||||
...(intro !== undefined && { intro }),
|
||||
...(externalReadUrl && { externalReadUrl }),
|
||||
...(externalReadUrl !== undefined && { externalReadUrl }),
|
||||
// move
|
||||
...(updatedDefaultPermission !== undefined && {
|
||||
defaultPermission: updatedDefaultPermission
|
||||
|
@@ -266,7 +266,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
workflowStreamResponse: workflowResponseWrite
|
||||
});
|
||||
}
|
||||
return Promise.reject('请升级工作流');
|
||||
return Promise.reject('您的工作流版本过低,请重新发布一次');
|
||||
})();
|
||||
|
||||
// save chat
|
||||
|
@@ -67,7 +67,7 @@ const Header = () => {
|
||||
future,
|
||||
setPast
|
||||
} = useContextSelector(WorkflowContext, (v) => v);
|
||||
const { appType } = useSystemStore();
|
||||
const { lastAppListRouteType } = useSystemStore();
|
||||
|
||||
const [isPublished, setIsPublished] = useState(false);
|
||||
useDebounceEffect(
|
||||
@@ -138,11 +138,11 @@ const Header = () => {
|
||||
pathname: '/app/list',
|
||||
query: {
|
||||
parentId: appDetail.parentId,
|
||||
type: appType
|
||||
type: lastAppListRouteType
|
||||
}
|
||||
});
|
||||
} catch (error) {}
|
||||
}, [appDetail._id, appDetail.parentId, router]);
|
||||
}, [appDetail._id, appDetail.parentId, lastAppListRouteType, router]);
|
||||
|
||||
const Render = useMemo(() => {
|
||||
return (
|
||||
|
@@ -37,7 +37,7 @@ const Header = ({
|
||||
const router = useRouter();
|
||||
const { toast } = useToast();
|
||||
const { appId, appDetail, onSaveApp, currentTab } = useContextSelector(AppContext, (v) => v);
|
||||
const { appType } = useSystemStore();
|
||||
const { lastAppListRouteType } = useSystemStore();
|
||||
|
||||
const { data: paths = [] } = useRequest2(() => getAppFolderPath(appId), {
|
||||
manual: false,
|
||||
@@ -49,11 +49,11 @@ const Header = ({
|
||||
pathname: '/app/list',
|
||||
query: {
|
||||
parentId,
|
||||
type: appType
|
||||
type: lastAppListRouteType
|
||||
}
|
||||
});
|
||||
},
|
||||
[router, appType]
|
||||
[router, lastAppListRouteType]
|
||||
);
|
||||
|
||||
const isPublished = useMemo(() => {
|
||||
|
@@ -67,7 +67,8 @@ const Header = () => {
|
||||
future,
|
||||
setPast
|
||||
} = useContextSelector(WorkflowContext, (v) => v);
|
||||
const { appType } = useSystemStore();
|
||||
|
||||
const { lastAppListRouteType } = useSystemStore();
|
||||
|
||||
// Check if the workflow is published
|
||||
const [isPublished, setIsPublished] = useState(false);
|
||||
@@ -139,11 +140,11 @@ const Header = () => {
|
||||
pathname: '/app/list',
|
||||
query: {
|
||||
parentId: appDetail.parentId,
|
||||
type: appType
|
||||
type: lastAppListRouteType
|
||||
}
|
||||
});
|
||||
} catch (error) {}
|
||||
}, [appDetail._id, appDetail.parentId, router]);
|
||||
}, [appDetail._id, appDetail.parentId, lastAppListRouteType, router]);
|
||||
|
||||
const Render = useMemo(() => {
|
||||
return (
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import NodeCard from './render/NodeCard';
|
||||
import { NodeProps } from 'reactflow';
|
||||
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||
@@ -12,7 +12,8 @@ import {
|
||||
NumberInput,
|
||||
NumberInputField,
|
||||
NumberInputStepper,
|
||||
Switch
|
||||
Switch,
|
||||
Textarea
|
||||
} from '@chakra-ui/react';
|
||||
import { TUpdateListItem } from '@fastgpt/global/core/workflow/template/system/variableUpdate/type';
|
||||
import { NodeInputKeyEnum, WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
@@ -33,7 +34,7 @@ import { getRefData } from '@/web/core/workflow/utils';
|
||||
import { isReferenceValue } from '@fastgpt/global/core/workflow/utils';
|
||||
import { AppContext } from '@/pages/app/detail/components/context';
|
||||
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
|
||||
import { useCreation } from 'ahooks';
|
||||
import { useCreation, useMemoizedFn } from 'ahooks';
|
||||
import { getEditorVariables } from '../../utils';
|
||||
|
||||
const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
@@ -45,6 +46,19 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
|
||||
const appDetail = useContextSelector(AppContext, (v) => v.appDetail);
|
||||
const edges = useContextSelector(WorkflowContext, (v) => v.edges);
|
||||
|
||||
const menuList = useRef([
|
||||
{
|
||||
renderType: FlowNodeInputTypeEnum.input,
|
||||
icon: FlowNodeInputMap[FlowNodeInputTypeEnum.input].icon,
|
||||
label: t('common:core.workflow.inputType.Manual input')
|
||||
},
|
||||
{
|
||||
renderType: FlowNodeInputTypeEnum.reference,
|
||||
icon: FlowNodeInputMap[FlowNodeInputTypeEnum.reference].icon,
|
||||
label: t('common:core.workflow.inputType.Reference')
|
||||
}
|
||||
]);
|
||||
|
||||
const variables = useCreation(() => {
|
||||
return getEditorVariables({
|
||||
nodeId,
|
||||
@@ -80,195 +94,182 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
|
||||
[inputs, nodeId, onChangeNode]
|
||||
);
|
||||
|
||||
const Render = useMemo(() => {
|
||||
const menuList = [
|
||||
{
|
||||
renderType: FlowNodeInputTypeEnum.input,
|
||||
icon: FlowNodeInputMap[FlowNodeInputTypeEnum.input].icon,
|
||||
label: t('common:core.workflow.inputType.Manual input')
|
||||
},
|
||||
{
|
||||
renderType: FlowNodeInputTypeEnum.reference,
|
||||
icon: FlowNodeInputMap[FlowNodeInputTypeEnum.reference].icon,
|
||||
label: t('common:core.workflow.inputType.Reference')
|
||||
}
|
||||
];
|
||||
const ValueRender = useMemoizedFn(
|
||||
({ updateItem, index }: { updateItem: TUpdateListItem; index: number }) => {
|
||||
const { valueType } = getRefData({
|
||||
variable: updateItem.variable,
|
||||
nodeList,
|
||||
chatConfig: appDetail.chatConfig
|
||||
});
|
||||
const renderTypeData = menuList.current.find(
|
||||
(item) => item.renderType === updateItem.renderType
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{updateList.map((updateItem, index) => {
|
||||
const { valueType } = getRefData({
|
||||
variable: updateItem.variable,
|
||||
nodeList,
|
||||
chatConfig: appDetail.chatConfig
|
||||
});
|
||||
const renderTypeData = menuList.find((item) => item.renderType === updateItem.renderType);
|
||||
const handleUpdate = (newValue: ReferenceValueProps | string) => {
|
||||
if (isReferenceValue(newValue)) {
|
||||
onUpdateList(
|
||||
updateList.map((update, i) =>
|
||||
i === index ? { ...update, value: newValue as ReferenceValueProps } : update
|
||||
)
|
||||
);
|
||||
} else {
|
||||
onUpdateList(
|
||||
updateList.map((update, i) =>
|
||||
i === index ? { ...update, value: ['', newValue as string] } : update
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
const handleUpdate = (newValue: ReferenceValueProps | string) => {
|
||||
if (isReferenceValue(newValue)) {
|
||||
onUpdateList(
|
||||
updateList.map((update, i) =>
|
||||
i === index ? { ...update, value: newValue as ReferenceValueProps } : update
|
||||
)
|
||||
);
|
||||
} else {
|
||||
onUpdateList(
|
||||
updateList.map((update, i) =>
|
||||
i === index ? { ...update, value: ['', newValue as string] } : update
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Container key={index} mt={4} w={'full'} mx={0}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Flex w={'60px'}>{t('common:core.workflow.variable')}</Flex>
|
||||
<Reference
|
||||
nodeId={nodeId}
|
||||
variable={updateItem.variable}
|
||||
onSelect={(value) => {
|
||||
return (
|
||||
<Container key={index} mt={4} w={'full'} mx={0}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Flex w={'60px'}>{t('common:core.workflow.variable')}</Flex>
|
||||
<Reference
|
||||
nodeId={nodeId}
|
||||
variable={updateItem.variable}
|
||||
onSelect={(value) => {
|
||||
onUpdateList(
|
||||
updateList.map((update, i) => {
|
||||
if (i === index) {
|
||||
return {
|
||||
...update,
|
||||
value: ['', ''],
|
||||
valueType,
|
||||
variable: value
|
||||
};
|
||||
}
|
||||
return update;
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Box flex={1} />
|
||||
{updateList.length > 1 && (
|
||||
<MyIcon
|
||||
className="delete"
|
||||
name={'delete'}
|
||||
w={'14px'}
|
||||
color={'myGray.600'}
|
||||
cursor={'pointer'}
|
||||
_hover={{ color: 'red.500' }}
|
||||
position={'absolute'}
|
||||
top={3}
|
||||
right={3}
|
||||
onClick={() => {
|
||||
onUpdateList(updateList.filter((_, i) => i !== index));
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
<Flex mt={2} w={'full'} alignItems={'center'} className="nodrag">
|
||||
<Flex w={'60px'}>
|
||||
<Box>{t('common:core.workflow.value')}</Box>
|
||||
<MyTooltip
|
||||
label={
|
||||
menuList.current.find((item) => item.renderType === updateItem.renderType)?.label
|
||||
}
|
||||
>
|
||||
<Button
|
||||
size={'xs'}
|
||||
bg={'white'}
|
||||
borderRadius={'xs'}
|
||||
mx={2}
|
||||
color={'primary.600'}
|
||||
onClick={() => {
|
||||
onUpdateList(
|
||||
updateList.map((update, i) => {
|
||||
if (i === index) {
|
||||
return {
|
||||
...update,
|
||||
value: ['', ''],
|
||||
valueType,
|
||||
variable: value
|
||||
renderType:
|
||||
updateItem.renderType === FlowNodeInputTypeEnum.input
|
||||
? FlowNodeInputTypeEnum.reference
|
||||
: FlowNodeInputTypeEnum.input
|
||||
};
|
||||
}
|
||||
return update;
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Box flex={1} />
|
||||
{updateList.length > 1 && (
|
||||
<MyIcon
|
||||
className="delete"
|
||||
name={'delete'}
|
||||
w={'14px'}
|
||||
color={'myGray.600'}
|
||||
cursor={'pointer'}
|
||||
_hover={{ color: 'red.500' }}
|
||||
position={'absolute'}
|
||||
top={3}
|
||||
right={3}
|
||||
onClick={() => {
|
||||
onUpdateList(updateList.filter((_, i) => i !== index));
|
||||
}}
|
||||
>
|
||||
<MyIcon name={renderTypeData?.icon as any} w={'14px'} />
|
||||
</Button>
|
||||
</MyTooltip>
|
||||
</Flex>
|
||||
|
||||
{/* Render input components */}
|
||||
{(() => {
|
||||
if (updateItem.renderType === FlowNodeInputTypeEnum.reference) {
|
||||
return (
|
||||
<Reference
|
||||
nodeId={nodeId}
|
||||
variable={updateItem.value}
|
||||
valueType={valueType}
|
||||
onSelect={handleUpdate}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
<Flex mt={2} w={'full'} alignItems={'center'} className="nodrag">
|
||||
<Flex w={'60px'}>
|
||||
<Box>{t('common:core.workflow.value')}</Box>
|
||||
<MyTooltip
|
||||
label={
|
||||
menuList.find((item) => item.renderType === updateItem.renderType)?.label
|
||||
}
|
||||
>
|
||||
<Button
|
||||
size={'xs'}
|
||||
bg={'white'}
|
||||
borderRadius={'xs'}
|
||||
mx={2}
|
||||
color={'primary.600'}
|
||||
onClick={() => {
|
||||
onUpdateList(
|
||||
updateList.map((update, i) => {
|
||||
if (i === index) {
|
||||
return {
|
||||
...update,
|
||||
value: ['', ''],
|
||||
renderType:
|
||||
updateItem.renderType === FlowNodeInputTypeEnum.input
|
||||
? FlowNodeInputTypeEnum.reference
|
||||
: FlowNodeInputTypeEnum.input
|
||||
};
|
||||
}
|
||||
return update;
|
||||
})
|
||||
);
|
||||
}}
|
||||
>
|
||||
<MyIcon name={renderTypeData?.icon as any} w={'14px'} />
|
||||
</Button>
|
||||
</MyTooltip>
|
||||
</Flex>
|
||||
|
||||
{/* Render input components */}
|
||||
{(() => {
|
||||
if (updateItem.renderType === FlowNodeInputTypeEnum.reference) {
|
||||
return (
|
||||
<Reference
|
||||
nodeId={nodeId}
|
||||
variable={updateItem.value}
|
||||
valueType={valueType}
|
||||
onSelect={handleUpdate}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (valueType === WorkflowIOValueTypeEnum.string) {
|
||||
return (
|
||||
<Box w={'300px'}>
|
||||
<PromptEditor
|
||||
value={updateItem.value?.[1] || ''}
|
||||
onChange={handleUpdate}
|
||||
showOpenModal={false}
|
||||
variableLabels={variables}
|
||||
h={100}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
if (valueType === WorkflowIOValueTypeEnum.number) {
|
||||
return (
|
||||
<NumberInput value={Number(updateItem.value?.[1]) || 0}>
|
||||
<NumberInputField
|
||||
bg="white"
|
||||
onChange={(e) => handleUpdate(e.target.value)}
|
||||
/>
|
||||
<NumberInputStepper>
|
||||
<NumberIncrementStepper />
|
||||
<NumberDecrementStepper />
|
||||
</NumberInputStepper>
|
||||
</NumberInput>
|
||||
);
|
||||
}
|
||||
if (valueType === WorkflowIOValueTypeEnum.boolean) {
|
||||
return (
|
||||
<Switch
|
||||
defaultChecked={updateItem.value?.[1] === 'true'}
|
||||
onChange={(e) => handleUpdate(String(e.target.checked))}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<JsonEditor
|
||||
bg="white"
|
||||
resize
|
||||
w="300px"
|
||||
value={String(updateItem.value?.[1] || '')}
|
||||
onChange={(e) => {
|
||||
handleUpdate(e);
|
||||
}}
|
||||
);
|
||||
}
|
||||
if (valueType === WorkflowIOValueTypeEnum.string) {
|
||||
return (
|
||||
<Box w={'300px'}>
|
||||
<PromptEditor
|
||||
value={updateItem.value?.[1] || ''}
|
||||
onChange={handleUpdate}
|
||||
showOpenModal={false}
|
||||
variableLabels={variables}
|
||||
h={100}
|
||||
/>
|
||||
);
|
||||
})()}
|
||||
</Flex>
|
||||
</Container>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}, [appDetail.chatConfig, nodeId, nodeList, onUpdateList, t, updateList, variables]);
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
if (valueType === WorkflowIOValueTypeEnum.number) {
|
||||
return (
|
||||
<NumberInput value={Number(updateItem.value?.[1]) || 0}>
|
||||
<NumberInputField bg="white" onChange={(e) => handleUpdate(e.target.value)} />
|
||||
<NumberInputStepper>
|
||||
<NumberIncrementStepper />
|
||||
<NumberDecrementStepper />
|
||||
</NumberInputStepper>
|
||||
</NumberInput>
|
||||
);
|
||||
}
|
||||
if (valueType === WorkflowIOValueTypeEnum.boolean) {
|
||||
return (
|
||||
<Switch
|
||||
defaultChecked={updateItem.value?.[1] === 'true'}
|
||||
onChange={(e) => handleUpdate(String(e.target.checked))}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box w={'300px'}>
|
||||
<PromptEditor
|
||||
value={updateItem.value?.[1] || ''}
|
||||
onChange={handleUpdate}
|
||||
showOpenModal={false}
|
||||
variableLabels={variables}
|
||||
h={100}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
})()}
|
||||
</Flex>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<NodeCard selected={selected} maxW={'1000px'} {...data}>
|
||||
<Box px={4} pb={4}>
|
||||
{Render}
|
||||
<>
|
||||
{updateList.map((updateItem, index) => (
|
||||
<ValueRender key={index} updateItem={updateItem} index={index} />
|
||||
))}
|
||||
</>
|
||||
<Flex className="nodrag" cursor={'default'} alignItems={'center'} position={'relative'}>
|
||||
<Button
|
||||
variant={'whiteBase'}
|
||||
|
@@ -549,20 +549,23 @@ const WorkflowContextProvider = ({
|
||||
);
|
||||
|
||||
/* If the module is connected by a tool, the tool input and the normal input are separated */
|
||||
const splitToolInputs = useMemoizedFn((inputs: FlowNodeInputItemType[], nodeId: string) => {
|
||||
const isTool = !!edges.find(
|
||||
(edge) => edge.targetHandle === NodeOutputKeyEnum.selectedTools && edge.target === nodeId
|
||||
);
|
||||
const splitToolInputs = useCallback(
|
||||
(inputs: FlowNodeInputItemType[], nodeId: string) => {
|
||||
const isTool = !!edges.find(
|
||||
(edge) => edge.targetHandle === NodeOutputKeyEnum.selectedTools && edge.target === nodeId
|
||||
);
|
||||
|
||||
return {
|
||||
isTool,
|
||||
toolInputs: inputs.filter((item) => isTool && item.toolDescription),
|
||||
commonInputs: inputs.filter((item) => {
|
||||
if (!isTool) return true;
|
||||
return !item.toolDescription;
|
||||
})
|
||||
};
|
||||
});
|
||||
return {
|
||||
isTool,
|
||||
toolInputs: inputs.filter((item) => isTool && item.toolDescription),
|
||||
commonInputs: inputs.filter((item) => {
|
||||
if (!isTool) return true;
|
||||
return !item.toolDescription;
|
||||
})
|
||||
};
|
||||
},
|
||||
[edges]
|
||||
);
|
||||
|
||||
/* ui flow to store data */
|
||||
const flowData2StoreDataAndCheck = useMemoizedFn((hideTip = false) => {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import React, { ReactNode, useCallback, useState } from 'react';
|
||||
import React, { ReactNode, useCallback, useEffect, useState } from 'react';
|
||||
import { createContext } from 'use-context-selector';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
@@ -14,6 +14,7 @@ import { AppUpdateParams } from '@/global/core/app/api';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
const MoveModal = dynamic(() => import('@/components/common/folder/MoveModal'));
|
||||
|
||||
type AppListContextType = {
|
||||
@@ -135,6 +136,11 @@ const AppListContextProvider = ({ children }: { children: ReactNode }) => {
|
||||
);
|
||||
}, []);
|
||||
|
||||
const { setLastAppListRouteType } = useSystemStore();
|
||||
useEffect(() => {
|
||||
setLastAppListRouteType(type);
|
||||
}, [setLastAppListRouteType, type]);
|
||||
|
||||
const contextValue: AppListContextType = {
|
||||
parentId,
|
||||
appType: type,
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
|
@@ -19,8 +19,12 @@ type LoginStoreType = { provider: `${OAuthEnum}`; lastRoute: string; state: stri
|
||||
type State = {
|
||||
initd: boolean;
|
||||
setInitd: () => void;
|
||||
|
||||
lastRoute: string;
|
||||
setLastRoute: (e: string) => void;
|
||||
lastAppListRouteType?: string;
|
||||
setLastAppListRouteType: (e?: string) => void;
|
||||
|
||||
loginStore?: LoginStoreType;
|
||||
setLoginStore: (e: LoginStoreType) => void;
|
||||
loading: boolean;
|
||||
@@ -67,6 +71,12 @@ export const useSystemStore = create<State>()(
|
||||
state.lastRoute = e;
|
||||
});
|
||||
},
|
||||
lastAppListRouteType: undefined,
|
||||
setLastAppListRouteType(e) {
|
||||
set((state) => {
|
||||
state.lastAppListRouteType = e;
|
||||
});
|
||||
},
|
||||
loginStore: undefined,
|
||||
setLoginStore(e) {
|
||||
set((state) => {
|
||||
|
Reference in New Issue
Block a user