Files
Inboxintel/src/InboxIntel.Application/Abstractions/IServices.cs
T
cesnimda d78fe601ff
CI / backend (push) Successful in 52s
CI / frontend (push) Successful in 14s
Deploy Staging / deploy (push) Successful in 26s
CI / backend (pull_request) Successful in 53s
CI / frontend (pull_request) Successful in 16s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 55s
Security / secrets (pull_request) Successful in 4s
Security / dependencies (pull_request) Successful in 53s
feat(ai): IEmbeddingProvider foundation (#18)
2026-07-02 02:05:57 +02:00

123 lines
6.2 KiB
C#

using InboxIntel.Application.Common;
using InboxIntel.Application.DTOs;
using InboxIntel.Domain.Enums;
namespace InboxIntel.Application.Abstractions;
/// <summary>Orchestrates full + incremental Gmail sync, with resume support.</summary>
public interface ISyncService
{
Task RunFullSyncAsync(Guid userId, CancellationToken ct = default);
Task RunIncrementalSyncAsync(Guid userId, CancellationToken ct = default);
/// <summary>Marks the sync as starting and queues it for the background worker.</summary>
Task QueueSyncAsync(Guid userId, bool fullSync, CancellationToken ct = default);
Task<SyncStatus> GetStatusAsync(Guid userId, CancellationToken ct = default);
/// <summary>Progress snapshot for the sync splash screen.</summary>
Task<DTOs.SyncProgressDto> GetProgressAsync(Guid userId, CancellationToken ct = default);
}
public interface IAnalyticsService
{
Task<DashboardSummaryDto> GetDashboardAsync(Guid userId, CancellationToken ct = default);
Task<InboxHealthDto> GetInboxHealthAsync(Guid userId, CancellationToken ct = default);
Task<IReadOnlyList<SenderStatDto>> GetTopSendersAsync(Guid userId, int take = 20, CancellationToken ct = default);
Task<IReadOnlyList<TimeSeriesPointDto>> GetVolumeOverTimeAsync(Guid userId, int days = 90, CancellationToken ct = default);
Task<IReadOnlyList<HeatmapCellDto>> GetHeatmapAsync(Guid userId, CancellationToken ct = default);
Task<IReadOnlyList<CategoryHeatmapCellDto>> GetCategoryHeatmapAsync(Guid userId, CancellationToken ct = default);
Task<IReadOnlyList<AttachmentBreakdownDto>> GetAttachmentBreakdownAsync(Guid userId, CancellationToken ct = default);
Task<SidebarCountsDto> GetSidebarCountsAsync(Guid userId, CancellationToken ct = default);
Task RefreshAggregatesAsync(Guid userId, CancellationToken ct = default);
}
public interface ISearchService
{
Task<PagedResult<EmailSummaryDto>> SearchAsync(Guid userId, SearchRequestDto request, CancellationToken ct = default);
}
public interface ICleanupService
{
Task<CleanupPreviewDto> PreviewAsync(Guid userId, CleanupRequestDto request, CancellationToken ct = default);
Task<Result<CleanupResultDto>> ExecuteAsync(Guid userId, CleanupRequestDto request, CancellationToken ct = default);
}
public interface IUnsubscribeService
{
Task<IReadOnlyList<UnsubscribeItemDto>> GetSafeToUnsubscribeAsync(Guid userId, CancellationToken ct = default);
Task DetectAsync(Guid userId, CancellationToken ct = default);
Task<Result<CleanupResultDto>> ProcessQueueAsync(Guid userId, UnsubscribeRequestDto request, CancellationToken ct = default);
}
/// <summary>
/// AI layer. May be disabled, local (Ollama) or cloud (OpenAI). Per the
/// safety rules, AI NEVER executes destructive actions - it only suggests.
/// </summary>
public interface IAiService
{
bool IsEnabled { get; }
Task<AiClassificationDto> ClassifyAsync(Guid userId, Guid emailId, CancellationToken ct = default);
/// <summary>
/// Lightweight classification used as a sync-time fallback when the
/// heuristic rule set doesn't match anything specific. Takes raw fields
/// directly (no DB lookup) since the email entity may not be persisted
/// yet during sync.
/// </summary>
Task<EmailCategory> ClassifyFallbackAsync(string? subject, string senderAddress, string? snippet, CancellationToken ct = default);
/// <summary>
/// AI opinion (0-1) on how safe it is to unsubscribe from a sender, given
/// the sender address and a recent subject line. Returns 0.5 (neutral) if
/// AI is disabled or the call fails - callers blend this with a heuristic.
/// </summary>
Task<double> ScoreUnsubscribeConfidenceAsync(string senderAddress, string? recentSubject, int emailCount, CancellationToken ct = default);
/// <summary>One-line plain-language summary of a single email, for list/preview UIs.</summary>
Task<string> SummarizeEmailAsync(string? subject, string? snippet, string? bodyText, CancellationToken ct = default);
Task<InboxSummaryDto> SummarizeInboxAsync(Guid userId, CancellationToken ct = default);
Task<IReadOnlyList<AiCleanupSuggestionDto>> SuggestCleanupAsync(Guid userId, CancellationToken ct = default);
Task<GeneratedQueryDto> GenerateQueryAsync(Guid userId, string naturalLanguage, CancellationToken ct = default);
}
/// <summary>Low-level chat completion abstraction implemented by Ollama / OpenAI providers.</summary>
public interface IAiProvider
{
AiProviderMode Mode { get; }
Task<string> CompleteAsync(string systemPrompt, string userPrompt, CancellationToken ct = default);
}
/// <summary>
/// Produces vector embeddings for text (the foundation for semantic search, near-duplicate
/// detection, and "find similar"). Kept separate from <see cref="IAiProvider"/> because
/// embeddings are a distinct capability with their own model. The Null implementation returns
/// an empty vector and <see cref="IsAvailable"/> = false, so callers detect unavailability and
/// fall back to lexical search — AI is never required for core functionality.
/// </summary>
public interface IEmbeddingProvider
{
/// <summary>False for the Null provider (AI off / no embedding model configured).</summary>
bool IsAvailable { get; }
/// <summary>Embed a single text. Returns an empty array when unavailable.</summary>
Task<float[]> EmbedAsync(string text, CancellationToken ct = default);
/// <summary>Embed many texts, result aligned to input order. Empty list when unavailable.</summary>
Task<IReadOnlyList<float[]>> EmbedBatchAsync(IReadOnlyList<string> texts, CancellationToken ct = default);
}
public enum ExportFormat { Pdf, Csv, Json }
public interface IExportService
{
Task<(byte[] Content, string ContentType, string FileName)> ExportReportAsync(Guid userId, ExportFormat format, CancellationToken ct = default);
}
/// <summary>SMTP-backed email delivery, used for digest notifications. No-ops if SMTP is not configured.</summary>
public interface IEmailSender
{
bool IsEnabled { get; }
Task SendAsync(string toAddress, string subject, string htmlBody, CancellationToken ct = default);
}
/// <summary>Builds and sends the periodic inbox digest for opted-in users.</summary>
public interface IDigestService
{
Task SendDigestAsync(Guid userId, CancellationToken ct = default);
}