Browse Source

Feat: Add AgentNode component #3221 (#8019)

### What problem does this PR solve?

Feat: Add AgentNode component #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
tags/v0.19.1
balibabu 5 months ago
parent
commit
e47186cc42
No account linked to committer's email address

+ 1
- 0
web/src/interfaces/database/flow.ts View File

@@ -152,6 +152,7 @@ export type IIterationNode = BaseNode;
export type IIterationStartNode = BaseNode;
export type IKeywordNode = BaseNode;
export type ICodeNode = BaseNode<ICodeForm>;
export type IAgentNode = BaseNode;

export type RAGFlowNodeType =
| IBeginNode

+ 2
- 0
web/src/pages/agent/canvas/index.tsx View File

@@ -19,6 +19,7 @@ import { useShowDrawer } from '../hooks/use-show-drawer';
import { ButtonEdge } from './edge';
import styles from './index.less';
import { RagNode } from './node';
import { AgentNode } from './node/agent-node';
import { BeginNode } from './node/begin-node';
import { CategorizeNode } from './node/categorize-node';
import { EmailNode } from './node/email-node';
@@ -53,6 +54,7 @@ const nodeTypes: NodeTypes = {
emailNode: EmailNode,
group: IterationNode,
iterationStartNode: IterationStartNode,
agentNode: AgentNode,
};

const edgeTypes = {

+ 48
- 0
web/src/pages/agent/canvas/node/agent-node.tsx View File

@@ -0,0 +1,48 @@
import { useTheme } from '@/components/theme-provider';
import { IAgentNode } from '@/interfaces/database/flow';
import { Handle, NodeProps, Position } from '@xyflow/react';
import classNames from 'classnames';
import { memo } from 'react';
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
import styles from './index.less';
import NodeHeader from './node-header';

function InnerAgentNode({
id,
data,
isConnectable = true,
selected,
}: NodeProps<IAgentNode>) {
const { theme } = useTheme();
return (
<section
className={classNames(
styles.ragNode,
theme === 'dark' ? styles.dark : '',
{
[styles.selectedNode]: selected,
},
)}
>
<Handle
id="c"
type="source"
position={Position.Left}
isConnectable={isConnectable}
className={styles.handle}
style={LeftHandleStyle}
></Handle>
<Handle
type="source"
position={Position.Right}
isConnectable={isConnectable}
className={styles.handle}
id="b"
style={RightHandleStyle}
></Handle>
<NodeHeader id={id} name={data.name} label={data.label}></NodeHeader>
</section>
);
}

export const AgentNode = memo(InnerAgentNode);

+ 17
- 0
web/src/pages/agent/constant.tsx View File

@@ -59,6 +59,7 @@ import {
} from '@ant-design/icons';
import upperFirst from 'lodash/upperFirst';
import {
Box,
CirclePower,
CloudUpload,
CodeXml,
@@ -112,6 +113,7 @@ export enum Operator {
IterationStart = 'IterationItem',
Code = 'Code',
WaitingDialogue = 'WaitingDialogue',
Agent = 'Agent',
}

export const CommonOperatorList = Object.values(Operator).filter(
@@ -132,6 +134,7 @@ export const AgentOperatorList = [
Operator.Iteration,
Operator.WaitingDialogue,
Operator.Note,
Operator.Agent,
];

export const operatorIconMap = {
@@ -173,6 +176,7 @@ export const operatorIconMap = {
[Operator.IterationStart]: CirclePower,
[Operator.Code]: CodeXml,
[Operator.WaitingDialogue]: MessageSquareMore,
[Operator.Agent]: Box,
};

export const operatorMap: Record<
@@ -313,6 +317,7 @@ export const operatorMap: Record<
[Operator.IterationStart]: { backgroundColor: '#e6f7ff' },
[Operator.Code]: { backgroundColor: '#4c5458' },
[Operator.WaitingDialogue]: { backgroundColor: '#a5d65c' },
[Operator.Agent]: { backgroundColor: '#a5d65c' },
};

export const componentMenuList = [
@@ -356,6 +361,9 @@ export const componentMenuList = [
{
name: Operator.WaitingDialogue,
},
{
name: Operator.Agent,
},
{
name: Operator.Note,
},
@@ -682,6 +690,14 @@ export const initialCodeValues = {

export const initialWaitingDialogueValues = {};

export const initialAgentValues = {
...initialLlmBaseValues,
sys_prompt: ``,
prompts: [],
message_history_window_size: 12,
tools: [],
};

export const CategorizeAnchorPointPositions = [
{ top: 1, right: 34 },
{ top: 8, right: 18 },
@@ -806,6 +822,7 @@ export const NodeMap = {
[Operator.IterationStart]: 'iterationStartNode',
[Operator.Code]: 'ragNode',
[Operator.WaitingDialogue]: 'ragNode',
[Operator.Agent]: 'agentNode',
};

export const LanguageOptions = [

+ 6
- 0
web/src/pages/agent/form-sheet/use-form-config-map.tsx View File

@@ -3,6 +3,7 @@ import { CodeTemplateStrMap, ProgrammingLanguage } from '@/constants/agent';
import { useTranslation } from 'react-i18next';
import { z } from 'zod';
import { Operator } from '../constant';
import AgentForm from '../form/agent-form';
import AkShareForm from '../form/akshare-form';
import AnswerForm from '../form/answer-form';
import ArXivForm from '../form/arxiv-form';
@@ -192,6 +193,11 @@ export function useFormConfigMap() {
),
}),
},
[Operator.Agent]: {
component: AgentForm,
defaultValues: {},
schema: z.object({}),
},
[Operator.Baidu]: {
component: BaiduForm,
defaultValues: { top_n: 10 },

+ 74
- 0
web/src/pages/agent/form/agent-form/index.tsx View File

@@ -0,0 +1,74 @@
import { FormContainer } from '@/components/form-container';
import { LargeModelFormField } from '@/components/large-model-form-field';
import { LlmSettingSchema } from '@/components/llm-setting-items/next';
import { PromptEditor } from '@/components/prompt-editor';
import { Form, FormControl, FormField, FormItem } from '@/components/ui/form';
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { z } from 'zod';
import { initialAgentValues } from '../../constant';
import { useFormValues } from '../../hooks/use-form-values';
import { INextOperatorForm } from '../../interface';

const FormSchema = z.object({
sys_prompt: z.string(),
prompts: z
.array(
z.object({
role: z.string(),
content: z.string(),
}),
)
.optional(),
message_history_window_size: z.coerce.number(),
...LlmSettingSchema,
tools: z
.array(
z.object({
component_name: z.string(),
}),
)
.optional(),
});

const AgentForm = ({ node }: INextOperatorForm) => {
const { t } = useTranslation();
const defaultValues = useFormValues(initialAgentValues, node);

const form = useForm({
defaultValues: defaultValues,
resolver: zodResolver(FormSchema),
});

return (
<Form {...form}>
<form
className="space-y-6 p-4"
onSubmit={(e) => {
e.preventDefault();
}}
>
<FormContainer>
<LargeModelFormField></LargeModelFormField>
<FormField
control={form.control}
name={`sys_prompt`}
render={({ field }) => (
<FormItem className="flex-1">
<FormControl>
<PromptEditor
{...field}
placeholder={t('flow.messagePlaceholder')}
></PromptEditor>
</FormControl>
</FormItem>
)}
/>
</FormContainer>
</form>
</Form>
);
};

export default AgentForm;

+ 2
- 3
web/src/pages/agent/form/retrieval-form/next.tsx View File

@@ -1,6 +1,5 @@
import { FormContainer } from '@/components/form-container';
import { KnowledgeBaseFormField } from '@/components/knowledge-base-item';
import { LargeModelFormField } from '@/components/large-model-form-field';
import { RerankFormFields } from '@/components/rerank';
import {
initialKeywordsSimilarityWeightValue,
@@ -64,7 +63,7 @@ const RetrievalForm = ({ node }: INextOperatorForm) => {
>
<FormContainer>
<QueryVariable></QueryVariable>
<LargeModelFormField></LargeModelFormField>
<KnowledgeBaseFormField></KnowledgeBaseFormField>
</FormContainer>
<FormContainer>
<SimilaritySliderFormField
@@ -73,7 +72,7 @@ const RetrievalForm = ({ node }: INextOperatorForm) => {
></SimilaritySliderFormField>
<TopNFormField></TopNFormField>
<RerankFormFields></RerankFormFields>
<KnowledgeBaseFormField></KnowledgeBaseFormField>
<FormField
control={form.control}
name="empty_response"

+ 4
- 0
web/src/pages/agent/hooks.tsx View File

@@ -33,6 +33,7 @@ import {
Operator,
RestrictedUpstreamMap,
SwitchElseTo,
initialAgentValues,
initialAkShareValues,
initialArXivValues,
initialBaiduFanyiValues,
@@ -65,6 +66,7 @@ import {
initialSwitchValues,
initialTemplateValues,
initialTuShareValues,
initialWaitingDialogueValues,
initialWenCaiValues,
initialWikipediaValues,
initialYahooFinanceValues,
@@ -143,6 +145,8 @@ export const useInitializeOperatorParams = () => {
[Operator.Iteration]: initialIterationValues,
[Operator.IterationStart]: initialIterationValues,
[Operator.Code]: initialCodeValues,
[Operator.WaitingDialogue]: initialWaitingDialogueValues,
[Operator.Agent]: initialAgentValues,
};
}, [llmId]);


+ 20
- 0
web/src/pages/agent/hooks/use-form-values.ts View File

@@ -0,0 +1,20 @@
import { RAGFlowNodeType } from '@/interfaces/database/flow';
import { isEmpty } from 'lodash';
import { useMemo } from 'react';

export function useFormValues(
defaultValues: Record<string, any>,
node?: RAGFlowNodeType,
) {
const values = useMemo(() => {
const formData = node?.data?.form;

if (isEmpty(formData)) {
return defaultValues;
}

return formData;
}, [defaultValues, node?.data?.form]);

return values;
}

+ 13
- 12
web/src/pages/agent/index.tsx View File

@@ -1,5 +1,5 @@
import { PageHeader } from '@/components/page-header';
import { Button } from '@/components/ui/button';
import { Button, ButtonLoading } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
@@ -19,6 +19,7 @@ import FlowCanvas from './canvas';
import { useHandleExportOrImportJsonFile } from './hooks/use-export-json';
import { useFetchDataOnMount } from './hooks/use-fetch-data';
import { useOpenDocument } from './hooks/use-open-document';
import { useSaveGraph } from './hooks/use-save-graph';
import { UploadAgentDialog } from './upload-agent-dialog';

function AgentDropdownMenuItem({
@@ -48,6 +49,7 @@ export default function Agent() {
onFileUploadOk,
hideFileUploadModal,
} = useHandleExportOrImportJsonFile();
const { saveGraph, loading } = useSaveGraph();

const { flowDetail } = useFetchDataOnMount();

@@ -55,6 +57,16 @@ export default function Agent() {
<section>
<PageHeader back={navigateToAgentList} title={flowDetail.title}>
<div className="flex items-center gap-2">
<ButtonLoading
variant={'outline'}
onClick={() => saveGraph()}
loading={loading}
>
Save
</ButtonLoading>
<Button variant={'outline'}>Run app</Button>
<Button variant={'outline'}>Publish</Button>

<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant={'icon'} size={'icon'}>
@@ -83,17 +95,6 @@ export default function Agent() {
</AgentDropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>

<Button variant={'outline'} size={'sm'}>
Save
</Button>
<Button variant={'outline'} size={'sm'}>
Run app
</Button>

<Button variant={'tertiary'} size={'sm'}>
Publish
</Button>
</div>
</PageHeader>
<ReactFlowProvider>

Loading…
Cancel
Save