pub mod breaker;
pub mod cache;
pub mod client;
pub mod schema;
pub mod shim;
pub use llmshim_catalog as catalog;
pub mod config;
pub mod cost;
pub mod env;
pub mod error;
pub mod fallback;
pub mod log;
pub mod models;
pub mod provider;
pub mod providers;
pub mod reasoning;
pub mod router;
pub mod streaming;
pub mod toolcall;
pub mod usage;
pub mod vision;
#[cfg(feature = "proxy")]
pub mod proxy;
#[cfg(feature = "gateway")]
pub mod gateway;
use client::ShimClient;
use error::Result;
pub use fallback::{completion_with_fallback, FallbackConfig};
use log::{LogEntry, Logger, RequestTimer};
use router::Router;
use serde_json::Value;
use futures::Stream;
use std::pin::Pin;
use std::sync::LazyLock;
pub static SHARED_CLIENT: LazyLock<ShimClient> = LazyLock::new(ShimClient::new);
pub async fn warmup(router: &Router) {
let urls: Vec<&str> = router
.provider_keys()
.iter()
.filter_map(|name| match *name {
"openai" => Some("https://api.openai.com"),
"anthropic" => Some("https://api.anthropic.com"),
"gemini" => Some("https://generativelanguage.googleapis.com"),
"xai" => Some("https://api.x.ai"),
_ => None,
})
.collect();
SHARED_CLIENT.warmup(&urls).await;
}
pub async fn completion(router: &Router, request: &Value) -> Result<Value> {
completion_with_logger(router, request, None).await
}
pub async fn completion_with_logger(
router: &Router,
request: &Value,
logger: Option<&Logger>,
) -> Result<Value> {
let request = router.expand_route(request)?;
let request = request.as_ref();
let model_str = request
.get("model")
.and_then(|m| m.as_str())
.ok_or(error::ShimError::MissingModel)?;
let (provider, model) = router.resolve(model_str)?;
let client = bound_client(router);
let timer = RequestTimer::start();
let result = client.completion(provider, &model, request).await;
match result {
Ok(resp) => {
if let Some(logger) = logger {
logger.log(&LogEntry::from_response(
provider.name(),
model_str,
&resp,
timer.elapsed(),
));
}
Ok(resp)
}
Err(e) => {
if let Some(logger) = logger {
logger.log(&LogEntry::from_error(
provider.name(),
model_str,
&e.to_string(),
timer.elapsed(),
));
}
Err(e)
}
}
}
pub async fn stream(
router: &Router,
request: &Value,
) -> Result<Pin<Box<dyn Stream<Item = Result<String>> + Send>>> {
let request = router.expand_route(request)?;
let request = request.as_ref();
let model_str = request
.get("model")
.and_then(|m| m.as_str())
.ok_or(error::ShimError::MissingModel)?;
let (provider, model) = router.resolve_owned(model_str)?;
bound_client(router)
.stream_owned(provider, &model, request)
.await
}
pub(crate) fn bound_client(router: &Router) -> ShimClient {
SHARED_CLIENT.clone().with_breaker(router.breaker().clone())
}