this a project for email filtering
Find a file
lorentz 3bfda9e585 Initial commit: Howl M365 email management daemon
Full implementation of the Howl email triage system:
- Microsoft Graph API integration with MSAL auth (client-credentials and delegated modes)
- Claude LLM classification via tool use for structured output
- PostgreSQL database with customers, vendors, whitelist, and email_log tables
- Alembic migration for full schema
- APScheduler daemon with graceful shutdown
- Typer CLI (run, dry-run, status commands)
- Business rule classifier with overrides (whitelist protection, low-confidence fallback)
- Action executor (move to folders, flag, escalate with webhook)
- 35 passing unit tests
- README, SETUP, and NEXT_STEPS documentation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 16:02:24 -04:00
howl Initial commit: Howl M365 email management daemon 2026-04-01 16:02:24 -04:00
migrations Initial commit: Howl M365 email management daemon 2026-04-01 16:02:24 -04:00
tests Initial commit: Howl M365 email management daemon 2026-04-01 16:02:24 -04:00
.env.example Initial commit: Howl M365 email management daemon 2026-04-01 16:02:24 -04:00
.gitignore Initial commit: Howl M365 email management daemon 2026-04-01 16:02:24 -04:00
alembic.ini Initial commit: Howl M365 email management daemon 2026-04-01 16:02:24 -04:00
NEXT_STEPS.md Initial commit: Howl M365 email management daemon 2026-04-01 16:02:24 -04:00
pyproject.toml Initial commit: Howl M365 email management daemon 2026-04-01 16:02:24 -04:00
README.md Initial commit: Howl M365 email management daemon 2026-04-01 16:02:24 -04:00
SETUP.md Initial commit: Howl M365 email management daemon 2026-04-01 16:02:24 -04:00

Howl

An M365 email management daemon that uses the Microsoft Graph API and Claude AI to automatically classify and route incoming email based on your customer, vendor, and whitelist database.

How it works

Inbox (M365)
    │
    ▼
Graph API polls for unread mail
    │
    ▼
PostgreSQL lookup — is the sender a customer, vendor, or trusted contact?
    │
    ▼
Claude (LLM) classifies the email using sender context + body content
    │
    ▼
Business rules applied (whitelist always wins, low-confidence → hold for review)
    │
    ▼
Action taken: move to folder / flag / escalate with notification
    │
    ▼
email_log table records every decision for audit and retry

Features

  • Supports both shared mailboxes (app-only, no user login) and personal mailboxes (delegated device-code auth)
  • No emails are ever deleted — only moved, flagged, or escalated
  • Dry-run mode — analyze and log without touching any email
  • Idempotent — safe to restart; already-processed messages are skipped
  • Automatic retry — failed messages are retried on the next poll cycle
  • Full audit log — every classification, confidence score, and action is stored in PostgreSQL

Folder routing

Sender type LLM classification Action
Customer Inquiry / correspondence Move to Customers folder
Vendor Invoice / notification Move to Vendors folder
Whitelist Trusted contact Mark read, stay in Inbox
Any Urgent / escalation Move to Escalate + webhook notification
Any Uncertain (low confidence) Move to Needs Review
Unknown Spam / newsletter Move to Junk Email

Quick start

# 1. Create and activate a virtual environment
python3 -m venv .venv && source .venv/bin/activate

# 2. Install dependencies
pip install -e ".[dev]"

# 3. Configure environment
cp .env.example .env
# Edit .env with your Azure, Anthropic, and PostgreSQL credentials

# 4. Run database migrations
alembic upgrade head

# 5. Test with dry-run (no email mutations)
howl dry-run --limit 10

# 6. Start the daemon
howl run

CLI commands

Command Description
howl run Start the daemon (blocking, polls on schedule)
howl dry-run [--limit N] Classify up to N emails, log results, take no action
howl status [--limit N] Display recent email_log entries in a table

Project structure

howl/
├── howl/
│   ├── config.py           # All configuration (pydantic-settings + .env)
│   ├── main.py             # CLI entry point (Typer)
│   ├── logging_config.py   # structlog setup (JSON or text)
│   ├── db/
│   │   ├── engine.py       # Async SQLAlchemy engine
│   │   ├── models.py       # ORM models
│   │   └── queries.py      # lookup_sender(), email log helpers
│   ├── graph/
│   │   ├── auth.py         # MSAL token provider (client-credentials or delegated)
│   │   └── client.py       # Graph API: list, read, move, flag messages
│   ├── llm/
│   │   ├── client.py       # Anthropic SDK wrapper with retry
│   │   ├── prompts.py      # Prompt builder (system prompt + per-email user turn)
│   │   └── schemas.py      # EmailClassification model + tool definition
│   ├── pipeline/
│   │   ├── processor.py    # EmailProcessor — orchestrates the full pipeline
│   │   ├── classifier.py   # Business rule overrides on LLM output
│   │   └── actions.py      # ActionExecutor — Graph mutations + webhook
│   └── daemon/
│       └── scheduler.py    # APScheduler poll loop + graceful shutdown
├── migrations/             # Alembic database migrations
├── tests/                  # Unit and integration tests
├── .env.example            # Configuration template
├── SETUP.md                # Detailed setup and configuration guide
└── NEXT_STEPS.md           # Roadmap and future improvements

Running tests

pytest tests/ -q                          # All tests (no external services needed)
pytest tests/ -q -m "not integration"    # Unit tests only (default)
pytest tests/ -q -m integration          # Requires real M365 + Anthropic credentials

Requirements

  • Python 3.11+
  • PostgreSQL 14+
  • Microsoft Azure app registration with Mail.Read + Mail.ReadWrite permissions
  • Anthropic API key