feat: add description text to operators and extract the useFetchModelId to logicHooks.ts and drag the operator to the canvas and initialize the form data #918 (#1379)

### What problem does this PR solve?

feat: add description text to operators #918 
feat: drag the operator to the canvas and initialize the form data #918
feat: extract the useFetchModelId to logicHooks.ts
### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu 2024-07-04 19:18:02 +08:00 committed by GitHub
parent 306108fe0e
commit 25a8c076bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 171 additions and 102 deletions

View File

@ -1,40 +1,40 @@
import { defineConfig } from 'umi'; import { defineConfig } from 'umi';
import { appName } from './src/conf.json'; import { appName } from './src/conf.json';
import routes from './src/routes'; import routes from './src/routes';
export default defineConfig({ export default defineConfig({
title: appName, title: appName,
outputPath: 'dist', outputPath: 'dist',
// alias: { '@': './src' }, // alias: { '@': './src' },
npmClient: 'npm', npmClient: 'npm',
base: '/', base: '/',
routes, routes,
publicPath: '/', publicPath: '/',
esbuildMinifyIIFE: true, esbuildMinifyIIFE: true,
icons: {}, icons: {},
hash: true, hash: true,
favicons: ['/logo.svg'], favicons: ['/logo.svg'],
clickToComponent: {}, clickToComponent: {},
history: { history: {
type: 'browser', type: 'browser',
}, },
plugins: ['@react-dev-inspector/umi4-plugin', '@umijs/plugins/dist/dva'], plugins: ['@react-dev-inspector/umi4-plugin', '@umijs/plugins/dist/dva'],
dva: {}, dva: {},
lessLoader: { lessLoader: {
modifyVars: { modifyVars: {
hack: `true; @import "~@/less/index.less";`, hack: `true; @import "~@/less/index.less";`,
}, },
}, },
devtool: 'source-map', devtool: 'source-map',
copy: ['src/conf.json'], copy: ['src/conf.json'],
proxy: { proxy: {
'/v1': { '/v1': {
target: '', target: '',
changeOrigin: true, changeOrigin: true,
ws: true, ws: true,
logger: console, logger: console,
// pathRewrite: { '^/v1': '/v1' }, // pathRewrite: { '^/v1': '/v1' },
}, },
}, },
}); });

View File

