use std::{env, sync::Arc};
use axum::http::StatusCode;
use tracing::info;
use super::upstream_router::{UpstreamRouter, UpstreamTarget};
pub(crate) type SharedRouter = Arc<tokio::sync::Mutex<UpstreamRouter>>;
#[allow(dead_code)]
pub(crate) const PRIMARY_ANTHROPIC: &str = "https://coding.dashscope.aliyuncs.com/apps/anthropic";
pub(crate) const PRIMARY_OPENAI: &str = "https://coding.dashscope.aliyuncs.com/v1";
#[allow(dead_code)]
pub(crate) const BACKUP_ANTHROPIC: &str =
"https://token-plan.cn-beijing.maas.aliyuncs.com/apps/anthropic";
pub(crate) const BACKUP_OPENAI: &str =
"https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1";
#[derive(Clone)]
pub(crate) struct ProxyConfig {
pub(crate) upstream_url: String,
pub(crate) upstream_api_key: String,
pub(crate) proxy_api_key: Option<String>,
pub(crate) router: Option<SharedRouter>,
}
impl ProxyConfig {
pub(crate) fn from_env() -> Self {
let primary_api_key = env::var("PRIMARY_API_KEY")
.or_else(|_| env::var("UPSTREAM_API_KEY"))
.expect("PRIMARY_API_KEY (or UPSTREAM_API_KEY) must be set");
let backup_api_key = env::var("BACKUP_API_KEY").expect("BACKUP_API_KEY must be set");
let proxy_api_key = env::var("PROXY_API_KEY").ok();
let primary_target = UpstreamTarget {
name: "primary".to_string(),
url: PRIMARY_OPENAI.trim_end_matches('/').to_string(),
api_key: primary_api_key.clone(),
};
let backup_target = UpstreamTarget {
name: "backup".to_string(),
url: BACKUP_OPENAI.trim_end_matches('/').to_string(),
api_key: backup_api_key,
};
info!(
primary_url = &primary_target.url,
backup_url = &backup_target.url,
"starting http proxy with primary/backup routing"
);
let router = Some(Arc::new(tokio::sync::Mutex::new(UpstreamRouter::new(
primary_target,
Some(backup_target),
))));
Self {
upstream_url: PRIMARY_OPENAI.trim_end_matches('/').to_string(),
upstream_api_key: primary_api_key,
proxy_api_key,
router,
}
}
pub(crate) async fn active_upstream(&self) -> UpstreamTarget {
if let Some(ref router) = self.router {
let guard = router.lock().await;
guard.active_target().clone()
} else {
UpstreamTarget {
name: "primary".to_string(),
url: self.upstream_url.clone(),
api_key: self.upstream_api_key.clone(),
}
}
}
pub(crate) async fn record_upstream_status(&self, status: StatusCode) {
if let Some(ref router) = self.router {
let mut guard = router.lock().await;
guard.record_response_status(status);
}
}
pub(crate) async fn record_upstream_error(&self) {
if let Some(ref router) = self.router {
let mut guard = router.lock().await;
guard.record_connection_failure();
}
}
}