diff --git a/web/src/interfaces/database/agent.ts b/web/src/interfaces/database/agent.ts index aac61a503..60f6957ab 100644 --- a/web/src/interfaces/database/agent.ts +++ b/web/src/interfaces/database/agent.ts @@ -135,9 +135,10 @@ export interface IRetrievalForm { } export interface ICodeForm { - inputs?: Array<{ name?: string; component_id?: string }>; + arguments: Record; lang: string; script?: string; + outputs: Record; } export interface IAgentForm { diff --git a/web/src/pages/agent/constant.tsx b/web/src/pages/agent/constant.tsx index 022a2742b..e1ac5cc07 100644 --- a/web/src/pages/agent/constant.tsx +++ b/web/src/pages/agent/constant.tsx @@ -667,7 +667,7 @@ export const initialCodeValues = { arg1: '', arg2: '', }, - outputs: { result: { value: '', type: 'string' } }, + outputs: {}, }; export const initialWaitingDialogueValues = {}; diff --git a/web/src/pages/agent/form/code-form/index.tsx b/web/src/pages/agent/form/code-form/index.tsx index 71e0f6f52..edeb609d6 100644 --- a/web/src/pages/agent/form/code-form/index.tsx +++ b/web/src/pages/agent/form/code-form/index.tsx @@ -13,16 +13,18 @@ import { import { Input } from '@/components/ui/input'; import { RAGFlowSelect } from '@/components/ui/select'; import { ProgrammingLanguage } from '@/constants/agent'; -import { ICodeForm } from '@/interfaces/database/flow'; +import { ICodeForm } from '@/interfaces/database/agent'; import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; -import { z } from 'zod'; +import { buildOutputList } from '../../utils/build-output-list'; +import { Output } from '../components/output'; import { DynamicInputVariable, TypeOptions, VariableTitle, } from './next-variable'; +import { FormSchema, FormSchemaType } from './schema'; import { useValues } from './use-values'; import { useHandleLanguageChange, @@ -36,26 +38,14 @@ const options = [ ProgrammingLanguage.Javascript, ].map((x) => ({ value: x, label: x })); +const DynamicFieldName = 'outputs'; + const CodeForm = ({ node }: INextOperatorForm) => { const formData = node?.data.form as ICodeForm; const { t } = useTranslation(); const values = useValues(node); - const FormSchema = z.object({ - lang: z.string(), - script: z.string(), - arguments: z.array( - z.object({ name: z.string(), component_id: z.string() }), - ), - return: z.union([ - z - .array(z.object({ name: z.string(), component_id: z.string() })) - .optional(), - z.object({ name: z.string(), component_id: z.string() }), - ]), - }); - - const form = useForm({ + const form = useForm({ defaultValues: values, resolver: zodResolver(FormSchema), }); @@ -75,6 +65,7 @@ const CodeForm = ({ node }: INextOperatorForm) => { { ) : (
@@ -132,7 +124,7 @@ const CodeForm = ({ node }: INextOperatorForm) => { ( Name @@ -148,7 +140,7 @@ const CodeForm = ({ node }: INextOperatorForm) => { /> ( Type @@ -167,6 +159,9 @@ const CodeForm = ({ node }: INextOperatorForm) => {
)} +
+ +
); }; diff --git a/web/src/pages/agent/form/code-form/next-variable.tsx b/web/src/pages/agent/form/code-form/next-variable.tsx index c3f0ac5a7..065adbf64 100644 --- a/web/src/pages/agent/form/code-form/next-variable.tsx +++ b/web/src/pages/agent/form/code-form/next-variable.tsx @@ -10,6 +10,7 @@ import { FormMessage, } from '@/components/ui/form'; import { BlurInput } from '@/components/ui/input'; +import { RAGFlowSelect } from '@/components/ui/select'; import { Separator } from '@/components/ui/separator'; import { RAGFlowNodeType } from '@/interfaces/database/flow'; import { X } from 'lucide-react'; @@ -21,18 +22,19 @@ import { useBuildQueryVariableOptions } from '../../hooks/use-get-begin-query'; interface IProps { node?: RAGFlowNodeType; name?: string; + isOutputs: boolean; } export const TypeOptions = [ 'String', 'Number', 'Boolean', - 'Array[String]', - 'Array[Number]', + 'Array', + 'Array', 'Object', ].map((x) => ({ label: x, value: x })); -export function DynamicVariableForm({ name = 'arguments' }: IProps) { +export function DynamicVariableForm({ name = 'arguments', isOutputs }: IProps) { const { t } = useTranslation(); const form = useFormContext(); @@ -67,14 +69,22 @@ export function DynamicVariableForm({ name = 'arguments' }: IProps) { ( - + {isOutputs ? ( + + ) : ( + + )} @@ -86,9 +96,7 @@ export function DynamicVariableForm({ name = 'arguments' }: IProps) { ); })} - append({ name: '', component_id: undefined })} - > + append({ name: '', type: undefined })}> {t('flow.addVariable')} @@ -103,12 +111,17 @@ export function DynamicInputVariable({ node, name, title, + isOutputs = false, }: IProps & { title: ReactNode }) { return (
- +
); diff --git a/web/src/pages/agent/form/code-form/schema.ts b/web/src/pages/agent/form/code-form/schema.ts new file mode 100644 index 000000000..fe694444e --- /dev/null +++ b/web/src/pages/agent/form/code-form/schema.ts @@ -0,0 +1,14 @@ +import { ProgrammingLanguage } from '@/constants/agent'; +import { z } from 'zod'; + +export const FormSchema = z.object({ + lang: z.enum([ProgrammingLanguage.Python, ProgrammingLanguage.Javascript]), + script: z.string(), + arguments: z.array(z.object({ name: z.string(), type: z.string() })), + outputs: z.union([ + z.array(z.object({ name: z.string(), type: z.string() })).optional(), + z.object({ name: z.string(), type: z.string() }), + ]), +}); + +export type FormSchemaType = z.infer; diff --git a/web/src/pages/agent/form/code-form/use-values.ts b/web/src/pages/agent/form/code-form/use-values.ts index ce8ef1f9b..ea6f2d67c 100644 --- a/web/src/pages/agent/form/code-form/use-values.ts +++ b/web/src/pages/agent/form/code-form/use-values.ts @@ -1,3 +1,5 @@ +import { ProgrammingLanguage } from '@/constants/agent'; +import { ICodeForm } from '@/interfaces/database/agent'; import { RAGFlowNodeType } from '@/interfaces/database/flow'; import { isEmpty } from 'lodash'; import { useMemo } from 'react'; @@ -6,10 +8,26 @@ import { initialCodeValues } from '../../constant'; function convertToArray(args: Record) { return Object.entries(args).map(([key, value]) => ({ name: key, - component_id: value, + type: value, })); } +type OutputsFormType = { name: string; type: string }; + +function convertOutputsToArray({ lang, outputs = {} }: ICodeForm) { + if (lang === ProgrammingLanguage.Python) { + return Object.entries(outputs).map(([key, val]) => ({ + name: key, + type: val.type, + })); + } + return Object.entries(outputs).reduce((pre, [key, val]) => { + pre.name = key; + pre.type = val.type; + return pre; + }, {} as OutputsFormType); +} + export function useValues(node?: RAGFlowNodeType) { const values = useMemo(() => { const formData = node?.data?.form; @@ -18,7 +36,11 @@ export function useValues(node?: RAGFlowNodeType) { return initialCodeValues; } - return { ...formData, arguments: convertToArray(formData.arguments) }; + return { + ...formData, + arguments: convertToArray(formData.arguments), + outputs: convertOutputsToArray(formData), + }; }, [node?.data?.form]); return values; diff --git a/web/src/pages/agent/form/code-form/use-watch-change.ts b/web/src/pages/agent/form/code-form/use-watch-change.ts index 6ce0187af..f01241e71 100644 --- a/web/src/pages/agent/form/code-form/use-watch-change.ts +++ b/web/src/pages/agent/form/code-form/use-watch-change.ts @@ -1,17 +1,55 @@ import { CodeTemplateStrMap, ProgrammingLanguage } from '@/constants/agent'; +import { ICodeForm } from '@/interfaces/database/agent'; +import { isEmpty } from 'lodash'; import { useCallback, useEffect } from 'react'; import { UseFormReturn, useWatch } from 'react-hook-form'; import useGraphStore from '../../store'; +import { FormSchemaType } from './schema'; -function convertToObject(list: Array<{ name: string; component_id: string }>) { +function convertToObject(list: FormSchemaType['arguments'] = []) { return list.reduce>((pre, cur) => { - pre[cur.name] = cur.component_id; + if (cur.name && cur.type) { + pre[cur.name] = cur.type; + } return pre; }, {}); } -export function useWatchFormChange(id?: string, form?: UseFormReturn) { +type ArrayOutputs = Extract>; + +type ObjectOutputs = Exclude>; + +function convertOutputsToObject({ lang, outputs }: FormSchemaType) { + if (lang === ProgrammingLanguage.Python) { + return (outputs as ArrayOutputs).reduce( + (pre, cur) => { + pre[cur.name] = { + value: '', + type: cur.type, + }; + + return pre; + }, + {}, + ); + } + const outputsObject = outputs as ObjectOutputs; + if (isEmpty(outputsObject)) { + return {}; + } + return { + [outputsObject.name]: { + value: '', + type: outputsObject.type, + }, + }; +} + +export function useWatchFormChange( + id?: string, + form?: UseFormReturn, +) { let values = useWatch({ control: form?.control }); const updateNodeForm = useGraphStore((state) => state.updateNodeForm); @@ -21,7 +59,10 @@ export function useWatchFormChange(id?: string, form?: UseFormReturn) { values = form?.getValues() || {}; let nextValues: any = { ...values, - arguments: convertToObject(values.arguments), + arguments: convertToObject( + values?.arguments as FormSchemaType['arguments'], + ), + outputs: convertOutputsToObject(values as FormSchemaType), }; updateNodeForm(id, nextValues); @@ -29,7 +70,10 @@ export function useWatchFormChange(id?: string, form?: UseFormReturn) { }, [form?.formState.isDirty, id, updateNodeForm, values]); } -export function useHandleLanguageChange(id?: string, form?: UseFormReturn) { +export function useHandleLanguageChange( + id?: string, + form?: UseFormReturn, +) { const updateNodeForm = useGraphStore((state) => state.updateNodeForm); const handleLanguageChange = useCallback( @@ -37,6 +81,12 @@ export function useHandleLanguageChange(id?: string, form?: UseFormReturn) { if (id) { const script = CodeTemplateStrMap[lang as ProgrammingLanguage]; form?.setValue('script', script); + form?.setValue( + 'outputs', + (lang === ProgrammingLanguage.Python + ? [] + : {}) as FormSchemaType['outputs'], + ); updateNodeForm(id, script, ['script']); } }, diff --git a/web/src/pages/agent/utils/build-output-list.ts b/web/src/pages/agent/utils/build-output-list.ts new file mode 100644 index 000000000..5a9452965 --- /dev/null +++ b/web/src/pages/agent/utils/build-output-list.ts @@ -0,0 +1,8 @@ +import { OutputType } from '../form/components/output'; + +export function buildOutputList(outputs: Record>) { + return Object.entries(outputs).reduce((pre, [key, val]) => { + pre.push({ title: key, type: val.type }); + return pre; + }, []); +}