mailrs-intelligence 1.0.2

LLM-powered email analysis primitives: structured extraction, importance scoring, spam classification, and embeddings — with a pluggable LlmProvider trait and an OpenAI-compatible reference implementation.
Documentation

mailrs-intelligence

Crates.io docs.rs License Downloads

LLM-powered email analysis primitives for Rust — pluggable provider, OpenAI-compatible reference impl, with no-LLM heuristics included.

Extracted from mailrs so any Rust project that needs to classify, summarize, or embed email content can lean on the same primitives without dragging in the entire mail server.

Highlights

  • Pluggable backendLlmProvider trait keeps the choice of model visible at the call site, so small-core vs big-core decisions stay grep-auditable in your own code.
  • OpenAI-compatible reference implOpenAiCompatibleProvider wraps reqwest and works against any service speaking the standard {system, messages, temperature} shape (self-hosted vLLM, llama.cpp servers, etc.).
  • Five primitives, one shape — full email analysis (analyze::analyze_email), spam classification with optional cache (spam::classify), structured-data extraction from JSON-LD (structured::extract_structured_data), heuristic importance scoring (importance::calculate_importance), and embeddings via the provider's embed method.
  • No-LLM modulesimportance and structured are pure heuristics — they don't need a provider, network, or async runtime.
  • Optional Redis cacheRedisSpamCache is included behind the default redis-cache feature, but SpamCache is a trait so you can plug in whatever store you have.

Quick start

use std::sync::Arc;

use mailrs_intelligence::{
    OpenAiCompatibleProvider,
    analyze::{analyze_email, PROMPT_VERSION},
    importance::{calculate_importance, ImportanceSignals},
    LlmProvider,
};

# async fn run() -> Option<()> {
let provider: Arc<dyn LlmProvider> = Arc::new(OpenAiCompatibleProvider::new(
    "https://llm.example.com/complete".into(),
    Some("sk-…".into()),
    format!("qwen3.5-9b/{PROMPT_VERSION}"),
));

// LLM-powered full analysis
let analysis = analyze_email(
    provider.as_ref(),
    "boss@example.com",
    "Q3 review",
    "Please send your Q3 self-review by Friday.",
)
.await?;
println!("category={} requires_action={}", analysis.category, analysis.requires_action);

// No-LLM heuristic importance
let (level, score) = calculate_importance(&ImportanceSignals {
    is_mutual_contact: true,
    is_reply_to_my_email: true,
    has_action_items: analysis.requires_action,
    ..Default::default()
});
println!("importance={} score={:.2}", level.as_str(), score);
# Some(())
# }

Feature flags

Flag Default What it enables
http yes OpenAiCompatibleProvider (reqwest + rustls). Disable if you supply your own LlmProvider.
redis-cache yes RedisSpamCache for spam::classify. Disable if you cache yourself or run without a cache.

Disable both default features (default-features = false) if you're plugging in your own backends:

[dependencies]
mailrs-intelligence = { version = "1", default-features = false }
async-trait = "0.1"

Why a trait

Production mail servers tend to mix cheap inference (per-message spam classification, hot path) with rarer expensive calls (deep structured extraction, big context). Letting analysis functions take &dyn LlmProvider keeps the choice of model visible at the call site — you can grep your own code for "which provider does this path hand into analyze_email?" without diving into config or environment variables. That visibility is the whole point of carving the trait out instead of shipping the concrete config struct.

License

Licensed under either of Apache License 2.0 or MIT license at your option.