refactor: 大规模模块拆分与代码精简,新增 ai-pipeline/data-contracts 独立 crate

- 新增 aether-ai-pipeline 和 aether-data-contracts crate,将 pipeline 逻辑与数据契约从 gateway 中解耦
- 重构 admin handlers:拆分单体模块为 auth/billing/endpoint/features/model/observability/provider/system 等独立子模块
- 合并 chat/cli 重复代码路径:精简 conversion、finalize、planner 中的 sync/chat/cli 分支
- 重构 scheduler/executor/data 层,引入 facade 模式降低模块间耦合
- 移除冗余的 intent 模块,将 plan_fallback/policy/stream_path/sync_path 迁移至 executor
- 前端适配:调整 admin API 调用和 provider 模型测试对话框
This commit is contained in:
fawney19
2026-04-07 02:50:19 +08:00
parent 763ff03a7b
commit 5d96d6673b
732 changed files with 28593 additions and 20666 deletions

View File

@@ -3,7 +3,7 @@ use std::sync::{Arc, RwLock};
use async_trait::async_trait;
use super::types::{SettlementWriteRepository, StoredUsageSettlement, UsageSettlementInput};
use super::{SettlementWriteRepository, StoredUsageSettlement, UsageSettlementInput};
use crate::repository::wallet::{InMemoryWalletRepository, StoredWalletSnapshot};
use crate::DataLayerError;

View File

@@ -1,9 +1,9 @@
mod memory;
mod sql;
mod types;
pub use memory::InMemorySettlementRepository;
pub use sql::SqlxSettlementRepository;
pub use types::{
#[allow(unused_imports)]
pub(crate) use aether_data_contracts::repository::settlement::{
SettlementRepository, SettlementWriteRepository, StoredUsageSettlement, UsageSettlementInput,
};
pub use memory::InMemorySettlementRepository;
pub use sql::SqlxSettlementRepository;

View File

@@ -1,7 +1,8 @@
use async_trait::async_trait;
use sqlx::{PgPool, Row};
use super::types::{SettlementWriteRepository, StoredUsageSettlement, UsageSettlementInput};
use super::{SettlementWriteRepository, StoredUsageSettlement, UsageSettlementInput};
use crate::error::SqlxResultExt;
use crate::postgres::PostgresTransactionRunner;
use crate::DataLayerError;
@@ -56,31 +57,42 @@ FOR UPDATE
)
.bind(&input.request_id)
.fetch_optional(&mut **tx)
.await?;
.await
.map_postgres_err()?;
let Some(usage_row) = row else {
return Ok(None);
};
let current_billing_status: String = usage_row.try_get("billing_status")?;
let current_billing_status: String =
usage_row.try_get("billing_status").map_postgres_err()?;
if current_billing_status == "settled" || current_billing_status == "void" {
return Ok(Some(StoredUsageSettlement {
request_id: usage_row.try_get("request_id")?,
wallet_id: usage_row.try_get("wallet_id")?,
request_id: usage_row.try_get("request_id").map_postgres_err()?,
wallet_id: usage_row.try_get("wallet_id").map_postgres_err()?,
billing_status: current_billing_status,
wallet_balance_before: usage_row.try_get("wallet_balance_before")?,
wallet_balance_after: usage_row.try_get("wallet_balance_after")?,
wallet_balance_before: usage_row
.try_get("wallet_balance_before")
.map_postgres_err()?,
wallet_balance_after: usage_row
.try_get("wallet_balance_after")
.map_postgres_err()?,
wallet_recharge_balance_before: usage_row
.try_get("wallet_recharge_balance_before")?,
.try_get("wallet_recharge_balance_before")
.map_postgres_err()?,
wallet_recharge_balance_after: usage_row
.try_get("wallet_recharge_balance_after")?,
.try_get("wallet_recharge_balance_after")
.map_postgres_err()?,
wallet_gift_balance_before: usage_row
.try_get("wallet_gift_balance_before")?,
.try_get("wallet_gift_balance_before")
.map_postgres_err()?,
wallet_gift_balance_after: usage_row
.try_get("wallet_gift_balance_after")?,
.try_get("wallet_gift_balance_after")
.map_postgres_err()?,
provider_monthly_used_usd: None,
finalized_at_unix_secs: usage_row
.try_get::<Option<i64>, _>("finalized_at_unix_secs")?
.try_get::<Option<i64>, _>("finalized_at_unix_secs")
.map_postgres_err()?
.map(|value| value as u64),
}));
}
@@ -136,7 +148,8 @@ LIMIT 1
)
.bind(api_key_id)
.fetch_optional(&mut **tx)
.await?
.await
.map_postgres_err()?
} else {
None
};
@@ -161,16 +174,20 @@ LIMIT 1
)
.bind(user_id)
.fetch_optional(&mut **tx)
.await?
.await
.map_postgres_err()?
} else {
None
};
if let Some(wallet_row) = wallet_row {
let wallet_id: String = wallet_row.try_get("id")?;
let before_recharge: f64 = wallet_row.try_get("balance")?;
let before_gift: f64 = wallet_row.try_get("gift_balance")?;
let limit_mode: String = wallet_row.try_get("limit_mode")?;
let wallet_id: String = wallet_row.try_get("id").map_postgres_err()?;
let before_recharge: f64 =
wallet_row.try_get("balance").map_postgres_err()?;
let before_gift: f64 =
wallet_row.try_get("gift_balance").map_postgres_err()?;
let limit_mode: String =
wallet_row.try_get("limit_mode").map_postgres_err()?;
let before_total = before_recharge + before_gift;
let mut after_recharge = before_recharge;
let mut after_gift = before_gift;
@@ -196,7 +213,8 @@ WHERE id = $1
.bind(after_gift)
.bind(input.total_cost_usd)
.execute(&mut **tx)
.await?;
.await
.map_postgres_err()?;
settlement.wallet_id = Some(wallet_id.clone());
settlement.wallet_balance_before = Some(before_total);
@@ -229,7 +247,8 @@ WHERE request_id = $1
.bind(before_gift)
.bind(after_gift)
.execute(&mut **tx)
.await?;
.await
.map_postgres_err()?;
}
if let Some(provider_id) = input
@@ -250,7 +269,8 @@ RETURNING CAST(monthly_used_usd AS DOUBLE PRECISION) AS monthly_used_usd
.bind(provider_id)
.bind(input.actual_total_cost_usd)
.fetch_optional(&mut **tx)
.await?;
.await
.map_postgres_err()?;
settlement.provider_monthly_used_usd =
quota_row.and_then(|row| row.try_get("monthly_used_usd").ok());
}
@@ -261,7 +281,8 @@ RETURNING CAST(monthly_used_usd AS DOUBLE PRECISION) AS monthly_used_usd
.bind(final_billing_status)
.bind(finalized_at)
.execute(&mut **tx)
.await?;
.await
.map_postgres_err()?;
Ok(Some(settlement))
})

