mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-08-23 00:31:06 +08:00
Fix: Clearing the files of the dataset selected in the chat should result in an error message appearing on the chat page. (#18510)
This commit is contained in:
@@ -104,38 +104,43 @@ export function useDisableDifferenceEmbeddingDataset(name: string) {
|
||||
}, [datasetId, datasetList]);
|
||||
|
||||
const nextOptions = useMemo(() => {
|
||||
const datasetListMap = datasetList.map((item: IDataset) => {
|
||||
return {
|
||||
label: item.name,
|
||||
icon: () => (
|
||||
<RAGFlowAvatar
|
||||
className="size-4"
|
||||
avatar={item.avatar}
|
||||
name={item.name}
|
||||
/>
|
||||
),
|
||||
suffix: (
|
||||
<section className="flex gap-2">
|
||||
<DatasetLabel text={item.nickname} />
|
||||
<DatasetLabel
|
||||
text={
|
||||
item.embedding_model_name
|
||||
? item.embedding_model_name
|
||||
: item.embedding_model
|
||||
}
|
||||
/>
|
||||
</section>
|
||||
),
|
||||
value: item.id,
|
||||
disabled:
|
||||
item.chunk_count <= 0 ||
|
||||
item.chunk_method === DocumentParserType.Tag ||
|
||||
(selectedEmbedBaseName !== '' &&
|
||||
getEmbeddingBaseName(item.embedding_model) !== selectedEmbedBaseName),
|
||||
};
|
||||
});
|
||||
|
||||
return datasetListMap;
|
||||
return (
|
||||
datasetList
|
||||
// Datasets without chunks are not selectable. A stale selected value
|
||||
// (emptied or deleted dataset) is excluded as well — the MultiSelect
|
||||
// badge falls back to rendering its raw id and stays removable.
|
||||
.filter((item) => item.chunk_count > 0)
|
||||
.map((item: IDataset) => {
|
||||
return {
|
||||
label: item.name,
|
||||
icon: () => (
|
||||
<RAGFlowAvatar
|
||||
className="size-4"
|
||||
avatar={item.avatar}
|
||||
name={item.name}
|
||||
/>
|
||||
),
|
||||
suffix: (
|
||||
<section className="flex gap-2">
|
||||
<DatasetLabel text={item.nickname} />
|
||||
<DatasetLabel
|
||||
text={
|
||||
item.embedding_model_name
|
||||
? item.embedding_model_name
|
||||
: item.embedding_model
|
||||
}
|
||||
/>
|
||||
</section>
|
||||
),
|
||||
value: item.id,
|
||||
disabled:
|
||||
item.chunk_method === DocumentParserType.Tag ||
|
||||
(selectedEmbedBaseName !== '' &&
|
||||
getEmbeddingBaseName(item.embedding_model) !==
|
||||
selectedEmbedBaseName),
|
||||
};
|
||||
})
|
||||
);
|
||||
}, [datasetList, selectedEmbedBaseName]);
|
||||
|
||||
const handleSearchChange = useCallback((value: string) => {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { cva, type VariantProps } from 'class-variance-authority';
|
||||
import {
|
||||
CheckIcon,
|
||||
ChevronDown,
|
||||
TriangleAlert,
|
||||
WandSparkles,
|
||||
XCircle,
|
||||
XIcon,
|
||||
@@ -377,8 +378,20 @@ export const MultiSelect = React.forwardRef<
|
||||
{IconComponent && (
|
||||
<IconComponent className="h-4 w-4" />
|
||||
)}
|
||||
<div className="max-w-28 text-ellipsis overflow-hidden">
|
||||
{option?.label}
|
||||
{/* A selected value with no matching option (e.g. the
|
||||
entity no longer exists) gets a warning marker and
|
||||
falls back to the raw value so the badge stays
|
||||
readable and removable. */}
|
||||
{!option && (
|
||||
<TriangleAlert className="h-4 w-4 flex-shrink-0 text-text-disabled" />
|
||||
)}
|
||||
<div
|
||||
className={cn(
|
||||
'max-w-28 text-ellipsis overflow-hidden',
|
||||
{ 'text-text-disabled': !option },
|
||||
)}
|
||||
>
|
||||
{option?.label ?? value}
|
||||
</div>
|
||||
{canRemoveValue(value) && (
|
||||
<XCircle
|
||||
|
||||
@@ -1145,9 +1145,9 @@ export const useSelectKnowledgeOptions = () => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch datasets by a set of IDs. Used to resolve the names of
|
||||
* already-selected datasets that are not present in the first page of
|
||||
* the paginated list so they can be echoed back in the form field.
|
||||
* Fetch datasets by a set of IDs. Used to resolve already-selected datasets
|
||||
* that are not present in the first page of the paginated list, e.g. to echo
|
||||
* their names in a form field. For staleness checks see `useStaleDatasetIds`.
|
||||
*/
|
||||
export const useFetchDatasetsByIds = (ids: string[]) => {
|
||||
const sortedIds = useMemo(() => [...ids].sort(), [ids]);
|
||||
@@ -1164,6 +1164,29 @@ export const useFetchDatasetsByIds = (ids: string[]) => {
|
||||
return { data, loading };
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolve which of the given persisted dataset ids have gone stale — the
|
||||
* dataset no longer exists or has been emptied of chunks. The set stays
|
||||
* empty while the lookup is in flight so consumers can hold off validation
|
||||
* until it settles; `settled` flips true once the lookup has finished.
|
||||
*/
|
||||
export const useStaleDatasetIds = (datasetIds?: string[]) => {
|
||||
const persistedIds = useMemo(() => datasetIds ?? [], [datasetIds]);
|
||||
const { data: datasets, loading } = useFetchDatasetsByIds(persistedIds);
|
||||
|
||||
const staleDatasetIds = useMemo(() => {
|
||||
if (loading) {
|
||||
return new Set<string>();
|
||||
}
|
||||
const usableIds = new Set(
|
||||
(datasets ?? []).filter((x) => x.chunk_count > 0).map((x) => x.id),
|
||||
);
|
||||
return new Set(persistedIds.filter((id) => !usableIds.has(id)));
|
||||
}, [datasets, loading, persistedIds]);
|
||||
|
||||
return { staleDatasetIds, settled: !loading };
|
||||
};
|
||||
|
||||
//#region tags
|
||||
export const useRenameTag = () => {
|
||||
const knowledgeBaseId = useKnowledgeBaseId();
|
||||
|
||||
@@ -1040,6 +1040,8 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s
|
||||
knowledgeBases: 'Datasets',
|
||||
knowledgeBasesPlaceholder: 'Select value',
|
||||
knowledgeBasesMessage: 'Please select',
|
||||
datasetUnavailable:
|
||||
'The selected knowledge base is unavailable (deleted or has no chunks), please re-select',
|
||||
knowledgeBasesTip:
|
||||
'Select the datasets to associate with this chat assistant. An empty dataset will not appear in the dropdown list.',
|
||||
system: 'System prompt',
|
||||
|
||||
@@ -938,6 +938,7 @@ NER:使用 spaCy NER 和基于规则的关键词提取来抽取 Entities 和 R
|
||||
knowledgeBases: '知识库',
|
||||
knowledgeBasesPlaceholder: '请选择',
|
||||
knowledgeBasesMessage: '请选择',
|
||||
datasetUnavailable: '所选知识库不可用(已删除或无 chunk),请重新选择',
|
||||
knowledgeBasesTip:
|
||||
'选择关联的知识库。新建或空知识库不会在下拉菜单中显示。',
|
||||
system: '系统提示词',
|
||||
|
||||
@@ -4,6 +4,7 @@ import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import { DatasetMetadata } from '@/constants/chat';
|
||||
import { useSetModalState } from '@/hooks/common-hooks';
|
||||
import { useFetchChat, useUpdateChat } from '@/hooks/use-chat-request';
|
||||
import { useStaleDatasetIds } from '@/hooks/use-knowledge-request';
|
||||
import { useFindLlmByUuid } from '@/hooks/use-llm-request';
|
||||
import { cn } from '@/lib/utils';
|
||||
import {
|
||||
@@ -14,7 +15,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { isEmpty, omit } from 'lodash';
|
||||
import { LucidePanelRightClose, LucideSettings } from 'lucide-react';
|
||||
import { useEffect } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useForm, useWatch } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useParams } from 'react-router';
|
||||
import { z } from 'zod';
|
||||
@@ -27,8 +28,16 @@ import { getWebSearchProvider } from '../web-search-api-key';
|
||||
type ChatSettingsProps = { hasSingleChatBox: boolean };
|
||||
|
||||
export function ChatSettings({ hasSingleChatBox }: ChatSettingsProps) {
|
||||
const formSchema = useChatSettingSchema();
|
||||
const { data } = useFetchChat();
|
||||
|
||||
// Only the persisted ids need validation: ids picked from the dataset
|
||||
// select are valid by construction, while a persisted id may reference a
|
||||
// dataset that has since been deleted or emptied of chunks.
|
||||
const { staleDatasetIds, settled: datasetsFetched } = useStaleDatasetIds(
|
||||
data?.dataset_ids,
|
||||
);
|
||||
|
||||
const formSchema = useChatSettingSchema(staleDatasetIds);
|
||||
const { updateChat, loading } = useUpdateChat();
|
||||
const findLlmByUuid = useFindLlmByUuid();
|
||||
const { id } = useParams();
|
||||
@@ -42,6 +51,7 @@ export function ChatSettings({ hasSingleChatBox }: ChatSettingsProps) {
|
||||
const form = useForm<FormSchemaType>({
|
||||
resolver: zodResolver(formSchema),
|
||||
shouldUnregister: false,
|
||||
mode: 'onChange',
|
||||
defaultValues: {
|
||||
name: '',
|
||||
icon: '',
|
||||
@@ -145,6 +155,20 @@ export function ChatSettings({ hasSingleChatBox }: ChatSettingsProps) {
|
||||
}
|
||||
}, [data, form]);
|
||||
|
||||
const datasetIds = useWatch({ control: form.control, name: 'dataset_ids' });
|
||||
const trigger = form.trigger;
|
||||
|
||||
// A persisted dataset_ids value never fires onChange validation, so once
|
||||
// the lookup of those ids has settled, revalidate explicitly — it may
|
||||
// reference datasets that have since been deleted or emptied of chunks.
|
||||
useEffect(() => {
|
||||
if (!datasetsFetched || !datasetIds?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
trigger('dataset_ids');
|
||||
}, [trigger, datasetsFetched, datasetIds.length]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{settingVisible || (
|
||||
|
||||
@@ -13,7 +13,7 @@ import { WebSearchProvider } from '@/constants/chat';
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { z } from 'zod';
|
||||
|
||||
export function useChatSettingSchema() {
|
||||
export function useChatSettingSchema(staleDatasetIds: Set<string>) {
|
||||
const { t } = useTranslate('chat');
|
||||
|
||||
const promptConfigSchema = z.object({
|
||||
@@ -65,5 +65,15 @@ export function useChatSettingSchema() {
|
||||
...MetadataFilterSchema,
|
||||
});
|
||||
|
||||
return formSchema;
|
||||
// A persisted dataset_ids value may reference datasets that have since been
|
||||
// deleted or emptied of chunks — those stale ids are flagged here.
|
||||
return formSchema.superRefine((data, ctx) => {
|
||||
if (data.dataset_ids.some((id) => staleDatasetIds.has(id))) {
|
||||
ctx.addIssue({
|
||||
path: ['dataset_ids'],
|
||||
message: t('datasetUnavailable'),
|
||||
code: z.ZodIssueCode.custom,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user