Skip to content

Commit ebdd570

Browse files
committed
feat: implement background cleanup for uploaded files and enhance attachment removal logic
1 parent 336f937 commit ebdd570

2 files changed

Lines changed: 27 additions & 8 deletions

File tree

src/Dashboard/AI/Tools/UploadedFileTools.cs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,22 @@ public sealed record UploadEntry(
3535
private const int TimeoutSeconds = 30;
3636
private const long MaxBytes = 100L * 1024 * 1024; // 100 MB
3737

38+
// .xls intentionally omitted — pandas needs the (uninstalled) xlrd package for legacy xls.
3839
private static readonly HashSet<string> SupportedExtensions = new(StringComparer.OrdinalIgnoreCase)
3940
{
40-
".csv", ".tsv", ".json", ".txt", ".log", ".md", ".xlsx", ".xls", ".pdf", ".parquet"
41+
".csv", ".tsv", ".json", ".txt", ".log", ".md", ".xlsx", ".pdf", ".parquet"
4142
};
4243

44+
static UploadedFileTools()
45+
{
46+
// Background TTL sweep — without this, expired temp files only get evicted
47+
// when a new upload happens. Fires every 5 min, ignores its own exceptions.
48+
_cleanupTimer = new Timer(_ => { try { Cleanup(); } catch { } },
49+
null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
50+
}
51+
52+
private static readonly Timer _cleanupTimer;
53+
4354
private readonly UserTokens _tokens; // not used today, kept for symmetry with other per-user tools
4455

4556
public UploadedFileTools(UserTokens tokens) => _tokens = tokens;
@@ -219,10 +230,11 @@ public static IReadOnlyList<UploadEntry> ListForUser(long userId)
219230

220231
public static bool RemoveForUser(long userId, string fileId)
221232
{
222-
// Soft remove: keep the entry + temp file alive so prior tool-call results in the
223-
// chat history remain valid for follow-ups. The 30-min TTL (Cleanup) and
224-
// /api/chat/reset (ClearForUser) handle actual disposal.
225-
return UserFiles.TryGetValue(userId, out var bucket) && bucket.ContainsKey(fileId);
233+
// Remove from the user's listing so the LLM context block no longer shows it,
234+
// but keep the temp file on disk so any prior tool-call results in the chat
235+
// history remain valid for replay. Actual file disposal happens on
236+
// /api/chat/reset (ClearForUser) or via the 30-min TTL sweep.
237+
return UserFiles.TryGetValue(userId, out var bucket) && bucket.TryRemove(fileId, out _);
226238
}
227239

228240
public static void ClearForUser(long userId)

src/Dashboard/client/src/components/ChatView.vue

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1648,10 +1648,17 @@ async function uploadFiles(files) {
16481648
}
16491649
16501650
function removeAttachment(att) {
1651-
// Soft remove on the client only — keep the file alive server-side so
1652-
// prior tool-call results in chat history stay valid for follow-ups.
1653-
// Actual disposal happens on /api/chat/reset or 30-min TTL.
1651+
// Drop from the client list immediately, and tell the server to remove it
1652+
// from the per-user listing so the next chat turn no longer surfaces this
1653+
// fileId in the [UPLOADED FILES…] context block. The temp file is kept
1654+
// on disk so prior tool-call results in chat history remain valid; full
1655+
// disposal happens on /api/chat/reset or via the 30-min TTL.
16541656
attachments.value = attachments.value.filter((a) => a !== att);
1657+
if (att.fileId) {
1658+
fetch(`/api/uploads/${encodeURIComponent(att.fileId)}`, {
1659+
method: "DELETE",
1660+
}).catch(() => {});
1661+
}
16551662
}
16561663
16571664
function formatBytes(n) {

0 commit comments

Comments
 (0)