mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 13:03:50 +00:00
feat: add update variable node (#1362)
* feat: add variable update node * fix * fix * change component quote
This commit is contained in:
@@ -101,7 +101,10 @@ export enum NodeInputKeyEnum {
|
||||
|
||||
// if else
|
||||
condition = 'condition',
|
||||
ifElseList = 'ifElseList'
|
||||
ifElseList = 'ifElseList',
|
||||
|
||||
// variable update
|
||||
updateList = 'updateList'
|
||||
}
|
||||
|
||||
export enum NodeOutputKeyEnum {
|
||||
|
@@ -112,7 +112,8 @@ export enum FlowNodeTypeEnum {
|
||||
tools = 'tools',
|
||||
stopTool = 'stopTool',
|
||||
lafModule = 'lafModule',
|
||||
ifElseNode = 'ifElseNode'
|
||||
ifElseNode = 'ifElseNode',
|
||||
variableUpdate = 'variableUpdate'
|
||||
}
|
||||
|
||||
export const EDGE_TYPE = 'default';
|
||||
|
@@ -9,7 +9,8 @@ export enum SseResponseEventEnum {
|
||||
toolCall = 'toolCall', // tool start
|
||||
toolParams = 'toolParams', // tool params return
|
||||
toolResponse = 'toolResponse', // tool response return
|
||||
flowResponses = 'flowResponses' // sse response request
|
||||
flowResponses = 'flowResponses', // sse response request
|
||||
variables = 'variables'
|
||||
}
|
||||
|
||||
export enum DispatchNodeResponseKeyEnum {
|
||||
|
@@ -18,10 +18,10 @@ import { PluginOutputModule } from './system/pluginOutput';
|
||||
import { RunPluginModule } from './system/runPlugin';
|
||||
import { AiQueryExtension } from './system/queryExtension';
|
||||
|
||||
import type { FlowNodeTemplateType, nodeTemplateListType } from '../type';
|
||||
import { FlowNodeTemplateTypeEnum } from '../../workflow/constants';
|
||||
import type { FlowNodeTemplateType } from '../type';
|
||||
import { lafModule } from './system/laf';
|
||||
import { ifElseNode } from './system/ifElse/index';
|
||||
import { variableUpdateNode } from './system/variableUpdate';
|
||||
|
||||
/* app flow module templates */
|
||||
export const appSystemModuleTemplates: FlowNodeTemplateType[] = [
|
||||
@@ -39,7 +39,8 @@ export const appSystemModuleTemplates: FlowNodeTemplateType[] = [
|
||||
HttpModule468,
|
||||
AiQueryExtension,
|
||||
lafModule,
|
||||
ifElseNode
|
||||
ifElseNode,
|
||||
variableUpdateNode
|
||||
];
|
||||
/* plugin flow module templates */
|
||||
export const pluginSystemModuleTemplates: FlowNodeTemplateType[] = [
|
||||
@@ -57,7 +58,8 @@ export const pluginSystemModuleTemplates: FlowNodeTemplateType[] = [
|
||||
HttpModule468,
|
||||
AiQueryExtension,
|
||||
lafModule,
|
||||
ifElseNode
|
||||
ifElseNode,
|
||||
variableUpdateNode
|
||||
];
|
||||
|
||||
/* all module */
|
||||
@@ -81,5 +83,6 @@ export const moduleTemplatesFlat: FlowNodeTemplateType[] = [
|
||||
RunPluginModule,
|
||||
AiQueryExtension,
|
||||
lafModule,
|
||||
ifElseNode
|
||||
ifElseNode,
|
||||
variableUpdateNode
|
||||
];
|
||||
|
@@ -0,0 +1,35 @@
|
||||
import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '../../../node/constant';
|
||||
import { FlowNodeTemplateType } from '../../../type/index.d';
|
||||
import {
|
||||
FlowNodeTemplateTypeEnum,
|
||||
NodeInputKeyEnum,
|
||||
WorkflowIOValueTypeEnum
|
||||
} from '../../../constants';
|
||||
import { getHandleConfig } from '../../utils';
|
||||
|
||||
export const variableUpdateNode: FlowNodeTemplateType = {
|
||||
id: FlowNodeTypeEnum.variableUpdate,
|
||||
templateType: FlowNodeTemplateTypeEnum.tools,
|
||||
flowNodeType: FlowNodeTypeEnum.variableUpdate,
|
||||
sourceHandle: getHandleConfig(true, true, true, true),
|
||||
targetHandle: getHandleConfig(true, true, true, true),
|
||||
avatar: '/imgs/workflow/variable.png',
|
||||
name: '变量更新',
|
||||
intro: '可以更新指定节点的输出值和全局变量',
|
||||
showStatus: true,
|
||||
isTool: false,
|
||||
inputs: [
|
||||
{
|
||||
key: NodeInputKeyEnum.updateList,
|
||||
valueType: WorkflowIOValueTypeEnum.any,
|
||||
label: '',
|
||||
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||
editField: {
|
||||
key: true,
|
||||
valueType: true
|
||||
},
|
||||
value: []
|
||||
}
|
||||
],
|
||||
outputs: []
|
||||
};
|
5
packages/global/core/workflow/template/system/variableUpdate/type.d.ts
vendored
Normal file
5
packages/global/core/workflow/template/system/variableUpdate/type.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
export type TUpdateListItem = {
|
||||
variable?: ReferenceValueProps;
|
||||
value?: ReferenceValueProps;
|
||||
renderType?: FlowNodeInputTypeEnum.input | FlowNodeInputTypeEnum.reference;
|
||||
};
|
@@ -42,7 +42,8 @@ import { dispatchLafRequest } from './tools/runLaf';
|
||||
import { dispatchIfElse } from './tools/runIfElse';
|
||||
import { RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
||||
import { getReferenceVariableValue } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { dispatchSystemConfig } from './init/systemConfiig';
|
||||
import { dispatchSystemConfig } from './init/systemConfig';
|
||||
import { dispatchUpdateVariable } from './tools/runUpdateVar';
|
||||
|
||||
const callbackMap: Record<`${FlowNodeTypeEnum}`, Function> = {
|
||||
[FlowNodeTypeEnum.workflowStart]: dispatchWorkflowStart,
|
||||
@@ -62,6 +63,7 @@ const callbackMap: Record<`${FlowNodeTypeEnum}`, Function> = {
|
||||
[FlowNodeTypeEnum.stopTool]: dispatchStopToolCall,
|
||||
[FlowNodeTypeEnum.lafModule]: dispatchLafRequest,
|
||||
[FlowNodeTypeEnum.ifElseNode]: dispatchIfElse,
|
||||
[FlowNodeTypeEnum.variableUpdate]: dispatchUpdateVariable,
|
||||
|
||||
// none
|
||||
[FlowNodeTypeEnum.systemConfig]: dispatchSystemConfig,
|
||||
@@ -355,7 +357,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
if (pluginOutputModule && props.mode !== 'debug') {
|
||||
await nodeRunWithActive(pluginOutputModule);
|
||||
}
|
||||
|
||||
const { userChatInput, ...leftVariables } = variables;
|
||||
return {
|
||||
flowResponses: chatResponses,
|
||||
flowUsages: chatNodeUsages,
|
||||
@@ -366,7 +368,8 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
},
|
||||
[DispatchNodeResponseKeyEnum.assistantResponses]:
|
||||
mergeAssistantResponseAnswerText(chatAssistantResponse),
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: toolRunResponse
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: toolRunResponse,
|
||||
newVariables: leftVariables
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,4 @@
|
||||
export const dispatchSystemConfig = (props: Record<string, any>) => {
|
||||
const { variables } = props;
|
||||
return variables;
|
||||
};
|
@@ -1,10 +0,0 @@
|
||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/type/index.d';
|
||||
export type UserChatInputProps = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.userChatInput]: string;
|
||||
}>;
|
||||
|
||||
export const dispatchSystemConfig = (props: Record<string, any>) => {
|
||||
const { variables } = props as UserChatInputProps;
|
||||
return variables;
|
||||
};
|
@@ -0,0 +1,53 @@
|
||||
import { NodeInputKeyEnum, VARIABLE_NODE_ID } from '@fastgpt/global/core/workflow/constants';
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import { getReferenceVariableValue } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { TUpdateListItem } from '@fastgpt/global/core/workflow/template/system/variableUpdate/type';
|
||||
import { ModuleDispatchProps } from '@fastgpt/global/core/workflow/type';
|
||||
|
||||
type Props = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.updateList]: TUpdateListItem[];
|
||||
}>;
|
||||
|
||||
export const dispatchUpdateVariable = async (
|
||||
props: Props
|
||||
): Promise<DispatchNodeResultType<any>> => {
|
||||
const { params, variables, runtimeNodes } = props;
|
||||
|
||||
const { updateList } = params;
|
||||
updateList.forEach((item) => {
|
||||
const varNodeId = item.variable?.[0];
|
||||
const varKey = item.variable?.[1];
|
||||
|
||||
if (!varNodeId || !varKey) {
|
||||
return;
|
||||
}
|
||||
let value = '';
|
||||
if (!item.value?.[0]) {
|
||||
value = item.value?.[1];
|
||||
} else {
|
||||
value = getReferenceVariableValue({
|
||||
value: item.value,
|
||||
variables,
|
||||
nodes: runtimeNodes
|
||||
});
|
||||
}
|
||||
if (varNodeId === VARIABLE_NODE_ID) {
|
||||
variables[varKey] = value;
|
||||
} else {
|
||||
const node = runtimeNodes.find((node) => node.nodeId === varNodeId);
|
||||
if (node) {
|
||||
const output = node.outputs.find((output) => output.id === varKey);
|
||||
if (output) {
|
||||
output.value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
totalPoints: 0
|
||||
}
|
||||
};
|
||||
};
|
@@ -19,4 +19,5 @@ export type DispatchFlowResponse = {
|
||||
};
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: ToolRunResponseItemType;
|
||||
[DispatchNodeResponseKeyEnum.assistantResponses]: AIChatItemValueItemType[];
|
||||
newVariables: Record<string, string>;
|
||||
};
|
||||
|
@@ -12,4 +12,5 @@ export type DispatchFlowResponse = {
|
||||
flowUsages: ChatNodeUsageType[];
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: ToolRunResponseItemType;
|
||||
[DispatchNodeResponseKeyEnum.assistantResponses]: AIChatItemValueItemType[];
|
||||
newVariables: Record<string, string>;
|
||||
};
|
||||
|
@@ -1050,8 +1050,75 @@
|
||||
"Intro placeholder": "If this plugin is invoked as a tool, this description will be used as the prompt."
|
||||
},
|
||||
"shareChat": {
|
||||
"Init Error": "Conversation initialization failed",
|
||||
"Init History Error": "Chat history initialization failed"
|
||||
"Init Error": "Init Chat Error",
|
||||
"Init History Error": "Init History Error"
|
||||
},
|
||||
"workflow": {
|
||||
"Add variable": "Add",
|
||||
"Can not delete node": "Can not delete the node",
|
||||
"Change input type tip": "Changing the input type will empty the entered values, please confirm!",
|
||||
"Check Failed": "Workflow verification fails. Check whether the node or connection is normal",
|
||||
"Confirm stop debug": "Do you want to terminate debugging? Debugging information is not retained.",
|
||||
"Copy node": "Copy node",
|
||||
"Current workflow": "Current workflow",
|
||||
"Custom inputs": "Inputs",
|
||||
"Custom outputs": "Outputs",
|
||||
"Custom variable": "Custom variable",
|
||||
"Dataset quote": "Dataset quote",
|
||||
"Debug": "Debug",
|
||||
"Debug Node": "Workflow Debug",
|
||||
"Failed": "Running failed",
|
||||
"Not intro": "This node is not introduced",
|
||||
"Run from here": "Run from here",
|
||||
"Run result": "Run result",
|
||||
"Running": "Running",
|
||||
"Skipped": "Skipped",
|
||||
"Stop debug": "Stop",
|
||||
"Success": "Running success",
|
||||
"Value type": "Type",
|
||||
"Variable outputs": "Variables",
|
||||
"chat": {
|
||||
"Quote prompt": "Quote prompt"
|
||||
},
|
||||
"debug": {
|
||||
"Done": "Done",
|
||||
"Hide result": "Hide result",
|
||||
"Not result": "Not result",
|
||||
"Run result": "",
|
||||
"Show result": "Show result"
|
||||
},
|
||||
"inputType": {
|
||||
"JSON Editor": "JSON Editor",
|
||||
"Manual input": "",
|
||||
"Manual select": "Select",
|
||||
"Reference": "Reference",
|
||||
"Required": "Required",
|
||||
"Select edit field": "Editable field",
|
||||
"Select input default value": "Default value",
|
||||
"Select input type": "Configurable input types",
|
||||
"Select input type placeholder": "Please select a configurable input type",
|
||||
"chat history": "History",
|
||||
"dynamicTargetInput": "dynamic Target Input",
|
||||
"input": "Input",
|
||||
"number input": "number input",
|
||||
"selectApp": "App Selector",
|
||||
"selectDataset": "Dataset Selector",
|
||||
"selectLLMModel": "Select Chat Model",
|
||||
"switch": "Switch",
|
||||
"target": "Target Data",
|
||||
"textarea": "Textarea"
|
||||
},
|
||||
"publish": {
|
||||
"OnRevert version": "Click back to that version",
|
||||
"OnRevert version confirm": "Are you sure to roll back the version?",
|
||||
"histories": "Publiish histories"
|
||||
},
|
||||
"tool": {
|
||||
"Handle": "Tool handle",
|
||||
"Select Tool": "Select Tool"
|
||||
},
|
||||
"value": "Value",
|
||||
"variable": "Variable"
|
||||
}
|
||||
},
|
||||
"dataset": {
|
||||
|
@@ -1160,7 +1160,9 @@
|
||||
"tool": {
|
||||
"Handle": "工具连接器",
|
||||
"Select Tool": "选择工具"
|
||||
}
|
||||
},
|
||||
"value": "值",
|
||||
"variable": "变量"
|
||||
}
|
||||
},
|
||||
"dataset": {
|
||||
|
@@ -91,6 +91,7 @@ type Props = OutLinkChatAuthProps & {
|
||||
onStartChat?: (e: StartChatFnProps) => Promise<{
|
||||
responseText: string;
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: ChatHistoryItemResType[];
|
||||
newVariables?: Record<string, any>;
|
||||
isNewChat?: boolean;
|
||||
}>;
|
||||
onDelMessage?: (e: { contentId: string }) => void;
|
||||
@@ -462,6 +463,7 @@ const ChatBox = (
|
||||
const {
|
||||
responseData,
|
||||
responseText,
|
||||
newVariables,
|
||||
isNewChat = false
|
||||
} = await onStartChat({
|
||||
chatList: newChatList,
|
||||
@@ -470,6 +472,7 @@ const ChatBox = (
|
||||
generatingMessage: (e) => generatingMessage({ ...e, autoTTSResponse }),
|
||||
variables
|
||||
});
|
||||
setValue('variables', newVariables || []);
|
||||
|
||||
isNewChatReplace.current = isNewChat;
|
||||
|
||||
@@ -549,6 +552,7 @@ const ChatBox = (
|
||||
resetInputVal,
|
||||
setAudioPlayingChatId,
|
||||
setChatHistories,
|
||||
setValue,
|
||||
splitText2Audio,
|
||||
startSegmentedAudio,
|
||||
t,
|
||||
|
@@ -68,7 +68,7 @@ const ChatTest = (
|
||||
const history = chatList.slice(-historyMaxLen - 2, -2);
|
||||
|
||||
// 流请求,获取数据
|
||||
const { responseText, responseData } = await streamFetch({
|
||||
const { responseText, responseData, newVariables } = await streamFetch({
|
||||
url: '/api/core/chat/chatTest',
|
||||
data: {
|
||||
history,
|
||||
@@ -84,7 +84,7 @@ const ChatTest = (
|
||||
abortCtrl: controller
|
||||
});
|
||||
|
||||
return { responseText, responseData };
|
||||
return { responseText, responseData, newVariables };
|
||||
},
|
||||
[app._id, app.name, edges, nodes]
|
||||
);
|
||||
|
@@ -57,7 +57,8 @@ const nodeTypes: Record<`${FlowNodeTypeEnum}`, any> = {
|
||||
<NodeSimple {...data} minW={'100px'} maxW={'300px'} />
|
||||
),
|
||||
[FlowNodeTypeEnum.lafModule]: dynamic(() => import('./nodes/NodeLaf')),
|
||||
[FlowNodeTypeEnum.ifElseNode]: dynamic(() => import('./nodes/NodeIfElse'))
|
||||
[FlowNodeTypeEnum.ifElseNode]: dynamic(() => import('./nodes/NodeIfElse')),
|
||||
[FlowNodeTypeEnum.variableUpdate]: dynamic(() => import('./nodes/NodeVariableUpdate'))
|
||||
};
|
||||
const edgeTypes = {
|
||||
[EDGE_TYPE]: ButtonEdge
|
||||
|
@@ -0,0 +1,277 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import NodeCard from './render/NodeCard';
|
||||
import { NodeProps } from 'reactflow';
|
||||
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
NumberDecrementStepper,
|
||||
NumberIncrementStepper,
|
||||
NumberInput,
|
||||
NumberInputField,
|
||||
NumberInputStepper,
|
||||
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';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '@/components/core/workflow/context';
|
||||
import {
|
||||
FlowNodeInputMap,
|
||||
FlowNodeInputTypeEnum
|
||||
} from '@fastgpt/global/core/workflow/node/constant';
|
||||
import Container from '../components/Container';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import JsonEditor from '@fastgpt/web/components/common/Textarea/JsonEditor';
|
||||
import { SmallAddIcon } from '@chakra-ui/icons';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import { ReferenceValueProps } from '@fastgpt/global/core/workflow/type/io';
|
||||
import { ReferSelector, useReference } from './render/RenderInput/templates/Reference';
|
||||
|
||||
const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
const { inputs = [], nodeId } = data;
|
||||
const { t } = useTranslation();
|
||||
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
const nodes = useContextSelector(WorkflowContext, (v) => v.nodes);
|
||||
|
||||
const updateList = useMemo(
|
||||
() =>
|
||||
(inputs.find((input) => input.key === NodeInputKeyEnum.updateList)
|
||||
?.value as TUpdateListItem[]) || [],
|
||||
[inputs]
|
||||
);
|
||||
|
||||
const onUpdateList = useCallback(
|
||||
(value: TUpdateListItem[]) => {
|
||||
const updateListInput = inputs.find((input) => input.key === NodeInputKeyEnum.updateList);
|
||||
if (!updateListInput) return;
|
||||
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'updateInput',
|
||||
key: NodeInputKeyEnum.updateList,
|
||||
value: {
|
||||
...updateListInput,
|
||||
value
|
||||
}
|
||||
});
|
||||
},
|
||||
[inputs, nodeId, onChangeNode]
|
||||
);
|
||||
|
||||
const menuList = [
|
||||
{
|
||||
renderType: FlowNodeInputTypeEnum.input,
|
||||
icon: FlowNodeInputMap[FlowNodeInputTypeEnum.input].icon,
|
||||
label: t('core.workflow.inputType.Manual input')
|
||||
},
|
||||
{
|
||||
renderType: FlowNodeInputTypeEnum.reference,
|
||||
icon: FlowNodeInputMap[FlowNodeInputTypeEnum.reference].icon,
|
||||
label: t('core.workflow.inputType.Reference')
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<NodeCard selected={selected} maxW={'1000px'} {...data}>
|
||||
<Box px={4} pb={4}>
|
||||
{updateList.map((updateItem, index) => {
|
||||
const type = (() => {
|
||||
const variable = updateItem.variable;
|
||||
const variableNodeId = variable?.[0];
|
||||
const variableNode = nodes.find((node) => node.id === variableNodeId);
|
||||
if (!variableNode) return 'any';
|
||||
const variableInput = variableNode.data.outputs.find(
|
||||
(output) => output.id === variable?.[1]
|
||||
);
|
||||
if (!variableInput) return 'any';
|
||||
return variableInput.valueType;
|
||||
})();
|
||||
|
||||
const renderTypeData = menuList.find((item) => item.renderType === updateItem.renderType);
|
||||
const handleUpdate = (newValue: any) => {
|
||||
onUpdateList(
|
||||
updateList.map((update, i) =>
|
||||
i === index ? { ...update, value: ['', newValue] } : update
|
||||
)
|
||||
);
|
||||
};
|
||||
return (
|
||||
<Flex key={index}>
|
||||
<Container mt={4}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Flex w={'60px'}>{t('core.workflow.variable')}</Flex>
|
||||
<Reference
|
||||
nodeId={nodeId}
|
||||
variable={updateItem.variable}
|
||||
onSelect={(value) => {
|
||||
onUpdateList(
|
||||
updateList.map((update, i) => {
|
||||
if (i === index) {
|
||||
return {
|
||||
...update,
|
||||
variable: value
|
||||
};
|
||||
}
|
||||
return update;
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Box flex={1} />
|
||||
<MyIcon
|
||||
className="delete"
|
||||
name={'delete'}
|
||||
w={'14px'}
|
||||
color={'myGray.600'}
|
||||
cursor={'pointer'}
|
||||
ml={2}
|
||||
_hover={{ color: 'red.500' }}
|
||||
onClick={() => {
|
||||
onUpdateList(updateList.filter((_, i) => i !== index));
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex mt={2} w={'full'} alignItems={'center'}>
|
||||
<Flex w={'60px'} flex={0}>
|
||||
<Box>{t('core.workflow.value')}</Box>
|
||||
<MyTooltip
|
||||
label={
|
||||
menuList.find((item) => item.renderType === updateItem.renderType)?.label
|
||||
}
|
||||
>
|
||||
<Button
|
||||
size={'xs'}
|
||||
bg={'white'}
|
||||
borderRadius={'xs'}
|
||||
mx={2}
|
||||
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>
|
||||
{updateItem.renderType === FlowNodeInputTypeEnum.reference ? (
|
||||
<Reference
|
||||
nodeId={nodeId}
|
||||
variable={updateItem.value}
|
||||
onSelect={handleUpdate}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
{type === 'string' && (
|
||||
<Textarea
|
||||
bg="white"
|
||||
value={updateItem.value?.[1] || ''}
|
||||
w="300px"
|
||||
onChange={(e) => handleUpdate(e.target.value)}
|
||||
/>
|
||||
)}
|
||||
{type === 'number' && (
|
||||
<NumberInput value={Number(updateItem.value?.[1]) || 0}>
|
||||
<NumberInputField
|
||||
bg="white"
|
||||
onChange={(e) => handleUpdate(e.target.value)}
|
||||
/>
|
||||
<NumberInputStepper>
|
||||
<NumberIncrementStepper />
|
||||
<NumberDecrementStepper />
|
||||
</NumberInputStepper>
|
||||
</NumberInput>
|
||||
)}
|
||||
{type === 'boolean' && (
|
||||
<Switch
|
||||
size="lg"
|
||||
defaultChecked={updateItem.value?.[1] === 'true'}
|
||||
onChange={(e) => handleUpdate(String(e.target.checked))}
|
||||
/>
|
||||
)}
|
||||
{type !== 'string' && type !== 'number' && type !== 'boolean' && (
|
||||
<JsonEditor
|
||||
bg="white"
|
||||
resize
|
||||
w="300px"
|
||||
value={updateItem.value?.[1] || ''}
|
||||
onChange={(e) => handleUpdate(e)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
</Container>
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
<Flex className="nodrag" cursor={'default'} alignItems={'center'} position={'relative'}>
|
||||
<Button
|
||||
variant={'whiteBase'}
|
||||
leftIcon={<SmallAddIcon />}
|
||||
iconSpacing={1}
|
||||
w={'full'}
|
||||
size={'sm'}
|
||||
onClick={() => {
|
||||
onUpdateList([
|
||||
...updateList,
|
||||
{
|
||||
variable: ['', ''],
|
||||
value: ['', ''],
|
||||
renderType: FlowNodeInputTypeEnum.input
|
||||
}
|
||||
]);
|
||||
}}
|
||||
>
|
||||
{t('common.Add New')}
|
||||
</Button>
|
||||
</Flex>
|
||||
</Box>
|
||||
</NodeCard>
|
||||
);
|
||||
};
|
||||
export default React.memo(NodeVariableUpdate);
|
||||
|
||||
const Reference = ({
|
||||
nodeId,
|
||||
variable,
|
||||
onSelect
|
||||
}: {
|
||||
nodeId: string;
|
||||
variable?: ReferenceValueProps;
|
||||
onSelect: (e: ReferenceValueProps) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { referenceList, formatValue } = useReference({
|
||||
nodeId,
|
||||
valueType: WorkflowIOValueTypeEnum.any,
|
||||
value: variable
|
||||
});
|
||||
|
||||
return (
|
||||
<ReferSelector
|
||||
placeholder={t('选择引用变量')}
|
||||
list={referenceList}
|
||||
value={formatValue}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
);
|
||||
};
|
@@ -69,7 +69,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
const { text, files } = chatValue2RuntimePrompt(prompt);
|
||||
|
||||
/* start process */
|
||||
const { flowResponses, flowUsages } = await dispatchWorkFlow({
|
||||
const { flowResponses, flowUsages, newVariables } = await dispatchWorkFlow({
|
||||
res,
|
||||
mode: 'test',
|
||||
teamId,
|
||||
@@ -99,6 +99,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
event: SseResponseEventEnum.flowResponses,
|
||||
data: JSON.stringify(flowResponses)
|
||||
});
|
||||
responseWrite({
|
||||
res,
|
||||
event: SseResponseEventEnum.variables,
|
||||
data: JSON.stringify(newVariables)
|
||||
});
|
||||
res.end();
|
||||
|
||||
pushChatUsage({
|
||||
|
@@ -179,7 +179,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const responseChatItemId: string | undefined = messages[messages.length - 1].dataId;
|
||||
|
||||
/* start flow controller */
|
||||
const { flowResponses, flowUsages, assistantResponses } = await (async () => {
|
||||
const { flowResponses, flowUsages, assistantResponses, newVariables } = await (async () => {
|
||||
if (app.version === 'v2') {
|
||||
return dispatchWorkFlow({
|
||||
res,
|
||||
@@ -247,7 +247,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
appId: app._id,
|
||||
teamId,
|
||||
tmbId: tmbId,
|
||||
variables,
|
||||
variables: newVariables,
|
||||
isUpdateUseTime: isOwnerUse && source === ChatSourceEnum.online, // owner update use time
|
||||
shareId,
|
||||
outLinkUid: outLinkUserId,
|
||||
@@ -288,6 +288,11 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
event: detail ? SseResponseEventEnum.answer : undefined,
|
||||
data: '[DONE]'
|
||||
});
|
||||
responseWrite({
|
||||
res,
|
||||
event: SseResponseEventEnum.variables,
|
||||
data: JSON.stringify(newVariables)
|
||||
});
|
||||
|
||||
if (responseDetail && detail) {
|
||||
responseWrite({
|
||||
|
@@ -74,7 +74,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
const prompts = messages.slice(-2);
|
||||
const completionChatId = chatId ? chatId : nanoid();
|
||||
|
||||
const { responseText, responseData } = await streamFetch({
|
||||
const { responseText, responseData, newVariables } = await streamFetch({
|
||||
data: {
|
||||
messages: prompts,
|
||||
variables,
|
||||
@@ -123,7 +123,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
history: ChatBoxRef.current?.getChatHistories() || state.history
|
||||
}));
|
||||
|
||||
return { responseText, responseData, isNewChat: forbidRefresh.current };
|
||||
return { responseText, responseData, isNewChat: forbidRefresh.current, newVariables };
|
||||
},
|
||||
[appId, chatId, histories, pushHistory, router, setChatData, updateHistory]
|
||||
);
|
||||
|
@@ -95,7 +95,7 @@ const OutLink = ({
|
||||
'*'
|
||||
);
|
||||
|
||||
const { responseText, responseData } = await streamFetch({
|
||||
const { responseText, responseData, newVariables } = await streamFetch({
|
||||
data: {
|
||||
messages: prompts,
|
||||
variables: {
|
||||
@@ -169,7 +169,7 @@ const OutLink = ({
|
||||
'*'
|
||||
);
|
||||
|
||||
return { responseText, responseData, isNewChat: forbidRefresh.current };
|
||||
return { responseText, responseData, isNewChat: forbidRefresh.current, newVariables };
|
||||
},
|
||||
[
|
||||
chatId,
|
||||
|
@@ -79,7 +79,7 @@ const OutLink = () => {
|
||||
const prompts = messages.slice(-2);
|
||||
const completionChatId = chatId ? chatId : nanoid();
|
||||
|
||||
const { responseText, responseData } = await streamFetch({
|
||||
const { responseText, responseData, newVariables } = await streamFetch({
|
||||
data: {
|
||||
messages: prompts,
|
||||
variables: {
|
||||
@@ -135,7 +135,7 @@ const OutLink = () => {
|
||||
history: ChatBoxRef.current?.getChatHistories() || state.history
|
||||
}));
|
||||
|
||||
return { responseText, responseData, isNewChat: forbidRefresh.current };
|
||||
return { responseText, responseData, isNewChat: forbidRefresh.current, newVariables };
|
||||
},
|
||||
[appId, teamToken, chatId, histories, pushHistory, router, setChatData, teamId, updateHistory]
|
||||
);
|
||||
|
@@ -69,6 +69,7 @@ export async function saveChat({
|
||||
chat.title = title;
|
||||
chat.updateTime = new Date();
|
||||
chat.metadata = metadataUpdate;
|
||||
chat.variables = variables || {};
|
||||
await chat.save({ session });
|
||||
} else {
|
||||
await MongoChat.create(
|
||||
|
@@ -22,6 +22,7 @@ type StreamFetchProps = {
|
||||
type StreamResponseType = {
|
||||
responseText: string;
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: ChatHistoryItemResType[];
|
||||
newVariables: Record<string, any>;
|
||||
};
|
||||
class FatalError extends Error {}
|
||||
|
||||
@@ -50,6 +51,7 @@ export const streamFetch = ({
|
||||
)[] = [];
|
||||
let errMsg: string | undefined;
|
||||
let responseData: ChatHistoryItemResType[] = [];
|
||||
let newVariables: Record<string, any> = {};
|
||||
let finished = false;
|
||||
|
||||
const finish = () => {
|
||||
@@ -57,6 +59,7 @@ export const streamFetch = ({
|
||||
return failedFinish();
|
||||
}
|
||||
return resolve({
|
||||
newVariables,
|
||||
responseText,
|
||||
responseData
|
||||
});
|
||||
@@ -198,6 +201,8 @@ export const streamFetch = ({
|
||||
});
|
||||
} else if (event === SseResponseEventEnum.flowResponses && Array.isArray(parseJson)) {
|
||||
responseData = parseJson;
|
||||
} else if (event === SseResponseEventEnum.variables) {
|
||||
newVariables = parseJson;
|
||||
} else if (event === SseResponseEventEnum.error) {
|
||||
if (parseJson.statusText === TeamErrEnum.aiPointsNotEnough) {
|
||||
useSystemStore.getState().setIsNotSufficientModal(true);
|
||||
|
Reference in New Issue
Block a user