### What problem does this PR solve? Feat: Convert the arguments parameter of the code operator to a dictionary #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.20.0
| @@ -130,16 +130,14 @@ const MessageItem = ({ | |||
| <Flex vertical gap={8} flex={1}> | |||
| <Space> | |||
| {isAssistant ? ( | |||
| index !== 0 && ( | |||
| <AssistantGroupButton | |||
| messageId={item.id} | |||
| content={item.content} | |||
| prompt={item.prompt} | |||
| showLikeButton={showLikeButton} | |||
| audioBinary={item.audio_binary} | |||
| showLoudspeaker={showLoudspeaker} | |||
| ></AssistantGroupButton> | |||
| ) | |||
| <AssistantGroupButton | |||
| messageId={item.id} | |||
| content={item.content} | |||
| prompt={item.prompt} | |||
| showLikeButton={showLikeButton} | |||
| audioBinary={item.audio_binary} | |||
| showLoudspeaker={showLoudspeaker} | |||
| ></AssistantGroupButton> | |||
| ) : ( | |||
| <UserGroupButton | |||
| content={item.content} | |||
| @@ -98,7 +98,7 @@ export interface IReference { | |||
| export interface IAnswer { | |||
| answer: string; | |||
| reference: IReference; | |||
| reference?: IReference; | |||
| conversationId?: string; | |||
| prompt?: string; | |||
| id?: string; | |||
| @@ -12,6 +12,7 @@ import { RightHandleStyle } from './handle-icon'; | |||
| import styles from './index.less'; | |||
| import NodeHeader from './node-header'; | |||
| import { NodeWrapper } from './node-wrapper'; | |||
| import { ToolBar } from './toolbar'; | |||
| function ResizeIcon() { | |||
| return ( | |||
| @@ -56,10 +57,13 @@ export function InnerIterationNode({ | |||
| return ( | |||
| <section | |||
| className={cn('h-full bg-transparent rounded-b-md', { | |||
| className={cn('h-full bg-transparent rounded-b-md relative', { | |||
| [styles.selectedHeader]: selected, | |||
| })} | |||
| > | |||
| <ToolBar selected={selected} id={id} label={data.label}> | |||
| <div className="h-full w-full"></div> | |||
| </ToolBar> | |||
| <NodeResizeControl style={controlStyle} minWidth={100} minHeight={50}> | |||
| <ResizeIcon /> | |||
| </NodeResizeControl> | |||
| @@ -22,6 +22,8 @@ import { useParams } from 'umi'; | |||
| import { v4 as uuid } from 'uuid'; | |||
| import { BeginId } from '../constant'; | |||
| import { AgentChatLogContext } from '../context'; | |||
| import { transferInputsArrayToObject } from '../form/begin-form/use-watch-change'; | |||
| import { useGetBeginNodeDataQuery } from '../hooks/use-get-begin-query'; | |||
| import { BeginQuery } from '../interface'; | |||
| import useGraphStore from '../store'; | |||
| import { receiveMessageError } from '../utils'; | |||
| @@ -109,6 +111,7 @@ export const useSendNextMessage = () => { | |||
| const { handleInputChange, value, setValue } = useHandleMessageInputChange(); | |||
| const { refetch } = useFetchAgent(); | |||
| const { addEventList } = useContext(AgentChatLogContext); | |||
| const getBeginNodeDataQuery = useGetBeginNodeDataQuery(); | |||
| const { send, answerList, done, stopOutputMessage } = useSendMessageBySSE( | |||
| api.runCanvas, | |||
| @@ -191,12 +194,22 @@ export const useSendNextMessage = () => { | |||
| ); | |||
| useEffect(() => { | |||
| if (prologue) { | |||
| const query = getBeginNodeDataQuery(); | |||
| if (query.length > 0) { | |||
| send({ id: agentId, inputs: transferInputsArrayToObject(query) }); | |||
| } else if (prologue) { | |||
| addNewestOneAnswer({ | |||
| answer: prologue, | |||
| }); | |||
| } | |||
| }, [addNewestOneAnswer, prologue]); | |||
| }, [ | |||
| addNewestOneAnswer, | |||
| agentId, | |||
| getBeginNodeDataQuery, | |||
| prologue, | |||
| send, | |||
| sendFormMessage, | |||
| ]); | |||
| useEffect(() => { | |||
| addEventList(answerList); | |||
| @@ -1,86 +0,0 @@ | |||
| 'use client'; | |||
| import { zodResolver } from '@hookform/resolvers/zod'; | |||
| import { useForm } from 'react-hook-form'; | |||
| import { toast } from 'sonner'; | |||
| import { z } from 'zod'; | |||
| import { Button } from '@/components/ui/button'; | |||
| import { | |||
| Form, | |||
| FormControl, | |||
| FormField, | |||
| FormItem, | |||
| FormLabel, | |||
| FormMessage, | |||
| } from '@/components/ui/form'; | |||
| import { Input } from '@/components/ui/input'; | |||
| import { Message } from '@/interfaces/database/chat'; | |||
| import { get } from 'lodash'; | |||
| import { useParams } from 'umi'; | |||
| import { useSendNextMessage } from './hooks'; | |||
| const FormSchema = z.object({ | |||
| username: z.string().min(2, { | |||
| message: 'Username must be at least 2 characters.', | |||
| }), | |||
| }); | |||
| type InputFormProps = Pick<ReturnType<typeof useSendNextMessage>, 'send'> & { | |||
| message: Message; | |||
| }; | |||
| export function InputForm({ send, message }: InputFormProps) { | |||
| const form = useForm<z.infer<typeof FormSchema>>({ | |||
| resolver: zodResolver(FormSchema), | |||
| defaultValues: { | |||
| username: '', | |||
| }, | |||
| }); | |||
| const { id: canvasId } = useParams(); | |||
| function onSubmit(data: z.infer<typeof FormSchema>) { | |||
| const inputs = get(message, 'data.inputs', {}); | |||
| const nextInputs = Object.entries(inputs).reduce((pre, [key, val]) => { | |||
| pre[key] = { ...val, value: data.username }; | |||
| return pre; | |||
| }, {}); | |||
| send({ | |||
| inputs: nextInputs, | |||
| id: canvasId, | |||
| }); | |||
| toast('You submitted the following values', { | |||
| description: ( | |||
| <pre className="mt-2 w-[320px] rounded-md bg-neutral-950 p-4"> | |||
| <code className="text-white">{JSON.stringify(data, null, 2)}</code> | |||
| </pre> | |||
| ), | |||
| }); | |||
| } | |||
| return ( | |||
| <Form {...form}> | |||
| <form onSubmit={form.handleSubmit(onSubmit)} className="w-2/3 space-y-6"> | |||
| <FormField | |||
| control={form.control} | |||
| name="username" | |||
| render={({ field }) => ( | |||
| <FormItem> | |||
| <FormLabel>Username</FormLabel> | |||
| <FormControl> | |||
| <Input placeholder="shadcn" {...field} /> | |||
| </FormControl> | |||
| <FormMessage /> | |||
| </FormItem> | |||
| )} | |||
| /> | |||
| <Button type="submit">Submit</Button> | |||
| </form> | |||
| </Form> | |||
| ); | |||
| } | |||
| @@ -661,16 +661,12 @@ export const initialIterationStartValues = { | |||
| }; | |||
| export const initialCodeValues = { | |||
| lang: 'python', | |||
| lang: ProgrammingLanguage.Python, | |||
| script: CodeTemplateStrMap[ProgrammingLanguage.Python], | |||
| arguments: [ | |||
| { | |||
| name: 'arg1', | |||
| }, | |||
| { | |||
| name: 'arg2', | |||
| }, | |||
| ], | |||
| arguments: { | |||
| arg1: '', | |||
| arg2: '', | |||
| }, | |||
| }; | |||
| export const initialWaitingDialogueValues = {}; | |||
| @@ -4,7 +4,7 @@ import { UseFormReturn, useWatch } from 'react-hook-form'; | |||
| import { BeginQuery } from '../../interface'; | |||
| import useGraphStore from '../../store'; | |||
| function transferInputsArrayToObject(inputs: BeginQuery[] = []) { | |||
| export function transferInputsArrayToObject(inputs: BeginQuery[] = []) { | |||
| return inputs.reduce<Record<string, Omit<BeginQuery, 'key'>>>((pre, cur) => { | |||
| pre[cur.key] = omit(cur, 'key'); | |||
| @@ -1,27 +1,25 @@ | |||
| import { CodeTemplateStrMap, ProgrammingLanguage } from '@/constants/agent'; | |||
| import { RAGFlowNodeType } from '@/interfaces/database/flow'; | |||
| import { isEmpty } from 'lodash'; | |||
| import { useMemo } from 'react'; | |||
| import { initialCodeValues } from '../../constant'; | |||
| export function useValues(node?: RAGFlowNodeType) { | |||
| const defaultValues = useMemo( | |||
| () => ({ | |||
| lang: ProgrammingLanguage.Python, | |||
| script: CodeTemplateStrMap[ProgrammingLanguage.Python], | |||
| arguments: [], | |||
| }), | |||
| [], | |||
| ); | |||
| function convertToArray(args: Record<string, string>) { | |||
| return Object.entries(args).map(([key, value]) => ({ | |||
| name: key, | |||
| component_id: value, | |||
| })); | |||
| } | |||
| export function useValues(node?: RAGFlowNodeType) { | |||
| const values = useMemo(() => { | |||
| const formData = node?.data?.form; | |||
| if (isEmpty(formData)) { | |||
| return defaultValues; | |||
| return initialCodeValues; | |||
| } | |||
| return formData; | |||
| }, [defaultValues, node?.data?.form]); | |||
| return { ...formData, arguments: convertToArray(formData.arguments) }; | |||
| }, [node?.data?.form]); | |||
| return values; | |||
| } | |||
| @@ -3,6 +3,14 @@ import { useCallback, useEffect } from 'react'; | |||
| import { UseFormReturn, useWatch } from 'react-hook-form'; | |||
| import useGraphStore from '../../store'; | |||
| function convertToObject(list: Array<{ name: string; component_id: string }>) { | |||
| return list.reduce<Record<string, string>>((pre, cur) => { | |||
| pre[cur.name] = cur.component_id; | |||
| return pre; | |||
| }, {}); | |||
| } | |||
| export function useWatchFormChange(id?: string, form?: UseFormReturn) { | |||
| let values = useWatch({ control: form?.control }); | |||
| const updateNodeForm = useGraphStore((state) => state.updateNodeForm); | |||
| @@ -11,7 +19,10 @@ export function useWatchFormChange(id?: string, form?: UseFormReturn) { | |||
| // Manually triggered form updates are synchronized to the canvas | |||
| if (id) { | |||
| values = form?.getValues() || {}; | |||
| let nextValues: any = values; | |||
| let nextValues: any = { | |||
| ...values, | |||
| arguments: convertToObject(values.arguments), | |||
| }; | |||
| updateNodeForm(id, nextValues); | |||
| } | |||
| @@ -37,7 +37,7 @@ export class VariableNode extends DecoratorNode<ReactNode> { | |||
| let content: ReactNode = ( | |||
| <span className="text-blue-600">{this.__label}</span> | |||
| ); | |||
| if (this.__value.startsWith(prefix)) { | |||
| if (this.__value?.startsWith(prefix)) { | |||
| content = ( | |||
| <div> | |||
| <span>{i18n.t(`flow.begin`)}</span> / {content} | |||
| @@ -220,6 +220,10 @@ export default function VariablePickerMenuPlugin({ | |||
| } | |||
| $getRoot().clear().append(paragraph); | |||
| if ($isRangeSelection($getSelection())) { | |||
| $getRoot().selectEnd(); | |||
| } | |||
| }, | |||
| [findLabelByValue], | |||
| ); | |||