@@ -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 )
0 commit comments