onnx-email-gate β€” multilingual KEEP/DROP email prefilter

A tiny multilingual CPU classifier that labels an inbound email KEEP (looks like a real job application β†’ worth further processing) or DROP (junk β†’ skip). It's meant as a cheap pre-filter in front of a more expensive downstream model, discarding obvious junk β€” newsletters, notifications, one-time codes, job-board alerts, bounces β€” at roughly a millisecond per email.

  • Labels: {0: DROP, 1: KEEP}
  • Architecture: paraphrase-multilingual-MiniLM-L12-v2 sentence embedder (mean-pool + L2-normalize) with a logistic-regression head folded into the ONNX graph as a final linear layer β€” so the whole thing is one classifier ONNX: tokens β†’ 2 logits β†’ argmax.
  • Quantization: dynamic INT8 (model_int8.onnx, ~119 MB).
  • Runtime: onnxruntime on CPU, ~ms per email.

Why multilingual

The embedder covers ~50 languages, so the gate reads non-English application emails directly instead of wrong-dropping them (verified on English, Hindi, and Spanish).

Recommended use β€” an override ladder

The model is best used as the last rung of a cheap rule ladder, so a genuine application is never dropped by the model alone:

  1. KEEP overrides β€” rΓ©sumΓ© attachment / forwarded application / recruiter-style sender β†’ KEEP.
  2. regex junk β€” noreply / notifications / OTP / bounce / job-board-alert / newsletter β†’ DROP.
  3. this model β€” KEEP/DROP on the remaining ambiguous mail.

Usage

import numpy as np, onnxruntime as ort
from transformers import AutoTokenizer

tok = AutoTokenizer.from_pretrained("curriculo-tech/onnx-email-gate")
sess = ort.InferenceSession("model_int8.onnx", providers=["CPUExecutionProvider"])
I2L = {0: "DROP", 1: "KEEP"}

def gate(from_addr, from_name, subject, body):
    text = f"{from_addr}\n{from_name}\n{subject}\n{body}"
    enc = tok(text, truncation=True, max_length=128, return_tensors="np", padding="max_length")
    feed = {"input_ids": enc["input_ids"].astype(np.int64),
            "attention_mask": enc["attention_mask"].astype(np.int64)}
    logits = sess.run(None, feed)[0][0]
    p = int(np.argmax(logits))
    sm = np.exp(logits - logits.max()); sm /= sm.sum()
    return I2L[p], float(sm[p])   # (label, confidence)

Input text = from_address\nfrom_name\nsubject\nbody, truncated to 128 tokens.

Files

File Purpose
model_int8.onnx the model β€” embedder + folded logistic-regression head, INT8
tokenizer.json, tokenizer_config.json, special_tokens_map.json tokenizer
config.json model config + id2label
logreg_head.npz, gate_meta.json raw head weights + fold record (reproducibility)

Limitations

  • The model alone has modest recall β€” use it behind the override ladder, not standalone.
  • Dynamic INT8 shifts a small fraction of borderline predictions vs fp32; ship fp32 if you need exact parity.
  • Very low-resource languages may be weaker than the ~50 the embedder covers well.
Downloads last month
-
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support

Model tree for curriculo-tech/onnx-email-gate