diff --git a/web/app/components/base/prompt-editor/plugins/memory-popup-plugin/index.tsx b/web/app/components/base/prompt-editor/plugins/memory-popup-plugin/index.tsx index 67f146420e..aeb349e4c8 100644 --- a/web/app/components/base/prompt-editor/plugins/memory-popup-plugin/index.tsx +++ b/web/app/components/base/prompt-editor/plugins/memory-popup-plugin/index.tsx @@ -212,10 +212,13 @@ export default function MemoryPopupPlugin({
{memoryVarInNode.map(variable => ( -
handleSelectVariable(['conversation', variable.name])}> +
handleSelectVariable(['memory_block', variable.id])} + >
{variable.name}
@@ -233,10 +236,13 @@ export default function MemoryPopupPlugin({
{memoryVarInApp.map(variable => ( -
handleSelectVariable(['conversation', variable.name])}> +
handleSelectVariable(['memory_block', variable.id])} + >
{variable.name}
diff --git a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/component.tsx b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/component.tsx index 72eb990dd3..e977296bd6 100644 --- a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/component.tsx +++ b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/component.tsx @@ -18,7 +18,7 @@ import { DELETE_WORKFLOW_VARIABLE_BLOCK_COMMAND, UPDATE_WORKFLOW_NODES_MAP, } from './index' -import { isConversationVar, isENV, isRagVariableVar, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' +import { isConversationVar as isConversationVariable, isENV, isMemoryVariable, isRagVariableVar, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' import Tooltip from '@/app/components/base/tooltip' import { isExceptionVariable } from '@/app/components/workflow/utils' import VarFullPathPanel from '@/app/components/workflow/nodes/_base/components/variable/var-full-path-panel' @@ -34,6 +34,7 @@ type WorkflowVariableBlockComponentProps = { workflowNodesMap: WorkflowNodesMap environmentVariables?: Var[] conversationVariables?: Var[] + memoryVariables?: Var[] ragVariables?: Var[] getVarType?: (payload: { nodeId: string, @@ -48,6 +49,7 @@ const WorkflowVariableBlockComponent = ({ getVarType, environmentVariables, conversationVariables, + memoryVariables, ragVariables, }: WorkflowVariableBlockComponentProps) => { const { t } = useTranslation() @@ -66,18 +68,23 @@ const WorkflowVariableBlockComponent = ({ const [localWorkflowNodesMap, setLocalWorkflowNodesMap] = useState(workflowNodesMap) const node = localWorkflowNodesMap![variables[isRagVar ? 1 : 0]] const isEnv = isENV(variables) - // const isChatVar = isConversationVar(variables) && conversationVariables?.some(v => v.variable === `${variables?.[0] ?? ''}.${variables?.[1] ?? ''}` && v.type !== 'memory') - const isMemoryVar = isConversationVar(variables) && conversationVariables?.some(v => v.variable === `${variables?.[0] ?? ''}.${variables?.[1] ?? ''}` && v.type === 'memory') + const isConversationVar = isConversationVariable(variables) + const isMemoryVar = isMemoryVariable(variables) const isException = isExceptionVariable(varName, node?.type) + let variableValid = true if (isEnv) { if (environmentVariables) variableValid = environmentVariables.some(v => v.variable === `${variables?.[0] ?? ''}.${variables?.[1] ?? ''}`) } - else if (isConversationVar(variables)) { + else if (isConversationVar) { if (conversationVariables) variableValid = conversationVariables.some(v => v.variable === `${variables?.[0] ?? ''}.${variables?.[1] ?? ''}`) } + else if (isMemoryVar) { + if (memoryVariables) + variableValid = memoryVariables.some(v => v.variable === `${variables?.[0] ?? ''}.${variables?.[1] ?? ''}`) + } else if (isRagVar) { if (ragVariables) variableValid = ragVariables.some(v => v.variable === `${variables?.[0] ?? ''}.${variables?.[1] ?? ''}.${variables?.[2] ?? ''}`) @@ -136,7 +143,6 @@ const WorkflowVariableBlockComponent = ({ handleVariableJump() }} isExceptionVariable={isException} - isMemoryVariable={isMemoryVar} errorMsg={!variableValid ? t('workflow.errorMsg.invalidVariable') : undefined} isSelected={isSelected} ref={ref} diff --git a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/node.tsx b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/node.tsx index bdecb21034..afdf7844ba 100644 --- a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/node.tsx +++ b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/node.tsx @@ -22,6 +22,7 @@ export class WorkflowVariableBlockNode extends DecoratorNode __getVarType?: GetVarType __environmentVariables?: Var[] __conversationVariables?: Var[] + __memoryVariables?: Var[] __ragVariables?: Var[] static getType(): string { @@ -36,7 +37,16 @@ export class WorkflowVariableBlockNode extends DecoratorNode return true } - constructor(variables: string[], workflowNodesMap: WorkflowNodesMap, getVarType: any, key?: NodeKey, environmentVariables?: Var[], conversationVariables?: Var[], ragVariables?: Var[]) { + constructor( + variables: string[], + workflowNodesMap: WorkflowNodesMap, + getVarType: any, + key?: NodeKey, + environmentVariables?: Var[], + conversationVariables?: Var[], + memoryVariables?: Var[], + ragVariables?: Var[], + ) { super(key) this.__variables = variables @@ -44,6 +54,7 @@ export class WorkflowVariableBlockNode extends DecoratorNode this.__getVarType = getVarType this.__environmentVariables = environmentVariables this.__conversationVariables = conversationVariables + this.__memoryVariables = memoryVariables this.__ragVariables = ragVariables } @@ -66,6 +77,7 @@ export class WorkflowVariableBlockNode extends DecoratorNode getVarType={this.__getVarType!} environmentVariables={this.__environmentVariables} conversationVariables={this.__conversationVariables} + memoryVariables={this.__memoryVariables} ragVariables={this.__ragVariables} /> ) @@ -115,6 +127,11 @@ export class WorkflowVariableBlockNode extends DecoratorNode return self.__conversationVariables } + getMemoryVariables(): any { + const self = this.getLatest() + return self.__memoryVariables + } + getRagVariables(): any { const self = this.getLatest() return self.__ragVariables @@ -124,8 +141,8 @@ export class WorkflowVariableBlockNode extends DecoratorNode return `{{#${this.getVariables().join('.')}#}}` } } -export function $createWorkflowVariableBlockNode(variables: string[], workflowNodesMap: WorkflowNodesMap, getVarType?: GetVarType, environmentVariables?: Var[], conversationVariables?: Var[], ragVariables?: Var[]): WorkflowVariableBlockNode { - return new WorkflowVariableBlockNode(variables, workflowNodesMap, getVarType, undefined, environmentVariables, conversationVariables, ragVariables) +export function $createWorkflowVariableBlockNode(variables: string[], workflowNodesMap: WorkflowNodesMap, getVarType?: GetVarType, environmentVariables?: Var[], conversationVariables?: Var[], memoryVariables?: Var[], ragVariables?: Var[]): WorkflowVariableBlockNode { + return new WorkflowVariableBlockNode(variables, workflowNodesMap, getVarType, undefined, environmentVariables, conversationVariables, memoryVariables, ragVariables) } export function $isWorkflowVariableBlockNode( diff --git a/web/app/components/workflow-app/components/workflow-main.tsx b/web/app/components/workflow-app/components/workflow-main.tsx index f639eda08d..2e953f12d8 100644 --- a/web/app/components/workflow-app/components/workflow-main.tsx +++ b/web/app/components/workflow-app/components/workflow-main.tsx @@ -53,7 +53,7 @@ const WorkflowMain = ({ } if (memory_blocks) { const { setMemoryVariables } = workflowStore.getState() - setMemoryVariables(formatMemoryVariables(memory_blocks, nodes)) + setMemoryVariables(formatMemoryVariables(memory_blocks)) } }, [featuresStore, workflowStore, formatMemoryVariables]) diff --git a/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts b/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts index 853dfb907c..174debb982 100644 --- a/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts +++ b/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts @@ -85,7 +85,7 @@ export const useNodesSyncDraft = () => { }, environment_variables: environmentVariables, conversation_variables: conversationVariables, - memory_blocks: memoryVariables.map(({ node, value_type, more, model, ...rest }) => { + memory_blocks: memoryVariables.map(({ value_type, more, model, ...rest }) => { return { ...rest, model: model ? { diff --git a/web/app/components/workflow-app/hooks/use-workflow-init.ts b/web/app/components/workflow-app/hooks/use-workflow-init.ts index 70049a3380..6defb6e4f5 100644 --- a/web/app/components/workflow-app/hooks/use-workflow-init.ts +++ b/web/app/components/workflow-app/hooks/use-workflow-init.ts @@ -55,7 +55,7 @@ export const useWorkflowInit = () => { }, {} as Record), environmentVariables: res.environment_variables?.map(env => env.value_type === 'secret' ? { ...env, value: '[__HIDDEN__]' } : env) || [], conversationVariables: res.conversation_variables || [], - memoryVariables: formatMemoryVariables((res.memory_blocks || []), res.graph.nodes), + memoryVariables: formatMemoryVariables((res.memory_blocks || [])), }) setSyncWorkflowDraftHash(res.hash) setIsLoading(false) diff --git a/web/app/components/workflow-app/hooks/use-workflow-refresh-draft.ts b/web/app/components/workflow-app/hooks/use-workflow-refresh-draft.ts index 110f96fee9..b26ee0d45d 100644 --- a/web/app/components/workflow-app/hooks/use-workflow-refresh-draft.ts +++ b/web/app/components/workflow-app/hooks/use-workflow-refresh-draft.ts @@ -30,7 +30,7 @@ export const useWorkflowRefreshDraft = () => { }, {} as Record)) setEnvironmentVariables(response.environment_variables?.map(env => env.value_type === 'secret' ? { ...env, value: '[__HIDDEN__]' } : env) || []) setConversationVariables(response.conversation_variables || []) - setMemoryVariables(formatMemoryVariables((response.memory_blocks || []), response.graph.nodes)) + setMemoryVariables(formatMemoryVariables((response.memory_blocks || []))) }).finally(() => setIsSyncingWorkflowDraft(false)) }, [handleUpdateWorkflowCanvas, workflowStore, formatMemoryVariables]) diff --git a/web/app/components/workflow/hooks/use-memory-variable.ts b/web/app/components/workflow/hooks/use-memory-variable.ts index 5b4b4d523f..edd913dda3 100644 --- a/web/app/components/workflow/hooks/use-memory-variable.ts +++ b/web/app/components/workflow/hooks/use-memory-variable.ts @@ -1,82 +1,29 @@ import { useCallback } from 'react' -import { useStoreApi } from 'reactflow' -import produce from 'immer' import { useStore, useWorkflowStore, } from '@/app/components/workflow/store' -import { BlockEnum } from '@/app/components/workflow/types' import { ChatVarType } from '@/app/components/workflow/panel/chat-variable-panel/type' -import type { MemoryVariable, Node } from '@/app/components/workflow/types' -import type { LLMNodeType } from '@/app/components/workflow/nodes/llm/types' +import type { MemoryVariable } from '@/app/components/workflow/types' export const useMemoryVariable = () => { const workflowStore = useWorkflowStore() const setMemoryVariables = useStore(s => s.setMemoryVariables) - const store = useStoreApi() - - const handleAddMemoryVariableToNode = useCallback((nodeId: string, memoryVariableId: string) => { - const { getNodes, setNodes } = store.getState() - const nodes = getNodes() - const newNodes = produce(nodes, (draft) => { - const currentNode = draft.find(n => n.id === nodeId) - if (currentNode) { - currentNode.data.memory = { - ...(currentNode.data.memory || {}), - block_id: [...(currentNode.data.memory?.block_id || []), memoryVariableId], - } - } - }) - setNodes(newNodes) - }, [store]) - - const handleDeleteMemoryVariableFromNode = useCallback((nodeId: string, memoryVariableId: string) => { - const { getNodes, setNodes } = store.getState() - const nodes = getNodes() - const newNodes = produce(nodes, (draft) => { - const currentNode = draft.find(n => n.id === nodeId) - if (currentNode) { - currentNode.data.memory = { - ...(currentNode.data.memory || {}), - block_id: currentNode.data.memory?.block_id?.filter((id: string) => id !== memoryVariableId) || [], - } - } - }) - setNodes(newNodes) - }, [store]) const handleAddMemoryVariable = useCallback((memoryVariable: MemoryVariable) => { const { memoryVariables } = workflowStore.getState() setMemoryVariables([memoryVariable, ...memoryVariables]) - - if (memoryVariable.node) - handleAddMemoryVariableToNode(memoryVariable.node, memoryVariable.id) - }, [setMemoryVariables, workflowStore, handleAddMemoryVariableToNode]) + }, [setMemoryVariables, workflowStore]) const handleUpdateMemoryVariable = useCallback((memoryVariable: MemoryVariable) => { const { memoryVariables } = workflowStore.getState() - const oldMemoryVariable = memoryVariables.find(v => v.id === memoryVariable.id) setMemoryVariables(memoryVariables.map(v => v.id === memoryVariable.id ? memoryVariable : v)) - - if (oldMemoryVariable && !oldMemoryVariable?.node && memoryVariable.node) { - handleAddMemoryVariableToNode(memoryVariable.node, memoryVariable.id) - } - else if (oldMemoryVariable && oldMemoryVariable.node && !memoryVariable.node) { - handleDeleteMemoryVariableFromNode(oldMemoryVariable.node, memoryVariable.id) - } - else if (oldMemoryVariable && oldMemoryVariable.node && memoryVariable.node && memoryVariable.node !== oldMemoryVariable.node) { - handleDeleteMemoryVariableFromNode(oldMemoryVariable.node, memoryVariable.id) - handleAddMemoryVariableToNode(memoryVariable.node, memoryVariable.id) - } - }, [setMemoryVariables, workflowStore, handleAddMemoryVariableToNode, handleDeleteMemoryVariableFromNode]) + }, [setMemoryVariables, workflowStore]) const handleDeleteMemoryVariable = useCallback((memoryVariable: MemoryVariable) => { const { memoryVariables } = workflowStore.getState() setMemoryVariables(memoryVariables.filter(v => v.id !== memoryVariable.id)) - - if (memoryVariable.node) - handleDeleteMemoryVariableFromNode(memoryVariable.node, memoryVariable.id) - }, [setMemoryVariables, workflowStore, handleDeleteMemoryVariableFromNode]) + }, [setMemoryVariables, workflowStore]) return { handleAddMemoryVariable, @@ -86,32 +33,8 @@ export const useMemoryVariable = () => { } export const useFormatMemoryVariables = () => { - const formatMemoryVariables = useCallback((memoryVariables: MemoryVariable[], nodes: Node[]) => { - let clonedMemoryVariables = [...memoryVariables] - const nodeScopeMemoryVariablesIds = clonedMemoryVariables.filter(v => v.scope === 'node').map(v => v.id) - const nodeScopeMemoryVariablesMap = nodeScopeMemoryVariablesIds.reduce((acc, id) => { - acc[id] = id - return acc - }, {} as Record) - - if (!!nodeScopeMemoryVariablesIds.length) { - const llmNodes = nodes.filter(n => n.data.type === BlockEnum.LLM) - - clonedMemoryVariables = clonedMemoryVariables.map((v) => { - if (nodeScopeMemoryVariablesMap[v.id]) { - const node = llmNodes.find(n => ((n.data as LLMNodeType).memory?.block_id || []).includes(v.id)) - - return { - ...v, - node: node?.id, - } - } - - return v - }) - } - - return clonedMemoryVariables.map((v) => { + const formatMemoryVariables = useCallback((memoryVariables: MemoryVariable[]) => { + return memoryVariables.map((v) => { return { ...v, value_type: ChatVarType.Memory, diff --git a/web/app/components/workflow/nodes/_base/components/variable/utils.ts b/web/app/components/workflow/nodes/_base/components/variable/utils.ts index 29195377af..ddffcff4e7 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/utils.ts +++ b/web/app/components/workflow/nodes/_base/components/variable/utils.ts @@ -72,6 +72,10 @@ export const isConversationVar = (valueSelector: ValueSelector) => { return valueSelector[0] === 'conversation' } +export const isMemoryVariable = (valueSelector: ValueSelector) => { + return valueSelector[0] === 'memory_block' +} + export const isRagVariableVar = (valueSelector: ValueSelector) => { if (!valueSelector) return false return valueSelector[0] === 'rag' diff --git a/web/app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-icon.tsx b/web/app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-icon.tsx index aa78eae341..93f47f794a 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-icon.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-icon.tsx @@ -7,15 +7,13 @@ export type VariableIconProps = { className?: string variables?: string[] variableCategory?: VarInInspectType | string - isMemoryVariable?: boolean } const VariableIcon = ({ className, variables = [], variableCategory, - isMemoryVariable, }: VariableIconProps) => { - const VarIcon = useVarIcon(variables, variableCategory, isMemoryVariable) + const VarIcon = useVarIcon(variables, variableCategory) return VarIcon && ( { +export const useVarIcon = (variables: string[], variableCategory?: VarInInspectType | string) => { if (variableCategory === 'loop') return Loop @@ -21,7 +22,7 @@ export const useVarIcon = (variables: string[], variableCategory?: VarInInspectT if (isENV(variables) || variableCategory === VarInInspectType.environment || variableCategory === 'environment') return Env - if (isMemoryVariable) + if (isMemoryVariable(variables) || variableCategory === VarInInspectType.memory || variableCategory === 'memory_block') return Memory if (isConversationVar(variables) || variableCategory === VarInInspectType.conversation || variableCategory === 'conversation') @@ -44,6 +45,9 @@ export const useVarColor = (variables: string[], isExceptionVariable?: boolean, if (isConversationVar(variables) || variableCategory === VarInInspectType.conversation || variableCategory === 'conversation') return 'text-util-colors-teal-teal-700' + if (isMemoryVariable(variables) || variableCategory === VarInInspectType.memory || variableCategory === 'memory_block') + return 'text-util-colors-teal-teal-700' + return 'text-text-accent' }, [variables, isExceptionVariable, variableCategory]) } @@ -92,6 +96,15 @@ export const useVarBgColorInEditor = (variables: string[], hasError?: boolean) = } } + if (isMemoryVariable(variables)) { + return { + hoverBorderColor: 'hover:border-util-colors-teal-teal-100', + hoverBgColor: 'hover:bg-util-colors-teal-teal-50', + selectedBorderColor: 'border-util-colors-teal-teal-600', + selectedBgColor: 'bg-util-colors-teal-teal-50', + } + } + return { hoverBorderColor: 'hover:border-state-accent-alt', hoverBgColor: 'hover:bg-state-accent-hover', diff --git a/web/app/components/workflow/nodes/llm/components/memory-system/block-memory.tsx b/web/app/components/workflow/nodes/llm/components/memory-system/block-memory.tsx index 8f50a683ce..f05855e3ef 100644 --- a/web/app/components/workflow/nodes/llm/components/memory-system/block-memory.tsx +++ b/web/app/components/workflow/nodes/llm/components/memory-system/block-memory.tsx @@ -15,12 +15,12 @@ import Confirm from '@/app/components/base/confirm' import { Memory as MemoryIcon } from '@/app/components/base/icons/src/vender/line/others' type BlockMemoryProps = { + id: string payload: Memory } -const BlockMemory = ({ payload }: BlockMemoryProps) => { +const BlockMemory = ({ id }: BlockMemoryProps) => { const { t } = useTranslation() - const { block_id } = payload - const { memoryVariablesInUsed } = useMemoryVariables(block_id || []) + const { memoryVariablesInUsed } = useMemoryVariables(id) const [showConfirm, setShowConfirm] = useState<{ title: string desc: string diff --git a/web/app/components/workflow/nodes/llm/components/memory-system/hooks/use-memory-variables.ts b/web/app/components/workflow/nodes/llm/components/memory-system/hooks/use-memory-variables.ts index df416ae640..4b303aeda4 100644 --- a/web/app/components/workflow/nodes/llm/components/memory-system/hooks/use-memory-variables.ts +++ b/web/app/components/workflow/nodes/llm/components/memory-system/hooks/use-memory-variables.ts @@ -1,12 +1,12 @@ import { useMemo } from 'react' import { useStore } from '@/app/components/workflow/store' -export const useMemoryVariables = (blockIds: string[]) => { +export const useMemoryVariables = (nodeId: string) => { const memoryVariables = useStore(s => s.memoryVariables) const memoryVariablesInUsed = useMemo(() => { - return memoryVariables.filter(variable => blockIds.includes(variable.id)) - }, [memoryVariables, blockIds]) + return memoryVariables.filter(variable => variable.node_id === nodeId) + }, [memoryVariables, nodeId]) const handleDelete = (blockId: string) => { console.log('delete', blockId) diff --git a/web/app/components/workflow/nodes/llm/components/memory-system/index.tsx b/web/app/components/workflow/nodes/llm/components/memory-system/index.tsx index 4eb11e3785..d4c8f5506f 100644 --- a/web/app/components/workflow/nodes/llm/components/memory-system/index.tsx +++ b/web/app/components/workflow/nodes/llm/components/memory-system/index.tsx @@ -92,7 +92,7 @@ const MemorySystem = ({ } { memoryType === MemoryMode.block && !collapsed && ( - + ) } diff --git a/web/app/components/workflow/panel/chat-variable-panel/hooks/use-form/use-memory-schema.ts b/web/app/components/workflow/panel/chat-variable-panel/hooks/use-form/use-memory-schema.ts index 8bf1fe73d2..5763ebb72c 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/hooks/use-form/use-memory-schema.ts +++ b/web/app/components/workflow/panel/chat-variable-panel/hooks/use-form/use-memory-schema.ts @@ -118,7 +118,7 @@ export const useMemorySchema = () => { }, }, { - name: 'node', + name: 'node_id', label: 'Node', type: FormTypeEnum.nodeSelector, fieldClassName: 'flex justify-between', diff --git a/web/app/components/workflow/types.ts b/web/app/components/workflow/types.ts index 357fb1511e..a39426df57 100644 --- a/web/app/components/workflow/types.ts +++ b/web/app/components/workflow/types.ts @@ -176,10 +176,10 @@ export type MemoryVariable = { strategy?: string update_turns?: number scope?: string + node_id?: string term?: string end_user_editable?: boolean value_type: ChatVarType - node?: string more?: boolean } diff --git a/web/app/components/workflow/update-dsl-modal.tsx b/web/app/components/workflow/update-dsl-modal.tsx index d63cc4d8f5..29e53fa065 100644 --- a/web/app/components/workflow/update-dsl-modal.tsx +++ b/web/app/components/workflow/update-dsl-modal.tsx @@ -130,7 +130,7 @@ const UpdateDSLModal = ({ hash, conversation_variables: conversation_variables || [], environment_variables: environment_variables || [], - memory_blocks: formatMemoryVariables(memory_blocks || [], formattedNodes), + memory_blocks: formatMemoryVariables(memory_blocks || []), }, } as any) }, [eventEmitter, formatMemoryVariables]) diff --git a/web/types/workflow.ts b/web/types/workflow.ts index a32b6015bb..12f7763d5f 100644 --- a/web/types/workflow.ts +++ b/web/types/workflow.ts @@ -395,6 +395,7 @@ export enum VarInInspectType { environment = 'env', node = 'node', system = 'sys', + memory = 'memory_block', } export type FullContent = {