Skip to content

nerevilo/inbox-concierge

Repository files navigation

Inbox Concierge

AI-powered email triage that classifies your Gmail inbox into smart buckets — so you see what matters first.

Most email clients give you a search bar and a prayer. Inbox Concierge takes a different approach: a five-stage streaming pipeline that syncs your Gmail, embeds every thread into 384-dimensional vector space, classifies with a cascade of heuristics, semantic similarity, and LLM refinement, then triages with urgency scoring and action detection — all streamed to your browser in real time via Server-Sent Events.

The result is an inbox where the signal is already separated from the noise before you even look at it.


How It Works

Every email runs through five pipeline stages, each progressively enriching and classifying:

┌──────────┐    ┌──────────────┐    ┌──────────────┐    ┌─────────────────┐    ┌──────────────┐
│  1 SYNC  │───▸│  2 EMBED +   │───▸│  3 DOMAIN    │───▸│  4 SEMANTIC +   │───▸│  5 ANALYZE   │
│          │    │   ENRICH     │    │   FASTPATH   │    │    LLM CLASSIFY │    │   + TRIAGE   │
│ Gmail    │    │ 384-dim      │    │ Known sender │    │ Exemplar cosine │    │ Urgency,     │
│ History  │    │ vectors +    │    │ rules: ~40%  │    │ similarity →    │    │ deadlines,   │
│ API      │    │ security     │    │ resolved at  │    │ Claude Sonnet   │    │ action items,│
│ delta    │    │ scan         │    │ zero LLM     │    │ for the rest    │    │ reply status │
│ sync     │    │              │    │ cost         │    │                 │    │              │
└──────────┘    └──────────────┘    └──────────────┘    └─────────────────┘    └──────────────┘

Each stage streams progress events to the frontend. Buckets fill in as classification completes — you don't wait for the full run to see results.

The Classification Cascade

This is the core design insight: three tiers of classification at increasing cost, each catching what the previous tier couldn't.

Tier 1 — Domain Fast-Path (free, instant) Pattern matching on sender domain. GitHub notifications → Notifications. Substack → Newsletters. Amazon → Promotions. About 40% of email resolves here at zero LLM cost.

Tier 2 — Semantic Similarity (cheap, fast) Each thread's embedding is compared against weighted exemplar embeddings per bucket (top-K=5, weighted mean). If the margin between the best and second-best bucket exceeds 0.15, it's classified with high confidence. The system bootstraps immediately — new buckets get synthetic exemplar embeddings generated from their description, so there's no cold-start problem.

Tier 3 — LLM Refinement (precise, last resort) Low-confidence threads go to Claude Sonnet 4 with bucket descriptions and few-shot examples. If Claude fails, Gemini 2.0 Flash takes over. The LLM sees the full context — subject, sender, snippet, attachments, security flags — and returns structured classifications via tool use.

The exemplar pool is self-improving: every high-confidence classification (>0.7) becomes a new weighted exemplar, so the semantic tier catches more over time and the LLM tier handles less.


Features

Smart Bucket Creation

Describe a category in plain English — "receipts and order confirmations from online purchases" — and the system generates a rich description, boundary notes, synthetic exemplar embeddings, and overlap warnings against existing buckets. New buckets start classifying accurately from the moment they're created.

Security Scanning

Every thread is scanned in parallel for:

  • Phishing patterns — "verify your account", "suspended", "unusual activity"
  • Financial fraud — "wire transfer urgently", "prize claim"
  • Suspicious URLs — shortened links (bit.ly, t.co)
  • PII exposure — SSN and credit card number patterns
  • Dangerous attachments — executables (.exe, .bat, .ps1), archives (.zip, .rar)

Flagged threads surface with risk scores and security badges so nothing dangerous hides in the noise.

Priority Action Queue

A weighted formula ranks urgency, deadlines, action items, and reply status to surface what matters most. Each item links directly to the original thread in Gmail. For threads needing a response, the system generates draft replies in multiple tones — professional, concise, friendly — personalized to your writing style (extracted from your sent mail).

Email Health Dashboard

Volume trends, top senders, reply rates, urgency distribution, action item counts, and deadline tracking — all computed from parallel SQL aggregations over the enriched data. Available in 7-day, 30-day, and 90-day views.

Daily Briefing

An AI-generated narrative summary of today's high-priority emails. What needs your attention, what can wait, what's just noise — in one paragraph.

Attachments

Recursive extraction from nested MIME parts. Attachment filenames factor into embeddings and classification. File type icons and tooltips in the thread list. Dangerous extensions flagged by the security scanner.

Draft Replies & Send

AI-generated draft replies with tone selection, inline editing, two-click send confirmation, and an audit trail. Gracefully handles OAuth scope upgrades when gmail.send permission is needed.

Real-Time Streaming UI

The pipeline streams SSE events as each stage progresses. Buckets fill in as threads are classified. The analyze stage shows a progress bar as it works through triage. No spinners, no waiting for the full run — you see results as they happen.

Demo Mode

Try the full inbox experience without connecting a Google account. Eighteen realistic fixture threads across five buckets, with a working dashboard, action queue, and daily briefing. One click to enter, no signup required.


Stack

