diff --git a/web/app/components/workflow/nodes/_base/components/prompt/editor.tsx b/web/app/components/workflow/nodes/_base/components/prompt/editor.tsx index c59a6928d9..5ee8484f58 100644 --- a/web/app/components/workflow/nodes/_base/components/prompt/editor.tsx +++ b/web/app/components/workflow/nodes/_base/components/prompt/editor.tsx @@ -341,7 +341,7 @@ const Editor: FC = ({ - {isMemorySupported && } + {isMemorySupported && } ) 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 f05855e3ef..fe48f3ccee 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 @@ -13,6 +13,7 @@ import ActionButton from '@/app/components/base/action-button' import { useMemoryVariables } from './hooks/use-memory-variables' import Confirm from '@/app/components/base/confirm' import { Memory as MemoryIcon } from '@/app/components/base/icons/src/vender/line/others' +import VariableModal from '@/app/components/workflow/panel/chat-variable-panel/components/variable-modal' type BlockMemoryProps = { id: string @@ -20,17 +21,18 @@ type BlockMemoryProps = { } const BlockMemory = ({ id }: BlockMemoryProps) => { const { t } = useTranslation() - const { memoryVariablesInUsed } = useMemoryVariables(id) + const { + memoryVariablesInUsed, + editMemoryVariable, + handleSetEditMemoryVariable, + handleEdit, + } = useMemoryVariables(id) const [showConfirm, setShowConfirm] = useState<{ title: string desc: string onConfirm: () => void } | undefined>(undefined) - const handleEdit = (blockId: string) => { - console.log('edit', blockId) - } - const handleDelete = (blockId: string) => { setShowConfirm({ title: t('workflow.nodes.common.memory.block.deleteConfirmTitle'), @@ -67,7 +69,7 @@ const BlockMemory = ({ id }: BlockMemoryProps) => { handleEdit(memoryVariable.id)} + onClick={() => handleSetEditMemoryVariable(memoryVariable.id)} > @@ -93,6 +95,16 @@ const BlockMemory = ({ id }: BlockMemoryProps) => { /> ) } + { + !!editMemoryVariable && ( + handleSetEditMemoryVariable(undefined)} + onSave={handleEdit} + nodeScopeMemoryVariable={{ nodeId: id }} + /> + ) + } ) } 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 4b303aeda4..fa7069c2c1 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,8 +1,20 @@ -import { useMemo } from 'react' -import { useStore } from '@/app/components/workflow/store' +import { + useCallback, + useMemo, + useState, +} from 'react' +import { + useStore, + useWorkflowStore, +} from '@/app/components/workflow/store' +import type { MemoryVariable } from '@/app/components/workflow/types' +import { useNodesSyncDraft } from '@/app/components/workflow/hooks/use-nodes-sync-draft' export const useMemoryVariables = (nodeId: string) => { + const workflowStore = useWorkflowStore() const memoryVariables = useStore(s => s.memoryVariables) + const [editMemoryVariable, setEditMemoryVariable] = useState(undefined) + const { handleSyncWorkflowDraft } = useNodesSyncDraft() const memoryVariablesInUsed = useMemo(() => { return memoryVariables.filter(variable => variable.node_id === nodeId) @@ -11,8 +23,36 @@ export const useMemoryVariables = (nodeId: string) => { const handleDelete = (blockId: string) => { console.log('delete', blockId) } + + const handleSave = useCallback((newMemoryVar: MemoryVariable) => { + const { memoryVariables, setMemoryVariables } = workflowStore.getState() + const newList = [newMemoryVar, ...memoryVariables] + setMemoryVariables(newList) + handleSyncWorkflowDraft() + }, [handleSyncWorkflowDraft, workflowStore]) + + const handleSetEditMemoryVariable = (memoryVariableId?: string) => { + if (!memoryVariableId) { + setEditMemoryVariable(undefined) + return + } + const memoryVariable = memoryVariables.find(variable => variable.id === memoryVariableId) + setEditMemoryVariable(memoryVariable) + } + + const handleEdit = (memoryVariable: MemoryVariable) => { + const { memoryVariables, setMemoryVariables } = workflowStore.getState() + const newList = memoryVariables.map(variable => variable.id === memoryVariable.id ? memoryVariable : variable) + setMemoryVariables(newList) + handleSyncWorkflowDraft() + } + return { memoryVariablesInUsed, handleDelete, + handleSave, + handleSetEditMemoryVariable, + handleEdit, + editMemoryVariable, } } 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 d4c8f5506f..5997f2a2fc 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 @@ -1,7 +1,9 @@ import { memo, + useCallback, } from 'react' import { useTranslation } from 'react-i18next' +import { RiAddLine } from '@remixicon/react' import Collapse from '@/app/components/workflow/nodes/_base/components/collapse' import type { Node, @@ -17,6 +19,8 @@ import { useMemory } from './hooks/use-memory' import Split from '@/app/components/workflow/nodes/_base/components/split' import { MemoryMode } from '@/app/components/workflow/types' import BlockMemory from './block-memory' +import { ActionButton } from '@/app/components/base/action-button' +import cn from '@/utils/classnames' type MemoryProps = Pick & { readonly?: boolean @@ -38,6 +42,14 @@ const MemorySystem = ({ handleUpdateMemory, } = useMemory(id, data as LLMNodeType) + const renderTrigger = useCallback((open?: boolean) => { + return ( + + + + ) + }, []) + return ( <>
@@ -64,7 +76,10 @@ const MemorySystem = ({ <>
e.stopPropagation()}> - +
) diff --git a/web/app/components/workflow/nodes/llm/components/memory-system/memory-create-button.tsx b/web/app/components/workflow/nodes/llm/components/memory-system/memory-create-button.tsx index ce5ee64c6e..03cf35f88e 100644 --- a/web/app/components/workflow/nodes/llm/components/memory-system/memory-create-button.tsx +++ b/web/app/components/workflow/nodes/llm/components/memory-system/memory-create-button.tsx @@ -1,5 +1,4 @@ import { useCallback, useState } from 'react' -import { RiAddLine } from '@remixicon/react' import VariableModal from '@/app/components/workflow/panel/chat-variable-panel/components/variable-modal' import type { OffsetOptions, Placement } from '@floating-ui/react' import { @@ -7,52 +6,36 @@ import { PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import ActionButton from '@/app/components/base/action-button' import type { MemoryVariable } from '@/app/components/workflow/types' -import { useStore } from '@/app/components/workflow/store' -import { useNodesSyncDraft } from '@/app/components/workflow/hooks/use-nodes-sync-draft' -import useInspectVarsCrud from '@/app/components/workflow/hooks/use-inspect-vars-crud' import { useEventEmitterContextContext } from '@/context/event-emitter' import { MEMORY_VAR_CREATED_BY_MODAL_BY_EVENT_EMITTER, MEMORY_VAR_MODAL_SHOW_BY_EVENT_EMITTER } from '@/app/components/workflow/nodes/_base/components/prompt/type' +import { useMemoryVariables } from './hooks/use-memory-variables' type Props = { placement?: Placement offset?: number | OffsetOptions hideTrigger?: boolean instanceId?: string + nodeId: string + renderTrigger?: (open?: boolean) => React.ReactNode } const MemoryCreateButton = ({ placement, offset, - hideTrigger, instanceId, + nodeId, + renderTrigger = () =>
, }: Props) => { const { eventEmitter } = useEventEmitterContextContext() const [open, setOpen] = useState(false) - const varList = useStore(s => s.memoryVariables) as MemoryVariable[] - const updateMemoryVarList = useStore(s => s.setMemoryVariables) - const { doSyncWorkflowDraft } = useNodesSyncDraft() - const { - invalidateConversationVarValues, - } = useInspectVarsCrud() - - const handleVarChanged = useCallback(() => { - doSyncWorkflowDraft(false, { - onSuccess() { - invalidateConversationVarValues() - }, - }) - }, [doSyncWorkflowDraft, invalidateConversationVarValues]) + const { handleSave: handleSaveMemoryVariables } = useMemoryVariables(nodeId) const handleSave = useCallback((newMemoryVar: MemoryVariable) => { - const newList = [newMemoryVar, ...varList] - updateMemoryVarList(newList) - handleVarChanged() - setOpen(false) + handleSaveMemoryVariables(newMemoryVar) if (instanceId) eventEmitter?.emit({ type: MEMORY_VAR_CREATED_BY_MODAL_BY_EVENT_EMITTER, instanceId, variable: ['memory', newMemoryVar.name] } as any) - }, [varList, updateMemoryVarList, handleVarChanged, setOpen, eventEmitter, instanceId]) + }, [handleSaveMemoryVariables, eventEmitter, instanceId]) eventEmitter?.useSubscription((v: any) => { if (v.type === MEMORY_VAR_MODAL_SHOW_BY_EVENT_EMITTER && v.instanceId === instanceId) @@ -68,12 +51,7 @@ const MemoryCreateButton = ({ offset={offset} > setOpen(v => !v)}> - {hideTrigger &&
} - {!hideTrigger && ( - - - - )} + {renderTrigger?.(open)}
{ setOpen(false) }} + nodeScopeMemoryVariable={nodeId ? { nodeId } : undefined} /> diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal.tsx index c8a14aad71..2135b25e0a 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal.tsx @@ -19,15 +19,20 @@ import VariableForm from '@/app/components/base/form/form-scenarios/variable' import { useForm } from '../hooks' export type ModalPropsType = { + className?: string chatVar?: ConversationVariable | MemoryVariable onClose: () => void onSave: (chatVar: ConversationVariable | MemoryVariable) => void + nodeScopeMemoryVariable?: { + nodeId: string + } } const ChatVariableModal = ({ chatVar, onClose, onSave, + nodeScopeMemoryVariable, }: ModalPropsType) => { const { t } = useTranslation() const { notify } = useContext(ToastContext) @@ -36,7 +41,7 @@ const ChatVariableModal = ({ const { formSchemas, defaultValues, - } = useForm(chatVar) + } = useForm(chatVar, nodeScopeMemoryVariable) const formRef = useRef(null) const form = useTanstackForm({ defaultValues, diff --git a/web/app/components/workflow/panel/chat-variable-panel/hooks/use-form/index.ts b/web/app/components/workflow/panel/chat-variable-panel/hooks/use-form/index.ts index 4091857d55..4c05f00907 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/hooks/use-form/index.ts +++ b/web/app/components/workflow/panel/chat-variable-panel/hooks/use-form/index.ts @@ -10,14 +10,14 @@ import { } from './use-memory-schema' import type { ConversationVariable, MemoryVariable } from '@/app/components/workflow/types' -export const useForm = (chatVar?: ConversationVariable | MemoryVariable) => { +export const useForm = (chatVar?: ConversationVariable | MemoryVariable, nodeScopeMemoryVariable?: { nodeId: string }) => { const { t } = useTranslation() const typeSchema = useTypeSchema() const valueSchema = useValueSchema() const editInJSONSchema = useEditInJSONSchema() - const memorySchema = useMemorySchema() - const memoryDefaultValues = useMemoryDefaultValues() + const memorySchema = useMemorySchema(nodeScopeMemoryVariable) + const memoryDefaultValues = useMemoryDefaultValues(nodeScopeMemoryVariable) const formSchemas = useMemo(() => { return [ 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 5763ebb72c..b26744e4cd 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 @@ -3,9 +3,56 @@ import { FormTypeEnum } from '@/app/components/base/form/types' import type { FormSchema } from '@/app/components/base/form/types' import { ChatVarType } from '@/app/components/workflow/panel/chat-variable-panel/type' -export const useMemorySchema = () => { +export const useMemorySchema = (nodeScopeMemoryVariable?: { nodeId: string }) => { const { t } = useTranslation() + const scopeSchema = [ + { + name: 'scope', + label: t('workflow.chatVariable.modal.scope'), + type: FormTypeEnum.radio, + default: 'app', + fieldClassName: 'flex justify-between', + inputClassName: 'w-[102px]', + options: [ + { + label: 'App', + value: 'app', + }, + { + label: 'Node', + value: 'node', + }, + ], + show_on: [ + { + variable: 'value_type', + value: [ChatVarType.Memory], + }, + ], + selfFormProps: { + withTopDivider: true, + }, + }, + { + name: 'node_id', + label: 'Node', + type: FormTypeEnum.nodeSelector, + fieldClassName: 'flex justify-between', + default: '', + show_on: [ + { + variable: 'value_type', + value: [ChatVarType.Memory], + }, + { + variable: 'scope', + value: 'node', + }, + ], + }, + ] + return [ { name: 'template', @@ -90,50 +137,7 @@ export const useMemorySchema = () => { inputWrapperClassName: 'w-[102px]', }, }, - { - name: 'scope', - label: t('workflow.chatVariable.modal.scope'), - type: FormTypeEnum.radio, - default: 'app', - fieldClassName: 'flex justify-between', - inputClassName: 'w-[102px]', - options: [ - { - label: 'App', - value: 'app', - }, - { - label: 'Node', - value: 'node', - }, - ], - show_on: [ - { - variable: 'value_type', - value: [ChatVarType.Memory], - }, - ], - selfFormProps: { - withTopDivider: true, - }, - }, - { - name: 'node_id', - label: 'Node', - type: FormTypeEnum.nodeSelector, - fieldClassName: 'flex justify-between', - default: '', - show_on: [ - { - variable: 'value_type', - value: [ChatVarType.Memory], - }, - { - variable: 'scope', - value: 'node', - }, - ], - }, + ...(!nodeScopeMemoryVariable ? scopeSchema : []), { name: 'term', label: t('workflow.chatVariable.modal.term'), @@ -238,14 +242,15 @@ export const useMemorySchema = () => { ] as FormSchema[] } -export const useMemoryDefaultValues = () => { +export const useMemoryDefaultValues = (nodeScopeMemoryVariable?: { nodeId: string }) => { return { template: '', instruction: '', strategy: 'on_turns', update_turns: 500, preserved_turns: 10, - scope: 'app', + scope: nodeScopeMemoryVariable ? 'node' : 'app', + node_id: nodeScopeMemoryVariable?.nodeId || '', term: 'session', more: false, model: '',