Skip to main content

tt_shared/
provider.rs

1//! The `Provider` trait every adapter implements. See
2//! `docs/02-provider-adapter-guide.md` for the contract and the worked Anthropic example.
3
4use async_trait::async_trait;
5use futures::stream::BoxStream;
6
7use crate::{
8    ChatCompletionChunk, ChatCompletionRequest, ChatCompletionResponse, EmbeddingsRequest,
9    EmbeddingsResponse, ModelInfo, ModelPricing, ProviderError, RequestContext,
10};
11
12/// Adapters are stateless beyond their HTTP client and pricing table.
13/// All authentication, telemetry, and routing concerns live in the core layer.
14#[async_trait]
15pub trait Provider: Send + Sync {
16    /// Unique provider ID (e.g. "openai", "anthropic", "gemini").
17    fn id(&self) -> &'static str;
18
19    /// All models supported by this adapter, with capabilities.
20    fn models(&self) -> Vec<ModelInfo>;
21
22    /// Pricing for a model. Drawn from the manually-curated `data/pricing.toml`
23    /// snapshot embedded at build time; rates are updated by hand, not
24    /// automatically. Returns `None` only when the model is absent from the
25    /// catalog — local providers should return `Some` with zero rates instead.
26    fn pricing(&self, model: &str) -> Option<ModelPricing>;
27
28    /// Multiplier applied to computed cost/baseline to account for a provider
29    /// surcharge on top of the underlying model cost (e.g. OpenRouter's 5% BYOK
30    /// fee). Default `1.0` (no surcharge).
31    fn fee_multiplier(&self) -> f64 {
32        1.0
33    }
34
35    /// Non-streaming chat completion.
36    async fn chat_completion(
37        &self,
38        req: ChatCompletionRequest,
39        ctx: &RequestContext,
40    ) -> Result<ChatCompletionResponse, ProviderError>;
41
42    /// Streaming chat completion.
43    async fn chat_completion_stream(
44        &self,
45        req: ChatCompletionRequest,
46        ctx: &RequestContext,
47    ) -> Result<BoxStream<'static, Result<ChatCompletionChunk, ProviderError>>, ProviderError>;
48
49    /// Embeddings. Returns Unsupported if the provider doesn't offer them.
50    async fn embeddings(
51        &self,
52        _req: EmbeddingsRequest,
53        _ctx: &RequestContext,
54    ) -> Result<EmbeddingsResponse, ProviderError> {
55        Err(ProviderError::Unsupported(format!(
56            "{} does not support embeddings",
57            self.id()
58        )))
59    }
60
61    /// Liveness check. Should not call the provider's pricey endpoints.
62    async fn health_check(&self) -> Result<(), ProviderError> {
63        Ok(())
64    }
65}