Layer Technology Role
Framework Next.js 16, React 19 App Router, RSC, SSE streaming
Database Neon Postgres, pgvector Serverless DB + 384-dim vector similarity
ORM Drizzle Type-safe queries, migrations
Primary LLM Claude Sonnet 4 Classification, triage, draft generation
Fallback LLM Gemini 2.0 Flash Fallback classification + triage
Embeddings Gemini embedding-001 384-dim thread vectors (batched 100/req)
Email Gmail API OAuth, incremental sync, send
Auth JOSE (JWT) httpOnly secure session cookies
UI Tailwind CSS, Framer Motion Styling + animation
Charts Recharts Dashboard visualizations
Icons Lucide React Consistent icon set
Testing Playwright E2E tests
Deployment Vercel Edge-optimized hosting

Getting Started

Prerequisites

  • Node.js 18+
  • pnpm
  • A Neon Postgres database with pgvector enabled
  • Google Cloud project with Gmail API + OAuth credentials
  • Anthropic API key (Claude)
  • Google AI API key (Gemini embeddings + fallback)

Setup

pnpm install
cp .env.local.example .env.local

Fill in .env.local:

# Google OAuth
GOOGLE_CLIENT_ID=your-client-id
GOOGLE_CLIENT_SECRET=your-client-secret

# Database
DATABASE_URL=postgresql://user:pass@your-project.neon.tech/neondb?sslmode=require

# Session
SESSION_SECRET=any-random-uuid

# LLMs
ANTHROPIC_API_KEY=sk-ant-...
GEMINI_API_KEY=AI...

# App URL
NEXT_PUBLIC_URL=http://localhost:3100

Run migrations and start:

pnpm db:migrate
pnpm dev              # → http://localhost:3100

Type Checking

pnpm tsc --noEmit

E2E Tests

npx playwright test --reporter=list

Database Schema

The data model centers around classifications — one row per user per Gmail thread, progressively enriched through the pipeline stages.

users ──────────┐
  │              │
  ├─▸ buckets    │
  │    │         │
  │    ├─▸ tags  │
  │    │         │
  │    ├─▸ categoryExemplars (384-dim embeddings, weighted)
  │    │
  │    └─▸ classifications (thread metadata, embedding, enrichment, triage)
  │         │
  │         ├─▸ draftResponses ──▸ draftSendLog
  │         │
  │         └─▸ reclassificationLog
  │
  ├─▸ aiUsage (cost tracking per LLM call)
  │
  └─▸ userStyleProfiles (writing style extraction)

Key design decisions:

  • Embeddings stored in Postgres via pgvector — no external vector DB needed
  • Exemplars are weighted (0.0–1.0) with source tracking (description, confirmed, synthetic)
  • Idempotent upserts — re-running the pipeline only processes new/unclassified threads
  • Cost tracking per LLM call with per-model pricing

Default Buckets

Bucket Color What goes here
Important Blue Person-to-person email, high-urgency threads
Can Wait Amber Non-urgent but relevant — FYIs, team updates
Newsletters Teal Substack, Beehiiv, mailing lists
Promotions Green Marketing, deals, product updates
Auto-Archive Gray Receipts, shipping confirmations, 2FA codes, password resets

Users can create custom buckets at any time. The LLM generates synthetic exemplars from the description, so new buckets classify accurately immediately.


Architecture Decisions

Why three classification tiers? LLM calls are slow and expensive. Domain heuristics are instant and free. Semantic similarity is fast and cheap. By cascading through these tiers, roughly 40% of email never touches an LLM, another 30-40% resolves via semantic matching, and only the genuinely ambiguous 20-30% goes to Claude. This keeps per-user costs low while maintaining high accuracy.

Why exemplar-based semantic matching instead of fine-tuning? Exemplars adapt in real time. Every user correction improves the embedding space immediately — no retraining, no batch jobs, no model versioning. Synthetic exemplars solve the cold-start problem. Weight decay (0.5x after 90 days) keeps the space fresh.

Why SSE streaming instead of polling? The pipeline takes 10-30 seconds for a full run. Polling would either be too slow (missing progress updates) or too aggressive (wasted requests). SSE gives real-time progress with zero overhead — each stage reports its status as it completes, and partial results render immediately.

Why Neon Postgres with pgvector instead of a dedicated vector DB? One database for everything: relational data, vector similarity, and aggregation queries. No sync layer, no consistency issues, no extra infrastructure. Neon's serverless scaling handles the load, and pgvector's IVFFlat indexes keep similarity search fast enough for this use case.

Why Claude + Gemini instead of just one? Claude Sonnet 4 produces the best classification results, but availability isn't 100%. Gemini 2.0 Flash is faster and cheaper with slightly lower accuracy. The dual-provider setup means the pipeline never hard-fails — it gracefully degrades from Claude to Gemini to heuristic passthrough.


API Routes

Route Method Description
/api/auth/google GET Initiates Google OAuth flow
/api/auth/callback GET OAuth callback, creates session
/api/auth/demo POST Creates demo session, redirects to inbox
/api/auth/signout POST Clears session, redirects to landing
/api/classify POST Runs pipeline, streams SSE events
/api/inbox GET Fetches classified threads + buckets
/api/dashboard GET Analytics aggregations (7d/30d/90d)
/api/action-queue GET High-urgency threads needing action
/api/action-queue/{id}/generate POST Generate draft replies
/api/action-queue/{id}/send POST Send reply via Gmail
/api/action-queue/{id}/dismiss POST Dismiss from queue
/api/buckets GET/POST List or create buckets
/api/buckets/{id} PATCH/DELETE Update or delete bucket
/api/briefing GET AI-generated daily summary
/api/style-profile GET/POST Writing style extraction

About

Gmail inbox triage with AI classification

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages