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}