balibabu
commited on
Commit
·
e618152
1
Parent(s):
481246c
Feat: Add Template operator #3556 (#3559)
Browse files### What problem does this PR solve?
Feat: Add Template operator #3560
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
- web/src/assets/svg/template.svg +6 -0
- web/src/components/editable-cell.tsx +1 -1
- web/src/locales/en.ts +5 -2
- web/src/locales/zh-traditional.ts +2 -0
- web/src/locales/zh.ts +2 -0
- web/src/pages/chat/chat-id-modal/index.tsx +1 -1
- web/src/pages/flow/canvas/index.tsx +2 -0
- web/src/pages/flow/canvas/node/template-node.tsx +68 -0
- web/src/pages/flow/constant.tsx +16 -0
- web/src/pages/flow/flow-drawer/index.tsx +2 -0
- web/src/pages/flow/flow-id-modal/index.tsx +1 -1
- web/src/pages/flow/form/generate-form/dynamic-parameters.tsx +2 -0
- web/src/pages/flow/form/template-form/index.tsx +26 -0
- web/src/pages/flow/hooks.tsx +2 -0
web/src/assets/svg/template.svg
ADDED
|
web/src/components/editable-cell.tsx
CHANGED
@@ -78,7 +78,7 @@ export const EditableCell: React.FC<EditableCellProps> = ({
|
|
78 |
if (editable) {
|
79 |
childNode = editing ? (
|
80 |
<Form.Item
|
81 |
-
style={{ margin: 0,
|
82 |
name={dataIndex}
|
83 |
rules={[
|
84 |
{
|
|
|
78 |
if (editable) {
|
79 |
childNode = editing ? (
|
80 |
<Form.Item
|
81 |
+
style={{ margin: 0, minWidth: 70 }}
|
82 |
name={dataIndex}
|
83 |
rules={[
|
84 |
{
|
web/src/locales/en.ts
CHANGED
@@ -105,10 +105,10 @@ export default {
|
|
105 |
'Conduct a retrieval test to check if RAGFlow can recover the intended content for the LLM.',
|
106 |
similarityThreshold: 'Similarity threshold',
|
107 |
similarityThresholdTip:
|
108 |
-
|
109 |
vectorSimilarityWeight: 'Keywords similarity weight',
|
110 |
vectorSimilarityWeightTip:
|
111 |
-
|
112 |
testText: 'Test text',
|
113 |
testTextPlaceholder: 'Input your question here!',
|
114 |
testingLabel: 'Testing',
|
@@ -1037,6 +1037,9 @@ The above is the content you need to summarize.`,
|
|
1037 |
optional: 'Optional',
|
1038 |
pasteFileLink: 'Paste file link',
|
1039 |
testRun: 'Test Run',
|
|
|
|
|
|
|
1040 |
},
|
1041 |
footer: {
|
1042 |
profile: 'All rights reserved @ React',
|
|
|
105 |
'Conduct a retrieval test to check if RAGFlow can recover the intended content for the LLM.',
|
106 |
similarityThreshold: 'Similarity threshold',
|
107 |
similarityThresholdTip:
|
108 |
+
'RAGFlow employs either a combination of weighted keyword similarity and weighted vector cosine similarity, or a combination of weighted keyword similarity and weighted reranking score during retrieval. This parameter sets the threshold for similarities between the user query and chunks. Any chunk with a similarity score below this threshold will be excluded from the results.',
|
109 |
vectorSimilarityWeight: 'Keywords similarity weight',
|
110 |
vectorSimilarityWeightTip:
|
111 |
+
'This sets the weight of keyword similarity in the combined similarity score, either used with vector cosine similarity or with reranking score. The total of the two weights must equal 1.0.',
|
112 |
testText: 'Test text',
|
113 |
testTextPlaceholder: 'Input your question here!',
|
114 |
testingLabel: 'Testing',
|
|
|
1037 |
optional: 'Optional',
|
1038 |
pasteFileLink: 'Paste file link',
|
1039 |
testRun: 'Test Run',
|
1040 |
+
template: 'Template',
|
1041 |
+
templateDescription:
|
1042 |
+
'This component is used for typesetting the outputs of various components.',
|
1043 |
},
|
1044 |
footer: {
|
1045 |
profile: 'All rights reserved @ React',
|
web/src/locales/zh-traditional.ts
CHANGED
@@ -997,6 +997,8 @@ export default {
|
|
997 |
optional: '可選項',
|
998 |
pasteFileLink: '貼上文件連結',
|
999 |
testRun: '試運行',
|
|
|
|
|
1000 |
},
|
1001 |
footer: {
|
1002 |
profile: '“保留所有權利 @ react”',
|
|
|
997 |
optional: '可選項',
|
998 |
pasteFileLink: '貼上文件連結',
|
999 |
testRun: '試運行',
|
1000 |
+
template: '模板轉換',
|
1001 |
+
templateDescription: '此元件用於排版各種元件的輸出。 ',
|
1002 |
},
|
1003 |
footer: {
|
1004 |
profile: '“保留所有權利 @ react”',
|
web/src/locales/zh.ts
CHANGED
@@ -1017,6 +1017,8 @@ export default {
|
|
1017 |
optional: '可选项',
|
1018 |
pasteFileLink: '粘贴文件链接',
|
1019 |
testRun: '试运行',
|
|
|
|
|
1020 |
},
|
1021 |
footer: {
|
1022 |
profile: 'All rights reserved @ React',
|
|
|
1017 |
optional: '可选项',
|
1018 |
pasteFileLink: '粘贴文件链接',
|
1019 |
testRun: '试运行',
|
1020 |
+
template: '模板转换',
|
1021 |
+
templateDescription: '该组件用于排版各种组件的输出。',
|
1022 |
},
|
1023 |
footer: {
|
1024 |
profile: 'All rights reserved @ React',
|
web/src/pages/chat/chat-id-modal/index.tsx
CHANGED
@@ -26,7 +26,7 @@ const ChatIdModal = ({
|
|
26 |
{id}
|
27 |
</Paragraph>
|
28 |
<Link
|
29 |
-
href="https://ragflow.io/docs/dev/http_api_reference#create-session"
|
30 |
target="_blank"
|
31 |
>
|
32 |
{t('howUseId')}
|
|
|
26 |
{id}
|
27 |
</Paragraph>
|
28 |
<Link
|
29 |
+
href="https://ragflow.io/docs/dev/http_api_reference#create-session-with-chat-assistant"
|
30 |
target="_blank"
|
31 |
>
|
32 |
{t('howUseId')}
|
web/src/pages/flow/canvas/index.tsx
CHANGED
@@ -35,6 +35,7 @@ import { RelevantNode } from './node/relevant-node';
|
|
35 |
import { RetrievalNode } from './node/retrieval-node';
|
36 |
import { RewriteNode } from './node/rewrite-node';
|
37 |
import { SwitchNode } from './node/switch-node';
|
|
|
38 |
|
39 |
const nodeTypes = {
|
40 |
ragNode: RagNode,
|
@@ -50,6 +51,7 @@ const nodeTypes = {
|
|
50 |
rewriteNode: RewriteNode,
|
51 |
keywordNode: KeywordNode,
|
52 |
invokeNode: InvokeNode,
|
|
|
53 |
};
|
54 |
|
55 |
const edgeTypes = {
|
|
|
35 |
import { RetrievalNode } from './node/retrieval-node';
|
36 |
import { RewriteNode } from './node/rewrite-node';
|
37 |
import { SwitchNode } from './node/switch-node';
|
38 |
+
import { TemplateNode } from './node/template-node';
|
39 |
|
40 |
const nodeTypes = {
|
41 |
ragNode: RagNode,
|
|
|
51 |
rewriteNode: RewriteNode,
|
52 |
keywordNode: KeywordNode,
|
53 |
invokeNode: InvokeNode,
|
54 |
+
templateNode: TemplateNode,
|
55 |
};
|
56 |
|
57 |
const edgeTypes = {
|
web/src/pages/flow/canvas/node/template-node.tsx
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Flex } from 'antd';
|
2 |
+
import classNames from 'classnames';
|
3 |
+
import { get } from 'lodash';
|
4 |
+
import { Handle, NodeProps, Position } from 'reactflow';
|
5 |
+
import { useGetComponentLabelByValue } from '../../hooks';
|
6 |
+
import { IGenerateParameter, NodeData } from '../../interface';
|
7 |
+
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
|
8 |
+
import NodeHeader from './node-header';
|
9 |
+
|
10 |
+
import styles from './index.less';
|
11 |
+
|
12 |
+
export function TemplateNode({
|
13 |
+
id,
|
14 |
+
data,
|
15 |
+
isConnectable = true,
|
16 |
+
selected,
|
17 |
+
}: NodeProps<NodeData>) {
|
18 |
+
const parameters: IGenerateParameter[] = get(data, 'form.parameters', []);
|
19 |
+
const getLabel = useGetComponentLabelByValue(id);
|
20 |
+
|
21 |
+
return (
|
22 |
+
<section
|
23 |
+
className={classNames(styles.logicNode, {
|
24 |
+
[styles.selectedNode]: selected,
|
25 |
+
})}
|
26 |
+
>
|
27 |
+
<Handle
|
28 |
+
id="c"
|
29 |
+
type="source"
|
30 |
+
position={Position.Left}
|
31 |
+
isConnectable={isConnectable}
|
32 |
+
className={styles.handle}
|
33 |
+
style={LeftHandleStyle}
|
34 |
+
></Handle>
|
35 |
+
<Handle
|
36 |
+
type="source"
|
37 |
+
position={Position.Right}
|
38 |
+
isConnectable={isConnectable}
|
39 |
+
className={styles.handle}
|
40 |
+
style={RightHandleStyle}
|
41 |
+
id="b"
|
42 |
+
></Handle>
|
43 |
+
|
44 |
+
<NodeHeader
|
45 |
+
id={id}
|
46 |
+
name={data.name}
|
47 |
+
label={data.label}
|
48 |
+
className={styles.nodeHeader}
|
49 |
+
></NodeHeader>
|
50 |
+
|
51 |
+
<Flex gap={8} vertical className={styles.generateParameters}>
|
52 |
+
{parameters.map((x) => (
|
53 |
+
<Flex
|
54 |
+
key={x.id}
|
55 |
+
align="center"
|
56 |
+
gap={6}
|
57 |
+
className={styles.conditionBlock}
|
58 |
+
>
|
59 |
+
<label htmlFor="">{x.key}</label>
|
60 |
+
<span className={styles.parameterValue}>
|
61 |
+
{getLabel(x.component_id)}
|
62 |
+
</span>
|
63 |
+
</Flex>
|
64 |
+
))}
|
65 |
+
</Flex>
|
66 |
+
</section>
|
67 |
+
);
|
68 |
+
}
|
web/src/pages/flow/constant.tsx
CHANGED
@@ -19,6 +19,7 @@ import { ReactComponent as NoteIcon } from '@/assets/svg/note.svg';
|
|
19 |
import { ReactComponent as PubMedIcon } from '@/assets/svg/pubmed.svg';
|
20 |
import { ReactComponent as QWeatherIcon } from '@/assets/svg/qweather.svg';
|
21 |
import { ReactComponent as SwitchIcon } from '@/assets/svg/switch.svg';
|
|
|
22 |
import { ReactComponent as TuShareIcon } from '@/assets/svg/tushare.svg';
|
23 |
import { ReactComponent as WenCaiIcon } from '@/assets/svg/wencai.svg';
|
24 |
import { ReactComponent as WikipediaIcon } from '@/assets/svg/wikipedia.svg';
|
@@ -85,6 +86,7 @@ export enum Operator {
|
|
85 |
Note = 'Note',
|
86 |
Crawler = 'Crawler',
|
87 |
Invoke = 'Invoke',
|
|
|
88 |
}
|
89 |
|
90 |
export const CommonOperatorList = Object.values(Operator).filter(
|
@@ -124,6 +126,7 @@ export const operatorIconMap = {
|
|
124 |
[Operator.Note]: NoteIcon,
|
125 |
[Operator.Crawler]: CrawlerIcon,
|
126 |
[Operator.Invoke]: InvokeIcon,
|
|
|
127 |
};
|
128 |
|
129 |
export const operatorMap: Record<
|
@@ -253,6 +256,9 @@ export const operatorMap: Record<
|
|
253 |
[Operator.Invoke]: {
|
254 |
backgroundColor: '#dee0e2',
|
255 |
},
|
|
|
|
|
|
|
256 |
};
|
257 |
|
258 |
export const componentMenuList = [
|
@@ -286,6 +292,9 @@ export const componentMenuList = [
|
|
286 |
{
|
287 |
name: Operator.Concentrator,
|
288 |
},
|
|
|
|
|
|
|
289 |
{
|
290 |
name: Operator.Note,
|
291 |
},
|
@@ -566,6 +575,11 @@ export const initialInvokeValues = {
|
|
566 |
clean_html: false,
|
567 |
};
|
568 |
|
|
|
|
|
|
|
|
|
|
|
569 |
export const CategorizeAnchorPointPositions = [
|
570 |
{ top: 1, right: 34 },
|
571 |
{ top: 8, right: 18 },
|
@@ -645,6 +659,7 @@ export const RestrictedUpstreamMap = {
|
|
645 |
[Operator.Crawler]: [Operator.Begin],
|
646 |
[Operator.Note]: [],
|
647 |
[Operator.Invoke]: [Operator.Begin],
|
|
|
648 |
};
|
649 |
|
650 |
export const NodeMap = {
|
@@ -680,6 +695,7 @@ export const NodeMap = {
|
|
680 |
[Operator.Note]: 'noteNode',
|
681 |
[Operator.Crawler]: 'ragNode',
|
682 |
[Operator.Invoke]: 'invokeNode',
|
|
|
683 |
};
|
684 |
|
685 |
export const LanguageOptions = [
|
|
|
19 |
import { ReactComponent as PubMedIcon } from '@/assets/svg/pubmed.svg';
|
20 |
import { ReactComponent as QWeatherIcon } from '@/assets/svg/qweather.svg';
|
21 |
import { ReactComponent as SwitchIcon } from '@/assets/svg/switch.svg';
|
22 |
+
import { ReactComponent as TemplateIcon } from '@/assets/svg/template.svg';
|
23 |
import { ReactComponent as TuShareIcon } from '@/assets/svg/tushare.svg';
|
24 |
import { ReactComponent as WenCaiIcon } from '@/assets/svg/wencai.svg';
|
25 |
import { ReactComponent as WikipediaIcon } from '@/assets/svg/wikipedia.svg';
|
|
|
86 |
Note = 'Note',
|
87 |
Crawler = 'Crawler',
|
88 |
Invoke = 'Invoke',
|
89 |
+
Template = 'Template',
|
90 |
}
|
91 |
|
92 |
export const CommonOperatorList = Object.values(Operator).filter(
|
|
|
126 |
[Operator.Note]: NoteIcon,
|
127 |
[Operator.Crawler]: CrawlerIcon,
|
128 |
[Operator.Invoke]: InvokeIcon,
|
129 |
+
[Operator.Template]: TemplateIcon,
|
130 |
};
|
131 |
|
132 |
export const operatorMap: Record<
|
|
|
256 |
[Operator.Invoke]: {
|
257 |
backgroundColor: '#dee0e2',
|
258 |
},
|
259 |
+
[Operator.Template]: {
|
260 |
+
backgroundColor: '#dee0e2',
|
261 |
+
},
|
262 |
};
|
263 |
|
264 |
export const componentMenuList = [
|
|
|
292 |
{
|
293 |
name: Operator.Concentrator,
|
294 |
},
|
295 |
+
{
|
296 |
+
name: Operator.Template,
|
297 |
+
},
|
298 |
{
|
299 |
name: Operator.Note,
|
300 |
},
|
|
|
575 |
clean_html: false,
|
576 |
};
|
577 |
|
578 |
+
export const initialTemplateValues = {
|
579 |
+
content: '',
|
580 |
+
parameters: [],
|
581 |
+
};
|
582 |
+
|
583 |
export const CategorizeAnchorPointPositions = [
|
584 |
{ top: 1, right: 34 },
|
585 |
{ top: 8, right: 18 },
|
|
|
659 |
[Operator.Crawler]: [Operator.Begin],
|
660 |
[Operator.Note]: [],
|
661 |
[Operator.Invoke]: [Operator.Begin],
|
662 |
+
[Operator.Template]: [Operator.Begin, Operator.Relevant],
|
663 |
};
|
664 |
|
665 |
export const NodeMap = {
|
|
|
695 |
[Operator.Note]: 'noteNode',
|
696 |
[Operator.Crawler]: 'ragNode',
|
697 |
[Operator.Invoke]: 'invokeNode',
|
698 |
+
[Operator.Template]: 'templateNode',
|
699 |
};
|
700 |
|
701 |
export const LanguageOptions = [
|
web/src/pages/flow/flow-drawer/index.tsx
CHANGED
@@ -39,6 +39,7 @@ import OperatorIcon from '../operator-icon';
|
|
39 |
|
40 |
import { CloseOutlined } from '@ant-design/icons';
|
41 |
import { lowerFirst } from 'lodash';
|
|
|
42 |
import { getDrawerWidth } from '../utils';
|
43 |
import styles from './index.less';
|
44 |
|
@@ -79,6 +80,7 @@ const FormMap = {
|
|
79 |
[Operator.Invoke]: InvokeForm,
|
80 |
[Operator.Concentrator]: () => <></>,
|
81 |
[Operator.Note]: () => <></>,
|
|
|
82 |
};
|
83 |
|
84 |
const EmptyContent = () => <div></div>;
|
|
|
39 |
|
40 |
import { CloseOutlined } from '@ant-design/icons';
|
41 |
import { lowerFirst } from 'lodash';
|
42 |
+
import TemplateForm from '../form/template-form';
|
43 |
import { getDrawerWidth } from '../utils';
|
44 |
import styles from './index.less';
|
45 |
|
|
|
80 |
[Operator.Invoke]: InvokeForm,
|
81 |
[Operator.Concentrator]: () => <></>,
|
82 |
[Operator.Note]: () => <></>,
|
83 |
+
[Operator.Template]: TemplateForm,
|
84 |
};
|
85 |
|
86 |
const EmptyContent = () => <div></div>;
|
web/src/pages/flow/flow-id-modal/index.tsx
CHANGED
@@ -24,7 +24,7 @@ const FlowIdModal = ({ hideModal }: IModalProps<any>) => {
|
|
24 |
{id}
|
25 |
</Paragraph>
|
26 |
<Link
|
27 |
-
href="https://ragflow.io/docs/dev/http_api_reference#create-agent
|
28 |
target="_blank"
|
29 |
>
|
30 |
{t('howUseId')}
|
|
|
24 |
{id}
|
25 |
</Paragraph>
|
26 |
<Link
|
27 |
+
href="https://ragflow.io/docs/dev/http_api_reference#create-session-with-an-agent"
|
28 |
target="_blank"
|
29 |
>
|
30 |
{t('howUseId')}
|
web/src/pages/flow/form/generate-form/dynamic-parameters.tsx
CHANGED
@@ -36,6 +36,7 @@ const DynamicParameters = ({ nodeId }: IProps) => {
|
|
36 |
title: t('key'),
|
37 |
dataIndex: 'key',
|
38 |
key: 'key',
|
|
|
39 |
onCell: (record: IGenerateParameter) => ({
|
40 |
record,
|
41 |
editable: true,
|
@@ -49,6 +50,7 @@ const DynamicParameters = ({ nodeId }: IProps) => {
|
|
49 |
dataIndex: 'component_id',
|
50 |
key: 'component_id',
|
51 |
align: 'center',
|
|
|
52 |
render(text, record) {
|
53 |
return (
|
54 |
<Select
|
|
|
36 |
title: t('key'),
|
37 |
dataIndex: 'key',
|
38 |
key: 'key',
|
39 |
+
width: '40%',
|
40 |
onCell: (record: IGenerateParameter) => ({
|
41 |
record,
|
42 |
editable: true,
|
|
|
50 |
dataIndex: 'component_id',
|
51 |
key: 'component_id',
|
52 |
align: 'center',
|
53 |
+
width: '40%',
|
54 |
render(text, record) {
|
55 |
return (
|
56 |
<Select
|
web/src/pages/flow/form/template-form/index.tsx
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Form, Input } from 'antd';
|
2 |
+
import { useTranslation } from 'react-i18next';
|
3 |
+
import { IOperatorForm } from '../../interface';
|
4 |
+
import DynamicParameters from '../generate-form/dynamic-parameters';
|
5 |
+
|
6 |
+
const TemplateForm = ({ onValuesChange, form, node }: IOperatorForm) => {
|
7 |
+
const { t } = useTranslation();
|
8 |
+
|
9 |
+
return (
|
10 |
+
<Form
|
11 |
+
name="basic"
|
12 |
+
autoComplete="off"
|
13 |
+
form={form}
|
14 |
+
onValuesChange={onValuesChange}
|
15 |
+
layout={'vertical'}
|
16 |
+
>
|
17 |
+
<Form.Item name={['content']} label={t('flow.content')}>
|
18 |
+
<Input.TextArea rows={8} placeholder={t('flow.blank')} />
|
19 |
+
</Form.Item>
|
20 |
+
|
21 |
+
<DynamicParameters nodeId={node?.id}></DynamicParameters>
|
22 |
+
</Form>
|
23 |
+
);
|
24 |
+
};
|
25 |
+
|
26 |
+
export default TemplateForm;
|
web/src/pages/flow/hooks.tsx
CHANGED
@@ -60,6 +60,7 @@ import {
|
|
60 |
initialRetrievalValues,
|
61 |
initialRewriteQuestionValues,
|
62 |
initialSwitchValues,
|
|
|
63 |
initialTuShareValues,
|
64 |
initialWenCaiValues,
|
65 |
initialWikipediaValues,
|
@@ -139,6 +140,7 @@ export const useInitializeOperatorParams = () => {
|
|
139 |
[Operator.Note]: initialNoteValues,
|
140 |
[Operator.Crawler]: initialCrawlerValues,
|
141 |
[Operator.Invoke]: initialInvokeValues,
|
|
|
142 |
};
|
143 |
}, [llmId]);
|
144 |
|
|
|
60 |
initialRetrievalValues,
|
61 |
initialRewriteQuestionValues,
|
62 |
initialSwitchValues,
|
63 |
+
initialTemplateValues,
|
64 |
initialTuShareValues,
|
65 |
initialWenCaiValues,
|
66 |
initialWikipediaValues,
|
|
|
140 |
[Operator.Note]: initialNoteValues,
|
141 |
[Operator.Crawler]: initialCrawlerValues,
|
142 |
[Operator.Invoke]: initialInvokeValues,
|
143 |
+
[Operator.Template]: initialTemplateValues,
|
144 |
};
|
145 |
}, [llmId]);
|
146 |
|