mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-18 10:18:00 +00:00
Added mailboxes as local data sources (#1022)
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Sync Flatpak repo (push) Blocked by required conditions
Build and Release / Collect Flatpak artifacts (push) Blocked by required conditions
Build and Release / Verify (push) Waiting to run
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Sync Flatpak repo (push) Blocked by required conditions
Build and Release / Collect Flatpak artifacts (push) Blocked by required conditions
Build and Release / Verify (push) Waiting to run
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
This commit is contained in:
1 parent
93be539e50
commit
c4400c0ff6
343 files changed
+26051
-3122
No files matched your search
@@ -0,0 +1,101 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using AIStudio.Settings;
|
||||
using AIStudio.Settings.DataModel;
|
||||
using AIStudio.Tools.Mail;
|
||||
using AIStudio.Tools.Security;
|
||||
|
||||
using MailKit;
|
||||
|
||||
namespace AIStudio.Tools.Services.Indexing;
|
||||
|
||||
/// <remarks>
|
||||
/// The attachments of a mail: which of them are read, how, and how their text joins the chunks of
|
||||
/// the mail.
|
||||
/// </remarks>
|
||||
internal sealed partial class MailboxIndexer
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads the text of one attachment, should the rules of the mailbox allow it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// An attachment which cannot be read costs the mail nothing but that attachment: the mail is
|
||||
/// indexed with the reason stored in place of the text. Only a lost connection fails the mail,
|
||||
/// since every other mail would fail the same way.
|
||||
/// </remarks>
|
||||
/// <param name="context">The run the mail is indexed in.</param>
|
||||
/// <param name="mailbox">The mailbox the mail belongs to.</param>
|
||||
/// <param name="connector">The connection, with the folder of the mail open.</param>
|
||||
/// <param name="uid">The UID of the mail.</param>
|
||||
/// <param name="part">The attachment, from the structure of the mail.</param>
|
||||
/// <param name="name">The file name of the attachment, filtered for prompt injections.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
/// <returns>The attachment with its text, or with the reason why there is none.</returns>
|
||||
/// <exception cref="MailboxConnectionException">The server could not deliver the attachment.</exception>
|
||||
private async Task<MailAttachmentText> ReadAttachmentAsync(IndexedRunContext context, DataSourceMailbox mailbox, ImapMailboxConnector connector, UniqueId uid, BodyPartBasic part, string name, CancellationToken token)
|
||||
{
|
||||
if (MailAttachmentRules.GetReasonToSkip(part, MailAttachmentRules.GetMaxSizeMegabytes(mailbox)) is { } reasonToSkip)
|
||||
return MailAttachmentText.WithoutText(part, name, reasonToSkip);
|
||||
|
||||
string? path = null;
|
||||
try
|
||||
{
|
||||
path = MailAttachmentFiles.CreatePath(MailAttachmentRules.GetExtension(part));
|
||||
await using (var file = new FileStream(path, FileMode.CreateNew, FileAccess.Write, FileShare.None))
|
||||
await connector.FetchAttachmentAsync(uid, part, file, token);
|
||||
|
||||
// Filtered by the runtime while it reads, and reported as content of the mailbox, not of a file the user never saw:
|
||||
var content = await ExtractedFileText.ReadAsync(rustService, path, context.EmbeddingProvider, PromptInjectionSource.MailContent(mailbox.Name), token);
|
||||
return new(part, name, MailPartTextState.EXTRACTED, content, TextChunker.GetStrategyForFile(path));
|
||||
}
|
||||
catch (OperationCanceledException) when (token.IsCancellationRequested)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (MailboxConnectionException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
logger.LogWarning(exception, "The text of an attachment of a mail in mailbox '{MailboxId}' could not be read. The mail is indexed without it.", mailbox.Id);
|
||||
return MailAttachmentText.WithoutText(part, name, MailPartTextState.EXTRACTION_FAILED);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (path is not null)
|
||||
MailAttachmentFiles.Delete(path, logger);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cuts a mail into chunks: first its own text, then the text of each attachment.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Each attachment is cut on its own, by the strategy of its file type, and its first chunk
|
||||
/// names it. A chunk of an attachment carries no page: the chunks of a mail are told apart by
|
||||
/// the mail alone, so a page would not say which attachment it is a page of.
|
||||
/// </remarks>
|
||||
/// <param name="mailContent">The header block and the text of the mail.</param>
|
||||
/// <param name="attachments">The attachments, those whose text was not read included.</param>
|
||||
/// <param name="options">How large a chunk may become.</param>
|
||||
/// <param name="embeddingProvider">The embedding provider whose tokenizer measures the chunks.</param>
|
||||
/// <param name="token">The cancellation token.</param>
|
||||
/// <returns>The chunks of the mail, in order.</returns>
|
||||
private async IAsyncEnumerable<EmbeddingChunk> StreamMailChunksAsync(SegmentedText mailContent, IReadOnlyList<MailAttachmentText> attachments, ChunkingOptions options, EmbeddingProvider embeddingProvider, [EnumeratorCancellation] CancellationToken token)
|
||||
{
|
||||
await foreach (var chunk in textChunker.SplitAsync(mailContent, TextChunker.DOCUMENT_STRATEGY, options, embeddingProvider, token))
|
||||
yield return chunk;
|
||||
|
||||
foreach (var attachment in attachments)
|
||||
{
|
||||
if (attachment.TextState is not MailPartTextState.EXTRACTED || attachment.Content.Text.Length is 0)
|
||||
continue;
|
||||
|
||||
// In English like the header block, cf. MailTextBuilder:
|
||||
var heading = attachment.Name.Length > 0 ? $"Attachment: {attachment.Name}" : "Attachment without a name";
|
||||
await foreach (var chunk in textChunker.SplitAsync(attachment.Content, attachment.Strategy, options, embeddingProvider, token, heading))
|
||||
yield return chunk with { PageNumber = null };
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user