Fix: Issues with metadata parameter addition failures and single-file chunk saving failures. (#12818)

### What problem does this PR solve?

Fix: Issues with metadata parameter addition failures and single-file
chunk saving failures.

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
chanx
2026-01-26 18:00:40 +08:00
committed by GitHub
parent 13076bb87b
commit 1d93519cb2
11 changed files with 170 additions and 93 deletions

View File

@ -144,15 +144,13 @@ export function ChunkMethodDialog({
pages: z
.array(z.object({ from: z.coerce.number(), to: z.coerce.number() }))
.optional(),
metadata: z
metadata: z.any().optional(),
built_in_metadata: z
.array(
z
.object({
key: z.string().optional(),
description: z.string().optional(),
enum: z.array(z.string().optional()).optional(),
})
.optional(),
z.object({
key: z.string().optional(),
type: z.string().optional(),
}),
)
.optional(),
enable_metadata: z.boolean().optional(),

View File

@ -184,6 +184,7 @@ Example: A 1 KB message with 1024-dim embedding uses ~9 KB. The 5 MB default lim
},
knowledgeDetails: {
metadata: {
selectFiles: 'Selected {{count}} files',
type: 'Type',
fieldNameInvalid: 'Field name can only contain letters or underscores.',
builtIn: 'Built-in',
@ -205,10 +206,10 @@ Example: A 1 KB message with 1024-dim embedding uses ~9 KB. The 5 MB default lim
fieldExists: 'Field already exists.',
fieldSetting: 'Field settings',
changesAffectNewParses: 'Changes affect new parses only.',
editMetadataForDataset: 'View and edit metadata for ',
// editMetadataForDataset: 'View and edit metadata for ',
restrictDefinedValues: 'Restrict to defined values',
metadataGenerationSettings: 'Metadata generation settings',
manageMetadataForDataset: 'Manage metadata for this dataset',
// manageMetadataForDataset: 'Manage metadata for this dataset',
manageMetadata: 'Manage metadata',
metadata: 'Metadata',
values: 'Values',

View File

@ -175,6 +175,7 @@ export default {
},
knowledgeDetails: {
metadata: {
selectFiles: '已选择 {{count}} 个文件',
type: '类型',
fieldNameInvalid: '字段名称只能包含字母或下划线。',
builtIn: '内置',
@ -192,10 +193,10 @@ export default {
fieldExists: '字段名已存在。',
fieldSetting: '字段设置',
changesAffectNewParses: '更改仅影响新的解析。',
editMetadataForDataset: '查看和编辑元数据 ',
// editMetadataForDataset: '查看和编辑元数据 ',
restrictDefinedValues: '限制为已定义的值',
metadataGenerationSettings: '元数据生成设置',
manageMetadataForDataset: '管理此数据集的元数据',
// manageMetadataForDataset: '管理此数据集的元数据',
manageMetadata: '管理元数据',
metadata: '元数据',
values: '值',

View File

@ -11,11 +11,7 @@ import { RowSelectionState } from '@tanstack/react-table';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router';
import {
DEFAULT_VALUE_TYPE,
MetadataType,
metadataValueTypeEnum,
} from '../constant';
import { DEFAULT_VALUE_TYPE, MetadataType } from '../constant';
import {
IBuiltInMetadataItem,
IMetaDataReturnJSONSettings,
@ -25,6 +21,7 @@ import {
MetadataOperations,
MetadataValueType,
ShowManageMetadataModalProps,
UpdateOperation,
} from '../interface';
export const util = {
@ -136,6 +133,7 @@ export const useMetadataOperations = () => {
deletes: [],
updates: [],
});
// const operationsRef = useRef(operations);
const addDeleteRow = useCallback((key: string) => {
setOperations((prev) => ({
@ -165,41 +163,60 @@ export const useMetadataOperations = () => {
newValue: string | string[],
type?: MetadataValueType,
) => {
let newValuesRes: string | string[];
if (type !== metadataValueTypeEnum['list']) {
if (Array.isArray(newValue) && newValue.length > 0) {
newValuesRes = newValue[0];
} else {
newValuesRes = newValue;
}
} else {
newValuesRes = newValue;
}
// let newValuesRes: string | string[];
// if (type !== metadataValueTypeEnum['list']) {
// if (Array.isArray(newValue) && newValue.length > 0) {
// newValuesRes = newValue[0];
// } else {
// newValuesRes = newValue;
// }
// } else {
// newValuesRes = newValue;
// }
setOperations((prev) => {
let updatedUpdates = [...prev.updates];
const existsIndex = prev.updates.findIndex(
(update) => update.key === key && update.match === originalValue,
(update) =>
update.key === key &&
update.match === originalValue &&
update.match !== '',
);
if (existsIndex > -1) {
const updatedUpdates = [...prev.updates];
updatedUpdates[existsIndex] = {
key,
match: originalValue,
value: newValuesRes,
value: newValue,
valueType: type || DEFAULT_VALUE_TYPE,
};
return {
...prev,
updates: updatedUpdates,
};
// operationsRef.current = updatedOperations;
} else {
updatedUpdates.push({
key,
match: originalValue,
value: newValue,
valueType: type,
});
}
return {
updatedUpdates = updatedUpdates.reduce((pre, cur) => {
if (
!pre.some(
(item) =>
item.key === cur.key &&
item.match === cur.match &&
item.value === cur.value,
)
) {
pre.push(cur);
}
return pre;
}, [] as UpdateOperation[]);
const updatedOperations = {
...prev,
updates: [
...prev.updates,
{ key, match: originalValue, value: newValuesRes, valueType: type },
],
updates: updatedUpdates,
};
return updatedOperations;
});
},
[],
@ -213,6 +230,7 @@ export const useMetadataOperations = () => {
}, []);
return {
// operationsRef,
operations,
addDeleteBatch,
addDeleteRow,
@ -272,6 +290,7 @@ export const useManageMetaDataModal = (
const [tableData, setTableData] = useState<IMetaDataTableData[]>(metaData);
const queryClient = useQueryClient();
const {
// operationsRef,
operations,
addDeleteRow,
addDeleteBatch,

View File

@ -72,6 +72,7 @@ export type IManageModalProps = {
isVerticalShowValue?: boolean;
builtInMetadata?: IBuiltInMetadataItem[];
success?: (data: any) => void;
secondTitle?: ReactNode;
};
export interface IManageValuesProps {
@ -98,12 +99,12 @@ export interface IManageValuesProps {
addDeleteValue: (key: string, value: string) => void;
}
interface DeleteOperation {
export interface DeleteOperation {
key: string;
value?: string;
}
interface UpdateOperation {
export interface UpdateOperation {
key: string;
match: string;
value: string | string[];

View File

@ -4,12 +4,7 @@ import { Input } from '@/components/ui/input';
import { DateInput } from '@/components/ui/input-date';
import { formatDate } from '@/utils/date';
import { ColumnDef, Row, Table } from '@tanstack/react-table';
import {
ListChevronsDownUp,
ListChevronsUpDown,
Settings,
Trash2,
} from 'lucide-react';
import { ListChevronsDownUp, Settings, Trash2 } from 'lucide-react';
import { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
@ -51,12 +46,16 @@ export const useMetadataColumns = ({
onOk: () => {},
onCancel: () => {},
});
const [expanded, setExpanded] = useState(true);
// const [expanded, setExpanded] = useState(true);
const [editingValue, setEditingValue] = useState<{
field: string;
value: string;
newValue: string;
} | null>(null);
const [rowExpandedStates, setRowExpandedStates] = useState<
Record<string, boolean>
>({});
const isSettingsMode =
metadataType === MetadataType.Setting ||
metadataType === MetadataType.SingleFileSetting ||
@ -142,21 +141,7 @@ export const useMetadataColumns = ({
</div>
),
},
// ...(showTypeColumn
// ? ([
// {
// accessorKey: 'valueType',
// header: () => <span>Type</span>,
// cell: ({ row }) => (
// <div className="text-sm">
// {getMetadataValueTypeLabel(
// row.original.valueType as IMetaDataTableData['valueType'],
// )}
// </div>
// ),
// },
// ] as ColumnDef<IMetaDataTableData>[])
// : []),
{
accessorKey: 'description',
header: () => <span>{t('knowledgeDetails.metadata.description')}</span>,
@ -182,7 +167,7 @@ export const useMetadataColumns = ({
header: () => (
<div className="flex items-center">
<span>{t('knowledgeDetails.metadata.values')}</span>
<div
{/* <div
className="ml-2 p-1 cursor-pointer"
onClick={() => {
setExpanded(!expanded);
@ -194,16 +179,25 @@ export const useMetadataColumns = ({
<ListChevronsUpDown size={14} />
)}
{expanded}
</div>
</div> */}
</div>
),
cell: ({ row }) => {
const values = row.getValue('values') as Array<string>;
const displayedValues = expanded ? values : values.slice(0, 2);
const isRowExpanded = rowExpandedStates[row.original.field] ?? false;
const toggleRowExpanded = () => {
setRowExpandedStates((prev) => ({
...prev,
[row.original.field]: !isRowExpanded,
}));
};
const displayedValues = isRowExpanded ? values : values.slice(0, 2);
const hasMore = Array.isArray(values) && values.length > 2;
return (
<div className="flex flex-col gap-1">
<div className="flex gap-1">
<div className="flex flex-wrap gap-1">
{displayedValues?.map((value: string) => {
const isEditing =
@ -218,7 +212,6 @@ export const useMetadataColumns = ({
<DateInput
value={new Date(editingValue.newValue)}
onChange={(value) => {
console.log('value', value);
const newValue = {
...editingValue,
newValue: formatDate(
@ -228,13 +221,7 @@ export const useMetadataColumns = ({
};
setEditingValue(newValue);
saveEditedValue(newValue);
// onValueChange(index, formatDate(value), true);
}}
// openChange={(open) => {
// console.log('open', open);
// if (!open) {
// }
// }}
showTimeSelect={true}
/>
)}
@ -249,7 +236,7 @@ export const useMetadataColumns = ({
newValue: e.target.value,
})
}
onBlur={saveEditedValue}
onBlur={() => saveEditedValue()}
onKeyDown={(e) => {
if (e.key === 'Enter') {
saveEditedValue();
@ -316,10 +303,37 @@ export const useMetadataColumns = ({
</Button>
);
})}
{hasMore && !expanded && (
<div className="text-text-secondary self-end">...</div>
)}
</div>
{hasMore && !isRowExpanded && (
<Button
variant={'ghost'}
className="border border-border-button h-auto px-2 py-1"
onClick={(e) => {
e.stopPropagation();
toggleRowExpanded();
}}
>
<div className="text-text-secondary">
+{values.length - 2}
</div>
</Button>
)}
{hasMore && isRowExpanded && (
// <div className="self-end mt-1">
<Button
variant={'ghost'}
className="bg-transparent px-2 py-1"
onClick={(e) => {
e.stopPropagation();
toggleRowExpanded();
}}
>
<div className="text-text-secondary">
<ListChevronsDownUp size={14} />
</div>
</Button>
// </div>
)}
</div>
);
},
@ -390,10 +404,10 @@ export const useMetadataColumns = ({
isDeleteSingleValue,
handleEditValueRow,
metadataType,
expanded,
// expanded,
editingValue,
saveEditedValue,
showTypeColumn,
rowExpandedStates,
]);
return {

View File

@ -69,6 +69,7 @@ export const ManageMetadataModal = (props: IManageModalProps) => {
builtInMetadata,
success,
documentIds,
secondTitle,
} = props;
const { t } = useTranslation();
const [valueData, setValueData] = useState<IMetaDataTableData>({
@ -316,7 +317,9 @@ export const ManageMetadataModal = (props: IManageModalProps) => {
<>
<div className="flex flex-col gap-2">
<div className="flex items-center justify-between">
<div>{t('knowledgeDetails.metadata.metadata')}</div>
<div className="w-1/2">
{secondTitle || t('knowledgeDetails.metadata.metadata')}
</div>
<div>
{metadataType === MetadataType.Manage && (
<Button
@ -564,7 +567,7 @@ export const ManageMetadataModal = (props: IManageModalProps) => {
isAddValue={isAddValue || isAddValueMode}
isShowDescription={isShowDescription}
isShowValueSwitch={isShowValueSwitch}
isShowType={isSettingsMode}
isShowType={true}
isVerticalShowValue={isVerticalShowValue}
isAddValueMode={isAddValueMode}
// handleDeleteSingleValue={handleDeleteSingleValue}

View File

@ -40,11 +40,13 @@ import {
util,
} from '../../components/metedata/hooks/use-manage-modal';
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
import {
IBuiltInMetadataItem,
IMetaDataReturnJSONSettings,
} from '../../components/metedata/interface';
import { ManageMetadataModal } from '../../components/metedata/manage-modal';
import { useKnowledgeBaseContext } from '../../contexts/knowledge-base-context';
import {
useHandleKbEmbedding,
useHasParsedDocument,
@ -378,6 +380,7 @@ export function AutoMetadata({
const location = useLocation();
const form = useFormContext();
const datasetContext = useContext(DataSetContext);
const { knowledgeBase } = useKnowledgeBaseContext();
const {
manageMetadataVisible,
showManageMetadataModal,
@ -396,16 +399,31 @@ export function AutoMetadata({
type: type,
record: otherData,
builtInMetadata,
secondTitle: knowledgeBase ? (
<div className="w-full flex items-center gap-1 text-sm text-text-secondary">
<RAGFlowAvatar
avatar={knowledgeBase.avatar}
name={knowledgeBase.name}
className="size-8"
></RAGFlowAvatar>
<div className=" text-text-primary text-base space-y-1 overflow-hidden">
{knowledgeBase.name}
</div>
</div>
) : (
<></>
),
});
}, [form, otherData, showManageMetadataModal, type]);
}, [form, otherData, showManageMetadataModal, knowledgeBase, type]);
useEffect(() => {
const locationState = location.state as
| { openMetadata?: boolean }
| undefined;
if (locationState?.openMetadata && !datasetContext?.loading) {
setTimeout(() => {
const timer = setTimeout(() => {
handleClickOpenMetadata();
clearTimeout(timer);
}, 0);
locationState.openMetadata = false;
history.replace({ ...location }, locationState);
@ -475,6 +493,7 @@ export function AutoMetadata({
isShowValueSwitch={true}
isVerticalShowValue={false}
builtInMetadata={metadataConfig.builtInMetadata}
secondTitle={metadataConfig.secondTitle}
success={(data?: {
metadata?: IMetaDataReturnJSONSettings;
builtInMetadata?: IBuiltInMetadataItem[];

View File

@ -104,14 +104,21 @@ export default function Dataset() {
isEditField: false,
isDeleteSingleValue: true,
isAddValue: true,
secondTitle: (
<>
{t('knowledgeDetails.metadata.selectFiles', {
count: documents.length,
})}
</>
),
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">
{/* <div className="text-sm text-text-secondary">
{t('knowledgeDetails.metadata.manageMetadataForDataset')}
</div>
</div> */}
</div>
),
documentIds: documents.map((doc) => doc.id),
@ -240,9 +247,9 @@ export default function Dataset() {
<div className="text-base font-normal">
{t('knowledgeDetails.metadata.manageMetadata')}
</div>
<div className="text-sm text-text-secondary">
{/* <div className="text-sm text-text-secondary">
{t('knowledgeDetails.metadata.manageMetadataForDataset')}
</div>
</div> */}
</div>
)
}
@ -255,6 +262,7 @@ export default function Dataset() {
isVerticalShowValue={metadataConfig.isVerticalShowValue}
isEditField={metadataConfig.isEditField}
isDeleteSingleValue={metadataConfig.isDeleteSingleValue}
secondTitle={metadataConfig.secondTitle}
type={metadataConfig.type}
documentIds={metadataConfig.documentIds}
otherData={metadataConfig.record}

View File

@ -9,7 +9,14 @@ import {
useSetDocumentStatus,
} from '@/hooks/use-document-request';
import { IDocumentInfo } from '@/interfaces/database/document';
import { Ban, CircleCheck, CircleX, PenIcon, Play, Trash2 } from 'lucide-react';
import {
Ban,
CircleCheck,
CircleX,
Cylinder,
Play,
Trash2,
} from 'lucide-react';
import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { toast } from 'sonner';
@ -143,7 +150,7 @@ export function useBulkOperateDataset({
{
id: 'batch-metadata',
label: t('knowledgeDetails.metadata.metadata'),
icon: <PenIcon />,
icon: <Cylinder />,
},
];

View File

@ -191,10 +191,16 @@ export function useDatasetTableColumns({
<div className="text-base font-normal">
{t('metadata.editMetadata')}
</div>
<div className="text-sm text-text-secondary w-full truncate">
{/* <div className="text-sm text-text-secondary w-full truncate">
{t('metadata.editMetadataForDataset')}
{row.original.name}
</div>
</div> */}
</div>
),
secondTitle: (
<div className="w-full flex gap-1 text-sm text-text-secondary">
<FileIcon name={row.original.name}></FileIcon>
<div className="truncate">{row.original.name}</div>
</div>
),
isDeleteSingleValue: true,