feat(web): llm node add memory config
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
import React, { useState, useImperativeHandle, forwardRef, useRef } from 'react';
|
import { useState, useImperativeHandle, forwardRef, useRef } from 'react';
|
||||||
import { Button, Input, Space, Typography, Tooltip, message, List } from 'antd';
|
import { Button, Space, List } from 'antd';
|
||||||
import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import type { ChatVariable, AddChatVariableRef } from '../../types';
|
import type { ChatVariable, AddChatVariableRef } from '../../types';
|
||||||
import type { ChatVariableModalRef } from './types'
|
import type { ChatVariableModalRef } from './types'
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ const EditableTable: React.FC<EditableTableProps> = ({
|
|||||||
const AddButton = ({ block = false }: { block?: boolean }) => (
|
const AddButton = ({ block = false }: { block?: boolean }) => (
|
||||||
<Button
|
<Button
|
||||||
type={block ? "dashed" : "text"}
|
type={block ? "dashed" : "text"}
|
||||||
icon={<PlusOutlined />}
|
icon={block ? undefined : <PlusOutlined />}
|
||||||
onClick={() => add(createNewRow())}
|
onClick={() => add(createNewRow())}
|
||||||
size="small"
|
size="small"
|
||||||
block={block}
|
block={block}
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import { type FC } from "react";
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { Form, Row, Col, Divider, Switch, Slider } from 'antd'
|
||||||
|
import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin'
|
||||||
|
import MessageEditor from '../MessageEditor'
|
||||||
|
|
||||||
|
const MemoryConfig: FC<{ options: Suggestion[]; parentName: string; }> = ({
|
||||||
|
options,
|
||||||
|
parentName
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const form = Form.useFormInstance();
|
||||||
|
const values = Form.useWatch([], form) || {}
|
||||||
|
|
||||||
|
console.log('MemoryConfig', values)
|
||||||
|
|
||||||
|
const handleChangeEnable = (value: boolean) => {
|
||||||
|
if (value) {
|
||||||
|
form.setFieldsValue({
|
||||||
|
memory: {
|
||||||
|
...form.getFieldValue(parentName),
|
||||||
|
enable_window: false,
|
||||||
|
window_size: 20,
|
||||||
|
messages: "{{sys.message}}"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{values?.memory?.enable && <>
|
||||||
|
<div className="rb:flex rb:items-center rb:justify-between rb:py-1.5 rb:px-2 rb:bg-[#F6F8FC] rb:rounded-md rb:mb-2">
|
||||||
|
{t('workflow.config.llm.memory')}
|
||||||
|
<span>{t('workflow.config.llm.inner')}</span>
|
||||||
|
</div>
|
||||||
|
<Form.Item layout="horizontal" name={[parentName, 'messages']}>
|
||||||
|
<MessageEditor
|
||||||
|
title="USER"
|
||||||
|
isArray={false}
|
||||||
|
parentName={[parentName, 'messages']}
|
||||||
|
options={options}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
</>}
|
||||||
|
<Form.Item layout="horizontal" name={[parentName, 'enable']} label={t('workflow.config.llm.memory')}>
|
||||||
|
<Switch onChange={handleChangeEnable} />
|
||||||
|
</Form.Item>
|
||||||
|
{values?.memory?.enable && <>
|
||||||
|
<Row className="rb:mb-3">
|
||||||
|
<Col span={10}>
|
||||||
|
<Form.Item layout="horizontal" name={[parentName, 'enable_window']} noStyle>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<span className="rb:ml-2">{t('workflow.config.llm.enable_window')}</span>
|
||||||
|
</Col>
|
||||||
|
<Col span={14}>
|
||||||
|
<Form.Item layout="horizontal" name={[parentName, 'window_size']} noStyle>
|
||||||
|
<Slider min={1} max={100} step={1} className="rb:my-0!" disabled={!values?.memory?.enable_window} />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</>}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default MemoryConfig;
|
||||||
@@ -127,7 +127,7 @@ const MessageEditor: FC<MessageEditor> = ({
|
|||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<Form.Item>
|
<Form.Item noStyle>
|
||||||
<Button type="dashed" onClick={() => handleAdd(add)} block>
|
<Button type="dashed" onClick={() => handleAdd(add)} block>
|
||||||
+{t('workflow.addMessage')}
|
+{t('workflow.addMessage')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import ConditionList from './ConditionList'
|
|||||||
import CycleVarsList from './CycleVarsList'
|
import CycleVarsList from './CycleVarsList'
|
||||||
import AssignmentList from './AssignmentList'
|
import AssignmentList from './AssignmentList'
|
||||||
import ToolConfig from './ToolConfig'
|
import ToolConfig from './ToolConfig'
|
||||||
|
import MemoryConfig from './MemoryConfig'
|
||||||
// import { calculateVariableList } from './utils/variableListCalculator'
|
// import { calculateVariableList } from './utils/variableListCalculator'
|
||||||
|
|
||||||
interface PropertiesProps {
|
interface PropertiesProps {
|
||||||
@@ -1230,6 +1231,20 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (config.type === 'memoryConfig') {
|
||||||
|
return (
|
||||||
|
<Form.Item
|
||||||
|
key={key}
|
||||||
|
name={key}
|
||||||
|
noStyle
|
||||||
|
>
|
||||||
|
<MemoryConfig
|
||||||
|
parentName={key}
|
||||||
|
options={getFilteredVariableList('llm')}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
|||||||
@@ -135,6 +135,14 @@ export const nodeLibrary: NodeLibrary[] = [
|
|||||||
readonly: true
|
readonly: true
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
memory: {
|
||||||
|
type: 'memoryConfig',
|
||||||
|
defaultValue: {
|
||||||
|
enable: false,
|
||||||
|
enable_window: false,
|
||||||
|
window_size: 20
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -750,10 +758,6 @@ export const outputVariable: { [key: string]: OutputVariable } = {
|
|||||||
{ name: "body", type: "string" },
|
{ name: "body", type: "string" },
|
||||||
{ name: "status_code", type: "number" },
|
{ name: "status_code", type: "number" },
|
||||||
],
|
],
|
||||||
error: [
|
|
||||||
{ name: "error_message", type: "string" },
|
|
||||||
{ name: "error_type", type: "string" },
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
'tool': {
|
'tool': {
|
||||||
default: [
|
default: [
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Graph, Node, MiniMap, Snapline, Clipboard, Keyboard, type Edge } from '
|
|||||||
import { register } from '@antv/x6-react-shape';
|
import { register } from '@antv/x6-react-shape';
|
||||||
|
|
||||||
import { nodeRegisterLibrary, graphNodeLibrary, nodeLibrary, portMarkup, portAttrs } from '../constant';
|
import { nodeRegisterLibrary, graphNodeLibrary, nodeLibrary, portMarkup, portAttrs } from '../constant';
|
||||||
import type { WorkflowConfig, NodeProperties } from '../types';
|
import type { WorkflowConfig, NodeProperties, ChatVariable } from '../types';
|
||||||
import { getWorkflowConfig, saveWorkflowConfig } from '@/api/application'
|
import { getWorkflowConfig, saveWorkflowConfig } from '@/api/application'
|
||||||
import type { PortMetadata } from '@antv/x6/lib/model/port';
|
import type { PortMetadata } from '@antv/x6/lib/model/port';
|
||||||
|
|
||||||
@@ -35,6 +35,8 @@ export interface UseWorkflowGraphReturn {
|
|||||||
copyEvent: () => boolean | void;
|
copyEvent: () => boolean | void;
|
||||||
parseEvent: () => boolean | void;
|
parseEvent: () => boolean | void;
|
||||||
handleSave: (flag?: boolean) => Promise<unknown>;
|
handleSave: (flag?: boolean) => Promise<unknown>;
|
||||||
|
chatVariables: ChatVariable[];
|
||||||
|
setChatVariables: React.Dispatch<React.SetStateAction<ChatVariable[]>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const edge_color = '#155EEF';
|
export const edge_color = '#155EEF';
|
||||||
@@ -54,6 +56,7 @@ export const useWorkflowGraph = ({
|
|||||||
const [canRedo, setCanRedo] = useState(false);
|
const [canRedo, setCanRedo] = useState(false);
|
||||||
const [isHandMode, setIsHandMode] = useState(false);
|
const [isHandMode, setIsHandMode] = useState(false);
|
||||||
const [config, setConfig] = useState<WorkflowConfig | null>(null);
|
const [config, setConfig] = useState<WorkflowConfig | null>(null);
|
||||||
|
const [chatVariables, setChatVariables] = useState<ChatVariable[]>([])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getConfig()
|
getConfig()
|
||||||
@@ -63,16 +66,15 @@ export const useWorkflowGraph = ({
|
|||||||
getWorkflowConfig(id)
|
getWorkflowConfig(id)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
const { variables, ...rest } = res as WorkflowConfig
|
const { variables, ...rest } = res as WorkflowConfig
|
||||||
setConfig({
|
const initChatVariables = variables.map(v => {
|
||||||
...rest,
|
const { default: _, ...cleanV } = v
|
||||||
variables: variables.map(v => {
|
return {
|
||||||
const { default: _, ...cleanV } = v
|
...cleanV,
|
||||||
return {
|
defaultValue: v.default ?? ''
|
||||||
...cleanV,
|
}
|
||||||
defaultValue: v.default ?? ''
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
setChatVariables(initChatVariables)
|
||||||
|
setConfig({ ...rest, variables: initChatVariables })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +96,17 @@ export const useWorkflowGraph = ({
|
|||||||
|
|
||||||
if (nodeLibraryConfig?.config) {
|
if (nodeLibraryConfig?.config) {
|
||||||
Object.keys(nodeLibraryConfig.config).forEach(key => {
|
Object.keys(nodeLibraryConfig.config).forEach(key => {
|
||||||
if (key === 'knowledge_retrieval' && nodeLibraryConfig.config && nodeLibraryConfig.config[key]) {
|
if (key === 'memory' && nodeLibraryConfig.config && nodeLibraryConfig.config[key]) {
|
||||||
|
const { memory, messages } = config as any;
|
||||||
|
if (memory?.enable && messages && messages.length > 0) {
|
||||||
|
const lastMessage = messages[messages.length - 1]
|
||||||
|
nodeLibraryConfig.config[key].defaultValue = {
|
||||||
|
...memory,
|
||||||
|
messages: lastMessage.content
|
||||||
|
}
|
||||||
|
nodeLibraryConfig.config.messages.defaultValue.splice(-1, 1)
|
||||||
|
}
|
||||||
|
} else if (key === 'knowledge_retrieval' && nodeLibraryConfig.config && nodeLibraryConfig.config[key]) {
|
||||||
const { query, ...rest } = config
|
const { query, ...rest } = config
|
||||||
nodeLibraryConfig.config[key].defaultValue = {
|
nodeLibraryConfig.config[key].defaultValue = {
|
||||||
...rest
|
...rest
|
||||||
@@ -917,13 +929,13 @@ export const useWorkflowGraph = ({
|
|||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
...config,
|
...config,
|
||||||
variables: config.variables.map(v => {
|
variables: chatVariables.map(v => {
|
||||||
const { defaultValue, ...cleanV } = v
|
const { defaultValue, ...cleanV } = v
|
||||||
return {
|
return {
|
||||||
...cleanV,
|
...cleanV,
|
||||||
default: defaultValue ?? ''
|
default: defaultValue ?? ''
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
nodes: nodes.map((node: Node) => {
|
nodes: nodes.map((node: Node) => {
|
||||||
const data = node.getData();
|
const data = node.getData();
|
||||||
const position = node.getPosition();
|
const position = node.getPosition();
|
||||||
@@ -931,7 +943,15 @@ export const useWorkflowGraph = ({
|
|||||||
|
|
||||||
if (data.config) {
|
if (data.config) {
|
||||||
Object.keys(data.config).forEach(key => {
|
Object.keys(data.config).forEach(key => {
|
||||||
if (data.config[key] && 'defaultValue' in data.config[key] && key === 'group_variables') {
|
if (key === 'memory' && data.config[key] && 'defaultValue' in data.config[key]) {
|
||||||
|
const { messages, ...rest } = data.config[key].defaultValue
|
||||||
|
let memoryMessage = { role: 'USER', content: data.config[key].defaultValue.messages }
|
||||||
|
itemConfig = {
|
||||||
|
...itemConfig,
|
||||||
|
messages: rest.enable ? [...itemConfig.messages, memoryMessage] : itemConfig.messages,
|
||||||
|
memory: { ...rest },
|
||||||
|
}
|
||||||
|
} else if (data.config[key] && 'defaultValue' in data.config[key] && key === 'group_variables') {
|
||||||
let group_variables = data.config.group.defaultValue ? {} : data.config[key].defaultValue
|
let group_variables = data.config.group.defaultValue ? {} : data.config[key].defaultValue
|
||||||
if (data.config.group.defaultValue) {
|
if (data.config.group.defaultValue) {
|
||||||
data.config[key].defaultValue.map((vo: any) => {
|
data.config[key].defaultValue.map((vo: any) => {
|
||||||
@@ -1077,5 +1097,7 @@ export const useWorkflowGraph = ({
|
|||||||
copyEvent,
|
copyEvent,
|
||||||
parseEvent,
|
parseEvent,
|
||||||
handleSave,
|
handleSave,
|
||||||
|
chatVariables,
|
||||||
|
setChatVariables
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import PortClickHandler from './components/PortClickHandler';
|
|||||||
import { useWorkflowGraph } from './hooks/useWorkflowGraph';
|
import { useWorkflowGraph } from './hooks/useWorkflowGraph';
|
||||||
import type { WorkflowRef } from '@/views/ApplicationConfig/types'
|
import type { WorkflowRef } from '@/views/ApplicationConfig/types'
|
||||||
import Chat from './components/Chat/Chat';
|
import Chat from './components/Chat/Chat';
|
||||||
import type { ChatRef, AddChatVariableRef, ChatVariable } from './types'
|
import type { ChatRef, AddChatVariableRef } from './types'
|
||||||
import arrowIcon from '@/assets/images/workflow/arrow.png'
|
import arrowIcon from '@/assets/images/workflow/arrow.png'
|
||||||
import AddChatVariable from './components/AddChatVariable';
|
import AddChatVariable from './components/AddChatVariable';
|
||||||
|
|
||||||
@@ -21,7 +21,6 @@ const Workflow = forwardRef<WorkflowRef>((_props, ref) => {
|
|||||||
// 使用自定义Hook初始化工作流图
|
// 使用自定义Hook初始化工作流图
|
||||||
const {
|
const {
|
||||||
config,
|
config,
|
||||||
setConfig,
|
|
||||||
graphRef,
|
graphRef,
|
||||||
selectedNode,
|
selectedNode,
|
||||||
setSelectedNode,
|
setSelectedNode,
|
||||||
@@ -38,6 +37,8 @@ const Workflow = forwardRef<WorkflowRef>((_props, ref) => {
|
|||||||
copyEvent,
|
copyEvent,
|
||||||
parseEvent,
|
parseEvent,
|
||||||
handleSave,
|
handleSave,
|
||||||
|
chatVariables,
|
||||||
|
setChatVariables
|
||||||
} = useWorkflowGraph({ containerRef, miniMapRef });
|
} = useWorkflowGraph({ containerRef, miniMapRef });
|
||||||
|
|
||||||
const onDragOver = (event: React.DragEvent) => {
|
const onDragOver = (event: React.DragEvent) => {
|
||||||
@@ -52,15 +53,6 @@ const Workflow = forwardRef<WorkflowRef>((_props, ref) => {
|
|||||||
const addVariable = () => {
|
const addVariable = () => {
|
||||||
addChatVariableRef.current?.handleOpen()
|
addChatVariableRef.current?.handleOpen()
|
||||||
}
|
}
|
||||||
const handleUpdateChatVariable = (variables: ChatVariable[]) => {
|
|
||||||
setConfig(prev => {
|
|
||||||
if (!prev) return null
|
|
||||||
return {
|
|
||||||
...prev,
|
|
||||||
variables
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
handleSave,
|
handleSave,
|
||||||
@@ -125,8 +117,8 @@ const Workflow = forwardRef<WorkflowRef>((_props, ref) => {
|
|||||||
|
|
||||||
<AddChatVariable
|
<AddChatVariable
|
||||||
ref={addChatVariableRef}
|
ref={addChatVariableRef}
|
||||||
variables={config?.variables}
|
variables={chatVariables}
|
||||||
onChange={handleUpdateChatVariable}
|
onChange={setChatVariables}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user