From ef899a88590a88781cf6a82cb0a3aee8630ed0be Mon Sep 17 00:00:00 2001 From: balibabu Date: Tue, 3 Jun 2025 19:41:35 +0800 Subject: [PATCH] Feat: Add DynamicPrompt component #3221 (#8028) ### What problem does this PR solve? Feat: Add DynamicPrompt component #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/components/prompt-editor/index.tsx | 43 +++++--- web/src/locales/en.ts | 3 + web/src/locales/zh.ts | 2 + .../agent/form/agent-form/dynamic-prompt.tsx | 97 +++++++++++++++++++ web/src/pages/agent/form/agent-form/index.tsx | 28 +++++- web/src/pages/agent/form/begin-form/index.tsx | 2 +- .../agent/form/begin-form/use-watch-change.ts | 18 ---- web/src/pages/agent/hooks.tsx | 2 +- .../agent/hooks/use-watch-form-change.ts | 15 +++ 9 files changed, 171 insertions(+), 39 deletions(-) create mode 100644 web/src/pages/agent/form/agent-form/dynamic-prompt.tsx delete mode 100644 web/src/pages/agent/form/begin-form/use-watch-change.ts diff --git a/web/src/components/prompt-editor/index.tsx b/web/src/components/prompt-editor/index.tsx index e546e4443..38210cdeb 100644 --- a/web/src/components/prompt-editor/index.tsx +++ b/web/src/components/prompt-editor/index.tsx @@ -42,13 +42,15 @@ const Nodes: Array> = [ VariableNode, ]; +type PromptContentProps = { showToolbar?: boolean }; + type IProps = { value?: string; onChange?: (value?: string) => void; placeholder?: ReactNode; -}; +} & PromptContentProps; -function PromptContent() { +function PromptContent({ showToolbar = true }: PromptContentProps) { const [editor] = useLexicalComposerContext(); const [isBlur, setIsBlur] = useState(false); const { t } = useTranslation(); @@ -79,18 +81,20 @@ function PromptContent() {
-
- - - - - - - -

{t('flow.insertVariableTip')}

-
-
-
+ {showToolbar && ( +
+ + + + + + + +

{t('flow.insertVariableTip')}

+
+
+
+ )} } + contentEditable={ + + } placeholder={
{ + const { t } = useTranslation(); + const form = useFormContext(); + const name = 'prompts'; + + const { fields, append, remove } = useFieldArray({ + name: name, + control: form.control, + }); + + return ( + + {t('flow.msg')} +
+ {fields.map((field, index) => ( +
+
+ ( + + + + + + + + )} + /> + + ( + + +
+ +
+
+
+ )} + /> +
+ +
+ ))} +
+ + append({ content: '', role: PromptRole.User })} + > + Add + +
+ ); +}; + +export default memo(DynamicPrompt); diff --git a/web/src/pages/agent/form/agent-form/index.tsx b/web/src/pages/agent/form/agent-form/index.tsx index 44baf0aae..9aee6c99f 100644 --- a/web/src/pages/agent/form/agent-form/index.tsx +++ b/web/src/pages/agent/form/agent-form/index.tsx @@ -1,15 +1,25 @@ import { FormContainer } from '@/components/form-container'; import { LargeModelFormField } from '@/components/large-model-form-field'; import { LlmSettingSchema } from '@/components/llm-setting-items/next'; +import { MessageHistoryWindowSizeFormField } from '@/components/message-history-window-size-item'; import { PromptEditor } from '@/components/prompt-editor'; -import { Form, FormControl, FormField, FormItem } from '@/components/ui/form'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, +} from '@/components/ui/form'; +import { useFetchModelId } from '@/hooks/logic-hooks'; 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 { useWatchFormChange } from '../../hooks/use-watch-form-change'; import { INextOperatorForm } from '../../interface'; +import DynamicPrompt from './dynamic-prompt'; const FormSchema = z.object({ sys_prompt: z.string(), @@ -22,7 +32,6 @@ const FormSchema = z.object({ ) .optional(), message_history_window_size: z.coerce.number(), - ...LlmSettingSchema, tools: z .array( z.object({ @@ -30,17 +39,24 @@ const FormSchema = z.object({ }), ) .optional(), + ...LlmSettingSchema, }); const AgentForm = ({ node }: INextOperatorForm) => { const { t } = useTranslation(); - const defaultValues = useFormValues(initialAgentValues, node); + const llmId = useFetchModelId(); + const defaultValues = useFormValues( + { ...initialAgentValues, llm_id: llmId }, + node, + ); const form = useForm({ defaultValues: defaultValues, resolver: zodResolver(FormSchema), }); + useWatchFormChange(node?.id, form); + return (
{ name={`sys_prompt`} render={({ field }) => ( + Prompt )} /> + + + +
diff --git a/web/src/pages/agent/form/begin-form/index.tsx b/web/src/pages/agent/form/begin-form/index.tsx index e765e11ee..c5b4263b2 100644 --- a/web/src/pages/agent/form/begin-form/index.tsx +++ b/web/src/pages/agent/form/begin-form/index.tsx @@ -20,12 +20,12 @@ import { useForm, useWatch } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { z } from 'zod'; import { AgentDialogueMode } from '../../constant'; +import { useWatchFormChange } from '../../hooks/use-watch-form-change'; import { INextOperatorForm } from '../../interface'; import { ParameterDialog } from './parameter-dialog'; import { QueryTable } from './query-table'; import { useEditQueryRecord } from './use-edit-query'; import { useValues } from './use-values'; -import { useWatchFormChange } from './use-watch-change'; const ModeOptions = buildSelectOptions([ AgentDialogueMode.Conversational, diff --git a/web/src/pages/agent/form/begin-form/use-watch-change.ts b/web/src/pages/agent/form/begin-form/use-watch-change.ts deleted file mode 100644 index ac31dba4d..000000000 --- a/web/src/pages/agent/form/begin-form/use-watch-change.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { useEffect } from 'react'; -import { UseFormReturn, useWatch } from 'react-hook-form'; -import useGraphStore from '../../store'; - -export function useWatchFormChange(id?: string, form?: UseFormReturn) { - let values = useWatch({ control: form?.control }); - const updateNodeForm = useGraphStore((state) => state.updateNodeForm); - - useEffect(() => { - // Manually triggered form updates are synchronized to the canvas - if (id && form?.formState.isDirty) { - values = form?.getValues(); - let nextValues: any = values; - - updateNodeForm(id, nextValues); - } - }, [form?.formState.isDirty, id, updateNodeForm, values]); -} diff --git a/web/src/pages/agent/hooks.tsx b/web/src/pages/agent/hooks.tsx index 0a7539103..0257e19dd 100644 --- a/web/src/pages/agent/hooks.tsx +++ b/web/src/pages/agent/hooks.tsx @@ -146,7 +146,7 @@ export const useInitializeOperatorParams = () => { [Operator.IterationStart]: initialIterationValues, [Operator.Code]: initialCodeValues, [Operator.WaitingDialogue]: initialWaitingDialogueValues, - [Operator.Agent]: initialAgentValues, + [Operator.Agent]: { ...initialAgentValues, llm_id: llmId }, }; }, [llmId]); diff --git a/web/src/pages/agent/hooks/use-watch-form-change.ts b/web/src/pages/agent/hooks/use-watch-form-change.ts index 04a1c413b..c3babfe43 100644 --- a/web/src/pages/agent/hooks/use-watch-form-change.ts +++ b/web/src/pages/agent/hooks/use-watch-form-change.ts @@ -149,3 +149,18 @@ export const useHandleFormValuesChange = ( return { handleValuesChange }; }; + +export function useWatchFormChange(id?: string, form?: UseFormReturn) { + let values = useWatch({ control: form?.control }); + const updateNodeForm = useGraphStore((state) => state.updateNodeForm); + + useEffect(() => { + // Manually triggered form updates are synchronized to the canvas + if (id && form?.formState.isDirty) { + values = form?.getValues(); + let nextValues: any = values; + + updateNodeForm(id, nextValues); + } + }, [form?.formState.isDirty, id, updateNodeForm, values]); +}