Feature: Implement metadata functionality (#12049)

### What problem does this PR solve?

Feature: Implement metadata functionality

### Type of change

- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
chanx
2025-12-19 19:13:33 +08:00
committed by GitHub
parent aceca266ff
commit eeb36a5ce7
19 changed files with 1326 additions and 85 deletions

View File

@ -691,7 +691,7 @@ const DynamicForm = {
useImperativeHandle( useImperativeHandle(
ref, ref,
() => ({ () => ({
submit: form.handleSubmit, submit: form.handleSubmit(onSubmit),
getValues: form.getValues, getValues: form.getValues,
reset: (values?: T) => { reset: (values?: T) => {
if (values) { if (values) {

View File

@ -1,7 +1,7 @@
import { PlusOutlined } from '@ant-design/icons'; import { PlusOutlined } from '@ant-design/icons';
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import { X } from 'lucide-react'; import { Trash2 } from 'lucide-react';
import { Button } from '../ui/button'; import { Button } from '../ui/button';
import { import {
HoverCard, HoverCard,
@ -57,14 +57,15 @@ const EditTag = React.forwardRef<HTMLDivElement, EditTagsProps>(
<HoverCard key={tag}> <HoverCard key={tag}>
<HoverCardContent side="top">{tag}</HoverCardContent> <HoverCardContent side="top">{tag}</HoverCardContent>
<HoverCardTrigger asChild> <HoverCardTrigger asChild>
<div className="w-fit flex items-center justify-center gap-2 border-dashed border px-2 py-1 rounded-sm bg-bg-card"> <div className="w-fit flex items-center justify-center gap-2 border border-border-button px-2 py-1 rounded-sm bg-bg-card">
<div className="flex gap-2 items-center"> <div className="flex gap-2 items-center">
<div className="max-w-80 overflow-hidden text-ellipsis"> <div className="max-w-80 overflow-hidden text-ellipsis">
{tag} {tag}
</div> </div>
{!disabled && ( {!disabled && (
<X <Trash2
className="w-4 h-4 text-muted-foreground hover:text-primary" size={14}
className="text-text-secondary hover:text-state-error"
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
handleClose(tag); handleClose(tag);
@ -80,10 +81,6 @@ const EditTag = React.forwardRef<HTMLDivElement, EditTagsProps>(
const tagChild = value?.map(forMap); const tagChild = value?.map(forMap);
const tagPlusStyle: React.CSSProperties = {
borderStyle: 'dashed',
};
return ( return (
<div> <div>
{inputVisible && ( {inputVisible && (
@ -102,15 +99,14 @@ const EditTag = React.forwardRef<HTMLDivElement, EditTagsProps>(
}} }}
/> />
)} )}
<div className="flex gap-2 py-1"> <div className="flex gap-2 py-1 flex-wrap">
{Array.isArray(tagChild) && tagChild.length > 0 && <>{tagChild}</>} {Array.isArray(tagChild) && tagChild.length > 0 && <>{tagChild}</>}
{!inputVisible && !disabled && ( {!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-border-button border"
onClick={showInput} onClick={showInput}
disabled={disabled} disabled={disabled}
style={tagPlusStyle}
> >
<PlusOutlined /> <PlusOutlined />
</Button> </Button>

View File

@ -175,6 +175,21 @@ Procedural Memory: Learned skills, habits, and automated procedures.`,
parserRequired: 'Chunk method is required', parserRequired: 'Chunk method is required',
}, },
knowledgeDetails: { knowledgeDetails: {
metadata: {
changesAffectNewParses: 'Changes affect new parses only.',
editMetadataForDataset: 'View and edit metadata for ',
restrictDefinedValues: 'Restrict to defined values',
metadataGenerationSettings: 'Metadata generation settings',
manageMetadataForDataset: 'Manage metadata for this dataset',
manageMetadata: 'Manage metadata',
metadata: 'Metadata',
values: 'Values',
action: 'Action',
field: 'Field',
description: 'Description',
fieldName: 'Field name',
editMetadata: 'Edit metadata',
},
localUpload: 'Local upload', localUpload: 'Local upload',
fileSize: 'File size', fileSize: 'File size',
fileType: 'File type', fileType: 'File type',
@ -348,6 +363,8 @@ Procedural Memory: Learned skills, habits, and automated procedures.`,
reRankModelWaring: 'Re-rank model is very time consuming.', reRankModelWaring: 'Re-rank model is very time consuming.',
}, },
knowledgeConfiguration: { knowledgeConfiguration: {
settings: 'Settings',
autoMetadata: 'Auto metadata',
mineruOptions: 'MinerU Options', mineruOptions: 'MinerU Options',
mineruParseMethod: 'Parse Method', mineruParseMethod: 'Parse Method',
mineruParseMethodTip: mineruParseMethodTip:

View File

@ -267,6 +267,8 @@ export default {
theDocumentBeingParsedCannotBeDeleted: '正在解析的文档不能被删除', theDocumentBeingParsedCannotBeDeleted: '正在解析的文档不能被删除',
}, },
knowledgeConfiguration: { knowledgeConfiguration: {
settings: '设置',
autoMetadata: '自动元数据',
mineruOptions: 'MinerU 选项', mineruOptions: 'MinerU 选项',
mineruParseMethod: '解析方法', mineruParseMethod: '解析方法',
mineruParseMethodTip: mineruParseMethodTip:

View File

@ -0,0 +1,370 @@
import message from '@/components/ui/message';
import { useSetModalState } from '@/hooks/common-hooks';
import { useSetDocumentMeta } from '@/hooks/use-document-request';
import {
getMetaDataService,
updateMetaData,
} from '@/services/knowledge-service';
import { useQuery } from '@tanstack/react-query';
import { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useParams } from 'umi';
import {
IMetaDataReturnJSONSettings,
IMetaDataReturnJSONType,
IMetaDataReturnType,
IMetaDataTableData,
MetadataOperations,
ShowManageMetadataModalProps,
} from './interface';
export enum MetadataType {
Manage = 1,
UpdateSingle = 2,
Setting = 3,
}
export const util = {
changeToMetaDataTableData(data: IMetaDataReturnType): IMetaDataTableData[] {
return Object.entries(data).map(([key, value]) => {
const values = value.map(([v]) => v.toString());
console.log('values', values);
return {
field: key,
description: '',
values: values,
} as IMetaDataTableData;
});
},
JSONToMetaDataTableData(
data: Record<string, string | string[]>,
): IMetaDataTableData[] {
return Object.entries(data).map(([key, value]) => {
return {
field: key,
description: '',
values: value,
} as IMetaDataTableData;
});
},
tableDataToMetaDataJSON(data: IMetaDataTableData[]): IMetaDataReturnJSONType {
return data.reduce<IMetaDataReturnJSONType>((pre, cur) => {
pre[cur.field] = cur.values;
return pre;
}, {});
},
tableDataToMetaDataSettingJSON(
data: IMetaDataTableData[],
): IMetaDataReturnJSONSettings {
return data.map((item) => {
return {
key: item.field,
description: item.description,
enum: item.values,
};
});
},
metaDataSettingJSONToMetaDataTableData(
data: IMetaDataReturnJSONSettings,
): IMetaDataTableData[] {
return data.map((item) => {
return {
field: item.key,
description: item.description,
values: item.enum,
restrictDefinedValues: !!item.enum?.length,
} as IMetaDataTableData;
});
},
};
export const useMetadataOperations = () => {
const [operations, setOperations] = useState<MetadataOperations>({
deletes: [],
updates: [],
});
const addDeleteRow = useCallback((key: string) => {
setOperations((prev) => ({
...prev,
deletes: [...prev.deletes, { key }],
}));
}, []);
const addDeleteValue = useCallback((key: string, value: string) => {
setOperations((prev) => ({
...prev,
deletes: [...prev.deletes, { key, value }],
}));
}, []);
const addUpdateValue = useCallback(
(key: string, value: string | string[]) => {
setOperations((prev) => ({
...prev,
updates: [...prev.updates, { key, value }],
}));
},
[],
);
const resetOperations = useCallback(() => {
setOperations({
deletes: [],
updates: [],
});
}, []);
return {
operations,
addDeleteRow,
addDeleteValue,
addUpdateValue,
resetOperations,
};
};
export const useFetchMetaDataManageData = (
type: MetadataType = MetadataType.Manage,
) => {
const { id } = useParams();
// const [data, setData] = useState<IMetaDataTableData[]>([]);
// const [loading, setLoading] = useState(false);
// const fetchData = useCallback(async (): Promise<IMetaDataTableData[]> => {
// setLoading(true);
// const { data } = await getMetaDataService({
// kb_id: id as string,
// });
// setLoading(false);
// if (data?.data?.summary) {
// return util.changeToMetaDataTableData(data.data.summary);
// }
// return [];
// }, [id]);
// useEffect(() => {
// if (type === MetadataType.Manage) {
// fetchData()
// .then((res) => {
// setData(res);
// })
// .catch((res) => {
// console.error(res);
// });
// }
// }, [type, fetchData]);
const {
data,
isFetching: loading,
refetch,
} = useQuery<IMetaDataTableData[]>({
queryKey: ['fetchMetaData', id],
enabled: !!id && type === MetadataType.Manage,
initialData: [],
gcTime: 1000,
queryFn: async () => {
const { data } = await getMetaDataService({
kb_id: id as string,
});
if (data?.data?.summary) {
return util.changeToMetaDataTableData(data.data.summary);
}
return [];
},
});
return {
data,
loading,
refetch,
};
};
export const useManageMetaDataModal = (
metaData: IMetaDataTableData[] = [],
type: MetadataType = MetadataType.Manage,
otherData?: Record<string, any>,
) => {
const { id } = useParams();
const { t } = useTranslation();
const { data, loading } = useFetchMetaDataManageData(type);
const [tableData, setTableData] = useState<IMetaDataTableData[]>(metaData);
const { operations, addDeleteRow, addDeleteValue, addUpdateValue } =
useMetadataOperations();
const { setDocumentMeta } = useSetDocumentMeta();
useEffect(() => {
if (data) {
setTableData(data);
} else {
setTableData([]);
}
}, [data]);
useEffect(() => {
if (metaData) {
setTableData(metaData);
} else {
setTableData([]);
}
}, [metaData]);
const handleDeleteSingleValue = useCallback(
(field: string, value: string) => {
addDeleteValue(field, value);
setTableData((prevTableData) => {
const newTableData = prevTableData.map((item) => {
if (item.field === field) {
return {
...item,
values: item.values.filter((v) => v !== value),
};
}
return item;
});
// console.log('newTableData', newTableData, prevTableData);
return newTableData;
});
},
[addDeleteValue],
);
const handleDeleteSingleRow = useCallback(
(field: string) => {
addDeleteRow(field);
setTableData((prevTableData) => {
const newTableData = prevTableData.filter(
(item) => item.field !== field,
);
// console.log('newTableData', newTableData, prevTableData);
return newTableData;
});
},
[addDeleteRow],
);
const handleSaveManage = useCallback(
async (callback: () => void) => {
const { data: res } = await updateMetaData({
kb_id: id as string,
data: operations,
});
if (res.code === 0) {
message.success(t('message.success'));
callback();
}
},
[operations, id, t],
);
const handleSaveUpdateSingle = useCallback(
async (callback: () => void) => {
const reqData = util.tableDataToMetaDataJSON(tableData);
if (otherData?.id) {
const ret = await setDocumentMeta({
documentId: otherData?.id,
meta: JSON.stringify(reqData),
});
if (ret === 0) {
// message.success(t('message.success'));
callback();
}
}
},
[tableData, otherData, setDocumentMeta],
);
const handleSaveSettings = useCallback(
async (callback: () => void) => {
const data = util.tableDataToMetaDataSettingJSON(tableData);
callback();
return data;
},
[tableData],
);
const handleSave = useCallback(
async ({ callback }: { callback: () => void }) => {
switch (type) {
case MetadataType.UpdateSingle:
handleSaveUpdateSingle(callback);
break;
case MetadataType.Manage:
handleSaveManage(callback);
break;
case MetadataType.Setting:
return handleSaveSettings(callback);
default:
handleSaveManage(callback);
break;
}
},
[handleSaveManage, type, handleSaveUpdateSingle, handleSaveSettings],
);
return {
tableData,
setTableData,
handleDeleteSingleValue,
handleDeleteSingleRow,
loading,
handleSave,
addUpdateValue,
addDeleteValue,
};
};
export const useManageMetadata = () => {
const [tableData, setTableData] = useState<IMetaDataTableData[]>([]);
const [config, setConfig] = useState<ShowManageMetadataModalProps>(
{} as ShowManageMetadataModalProps,
);
const {
visible: manageMetadataVisible,
showModal,
hideModal: hideManageMetadataModal,
} = useSetModalState();
const showManageMetadataModal = useCallback(
(config?: ShowManageMetadataModalProps) => {
const { metadata } = config || {};
if (metadata) {
// const dataTemp = Object.entries(metadata).map(([key, value]) => {
// return {
// field: key,
// description: '',
// values: Array.isArray(value) ? value : [value],
// } as IMetaDataTableData;
// });
setTableData(metadata);
console.log('metadata-2', metadata);
}
console.log('metadata-3', metadata);
if (config) {
setConfig(config);
}
showModal();
},
[showModal],
);
return {
manageMetadataVisible,
showManageMetadataModal,
hideManageMetadataModal,
tableData,
config,
};
};
export const useManageValues = () => {
const [updateValues, setUpdateValues] = useState<{
field: string;
values: string[];
} | null>(null);
return { updateValues, setUpdateValues };
};

View File

@ -0,0 +1,80 @@
import { ReactNode } from 'react';
import { MetadataType } from './hook';
export type IMetaDataReturnType = Record<string, Array<Array<string | number>>>;
export type IMetaDataReturnJSONType = Record<
string,
Array<string | number> | string
>;
export interface IMetaDataReturnJSONSettingItem {
key: string;
description?: string;
enum?: string[];
}
export type IMetaDataReturnJSONSettings = Array<IMetaDataReturnJSONSettingItem>;
export type IMetaDataTableData = {
field: string;
description: string;
restrictDefinedValues?: boolean;
values: string[];
};
export type IManageModalProps = {
title: ReactNode;
isShowDescription?: boolean;
isDeleteSingleValue?: boolean;
visible: boolean;
hideModal: () => void;
tableData?: IMetaDataTableData[];
isCanAdd: boolean;
type: MetadataType;
otherData?: Record<string, any>;
isEditField?: boolean;
isAddValue?: boolean;
isShowValueSwitch?: boolean;
isVerticalShowValue?: boolean;
success?: (data: any) => void;
};
export interface IManageValuesProps {
title: ReactNode;
visible: boolean;
isEditField?: boolean;
isAddValue?: boolean;
isShowDescription?: boolean;
isShowValueSwitch?: boolean;
isVerticalShowValue?: boolean;
data: IMetaDataTableData;
hideModal: () => void;
onSave: (data: IMetaDataTableData) => void;
addUpdateValue: (key: string, value: string | string[]) => void;
addDeleteValue: (key: string, value: string) => void;
}
interface DeleteOperation {
key: string;
value?: string;
}
interface UpdateOperation {
key: string;
value: string | string[];
}
export interface MetadataOperations {
deletes: DeleteOperation[];
updates: UpdateOperation[];
}
export interface ShowManageMetadataModalOptions {
title?: ReactNode | string;
}
export type ShowManageMetadataModalProps = Partial<IManageModalProps> & {
metadata?: IMetaDataTableData[];
isCanAdd: boolean;
type: MetadataType;
record?: Record<string, any>;
options?: ShowManageMetadataModalOptions;
title?: ReactNode | string;
isDeleteSingleValue?: boolean;
};

View File

@ -0,0 +1,373 @@
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 {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import { useSetModalState } from '@/hooks/common-hooks';
import {
ColumnDef,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from '@tanstack/react-table';
import { Plus, Settings, Trash2 } from 'lucide-react';
import { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useManageMetaDataModal } from './hook';
import { IManageModalProps, IMetaDataTableData } from './interface';
import { ManageValuesModal } from './manage-values-modal';
export const ManageMetadataModal = (props: IManageModalProps) => {
const {
title,
visible,
hideModal,
isDeleteSingleValue,
tableData: originalTableData,
isCanAdd,
type: metadataType,
otherData,
isEditField,
isAddValue,
isShowDescription = false,
isShowValueSwitch = false,
isVerticalShowValue = true,
success,
} = props;
const { t } = useTranslation();
const [valueData, setValueData] = useState<IMetaDataTableData>({
field: '',
description: '',
values: [],
});
const [currentValueIndex, setCurrentValueIndex] = useState<number>(0);
const [deleteDialogContent, setDeleteDialogContent] = useState({
visible: false,
title: '',
name: '',
warnText: '',
onOk: () => {},
onCancel: () => {},
});
const {
tableData,
setTableData,
handleDeleteSingleValue,
handleDeleteSingleRow,
handleSave,
addUpdateValue,
addDeleteValue,
} = useManageMetaDataModal(originalTableData, metadataType, otherData);
const {
visible: manageValuesVisible,
showModal: showManageValuesModal,
hideModal: hideManageValuesModal,
} = useSetModalState();
const hideDeleteModal = () => {
setDeleteDialogContent({
visible: false,
title: '',
name: '',
warnText: '',
onOk: () => {},
onCancel: () => {},
});
};
const handAddValueRow = () => {
setValueData({
field: '',
description: '',
values: [],
});
setCurrentValueIndex(tableData.length || 0);
showManageValuesModal();
};
const handleEditValueRow = useCallback(
(data: IMetaDataTableData, index: number) => {
setCurrentValueIndex(index);
setValueData(data);
showManageValuesModal();
},
[showManageValuesModal],
);
const columns: ColumnDef<IMetaDataTableData>[] = useMemo(() => {
const cols: ColumnDef<IMetaDataTableData>[] = [
{
accessorKey: 'field',
header: () => <span>{t('knowledgeDetails.metadata.field')}</span>,
cell: ({ row }) => (
<div className="text-sm text-accent-primary">
{row.getValue('field')}
</div>
),
},
{
accessorKey: 'description',
header: () => <span>{t('knowledgeDetails.metadata.description')}</span>,
cell: ({ row }) => (
<div className="text-sm truncate max-w-32">
{row.getValue('description')}
</div>
),
},
{
accessorKey: 'values',
header: () => <span>{t('knowledgeDetails.metadata.values')}</span>,
cell: ({ row }) => {
const values = row.getValue('values') as Array<string>;
return (
<div className="flex items-center gap-1">
{values.length > 0 &&
values
.filter((value: string, index: number) => index < 2)
?.map((value: string) => {
return (
<Button
key={value}
variant={'ghost'}
className="border border-border-button"
aria-label="Edit"
>
<div className="flex gap-1 items-center">
<div className="text-sm truncate max-w-24">
{value}
</div>
{isDeleteSingleValue && (
<Button
variant={'delete'}
className="p-0 bg-transparent"
onClick={() => {
handleDeleteSingleValue(
row.getValue('field'),
value,
);
}}
>
<Trash2 />
</Button>
)}
</div>
</Button>
);
})}
{values.length > 2 && (
<div className="text-text-secondary self-end">...</div>
)}
</div>
);
},
},
{
accessorKey: 'action',
header: () => <span>{t('knowledgeDetails.metadata.action')}</span>,
meta: {
cellClassName: 'w-12',
},
cell: ({ row }) => (
<div className=" flex opacity-0 group-hover:opacity-100 gap-2">
<Button
variant={'ghost'}
className="bg-transparent px-1 py-0"
onClick={() => {
handleEditValueRow(row.original, row.index);
}}
>
<Settings />
</Button>
<Button
variant={'delete'}
className="p-0 bg-transparent"
onClick={() => {
setDeleteDialogContent({
visible: true,
title:
t('common.delete') +
' ' +
t('knowledgeDetails.metadata.metadata'),
name: row.getValue('field'),
warnText: t('knowledgeDetails.metadata.deleteWarn'),
onOk: () => {
hideDeleteModal();
handleDeleteSingleRow(row.getValue('field'));
},
onCancel: () => {
hideDeleteModal();
},
});
}}
>
<Trash2 />
</Button>
</div>
),
},
];
if (!isShowDescription) {
cols.splice(1, 1);
}
return cols;
}, [
handleDeleteSingleRow,
t,
handleDeleteSingleValue,
isShowDescription,
isDeleteSingleValue,
handleEditValueRow,
]);
const table = useReactTable({
data: tableData as IMetaDataTableData[],
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
manualPagination: true,
});
const handleSaveValues = (data: IMetaDataTableData) => {
setTableData((prev) => {
if (currentValueIndex >= prev.length) {
return [...prev, data];
} else {
return prev.map((item, index) => {
if (index === currentValueIndex) {
return data;
}
return item;
});
}
});
};
return (
<>
<Modal
title={title}
open={visible}
onCancel={hideModal}
maskClosable={false}
okText={t('common.save')}
onOk={async () => {
const res = await handleSave({ callback: hideModal });
console.log('data', res);
success?.(res);
}}
>
<div className="flex flex-col gap-2">
<div className="flex items-center justify-between">
<div>{t('knowledgeDetails.metadata.metadata')}</div>
{isCanAdd && (
<Button
variant={'ghost'}
className="border border-border-button"
onClick={handAddValueRow}
>
<Plus />
</Button>
)}
</div>
<Table rootClassName="max-h-[800px]">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody className="relative">
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && 'selected'}
className="group"
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
<Empty type={EmptyType.Data} />
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
</Modal>
{manageValuesVisible && (
<ManageValuesModal
title={<div>{t('knowledgeDetails.metadata.editMetadata')}</div>}
visible={manageValuesVisible}
hideModal={hideManageValuesModal}
data={valueData}
onSave={handleSaveValues}
addUpdateValue={addUpdateValue}
addDeleteValue={addDeleteValue}
isEditField={isEditField || isCanAdd}
isAddValue={isAddValue || isCanAdd}
isShowDescription={isShowDescription}
isShowValueSwitch={isShowValueSwitch}
isVerticalShowValue={isVerticalShowValue}
// handleDeleteSingleValue={handleDeleteSingleValue}
// handleDeleteSingleRow={handleDeleteSingleRow}
/>
)}
{deleteDialogContent.visible && (
<ConfirmDeleteDialog
open={deleteDialogContent.visible}
onCancel={deleteDialogContent.onCancel}
onOk={deleteDialogContent.onOk}
title={deleteDialogContent.title}
content={{
node: (
<ConfirmDeleteDialogNode
name={deleteDialogContent.name}
warnText={deleteDialogContent.warnText}
/>
),
}}
/>
)}
</>
);
};

View File

@ -0,0 +1,250 @@
import EditTag from '@/components/edit-tag';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Modal } from '@/components/ui/modal/modal';
import { Switch } from '@/components/ui/switch';
import { Textarea } from '@/components/ui/textarea';
import { Plus, Trash2 } from 'lucide-react';
import { memo, useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { IManageValuesProps, IMetaDataTableData } from './interface';
// Create a separate input component, wrapped with memo to avoid unnecessary re-renders
const ValueInputItem = memo(
({
item,
index,
onValueChange,
onDelete,
onBlur,
}: {
item: string;
index: number;
onValueChange: (index: number, value: string) => void;
onDelete: (index: number) => void;
onBlur: (index: number) => void;
}) => {
return (
<div
key={`value-item-${index}`}
className="flex items-center gap-2.5 w-full"
>
<div className="flex-1 w-full">
<Input
value={item}
onChange={(e) => onValueChange(index, e.target.value)}
onBlur={() => onBlur(index)}
/>
</div>
<Button
type="button"
variant="delete"
className="border border-border-button px-1 h-6 w-6 rounded-sm"
onClick={() => onDelete(index)}
>
<Trash2 size={14} className="w-4 h-4" />
</Button>
</div>
);
},
);
export const ManageValuesModal = (props: IManageValuesProps) => {
const {
title,
data,
isEditField,
visible,
isAddValue,
isShowDescription,
isShowValueSwitch,
isVerticalShowValue,
hideModal,
onSave,
addUpdateValue,
addDeleteValue,
} = props;
const [metaData, setMetaData] = useState(data);
const { t } = useTranslation();
// Use functional update to avoid closure issues
const handleChange = useCallback((field: string, value: any) => {
setMetaData((prev) => ({
...prev,
[field]: value,
}));
}, []);
// Maintain separate state for each input box
const [tempValues, setTempValues] = useState<string[]>([...data.values]);
useEffect(() => {
setTempValues([...data.values]);
setMetaData(data);
}, [data]);
const handleHideModal = useCallback(() => {
hideModal();
setMetaData({} as IMetaDataTableData);
}, [hideModal]);
const handleSave = useCallback(() => {
if (!metaData.restrictDefinedValues && isShowValueSwitch) {
const newMetaData = { ...metaData, values: [] };
onSave(newMetaData);
} else {
onSave(metaData);
}
handleHideModal();
}, [metaData, onSave, handleHideModal, isShowValueSwitch]);
// Handle value changes, only update temporary state
const handleValueChange = useCallback((index: number, value: string) => {
setTempValues((prev) => {
const newValues = [...prev];
newValues[index] = value;
return newValues;
});
}, []);
// Handle blur event, synchronize to main state
const handleValueBlur = useCallback(() => {
addUpdateValue(metaData.field, [...tempValues]);
handleChange('values', [...tempValues]);
}, [handleChange, tempValues, metaData, addUpdateValue]);
// Handle delete operation
const handleDelete = useCallback(
(index: number) => {
setTempValues((prev) => {
const newTempValues = [...prev];
addDeleteValue(metaData.field, newTempValues[index]);
newTempValues.splice(index, 1);
return newTempValues;
});
// Synchronize to main state
setMetaData((prev) => {
const newMetaDataValues = [...prev.values];
newMetaDataValues.splice(index, 1);
return {
...prev,
values: newMetaDataValues,
};
});
},
[addDeleteValue, metaData],
);
// Handle adding new value
const handleAddValue = useCallback(() => {
setTempValues((prev) => [...prev, '']);
// Synchronize to main state
setMetaData((prev) => ({
...prev,
values: [...prev.values, ''],
}));
}, []);
return (
<Modal
title={title}
open={visible}
onCancel={handleHideModal}
className="!w-[460px]"
okText={t('common.save')}
onOk={handleSave}
maskClosable={false}
footer={null}
>
<div className="flex flex-col gap-4">
{!isEditField && (
<div className="text-base p-5 border border-border-button rounded-lg">
{metaData.field}
</div>
)}
{isEditField && (
<div className="flex flex-col gap-2">
<div>{t('knowledgeDetails.metadata.fieldName')}</div>
<div>
<Input
value={metaData.field}
onChange={(e) => {
handleChange('field', e.target?.value || '');
}}
/>
</div>
</div>
)}
{isShowDescription && (
<div className="flex flex-col gap-2">
<div>{t('knowledgeDetails.metadata.description')}</div>
<div>
<Textarea
value={metaData.description}
onChange={(e) => {
handleChange('description', e.target?.value || '');
}}
/>
</div>
</div>
)}
{isShowValueSwitch && (
<div className="flex flex-col gap-2">
<div>{t('knowledgeDetails.metadata.restrictDefinedValues')}</div>
<div>
<Switch
checked={metaData.restrictDefinedValues || false}
onCheckedChange={(checked) =>
handleChange('restrictDefinedValues', checked)
}
/>
</div>
</div>
)}
{metaData.restrictDefinedValues && (
<div className="flex flex-col gap-2">
<div className="flex justify-between items-center">
<div>{t('knowledgeDetails.metadata.values')}</div>
{isAddValue && isVerticalShowValue && (
<div>
<Button
variant={'ghost'}
className="border border-border-button"
onClick={handleAddValue}
>
<Plus />
</Button>
</div>
)}
</div>
{isVerticalShowValue && (
<div className="flex flex-col gap-2 w-full">
{tempValues?.map((item, index) => {
return (
<ValueInputItem
key={`value-item-${index}`}
item={item}
index={index}
onValueChange={handleValueChange}
onDelete={handleDelete}
onBlur={handleValueBlur}
/>
);
})}
</div>
)}
{!isVerticalShowValue && (
<EditTag
value={metaData.values}
onChange={(value) => handleChange('values', value)}
/>
)}
</div>
)}
</div>
</Modal>
);
};

View File

@ -1,5 +1,11 @@
import {
FormFieldConfig,
FormFieldType,
RenderField,
} from '@/components/dynamic-form';
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 { SliderInputFormField } from '@/components/slider-input-form-field';
import { Button } from '@/components/ui/button';
import { import {
FormControl, FormControl,
FormField, FormField,
@ -13,8 +19,19 @@ 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 { t } from 'i18next';
import { Settings } from 'lucide-react';
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import { FieldValues, useFormContext } from 'react-hook-form'; import {
ControllerRenderProps,
FieldValues,
useFormContext,
} from 'react-hook-form';
import {
MetadataType,
useManageMetadata,
util,
} from '../../components/metedata/hook';
import { ManageMetadataModal } from '../../components/metedata/manage-modal';
import { import {
useHandleKbEmbedding, useHandleKbEmbedding,
useHasParsedDocument, useHasParsedDocument,
@ -304,3 +321,84 @@ export function OverlappedPercent() {
></SliderInputFormField> ></SliderInputFormField>
); );
} }
export function AutoMetadata() {
// get metadata field
const form = useFormContext();
const {
manageMetadataVisible,
showManageMetadataModal,
hideManageMetadataModal,
tableData,
config: metadataConfig,
} = useManageMetadata();
const autoMetadataField: FormFieldConfig = {
name: 'parser_config.enable_metadata',
label: t('knowledgeConfiguration.autoMetadata'),
type: FormFieldType.Custom,
horizontal: true,
defaultValue: true,
render: (fieldProps: ControllerRenderProps) => (
<div className="flex items-center justify-between">
<Button
variant="ghost"
onClick={() => {
const metadata = form.getValues('parser_config.metadata');
const tableMetaData =
util.metaDataSettingJSONToMetaDataTableData(metadata);
showManageMetadataModal({
metadata: tableMetaData,
isCanAdd: true,
type: MetadataType.Setting,
});
}}
>
<div className="flex items-center gap-2">
<Settings />
{t('knowledgeConfiguration.settings')}
</div>
</Button>
<Switch
checked={fieldProps.value}
onCheckedChange={fieldProps.onChange}
/>
</div>
),
};
return (
<>
<RenderField field={autoMetadataField} />
{manageMetadataVisible && (
<ManageMetadataModal
title={
metadataConfig.title || (
<div className="flex flex-col gap-2">
<div className="text-base font-normal">
{t('knowledgeDetails.metadata.metadataGenerationSettings')}
</div>
<div className="text-sm text-text-secondary">
{t('knowledgeDetails.metadata.changesAffectNewParses')}
</div>
</div>
)
}
visible={manageMetadataVisible}
hideModal={hideManageMetadataModal}
// selectedRowKeys={selectedRowKeys}
tableData={tableData}
isCanAdd={metadataConfig.isCanAdd}
isDeleteSingleValue={metadataConfig.isDeleteSingleValue}
type={metadataConfig.type}
otherData={metadataConfig.record}
isShowDescription={true}
isShowValueSwitch={true}
isVerticalShowValue={false}
success={(data) => {
form.setValue('parser_config.metadata', data || []);
}}
/>
)}
</>
);
}

View File

@ -11,7 +11,11 @@ import {
ConfigurationFormContainer, ConfigurationFormContainer,
MainContainer, MainContainer,
} from '../configuration-form-container'; } from '../configuration-form-container';
import { EnableTocToggle, OverlappedPercent } from './common-item'; import {
AutoMetadata,
EnableTocToggle,
OverlappedPercent,
} from './common-item';
export function NaiveConfiguration() { export function NaiveConfiguration() {
return ( return (
@ -22,6 +26,7 @@ export function NaiveConfiguration() {
<DelimiterFormField></DelimiterFormField> <DelimiterFormField></DelimiterFormField>
<ChildrenDelimiterForm /> <ChildrenDelimiterForm />
<EnableTocToggle /> <EnableTocToggle />
<AutoMetadata />
<OverlappedPercent /> <OverlappedPercent />
</ConfigurationFormContainer> </ConfigurationFormContainer>
<ConfigurationFormContainer> <ConfigurationFormContainer>

View File

@ -83,6 +83,18 @@ export const formSchema = z
path: ['entity_types'], path: ['entity_types'],
}, },
), ),
metadata: z
.array(
z
.object({
key: z.string().optional(),
description: z.string().optional(),
enum: z.array(z.string().optional()).optional(),
})
.optional(),
)
.optional(),
enable_metadata: z.boolean().optional(),
}) })
.optional(), .optional(),
pagerank: z.number(), pagerank: z.number(),

View File

@ -90,6 +90,8 @@ export default function DatasetSettings() {
entity_types: initialEntityTypes, entity_types: initialEntityTypes,
method: MethodValue.Light, method: MethodValue.Light,
}, },
metadata: [],
enable_metadata: false,
}, },
pipeline_id: '', pipeline_id: '',
parseType: 1, parseType: 1,
@ -238,11 +240,8 @@ export default function DatasetSettings() {
} }
return connector; return connector;
}); });
console.log('🚀 ~ DatasetSettings ~ connectors:', connectors);
setSourceData(connectors as IDataSourceNodeProps[]); setSourceData(connectors as IDataSourceNodeProps[]);
form.setValue('connectors', connectors || []); form.setValue('connectors', connectors || []);
// form.setValue('pipeline_name', data.name || '');
// form.setValue('pipeline_avatar', data.avatar || '');
} }
}; };

View File

@ -58,7 +58,11 @@ export function SavingButton() {
onClick={() => { onClick={() => {
(async () => { (async () => {
try { try {
let beValid = await form.formControl.trigger(); let beValid = await form.trigger();
if (!beValid) {
const errors = form.formState.errors;
console.error('Validation errors:', errors);
}
if (beValid) { if (beValid) {
form.handleSubmit(async (values) => { form.handleSubmit(async (values) => {
console.log('saveKnowledgeConfiguration: ', values); console.log('saveKnowledgeConfiguration: ', values);

View File

@ -32,19 +32,20 @@ import { getExtension } from '@/utils/document-util';
import { t } from 'i18next'; import { t } from 'i18next';
import { pick } from 'lodash'; import { pick } from 'lodash';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { ShowManageMetadataModalProps } from '../components/metedata/interface';
import ProcessLogModal from '../process-log-modal'; import ProcessLogModal from '../process-log-modal';
import { useShowLog } from './hooks'; import { useShowLog } from './hooks';
import { SetMetaDialog } from './set-meta-dialog';
import { useChangeDocumentParser } from './use-change-document-parser'; import { useChangeDocumentParser } from './use-change-document-parser';
import { useDatasetTableColumns } from './use-dataset-table-columns'; import { useDatasetTableColumns } from './use-dataset-table-columns';
import { useRenameDocument } from './use-rename-document'; import { useRenameDocument } from './use-rename-document';
import { useSaveMeta } from './use-save-meta';
export type DatasetTableProps = Pick< export type DatasetTableProps = Pick<
ReturnType<typeof useFetchDocumentList>, ReturnType<typeof useFetchDocumentList>,
'documents' | 'setPagination' | 'pagination' | 'loading' 'documents' | 'setPagination' | 'pagination' | 'loading'
> & > &
Pick<UseRowSelectionType, 'rowSelection' | 'setRowSelection'>; Pick<UseRowSelectionType, 'rowSelection' | 'setRowSelection'> & {
showManageMetadataModal: (config: ShowManageMetadataModalProps) => void;
};
export function DatasetTable({ export function DatasetTable({
documents, documents,
@ -52,6 +53,7 @@ export function DatasetTable({
setPagination, setPagination,
rowSelection, rowSelection,
setRowSelection, setRowSelection,
showManageMetadataModal,
}: DatasetTableProps) { }: DatasetTableProps) {
const [sorting, setSorting] = React.useState<SortingState>([]); const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>( const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
@ -78,20 +80,19 @@ export function DatasetTable({
initialName, initialName,
} = useRenameDocument(); } = useRenameDocument();
const { // const {
showSetMetaModal, // hideSetMetaModal,
hideSetMetaModal, // setMetaVisible,
setMetaVisible, // setMetaLoading,
setMetaLoading, // onSetMetaModalOk,
onSetMetaModalOk, // metaRecord,
metaRecord, // } = useSaveMeta();
} = useSaveMeta();
const { showLog, logInfo, logVisible, hideLog } = useShowLog(documents); const { showLog, logInfo, logVisible, hideLog } = useShowLog(documents);
const columns = useDatasetTableColumns({ const columns = useDatasetTableColumns({
showChangeParserModal, showChangeParserModal,
showRenameModal, showRenameModal,
showSetMetaModal, showManageMetadataModal,
showLog, showLog,
}); });
@ -207,14 +208,14 @@ export function DatasetTable({
></RenameDialog> ></RenameDialog>
)} )}
{setMetaVisible && ( {/* {setMetaVisible && (
<SetMetaDialog <SetMetaDialog
hideModal={hideSetMetaModal} hideModal={hideSetMetaModal}
loading={setMetaLoading} loading={setMetaLoading}
onOk={onSetMetaModalOk} onOk={onSetMetaModalOk}
initialMetaData={metaRecord.meta_fields} initialMetaData={metaRecord.meta_fields}
></SetMetaDialog> ></SetMetaDialog>
)} )} */}
{logVisible && ( {logVisible && (
<ProcessLogModal <ProcessLogModal
title={t('knowledgeDetails.fileLogs')} title={t('knowledgeDetails.fileLogs')}

View File

@ -13,9 +13,11 @@ import {
import { useRowSelection } from '@/hooks/logic-hooks/use-row-selection'; import { useRowSelection } from '@/hooks/logic-hooks/use-row-selection';
import { useFetchDocumentList } from '@/hooks/use-document-request'; import { useFetchDocumentList } from '@/hooks/use-document-request';
import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request'; import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request';
import { Upload } from 'lucide-react'; import { Pen, Upload } from 'lucide-react';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useManageMetadata } from '../components/metedata/hook';
import { ManageMetadataModal } from '../components/metedata/manage-modal';
import { DatasetTable } from './dataset-table'; import { DatasetTable } from './dataset-table';
import Generate from './generate-button/generate'; import Generate from './generate-button/generate';
import { useBulkOperateDataset } from './use-bulk-operate-dataset'; import { useBulkOperateDataset } from './use-bulk-operate-dataset';
@ -61,6 +63,14 @@ export default function Dataset() {
showCreateModal, showCreateModal,
} = useCreateEmptyDocument(); } = useCreateEmptyDocument();
const {
manageMetadataVisible,
showManageMetadataModal,
hideManageMetadataModal,
tableData,
config: metadataConfig,
} = useManageMetadata();
const { rowSelection, rowSelectionIsEmpty, setRowSelection, selectedCount } = const { rowSelection, rowSelectionIsEmpty, setRowSelection, selectedCount } =
useRowSelection(); useRowSelection();
@ -91,6 +101,18 @@ export default function Dataset() {
</div> </div>
</div> </div>
} }
preChildren={
<Button
variant={'ghost'}
className="border border-border-button"
onClick={() => showManageMetadataModal()}
>
<div className="flex gap-1 items-center">
<Pen size={14} />
{t('knowledgeDetails.metadata.metadata')}
</div>
</Button>
}
> >
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
@ -119,6 +141,7 @@ export default function Dataset() {
setPagination={setPagination} setPagination={setPagination}
rowSelection={rowSelection} rowSelection={rowSelection}
setRowSelection={setRowSelection} setRowSelection={setRowSelection}
showManageMetadataModal={showManageMetadataModal}
loading={loading} loading={loading}
></DatasetTable> ></DatasetTable>
{documentUploadVisible && ( {documentUploadVisible && (
@ -137,6 +160,30 @@ export default function Dataset() {
title={'File Name'} title={'File Name'}
></RenameDialog> ></RenameDialog>
)} )}
{manageMetadataVisible && (
<ManageMetadataModal
title={
metadataConfig.title || (
<div className="flex flex-col gap-2">
<div className="text-base font-normal">
{t('knowledgeDetails.metadata.manageMetadata')}
</div>
<div className="text-sm text-text-secondary">
{t('knowledgeDetails.metadata.manageMetadataForDataset')}
</div>
</div>
)
}
visible={manageMetadataVisible}
hideModal={hideManageMetadataModal}
// selectedRowKeys={selectedRowKeys}
tableData={tableData}
isCanAdd={metadataConfig.isCanAdd}
isDeleteSingleValue={metadataConfig.isDeleteSingleValue}
type={metadataConfig.type}
otherData={metadataConfig.record}
/>
)}
</section> </section>
</> </>
); );

View File

@ -16,20 +16,23 @@ import { formatDate } from '@/utils/date';
import { ColumnDef } from '@tanstack/table-core'; import { ColumnDef } from '@tanstack/table-core';
import { ArrowUpDown, MonitorUp } from 'lucide-react'; import { ArrowUpDown, MonitorUp } from 'lucide-react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { MetadataType, util } from '../components/metedata/hook';
import { ShowManageMetadataModalProps } from '../components/metedata/interface';
import { DatasetActionCell } from './dataset-action-cell'; import { DatasetActionCell } from './dataset-action-cell';
import { ParsingStatusCell } from './parsing-status-cell'; import { ParsingStatusCell } from './parsing-status-cell';
import { UseChangeDocumentParserShowType } from './use-change-document-parser'; import { UseChangeDocumentParserShowType } from './use-change-document-parser';
import { UseRenameDocumentShowType } from './use-rename-document'; import { UseRenameDocumentShowType } from './use-rename-document';
import { UseSaveMetaShowType } from './use-save-meta';
type UseDatasetTableColumnsType = UseChangeDocumentParserShowType & type UseDatasetTableColumnsType = UseChangeDocumentParserShowType &
UseRenameDocumentShowType & UseRenameDocumentShowType & {
UseSaveMetaShowType & { showLog: (record: IDocumentInfo) => void }; showLog: (record: IDocumentInfo) => void;
showManageMetadataModal: (config: ShowManageMetadataModalProps) => void;
};
export function useDatasetTableColumns({ export function useDatasetTableColumns({
showChangeParserModal, showChangeParserModal,
showRenameModal, showRenameModal,
showSetMetaModal, showManageMetadataModal,
showLog, showLog,
}: UseDatasetTableColumnsType) { }: UseDatasetTableColumnsType) {
const { t } = useTranslation('translation', { const { t } = useTranslation('translation', {
@ -174,7 +177,26 @@ export function useDatasetTableColumns({
<ParsingStatusCell <ParsingStatusCell
record={row.original} record={row.original}
showChangeParserModal={showChangeParserModal} showChangeParserModal={showChangeParserModal}
showSetMetaModal={showSetMetaModal} showSetMetaModal={(row) =>
showManageMetadataModal({
metadata: util.JSONToMetaDataTableData(row.meta_fields || {}),
isCanAdd: true,
type: MetadataType.UpdateSingle,
record: row,
title: (
<div className="flex flex-col gap-2">
<div className="text-base font-normal">
{t('metadata.editMetadata')}
</div>
<div className="text-sm text-text-secondary">
{t('metadata.editMetadataForDataset')}
{row.name}
</div>
</div>
),
isDeleteSingleValue: true,
})
}
showLog={showLog} showLog={showLog}
></ParsingStatusCell> ></ParsingStatusCell>
); );

View File

@ -16,18 +16,10 @@ import {
FormMessage, FormMessage,
} from '@/components/ui/form'; } from '@/components/ui/form';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { LanguageTranslationMap } from '@/constants/common';
import { FormLayout } from '@/constants/form'; import { FormLayout } from '@/constants/form';
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { useEffect, useMemo } from 'react'; import { useEffect } from 'react';
import { useForm, useWatch } from 'react-hook-form'; import { useForm, useWatch } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { z } from 'zod'; import { z } from 'zod';
@ -42,13 +34,6 @@ const FormId = 'dataset-creating-form';
export function InputForm({ onOk }: IModalProps<any>) { export function InputForm({ onOk }: IModalProps<any>) {
const { t } = useTranslation(); const { t } = useTranslation();
const languageOptions = useMemo(() => {
return Object.keys(LanguageTranslationMap).map((x) => ({
label: x,
value: x,
}));
}, []);
const FormSchema = z const FormSchema = z
.object({ .object({
name: z name: z
@ -66,7 +51,6 @@ export function InputForm({ onOk }: IModalProps<any>) {
.trim(), .trim(),
parser_id: z.string().optional(), parser_id: z.string().optional(),
pipeline_id: z.string().optional(), pipeline_id: z.string().optional(),
language: z.string().optional(),
}) })
.superRefine((data, ctx) => { .superRefine((data, ctx) => {
// When parseType === 1, parser_id is required // When parseType === 1, parser_id is required
@ -80,8 +64,6 @@ export function InputForm({ onOk }: IModalProps<any>) {
path: ['parser_id'], path: ['parser_id'],
}); });
} }
console.log('form-data', data);
// When parseType === 1, pipline_id required // When parseType === 1, pipline_id required
if (data.parseType === 2 && !data.pipeline_id) { if (data.parseType === 2 && !data.pipeline_id) {
ctx.addIssue({ ctx.addIssue({
@ -99,7 +81,6 @@ export function InputForm({ onOk }: IModalProps<any>) {
parseType: 1, parseType: 1,
parser_id: '', parser_id: '',
embd_id: '', embd_id: '',
language: 'English',
}, },
}); });
@ -147,33 +128,6 @@ export function InputForm({ onOk }: IModalProps<any>) {
)} )}
/> />
<FormField
control={form.control}
name="language"
render={({ field }) => (
<FormItem>
<FormLabel>{t('common.language')}</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue
placeholder={t('common.languagePlaceholder')}
/>
</SelectTrigger>
</FormControl>
<SelectContent>
{languageOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<EmbeddingModelItem line={2} isEdit={false} /> <EmbeddingModelItem line={2} isEdit={false} />
<ParseTypeItem /> <ParseTypeItem />
{parseType === 1 && <ChunkMethodItem></ChunkMethodItem>} {parseType === 1 && <ChunkMethodItem></ChunkMethodItem>}

View File

@ -215,6 +215,10 @@ const methods = {
url: check_embedding, url: check_embedding,
method: 'post', method: 'post',
}, },
// getMetaData: {
// url: getMetaData,
// method: 'get',
// },
}; };
const kbService = registerServer<keyof typeof methods>(methods, request); const kbService = registerServer<keyof typeof methods>(methods, request);
@ -251,6 +255,11 @@ export const listDocument = (
export const documentFilter = (kb_id: string) => export const documentFilter = (kb_id: string) =>
request.post(api.get_dataset_filter, { kb_id }); request.post(api.get_dataset_filter, { kb_id });
export const getMetaDataService = ({ kb_id }: { kb_id: string }) =>
request.post(api.getMetaData, { data: { kb_id } });
export const updateMetaData = ({ kb_id, data }: { kb_id: string; data: any }) =>
request.post(api.updateMetaData, { data: { kb_id, data } });
export const listDataPipelineLogDocument = ( export const listDataPipelineLogDocument = (
params?: IFetchKnowledgeListRequestParams, params?: IFetchKnowledgeListRequestParams,
body?: IFetchDocumentListRequestBody, body?: IFetchDocumentListRequestBody,

View File

@ -77,6 +77,8 @@ export default {
unbindPipelineTask: ({ kb_id, type }: { kb_id: string; type: string }) => unbindPipelineTask: ({ kb_id, type }: { kb_id: string; type: string }) =>
`${api_host}/kb/unbind_task?kb_id=${kb_id}&pipeline_task_type=${type}`, `${api_host}/kb/unbind_task?kb_id=${kb_id}&pipeline_task_type=${type}`,
pipelineRerun: `${api_host}/canvas/rerun`, pipelineRerun: `${api_host}/canvas/rerun`,
getMetaData: `${api_host}/document/metadata/summary`,
updateMetaData: `${api_host}/document/metadata/update`,
// tags // tags
listTag: (knowledgeId: string) => `${api_host}/kb/${knowledgeId}/tags`, listTag: (knowledgeId: string) => `${api_host}/kb/${knowledgeId}/tags`,