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}