balibabu
commited on
Commit
·
4138aee
1
Parent(s):
845e389
feat: Add hint for operators, round to square, input variable, readable operator ID. #3056 (#3057)
Browse files### What problem does this PR solve?
feat: Add hint for operators, round to square, input variable, readable
operator ID. #3056
### Type of change
- [ ] Bug Fix (non-breaking change which fixes an issue)
- [x] New Feature (non-breaking change which adds functionality)
- [ ] Documentation Update
- [ ] Refactoring
- [ ] Performance Improvement
- [ ] Other (please describe):
This view is limited to 50 files because it contains too many changes.
See raw diff
- web/src/assets/svg/begin.svg +9 -0
- web/src/assets/svg/concentrator.svg +2 -2
- web/src/assets/svg/keyword.svg +2 -2
- web/src/assets/svg/llm/baai.svg +12 -0
- web/src/assets/svg/llm/nomic-ai.svg +7 -0
- web/src/assets/svg/llm/sentence-transformers.svg +29 -0
- web/src/assets/svg/llm/youdao.svg +13 -0
- web/src/assets/svg/plus-circle-fill.svg +2 -0
- web/src/assets/svg/plus.svg +5 -0
- web/src/assets/svg/resize.svg +6 -0
- web/src/assets/svg/switch.svg +1 -1
- web/src/components/knowledge-base-item.tsx +8 -2
- web/src/components/llm-select/index.less +3 -0
- web/src/components/llm-select/index.tsx +3 -1
- web/src/components/llm-select/llm-label.tsx +31 -0
- web/src/components/svg-icon.tsx +24 -1
- web/src/constants/setting.ts +50 -0
- web/src/hooks/{llm-hooks.ts → llm-hooks.tsx} +21 -2
- web/src/locales/en.ts +4 -2
- web/src/locales/zh-traditional.ts +5 -2
- web/src/locales/zh.ts +7 -4
- web/src/pages/flow/canvas/index.tsx +12 -0
- web/src/pages/flow/canvas/node/begin-node.tsx +16 -13
- web/src/pages/flow/canvas/node/categorize-node.tsx +32 -53
- web/src/pages/flow/canvas/node/dropdown.tsx +1 -1
- web/src/pages/flow/canvas/node/generate-node.tsx +74 -0
- web/src/pages/flow/canvas/node/handle-icon.tsx +20 -0
- web/src/pages/flow/canvas/node/hooks.ts +73 -73
- web/src/pages/flow/canvas/node/index.less +118 -56
- web/src/pages/flow/canvas/node/index.tsx +5 -35
- web/src/pages/flow/canvas/node/keyword-node.tsx +54 -0
- web/src/pages/flow/canvas/node/logic-node.tsx +5 -52
- web/src/pages/flow/canvas/node/message-node.tsx +63 -0
- web/src/pages/flow/canvas/node/node-header.tsx +35 -0
- web/src/pages/flow/canvas/node/note-node.tsx +61 -22
- web/src/pages/flow/canvas/node/relevant-node.tsx +28 -38
- web/src/pages/flow/canvas/node/retrieval-node.tsx +85 -0
- web/src/pages/flow/canvas/node/rewrite-node.tsx +54 -0
- web/src/pages/flow/canvas/node/switch-node.tsx +112 -0
- web/src/pages/flow/constant.tsx +15 -14
- web/src/pages/flow/flow-drawer/index.less +6 -0
- web/src/pages/flow/flow-drawer/index.tsx +29 -15
- web/src/pages/flow/flow-sider/index.tsx +5 -2
- web/src/pages/flow/form-hooks.ts +4 -15
- web/src/pages/flow/form/categorize-form/dynamic-categorize.tsx +20 -6
- web/src/pages/flow/form/categorize-form/index.less +11 -0
- web/src/pages/flow/form/generate-form/dynamic-parameters.tsx +1 -0
- web/src/pages/flow/form/switch-form/index.less +21 -0
- web/src/pages/flow/form/switch-form/index.tsx +121 -97
- web/src/pages/flow/hooks.ts +23 -3
web/src/assets/svg/begin.svg
ADDED
|
web/src/assets/svg/concentrator.svg
CHANGED
|
|
web/src/assets/svg/keyword.svg
CHANGED
|
|
web/src/assets/svg/llm/baai.svg
ADDED
|
web/src/assets/svg/llm/nomic-ai.svg
ADDED
|
web/src/assets/svg/llm/sentence-transformers.svg
ADDED
|
web/src/assets/svg/llm/youdao.svg
ADDED
|
web/src/assets/svg/plus-circle-fill.svg
CHANGED
|
|
web/src/assets/svg/plus.svg
ADDED
|
web/src/assets/svg/resize.svg
ADDED
|
web/src/assets/svg/switch.svg
CHANGED
|
|
web/src/components/knowledge-base-item.tsx
CHANGED
@@ -1,6 +1,7 @@
|
|
1 |
import { useTranslate } from '@/hooks/common-hooks';
|
2 |
import { useNextFetchKnowledgeList } from '@/hooks/knowledge-hooks';
|
3 |
-
import {
|
|
|
4 |
|
5 |
const KnowledgeBaseItem = () => {
|
6 |
const { t } = useTranslate('chat');
|
@@ -8,7 +9,12 @@ const KnowledgeBaseItem = () => {
|
|
8 |
const { list: knowledgeList } = useNextFetchKnowledgeList(true);
|
9 |
|
10 |
const knowledgeOptions = knowledgeList.map((x) => ({
|
11 |
-
label:
|
|
|
|
|
|
|
|
|
|
|
12 |
value: x.id,
|
13 |
}));
|
14 |
|
|
|
1 |
import { useTranslate } from '@/hooks/common-hooks';
|
2 |
import { useNextFetchKnowledgeList } from '@/hooks/knowledge-hooks';
|
3 |
+
import { UserOutlined } from '@ant-design/icons';
|
4 |
+
import { Avatar, Form, Select, Space } from 'antd';
|
5 |
|
6 |
const KnowledgeBaseItem = () => {
|
7 |
const { t } = useTranslate('chat');
|
|
|
9 |
const { list: knowledgeList } = useNextFetchKnowledgeList(true);
|
10 |
|
11 |
const knowledgeOptions = knowledgeList.map((x) => ({
|
12 |
+
label: (
|
13 |
+
<Space>
|
14 |
+
<Avatar size={20} icon={<UserOutlined />} src={x.avatar} />
|
15 |
+
{x.name}
|
16 |
+
</Space>
|
17 |
+
),
|
18 |
value: x.id,
|
19 |
}));
|
20 |
|
web/src/components/llm-select/index.less
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
.llmLabel {
|
2 |
+
font-size: 14px;
|
3 |
+
}
|
web/src/components/llm-select/index.tsx
CHANGED
@@ -7,9 +7,10 @@ interface IProps {
|
|
7 |
id?: string;
|
8 |
value?: string;
|
9 |
onChange?: (value: string) => void;
|
|
|
10 |
}
|
11 |
|
12 |
-
const LLMSelect = ({ id, value, onChange }: IProps) => {
|
13 |
const modelOptions = useComposeLlmOptionsByModelTypes([
|
14 |
LlmModelType.Chat,
|
15 |
LlmModelType.Image2text,
|
@@ -38,6 +39,7 @@ const LLMSelect = ({ id, value, onChange }: IProps) => {
|
|
38 |
id={id}
|
39 |
value={value}
|
40 |
onChange={onChange}
|
|
|
41 |
/>
|
42 |
</Popover>
|
43 |
);
|
|
|
7 |
id?: string;
|
8 |
value?: string;
|
9 |
onChange?: (value: string) => void;
|
10 |
+
disabled?: boolean;
|
11 |
}
|
12 |
|
13 |
+
const LLMSelect = ({ id, value, onChange, disabled }: IProps) => {
|
14 |
const modelOptions = useComposeLlmOptionsByModelTypes([
|
15 |
LlmModelType.Chat,
|
16 |
LlmModelType.Image2text,
|
|
|
39 |
id={id}
|
40 |
value={value}
|
41 |
onChange={onChange}
|
42 |
+
disabled={disabled}
|
43 |
/>
|
44 |
</Popover>
|
45 |
);
|
web/src/components/llm-select/llm-label.tsx
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { LlmModelType } from '@/constants/knowledge';
|
2 |
+
import { useComposeLlmOptionsByModelTypes } from '@/hooks/llm-hooks';
|
3 |
+
import { useMemo } from 'react';
|
4 |
+
|
5 |
+
interface IProps {
|
6 |
+
id?: string;
|
7 |
+
value?: string;
|
8 |
+
onChange?: (value: string) => void;
|
9 |
+
disabled?: boolean;
|
10 |
+
}
|
11 |
+
|
12 |
+
const LLMLabel = ({ value }: IProps) => {
|
13 |
+
const modelOptions = useComposeLlmOptionsByModelTypes([
|
14 |
+
LlmModelType.Chat,
|
15 |
+
LlmModelType.Image2text,
|
16 |
+
]);
|
17 |
+
|
18 |
+
const label = useMemo(() => {
|
19 |
+
for (const item of modelOptions) {
|
20 |
+
for (const option of item.options) {
|
21 |
+
if (option.value === value) {
|
22 |
+
return option.label;
|
23 |
+
}
|
24 |
+
}
|
25 |
+
}
|
26 |
+
}, [modelOptions, value]);
|
27 |
+
|
28 |
+
return <div>{label}</div>;
|
29 |
+
};
|
30 |
+
|
31 |
+
export default LLMLabel;
|
web/src/components/svg-icon.tsx
CHANGED
@@ -1,5 +1,8 @@
|
|
1 |
-
import
|
|
|
2 |
import { IconComponentProps } from '@ant-design/icons/lib/components/Icon';
|
|
|
|
|
3 |
|
4 |
const importAll = (requireContext: __WebpackModuleApi.RequireContext) => {
|
5 |
const list = requireContext.keys().map((key) => {
|
@@ -36,4 +39,24 @@ const SvgIcon = ({ name, width, height, ...restProps }: IProps) => {
|
|
36 |
);
|
37 |
};
|
38 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
export default SvgIcon;
|
|
|
1 |
+
import { IconMap } from '@/constants/setting';
|
2 |
+
import Icon, { UserOutlined } from '@ant-design/icons';
|
3 |
import { IconComponentProps } from '@ant-design/icons/lib/components/Icon';
|
4 |
+
import { Avatar } from 'antd';
|
5 |
+
import { AvatarSize } from 'antd/es/avatar/AvatarContext';
|
6 |
|
7 |
const importAll = (requireContext: __WebpackModuleApi.RequireContext) => {
|
8 |
const list = requireContext.keys().map((key) => {
|
|
|
39 |
);
|
40 |
};
|
41 |
|
42 |
+
export const LlmIcon = ({
|
43 |
+
name,
|
44 |
+
height = 48,
|
45 |
+
width = 48,
|
46 |
+
size = 'large',
|
47 |
+
}: {
|
48 |
+
name: string;
|
49 |
+
height?: number;
|
50 |
+
width?: number;
|
51 |
+
size?: AvatarSize;
|
52 |
+
}) => {
|
53 |
+
const icon = IconMap[name as keyof typeof IconMap];
|
54 |
+
|
55 |
+
return icon ? (
|
56 |
+
<SvgIcon name={`llm/${icon}`} width={width} height={height}></SvgIcon>
|
57 |
+
) : (
|
58 |
+
<Avatar shape="square" size={size} icon={<UserOutlined />} />
|
59 |
+
);
|
60 |
+
};
|
61 |
+
|
62 |
export default SvgIcon;
|
web/src/constants/setting.ts
CHANGED
@@ -19,6 +19,56 @@ export const UserSettingRouteMap = {
|
|
19 |
[UserSettingRouteKey.Logout]: 'Log out',
|
20 |
};
|
21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
export const TimezoneList = [
|
23 |
'UTC-11\tPacific/Midway',
|
24 |
'UTC-11\tPacific/Niue',
|
|
|
19 |
[UserSettingRouteKey.Logout]: 'Log out',
|
20 |
};
|
21 |
|
22 |
+
// Please lowercase the file name
|
23 |
+
export const IconMap = {
|
24 |
+
'Tongyi-Qianwen': 'tongyi',
|
25 |
+
Moonshot: 'moonshot',
|
26 |
+
OpenAI: 'openai',
|
27 |
+
'ZHIPU-AI': 'zhipu',
|
28 |
+
文心一言: 'wenxin',
|
29 |
+
Ollama: 'ollama',
|
30 |
+
Xinference: 'xinference',
|
31 |
+
DeepSeek: 'deepseek',
|
32 |
+
VolcEngine: 'volc_engine',
|
33 |
+
BaiChuan: 'baichuan',
|
34 |
+
Jina: 'jina',
|
35 |
+
MiniMax: 'chat-minimax',
|
36 |
+
Mistral: 'mistral',
|
37 |
+
'Azure-OpenAI': 'azure',
|
38 |
+
Bedrock: 'bedrock',
|
39 |
+
Gemini: 'gemini',
|
40 |
+
Groq: 'groq-next',
|
41 |
+
OpenRouter: 'open-router',
|
42 |
+
LocalAI: 'local-ai',
|
43 |
+
StepFun: 'stepfun',
|
44 |
+
NVIDIA: 'nvidia',
|
45 |
+
'LM-Studio': 'lm-studio',
|
46 |
+
'OpenAI-API-Compatible': 'openai-api',
|
47 |
+
cohere: 'cohere',
|
48 |
+
LeptonAI: 'lepton-ai',
|
49 |
+
TogetherAI: 'together-ai',
|
50 |
+
PerfXCloud: 'perfx-cloud',
|
51 |
+
Upstage: 'upstage',
|
52 |
+
'novita.ai': 'novita-ai',
|
53 |
+
SILICONFLOW: 'siliconflow',
|
54 |
+
'01.AI': 'yi',
|
55 |
+
Replicate: 'replicate',
|
56 |
+
'Tencent Hunyuan': 'hunyuan',
|
57 |
+
'XunFei Spark': 'spark',
|
58 |
+
BaiduYiyan: 'yiyan',
|
59 |
+
'Fish Audio': 'fish-audio',
|
60 |
+
'Tencent Cloud': 'tencent-cloud',
|
61 |
+
Anthropic: 'anthropic',
|
62 |
+
'Voyage AI': 'voyage',
|
63 |
+
'Google Cloud': 'google-cloud',
|
64 |
+
HuggingFace: 'huggingface',
|
65 |
+
Youdao: 'youdao',
|
66 |
+
BAAI: 'baai',
|
67 |
+
'nomic-ai': 'nomic-ai',
|
68 |
+
jinaai: 'jina',
|
69 |
+
'sentence-transformers': 'sentence-transformers',
|
70 |
+
};
|
71 |
+
|
72 |
export const TimezoneList = [
|
73 |
'UTC-11\tPacific/Midway',
|
74 |
'UTC-11\tPacific/Niue',
|
web/src/hooks/{llm-hooks.ts → llm-hooks.tsx}
RENAMED
@@ -1,3 +1,4 @@
|
|
|
|
1 |
import { LlmModelType } from '@/constants/knowledge';
|
2 |
import { ResponseGetType } from '@/interfaces/database/base';
|
3 |
import {
|
@@ -13,7 +14,7 @@ import {
|
|
13 |
import userService from '@/services/user-service';
|
14 |
import { sortLLmFactoryListBySpecifiedOrder } from '@/utils/common-util';
|
15 |
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
16 |
-
import { message } from 'antd';
|
17 |
import { DefaultOptionType } from 'antd/es/select';
|
18 |
import { useMemo } from 'react';
|
19 |
import { useTranslation } from 'react-i18next';
|
@@ -53,6 +54,14 @@ export const useSelectLlmOptions = () => {
|
|
53 |
return embeddingModelOptions;
|
54 |
};
|
55 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
export const useSelectLlmOptionsByModelType = () => {
|
57 |
const llmInfo: IThirdOAIModelCollection = useFetchLlmList();
|
58 |
|
@@ -71,7 +80,17 @@ export const useSelectLlmOptionsByModelType = () => {
|
|
71 |
x.available,
|
72 |
)
|
73 |
.map((x) => ({
|
74 |
-
label:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
value: `${x.llm_name}@${x.fid}`,
|
76 |
disabled: !x.available,
|
77 |
})),
|
|
|
1 |
+
import { LlmIcon } from '@/components/svg-icon';
|
2 |
import { LlmModelType } from '@/constants/knowledge';
|
3 |
import { ResponseGetType } from '@/interfaces/database/base';
|
4 |
import {
|
|
|
14 |
import userService from '@/services/user-service';
|
15 |
import { sortLLmFactoryListBySpecifiedOrder } from '@/utils/common-util';
|
16 |
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
17 |
+
import { Flex, message } from 'antd';
|
18 |
import { DefaultOptionType } from 'antd/es/select';
|
19 |
import { useMemo } from 'react';
|
20 |
import { useTranslation } from 'react-i18next';
|
|
|
54 |
return embeddingModelOptions;
|
55 |
};
|
56 |
|
57 |
+
const getLLMIconName = (fid: string, llm_name: string) => {
|
58 |
+
if (fid === 'FastEmbed') {
|
59 |
+
return llm_name.split('/').at(0) ?? '';
|
60 |
+
}
|
61 |
+
|
62 |
+
return fid;
|
63 |
+
};
|
64 |
+
|
65 |
export const useSelectLlmOptionsByModelType = () => {
|
66 |
const llmInfo: IThirdOAIModelCollection = useFetchLlmList();
|
67 |
|
|
|
80 |
x.available,
|
81 |
)
|
82 |
.map((x) => ({
|
83 |
+
label: (
|
84 |
+
<Flex align="center" gap={6}>
|
85 |
+
<LlmIcon
|
86 |
+
name={getLLMIconName(x.fid, x.llm_name)}
|
87 |
+
width={26}
|
88 |
+
height={26}
|
89 |
+
size={'small'}
|
90 |
+
/>
|
91 |
+
<span>{x.llm_name}</span>
|
92 |
+
</Flex>
|
93 |
+
),
|
94 |
value: `${x.llm_name}@${x.fid}`,
|
95 |
disabled: !x.available,
|
96 |
})),
|
web/src/locales/en.ts
CHANGED
@@ -728,18 +728,20 @@ The above is the content you need to summarize.`,
|
|
728 |
'The window size of conversation history that needed to be seen by LLM. The larger the better. But be careful with the maximum content length of LLM.',
|
729 |
wikipedia: 'Wikipedia',
|
730 |
pubMed: 'PubMed',
|
|
|
|
|
731 |
email: 'Email',
|
732 |
emailTip:
|
733 |
'This component is used to get search result from https://pubmed.ncbi.nlm.nih.gov/. Typically, it performs as a supplement to knowledgebases. Top N specifies the number of search results you need to adapt. E-mail is a required field.',
|
734 |
arXiv: 'ArXiv',
|
735 |
-
|
736 |
'This component is used to get search result from https://arxiv.org/. Typically, it performs as a supplement to knowledgebases. Top N specifies the number of search results you need to adapt.',
|
737 |
sortBy: 'Sort by',
|
738 |
submittedDate: 'Submitted date',
|
739 |
lastUpdatedDate: 'Last updated date',
|
740 |
relevance: 'Relevance',
|
741 |
google: 'Google',
|
742 |
-
|
743 |
'This component is used to get search result fromhttps://www.google.com/ . Typically, it performs as a supplement to knowledgebases. Top N and SerpApi API key specifies the number of search results you need to adapt.',
|
744 |
bing: 'Bing',
|
745 |
bingTip:
|
|
|
728 |
'The window size of conversation history that needed to be seen by LLM. The larger the better. But be careful with the maximum content length of LLM.',
|
729 |
wikipedia: 'Wikipedia',
|
730 |
pubMed: 'PubMed',
|
731 |
+
pubMedDescription:
|
732 |
+
'This component is used to get search result from https://pubmed.ncbi.nlm.nih.gov/. Typically, it performs as a supplement to knowledgebases. Top N specifies the number of search results you need to adapt. E-mail is a required field.',
|
733 |
email: 'Email',
|
734 |
emailTip:
|
735 |
'This component is used to get search result from https://pubmed.ncbi.nlm.nih.gov/. Typically, it performs as a supplement to knowledgebases. Top N specifies the number of search results you need to adapt. E-mail is a required field.',
|
736 |
arXiv: 'ArXiv',
|
737 |
+
arXivDescription:
|
738 |
'This component is used to get search result from https://arxiv.org/. Typically, it performs as a supplement to knowledgebases. Top N specifies the number of search results you need to adapt.',
|
739 |
sortBy: 'Sort by',
|
740 |
submittedDate: 'Submitted date',
|
741 |
lastUpdatedDate: 'Last updated date',
|
742 |
relevance: 'Relevance',
|
743 |
google: 'Google',
|
744 |
+
googleDescription:
|
745 |
'This component is used to get search result fromhttps://www.google.com/ . Typically, it performs as a supplement to knowledgebases. Top N and SerpApi API key specifies the number of search results you need to adapt.',
|
746 |
bing: 'Bing',
|
747 |
bingTip:
|
web/src/locales/zh-traditional.ts
CHANGED
@@ -680,18 +680,21 @@ export default {
|
|
680 |
messageHistoryWindowSizeTip:
|
681 |
'LLM需要查看的對話記錄的視窗大小。越大越好。但要注意LLM的最大內容長度。',
|
682 |
wikipedia: '維基百科',
|
|
|
|
|
|
|
683 |
email: '信箱',
|
684 |
emailTip:
|
685 |
'此元件用於從 https://pubmed.ncbi.nlm.nih.gov/ 取得搜尋結果。通常,它充當知識庫的補充。 Top N 指定您需要適應的搜尋結果的數量。電子郵件是必填欄位。',
|
686 |
arXiv: 'ArXiv',
|
687 |
-
|
688 |
'此元件用於從 https://arxiv.org/ 取得搜尋結果。通常,它充當知識庫的補充。 Top N 指定您需要適應的搜尋結果的數量。',
|
689 |
sortBy: '排序方式',
|
690 |
submittedDate: '提交日期',
|
691 |
lastUpdatedDate: '最後更新日期',
|
692 |
relevance: '關聯',
|
693 |
google: 'Google',
|
694 |
-
|
695 |
'此元件用於從https://www.google.com/取得搜尋結果。通常,它作為知識庫的補充。 Top N 和 SerpApi API 金鑰指定您需要調整的搜尋結果數量。',
|
696 |
bing: 'Bing',
|
697 |
bingTip:
|
|
|
680 |
messageHistoryWindowSizeTip:
|
681 |
'LLM需要查看的對話記錄的視窗大小。越大越好。但要注意LLM的最大內容長度。',
|
682 |
wikipedia: '維基百科',
|
683 |
+
pubMed: 'PubMed',
|
684 |
+
pubMedDescription:
|
685 |
+
'此元件用於從 https://pubmed.ncbi.nlm.nih.gov/ 取得搜尋結果。通常,它充當知識庫的補充。 Top N 指定您需要適應的搜尋結果的數量。電子郵件是必填欄位。',
|
686 |
email: '信箱',
|
687 |
emailTip:
|
688 |
'此元件用於從 https://pubmed.ncbi.nlm.nih.gov/ 取得搜尋結果。通常,它充當知識庫的補充。 Top N 指定您需要適應的搜尋結果的數量。電子郵件是必填欄位。',
|
689 |
arXiv: 'ArXiv',
|
690 |
+
arXivDescription:
|
691 |
'此元件用於從 https://arxiv.org/ 取得搜尋結果。通常,它充當知識庫的補充。 Top N 指定您需要適應的搜尋結果的數量。',
|
692 |
sortBy: '排序方式',
|
693 |
submittedDate: '提交日期',
|
694 |
lastUpdatedDate: '最後更新日期',
|
695 |
relevance: '關聯',
|
696 |
google: 'Google',
|
697 |
+
googleDescription:
|
698 |
'此元件用於從https://www.google.com/取得搜尋結果。通常,它作為知識庫的補充。 Top N 和 SerpApi API 金鑰指定您需要調整的搜尋結果數量。',
|
699 |
bing: 'Bing',
|
700 |
bingTip:
|
web/src/locales/zh.ts
CHANGED
@@ -688,7 +688,7 @@ export default {
|
|
688 |
keywordExtract: '关键词',
|
689 |
keywordExtractDescription: `该组件用于从用户的问题中提取关键词。Top N指定需要提取的关键词数量。`,
|
690 |
baidu: '百度',
|
691 |
-
baiduDescription:
|
692 |
duckDuckGo: 'DuckDuckGo',
|
693 |
duckDuckGoDescription:
|
694 |
'此元件用於從 www.duckduckgo.com 取得搜尋結果。通常,它作為知識庫的補充。 Top N 指定您需要調整的搜尋結果數。',
|
@@ -700,18 +700,21 @@ export default {
|
|
700 |
messageHistoryWindowSizeTip:
|
701 |
'LLM 需要查看的对话历史窗口大小。越大越好。但要注意 LLM 的最大内容长度。',
|
702 |
wikipedia: '维基百科',
|
703 |
-
email: '邮箱',
|
704 |
emailTip:
|
705 |
'此组件用于从 https://pubmed.ncbi.nlm.nih.gov/ 获取搜索结果。通常,它作为知识库的补充。Top N 指定您需要调整的搜索结果数。电子邮件是必填字段。',
|
|
|
|
|
|
|
|
|
706 |
arXiv: 'ArXiv',
|
707 |
-
|
708 |
'此组件用于从 https://arxiv.org/ 获取搜索结果。通常,它作为知识库的补充。Top N 指定您需要调整的搜索结果数量。',
|
709 |
sortBy: '排序方式',
|
710 |
submittedDate: '提交日期',
|
711 |
lastUpdatedDate: '最后更新日期',
|
712 |
relevance: '关联',
|
713 |
google: 'Google',
|
714 |
-
|
715 |
'此组件用于从https://www.google.com/获取搜索结果。通常,它作为知识库的补充。Top N 和 SerpApi API 密钥指定您需要调整的搜索结果数量。',
|
716 |
bing: 'Bing',
|
717 |
bingTip:
|
|
|
688 |
keywordExtract: '关键词',
|
689 |
keywordExtractDescription: `该组件用于从用户的问题中提取关键词。Top N指定需要提取的关键词数量。`,
|
690 |
baidu: '百度',
|
691 |
+
baiduDescription: `此组件用于从 www.baidu.com 获取搜索结果。通常,它作为知识库的补充。Top N 指定您需要调整的搜索结果数量。`,
|
692 |
duckDuckGo: 'DuckDuckGo',
|
693 |
duckDuckGoDescription:
|
694 |
'此元件用於從 www.duckduckgo.com 取得搜尋結果。通常,它作為知識庫的補充。 Top N 指定您需要調整的搜尋結果數。',
|
|
|
700 |
messageHistoryWindowSizeTip:
|
701 |
'LLM 需要查看的对话历史窗口大小。越大越好。但要注意 LLM 的最大内容长度。',
|
702 |
wikipedia: '维基百科',
|
|
|
703 |
emailTip:
|
704 |
'此组件用于从 https://pubmed.ncbi.nlm.nih.gov/ 获取搜索结果。通常,它作为知识库的补充。Top N 指定您需要调整的搜索结果数。电子邮件是必填字段。',
|
705 |
+
email: '邮箱',
|
706 |
+
pubMed: 'PubMed',
|
707 |
+
pubMedDescription:
|
708 |
+
'此组件用于从 https://pubmed.ncbi.nlm.nih.gov/ 获取搜索结果。通常,它作为知识库的补充。Top N 指定您需要调整的搜索结果数。电子邮件是必填字段。',
|
709 |
arXiv: 'ArXiv',
|
710 |
+
arXivDescription:
|
711 |
'此组件用于从 https://arxiv.org/ 获取搜索结果。通常,它作为知识库的补充。Top N 指定您需要调整的搜索结果数量。',
|
712 |
sortBy: '排序方式',
|
713 |
submittedDate: '提交日期',
|
714 |
lastUpdatedDate: '最后更新日期',
|
715 |
relevance: '关联',
|
716 |
google: 'Google',
|
717 |
+
googleDescription:
|
718 |
'此组件用于从https://www.google.com/获取搜索结果。通常,它作为知识库的补充。Top N 和 SerpApi API 密钥指定您需要调整的搜索结果数量。',
|
719 |
bing: 'Bing',
|
720 |
bingTip:
|
web/src/pages/flow/canvas/index.tsx
CHANGED
@@ -22,9 +22,15 @@ import styles from './index.less';
|
|
22 |
import { RagNode } from './node';
|
23 |
import { BeginNode } from './node/begin-node';
|
24 |
import { CategorizeNode } from './node/categorize-node';
|
|
|
|
|
25 |
import { LogicNode } from './node/logic-node';
|
|
|
26 |
import NoteNode from './node/note-node';
|
27 |
import { RelevantNode } from './node/relevant-node';
|
|
|
|
|
|
|
28 |
|
29 |
const nodeTypes = {
|
30 |
ragNode: RagNode,
|
@@ -33,6 +39,12 @@ const nodeTypes = {
|
|
33 |
relevantNode: RelevantNode,
|
34 |
logicNode: LogicNode,
|
35 |
noteNode: NoteNode,
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
};
|
37 |
|
38 |
const edgeTypes = {
|
|
|
22 |
import { RagNode } from './node';
|
23 |
import { BeginNode } from './node/begin-node';
|
24 |
import { CategorizeNode } from './node/categorize-node';
|
25 |
+
import { GenerateNode } from './node/generate-node';
|
26 |
+
import { KeywordNode } from './node/keyword-node';
|
27 |
import { LogicNode } from './node/logic-node';
|
28 |
+
import { MessageNode } from './node/message-node';
|
29 |
import NoteNode from './node/note-node';
|
30 |
import { RelevantNode } from './node/relevant-node';
|
31 |
+
import { RetrievalNode } from './node/retrieval-node';
|
32 |
+
import { RewriteNode } from './node/rewrite-node';
|
33 |
+
import { SwitchNode } from './node/switch-node';
|
34 |
|
35 |
const nodeTypes = {
|
36 |
ragNode: RagNode,
|
|
|
39 |
relevantNode: RelevantNode,
|
40 |
logicNode: LogicNode,
|
41 |
noteNode: NoteNode,
|
42 |
+
switchNode: SwitchNode,
|
43 |
+
generateNode: GenerateNode,
|
44 |
+
retrievalNode: RetrievalNode,
|
45 |
+
messageNode: MessageNode,
|
46 |
+
rewriteNode: RewriteNode,
|
47 |
+
keywordNode: KeywordNode,
|
48 |
};
|
49 |
|
50 |
const edgeTypes = {
|
web/src/pages/flow/canvas/node/begin-node.tsx
CHANGED
@@ -1,25 +1,24 @@
|
|
1 |
-
import { useTranslate } from '@/hooks/common-hooks';
|
2 |
import { Flex } from 'antd';
|
3 |
import classNames from 'classnames';
|
4 |
-
import
|
5 |
import { Handle, NodeProps, Position } from 'reactflow';
|
6 |
import { Operator, operatorMap } from '../../constant';
|
7 |
import { NodeData } from '../../interface';
|
|
|
|
|
8 |
import styles from './index.less';
|
9 |
|
10 |
// TODO: do not allow other nodes to connect to this node
|
11 |
-
export function BeginNode({
|
12 |
-
const { t } =
|
|
|
13 |
return (
|
14 |
<section
|
15 |
className={classNames(styles.ragNode, {
|
16 |
[styles.selectedNode]: selected,
|
17 |
})}
|
18 |
style={{
|
19 |
-
|
20 |
-
color: 'white',
|
21 |
-
width: 50,
|
22 |
-
height: 50,
|
23 |
}}
|
24 |
>
|
25 |
<Handle
|
@@ -27,13 +26,17 @@ export function BeginNode({ id, data, selected }: NodeProps<NodeData>) {
|
|
27 |
position={Position.Right}
|
28 |
isConnectable
|
29 |
className={styles.handle}
|
|
|
30 |
></Handle>
|
31 |
-
|
32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
</Flex>
|
34 |
-
<section className={styles.bottomBox}>
|
35 |
-
<div className={styles.nodeName}>{data.name}</div>
|
36 |
-
</section>
|
37 |
</section>
|
38 |
);
|
39 |
}
|
|
|
|
|
1 |
import { Flex } from 'antd';
|
2 |
import classNames from 'classnames';
|
3 |
+
import { useTranslation } from 'react-i18next';
|
4 |
import { Handle, NodeProps, Position } from 'reactflow';
|
5 |
import { Operator, operatorMap } from '../../constant';
|
6 |
import { NodeData } from '../../interface';
|
7 |
+
import OperatorIcon from '../../operator-icon';
|
8 |
+
import { RightHandleStyle } from './handle-icon';
|
9 |
import styles from './index.less';
|
10 |
|
11 |
// TODO: do not allow other nodes to connect to this node
|
12 |
+
export function BeginNode({ selected, data }: NodeProps<NodeData>) {
|
13 |
+
const { t } = useTranslation();
|
14 |
+
|
15 |
return (
|
16 |
<section
|
17 |
className={classNames(styles.ragNode, {
|
18 |
[styles.selectedNode]: selected,
|
19 |
})}
|
20 |
style={{
|
21 |
+
width: 100,
|
|
|
|
|
|
|
22 |
}}
|
23 |
>
|
24 |
<Handle
|
|
|
26 |
position={Position.Right}
|
27 |
isConnectable
|
28 |
className={styles.handle}
|
29 |
+
style={RightHandleStyle}
|
30 |
></Handle>
|
31 |
+
|
32 |
+
<Flex align="center" justify={'space-around'}>
|
33 |
+
<OperatorIcon
|
34 |
+
name={data.label as Operator}
|
35 |
+
fontSize={24}
|
36 |
+
color={operatorMap[data.label as Operator].color}
|
37 |
+
></OperatorIcon>
|
38 |
+
<div className={styles.nodeTitle}>{t(`flow.begin`)}</div>
|
39 |
</Flex>
|
|
|
|
|
|
|
40 |
</section>
|
41 |
);
|
42 |
}
|
web/src/pages/flow/canvas/node/categorize-node.tsx
CHANGED
@@ -1,22 +1,17 @@
|
|
1 |
-
import
|
2 |
import { Flex } from 'antd';
|
3 |
import classNames from 'classnames';
|
4 |
-
import
|
5 |
import { Handle, NodeProps, Position } from 'reactflow';
|
6 |
-
import { Operator, SwitchElseTo, operatorMap } from '../../constant';
|
7 |
import { NodeData } from '../../interface';
|
8 |
-
import
|
9 |
-
import CategorizeHandle from './categorize-handle';
|
10 |
-
import NodeDropdown from './dropdown';
|
11 |
import { useBuildCategorizeHandlePositions } from './hooks';
|
12 |
import styles from './index.less';
|
|
|
13 |
import NodePopover from './popover';
|
14 |
|
15 |
export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
|
16 |
-
const style = operatorMap[data.label as Operator];
|
17 |
-
const { t } = useTranslate('flow');
|
18 |
const { positions } = useBuildCategorizeHandlePositions({ data, id });
|
19 |
-
const operatorName = data.label;
|
20 |
|
21 |
return (
|
22 |
<NodePopover nodeId={id}>
|
@@ -24,10 +19,6 @@ export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
|
|
24 |
className={classNames(styles.logicNode, {
|
25 |
[styles.selectedNode]: selected,
|
26 |
})}
|
27 |
-
style={{
|
28 |
-
backgroundColor: style.backgroundColor,
|
29 |
-
color: style.color,
|
30 |
-
}}
|
31 |
>
|
32 |
<Handle
|
33 |
type="target"
|
@@ -36,47 +27,35 @@ export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
|
|
36 |
className={styles.handle}
|
37 |
id={'a'}
|
38 |
></Handle>
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
></
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
);
|
68 |
-
})}
|
69 |
-
<Flex vertical align="center" justify="center" gap={6}>
|
70 |
-
<OperatorIcon
|
71 |
-
name={data.label as Operator}
|
72 |
-
fontSize={24}
|
73 |
-
></OperatorIcon>
|
74 |
-
<span className={styles.type}>{t(lowerFirst(data.label))}</span>
|
75 |
-
<NodeDropdown id={id}></NodeDropdown>
|
76 |
</Flex>
|
77 |
-
<section className={styles.bottomBox}>
|
78 |
-
<div className={styles.nodeName}>{data.name}</div>
|
79 |
-
</section>
|
80 |
</section>
|
81 |
</NodePopover>
|
82 |
);
|
|
|
1 |
+
import LLMLabel from '@/components/llm-select/llm-label';
|
2 |
import { Flex } from 'antd';
|
3 |
import classNames from 'classnames';
|
4 |
+
import { get } from 'lodash';
|
5 |
import { Handle, NodeProps, Position } from 'reactflow';
|
|
|
6 |
import { NodeData } from '../../interface';
|
7 |
+
import { RightHandleStyle } from './handle-icon';
|
|
|
|
|
8 |
import { useBuildCategorizeHandlePositions } from './hooks';
|
9 |
import styles from './index.less';
|
10 |
+
import NodeHeader from './node-header';
|
11 |
import NodePopover from './popover';
|
12 |
|
13 |
export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
|
|
|
|
|
14 |
const { positions } = useBuildCategorizeHandlePositions({ data, id });
|
|
|
15 |
|
16 |
return (
|
17 |
<NodePopover nodeId={id}>
|
|
|
19 |
className={classNames(styles.logicNode, {
|
20 |
[styles.selectedNode]: selected,
|
21 |
})}
|
|
|
|
|
|
|
|
|
22 |
>
|
23 |
<Handle
|
24 |
type="target"
|
|
|
27 |
className={styles.handle}
|
28 |
id={'a'}
|
29 |
></Handle>
|
30 |
+
|
31 |
+
<NodeHeader
|
32 |
+
id={id}
|
33 |
+
name={data.name}
|
34 |
+
label={data.label}
|
35 |
+
className={styles.nodeHeader}
|
36 |
+
></NodeHeader>
|
37 |
+
|
38 |
+
<Flex vertical gap={8}>
|
39 |
+
<div className={styles.nodeText}>
|
40 |
+
<LLMLabel value={get(data, 'form.llm_id')}></LLMLabel>
|
41 |
+
</div>
|
42 |
+
{positions.map((position, idx) => {
|
43 |
+
return (
|
44 |
+
<div key={idx}>
|
45 |
+
<div className={styles.nodeText}>{position.text}</div>
|
46 |
+
<Handle
|
47 |
+
key={position.text}
|
48 |
+
id={position.text}
|
49 |
+
type="source"
|
50 |
+
position={Position.Right}
|
51 |
+
isConnectable
|
52 |
+
className={styles.handle}
|
53 |
+
style={{ ...RightHandleStyle, top: position.top }}
|
54 |
+
></Handle>
|
55 |
+
</div>
|
56 |
+
);
|
57 |
+
})}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
</Flex>
|
|
|
|
|
|
|
59 |
</section>
|
60 |
</NodePopover>
|
61 |
);
|
web/src/pages/flow/canvas/node/dropdown.tsx
CHANGED
@@ -38,7 +38,7 @@ const NodeDropdown = ({ id, iconFontColor }: IProps) => {
|
|
38 |
|
39 |
return (
|
40 |
<OperateDropdown
|
41 |
-
iconFontSize={
|
42 |
height={14}
|
43 |
deleteItem={deleteNode}
|
44 |
items={items}
|
|
|
38 |
|
39 |
return (
|
40 |
<OperateDropdown
|
41 |
+
iconFontSize={22}
|
42 |
height={14}
|
43 |
deleteItem={deleteNode}
|
44 |
items={items}
|
web/src/pages/flow/canvas/node/generate-node.tsx
ADDED
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import LLMLabel from '@/components/llm-select/llm-label';
|
2 |
+
import { Flex } from 'antd';
|
3 |
+
import classNames from 'classnames';
|
4 |
+
import { get } from 'lodash';
|
5 |
+
import { Handle, NodeProps, Position } from 'reactflow';
|
6 |
+
import { useGetComponentLabelByValue } from '../../hooks';
|
7 |
+
import { IGenerateParameter, NodeData } from '../../interface';
|
8 |
+
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
|
9 |
+
import styles from './index.less';
|
10 |
+
import NodeHeader from './node-header';
|
11 |
+
import NodePopover from './popover';
|
12 |
+
|
13 |
+
export function GenerateNode({
|
14 |
+
id,
|
15 |
+
data,
|
16 |
+
isConnectable = true,
|
17 |
+
selected,
|
18 |
+
}: NodeProps<NodeData>) {
|
19 |
+
const parameters: IGenerateParameter[] = get(data, 'form.parameters', []);
|
20 |
+
const getLabel = useGetComponentLabelByValue(id);
|
21 |
+
|
22 |
+
return (
|
23 |
+
<NodePopover nodeId={id}>
|
24 |
+
<section
|
25 |
+
className={classNames(styles.logicNode, {
|
26 |
+
[styles.selectedNode]: selected,
|
27 |
+
})}
|
28 |
+
>
|
29 |
+
<Handle
|
30 |
+
id="c"
|
31 |
+
type="source"
|
32 |
+
position={Position.Left}
|
33 |
+
isConnectable={isConnectable}
|
34 |
+
className={styles.handle}
|
35 |
+
style={LeftHandleStyle}
|
36 |
+
></Handle>
|
37 |
+
<Handle
|
38 |
+
type="source"
|
39 |
+
position={Position.Right}
|
40 |
+
isConnectable={isConnectable}
|
41 |
+
className={styles.handle}
|
42 |
+
style={RightHandleStyle}
|
43 |
+
id="b"
|
44 |
+
></Handle>
|
45 |
+
|
46 |
+
<NodeHeader
|
47 |
+
id={id}
|
48 |
+
name={data.name}
|
49 |
+
label={data.label}
|
50 |
+
className={styles.nodeHeader}
|
51 |
+
></NodeHeader>
|
52 |
+
|
53 |
+
<div className={styles.nodeText}>
|
54 |
+
<LLMLabel value={get(data, 'form.llm_id')}></LLMLabel>
|
55 |
+
</div>
|
56 |
+
<Flex gap={8} vertical className={styles.generateParameters}>
|
57 |
+
{parameters.map((x) => (
|
58 |
+
<Flex
|
59 |
+
key={x.id}
|
60 |
+
align="center"
|
61 |
+
gap={6}
|
62 |
+
className={styles.conditionBlock}
|
63 |
+
>
|
64 |
+
<label htmlFor="">{x.key}</label>
|
65 |
+
<span className={styles.parameterValue}>
|
66 |
+
{getLabel(x.component_id)}
|
67 |
+
</span>
|
68 |
+
</Flex>
|
69 |
+
))}
|
70 |
+
</Flex>
|
71 |
+
</section>
|
72 |
+
</NodePopover>
|
73 |
+
);
|
74 |
+
}
|
web/src/pages/flow/canvas/node/handle-icon.tsx
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { PlusOutlined } from '@ant-design/icons';
|
2 |
+
import { CSSProperties } from 'react';
|
3 |
+
|
4 |
+
export const HandleIcon = () => {
|
5 |
+
return (
|
6 |
+
<PlusOutlined
|
7 |
+
style={{ fontSize: 6, color: 'white', position: 'absolute', zIndex: 10 }}
|
8 |
+
/>
|
9 |
+
);
|
10 |
+
};
|
11 |
+
|
12 |
+
export const RightHandleStyle: CSSProperties = {
|
13 |
+
right: -5,
|
14 |
+
};
|
15 |
+
|
16 |
+
export const LeftHandleStyle: CSSProperties = {
|
17 |
+
left: -7,
|
18 |
+
};
|
19 |
+
|
20 |
+
export default HandleIcon;
|
web/src/pages/flow/canvas/node/hooks.ts
CHANGED
@@ -1,14 +1,13 @@
|
|
1 |
import get from 'lodash/get';
|
2 |
-
import
|
3 |
-
import { useEffect, useMemo, useState } from 'react';
|
4 |
import { useUpdateNodeInternals } from 'reactflow';
|
5 |
-
import {
|
6 |
-
import { IPosition, NodeData } from '../../interface';
|
7 |
import {
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
} from '../../
|
|
|
12 |
|
13 |
export const useBuildCategorizeHandlePositions = ({
|
14 |
data,
|
@@ -17,85 +16,86 @@ export const useBuildCategorizeHandlePositions = ({
|
|
17 |
id: string;
|
18 |
data: NodeData;
|
19 |
}) => {
|
20 |
-
const operatorName = data.label as Operator;
|
21 |
const updateNodeInternals = useUpdateNodeInternals();
|
22 |
-
const [positionMap, setPositionMap] = useState<Record<string, IPosition>>({});
|
23 |
|
24 |
-
const categoryData = useMemo(() => {
|
25 |
-
|
26 |
-
|
27 |
-
} else if (operatorName === Operator.Switch) {
|
28 |
-
return get(data, 'form.conditions', []);
|
29 |
-
}
|
30 |
-
return {};
|
31 |
-
}, [data, operatorName]);
|
32 |
|
33 |
const positions = useMemo(() => {
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
text = generateSwitchHandleText(idx);
|
40 |
-
}
|
41 |
-
return { text, ...position };
|
42 |
-
})
|
43 |
-
.filter((x) => typeof x?.right === 'number');
|
44 |
-
}, [categoryData, positionMap, operatorName]);
|
45 |
-
|
46 |
-
useEffect(() => {
|
47 |
-
// Cache used coordinates
|
48 |
-
setPositionMap((state) => {
|
49 |
-
const categoryDataKeys = Object.keys(categoryData);
|
50 |
-
const stateKeys = Object.keys(state);
|
51 |
-
if (!isKeysEqual(categoryDataKeys, stateKeys)) {
|
52 |
-
const { newPositionMap, intersectionKeys } = buildNewPositionMap(
|
53 |
-
categoryDataKeys,
|
54 |
-
state,
|
55 |
-
);
|
56 |
-
|
57 |
-
const nextPositionMap = {
|
58 |
-
...pick(state, intersectionKeys),
|
59 |
-
...newPositionMap,
|
60 |
-
};
|
61 |
|
62 |
-
|
63 |
-
|
64 |
-
|
|
|
|
|
|
|
65 |
});
|
|
|
|
|
66 |
}, [categoryData]);
|
67 |
|
68 |
useEffect(() => {
|
69 |
updateNodeInternals(id);
|
70 |
-
}, [id, updateNodeInternals,
|
71 |
|
72 |
return { positions };
|
73 |
};
|
74 |
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
// const conditions = useMemo(() => get(data, 'form.conditions', []), [data]);
|
84 |
-
// const updateNodeInternals = useUpdateNodeInternals();
|
85 |
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
// const text = `Item ${idx}`;
|
90 |
-
// const position = positionMap[text];
|
91 |
-
// return { text: text, ...position };
|
92 |
-
// })
|
93 |
-
// .filter((x) => typeof x?.right === 'number');
|
94 |
-
// }, [conditions, positionMap]);
|
95 |
|
96 |
-
|
97 |
-
|
98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
99 |
|
100 |
-
|
101 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import get from 'lodash/get';
|
2 |
+
import { useEffect, useMemo } from 'react';
|
|
|
3 |
import { useUpdateNodeInternals } from 'reactflow';
|
4 |
+
import { SwitchElseTo } from '../../constant';
|
|
|
5 |
import {
|
6 |
+
ICategorizeItemResult,
|
7 |
+
ISwitchCondition,
|
8 |
+
NodeData,
|
9 |
+
} from '../../interface';
|
10 |
+
import { generateSwitchHandleText } from '../../utils';
|
11 |
|
12 |
export const useBuildCategorizeHandlePositions = ({
|
13 |
data,
|
|
|
16 |
id: string;
|
17 |
data: NodeData;
|
18 |
}) => {
|
|
|
19 |
const updateNodeInternals = useUpdateNodeInternals();
|
|
|
20 |
|
21 |
+
const categoryData: ICategorizeItemResult = useMemo(() => {
|
22 |
+
return get(data, `form.category_description`, {});
|
23 |
+
}, [data]);
|
|
|
|
|
|
|
|
|
|
|
24 |
|
25 |
const positions = useMemo(() => {
|
26 |
+
const list: Array<{
|
27 |
+
text: string;
|
28 |
+
top: number;
|
29 |
+
idx: number;
|
30 |
+
}> = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
|
32 |
+
Object.keys(categoryData).forEach((x, idx) => {
|
33 |
+
list.push({
|
34 |
+
text: x,
|
35 |
+
idx,
|
36 |
+
top: idx === 0 ? 98 : list[idx - 1].top + 8 + 26,
|
37 |
+
});
|
38 |
});
|
39 |
+
|
40 |
+
return list;
|
41 |
}, [categoryData]);
|
42 |
|
43 |
useEffect(() => {
|
44 |
updateNodeInternals(id);
|
45 |
+
}, [id, updateNodeInternals, categoryData]);
|
46 |
|
47 |
return { positions };
|
48 |
};
|
49 |
|
50 |
+
export const useBuildSwitchHandlePositions = ({
|
51 |
+
data,
|
52 |
+
id,
|
53 |
+
}: {
|
54 |
+
id: string;
|
55 |
+
data: NodeData;
|
56 |
+
}) => {
|
57 |
+
const updateNodeInternals = useUpdateNodeInternals();
|
|
|
|
|
58 |
|
59 |
+
const conditions: ISwitchCondition[] = useMemo(() => {
|
60 |
+
return get(data, 'form.conditions', []);
|
61 |
+
}, [data]);
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
|
63 |
+
const positions = useMemo(() => {
|
64 |
+
const list: Array<{
|
65 |
+
text: string;
|
66 |
+
top: number;
|
67 |
+
idx: number;
|
68 |
+
condition?: ISwitchCondition;
|
69 |
+
}> = [];
|
70 |
+
|
71 |
+
[...conditions, ''].forEach((x, idx) => {
|
72 |
+
let top = idx === 0 ? 58 : list[idx - 1].top + 32; // case number (Case 1) height + flex gap
|
73 |
+
if (idx - 1 >= 0) {
|
74 |
+
const previousItems = conditions[idx - 1]?.items ?? [];
|
75 |
+
if (previousItems.length > 0) {
|
76 |
+
top += 12; // ConditionBlock padding
|
77 |
+
top += previousItems.length * 22; // condition variable height
|
78 |
+
top += (previousItems.length - 1) * 25; // operator height
|
79 |
+
}
|
80 |
+
}
|
81 |
+
|
82 |
+
list.push({
|
83 |
+
text:
|
84 |
+
idx < conditions.length
|
85 |
+
? generateSwitchHandleText(idx)
|
86 |
+
: SwitchElseTo,
|
87 |
+
idx,
|
88 |
+
top,
|
89 |
+
condition: typeof x === 'string' ? undefined : x,
|
90 |
+
});
|
91 |
+
});
|
92 |
|
93 |
+
return list;
|
94 |
+
}, [conditions]);
|
95 |
+
|
96 |
+
useEffect(() => {
|
97 |
+
updateNodeInternals(id);
|
98 |
+
}, [id, updateNodeInternals, conditions]);
|
99 |
+
|
100 |
+
return { positions };
|
101 |
+
};
|
web/src/pages/flow/canvas/node/index.less
CHANGED
@@ -3,22 +3,16 @@
|
|
3 |
-6px 0 12px 0 rgba(179, 177, 177, 0.08),
|
4 |
-3px 0 6px -4px rgba(0, 0, 0, 0.12),
|
5 |
-6px 0 16px 6px rgba(0, 0, 0, 0.05);
|
|
|
|
|
|
|
|
|
|
|
6 |
}
|
7 |
|
8 |
.ragNode {
|
9 |
-
position: relative;
|
10 |
.commonNode();
|
11 |
|
12 |
-
padding: 5px;
|
13 |
-
border-radius: 5px;
|
14 |
-
background: white;
|
15 |
-
width: 50px;
|
16 |
-
height: 50px;
|
17 |
-
border-radius: 50%;
|
18 |
-
display: flex;
|
19 |
-
// align-items: center;
|
20 |
-
// justify-self: center;
|
21 |
-
justify-content: center;
|
22 |
.nodeName {
|
23 |
font-size: 10px;
|
24 |
color: black;
|
@@ -28,23 +22,10 @@
|
|
28 |
color: #777;
|
29 |
font-size: 12px;
|
30 |
}
|
31 |
-
.type {
|
32 |
-
// font-size: 12px;
|
33 |
-
}
|
34 |
.description {
|
35 |
font-size: 10px;
|
36 |
}
|
37 |
-
|
38 |
-
position: absolute;
|
39 |
-
bottom: -34px;
|
40 |
-
background: white;
|
41 |
-
padding: 2px 5px;
|
42 |
-
border-radius: 5px;
|
43 |
-
box-shadow:
|
44 |
-
-6px 0 12px 0 rgba(179, 177, 177, 0.08),
|
45 |
-
-3px 0 6px -4px rgba(0, 0, 0, 0.12),
|
46 |
-
-6px 0 16px 6px rgba(0, 0, 0, 0.05);
|
47 |
-
}
|
48 |
.categorizeAnchorPointText {
|
49 |
position: absolute;
|
50 |
top: -4px;
|
@@ -53,14 +34,25 @@
|
|
53 |
}
|
54 |
}
|
55 |
|
|
|
|
|
|
|
56 |
.selectedNode {
|
57 |
-
border:
|
58 |
}
|
59 |
|
60 |
.handle {
|
61 |
display: inline-flex;
|
62 |
-
|
63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
}
|
65 |
|
66 |
.jsonView {
|
@@ -71,19 +63,8 @@
|
|
71 |
}
|
72 |
|
73 |
.logicNode {
|
74 |
-
position: relative;
|
75 |
.commonNode();
|
76 |
|
77 |
-
padding: 5px;
|
78 |
-
border-radius: 5px;
|
79 |
-
background: white;
|
80 |
-
width: 100px;
|
81 |
-
height: 100px;
|
82 |
-
border-radius: 50%;
|
83 |
-
display: flex;
|
84 |
-
// align-items: center;
|
85 |
-
// justify-self: center;
|
86 |
-
justify-content: center;
|
87 |
.nodeName {
|
88 |
font-size: 10px;
|
89 |
color: black;
|
@@ -93,41 +74,122 @@
|
|
93 |
color: #777;
|
94 |
font-size: 12px;
|
95 |
}
|
96 |
-
|
97 |
-
// font-size: 12px;
|
98 |
-
}
|
99 |
.description {
|
100 |
font-size: 10px;
|
101 |
}
|
102 |
-
|
103 |
-
position: absolute;
|
104 |
-
bottom: -34px;
|
105 |
-
background: white;
|
106 |
-
padding: 2px 5px;
|
107 |
-
border-radius: 5px;
|
108 |
-
box-shadow:
|
109 |
-
-6px 0 12px 0 rgba(179, 177, 177, 0.08),
|
110 |
-
-3px 0 6px -4px rgba(0, 0, 0, 0.12),
|
111 |
-
-6px 0 16px 6px rgba(0, 0, 0, 0.05);
|
112 |
-
}
|
113 |
.categorizeAnchorPointText {
|
114 |
position: absolute;
|
115 |
top: -4px;
|
116 |
left: 8px;
|
117 |
white-space: nowrap;
|
118 |
}
|
|
|
|
|
|
|
119 |
}
|
120 |
|
121 |
.noteNode {
|
122 |
.commonNode();
|
123 |
-
width: 140px;
|
124 |
-
|
|
|
|
|
125 |
border-radius: 10px;
|
126 |
-
|
127 |
.noteTitle {
|
|
|
128 |
font-size: 12px;
|
|
|
|
|
|
|
129 |
}
|
130 |
.noteForm {
|
131 |
margin-top: 4px;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
132 |
}
|
133 |
}
|
|
|
3 |
-6px 0 12px 0 rgba(179, 177, 177, 0.08),
|
4 |
-3px 0 6px -4px rgba(0, 0, 0, 0.12),
|
5 |
-6px 0 16px 6px rgba(0, 0, 0, 0.05);
|
6 |
+
|
7 |
+
padding: 10px;
|
8 |
+
border-radius: 10px;
|
9 |
+
background: white;
|
10 |
+
width: 200px;
|
11 |
}
|
12 |
|
13 |
.ragNode {
|
|
|
14 |
.commonNode();
|
15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
.nodeName {
|
17 |
font-size: 10px;
|
18 |
color: black;
|
|
|
22 |
color: #777;
|
23 |
font-size: 12px;
|
24 |
}
|
|
|
|
|
|
|
25 |
.description {
|
26 |
font-size: 10px;
|
27 |
}
|
28 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
.categorizeAnchorPointText {
|
30 |
position: absolute;
|
31 |
top: -4px;
|
|
|
34 |
}
|
35 |
}
|
36 |
|
37 |
+
@lightBackgroundColor: rgba(150, 150, 150, 0.1);
|
38 |
+
@darkBackgroundColor: rgba(150, 150, 150, 0.2);
|
39 |
+
|
40 |
.selectedNode {
|
41 |
+
border: 1.5px solid rgb(59, 118, 244);
|
42 |
}
|
43 |
|
44 |
.handle {
|
45 |
display: inline-flex;
|
46 |
+
align-items: center;
|
47 |
+
justify-content: center;
|
48 |
+
width: 12px;
|
49 |
+
height: 12px;
|
50 |
+
background: rgb(59, 88, 253);
|
51 |
+
border: 1px solid white;
|
52 |
+
z-index: 1;
|
53 |
+
background-image: url('@/assets/svg/plus.svg');
|
54 |
+
background-size: cover;
|
55 |
+
background-position: center;
|
56 |
}
|
57 |
|
58 |
.jsonView {
|
|
|
63 |
}
|
64 |
|
65 |
.logicNode {
|
|
|
66 |
.commonNode();
|
67 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
.nodeName {
|
69 |
font-size: 10px;
|
70 |
color: black;
|
|
|
74 |
color: #777;
|
75 |
font-size: 12px;
|
76 |
}
|
77 |
+
|
|
|
|
|
78 |
.description {
|
79 |
font-size: 10px;
|
80 |
}
|
81 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
.categorizeAnchorPointText {
|
83 |
position: absolute;
|
84 |
top: -4px;
|
85 |
left: 8px;
|
86 |
white-space: nowrap;
|
87 |
}
|
88 |
+
.relevantSourceLabel {
|
89 |
+
font-size: 10px;
|
90 |
+
}
|
91 |
}
|
92 |
|
93 |
.noteNode {
|
94 |
.commonNode();
|
95 |
+
min-width: 140px;
|
96 |
+
width: auto;
|
97 |
+
height: 100%;
|
98 |
+
padding: 0;
|
99 |
border-radius: 10px;
|
100 |
+
min-height: 128px;
|
101 |
.noteTitle {
|
102 |
+
background-color: #edfcff;
|
103 |
font-size: 12px;
|
104 |
+
padding: 6px 6px 4px;
|
105 |
+
border-top-left-radius: 10px;
|
106 |
+
border-top-right-radius: 10px;
|
107 |
}
|
108 |
.noteForm {
|
109 |
margin-top: 4px;
|
110 |
+
height: calc(100% - 50px);
|
111 |
+
}
|
112 |
+
.noteName {
|
113 |
+
padding: 0px 4px;
|
114 |
+
}
|
115 |
+
.noteTextarea {
|
116 |
+
resize: none;
|
117 |
+
border: 0;
|
118 |
+
border-radius: 0;
|
119 |
+
height: 100%;
|
120 |
+
&:focus {
|
121 |
+
border: none;
|
122 |
+
box-shadow: none;
|
123 |
+
}
|
124 |
+
}
|
125 |
+
}
|
126 |
+
|
127 |
+
.nodeText {
|
128 |
+
padding-inline: 0.4em;
|
129 |
+
padding-block: 0.2em 0.1em;
|
130 |
+
background: @lightBackgroundColor;
|
131 |
+
border-radius: 3px;
|
132 |
+
min-height: 22px;
|
133 |
+
.textEllipsis();
|
134 |
+
}
|
135 |
+
|
136 |
+
.nodeTitle {
|
137 |
+
font-weight: 600;
|
138 |
+
text-align: center;
|
139 |
+
.textEllipsis();
|
140 |
+
}
|
141 |
+
|
142 |
+
.nodeHeader {
|
143 |
+
padding-bottom: 12px;
|
144 |
+
}
|
145 |
+
|
146 |
+
.zeroDivider {
|
147 |
+
margin: 0 !important;
|
148 |
+
}
|
149 |
+
|
150 |
+
.conditionBlock {
|
151 |
+
border-radius: 4px;
|
152 |
+
padding: 6px;
|
153 |
+
background: @lightBackgroundColor;
|
154 |
+
}
|
155 |
+
|
156 |
+
.conditionLine {
|
157 |
+
border-radius: 4px;
|
158 |
+
padding: 0 4px;
|
159 |
+
background: @darkBackgroundColor;
|
160 |
+
.textEllipsis();
|
161 |
+
}
|
162 |
+
|
163 |
+
.conditionKey {
|
164 |
+
flex: 1;
|
165 |
+
}
|
166 |
+
|
167 |
+
.conditionOperator {
|
168 |
+
padding: 0 2px;
|
169 |
+
text-align: center;
|
170 |
+
}
|
171 |
+
|
172 |
+
.relevantLabel {
|
173 |
+
text-align: right;
|
174 |
+
}
|
175 |
+
|
176 |
+
.knowledgeNodeName {
|
177 |
+
.textEllipsis();
|
178 |
+
}
|
179 |
+
|
180 |
+
.messageNodeContainer {
|
181 |
+
overflow-y: auto;
|
182 |
+
max-height: 300px;
|
183 |
+
}
|
184 |
+
|
185 |
+
.generateParameters {
|
186 |
+
padding-top: 8px;
|
187 |
+
label {
|
188 |
+
flex: 2;
|
189 |
+
.textEllipsis();
|
190 |
+
}
|
191 |
+
.parameterValue {
|
192 |
+
flex: 3;
|
193 |
+
.conditionLine;
|
194 |
}
|
195 |
}
|
web/src/pages/flow/canvas/node/index.tsx
CHANGED
@@ -1,12 +1,9 @@
|
|
1 |
-
import { Flex } from 'antd';
|
2 |
import classNames from 'classnames';
|
3 |
-
import pick from 'lodash/pick';
|
4 |
import { Handle, NodeProps, Position } from 'reactflow';
|
5 |
-
import { Operator, operatorMap } from '../../constant';
|
6 |
import { NodeData } from '../../interface';
|
7 |
-
import
|
8 |
-
import NodeDropdown from './dropdown';
|
9 |
import styles from './index.less';
|
|
|
10 |
import NodePopover from './popover';
|
11 |
|
12 |
export function RagNode({
|
@@ -15,17 +12,12 @@ export function RagNode({
|
|
15 |
isConnectable = true,
|
16 |
selected,
|
17 |
}: NodeProps<NodeData>) {
|
18 |
-
const style = operatorMap[data.label as Operator];
|
19 |
-
|
20 |
return (
|
21 |
<NodePopover nodeId={id}>
|
22 |
<section
|
23 |
className={classNames(styles.ragNode, {
|
24 |
[styles.selectedNode]: selected,
|
25 |
})}
|
26 |
-
style={{
|
27 |
-
...pick(style, ['backgroundColor', 'color']),
|
28 |
-
}}
|
29 |
>
|
30 |
<Handle
|
31 |
id="c"
|
@@ -33,39 +25,17 @@ export function RagNode({
|
|
33 |
position={Position.Left}
|
34 |
isConnectable={isConnectable}
|
35 |
className={styles.handle}
|
|
|
36 |
></Handle>
|
37 |
-
<Handle type="source" position={Position.Top} id="d" isConnectable />
|
38 |
<Handle
|
39 |
type="source"
|
40 |
position={Position.Right}
|
41 |
isConnectable={isConnectable}
|
42 |
className={styles.handle}
|
43 |
id="b"
|
|
|
44 |
></Handle>
|
45 |
-
<
|
46 |
-
<Flex vertical align="center" justify={'space-around'}>
|
47 |
-
<Flex flex={1} justify="center" align="center">
|
48 |
-
<label htmlFor=""> </label>
|
49 |
-
</Flex>
|
50 |
-
|
51 |
-
<Flex flex={1}>
|
52 |
-
<OperatorIcon
|
53 |
-
name={data.label as Operator}
|
54 |
-
fontSize={style?.iconFontSize ?? 16}
|
55 |
-
width={style?.iconWidth}
|
56 |
-
></OperatorIcon>
|
57 |
-
</Flex>
|
58 |
-
<Flex flex={1}>
|
59 |
-
<NodeDropdown
|
60 |
-
id={id}
|
61 |
-
iconFontColor={style?.moreIconColor}
|
62 |
-
></NodeDropdown>
|
63 |
-
</Flex>
|
64 |
-
</Flex>
|
65 |
-
|
66 |
-
<section className={styles.bottomBox}>
|
67 |
-
<div className={styles.nodeName}>{data.name}</div>
|
68 |
-
</section>
|
69 |
</section>
|
70 |
</NodePopover>
|
71 |
);
|
|
|
|
|
1 |
import classNames from 'classnames';
|
|
|
2 |
import { Handle, NodeProps, Position } from 'reactflow';
|
|
|
3 |
import { NodeData } from '../../interface';
|
4 |
+
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
|
|
|
5 |
import styles from './index.less';
|
6 |
+
import NodeHeader from './node-header';
|
7 |
import NodePopover from './popover';
|
8 |
|
9 |
export function RagNode({
|
|
|
12 |
isConnectable = true,
|
13 |
selected,
|
14 |
}: NodeProps<NodeData>) {
|
|
|
|
|
15 |
return (
|
16 |
<NodePopover nodeId={id}>
|
17 |
<section
|
18 |
className={classNames(styles.ragNode, {
|
19 |
[styles.selectedNode]: selected,
|
20 |
})}
|
|
|
|
|
|
|
21 |
>
|
22 |
<Handle
|
23 |
id="c"
|
|
|
25 |
position={Position.Left}
|
26 |
isConnectable={isConnectable}
|
27 |
className={styles.handle}
|
28 |
+
style={LeftHandleStyle}
|
29 |
></Handle>
|
|
|
30 |
<Handle
|
31 |
type="source"
|
32 |
position={Position.Right}
|
33 |
isConnectable={isConnectable}
|
34 |
className={styles.handle}
|
35 |
id="b"
|
36 |
+
style={RightHandleStyle}
|
37 |
></Handle>
|
38 |
+
<NodeHeader id={id} name={data.name} label={data.label}></NodeHeader>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
</section>
|
40 |
</NodePopover>
|
41 |
);
|
web/src/pages/flow/canvas/node/keyword-node.tsx
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import LLMLabel from '@/components/llm-select/llm-label';
|
2 |
+
import classNames from 'classnames';
|
3 |
+
import { get } from 'lodash';
|
4 |
+
import { Handle, NodeProps, Position } from 'reactflow';
|
5 |
+
import { NodeData } from '../../interface';
|
6 |
+
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
|
7 |
+
import styles from './index.less';
|
8 |
+
import NodeHeader from './node-header';
|
9 |
+
import NodePopover from './popover';
|
10 |
+
|
11 |
+
export function KeywordNode({
|
12 |
+
id,
|
13 |
+
data,
|
14 |
+
isConnectable = true,
|
15 |
+
selected,
|
16 |
+
}: NodeProps<NodeData>) {
|
17 |
+
return (
|
18 |
+
<NodePopover nodeId={id}>
|
19 |
+
<section
|
20 |
+
className={classNames(styles.logicNode, {
|
21 |
+
[styles.selectedNode]: selected,
|
22 |
+
})}
|
23 |
+
>
|
24 |
+
<Handle
|
25 |
+
id="c"
|
26 |
+
type="source"
|
27 |
+
position={Position.Left}
|
28 |
+
isConnectable={isConnectable}
|
29 |
+
className={styles.handle}
|
30 |
+
style={LeftHandleStyle}
|
31 |
+
></Handle>
|
32 |
+
<Handle
|
33 |
+
type="source"
|
34 |
+
position={Position.Right}
|
35 |
+
isConnectable={isConnectable}
|
36 |
+
className={styles.handle}
|
37 |
+
style={RightHandleStyle}
|
38 |
+
id="b"
|
39 |
+
></Handle>
|
40 |
+
|
41 |
+
<NodeHeader
|
42 |
+
id={id}
|
43 |
+
name={data.name}
|
44 |
+
label={data.label}
|
45 |
+
className={styles.nodeHeader}
|
46 |
+
></NodeHeader>
|
47 |
+
|
48 |
+
<div className={styles.nodeText}>
|
49 |
+
<LLMLabel value={get(data, 'form.llm_id')}></LLMLabel>
|
50 |
+
</div>
|
51 |
+
</section>
|
52 |
+
</NodePopover>
|
53 |
+
);
|
54 |
+
}
|
web/src/pages/flow/canvas/node/logic-node.tsx
CHANGED
@@ -1,38 +1,23 @@
|
|
1 |
-
import { useTranslate } from '@/hooks/common-hooks';
|
2 |
-
import { Flex } from 'antd';
|
3 |
import classNames from 'classnames';
|
4 |
-
import lowerFirst from 'lodash/lowerFirst';
|
5 |
-
import pick from 'lodash/pick';
|
6 |
import { Handle, NodeProps, Position } from 'reactflow';
|
7 |
-
import { Operator, operatorMap } from '../../constant';
|
8 |
import { NodeData } from '../../interface';
|
9 |
-
import
|
10 |
-
import NodeDropdown from './dropdown';
|
11 |
import styles from './index.less';
|
|
|
12 |
import NodePopover from './popover';
|
13 |
|
14 |
-
const ZeroGapOperators = [
|
15 |
-
Operator.RewriteQuestion,
|
16 |
-
Operator.KeywordExtract,
|
17 |
-
Operator.ArXiv,
|
18 |
-
];
|
19 |
-
|
20 |
export function LogicNode({
|
21 |
id,
|
22 |
data,
|
23 |
isConnectable = true,
|
24 |
selected,
|
25 |
}: NodeProps<NodeData>) {
|
26 |
-
const style = operatorMap[data.label as Operator];
|
27 |
-
const { t } = useTranslate('flow');
|
28 |
-
|
29 |
return (
|
30 |
<NodePopover nodeId={id}>
|
31 |
<section
|
32 |
className={classNames(styles.logicNode, {
|
33 |
[styles.selectedNode]: selected,
|
34 |
})}
|
35 |
-
style={pick(style, ['backgroundColor', 'width', 'height', 'color'])}
|
36 |
>
|
37 |
<Handle
|
38 |
id="c"
|
@@ -40,49 +25,17 @@ export function LogicNode({
|
|
40 |
position={Position.Left}
|
41 |
isConnectable={isConnectable}
|
42 |
className={styles.handle}
|
|
|
43 |
></Handle>
|
44 |
-
<Handle type="source" position={Position.Top} id="d" isConnectable />
|
45 |
<Handle
|
46 |
type="source"
|
47 |
position={Position.Right}
|
48 |
isConnectable={isConnectable}
|
49 |
className={styles.handle}
|
|
|
50 |
id="b"
|
51 |
></Handle>
|
52 |
-
<
|
53 |
-
<Flex
|
54 |
-
vertical
|
55 |
-
align="center"
|
56 |
-
justify={'space-around'}
|
57 |
-
gap={ZeroGapOperators.some((x) => x === data.label) ? 0 : 6}
|
58 |
-
>
|
59 |
-
<Flex flex={1} justify="center" align="center">
|
60 |
-
<OperatorIcon
|
61 |
-
name={data.label as Operator}
|
62 |
-
fontSize={style?.iconFontSize ?? 24}
|
63 |
-
width={style?.iconWidth}
|
64 |
-
></OperatorIcon>
|
65 |
-
</Flex>
|
66 |
-
|
67 |
-
<Flex flex={1}>
|
68 |
-
<span
|
69 |
-
className={styles.type}
|
70 |
-
style={{ fontSize: style?.fontSize ?? 14 }}
|
71 |
-
>
|
72 |
-
{t(lowerFirst(data.label))}
|
73 |
-
</span>
|
74 |
-
</Flex>
|
75 |
-
<Flex flex={1}>
|
76 |
-
<NodeDropdown
|
77 |
-
id={id}
|
78 |
-
iconFontColor={style?.moreIconColor}
|
79 |
-
></NodeDropdown>
|
80 |
-
</Flex>
|
81 |
-
</Flex>
|
82 |
-
|
83 |
-
<section className={styles.bottomBox}>
|
84 |
-
<div className={styles.nodeName}>{data.name}</div>
|
85 |
-
</section>
|
86 |
</section>
|
87 |
</NodePopover>
|
88 |
);
|
|
|
|
|
|
|
1 |
import classNames from 'classnames';
|
|
|
|
|
2 |
import { Handle, NodeProps, Position } from 'reactflow';
|
|
|
3 |
import { NodeData } from '../../interface';
|
4 |
+
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
|
|
|
5 |
import styles from './index.less';
|
6 |
+
import NodeHeader from './node-header';
|
7 |
import NodePopover from './popover';
|
8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
export function LogicNode({
|
10 |
id,
|
11 |
data,
|
12 |
isConnectable = true,
|
13 |
selected,
|
14 |
}: NodeProps<NodeData>) {
|
|
|
|
|
|
|
15 |
return (
|
16 |
<NodePopover nodeId={id}>
|
17 |
<section
|
18 |
className={classNames(styles.logicNode, {
|
19 |
[styles.selectedNode]: selected,
|
20 |
})}
|
|
|
21 |
>
|
22 |
<Handle
|
23 |
id="c"
|
|
|
25 |
position={Position.Left}
|
26 |
isConnectable={isConnectable}
|
27 |
className={styles.handle}
|
28 |
+
style={LeftHandleStyle}
|
29 |
></Handle>
|
|
|
30 |
<Handle
|
31 |
type="source"
|
32 |
position={Position.Right}
|
33 |
isConnectable={isConnectable}
|
34 |
className={styles.handle}
|
35 |
+
style={RightHandleStyle}
|
36 |
id="b"
|
37 |
></Handle>
|
38 |
+
<NodeHeader id={id} name={data.name} label={data.label}></NodeHeader>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
</section>
|
40 |
</NodePopover>
|
41 |
);
|
web/src/pages/flow/canvas/node/message-node.tsx
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Flex } from 'antd';
|
2 |
+
import classNames from 'classnames';
|
3 |
+
import { get } from 'lodash';
|
4 |
+
import { Handle, NodeProps, Position } from 'reactflow';
|
5 |
+
import { NodeData } from '../../interface';
|
6 |
+
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
|
7 |
+
import styles from './index.less';
|
8 |
+
import NodeHeader from './node-header';
|
9 |
+
import NodePopover from './popover';
|
10 |
+
|
11 |
+
export function MessageNode({
|
12 |
+
id,
|
13 |
+
data,
|
14 |
+
isConnectable = true,
|
15 |
+
selected,
|
16 |
+
}: NodeProps<NodeData>) {
|
17 |
+
const messages: string[] = get(data, 'form.messages', []);
|
18 |
+
|
19 |
+
return (
|
20 |
+
<NodePopover nodeId={id}>
|
21 |
+
<section
|
22 |
+
className={classNames(styles.logicNode, {
|
23 |
+
[styles.selectedNode]: selected,
|
24 |
+
})}
|
25 |
+
>
|
26 |
+
<Handle
|
27 |
+
id="c"
|
28 |
+
type="source"
|
29 |
+
position={Position.Left}
|
30 |
+
isConnectable={isConnectable}
|
31 |
+
className={styles.handle}
|
32 |
+
style={LeftHandleStyle}
|
33 |
+
></Handle>
|
34 |
+
<Handle
|
35 |
+
type="source"
|
36 |
+
position={Position.Right}
|
37 |
+
isConnectable={isConnectable}
|
38 |
+
className={styles.handle}
|
39 |
+
style={RightHandleStyle}
|
40 |
+
id="b"
|
41 |
+
></Handle>
|
42 |
+
<NodeHeader
|
43 |
+
id={id}
|
44 |
+
name={data.name}
|
45 |
+
label={data.label}
|
46 |
+
className={classNames({
|
47 |
+
[styles.nodeHeader]: messages.length > 0,
|
48 |
+
})}
|
49 |
+
></NodeHeader>
|
50 |
+
|
51 |
+
<Flex vertical gap={8} className={styles.messageNodeContainer}>
|
52 |
+
{messages.map((message, idx) => {
|
53 |
+
return (
|
54 |
+
<div className={styles.nodeText} key={idx}>
|
55 |
+
{message}
|
56 |
+
</div>
|
57 |
+
);
|
58 |
+
})}
|
59 |
+
</Flex>
|
60 |
+
</section>
|
61 |
+
</NodePopover>
|
62 |
+
);
|
63 |
+
}
|
web/src/pages/flow/canvas/node/node-header.tsx
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Flex } from 'antd';
|
2 |
+
|
3 |
+
import { Operator, operatorMap } from '../../constant';
|
4 |
+
import OperatorIcon from '../../operator-icon';
|
5 |
+
import NodeDropdown from './dropdown';
|
6 |
+
import styles from './index.less';
|
7 |
+
|
8 |
+
interface IProps {
|
9 |
+
id: string;
|
10 |
+
label: string;
|
11 |
+
name: string;
|
12 |
+
gap?: number;
|
13 |
+
className?: string;
|
14 |
+
}
|
15 |
+
|
16 |
+
const NodeHeader = ({ label, id, name, gap = 4, className }: IProps) => {
|
17 |
+
return (
|
18 |
+
<Flex
|
19 |
+
flex={1}
|
20 |
+
align="center"
|
21 |
+
justify={'space-between'}
|
22 |
+
gap={gap}
|
23 |
+
className={className}
|
24 |
+
>
|
25 |
+
<OperatorIcon
|
26 |
+
name={label as Operator}
|
27 |
+
color={operatorMap[label as Operator].color}
|
28 |
+
></OperatorIcon>
|
29 |
+
<span className={styles.nodeTitle}>{name}</span>
|
30 |
+
<NodeDropdown id={id}></NodeDropdown>
|
31 |
+
</Flex>
|
32 |
+
);
|
33 |
+
};
|
34 |
+
|
35 |
+
export default NodeHeader;
|
web/src/pages/flow/canvas/node/note-node.tsx
CHANGED
@@ -1,20 +1,33 @@
|
|
1 |
-
import { Flex, Form, Input
|
2 |
-
import
|
|
|
3 |
import { NodeData } from '../../interface';
|
4 |
import NodeDropdown from './dropdown';
|
5 |
|
6 |
import SvgIcon from '@/components/svg-icon';
|
7 |
-
import { useEffect } from 'react';
|
8 |
import { useTranslation } from 'react-i18next';
|
9 |
-
import {
|
|
|
|
|
|
|
10 |
import styles from './index.less';
|
11 |
|
12 |
const { TextArea } = Input;
|
13 |
|
|
|
|
|
|
|
|
|
|
|
14 |
function NoteNode({ data, id }: NodeProps<NodeData>) {
|
15 |
const { t } = useTranslation();
|
16 |
const [form] = Form.useForm();
|
17 |
|
|
|
|
|
|
|
|
|
18 |
const { handleValuesChange } = useHandleFormValuesChange(id);
|
19 |
|
20 |
useEffect(() => {
|
@@ -22,25 +35,51 @@ function NoteNode({ data, id }: NodeProps<NodeData>) {
|
|
22 |
}, [form, data?.form]);
|
23 |
|
24 |
return (
|
25 |
-
|
26 |
-
<
|
27 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
<SvgIcon name="note" width={14}></SvgIcon>
|
29 |
-
<
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
);
|
44 |
}
|
45 |
|
46 |
-
export default NoteNode;
|
|
|
1 |
+
import { Flex, Form, Input } from 'antd';
|
2 |
+
import classNames from 'classnames';
|
3 |
+
import { NodeProps, NodeResizeControl } from 'reactflow';
|
4 |
import { NodeData } from '../../interface';
|
5 |
import NodeDropdown from './dropdown';
|
6 |
|
7 |
import SvgIcon from '@/components/svg-icon';
|
8 |
+
import { memo, useEffect } from 'react';
|
9 |
import { useTranslation } from 'react-i18next';
|
10 |
+
import {
|
11 |
+
useHandleFormValuesChange,
|
12 |
+
useHandleNodeNameChange,
|
13 |
+
} from '../../hooks';
|
14 |
import styles from './index.less';
|
15 |
|
16 |
const { TextArea } = Input;
|
17 |
|
18 |
+
const controlStyle = {
|
19 |
+
background: 'transparent',
|
20 |
+
border: 'none',
|
21 |
+
};
|
22 |
+
|
23 |
function NoteNode({ data, id }: NodeProps<NodeData>) {
|
24 |
const { t } = useTranslation();
|
25 |
const [form] = Form.useForm();
|
26 |
|
27 |
+
const { name, handleNameBlur, handleNameChange } = useHandleNodeNameChange({
|
28 |
+
id,
|
29 |
+
data,
|
30 |
+
});
|
31 |
const { handleValuesChange } = useHandleFormValuesChange(id);
|
32 |
|
33 |
useEffect(() => {
|
|
|
35 |
}, [form, data?.form]);
|
36 |
|
37 |
return (
|
38 |
+
<>
|
39 |
+
<NodeResizeControl style={controlStyle} minWidth={190} minHeight={128}>
|
40 |
+
<SvgIcon
|
41 |
+
name="resize"
|
42 |
+
width={12}
|
43 |
+
style={{
|
44 |
+
position: 'absolute',
|
45 |
+
right: 5,
|
46 |
+
bottom: 5,
|
47 |
+
cursor: 'nwse-resize',
|
48 |
+
}}
|
49 |
+
></SvgIcon>
|
50 |
+
</NodeResizeControl>
|
51 |
+
<section className={styles.noteNode}>
|
52 |
+
<Flex
|
53 |
+
justify={'space-between'}
|
54 |
+
className={classNames(styles.noteTitle, 'note-drag-handle')}
|
55 |
+
align="center"
|
56 |
+
gap={6}
|
57 |
+
>
|
58 |
<SvgIcon name="note" width={14}></SvgIcon>
|
59 |
+
<Input
|
60 |
+
value={name ?? t('flow.note')}
|
61 |
+
onBlur={handleNameBlur}
|
62 |
+
onChange={handleNameChange}
|
63 |
+
className={styles.noteName}
|
64 |
+
></Input>
|
65 |
+
<NodeDropdown id={id}></NodeDropdown>
|
66 |
+
</Flex>
|
67 |
+
<Form
|
68 |
+
onValuesChange={handleValuesChange}
|
69 |
+
form={form}
|
70 |
+
className={styles.noteForm}
|
71 |
+
>
|
72 |
+
<Form.Item name="text" noStyle>
|
73 |
+
<TextArea
|
74 |
+
rows={3}
|
75 |
+
placeholder={t('flow.notePlaceholder')}
|
76 |
+
className={styles.noteTextarea}
|
77 |
+
/>
|
78 |
+
</Form.Item>
|
79 |
+
</Form>
|
80 |
+
</section>
|
81 |
+
</>
|
82 |
);
|
83 |
}
|
84 |
|
85 |
+
export default memo(NoteNode);
|
web/src/pages/flow/canvas/node/relevant-node.tsx
CHANGED
@@ -1,28 +1,23 @@
|
|
1 |
-
import { useTranslate } from '@/hooks/common-hooks';
|
2 |
import { Flex } from 'antd';
|
3 |
import classNames from 'classnames';
|
4 |
-
import lowerFirst from 'lodash/lowerFirst';
|
5 |
-
import pick from 'lodash/pick';
|
6 |
import { Handle, NodeProps, Position } from 'reactflow';
|
7 |
-
import { Operator, operatorMap } from '../../constant';
|
8 |
import { NodeData } from '../../interface';
|
9 |
-
import
|
10 |
-
import
|
11 |
|
12 |
-
import
|
13 |
import styles from './index.less';
|
14 |
-
import
|
15 |
|
16 |
export function RelevantNode({ id, data, selected }: NodeProps<NodeData>) {
|
17 |
-
const
|
18 |
-
const
|
19 |
return (
|
20 |
<NodePopover nodeId={id}>
|
21 |
<section
|
22 |
className={classNames(styles.logicNode, {
|
23 |
[styles.selectedNode]: selected,
|
24 |
})}
|
25 |
-
style={pick(style, ['backgroundColor', 'width', 'height', 'color'])}
|
26 |
>
|
27 |
<Handle
|
28 |
type="target"
|
@@ -32,43 +27,38 @@ export function RelevantNode({ id, data, selected }: NodeProps<NodeData>) {
|
|
32 |
id={'a'}
|
33 |
></Handle>
|
34 |
<Handle
|
35 |
-
type="
|
36 |
-
position={Position.
|
37 |
isConnectable
|
38 |
className={styles.handle}
|
39 |
-
id={'
|
|
|
40 |
></Handle>
|
41 |
<Handle
|
42 |
-
type="
|
43 |
-
position={Position.
|
44 |
isConnectable
|
45 |
className={styles.handle}
|
46 |
-
id={'
|
|
|
47 |
></Handle>
|
48 |
-
<
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
<
|
59 |
-
className={styles.type}
|
60 |
-
style={{ fontSize: style.fontSize ?? 14 }}
|
61 |
-
>
|
62 |
-
{t(lowerFirst(data.label))}
|
63 |
-
</span>
|
64 |
</Flex>
|
65 |
-
<Flex
|
66 |
-
<
|
|
|
67 |
</Flex>
|
68 |
</Flex>
|
69 |
-
<section className={styles.bottomBox}>
|
70 |
-
<div className={styles.nodeName}>{data.name}</div>
|
71 |
-
</section>
|
72 |
</section>
|
73 |
</NodePopover>
|
74 |
);
|
|
|
|
|
1 |
import { Flex } from 'antd';
|
2 |
import classNames from 'classnames';
|
|
|
|
|
3 |
import { Handle, NodeProps, Position } from 'reactflow';
|
|
|
4 |
import { NodeData } from '../../interface';
|
5 |
+
import { RightHandleStyle } from './handle-icon';
|
6 |
+
import NodePopover from './popover';
|
7 |
|
8 |
+
import { get } from 'lodash';
|
9 |
import styles from './index.less';
|
10 |
+
import NodeHeader from './node-header';
|
11 |
|
12 |
export function RelevantNode({ id, data, selected }: NodeProps<NodeData>) {
|
13 |
+
const yes = get(data, 'form.yes');
|
14 |
+
const no = get(data, 'form.no');
|
15 |
return (
|
16 |
<NodePopover nodeId={id}>
|
17 |
<section
|
18 |
className={classNames(styles.logicNode, {
|
19 |
[styles.selectedNode]: selected,
|
20 |
})}
|
|
|
21 |
>
|
22 |
<Handle
|
23 |
type="target"
|
|
|
27 |
id={'a'}
|
28 |
></Handle>
|
29 |
<Handle
|
30 |
+
type="source"
|
31 |
+
position={Position.Right}
|
32 |
isConnectable
|
33 |
className={styles.handle}
|
34 |
+
id={'yes'}
|
35 |
+
style={{ ...RightHandleStyle, top: 59 }}
|
36 |
></Handle>
|
37 |
<Handle
|
38 |
+
type="source"
|
39 |
+
position={Position.Right}
|
40 |
isConnectable
|
41 |
className={styles.handle}
|
42 |
+
id={'no'}
|
43 |
+
style={{ ...RightHandleStyle, top: 112 }}
|
44 |
></Handle>
|
45 |
+
<NodeHeader
|
46 |
+
id={id}
|
47 |
+
name={data.name}
|
48 |
+
label={data.label}
|
49 |
+
className={styles.nodeHeader}
|
50 |
+
></NodeHeader>
|
51 |
+
|
52 |
+
<Flex vertical gap={10}>
|
53 |
+
<Flex vertical>
|
54 |
+
<div className={styles.relevantLabel}>Yes</div>
|
55 |
+
<div className={styles.nodeText}>{yes}</div>
|
|
|
|
|
|
|
|
|
|
|
56 |
</Flex>
|
57 |
+
<Flex vertical>
|
58 |
+
<div className={styles.relevantLabel}>No</div>
|
59 |
+
<div className={styles.nodeText}>{no}</div>
|
60 |
</Flex>
|
61 |
</Flex>
|
|
|
|
|
|
|
62 |
</section>
|
63 |
</NodePopover>
|
64 |
);
|
web/src/pages/flow/canvas/node/retrieval-node.tsx
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useNextFetchKnowledgeList } from '@/hooks/knowledge-hooks';
|
2 |
+
import { UserOutlined } from '@ant-design/icons';
|
3 |
+
import { Avatar, Flex } from 'antd';
|
4 |
+
import classNames from 'classnames';
|
5 |
+
import { get } from 'lodash';
|
6 |
+
import { useMemo } from 'react';
|
7 |
+
import { Handle, NodeProps, Position } from 'reactflow';
|
8 |
+
import { NodeData } from '../../interface';
|
9 |
+
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
|
10 |
+
import styles from './index.less';
|
11 |
+
import NodeHeader from './node-header';
|
12 |
+
import NodePopover from './popover';
|
13 |
+
|
14 |
+
export function RetrievalNode({
|
15 |
+
id,
|
16 |
+
data,
|
17 |
+
isConnectable = true,
|
18 |
+
selected,
|
19 |
+
}: NodeProps<NodeData>) {
|
20 |
+
const knowledgeBaseIds: string[] = get(data, 'form.kb_ids', []);
|
21 |
+
const { list: knowledgeList } = useNextFetchKnowledgeList(true);
|
22 |
+
const knowledgeBases = useMemo(() => {
|
23 |
+
return knowledgeBaseIds.map((x) => {
|
24 |
+
const item = knowledgeList.find((y) => x === y.id);
|
25 |
+
return {
|
26 |
+
name: item?.name,
|
27 |
+
avatar: item?.avatar,
|
28 |
+
id: x,
|
29 |
+
};
|
30 |
+
});
|
31 |
+
}, [knowledgeList, knowledgeBaseIds]);
|
32 |
+
|
33 |
+
return (
|
34 |
+
<NodePopover nodeId={id}>
|
35 |
+
<section
|
36 |
+
className={classNames(styles.logicNode, {
|
37 |
+
[styles.selectedNode]: selected,
|
38 |
+
})}
|
39 |
+
>
|
40 |
+
<Handle
|
41 |
+
id="c"
|
42 |
+
type="source"
|
43 |
+
position={Position.Left}
|
44 |
+
isConnectable={isConnectable}
|
45 |
+
className={styles.handle}
|
46 |
+
style={LeftHandleStyle}
|
47 |
+
></Handle>
|
48 |
+
<Handle
|
49 |
+
type="source"
|
50 |
+
position={Position.Right}
|
51 |
+
isConnectable={isConnectable}
|
52 |
+
className={styles.handle}
|
53 |
+
style={RightHandleStyle}
|
54 |
+
id="b"
|
55 |
+
></Handle>
|
56 |
+
<NodeHeader
|
57 |
+
id={id}
|
58 |
+
name={data.name}
|
59 |
+
label={data.label}
|
60 |
+
className={classNames({
|
61 |
+
[styles.nodeHeader]: knowledgeBaseIds.length > 0,
|
62 |
+
})}
|
63 |
+
></NodeHeader>
|
64 |
+
<Flex vertical gap={8}>
|
65 |
+
{knowledgeBases.map((knowledge) => {
|
66 |
+
return (
|
67 |
+
<div className={styles.nodeText} key={knowledge.id}>
|
68 |
+
<Flex align={'center'} gap={6}>
|
69 |
+
<Avatar
|
70 |
+
size={26}
|
71 |
+
icon={<UserOutlined />}
|
72 |
+
src={knowledge.avatar}
|
73 |
+
/>
|
74 |
+
<Flex className={styles.knowledgeNodeName} flex={1}>
|
75 |
+
{knowledge.name}
|
76 |
+
</Flex>
|
77 |
+
</Flex>
|
78 |
+
</div>
|
79 |
+
);
|
80 |
+
})}
|
81 |
+
</Flex>
|
82 |
+
</section>
|
83 |
+
</NodePopover>
|
84 |
+
);
|
85 |
+
}
|
web/src/pages/flow/canvas/node/rewrite-node.tsx
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import LLMLabel from '@/components/llm-select/llm-label';
|
2 |
+
import classNames from 'classnames';
|
3 |
+
import { get } from 'lodash';
|
4 |
+
import { Handle, NodeProps, Position } from 'reactflow';
|
5 |
+
import { NodeData } from '../../interface';
|
6 |
+
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
|
7 |
+
import styles from './index.less';
|
8 |
+
import NodeHeader from './node-header';
|
9 |
+
import NodePopover from './popover';
|
10 |
+
|
11 |
+
export function RewriteNode({
|
12 |
+
id,
|
13 |
+
data,
|
14 |
+
isConnectable = true,
|
15 |
+
selected,
|
16 |
+
}: NodeProps<NodeData>) {
|
17 |
+
return (
|
18 |
+
<NodePopover nodeId={id}>
|
19 |
+
<section
|
20 |
+
className={classNames(styles.logicNode, {
|
21 |
+
[styles.selectedNode]: selected,
|
22 |
+
})}
|
23 |
+
>
|
24 |
+
<Handle
|
25 |
+
id="c"
|
26 |
+
type="source"
|
27 |
+
position={Position.Left}
|
28 |
+
isConnectable={isConnectable}
|
29 |
+
className={styles.handle}
|
30 |
+
style={LeftHandleStyle}
|
31 |
+
></Handle>
|
32 |
+
<Handle
|
33 |
+
type="source"
|
34 |
+
position={Position.Right}
|
35 |
+
isConnectable={isConnectable}
|
36 |
+
className={styles.handle}
|
37 |
+
style={RightHandleStyle}
|
38 |
+
id="b"
|
39 |
+
></Handle>
|
40 |
+
|
41 |
+
<NodeHeader
|
42 |
+
id={id}
|
43 |
+
name={data.name}
|
44 |
+
label={data.label}
|
45 |
+
className={styles.nodeHeader}
|
46 |
+
></NodeHeader>
|
47 |
+
|
48 |
+
<div className={styles.nodeText}>
|
49 |
+
<LLMLabel value={get(data, 'form.llm_id')}></LLMLabel>
|
50 |
+
</div>
|
51 |
+
</section>
|
52 |
+
</NodePopover>
|
53 |
+
);
|
54 |
+
}
|
web/src/pages/flow/canvas/node/switch-node.tsx
ADDED
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Divider, Flex } from 'antd';
|
2 |
+
import classNames from 'classnames';
|
3 |
+
import { Handle, NodeProps, Position } from 'reactflow';
|
4 |
+
import { useGetComponentLabelByValue } from '../../hooks';
|
5 |
+
import { ISwitchCondition, NodeData } from '../../interface';
|
6 |
+
import { RightHandleStyle } from './handle-icon';
|
7 |
+
import { useBuildSwitchHandlePositions } from './hooks';
|
8 |
+
import styles from './index.less';
|
9 |
+
import NodeHeader from './node-header';
|
10 |
+
import NodePopover from './popover';
|
11 |
+
|
12 |
+
const getConditionKey = (idx: number, length: number) => {
|
13 |
+
if (idx === 0 && length !== 1) {
|
14 |
+
return 'If';
|
15 |
+
} else if (idx === length - 1) {
|
16 |
+
return 'Else';
|
17 |
+
}
|
18 |
+
|
19 |
+
return 'ElseIf';
|
20 |
+
};
|
21 |
+
|
22 |
+
const ConditionBlock = ({
|
23 |
+
condition,
|
24 |
+
nodeId,
|
25 |
+
}: {
|
26 |
+
condition: ISwitchCondition;
|
27 |
+
nodeId: string;
|
28 |
+
}) => {
|
29 |
+
const items = condition?.items ?? [];
|
30 |
+
const getLabel = useGetComponentLabelByValue(nodeId);
|
31 |
+
return (
|
32 |
+
<Flex vertical className={styles.conditionBlock}>
|
33 |
+
{items.map((x, idx) => (
|
34 |
+
<div key={idx}>
|
35 |
+
<Flex>
|
36 |
+
<div
|
37 |
+
className={classNames(styles.conditionLine, styles.conditionKey)}
|
38 |
+
>
|
39 |
+
{getLabel(x?.cpn_id)}
|
40 |
+
</div>
|
41 |
+
<span className={styles.conditionOperator}>{x?.operator}</span>
|
42 |
+
<Flex flex={1} className={styles.conditionLine}>
|
43 |
+
{x?.value}
|
44 |
+
</Flex>
|
45 |
+
</Flex>
|
46 |
+
{idx + 1 < items.length && (
|
47 |
+
<Divider orientationMargin="0" className={styles.zeroDivider}>
|
48 |
+
{condition?.logical_operator}
|
49 |
+
</Divider>
|
50 |
+
)}
|
51 |
+
</div>
|
52 |
+
))}
|
53 |
+
</Flex>
|
54 |
+
);
|
55 |
+
};
|
56 |
+
|
57 |
+
export function SwitchNode({ id, data, selected }: NodeProps<NodeData>) {
|
58 |
+
const { positions } = useBuildSwitchHandlePositions({ data, id });
|
59 |
+
|
60 |
+
return (
|
61 |
+
<NodePopover nodeId={id}>
|
62 |
+
<section
|
63 |
+
className={classNames(styles.logicNode, {
|
64 |
+
[styles.selectedNode]: selected,
|
65 |
+
})}
|
66 |
+
>
|
67 |
+
<Handle
|
68 |
+
type="target"
|
69 |
+
position={Position.Left}
|
70 |
+
isConnectable
|
71 |
+
className={styles.handle}
|
72 |
+
id={'a'}
|
73 |
+
></Handle>
|
74 |
+
<NodeHeader
|
75 |
+
id={id}
|
76 |
+
name={data.name}
|
77 |
+
label={data.label}
|
78 |
+
className={styles.nodeHeader}
|
79 |
+
></NodeHeader>
|
80 |
+
<Flex vertical gap={10}>
|
81 |
+
{positions.map((position, idx) => {
|
82 |
+
return (
|
83 |
+
<div key={idx}>
|
84 |
+
<Flex vertical>
|
85 |
+
<Flex justify={'space-between'}>
|
86 |
+
<span>{idx < positions.length - 1 && position.text}</span>
|
87 |
+
<span>{getConditionKey(idx, positions.length)}</span>
|
88 |
+
</Flex>
|
89 |
+
{position.condition && (
|
90 |
+
<ConditionBlock
|
91 |
+
nodeId={id}
|
92 |
+
condition={position.condition}
|
93 |
+
></ConditionBlock>
|
94 |
+
)}
|
95 |
+
</Flex>
|
96 |
+
<Handle
|
97 |
+
key={position.text}
|
98 |
+
id={position.text}
|
99 |
+
type="source"
|
100 |
+
position={Position.Right}
|
101 |
+
isConnectable
|
102 |
+
className={styles.handle}
|
103 |
+
style={{ ...RightHandleStyle, top: position.top }}
|
104 |
+
></Handle>
|
105 |
+
</div>
|
106 |
+
);
|
107 |
+
})}
|
108 |
+
</Flex>
|
109 |
+
</section>
|
110 |
+
</NodePopover>
|
111 |
+
);
|
112 |
+
}
|
web/src/pages/flow/constant.tsx
CHANGED
@@ -2,6 +2,7 @@ import { ReactComponent as AkShareIcon } from '@/assets/svg/akshare.svg';
|
|
2 |
import { ReactComponent as ArXivIcon } from '@/assets/svg/arxiv.svg';
|
3 |
import { ReactComponent as baiduFanyiIcon } from '@/assets/svg/baidu-fanyi.svg';
|
4 |
import { ReactComponent as BaiduIcon } from '@/assets/svg/baidu.svg';
|
|
|
5 |
import { ReactComponent as BingIcon } from '@/assets/svg/bing.svg';
|
6 |
import { ReactComponent as ConcentratorIcon } from '@/assets/svg/concentrator.svg';
|
7 |
import { ReactComponent as CrawlerIcon } from '@/assets/svg/crawler.svg';
|
@@ -39,7 +40,6 @@ import {
|
|
39 |
MessageOutlined,
|
40 |
RocketOutlined,
|
41 |
SendOutlined,
|
42 |
-
SlidersOutlined,
|
43 |
} from '@ant-design/icons';
|
44 |
import upperFirst from 'lodash/upperFirst';
|
45 |
|
@@ -85,7 +85,7 @@ export const operatorIconMap = {
|
|
85 |
[Operator.Retrieval]: RocketOutlined,
|
86 |
[Operator.Generate]: MergeCellsOutlined,
|
87 |
[Operator.Answer]: SendOutlined,
|
88 |
-
[Operator.Begin]:
|
89 |
[Operator.Categorize]: DatabaseOutlined,
|
90 |
[Operator.Message]: MessageOutlined,
|
91 |
[Operator.Relevant]: BranchesOutlined,
|
@@ -142,7 +142,7 @@ export const operatorMap: Record<
|
|
142 |
},
|
143 |
[Operator.Answer]: {
|
144 |
backgroundColor: '#f4816d',
|
145 |
-
color: '
|
146 |
},
|
147 |
[Operator.Begin]: {
|
148 |
backgroundColor: '#4f51d6',
|
@@ -157,7 +157,7 @@ export const operatorMap: Record<
|
|
157 |
},
|
158 |
[Operator.Relevant]: {
|
159 |
backgroundColor: '#9fd94d',
|
160 |
-
color: '
|
161 |
width: 70,
|
162 |
height: 70,
|
163 |
fontSize: 12,
|
@@ -165,7 +165,7 @@ export const operatorMap: Record<
|
|
165 |
},
|
166 |
[Operator.RewriteQuestion]: {
|
167 |
backgroundColor: '#f8c7f8',
|
168 |
-
color: '
|
169 |
width: 70,
|
170 |
height: 70,
|
171 |
fontSize: 12,
|
@@ -175,7 +175,7 @@ export const operatorMap: Record<
|
|
175 |
width: 70,
|
176 |
height: 70,
|
177 |
backgroundColor: '#0f0e0f',
|
178 |
-
color: '#
|
179 |
fontSize: 12,
|
180 |
iconWidth: 16,
|
181 |
// iconFontSize: 16,
|
@@ -221,14 +221,14 @@ export const operatorMap: Record<
|
|
221 |
[Operator.BaiduFanyi]: { backgroundColor: '#e5f2d3' },
|
222 |
[Operator.QWeather]: { backgroundColor: '#a4bbf3' },
|
223 |
[Operator.ExeSQL]: { backgroundColor: '#b9efe8' },
|
224 |
-
[Operator.Switch]: { backgroundColor: '#dbaff6' },
|
225 |
[Operator.WenCai]: { backgroundColor: '#faac5b' },
|
226 |
[Operator.AkShare]: { backgroundColor: '#8085f5' },
|
227 |
[Operator.YahooFinance]: { backgroundColor: '#b474ff' },
|
228 |
[Operator.Jin10]: { backgroundColor: '#a0b9f8' },
|
229 |
[Operator.Concentrator]: {
|
230 |
backgroundColor: '#32d2a3',
|
231 |
-
color: '
|
232 |
width: 70,
|
233 |
height: 70,
|
234 |
fontSize: 10,
|
@@ -586,18 +586,19 @@ export const RestrictedUpstreamMap = {
|
|
586 |
[Operator.Concentrator]: [Operator.Begin],
|
587 |
[Operator.TuShare]: [Operator.Begin],
|
588 |
[Operator.Crawler]: [Operator.Begin],
|
|
|
589 |
};
|
590 |
|
591 |
export const NodeMap = {
|
592 |
[Operator.Begin]: 'beginNode',
|
593 |
[Operator.Categorize]: 'categorizeNode',
|
594 |
-
[Operator.Retrieval]: '
|
595 |
-
[Operator.Generate]: '
|
596 |
[Operator.Answer]: 'logicNode',
|
597 |
-
[Operator.Message]: '
|
598 |
[Operator.Relevant]: 'relevantNode',
|
599 |
-
[Operator.RewriteQuestion]: '
|
600 |
-
[Operator.KeywordExtract]: '
|
601 |
[Operator.DuckDuckGo]: 'ragNode',
|
602 |
[Operator.Baidu]: 'ragNode',
|
603 |
[Operator.Wikipedia]: 'ragNode',
|
@@ -611,7 +612,7 @@ export const NodeMap = {
|
|
611 |
[Operator.BaiduFanyi]: 'ragNode',
|
612 |
[Operator.QWeather]: 'ragNode',
|
613 |
[Operator.ExeSQL]: 'ragNode',
|
614 |
-
[Operator.Switch]: '
|
615 |
[Operator.Concentrator]: 'logicNode',
|
616 |
[Operator.WenCai]: 'ragNode',
|
617 |
[Operator.AkShare]: 'ragNode',
|
|
|
2 |
import { ReactComponent as ArXivIcon } from '@/assets/svg/arxiv.svg';
|
3 |
import { ReactComponent as baiduFanyiIcon } from '@/assets/svg/baidu-fanyi.svg';
|
4 |
import { ReactComponent as BaiduIcon } from '@/assets/svg/baidu.svg';
|
5 |
+
import { ReactComponent as BeginIcon } from '@/assets/svg/begin.svg';
|
6 |
import { ReactComponent as BingIcon } from '@/assets/svg/bing.svg';
|
7 |
import { ReactComponent as ConcentratorIcon } from '@/assets/svg/concentrator.svg';
|
8 |
import { ReactComponent as CrawlerIcon } from '@/assets/svg/crawler.svg';
|
|
|
40 |
MessageOutlined,
|
41 |
RocketOutlined,
|
42 |
SendOutlined,
|
|
|
43 |
} from '@ant-design/icons';
|
44 |
import upperFirst from 'lodash/upperFirst';
|
45 |
|
|
|
85 |
[Operator.Retrieval]: RocketOutlined,
|
86 |
[Operator.Generate]: MergeCellsOutlined,
|
87 |
[Operator.Answer]: SendOutlined,
|
88 |
+
[Operator.Begin]: BeginIcon,
|
89 |
[Operator.Categorize]: DatabaseOutlined,
|
90 |
[Operator.Message]: MessageOutlined,
|
91 |
[Operator.Relevant]: BranchesOutlined,
|
|
|
142 |
},
|
143 |
[Operator.Answer]: {
|
144 |
backgroundColor: '#f4816d',
|
145 |
+
color: '#f4816d',
|
146 |
},
|
147 |
[Operator.Begin]: {
|
148 |
backgroundColor: '#4f51d6',
|
|
|
157 |
},
|
158 |
[Operator.Relevant]: {
|
159 |
backgroundColor: '#9fd94d',
|
160 |
+
color: '#8ef005',
|
161 |
width: 70,
|
162 |
height: 70,
|
163 |
fontSize: 12,
|
|
|
165 |
},
|
166 |
[Operator.RewriteQuestion]: {
|
167 |
backgroundColor: '#f8c7f8',
|
168 |
+
color: '#f32bf3',
|
169 |
width: 70,
|
170 |
height: 70,
|
171 |
fontSize: 12,
|
|
|
175 |
width: 70,
|
176 |
height: 70,
|
177 |
backgroundColor: '#0f0e0f',
|
178 |
+
color: '#0f0e0f',
|
179 |
fontSize: 12,
|
180 |
iconWidth: 16,
|
181 |
// iconFontSize: 16,
|
|
|
221 |
[Operator.BaiduFanyi]: { backgroundColor: '#e5f2d3' },
|
222 |
[Operator.QWeather]: { backgroundColor: '#a4bbf3' },
|
223 |
[Operator.ExeSQL]: { backgroundColor: '#b9efe8' },
|
224 |
+
[Operator.Switch]: { backgroundColor: '#dbaff6', color: '#dbaff6' },
|
225 |
[Operator.WenCai]: { backgroundColor: '#faac5b' },
|
226 |
[Operator.AkShare]: { backgroundColor: '#8085f5' },
|
227 |
[Operator.YahooFinance]: { backgroundColor: '#b474ff' },
|
228 |
[Operator.Jin10]: { backgroundColor: '#a0b9f8' },
|
229 |
[Operator.Concentrator]: {
|
230 |
backgroundColor: '#32d2a3',
|
231 |
+
color: '#32d2a3',
|
232 |
width: 70,
|
233 |
height: 70,
|
234 |
fontSize: 10,
|
|
|
586 |
[Operator.Concentrator]: [Operator.Begin],
|
587 |
[Operator.TuShare]: [Operator.Begin],
|
588 |
[Operator.Crawler]: [Operator.Begin],
|
589 |
+
[Operator.Note]: [],
|
590 |
};
|
591 |
|
592 |
export const NodeMap = {
|
593 |
[Operator.Begin]: 'beginNode',
|
594 |
[Operator.Categorize]: 'categorizeNode',
|
595 |
+
[Operator.Retrieval]: 'retrievalNode',
|
596 |
+
[Operator.Generate]: 'generateNode',
|
597 |
[Operator.Answer]: 'logicNode',
|
598 |
+
[Operator.Message]: 'messageNode',
|
599 |
[Operator.Relevant]: 'relevantNode',
|
600 |
+
[Operator.RewriteQuestion]: 'rewriteNode',
|
601 |
+
[Operator.KeywordExtract]: 'keywordNode',
|
602 |
[Operator.DuckDuckGo]: 'ragNode',
|
603 |
[Operator.Baidu]: 'ragNode',
|
604 |
[Operator.Wikipedia]: 'ragNode',
|
|
|
612 |
[Operator.BaiduFanyi]: 'ragNode',
|
613 |
[Operator.QWeather]: 'ragNode',
|
614 |
[Operator.ExeSQL]: 'ragNode',
|
615 |
+
[Operator.Switch]: 'switchNode',
|
616 |
[Operator.Concentrator]: 'logicNode',
|
617 |
[Operator.WenCai]: 'ragNode',
|
618 |
[Operator.AkShare]: 'ragNode',
|
web/src/pages/flow/flow-drawer/index.less
CHANGED
@@ -7,3 +7,9 @@
|
|
7 |
font-weight: 600;
|
8 |
}
|
9 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
font-weight: 600;
|
8 |
}
|
9 |
}
|
10 |
+
|
11 |
+
.operatorDescription {
|
12 |
+
font-size: 14px;
|
13 |
+
padding-top: 16px;
|
14 |
+
font-weight: normal;
|
15 |
+
}
|
web/src/pages/flow/flow-drawer/index.tsx
CHANGED
@@ -3,7 +3,7 @@ import { IModalProps } from '@/interfaces/common';
|
|
3 |
import { Drawer, Flex, Form, Input } from 'antd';
|
4 |
import { useEffect } from 'react';
|
5 |
import { Node } from 'reactflow';
|
6 |
-
import { Operator } from '../constant';
|
7 |
import AkShareForm from '../form/akshare-form';
|
8 |
import AnswerForm from '../form/answer-form';
|
9 |
import ArXivForm from '../form/arxiv-form';
|
@@ -36,6 +36,8 @@ import YahooFinanceForm from '../form/yahoo-finance-form';
|
|
36 |
import { useHandleFormValuesChange, useHandleNodeNameChange } from '../hooks';
|
37 |
import OperatorIcon from '../operator-icon';
|
38 |
|
|
|
|
|
39 |
import styles from './index.less';
|
40 |
|
41 |
interface IProps {
|
@@ -74,7 +76,7 @@ const FormMap = {
|
|
74 |
[Operator.Crawler]: CrawlerForm,
|
75 |
};
|
76 |
|
77 |
-
const EmptyContent = () => <div
|
78 |
|
79 |
const FlowDrawer = ({
|
80 |
visible,
|
@@ -84,8 +86,10 @@ const FlowDrawer = ({
|
|
84 |
const operatorName: Operator = node?.data.label;
|
85 |
const OperatorForm = FormMap[operatorName] ?? EmptyContent;
|
86 |
const [form] = Form.useForm();
|
87 |
-
const { name, handleNameBlur, handleNameChange } =
|
88 |
-
|
|
|
|
|
89 |
const { t } = useTranslate('flow');
|
90 |
|
91 |
const { handleValuesChange } = useHandleFormValuesChange(node?.id);
|
@@ -99,18 +103,27 @@ const FlowDrawer = ({
|
|
99 |
return (
|
100 |
<Drawer
|
101 |
title={
|
102 |
-
<Flex
|
103 |
-
<
|
104 |
-
|
105 |
-
|
106 |
-
{
|
107 |
-
|
108 |
-
<
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
113 |
</Flex>
|
|
|
|
|
|
|
114 |
</Flex>
|
115 |
}
|
116 |
placement="right"
|
@@ -119,6 +132,7 @@ const FlowDrawer = ({
|
|
119 |
getContainer={false}
|
120 |
mask={false}
|
121 |
width={470}
|
|
|
122 |
>
|
123 |
<section className={styles.formWrapper}>
|
124 |
{visible && (
|
|
|
3 |
import { Drawer, Flex, Form, Input } from 'antd';
|
4 |
import { useEffect } from 'react';
|
5 |
import { Node } from 'reactflow';
|
6 |
+
import { Operator, operatorMap } from '../constant';
|
7 |
import AkShareForm from '../form/akshare-form';
|
8 |
import AnswerForm from '../form/answer-form';
|
9 |
import ArXivForm from '../form/arxiv-form';
|
|
|
36 |
import { useHandleFormValuesChange, useHandleNodeNameChange } from '../hooks';
|
37 |
import OperatorIcon from '../operator-icon';
|
38 |
|
39 |
+
import { CloseOutlined } from '@ant-design/icons';
|
40 |
+
import { lowerFirst } from 'lodash';
|
41 |
import styles from './index.less';
|
42 |
|
43 |
interface IProps {
|
|
|
76 |
[Operator.Crawler]: CrawlerForm,
|
77 |
};
|
78 |
|
79 |
+
const EmptyContent = () => <div></div>;
|
80 |
|
81 |
const FlowDrawer = ({
|
82 |
visible,
|
|
|
86 |
const operatorName: Operator = node?.data.label;
|
87 |
const OperatorForm = FormMap[operatorName] ?? EmptyContent;
|
88 |
const [form] = Form.useForm();
|
89 |
+
const { name, handleNameBlur, handleNameChange } = useHandleNodeNameChange({
|
90 |
+
id: node?.id,
|
91 |
+
data: node?.data,
|
92 |
+
});
|
93 |
const { t } = useTranslate('flow');
|
94 |
|
95 |
const { handleValuesChange } = useHandleFormValuesChange(node?.id);
|
|
|
103 |
return (
|
104 |
<Drawer
|
105 |
title={
|
106 |
+
<Flex vertical>
|
107 |
+
<Flex gap={'middle'} align="center">
|
108 |
+
<OperatorIcon
|
109 |
+
name={operatorName}
|
110 |
+
color={operatorMap[operatorName]?.color}
|
111 |
+
></OperatorIcon>
|
112 |
+
<Flex align="center" gap={'small'} flex={1}>
|
113 |
+
<label htmlFor="" className={styles.title}>
|
114 |
+
{t('title')}
|
115 |
+
</label>
|
116 |
+
<Input
|
117 |
+
value={name}
|
118 |
+
onBlur={handleNameBlur}
|
119 |
+
onChange={handleNameChange}
|
120 |
+
></Input>
|
121 |
+
</Flex>
|
122 |
+
<CloseOutlined onClick={hideModal} />
|
123 |
</Flex>
|
124 |
+
<span className={styles.operatorDescription}>
|
125 |
+
{t(`${lowerFirst(operatorName)}Description`)}
|
126 |
+
</span>
|
127 |
</Flex>
|
128 |
}
|
129 |
placement="right"
|
|
|
132 |
getContainer={false}
|
133 |
mask={false}
|
134 |
width={470}
|
135 |
+
closeIcon={null}
|
136 |
>
|
137 |
<section className={styles.formWrapper}>
|
138 |
{visible && (
|
web/src/pages/flow/flow-sider/index.tsx
CHANGED
@@ -3,7 +3,7 @@ import { Card, Divider, Flex, Layout, Tooltip } from 'antd';
|
|
3 |
import classNames from 'classnames';
|
4 |
import lowerFirst from 'lodash/lowerFirst';
|
5 |
import React from 'react';
|
6 |
-
import { Operator, componentMenuList } from '../constant';
|
7 |
import { useHandleDrag } from '../hooks';
|
8 |
import OperatorIcon from '../operator-icon';
|
9 |
import styles from './index.less';
|
@@ -53,7 +53,10 @@ const FlowSide = ({ setCollapsed, collapsed }: IProps) => {
|
|
53 |
onDragStart={handleDragStart(x.name)}
|
54 |
>
|
55 |
<Flex align="center" gap={15}>
|
56 |
-
<OperatorIcon
|
|
|
|
|
|
|
57 |
<section>
|
58 |
<Tooltip title={t(`${lowerFirst(x.name)}Description`)}>
|
59 |
<b>{t(lowerFirst(x.name))}</b>
|
|
|
3 |
import classNames from 'classnames';
|
4 |
import lowerFirst from 'lodash/lowerFirst';
|
5 |
import React from 'react';
|
6 |
+
import { Operator, componentMenuList, operatorMap } from '../constant';
|
7 |
import { useHandleDrag } from '../hooks';
|
8 |
import OperatorIcon from '../operator-icon';
|
9 |
import styles from './index.less';
|
|
|
53 |
onDragStart={handleDragStart(x.name)}
|
54 |
>
|
55 |
<Flex align="center" gap={15}>
|
56 |
+
<OperatorIcon
|
57 |
+
name={x.name}
|
58 |
+
color={operatorMap[x.name].color}
|
59 |
+
></OperatorIcon>
|
60 |
<section>
|
61 |
<Tooltip title={t(`${lowerFirst(x.name)}Description`)}>
|
62 |
<b>{t(lowerFirst(x.name))}</b>
|
web/src/pages/flow/form-hooks.ts
CHANGED
@@ -3,19 +3,6 @@ import { useCallback, useMemo } from 'react';
|
|
3 |
import { Operator, RestrictedUpstreamMap } from './constant';
|
4 |
import useGraphStore from './store';
|
5 |
|
6 |
-
const ExcludedNodesMap = {
|
7 |
-
// exclude some nodes downstream of the classification node
|
8 |
-
[Operator.Categorize]: [
|
9 |
-
Operator.Categorize,
|
10 |
-
Operator.Answer,
|
11 |
-
Operator.Begin,
|
12 |
-
Operator.Relevant,
|
13 |
-
],
|
14 |
-
[Operator.Relevant]: [Operator.Begin, Operator.Answer, Operator.Relevant],
|
15 |
-
[Operator.Generate]: [Operator.Begin],
|
16 |
-
[Operator.Switch]: [Operator.Begin],
|
17 |
-
};
|
18 |
-
|
19 |
export const useBuildFormSelectOptions = (
|
20 |
operatorName: Operator,
|
21 |
selfId?: string, // exclude the current node
|
@@ -24,8 +11,10 @@ export const useBuildFormSelectOptions = (
|
|
24 |
|
25 |
const buildCategorizeToOptions = useCallback(
|
26 |
(toList: string[]) => {
|
27 |
-
const excludedNodes: Operator[] =
|
28 |
-
|
|
|
|
|
29 |
return nodes
|
30 |
.filter(
|
31 |
(x) =>
|
|
|
3 |
import { Operator, RestrictedUpstreamMap } from './constant';
|
4 |
import useGraphStore from './store';
|
5 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
export const useBuildFormSelectOptions = (
|
7 |
operatorName: Operator,
|
8 |
selfId?: string, // exclude the current node
|
|
|
11 |
|
12 |
const buildCategorizeToOptions = useCallback(
|
13 |
(toList: string[]) => {
|
14 |
+
const excludedNodes: Operator[] = [
|
15 |
+
Operator.Note,
|
16 |
+
...(RestrictedUpstreamMap[operatorName] ?? []),
|
17 |
+
];
|
18 |
return nodes
|
19 |
.filter(
|
20 |
(x) =>
|
web/src/pages/flow/form/categorize-form/dynamic-categorize.tsx
CHANGED
@@ -1,6 +1,14 @@
|
|
1 |
import { useTranslate } from '@/hooks/common-hooks';
|
2 |
import { CloseOutlined } from '@ant-design/icons';
|
3 |
-
import {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
import { FormInstance } from 'antd/lib';
|
5 |
import { humanId } from 'human-id';
|
6 |
import trim from 'lodash/trim';
|
@@ -15,6 +23,8 @@ import { useUpdateNodeInternals } from 'reactflow';
|
|
15 |
import { Operator } from '../../constant';
|
16 |
import { useBuildFormSelectOptions } from '../../form-hooks';
|
17 |
|
|
|
|
|
18 |
interface IProps {
|
19 |
nodeId?: string;
|
20 |
}
|
@@ -105,13 +115,12 @@ const DynamicCategorize = ({ nodeId }: IProps) => {
|
|
105 |
if (nodeId) updateNodeInternals(nodeId);
|
106 |
};
|
107 |
return (
|
108 |
-
<
|
109 |
-
style={{ display: 'flex', rowGap: 10, flexDirection: 'column' }}
|
110 |
-
>
|
111 |
{fields.map((field) => (
|
112 |
<Card
|
113 |
size="small"
|
114 |
key={field.key}
|
|
|
115 |
extra={
|
116 |
<CloseOutlined
|
117 |
onClick={() => {
|
@@ -172,10 +181,15 @@ const DynamicCategorize = ({ nodeId }: IProps) => {
|
|
172 |
</Card>
|
173 |
))}
|
174 |
|
175 |
-
<Button
|
|
|
|
|
|
|
|
|
|
|
176 |
+ {t('addItem')}
|
177 |
</Button>
|
178 |
-
</
|
179 |
);
|
180 |
}}
|
181 |
</Form.List>
|
|
|
1 |
import { useTranslate } from '@/hooks/common-hooks';
|
2 |
import { CloseOutlined } from '@ant-design/icons';
|
3 |
+
import {
|
4 |
+
Button,
|
5 |
+
Card,
|
6 |
+
Flex,
|
7 |
+
Form,
|
8 |
+
FormListFieldData,
|
9 |
+
Input,
|
10 |
+
Select,
|
11 |
+
} from 'antd';
|
12 |
import { FormInstance } from 'antd/lib';
|
13 |
import { humanId } from 'human-id';
|
14 |
import trim from 'lodash/trim';
|
|
|
23 |
import { Operator } from '../../constant';
|
24 |
import { useBuildFormSelectOptions } from '../../form-hooks';
|
25 |
|
26 |
+
import styles from './index.less';
|
27 |
+
|
28 |
interface IProps {
|
29 |
nodeId?: string;
|
30 |
}
|
|
|
115 |
if (nodeId) updateNodeInternals(nodeId);
|
116 |
};
|
117 |
return (
|
118 |
+
<Flex gap={18} vertical>
|
|
|
|
|
119 |
{fields.map((field) => (
|
120 |
<Card
|
121 |
size="small"
|
122 |
key={field.key}
|
123 |
+
className={styles.caseCard}
|
124 |
extra={
|
125 |
<CloseOutlined
|
126 |
onClick={() => {
|
|
|
181 |
</Card>
|
182 |
))}
|
183 |
|
184 |
+
<Button
|
185 |
+
type="dashed"
|
186 |
+
onClick={handleAdd}
|
187 |
+
block
|
188 |
+
className={styles.addButton}
|
189 |
+
>
|
190 |
+ {t('addItem')}
|
191 |
</Button>
|
192 |
+
</Flex>
|
193 |
);
|
194 |
}}
|
195 |
</Form.List>
|
web/src/pages/flow/form/categorize-form/index.less
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@lightBackgroundColor: rgba(150, 150, 150, 0.07);
|
2 |
+
@darkBackgroundColor: rgba(150, 150, 150, 0.12);
|
3 |
+
|
4 |
+
.caseCard {
|
5 |
+
background-color: @darkBackgroundColor;
|
6 |
+
}
|
7 |
+
|
8 |
+
.addButton {
|
9 |
+
color: rgb(22, 119, 255);
|
10 |
+
font-weight: 600;
|
11 |
+
}
|
web/src/pages/flow/form/generate-form/dynamic-parameters.tsx
CHANGED
@@ -90,6 +90,7 @@ const DynamicParameters = ({ nodeId }: IProps) => {
|
|
90 |
components={components}
|
91 |
rowClassName={() => styles.editableRow}
|
92 |
scroll={{ x: true }}
|
|
|
93 |
/>
|
94 |
</section>
|
95 |
);
|
|
|
90 |
components={components}
|
91 |
rowClassName={() => styles.editableRow}
|
92 |
scroll={{ x: true }}
|
93 |
+
bordered
|
94 |
/>
|
95 |
</section>
|
96 |
);
|
web/src/pages/flow/form/switch-form/index.less
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@lightBackgroundColor: rgba(150, 150, 150, 0.07);
|
2 |
+
@darkBackgroundColor: rgba(150, 150, 150, 0.12);
|
3 |
+
|
4 |
+
.caseCard {
|
5 |
+
background-color: @lightBackgroundColor;
|
6 |
+
}
|
7 |
+
|
8 |
+
.conditionCard {
|
9 |
+
background-color: @darkBackgroundColor;
|
10 |
+
}
|
11 |
+
|
12 |
+
.elseCase {
|
13 |
+
background-color: @lightBackgroundColor;
|
14 |
+
padding: 12px;
|
15 |
+
border-radius: 8px;
|
16 |
+
}
|
17 |
+
|
18 |
+
.addButton {
|
19 |
+
color: rgb(22, 119, 255);
|
20 |
+
font-weight: 600;
|
21 |
+
}
|
web/src/pages/flow/form/switch-form/index.tsx
CHANGED
@@ -13,6 +13,8 @@ import { useBuildComponentIdSelectOptions } from '../../hooks';
|
|
13 |
import { IOperatorForm, ISwitchForm } from '../../interface';
|
14 |
import { getOtherFieldValues } from '../../utils';
|
15 |
|
|
|
|
|
16 |
const SwitchForm = ({ onValuesChange, node, form }: IOperatorForm) => {
|
17 |
const { t } = useTranslation();
|
18 |
const buildCategorizeToOptions = useBuildFormSelectOptions(
|
@@ -55,112 +57,134 @@ const SwitchForm = ({ onValuesChange, node, form }: IOperatorForm) => {
|
|
55 |
<Form.List name="conditions">
|
56 |
{(fields, { add, remove }) => (
|
57 |
<div style={{ display: 'flex', rowGap: 16, flexDirection: 'column' }}>
|
58 |
-
{fields.map((field) =>
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
<Form.Item noStyle dependencies={[field.name, 'items']}>
|
72 |
-
{({ getFieldValue }) =>
|
73 |
-
getFieldValue(['conditions', field.name, 'items'])?.length >
|
74 |
-
1 && (
|
75 |
-
<Form.Item
|
76 |
-
label={t('flow.logicalOperator')}
|
77 |
-
name={[field.name, 'logical_operator']}
|
78 |
-
>
|
79 |
-
<Select options={switchLogicOperatorOptions} />
|
80 |
-
</Form.Item>
|
81 |
-
)
|
82 |
}
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
<Select
|
121 |
-
placeholder={t('flow.componentId')}
|
122 |
-
options={componentIdOptions}
|
123 |
-
/>
|
124 |
-
</Form.Item>
|
125 |
-
<Form.Item
|
126 |
-
label={t('flow.operator')}
|
127 |
-
name={[subField.name, 'operator']}
|
128 |
-
>
|
129 |
-
<Select
|
130 |
-
placeholder={t('flow.operator')}
|
131 |
-
options={switchOperatorOptions}
|
132 |
-
/>
|
133 |
-
</Form.Item>
|
134 |
-
<Form.Item
|
135 |
-
label={t('flow.value')}
|
136 |
-
name={[subField.name, 'value']}
|
137 |
-
>
|
138 |
-
<Input placeholder={t('flow.value')} />
|
139 |
-
</Form.Item>
|
140 |
-
</Card>
|
141 |
-
))}
|
142 |
-
<Button
|
143 |
-
type="dashed"
|
144 |
-
onClick={() => subOpt.add()}
|
145 |
-
block
|
146 |
>
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
155 |
|
156 |
-
<Button
|
157 |
+ Add Case
|
158 |
</Button>
|
159 |
</div>
|
160 |
)}
|
161 |
</Form.List>
|
162 |
<Divider />
|
163 |
-
<Form.Item
|
|
|
|
|
|
|
|
|
164 |
<Select
|
165 |
allowClear
|
166 |
options={buildCategorizeToOptions(getSelectedConditionTos())}
|
|
|
13 |
import { IOperatorForm, ISwitchForm } from '../../interface';
|
14 |
import { getOtherFieldValues } from '../../utils';
|
15 |
|
16 |
+
import styles from './index.less';
|
17 |
+
|
18 |
const SwitchForm = ({ onValuesChange, node, form }: IOperatorForm) => {
|
19 |
const { t } = useTranslation();
|
20 |
const buildCategorizeToOptions = useBuildFormSelectOptions(
|
|
|
57 |
<Form.List name="conditions">
|
58 |
{(fields, { add, remove }) => (
|
59 |
<div style={{ display: 'flex', rowGap: 16, flexDirection: 'column' }}>
|
60 |
+
{fields.map((field) => {
|
61 |
+
return (
|
62 |
+
<Card
|
63 |
+
size="small"
|
64 |
+
title={`Case ${field.name + 1}`}
|
65 |
+
key={field.key}
|
66 |
+
className={styles.caseCard}
|
67 |
+
extra={
|
68 |
+
<CloseOutlined
|
69 |
+
onClick={() => {
|
70 |
+
remove(field.name);
|
71 |
+
}}
|
72 |
+
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
}
|
74 |
+
>
|
75 |
+
<Form.Item noStyle dependencies={[field.name, 'items']}>
|
76 |
+
{({ getFieldValue }) =>
|
77 |
+
getFieldValue(['conditions', field.name, 'items'])
|
78 |
+
?.length > 1 && (
|
79 |
+
<Form.Item
|
80 |
+
label={t('flow.logicalOperator')}
|
81 |
+
name={[field.name, 'logical_operator']}
|
82 |
+
>
|
83 |
+
<Select options={switchLogicOperatorOptions} />
|
84 |
+
</Form.Item>
|
85 |
+
)
|
86 |
+
}
|
87 |
+
</Form.Item>
|
88 |
+
<Form.Item label={t('flow.to')} name={[field.name, 'to']}>
|
89 |
+
<Select
|
90 |
+
allowClear
|
91 |
+
options={buildCategorizeToOptions([
|
92 |
+
form?.getFieldValue(SwitchElseTo),
|
93 |
+
...getOtherFieldValues(
|
94 |
+
form!,
|
95 |
+
'conditions',
|
96 |
+
field,
|
97 |
+
'to',
|
98 |
+
),
|
99 |
+
])}
|
100 |
+
/>
|
101 |
+
</Form.Item>
|
102 |
+
<Form.Item label="Condition">
|
103 |
+
<Form.List name={[field.name, 'items']}>
|
104 |
+
{(subFields, subOpt) => (
|
105 |
+
<div
|
106 |
+
style={{
|
107 |
+
display: 'flex',
|
108 |
+
flexDirection: 'column',
|
109 |
+
rowGap: 16,
|
110 |
+
}}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
111 |
>
|
112 |
+
{subFields.map((subField) => (
|
113 |
+
<Card
|
114 |
+
key={subField.key}
|
115 |
+
title={null}
|
116 |
+
size="small"
|
117 |
+
className={styles.conditionCard}
|
118 |
+
bordered
|
119 |
+
extra={
|
120 |
+
<CloseOutlined
|
121 |
+
onClick={() => {
|
122 |
+
subOpt.remove(subField.name);
|
123 |
+
}}
|
124 |
+
/>
|
125 |
+
}
|
126 |
+
>
|
127 |
+
<Form.Item
|
128 |
+
label={t('flow.componentId')}
|
129 |
+
name={[subField.name, 'cpn_id']}
|
130 |
+
>
|
131 |
+
<Select
|
132 |
+
placeholder={t('flow.componentId')}
|
133 |
+
options={componentIdOptions}
|
134 |
+
/>
|
135 |
+
</Form.Item>
|
136 |
+
<Form.Item
|
137 |
+
label={t('flow.operator')}
|
138 |
+
name={[subField.name, 'operator']}
|
139 |
+
>
|
140 |
+
<Select
|
141 |
+
placeholder={t('flow.operator')}
|
142 |
+
options={switchOperatorOptions}
|
143 |
+
/>
|
144 |
+
</Form.Item>
|
145 |
+
<Form.Item
|
146 |
+
label={t('flow.value')}
|
147 |
+
name={[subField.name, 'value']}
|
148 |
+
>
|
149 |
+
<Input placeholder={t('flow.value')} />
|
150 |
+
</Form.Item>
|
151 |
+
</Card>
|
152 |
+
))}
|
153 |
+
<Button
|
154 |
+
onClick={() => {
|
155 |
+
form?.setFieldValue(
|
156 |
+
['conditions', field.name, 'logical_operator'],
|
157 |
+
SwitchLogicOperatorOptions[0],
|
158 |
+
);
|
159 |
+
subOpt.add({
|
160 |
+
operator: SwitchOperatorOptions[0].value,
|
161 |
+
});
|
162 |
+
}}
|
163 |
+
block
|
164 |
+
className={styles.addButton}
|
165 |
+
>
|
166 |
+
+ Add Condition
|
167 |
+
</Button>
|
168 |
+
</div>
|
169 |
+
)}
|
170 |
+
</Form.List>
|
171 |
+
</Form.Item>
|
172 |
+
</Card>
|
173 |
+
);
|
174 |
+
})}
|
175 |
|
176 |
+
<Button onClick={() => add()} block className={styles.addButton}>
|
177 |
+ Add Case
|
178 |
</Button>
|
179 |
</div>
|
180 |
)}
|
181 |
</Form.List>
|
182 |
<Divider />
|
183 |
+
<Form.Item
|
184 |
+
label={'ELSE'}
|
185 |
+
name={[SwitchElseTo]}
|
186 |
+
className={styles.elseCase}
|
187 |
+
>
|
188 |
<Select
|
189 |
allowClear
|
190 |
options={buildCategorizeToOptions(getSelectedConditionTos())}
|
web/src/pages/flow/hooks.ts
CHANGED
@@ -69,6 +69,7 @@ import useGraphStore, { RFState } from './store';
|
|
69 |
import {
|
70 |
buildDslComponentsByGraph,
|
71 |
generateSwitchHandleText,
|
|
|
72 |
receiveMessageError,
|
73 |
replaceIdWithText,
|
74 |
} from './utils';
|
@@ -250,6 +251,7 @@ export const useHandleDrop = () => {
|
|
250 |
},
|
251 |
sourcePosition: Position.Right,
|
252 |
targetPosition: Position.Left,
|
|
|
253 |
};
|
254 |
|
255 |
addNode(newNode);
|
@@ -448,11 +450,16 @@ export const useValidateConnection = () => {
|
|
448 |
return isValidConnection;
|
449 |
};
|
450 |
|
451 |
-
export const useHandleNodeNameChange = (
|
|
|
|
|
|
|
|
|
|
|
|
|
452 |
const [name, setName] = useState<string>('');
|
453 |
const { updateNodeName, nodes } = useGraphStore((state) => state);
|
454 |
-
const previousName =
|
455 |
-
const id = node?.id;
|
456 |
|
457 |
const handleNameBlur = useCallback(() => {
|
458 |
const existsSameName = nodes.some((x) => x.data.name === name);
|
@@ -639,6 +646,7 @@ const ExcludedNodes = [
|
|
639 |
Operator.Relevant,
|
640 |
Operator.Begin,
|
641 |
Operator.Answer,
|
|
|
642 |
];
|
643 |
|
644 |
export const useBuildComponentIdSelectOptions = (nodeId?: string) => {
|
@@ -655,3 +663,15 @@ export const useBuildComponentIdSelectOptions = (nodeId?: string) => {
|
|
655 |
|
656 |
return options;
|
657 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
import {
|
70 |
buildDslComponentsByGraph,
|
71 |
generateSwitchHandleText,
|
72 |
+
getNodeDragHandle,
|
73 |
receiveMessageError,
|
74 |
replaceIdWithText,
|
75 |
} from './utils';
|
|
|
251 |
},
|
252 |
sourcePosition: Position.Right,
|
253 |
targetPosition: Position.Left,
|
254 |
+
dragHandle: getNodeDragHandle(type),
|
255 |
};
|
256 |
|
257 |
addNode(newNode);
|
|
|
450 |
return isValidConnection;
|
451 |
};
|
452 |
|
453 |
+
export const useHandleNodeNameChange = ({
|
454 |
+
id,
|
455 |
+
data,
|
456 |
+
}: {
|
457 |
+
id?: string;
|
458 |
+
data: any;
|
459 |
+
}) => {
|
460 |
const [name, setName] = useState<string>('');
|
461 |
const { updateNodeName, nodes } = useGraphStore((state) => state);
|
462 |
+
const previousName = data?.name;
|
|
|
463 |
|
464 |
const handleNameBlur = useCallback(() => {
|
465 |
const existsSameName = nodes.some((x) => x.data.name === name);
|
|
|
646 |
Operator.Relevant,
|
647 |
Operator.Begin,
|
648 |
Operator.Answer,
|
649 |
+
Operator.Note,
|
650 |
];
|
651 |
|
652 |
export const useBuildComponentIdSelectOptions = (nodeId?: string) => {
|
|
|
663 |
|
664 |
return options;
|
665 |
};
|
666 |
+
|
667 |
+
export const useGetComponentLabelByValue = (nodeId: string) => {
|
668 |
+
const options = useBuildComponentIdSelectOptions(nodeId);
|
669 |
+
|
670 |
+
const getLabel = useCallback(
|
671 |
+
(val?: string) => {
|
672 |
+
return options.find((x) => x.value === val)?.label;
|
673 |
+
},
|
674 |
+
[options],
|
675 |
+
);
|
676 |
+
return getLabel;
|
677 |
+
};
|