mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-13 23:13:48 +00:00
Improved mailboxes to include sent mails and drafts (#1038)
This commit is contained in:
1 parent
67d44588d4
commit
5c319c45a0
31 files changed
+792
-103
No files matched your search
@@ -20,4 +20,10 @@ internal enum DataSourceEmbeddingRefreshMode
|
||||
/// changes, e.g. a mailbox on a server.
|
||||
/// </summary>
|
||||
INTERVAL_CHECK,
|
||||
|
||||
/// <summary>
|
||||
/// A tool is about to read what may have changed since the last run, e.g. the drafts of a
|
||||
/// mailbox. Unlike a retry of the user, it never signs in despite a refused sign-in.
|
||||
/// </summary>
|
||||
TOOL_REQUEST,
|
||||
}
|
||||
@@ -408,6 +408,32 @@ public sealed partial class DataSourceEmbeddingService(SettingsManager settingsM
|
||||
: Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Syncs a mailbox soon, because a tool is about to read what may have changed on its server since the last sync.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Runs like a sync at the interval: only while the user lets the data sources refresh on their
|
||||
/// own, and never despite a refused sign-in. A sync which is running already gets one follow-up,
|
||||
/// since it may have looked at the folder in question before the change. One which is queued
|
||||
/// already stays the only one.
|
||||
/// </remarks>
|
||||
/// <param name="mailboxId">The id of the mailbox.</param>
|
||||
/// <returns>True when a sync is on its way; false when the user switched the automatic refresh off, the mailbox is not configured, or it waits for the user to sign in again.</returns>
|
||||
public async Task<bool> RequestMailboxSyncAsync(string mailboxId)
|
||||
{
|
||||
if (!settingsManager.ConfigurationData.App.DataSourceIndexing.AutomaticRefresh)
|
||||
return false;
|
||||
|
||||
if (!this.TryGetConfiguredIndexedSource(mailboxId, out var dataSource) || dataSource is not DataSourceMailbox || !this.IsSupportedIndexedSource(dataSource))
|
||||
return false;
|
||||
|
||||
if (this.statuses.TryGetValue(mailboxId, out var status) && IsWaitingForSignIn(status))
|
||||
return false;
|
||||
|
||||
await this.QueueDataSourceAsync(dataSource, true, DataSourceEmbeddingRefreshMode.TOOL_REQUEST);
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task QueueDataSourceAsync(IDataSourceBase dataSource, bool queueAfterCurrentRun, DataSourceEmbeddingRefreshMode refreshMode)
|
||||
{
|
||||
if (!this.IsSupportedIndexedSource(dataSource))
|
||||
|
||||
@@ -219,7 +219,7 @@ internal sealed partial class MailboxIndexer(SettingsManager settingsManager, Ru
|
||||
|
||||
try
|
||||
{
|
||||
var selection = MailFolderSelection.Select(await connector.GetFoldersAsync(token), mailbox.RootFolder);
|
||||
var selection = MailFolderSelection.Select(await connector.GetFoldersAsync(token), mailbox.RootFolder, mailbox.IncludeSentAndDrafts);
|
||||
if (!selection.RootFolderFound)
|
||||
{
|
||||
//
|
||||
@@ -385,7 +385,7 @@ internal sealed partial class MailboxIndexer(SettingsManager settingsManager, Ru
|
||||
var serverState = await connector.OpenFolderAsync(folder.FullName, token);
|
||||
var storedFolder = storedFolders.GetValueOrDefault(folder.FullName);
|
||||
var storedLocations = await context.IndexStore.GetMailLocationsAsync(mailbox.Id, folder.FullName, token);
|
||||
var indexedUids = await connector.SearchIndexedMailsAsync(receivedSince, token);
|
||||
var indexedUids = await connector.SearchIndexedMailsAsync(MailFolderSelection.GetReceivedSince(folder, receivedSince), token);
|
||||
var plan = MailFolderSyncPlan.Create(storedFolder, storedLocations, serverState, indexedUids);
|
||||
|
||||
//
|
||||
@@ -498,7 +498,7 @@ internal sealed partial class MailboxIndexer(SettingsManager settingsManager, Ru
|
||||
/// </remarks>
|
||||
private static async Task<PlannedRemovals> CollectRemovalsAsync(IndexedRunContext context, DataSourceMailbox mailbox, IReadOnlyList<PlannedFolder> plannedFolders, IEnumerable<string> storedFolderPaths, CancellationToken token)
|
||||
{
|
||||
// Folders which the server no longer lists, or which no longer lie below the root folder:
|
||||
// Folders which the server no longer lists, or which the sync no longer selects, e.g. since the mailbox no longer includes sent mails and drafts:
|
||||
var plannedPaths = plannedFolders.Select(folder => folder.Folder.FullName).ToHashSet(StringComparer.Ordinal);
|
||||
var goneFolderPaths = storedFolderPaths.Where(path => !plannedPaths.Contains(path)).ToList();
|
||||
|
||||
|
||||
@@ -10,4 +10,5 @@ namespace AIStudio.Tools.Services;
|
||||
/// <param name="Summary">What a list of mails shows about the mail, e.g., its subject and its folders.</param>
|
||||
/// <param name="Mail">Its addresses, its parts as AI Studio read them, and its places on the server.</param>
|
||||
/// <param name="InReplyToMailId">The id of the mail it replies to, or null when the index does not hold that one.</param>
|
||||
public sealed record MailReading(DataSourceMailbox Mailbox, MailSummary Summary, MailRecord Mail, string? InReplyToMailId);
|
||||
/// <param name="MailboxFolders">The folders of the mailbox, which tell what the folders of the mail are for, e.g., the sent mails.</param>
|
||||
public sealed record MailReading(DataSourceMailbox Mailbox, MailSummary Summary, MailRecord Mail, string? InReplyToMailId, IReadOnlyList<MailFolderRecord> MailboxFolders);
|
||||
@@ -5,7 +5,7 @@ namespace AIStudio.Tools.Services;
|
||||
/// <summary>
|
||||
/// How far the index of a mailbox reaches, so that an answer from it can say what it does not cover.
|
||||
/// </summary>
|
||||
/// <param name="ReceivedSinceUtc">Since when mails are indexed, or null when all of them are. Flagged mails are indexed regardless of their age.</param>
|
||||
/// <param name="ReceivedSinceUtc">Since when mails are indexed, or null when all of them are. Flagged mails and drafts are indexed regardless of their age.</param>
|
||||
/// <param name="LastCompleteSyncUtc">When the last complete sync ended, or null while the first one is still running.</param>
|
||||
/// <param name="SignInRefusedAtUtc">When the server refused to let AI Studio sign in, or null when it did not. Until the user deals with that, no new mail arrives in the index.</param>
|
||||
/// <param name="PendingRemovalCount">How many mails the index still holds although a sync would have removed them, waiting for the user to agree, or null when there are none.</param>
|
||||
|
||||
@@ -381,7 +381,8 @@ public sealed class MailboxRetrievalService(SettingsManager settingsManager, Dat
|
||||
continue;
|
||||
|
||||
var inReplyToMailId = await indexStore.FindMailByMessageIdAsync(mailbox.Id, mail.InReplyTo, token);
|
||||
return new(mailbox, summaries[0], mail, inReplyToMailId == mailId ? null : inReplyToMailId);
|
||||
var folders = await indexStore.GetMailFoldersAsync(mailbox.Id, token);
|
||||
return new(mailbox, summaries[0], mail, inReplyToMailId == mailId ? null : inReplyToMailId, folders);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
Reference in new issue
Block a user