use std::sync::Arc;
use crate::gate::GateHealthRegistry;
use crate::provider::ProviderRegistry;
use crate::{AppState, ProxyConfig, app, store};
pub fn init_tracing() {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
)
.init();
}
#[must_use]
pub fn build_gate_health(config: &ProxyConfig) -> GateHealthRegistry {
let mut gate_health = GateHealthRegistry::new();
if let Some(routing) = config.routing.as_ref() {
let mut seen = std::collections::HashSet::new();
for route in &routing.routes {
for gate in route.gates.iter().chain(&route.deferred_gates) {
if seen.insert(gate.clone()) {
gate_health = gate_health.with_budget(gate.clone(), 50, 0.25);
}
}
}
}
gate_health
}
pub async fn serve(config: ProxyConfig) -> Result<(), Box<dyn std::error::Error>> {
let (traces, writer) = store::open(&config.db_path)?;
let bind = config.bind.clone();
let provider_defs = config
.routing
.as_ref()
.map(|r| r.providers.as_slice())
.unwrap_or_default();
let providers = ProviderRegistry::from_config(
provider_defs,
&config.upstream_anthropic,
&config.upstream_openai,
);
let gate_health = build_gate_health(&config);
let adaptive = config
.routing
.as_ref()
.and_then(|r| r.escalation.adaptive.as_ref())
.map(|a| {
let init = config
.routing
.as_ref()
.and_then(|r| r.escalation.serve_threshold)
.unwrap_or(0.5);
Arc::new(std::sync::Mutex::new(
firstpass_core::conformal::AdaptiveConformal::new(a.alpha, a.gamma, init),
))
});
let state = AppState {
config: Arc::new(config),
http: reqwest::Client::builder()
.connect_timeout(std::time::Duration::from_secs(10))
.build()?,
providers,
gate_health: Arc::new(gate_health),
traces,
adaptive,
};
let listener = tokio::net::TcpListener::bind(&bind).await?;
tracing::info!(%bind, "firstpass listening");
tracing::info!("offboard: unset ANTHROPIC_BASE_URL");
axum::serve(listener, app(state)?)
.with_graceful_shutdown(shutdown_signal())
.await?;
drop(writer);
Ok(())
}
async fn shutdown_signal() {
if let Err(err) = tokio::signal::ctrl_c().await {
tracing::warn!(%err, "failed to install Ctrl-C handler; shutting down anyway");
}
}