Skip to content

Commit 2d0933c

Browse files
committed
Simplify InboxAnchor to single-label triage
1 parent 0a9879c commit 2d0933c

6 files changed

Lines changed: 538 additions & 303 deletions

File tree

inboxanchor/core/rules.py

Lines changed: 69 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,15 @@
33
from datetime import datetime, timedelta, timezone
44
from typing import Optional
55

6-
from inboxanchor.mail_intelligence import (
7-
is_high_value_newsletter,
8-
looks_automated_email,
9-
recommend_mailbox_labels,
10-
)
6+
from inboxanchor.mail_intelligence import assign_single_label
117
from inboxanchor.models import (
128
EmailClassification,
139
EmailMessage,
1410
EmailRecommendation,
1511
WorkspacePolicy,
1612
)
1713
from inboxanchor.models.email import EmailCategory, RecommendationStatus
14+
from inboxanchor.sender_intelligence import analyze_message_signals
1815

1916

2017
class RulesEngine:
@@ -29,32 +26,78 @@ def recommend(
2926
now = now or datetime.now(timezone.utc)
3027
policy = policy or WorkspacePolicy()
3128
age = now - email.received_at
32-
labels = (
33-
recommend_mailbox_labels(
34-
sender=email.sender,
35-
subject=email.subject,
36-
snippet=email.snippet,
37-
body=email.content_for_processing(),
38-
has_attachments=email.has_attachments,
39-
category=classification.category,
40-
priority=classification.priority,
41-
)
42-
if policy.auto_label_recommendations
43-
else []
44-
)
45-
automated = looks_automated_email(
29+
body = email.content_for_processing()
30+
signals = analyze_message_signals(email)
31+
single_label = assign_single_label(
4632
sender=email.sender,
4733
subject=email.subject,
4834
snippet=email.snippet,
49-
body=email.content_for_processing(),
35+
body=body,
36+
has_attachments=email.has_attachments,
37+
signals=signals,
5038
)
51-
high_value_newsletter = is_high_value_newsletter(
52-
sender=email.sender,
53-
subject=email.subject,
54-
snippet=email.snippet,
55-
body=email.content_for_processing(),
39+
labels = [single_label] if policy.auto_label_recommendations else []
40+
automated = signals.automated
41+
high_value_newsletter = signals.high_value_newsletter
42+
43+
cleanup_guard = any(
44+
(
45+
email.has_attachments,
46+
signals.finance_invoice,
47+
signals.finance_receipt,
48+
signals.security,
49+
signals.reply_needed,
50+
classification.category
51+
in {
52+
EmailCategory.urgent,
53+
EmailCategory.work,
54+
EmailCategory.finance,
55+
EmailCategory.opportunity,
56+
EmailCategory.personal,
57+
},
58+
)
5659
)
5760

61+
if (
62+
single_label == "cleanup"
63+
and classification.category == EmailCategory.spam_like
64+
and classification.confidence >= 0.85
65+
and not cleanup_guard
66+
):
67+
return EmailRecommendation(
68+
email_id=email.id,
69+
recommended_action="archive",
70+
reason="Spam-like email detected — auto-archived.",
71+
confidence=classification.confidence,
72+
status=RecommendationStatus.safe,
73+
requires_approval=False,
74+
proposed_labels=labels,
75+
)
76+
77+
if (
78+
single_label == "cleanup"
79+
and classification.category
80+
in {
81+
EmailCategory.promo,
82+
EmailCategory.low_priority,
83+
EmailCategory.unknown,
84+
EmailCategory.newsletter,
85+
}
86+
and automated
87+
and classification.confidence >= 0.80
88+
and not high_value_newsletter
89+
and not cleanup_guard
90+
):
91+
return EmailRecommendation(
92+
email_id=email.id,
93+
recommended_action="archive",
94+
reason="Low-value automated email — auto-archived.",
95+
confidence=classification.confidence,
96+
status=RecommendationStatus.safe,
97+
requires_approval=False,
98+
proposed_labels=labels,
99+
)
100+
58101
if (
59102
policy.allow_newsletter_mark_read
60103
and classification.category == EmailCategory.newsletter
@@ -100,7 +143,7 @@ def recommend(
100143
confidence=classification.confidence,
101144
status=RecommendationStatus.requires_approval,
102145
requires_approval=True,
103-
proposed_labels=labels or ["cleanup/low-priority"],
146+
proposed_labels=labels or ["cleanup"],
104147
)
105148

106149
if (

0 commit comments

Comments
 (0)