diff --git a/web/app/components/app/configuration/config/automatic/get-automatic-res.tsx b/web/app/components/app/configuration/config/automatic/get-automatic-res.tsx index 5c87eea3ca..2b772ae6f3 100644 --- a/web/app/components/app/configuration/config/automatic/get-automatic-res.tsx +++ b/web/app/components/app/configuration/config/automatic/get-automatic-res.tsx @@ -53,6 +53,7 @@ export type IGetAutomaticResProps = { editorId?: string currentPrompt?: string isBasicMode?: boolean + hideTryIt?: boolean } const TryLabel: FC<{ @@ -81,6 +82,7 @@ const GetAutomaticRes: FC = ({ currentPrompt, isBasicMode, onFinished, + hideTryIt, }) => { const { t } = useTranslation() const localModel = localStorage.getItem('auto-gen-model') @@ -307,7 +309,7 @@ const GetAutomaticRes: FC = ({ hideDebugWithMultipleModel /> - {isBasicMode && ( + {isBasicMode && !hideTryIt && (
{t('appDebug.generate.tryIt')}
diff --git a/web/app/components/base/form/components/base/base-field.tsx b/web/app/components/base/form/components/base/base-field.tsx index 0769a40963..fce80f208e 100644 --- a/web/app/components/base/form/components/base/base-field.tsx +++ b/web/app/components/base/form/components/base/base-field.tsx @@ -22,7 +22,7 @@ import Radio from '@/app/components/base/radio' import RadioE from '@/app/components/base/radio/ui' import Textarea from '@/app/components/base/textarea' import PromptEditor from '@/app/components/base/prompt-editor' -import ModelParameterModal from '@/app/components/plugins/plugin-detail-panel/model-selector' +import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal' import ObjectValueList from '@/app/components/workflow/panel/chat-variable-panel/components/object-value-list' import ArrayValueList from '@/app/components/workflow/panel/chat-variable-panel/components/array-value-list' import ArrayBooleanValueList from '@/app/components/workflow/panel/chat-variable-panel/components/array-bool-list' @@ -136,6 +136,7 @@ const BaseField = ({ const handleChange = useCallback((value: any) => { if (disabled) return + field.handleChange(value) formOnChange?.(field.form, value) onChange?.(field.name, value) @@ -340,10 +341,12 @@ const BaseField = ({ selfProps?.enablePromptGenerator && ( ) } @@ -405,11 +408,28 @@ const BaseField = ({ type === FormTypeEnum.modelSelector && ( { + handleChange({ + mode, + provider, + name: modelId, + completion_params: value?.completion_params, + }) + }} + completionParams={value?.completion_params} + onCompletionParamsChange={(params) => { + handleChange({ + ...value, + completion_params: params, + }) + }} readonly={disabled} - scope={formSchema.scope} isAdvancedMode + isInWorkflow + hideDebugWithMultipleModel /> ) } diff --git a/web/app/components/base/prompt-editor/constants.tsx b/web/app/components/base/prompt-editor/constants.tsx index 54e2f1aa21..43d20fcc04 100644 --- a/web/app/components/base/prompt-editor/constants.tsx +++ b/web/app/components/base/prompt-editor/constants.tsx @@ -10,6 +10,7 @@ export const LAST_RUN_PLACEHOLDER_TEXT = '{{#last_run#}}' export const PRE_PROMPT_PLACEHOLDER_TEXT = '{{#pre_prompt#}}' export const UPDATE_DATASETS_EVENT_EMITTER = 'prompt-editor-context-block-update-datasets' export const UPDATE_HISTORY_EVENT_EMITTER = 'prompt-editor-history-block-update-role' +export const UPDATE_WORKFLOW_VARIABLES_EVENT_EMITTER = 'prompt-editor-workflow-variables-block-update-variables' export const checkHasContextBlock = (text: string) => { if (!text) diff --git a/web/app/components/base/prompt-editor/index.tsx b/web/app/components/base/prompt-editor/index.tsx index a98a5bacb7..f2c86295e1 100644 --- a/web/app/components/base/prompt-editor/index.tsx +++ b/web/app/components/base/prompt-editor/index.tsx @@ -78,9 +78,11 @@ import type { import { UPDATE_DATASETS_EVENT_EMITTER, UPDATE_HISTORY_EVENT_EMITTER, + UPDATE_WORKFLOW_VARIABLES_EVENT_EMITTER, } from './constants' import { useEventEmitterContextContext } from '@/context/event-emitter' import cn from '@/utils/classnames' +import PromptEditorProvider from './store/provider' export type PromptEditorProps = { instanceId?: string @@ -176,6 +178,13 @@ const PromptEditor: FC = ({ payload: historyBlock?.history, } as any) }, [eventEmitter, historyBlock?.history]) + useEffect(() => { + eventEmitter?.emit({ + type: UPDATE_WORKFLOW_VARIABLES_EVENT_EMITTER, + payload: workflowVariableBlock?.variables, + instanceId, + } as any) + }, [eventEmitter, workflowVariableBlock?.variables]) return ( @@ -267,7 +276,7 @@ const PromptEditor: FC = ({ { workflowVariableBlock?.show && ( <> - + ) @@ -311,4 +320,12 @@ const PromptEditor: FC = ({ ) } -export default PromptEditor +const PromptEditorWithProvider = ({ instanceId, ...props }: PromptEditorProps) => { + return ( + + + + ) +} + +export default PromptEditorWithProvider 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 ea805c63ed..59d545f609 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 @@ -24,23 +24,26 @@ 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' import { Type } from '@/app/components/workflow/nodes/llm/types' -import type { ValueSelector, Var } from '@/app/components/workflow/types' +import type { + NodeOutPutVar, + ValueSelector, +} from '@/app/components/workflow/types' import { VariableLabelInEditor, } from '@/app/components/workflow/nodes/_base/components/variable/variable-label' +import { useEventEmitterContextContext } from '@/context/event-emitter' +import { UPDATE_WORKFLOW_VARIABLES_EVENT_EMITTER } from '../../constants' +import { usePromptEditorStore } from '../../store/store' type WorkflowVariableBlockComponentProps = { nodeKey: string variables: string[] workflowNodesMap: WorkflowNodesMap - environmentVariables?: Var[] - conversationVariables?: Var[] - ragVariables?: Var[] + availableVariables: NodeOutPutVar[] getVarType?: (payload: { nodeId: string, valueSelector: ValueSelector, }) => Type - isMemorySupported?: boolean } const WorkflowVariableBlockComponent = ({ @@ -48,13 +51,27 @@ const WorkflowVariableBlockComponent = ({ variables, workflowNodesMap = {}, getVarType, - environmentVariables, - conversationVariables, - ragVariables, - isMemorySupported, + availableVariables: initialAvailableVariables, }: WorkflowVariableBlockComponentProps) => { const { t } = useTranslation() const [editor] = useLexicalComposerContext() + const instanceId = usePromptEditorStore(s => s.instanceId) + const { eventEmitter } = useEventEmitterContextContext() + const [availableVariables, setAvailableVariables] = useState(initialAvailableVariables) + eventEmitter?.useSubscription((v: any) => { + if (v?.type === UPDATE_WORKFLOW_VARIABLES_EVENT_EMITTER && instanceId && v.instanceId === instanceId) + setAvailableVariables(v.payload) + }) + const environmentVariables = availableVariables?.find(v => v.nodeId === 'env')?.vars || [] + const conversationVariables = availableVariables?.find(v => v.nodeId === 'conversation')?.vars || [] + const memoryVariables = conversationVariables?.filter(v => v.variable.startsWith('memory_block.')) + const ragVariables = availableVariables?.reduce((acc, curr) => { + if (curr.nodeId === 'rag') + acc.push(...curr.vars) + else + acc.push(...curr.vars.filter(v => v.isRagVariable)) + return acc + }, []) const [ref, isSelected] = useSelectOrDelete(nodeKey, DELETE_WORKFLOW_VARIABLE_BLOCK_COMMAND) const variablesLength = variables.length const isRagVar = isRagVariableVar(variables) @@ -73,27 +90,19 @@ const WorkflowVariableBlockComponent = ({ const isMemoryVar = isMemoryVariable(variables) const isException = isExceptionVariable(varName, node?.type) - const memoryVariables = conversationVariables?.filter(v => v.variable.startsWith('memory_block.')) - let variableValid = true if (isEnv) { - if (environmentVariables) - variableValid = environmentVariables.some(v => v.variable === `${variables?.[0] ?? ''}.${variables?.[1] ?? ''}`) + variableValid = environmentVariables.some(v => + v.variable === `${variables?.[0] ?? ''}.${variables?.[1] ?? ''}`) } else if (isConversationVar) { - if (conversationVariables) - variableValid = conversationVariables.some(v => v.variable === `${variables?.[0] ?? ''}.${variables?.[1] ?? ''}`) + 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] ?? ''}`) - - if (!isMemorySupported) - variableValid = false + 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] ?? ''}`) + variableValid = ragVariables.some(v => v.variable === `${variables?.[0] ?? ''}.${variables?.[1] ?? ''}.${variables?.[2] ?? ''}`) } else { variableValid = !!node diff --git a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/index.tsx b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/index.tsx index b8d5b0b5bd..f3126a6256 100644 --- a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/index.tsx +++ b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/index.tsx @@ -33,18 +33,9 @@ const WorkflowVariableBlock = memo(({ onDelete, getVarType, variables: originalVariables, - isMemorySupported, }: WorkflowVariableBlockType) => { const [editor] = useLexicalComposerContext() - const ragVariables = originalVariables?.reduce((acc, curr) => { - if (curr.nodeId === 'rag') - acc.push(...curr.vars) - else - acc.push(...curr.vars.filter(v => v.isRagVariable)) - return acc - }, []) - useEffect(() => { editor.update(() => { editor.dispatchCommand(UPDATE_WORKFLOW_NODES_MAP, workflowNodesMap) @@ -60,7 +51,7 @@ const WorkflowVariableBlock = memo(({ INSERT_WORKFLOW_VARIABLE_BLOCK_COMMAND, (variables: string[]) => { editor.dispatchCommand(CLEAR_HIDE_MENU_TIMEOUT, undefined) - const workflowVariableBlockNode = $createWorkflowVariableBlockNode(variables, workflowNodesMap, getVarType, originalVariables?.find(o => o.nodeId === 'env')?.vars || [], originalVariables?.find(o => o.nodeId === 'conversation')?.vars || [], ragVariables, isMemorySupported) + const workflowVariableBlockNode = $createWorkflowVariableBlockNode(variables, workflowNodesMap, getVarType, originalVariables || []) $insertNodes([workflowVariableBlockNode]) if (onInsert) 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 34e3a06dd3..d8d16da718 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 @@ -3,7 +3,7 @@ import { DecoratorNode } from 'lexical' import type { WorkflowVariableBlockType } from '../../types' import WorkflowVariableBlockComponent from './component' import type { GetVarType } from '../../types' -import type { Var } from '@/app/components/workflow/types' +import type { NodeOutPutVar } from '@/app/components/workflow/types' export type WorkflowNodesMap = WorkflowVariableBlockType['workflowNodesMap'] @@ -11,27 +11,21 @@ export type SerializedNode = SerializedLexicalNode & { variables: string[] workflowNodesMap: WorkflowNodesMap getVarType?: GetVarType - environmentVariables?: Var[] - conversationVariables?: Var[] - ragVariables?: Var[] - isMemorySupported?: boolean + availableVariables?: NodeOutPutVar[] } export class WorkflowVariableBlockNode extends DecoratorNode { __variables: string[] __workflowNodesMap: WorkflowNodesMap __getVarType?: GetVarType - __environmentVariables?: Var[] - __conversationVariables?: Var[] - __ragVariables?: Var[] - __isMemorySupported?: boolean + __availableVariables?: NodeOutPutVar[] static getType(): string { return 'workflow-variable-block' } static clone(node: WorkflowVariableBlockNode): WorkflowVariableBlockNode { - return new WorkflowVariableBlockNode(node.__variables, node.__workflowNodesMap, node.__getVarType, node.__key, node.__environmentVariables, node.__conversationVariables, node.__ragVariables) + return new WorkflowVariableBlockNode(node.__variables, node.__workflowNodesMap, node.__getVarType, node.__key, node.__availableVariables) } isInline(): boolean { @@ -43,20 +37,14 @@ export class WorkflowVariableBlockNode extends DecoratorNode workflowNodesMap: WorkflowNodesMap, getVarType: any, key?: NodeKey, - environmentVariables?: Var[], - conversationVariables?: Var[], - ragVariables?: Var[], - isMemorySupported?: boolean, + availableVariables?: NodeOutPutVar[], ) { super(key) this.__variables = variables this.__workflowNodesMap = workflowNodesMap this.__getVarType = getVarType - this.__environmentVariables = environmentVariables - this.__conversationVariables = conversationVariables - this.__ragVariables = ragVariables - this.__isMemorySupported = isMemorySupported + this.__availableVariables = availableVariables } createDOM(): HTMLElement { @@ -76,16 +64,13 @@ export class WorkflowVariableBlockNode extends DecoratorNode variables={this.__variables} workflowNodesMap={this.__workflowNodesMap} getVarType={this.__getVarType!} - environmentVariables={this.__environmentVariables} - conversationVariables={this.__conversationVariables} - ragVariables={this.__ragVariables} - isMemorySupported={this.__isMemorySupported} + availableVariables={this.__availableVariables || []} /> ) } static importJSON(serializedNode: SerializedNode): WorkflowVariableBlockNode { - const node = $createWorkflowVariableBlockNode(serializedNode.variables, serializedNode.workflowNodesMap, serializedNode.getVarType, serializedNode.environmentVariables, serializedNode.conversationVariables, serializedNode.ragVariables, serializedNode.isMemorySupported) + const node = $createWorkflowVariableBlockNode(serializedNode.variables, serializedNode.workflowNodesMap, serializedNode.getVarType, serializedNode.availableVariables) return node } @@ -97,9 +82,7 @@ export class WorkflowVariableBlockNode extends DecoratorNode variables: this.getVariables(), workflowNodesMap: this.getWorkflowNodesMap(), getVarType: this.getVarType(), - environmentVariables: this.getEnvironmentVariables(), - conversationVariables: this.getConversationVariables(), - ragVariables: this.getRagVariables(), + availableVariables: this.getAvailableVariables(), } } @@ -118,32 +101,17 @@ export class WorkflowVariableBlockNode extends DecoratorNode return self.__getVarType } - getEnvironmentVariables(): any { + getAvailableVariables(): NodeOutPutVar[] { const self = this.getLatest() - return self.__environmentVariables - } - - getConversationVariables(): any { - const self = this.getLatest() - return self.__conversationVariables - } - - getRagVariables(): any { - const self = this.getLatest() - return self.__ragVariables - } - - getIsMemorySupported() { - const self = this.getLatest() - return self.__isMemorySupported + return self.__availableVariables || [] } getTextContent(): string { return `{{#${this.getVariables().join('.')}#}}` } } -export function $createWorkflowVariableBlockNode(variables: string[], workflowNodesMap: WorkflowNodesMap, getVarType?: GetVarType, environmentVariables?: Var[], conversationVariables?: Var[], ragVariables?: Var[], isMemorySupported?: boolean): WorkflowVariableBlockNode { - return new WorkflowVariableBlockNode(variables, workflowNodesMap, getVarType, undefined, environmentVariables, conversationVariables, ragVariables, isMemorySupported) +export function $createWorkflowVariableBlockNode(variables: string[], workflowNodesMap: WorkflowNodesMap, getVarType?: GetVarType, availableVariables?: NodeOutPutVar[]): WorkflowVariableBlockNode { + return new WorkflowVariableBlockNode(variables, workflowNodesMap, getVarType, undefined, availableVariables) } export function $isWorkflowVariableBlockNode( diff --git a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/workflow-variable-block-replacement-block.tsx b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/workflow-variable-block-replacement-block.tsx index 37985d8c2a..60bc47d266 100644 --- a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/workflow-variable-block-replacement-block.tsx +++ b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/workflow-variable-block-replacement-block.tsx @@ -19,16 +19,8 @@ const WorkflowVariableBlockReplacementBlock = ({ getVarType, onInsert, variables, - isMemorySupported, }: WorkflowVariableBlockType) => { const [editor] = useLexicalComposerContext() - const ragVariables = variables?.reduce((acc, curr) => { - if (curr.nodeId === 'rag') - acc.push(...curr.vars) - else - acc.push(...curr.vars.filter(v => v.isRagVariable)) - return acc - }, []) useEffect(() => { if (!editor.hasNodes([WorkflowVariableBlockNode])) @@ -40,7 +32,7 @@ const WorkflowVariableBlockReplacementBlock = ({ onInsert() const nodePathString = textNode.getTextContent().slice(3, -3) - return $applyNodeReplacement($createWorkflowVariableBlockNode(nodePathString.split('.'), workflowNodesMap, getVarType, variables?.find(o => o.nodeId === 'env')?.vars || [], variables?.find(o => o.nodeId === 'conversation')?.vars || [], ragVariables, isMemorySupported)) + return $applyNodeReplacement($createWorkflowVariableBlockNode(nodePathString.split('.'), workflowNodesMap, getVarType, variables)) }, [onInsert, workflowNodesMap, getVarType, variables]) const getMatch = useCallback((text: string) => { diff --git a/web/app/components/base/prompt-editor/store/provider.tsx b/web/app/components/base/prompt-editor/store/provider.tsx new file mode 100644 index 0000000000..66ff301d27 --- /dev/null +++ b/web/app/components/base/prompt-editor/store/provider.tsx @@ -0,0 +1,31 @@ +import { createContext, useRef } from 'react' +import { createPromptEditorStore } from './store' + +type PromptEditorStoreApi = ReturnType + +type PromptEditorContextType = PromptEditorStoreApi | undefined + +export const PromptEditorContext = createContext(undefined) + +type PromptEditorProviderProps = { + instanceId?: string + children: React.ReactNode +} + +const PromptEditorProvider = ({ + instanceId, + children, +}: PromptEditorProviderProps) => { + const storeRef = useRef(undefined) + + if (!storeRef.current) + storeRef.current = createPromptEditorStore({ instanceId }) + + return ( + + {children} + + ) +} + +export default PromptEditorProvider diff --git a/web/app/components/base/prompt-editor/store/store.ts b/web/app/components/base/prompt-editor/store/store.ts new file mode 100644 index 0000000000..86733b6802 --- /dev/null +++ b/web/app/components/base/prompt-editor/store/store.ts @@ -0,0 +1,24 @@ +import { useContext } from 'react' +import { createStore, useStore } from 'zustand' +import { PromptEditorContext } from './provider' + +type PromptEditorStoreProps = { + instanceId?: string +} +type PromptEditorStore = { + instanceId?: string +} + +export const createPromptEditorStore = ({ instanceId }: PromptEditorStoreProps) => { + return createStore(() => ({ + instanceId, + })) +} + +export const usePromptEditorStore = (selector: (state: PromptEditorStore) => T): T => { + const store = useContext(PromptEditorContext) + if (!store) + throw new Error('Missing PromptEditorContext.Provider in the tree') + + return useStore(store, selector) +} diff --git a/web/app/components/header/account-setting/model-provider-page/hooks.ts b/web/app/components/header/account-setting/model-provider-page/hooks.ts index 48dc609795..a6bb6a55bd 100644 --- a/web/app/components/header/account-setting/model-provider-page/hooks.ts +++ b/web/app/components/header/account-setting/model-provider-page/hooks.ts @@ -129,8 +129,8 @@ export const useProviderCredentialsAndLoadBalancing = ( // as ([Record | undefined, ModelLoadBalancingConfig | undefined]) } -export const useModelList = (type: ModelTypeEnum) => { - const { data, mutate, isLoading } = useSWR(`/workspaces/current/models/model-types/${type}`, fetchModelList) +export const useModelList = (type: ModelTypeEnum, enabled?: boolean) => { + const { data, mutate, isLoading } = useSWR(enabled ? `/workspaces/current/models/model-types/${type}` : null, fetchModelList) return { data: data?.data || [], @@ -139,8 +139,8 @@ export const useModelList = (type: ModelTypeEnum) => { } } -export const useDefaultModel = (type: ModelTypeEnum) => { - const { data, mutate, isLoading } = useSWR(`/workspaces/current/default-model?model_type=${type}`, fetchDefaultModal) +export const useDefaultModel = (type: ModelTypeEnum, enabled?: boolean) => { + const { data, mutate, isLoading } = useSWR(enabled ? `/workspaces/current/default-model?model_type=${type}` : null, fetchDefaultModal) return { data: data?.data, 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 406532b3cb..a75f3a66bc 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,17 +85,11 @@ export const useNodesSyncDraft = () => { }, environment_variables: environmentVariables, conversation_variables: conversationVariables, - memory_blocks: memoryVariables.map(({ value_type, scope, more, node_id, model, ...rest }) => { + memory_blocks: memoryVariables.map(({ value_type, scope, more, node_id, ...rest }) => { return { ...rest, node_id: scope === 'node' ? node_id : undefined, scope, - model: model ? { - completion_params: model.completion_params, - mode: model.mode, - provider: model.provider, - name: model.model, - } : undefined, } }), hash: syncWorkflowDraftHash, diff --git a/web/app/components/workflow/hooks/use-memory-variable.ts b/web/app/components/workflow/hooks/use-memory-variable.ts index e5d131f06c..e75b68b9cb 100644 --- a/web/app/components/workflow/hooks/use-memory-variable.ts +++ b/web/app/components/workflow/hooks/use-memory-variable.ts @@ -40,12 +40,6 @@ export const useFormatMemoryVariables = () => { return { ...v, value_type: ChatVarType.Memory, - model: v.model ? { - completion_params: v.model?.completion_params, - mode: v.model?.mode, - provider: v.model?.provider, - model: v.model?.name, - } : undefined, } }) }, []) 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 b93a28e135..59c8e20fae 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/utils.ts +++ b/web/app/components/workflow/nodes/_base/components/variable/utils.ts @@ -823,7 +823,7 @@ export const toNodeOutputVars = ( let nodeList = [] if (conversationVariablesFirst) { nodeList = [ - ...((isChatMode && conversationVariables.length > 0) ? [CHAT_VAR_NODE] : []), + ...((isChatMode && (conversationVariables.length > 0 || memoryVariables.length > 0)) ? [CHAT_VAR_NODE] : []), ...(environmentVariables.length > 0 ? [ENV_NODE] : []), ...sortedNodes.filter(node => SUPPORT_OUTPUT_VARS_NODE.includes(node?.data?.type)), ...(RAG_PIPELINE_NODE.data.ragVariables.length > 0 @@ -835,7 +835,7 @@ export const toNodeOutputVars = ( nodeList = [ ...sortedNodes.filter(node => SUPPORT_OUTPUT_VARS_NODE.includes(node?.data?.type)), ...(environmentVariables.length > 0 ? [ENV_NODE] : []), - ...((isChatMode && conversationVariables.length > 0) ? [CHAT_VAR_NODE] : []), + ...((isChatMode && (conversationVariables.length > 0 || memoryVariables.length > 0)) ? [CHAT_VAR_NODE] : []), ...(RAG_PIPELINE_NODE.data.ragVariables.length > 0 ? [RAG_PIPELINE_NODE] : []), diff --git a/web/app/components/workflow/nodes/_base/hooks/use-available-var-list.ts b/web/app/components/workflow/nodes/_base/hooks/use-available-var-list.ts index c58b6f8c1c..929e468ab6 100644 --- a/web/app/components/workflow/nodes/_base/hooks/use-available-var-list.ts +++ b/web/app/components/workflow/nodes/_base/hooks/use-available-var-list.ts @@ -16,6 +16,7 @@ type Params = { filterVar: (payload: Var, selector: ValueSelector) => boolean passedInAvailableNodes?: Node[] conversationVariablesFirst?: boolean + isMemorySupported?: boolean } // TODO: loop type? @@ -26,6 +27,7 @@ const useAvailableVarList = (nodeId: string, { hideChatVar, passedInAvailableNodes, conversationVariablesFirst, + isMemorySupported, }: Params = { onlyLeafNodeVar: false, filterVar: () => true, @@ -80,6 +82,11 @@ const useAvailableVarList = (nodeId: string, { return { ...availableVar, vars: availableVar.vars.filter((v) => { + if (!v.isMemoryVariable) + return true + + if (!isMemorySupported) + return false if (!v.memoryVariableNodeId) return true diff --git a/web/app/components/workflow/nodes/llm/components/config-prompt.tsx b/web/app/components/workflow/nodes/llm/components/config-prompt.tsx index 921c6b8e12..9a6b6d30f5 100644 --- a/web/app/components/workflow/nodes/llm/components/config-prompt.tsx +++ b/web/app/components/workflow/nodes/llm/components/config-prompt.tsx @@ -76,6 +76,7 @@ const ConfigPrompt: FC = ({ onlyLeafNodeVar: false, filterVar, conversationVariablesFirst: true, + isMemorySupported, }) const handleChatModePromptChange = useCallback((index: number) => { 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 1a2b85401e..205ac4a760 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 @@ -28,20 +28,11 @@ const BlockMemory = ({ id }: BlockMemoryProps) => { editMemoryVariable, handleSetEditMemoryVariable, handleEdit, + handleDelete, + handleDeleteConfirm, + cacheForDeleteMemoryVariable, + setCacheForDeleteMemoryVariable, } = useMemoryVariables(id) - const [showConfirm, setShowConfirm] = useState<{ - title: string - desc: string - onConfirm: () => void - } | undefined>(undefined) - - const handleDelete = (blockId: string) => { - setShowConfirm({ - title: t('workflow.nodes.common.memory.block.deleteConfirmTitle'), - desc: t('workflow.nodes.common.memory.block.deleteConfirmDesc'), - onConfirm: () => handleDelete(blockId), - }) - } if (!memoryVariablesInUsed?.length) { return ( @@ -87,7 +78,7 @@ const BlockMemory = ({ id }: BlockMemoryProps) => { editMemoryVariable?.id === memoryVariable.id && 'inline-flex', )} size='m' - onClick={() => handleDelete(memoryVariable.id)} + onClick={() => handleDelete(memoryVariable)} onMouseOver={() => setDestructiveItemId(memoryVariable.id)} onMouseOut={() => setDestructiveItemId(undefined)} > @@ -98,13 +89,13 @@ const BlockMemory = ({ id }: BlockMemoryProps) => { }
{ - !!showConfirm && ( + !!cacheForDeleteMemoryVariable && ( setShowConfirm(undefined)} - onConfirm={showConfirm.onConfirm} - title={showConfirm.title} - content={showConfirm.desc} + onCancel={() => setCacheForDeleteMemoryVariable(undefined)} + onConfirm={() => handleDeleteConfirm(cacheForDeleteMemoryVariable.id)} + title={t('workflow.nodes.common.memory.deleteNodeMemoryVariableConfirmTitle', { name: cacheForDeleteMemoryVariable.name })} + content={t('workflow.nodes.common.memory.deleteNodeMemoryVariableConfirmDesc')} /> ) } diff --git a/web/app/components/workflow/nodes/llm/components/memory-system/hooks/use-memory-used-detector.ts b/web/app/components/workflow/nodes/llm/components/memory-system/hooks/use-memory-used-detector.ts new file mode 100644 index 0000000000..c47165ae49 --- /dev/null +++ b/web/app/components/workflow/nodes/llm/components/memory-system/hooks/use-memory-used-detector.ts @@ -0,0 +1,20 @@ +import { useCallback } from 'react' +import type { MemoryVariable } from '@/app/components/workflow/types' +import { useNodeUpdate } from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import { findUsedVarNodes } from '@/app/components/workflow/nodes/_base/components/variable/utils' + +export const useMemoryUsedDetector = (nodeId: string) => { + const { getNodeData } = useNodeUpdate(nodeId) + const getMemoryUsedDetector = useCallback((chatVar: MemoryVariable) => { + const nodeData = getNodeData()! + const valueSelector = ['memory_block', chatVar.node_id ? `${chatVar.node_id}_${chatVar.id}` : chatVar.id] + return findUsedVarNodes( + valueSelector, + [nodeData], + ) + }, [getNodeData]) + + return { + getMemoryUsedDetector, + } +} 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 fa7069c2c1..73fe012f6e 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 @@ -9,21 +9,20 @@ import { } from '@/app/components/workflow/store' import type { MemoryVariable } from '@/app/components/workflow/types' import { useNodesSyncDraft } from '@/app/components/workflow/hooks/use-nodes-sync-draft' +import { useMemoryUsedDetector } from './use-memory-used-detector' export const useMemoryVariables = (nodeId: string) => { const workflowStore = useWorkflowStore() const memoryVariables = useStore(s => s.memoryVariables) const [editMemoryVariable, setEditMemoryVariable] = useState(undefined) const { handleSyncWorkflowDraft } = useNodesSyncDraft() + const { getMemoryUsedDetector } = useMemoryUsedDetector(nodeId) + const [cacheForDeleteMemoryVariable, setCacheForDeleteMemoryVariable] = useState(undefined) const memoryVariablesInUsed = useMemo(() => { return memoryVariables.filter(variable => variable.node_id === nodeId) }, [memoryVariables, nodeId]) - const handleDelete = (blockId: string) => { - console.log('delete', blockId) - } - const handleSave = useCallback((newMemoryVar: MemoryVariable) => { const { memoryVariables, setMemoryVariables } = workflowStore.getState() const newList = [newMemoryVar, ...memoryVariables] @@ -47,6 +46,23 @@ export const useMemoryVariables = (nodeId: string) => { handleSyncWorkflowDraft() } + const handleDeleteConfirm = (memoryVariableId?: string) => { + const { memoryVariables, setMemoryVariables } = workflowStore.getState() + const newList = memoryVariables.filter(variable => variable.id !== memoryVariableId) + setMemoryVariables(newList) + handleSyncWorkflowDraft() + setCacheForDeleteMemoryVariable(undefined) + } + + const handleDelete = (memoryVariable: MemoryVariable) => { + const effectedNodes = getMemoryUsedDetector(memoryVariable) + if (effectedNodes.length > 0) { + setCacheForDeleteMemoryVariable(memoryVariable) + return + } + handleDeleteConfirm(memoryVariable.id) + } + return { memoryVariablesInUsed, handleDelete, @@ -54,5 +70,8 @@ export const useMemoryVariables = (nodeId: string) => { handleSetEditMemoryVariable, handleEdit, editMemoryVariable, + handleDeleteConfirm, + cacheForDeleteMemoryVariable, + setCacheForDeleteMemoryVariable, } } diff --git a/web/app/components/workflow/nodes/llm/components/memory-system/hooks/use-memory.ts b/web/app/components/workflow/nodes/llm/components/memory-system/hooks/use-memory.ts index a98a908bbd..4159187de1 100644 --- a/web/app/components/workflow/nodes/llm/components/memory-system/hooks/use-memory.ts +++ b/web/app/components/workflow/nodes/llm/components/memory-system/hooks/use-memory.ts @@ -10,11 +10,14 @@ import { } from '../linear-memory' import type { Memory } from '@/app/components/workflow/types' import { MemoryMode } from '@/app/components/workflow/types' +import { useWorkflowStore } from '@/app/components/workflow/store' +import { useMemoryUsedDetector } from './use-memory-used-detector' export const useMemory = ( id: string, data: LLMNodeType, ) => { + const workflowStore = useWorkflowStore() const { memory } = data const initCollapsed = useMemo(() => { if (!memory?.enabled) @@ -27,10 +30,12 @@ export const useMemory = ( getNodeData, handleNodeDataUpdate, } = useNodeUpdate(id) + const [showTipsWhenMemoryModeBlockToLinear, setShowTipsWhenMemoryModeBlockToLinear] = useState(false) + const { getMemoryUsedDetector } = useMemoryUsedDetector(id) const handleMemoryTypeChange = useCallback((value: string) => { const nodeData = getNodeData() - const { memory: memoryData = {} } = nodeData as any + const { memory: memoryData = {} as Memory } = nodeData?.data as LLMNodeType if (value === MemoryMode.disabled) { setCollapsed(true) @@ -66,8 +71,34 @@ export const useMemory = ( }, }) } + setShowTipsWhenMemoryModeBlockToLinear(false) }, [getNodeData, handleNodeDataUpdate]) + const handleMemoryTypeChangeBefore = useCallback((value: string) => { + const nodeData = getNodeData() + const { memory: memoryData = {} as Memory } = nodeData?.data as LLMNodeType + const { memoryVariables } = workflowStore.getState() + + if (memoryData.mode === MemoryMode.block && value === MemoryMode.linear && nodeData) { + const globalMemoryVariables = memoryVariables.filter(variable => variable.scope === 'app') + const currentNodeMemoryVariables = memoryVariables.filter(variable => variable.node_id === id) + const allMemoryVariables = [...globalMemoryVariables, ...currentNodeMemoryVariables] + + for (const variable of allMemoryVariables) { + const effectedNodes = getMemoryUsedDetector(variable) + + if (effectedNodes.length > 0) { + setShowTipsWhenMemoryModeBlockToLinear(true) + return + } + } + handleMemoryTypeChange(value) + } + else { + handleMemoryTypeChange(value) + } + }, [getNodeData, workflowStore, handleMemoryTypeChange]) + const handleUpdateMemory = useCallback((memory: Memory) => { handleNodeDataUpdate({ memory, @@ -96,7 +127,10 @@ export const useMemory = ( collapsed, setCollapsed, handleMemoryTypeChange, + handleMemoryTypeChangeBefore, memoryType, handleUpdateMemory, + showTipsWhenMemoryModeBlockToLinear, + setShowTipsWhenMemoryModeBlockToLinear, } } 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 5997f2a2fc..53e2b4c11e 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 @@ -21,6 +21,7 @@ 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' +import Confirm from '@/app/components/base/confirm' type MemoryProps = Pick & { readonly?: boolean @@ -38,8 +39,11 @@ const MemorySystem = ({ collapsed, setCollapsed, handleMemoryTypeChange, + handleMemoryTypeChangeBefore, memoryType, handleUpdateMemory, + showTipsWhenMemoryModeBlockToLinear, + setShowTipsWhenMemoryModeBlockToLinear, } = useMemory(id, data as LLMNodeType) const renderTrigger = useCallback((open?: boolean) => { @@ -87,7 +91,7 @@ const MemorySystem = ({
@@ -114,6 +118,20 @@ const MemorySystem = ({ + { + showTipsWhenMemoryModeBlockToLinear && ( + setShowTipsWhenMemoryModeBlockToLinear(false)} + onConfirm={() => handleMemoryTypeChange(MemoryMode.linear)} + confirmText={t('workflow.nodes.common.memory.toLinearConfirmButton')} + title={t('workflow.nodes.common.memory.toLinearConfirmTitle')} + content={t('workflow.nodes.common.memory.toLinearConfirmDesc')} + /> + ) + } ) } diff --git a/web/app/components/workflow/nodes/llm/components/prompt-generator-btn.tsx b/web/app/components/workflow/nodes/llm/components/prompt-generator-btn.tsx index c3c0483bec..0340e141e6 100644 --- a/web/app/components/workflow/nodes/llm/components/prompt-generator-btn.tsx +++ b/web/app/components/workflow/nodes/llm/components/prompt-generator-btn.tsx @@ -18,6 +18,7 @@ type Props = { nodeId: string editorId?: string currentPrompt?: string + isBasicMode?: boolean } const PromptGeneratorBtn: FC = ({ @@ -26,6 +27,7 @@ const PromptGeneratorBtn: FC = ({ nodeId, editorId, currentPrompt, + isBasicMode, }) => { const [showAutomatic, { setTrue: showAutomaticTrue, setFalse: showAutomaticFalse }] = useBoolean(false) const handleAutomaticRes = useCallback((res: GenRes) => { @@ -50,6 +52,8 @@ const PromptGeneratorBtn: FC = ({ nodeId={nodeId} editorId={editorId} currentPrompt={currentPrompt} + isBasicMode={isBasicMode} + hideTryIt /> )} diff --git a/web/app/components/workflow/nodes/llm/panel.tsx b/web/app/components/workflow/nodes/llm/panel.tsx index 97c38d814c..1b5018c996 100644 --- a/web/app/components/workflow/nodes/llm/panel.tsx +++ b/web/app/components/workflow/nodes/llm/panel.tsx @@ -214,7 +214,6 @@ const Panel: FC> = ({ availableNodes={availableNodesWithParent} isSupportFileVar instanceId={`${id}-chat-workflow-llm-prompt-editor-user`} - isMemorySupported={memoryType === MemoryMode.block} /> {inputs.memory?.query_prompt_template && !inputs.memory.query_prompt_template.includes('{{#sys.query#}}') && ( diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/node-selector.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/node-selector.tsx index 95371bf43d..c47700858e 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/components/node-selector.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/components/node-selector.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' import React, { useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' import { RiArrowDownSLine, RiCheckLine, @@ -29,6 +30,7 @@ const NodeSelector: FC = ({ onChange, nodeType = BlockEnum.LLM, }) => { + const { t } = useTranslation() const [open, setOpen] = useState(false) const filteredNodes = useStore(useShallow((s) => { @@ -46,11 +48,14 @@ const NodeSelector: FC = ({ > setOpen(v => !v)}> {currentNode && ( -
{currentNode.data?.title}
+
+ +
{currentNode.data?.title}
+
)} {!currentNode && (
-
Select node...
+
{t('workflow.chatVariable.modal.selectNode')}
)} 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 17b8faed91..41d77be05e 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 @@ -1,4 +1,4 @@ -import React, { useCallback, useRef } from 'react' +import React, { useCallback, useEffect, useRef } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import { @@ -16,6 +16,10 @@ import { checkKeys } from '@/utils/var' import type { FormRefObject, FormSchema } from '@/app/components/base/form/types' import VariableForm from '@/app/components/base/form/form-scenarios/variable' import { useForm } from '../hooks' +import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks' +import { + ModelTypeEnum, +} from '@/app/components/header/account-setting/model-provider-page/declarations' export type ModalPropsType = { className?: string @@ -46,7 +50,19 @@ const ChatVariableModal = ({ defaultValues, }) const type = useTanstackStore(form.store, (s: any) => s.values.value_type) + const isPristine = useTanstackStore(form.store, (s: any) => s.fieldMeta?.model?.isPristine) + const { data: defaultModel } = useDefaultModel(ModelTypeEnum.textGeneration, !chatVar && type === ChatVarType.Memory && isPristine) + useEffect(() => { + if (defaultModel) { + form.setFieldValue('model', { + mode: 'chat', + name: defaultModel.model, + provider: defaultModel.provider.provider, + completion_params: {}, + }) + } + }, [defaultModel]) const checkVariableName = (value: string) => { const { isValid, errorMessageKey } = checkKeys([value], false) if (!isValid) { 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 4c05f00907..fdf508dd11 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 @@ -17,7 +17,7 @@ export const useForm = (chatVar?: ConversationVariable | MemoryVariable, nodeSco const valueSchema = useValueSchema() const editInJSONSchema = useEditInJSONSchema() const memorySchema = useMemorySchema(nodeScopeMemoryVariable) - const memoryDefaultValues = useMemoryDefaultValues(nodeScopeMemoryVariable) + const memoryDefaultValues = useMemoryDefaultValues(chatVar, 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 b26744e4cd..8d4d0d9bf4 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 @@ -1,11 +1,42 @@ +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' +import type { + AnyFormApi, +} from '@tanstack/react-form' 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' +import type { MemoryVariable } from '@/app/components/workflow/types' export const useMemorySchema = (nodeScopeMemoryVariable?: { nodeId: string }) => { const { t } = useTranslation() + const getTemplateSelfFormProps = useCallback((form: AnyFormApi) => { + const { + node_id, + } = form.state.values + + return { + enablePromptGenerator: true, + withTopDivider: true, + editorId: 'memory-template', + nodeId: node_id, + } + }, [t]) + + const getInstructionSelfFormProps = useCallback((form: AnyFormApi) => { + const { + node_id, + } = form.state.values + + return { + enablePromptGenerator: true, + placeholder: t('workflow.chatVariable.modal.updateInstructionsPlaceholder'), + editorId: 'memory-instruction', + nodeId: node_id, + } + }, [t]) + const scopeSchema = [ { name: 'scope', @@ -66,9 +97,7 @@ export const useMemorySchema = (nodeScopeMemoryVariable?: { nodeId: string }) => value: [ChatVarType.Memory], }, ], - selfFormProps: { - withTopDivider: true, - }, + selfFormProps: getTemplateSelfFormProps, }, { name: 'instruction', @@ -80,10 +109,7 @@ export const useMemorySchema = (nodeScopeMemoryVariable?: { nodeId: string }) => value: [ChatVarType.Memory], }, ], - selfFormProps: { - enablePromptGenerator: true, - placeholder: t('workflow.chatVariable.modal.updateInstructionsPlaceholder'), - }, + selfFormProps: getInstructionSelfFormProps, }, { name: 'strategy', @@ -242,7 +268,7 @@ export const useMemorySchema = (nodeScopeMemoryVariable?: { nodeId: string }) => ] as FormSchema[] } -export const useMemoryDefaultValues = (nodeScopeMemoryVariable?: { nodeId: string }) => { +export const useMemoryDefaultValues = (chatVar?: MemoryVariable, nodeScopeMemoryVariable?: { nodeId: string }) => { return { template: '', instruction: '', @@ -252,8 +278,8 @@ export const useMemoryDefaultValues = (nodeScopeMemoryVariable?: { nodeId: strin scope: nodeScopeMemoryVariable ? 'node' : 'app', node_id: nodeScopeMemoryVariable?.nodeId || '', term: 'session', - more: false, - model: '', + more: true, + model: undefined, schedule_mode: 'sync', end_user_visible: false, end_user_editable: false, diff --git a/web/app/components/workflow/panel/chat-variable-panel/index.tsx b/web/app/components/workflow/panel/chat-variable-panel/index.tsx index 1dafb04da6..10f01e7b2b 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/index.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/index.tsx @@ -74,8 +74,20 @@ const ChatVariablePanel = () => { const getEffectedNodes = useCallback((chatVar: ConversationVariable | MemoryVariable) => { const { getNodes } = store.getState() const allNodes = getNodes() + let valueSelector = [] + + if (chatVar.value_type === ChatVarType.Memory) { + const { node_id, id } = chatVar as MemoryVariable + valueSelector = [ + 'memory_block', + node_id ? `${node_id}_${id}` : id, + ] + } + else { + valueSelector = ['conversation', chatVar.name] + } return findUsedVarNodes( - ['conversation', chatVar.name], + valueSelector, allNodes, ) }, [store]) diff --git a/web/app/components/workflow/types.ts b/web/app/components/workflow/types.ts index 950ab89eac..47de733dbc 100644 --- a/web/app/components/workflow/types.ts +++ b/web/app/components/workflow/types.ts @@ -334,6 +334,7 @@ export type Var = { nodeId?: string isRagVariable?: boolean schemaType?: string + isMemoryVariable?: boolean memoryVariableName?: string memoryVariableNodeId?: string } diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index c61a44c34f..6a447aad93 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -183,6 +183,7 @@ const translation = { memoryModel: 'Memory Model', updateMethod: 'Update Method', editableInWebApp: 'Editable in Web App', + selectNode: 'Select node...', }, storedContent: 'Stored content', updatedAt: 'Updated at ', @@ -382,6 +383,11 @@ const translation = { desc: 'AI remembers specific information you define using custom templates', empty: 'Please add a memory variable in the Prompt', }, + deleteNodeMemoryVariableConfirmTitle: 'Delete "{{name}}"?', + deleteNodeMemoryVariableConfirmDesc: 'This variable is currently in use. Deleting it may cause unexpected behavior. This action cannot be undone.', + toLinearConfirmTitle: 'Cannot Enable Linear Mode', + toLinearConfirmDesc: 'Linear mode is incompatible with memory variables. To use linear mode, please remove the memory variable from your prompt first.', + toLinearConfirmButton: 'Got it', }, memories: { title: 'Memories', diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts index 68fa9845ae..5faf6152ec 100644 --- a/web/i18n/zh-Hans/workflow.ts +++ b/web/i18n/zh-Hans/workflow.ts @@ -183,6 +183,7 @@ const translation = { memoryModel: '记忆模型', updateMethod: '更新方法', editableInWebApp: '在 Web 应用中可编辑', + selectNode: '选择节点...', }, storedContent: '存储内容', updatedAt: '更新时间 ', @@ -382,6 +383,11 @@ const translation = { desc: 'AI 会记住你定义的特定信息', empty: '请在提示词中添加记忆变量以启用记忆块', }, + deleteNodeMemoryVariableConfirmTitle: '删除 "{{name}}"?', + deleteNodeMemoryVariableConfirmDesc: '该变量当前正在使用。删除它可能会导致意外行为。此操作无法撤销。', + toLinearConfirmTitle: '无法启用线性模式', + toLinearConfirmDesc: '线性模式与记忆变量不兼容。要使用线性模式,请先从提示词中删除记忆变量。', + toLinearConfirmButton: '知道了', }, memories: { title: '记忆',