Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
c16b358
update style for oss of the failed task in the task panel
May 21, 2026
ee99d06
keep logic on click, remove unecessary useeffect
May 21, 2026
d3f05c1
fix padding
May 21, 2026
1d4f425
wip implementing Saas style
May 21, 2026
e1fa26d
utils to reshape error until backend provide info we need
May 21, 2026
1e5e7e3
utils to reshape error until backend provide info we need
May 21, 2026
550b759
utils to reshape error until backend provide info we need and fixinf …
May 21, 2026
4cadb5f
utils to reshape error until backend provide into
May 21, 2026
45d782c
have Saas style for failed and complete labelstatus and width and border
May 21, 2026
8a7b330
few style adjustment to follow codebase pattern
May 21, 2026
5984d90
adjust succeed and partially succeed case
May 21, 2026
a30991d
adding comment for TODO implementation or more clarity
May 21, 2026
0f877c2
Merge branch 'main' into task-panel-expend-error-style
Wallgau May 21, 2026
8acdc31
Merge branch 'main' into task-ingest-dialog
May 25, 2026
357ca70
Introduce a modular TaskDialog for reviewing task files from the error
May 26, 2026
1246fa6
Merge branch 'main' into task-ingest-dialog
Wallgau May 26, 2026
d51cfe2
Backend
May 27, 2026
f418f5f
style: ruff autofix (auto)
autofix-ci[bot] May 27, 2026
f15efa0
Merge branch 'main' into task-ingest-dialog
Wallgau May 28, 2026
4fff47b
lint fix and re-rendering in gridRows changes
May 28, 2026
42816a1
style: ruff autofix (auto)
autofix-ci[bot] May 28, 2026
8a9b701
normalize task dialog styling with tokens and brand-aware surfaces:
May 28, 2026
b85bf44
style: ruff autofix (auto)
autofix-ci[bot] May 28, 2026
7a3dbb1
adjust backend retry to work for connectors too
May 28, 2026
cb4722a
Treat disconnect / remote protocol errors as RETRYABLE Langflow fail…
May 28, 2026
12d6c26
addressed rabbit code comments
May 28, 2026
b3d1ad1
style: ruff autofix (auto)
autofix-ci[bot] May 28, 2026
bfec39a
addressed rabbit code comments
May 28, 2026
145773b
use regenerated-key retry safely
May 28, 2026
5e94758
Merge branch 'main' into task-ingest-dialog
Wallgau May 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions frontend/app/api/mutations/useCancelTaskMutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import {
useMutation,
useQueryClient,
} from "@tanstack/react-query";
import { taskDetailQueryKey } from "@/app/api/queries/useGetTaskQuery";
import { TASKS_QUERY_KEY } from "@/app/api/queries/useGetTasksQuery";

