From d9b98cbb18bc4ef37da5618bd767e8fdff4cdfe5 Mon Sep 17 00:00:00 2001 From: balibabu Date: Mon, 9 Jun 2025 16:02:33 +0800 Subject: [PATCH] Feat: Convert the prompt field of the agent operator to an array #3221 (#8137) ### What problem does this PR solve? Feat: Convert the prompt field of the agent operator to an array #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/pages/agent/chat/hooks.ts | 41 +++++++++++----- .../pages/agent/form/agent-form/constant.ts | 4 ++ .../agent/form/agent-form/dynamic-prompt.tsx | 6 +-- web/src/pages/agent/form/agent-form/index.tsx | 47 ++++++++++++------- .../pages/agent/form/agent-form/use-values.ts | 30 ++++++++++++ .../agent/form/agent-form/use-watch-change.ts | 22 +++++++++ .../pages/agent/hooks/use-get-begin-query.tsx | 9 ++-- 7 files changed, 120 insertions(+), 39 deletions(-) create mode 100644 web/src/pages/agent/form/agent-form/constant.ts create mode 100644 web/src/pages/agent/form/agent-form/use-values.ts create mode 100644 web/src/pages/agent/form/agent-form/use-watch-change.ts diff --git a/web/src/pages/agent/chat/hooks.ts b/web/src/pages/agent/chat/hooks.ts index ba4b03d39..a5d3f98ad 100644 --- a/web/src/pages/agent/chat/hooks.ts +++ b/web/src/pages/agent/chat/hooks.ts @@ -14,10 +14,13 @@ import { Message } from '@/interfaces/database/chat'; import i18n from '@/locales/config'; import api from '@/utils/api'; import { message } from 'antd'; +import { get } from 'lodash'; import trim from 'lodash/trim'; -import { useCallback, useEffect } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; import { useParams } from 'umi'; import { v4 as uuid } from 'uuid'; +import { BeginId } from '../constant'; +import useGraphStore from '../store'; import { receiveMessageError } from '../utils'; const antMessage = message; @@ -56,6 +59,17 @@ function findMessageFromList(eventList: IEventList) { return event?.data?.content; } +const useGetBeginNodePrologue = () => { + const getNode = useGraphStore((state) => state.getNode); + + return useMemo(() => { + const formData = get(getNode(BeginId), 'data.form', {}); + if (formData?.enablePrologue) { + return formData?.prologue; + } + }, [getNode]); +}; + export const useSendNextMessage = () => { const { reference, @@ -75,6 +89,8 @@ export const useSendNextMessage = () => { api.runCanvas, ); + const prologue = useGetBeginNodePrologue(); + const sendMessage = useCallback( async ({ message }: { message: Message; messages?: Message[] }) => { const params: Record = { @@ -138,19 +154,18 @@ export const useSendNextMessage = () => { }); }, [addNewestQuestion, handleSendMessage, done, setValue, value]); - const fetchPrologue = useCallback(async () => { - // fetch prologue - const sendRet = await send({ id: agentId }); - if (receiveMessageError(sendRet)) { - message.error(sendRet?.data?.message); - } else { - refetch(); - } - }, [agentId, refetch, send]); - useEffect(() => { - fetchPrologue(); - }, [fetchPrologue]); + if (prologue) { + addNewestAnswer({ + answer: prologue, + reference: { + chunks: [], + doc_aggs: [], + total: 0, + }, + }); + } + }, [addNewestAnswer, prologue]); return { handlePressEnter, diff --git a/web/src/pages/agent/form/agent-form/constant.ts b/web/src/pages/agent/form/agent-form/constant.ts new file mode 100644 index 000000000..bcbfb33dd --- /dev/null +++ b/web/src/pages/agent/form/agent-form/constant.ts @@ -0,0 +1,4 @@ +export enum PromptRole { + User = 'user', + Assistant = 'assistant', +} diff --git a/web/src/pages/agent/form/agent-form/dynamic-prompt.tsx b/web/src/pages/agent/form/agent-form/dynamic-prompt.tsx index 0ae9330bb..4eda67c0a 100644 --- a/web/src/pages/agent/form/agent-form/dynamic-prompt.tsx +++ b/web/src/pages/agent/form/agent-form/dynamic-prompt.tsx @@ -12,11 +12,7 @@ import { memo } from 'react'; import { useFieldArray, useFormContext } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { PromptEditor } from '../components/prompt-editor'; - -export enum PromptRole { - User = 'user', - Assistant = 'assistant', -} +import { PromptRole } from './constant'; const options = [ { label: 'User', value: PromptRole.User }, diff --git a/web/src/pages/agent/form/agent-form/index.tsx b/web/src/pages/agent/form/agent-form/index.tsx index b4084e67e..edffbd2a7 100644 --- a/web/src/pages/agent/form/agent-form/index.tsx +++ b/web/src/pages/agent/form/agent-form/index.tsx @@ -2,6 +2,7 @@ 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 { BlockButton } from '@/components/ui/button'; import { Form, FormControl, @@ -9,30 +10,29 @@ import { FormItem, FormLabel, } from '@/components/ui/form'; -import { useFetchModelId } from '@/hooks/logic-hooks'; 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 { useFormValues } from '../../hooks/use-form-values'; -import { useWatchFormChange } from '../../hooks/use-watch-form-change'; import { INextOperatorForm } from '../../interface'; import { Output } from '../components/output'; import { PromptEditor } from '../components/prompt-editor'; -import DynamicPrompt from './dynamic-prompt'; +import { useValues } from './use-values'; +import { useWatchFormChange } from './use-watch-change'; const FormSchema = z.object({ sys_prompt: z.string(), - prompts: z - .array( - z.object({ - role: z.string(), - content: z.string(), - }), - ) - .optional(), + prompts: z.string().optional(), + // prompts: z + // .array( + // z.object({ + // role: z.string(), + // content: z.string(), + // }), + // ) + // .optional(), message_history_window_size: z.coerce.number(), tools: z .array( @@ -46,11 +46,8 @@ const FormSchema = z.object({ const AgentForm = ({ node }: INextOperatorForm) => { const { t } = useTranslation(); - const llmId = useFetchModelId(); - const defaultValues = useFormValues( - { ...initialAgentValues, llm_id: llmId }, - node, - ); + + const defaultValues = useValues(node); const outputList = useMemo(() => { return [ @@ -94,8 +91,22 @@ const AgentForm = ({ node }: INextOperatorForm) => { - + {/* */} + ( + + +
+ +
+
+
+ )} + />
+ Add Agent diff --git a/web/src/pages/agent/form/agent-form/use-values.ts b/web/src/pages/agent/form/agent-form/use-values.ts new file mode 100644 index 000000000..3e6b057a6 --- /dev/null +++ b/web/src/pages/agent/form/agent-form/use-values.ts @@ -0,0 +1,30 @@ +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'; + +export function useValues(node?: RAGFlowNodeType) { + const llmId = useFetchModelId(); + + const defaultValues = useMemo( + () => ({ + ...initialAgentValues, + llm_id: llmId, + prompts: '', + }), + [llmId], + ); + + const values = useMemo(() => { + const formData = node?.data?.form; + + if (isEmpty(formData)) { + return defaultValues; + } + + return { ...formData, prompts: get(formData, 'prompts.0.content', '') }; + }, [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 new file mode 100644 index 000000000..2ef81432a --- /dev/null +++ b/web/src/pages/agent/form/agent-form/use-watch-change.ts @@ -0,0 +1,22 @@ +import { useEffect } from 'react'; +import { UseFormReturn, useWatch } from 'react-hook-form'; +import useGraphStore from '../../store'; +import { PromptRole } from './constant'; + +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, + prompts: [{ role: PromptRole.User, content: values.prompts }], + }; + + updateNodeForm(id, nextValues); + } + }, [form?.formState.isDirty, id, updateNodeForm, values]); +} 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 0c167f2e4..92696e295 100644 --- a/web/src/pages/agent/hooks/use-get-begin-query.tsx +++ b/web/src/pages/agent/hooks/use-get-begin-query.tsx @@ -56,10 +56,13 @@ function filterAllUpstreamNodeIds(edges: Edge[], nodeIds: string[]) { }, []); } -function buildOutputOptions(outputs: Record = {}) { +function buildOutputOptions( + outputs: Record = {}, + nodeId?: string, +) { return Object.keys(outputs).map((x) => ({ label: x, - value: x, + value: `${nodeId}@${x}`, })); } @@ -84,7 +87,7 @@ export function useBuildNodeOutputOptions(nodeId?: string) { label: x.data.name, value: x.id, title: x.data.name, - options: buildOutputOptions(x.data.form.outputs), + options: buildOutputOptions(x.data.form.outputs, x.id), })); }, [edges, nodeId, nodes]);