Skip to main content

llmkit_core/
provider.rs

1//! The [`LlmProvider`] trait — one call site regardless of backend.
2
3use async_trait::async_trait;
4
5use crate::error::LlmResult;
6use crate::stream::ChatStream;
7use crate::types::{ChatRequest, ChatResponse, EmbedRequest, EmbedResponse};
8use crate::usage::CostEstimate;
9
10/// A unified, async LLM backend.
11///
12/// Implemented by each provider adapter (OpenAI, Anthropic, Ollama) and by the
13/// Tower-wrapped client, so middleware and fallback chains compose transparently.
14#[async_trait]
15pub trait LlmProvider: Send + Sync + 'static {
16    /// Single-shot chat completion.
17    async fn chat(&self, req: ChatRequest) -> LlmResult<ChatResponse>;
18
19    /// Streaming chat completion.
20    async fn chat_stream(&self, req: ChatRequest) -> LlmResult<ChatStream>;
21
22    /// Generate embeddings.
23    async fn embed(&self, req: EmbedRequest) -> LlmResult<EmbedResponse>;
24
25    /// Provider name ("openai" | "anthropic" | "ollama").
26    fn name(&self) -> &'static str;
27
28    /// Active default model slug.
29    fn model(&self) -> &str;
30
31    /// Pre-flight cost estimate (no network call). `None` if unknown.
32    fn estimate_cost(&self, _req: &ChatRequest) -> Option<CostEstimate> {
33        None
34    }
35}
36
37#[async_trait]
38impl<T: LlmProvider + ?Sized> LlmProvider for std::sync::Arc<T> {
39    async fn chat(&self, req: ChatRequest) -> LlmResult<ChatResponse> {
40        (**self).chat(req).await
41    }
42
43    async fn chat_stream(&self, req: ChatRequest) -> LlmResult<ChatStream> {
44        (**self).chat_stream(req).await
45    }
46
47    async fn embed(&self, req: EmbedRequest) -> LlmResult<EmbedResponse> {
48        (**self).embed(req).await
49    }
50
51    fn name(&self) -> &'static str {
52        (**self).name()
53    }
54
55    fn model(&self) -> &str {
56        (**self).model()
57    }
58
59    fn estimate_cost(&self, req: &ChatRequest) -> Option<CostEstimate> {
60        (**self).estimate_cost(req)
61    }
62}