diff --git a/web/app/components/workflow/nodes/_base/components/field.tsx b/web/app/components/workflow/nodes/_base/components/field.tsx index 5b42b0a149..76e713f932 100644 --- a/web/app/components/workflow/nodes/_base/components/field.tsx +++ b/web/app/components/workflow/nodes/_base/components/field.tsx @@ -25,7 +25,7 @@ const Filed: FC = ({
{title}
{tooltip && ( - + )} diff --git a/web/app/components/workflow/nodes/_base/components/memory-config.tsx b/web/app/components/workflow/nodes/_base/components/memory-config.tsx new file mode 100644 index 0000000000..2f8849c811 --- /dev/null +++ b/web/app/components/workflow/nodes/_base/components/memory-config.tsx @@ -0,0 +1,122 @@ +'use client' +import type { FC } from 'react' +import React, { useCallback } from 'react' +import { useTranslation } from 'react-i18next' +import produce from 'immer' +import cn from 'classnames' +import type { Memory } from '../../../types' +import Field from '@/app/components/workflow/nodes/_base/components/field' +import Switch from '@/app/components/base/switch' +import Slider from '@/app/components/base/slider' + +const i18nPrefix = 'workflow.nodes.common.memory' + +type Props = { + className?: string + readonly: boolean + payload: Memory + onChange: (memory: Memory) => void + canSetRoleName?: boolean +} + +const WINDOW_SIZE_MIN = 1 +const WINDOW_SIZE_MAX = 100 +const WINDOW_SIZE_DEFAULT = 50 + +const MemoryConfig: FC = ({ + className, + readonly, + payload, + onChange, + canSetRoleName = false, +}) => { + const { t } = useTranslation() + const handleWindowEnabledChange = useCallback((enabled: boolean) => { + const newPayload = produce(payload, (draft) => { + if (!draft.window) + draft.window = { enabled: false, size: WINDOW_SIZE_DEFAULT } + + draft.window.enabled = enabled + }) + onChange(newPayload) + }, [payload, onChange]) + + const handleWindowSizeChange = useCallback((size: number | string) => { + const newPayload = produce(payload, (draft) => { + if (!draft.window) + draft.window = { enabled: true, size: WINDOW_SIZE_DEFAULT } + let limitedSize: null | string | number = size + if (limitedSize === '') { + limitedSize = null + } + else { + limitedSize = parseInt(limitedSize as string, 10) + if (isNaN(limitedSize)) + limitedSize = WINDOW_SIZE_DEFAULT + + if (limitedSize < WINDOW_SIZE_MIN) + limitedSize = WINDOW_SIZE_MIN + + if (limitedSize > WINDOW_SIZE_MAX) + limitedSize = WINDOW_SIZE_MAX + } + + draft.window.size = limitedSize as number + }) + onChange(newPayload) + }, [payload, onChange]) + + const handleBlur = useCallback(() => { + if (payload.window.size === '' || payload.window.size === null) + handleWindowSizeChange(WINDOW_SIZE_DEFAULT) + }, [handleWindowSizeChange, payload.window?.size]) + return ( +
+ + <> + {/* window size */} +
+
+ +
{t(`${i18nPrefix}.windowSize`)}
+
+
+ + handleWindowSizeChange(e.target.value)} + onBlur={handleBlur} + disabled={readonly} + /> +
+
+ {canSetRoleName && ( +
Role name
+ )} + +
+
+ ) +} +export default React.memo(MemoryConfig) diff --git a/web/app/components/workflow/nodes/question-classifier/components/advanced-setting.tsx b/web/app/components/workflow/nodes/question-classifier/components/advanced-setting.tsx index 24b7df6746..fa52a3039c 100644 --- a/web/app/components/workflow/nodes/question-classifier/components/advanced-setting.tsx +++ b/web/app/components/workflow/nodes/question-classifier/components/advanced-setting.tsx @@ -3,25 +3,27 @@ import type { FC } from 'react' import React from 'react' import { useTranslation } from 'react-i18next' import TextEditor from '../../_base/components/editor/text-editor' +import MemoryConfig from '../../_base/components/memory-config' import type { Memory } from '@/app/components/workflow/types' - const i18nPrefix = 'workflow.nodes.questionClassifiers' type Props = { instruction: string onInstructionChange: (instruction: string) => void memory: Memory + onMemoryChange: (memory: Memory) => void } const AdvancedSetting: FC = ({ instruction, onInstructionChange, memory, + onMemoryChange, }) => { const { t } = useTranslation() return ( -
+ <> = ({
)} /> -
+ + ) } export default React.memo(AdvancedSetting) diff --git a/web/app/components/workflow/nodes/question-classifier/panel.tsx b/web/app/components/workflow/nodes/question-classifier/panel.tsx index d33b506479..c046b8e951 100644 --- a/web/app/components/workflow/nodes/question-classifier/panel.tsx +++ b/web/app/components/workflow/nodes/question-classifier/panel.tsx @@ -20,6 +20,7 @@ const Panel: FC = () => { handleQueryVarChange, handleTopicsChange, handleInstructionChange, + handleMemoryChange, } = useConfig(mockData) const model = inputs.model @@ -65,6 +66,7 @@ const Panel: FC = () => { instruction={inputs.instruction} onInstructionChange={handleInstructionChange} memory={inputs.memory} + onMemoryChange={handleMemoryChange} /> diff --git a/web/app/components/workflow/nodes/question-classifier/use-config.ts b/web/app/components/workflow/nodes/question-classifier/use-config.ts index 293c676c9a..a417d5dd03 100644 --- a/web/app/components/workflow/nodes/question-classifier/use-config.ts +++ b/web/app/components/workflow/nodes/question-classifier/use-config.ts @@ -1,6 +1,6 @@ import { useCallback, useState } from 'react' import produce from 'immer' -import type { ValueSelector } from '../../types' +import type { Memory, ValueSelector } from '../../types' import type { QuestionClassifierNodeType } from './types' const useConfig = (initInputs: QuestionClassifierNodeType) => { @@ -44,6 +44,13 @@ const useConfig = (initInputs: QuestionClassifierNodeType) => { setInputs(newInputs) }, [inputs, setInputs]) + const handleMemoryChange = useCallback((memory: Memory) => { + const newInputs = produce(inputs, (draft) => { + draft.memory = memory + }) + setInputs(newInputs) + }, [inputs, setInputs]) + return { inputs, handleModelChanged, @@ -51,6 +58,7 @@ const useConfig = (initInputs: QuestionClassifierNodeType) => { handleQueryVarChange, handleTopicsChange, handleInstructionChange, + handleMemoryChange, } } diff --git a/web/app/components/workflow/types.ts b/web/app/components/workflow/types.ts index fd23a6aefb..2b77295d7c 100644 --- a/web/app/components/workflow/types.ts +++ b/web/app/components/workflow/types.ts @@ -91,10 +91,10 @@ export enum MemoryRole { } export type Memory = { - role_prefix: MemoryRole + role_prefix?: MemoryRole window: { enabled: boolean - size: number + size: number | string | null } } diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index 5c561b6964..fc3a26ad19 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -3,6 +3,11 @@ const translation = { common: { outputVars: 'Output Variables', insertVarTip: 'Insert Variable', + memory: { + memory: 'Memory', + memoryTip: 'Chat memory settings', + windowSize: 'Window Size', + }, }, start: { required: 'required', diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts index fea6d9e6df..15d3766821 100644 --- a/web/i18n/zh-Hans/workflow.ts +++ b/web/i18n/zh-Hans/workflow.ts @@ -3,6 +3,11 @@ const translation = { common: { outputVars: '输出变量', insertVarTip: '插入变量', + memory: { + memory: '记忆', + memoryTip: '聊天记忆设置', + windowSize: '记忆窗口', + }, }, start: { required: '必填',