pub mod cli;
use std::sync::Arc;
use anyhow::{Context, Result};
use bitrouter_cloud_sdk::BitrouterCloudAuthApplier;
use bitrouter_cloud_sdk::auth::credentials::default_credentials_path;
use bitrouter_cloud_sdk::provider::PROVIDER_ID;
use bitrouter_sdk::config::{Config, ProviderConfig};
use bitrouter_sdk::language_model::{AuthApplier, auth::AuthAppliers};
pub fn enable_in_zero_config(config: &mut Config) {
let Ok(path) = default_credentials_path() else {
return;
};
enable_in_zero_config_with_path(config, &path);
}
fn enable_in_zero_config_with_path(config: &mut Config, credentials_path: &std::path::Path) {
if config.providers.contains_key(PROVIDER_ID) {
return;
}
if !credentials_path.exists() {
return;
}
config.providers.insert(
PROVIDER_ID.to_string(),
ProviderConfig {
auto_discover: true,
..ProviderConfig::default()
},
);
}
pub fn build_auth_applier() -> Result<Arc<dyn AuthApplier>> {
let path = default_credentials_path().context("resolving BitRouter Cloud credentials path")?;
let applier =
BitrouterCloudAuthApplier::new(path).context("building the BitRouter Cloud AuthApplier")?;
Ok(Arc::new(applier))
}
pub fn register_if_configured(config: &Config, appliers: &mut AuthAppliers) -> Result<()> {
if !config.providers.contains_key(PROVIDER_ID) {
return Ok(());
}
let applier = build_auth_applier()?;
appliers.register(PROVIDER_ID, applier);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use std::sync::atomic::{AtomicU64, Ordering};
fn fresh_tmp_creds_path(label: &str) -> std::path::PathBuf {
static COUNTER: AtomicU64 = AtomicU64::new(0);
let id = COUNTER.fetch_add(1, Ordering::Relaxed);
let dir = std::env::temp_dir().join(format!(
"bitrouter-cloud-glue-{label}-{}-{id}",
std::process::id()
));
let _ = fs::remove_dir_all(&dir);
fs::create_dir_all(&dir).unwrap();
dir.join("account-credentials.json")
}
#[test]
fn enable_in_zero_config_noop_when_no_credentials_file() {
let path = fresh_tmp_creds_path("noop");
let mut config = Config::default();
enable_in_zero_config_with_path(&mut config, &path);
assert!(!config.providers.contains_key(PROVIDER_ID));
}
#[test]
fn enable_in_zero_config_inserts_when_credentials_file_present() {
let path = fresh_tmp_creds_path("inserts");
fs::write(&path, "{}").unwrap();
let mut config = Config::default();
enable_in_zero_config_with_path(&mut config, &path);
let provider = config
.providers
.get(PROVIDER_ID)
.expect("`bitrouter` provider should be auto-enabled when creds file is present");
assert!(
provider.auto_discover,
"auto_discover should be true so /models populates the routable list"
);
}
#[test]
fn enable_in_zero_config_noop_when_already_configured() {
let path = fresh_tmp_creds_path("already");
fs::write(&path, "{}").unwrap();
let mut config = Config::default();
config.providers.insert(
PROVIDER_ID.to_string(),
ProviderConfig {
api_base: "https://example.invalid".to_string(),
..ProviderConfig::default()
},
);
enable_in_zero_config_with_path(&mut config, &path);
assert_eq!(
config.providers.get(PROVIDER_ID).unwrap().api_base,
"https://example.invalid",
"existing entry must not be overwritten"
);
}
}