Fix workflow detail (#3382)

* fix: loop node init

* fix: workflow detail

* fix: point table

* add null check
This commit is contained in:
Archer
2024-12-12 17:14:46 +08:00
committed by GitHub
parent 181b854342
commit ddddd998c8
34 changed files with 292 additions and 237 deletions

View File

@@ -40,6 +40,18 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4815' \
会重置应用定时执行的字段,把 null 去掉,减少索引大小。 会重置应用定时执行的字段,把 null 去掉,减少索引大小。
----
从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`{{host}} 替换成**FastGPT 域名**。
```bash
curl --location --request POST 'https://{{host}}/api/admin/refreshFreeUser' \
--header 'rootkey: {{rootkey}}' \
--header 'Content-Type: application/json'
```
重新计算一次免费版用户的时长,之前有版本升级时没有重新计算时间,导致会误发通知。
## 完整更新内容 ## 完整更新内容

View File

@@ -12,4 +12,9 @@ weight: 808
1. 1.
2. 新增 - 商业版支持 API 知识库和链接集合定时同步。 2. 新增 - 商业版支持 API 知识库和链接集合定时同步。
3. 修复 - 站点同步知识库,链接同步时未使用选择器 3. 优化 - 工作流/简易模式变量初始化代码,去除监听初始化,避免因渲染顺序不一致导致的失败
4. 修复 - 无法自动切换默认语言。增加分享链接,强制执行一次切换默认语言。
5. 修复 - 数组选择器自动兼容 4.8.13 以前的数据。
6. 修复 - 站点同步知识库,链接同步时未使用选择器。
7. 修复 - 简易模式转工作流,没有把系统配置项转化。
8. 修复 - 插件独立运行,变量初始值未赋上。

View File

@@ -29,7 +29,7 @@ export const simpleText = (text = '') => {
replace {{variable}} to value replace {{variable}} to value
*/ */
export function replaceVariable(text: any, obj: Record<string, string | number>) { export function replaceVariable(text: any, obj: Record<string, string | number>) {
if (!(typeof text === 'string')) return text; if (typeof text !== 'string') return text;
for (const key in obj) { for (const key in obj) {
const val = obj[key]; const val = obj[key];

View File

@@ -251,6 +251,7 @@ export const getReferenceVariableValue = ({
return variables[outputId]; return variables[outputId];
} }
// 避免 value 刚好就是二个元素的字符串数组
const node = nodes.find((node) => node.nodeId === sourceNodeId); const node = nodes.find((node) => node.nodeId === sourceNodeId);
if (!node) { if (!node) {
return value; return value;

View File

@@ -193,6 +193,18 @@ export const MultipleRowArraySelect = ({
ref: ref, ref: ref,
handler: onClose handler: onClose
}); });
const onChange = useCallback(
(val: any[][]) => {
// Filter invalid value
const validList = val.filter((item) => {
const listItem = list.find((v) => v.value === item[0]);
if (!listItem) return false;
return listItem.children?.some((v) => v.value === item[1]);
});
onSelect(validList);
},
[onSelect]
);
const RenderList = useCallback( const RenderList = useCallback(
({ index, list }: { index: number; list: MultipleSelectProps['list'] }) => { ({ index, list }: { index: number; list: MultipleSelectProps['list'] }) => {
@@ -213,9 +225,9 @@ export const MultipleRowArraySelect = ({
const newValue = [parentValue, item.value]; const newValue = [parentValue, item.value];
if (newValues.some((v) => v[0] === parentValue && v[1] === item.value)) { if (newValues.some((v) => v[0] === parentValue && v[1] === item.value)) {
onSelect(newValues.filter((v) => !(v[0] === parentValue && v[1] === item.value))); onChange(newValues.filter((v) => !(v[0] === parentValue && v[1] === item.value)));
} else { } else {
onSelect([...newValues, newValue]); onChange([...newValues, newValue]);
} }
} }
}; };

View File

@@ -43,14 +43,16 @@ export const useI18nLng = () => {
setLang(lang); setLang(lang);
await i18n?.changeLanguage?.(lang); await i18n?.changeLanguage?.(lang);
if (prevLang && prevLang !== lang) {
if (!i18n.hasResourceBundle(lang, 'common') && prevLang !== lang) {
window?.location?.reload?.(); window?.location?.reload?.();
} }
}; };
const setUserDefaultLng = () => { const setUserDefaultLng = (forceGetDefaultLng: boolean = false) => {
if (!navigator || !localStorage) return; if (!navigator || !localStorage) return;
if (getLang()) return onChangeLng(getLang() as string);
if (getLang() && !forceGetDefaultLng) return onChangeLng(getLang() as string);
const lang = languageMap[navigator.language] || 'en'; const lang = languageMap[navigator.language] || 'en';

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useMemo } from 'react'; import React, { useEffect } from 'react';
import { Controller, UseFormReturn } from 'react-hook-form'; import { Controller, UseFormReturn } from 'react-hook-form';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { Box, Button, Card, Textarea } from '@chakra-ui/react'; import { Box, Button, Card, Textarea } from '@chakra-ui/react';
@@ -121,21 +121,7 @@ const VariableInput = ({
const variablesForm = useContextSelector(ChatItemContext, (v) => v.variablesForm); const variablesForm = useContextSelector(ChatItemContext, (v) => v.variablesForm);
const variableList = useContextSelector(ChatBoxContext, (v) => v.variableList); const variableList = useContextSelector(ChatBoxContext, (v) => v.variableList);
const { setValue, handleSubmit: handleSubmitChat } = variablesForm; const { handleSubmit: handleSubmitChat } = variablesForm;
const defaultValues = useMemo(() => {
return variableList.reduce((acc: Record<string, any>, item) => {
acc[item.key] = item.defaultValue;
return acc;
}, {});
}, [variableList]);
useEffect(() => {
const values = variablesForm.getValues('variables');
// If form is not empty, do not reset the variables
if (Object.values(values).filter(Boolean).length > 0) return;
setValue('variables', defaultValues);
}, [defaultValues, setValue, variablesForm]);
return ( return (
<Box py={3}> <Box py={3}>

View File

@@ -805,7 +805,7 @@ const ChatBox = ({
setQuestionGuide([]); setQuestionGuide([]);
setValue('chatStarted', false); setValue('chatStarted', false);
abortRequest('leave'); abortRequest('leave');
}, [abortRequest, setValue]); }, [chatId, appId, abortRequest, setValue]);
// Add listener // Add listener
useEffect(() => { useEffect(() => {

View File

@@ -110,17 +110,15 @@ const RenderInput = () => {
return; return;
} }
const defaultFormValues = formatPluginInputs.reduce(
(acc, input) => {
acc[input.key] = input.defaultValue;
return acc;
},
{} as Record<string, any>
);
reset({ reset({
files: [], files: [],
variables: defaultFormValues variables: formatPluginInputs.reduce(
(acc, input) => {
acc[input.key] = input.defaultValue;
return acc;
},
{} as Record<string, any>
)
}); });
return; return;
} }
@@ -164,7 +162,7 @@ const RenderInput = () => {
files: historyFileList files: historyFileList
}); });
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [histories]); }, [histories, formatPluginInputs]);
const [uploading, setUploading] = useState(false); const [uploading, setUploading] = useState(false);

View File

@@ -172,7 +172,6 @@ const RenderPluginInput = ({
return ( return (
<Textarea <Textarea
value={value} value={value}
defaultValue={input.defaultValue}
onChange={onChange} onChange={onChange}
isDisabled={isDisabled} isDisabled={isDisabled}
placeholder={t(input.placeholder as any)} placeholder={t(input.placeholder as any)}
@@ -192,7 +191,6 @@ const RenderPluginInput = ({
isInvalid={isInvalid} isInvalid={isInvalid}
value={value} value={value}
onChange={onChange} onChange={onChange}
defaultValue={input.defaultValue}
/> />
); );
} }
@@ -203,7 +201,6 @@ const RenderPluginInput = ({
onChange={onChange} onChange={onChange}
isDisabled={isDisabled} isDisabled={isDisabled}
isInvalid={isInvalid} isInvalid={isInvalid}
defaultChecked={!!input.defaultValue}
/> />
); );
} }
@@ -216,7 +213,6 @@ const RenderPluginInput = ({
value={value} value={value}
onChange={onChange} onChange={onChange}
isInvalid={isInvalid} isInvalid={isInvalid}
defaultValue={input.defaultValue}
/> />
); );
})(); })();

View File

@@ -2,12 +2,15 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
import { StandardSubLevelEnum, SubModeEnum } from '@fastgpt/global/support/wallet/sub/constants'; import { StandardSubLevelEnum, SubModeEnum } from '@fastgpt/global/support/wallet/sub/constants';
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { standardSubLevelMap } from '@fastgpt/global/support/wallet/sub/constants'; import { standardSubLevelMap } from '@fastgpt/global/support/wallet/sub/constants';
import { Box, Flex, Grid } from '@chakra-ui/react'; import { Box, Flex, Grid, useDisclosure } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { useRouter } from 'next/router';
import { getAiPointUsageCardRoute } from '@/web/support/wallet/sub/constants';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import dynamic from 'next/dynamic';
const AiPointsModal = dynamic(() =>
import('@/pages/price/components/Points').then((mod) => mod.AiPointsModal)
);
const StandardPlanContentList = ({ const StandardPlanContentList = ({
level, level,
@@ -18,7 +21,12 @@ const StandardPlanContentList = ({
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { subPlans } = useSystemStore(); const { subPlans } = useSystemStore();
const router = useRouter();
const {
isOpen: isOpenAiPointsModal,
onClose: onCloseAiPointsModal,
onOpen: onOpenAiPointsModal
} = useDisclosure();
const planContent = useMemo(() => { const planContent = useMemo(() => {
const plan = subPlans?.standard?.[level]; const plan = subPlans?.standard?.[level];
@@ -95,9 +103,7 @@ const StandardPlanContentList = ({
<QuestionTip <QuestionTip
ml={1} ml={1}
label={t('common:support.wallet.subscription.AI points click to read tip')} label={t('common:support.wallet.subscription.AI points click to read tip')}
onClick={() => { onClick={onOpenAiPointsModal}
router.push(getAiPointUsageCardRoute());
}}
></QuestionTip> ></QuestionTip>
</Flex> </Flex>
</Flex> </Flex>
@@ -121,6 +127,7 @@ const StandardPlanContentList = ({
<Box color={'myGray.600'}>{t('common:support.wallet.subscription.web_site_sync')}</Box> <Box color={'myGray.600'}>{t('common:support.wallet.subscription.web_site_sync')}</Box>
</Flex> </Flex>
)} )}
{isOpenAiPointsModal && <AiPointsModal onClose={onCloseAiPointsModal} />}
</Grid> </Grid>
) : null; ) : null;
}; };

View File

@@ -42,7 +42,7 @@ export type InitChatResponse = {
appId: string; appId: string;
userAvatar?: string; userAvatar?: string;
title?: string; title?: string;
variables: Record<string, any>; variables?: Record<string, any>;
app: { app: {
chatConfig?: AppChatConfigType; chatConfig?: AppChatConfigType;
chatModels?: string[]; chatModels?: string[];

View File

@@ -38,6 +38,7 @@ async function handler(
type: AppTypeEnum.workflow, type: AppTypeEnum.workflow,
modules: app.modules, modules: app.modules,
edges: app.edges, edges: app.edges,
chatConfig: app.chatConfig,
teamId: app.teamId, teamId: app.teamId,
tmbId tmbId
}); });

View File

@@ -1,4 +1,4 @@
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiResponse } from 'next';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { authApp } from '@fastgpt/service/support/permission/app/auth'; import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { MongoAppVersion } from '@fastgpt/service/core/app/version/schema'; import { MongoAppVersion } from '@fastgpt/service/core/app/version/schema';
@@ -10,7 +10,6 @@ import { PostPublishAppProps } from '@/global/core/app/api';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant'; import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { ApiRequestProps } from '@fastgpt/service/type/next'; import { ApiRequestProps } from '@fastgpt/service/type/next';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { getScheduleTriggerApp } from '@/service/core/app/utils';
async function handler( async function handler(
req: ApiRequestProps<PostPublishAppProps>, req: ApiRequestProps<PostPublishAppProps>,

View File

@@ -52,7 +52,7 @@ async function handler(
appId, appId,
title: chat?.title, title: chat?.title,
userAvatar: undefined, userAvatar: undefined,
variables: chat?.variables || {}, variables: chat?.variables,
app: { app: {
chatConfig: getAppChatConfig({ chatConfig: getAppChatConfig({
chatConfig, chatConfig,

View File

@@ -43,7 +43,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
title: chat?.title, title: chat?.title,
//@ts-ignore //@ts-ignore
userAvatar: tmb?.userId?.avatar, userAvatar: tmb?.userId?.avatar,
variables: chat?.variables || {}, variables: chat?.variables,
app: { app: {
chatConfig: getAppChatConfig({ chatConfig: getAppChatConfig({
chatConfig, chatConfig,

View File

@@ -50,7 +50,7 @@ async function handler(req: ApiRequestProps<InitTeamChatProps>, res: NextApiResp
appId, appId,
title: chat?.title, title: chat?.title,
userAvatar: team?.avatar, userAvatar: team?.avatar,
variables: chat?.variables || {}, variables: chat?.variables,
app: { app: {
chatConfig: getAppChatConfig({ chatConfig: getAppChatConfig({
chatConfig, chatConfig,

View File

@@ -48,7 +48,8 @@ const DetailLogsModal = ({ appId, chatId, onClose }: Props) => {
setChatBoxData(res); setChatBoxData(res);
resetVariables({ resetVariables({
variables: res.variables variables: res.variables,
variableList: res.app?.chatConfig?.variables
}); });
return res; return res;

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'; import React, { useState } from 'react';
import { import {
Flex, Flex,
Box, Box,
@@ -31,7 +31,8 @@ import { cardStyles } from '../constants';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { useSystem } from '@fastgpt/web/hooks/useSystem'; import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { useUserStore } from '@/web/support/user/useUserStore'; import { useUserStore } from '@/web/support/user/useUserStore';
import Tag from '@fastgpt/web/components/common/Tag'; import { useMount } from 'ahooks';
const DetailLogsModal = dynamic(() => import('./DetailLogsModal')); const DetailLogsModal = dynamic(() => import('./DetailLogsModal'));
const Logs = () => { const Logs = () => {
@@ -41,9 +42,9 @@ const Logs = () => {
const appId = useContextSelector(AppContext, (v) => v.appId); const appId = useContextSelector(AppContext, (v) => v.appId);
const { teamMembers, loadAndGetTeamMembers } = useUserStore(); const { teamMembers, loadAndGetTeamMembers } = useUserStore();
useEffect(() => { useMount(() => {
loadAndGetTeamMembers(); loadAndGetTeamMembers();
}, []); });
const [dateRange, setDateRange] = useState<DateRangeType>({ const [dateRange, setDateRange] = useState<DateRangeType>({
from: addDays(new Date(), -7), from: addDays(new Date(), -7),
@@ -140,11 +141,11 @@ const Logs = () => {
) : ( ) : (
<HStack> <HStack>
<Avatar <Avatar
src={teamMembers.find((v) => v.tmbId === item.tmbId)?.avatar} src={teamMembers?.find((v) => v.tmbId === item.tmbId)?.avatar}
w="1.25rem" w="1.25rem"
/> />
<Box fontSize={'sm'} ml={1}> <Box fontSize={'sm'} ml={1}>
{teamMembers.find((v) => v.tmbId === item.tmbId)?.memberName} {teamMembers?.find((v) => v.tmbId === item.tmbId)?.memberName}
</Box> </Box>
</HStack> </HStack>
)} )}

View File

@@ -10,8 +10,6 @@ import {
NodePositionChange, NodePositionChange,
XYPosition, XYPosition,
useReactFlow, useReactFlow,
getNodesBounds,
Rect,
NodeRemoveChange, NodeRemoveChange,
NodeSelectionChange, NodeSelectionChange,
EdgeRemoveChange EdgeRemoveChange
@@ -26,15 +24,12 @@ import { WorkflowContext } from '../../context';
import { THelperLine } from '@fastgpt/global/core/workflow/type'; import { THelperLine } from '@fastgpt/global/core/workflow/type';
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants'; import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { useDebounceEffect, useMemoizedFn } from 'ahooks'; import { useDebounceEffect, useMemoizedFn } from 'ahooks';
import {
Input_Template_Node_Height,
Input_Template_Node_Width
} from '@fastgpt/global/core/workflow/template/input';
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node'; import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
import { WorkflowNodeEdgeContext, WorkflowInitContext } from '../../context/workflowInitContext'; import { WorkflowNodeEdgeContext, WorkflowInitContext } from '../../context/workflowInitContext';
import { formatTime2YMDHMS } from '@fastgpt/global/common/string/time'; import { formatTime2YMDHMS } from '@fastgpt/global/common/string/time';
import { AppContext } from '../../../context'; import { AppContext } from '../../../context';
import { WorkflowEventContext } from '../../context/workflowEventContext'; import { WorkflowEventContext } from '../../context/workflowEventContext';
import { WorkflowStatusContext } from '../../context/workflowStatusContext';
/* /*
Compute helper lines for snapping nodes to each other Compute helper lines for snapping nodes to each other
@@ -282,18 +277,22 @@ export const useWorkflow = () => {
const edges = useContextSelector(WorkflowNodeEdgeContext, (state) => state.edges); const edges = useContextSelector(WorkflowNodeEdgeContext, (state) => state.edges);
const setEdges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.setEdges); const setEdges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.setEdges);
const onEdgesChange = useContextSelector(WorkflowNodeEdgeContext, (v) => v.onEdgesChange); const onEdgesChange = useContextSelector(WorkflowNodeEdgeContext, (v) => v.onEdgesChange);
const { setConnectingEdge, nodeList, onChangeNode, pushPastSnapshot } = useContextSelector(
WorkflowContext, const setConnectingEdge = useContextSelector(WorkflowContext, (v) => v.setConnectingEdge);
(v) => v const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
); const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
const pushPastSnapshot = useContextSelector(WorkflowContext, (v) => v.pushPastSnapshot);
const setHoverEdgeId = useContextSelector(WorkflowEventContext, (v) => v.setHoverEdgeId); const setHoverEdgeId = useContextSelector(WorkflowEventContext, (v) => v.setHoverEdgeId);
const setMenu = useContextSelector(WorkflowEventContext, (v) => v.setMenu); const setMenu = useContextSelector(WorkflowEventContext, (v) => v.setMenu);
const resetParentNodeSizeAndPosition = useContextSelector(
WorkflowStatusContext,
(v) => v.resetParentNodeSizeAndPosition
);
const { getIntersectingNodes } = useReactFlow(); const { getIntersectingNodes } = useReactFlow();
const { isDowningCtrl } = useKeyboard(); const { isDowningCtrl } = useKeyboard();
const { resetParentNodeSizeAndPosition } = useLoopNode();
/* helper line */ /* helper line */
const [helperLineHorizontal, setHelperLineHorizontal] = useState<THelperLine>(); const [helperLineHorizontal, setHelperLineHorizontal] = useState<THelperLine>();
const [helperLineVertical, setHelperLineVertical] = useState<THelperLine>(); const [helperLineVertical, setHelperLineVertical] = useState<THelperLine>();
@@ -669,73 +668,6 @@ export const useWorkflow = () => {
}; };
}; };
export const useLoopNode = () => {
const nodes = useContextSelector(WorkflowInitContext, (state) => state.nodes);
const onNodesChange = useContextSelector(WorkflowNodeEdgeContext, (state) => state.onNodesChange);
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
const resetParentNodeSizeAndPosition = useMemoizedFn((parentId: string) => {
const { childNodes, loopNode } = nodes.reduce(
(acc, node) => {
if (node.data.parentNodeId === parentId) {
acc.childNodes.push(node);
}
if (node.id === parentId) {
acc.loopNode = node;
}
return acc;
},
{ childNodes: [] as Node[], loopNode: undefined as Node<FlowNodeItemType> | undefined }
);
if (!loopNode) return;
const rect = getNodesBounds(childNodes);
// Calculate parent node size with minimum width/height constraints
const width = Math.max(rect.width + 80, 840);
const height = Math.max(rect.height + 80, 600);
const offsetHeight =
loopNode.data.inputs.find((input) => input.key === NodeInputKeyEnum.loopNodeInputHeight)
?.value ?? 83;
// Update parentNode size and position
onChangeNode({
nodeId: parentId,
type: 'updateInput',
key: NodeInputKeyEnum.nodeWidth,
value: {
...Input_Template_Node_Width,
value: width
}
});
onChangeNode({
nodeId: parentId,
type: 'updateInput',
key: NodeInputKeyEnum.nodeHeight,
value: {
...Input_Template_Node_Height,
value: height
}
});
// Update parentNode position
onNodesChange([
{
id: parentId,
type: 'position',
position: {
x: rect.x - 70,
y: rect.y - offsetHeight - 240
}
}
]);
});
return {
resetParentNodeSizeAndPosition
};
};
export default function Dom() { export default function Dom() {
return <></>; return <></>;
} }

View File

@@ -3,7 +3,6 @@
When the childNodes of loopFlow change, it automatically calculates the rectangular width, height, and position of the childNodes, When the childNodes of loopFlow change, it automatically calculates the rectangular width, height, and position of the childNodes,
thereby further updating the width and height properties of the loop node. thereby further updating the width and height properties of the loop node.
*/ */
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node'; import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
import React, { useEffect, useMemo, useRef } from 'react'; import React, { useEffect, useMemo, useRef } from 'react';
import { Background, NodeProps } from 'reactflow'; import { Background, NodeProps } from 'reactflow';
@@ -31,8 +30,8 @@ import { getWorkflowGlobalVariables } from '@/web/core/workflow/utils';
import { AppContext } from '../../../../context'; import { AppContext } from '../../../../context';
import { isValidArrayReferenceValue } from '@fastgpt/global/core/workflow/utils'; import { isValidArrayReferenceValue } from '@fastgpt/global/core/workflow/utils';
import { ReferenceArrayValueType } from '@fastgpt/global/core/workflow/type/io'; import { ReferenceArrayValueType } from '@fastgpt/global/core/workflow/type/io';
import { useLoopNode } from '../../hooks/useWorkflow';
import { useSize } from 'ahooks'; import { useSize } from 'ahooks';
import { WorkflowStatusContext } from '../../../context/workflowStatusContext';
const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => { const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -40,8 +39,10 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList); const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode); const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
const appDetail = useContextSelector(AppContext, (v) => v.appDetail); const appDetail = useContextSelector(AppContext, (v) => v.appDetail);
const resetParentNodeSizeAndPosition = useContextSelector(
const { resetParentNodeSizeAndPosition } = useLoopNode(); WorkflowStatusContext,
(v) => v.resetParentNodeSizeAndPosition
);
const { const {
nodeWidth, nodeWidth,
@@ -50,11 +51,11 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
loopNodeInputHeight = Input_Template_LOOP_NODE_OFFSET loopNodeInputHeight = Input_Template_LOOP_NODE_OFFSET
} = useMemo(() => { } = useMemo(() => {
return { return {
nodeWidth: Number( nodeWidth: Math.round(
inputs.find((input) => input.key === NodeInputKeyEnum.nodeWidth)?.value?.toFixed(0) Number(inputs.find((input) => input.key === NodeInputKeyEnum.nodeWidth)?.value) || 500
), ),
nodeHeight: Number( nodeHeight: Math.round(
inputs.find((input) => input.key === NodeInputKeyEnum.nodeHeight)?.value?.toFixed(0) Number(inputs.find((input) => input.key === NodeInputKeyEnum.nodeHeight)?.value) || 500
), ),
loopInputArray: inputs.find((input) => input.key === NodeInputKeyEnum.loopInputArray), loopInputArray: inputs.find((input) => input.key === NodeInputKeyEnum.loopInputArray),
loopNodeInputHeight: inputs.find( loopNodeInputHeight: inputs.find(
@@ -113,7 +114,7 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
return JSON.stringify( return JSON.stringify(
nodeList.filter((node) => node.parentNodeId === nodeId).map((node) => node.nodeId) nodeList.filter((node) => node.parentNodeId === nodeId).map((node) => node.nodeId)
); );
}, [nodeId, nodeList]); }, [nodeId, nodeList.length]);
useEffect(() => { useEffect(() => {
onChangeNode({ onChangeNode({
nodeId, nodeId,
@@ -148,39 +149,54 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
}, 50); }, 50);
}, [size?.height]); }, [size?.height]);
const Render = useMemo(() => { const RenderInputDom = useMemo(() => {
return ( return (
<NodeCard selected={selected} maxW="full" menuForbid={{ copy: true }} {...data}> <Box mb={6} maxW={'500px'} ref={inputBoxRef}>
<Container position={'relative'} flex={1}> <RenderInput nodeId={nodeId} flowInputList={inputs} />
<IOTitle text={t('common:common.Input')} /> </Box>
<Box mb={6} maxW={'500px'} ref={inputBoxRef}>
<RenderInput nodeId={nodeId} flowInputList={inputs} />
</Box>
<FormLabel required fontWeight={'medium'} mb={3} color={'myGray.600'}>
{t('workflow:loop_body')}
</FormLabel>
<Box
flex={1}
position={'relative'}
border={'base'}
bg={'myGray.100'}
rounded={'8px'}
{...(!isFolded && {
minW: nodeWidth,
minH: nodeHeight
})}
>
<Background />
</Box>
</Container>
<Container>
<RenderOutput nodeId={nodeId} flowOutputList={outputs} />
</Container>
</NodeCard>
); );
}, [selected, isFolded, nodeWidth, nodeHeight, data, t, nodeId, inputs, outputs]); }, [inputs, nodeId]);
const RenderChildrenNodes = useMemo(() => {
return (
<>
<FormLabel required fontWeight={'medium'} mb={3} color={'myGray.600'}>
{t('workflow:loop_body')}
</FormLabel>
<Box
flex={1}
position={'relative'}
border={'base'}
bg={'myGray.100'}
rounded={'8px'}
{...(!isFolded && {
minW: nodeWidth,
minH: nodeHeight
})}
>
<Background />
</Box>
</>
);
}, [isFolded, nodeHeight, nodeWidth, t]);
return Render; const MemoRenderOutput = useMemo(() => {
return (
<Container>
<RenderOutput nodeId={nodeId} flowOutputList={outputs} />
</Container>
);
}, [nodeId, outputs]);
return (
<NodeCard selected={selected} maxW="full" menuForbid={{ copy: true }} {...data}>
<Container position={'relative'} flex={1}>
<IOTitle text={t('common:common.Input')} />
{RenderInputDom}
{RenderChildrenNodes}
</Container>
{MemoRenderOutput}
</NodeCard>
);
}; };
export default React.memo(NodeLoop); export default React.memo(NodeLoop);

View File

@@ -23,13 +23,15 @@ const typeMap = {
[WorkflowIOValueTypeEnum.arrayString]: WorkflowIOValueTypeEnum.string, [WorkflowIOValueTypeEnum.arrayString]: WorkflowIOValueTypeEnum.string,
[WorkflowIOValueTypeEnum.arrayNumber]: WorkflowIOValueTypeEnum.number, [WorkflowIOValueTypeEnum.arrayNumber]: WorkflowIOValueTypeEnum.number,
[WorkflowIOValueTypeEnum.arrayBoolean]: WorkflowIOValueTypeEnum.boolean, [WorkflowIOValueTypeEnum.arrayBoolean]: WorkflowIOValueTypeEnum.boolean,
[WorkflowIOValueTypeEnum.arrayObject]: WorkflowIOValueTypeEnum.object [WorkflowIOValueTypeEnum.arrayObject]: WorkflowIOValueTypeEnum.object,
[WorkflowIOValueTypeEnum.arrayAny]: WorkflowIOValueTypeEnum.any
}; };
const NodeLoopStart = ({ data, selected }: NodeProps<FlowNodeItemType>) => { const NodeLoopStart = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { nodeId, outputs } = data; const { nodeId, outputs } = data;
const { nodeList, onChangeNode } = useContextSelector(WorkflowContext, (v) => v); const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
const loopStartNode = useMemo( const loopStartNode = useMemo(
() => nodeList.find((node) => node.nodeId === nodeId), () => nodeList.find((node) => node.nodeId === nodeId),

View File

@@ -88,6 +88,13 @@ const RenderInput = ({ flowInputList, nodeId, CustomComponent, mb = 5 }: Props)
const filterInputs = useMemo(() => { const filterInputs = useMemo(() => {
return flowInputList.filter((input) => { return flowInputList.filter((input) => {
if (input.isPro && !feConfigs?.isPlus) return false; if (input.isPro && !feConfigs?.isPlus) return false;
const renderType = input.renderTypeList?.[input.selectedTypeIndex || 0];
if (renderType === FlowNodeInputTypeEnum.hidden) return false;
const isDynamic = !!input.canEdit;
if (isDynamic) return false;
return true; return true;
}); });
}, [feConfigs?.isPlus, flowInputList]); }, [feConfigs?.isPlus, flowInputList]);
@@ -96,7 +103,6 @@ const RenderInput = ({ flowInputList, nodeId, CustomComponent, mb = 5 }: Props)
<> <>
{filterInputs.map((input) => { {filterInputs.map((input) => {
const renderType = input.renderTypeList?.[input.selectedTypeIndex || 0]; const renderType = input.renderTypeList?.[input.selectedTypeIndex || 0];
const isDynamic = !!input.canEdit;
const RenderComponent = (() => { const RenderComponent = (() => {
if (renderType === FlowNodeInputTypeEnum.custom && CustomComponent?.[input.key]) { if (renderType === FlowNodeInputTypeEnum.custom && CustomComponent?.[input.key]) {
@@ -109,7 +115,7 @@ const RenderInput = ({ flowInputList, nodeId, CustomComponent, mb = 5 }: Props)
return <Component inputs={filterInputs} item={input} nodeId={nodeId} />; return <Component inputs={filterInputs} item={input} nodeId={nodeId} />;
})(); })();
return renderType !== FlowNodeInputTypeEnum.hidden && !isDynamic ? ( return (
<Box key={input.key} _notLast={{ mb }} position={'relative'}> <Box key={input.key} _notLast={{ mb }} position={'relative'}>
{!!input.label && !hideLabelTypeList.includes(renderType) && ( {!!input.label && !hideLabelTypeList.includes(renderType) && (
<InputLabel nodeId={nodeId} input={input} /> <InputLabel nodeId={nodeId} input={input} />
@@ -120,7 +126,7 @@ const RenderInput = ({ flowInputList, nodeId, CustomComponent, mb = 5 }: Props)
</Box> </Box>
)} )}
</Box> </Box>
) : null; );
})} })}
</> </>
); );

View File

@@ -115,7 +115,10 @@ export const useReference = ({
const Reference = ({ item, nodeId }: RenderInputProps) => { const Reference = ({ item, nodeId }: RenderInputProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { onChangeNode, nodeList } = useContextSelector(WorkflowContext, (v) => v);
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
const isArray = item.valueType?.includes('array') ?? false; const isArray = item.valueType?.includes('array') ?? false;
const onSelect = useCallback( const onSelect = useCallback(
@@ -254,22 +257,25 @@ const MultipleReferenceSelector = ({
}); });
}, [getSelectValue, value]); }, [getSelectValue, value]);
// useEffect(() => { useEffect(() => {
// const validList = formatList.filter((item) => item.nodeName && item.outputName); // Adapt array type from old version
// if (validList.length !== value?.length) { if (Array.isArray(value) && typeof value[0] === 'string') {
// onSelect(validList.map((item) => item.rawValue)); // @ts-ignore
// } onSelect([value]);
// }, [formatList, onSelect, value]); }
}, [formatList, onSelect, value]);
const invalidList = useMemo(() => {
return formatList.filter((item) => item.nodeName && item.outputName);
}, [formatList]);
const ArraySelector = useMemo(() => { const ArraySelector = useMemo(() => {
return ( return (
<MultipleRowArraySelect <MultipleRowArraySelect
label={ label={
formatList.length > 0 ? ( invalidList.length > 0 ? (
<Grid py={3} gridTemplateColumns={'1fr 1fr'} gap={2} fontSize={'sm'}> <Grid py={3} gridTemplateColumns={'1fr 1fr'} gap={2} fontSize={'sm'}>
{formatList.map(({ nodeName, outputName }, index) => { {invalidList.map(({ nodeName, outputName }, index) => {
if (!nodeName || !outputName) return null;
return ( return (
<Flex <Flex
alignItems={'center'} alignItems={'center'}
@@ -325,7 +331,7 @@ const MultipleReferenceSelector = ({
popDirection={popDirection} popDirection={popDirection}
/> />
); );
}, [formatList, list, onSelect, placeholder, popDirection, value]); }, [invalidList, list, onSelect, placeholder, popDirection, value]);
return ArraySelector; return ArraySelector;
}; };

View File

@@ -1,7 +1,7 @@
import { createContext } from 'use-context-selector'; import { createContext } from 'use-context-selector';
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node'; import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
import { useMemoizedFn } from 'ahooks'; import { useCreation, useMemoizedFn } from 'ahooks';
import React, { Dispatch, SetStateAction, ReactNode, useEffect, useMemo } from 'react'; import React, { Dispatch, SetStateAction, ReactNode, useEffect, useMemo } from 'react';
import { Edge, EdgeChange, Node, NodeChange, useEdgesState, useNodesState } from 'reactflow'; import { Edge, EdgeChange, Node, NodeChange, useEdgesState, useNodesState } from 'reactflow';
@@ -50,7 +50,7 @@ const WorkflowInitContextProvider = ({ children }: { children: ReactNode }) => {
const [nodes = [], setNodes, onNodesChange] = useNodesState<FlowNodeItemType>([]); const [nodes = [], setNodes, onNodesChange] = useNodesState<FlowNodeItemType>([]);
const getNodes = useMemoizedFn(() => nodes); const getNodes = useMemoizedFn(() => nodes);
const nodeListString = JSON.stringify(nodes.map((node) => node.data)); const nodeListString = JSON.stringify(nodes.map((node) => node.data));
const nodeList = useMemo( const nodeList = useCreation(
() => JSON.parse(nodeListString) as FlowNodeItemType[], () => JSON.parse(nodeListString) as FlowNodeItemType[],
[nodeListString] [nodeListString]
); );
@@ -73,7 +73,7 @@ const WorkflowInitContextProvider = ({ children }: { children: ReactNode }) => {
: item : item
) )
); );
}, [edges.length]); }, [nodeList, edges.length]);
const actionContextValue = useMemo( const actionContextValue = useMemo(
() => ({ () => ({

View File

@@ -1,4 +1,4 @@
import { useDebounceEffect } from 'ahooks'; import { useDebounceEffect, useMemoizedFn } from 'ahooks';
import React, { ReactNode, useMemo, useRef, useState } from 'react'; import React, { ReactNode, useMemo, useRef, useState } from 'react';
import { createContext, useContextSelector } from 'use-context-selector'; import { createContext, useContextSelector } from 'use-context-selector';
import { WorkflowInitContext, WorkflowNodeEdgeContext } from './workflowInitContext'; import { WorkflowInitContext, WorkflowNodeEdgeContext } from './workflowInitContext';
@@ -7,10 +7,18 @@ import { AppContext } from '../../context';
import { compareSnapshot } from '@/web/core/workflow/utils'; import { compareSnapshot } from '@/web/core/workflow/utils';
import { useBeforeunload } from '@fastgpt/web/hooks/useBeforeunload'; import { useBeforeunload } from '@fastgpt/web/hooks/useBeforeunload';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { getNodesBounds, Node } from 'reactflow';
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import {
Input_Template_Node_Height,
Input_Template_Node_Width
} from '@fastgpt/global/core/workflow/template/input';
type WorkflowStatusContextType = { type WorkflowStatusContextType = {
isSaved: boolean; isSaved: boolean;
leaveSaveSign: React.MutableRefObject<boolean>; leaveSaveSign: React.MutableRefObject<boolean>;
resetParentNodeSizeAndPosition: (parentId: string) => void;
}; };
export const WorkflowStatusContext = createContext<WorkflowStatusContextType>({ export const WorkflowStatusContext = createContext<WorkflowStatusContextType>({
@@ -75,12 +83,72 @@ const WorkflowStatusContextProvider = ({ children }: { children: ReactNode }) =>
} }
}); });
const onNodesChange = useContextSelector(WorkflowNodeEdgeContext, (state) => state.onNodesChange);
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
const resetParentNodeSizeAndPosition = useMemoizedFn((parentId: string) => {
const { childNodes, loopNode } = nodes.reduce(
(acc, node) => {
if (node.data.parentNodeId === parentId) {
acc.childNodes.push(node);
}
if (node.id === parentId) {
acc.loopNode = node;
}
return acc;
},
{ childNodes: [] as Node[], loopNode: undefined as Node<FlowNodeItemType> | undefined }
);
if (!loopNode) return;
const rect = getNodesBounds(childNodes);
// Calculate parent node size with minimum width/height constraints
const width = Math.max(rect.width + 80, 840);
const height = Math.max(rect.height + 80, 600);
const offsetHeight =
loopNode.data.inputs.find((input) => input.key === NodeInputKeyEnum.loopNodeInputHeight)
?.value ?? 83;
// Update parentNode size and position
onChangeNode({
nodeId: parentId,
type: 'updateInput',
key: NodeInputKeyEnum.nodeWidth,
value: {
...Input_Template_Node_Width,
value: width
}
});
onChangeNode({
nodeId: parentId,
type: 'updateInput',
key: NodeInputKeyEnum.nodeHeight,
value: {
...Input_Template_Node_Height,
value: height
}
});
// Update parentNode position
onNodesChange([
{
id: parentId,
type: 'position',
position: {
x: Math.round(rect.x - 70),
y: Math.round(rect.y - offsetHeight - 240)
}
}
]);
});
const contextValue = useMemo(() => { const contextValue = useMemo(() => {
return { return {
isSaved, isSaved,
leaveSaveSign leaveSaveSign,
resetParentNodeSizeAndPosition
}; };
}, [isSaved]); }, [isSaved, resetParentNodeSizeAndPosition]);
return ( return (
<WorkflowStatusContext.Provider value={contextValue}>{children}</WorkflowStatusContext.Provider> <WorkflowStatusContext.Provider value={contextValue}>{children}</WorkflowStatusContext.Provider>
); );

View File

@@ -107,8 +107,10 @@ export const useChatTest = ({
async () => { async () => {
if (!appId || !chatId) return; if (!appId || !chatId) return;
const res = await getInitChatInfo({ appId, chatId }); const res = await getInitChatInfo({ appId, chatId });
resetVariables({ resetVariables({
variables: res.variables variables: res.variables,
variableList: res.app?.chatConfig?.variables
}); });
}, },
{ {

View File

@@ -74,7 +74,8 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
// reset chat variables // reset chat variables
resetVariables({ resetVariables({
variables: res.variables variables: res.variables,
variableList: res.app?.chatConfig?.variables
}); });
}, },
{ {

View File

@@ -22,8 +22,7 @@ import { connectToDatabase } from '@/service/mongo';
import NextHead from '@/components/common/NextHead'; import NextHead from '@/components/common/NextHead';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import ChatContextProvider, { ChatContext } from '@/web/core/chat/context/chatContext'; import ChatContextProvider, { ChatContext } from '@/web/core/chat/context/chatContext';
import { InitChatResponse } from '@/global/core/chat/api'; import { GetChatTypeEnum } from '@/global/core/chat/constants';
import { defaultChatData, GetChatTypeEnum } from '@/global/core/chat/constants';
import { useMount } from 'ahooks'; import { useMount } from 'ahooks';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { getNanoid } from '@fastgpt/global/common/string/tools'; import { getNanoid } from '@fastgpt/global/common/string/tools';
@@ -37,6 +36,8 @@ import ChatRecordContextProvider, {
} from '@/web/core/chat/context/chatRecordContext'; } from '@/web/core/chat/context/chatRecordContext';
import { useChatStore } from '@/web/core/chat/context/useChatStore'; import { useChatStore } from '@/web/core/chat/context/useChatStore';
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants'; import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
import { useI18nLng } from '@fastgpt/web/hooks/useI18n';
const CustomPluginRunBox = dynamic(() => import('./components/CustomPluginRunBox')); const CustomPluginRunBox = dynamic(() => import('./components/CustomPluginRunBox'));
type Props = { type Props = {
@@ -102,7 +103,8 @@ const OutLink = (props: Props) => {
setChatBoxData(res); setChatBoxData(res);
resetVariables({ resetVariables({
variables: res.variables variables: res.variables,
variableList: res.app?.chatConfig?.variables
}); });
return res; return res;
@@ -299,8 +301,9 @@ const OutLink = (props: Props) => {
const Render = (props: Props) => { const Render = (props: Props) => {
const { shareId, authToken, customUid, appId } = props; const { shareId, authToken, customUid, appId } = props;
const { localUId, loaded } = useShareChatStore(); const { localUId } = useShareChatStore();
const { source, chatId, setSource, setAppId, setOutLinkAuthData } = useChatStore(); const { source, chatId, setSource, setAppId, setOutLinkAuthData } = useChatStore();
const { setUserDefaultLng } = useI18nLng();
const chatHistoryProviderParams = useMemo(() => { const chatHistoryProviderParams = useMemo(() => {
return { shareId, outLinkUid: authToken || customUid || localUId }; return { shareId, outLinkUid: authToken || customUid || localUId };
@@ -317,6 +320,7 @@ const Render = (props: Props) => {
useMount(() => { useMount(() => {
setSource('share'); setSource('share');
setUserDefaultLng(true);
}); });
// Set outLinkAuthData // Set outLinkAuthData

View File

@@ -79,7 +79,8 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
// reset chat records // reset chat records
resetVariables({ resetVariables({
variables: res.variables variables: res.variables,
variableList: res.app?.chatConfig?.variables
}); });
}, },
{ {

View File

@@ -23,5 +23,7 @@ export const langMap = {
export const serviceSideProps = (content: any, ns: I18nNsType = []) => { export const serviceSideProps = (content: any, ns: I18nNsType = []) => {
const lang = content.req?.cookies?.NEXT_LOCALE || content.locale; const lang = content.req?.cookies?.NEXT_LOCALE || content.locale;
return serverSideTranslations(lang, ['common', ...ns], null);
const extraLng = content.req?.cookies?.NEXT_LOCALE ? undefined : content.locales;
return serverSideTranslations(lang, ['common', ...ns], null, extraLng);
}; };

View File

@@ -6,7 +6,7 @@ import { ComponentRef as ChatComponentRef } from '@/components/core/chat/ChatCon
import { useForm, UseFormReturn } from 'react-hook-form'; import { useForm, UseFormReturn } from 'react-hook-form';
import { defaultChatData } from '@/global/core/chat/constants'; import { defaultChatData } from '@/global/core/chat/constants';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { AppChatConfigType } from '@fastgpt/global/core/app/type'; import { AppChatConfigType, VariableItemType } from '@fastgpt/global/core/app/type';
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io'; import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
type ChatBoxDataType = { type ChatBoxDataType = {
@@ -29,7 +29,10 @@ type ChatItemContextType = {
variablesForm: UseFormReturn<ChatBoxInputFormType, any>; variablesForm: UseFormReturn<ChatBoxInputFormType, any>;
pluginRunTab: PluginRunBoxTabEnum; pluginRunTab: PluginRunBoxTabEnum;
setPluginRunTab: React.Dispatch<React.SetStateAction<PluginRunBoxTabEnum>>; setPluginRunTab: React.Dispatch<React.SetStateAction<PluginRunBoxTabEnum>>;
resetVariables: (props?: { variables?: Record<string, any> }) => void; resetVariables: (props?: {
variables?: Record<string, any>;
variableList?: VariableItemType[];
}) => void;
clearChatRecords: () => void; clearChatRecords: () => void;
chatBoxData: ChatBoxDataType; chatBoxData: ChatBoxDataType;
setChatBoxData: React.Dispatch<React.SetStateAction<ChatBoxDataType>>; setChatBoxData: React.Dispatch<React.SetStateAction<ChatBoxDataType>>;
@@ -44,7 +47,10 @@ export const ChatItemContext = createContext<ChatItemContextType>({
setPluginRunTab: function (value: React.SetStateAction<PluginRunBoxTabEnum>): void { setPluginRunTab: function (value: React.SetStateAction<PluginRunBoxTabEnum>): void {
throw new Error('Function not implemented.'); throw new Error('Function not implemented.');
}, },
resetVariables: function (props?: { variables?: Record<string, any> }): void { resetVariables: function (props?: {
variables?: Record<string, any>;
variableList?: VariableItemType[];
}): void {
throw new Error('Function not implemented.'); throw new Error('Function not implemented.');
}, },
clearChatRecords: function (): void { clearChatRecords: function (): void {
@@ -69,27 +75,21 @@ const ChatItemContextProvider = ({ children }: { children: ReactNode }) => {
const [pluginRunTab, setPluginRunTab] = useState<PluginRunBoxTabEnum>(PluginRunBoxTabEnum.input); const [pluginRunTab, setPluginRunTab] = useState<PluginRunBoxTabEnum>(PluginRunBoxTabEnum.input);
const resetVariables = useCallback( const resetVariables = useCallback(
(props?: { variables?: Record<string, any> }) => { (props?: { variables?: Record<string, any>; variableList?: VariableItemType[] }) => {
const { variables = {} } = props || {}; const { variables, variableList = [] } = props || {};
// Reset to empty input let newVariableValue: Record<string, any> = {};
const data = variablesForm.getValues(); if (variables) {
variableList.forEach((item) => {
// Reset the old variables to empty newVariableValue[item.key] = variables[item.key];
const resetVariables: Record<string, any> = {}; });
for (const key in data.variables) { } else {
resetVariables[key] = (() => { variableList.forEach((item) => {
if (Array.isArray(data.variables[key])) { newVariableValue[item.key] = item.defaultValue;
return []; });
}
return '';
})();
} }
variablesForm.setValue('variables', { variablesForm.setValue('variables', newVariableValue);
...resetVariables,
...variables
});
}, },
[variablesForm] [variablesForm]
); );

View File

@@ -119,6 +119,7 @@ export const storeNode2FlowNode = ({
selectedTypeIndex: storeInput.selectedTypeIndex ?? templateInput.selectedTypeIndex, selectedTypeIndex: storeInput.selectedTypeIndex ?? templateInput.selectedTypeIndex,
value: storeInput.value ?? templateInput.value, value: storeInput.value ?? templateInput.value,
valueType: storeInput.valueType ?? templateInput.valueType,
label: storeInput.label ?? templateInput.label label: storeInput.label ?? templateInput.label
}; };
}) })
@@ -148,7 +149,8 @@ export const storeNode2FlowNode = ({
id: storeOutput.id ?? templateOutput.id, id: storeOutput.id ?? templateOutput.id,
label: storeOutput.label ?? templateOutput.label, label: storeOutput.label ?? templateOutput.label,
value: storeOutput.value ?? templateOutput.value value: storeOutput.value ?? templateOutput.value,
valueType: storeOutput.valueType ?? templateOutput.valueType
}; };
}) })
.concat( .concat(

View File

@@ -1,14 +1,6 @@
import { getDocPath } from '@/web/common/system/doc'; import { getDocPath } from '@/web/common/system/doc';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
export const AI_POINT_USAGE_CARD_ROUTE = '/price#point-card';
export const getAiPointUsageCardRoute = () => {
const subPlans = useSystemStore.getState().subPlans;
return subPlans?.planDescriptionUrl
? getDocPath(subPlans.planDescriptionUrl)
: AI_POINT_USAGE_CARD_ROUTE;
};
export const EXTRA_PLAN_CARD_ROUTE = '/price#extra-plan'; export const EXTRA_PLAN_CARD_ROUTE = '/price#extra-plan';
export const getExtraPlanCardRoute = () => { export const getExtraPlanCardRoute = () => {
const subPlans = useSystemStore.getState().subPlans; const subPlans = useSystemStore.getState().subPlans;