llm_latency_lens_providers/lib.rs
1//! Provider adapters for LLM Latency Lens
2//!
3//! This crate provides production-ready adapters for various LLM providers,
4//! enabling high-precision latency measurements and streaming token analysis.
5//!
6//! # Features
7//!
8//! - **OpenAI**: Full implementation with GPT-4, GPT-4o, and GPT-3.5 support
9//! - **Anthropic**: Complete Claude integration with extended thinking support
10//! - **Google**: Stub implementation for Gemini (coming soon)
11//! - **Streaming**: Server-Sent Events (SSE) with fine-grained token timing
12//! - **Retries**: Automatic retry logic with exponential backoff
13//! - **Cost Calculation**: Accurate pricing for all supported models
14//!
15//! # Example
16//!
17//! ```no_run
18//! use llm_latency_lens_providers::{
19//! openai::OpenAIProvider,
20//! traits::{Provider, StreamingRequest, MessageRole},
21//! };
22//! use llm_latency_lens_core::TimingEngine;
23//!
24//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
25//! // Create provider
26//! let provider = OpenAIProvider::new("sk-...");
27//!
28//! // Create timing engine
29//! let timing = TimingEngine::new();
30//!
31//! // Build request
32//! let request = StreamingRequest::builder()
33//! .model("gpt-4o")
34//! .message(MessageRole::User, "Explain quantum computing")
35//! .max_tokens(500)
36//! .temperature(0.7)
37//! .build();
38//!
39//! // Stream response
40//! let mut response = provider.stream(request, &timing).await?;
41//!
42//! // Process tokens
43//! use futures::StreamExt;
44//! while let Some(token) = response.token_stream.next().await {
45//! let event = token?;
46//! println!("Token {}: {:?} (latency: {:?})",
47//! event.sequence,
48//! event.content,
49//! event.inter_token_latency
50//! );
51//! }
52//! # Ok(())
53//! # }
54//! ```
55//!
56//! # Provider Implementations
57//!
58//! ## OpenAI
59//!
60//! The OpenAI provider supports all GPT models with comprehensive streaming
61//! and timing capabilities:
62//!
63//! ```no_run
64//! use llm_latency_lens_providers::openai::OpenAIProvider;
65//!
66//! let provider = OpenAIProvider::builder()
67//! .api_key("sk-...")
68//! .organization("org-...") // Optional
69//! .max_retries(3)
70//! .build();
71//! ```
72//!
73//! ## Anthropic
74//!
75//! The Anthropic provider supports all Claude models including extended
76//! thinking mode:
77//!
78//! ```no_run
79//! use llm_latency_lens_providers::anthropic::AnthropicProvider;
80//!
81//! let provider = AnthropicProvider::builder()
82//! .api_key("sk-ant-...")
83//! .api_version("2023-06-01")
84//! .max_retries(3)
85//! .build();
86//! ```
87//!
88//! ## Google
89//!
90//! The Google provider is currently a stub. Full implementation coming soon:
91//!
92//! ```no_run
93//! use llm_latency_lens_providers::google::GoogleProvider;
94//!
95//! let provider = GoogleProvider::new("AIza...");
96//! // Note: stream() will return an error until implemented
97//! ```
98//!
99//! # Error Handling
100//!
101//! All providers use a comprehensive error type that distinguishes between
102//! retryable and non-retryable errors:
103//!
104//! ```no_run
105//! use llm_latency_lens_providers::error::ProviderError;
106//!
107//! # async fn example() -> Result<(), ProviderError> {
108//! # use llm_latency_lens_providers::openai::OpenAIProvider;
109//! # use llm_latency_lens_providers::traits::{Provider, StreamingRequest, MessageRole};
110//! # use llm_latency_lens_core::TimingEngine;
111//! # let provider = OpenAIProvider::new("sk-...");
112//! # let timing = TimingEngine::new();
113//! # let request = StreamingRequest::builder()
114//! # .model("gpt-4o")
115//! # .message(MessageRole::User, "test")
116//! # .build();
117//! match provider.stream(request, &timing).await {
118//! Ok(response) => {
119//! // Process response
120//! }
121//! Err(e) => {
122//! if e.is_retryable() {
123//! eprintln!("Retryable error: {}", e);
124//! if let Some(delay) = e.retry_delay() {
125//! eprintln!("Retry after {} seconds", delay);
126//! }
127//! } else {
128//! eprintln!("Fatal error: {}", e);
129//! }
130//! }
131//! }
132//! # Ok(())
133//! # }
134//! ```
135//!
136//! # Timing Measurements
137//!
138//! All providers integrate with the core timing engine to provide
139//! nanosecond-precision measurements:
140//!
141//! - **TTFT** (Time to First Token): Critical for perceived responsiveness
142//! - **Inter-token latency**: Consistency of token generation
143//! - **Total generation time**: Overall performance
144//! - **Network timing**: DNS, TLS, connection establishment
145//!
146//! # Cost Calculation
147//!
148//! Each provider implements accurate cost calculation based on current
149//! pricing (as of 2024):
150//!
151//! ```no_run
152//! use llm_latency_lens_providers::openai::OpenAIProvider;
153//! use llm_latency_lens_providers::traits::Provider;
154//!
155//! let provider = OpenAIProvider::new("sk-...");
156//!
157//! // Calculate cost for GPT-4o
158//! let cost = provider.calculate_cost("gpt-4o", 1000, 2000);
159//! if let Some(usd) = cost {
160//! println!("Estimated cost: ${:.6}", usd);
161//! }
162//! ```
163
164pub mod anthropic;
165pub mod error;
166pub mod google;
167pub mod openai;
168pub mod traits;
169
170// Re-export commonly used types
171pub use error::{ProviderError, Result};
172pub use traits::{
173 CompletionResult, Message, MessageRole, Provider, ResponseMetadata, StreamingRequest,
174 StreamingResponse,
175};
176
177// Re-export provider implementations
178pub use anthropic::AnthropicProvider;
179pub use google::GoogleProvider;
180pub use openai::OpenAIProvider;
181
182/// Version of the providers crate
183pub const VERSION: &str = env!("CARGO_PKG_VERSION");
184
185/// Create a provider from a string identifier
186///
187/// This is a convenience function for dynamically selecting providers.
188///
189/// # Arguments
190///
191/// * `provider` - Provider identifier ("openai", "anthropic", "google")
192/// * `api_key` - API key for the provider
193///
194/// # Example
195///
196/// ```no_run
197/// use llm_latency_lens_providers::create_provider;
198///
199/// let provider = create_provider("openai", "sk-...").unwrap();
200/// ```
201pub fn create_provider(
202 provider: &str,
203 api_key: impl Into<String>,
204) -> Result<Box<dyn Provider>> {
205 match provider.to_lowercase().as_str() {
206 "openai" => Ok(Box::new(OpenAIProvider::new(api_key))),
207 "anthropic" => Ok(Box::new(AnthropicProvider::new(api_key))),
208 "google" => Ok(Box::new(GoogleProvider::new(api_key))),
209 _ => Err(ProviderError::ConfigError(format!(
210 "Unknown provider: {}. Supported providers: openai, anthropic, google",
211 provider
212 ))),
213 }
214}
215
216/// List all supported providers
217pub fn supported_providers() -> Vec<&'static str> {
218 vec!["openai", "anthropic", "google"]
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224
225 #[test]
226 fn test_create_provider_openai() {
227 let provider = create_provider("openai", "test-key");
228 assert!(provider.is_ok());
229 let provider = provider.unwrap();
230 assert_eq!(provider.name(), "openai");
231 }
232
233 #[test]
234 fn test_create_provider_anthropic() {
235 let provider = create_provider("anthropic", "test-key");
236 assert!(provider.is_ok());
237 let provider = provider.unwrap();
238 assert_eq!(provider.name(), "anthropic");
239 }
240
241 #[test]
242 fn test_create_provider_google() {
243 let provider = create_provider("google", "test-key");
244 assert!(provider.is_ok());
245 let provider = provider.unwrap();
246 assert_eq!(provider.name(), "google");
247 }
248
249 #[test]
250 fn test_create_provider_unknown() {
251 let provider = create_provider("unknown", "test-key");
252 assert!(provider.is_err());
253 }
254
255 #[test]
256 fn test_create_provider_case_insensitive() {
257 assert!(create_provider("OpenAI", "test-key").is_ok());
258 assert!(create_provider("ANTHROPIC", "test-key").is_ok());
259 assert!(create_provider("Google", "test-key").is_ok());
260 }
261
262 #[test]
263 fn test_supported_providers() {
264 let providers = supported_providers();
265 assert_eq!(providers.len(), 3);
266 assert!(providers.contains(&"openai"));
267 assert!(providers.contains(&"anthropic"));
268 assert!(providers.contains(&"google"));
269 }
270
271 #[test]
272 fn test_version() {
273 assert!(!VERSION.is_empty());
274 }
275}