Skip to content

Commit 62d4939

Browse files
authored
[elsa] feat: add reply node (#324)
* [elsa] feat: add reply node * [elsa] Update according to reviews
1 parent 3c99c16 commit 62d4939

File tree

10 files changed

+457
-2
lines changed

10 files changed

+457
-2
lines changed

framework/elsa/fit-elsa-react/src/App.jsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ function App({i18n}) {
5353
testCodeUrl: '',
5454
},
5555
});
56+
configs.push({
57+
node: 'replyNodeState', urls: {
58+
testCodeUrl: '',
59+
},
60+
});
5661
configs.push({
5762
node: 'evaluationAlgorithmsNodeState', urls: {
5863
evaluationAlgorithmsUrl: '',
@@ -282,6 +287,7 @@ function App({i18n}) {
282287
<Button onClick={() => window.agent.createNodeByPosition('queryOptimizationNodeState', {x:100, y:100}, {uniqueName : ''})}>创建问题优化节点</Button>
283288
<Button onClick={() => window.agent.createNodeByPosition('codeNodeState', {x:100, y:100}, {uniqueName : ''})}>创建code节点</Button>
284289
<Button onClick={() => window.agent.createNodeByPosition('textConcatenateNodeState', {x:100, y:100}, {uniqueName : ''})}>创建文本拼接节点</Button>
290+
<Button onClick={() => window.agent.createNodeByPosition('replyNodeState', {x:100, y:100}, {uniqueName : ''})}>创建直接回复节点</Button>
285291
<Button onClick={() => {
286292
const nodeTypes = ['endNodeEnd',
287293
'retrievalNodeState',
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import {JadeInputForm} from '../common/JadeInputForm.jsx';
4+
import {useDispatch, useShapeContext} from '@/components/DefaultRoot.jsx';
5+
import {TemplatePanel} from "@/components/common/TemplatePanel.jsx";
6+
import {Trans, useTranslation} from "react-i18next";
7+
8+
ReplyWrapper.propTypes = {
9+
data: PropTypes.object.isRequired,
10+
shapeStatus: PropTypes.object,
11+
};
12+
13+
export default function ReplyWrapper({data, shapeStatus}) {
14+
const dispatch = useDispatch();
15+
const shape = useShapeContext();
16+
const {t} = useTranslation();
17+
const template = data.inputParams.find(item => item.name === 'template');
18+
19+
/**
20+
* 初始化数据
21+
*
22+
* @return {*}
23+
*/
24+
const initItems = () => {
25+
return data.inputParams.find(item => item.name === "variables").value
26+
};
27+
28+
/**
29+
* 添加输入的变量
30+
*
31+
* @param id id 数据id
32+
*/
33+
const addItem = (id) => {
34+
// 代码节点入参最大数量为20
35+
if (data.inputParams.find(item => item.name === 'variables').value.length < 20) {
36+
dispatch({type: 'addInputParam', id: id});
37+
}
38+
};
39+
40+
/**
41+
* 更新入参变量属性名或者类型
42+
*
43+
* @param id 数据id
44+
* @param value 新值
45+
*/
46+
const updateItem = (id, value) => {
47+
dispatch({type: 'editInputParam', id: id, newValue: value});
48+
};
49+
50+
/**
51+
* 删除input
52+
*
53+
* @param id 需要删除的数据id
54+
*/
55+
const deleteItem = (id) => {
56+
dispatch({type: 'deleteInputParam', id: id});
57+
};
58+
59+
return (
60+
<div>
61+
<JadeInputForm
62+
shapeStatus={shapeStatus}
63+
items={initItems()}
64+
addItem={addItem}
65+
updateItem={updateItem}
66+
deleteItem={deleteItem}
67+
content={
68+
(<div className={'jade-font-size'} style={{lineHeight: '1.2'}}>
69+
<Trans i18nKey='templateInputPopover' components={{p: <p/>}}/>
70+
</div>)}
71+
maxInputLength={1000}
72+
/>
73+
<TemplatePanel
74+
disabled={shapeStatus.disabled}
75+
template={template}
76+
title={t("replyTextLabel")}
77+
placeHolder={t("promptPlaceHolder")}
78+
name={"reply"}
79+
onChange={(templateText) => {
80+
dispatch({type: 'changeTemplate', id: template.id, value: templateText});
81+
}}
82+
labelName={t("replyTextLabel")}
83+
/>
84+
</div>
85+
);
86+
}
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved.
3+
* This file is a part of the ModelEngine Project.
4+
* Licensed under the MIT License. See License.txt in the project root for license information.
5+
*--------------------------------------------------------------------------------------------*/
6+
7+
/**
8+
* changeTemplate 事件处理器.
9+
*
10+
* @return {{}} 处理器对象.
11+
* @constructor
12+
*/
13+
export const ChangeTemplateReducer = () => {
14+
const self = {};
15+
self.type = 'changeTemplate';
16+
17+
/**
18+
* 处理方法.
19+
*
20+
* @param config 配置数据.
21+
* @param action 事件对象.
22+
* @return {*} 处理之后的数据.
23+
*/
24+
self.reduce = (config, action) => {
25+
const newConfig = {};
26+
Object.entries(config).forEach(([key, value]) => {
27+
if (key === 'inputParams') {
28+
newConfig[key] = value.map(item => {
29+
if (item.id === action.id) {
30+
return {
31+
...item, value: action.value,
32+
};
33+
} else {
34+
return item;
35+
}
36+
});
37+
} else {
38+
newConfig[key] = value;
39+
}
40+
});
41+
return newConfig;
42+
};
43+
44+
return self;
45+
};
46+
47+
/**
48+
* addInputParam 事件处理器.
49+
*
50+
* @return {{}} 处理器对象.
51+
* @constructor
52+
*/
53+
export const AddInputParamReducer = () => {
54+
const self = {};
55+
self.type = 'addInputParam';
56+
57+
/**
58+
* 处理方法.
59+
*
60+
* @param config 配置数据.
61+
* @param action 事件对象.
62+
* @return {*} 处理之后的数据.
63+
*/
64+
self.reduce = (config, action) => {
65+
const newConfig = {};
66+
Object.entries(config).forEach(([key, value]) => {
67+
if (key === 'inputParams') {
68+
newConfig[key] = value.map(item => {
69+
if (item.name === 'variables') {
70+
return {
71+
...item,
72+
value: [
73+
...item.value,
74+
{
75+
id: action.id,
76+
name: undefined,
77+
type: 'String',
78+
from: 'Reference',
79+
value: '',
80+
referenceNode: '',
81+
referenceId: '',
82+
referenceKey: '',
83+
}
84+
]
85+
};
86+
} else {
87+
return item;
88+
}
89+
});
90+
} else {
91+
newConfig[key] = value;
92+
}
93+
});
94+
return newConfig;
95+
};
96+
97+
98+
return self;
99+
};
100+
101+
/**
102+
* deleteInputParam 事件处理器.
103+
*
104+
* @return {{}} 处理器对象.
105+
* @constructor
106+
*/
107+
export const DeleteInputParamReducer = () => {
108+
const self = {};
109+
self.type = 'deleteInputParam';
110+
111+
/**
112+
* 处理方法.
113+
*
114+
* @param config 配置数据.
115+
* @param action 事件对象.
116+
* @return {*} 处理之后的数据.
117+
*/
118+
self.reduce = (config, action) => {
119+
const newConfig = {};
120+
Object.entries(config).forEach(([key, value]) => {
121+
if (key === 'inputParams') {
122+
newConfig[key] = value.map(item => {
123+
if (item.name === 'variables') {
124+
return {
125+
...item,
126+
value: item.value.filter((item) => item.id !== action.id),
127+
};
128+
} else {
129+
return item;
130+
}
131+
});
132+
} else {
133+
newConfig[key] = value;
134+
}
135+
});
136+
return newConfig;
137+
};
138+
139+
return self;
140+
};
141+
142+
/**
143+
* deleteInputParam 事件处理器.
144+
*
145+
* @return {{}} 处理器对象.
146+
* @constructor
147+
*/
148+
export const EditInputParamReducer = () => {
149+
const self = {};
150+
self.type = 'editInputParam';
151+
152+
/**
153+
* 处理方法.
154+
*
155+
* @param config 配置数据.
156+
* @param action 事件对象.
157+
* @return {*} 处理之后的数据.
158+
*/
159+
self.reduce = (config, action) => {
160+
const newConfig = {};
161+
Object.entries(config).forEach(([key, value]) => {
162+
if (key === 'inputParams') {
163+
newConfig[key] = value.map(item => {
164+
if (item.name === 'variables') {
165+
return {
166+
...item,
167+
value: item.value.map(inputItem => {
168+
if (inputItem.id === action.id) {
169+
let updatedInputItem = {...inputItem};
170+
action.newValue.map((param) => {
171+
updatedInputItem[param.key] = param.value;
172+
});
173+
return updatedInputItem;
174+
} else {
175+
return inputItem;
176+
}
177+
}),
178+
};
179+
} else {
180+
return item;
181+
}
182+
});
183+
} else {
184+
newConfig[key] = value;
185+
}
186+
});
187+
return newConfig;
188+
};
189+
190+
return self;
191+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved.
3+
* This file is a part of the ModelEngine Project.
4+
* Licensed under the MIT License. See License.txt in the project root for license information.
5+
*--------------------------------------------------------------------------------------------*/
6+
7+
import ReplyWrapper from './ReplyWrapper.jsx';
8+
import {v4 as uuidv4} from 'uuid';
9+
import {defaultComponent} from '@/components/defaultComponent.js';
10+
import {
11+
AddInputParamReducer,
12+
ChangeTemplateReducer,
13+
DeleteInputParamReducer,
14+
EditInputParamReducer
15+
} from "@/components/replyNode/reducers/reducer.js";
16+
17+
/**
18+
* 直接回复节点组件
19+
*
20+
* @param jadeConfig
21+
* @param shape 图形对象.
22+
*/
23+
export const replyNodeComponent = (jadeConfig, shape) => {
24+
const self = defaultComponent(jadeConfig);
25+
const addReducer = (map, reducer) => map.set(reducer.type, reducer);
26+
const builtInReducers = new Map();
27+
addReducer(builtInReducers, AddInputParamReducer());
28+
addReducer(builtInReducers, EditInputParamReducer());
29+
addReducer(builtInReducers, ChangeTemplateReducer());
30+
addReducer(builtInReducers, DeleteInputParamReducer());
31+
32+
/**
33+
* 必须.
34+
*/
35+
self.getJadeConfig = () => {
36+
return jadeConfig ? jadeConfig : {
37+
inputParams: [
38+
{
39+
id: uuidv4(), name: 'variables', type: 'Object', from: 'Expand', value: [
40+
{
41+
id: uuidv4(),
42+
name: undefined,
43+
type: 'String',
44+
from: 'Reference',
45+
value: '',
46+
referenceNode: '',
47+
referenceId: '',
48+
referenceKey: '',
49+
}
50+
],
51+
},
52+
{
53+
id: uuidv4(),
54+
name: 'template',
55+
type: 'String',
56+
from: 'Input',
57+
value: ''
58+
},
59+
],
60+
outputParams: [],
61+
tempReference: {},
62+
};
63+
};
64+
65+
/**
66+
* 必须.
67+
*
68+
* @param shapeStatus 图形状态集合.
69+
* @param data 数据.
70+
*/
71+
self.getReactComponents = (shapeStatus, data) => {
72+
return (<><ReplyWrapper shapeStatus={shapeStatus} data={data}/></>);
73+
};
74+
75+
/**
76+
* 必须.
77+
*/
78+
const reducers = self.reducers;
79+
self.reducers = (data, action) => {
80+
const reducer = builtInReducers.get(action.type);
81+
return reducer ? reducer.reduce(data, action) : reducers.apply(self, [data, action]);
82+
};
83+
84+
return self;
85+
};

0 commit comments

Comments
 (0)