Skip to main content

mailrs_intelligence/
provider.rs

1use async_trait::async_trait;
2
3/// Pluggable LLM backend.
4///
5/// All analysis primitives in this crate (`analyze_email`, `classify_spam`,
6/// `generate_embedding`, …) take `&dyn LlmProvider` so the caller is the one
7/// who chooses *which* model handles *which* request — making small-core vs
8/// big-core decisions explicit and grep-auditable in the consumer code.
9///
10/// A reference implementation backed by an OpenAI-compatible HTTP endpoint
11/// is provided as [`crate::OpenAiCompatibleProvider`] under the default
12/// `http` feature. To plug in another backend, implement this trait yourself
13/// and disable the `http` feature.
14#[async_trait]
15pub trait LlmProvider: Send + Sync {
16    /// Run a chat completion against the model.
17    ///
18    /// Returns `None` on transport error, non-success HTTP status, or any
19    /// retry-exhaustion the implementation defines. Errors should already
20    /// have been logged via `tracing` by the provider.
21    async fn complete(&self, system: &str, user_message: &str, temperature: f32) -> Option<String>;
22
23    /// Generate an embedding vector for `text`.
24    ///
25    /// Providers that don't expose an embedding endpoint should return
26    /// `None`. Callers that always need embeddings should pair such a
27    /// provider with an embedding-capable one rather than encoding the
28    /// constraint into the trait.
29    async fn embed(&self, text: &str) -> Option<Vec<f32>>;
30
31    /// Stable identifier for the model + prompt revision in use.
32    ///
33    /// This is what consumers persist alongside a stored analysis result
34    /// to know whether re-analysis is required after a prompt or model
35    /// change. Typical format: `"qwen3.5-9b/v8"`.
36    fn model_id(&self) -> &str;
37}