### What problem does this PR solve? Feat: Verify the parameters of the begin operator #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.19.x
| @@ -3,7 +3,7 @@ import { cva, type VariantProps } from 'class-variance-authority'; | |||
| import * as React from 'react'; | |||
| import { cn } from '@/lib/utils'; | |||
| import { Loader2 } from 'lucide-react'; | |||
| import { Loader2, Plus } from 'lucide-react'; | |||
| const buttonVariants = cva( | |||
| 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', | |||
| @@ -93,3 +93,18 @@ export const ButtonLoading = React.forwardRef< | |||
| ButtonLoading.displayName = 'ButtonLoading'; | |||
| export { Button, buttonVariants }; | |||
| export const BlockButton = React.forwardRef<HTMLButtonElement, ButtonProps>( | |||
| ({ children, className, ...props }, ref) => { | |||
| return ( | |||
| <Button | |||
| variant={'outline'} | |||
| ref={ref} | |||
| className={cn('w-full border-dashed border-input-border', className)} | |||
| {...props} | |||
| > | |||
| <Plus /> {children} | |||
| </Button> | |||
| ); | |||
| }, | |||
| ); | |||
| @@ -3006,3 +3006,8 @@ export const NoDebugOperatorsList = [ | |||
| Operator.Switch, | |||
| Operator.Iteration, | |||
| ]; | |||
| export enum AgentDialogueMode { | |||
| Conversational = 'Conversational', | |||
| Task = 'Task', | |||
| } | |||
| @@ -1,7 +1,7 @@ | |||
| import { CodeTemplateStrMap, ProgrammingLanguage } from '@/constants/agent'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { z } from 'zod'; | |||
| import { Operator } from '../constant'; | |||
| import { AgentDialogueMode, Operator } from '../constant'; | |||
| import AkShareForm from '../form/akshare-form'; | |||
| import AnswerForm from '../form/answer-form'; | |||
| import ArXivForm from '../form/arxiv-form'; | |||
| @@ -44,15 +44,19 @@ export function useFormConfigMap() { | |||
| [Operator.Begin]: { | |||
| component: BeginForm, | |||
| defaultValues: { | |||
| enablePrologue: true, | |||
| prologue: t('chat.setAnOpenerInitial'), | |||
| mode: AgentDialogueMode.Conversational, | |||
| }, | |||
| schema: z.object({ | |||
| enablePrologue: z.boolean(), | |||
| prologue: z | |||
| .string() | |||
| .min(1, { | |||
| message: t('common.namePlaceholder'), | |||
| }) | |||
| .trim(), | |||
| mode: z.string(), | |||
| query: z.array( | |||
| z.object({ | |||
| key: z.string(), | |||
| @@ -1,4 +1,4 @@ | |||
| import { Button } from '@/components/ui/button'; | |||
| import { BlockButton } from '@/components/ui/button'; | |||
| import { | |||
| Form, | |||
| FormControl, | |||
| @@ -7,19 +7,31 @@ import { | |||
| FormLabel, | |||
| FormMessage, | |||
| } from '@/components/ui/form'; | |||
| import { RAGFlowSelect } from '@/components/ui/select'; | |||
| import { Switch } from '@/components/ui/switch'; | |||
| import { Textarea } from '@/components/ui/textarea'; | |||
| import { buildSelectOptions } from '@/utils/common-util'; | |||
| import { useCallback } from 'react'; | |||
| import { useWatch } from 'react-hook-form'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { AgentDialogueMode } from '../../constant'; | |||
| import { BeginQuery, INextOperatorForm } from '../../interface'; | |||
| import { useEditQueryRecord } from './hooks'; | |||
| import { ParameterDialog } from './next-paramater-modal'; | |||
| import { ParameterDialog } from './paramater-dialog'; | |||
| import QueryTable from './query-table'; | |||
| const ModeOptions = buildSelectOptions([ | |||
| (AgentDialogueMode.Conversational, AgentDialogueMode.Task), | |||
| ]); | |||
| const BeginForm = ({ form }: INextOperatorForm) => { | |||
| const { t } = useTranslation(); | |||
| const query = useWatch({ control: form.control, name: 'query' }); | |||
| const enablePrologue = useWatch({ | |||
| control: form.control, | |||
| name: 'enablePrologue', | |||
| }); | |||
| const { | |||
| ok, | |||
| @@ -47,51 +59,89 @@ const BeginForm = ({ form }: INextOperatorForm) => { | |||
| ); | |||
| return ( | |||
| <Form {...form}> | |||
| <FormField | |||
| control={form.control} | |||
| name={'prologue'} | |||
| render={({ field }) => ( | |||
| <FormItem> | |||
| <FormLabel tooltip={t('chat.setAnOpenerTip')}> | |||
| {t('chat.setAnOpener')} | |||
| </FormLabel> | |||
| <FormControl> | |||
| <Textarea | |||
| rows={5} | |||
| {...field} | |||
| placeholder={t('common.pleaseInput')} | |||
| ></Textarea> | |||
| </FormControl> | |||
| <FormMessage /> | |||
| </FormItem> | |||
| <section className="px-5 space-y-5"> | |||
| <Form {...form}> | |||
| <FormField | |||
| control={form.control} | |||
| name={'mode'} | |||
| render={({ field }) => ( | |||
| <FormItem> | |||
| <FormLabel tooltip={t('chat.setAnOpenerTip')}>Mode</FormLabel> | |||
| <FormControl> | |||
| <RAGFlowSelect | |||
| placeholder={t('common.pleaseSelect')} | |||
| options={ModeOptions} | |||
| {...field} | |||
| ></RAGFlowSelect> | |||
| </FormControl> | |||
| <FormMessage /> | |||
| </FormItem> | |||
| )} | |||
| /> | |||
| <FormField | |||
| control={form.control} | |||
| name={'enablePrologue'} | |||
| render={({ field }) => ( | |||
| <FormItem> | |||
| <FormLabel tooltip={t('chat.setAnOpenerTip')}> | |||
| Welcome Message | |||
| </FormLabel> | |||
| <FormControl> | |||
| <Switch | |||
| checked={field.value} | |||
| onCheckedChange={field.onChange} | |||
| /> | |||
| </FormControl> | |||
| <FormMessage /> | |||
| </FormItem> | |||
| )} | |||
| /> | |||
| {enablePrologue && ( | |||
| <FormField | |||
| control={form.control} | |||
| name={'prologue'} | |||
| render={({ field }) => ( | |||
| <FormItem> | |||
| <FormLabel tooltip={t('chat.setAnOpenerTip')}> | |||
| {t('chat.setAnOpener')} | |||
| </FormLabel> | |||
| <FormControl> | |||
| <Textarea | |||
| rows={5} | |||
| {...field} | |||
| placeholder={t('common.pleaseInput')} | |||
| ></Textarea> | |||
| </FormControl> | |||
| <FormMessage /> | |||
| </FormItem> | |||
| )} | |||
| /> | |||
| )} | |||
| /> | |||
| {/* Create a hidden field to make Form instance record this */} | |||
| <FormField | |||
| control={form.control} | |||
| name={'query'} | |||
| render={() => <div></div>} | |||
| /> | |||
| <QueryTable | |||
| data={query} | |||
| showModal={showModal} | |||
| deleteRecord={handleDeleteRecord} | |||
| ></QueryTable> | |||
| <Button onClick={() => showModal()}>{t('flow.addItem')}</Button> | |||
| {visible && ( | |||
| <ParameterDialog | |||
| visible={visible} | |||
| hideModal={hideModal} | |||
| initialValue={currentRecord} | |||
| onOk={ok} | |||
| otherThanCurrentQuery={otherThanCurrentQuery} | |||
| ></ParameterDialog> | |||
| )} | |||
| </Form> | |||
| {/* Create a hidden field to make Form instance record this */} | |||
| {/* <FormField | |||
| control={form.control} | |||
| name={'query'} | |||
| render={() => <div></div>} | |||
| /> */} | |||
| <QueryTable | |||
| data={query} | |||
| showModal={showModal} | |||
| deleteRecord={handleDeleteRecord} | |||
| ></QueryTable> | |||
| <BlockButton onClick={() => showModal()}> | |||
| {t('flow.addItem')} | |||
| </BlockButton> | |||
| {visible && ( | |||
| <ParameterDialog | |||
| visible={visible} | |||
| hideModal={hideModal} | |||
| initialValue={currentRecord} | |||
| onOk={ok} | |||
| otherThanCurrentQuery={otherThanCurrentQuery} | |||
| ></ParameterDialog> | |||
| )} | |||
| </Form> | |||
| </section> | |||
| ); | |||
| }; | |||
| @@ -1,4 +1,3 @@ | |||
| import { toast } from '@/components/hooks/use-toast'; | |||
| import { Button } from '@/components/ui/button'; | |||
| import { | |||
| Dialog, | |||
| @@ -44,13 +43,15 @@ function ParameterForm({ | |||
| key: z | |||
| .string() | |||
| .trim() | |||
| .min(1) | |||
| .refine( | |||
| (value) => | |||
| !value || !otherThanCurrentQuery.some((x) => x.key === value), | |||
| { message: 'The key cannot be repeated!' }, | |||
| ), | |||
| optional: z.boolean(), | |||
| options: z.array(z.string().or(z.boolean()).or(z.number())), | |||
| name: z.string().trim().min(1), | |||
| options: z.array(z.string().or(z.boolean()).or(z.number())).optional(), | |||
| }); | |||
| const form = useForm<z.infer<typeof FormSchema>>({ | |||
| @@ -58,6 +59,8 @@ function ParameterForm({ | |||
| defaultValues: { | |||
| type: BeginQueryType.Line, | |||
| optional: false, | |||
| key: '', | |||
| name: '', | |||
| }, | |||
| }); | |||
| @@ -95,19 +98,17 @@ function ParameterForm({ | |||
| }, [form, initialValue]); | |||
| function onSubmit(data: z.infer<typeof FormSchema>) { | |||
| toast({ | |||
| title: 'You submitted the following values:', | |||
| description: ( | |||
| <pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4"> | |||
| <code className="text-white">{JSON.stringify(data, null, 2)}</code> | |||
| </pre> | |||
| ), | |||
| }); | |||
| console.log('🚀 ~ onSubmit ~ data:', data); | |||
| } | |||
| return ( | |||
| <Form {...form}> | |||
| <form onSubmit={form.handleSubmit(onSubmit)} id={FormId}> | |||
| <form | |||
| onSubmit={form.handleSubmit(onSubmit)} | |||
| id={FormId} | |||
| className="space-y-5" | |||
| autoComplete="off" | |||
| > | |||
| <FormField | |||
| name="type" | |||
| control={form.control} | |||
| @@ -127,6 +128,19 @@ function ParameterForm({ | |||
| render={({ field }) => ( | |||
| <FormItem> | |||
| <FormLabel>Key</FormLabel> | |||
| <FormControl> | |||
| <Input {...field} autoComplete="off" /> | |||
| </FormControl> | |||
| <FormMessage /> | |||
| </FormItem> | |||
| )} | |||
| /> | |||
| <FormField | |||
| name="name" | |||
| control={form.control} | |||
| render={({ field }) => ( | |||
| <FormItem> | |||
| <FormLabel>Name</FormLabel> | |||
| <FormControl> | |||
| <Input {...field} /> | |||
| </FormControl> | |||
| @@ -175,12 +189,12 @@ export function ParameterDialog({ | |||
| initialValue={initialValue} | |||
| otherThanCurrentQuery={otherThanCurrentQuery} | |||
| ></ParameterForm> | |||
| <DialogFooter> | |||
| <Button type="submit" form={FormId}> | |||
| Confirm | |||
| </Button> | |||
| </DialogFooter> | |||
| </DialogContent> | |||
| <DialogFooter> | |||
| <Button type="submit" id={FormId}> | |||
| Confirm | |||
| </Button> | |||
| </DialogFooter> | |||
| </Dialog> | |||
| ); | |||
| } | |||
| @@ -140,3 +140,7 @@ export function formatFileSize(bytes: number, si = true, dp = 1) { | |||
| return nextBytes.toFixed(dp) + ' ' + units[u]; | |||
| } | |||
| export function buildSelectOptions(list: Array<string>) { | |||
| return list.map((x) => ({ label: x, value: x })); | |||
| } | |||
| @@ -52,6 +52,8 @@ module.exports = { | |||
| 'background-card': 'var(--background-card)', | |||
| 'background-checked': 'var(--background-checked)', | |||
| 'input-border': 'var(--input-border)', | |||
| primary: { | |||
| DEFAULT: 'hsl(var(--primary))', | |||
| foreground: 'hsl(var(--primary-foreground))', | |||
| @@ -86,6 +86,8 @@ | |||
| --background-card: rgba(22, 22, 24, 0.05); | |||
| --background-checked: rgba(76, 164, 231, 1); | |||
| --input-border: rgba(22, 22, 24, 0.2); | |||
| } | |||
| .dark { | |||
| @@ -192,6 +194,8 @@ | |||
| --background-card: rgba(255, 255, 255, 0.05); | |||
| --background-checked: rgba(76, 164, 231, 1); | |||
| --input-border: rgba(255, 255, 255, 0.2); | |||
| } | |||
| } | |||