export interface CancelTaskRequest {
taskId: string;
Expand Down Expand Up @@ -36,12 +38,19 @@ export const useCancelTaskMutation = (
return response.json();
}

const { onSuccess, onError, onSettled, ...restOptions } = options ?? {};

return useMutation({
mutationFn: cancelTask,
onSuccess: () => {
// Invalidate tasks query to refresh the list
queryClient.invalidateQueries({ queryKey: ["tasks"] });
...restOptions,
onSuccess: (data, variables, context) => {
queryClient.invalidateQueries({ queryKey: [...TASKS_QUERY_KEY] });
queryClient.invalidateQueries({
queryKey: taskDetailQueryKey(variables.taskId),
});
onSuccess?.(data, variables, context);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
},
...options,
onError,
onSettled,
});
};
13 changes: 7 additions & 6 deletions frontend/app/api/mutations/useDeleteDocument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ interface DeleteDocumentResponse {
message: string;
}

const deleteDocument = async (
data: DeleteDocumentRequest,
): Promise<DeleteDocumentResponse> => {
export async function deleteDocumentByFilename(
filename: string,
): Promise<DeleteDocumentResponse> {
const response = await fetch("/api/documents/delete-by-filename", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
body: JSON.stringify({ filename } satisfies DeleteDocumentRequest),
});

if (!response.ok) {
Expand All @@ -30,13 +30,14 @@ const deleteDocument = async (
}

return response.json();
};
}

export const useDeleteDocument = () => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: deleteDocument,
mutationFn: ({ filename }: DeleteDocumentRequest) =>
deleteDocumentByFilename(filename),
onSettled: () => {
// Invalidate and refetch search queries to update the UI
setTimeout(() => {
Expand Down
84 changes: 84 additions & 0 deletions frontend/app/api/mutations/useRetryTaskMutation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import {
type UseMutationOptions,
useMutation,
useQueryClient,
} from "@tanstack/react-query";
import { taskDetailQueryKey } from "@/app/api/queries/useGetTaskQuery";
import { TASKS_QUERY_KEY } from "@/app/api/queries/useGetTasksQuery";

export interface RetryTaskRequest {
taskId: string;
/** When set, only these task file paths are retried. Omit to retry all failed RETRYABLE files. */
filePaths?: string[];
}

export interface RetryTaskSkippedFile {
file_path: string;
filename?: string;
reason:
| "not_retryable"
| "source_file_missing"
| "file_not_in_task"
| "not_failed"
| string;
}

export interface RetryTaskResponse {
task_id: string;
retried: number;
skipped: RetryTaskSkippedFile[];
status: string;
message?: string;
error?: string;
}

export const useRetryTaskMutation = (
options?: Omit<
UseMutationOptions<RetryTaskResponse, Error, RetryTaskRequest>,
"mutationFn"
>,
) => {
const queryClient = useQueryClient();

async function retryTask(
variables: RetryTaskRequest,
): Promise<RetryTaskResponse> {
const body = JSON.stringify(
variables.filePaths != null ? { file_paths: variables.filePaths } : {},
);

const response = await fetch(`/api/tasks/${variables.taskId}/retry`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body,
});

const payload = (await response
.json()
.catch(() => ({}))) as RetryTaskResponse;

if (!response.ok) {
throw new Error(
payload.message || payload.error || "Failed to retry task files",
);
}

return payload;
}

const { onSuccess, onError, onSettled, ...restOptions } = options ?? {};

return useMutation({
mutationFn: retryTask,
...restOptions,
onSuccess: (data, variables, context) => {
queryClient.invalidateQueries({ queryKey: [...TASKS_QUERY_KEY] });
queryClient.invalidateQueries({
queryKey: taskDetailQueryKey(variables.taskId),
});
onSuccess?.(data, variables, context);
},
onError,
onSettled,
});
};
33 changes: 33 additions & 0 deletions frontend/app/api/queries/useGetTaskQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { type UseQueryOptions, useQuery } from "@tanstack/react-query";
import type { Task } from "@/app/api/queries/useGetTasksQuery";
export const TASK_DETAIL_QUERY_KEY = ["tasks", "detail"] as const;

export function taskDetailQueryKey(taskId: string) {
return [...TASK_DETAIL_QUERY_KEY, taskId] as const;
}

export function useGetTaskQuery(
taskId: string | null,
options?: Omit<UseQueryOptions<Task | null>, "queryKey" | "queryFn">,
) {
return useQuery({
queryKey: taskId
? taskDetailQueryKey(taskId)
: [...TASK_DETAIL_QUERY_KEY, "idle"],
queryFn: async (): Promise<Task | null> => {
if (!taskId) {
return null;
}
const response = await fetch(`/api/tasks/${taskId}/enhanced`);
if (response.status === 404) {
return null;
}
if (!response.ok) {
throw new Error("Failed to fetch task");
}
return response.json() as Promise<Task>;
},
...options,
enabled: options?.enabled ?? !!taskId,
});
}
43 changes: 35 additions & 8 deletions frontend/app/api/queries/useGetTasksQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,25 @@ import {
useQueryClient,
} from "@tanstack/react-query";

/** Component that failed, from GET /tasks/enhanced file metadata. */
export type TaskFailureComponent =
| "docling"
| "openrag"
| "langflow"
| "opensearch";

/** Pipeline or validation step where failure occurred. */
export type TaskFailurePhase =
| "parsing"
| "chunking"
| "embedding"
| "indexing"
| "file_validation"
| "unknown";

/** Who can resolve the failure (enhanced API). */
export type TaskActionableBy = "USER_ACTIONABLE" | "RETRYABLE";

export interface TaskFileEntry {
status?:
| "pending"
Expand All @@ -21,7 +40,14 @@ export interface TaskFileEntry {
filename?: string;
embedding_model?: string;
embedding_dimensions?: number;
[key: string]: unknown;
phase?: "docling" | "langflow" | "complete" | string;
docling_status?: string;
docling_task_id?: string;
/** Present on failed files when the enhanced API can classify the failure. */
component?: TaskFailureComponent;
failure_phase?: TaskFailurePhase;
user_facing_message?: string;
actionable_by?: TaskActionableBy;
}

export interface Task {
Expand Down Expand Up @@ -51,13 +77,15 @@ export interface TasksResponse {
tasks: Task[];
}

export const TASKS_QUERY_KEY = ["tasks", "enhanced"] as const;

export const useGetTasksQuery = (
options?: Omit<UseQueryOptions<Task[]>, "queryKey" | "queryFn">,
) => {
const queryClient = useQueryClient();

async function getTasks(): Promise<Task[]> {
const response = await fetch("/api/tasks");
const response = await fetch("/api/tasks/enhanced");

if (!response.ok) {
throw new Error("Failed to fetch tasks");
Expand All @@ -69,13 +97,12 @@ export const useGetTasksQuery = (

const queryResult = useQuery(
{
queryKey: ["tasks"],
queryKey: [...TASKS_QUERY_KEY],
queryFn: getTasks,
refetchInterval: (query) => {
// Only poll if there are tasks with pending or running status
const data = query.state.data;
if (!data || data.length === 0) {
return false; // Stop polling if no tasks
return false;
}

const hasActiveTasks = data.some(
Expand All @@ -85,11 +112,11 @@ export const useGetTasksQuery = (
task.status === "processing",
);

return hasActiveTasks ? 3000 : false; // Poll every 3 seconds if active tasks exist
return hasActiveTasks ? 3000 : false;
},
refetchIntervalInBackground: true,
staleTime: 0, // Always consider data stale to ensure fresh updates
gcTime: 5 * 60 * 1000, // Keep in cache for 5 minutes
staleTime: 0,
gcTime: 5 * 60 * 1000,
...options,
},
queryClient,
Expand Down
17 changes: 17 additions & 0 deletions frontend/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@
--placeholder-foreground: 240 5% 65%;

/* App-level UI tokens (safe defaults, override per brand/theme) */
--canvas: 0 0% 100%;
--border-subtle-background-contextual: var(--border);
/* Task dialog layout (file row indent = px + checkbox + gaps + chevron) */
--task-dialog-width: 40rem;
--task-dialog-max-height: 90vh;
--task-dialog-file-type-width: 10rem;
--task-dialog-error-indent: 4.5rem;
--task-dialog-error-indent-cloud: 4.75rem;
--task-dialog-oss-bg: var(--background);
--task-dialog-oss-selected: var(--muted);
--z-task-dialog-menu: 100;
--layered-select-bg: hsl(var(--muted) / 0.5);
--chat-surface-gradient: rgba(69, 137, 255, 0.16);
--chat-input-border: hsl(var(--input));
Expand Down Expand Up @@ -118,6 +129,10 @@
--placeholder-foreground: 240 4% 46%;

/* App-level UI tokens (safe defaults, override per brand/theme) */
--canvas: 0 0% 3.9%; /* #0A0A0A */
--border-subtle-background-contextual: var(--border);
--task-dialog-oss-bg: 0 0% 9.02%; /* #171717 */
--task-dialog-oss-selected: 0 0% 13.73%; /* #232323 */
--layered-select-bg: hsl(var(--muted) / 0.5);
--chat-surface-gradient: rgba(69, 137, 255, 0.16);
--chat-input-border: hsl(var(--input));
Expand Down Expand Up @@ -236,6 +251,7 @@
--layered-select-bg: rgba(131, 131, 131, 0.24);
--chat-input-border: #f4f4f4;
--icon-disabled: #525252;
--border-subtle-background-contextual: 0 0% 82%;
/* Contextual layer (~Gray 100); use hsl(var(--layer-contextual)) */
/* Layer / contextual surfaces (IBM Gray 10 light, Gray 100 dark) */
--layer-contextual: 0 0% 96%;
Expand Down Expand Up @@ -336,6 +352,7 @@
--icon-primary: 0 0% 96%; /* #f4f4f4*/
--placeholder: 0 0% 44%; /* #6F6F6F */

--border-subtle-background-contextual: 0 0% 22.4%; /* #393939 */
--layer-contextual: 0 0% 15%;
--layer-contextual-foreground: 0 0% 96%;
--text-text-01: 0 0% 96%;
Expand Down
Loading
Loading