From 103027580e98935bdbf91b9149ba966698f3840f Mon Sep 17 00:00:00 2001 From: balibabu Date: Tue, 1 Jul 2025 10:52:48 +0800 Subject: [PATCH] Feat: Add agent advanced settings form #3221 (#8592) ### What problem does this PR solve? Feat: Add agent advanced settings form #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .../message-history-window-size-item.tsx | 17 +--- web/src/components/ui/input.tsx | 15 +++ web/src/components/ui/sheet.tsx | 2 +- web/src/pages/agent/constant.tsx | 14 +++ web/src/pages/agent/form-sheet/next.tsx | 7 +- web/src/pages/agent/form/agent-form/index.tsx | 99 ++++++++++++++++++- .../pages/agent/form/agent-form/use-values.ts | 11 ++- .../agent/form/agent-form/use-watch-change.ts | 8 +- .../agent/form/categorize-form/index.tsx | 4 +- .../form/categorize-form/use-watch-change.ts | 2 +- .../agent/form/components/query-variable.tsx | 2 +- .../pages/agent/hooks/use-get-begin-query.tsx | 5 +- 12 files changed, 157 insertions(+), 29 deletions(-) diff --git a/web/src/components/message-history-window-size-item.tsx b/web/src/components/message-history-window-size-item.tsx index 2f2043f3c..e717076e5 100644 --- a/web/src/components/message-history-window-size-item.tsx +++ b/web/src/components/message-history-window-size-item.tsx @@ -1,5 +1,4 @@ import { Form, InputNumber } from 'antd'; -import { useMemo } from 'react'; import { useFormContext } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { @@ -9,7 +8,7 @@ import { FormLabel, FormMessage, } from './ui/form'; -import { BlurInput, Input } from './ui/input'; +import { NumberInput } from './ui/input'; const MessageHistoryWindowSizeItem = ({ initialValue, @@ -32,20 +31,10 @@ const MessageHistoryWindowSizeItem = ({ export default MessageHistoryWindowSizeItem; -type MessageHistoryWindowSizeFormFieldProps = { - useBlurInput?: boolean; -}; - -export function MessageHistoryWindowSizeFormField({ - useBlurInput = false, -}: MessageHistoryWindowSizeFormFieldProps) { +export function MessageHistoryWindowSizeFormField() { const form = useFormContext(); const { t } = useTranslation(); - const NextInput = useMemo(() => { - return useBlurInput ? BlurInput : Input; - }, [useBlurInput]); - return ( - + diff --git a/web/src/components/ui/input.tsx b/web/src/components/ui/input.tsx index f0ee3c5f3..e8dee5613 100644 --- a/web/src/components/ui/input.tsx +++ b/web/src/components/ui/input.tsx @@ -110,3 +110,18 @@ if (process.env.NODE_ENV !== 'production') { export const BlurInput = React.memo(InnerBlurInput); export { ExpandedInput, Input, SearchInput }; + +type NumberInputProps = { onChange?(value: number): void } & InputProps; + +export const NumberInput = ({ onChange, ...props }: NumberInputProps) => { + return ( + { + const value = ev.target.value; + onChange?.(value === '' ? 0 : Number(value)); // convert to number + }} + {...props} + > + ); +}; diff --git a/web/src/components/ui/sheet.tsx b/web/src/components/ui/sheet.tsx index f24cb86ab..8be732d44 100644 --- a/web/src/components/ui/sheet.tsx +++ b/web/src/components/ui/sheet.tsx @@ -81,7 +81,7 @@ const SheetContent = React.forwardRef< ), ); -SheetContent.displayName = SheetPrimitive.Content.displayName; +SheetContent.displayName = SheetPrimitive.Content.displayName || 'SheetContent'; const SheetHeader = ({ className, diff --git a/web/src/pages/agent/constant.tsx b/web/src/pages/agent/constant.tsx index 7fe79978b..5319eb312 100644 --- a/web/src/pages/agent/constant.tsx +++ b/web/src/pages/agent/constant.tsx @@ -681,6 +681,13 @@ export const initialAgentValues = { sys_prompt: ``, prompts: [{ role: PromptRole.User, content: `{${AgentGlobals.SysQuery}}` }], message_history_window_size: 12, + max_retries: 3, + delay_after_error: 1, + visual_files_var: '', + max_rounds: 5, + exception_method: null, + exception_comment: '', + exception_goto: '', tools: [], outputs: { structured_output: { @@ -941,6 +948,7 @@ export enum NodeHandleId { export enum VariableType { String = 'string', Array = 'array', + File = 'file', } export const DefaultAgentToolValuesMap = { @@ -952,3 +960,9 @@ export const DefaultAgentToolValuesMap = { api_key: '', }, }; + +export enum AgentExceptionMethod { + Comment = 'comment', + Goto = 'goto', + Null = 'null', +} diff --git a/web/src/pages/agent/form-sheet/next.tsx b/web/src/pages/agent/form-sheet/next.tsx index 8bfd9d2af..96ea78ff4 100644 --- a/web/src/pages/agent/form-sheet/next.tsx +++ b/web/src/pages/agent/form-sheet/next.tsx @@ -54,7 +54,10 @@ const FormSheet = ({ return ( - +
@@ -86,7 +89,7 @@ const FormSheet = ({ {t(`${lowerFirst(operatorName)}Description`)}
-
+
{visible && ( diff --git a/web/src/pages/agent/form/agent-form/index.tsx b/web/src/pages/agent/form/agent-form/index.tsx index 59b6ec892..b3d3b7edc 100644 --- a/web/src/pages/agent/form/agent-form/index.tsx +++ b/web/src/pages/agent/form/agent-form/index.tsx @@ -1,3 +1,4 @@ +import { Collapse } from '@/components/collapse'; import { FormContainer } from '@/components/form-container'; import { LargeModelFormField } from '@/components/large-model-form-field'; import { LlmSettingSchema } from '@/components/llm-setting-items/next'; @@ -9,22 +10,32 @@ import { FormItem, FormLabel, } from '@/components/ui/form'; +import { Input, NumberInput } from '@/components/ui/input'; +import { RAGFlowSelect } from '@/components/ui/select'; +import { buildOptions } from '@/utils/form'; import { zodResolver } from '@hookform/resolvers/zod'; import { useMemo } from 'react'; import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { z } from 'zod'; -import { initialAgentValues } from '../../constant'; +import { + AgentExceptionMethod, + VariableType, + initialAgentValues, +} from '../../constant'; import { INextOperatorForm } from '../../interface'; import useGraphStore from '../../store'; import { isBottomSubAgent } from '../../utils'; import { DescriptionField } from '../components/description-field'; import { Output } from '../components/output'; import { PromptEditor } from '../components/prompt-editor'; +import { QueryVariable } from '../components/query-variable'; import { AgentTools, Agents } from './agent-tools'; import { useValues } from './use-values'; import { useWatchFormChange } from './use-watch-change'; +const exceptionMethodOptions = buildOptions(AgentExceptionMethod); + const FormSchema = z.object({ sys_prompt: z.string(), description: z.string().optional(), @@ -46,6 +57,13 @@ const FormSchema = z.object({ ) .optional(), ...LlmSettingSchema, + max_retries: z.coerce.number(), + delay_after_error: z.coerce.number().optional(), + visual_files_var: z.string().optional(), + max_rounds: z.coerce.number().optional(), + exception_method: z.string().nullable(), + exception_comment: z.string().optional(), + exception_goto: z.string().optional(), }); const AgentForm = ({ node }: INextOperatorForm) => { @@ -64,7 +82,7 @@ const AgentForm = ({ node }: INextOperatorForm) => { ]; }, []); - const form = useForm({ + const form = useForm>({ defaultValues: defaultValues, resolver: zodResolver(FormSchema), }); @@ -122,10 +140,87 @@ const AgentForm = ({ node }: INextOperatorForm) => { /> )} + + Advanced Settings}> + + + ( + + Max retries + + + + + )} + /> + ( + + Delay after error + + + + + )} + /> + ( + + Max rounds + + + + + )} + /> + ( + + Exception method + + + + + )} + /> + ( + + Exception comment + + + + + )} + /> + + + diff --git a/web/src/pages/agent/form/agent-form/use-values.ts b/web/src/pages/agent/form/agent-form/use-values.ts index 3e6b057a6..21addb892 100644 --- a/web/src/pages/agent/form/agent-form/use-values.ts +++ b/web/src/pages/agent/form/agent-form/use-values.ts @@ -2,7 +2,7 @@ import { useFetchModelId } from '@/hooks/logic-hooks'; import { RAGFlowNodeType } from '@/interfaces/database/flow'; import { get, isEmpty } from 'lodash'; import { useMemo } from 'react'; -import { initialAgentValues } from '../../constant'; +import { AgentExceptionMethod, initialAgentValues } from '../../constant'; export function useValues(node?: RAGFlowNodeType) { const llmId = useFetchModelId(); @@ -23,7 +23,14 @@ export function useValues(node?: RAGFlowNodeType) { return defaultValues; } - return { ...formData, prompts: get(formData, 'prompts.0.content', '') }; + return { + ...formData, + prompts: get(formData, 'prompts.0.content', ''), + exception_method: + formData.exception_method === null + ? AgentExceptionMethod.Null + : formData.exception_method, + }; }, [defaultValues, node?.data?.form]); return values; diff --git a/web/src/pages/agent/form/agent-form/use-watch-change.ts b/web/src/pages/agent/form/agent-form/use-watch-change.ts index 8640a4518..97c2cb437 100644 --- a/web/src/pages/agent/form/agent-form/use-watch-change.ts +++ b/web/src/pages/agent/form/agent-form/use-watch-change.ts @@ -1,9 +1,9 @@ import { useEffect } from 'react'; import { UseFormReturn, useWatch } from 'react-hook-form'; -import { PromptRole } from '../../constant'; +import { AgentExceptionMethod, PromptRole } from '../../constant'; import useGraphStore from '../../store'; -export function useWatchFormChange(id?: string, form?: UseFormReturn) { +export function useWatchFormChange(id?: string, form?: UseFormReturn) { let values = useWatch({ control: form?.control }); const updateNodeForm = useGraphStore((state) => state.updateNodeForm); @@ -14,6 +14,10 @@ export function useWatchFormChange(id?: string, form?: UseFormReturn) { let nextValues: any = { ...values, prompts: [{ role: PromptRole.User, content: values.prompts }], + exception_method: + values.exception_method === AgentExceptionMethod.Null + ? null + : values.exception_method, }; updateNodeForm(id, nextValues); diff --git a/web/src/pages/agent/form/categorize-form/index.tsx b/web/src/pages/agent/form/categorize-form/index.tsx index a4faef254..96d159a74 100644 --- a/web/src/pages/agent/form/categorize-form/index.tsx +++ b/web/src/pages/agent/form/categorize-form/index.tsx @@ -59,9 +59,7 @@ const CategorizeForm = ({ node }: INextOperatorForm) => { - + diff --git a/web/src/pages/agent/form/categorize-form/use-watch-change.ts b/web/src/pages/agent/form/categorize-form/use-watch-change.ts index 6f01dc1a9..91ae84e80 100644 --- a/web/src/pages/agent/form/categorize-form/use-watch-change.ts +++ b/web/src/pages/agent/form/categorize-form/use-watch-change.ts @@ -4,7 +4,7 @@ import { UseFormReturn, useWatch } from 'react-hook-form'; import useGraphStore from '../../store'; import { buildCategorizeObjectFromList } from '../../utils'; -export function useWatchFormChange(id?: string, form?: UseFormReturn) { +export function useWatchFormChange(id?: string, form?: UseFormReturn) { let values = useWatch({ control: form?.control }); const updateNodeForm = useGraphStore((state) => state.updateNodeForm); diff --git a/web/src/pages/agent/form/components/query-variable.tsx b/web/src/pages/agent/form/components/query-variable.tsx index fc13d3f22..e235a9504 100644 --- a/web/src/pages/agent/form/components/query-variable.tsx +++ b/web/src/pages/agent/form/components/query-variable.tsx @@ -34,7 +34,7 @@ export function QueryVariable({ ? nextOptions.map((x) => { return { ...x, - options: x.options.filter((y) => toLower(y.type).startsWith(type)), + options: x.options.filter((y) => toLower(y.type).includes(type)), }; }) : nextOptions; diff --git a/web/src/pages/agent/hooks/use-get-begin-query.tsx b/web/src/pages/agent/hooks/use-get-begin-query.tsx index 6468e2675..8a3f89763 100644 --- a/web/src/pages/agent/hooks/use-get-begin-query.tsx +++ b/web/src/pages/agent/hooks/use-get-begin-query.tsx @@ -1,3 +1,4 @@ +import { AgentGlobals } from '@/constants/agent'; import { useFetchAgent } from '@/hooks/use-agent-request'; import { RAGFlowNodeType } from '@/interfaces/database/flow'; import { Edge } from '@xyflow/react'; @@ -161,7 +162,9 @@ export function useBuildQueryVariableOptions() { const globalOptions = Object.entries(globals).map(([key, value]) => ({ label: key, value: key, - type: Array.isArray(value) ? VariableType.Array : typeof value, + type: Array.isArray(value) + ? `${VariableType.Array}${key === AgentGlobals.SysFiles ? '' : ''}` + : typeof value, })); return [ { ...options[0], options: [...options[0]?.options, ...globalOptions] },