mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-19 03:56:42 +08:00
Feature: Memory interface integration testing (#11833)
### What problem does this PR solve? Feature: Memory interface integration testing ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -45,7 +45,7 @@ export function ConfirmDeleteDialog({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
if (hidden) {
|
if (hidden) {
|
||||||
return children;
|
return children || <></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -54,7 +54,7 @@ export function ConfirmDeleteDialog({
|
|||||||
open={open}
|
open={open}
|
||||||
defaultOpen={defaultOpen}
|
defaultOpen={defaultOpen}
|
||||||
>
|
>
|
||||||
<AlertDialogTrigger asChild>{children}</AlertDialogTrigger>
|
{children && <AlertDialogTrigger asChild>{children}</AlertDialogTrigger>}
|
||||||
<AlertDialogOverlay
|
<AlertDialogOverlay
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@ -109,23 +109,28 @@ export function ConfirmDeleteDialog({
|
|||||||
export const ConfirmDeleteDialogNode = ({
|
export const ConfirmDeleteDialogNode = ({
|
||||||
avatar,
|
avatar,
|
||||||
name,
|
name,
|
||||||
|
warnText,
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
avatar?: { avatar?: string; name?: string; isPerson?: boolean };
|
avatar?: { avatar?: string; name?: string; isPerson?: boolean };
|
||||||
name?: string;
|
name?: string;
|
||||||
|
warnText?: string;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center border-0.5 text-text-secondary border-border-button rounded-lg px-3 py-4">
|
<div className="flex flex-col gap-2.5">
|
||||||
{avatar && (
|
<div className="flex items-center border-0.5 text-text-secondary border-border-button rounded-lg px-3 py-4">
|
||||||
<RAGFlowAvatar
|
{avatar && (
|
||||||
className="w-8 h-8"
|
<RAGFlowAvatar
|
||||||
avatar={avatar.avatar}
|
className="w-8 h-8"
|
||||||
isPerson={avatar.isPerson}
|
avatar={avatar.avatar}
|
||||||
name={avatar.name}
|
isPerson={avatar.isPerson}
|
||||||
/>
|
name={avatar.name}
|
||||||
)}
|
/>
|
||||||
{name && <div className="ml-3">{name}</div>}
|
)}
|
||||||
|
{name && <div className="ml-3">{name}</div>}
|
||||||
|
</div>
|
||||||
|
{warnText && <div className="text-state-error text-xs">{warnText}</div>}
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -110,7 +110,7 @@ export interface DynamicFormRef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate Zod validation schema based on field configurations
|
// Generate Zod validation schema based on field configurations
|
||||||
const generateSchema = (fields: FormFieldConfig[]): ZodSchema<any> => {
|
export const generateSchema = (fields: FormFieldConfig[]): ZodSchema<any> => {
|
||||||
const schema: Record<string, ZodSchema> = {};
|
const schema: Record<string, ZodSchema> = {};
|
||||||
const nestedSchemas: Record<string, Record<string, ZodSchema>> = {};
|
const nestedSchemas: Record<string, Record<string, ZodSchema>> = {};
|
||||||
|
|
||||||
@ -311,6 +311,271 @@ const generateDefaultValues = <T extends FieldValues>(
|
|||||||
|
|
||||||
return defaultValues as DefaultValues<T>;
|
return defaultValues as DefaultValues<T>;
|
||||||
};
|
};
|
||||||
|
// Render form fields
|
||||||
|
export const RenderField = ({
|
||||||
|
field,
|
||||||
|
labelClassName,
|
||||||
|
}: {
|
||||||
|
field: FormFieldConfig;
|
||||||
|
labelClassName?: string;
|
||||||
|
}) => {
|
||||||
|
const form = useFormContext();
|
||||||
|
if (field.render) {
|
||||||
|
if (field.type === FormFieldType.Custom && field.hideLabel) {
|
||||||
|
return <div className="w-full">{field.render({})}</div>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<RAGFlowFormItem
|
||||||
|
name={field.name}
|
||||||
|
label={field.label}
|
||||||
|
required={field.required}
|
||||||
|
horizontal={field.horizontal}
|
||||||
|
tooltip={field.tooltip}
|
||||||
|
labelClassName={labelClassName || field.labelClassName}
|
||||||
|
>
|
||||||
|
{(fieldProps) => {
|
||||||
|
const finalFieldProps = field.onChange
|
||||||
|
? {
|
||||||
|
...fieldProps,
|
||||||
|
onChange: (e: any) => {
|
||||||
|
fieldProps.onChange(e);
|
||||||
|
field.onChange?.(e.target?.value ?? e);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: fieldProps;
|
||||||
|
return field.render?.(finalFieldProps);
|
||||||
|
}}
|
||||||
|
</RAGFlowFormItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
switch (field.type) {
|
||||||
|
case FormFieldType.Textarea:
|
||||||
|
return (
|
||||||
|
<RAGFlowFormItem
|
||||||
|
name={field.name}
|
||||||
|
label={field.label}
|
||||||
|
required={field.required}
|
||||||
|
horizontal={field.horizontal}
|
||||||
|
tooltip={field.tooltip}
|
||||||
|
labelClassName={labelClassName || field.labelClassName}
|
||||||
|
>
|
||||||
|
{(fieldProps) => {
|
||||||
|
const finalFieldProps = field.onChange
|
||||||
|
? {
|
||||||
|
...fieldProps,
|
||||||
|
onChange: (e: any) => {
|
||||||
|
fieldProps.onChange(e);
|
||||||
|
field.onChange?.(e.target.value);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: fieldProps;
|
||||||
|
return (
|
||||||
|
<Textarea
|
||||||
|
{...finalFieldProps}
|
||||||
|
placeholder={field.placeholder}
|
||||||
|
// className="resize-none"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</RAGFlowFormItem>
|
||||||
|
);
|
||||||
|
|
||||||
|
case FormFieldType.Select:
|
||||||
|
return (
|
||||||
|
<RAGFlowFormItem
|
||||||
|
name={field.name}
|
||||||
|
label={field.label}
|
||||||
|
required={field.required}
|
||||||
|
horizontal={field.horizontal}
|
||||||
|
tooltip={field.tooltip}
|
||||||
|
labelClassName={labelClassName || field.labelClassName}
|
||||||
|
>
|
||||||
|
{(fieldProps) => {
|
||||||
|
const finalFieldProps = field.onChange
|
||||||
|
? {
|
||||||
|
...fieldProps,
|
||||||
|
onChange: (value: string) => {
|
||||||
|
console.log('select value', value);
|
||||||
|
if (fieldProps.onChange) {
|
||||||
|
fieldProps.onChange(value);
|
||||||
|
}
|
||||||
|
field.onChange?.(value);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: fieldProps;
|
||||||
|
return (
|
||||||
|
<SelectWithSearch
|
||||||
|
triggerClassName="!shrink"
|
||||||
|
{...finalFieldProps}
|
||||||
|
options={field.options}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</RAGFlowFormItem>
|
||||||
|
);
|
||||||
|
|
||||||
|
case FormFieldType.MultiSelect:
|
||||||
|
return (
|
||||||
|
<RAGFlowFormItem
|
||||||
|
name={field.name}
|
||||||
|
label={field.label}
|
||||||
|
required={field.required}
|
||||||
|
horizontal={field.horizontal}
|
||||||
|
tooltip={field.tooltip}
|
||||||
|
labelClassName={labelClassName || field.labelClassName}
|
||||||
|
>
|
||||||
|
{(fieldProps) => {
|
||||||
|
console.log('multi select value', fieldProps);
|
||||||
|
const finalFieldProps = {
|
||||||
|
...fieldProps,
|
||||||
|
onValueChange: (value: string[]) => {
|
||||||
|
if (fieldProps.onChange) {
|
||||||
|
fieldProps.onChange(value);
|
||||||
|
}
|
||||||
|
field.onChange?.(value);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<MultiSelect
|
||||||
|
variant="inverted"
|
||||||
|
maxCount={100}
|
||||||
|
{...finalFieldProps}
|
||||||
|
// onValueChange={(data) => {
|
||||||
|
// console.log(data);
|
||||||
|
// field.onChange?.(data);
|
||||||
|
// }}
|
||||||
|
options={field.options as MultiSelectOptionType[]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</RAGFlowFormItem>
|
||||||
|
);
|
||||||
|
|
||||||
|
case FormFieldType.Checkbox:
|
||||||
|
return (
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name={field.name as any}
|
||||||
|
render={({ field: formField }) => (
|
||||||
|
<FormItem
|
||||||
|
className={cn('flex items-center w-full', {
|
||||||
|
'flex-row items-center space-x-3 space-y-0': !field.horizontal,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{field.label && !field.horizontal && (
|
||||||
|
<div className="space-y-1 leading-none">
|
||||||
|
<FormLabel
|
||||||
|
className={cn(
|
||||||
|
'font-medium',
|
||||||
|
labelClassName || field.labelClassName,
|
||||||
|
)}
|
||||||
|
tooltip={field.tooltip}
|
||||||
|
>
|
||||||
|
{field.label}{' '}
|
||||||
|
{field.required && (
|
||||||
|
<span className="text-destructive">*</span>
|
||||||
|
)}
|
||||||
|
</FormLabel>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{field.label && field.horizontal && (
|
||||||
|
<div className="space-y-1 leading-none w-1/4">
|
||||||
|
<FormLabel
|
||||||
|
className={cn(
|
||||||
|
'font-medium',
|
||||||
|
labelClassName || field.labelClassName,
|
||||||
|
)}
|
||||||
|
tooltip={field.tooltip}
|
||||||
|
>
|
||||||
|
{field.label}{' '}
|
||||||
|
{field.required && (
|
||||||
|
<span className="text-destructive">*</span>
|
||||||
|
)}
|
||||||
|
</FormLabel>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<FormControl>
|
||||||
|
<div className={cn({ 'w-full': field.horizontal })}>
|
||||||
|
<Checkbox
|
||||||
|
checked={formField.value}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
formField.onChange(checked);
|
||||||
|
field.onChange?.(checked);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
case FormFieldType.Tag:
|
||||||
|
return (
|
||||||
|
<RAGFlowFormItem
|
||||||
|
name={field.name}
|
||||||
|
label={field.label}
|
||||||
|
required={field.required}
|
||||||
|
horizontal={field.horizontal}
|
||||||
|
tooltip={field.tooltip}
|
||||||
|
labelClassName={labelClassName || field.labelClassName}
|
||||||
|
>
|
||||||
|
{(fieldProps) => {
|
||||||
|
const finalFieldProps = field.onChange
|
||||||
|
? {
|
||||||
|
...fieldProps,
|
||||||
|
onChange: (value: string[]) => {
|
||||||
|
fieldProps.onChange(value);
|
||||||
|
field.onChange?.(value);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: fieldProps;
|
||||||
|
return (
|
||||||
|
// <TagInput {...fieldProps} placeholder={field.placeholder} />
|
||||||
|
<div className="w-full">
|
||||||
|
<EditTag {...finalFieldProps}></EditTag>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</RAGFlowFormItem>
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<RAGFlowFormItem
|
||||||
|
name={field.name}
|
||||||
|
label={field.label}
|
||||||
|
required={field.required}
|
||||||
|
horizontal={field.horizontal}
|
||||||
|
tooltip={field.tooltip}
|
||||||
|
labelClassName={labelClassName || field.labelClassName}
|
||||||
|
>
|
||||||
|
{(fieldProps) => {
|
||||||
|
const finalFieldProps = field.onChange
|
||||||
|
? {
|
||||||
|
...fieldProps,
|
||||||
|
onChange: (e: any) => {
|
||||||
|
fieldProps.onChange(e);
|
||||||
|
field.onChange?.(e.target.value);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: fieldProps;
|
||||||
|
return (
|
||||||
|
<div className="w-full">
|
||||||
|
<Input
|
||||||
|
{...finalFieldProps}
|
||||||
|
type={field.type}
|
||||||
|
placeholder={field.placeholder}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</RAGFlowFormItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Dynamic form component
|
// Dynamic form component
|
||||||
const DynamicForm = {
|
const DynamicForm = {
|
||||||
@ -497,266 +762,6 @@ const DynamicForm = {
|
|||||||
// Submit handler
|
// Submit handler
|
||||||
// const handleSubmit = form.handleSubmit(onSubmit);
|
// const handleSubmit = form.handleSubmit(onSubmit);
|
||||||
|
|
||||||
// Render form fields
|
|
||||||
const renderField = (field: FormFieldConfig) => {
|
|
||||||
if (field.render) {
|
|
||||||
if (field.type === FormFieldType.Custom && field.hideLabel) {
|
|
||||||
return <div className="w-full">{field.render({})}</div>;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<RAGFlowFormItem
|
|
||||||
name={field.name}
|
|
||||||
label={field.label}
|
|
||||||
required={field.required}
|
|
||||||
horizontal={field.horizontal}
|
|
||||||
tooltip={field.tooltip}
|
|
||||||
labelClassName={labelClassName || field.labelClassName}
|
|
||||||
>
|
|
||||||
{(fieldProps) => {
|
|
||||||
const finalFieldProps = field.onChange
|
|
||||||
? {
|
|
||||||
...fieldProps,
|
|
||||||
onChange: (e: any) => {
|
|
||||||
fieldProps.onChange(e);
|
|
||||||
field.onChange?.(e.target?.value ?? e);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: fieldProps;
|
|
||||||
return field.render?.(finalFieldProps);
|
|
||||||
}}
|
|
||||||
</RAGFlowFormItem>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
switch (field.type) {
|
|
||||||
case FormFieldType.Textarea:
|
|
||||||
return (
|
|
||||||
<RAGFlowFormItem
|
|
||||||
name={field.name}
|
|
||||||
label={field.label}
|
|
||||||
required={field.required}
|
|
||||||
horizontal={field.horizontal}
|
|
||||||
tooltip={field.tooltip}
|
|
||||||
labelClassName={labelClassName || field.labelClassName}
|
|
||||||
>
|
|
||||||
{(fieldProps) => {
|
|
||||||
const finalFieldProps = field.onChange
|
|
||||||
? {
|
|
||||||
...fieldProps,
|
|
||||||
onChange: (e: any) => {
|
|
||||||
fieldProps.onChange(e);
|
|
||||||
field.onChange?.(e.target.value);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: fieldProps;
|
|
||||||
return (
|
|
||||||
<Textarea
|
|
||||||
{...finalFieldProps}
|
|
||||||
placeholder={field.placeholder}
|
|
||||||
// className="resize-none"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</RAGFlowFormItem>
|
|
||||||
);
|
|
||||||
|
|
||||||
case FormFieldType.Select:
|
|
||||||
return (
|
|
||||||
<RAGFlowFormItem
|
|
||||||
name={field.name}
|
|
||||||
label={field.label}
|
|
||||||
required={field.required}
|
|
||||||
horizontal={field.horizontal}
|
|
||||||
tooltip={field.tooltip}
|
|
||||||
labelClassName={labelClassName || field.labelClassName}
|
|
||||||
>
|
|
||||||
{(fieldProps) => {
|
|
||||||
const finalFieldProps = field.onChange
|
|
||||||
? {
|
|
||||||
...fieldProps,
|
|
||||||
onChange: (value: string) => {
|
|
||||||
console.log('select value', value);
|
|
||||||
if (fieldProps.onChange) {
|
|
||||||
fieldProps.onChange(value);
|
|
||||||
}
|
|
||||||
field.onChange?.(value);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: fieldProps;
|
|
||||||
return (
|
|
||||||
<SelectWithSearch
|
|
||||||
triggerClassName="!shrink"
|
|
||||||
{...finalFieldProps}
|
|
||||||
options={field.options}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</RAGFlowFormItem>
|
|
||||||
);
|
|
||||||
|
|
||||||
case FormFieldType.MultiSelect:
|
|
||||||
return (
|
|
||||||
<RAGFlowFormItem
|
|
||||||
name={field.name}
|
|
||||||
label={field.label}
|
|
||||||
required={field.required}
|
|
||||||
horizontal={field.horizontal}
|
|
||||||
tooltip={field.tooltip}
|
|
||||||
labelClassName={labelClassName || field.labelClassName}
|
|
||||||
>
|
|
||||||
{(fieldProps) => {
|
|
||||||
console.log('multi select value', fieldProps);
|
|
||||||
const finalFieldProps = {
|
|
||||||
...fieldProps,
|
|
||||||
onValueChange: (value: string[]) => {
|
|
||||||
if (fieldProps.onChange) {
|
|
||||||
fieldProps.onChange(value);
|
|
||||||
}
|
|
||||||
field.onChange?.(value);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<MultiSelect
|
|
||||||
variant="inverted"
|
|
||||||
maxCount={100}
|
|
||||||
{...finalFieldProps}
|
|
||||||
// onValueChange={(data) => {
|
|
||||||
// console.log(data);
|
|
||||||
// field.onChange?.(data);
|
|
||||||
// }}
|
|
||||||
options={field.options as MultiSelectOptionType[]}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</RAGFlowFormItem>
|
|
||||||
);
|
|
||||||
|
|
||||||
case FormFieldType.Checkbox:
|
|
||||||
return (
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name={field.name as any}
|
|
||||||
render={({ field: formField }) => (
|
|
||||||
<FormItem
|
|
||||||
className={cn('flex items-center w-full', {
|
|
||||||
'flex-row items-center space-x-3 space-y-0':
|
|
||||||
!field.horizontal,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{field.label && !field.horizontal && (
|
|
||||||
<div className="space-y-1 leading-none">
|
|
||||||
<FormLabel
|
|
||||||
className={cn(
|
|
||||||
'font-medium',
|
|
||||||
labelClassName || field.labelClassName,
|
|
||||||
)}
|
|
||||||
tooltip={field.tooltip}
|
|
||||||
>
|
|
||||||
{field.label}{' '}
|
|
||||||
{field.required && (
|
|
||||||
<span className="text-destructive">*</span>
|
|
||||||
)}
|
|
||||||
</FormLabel>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{field.label && field.horizontal && (
|
|
||||||
<div className="space-y-1 leading-none w-1/4">
|
|
||||||
<FormLabel
|
|
||||||
className={cn(
|
|
||||||
'font-medium',
|
|
||||||
labelClassName || field.labelClassName,
|
|
||||||
)}
|
|
||||||
tooltip={field.tooltip}
|
|
||||||
>
|
|
||||||
{field.label}{' '}
|
|
||||||
{field.required && (
|
|
||||||
<span className="text-destructive">*</span>
|
|
||||||
)}
|
|
||||||
</FormLabel>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<FormControl>
|
|
||||||
<div className={cn({ 'w-full': field.horizontal })}>
|
|
||||||
<Checkbox
|
|
||||||
checked={formField.value}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
formField.onChange(checked);
|
|
||||||
field.onChange?.(checked);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
case FormFieldType.Tag:
|
|
||||||
return (
|
|
||||||
<RAGFlowFormItem
|
|
||||||
name={field.name}
|
|
||||||
label={field.label}
|
|
||||||
required={field.required}
|
|
||||||
horizontal={field.horizontal}
|
|
||||||
tooltip={field.tooltip}
|
|
||||||
labelClassName={labelClassName || field.labelClassName}
|
|
||||||
>
|
|
||||||
{(fieldProps) => {
|
|
||||||
const finalFieldProps = field.onChange
|
|
||||||
? {
|
|
||||||
...fieldProps,
|
|
||||||
onChange: (value: string[]) => {
|
|
||||||
fieldProps.onChange(value);
|
|
||||||
field.onChange?.(value);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: fieldProps;
|
|
||||||
return (
|
|
||||||
// <TagInput {...fieldProps} placeholder={field.placeholder} />
|
|
||||||
<div className="w-full">
|
|
||||||
<EditTag {...finalFieldProps}></EditTag>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</RAGFlowFormItem>
|
|
||||||
);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
<RAGFlowFormItem
|
|
||||||
name={field.name}
|
|
||||||
label={field.label}
|
|
||||||
required={field.required}
|
|
||||||
horizontal={field.horizontal}
|
|
||||||
tooltip={field.tooltip}
|
|
||||||
labelClassName={labelClassName || field.labelClassName}
|
|
||||||
>
|
|
||||||
{(fieldProps) => {
|
|
||||||
const finalFieldProps = field.onChange
|
|
||||||
? {
|
|
||||||
...fieldProps,
|
|
||||||
onChange: (e: any) => {
|
|
||||||
fieldProps.onChange(e);
|
|
||||||
field.onChange?.(e.target.value);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: fieldProps;
|
|
||||||
return (
|
|
||||||
<div className="w-full">
|
|
||||||
<Input
|
|
||||||
{...finalFieldProps}
|
|
||||||
type={field.type}
|
|
||||||
placeholder={field.placeholder}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</RAGFlowFormItem>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Watch all form values to re-render when they change (for shouldRender checks)
|
// Watch all form values to re-render when they change (for shouldRender checks)
|
||||||
const formValues = form.watch();
|
const formValues = form.watch();
|
||||||
|
|
||||||
@ -779,7 +784,10 @@ const DynamicForm = {
|
|||||||
key={field.name}
|
key={field.name}
|
||||||
className={cn({ hidden: field.hidden || !shouldShow })}
|
className={cn({ hidden: field.hidden || !shouldShow })}
|
||||||
>
|
>
|
||||||
{renderField(field)}
|
<RenderField
|
||||||
|
field={field}
|
||||||
|
labelClassName={labelClassName}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@ -798,7 +806,7 @@ const DynamicForm = {
|
|||||||
buttonText,
|
buttonText,
|
||||||
submitFunc,
|
submitFunc,
|
||||||
}: {
|
}: {
|
||||||
submitLoading: boolean;
|
submitLoading?: boolean;
|
||||||
buttonText?: string;
|
buttonText?: string;
|
||||||
submitFunc?: (values: FieldValues) => void;
|
submitFunc?: (values: FieldValues) => void;
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
@ -53,7 +53,12 @@ export function RAGFlowFormItem({
|
|||||||
{label}
|
{label}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
)}
|
)}
|
||||||
<div className="w-full flex flex-col">
|
<div
|
||||||
|
className={cn('flex flex-col', {
|
||||||
|
'w-3/4': horizontal,
|
||||||
|
'w-full': !horizontal,
|
||||||
|
})}
|
||||||
|
>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
{typeof children === 'function'
|
{typeof children === 'function'
|
||||||
? children(field)
|
? children(field)
|
||||||
|
|||||||
@ -70,6 +70,7 @@ export function Header() {
|
|||||||
{ path: Routes.Chats, name: t('header.chat'), icon: MessageSquareText },
|
{ path: Routes.Chats, name: t('header.chat'), icon: MessageSquareText },
|
||||||
{ path: Routes.Searches, name: t('header.search'), icon: Search },
|
{ path: Routes.Searches, name: t('header.search'), icon: Search },
|
||||||
{ path: Routes.Agents, name: t('header.flow'), icon: Cpu },
|
{ path: Routes.Agents, name: t('header.flow'), icon: Cpu },
|
||||||
|
// { path: Routes.Memories, name: t('header.Memories'), icon: Cpu },
|
||||||
{ path: Routes.Files, name: t('header.fileManager'), icon: File },
|
{ path: Routes.Files, name: t('header.fileManager'), icon: File },
|
||||||
],
|
],
|
||||||
[t],
|
[t],
|
||||||
|
|||||||
@ -101,7 +101,7 @@ export default {
|
|||||||
dataset: 'Dataset',
|
dataset: 'Dataset',
|
||||||
Memories: 'Memory',
|
Memories: 'Memory',
|
||||||
},
|
},
|
||||||
memory: {
|
memories: {
|
||||||
memory: 'Memory',
|
memory: 'Memory',
|
||||||
createMemory: 'Create Memory',
|
createMemory: 'Create Memory',
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
@ -110,9 +110,15 @@ export default {
|
|||||||
embeddingModel: 'Embedding model',
|
embeddingModel: 'Embedding model',
|
||||||
selectModel: 'Select model',
|
selectModel: 'Select model',
|
||||||
llm: 'LLM',
|
llm: 'LLM',
|
||||||
|
delMemoryWarn: `After deletion, all messages in this memory will be deleted and cannot be retrieved by agents.`,
|
||||||
},
|
},
|
||||||
memoryDetail: {
|
memory: {
|
||||||
messages: {
|
messages: {
|
||||||
|
copied: 'Copied!',
|
||||||
|
contentEmbed: 'Content embed',
|
||||||
|
content: 'Content',
|
||||||
|
delMessageWarn: `After forgetting, this message will not be retrieved by agents.`,
|
||||||
|
forgetMessage: 'Forget message',
|
||||||
sessionId: 'Session ID',
|
sessionId: 'Session ID',
|
||||||
agent: 'Agent',
|
agent: 'Agent',
|
||||||
type: 'Type',
|
type: 'Type',
|
||||||
@ -122,6 +128,27 @@ export default {
|
|||||||
enable: 'Enable',
|
enable: 'Enable',
|
||||||
action: 'Action',
|
action: 'Action',
|
||||||
},
|
},
|
||||||
|
config: {
|
||||||
|
avatar: 'Avatar',
|
||||||
|
description: 'Description',
|
||||||
|
memorySize: 'Memory size',
|
||||||
|
advancedSettings: 'Advanced Settings',
|
||||||
|
permission: 'Permission',
|
||||||
|
onlyMe: 'Only Me',
|
||||||
|
team: 'Team',
|
||||||
|
storageType: 'Storage Type',
|
||||||
|
storageTypePlaceholder: 'Please select storage type',
|
||||||
|
forgetPolicy: 'Forget Policy',
|
||||||
|
temperature: 'Temperature',
|
||||||
|
systemPrompt: 'System Prompt',
|
||||||
|
systemPromptPlaceholder: 'Please enter system prompt',
|
||||||
|
userPrompt: 'User Prompt',
|
||||||
|
userPromptPlaceholder: 'Please enter user prompt',
|
||||||
|
},
|
||||||
|
sideBar: {
|
||||||
|
messages: 'Messages',
|
||||||
|
configuration: 'Configuration',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
knowledgeList: {
|
knowledgeList: {
|
||||||
welcome: 'Welcome back',
|
welcome: 'Welcome back',
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
||||||
|
import { SliderInputFormField } from '@/components/slider-input-form-field';
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormField,
|
FormField,
|
||||||
@ -11,6 +12,7 @@ import { Spin } from '@/components/ui/spin';
|
|||||||
import { Switch } from '@/components/ui/switch';
|
import { Switch } from '@/components/ui/switch';
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
import { useTranslate } from '@/hooks/common-hooks';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
import { t } from 'i18next';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { FieldValues, useFormContext } from 'react-hook-form';
|
import { FieldValues, useFormContext } from 'react-hook-form';
|
||||||
import {
|
import {
|
||||||
@ -285,3 +287,14 @@ export function EnableTocToggle() {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function OverlappedPercent() {
|
||||||
|
return (
|
||||||
|
<SliderInputFormField
|
||||||
|
name="parser_config.overlapped_percent"
|
||||||
|
label={t('flow.filenameEmbeddingWeight')}
|
||||||
|
max={0.5}
|
||||||
|
step={0.01}
|
||||||
|
></SliderInputFormField>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import {
|
|||||||
ConfigurationFormContainer,
|
ConfigurationFormContainer,
|
||||||
MainContainer,
|
MainContainer,
|
||||||
} from '../configuration-form-container';
|
} from '../configuration-form-container';
|
||||||
import { EnableTocToggle } from './common-item';
|
import { EnableTocToggle, OverlappedPercent } from './common-item';
|
||||||
|
|
||||||
export function NaiveConfiguration() {
|
export function NaiveConfiguration() {
|
||||||
return (
|
return (
|
||||||
@ -20,6 +20,7 @@ export function NaiveConfiguration() {
|
|||||||
<MaxTokenNumberFormField initialValue={512}></MaxTokenNumberFormField>
|
<MaxTokenNumberFormField initialValue={512}></MaxTokenNumberFormField>
|
||||||
<DelimiterFormField></DelimiterFormField>
|
<DelimiterFormField></DelimiterFormField>
|
||||||
<EnableTocToggle />
|
<EnableTocToggle />
|
||||||
|
<OverlappedPercent />
|
||||||
</ConfigurationFormContainer>
|
</ConfigurationFormContainer>
|
||||||
<ConfigurationFormContainer>
|
<ConfigurationFormContainer>
|
||||||
<AutoKeywordsFormField></AutoKeywordsFormField>
|
<AutoKeywordsFormField></AutoKeywordsFormField>
|
||||||
|
|||||||
@ -29,6 +29,7 @@ export const formSchema = z
|
|||||||
tag_kb_ids: z.array(z.string()).nullish(),
|
tag_kb_ids: z.array(z.string()).nullish(),
|
||||||
topn_tags: z.number().optional(),
|
topn_tags: z.number().optional(),
|
||||||
toc_extraction: z.boolean().optional(),
|
toc_extraction: z.boolean().optional(),
|
||||||
|
overlapped_percent: z.number().optional(),
|
||||||
raptor: z
|
raptor: z
|
||||||
.object({
|
.object({
|
||||||
use_raptor: z.boolean().optional(),
|
use_raptor: z.boolean().optional(),
|
||||||
|
|||||||
@ -67,6 +67,7 @@ export default function DatasetSettings() {
|
|||||||
html4excel: false,
|
html4excel: false,
|
||||||
topn_tags: 3,
|
topn_tags: 3,
|
||||||
toc_extraction: false,
|
toc_extraction: false,
|
||||||
|
overlapped_percent: 0,
|
||||||
raptor: {
|
raptor: {
|
||||||
use_raptor: true,
|
use_raptor: true,
|
||||||
max_token: 256,
|
max_token: 256,
|
||||||
|
|||||||
@ -14,16 +14,18 @@ export function PermissionFormField() {
|
|||||||
}, [t]);
|
}, [t]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RAGFlowFormItem
|
<div className="items-center">
|
||||||
name="permission"
|
<RAGFlowFormItem
|
||||||
label={t('knowledgeConfiguration.permissions')}
|
name="permission"
|
||||||
tooltip={t('knowledgeConfiguration.permissionsTip')}
|
label={t('knowledgeConfiguration.permissions')}
|
||||||
horizontal
|
tooltip={t('knowledgeConfiguration.permissionsTip')}
|
||||||
>
|
horizontal={true}
|
||||||
<SelectWithSearch
|
>
|
||||||
options={teamOptions}
|
<SelectWithSearch
|
||||||
triggerClassName="w-3/4"
|
options={teamOptions}
|
||||||
></SelectWithSearch>
|
triggerClassName="w-full"
|
||||||
</RAGFlowFormItem>
|
></SelectWithSearch>
|
||||||
|
</RAGFlowFormItem>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { useModelOptions } from '@/components/llm-setting-items/llm-form-field';
|
|||||||
import { HomeIcon } from '@/components/svg-icon';
|
import { HomeIcon } from '@/components/svg-icon';
|
||||||
import { Modal } from '@/components/ui/modal/modal';
|
import { Modal } from '@/components/ui/modal/modal';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { memo, useCallback, useMemo, useState } from 'react';
|
||||||
import { createMemoryFields } from './constants';
|
import { createMemoryFields } from './constants';
|
||||||
import { IMemory } from './interface';
|
import { IMemory } from './interface';
|
||||||
|
|
||||||
@ -13,11 +13,10 @@ type IProps = {
|
|||||||
onSubmit?: (data: any) => void;
|
onSubmit?: (data: any) => void;
|
||||||
initialMemory: IMemory;
|
initialMemory: IMemory;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
|
isCreate?: boolean;
|
||||||
};
|
};
|
||||||
export const AddOrEditModal = (props: IProps) => {
|
export const AddOrEditModal = memo((props: IProps) => {
|
||||||
const { open, onClose, onSubmit, initialMemory } = props;
|
const { open, onClose, onSubmit, initialMemory, isCreate } = props;
|
||||||
// const [fields, setFields] = useState<FormFieldConfig[]>(createMemoryFields);
|
|
||||||
// const formRef = useRef<DynamicFormRef>(null);
|
|
||||||
const [formInstance, setFormInstance] = useState<DynamicFormRef | null>(null);
|
const [formInstance, setFormInstance] = useState<DynamicFormRef | null>(null);
|
||||||
|
|
||||||
const formCallbackRef = useCallback((node: DynamicFormRef | null) => {
|
const formCallbackRef = useCallback((node: DynamicFormRef | null) => {
|
||||||
@ -28,15 +27,25 @@ export const AddOrEditModal = (props: IProps) => {
|
|||||||
}, []);
|
}, []);
|
||||||
const { modelOptions } = useModelOptions();
|
const { modelOptions } = useModelOptions();
|
||||||
|
|
||||||
useEffect(() => {
|
const fields = useMemo(() => {
|
||||||
if (initialMemory && initialMemory.id) {
|
if (!isCreate) {
|
||||||
formInstance?.onFieldUpdate('memory_type', { hidden: true });
|
return createMemoryFields.filter((field: any) => field.name === 'name');
|
||||||
formInstance?.onFieldUpdate('embedding', { hidden: true });
|
|
||||||
formInstance?.onFieldUpdate('llm', { hidden: true });
|
|
||||||
} else {
|
} else {
|
||||||
formInstance?.onFieldUpdate('llm', { options: modelOptions as any });
|
const tempFields = createMemoryFields.map((field: any) => {
|
||||||
|
if (field.name === 'llm_id') {
|
||||||
|
return {
|
||||||
|
...field,
|
||||||
|
options: modelOptions,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
...field,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return tempFields;
|
||||||
}
|
}
|
||||||
}, [modelOptions, formInstance, initialMemory]);
|
}, [modelOptions, isCreate]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@ -48,7 +57,7 @@ export const AddOrEditModal = (props: IProps) => {
|
|||||||
<div>
|
<div>
|
||||||
<HomeIcon name="memory" width={'24'} />
|
<HomeIcon name="memory" width={'24'} />
|
||||||
</div>
|
</div>
|
||||||
{t('memory.createMemory')}
|
{t('memories.createMemory')}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
showfooter={false}
|
showfooter={false}
|
||||||
@ -56,7 +65,7 @@ export const AddOrEditModal = (props: IProps) => {
|
|||||||
>
|
>
|
||||||
<DynamicForm.Root
|
<DynamicForm.Root
|
||||||
ref={formCallbackRef}
|
ref={formCallbackRef}
|
||||||
fields={createMemoryFields}
|
fields={fields}
|
||||||
onSubmit={() => {}}
|
onSubmit={() => {}}
|
||||||
defaultValues={initialMemory}
|
defaultValues={initialMemory}
|
||||||
>
|
>
|
||||||
@ -72,4 +81,4 @@ export const AddOrEditModal = (props: IProps) => {
|
|||||||
</DynamicForm.Root>
|
</DynamicForm.Root>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|||||||
@ -4,16 +4,16 @@ import { t } from 'i18next';
|
|||||||
|
|
||||||
export const createMemoryFields = [
|
export const createMemoryFields = [
|
||||||
{
|
{
|
||||||
name: 'memory_name',
|
name: 'name',
|
||||||
label: t('memory.name'),
|
label: t('memories.name'),
|
||||||
placeholder: t('memory.memoryNamePlaceholder'),
|
placeholder: t('memories.memoryNamePlaceholder'),
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'memory_type',
|
name: 'memory_type',
|
||||||
label: t('memory.memoryType'),
|
label: t('memories.memoryType'),
|
||||||
type: FormFieldType.MultiSelect,
|
type: FormFieldType.MultiSelect,
|
||||||
placeholder: t('memory.descriptionPlaceholder'),
|
placeholder: t('memories.descriptionPlaceholder'),
|
||||||
options: [
|
options: [
|
||||||
{ label: 'Raw', value: 'raw' },
|
{ label: 'Raw', value: 'raw' },
|
||||||
{ label: 'Semantic', value: 'semantic' },
|
{ label: 'Semantic', value: 'semantic' },
|
||||||
@ -23,18 +23,18 @@ export const createMemoryFields = [
|
|||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'embedding',
|
name: 'embd_id',
|
||||||
label: t('memory.embeddingModel'),
|
label: t('memories.embeddingModel'),
|
||||||
placeholder: t('memory.selectModel'),
|
placeholder: t('memories.selectModel'),
|
||||||
required: true,
|
required: true,
|
||||||
// hideLabel: true,
|
// hideLabel: true,
|
||||||
// type: 'custom',
|
// type: 'custom',
|
||||||
render: (field) => <EmbeddingSelect field={field} isEdit={false} />,
|
render: (field) => <EmbeddingSelect field={field} isEdit={false} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'llm',
|
name: 'llm_id',
|
||||||
label: t('memory.llm'),
|
label: t('memories.llm'),
|
||||||
placeholder: t('memory.selectModel'),
|
placeholder: t('memories.selectModel'),
|
||||||
required: true,
|
required: true,
|
||||||
type: FormFieldType.Select,
|
type: FormFieldType.Select,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
|||||||
import memoryService, { updateMemoryById } from '@/services/memory-service';
|
import memoryService, { updateMemoryById } from '@/services/memory-service';
|
||||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
import { useDebounce } from 'ahooks';
|
import { useDebounce } from 'ahooks';
|
||||||
|
import { omit } from 'lodash';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useParams, useSearchParams } from 'umi';
|
import { useParams, useSearchParams } from 'umi';
|
||||||
@ -73,12 +74,12 @@ export const useFetchMemoryList = () => {
|
|||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const { data: response } = await memoryService.getMemoryList(
|
const { data: response } = await memoryService.getMemoryList(
|
||||||
{
|
{
|
||||||
params: {
|
data: {
|
||||||
keywords: debouncedSearchString,
|
keywords: debouncedSearchString,
|
||||||
page_size: pagination.pageSize,
|
page_size: pagination.pageSize,
|
||||||
page: pagination.current,
|
page: pagination.current,
|
||||||
},
|
},
|
||||||
data: {},
|
params: {},
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
@ -153,7 +154,9 @@ export const useDeleteMemory = () => {
|
|||||||
} = useMutation<DeleteMemoryResponse, Error, DeleteMemoryProps>({
|
} = useMutation<DeleteMemoryResponse, Error, DeleteMemoryProps>({
|
||||||
mutationKey: ['deleteMemory'],
|
mutationKey: ['deleteMemory'],
|
||||||
mutationFn: async (props) => {
|
mutationFn: async (props) => {
|
||||||
const { data: response } = await memoryService.deleteMemory(props);
|
const { data: response } = await memoryService.deleteMemory(
|
||||||
|
props.memory_id,
|
||||||
|
);
|
||||||
if (response.code !== 0) {
|
if (response.code !== 0) {
|
||||||
throw new Error(response.message || 'Failed to delete memory');
|
throw new Error(response.message || 'Failed to delete memory');
|
||||||
}
|
}
|
||||||
@ -189,7 +192,8 @@ export const useUpdateMemory = () => {
|
|||||||
} = useMutation<any, Error, IMemoryAppDetailProps>({
|
} = useMutation<any, Error, IMemoryAppDetailProps>({
|
||||||
mutationKey: ['updateMemory'],
|
mutationKey: ['updateMemory'],
|
||||||
mutationFn: async (formData) => {
|
mutationFn: async (formData) => {
|
||||||
const { data: response } = await updateMemoryById(formData.id, formData);
|
const param = omit(formData, ['id']);
|
||||||
|
const { data: response } = await updateMemoryById(formData.id, param);
|
||||||
if (response.code !== 0) {
|
if (response.code !== 0) {
|
||||||
throw new Error(response.message || 'Failed to update memory');
|
throw new Error(response.message || 'Failed to update memory');
|
||||||
}
|
}
|
||||||
@ -259,7 +263,7 @@ export const useRenameMemory = () => {
|
|||||||
// const { id, created_by, update_time, ...memoryDataTemp } = detail;
|
// const { id, created_by, update_time, ...memoryDataTemp } = detail;
|
||||||
res = await updateMemory({
|
res = await updateMemory({
|
||||||
// ...memoryDataTemp,
|
// ...memoryDataTemp,
|
||||||
name: data.memory_name,
|
name: data.name,
|
||||||
id: memory?.id,
|
id: memory?.id,
|
||||||
} as unknown as IMemoryAppDetailProps);
|
} as unknown as IMemoryAppDetailProps);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -268,9 +272,9 @@ export const useRenameMemory = () => {
|
|||||||
} else {
|
} else {
|
||||||
res = await createMemory(data);
|
res = await createMemory(data);
|
||||||
}
|
}
|
||||||
if (res && !memory?.id) {
|
// if (res && !memory?.id) {
|
||||||
navigateToMemory(res?.id)();
|
// navigateToMemory(res?.id)();
|
||||||
}
|
// }
|
||||||
callBack?.();
|
callBack?.();
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
handleHideModal();
|
handleHideModal();
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { RAGFlowPagination } from '@/components/ui/ragflow-pagination';
|
|||||||
import { useTranslate } from '@/hooks/common-hooks';
|
import { useTranslate } from '@/hooks/common-hooks';
|
||||||
import { pick } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
import { Plus } from 'lucide-react';
|
import { Plus } from 'lucide-react';
|
||||||
import { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { useSearchParams } from 'umi';
|
import { useSearchParams } from 'umi';
|
||||||
import { AddOrEditModal } from './add-or-edit-modal';
|
import { AddOrEditModal } from './add-or-edit-modal';
|
||||||
import { useFetchMemoryList, useRenameMemory } from './hooks';
|
import { useFetchMemoryList, useRenameMemory } from './hooks';
|
||||||
@ -16,7 +16,8 @@ import { MemoryCard } from './memory-card';
|
|||||||
|
|
||||||
export default function MemoryList() {
|
export default function MemoryList() {
|
||||||
// const { data } = useFetchFlowList();
|
// const { data } = useFetchFlowList();
|
||||||
const { t } = useTranslate('memory');
|
const { t } = useTranslate('memories');
|
||||||
|
const [addOrEditType, setAddOrEditType] = useState<'add' | 'edit'>('add');
|
||||||
// const [isEdit, setIsEdit] = useState(false);
|
// const [isEdit, setIsEdit] = useState(false);
|
||||||
const {
|
const {
|
||||||
data: list,
|
data: list,
|
||||||
@ -43,6 +44,7 @@ export default function MemoryList() {
|
|||||||
};
|
};
|
||||||
const openCreateModalFun = useCallback(() => {
|
const openCreateModalFun = useCallback(() => {
|
||||||
// setIsEdit(false);
|
// setIsEdit(false);
|
||||||
|
setAddOrEditType('add');
|
||||||
showMemoryRenameModal();
|
showMemoryRenameModal();
|
||||||
}, [showMemoryRenameModal]);
|
}, [showMemoryRenameModal]);
|
||||||
const handlePageChange = useCallback(
|
const handlePageChange = useCallback(
|
||||||
@ -121,6 +123,7 @@ export default function MemoryList() {
|
|||||||
key={x.id}
|
key={x.id}
|
||||||
data={x}
|
data={x}
|
||||||
showMemoryRenameModal={() => {
|
showMemoryRenameModal={() => {
|
||||||
|
setAddOrEditType('edit');
|
||||||
showMemoryRenameModal(x);
|
showMemoryRenameModal(x);
|
||||||
}}
|
}}
|
||||||
></MemoryCard>
|
></MemoryCard>
|
||||||
@ -152,6 +155,7 @@ export default function MemoryList() {
|
|||||||
{openCreateModal && (
|
{openCreateModal && (
|
||||||
<AddOrEditModal
|
<AddOrEditModal
|
||||||
initialMemory={initialMemory}
|
initialMemory={initialMemory}
|
||||||
|
isCreate={addOrEditType === 'add'}
|
||||||
open={openCreateModal}
|
open={openCreateModal}
|
||||||
loading={searchRenameLoading}
|
loading={searchRenameLoading}
|
||||||
onClose={hideMemoryModal}
|
onClose={hideMemoryModal}
|
||||||
|
|||||||
@ -1,10 +1,3 @@
|
|||||||
export interface ICreateMemoryProps {
|
|
||||||
memory_name: string;
|
|
||||||
memory_type: Array<string>;
|
|
||||||
embedding: string;
|
|
||||||
llm: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CreateMemoryResponse {
|
export interface CreateMemoryResponse {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
@ -24,17 +17,18 @@ export type MemoryType = 'raw' | 'semantic' | 'episodic' | 'procedural';
|
|||||||
export type StorageType = 'table' | 'graph';
|
export type StorageType = 'table' | 'graph';
|
||||||
export type Permissions = 'me' | 'team';
|
export type Permissions = 'me' | 'team';
|
||||||
export type ForgettingPolicy = 'fifo' | 'lru';
|
export type ForgettingPolicy = 'fifo' | 'lru';
|
||||||
|
export interface ICreateMemoryProps {
|
||||||
export interface IMemory {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
name: string;
|
||||||
|
memory_type: MemoryType[];
|
||||||
|
embd_id: string;
|
||||||
|
llm_id: string;
|
||||||
|
}
|
||||||
|
export interface IMemory extends ICreateMemoryProps {
|
||||||
|
id: string;
|
||||||
avatar: string;
|
avatar: string;
|
||||||
tenant_id: string;
|
tenant_id: string;
|
||||||
owner_name: string;
|
owner_name: string;
|
||||||
memory_type: MemoryType[];
|
|
||||||
storage_type: StorageType;
|
storage_type: StorageType;
|
||||||
embedding: string;
|
|
||||||
llm: string;
|
|
||||||
permissions: Permissions;
|
permissions: Permissions;
|
||||||
description: string;
|
description: string;
|
||||||
memory_size: number;
|
memory_size: number;
|
||||||
|
|||||||
@ -20,7 +20,7 @@ export function MemoryCard({ data, showMemoryRenameModal }: IProps) {
|
|||||||
}}
|
}}
|
||||||
moreDropdown={
|
moreDropdown={
|
||||||
<MemoryDropdown
|
<MemoryDropdown
|
||||||
dataset={data}
|
memory={data}
|
||||||
showMemoryRenameModal={showMemoryRenameModal}
|
showMemoryRenameModal={showMemoryRenameModal}
|
||||||
>
|
>
|
||||||
<MoreButton></MoreButton>
|
<MoreButton></MoreButton>
|
||||||
|
|||||||
@ -12,15 +12,16 @@ import {
|
|||||||
import { PenLine, Trash2 } from 'lucide-react';
|
import { PenLine, Trash2 } from 'lucide-react';
|
||||||
import { MouseEventHandler, PropsWithChildren, useCallback } from 'react';
|
import { MouseEventHandler, PropsWithChildren, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { IMemoryAppProps, useDeleteMemory } from './hooks';
|
import { useDeleteMemory } from './hooks';
|
||||||
|
import { IMemory } from './interface';
|
||||||
|
|
||||||
export function MemoryDropdown({
|
export function MemoryDropdown({
|
||||||
children,
|
children,
|
||||||
dataset,
|
memory,
|
||||||
showMemoryRenameModal,
|
showMemoryRenameModal,
|
||||||
}: PropsWithChildren & {
|
}: PropsWithChildren & {
|
||||||
dataset: IMemoryAppProps;
|
memory: IMemory;
|
||||||
showMemoryRenameModal: (dataset: IMemoryAppProps) => void;
|
showMemoryRenameModal: (memory: IMemory) => void;
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { deleteMemory } = useDeleteMemory();
|
const { deleteMemory } = useDeleteMemory();
|
||||||
@ -28,13 +29,13 @@ export function MemoryDropdown({
|
|||||||
useCallback(
|
useCallback(
|
||||||
(e) => {
|
(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
showMemoryRenameModal(dataset);
|
showMemoryRenameModal(memory);
|
||||||
},
|
},
|
||||||
[dataset, showMemoryRenameModal],
|
[memory, showMemoryRenameModal],
|
||||||
);
|
);
|
||||||
const handleDelete: MouseEventHandler<HTMLDivElement> = useCallback(() => {
|
const handleDelete: MouseEventHandler<HTMLDivElement> = useCallback(() => {
|
||||||
deleteMemory({ search_id: dataset.id });
|
deleteMemory({ memory_id: memory.id });
|
||||||
}, [dataset.id, deleteMemory]);
|
}, [memory, deleteMemory]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
@ -50,8 +51,9 @@ export function MemoryDropdown({
|
|||||||
content={{
|
content={{
|
||||||
node: (
|
node: (
|
||||||
<ConfirmDeleteDialogNode
|
<ConfirmDeleteDialogNode
|
||||||
avatar={{ avatar: dataset.avatar, name: dataset.name }}
|
avatar={{ avatar: memory.avatar, name: memory.name }}
|
||||||
name={dataset.name}
|
name={memory.name}
|
||||||
|
warnText={t('memories.delMemoryWarn')}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
export enum MemoryApiAction {
|
export enum MemoryApiAction {
|
||||||
FetchMemoryDetail = 'fetchMemoryDetail',
|
FetchMemoryDetail = 'fetchMemoryDetail',
|
||||||
|
FetchMemoryMessage = 'fetchMemoryMessage',
|
||||||
|
FetchMessageContent = 'fetchMessageContent',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,59 +0,0 @@
|
|||||||
import { useHandleSearchChange } from '@/hooks/logic-hooks';
|
|
||||||
import { getMemoryDetailById } from '@/services/memory-service';
|
|
||||||
import { useQuery } from '@tanstack/react-query';
|
|
||||||
import { useParams, useSearchParams } from 'umi';
|
|
||||||
import { MemoryApiAction } from '../constant';
|
|
||||||
import { IMessageTableProps } from '../memory-message/interface';
|
|
||||||
|
|
||||||
export const useFetchMemoryMessageList = (props?: {
|
|
||||||
refreshCount?: number;
|
|
||||||
}) => {
|
|
||||||
const { refreshCount } = props || {};
|
|
||||||
const { id } = useParams();
|
|
||||||
const [searchParams] = useSearchParams();
|
|
||||||
const memoryBaseId = searchParams.get('id') || id;
|
|
||||||
const { handleInputChange, searchString, pagination, setPagination } =
|
|
||||||
useHandleSearchChange();
|
|
||||||
|
|
||||||
let queryKey: (MemoryApiAction | number)[] = [
|
|
||||||
MemoryApiAction.FetchMemoryDetail,
|
|
||||||
];
|
|
||||||
if (typeof refreshCount === 'number') {
|
|
||||||
queryKey = [MemoryApiAction.FetchMemoryDetail, refreshCount];
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data, isFetching: loading } = useQuery<IMessageTableProps>({
|
|
||||||
queryKey: [...queryKey, searchString, pagination],
|
|
||||||
initialData: {} as IMessageTableProps,
|
|
||||||
gcTime: 0,
|
|
||||||
queryFn: async () => {
|
|
||||||
if (memoryBaseId) {
|
|
||||||
const { data } = await getMemoryDetailById(memoryBaseId as string, {
|
|
||||||
// filter: {
|
|
||||||
// agent_id: '',
|
|
||||||
// },
|
|
||||||
keyword: searchString,
|
|
||||||
page: pagination.current,
|
|
||||||
page_size: pagination.pageSize,
|
|
||||||
});
|
|
||||||
// setPagination({
|
|
||||||
// page: data?.page ?? 1,
|
|
||||||
// pageSize: data?.page_size ?? 10,
|
|
||||||
// total: data?.total ?? 0,
|
|
||||||
// });
|
|
||||||
return data?.data ?? {};
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
data,
|
|
||||||
loading,
|
|
||||||
handleInputChange,
|
|
||||||
searchString,
|
|
||||||
pagination,
|
|
||||||
setPagination,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,14 +1,11 @@
|
|||||||
import { useHandleSearchChange } from '@/hooks/logic-hooks';
|
import { useHandleSearchChange } from '@/hooks/logic-hooks';
|
||||||
import { IMemory } from '@/pages/memories/interface';
|
import { IMemory } from '@/pages/memories/interface';
|
||||||
import { getMemoryDetailById } from '@/services/memory-service';
|
import memoryService from '@/services/memory-service';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { useParams, useSearchParams } from 'umi';
|
import { useParams, useSearchParams } from 'umi';
|
||||||
import { MemoryApiAction } from '../constant';
|
import { MemoryApiAction } from '../constant';
|
||||||
|
|
||||||
export const useFetchMemoryBaseConfiguration = (props?: {
|
export const useFetchMemoryBaseConfiguration = () => {
|
||||||
refreshCount?: number;
|
|
||||||
}) => {
|
|
||||||
const { refreshCount } = props || {};
|
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const memoryBaseId = searchParams.get('id') || id;
|
const memoryBaseId = searchParams.get('id') || id;
|
||||||
@ -18,9 +15,6 @@ export const useFetchMemoryBaseConfiguration = (props?: {
|
|||||||
let queryKey: (MemoryApiAction | number)[] = [
|
let queryKey: (MemoryApiAction | number)[] = [
|
||||||
MemoryApiAction.FetchMemoryDetail,
|
MemoryApiAction.FetchMemoryDetail,
|
||||||
];
|
];
|
||||||
if (typeof refreshCount === 'number') {
|
|
||||||
queryKey = [MemoryApiAction.FetchMemoryDetail, refreshCount];
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data, isFetching: loading } = useQuery<IMemory>({
|
const { data, isFetching: loading } = useQuery<IMemory>({
|
||||||
queryKey: [...queryKey, searchString, pagination],
|
queryKey: [...queryKey, searchString, pagination],
|
||||||
@ -28,19 +22,9 @@ export const useFetchMemoryBaseConfiguration = (props?: {
|
|||||||
gcTime: 0,
|
gcTime: 0,
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
if (memoryBaseId) {
|
if (memoryBaseId) {
|
||||||
const { data } = await getMemoryDetailById(memoryBaseId as string, {
|
const { data } = await memoryService.getMemoryConfig(
|
||||||
// filter: {
|
memoryBaseId as string,
|
||||||
// agent_id: '',
|
);
|
||||||
// },
|
|
||||||
keyword: searchString,
|
|
||||||
page: pagination.current,
|
|
||||||
page_size: pagination.size,
|
|
||||||
});
|
|
||||||
// setPagination({
|
|
||||||
// page: data?.page ?? 1,
|
|
||||||
// pageSize: data?.page_size ?? 10,
|
|
||||||
// total: data?.total ?? 0,
|
|
||||||
// });
|
|
||||||
return data?.data ?? {};
|
return data?.data ?? {};
|
||||||
} else {
|
} else {
|
||||||
return {};
|
return {};
|
||||||
|
|||||||
128
web/src/pages/memory/memory-message/hook.ts
Normal file
128
web/src/pages/memory/memory-message/hook.ts
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
import message from '@/components/ui/message';
|
||||||
|
import { useHandleSearchChange } from '@/hooks/logic-hooks';
|
||||||
|
import memoryService, { getMemoryDetailById } from '@/services/memory-service';
|
||||||
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
import { useParams, useSearchParams } from 'umi';
|
||||||
|
import { MemoryApiAction } from '../constant';
|
||||||
|
import {
|
||||||
|
IMessageContentProps,
|
||||||
|
IMessageTableProps,
|
||||||
|
} from '../memory-message/interface';
|
||||||
|
import { IMessageInfo } from './interface';
|
||||||
|
|
||||||
|
export const useFetchMemoryMessageList = () => {
|
||||||
|
const { id } = useParams();
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
const memoryBaseId = searchParams.get('id') || id;
|
||||||
|
const { handleInputChange, searchString, pagination, setPagination } =
|
||||||
|
useHandleSearchChange();
|
||||||
|
|
||||||
|
let queryKey: (MemoryApiAction | number)[] = [
|
||||||
|
MemoryApiAction.FetchMemoryMessage,
|
||||||
|
];
|
||||||
|
|
||||||
|
const { data, isFetching: loading } = useQuery<IMessageTableProps>({
|
||||||
|
queryKey: [...queryKey, searchString, pagination],
|
||||||
|
initialData: {} as IMessageTableProps,
|
||||||
|
gcTime: 0,
|
||||||
|
queryFn: async () => {
|
||||||
|
if (memoryBaseId) {
|
||||||
|
const { data } = await getMemoryDetailById(memoryBaseId as string, {
|
||||||
|
keyword: searchString,
|
||||||
|
page: pagination.current,
|
||||||
|
page_size: pagination.pageSize,
|
||||||
|
});
|
||||||
|
return data?.data ?? {};
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
loading,
|
||||||
|
handleInputChange,
|
||||||
|
searchString,
|
||||||
|
pagination,
|
||||||
|
setPagination,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useMessageAction = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const [selectedMessage, setSelectedMessage] = useState<IMessageInfo>(
|
||||||
|
{} as IMessageInfo,
|
||||||
|
);
|
||||||
|
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
||||||
|
const handleClickDeleteMessage = useCallback((message: IMessageInfo) => {
|
||||||
|
console.log('handleClickDeleteMessage', message);
|
||||||
|
setSelectedMessage(message);
|
||||||
|
setShowDeleteDialog(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleDeleteMessage = useCallback(() => {
|
||||||
|
// delete message
|
||||||
|
memoryService.deleteMemoryMessage(selectedMessage.message_id).then(() => {
|
||||||
|
message.success(t('message.deleted'));
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: [MemoryApiAction.FetchMemoryMessage],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
setShowDeleteDialog(false);
|
||||||
|
}, [selectedMessage.message_id, queryClient]);
|
||||||
|
|
||||||
|
const [showMessageContentDialog, setShowMessageContentDialog] =
|
||||||
|
useState(false);
|
||||||
|
const [selectedMessageContent, setSelectedMessageContent] =
|
||||||
|
useState<IMessageContentProps>({} as IMessageContentProps);
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: messageContent,
|
||||||
|
isPending: fetchMessageContentLoading,
|
||||||
|
mutateAsync: fetchMessageContent,
|
||||||
|
} = useMutation<IMessageContentProps>({
|
||||||
|
mutationKey: [
|
||||||
|
MemoryApiAction.FetchMessageContent,
|
||||||
|
selectedMessage.message_id,
|
||||||
|
],
|
||||||
|
mutationFn: async () => {
|
||||||
|
setShowMessageContentDialog(true);
|
||||||
|
const res = await memoryService.getMessageContent(
|
||||||
|
selectedMessage.message_id,
|
||||||
|
);
|
||||||
|
if (res.data.code === 0) {
|
||||||
|
setSelectedMessageContent(res.data.data);
|
||||||
|
} else {
|
||||||
|
message.error(res.data.message);
|
||||||
|
}
|
||||||
|
return res.data.data;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleClickMessageContentDialog = useCallback(
|
||||||
|
(message: IMessageInfo) => {
|
||||||
|
setSelectedMessage(message);
|
||||||
|
fetchMessageContent();
|
||||||
|
},
|
||||||
|
[fetchMessageContent],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
selectedMessage,
|
||||||
|
setSelectedMessage,
|
||||||
|
showDeleteDialog,
|
||||||
|
setShowDeleteDialog,
|
||||||
|
handleClickDeleteMessage,
|
||||||
|
handleDeleteMessage,
|
||||||
|
messageContent,
|
||||||
|
fetchMessageContentLoading,
|
||||||
|
fetchMessageContent,
|
||||||
|
selectedMessageContent,
|
||||||
|
showMessageContentDialog,
|
||||||
|
setShowMessageContentDialog,
|
||||||
|
handleClickMessageContentDialog,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import ListFilterBar from '@/components/list-filter-bar';
|
import ListFilterBar from '@/components/list-filter-bar';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { useFetchMemoryMessageList } from '../hooks/use-memory-messages';
|
import { useFetchMemoryMessageList } from './hook';
|
||||||
import { MemoryTable } from './message-table';
|
import { MemoryTable } from './message-table';
|
||||||
|
|
||||||
export default function MemoryMessage() {
|
export default function MemoryMessage() {
|
||||||
|
|||||||
@ -17,3 +17,8 @@ export interface IMessageTableProps {
|
|||||||
messages: { message_list: Array<IMessageInfo>; total: number };
|
messages: { message_list: Array<IMessageInfo>; total: number };
|
||||||
storage_type: string;
|
storage_type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IMessageContentProps {
|
||||||
|
content: string;
|
||||||
|
content_embed: string;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,3 +1,23 @@
|
|||||||
|
import {
|
||||||
|
ConfirmDeleteDialog,
|
||||||
|
ConfirmDeleteDialogNode,
|
||||||
|
} from '@/components/confirm-delete-dialog';
|
||||||
|
import { EmptyType } from '@/components/empty/constant';
|
||||||
|
import Empty from '@/components/empty/empty';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Modal } from '@/components/ui/modal/modal';
|
||||||
|
import { RAGFlowPagination } from '@/components/ui/ragflow-pagination';
|
||||||
|
import { Switch } from '@/components/ui/switch';
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from '@/components/ui/table';
|
||||||
|
import { Pagination } from '@/interfaces/common';
|
||||||
|
import { replaceText } from '@/pages/dataset/process-log-modal';
|
||||||
import {
|
import {
|
||||||
ColumnDef,
|
ColumnDef,
|
||||||
ColumnFiltersState,
|
ColumnFiltersState,
|
||||||
@ -10,26 +30,13 @@ import {
|
|||||||
getSortedRowModel,
|
getSortedRowModel,
|
||||||
useReactTable,
|
useReactTable,
|
||||||
} from '@tanstack/react-table';
|
} from '@tanstack/react-table';
|
||||||
import * as React from 'react';
|
|
||||||
|
|
||||||
import { EmptyType } from '@/components/empty/constant';
|
|
||||||
import Empty from '@/components/empty/empty';
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import { RAGFlowPagination } from '@/components/ui/ragflow-pagination';
|
|
||||||
import { Switch } from '@/components/ui/switch';
|
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableHead,
|
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
} from '@/components/ui/table';
|
|
||||||
import { Pagination } from '@/interfaces/common';
|
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { pick } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
import { Eraser, TextSelect } from 'lucide-react';
|
import { Copy, Eraser, TextSelect } from 'lucide-react';
|
||||||
import { useMemo } from 'react';
|
import * as React from 'react';
|
||||||
|
import { useMemo, useState } from 'react';
|
||||||
|
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
||||||
|
import { useMessageAction } from './hook';
|
||||||
import { IMessageInfo } from './interface';
|
import { IMessageInfo } from './interface';
|
||||||
|
|
||||||
export type MemoryTableProps = {
|
export type MemoryTableProps = {
|
||||||
@ -51,13 +58,27 @@ export function MemoryTable({
|
|||||||
);
|
);
|
||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
React.useState<VisibilityState>({});
|
React.useState<VisibilityState>({});
|
||||||
|
const [copied, setCopied] = useState(false);
|
||||||
|
const {
|
||||||
|
showDeleteDialog,
|
||||||
|
setShowDeleteDialog,
|
||||||
|
handleClickDeleteMessage,
|
||||||
|
selectedMessage,
|
||||||
|
handleDeleteMessage,
|
||||||
|
|
||||||
|
fetchMessageContent,
|
||||||
|
selectedMessageContent,
|
||||||
|
showMessageContentDialog,
|
||||||
|
setShowMessageContentDialog,
|
||||||
|
handleClickMessageContentDialog,
|
||||||
|
} = useMessageAction();
|
||||||
|
|
||||||
// Define columns for the memory table
|
// Define columns for the memory table
|
||||||
const columns: ColumnDef<IMessageInfo>[] = useMemo(
|
const columns: ColumnDef<IMessageInfo>[] = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
accessorKey: 'session_id',
|
accessorKey: 'session_id',
|
||||||
header: () => <span>{t('memoryDetail.messages.sessionId')}</span>,
|
header: () => <span>{t('memory.messages.sessionId')}</span>,
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<div className="text-sm font-medium ">
|
<div className="text-sm font-medium ">
|
||||||
{row.getValue('session_id')}
|
{row.getValue('session_id')}
|
||||||
@ -66,7 +87,7 @@ export function MemoryTable({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'agent_name',
|
accessorKey: 'agent_name',
|
||||||
header: () => <span>{t('memoryDetail.messages.agent')}</span>,
|
header: () => <span>{t('memory.messages.agent')}</span>,
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<div className="text-sm font-medium ">
|
<div className="text-sm font-medium ">
|
||||||
{row.getValue('agent_name')}
|
{row.getValue('agent_name')}
|
||||||
@ -75,7 +96,7 @@ export function MemoryTable({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'message_type',
|
accessorKey: 'message_type',
|
||||||
header: () => <span>{t('memoryDetail.messages.type')}</span>,
|
header: () => <span>{t('memory.messages.type')}</span>,
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<div className="text-sm font-medium capitalize">
|
<div className="text-sm font-medium capitalize">
|
||||||
{row.getValue('message_type')}
|
{row.getValue('message_type')}
|
||||||
@ -84,28 +105,28 @@ export function MemoryTable({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'valid_at',
|
accessorKey: 'valid_at',
|
||||||
header: () => <span>{t('memoryDetail.messages.validDate')}</span>,
|
header: () => <span>{t('memory.messages.validDate')}</span>,
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<div className="text-sm ">{row.getValue('valid_at')}</div>
|
<div className="text-sm ">{row.getValue('valid_at')}</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'forget_at',
|
accessorKey: 'forget_at',
|
||||||
header: () => <span>{t('memoryDetail.messages.forgetAt')}</span>,
|
header: () => <span>{t('memory.messages.forgetAt')}</span>,
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<div className="text-sm ">{row.getValue('forget_at')}</div>
|
<div className="text-sm ">{row.getValue('forget_at')}</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'source_id',
|
accessorKey: 'source_id',
|
||||||
header: () => <span>{t('memoryDetail.messages.source')}</span>,
|
header: () => <span>{t('memory.messages.source')}</span>,
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<div className="text-sm ">{row.getValue('source_id')}</div>
|
<div className="text-sm ">{row.getValue('source_id')}</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'status',
|
accessorKey: 'status',
|
||||||
header: () => <span>{t('memoryDetail.messages.enable')}</span>,
|
header: () => <span>{t('memory.messages.enable')}</span>,
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const isEnabled = row.getValue('status') as boolean;
|
const isEnabled = row.getValue('status') as boolean;
|
||||||
return (
|
return (
|
||||||
@ -117,19 +138,28 @@ export function MemoryTable({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'action',
|
accessorKey: 'action',
|
||||||
header: () => <span>{t('memoryDetail.messages.action')}</span>,
|
header: () => <span>{t('memory.messages.action')}</span>,
|
||||||
meta: {
|
meta: {
|
||||||
cellClassName: 'w-12',
|
cellClassName: 'w-12',
|
||||||
},
|
},
|
||||||
cell: () => (
|
cell: ({ row }) => (
|
||||||
<div className=" flex opacity-0 group-hover:opacity-100">
|
<div className=" flex opacity-0 group-hover:opacity-100">
|
||||||
<Button variant={'ghost'} className="bg-transparent">
|
<Button
|
||||||
|
variant={'ghost'}
|
||||||
|
className="bg-transparent"
|
||||||
|
onClick={() => {
|
||||||
|
handleClickMessageContentDialog(row.original);
|
||||||
|
}}
|
||||||
|
>
|
||||||
<TextSelect />
|
<TextSelect />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant={'delete'}
|
variant={'delete'}
|
||||||
className="bg-transparent"
|
className="bg-transparent"
|
||||||
aria-label="Edit"
|
aria-label="Edit"
|
||||||
|
onClick={() => {
|
||||||
|
handleClickDeleteMessage(row.original);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Eraser />
|
<Eraser />
|
||||||
</Button>
|
</Button>
|
||||||
@ -137,7 +167,7 @@ export function MemoryTable({
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[],
|
[handleClickDeleteMessage],
|
||||||
);
|
);
|
||||||
|
|
||||||
const currentPagination = useMemo(() => {
|
const currentPagination = useMemo(() => {
|
||||||
@ -210,6 +240,85 @@ export function MemoryTable({
|
|||||||
)}
|
)}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
|
{showDeleteDialog && (
|
||||||
|
<ConfirmDeleteDialog
|
||||||
|
onOk={handleDeleteMessage}
|
||||||
|
title={t('memory.messages.forgetMessage')}
|
||||||
|
open={showDeleteDialog}
|
||||||
|
onOpenChange={setShowDeleteDialog}
|
||||||
|
content={{
|
||||||
|
node: (
|
||||||
|
<ConfirmDeleteDialogNode
|
||||||
|
// avatar={{ avatar: selectedMessage.avatar, name: selectedMessage.name }}
|
||||||
|
name={
|
||||||
|
t('memory.messages.sessionId') +
|
||||||
|
': ' +
|
||||||
|
selectedMessage.session_id
|
||||||
|
}
|
||||||
|
warnText={t('memory.messages.delMessageWarn')}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showMessageContentDialog && (
|
||||||
|
<Modal
|
||||||
|
title={t('memory.messages.content')}
|
||||||
|
open={showMessageContentDialog}
|
||||||
|
onOpenChange={setShowMessageContentDialog}
|
||||||
|
className="!w-[640px]"
|
||||||
|
footer={
|
||||||
|
<div className="flex justify-end gap-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowMessageContentDialog(false)}
|
||||||
|
className={
|
||||||
|
'px-2 py-1 border border-border-button rounded-md hover:bg-bg-card hover:text-text-primary '
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t('common.close')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col gap-2.5">
|
||||||
|
<div className="text-text-secondary text-sm">
|
||||||
|
{t('memory.messages.sessionId')}:
|
||||||
|
{selectedMessage.session_id}
|
||||||
|
</div>
|
||||||
|
{selectedMessageContent?.content && (
|
||||||
|
<div className="w-full bg-accent-primary-5 whitespace-pre-line text-wrap rounded-lg h-fit max-h-[350px] overflow-y-auto scrollbar-auto px-2.5 py-1">
|
||||||
|
{replaceText(selectedMessageContent?.content || '')}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{selectedMessageContent?.content_embed && (
|
||||||
|
<div className="flex gap-2 items-center">
|
||||||
|
<CopyToClipboard
|
||||||
|
text={selectedMessageContent?.content_embed}
|
||||||
|
onCopy={() => {
|
||||||
|
setCopied(true);
|
||||||
|
setTimeout(() => setCopied(false), 1000);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant={'ghost'}
|
||||||
|
className="border border-border-button "
|
||||||
|
>
|
||||||
|
{t('memory.messages.contentEmbed')}
|
||||||
|
<Copy />
|
||||||
|
</Button>
|
||||||
|
</CopyToClipboard>
|
||||||
|
{copied && (
|
||||||
|
<span className="text-xs text-text-secondary">
|
||||||
|
{t('memory.messages.copied')}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="flex items-center justify-end py-4 absolute bottom-3 right-3">
|
<div className="flex items-center justify-end py-4 absolute bottom-3 right-3">
|
||||||
<RAGFlowPagination
|
<RAGFlowPagination
|
||||||
|
|||||||
159
web/src/pages/memory/memory-setting/advanced-settings-form.tsx
Normal file
159
web/src/pages/memory/memory-setting/advanced-settings-form.tsx
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
import { FormFieldType, RenderField } from '@/components/dynamic-form';
|
||||||
|
import { SingleFormSlider } from '@/components/ui/dual-range-slider';
|
||||||
|
import { NumberInput } from '@/components/ui/input';
|
||||||
|
import { Label } from '@/components/ui/label';
|
||||||
|
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
import { ListChevronsDownUp, ListChevronsUpDown } from 'lucide-react';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export const advancedSettingsFormSchema = {
|
||||||
|
permission: z.string().optional(),
|
||||||
|
storage_type: z.enum(['table', 'graph']).optional(),
|
||||||
|
forget_policy: z.enum(['lru', 'fifo']).optional(),
|
||||||
|
temperature: z.number().optional(),
|
||||||
|
system_prompt: z.string().optional(),
|
||||||
|
user_prompt: z.string().optional(),
|
||||||
|
};
|
||||||
|
export const defaultAdvancedSettingsForm = {
|
||||||
|
permission: 'me',
|
||||||
|
storage_type: 'table',
|
||||||
|
forget_policy: 'fifo',
|
||||||
|
temperature: 0.7,
|
||||||
|
system_prompt: '',
|
||||||
|
user_prompt: '',
|
||||||
|
};
|
||||||
|
export const AdvancedSettingsForm = () => {
|
||||||
|
const [showAdvancedSettings, setShowAdvancedSettings] = useState(false);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
className="flex items-center gap-1 w-full cursor-pointer"
|
||||||
|
onClick={() => setShowAdvancedSettings(!showAdvancedSettings)}
|
||||||
|
>
|
||||||
|
{showAdvancedSettings ? (
|
||||||
|
<ListChevronsDownUp size={14} />
|
||||||
|
) : (
|
||||||
|
<ListChevronsUpDown size={14} />
|
||||||
|
)}
|
||||||
|
{t('memory.config.advancedSettings')}
|
||||||
|
</div>
|
||||||
|
{/* {showAdvancedSettings && ( */}
|
||||||
|
<>
|
||||||
|
<RenderField
|
||||||
|
field={{
|
||||||
|
name: 'permission',
|
||||||
|
label: t('memory.config.permission'),
|
||||||
|
required: false,
|
||||||
|
horizontal: true,
|
||||||
|
// hideLabel: true,
|
||||||
|
type: FormFieldType.Custom,
|
||||||
|
render: (field) => (
|
||||||
|
<RadioGroup
|
||||||
|
defaultValue="me"
|
||||||
|
className="flex"
|
||||||
|
{...field}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
console.log(value);
|
||||||
|
field.onChange(value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<RadioGroupItem value="me" id="r1" />
|
||||||
|
<Label htmlFor="r1">{t('memory.config.onlyMe')}</Label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<RadioGroupItem value="team" id="r2" />
|
||||||
|
<Label htmlFor="r2">{t('memory.config.team')}</Label>
|
||||||
|
</div>
|
||||||
|
</RadioGroup>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<RenderField
|
||||||
|
field={{
|
||||||
|
name: 'storage_type',
|
||||||
|
label: t('memory.config.storageType'),
|
||||||
|
type: FormFieldType.Select,
|
||||||
|
horizontal: true,
|
||||||
|
placeholder: t('memory.config.storageTypePlaceholder'),
|
||||||
|
options: [
|
||||||
|
{ label: 'table', value: 'table' },
|
||||||
|
// { label: 'graph', value: 'graph' },
|
||||||
|
],
|
||||||
|
required: false,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<RenderField
|
||||||
|
field={{
|
||||||
|
name: 'forget_policy',
|
||||||
|
label: t('memory.config.forgetPolicy'),
|
||||||
|
type: FormFieldType.Select,
|
||||||
|
horizontal: true,
|
||||||
|
// placeholder: t('memory.config.storageTypePlaceholder'),
|
||||||
|
options: [
|
||||||
|
{ label: 'lru', value: 'lru' },
|
||||||
|
{ label: 'fifo', value: 'fifo' },
|
||||||
|
],
|
||||||
|
required: false,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<RenderField
|
||||||
|
field={{
|
||||||
|
name: 'temperature',
|
||||||
|
label: t('memory.config.temperature'),
|
||||||
|
type: FormFieldType.Custom,
|
||||||
|
horizontal: true,
|
||||||
|
required: false,
|
||||||
|
render: (field) => (
|
||||||
|
<div className="flex gap-2 items-center">
|
||||||
|
<SingleFormSlider
|
||||||
|
{...field}
|
||||||
|
onChange={(value: number) => {
|
||||||
|
field.onChange(value);
|
||||||
|
}}
|
||||||
|
max={1}
|
||||||
|
step={0.01}
|
||||||
|
min={0}
|
||||||
|
disabled={false}
|
||||||
|
></SingleFormSlider>
|
||||||
|
<NumberInput
|
||||||
|
className={cn(
|
||||||
|
'h-6 w-10 p-1 border border-border-button rounded-sm',
|
||||||
|
)}
|
||||||
|
max={1}
|
||||||
|
step={0.01}
|
||||||
|
min={0}
|
||||||
|
{...field}
|
||||||
|
></NumberInput>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<RenderField
|
||||||
|
field={{
|
||||||
|
name: 'system_prompt',
|
||||||
|
label: t('memory.config.systemPrompt'),
|
||||||
|
type: FormFieldType.Textarea,
|
||||||
|
horizontal: true,
|
||||||
|
placeholder: t('memory.config.systemPromptPlaceholder'),
|
||||||
|
required: false,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<RenderField
|
||||||
|
field={{
|
||||||
|
name: 'user_prompt',
|
||||||
|
label: t('memory.config.userPrompt'),
|
||||||
|
type: FormFieldType.Text,
|
||||||
|
horizontal: true,
|
||||||
|
placeholder: t('memory.config.userPromptPlaceholder'),
|
||||||
|
required: false,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
{/* )} */}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
53
web/src/pages/memory/memory-setting/basic-form.tsx
Normal file
53
web/src/pages/memory/memory-setting/basic-form.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { AvatarUpload } from '@/components/avatar-upload';
|
||||||
|
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
||||||
|
import { Input } from '@/components/ui/input';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
import { z } from 'zod';
|
||||||
|
export const basicInfoSchema = {
|
||||||
|
name: z.string().min(1, { message: t('setting.nameRequired') }),
|
||||||
|
avatar: z.string().optional(),
|
||||||
|
description: z.string().optional(),
|
||||||
|
};
|
||||||
|
export const defaultBasicInfo = { name: '', avatar: '', description: '' };
|
||||||
|
export const BasicInfo = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<RAGFlowFormItem
|
||||||
|
name={'name'}
|
||||||
|
label={t('memories.name')}
|
||||||
|
required={true}
|
||||||
|
horizontal={true}
|
||||||
|
// tooltip={field.tooltip}
|
||||||
|
// labelClassName={labelClassName || field.labelClassName}
|
||||||
|
>
|
||||||
|
{(field) => {
|
||||||
|
return <Input {...field}></Input>;
|
||||||
|
}}
|
||||||
|
</RAGFlowFormItem>
|
||||||
|
<RAGFlowFormItem
|
||||||
|
name={'avatar'}
|
||||||
|
label={t('memory.config.avatar')}
|
||||||
|
required={false}
|
||||||
|
horizontal={true}
|
||||||
|
// tooltip={field.tooltip}
|
||||||
|
// labelClassName={labelClassName || field.labelClassName}
|
||||||
|
>
|
||||||
|
{(field) => {
|
||||||
|
return <AvatarUpload {...field}></AvatarUpload>;
|
||||||
|
}}
|
||||||
|
</RAGFlowFormItem>
|
||||||
|
<RAGFlowFormItem
|
||||||
|
name={'description'}
|
||||||
|
label={t('memory.config.description')}
|
||||||
|
required={false}
|
||||||
|
horizontal={true}
|
||||||
|
// tooltip={field.tooltip}
|
||||||
|
// labelClassName={labelClassName || field.labelClassName}
|
||||||
|
>
|
||||||
|
{(field) => {
|
||||||
|
return <Input {...field}></Input>;
|
||||||
|
}}
|
||||||
|
</RAGFlowFormItem>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
42
web/src/pages/memory/memory-setting/hook.ts
Normal file
42
web/src/pages/memory/memory-setting/hook.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { useUpdateMemory } from '@/pages/memories/hooks';
|
||||||
|
import { IMemory, IMemoryAppDetailProps } from '@/pages/memories/interface';
|
||||||
|
import { omit } from 'lodash';
|
||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
|
export const useUpdateMemoryConfig = () => {
|
||||||
|
const { updateMemory } = useUpdateMemory();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const onMemoryRenameOk = useCallback(
|
||||||
|
async (data: IMemory) => {
|
||||||
|
let res;
|
||||||
|
setLoading(true);
|
||||||
|
if (data?.id) {
|
||||||
|
// console.log('memory-->', memory, data);
|
||||||
|
try {
|
||||||
|
const params = omit(data, [
|
||||||
|
'id',
|
||||||
|
'memory_type',
|
||||||
|
'embd_id',
|
||||||
|
'storage_type',
|
||||||
|
]);
|
||||||
|
res = await updateMemory({
|
||||||
|
// ...memoryDataTemp,
|
||||||
|
// data: data,
|
||||||
|
id: data.id,
|
||||||
|
...params,
|
||||||
|
} as unknown as IMemoryAppDetailProps);
|
||||||
|
// if (res && res.data.code === 0) {
|
||||||
|
// message.success(t('message.update_success'));
|
||||||
|
// } else {
|
||||||
|
// message.error(t('message.update_fail'));
|
||||||
|
// }
|
||||||
|
} catch (e) {
|
||||||
|
console.error('error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
},
|
||||||
|
[updateMemory],
|
||||||
|
);
|
||||||
|
return { onMemoryRenameOk, loading };
|
||||||
|
};
|
||||||
@ -1,13 +1,110 @@
|
|||||||
|
import { DynamicForm } from '@/components/dynamic-form';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import Divider from '@/components/ui/divider';
|
||||||
|
import { Form } from '@/components/ui/form';
|
||||||
|
import { MainContainer } from '@/pages/dataset/dataset-setting/configuration-form-container';
|
||||||
|
import { TopTitle } from '@/pages/dataset/dataset-title';
|
||||||
|
import { IMemory } from '@/pages/memories/interface';
|
||||||
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { useFetchMemoryBaseConfiguration } from '../hooks/use-memory-setting';
|
||||||
|
import {
|
||||||
|
AdvancedSettingsForm,
|
||||||
|
advancedSettingsFormSchema,
|
||||||
|
defaultAdvancedSettingsForm,
|
||||||
|
} from './advanced-settings-form';
|
||||||
|
import { BasicInfo, basicInfoSchema, defaultBasicInfo } from './basic-form';
|
||||||
|
import { useUpdateMemoryConfig } from './hook';
|
||||||
|
import {
|
||||||
|
MemoryModelForm,
|
||||||
|
defaultMemoryModelForm,
|
||||||
|
memoryModelFormSchema,
|
||||||
|
} from './memory-model-form';
|
||||||
|
|
||||||
|
const MemoryMessageSchema = z.object({
|
||||||
|
id: z.string(),
|
||||||
|
...basicInfoSchema,
|
||||||
|
...memoryModelFormSchema,
|
||||||
|
...advancedSettingsFormSchema,
|
||||||
|
});
|
||||||
|
// type MemoryMessageForm = z.infer<typeof MemoryMessageSchema>;
|
||||||
export default function MemoryMessage() {
|
export default function MemoryMessage() {
|
||||||
|
const form = useForm<IMemory>({
|
||||||
|
resolver: zodResolver(MemoryMessageSchema),
|
||||||
|
defaultValues: {
|
||||||
|
id: '',
|
||||||
|
...defaultBasicInfo,
|
||||||
|
...defaultMemoryModelForm,
|
||||||
|
...defaultAdvancedSettingsForm,
|
||||||
|
} as unknown as IMemory,
|
||||||
|
});
|
||||||
|
const { data } = useFetchMemoryBaseConfiguration();
|
||||||
|
const { onMemoryRenameOk, loading } = useUpdateMemoryConfig();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
form.reset({
|
||||||
|
id: data?.id,
|
||||||
|
embd_id: data?.embd_id,
|
||||||
|
llm_id: data?.llm_id,
|
||||||
|
name: data?.name || '',
|
||||||
|
description: data?.description || '',
|
||||||
|
avatar: data?.avatar || '',
|
||||||
|
memory_size: data?.memory_size,
|
||||||
|
memory_type: data?.memory_type,
|
||||||
|
temperature: data?.temperature,
|
||||||
|
system_prompt: data?.system_prompt || '',
|
||||||
|
user_prompt: data?.user_prompt || '',
|
||||||
|
forgetting_policy: data?.forgetting_policy || 'fifo',
|
||||||
|
permissions: data?.permissions || 'me',
|
||||||
|
});
|
||||||
|
}, [data, form]);
|
||||||
|
const onSubmit = (data: IMemory) => {
|
||||||
|
onMemoryRenameOk(data);
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-2">
|
<section className="h-full flex flex-col">
|
||||||
<div className="flex items-center gap-2">
|
<TopTitle
|
||||||
<div className="h-4 w-4 rounded-full bg-text-secondary">11</div>
|
title={t('knowledgeDetails.configuration')}
|
||||||
<div className="h-4 w-4 rounded-full bg-text-secondary">11</div>
|
description={t('knowledgeConfiguration.titleDescription')}
|
||||||
|
></TopTitle>
|
||||||
|
<div className="flex gap-14 flex-1 min-h-0">
|
||||||
|
<Form {...form}>
|
||||||
|
<form onSubmit={form.handleSubmit(() => {})} className="space-y-6 ">
|
||||||
|
<div className="w-[768px] h-[calc(100vh-300px)] pr-1 overflow-y-auto scrollbar-auto">
|
||||||
|
<MainContainer className="text-text-secondary !space-y-10">
|
||||||
|
<div className="text-base font-medium text-text-primary">
|
||||||
|
{t('knowledgeConfiguration.baseInfo')}
|
||||||
|
</div>
|
||||||
|
<BasicInfo></BasicInfo>
|
||||||
|
<Divider />
|
||||||
|
<MemoryModelForm />
|
||||||
|
<AdvancedSettingsForm />
|
||||||
|
</MainContainer>
|
||||||
|
</div>
|
||||||
|
<div className="text-right items-center flex justify-end gap-3 w-[768px]">
|
||||||
|
<Button
|
||||||
|
type="reset"
|
||||||
|
className="bg-transparent text-color-white hover:bg-transparent border-border-button border"
|
||||||
|
onClick={() => {
|
||||||
|
form.reset();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('knowledgeConfiguration.cancel')}
|
||||||
|
</Button>
|
||||||
|
<DynamicForm.SavingButton
|
||||||
|
submitLoading={loading}
|
||||||
|
submitFunc={(value) => {
|
||||||
|
console.log('form-value', value);
|
||||||
|
onSubmit(value as IMemory);
|
||||||
|
}}
|
||||||
|
></DynamicForm.SavingButton>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
</section>
|
||||||
<div className="h-4 w-4 rounded-full bg-text ">setting</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
74
web/src/pages/memory/memory-setting/memory-model-form.tsx
Normal file
74
web/src/pages/memory/memory-setting/memory-model-form.tsx
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import { FormFieldType, RenderField } from '@/components/dynamic-form';
|
||||||
|
import { useModelOptions } from '@/components/llm-setting-items/llm-form-field';
|
||||||
|
import { EmbeddingSelect } from '@/pages/dataset/dataset-setting/configuration/common-item';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export const memoryModelFormSchema = {
|
||||||
|
embd_id: z.string(),
|
||||||
|
llm_id: z.string(),
|
||||||
|
memory_type: z.array(z.string()).optional(),
|
||||||
|
memory_size: z.number().optional(),
|
||||||
|
};
|
||||||
|
export const defaultMemoryModelForm = {
|
||||||
|
embd_id: '',
|
||||||
|
llm_id: '',
|
||||||
|
memory_type: [],
|
||||||
|
memory_size: 0,
|
||||||
|
};
|
||||||
|
export const MemoryModelForm = () => {
|
||||||
|
const { modelOptions } = useModelOptions();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<RenderField
|
||||||
|
field={{
|
||||||
|
name: 'embd_id',
|
||||||
|
label: t('memories.embeddingModel'),
|
||||||
|
placeholder: t('memories.selectModel'),
|
||||||
|
required: true,
|
||||||
|
horizontal: true,
|
||||||
|
// hideLabel: true,
|
||||||
|
type: FormFieldType.Custom,
|
||||||
|
render: (field) => <EmbeddingSelect field={field} isEdit={false} />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<RenderField
|
||||||
|
field={{
|
||||||
|
name: 'llm_id',
|
||||||
|
label: t('memories.llm'),
|
||||||
|
placeholder: t('memories.selectModel'),
|
||||||
|
required: true,
|
||||||
|
horizontal: true,
|
||||||
|
type: FormFieldType.Select,
|
||||||
|
options: modelOptions as { value: string; label: string }[],
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<RenderField
|
||||||
|
field={{
|
||||||
|
name: 'memory_type',
|
||||||
|
label: t('memories.memoryType'),
|
||||||
|
type: FormFieldType.MultiSelect,
|
||||||
|
horizontal: true,
|
||||||
|
placeholder: t('memories.memoryTypePlaceholder'),
|
||||||
|
options: [
|
||||||
|
{ label: 'Raw', value: 'raw' },
|
||||||
|
{ label: 'Semantic', value: 'semantic' },
|
||||||
|
{ label: 'Episodic', value: 'episodic' },
|
||||||
|
{ label: 'Procedural', value: 'procedural' },
|
||||||
|
],
|
||||||
|
required: true,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<RenderField
|
||||||
|
field={{
|
||||||
|
name: 'memory_size',
|
||||||
|
label: t('memory.config.memorySize'),
|
||||||
|
type: FormFieldType.Number,
|
||||||
|
horizontal: true,
|
||||||
|
// placeholder: t('memory.config.memorySizePlaceholder'),
|
||||||
|
required: false,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,36 +1,31 @@
|
|||||||
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
|
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { useSecondPathName } from '@/hooks/route-hook';
|
import { useSecondPathName } from '@/hooks/route-hook';
|
||||||
import { cn, formatBytes } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { Routes } from '@/routes';
|
import { Routes } from '@/routes';
|
||||||
import { formatPureDate } from '@/utils/date';
|
|
||||||
import { Banknote, Logs } from 'lucide-react';
|
import { Banknote, Logs } from 'lucide-react';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useFetchMemoryBaseConfiguration } from '../hooks/use-memory-setting';
|
import { useFetchMemoryBaseConfiguration } from '../hooks/use-memory-setting';
|
||||||
import { useHandleMenuClick } from './hooks';
|
import { useHandleMenuClick } from './hooks';
|
||||||
|
|
||||||
type PropType = {
|
export function SideBar() {
|
||||||
refreshCount?: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function SideBar({ refreshCount }: PropType) {
|
|
||||||
const pathName = useSecondPathName();
|
const pathName = useSecondPathName();
|
||||||
const { handleMenuClick } = useHandleMenuClick();
|
const { handleMenuClick } = useHandleMenuClick();
|
||||||
// refreshCount: be for avatar img sync update on top left
|
// refreshCount: be for avatar img sync update on top left
|
||||||
const { data } = useFetchMemoryBaseConfiguration({ refreshCount });
|
const { data } = useFetchMemoryBaseConfiguration();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const items = useMemo(() => {
|
const items = useMemo(() => {
|
||||||
const list = [
|
const list = [
|
||||||
{
|
{
|
||||||
icon: <Logs className="size-4" />,
|
icon: <Logs className="size-4" />,
|
||||||
label: t(`knowledgeDetails.overview`),
|
label: t(`memory.sideBar.messages`),
|
||||||
key: Routes.MemoryMessage,
|
key: Routes.MemoryMessage,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <Banknote className="size-4" />,
|
icon: <Banknote className="size-4" />,
|
||||||
label: t(`knowledgeDetails.configuration`),
|
label: t(`memory.sideBar.configuration`),
|
||||||
key: Routes.MemorySetting,
|
key: Routes.MemorySetting,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@ -49,15 +44,15 @@ export function SideBar({ refreshCount }: PropType) {
|
|||||||
<h3 className="text-lg font-semibold line-clamp-1 text-text-primary text-ellipsis overflow-hidden">
|
<h3 className="text-lg font-semibold line-clamp-1 text-text-primary text-ellipsis overflow-hidden">
|
||||||
{data.name}
|
{data.name}
|
||||||
</h3>
|
</h3>
|
||||||
<div className="flex justify-between">
|
{/* <div className="flex justify-between">
|
||||||
<span>
|
<span>
|
||||||
{data.doc_num} {t('knowledgeDetails.files')}
|
{data.doc_num} {t('knowledgeDetails.files')}
|
||||||
</span>
|
</span>
|
||||||
<span>{formatBytes(data.size)}</span>
|
<span>{formatBytes(data.size)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{t('knowledgeDetails.created')} {formatPureDate(data.create_time)}
|
{t('knowledgeDetails.created')} {formatPureDate(data.)}
|
||||||
</div>
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,9 @@ const {
|
|||||||
deleteMemory,
|
deleteMemory,
|
||||||
getMemoryDetail,
|
getMemoryDetail,
|
||||||
updateMemorySetting,
|
updateMemorySetting,
|
||||||
|
getMemoryConfig,
|
||||||
|
deleteMemoryMessage,
|
||||||
|
getMessageContent,
|
||||||
// getMemoryDetailShare,
|
// getMemoryDetailShare,
|
||||||
} = api;
|
} = api;
|
||||||
const methods = {
|
const methods = {
|
||||||
@ -17,27 +20,21 @@ const methods = {
|
|||||||
},
|
},
|
||||||
getMemoryList: {
|
getMemoryList: {
|
||||||
url: getMemoryList,
|
url: getMemoryList,
|
||||||
method: 'post',
|
method: 'get',
|
||||||
},
|
},
|
||||||
deleteMemory: { url: deleteMemory, method: 'post' },
|
deleteMemory: { url: deleteMemory, method: 'delete' },
|
||||||
// getMemoryDetail: {
|
getMemoryConfig: {
|
||||||
// url: getMemoryDetail,
|
url: getMemoryConfig,
|
||||||
// method: 'get',
|
method: 'get',
|
||||||
// },
|
},
|
||||||
// updateMemorySetting: {
|
deleteMemoryMessage: { url: deleteMemoryMessage, method: 'delete' },
|
||||||
// url: updateMemorySetting,
|
getMessageContent: { url: getMessageContent, method: 'get' },
|
||||||
// method: 'post',
|
|
||||||
// },
|
|
||||||
// getMemoryDetailShare: {
|
|
||||||
// url: getMemoryDetailShare,
|
|
||||||
// method: 'get',
|
|
||||||
// },
|
|
||||||
} as const;
|
} as const;
|
||||||
const memoryService = registerNextServer<keyof typeof methods>(methods);
|
const memoryService = registerNextServer<keyof typeof methods>(methods);
|
||||||
export const updateMemoryById = (id: string, data: any) => {
|
export const updateMemoryById = (id: string, data: any) => {
|
||||||
return request.post(updateMemorySetting(id), { data });
|
return request.put(updateMemorySetting(id), { data });
|
||||||
};
|
};
|
||||||
export const getMemoryDetailById = (id: string, data: any) => {
|
export const getMemoryDetailById = (id: string, data: any) => {
|
||||||
return request.post(getMemoryDetail(id), { data });
|
return request.get(getMemoryDetail(id), { params: data });
|
||||||
};
|
};
|
||||||
export default memoryService;
|
export default memoryService;
|
||||||
|
|||||||
@ -227,11 +227,15 @@ export default {
|
|||||||
retrievalTestShare: `${ExternalApi}${api_host}/searchbots/retrieval_test`,
|
retrievalTestShare: `${ExternalApi}${api_host}/searchbots/retrieval_test`,
|
||||||
|
|
||||||
// memory
|
// memory
|
||||||
createMemory: `${api_host}/memory/create`,
|
createMemory: `${api_host}/memories`,
|
||||||
getMemoryList: `${api_host}/memory/list`,
|
getMemoryList: `${api_host}/memories`,
|
||||||
|
getMemoryConfig: (id: string) => `${api_host}/memories/${id}/config`,
|
||||||
deleteMemory: (id: string) => `${api_host}/memory/rm/${id}`,
|
deleteMemory: (id: string) => `${api_host}/memory/rm/${id}`,
|
||||||
getMemoryDetail: (id: string) => `${api_host}/memory/detail/${id}`,
|
getMemoryDetail: (id: string) => `${api_host}/memories/${id}`,
|
||||||
updateMemorySetting: (id: string) => `${api_host}/memory/update/${id}`,
|
updateMemorySetting: (id: string) => `${api_host}/memories/${id}`,
|
||||||
|
deleteMemoryMessage: (id: string) => `${api_host}/message/rm/${id}`,
|
||||||
|
getMessageContent: (message_id: string) =>
|
||||||
|
`${api_host}/messages/${message_id}/content`,
|
||||||
|
|
||||||
// data pipeline
|
// data pipeline
|
||||||
fetchDataflow: (id: string) => `${api_host}/dataflow/get/${id}`,
|
fetchDataflow: (id: string) => `${api_host}/dataflow/get/${id}`,
|
||||||
|
|||||||
Reference in New Issue
Block a user