feat: AI categorisation fallback, unsubscribe confidence scoring, one-line summaries
Heuristic classifier falls back to AI only when it can't determine a category; unsubscribe confidence blends method reliability with an AI safety opinion and surfaces it in the Unsubscribe Manager; emails get an on-demand AI one-line summary in the detail pane. All AI calls degrade gracefully when AI is disabled or the provider errors. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -18,12 +18,14 @@ public class EmailController : ApiControllerBase
|
||||
{
|
||||
private readonly ICleanupService _cleanup;
|
||||
private readonly IUnsubscribeService _unsub;
|
||||
private readonly IAiService _ai;
|
||||
private readonly AppDbContext _db;
|
||||
|
||||
public EmailController(ICleanupService cleanup, IUnsubscribeService unsub, AppDbContext db)
|
||||
public EmailController(ICleanupService cleanup, IUnsubscribeService unsub, IAiService ai, AppDbContext db)
|
||||
{
|
||||
_cleanup = cleanup;
|
||||
_unsub = unsub;
|
||||
_ai = ai;
|
||||
_db = db;
|
||||
}
|
||||
|
||||
@@ -42,6 +44,16 @@ public class EmailController : ApiControllerBase
|
||||
return e is null ? NotFound() : Ok(e);
|
||||
}
|
||||
|
||||
/// <summary>One-line AI summary, fetched on demand (returns the snippet verbatim if AI is disabled).</summary>
|
||||
[HttpGet("{id:guid}/summary")]
|
||||
public async Task<IActionResult> Summary(Guid id, CancellationToken ct)
|
||||
{
|
||||
var e = await _db.Emails.FirstOrDefaultAsync(e => e.Id == id && e.UserId == UserId, ct);
|
||||
if (e is null) return NotFound();
|
||||
var summary = await _ai.SummarizeEmailAsync(e.Subject, e.Snippet, e.BodyText, ct);
|
||||
return Ok(new { summary });
|
||||
}
|
||||
|
||||
[HttpPost("{id:guid}/read")]
|
||||
public Task<IActionResult> MarkRead(Guid id, CancellationToken ct) => Act(id, CleanupActionType.MarkRead, ct);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user