Feature:memory function complete (#11982)

### What problem does this PR solve?

memory function complete

### Type of change

- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
chanx
2025-12-17 12:35:26 +08:00
committed by GitHub
parent 2595644dfd
commit 205a6483f5
20 changed files with 226 additions and 142 deletions

View File

@ -81,6 +81,7 @@ export interface FormFieldConfig {
schema?: ZodSchema; schema?: ZodSchema;
shouldRender?: (formValues: any) => boolean; shouldRender?: (formValues: any) => boolean;
labelClassName?: string; labelClassName?: string;
disabled?: boolean;
} }
// Component props interface // Component props interface
@ -328,11 +329,7 @@ export const RenderField = ({
} }
return ( return (
<RAGFlowFormItem <RAGFlowFormItem
name={field.name} {...field}
label={field.label}
required={field.required}
horizontal={field.horizontal}
tooltip={field.tooltip}
labelClassName={labelClassName || field.labelClassName} labelClassName={labelClassName || field.labelClassName}
> >
{(fieldProps) => { {(fieldProps) => {
@ -354,11 +351,7 @@ export const RenderField = ({
case FormFieldType.Textarea: case FormFieldType.Textarea:
return ( return (
<RAGFlowFormItem <RAGFlowFormItem
name={field.name} {...field}
label={field.label}
required={field.required}
horizontal={field.horizontal}
tooltip={field.tooltip}
labelClassName={labelClassName || field.labelClassName} labelClassName={labelClassName || field.labelClassName}
> >
{(fieldProps) => { {(fieldProps) => {
@ -375,6 +368,7 @@ export const RenderField = ({
<Textarea <Textarea
{...finalFieldProps} {...finalFieldProps}
placeholder={field.placeholder} placeholder={field.placeholder}
disabled={field.disabled}
// className="resize-none" // className="resize-none"
/> />
); );
@ -385,11 +379,7 @@ export const RenderField = ({
case FormFieldType.Select: case FormFieldType.Select:
return ( return (
<RAGFlowFormItem <RAGFlowFormItem
name={field.name} {...field}
label={field.label}
required={field.required}
horizontal={field.horizontal}
tooltip={field.tooltip}
labelClassName={labelClassName || field.labelClassName} labelClassName={labelClassName || field.labelClassName}
> >
{(fieldProps) => { {(fieldProps) => {
@ -410,6 +400,7 @@ export const RenderField = ({
triggerClassName="!shrink" triggerClassName="!shrink"
{...finalFieldProps} {...finalFieldProps}
options={field.options} options={field.options}
disabled={field.disabled}
/> />
); );
}} }}
@ -419,11 +410,7 @@ export const RenderField = ({
case FormFieldType.MultiSelect: case FormFieldType.MultiSelect:
return ( return (
<RAGFlowFormItem <RAGFlowFormItem
name={field.name} {...field}
label={field.label}
required={field.required}
horizontal={field.horizontal}
tooltip={field.tooltip}
labelClassName={labelClassName || field.labelClassName} labelClassName={labelClassName || field.labelClassName}
> >
{(fieldProps) => { {(fieldProps) => {
@ -447,6 +434,7 @@ export const RenderField = ({
// field.onChange?.(data); // field.onChange?.(data);
// }} // }}
options={field.options as MultiSelectOptionType[]} options={field.options as MultiSelectOptionType[]}
disabled={field.disabled}
/> />
); );
}} }}
@ -504,6 +492,7 @@ export const RenderField = ({
formField.onChange(checked); formField.onChange(checked);
field.onChange?.(checked); field.onChange?.(checked);
}} }}
disabled={field.disabled}
/> />
</div> </div>
</FormControl> </FormControl>
@ -517,11 +506,7 @@ export const RenderField = ({
case FormFieldType.Tag: case FormFieldType.Tag:
return ( return (
<RAGFlowFormItem <RAGFlowFormItem
name={field.name} {...field}
label={field.label}
required={field.required}
horizontal={field.horizontal}
tooltip={field.tooltip}
labelClassName={labelClassName || field.labelClassName} labelClassName={labelClassName || field.labelClassName}
> >
{(fieldProps) => { {(fieldProps) => {
@ -537,7 +522,10 @@ export const RenderField = ({
return ( return (
// <TagInput {...fieldProps} placeholder={field.placeholder} /> // <TagInput {...fieldProps} placeholder={field.placeholder} />
<div className="w-full"> <div className="w-full">
<EditTag {...finalFieldProps}></EditTag> <EditTag
{...finalFieldProps}
disabled={field.disabled}
></EditTag>
</div> </div>
); );
}} }}
@ -547,11 +535,7 @@ export const RenderField = ({
default: default:
return ( return (
<RAGFlowFormItem <RAGFlowFormItem
name={field.name} {...field}
label={field.label}
required={field.required}
horizontal={field.horizontal}
tooltip={field.tooltip}
labelClassName={labelClassName || field.labelClassName} labelClassName={labelClassName || field.labelClassName}
> >
{(fieldProps) => { {(fieldProps) => {
@ -570,6 +554,7 @@ export const RenderField = ({
{...finalFieldProps} {...finalFieldProps}
type={field.type} type={field.type}
placeholder={field.placeholder} placeholder={field.placeholder}
disabled={field.disabled}
/> />
</div> </div>
); );

View File

@ -12,10 +12,11 @@ import { Input } from '../ui/input';
interface EditTagsProps { interface EditTagsProps {
value?: string[]; value?: string[];
onChange?: (tags: string[]) => void; onChange?: (tags: string[]) => void;
disabled?: boolean;
} }
const EditTag = React.forwardRef<HTMLDivElement, EditTagsProps>( const EditTag = React.forwardRef<HTMLDivElement, EditTagsProps>(
({ value = [], onChange }: EditTagsProps) => { ({ value = [], onChange, disabled }: EditTagsProps) => {
const [inputVisible, setInputVisible] = useState(false); const [inputVisible, setInputVisible] = useState(false);
const [inputValue, setInputValue] = useState(''); const [inputValue, setInputValue] = useState('');
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
@ -61,13 +62,15 @@ const EditTag = React.forwardRef<HTMLDivElement, EditTagsProps>(
<div className="max-w-80 overflow-hidden text-ellipsis"> <div className="max-w-80 overflow-hidden text-ellipsis">
{tag} {tag}
</div> </div>
<X {!disabled && (
className="w-4 h-4 text-muted-foreground hover:text-primary" <X
onClick={(e) => { className="w-4 h-4 text-muted-foreground hover:text-primary"
e.preventDefault(); onClick={(e) => {
handleClose(tag); e.preventDefault();
}} handleClose(tag);
/> }}
/>
)}
</div> </div>
</div> </div>
</HoverCardTrigger> </HoverCardTrigger>
@ -91,6 +94,7 @@ const EditTag = React.forwardRef<HTMLDivElement, EditTagsProps>(
value={inputValue} value={inputValue}
onChange={handleInputChange} onChange={handleInputChange}
onBlur={handleInputConfirm} onBlur={handleInputConfirm}
disabled={disabled}
onKeyDown={(e) => { onKeyDown={(e) => {
if (e?.key === 'Enter') { if (e?.key === 'Enter') {
handleInputConfirm(); handleInputConfirm();
@ -100,11 +104,12 @@ const EditTag = React.forwardRef<HTMLDivElement, EditTagsProps>(
)} )}
<div className="flex gap-2 py-1"> <div className="flex gap-2 py-1">
{Array.isArray(tagChild) && tagChild.length > 0 && <>{tagChild}</>} {Array.isArray(tagChild) && tagChild.length > 0 && <>{tagChild}</>}
{!inputVisible && ( {!inputVisible && !disabled && (
<Button <Button
variant="ghost" variant="ghost"
className="w-fit flex items-center justify-center gap-2 bg-bg-card border-dashed border" className="w-fit flex items-center justify-center gap-2 bg-bg-card border-dashed border"
onClick={showInput} onClick={showInput}
disabled={disabled}
style={tagPlusStyle} style={tagPlusStyle}
> >
<PlusOutlined /> <PlusOutlined />

View File

@ -179,7 +179,7 @@ export const SelectWithSearch = forwardRef<
align="start" align="start"
> >
<Command className="p-5"> <Command className="p-5">
{options && options.length > 0 && ( {options && options.length > 5 && (
<CommandInput <CommandInput
placeholder={t('common.search') + '...'} placeholder={t('common.search') + '...'}
className=" placeholder:text-text-disabled" className=" placeholder:text-text-disabled"
@ -193,7 +193,7 @@ export const SelectWithSearch = forwardRef<
if (group.options) { if (group.options) {
return ( return (
<Fragment key={idx}> <Fragment key={idx}>
<CommandGroup heading={group.label}> <CommandGroup heading={group.label} className="mb-1">
{group.options.map((option) => ( {group.options.map((option) => (
<CommandItem <CommandItem
key={option.value} key={option.value}
@ -221,11 +221,9 @@ export const SelectWithSearch = forwardRef<
value={group.value} value={group.value}
disabled={group.disabled} disabled={group.disabled}
onSelect={handleSelect} onSelect={handleSelect}
className={ className={cn('mb-1 min-h-10 ', {
value === group.value 'bg-bg-card ': value === group.value,
? 'bg-bg-card min-h-10' })}
: 'min-h-10'
}
> >
<span className="leading-none">{group.label}</span> <span className="leading-none">{group.label}</span>

View File

@ -70,7 +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.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],

View File

@ -102,8 +102,19 @@ export default {
Memories: 'Memory', Memories: 'Memory',
}, },
memories: { memories: {
llmTooltip:
'Analyzes conversation content, extracts key information, and generates structured memory summaries.',
embeddingModelTooltip:
'Converts text into numerical vectors for meaning similarity search and memory retrieval.',
embeddingModelError:
'Memory type is required and "raw" cannot be deleted.',
memoryTypeTooltip: `Raw: The raw dialogue content between the user and the agent (Required by default).
Semantic Memory: General knowledge and facts about the user and world.
Episodic Memory: Time-stamped records of specific events and experiences.
Procedural Memory: Learned skills, habits, and automated procedures.`,
editName: 'Edit name',
memory: 'Memory', memory: 'Memory',
createMemory: 'Create Memory', createMemory: 'Create memory',
name: 'Name', name: 'Name',
memoryNamePlaceholder: 'memory name', memoryNamePlaceholder: 'memory name',
memoryType: 'Memory type', memoryType: 'Memory type',
@ -114,6 +125,8 @@ export default {
}, },
memory: { memory: {
messages: { messages: {
messageDescription:
'Memory retrieval is configured with Similarity threshold, Keyword similarity weight, and Top N from Advanced Settings.',
copied: 'Copied!', copied: 'Copied!',
contentEmbed: 'Content embed', contentEmbed: 'Content embed',
content: 'Content', content: 'Content',

View File

@ -72,16 +72,18 @@ export const EmbeddingSelect = ({
isEdit, isEdit,
field, field,
name, name,
disabled = false,
}: { }: {
isEdit: boolean; isEdit: boolean;
field: FieldValues; field: FieldValues;
name?: string; name?: string;
disabled?: boolean;
}) => { }) => {
const { t } = useTranslate('knowledgeConfiguration'); const { t } = useTranslate('knowledgeConfiguration');
const form = useFormContext(); const form = useFormContext();
const embeddingModelOptions = useSelectEmbeddingModelOptions(); const embeddingModelOptions = useSelectEmbeddingModelOptions();
const { handleChange } = useHandleKbEmbedding(); const { handleChange } = useHandleKbEmbedding();
const disabled = useHasParsedDocument(isEdit);
const oldValue = useMemo(() => { const oldValue = useMemo(() => {
const embdStr = form.getValues(name || 'embd_id'); const embdStr = form.getValues(name || 'embd_id');
return embdStr || ''; return embdStr || '';
@ -101,7 +103,7 @@ export const EmbeddingSelect = ({
setLoading(true); setLoading(true);
const res = await handleChange({ const res = await handleChange({
embed_id: value, embed_id: value,
callback: field.onChange, // callback: field.onChange,
}); });
if (res.code !== 0) { if (res.code !== 0) {
field.onChange(oldValue); field.onChange(oldValue);
@ -109,6 +111,7 @@ export const EmbeddingSelect = ({
setLoading(false); setLoading(false);
} }
}} }}
disabled={disabled && !isEdit}
value={field.value} value={field.value}
options={embeddingModelOptions} options={embeddingModelOptions}
placeholder={t('embeddingModelPlaceholder')} placeholder={t('embeddingModelPlaceholder')}
@ -120,6 +123,7 @@ export const EmbeddingSelect = ({
export function EmbeddingModelItem({ line = 1, isEdit }: IProps) { export function EmbeddingModelItem({ line = 1, isEdit }: IProps) {
const { t } = useTranslate('knowledgeConfiguration'); const { t } = useTranslate('knowledgeConfiguration');
const form = useFormContext(); const form = useFormContext();
const disabled = useHasParsedDocument(isEdit);
return ( return (
<> <>
<FormField <FormField
@ -149,6 +153,7 @@ export function EmbeddingModelItem({ line = 1, isEdit }: IProps) {
<EmbeddingSelect <EmbeddingSelect
isEdit={!!isEdit} isEdit={!!isEdit}
field={field} field={field}
disabled={disabled}
></EmbeddingSelect> ></EmbeddingSelect>
</FormControl> </FormControl>
</div> </div>

View File

@ -1,9 +1,9 @@
import { DynamicForm, DynamicFormRef } from '@/components/dynamic-form'; import { DynamicForm } from '@/components/dynamic-form';
import { useModelOptions } from '@/components/llm-setting-items/llm-form-field'; 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 { memo, useMemo } from 'react';
import { memo, useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next';
import { createMemoryFields } from './constants'; import { createMemoryFields } from './constants';
import { IMemory } from './interface'; import { IMemory } from './interface';
@ -17,21 +17,16 @@ type IProps = {
}; };
export const AddOrEditModal = memo((props: IProps) => { export const AddOrEditModal = memo((props: IProps) => {
const { open, onClose, onSubmit, initialMemory, isCreate } = props; const { open, onClose, onSubmit, initialMemory, isCreate } = props;
const [formInstance, setFormInstance] = useState<DynamicFormRef | null>(null); const { t } = useTranslation();
const formCallbackRef = useCallback((node: DynamicFormRef | null) => {
if (node) {
// formRef.current = node;
setFormInstance(node);
}
}, []);
const { modelOptions } = useModelOptions(); const { modelOptions } = useModelOptions();
const fields = useMemo(() => { const fields = useMemo(() => {
if (!isCreate) { if (!isCreate) {
return createMemoryFields.filter((field: any) => field.name === 'name'); return createMemoryFields(t).filter(
(field: any) => field.name === 'name',
);
} else { } else {
const tempFields = createMemoryFields.map((field: any) => { const tempFields = createMemoryFields(t).map((field: any) => {
if (field.name === 'llm_id') { if (field.name === 'llm_id') {
return { return {
...field, ...field,
@ -57,14 +52,13 @@ export const AddOrEditModal = memo((props: IProps) => {
<div> <div>
<HomeIcon name="memory" width={'24'} /> <HomeIcon name="memory" width={'24'} />
</div> </div>
{t('memories.createMemory')} {isCreate ? t('memories.createMemory') : t('memories.editName')}
</div> </div>
} }
showfooter={false} showfooter={false}
confirmLoading={props.loading} confirmLoading={props.loading}
> >
<DynamicForm.Root <DynamicForm.Root
ref={formCallbackRef}
fields={fields} fields={fields}
onSubmit={() => {}} onSubmit={() => {}}
defaultValues={initialMemory} defaultValues={initialMemory}

View File

@ -1,41 +1,63 @@
import { FormFieldConfig, FormFieldType } from '@/components/dynamic-form'; import { FormFieldConfig, FormFieldType } from '@/components/dynamic-form';
import { EmbeddingSelect } from '@/pages/dataset/dataset-setting/configuration/common-item'; import { EmbeddingSelect } from '@/pages/dataset/dataset-setting/configuration/common-item';
import { t } from 'i18next'; import { TFunction } from 'i18next';
export enum MemoryType {
Raw = 'raw',
Semantic = 'semantic',
Episodic = 'episodic',
Procedural = 'procedural',
}
export const createMemoryFields = (t: TFunction) =>
[
{
name: 'name',
label: t('memories.name'),
placeholder: t('memories.memoryNamePlaceholder'),
required: true,
},
{
name: 'memory_type',
label: t('memories.memoryType'),
type: FormFieldType.MultiSelect,
placeholder: t('memories.descriptionPlaceholder'),
tooltip: t('memories.memoryTypeTooltip'),
options: [
{ label: 'Raw', value: MemoryType.Raw },
{ label: 'Semantic', value: MemoryType.Semantic },
{ label: 'Episodic', value: MemoryType.Episodic },
{ label: 'Procedural', value: MemoryType.Procedural },
],
required: true,
customValidate: (value) => {
if (!value.includes(MemoryType.Raw) || !value.length) {
return t('memories.embeddingModelError');
}
return true;
},
},
{
name: 'embd_id',
label: t('memories.embeddingModel'),
placeholder: t('memories.selectModel'),
tooltip: t('memories.embeddingModelTooltip'),
required: true,
// hideLabel: true,
// type: 'custom',
render: (field) => <EmbeddingSelect field={field} isEdit={false} />,
},
{
name: 'llm_id',
label: t('memories.llm'),
placeholder: t('memories.selectModel'),
required: true,
type: FormFieldType.Select,
tooltip: t('memories.llmTooltip'),
},
] as FormFieldConfig[];
export const createMemoryFields = [ export const defaultMemoryFields = {
{ name: '',
name: 'name', memory_type: [MemoryType.Raw],
label: t('memories.name'), embd_id: '',
placeholder: t('memories.memoryNamePlaceholder'), llm_id: '',
required: true, };
},
{
name: 'memory_type',
label: t('memories.memoryType'),
type: FormFieldType.MultiSelect,
placeholder: t('memories.descriptionPlaceholder'),
options: [
{ label: 'Raw', value: 'raw' },
{ label: 'Semantic', value: 'semantic' },
{ label: 'Episodic', value: 'episodic' },
{ label: 'Procedural', value: 'procedural' },
],
required: true,
},
{
name: 'embd_id',
label: t('memories.embeddingModel'),
placeholder: t('memories.selectModel'),
required: true,
// hideLabel: true,
// type: 'custom',
render: (field) => <EmbeddingSelect field={field} isEdit={false} />,
},
{
name: 'llm_id',
label: t('memories.llm'),
placeholder: t('memories.selectModel'),
required: true,
type: FormFieldType.Select,
},
] as FormFieldConfig[];

View File

@ -74,12 +74,12 @@ export const useFetchMemoryList = () => {
queryFn: async () => { queryFn: async () => {
const { data: response } = await memoryService.getMemoryList( const { data: response } = await memoryService.getMemoryList(
{ {
data: { params: {
keywords: debouncedSearchString, keywords: debouncedSearchString,
page_size: pagination.pageSize, page_size: pagination.pageSize,
page: pagination.current, page: pagination.current,
}, },
params: {}, data: {},
}, },
true, true,
); );
@ -197,6 +197,7 @@ export const useUpdateMemory = () => {
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');
} }
return response.data; return response.data;
}, },
onSuccess: (data, variables) => { onSuccess: (data, variables) => {

View File

@ -10,8 +10,9 @@ import { Plus } from 'lucide-react';
import { useCallback, useEffect, useState } 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 { defaultMemoryFields } from './constants';
import { useFetchMemoryList, useRenameMemory } from './hooks'; import { useFetchMemoryList, useRenameMemory } from './hooks';
import { ICreateMemoryProps } from './interface'; import { ICreateMemoryProps, IMemory } from './interface';
import { MemoryCard } from './memory-card'; import { MemoryCard } from './memory-card';
export default function MemoryList() { export default function MemoryList() {
@ -45,7 +46,7 @@ export default function MemoryList() {
const openCreateModalFun = useCallback(() => { const openCreateModalFun = useCallback(() => {
// setIsEdit(false); // setIsEdit(false);
setAddOrEditType('add'); setAddOrEditType('add');
showMemoryRenameModal(); showMemoryRenameModal(defaultMemoryFields as unknown as IMemory);
}, [showMemoryRenameModal]); }, [showMemoryRenameModal]);
const handlePageChange = useCallback( const handlePageChange = useCallback(
(page: number, pageSize?: number) => { (page: number, pageSize?: number) => {
@ -131,12 +132,12 @@ export default function MemoryList() {
})} })}
</CardContainer> </CardContainer>
</div> </div>
{list?.data.total && list?.data.total > 0 && ( {list?.data.total_count && list?.data.total_count > 0 && (
<div className="px-8 mb-4"> <div className="px-8 mb-4">
<RAGFlowPagination <RAGFlowPagination
{...pick(pagination, 'current', 'pageSize')} {...pick(pagination, 'current', 'pageSize')}
// total={pagination.total} // total={pagination.total}
total={list?.data.total} total={list?.data.total_count}
onChange={handlePageChange} onChange={handlePageChange}
/> />
</div> </div>

View File

@ -36,12 +36,14 @@ export interface IMemory extends ICreateMemoryProps {
temperature: string; temperature: string;
system_prompt: string; system_prompt: string;
user_prompt: string; user_prompt: string;
create_date: string;
create_time: number;
} }
export interface MemoryListResponse { export interface MemoryListResponse {
code: number; code: number;
data: { data: {
memory_list: Array<IMemory>; memory_list: Array<IMemory>;
total: number; total_count: number;
}; };
message: string; message: string;
} }

View File

@ -17,6 +17,7 @@ export function MemoryCard({ data, showMemoryRenameModal }: IProps) {
name: data?.name, name: data?.name,
avatar: data?.avatar, avatar: data?.avatar,
description: data?.description, description: data?.description,
update_time: data?.create_time,
}} }}
moreDropdown={ moreDropdown={
<MemoryDropdown <MemoryDropdown

View File

@ -30,7 +30,7 @@ export const useFetchMemoryMessageList = () => {
queryFn: async () => { queryFn: async () => {
if (memoryBaseId) { if (memoryBaseId) {
const { data } = await getMemoryDetailById(memoryBaseId as string, { const { data } = await getMemoryDetailById(memoryBaseId as string, {
keyword: searchString, keywords: searchString,
page: pagination.current, page: pagination.current,
page_size: pagination.pageSize, page_size: pagination.pageSize,
}); });
@ -53,27 +53,61 @@ export const useFetchMemoryMessageList = () => {
export const useMessageAction = () => { export const useMessageAction = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { id: memoryId } = useParams();
const [selectedMessage, setSelectedMessage] = useState<IMessageInfo>( const [selectedMessage, setSelectedMessage] = useState<IMessageInfo>(
{} as IMessageInfo, {} as IMessageInfo,
); );
const [showDeleteDialog, setShowDeleteDialog] = useState(false); const [showDeleteDialog, setShowDeleteDialog] = useState(false);
const handleClickDeleteMessage = useCallback((message: IMessageInfo) => { const handleClickDeleteMessage = useCallback((message: IMessageInfo) => {
console.log('handleClickDeleteMessage', message);
setSelectedMessage(message); setSelectedMessage(message);
setShowDeleteDialog(true); setShowDeleteDialog(true);
}, []); }, []);
const handleDeleteMessage = useCallback(() => { const handleDeleteMessage = useCallback(() => {
// delete message // delete message
memoryService.deleteMemoryMessage(selectedMessage.message_id).then(() => { memoryService
message.success(t('message.deleted')); .deleteMemoryMessage({
queryClient.invalidateQueries({ memory_id: memoryId,
queryKey: [MemoryApiAction.FetchMemoryMessage], message_id: selectedMessage.message_id,
})
.then(() => {
message.success(t('message.deleted'));
queryClient.invalidateQueries({
queryKey: [MemoryApiAction.FetchMemoryMessage],
});
}); });
});
setShowDeleteDialog(false); setShowDeleteDialog(false);
}, [selectedMessage.message_id, queryClient]); }, [selectedMessage.message_id, queryClient]);
const handleUpdateMessageState = useCallback(
(messageInfo: IMessageInfo, enable: boolean) => {
// delete message
const selectedMessageInfo = messageInfo || selectedMessage;
memoryService
.updateMessageState({
memory_id: memoryId,
message_id: selectedMessageInfo.message_id,
status: enable || false,
})
.then(() => {
message.success(t('message.updated'));
queryClient.invalidateQueries({
queryKey: [MemoryApiAction.FetchMemoryMessage],
});
});
setShowDeleteDialog(false);
},
[selectedMessage, queryClient, memoryId],
);
const handleClickUpdateMessageState = useCallback(
(message: IMessageInfo, enable: boolean) => {
setSelectedMessage(message);
handleUpdateMessageState(message, enable);
},
[handleUpdateMessageState],
);
const [showMessageContentDialog, setShowMessageContentDialog] = const [showMessageContentDialog, setShowMessageContentDialog] =
useState(false); useState(false);
const [selectedMessageContent, setSelectedMessageContent] = const [selectedMessageContent, setSelectedMessageContent] =
@ -90,9 +124,10 @@ export const useMessageAction = () => {
], ],
mutationFn: async () => { mutationFn: async () => {
setShowMessageContentDialog(true); setShowMessageContentDialog(true);
const res = await memoryService.getMessageContent( const res = await memoryService.getMessageContent({
selectedMessage.message_id, memory_id: memoryId,
); message_id: selectedMessage.message_id,
});
if (res.data.code === 0) { if (res.data.code === 0) {
setSelectedMessageContent(res.data.data); setSelectedMessageContent(res.data.data);
} else { } else {
@ -117,6 +152,7 @@ export const useMessageAction = () => {
setShowDeleteDialog, setShowDeleteDialog,
handleClickDeleteMessage, handleClickDeleteMessage,
handleDeleteMessage, handleDeleteMessage,
handleUpdateMessageState,
messageContent, messageContent,
fetchMessageContentLoading, fetchMessageContentLoading,
fetchMessageContent, fetchMessageContent,
@ -124,5 +160,6 @@ export const useMessageAction = () => {
showMessageContentDialog, showMessageContentDialog,
setShowMessageContentDialog, setShowMessageContentDialog,
handleClickMessageContentDialog, handleClickMessageContentDialog,
handleClickUpdateMessageState,
}; };
}; };

View File

@ -21,15 +21,16 @@ export default function MemoryMessage() {
title="Dataset" title="Dataset"
onSearchChange={handleInputChange} onSearchChange={handleInputChange}
searchString={searchString} searchString={searchString}
showFilter={false}
// value={filterValue} // value={filterValue}
// onChange={handleFilterSubmit} // onChange={handleFilterSubmit}
// onOpenChange={onOpenChange} // onOpenChange={onOpenChange}
// filters={filters} // filters={filters}
leftPanel={ leftPanel={
<div className="items-start"> <div className="items-start">
<div className="pb-1">{t('knowledgeDetails.subbarFiles')}</div> <div className="pb-1">{t('memory.sideBar.messages')}</div>
<div className="text-text-secondary text-sm"> <div className="text-text-secondary text-sm">
{t('knowledgeDetails.datasetDescription')} {t('memory.messages.messageDescription')}
</div> </div>
</div> </div>
} }

View File

@ -66,7 +66,7 @@ export function MemoryTable({
selectedMessage, selectedMessage,
handleDeleteMessage, handleDeleteMessage,
fetchMessageContent, handleClickUpdateMessageState,
selectedMessageContent, selectedMessageContent,
showMessageContentDialog, showMessageContentDialog,
setShowMessageContentDialog, setShowMessageContentDialog,
@ -131,7 +131,12 @@ export function MemoryTable({
const isEnabled = row.getValue('status') as boolean; const isEnabled = row.getValue('status') as boolean;
return ( return (
<div className="flex items-center"> <div className="flex items-center">
<Switch defaultChecked={isEnabled} onChange={() => {}} /> <Switch
defaultChecked={isEnabled}
onCheckedChange={(val) => {
handleClickUpdateMessageState(row.original, val);
}}
/>
</div> </div>
); );
}, },

View File

@ -94,7 +94,7 @@ export const AdvancedSettingsForm = () => {
horizontal: true, horizontal: true,
// placeholder: t('memory.config.storageTypePlaceholder'), // placeholder: t('memory.config.storageTypePlaceholder'),
options: [ options: [
{ label: 'lru', value: 'lru' }, // { label: 'lru', value: 'lru' },
{ label: 'fifo', value: 'fifo' }, { label: 'fifo', value: 'fifo' },
], ],
required: false, required: false,

View File

@ -1,7 +1,8 @@
import { FormFieldType, RenderField } from '@/components/dynamic-form'; import { FormFieldType, RenderField } from '@/components/dynamic-form';
import { useModelOptions } from '@/components/llm-setting-items/llm-form-field'; import { useModelOptions } from '@/components/llm-setting-items/llm-form-field';
import { EmbeddingSelect } from '@/pages/dataset/dataset-setting/configuration/common-item'; import { EmbeddingSelect } from '@/pages/dataset/dataset-setting/configuration/common-item';
import { t } from 'i18next'; import { MemoryType } from '@/pages/memories/constants';
import { useTranslation } from 'react-i18next';
import { z } from 'zod'; import { z } from 'zod';
export const memoryModelFormSchema = { export const memoryModelFormSchema = {
@ -13,11 +14,12 @@ export const memoryModelFormSchema = {
export const defaultMemoryModelForm = { export const defaultMemoryModelForm = {
embd_id: '', embd_id: '',
llm_id: '', llm_id: '',
memory_type: [], memory_type: [MemoryType.Raw],
memory_size: 0, memory_size: 0,
}; };
export const MemoryModelForm = () => { export const MemoryModelForm = () => {
const { modelOptions } = useModelOptions(); const { modelOptions } = useModelOptions();
const { t } = useTranslation();
return ( return (
<> <>
<RenderField <RenderField
@ -29,7 +31,12 @@ export const MemoryModelForm = () => {
horizontal: true, horizontal: true,
// hideLabel: true, // hideLabel: true,
type: FormFieldType.Custom, type: FormFieldType.Custom,
render: (field) => <EmbeddingSelect field={field} isEdit={false} />, disabled: true,
render: (field) => (
<EmbeddingSelect field={field} isEdit={false} disabled={true} />
),
tooltip: t('memories.embeddingModelTooltip'),
}} }}
/> />
<RenderField <RenderField
@ -41,6 +48,7 @@ export const MemoryModelForm = () => {
horizontal: true, horizontal: true,
type: FormFieldType.Select, type: FormFieldType.Select,
options: modelOptions as { value: string; label: string }[], options: modelOptions as { value: string; label: string }[],
tooltip: t('memories.llmTooltip'),
}} }}
/> />
<RenderField <RenderField
@ -50,6 +58,8 @@ export const MemoryModelForm = () => {
type: FormFieldType.MultiSelect, type: FormFieldType.MultiSelect,
horizontal: true, horizontal: true,
placeholder: t('memories.memoryTypePlaceholder'), placeholder: t('memories.memoryTypePlaceholder'),
tooltip: t('memories.memoryTypeTooltip'),
disabled: true,
options: [ options: [
{ label: 'Raw', value: 'raw' }, { label: 'Raw', value: 'raw' },
{ label: 'Semantic', value: 'semantic' }, { label: 'Semantic', value: 'semantic' },

View File

@ -3,7 +3,8 @@ import { Button } from '@/components/ui/button';
import { useSecondPathName } from '@/hooks/route-hook'; import { useSecondPathName } from '@/hooks/route-hook';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { Routes } from '@/routes'; import { Routes } from '@/routes';
import { Banknote, Logs } from 'lucide-react'; import { formatPureDate } from '@/utils/date';
import { MemoryStick, Settings } 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';
@ -19,12 +20,12 @@ export function SideBar() {
const items = useMemo(() => { const items = useMemo(() => {
const list = [ const list = [
{ {
icon: <Logs className="size-4" />, icon: <MemoryStick className="size-4" />,
label: t(`memory.sideBar.messages`), label: t(`memory.sideBar.messages`),
key: Routes.MemoryMessage, key: Routes.MemoryMessage,
}, },
{ {
icon: <Banknote className="size-4" />, icon: <Settings className="size-4" />,
label: t(`memory.sideBar.configuration`), label: t(`memory.sideBar.configuration`),
key: Routes.MemorySetting, key: Routes.MemorySetting,
}, },
@ -44,15 +45,13 @@ export function SideBar() {
<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 className="truncate ">{data.description}</span>
{data.doc_num} {t('knowledgeDetails.files')} {/* <span>{formatBytes(data.size)}</span> */}
</span>
<span>{formatBytes(data.size)}</span>
</div> </div>
<div> <div>
{t('knowledgeDetails.created')} {formatPureDate(data.)} {t('knowledgeDetails.created')} {formatPureDate(data.create_time)}
</div> */} </div>
</div> </div>
</div> </div>

View File

@ -11,6 +11,7 @@ const {
getMemoryConfig, getMemoryConfig,
deleteMemoryMessage, deleteMemoryMessage,
getMessageContent, getMessageContent,
updateMessageState,
// getMemoryDetailShare, // getMemoryDetailShare,
} = api; } = api;
const methods = { const methods = {
@ -29,6 +30,7 @@ const methods = {
}, },
deleteMemoryMessage: { url: deleteMemoryMessage, method: 'delete' }, deleteMemoryMessage: { url: deleteMemoryMessage, method: 'delete' },
getMessageContent: { url: getMessageContent, method: 'get' }, getMessageContent: { url: getMessageContent, method: 'get' },
updateMessageState: { url: updateMessageState, method: 'put' },
} 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) => {

View File

@ -235,9 +235,12 @@ export default {
deleteMemory: (id: string) => `${api_host}/memory/rm/${id}`, deleteMemory: (id: string) => `${api_host}/memory/rm/${id}`,
getMemoryDetail: (id: string) => `${api_host}/memories/${id}`, getMemoryDetail: (id: string) => `${api_host}/memories/${id}`,
updateMemorySetting: (id: string) => `${api_host}/memories/${id}`, updateMemorySetting: (id: string) => `${api_host}/memories/${id}`,
deleteMemoryMessage: (id: string) => `${api_host}/message/rm/${id}`, deleteMemoryMessage: (data: { memory_id: string; message_id: string }) =>
getMessageContent: (message_id: string) => `${api_host}/messages/${data.memory_id}:${data.message_id}`,
`${api_host}/messages/${message_id}/content`, getMessageContent: (data: { memory_id: string; message_id: string }) =>
`${api_host}/messages/${data.memory_id}:${data.message_id}/content`,
updateMessageState: (data: { memory_id: string; message_id: string }) =>
`${api_host}/messages/${data.memory_id}:${data.message_id}`,
// data pipeline // data pipeline
fetchDataflow: (id: string) => `${api_host}/dataflow/get/${id}`, fetchDataflow: (id: string) => `${api_host}/dataflow/get/${id}`,