fix(threads): clean up local thread data after thread deletion (#1262)

* fix(threads): clean up local thread data after thread deletion

Delete DeerFlow-managed thread directories after the web UI removes a LangGraph thread.
This keeps local thread data in sync with conversation deletion and adds regression coverage for the cleanup flow.

* fix(threads): address thread cleanup review feedback

Encode thread cleanup URLs in the web client, keep cache updates explicit when no thread search data is cached, and return a generic 500 response from the cleanup endpoint while documenting the sanitized error behavior.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
This commit is contained in:
amdoi7.
2026-03-24 00:36:08 +08:00
committed by GitHub
parent 79acc3939a
commit 8b0f3fe233
11 changed files with 240 additions and 9 deletions

View File

@@ -8,6 +8,7 @@ import { toast } from "sonner";
import type { PromptInputMessage } from "@/components/ai-elements/prompt-input";
import { getAPIClient } from "../api";
import { getBackendBaseURL } from "../config";
import { useI18n } from "../i18n/hooks";
import type { FileInMessage } from "../messages/utils";
import type { LocalSettings } from "../settings";
@@ -481,6 +482,20 @@ export function useDeleteThread() {
return useMutation({
mutationFn: async ({ threadId }: { threadId: string }) => {
await apiClient.threads.delete(threadId);
const response = await fetch(
`${getBackendBaseURL()}/api/threads/${encodeURIComponent(threadId)}`,
{
method: "DELETE",
},
);
if (!response.ok) {
const error = await response
.json()
.catch(() => ({ detail: "Failed to delete local thread data." }));
throw new Error(error.detail ?? "Failed to delete local thread data.");
}
},
onSuccess(_, { threadId }) {
queryClient.setQueriesData(
@@ -488,11 +503,17 @@ export function useDeleteThread() {
queryKey: ["threads", "search"],
exact: false,
},
(oldData: Array<AgentThread>) => {
(oldData: Array<AgentThread> | undefined) => {
if (oldData == null) {
return oldData;
}
return oldData.filter((t) => t.thread_id !== threadId);
},
);
},
onSettled() {
void queryClient.invalidateQueries({ queryKey: ["threads", "search"] });
},
});
}