mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
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:
@@ -2,7 +2,7 @@ use std::sync::RwLock;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use super::types::{MinimalCandidateSelectionReadRepository, StoredMinimalCandidateSelectionRow};
|
||||
use super::{MinimalCandidateSelectionReadRepository, StoredMinimalCandidateSelectionRow};
|
||||
use crate::DataLayerError;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
mod memory;
|
||||
mod sql;
|
||||
mod types;
|
||||
|
||||
pub use memory::InMemoryMinimalCandidateSelectionReadRepository;
|
||||
pub use sql::SqlxMinimalCandidateSelectionReadRepository;
|
||||
pub use types::{
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use aether_data_contracts::repository::candidate_selection::{
|
||||
MinimalCandidateSelectionReadRepository, MinimalCandidateSelectionRepository,
|
||||
StoredMinimalCandidateSelectionRow, StoredProviderModelMapping,
|
||||
};
|
||||
pub use memory::InMemoryMinimalCandidateSelectionReadRepository;
|
||||
pub use sql::SqlxMinimalCandidateSelectionReadRepository;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use async_trait::async_trait;
|
||||
use sqlx::{PgPool, Row};
|
||||
|
||||
use super::types::{
|
||||
use super::{
|
||||
MinimalCandidateSelectionReadRepository, StoredMinimalCandidateSelectionRow,
|
||||
StoredProviderModelMapping,
|
||||
};
|
||||
use crate::DataLayerError;
|
||||
use crate::{error::SqlxResultExt, DataLayerError};
|
||||
|
||||
const LIST_FOR_EXACT_API_FORMAT_SQL: &str = r#"
|
||||
SELECT
|
||||
@@ -172,7 +172,8 @@ impl SqlxMinimalCandidateSelectionReadRepository {
|
||||
let rows = sqlx::query(LIST_FOR_EXACT_API_FORMAT_SQL)
|
||||
.bind(api_format)
|
||||
.fetch_all(&self.pool)
|
||||
.await?;
|
||||
.await
|
||||
.map_postgres_err()?;
|
||||
rows.iter().map(map_candidate_selection_row).collect()
|
||||
}
|
||||
|
||||
@@ -185,7 +186,8 @@ impl SqlxMinimalCandidateSelectionReadRepository {
|
||||
.bind(api_format)
|
||||
.bind(global_model_name)
|
||||
.fetch_all(&self.pool)
|
||||
.await?;
|
||||
.await
|
||||
.map_postgres_err()?;
|
||||
rows.iter().map(map_candidate_selection_row).collect()
|
||||
}
|
||||
}
|
||||
@@ -212,46 +214,53 @@ fn map_candidate_selection_row(
|
||||
row: &sqlx::postgres::PgRow,
|
||||
) -> Result<StoredMinimalCandidateSelectionRow, DataLayerError> {
|
||||
Ok(StoredMinimalCandidateSelectionRow {
|
||||
provider_id: row.try_get("provider_id")?,
|
||||
provider_name: row.try_get("provider_name")?,
|
||||
provider_type: row.try_get("provider_type")?,
|
||||
provider_priority: row.try_get("provider_priority")?,
|
||||
provider_is_active: row.try_get("provider_is_active")?,
|
||||
endpoint_id: row.try_get("endpoint_id")?,
|
||||
endpoint_api_format: row.try_get("endpoint_api_format")?,
|
||||
endpoint_api_family: row.try_get("endpoint_api_family")?,
|
||||
endpoint_kind: row.try_get("endpoint_kind")?,
|
||||
endpoint_is_active: row.try_get("endpoint_is_active")?,
|
||||
key_id: row.try_get("key_id")?,
|
||||
key_name: row.try_get("key_name")?,
|
||||
key_auth_type: row.try_get("key_auth_type")?,
|
||||
key_is_active: row.try_get("key_is_active")?,
|
||||
provider_id: row.try_get("provider_id").map_postgres_err()?,
|
||||
provider_name: row.try_get("provider_name").map_postgres_err()?,
|
||||
provider_type: row.try_get("provider_type").map_postgres_err()?,
|
||||
provider_priority: row.try_get("provider_priority").map_postgres_err()?,
|
||||
provider_is_active: row.try_get("provider_is_active").map_postgres_err()?,
|
||||
endpoint_id: row.try_get("endpoint_id").map_postgres_err()?,
|
||||
endpoint_api_format: row.try_get("endpoint_api_format").map_postgres_err()?,
|
||||
endpoint_api_family: row.try_get("endpoint_api_family").map_postgres_err()?,
|
||||
endpoint_kind: row.try_get("endpoint_kind").map_postgres_err()?,
|
||||
endpoint_is_active: row.try_get("endpoint_is_active").map_postgres_err()?,
|
||||
key_id: row.try_get("key_id").map_postgres_err()?,
|
||||
key_name: row.try_get("key_name").map_postgres_err()?,
|
||||
key_auth_type: row.try_get("key_auth_type").map_postgres_err()?,
|
||||
key_is_active: row.try_get("key_is_active").map_postgres_err()?,
|
||||
key_api_formats: parse_string_list(
|
||||
row.try_get("key_api_formats")?,
|
||||
row.try_get("key_api_formats").map_postgres_err()?,
|
||||
"provider_api_keys.api_formats",
|
||||
)?,
|
||||
key_allowed_models: parse_string_list(
|
||||
row.try_get("key_allowed_models")?,
|
||||
row.try_get("key_allowed_models").map_postgres_err()?,
|
||||
"provider_api_keys.allowed_models",
|
||||
)?,
|
||||
key_capabilities: row.try_get("key_capabilities")?,
|
||||
key_internal_priority: row.try_get("key_internal_priority")?,
|
||||
key_global_priority_by_format: row.try_get("key_global_priority_by_format")?,
|
||||
model_id: row.try_get("model_id")?,
|
||||
global_model_id: row.try_get("global_model_id")?,
|
||||
global_model_name: row.try_get("global_model_name")?,
|
||||
key_capabilities: row.try_get("key_capabilities").map_postgres_err()?,
|
||||
key_internal_priority: row.try_get("key_internal_priority").map_postgres_err()?,
|
||||
key_global_priority_by_format: row
|
||||
.try_get("key_global_priority_by_format")
|
||||
.map_postgres_err()?,
|
||||
model_id: row.try_get("model_id").map_postgres_err()?,
|
||||
global_model_id: row.try_get("global_model_id").map_postgres_err()?,
|
||||
global_model_name: row.try_get("global_model_name").map_postgres_err()?,
|
||||
global_model_mappings: parse_string_list(
|
||||
row.try_get("global_model_mappings")?,
|
||||
row.try_get("global_model_mappings").map_postgres_err()?,
|
||||
"global_models.config.model_mappings",
|
||||
)?,
|
||||
global_model_supports_streaming: row.try_get("global_model_supports_streaming")?,
|
||||
model_provider_model_name: row.try_get("model_provider_model_name")?,
|
||||
global_model_supports_streaming: row
|
||||
.try_get("global_model_supports_streaming")
|
||||
.map_postgres_err()?,
|
||||
model_provider_model_name: row
|
||||
.try_get("model_provider_model_name")
|
||||
.map_postgres_err()?,
|
||||
model_provider_model_mappings: parse_provider_model_mappings(
|
||||
row.try_get("model_provider_model_mappings")?,
|
||||
row.try_get("model_provider_model_mappings")
|
||||
.map_postgres_err()?,
|
||||
)?,
|
||||
model_supports_streaming: row.try_get("model_supports_streaming")?,
|
||||
model_is_active: row.try_get("model_is_active")?,
|
||||
model_is_available: row.try_get("model_is_available")?,
|
||||
model_supports_streaming: row.try_get("model_supports_streaming").map_postgres_err()?,
|
||||
model_is_active: row.try_get("model_is_active").map_postgres_err()?,
|
||||
model_is_available: row.try_get("model_is_available").map_postgres_err()?,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct StoredProviderModelMapping {
|
||||
pub name: String,
|
||||
pub priority: i32,
|
||||
pub api_formats: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct StoredMinimalCandidateSelectionRow {
|
||||
pub provider_id: String,
|
||||
pub provider_name: String,
|
||||
pub provider_type: String,
|
||||
pub provider_priority: i32,
|
||||
pub provider_is_active: bool,
|
||||
pub endpoint_id: String,
|
||||
pub endpoint_api_format: String,
|
||||
pub endpoint_api_family: Option<String>,
|
||||
pub endpoint_kind: Option<String>,
|
||||
pub endpoint_is_active: bool,
|
||||
pub key_id: String,
|
||||
pub key_name: String,
|
||||
pub key_auth_type: String,
|
||||
pub key_is_active: bool,
|
||||
pub key_api_formats: Option<Vec<String>>,
|
||||
pub key_allowed_models: Option<Vec<String>>,
|
||||
pub key_capabilities: Option<serde_json::Value>,
|
||||
pub key_internal_priority: i32,
|
||||
pub key_global_priority_by_format: Option<serde_json::Value>,
|
||||
pub model_id: String,
|
||||
pub global_model_id: String,
|
||||
pub global_model_name: String,
|
||||
pub global_model_mappings: Option<Vec<String>>,
|
||||
pub global_model_supports_streaming: Option<bool>,
|
||||
pub model_provider_model_name: String,
|
||||
pub model_provider_model_mappings: Option<Vec<StoredProviderModelMapping>>,
|
||||
pub model_supports_streaming: Option<bool>,
|
||||
pub model_is_active: bool,
|
||||
pub model_is_available: bool,
|
||||
}
|
||||
|
||||
impl StoredMinimalCandidateSelectionRow {
|
||||
pub fn supports_streaming(&self) -> bool {
|
||||
self.model_supports_streaming
|
||||
.or(self.global_model_supports_streaming)
|
||||
.unwrap_or(true)
|
||||
}
|
||||
|
||||
pub fn key_supports_api_format(&self, api_format: &str) -> bool {
|
||||
let target = api_format.trim();
|
||||
match self.key_api_formats.as_deref() {
|
||||
None => true,
|
||||
Some(formats) => formats
|
||||
.iter()
|
||||
.any(|value| value.eq_ignore_ascii_case(target)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait MinimalCandidateSelectionReadRepository: Send + Sync {
|
||||
async fn list_for_exact_api_format(
|
||||
&self,
|
||||
api_format: &str,
|
||||
) -> Result<Vec<StoredMinimalCandidateSelectionRow>, crate::DataLayerError>;
|
||||
|
||||
async fn list_for_exact_api_format_and_global_model(
|
||||
&self,
|
||||
api_format: &str,
|
||||
global_model_name: &str,
|
||||
) -> Result<Vec<StoredMinimalCandidateSelectionRow>, crate::DataLayerError>;
|
||||
}
|
||||
|
||||
pub trait MinimalCandidateSelectionRepository:
|
||||
MinimalCandidateSelectionReadRepository + Send + Sync
|
||||
{
|
||||
}
|
||||
|
||||
impl<T> MinimalCandidateSelectionRepository for T where
|
||||
T: MinimalCandidateSelectionReadRepository + Send + Sync
|
||||
{
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{StoredMinimalCandidateSelectionRow, StoredProviderModelMapping};
|
||||
|
||||
fn sample_row() -> StoredMinimalCandidateSelectionRow {
|
||||
StoredMinimalCandidateSelectionRow {
|
||||
provider_id: "provider-1".to_string(),
|
||||
provider_name: "OpenAI".to_string(),
|
||||
provider_type: "custom".to_string(),
|
||||
provider_priority: 10,
|
||||
provider_is_active: true,
|
||||
endpoint_id: "endpoint-1".to_string(),
|
||||
endpoint_api_format: "openai:chat".to_string(),
|
||||
endpoint_api_family: Some("openai".to_string()),
|
||||
endpoint_kind: Some("chat".to_string()),
|
||||
endpoint_is_active: true,
|
||||
key_id: "key-1".to_string(),
|
||||
key_name: "prod".to_string(),
|
||||
key_auth_type: "api_key".to_string(),
|
||||
key_is_active: true,
|
||||
key_api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
key_allowed_models: None,
|
||||
key_capabilities: None,
|
||||
key_internal_priority: 50,
|
||||
key_global_priority_by_format: None,
|
||||
model_id: "model-1".to_string(),
|
||||
global_model_id: "global-model-1".to_string(),
|
||||
global_model_name: "gpt-4.1".to_string(),
|
||||
global_model_mappings: Some(vec!["gpt-4\\.1-.*".to_string()]),
|
||||
global_model_supports_streaming: Some(true),
|
||||
model_provider_model_name: "gpt-4.1-upstream".to_string(),
|
||||
model_provider_model_mappings: Some(vec![StoredProviderModelMapping {
|
||||
name: "gpt-4.1-canary".to_string(),
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
}]),
|
||||
model_supports_streaming: None,
|
||||
model_is_active: true,
|
||||
model_is_available: true,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn defaults_streaming_support_to_true() {
|
||||
let mut row = sample_row();
|
||||
row.model_supports_streaming = None;
|
||||
row.global_model_supports_streaming = None;
|
||||
|
||||
assert!(row.supports_streaming());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_api_formats_none_means_support_all_formats() {
|
||||
let mut row = sample_row();
|
||||
row.key_api_formats = None;
|
||||
|
||||
assert!(row.key_supports_api_format("openai:chat"));
|
||||
assert!(row.key_supports_api_format("openai:responses"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user