Feat: Add type card to create agent dialog #9869 (#10025)

### What problem does this PR solve?

Feat: Add type card to create agent dialog #9869
### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2025-09-10 15:56:10 +08:00
committed by GitHub
parent bbe6ed3b90
commit 7d14455fbe
8 changed files with 151 additions and 50 deletions

View File

@ -15,6 +15,7 @@ type RAGFlowFormItemProps = {
tooltip?: ReactNode; tooltip?: ReactNode;
children: ReactNode | ((field: ControllerRenderProps) => ReactNode); children: ReactNode | ((field: ControllerRenderProps) => ReactNode);
horizontal?: boolean; horizontal?: boolean;
required?: boolean;
}; };
export function RAGFlowFormItem({ export function RAGFlowFormItem({
@ -23,6 +24,7 @@ export function RAGFlowFormItem({
tooltip, tooltip,
children, children,
horizontal = false, horizontal = false,
required = false,
}: RAGFlowFormItemProps) { }: RAGFlowFormItemProps) {
const form = useFormContext(); const form = useFormContext();
return ( return (
@ -35,7 +37,11 @@ export function RAGFlowFormItem({
'flex items-center': horizontal, 'flex items-center': horizontal,
})} })}
> >
<FormLabel tooltip={tooltip} className={cn({ 'w-1/4': horizontal })}> <FormLabel
required={required}
tooltip={tooltip}
className={cn({ 'w-1/4': horizontal })}
>
{label} {label}
</FormLabel> </FormLabel>
<FormControl> <FormControl>

View File

@ -38,7 +38,7 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content <DialogPrimitive.Content
ref={ref} ref={ref}
className={cn( className={cn(
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-xl translate-x-[-50%] translate-y-[-50%] gap-4 border bg-colors-background-neutral-standard p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg', 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-xl translate-x-[-50%] translate-y-[-50%] gap-4 border bg-bg-base p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
className, className,
)} )}
{...props} {...props}

View File

@ -934,7 +934,7 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s
exceptionMethod: 'Exception method', exceptionMethod: 'Exception method',
maxRounds: 'Max reflection rounds', maxRounds: 'Max reflection rounds',
delayEfterError: 'Delay after error', delayEfterError: 'Delay after error',
maxRetries: 'Max retries', maxRetries: 'Max reflection rounds',
advancedSettings: 'Advanced Settings', advancedSettings: 'Advanced Settings',
addTools: 'Add Tools', addTools: 'Add Tools',
sysPromptDefultValue: ` sysPromptDefultValue: `

View File

@ -892,7 +892,7 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
exceptionMethod: '异常处理方法', exceptionMethod: '异常处理方法',
maxRounds: '最大反思轮数', maxRounds: '最大反思轮数',
delayEfterError: '错误后延迟', delayEfterError: '错误后延迟',
maxRetries: '最大重试次数', maxRetries: '最大反思轮数',
advancedSettings: '高级设置', advancedSettings: '高级设置',
addTools: '添加工具', addTools: '添加工具',
sysPromptDefultValue: ` sysPromptDefultValue: `

View File

@ -27,9 +27,11 @@ export default function AgentTemplates() {
const [selectMenuItem, setSelectMenuItem] = useState<string>( const [selectMenuItem, setSelectMenuItem] = useState<string>(
MenuItemKey.Recommended, MenuItemKey.Recommended,
); );
useEffect(() => { useEffect(() => {
setTemplateList(list); setTemplateList(list);
}, [list]); }, [list]);
const { const {
visible: creatingVisible, visible: creatingVisible,
hideModal: hideCreatingModal, hideModal: hideCreatingModal,

View File

@ -6,16 +6,18 @@ import {
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from '@/components/ui/dialog'; } from '@/components/ui/dialog';
import { IModalProps } from '@/interfaces/common';
import { TagRenameId } from '@/pages/add-knowledge/constant'; import { TagRenameId } from '@/pages/add-knowledge/constant';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { CreateAgentForm } from './create-agent-form'; import { CreateAgentForm, CreateAgentFormProps } from './create-agent-form';
type CreateAgentDialogProps = CreateAgentFormProps;
export function CreateAgentDialog({ export function CreateAgentDialog({
hideModal, hideModal,
onOk, onOk,
loading, loading,
}: IModalProps<any>) { shouldChooseAgent,
}: CreateAgentDialogProps) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
@ -24,7 +26,11 @@ export function CreateAgentDialog({
<DialogHeader> <DialogHeader>
<DialogTitle>{t('flow.createGraph')}</DialogTitle> <DialogTitle>{t('flow.createGraph')}</DialogTitle>
</DialogHeader> </DialogHeader>
<CreateAgentForm hideModal={hideModal} onOk={onOk}></CreateAgentForm> <CreateAgentForm
hideModal={hideModal}
onOk={onOk}
shouldChooseAgent={shouldChooseAgent}
></CreateAgentForm>
<DialogFooter> <DialogFooter>
<ButtonLoading type="submit" form={TagRenameId} loading={loading}> <ButtonLoading type="submit" form={TagRenameId} loading={loading}>
{t('common.save')} {t('common.save')}

View File

@ -4,6 +4,8 @@ import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { z } from 'zod'; import { z } from 'zod';
import { RAGFlowFormItem } from '@/components/ragflow-form';
import { Card, CardContent } from '@/components/ui/card';
import { import {
Form, Form,
FormControl, FormControl,
@ -14,10 +16,76 @@ import {
} from '@/components/ui/form'; } from '@/components/ui/form';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { cn } from '@/lib/utils';
import { TagRenameId } from '@/pages/add-knowledge/constant'; import { TagRenameId } from '@/pages/add-knowledge/constant';
import { BrainCircuit, Check, Route } from 'lucide-react';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
export function CreateAgentForm({ hideModal, onOk }: IModalProps<any>) { export type CreateAgentFormProps = IModalProps<any> & {
shouldChooseAgent?: boolean;
};
enum FlowType {
Agent = 'agent',
Flow = 'flow',
}
type FlowTypeCardProps = {
value?: FlowType;
onChange?: (value: FlowType) => void;
};
function FlowTypeCards({ value, onChange }: FlowTypeCardProps) {
const handleChange = useCallback(
(value: FlowType) => () => {
onChange?.(value);
},
[onChange],
);
return (
<section className="flex gap-10">
{Object.values(FlowType).map((val) => {
const isActive = value === val;
return (
<Card
key={val}
className={cn('flex-1 rounded-lg border bg-transparent', {
'border-bg-base': isActive,
'border-border-default': !isActive,
})}
>
<CardContent
onClick={handleChange(val)}
className={cn(
'cursor-pointer p-5 text-text-secondary flex justify-between items-center',
{
'text-text-primary': isActive,
},
)}
>
<div className="flex gap-2">
{val === FlowType.Agent ? (
<BrainCircuit className="size-6" />
) : (
<Route className="size-6" />
)}
<p>{val}</p>
</div>
{isActive && <Check />}
</CardContent>
</Card>
);
})}
</section>
);
}
export function CreateAgentForm({
hideModal,
onOk,
shouldChooseAgent = false,
}: CreateAgentFormProps) {
const { t } = useTranslation(); const { t } = useTranslation();
const FormSchema = z.object({ const FormSchema = z.object({
name: z name: z
@ -28,11 +96,12 @@ export function CreateAgentForm({ hideModal, onOk }: IModalProps<any>) {
.trim(), .trim(),
tag: z.string().trim().optional(), tag: z.string().trim().optional(),
description: z.string().trim().optional(), description: z.string().trim().optional(),
type: z.nativeEnum(FlowType).optional(),
}); });
const form = useForm<z.infer<typeof FormSchema>>({ const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema), resolver: zodResolver(FormSchema),
defaultValues: { name: '' }, defaultValues: { name: '', type: FlowType.Agent },
}); });
async function onSubmit(data: z.infer<typeof FormSchema>) { async function onSubmit(data: z.infer<typeof FormSchema>) {
@ -49,12 +118,17 @@ export function CreateAgentForm({ hideModal, onOk }: IModalProps<any>) {
className="space-y-6" className="space-y-6"
id={TagRenameId} id={TagRenameId}
> >
{shouldChooseAgent && (
<RAGFlowFormItem required name="type" label={t('common.type')}>
<FlowTypeCards></FlowTypeCards>
</RAGFlowFormItem>
)}
<FormField <FormField
control={form.control} control={form.control}
name="name" name="name"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t('common.name')}</FormLabel> <FormLabel required>{t('common.name')}</FormLabel>
<FormControl> <FormControl>
<Input <Input
placeholder={t('common.namePlaceholder')} placeholder={t('common.namePlaceholder')}
@ -66,40 +140,6 @@ export function CreateAgentForm({ hideModal, onOk }: IModalProps<any>) {
</FormItem> </FormItem>
)} )}
/> />
{/* <FormField
control={form.control}
name="tag"
render={({ field }) => (
<FormItem>
<FormLabel>{t('flow.tag')}</FormLabel>
<FormControl>
<Input
placeholder={t('flow.tagPlaceholder')}
{...field}
autoComplete="off"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
render={({ field }) => (
<FormItem>
<FormLabel>{t('flow.description')}</FormLabel>
<FormControl>
<Input
placeholder={t('flow.descriptionPlaceholder')}
{...field}
autoComplete="off"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/> */}
</form> </form>
</Form> </Form>
); );

View File

@ -1,14 +1,22 @@
import ListFilterBar from '@/components/list-filter-bar'; import ListFilterBar from '@/components/list-filter-bar';
import { RenameDialog } from '@/components/rename-dialog'; import { RenameDialog } from '@/components/rename-dialog';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { RAGFlowPagination } from '@/components/ui/ragflow-pagination'; import { RAGFlowPagination } from '@/components/ui/ragflow-pagination';
import { useSetModalState } from '@/hooks/common-hooks';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { useFetchAgentListByPage } from '@/hooks/use-agent-request'; import { useFetchAgentListByPage } from '@/hooks/use-agent-request';
import { t } from 'i18next'; import { t } from 'i18next';
import { pick } from 'lodash'; import { pick } from 'lodash';
import { Plus } from 'lucide-react'; import { Clipboard, ClipboardPlus, FileInput, Plus } from 'lucide-react';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { AgentCard } from './agent-card'; import { AgentCard } from './agent-card';
import { CreateAgentDialog } from './create-agent-dialog';
import { useRenameAgent } from './use-rename-agent'; import { useRenameAgent } from './use-rename-agent';
export default function Agents() { export default function Agents() {
@ -25,6 +33,12 @@ export default function Agents() {
showAgentRenameModal, showAgentRenameModal,
} = useRenameAgent(); } = useRenameAgent();
const {
visible: creatingVisible,
hideModal: hideCreatingModal,
showModal: showCreatingModal,
} = useSetModalState();
const handlePageChange = useCallback( const handlePageChange = useCallback(
(page: number, pageSize?: number) => { (page: number, pageSize?: number) => {
setPagination({ page, pageSize }); setPagination({ page, pageSize });
@ -41,10 +55,34 @@ export default function Agents() {
onSearchChange={handleInputChange} onSearchChange={handleInputChange}
icon="agent" icon="agent"
> >
<Button onClick={navigateToAgentTemplates}> <DropdownMenu>
<Plus className="mr-2 h-4 w-4" /> <DropdownMenuTrigger>
{t('flow.createGraph')} <Button>
</Button> <Plus className="mr-2 h-4 w-4" />
{t('flow.createGraph')}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem
justifyBetween={false}
onClick={showCreatingModal}
>
<Clipboard />
Create from Blank
</DropdownMenuItem>
<DropdownMenuItem
justifyBetween={false}
onClick={navigateToAgentTemplates}
>
<ClipboardPlus />
Create from Template
</DropdownMenuItem>
<DropdownMenuItem justifyBetween={false}>
<FileInput />
Import json file
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</ListFilterBar> </ListFilterBar>
</div> </div>
<div className="flex-1 overflow-auto"> <div className="flex-1 overflow-auto">
@ -75,6 +113,15 @@ export default function Agents() {
loading={agentRenameLoading} loading={agentRenameLoading}
></RenameDialog> ></RenameDialog>
)} )}
{creatingVisible && (
<CreateAgentDialog
loading={false}
visible={creatingVisible}
hideModal={hideCreatingModal}
shouldChooseAgent
onOk={() => {}}
></CreateAgentDialog>
)}
</section> </section>
); );
} }