### 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); | |||
} | |||
} | |||