View File

@@ -1,83 +0,0 @@
use async_trait::async_trait;
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct UsageSettlementInput {
pub request_id: String,
pub user_id: Option<String>,
pub api_key_id: Option<String>,
pub provider_id: Option<String>,
pub status: String,
pub billing_status: String,
pub total_cost_usd: f64,
pub actual_total_cost_usd: f64,
pub finalized_at_unix_secs: Option<u64>,
}
impl UsageSettlementInput {
pub fn validate(&self) -> Result<(), crate::DataLayerError> {
if self.request_id.trim().is_empty() {
return Err(crate::DataLayerError::InvalidInput(
"settlement request_id cannot be empty".to_string(),
));
}
if self.status.trim().is_empty() || self.billing_status.trim().is_empty() {
return Err(crate::DataLayerError::InvalidInput(
"settlement status cannot be empty".to_string(),
));
}
if !self.total_cost_usd.is_finite() || !self.actual_total_cost_usd.is_finite() {
return Err(crate::DataLayerError::InvalidInput(
"settlement cost must be finite".to_string(),
));
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct StoredUsageSettlement {
pub request_id: String,
pub wallet_id: Option<String>,
pub billing_status: String,
pub wallet_balance_before: Option<f64>,
pub wallet_balance_after: Option<f64>,
pub wallet_recharge_balance_before: Option<f64>,
pub wallet_recharge_balance_after: Option<f64>,
pub wallet_gift_balance_before: Option<f64>,
pub wallet_gift_balance_after: Option<f64>,
pub provider_monthly_used_usd: Option<f64>,
pub finalized_at_unix_secs: Option<u64>,
}
#[async_trait]
pub trait SettlementWriteRepository: Send + Sync {
async fn settle_usage(
&self,
input: UsageSettlementInput,
) -> Result<Option<StoredUsageSettlement>, crate::DataLayerError>;
}
pub trait SettlementRepository: SettlementWriteRepository + Send + Sync {}
impl<T> SettlementRepository for T where T: SettlementWriteRepository + Send + Sync {}
#[cfg(test)]
mod tests {
use super::UsageSettlementInput;
#[test]
fn rejects_invalid_settlement_input() {
let input = UsageSettlementInput {
request_id: "".to_string(),
user_id: None,
api_key_id: None,
provider_id: None,
status: "completed".to_string(),
billing_status: "pending".to_string(),
total_cost_usd: 0.1,
actual_total_cost_usd: 0.1,
finalized_at_unix_secs: None,
};
assert!(input.validate().is_err());
}
}