@ -4,10 +4,10 @@
display: inline-block; display: inline-block;
} }
.messageItemSectionLeft { .messageItemSectionLeft {
width: 70%; width: 80%;
} }
.messageItemSectionRight { .messageItemSectionRight {
width: 40%; width: 80%;
} }
.messageItemContent { .messageItemContent {
display: inline-flex; display: inline-flex;

View File

@ -22,8 +22,13 @@ import { useTranslation } from 'react-i18next';
import { useDispatch } from 'umi'; import { useDispatch } from 'umi';
import { useSetModalState, useTranslate } from './commonHooks'; import { useSetModalState, useTranslate } from './commonHooks';
import { useSetDocumentParser } from './documentHooks'; import { useSetDocumentParser } from './documentHooks';
import { useFetchLlmList } from './llmHooks';
import { useOneNamespaceEffectsLoading } from './storeHooks'; import { useOneNamespaceEffectsLoading } from './storeHooks';
import { useSaveSetting } from './userSettingHook'; import {
useFetchTenantInfo,
useSaveSetting,
useSelectTenantInfo,
} from './userSettingHook';
export const useChangeDocumentParser = (documentId: string) => { export const useChangeDocumentParser = (documentId: string) => {
const setDocumentParser = useSetDocumentParser(); const setDocumentParser = useSetDocumentParser();
@ -269,3 +274,26 @@ export const useSelectItem = (defaultId?: string) => {
return { selectedId, handleItemClick }; return { selectedId, handleItemClick };
}; };
export const useFetchModelId = (visible: boolean) => {
const fetchTenantInfo = useFetchTenantInfo(false);
const tenantInfo = useSelectTenantInfo();
useEffect(() => {
if (visible) {
fetchTenantInfo();
}
}, [visible, fetchTenantInfo]);
return tenantInfo?.llm_id ?? '';
};
export const useFetchLlmModelOnVisible = (visible: boolean) => {
const fetchLlmList = useFetchLlmList();
useEffect(() => {
if (visible) {
fetchLlmList();
}
}, [fetchLlmList, visible]);
};

View File

@ -565,6 +565,17 @@ The above is the content you need to summarize.`,
componentId: 'component id', componentId: 'component id',
add: 'Add', add: 'Add',
operation: 'operation', operation: 'operation',
beginDescription: 'This is where the flow begin',
answerDescription: `This component is used as an interface between bot and human. It receives input of user and display the result of the computation of the bot.`,
retrievalDescription: `This component is for the process of retrieving relevent information from knowledge base. So, knowledgebases should be selected. If there's nothing retrieved, the 'Empty response' will be returned.`,
generateDescription: `This component is used to call LLM to generate text. Be careful about the prompt setting.`,
categorizeDescription: `This component is used to categorize text. Please specify the name, description and examples of the category. Every single category leads to different downstream components.`,
relevantDescription: `This component is used to judge if the retrieved information is relevent to user's question. 'Yes' represents that they're relevant. 'No' represents they're irrelevant.`,
rewriteQuestionDescription: `This component is used to refine user's quesion. Typically, when a user's original question can't retrieve relevant information from knowledge base, this component help you change the question into a proper one which might be more consistant with the expressions in knowledge base. Only 'Retrieval' can be its downstreams.`,
messageDescription:
'This component is used to send user static information.',
keywordDescription:
'This component is used to send user static information.',
}, },
footer: { footer: {
profile: 'All rights reserved @ React', profile: 'All rights reserved @ React',

View File

@ -1,29 +0,0 @@
import { useFetchLlmList } from '@/hooks/llmHooks';
import {
useFetchTenantInfo,
useSelectTenantInfo,
} from '@/hooks/userSettingHook';
import { useEffect } from 'react';
export const useFetchModelId = (visible: boolean) => {
const fetchTenantInfo = useFetchTenantInfo(false);
const tenantInfo = useSelectTenantInfo();
useEffect(() => {
if (visible) {
fetchTenantInfo();
}
}, [visible, fetchTenantInfo]);
return tenantInfo?.llm_id ?? '';
};
export const useFetchLlmModelOnVisible = (visible: boolean) => {
const fetchLlmList = useFetchLlmList();
useEffect(() => {
if (visible) {
fetchLlmList();
}
}, [fetchLlmList, visible]);
};

View File

@ -11,11 +11,11 @@ import camelCase from 'lodash/camelCase';
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { IPromptConfigParameters } from '../interface'; import { IPromptConfigParameters } from '../interface';
import AssistantSetting from './assistant-setting'; import AssistantSetting from './assistant-setting';
import { useFetchLlmModelOnVisible, useFetchModelId } from './hooks';
import ModelSetting from './model-setting'; import ModelSetting from './model-setting';
import PromptEngine from './prompt-engine'; import PromptEngine from './prompt-engine';
import { useTranslate } from '@/hooks/commonHooks'; import { useTranslate } from '@/hooks/commonHooks';
import { useFetchLlmModelOnVisible, useFetchModelId } from '@/hooks/logicHooks';
import { getBase64FromUploadFileList } from '@/utils/fileUtil'; import { getBase64FromUploadFileList } from '@/utils/fileUtil';
import { removeUselessFieldsFromValues } from '@/utils/form'; import { removeUselessFieldsFromValues } from '@/utils/form';
import styles from './index.less'; import styles from './index.less';

View File

@ -1,16 +1,19 @@
import { useFetchFlow } from '@/hooks/flow-hooks';
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { Drawer } from 'antd'; import { Drawer } from 'antd';
import FlowChatBox from './box'; import FlowChatBox from './box';
const ChatDrawer = ({ visible, hideModal }: IModalProps<any>) => { const ChatDrawer = ({ visible, hideModal }: IModalProps<any>) => {
const { data } = useFetchFlow();
return ( return (
<Drawer <Drawer
title="Debug" title={data.title}
placement="right" placement="right"
onClose={hideModal} onClose={hideModal}
open={visible} open={visible}
getContainer={false} getContainer={false}
width={window.innerWidth > 1278 ? '30%' : 470} width={window.innerWidth > 1278 ? '40%' : 470}
mask={false} mask={false}
// zIndex={10000} // zIndex={10000}
> >

View File

@ -1,3 +1,4 @@
import { variableEnabledFieldMap } from '@/constants/chat';
import { import {
BranchesOutlined, BranchesOutlined,
DatabaseOutlined, DatabaseOutlined,
@ -33,7 +34,7 @@ export const operatorIconMap = {
export const operatorMap = { export const operatorMap = {
[Operator.Retrieval]: { [Operator.Retrieval]: {
description: 'Retrieval description drjlftglrthjftl', description: 'This is where the flow begin',
backgroundColor: '#cad6e0', backgroundColor: '#cad6e0',
color: '#385974', color: '#385974',
}, },
@ -47,7 +48,8 @@ export const operatorMap = {
color: '#996464', color: '#996464',
}, },
[Operator.Answer]: { [Operator.Answer]: {
description: 'Answer description', description:
'This component is used as an interface between bot and human. It receives input of user and display the result of the computation of the bot.',
backgroundColor: '#f4816d', backgroundColor: '#f4816d',
color: 'white', color: 'white',
}, },
@ -126,7 +128,15 @@ export const initialBeginValues = {
prologue: `Hi! I'm your assistant, what can I do for you?`, prologue: `Hi! I'm your assistant, what can I do for you?`,
}; };
export const variableCheckBoxFieldMap = Object.keys(
variableEnabledFieldMap,
).reduce<Record<string, boolean>>((pre, cur) => {
pre[cur] = true;
return pre;
}, {});
const initialLlmBaseValues = { const initialLlmBaseValues = {
...variableCheckBoxFieldMap,
temperature: 0.1, temperature: 0.1,
top_p: 0.3, top_p: 0.3,
frequency_penalty: 0.7, frequency_penalty: 0.7,
@ -196,7 +206,14 @@ export const RestrictedUpstreamMap = {
[Operator.Answer]: [], [Operator.Answer]: [],
[Operator.Retrieval]: [], [Operator.Retrieval]: [],
[Operator.Generate]: [], [Operator.Generate]: [],
[Operator.Message]: [], [Operator.Message]: [
Operator.Begin,
Operator.Message,
Operator.Generate,
Operator.Retrieval,
Operator.RewriteQuestion,
Operator.Categorize,
],
[Operator.Relevant]: [], [Operator.Relevant]: [],
[Operator.RewriteQuestion]: [], [Operator.RewriteQuestion]: [],
}; };

View File

@ -1,6 +1,7 @@
import { Card, Flex, Layout, Space, Typography } from 'antd'; import { useTranslate } from '@/hooks/commonHooks';
import { Card, Flex, Layout, Space, Tooltip } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import lowerFirst from 'lodash/lowerFirst';
import { componentMenuList } from '../constant'; import { componentMenuList } from '../constant';
import { useHandleDrag } from '../hooks'; import { useHandleDrag } from '../hooks';
import OperatorIcon from '../operator-icon'; import OperatorIcon from '../operator-icon';
@ -8,8 +9,6 @@ import styles from './index.less';
const { Sider } = Layout; const { Sider } = Layout;
const { Text } = Typography;
interface IProps { interface IProps {
setCollapsed: (width: boolean) => void; setCollapsed: (width: boolean) => void;
collapsed: boolean; collapsed: boolean;
@ -17,6 +16,7 @@ interface IProps {
const FlowSide = ({ setCollapsed, collapsed }: IProps) => { const FlowSide = ({ setCollapsed, collapsed }: IProps) => {
const { handleDragStart } = useHandleDrag(); const { handleDragStart } = useHandleDrag();
const { t } = useTranslate('flow');
return ( return (
<Sider <Sider
@ -40,13 +40,9 @@ const FlowSide = ({ setCollapsed, collapsed }: IProps) => {
<Space size={15}> <Space size={15}>
<OperatorIcon name={x.name}></OperatorIcon> <OperatorIcon name={x.name}></OperatorIcon>
<section> <section>
<b>{x.name}</b> <Tooltip title={t(`${lowerFirst(x.name)}Description`)}>
<Text <b>{x.name}</b>
ellipsis={{ tooltip: x.description }} </Tooltip>
style={{ width: 130 }}
>
{x.description}
</Text>
</section> </section>
</Space> </Space>
</Flex> </Flex>

View File

@ -17,13 +17,25 @@ import {
ModelVariableType, ModelVariableType,
settledModelVariableMap, settledModelVariableMap,
} from '@/constants/knowledge'; } from '@/constants/knowledge';
import { useFetchModelId } from '@/hooks/logicHooks';
import { Variable } from '@/interfaces/database/chat'; import { Variable } from '@/interfaces/database/chat';
import { useDebounceEffect } from 'ahooks'; import { useDebounceEffect } from 'ahooks';
import { FormInstance, message } from 'antd'; import { FormInstance, message } from 'antd';
import { humanId } from 'human-id'; import { humanId } from 'human-id';
import trim from 'lodash/trim'; import trim from 'lodash/trim';
import { useParams } from 'umi'; import { useParams } from 'umi';
import { NodeMap, Operator, RestrictedUpstreamMap } from './constant'; import {
NodeMap,
Operator,
RestrictedUpstreamMap,
initialBeginValues,
initialCategorizeValues,
initialGenerateValues,
initialMessageValues,
initialRelevantValues,
initialRetrievalValues,
initialRewriteQuestionValues,
} from './constant';
import useGraphStore, { RFState } from './store'; import useGraphStore, { RFState } from './store';
import { buildDslComponentsByGraph } from './utils'; import { buildDslComponentsByGraph } from './utils';
@ -43,6 +55,32 @@ export const useSelectCanvasData = () => {
return useGraphStore(selector); return useGraphStore(selector);
}; };
export const useInitializeOperatorParams = () => {
const llmId = useFetchModelId(true);
const initializeOperatorParams = useCallback(
(operatorName: Operator) => {
const initialFormValuesMap = {
[Operator.Begin]: initialBeginValues,
[Operator.Retrieval]: initialRetrievalValues,
[Operator.Generate]: { ...initialGenerateValues, llm_id: llmId },
[Operator.Answer]: {},
[Operator.Categorize]: { ...initialCategorizeValues, llm_id: llmId },
[Operator.Relevant]: { ...initialRelevantValues, llm_id: llmId },
[Operator.RewriteQuestion]: {
...initialRewriteQuestionValues,
llm_id: llmId,
},
[Operator.Message]: initialMessageValues,
};
return initialFormValuesMap[operatorName];
},
[llmId],
);
return initializeOperatorParams;
};
export const useHandleDrag = () => { export const useHandleDrag = () => {
const handleDragStart = useCallback( const handleDragStart = useCallback(
(operatorId: string) => (ev: React.DragEvent<HTMLDivElement>) => { (operatorId: string) => (ev: React.DragEvent<HTMLDivElement>) => {
@ -59,6 +97,7 @@ export const useHandleDrop = () => {
const addNode = useGraphStore((state) => state.addNode); const addNode = useGraphStore((state) => state.addNode);
const [reactFlowInstance, setReactFlowInstance] = const [reactFlowInstance, setReactFlowInstance] =
useState<ReactFlowInstance<any, any>>(); useState<ReactFlowInstance<any, any>>();
const initializeOperatorParams = useInitializeOperatorParams();
const onDragOver = useCallback((event: React.DragEvent<HTMLDivElement>) => { const onDragOver = useCallback((event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault(); event.preventDefault();
@ -93,6 +132,7 @@ export const useHandleDrop = () => {
data: { data: {
label: `${type}`, label: `${type}`,
name: humanId(), name: humanId(),
form: initializeOperatorParams(type as Operator),
}, },
sourcePosition: Position.Right, sourcePosition: Position.Right,
targetPosition: Position.Left, targetPosition: Position.Left,
@ -100,7 +140,7 @@ export const useHandleDrop = () => {
addNode(newNode); addNode(newNode);
}, },
[reactFlowInstance, addNode], [reactFlowInstance, addNode, initializeOperatorParams],
); );
return { onDrop, onDragOver, setReactFlowInstance }; return { onDrop, onDragOver, setReactFlowInstance };
@ -244,7 +284,10 @@ export const useSetLlmSetting = (form?: FormInstance) => {
return pre; return pre;
}, {}); }, {});
const otherValues = settledModelVariableMap[ModelVariableType.Precise]; const otherValues = settledModelVariableMap[ModelVariableType.Precise];
form?.setFieldsValue({ ...switchBoxValues, ...otherValues }); form?.setFieldsValue({
...switchBoxValues,
...otherValues,
});
}, [form, initialLlmSetting]); }, [form, initialLlmSetting]);
}; };

View File

@ -56,6 +56,6 @@
} }
.templatesBox { .templatesBox {
max-height: 500px; // max-height: 500px;
overflow: auto; overflow: auto;
} }

View File

@ -2,11 +2,11 @@ import { DSLComponents } from '@/interfaces/database/flow';
import { removeUselessFieldsFromValues } from '@/utils/form'; import { removeUselessFieldsFromValues } from '@/utils/form';
import dagre from 'dagre'; import dagre from 'dagre';
import { humanId } from 'human-id'; import { humanId } from 'human-id';
import { curry, isEmpty } from 'lodash'; import { curry } from 'lodash';
import pipe from 'lodash/fp/pipe'; import pipe from 'lodash/fp/pipe';
import { Edge, Node, Position } from 'reactflow'; import { Edge, Node, Position } from 'reactflow';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { NodeMap, Operator, initialFormValuesMap } from './constant'; import { NodeMap, Operator } from './constant';
import { ICategorizeItemResult, NodeData } from './interface'; import { ICategorizeItemResult, NodeData } from './interface';
const buildEdges = ( const buildEdges = (
@ -143,17 +143,17 @@ const removeUselessDataInTheOperator = curry(
}, },
); );
// initialize data for operators without parameters // initialize data for operators without parameters
const initializeOperatorParams = curry((operatorName: string, values: any) => { // const initializeOperatorParams = curry((operatorName: string, values: any) => {
if (isEmpty(values)) { // if (isEmpty(values)) {
return initialFormValuesMap[operatorName as Operator]; // return initialFormValuesMap[operatorName as Operator];
} // }
return values; // return values;
}); // });
const buildOperatorParams = (operatorName: string) => const buildOperatorParams = (operatorName: string) =>
pipe( pipe(
removeUselessDataInTheOperator(operatorName), removeUselessDataInTheOperator(operatorName),
initializeOperatorParams(operatorName), // Final processing, for guarantee // initializeOperatorParams(operatorName), // Final processing, for guarantee
); );
// construct a dsl based on the node information of the graph // construct a dsl based on the node information of the graph