use std::time::Duration;
use allama::mock::MockProvider;
use allama::prelude::*;
use allama::wrappers::{FallbackAttempts, FallbackProvider, MapModelProvider};
use allama::{CapabilitySupport, ChatCapability};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let primary = MockProvider::build(|b| {
b.error(
Error::overloaded("primary overloaded")
.with_retry_after(Duration::from_millis(25))
.with_request_id("req_primary_1"),
)
.provider_name("primary-mock")
});
let backup = MockProvider::build(|b| {
b.text("Served by fallback provider")
.provider_name("fallback-mock")
})
.with_chat_capability(
ChatCapability::ReasoningConfig,
CapabilitySupport::Unsupported,
);
let provider = FallbackProvider::new(
primary.clone(),
MapModelProvider::new(backup.clone(), |model: &str| match model {
"demo-model" => "backup-model".into(),
other => other.into(),
}),
);
let request = ChatRequest::new("demo-model")
.user("Say hello")
.reasoning(ReasoningEffort::Low);
let validation = provider.validate_chat_request(&request)?;
if let Some(backup) = validation.fallback() {
match &backup.result {
Ok(backup_validation) => println!(
"backup {} would serve, with {} warning(s)",
backup.provider,
backup_validation.warnings().len()
),
Err(err) => println!("backup {} could not serve: {err}", backup.provider),
}
}
let response = provider.chat(&request).await?;
println!("text: {}", response.text_or_empty());
println!("served by: {:?}", response.provider);
for warning in &response.warnings {
println!("warning: {warning}");
}
if let Some(attempts) = response.provider_data.get::<FallbackAttempts>()? {
for attempt in &attempts {
println!(
"failed attempt: {} ({})",
attempt.provider, attempt.error_kind
);
}
}
println!(
"calls: primary={}, fallback={}",
primary.call_count(),
backup.call_count()
);
println!(
"models sent: primary={:?}, fallback={:?}",
primary.last_request().map(|r| r.model),
backup.last_request().map(|r| r.model)
);
let failing = FallbackProvider::new(
MockProvider::with_error(Error::overloaded("HTTP 529").with_status(529))
.with_provider_name("primary-mock"),
MockProvider::with_error(Error::rate_limited("HTTP 429").with_status(429))
.with_provider_name("fallback-mock"),
);
let Err(error) = failing.chat(&request).await else {
unreachable!("both sides fail");
};
let status = match &error {
Error::RateLimited { .. } => 429,
_ if error.is_retryable() => 503,
_ => 502,
};
println!(
"failed failover: kind={} -> HTTP {status}",
error.kind_name()
);
let mut earlier = error.fallback_from();
while let Some(tried) = earlier {
println!(" fell back from: {}", tried.as_log());
earlier = tried.fallback_from();
}
println!("redacted log: {}", error.as_log());
Ok(())
}