Fixed failed tasks not going away, and processing tasks going away (#393)
This commit is contained in:
parent
7ecd6a23ea
commit
583ba2ded3
2 changed files with 398 additions and 372 deletions
|
|
@ -1,10 +1,10 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
themeQuartz,
|
|
||||||
type CheckboxSelectionCallbackParams,
|
type CheckboxSelectionCallbackParams,
|
||||||
type ColDef,
|
type ColDef,
|
||||||
type GetRowIdParams,
|
type GetRowIdParams,
|
||||||
|
themeQuartz,
|
||||||
type ValueFormatterParams,
|
type ValueFormatterParams,
|
||||||
} from "ag-grid-community";
|
} from "ag-grid-community";
|
||||||
import { AgGridReact, type CustomCellRendererProps } from "ag-grid-react";
|
import { AgGridReact, type CustomCellRendererProps } from "ag-grid-react";
|
||||||
|
|
@ -21,12 +21,6 @@ import "@/components/AgGrid/registerAgGridModules";
|
||||||
import "@/components/AgGrid/agGridStyles.css";
|
import "@/components/AgGrid/agGridStyles.css";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { KnowledgeActionsDropdown } from "@/components/knowledge-actions-dropdown";
|
import { KnowledgeActionsDropdown } from "@/components/knowledge-actions-dropdown";
|
||||||
import { StatusBadge } from "@/components/ui/status-badge";
|
|
||||||
import { DeleteConfirmationDialog } from "../../../components/confirmation-dialog";
|
|
||||||
import { useDeleteDocument } from "../api/mutations/useDeleteDocument";
|
|
||||||
import GoogleDriveIcon from "../settings/icons/google-drive-icon";
|
|
||||||
import OneDriveIcon from "../settings/icons/one-drive-icon";
|
|
||||||
import SharePointIcon from "../settings/icons/share-point-icon";
|
|
||||||
import { KnowledgeSearchInput } from "@/components/knowledge-search-input";
|
import { KnowledgeSearchInput } from "@/components/knowledge-search-input";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
|
|
@ -36,6 +30,12 @@ import {
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
|
import { StatusBadge } from "@/components/ui/status-badge";
|
||||||
|
import { DeleteConfirmationDialog } from "../../../components/confirmation-dialog";
|
||||||
|
import { useDeleteDocument } from "../api/mutations/useDeleteDocument";
|
||||||
|
import GoogleDriveIcon from "../settings/icons/google-drive-icon";
|
||||||
|
import OneDriveIcon from "../settings/icons/one-drive-icon";
|
||||||
|
import SharePointIcon from "../settings/icons/share-point-icon";
|
||||||
|
|
||||||
// Function to get the appropriate icon for a connector type
|
// Function to get the appropriate icon for a connector type
|
||||||
function getSourceIcon(connectorType?: string) {
|
function getSourceIcon(connectorType?: string) {
|
||||||
|
|
@ -76,10 +76,10 @@ function SearchPage() {
|
||||||
|
|
||||||
const { data: searchData = [], isFetching } = useGetSearchQuery(
|
const { data: searchData = [], isFetching } = useGetSearchQuery(
|
||||||
queryOverride,
|
queryOverride,
|
||||||
parsedFilterData
|
parsedFilterData,
|
||||||
);
|
);
|
||||||
// Convert TaskFiles to File format and merge with backend results
|
// Convert TaskFiles to File format and merge with backend results
|
||||||
const taskFilesAsFiles: File[] = taskFiles.map(taskFile => {
|
const taskFilesAsFiles: File[] = taskFiles.map((taskFile) => {
|
||||||
return {
|
return {
|
||||||
filename: taskFile.filename,
|
filename: taskFile.filename,
|
||||||
mimetype: taskFile.mimetype,
|
mimetype: taskFile.mimetype,
|
||||||
|
|
@ -92,15 +92,13 @@ function SearchPage() {
|
||||||
embedding_dimensions: taskFile.embedding_dimensions,
|
embedding_dimensions: taskFile.embedding_dimensions,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create a map of task files by filename for quick lookup
|
// Create a map of task files by filename for quick lookup
|
||||||
const taskFileMap = new Map(
|
const taskFileMap = new Map(
|
||||||
taskFilesAsFiles.map(file => [file.filename, file])
|
taskFilesAsFiles.map((file) => [file.filename, file]),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Override backend files with task file status if they exist
|
// Override backend files with task file status if they exist
|
||||||
const backendFiles = (searchData as File[])
|
const backendFiles = (searchData as File[])
|
||||||
.map(file => {
|
.map((file) => {
|
||||||
const taskFile = taskFileMap.get(file.filename);
|
const taskFile = taskFileMap.get(file.filename);
|
||||||
if (taskFile) {
|
if (taskFile) {
|
||||||
// Override backend file with task file data (includes status)
|
// Override backend file with task file data (includes status)
|
||||||
|
|
@ -108,24 +106,23 @@ function SearchPage() {
|
||||||
}
|
}
|
||||||
return file;
|
return file;
|
||||||
})
|
})
|
||||||
.filter(file => {
|
.filter((file) => {
|
||||||
// Only filter out files that are currently processing AND in taskFiles
|
// Only filter out files that are currently processing AND in taskFiles
|
||||||
const taskFile = taskFileMap.get(file.filename);
|
const taskFile = taskFileMap.get(file.filename);
|
||||||
return !taskFile || taskFile.status !== "processing";
|
return !taskFile || taskFile.status !== "processing";
|
||||||
});
|
});
|
||||||
|
|
||||||
const filteredTaskFiles = taskFilesAsFiles.filter(taskFile => {
|
|
||||||
|
const filteredTaskFiles = taskFilesAsFiles.filter((taskFile) => {
|
||||||
return (
|
return (
|
||||||
taskFile.status !== "active" &&
|
taskFile.status !== "active" &&
|
||||||
!backendFiles.some(
|
!backendFiles.some(
|
||||||
backendFile => backendFile.filename === taskFile.filename
|
(backendFile) => backendFile.filename === taskFile.filename,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Combine task files first, then backend files
|
// Combine task files first, then backend files
|
||||||
const fileResults = [...backendFiles, ...filteredTaskFiles];
|
const fileResults = [...backendFiles, ...filteredTaskFiles];
|
||||||
|
|
||||||
const gridRef = useRef<AgGridReact>(null);
|
const gridRef = useRef<AgGridReact>(null);
|
||||||
|
|
||||||
const columnDefs: ColDef<File>[] = [
|
const columnDefs: ColDef<File>[] = [
|
||||||
|
|
@ -157,8 +154,8 @@ function SearchPage() {
|
||||||
}
|
}
|
||||||
router.push(
|
router.push(
|
||||||
`/knowledge/chunks?filename=${encodeURIComponent(
|
`/knowledge/chunks?filename=${encodeURIComponent(
|
||||||
data?.filename ?? ""
|
data?.filename ?? "",
|
||||||
)}`
|
)}`,
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
@ -244,7 +241,10 @@ function SearchPage() {
|
||||||
className="inline-flex items-center gap-1 text-red-500 transition hover:text-red-400"
|
className="inline-flex items-center gap-1 text-red-500 transition hover:text-red-400"
|
||||||
aria-label="View ingestion error"
|
aria-label="View ingestion error"
|
||||||
>
|
>
|
||||||
<StatusBadge status={status} className="pointer-events-none" />
|
<StatusBadge
|
||||||
|
status={status}
|
||||||
|
className="pointer-events-none"
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
|
|
@ -307,8 +307,8 @@ function SearchPage() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Delete each file individually since the API expects one filename at a time
|
// Delete each file individually since the API expects one filename at a time
|
||||||
const deletePromises = selectedRows.map(row =>
|
const deletePromises = selectedRows.map((row) =>
|
||||||
deleteDocumentMutation.mutateAsync({ filename: row.filename })
|
deleteDocumentMutation.mutateAsync({ filename: row.filename }),
|
||||||
);
|
);
|
||||||
|
|
||||||
await Promise.all(deletePromises);
|
await Promise.all(deletePromises);
|
||||||
|
|
@ -316,7 +316,7 @@ function SearchPage() {
|
||||||
toast.success(
|
toast.success(
|
||||||
`Successfully deleted ${selectedRows.length} document${
|
`Successfully deleted ${selectedRows.length} document${
|
||||||
selectedRows.length > 1 ? "s" : ""
|
selectedRows.length > 1 ? "s" : ""
|
||||||
}`
|
}`,
|
||||||
);
|
);
|
||||||
setSelectedRows([]);
|
setSelectedRows([]);
|
||||||
setShowBulkDeleteDialog(false);
|
setShowBulkDeleteDialog(false);
|
||||||
|
|
@ -329,7 +329,7 @@ function SearchPage() {
|
||||||
toast.error(
|
toast.error(
|
||||||
error instanceof Error
|
error instanceof Error
|
||||||
? error.message
|
? error.message
|
||||||
: "Failed to delete some documents"
|
: "Failed to delete some documents",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -406,7 +406,7 @@ function SearchPage() {
|
||||||
}? This will remove all chunks and data associated with these documents. This action cannot be undone.
|
}? This will remove all chunks and data associated with these documents. This action cannot be undone.
|
||||||
|
|
||||||
Documents to be deleted:
|
Documents to be deleted:
|
||||||
${selectedRows.map(row => `• ${row.filename}`).join("\n")}`}
|
${selectedRows.map((row) => `• ${row.filename}`).join("\n")}`}
|
||||||
confirmText="Delete All"
|
confirmText="Delete All"
|
||||||
onConfirm={handleBulkDelete}
|
onConfirm={handleBulkDelete}
|
||||||
isLoading={deleteDocumentMutation.isPending}
|
isLoading={deleteDocumentMutation.isPending}
|
||||||
|
|
|
||||||
|
|
@ -140,12 +140,30 @@ export function TaskProvider({ children }: { children: React.ReactNode }) {
|
||||||
(prev) => prev.task_id === currentTask.task_id,
|
(prev) => prev.task_id === currentTask.task_id,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Only show toasts if we have previous data and status has changed
|
// Check if task is in progress
|
||||||
if (
|
const isTaskInProgress =
|
||||||
|
currentTask.status === "pending" ||
|
||||||
|
currentTask.status === "running" ||
|
||||||
|
currentTask.status === "processing";
|
||||||
|
|
||||||
|
// On initial load, previousTasksRef is empty, so we need to process all in-progress tasks
|
||||||
|
const isInitialLoad = previousTasksRef.current.length === 0;
|
||||||
|
|
||||||
|
// Process files if:
|
||||||
|
// 1. Task is in progress (always process to keep files list updated)
|
||||||
|
// 2. Status has changed
|
||||||
|
// 3. New task appeared (not on initial load)
|
||||||
|
const shouldProcessFiles =
|
||||||
|
isTaskInProgress ||
|
||||||
(previousTask && previousTask.status !== currentTask.status) ||
|
(previousTask && previousTask.status !== currentTask.status) ||
|
||||||
(!previousTask && previousTasksRef.current.length !== 0)
|
(!previousTask && !isInitialLoad);
|
||||||
) {
|
|
||||||
// Process files from failed task and add them to files list
|
// Only show toasts if we have previous data and status has changed
|
||||||
|
const shouldShowToast =
|
||||||
|
previousTask && previousTask.status !== currentTask.status;
|
||||||
|
|
||||||
|
if (shouldProcessFiles) {
|
||||||
|
// Process files from task and add them to files list
|
||||||
if (currentTask.files && typeof currentTask.files === "object") {
|
if (currentTask.files && typeof currentTask.files === "object") {
|
||||||
const taskFileEntries = Object.entries(currentTask.files);
|
const taskFileEntries = Object.entries(currentTask.files);
|
||||||
const now = new Date().toISOString();
|
const now = new Date().toISOString();
|
||||||
|
|
@ -247,6 +265,7 @@ export function TaskProvider({ children }: { children: React.ReactNode }) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
|
shouldShowToast &&
|
||||||
previousTask &&
|
previousTask &&
|
||||||
previousTask.status !== "completed" &&
|
previousTask.status !== "completed" &&
|
||||||
currentTask.status === "completed"
|
currentTask.status === "completed"
|
||||||
|
|
@ -283,13 +302,14 @@ export function TaskProvider({ children }: { children: React.ReactNode }) {
|
||||||
setFiles((prevFiles) =>
|
setFiles((prevFiles) =>
|
||||||
prevFiles.filter(
|
prevFiles.filter(
|
||||||
(file) =>
|
(file) =>
|
||||||
file.task_id !== currentTask.task_id ||
|
file.status === "active" ||
|
||||||
file.status === "failed",
|
file.status === "failed",
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
refetchSearch();
|
refetchSearch();
|
||||||
}, 500);
|
}, 500);
|
||||||
} else if (
|
} else if (
|
||||||
|
shouldShowToast &&
|
||||||
previousTask &&
|
previousTask &&
|
||||||
previousTask.status !== "failed" &&
|
previousTask.status !== "failed" &&
|
||||||
previousTask.status !== "error" &&
|
previousTask.status !== "error" &&
|
||||||
|
|
@ -321,7 +341,13 @@ export function TaskProvider({ children }: { children: React.ReactNode }) {
|
||||||
);
|
);
|
||||||
|
|
||||||
const refreshTasks = useCallback(async () => {
|
const refreshTasks = useCallback(async () => {
|
||||||
setFiles([]);
|
setFiles((prevFiles) =>
|
||||||
|
prevFiles.filter(
|
||||||
|
(file) =>
|
||||||
|
file.status !== "active" &&
|
||||||
|
file.status !== "failed",
|
||||||
|
),
|
||||||
|
);
|
||||||
await refetchTasks();
|
await refetchTasks();
|
||||||
}, [refetchTasks]);
|
}, [refetchTasks]);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue