mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-10 11:19:50 +08:00
refactor: 大规模模块拆分与重组,新增 aether-admin crate
- 新建独立 aether-admin crate 承载 admin 相关共享契约与纯辅助函数 - 拆分 ai_pipeline 下 kiro/private_envelope/conversion/planner 等大文件为子模块目录 - 重组 admin handlers 各业务域(billing/oauth/provider/system/users 等)为目录结构,移除 shared.rs/builders.rs 等反模式 - 移除 ai_pipeline runtime adapters 旧实现(claude/openai/gemini/kiro/vertex/antigravity 等),改由 provider transport 统一承载 - 移除 control_facade/execution_facade/auth_snapshot_facade 等冗余 facade 层 - 拆分 query/billing 与 query/monitoring 模块、state/runtime/payments 与 security 模块 - 扩展架构测试覆盖 admin_billing/admin_model/admin_users 等新模块 - 删除 docs/architecture/refactor-execution-plan.md 已完成的执行计划文档
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
use super::super::{
|
||||
build_admin_billing_bad_request_response, build_admin_billing_not_found_response,
|
||||
build_admin_billing_read_only_response,
|
||||
};
|
||||
use super::support::{
|
||||
parse_admin_billing_preset_apply_request, resolve_admin_billing_preset_collectors,
|
||||
};
|
||||
use crate::handlers::admin::request::{AdminAppState, AdminRequestContext};
|
||||
use crate::handlers::admin::shared::attach_admin_audit_response;
|
||||
use crate::GatewayError;
|
||||
use axum::{
|
||||
body::{Body, Bytes},
|
||||
response::{IntoResponse, Response},
|
||||
Json,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
pub(super) async fn build_admin_apply_billing_preset_response(
|
||||
state: &AdminAppState<'_>,
|
||||
request_context: &AdminRequestContext<'_>,
|
||||
request_body: Option<&Bytes>,
|
||||
) -> Result<Response<Body>, GatewayError> {
|
||||
let (preset, mode) = match parse_admin_billing_preset_apply_request(request_body) {
|
||||
Ok(value) => value,
|
||||
Err(response) => return Ok(response),
|
||||
};
|
||||
let Some((resolved_preset, collectors)) = resolve_admin_billing_preset_collectors(&preset)
|
||||
else {
|
||||
let payload = json!({
|
||||
"ok": false,
|
||||
"preset": preset,
|
||||
"mode": mode,
|
||||
"created": 0,
|
||||
"updated": 0,
|
||||
"skipped": 0,
|
||||
"errors": ["Unknown preset: available presets are aether-core"],
|
||||
});
|
||||
return Ok(Json(payload).into_response());
|
||||
};
|
||||
|
||||
match state
|
||||
.apply_admin_billing_preset(resolved_preset, &mode, &collectors)
|
||||
.await?
|
||||
{
|
||||
crate::LocalMutationOutcome::Applied(result) => {
|
||||
let response = Json(json!({
|
||||
"ok": result.errors.is_empty(),
|
||||
"preset": result.preset,
|
||||
"mode": result.mode,
|
||||
"created": result.created,
|
||||
"updated": result.updated,
|
||||
"skipped": result.skipped,
|
||||
"errors": result.errors,
|
||||
}))
|
||||
.into_response();
|
||||
Ok(attach_admin_audit_response(
|
||||
response,
|
||||
"admin_billing_preset_applied",
|
||||
"apply_billing_preset",
|
||||
"billing_preset",
|
||||
resolved_preset,
|
||||
))
|
||||
}
|
||||
crate::LocalMutationOutcome::Unavailable => Ok(build_admin_billing_read_only_response(
|
||||
"当前为只读模式,无法应用计费预设",
|
||||
)),
|
||||
crate::LocalMutationOutcome::Invalid(detail) => {
|
||||
Ok(build_admin_billing_bad_request_response(detail))
|
||||
}
|
||||
crate::LocalMutationOutcome::NotFound => Ok(build_admin_billing_not_found_response(
|
||||
"Billing preset not found",
|
||||
)),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
mod apply;
|
||||
mod support;
|
||||
|
||||
use crate::handlers::admin::request::{AdminAppState, AdminRequestContext};
|
||||
use crate::GatewayError;
|
||||
use axum::{
|
||||
body::{Body, Bytes},
|
||||
http,
|
||||
response::{IntoResponse, Response},
|
||||
Json,
|
||||
};
|
||||
|
||||
pub(super) async fn maybe_build_local_admin_billing_presets_response(
|
||||
state: &AdminAppState<'_>,
|
||||
request_context: &AdminRequestContext<'_>,
|
||||
request_body: Option<&Bytes>,
|
||||
) -> Result<Option<Response<Body>>, GatewayError> {
|
||||
let Some(decision) = request_context.decision() else {
|
||||
return Ok(None);
|
||||
};
|
||||
let path = request_context.path();
|
||||
|
||||
match decision.route_kind.as_deref() {
|
||||
Some("list_presets")
|
||||
if request_context.method() == http::Method::GET
|
||||
&& matches!(
|
||||
path,
|
||||
"/api/admin/billing/presets" | "/api/admin/billing/presets/"
|
||||
) =>
|
||||
{
|
||||
Ok(Some(
|
||||
Json(support::build_admin_billing_presets_payload()).into_response(),
|
||||
))
|
||||
}
|
||||
Some("apply_preset")
|
||||
if request_context.method() == http::Method::POST
|
||||
&& matches!(
|
||||
path,
|
||||
"/api/admin/billing/presets/apply" | "/api/admin/billing/presets/apply/"
|
||||
) =>
|
||||
{
|
||||
Ok(Some(
|
||||
apply::build_admin_apply_billing_preset_response(
|
||||
state,
|
||||
request_context,
|
||||
request_body,
|
||||
)
|
||||
.await?,
|
||||
))
|
||||
}
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
use super::super::{
|
||||
build_admin_billing_bad_request_response, normalize_admin_billing_required_text,
|
||||
};
|
||||
use axum::{
|
||||
body::{Body, Bytes},
|
||||
response::Response,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
|
||||
fn default_admin_billing_preset_mode() -> String {
|
||||
"merge".to_string()
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct AdminBillingPresetApplyRequest {
|
||||
preset: String,
|
||||
#[serde(default = "default_admin_billing_preset_mode")]
|
||||
mode: String,
|
||||
}
|
||||
|
||||
pub(super) fn build_admin_billing_presets_payload() -> serde_json::Value {
|
||||
json!({
|
||||
"items": [
|
||||
{
|
||||
"name": "aether-core",
|
||||
"version": "1.0",
|
||||
"description": "Aether built-in dimension collectors for common api_formats/task_types.",
|
||||
"collector_count": build_admin_billing_aether_core_collectors().len(),
|
||||
}
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
fn build_admin_billing_aether_core_collectors() -> Vec<crate::AdminBillingCollectorWriteInput> {
|
||||
vec![
|
||||
crate::AdminBillingCollectorWriteInput {
|
||||
api_format: "OPENAI:CHAT".to_string(),
|
||||
task_type: "chat".to_string(),
|
||||
dimension_name: "input_tokens".to_string(),
|
||||
source_type: "response".to_string(),
|
||||
source_path: Some("usage.prompt_tokens".to_string()),
|
||||
value_type: "int".to_string(),
|
||||
transform_expression: None,
|
||||
default_value: None,
|
||||
priority: 10,
|
||||
is_enabled: true,
|
||||
},
|
||||
crate::AdminBillingCollectorWriteInput {
|
||||
api_format: "OPENAI:CHAT".to_string(),
|
||||
task_type: "chat".to_string(),
|
||||
dimension_name: "output_tokens".to_string(),
|
||||
source_type: "response".to_string(),
|
||||
source_path: Some("usage.completion_tokens".to_string()),
|
||||
value_type: "int".to_string(),
|
||||
transform_expression: None,
|
||||
default_value: None,
|
||||
priority: 10,
|
||||
is_enabled: true,
|
||||
},
|
||||
crate::AdminBillingCollectorWriteInput {
|
||||
api_format: "CLAUDE:CHAT".to_string(),
|
||||
task_type: "chat".to_string(),
|
||||
dimension_name: "input_tokens".to_string(),
|
||||
source_type: "response".to_string(),
|
||||
source_path: Some("usage.input_tokens".to_string()),
|
||||
value_type: "int".to_string(),
|
||||
transform_expression: None,
|
||||
default_value: None,
|
||||
priority: 10,
|
||||
is_enabled: true,
|
||||
},
|
||||
crate::AdminBillingCollectorWriteInput {
|
||||
api_format: "CLAUDE:CHAT".to_string(),
|
||||
task_type: "chat".to_string(),
|
||||
dimension_name: "output_tokens".to_string(),
|
||||
source_type: "response".to_string(),
|
||||
source_path: Some("usage.output_tokens".to_string()),
|
||||
value_type: "int".to_string(),
|
||||
transform_expression: None,
|
||||
default_value: None,
|
||||
priority: 10,
|
||||
is_enabled: true,
|
||||
},
|
||||
crate::AdminBillingCollectorWriteInput {
|
||||
api_format: "GEMINI:CHAT".to_string(),
|
||||
task_type: "chat".to_string(),
|
||||
dimension_name: "input_tokens".to_string(),
|
||||
source_type: "response".to_string(),
|
||||
source_path: Some("usageMetadata.promptTokenCount".to_string()),
|
||||
value_type: "int".to_string(),
|
||||
transform_expression: None,
|
||||
default_value: None,
|
||||
priority: 10,
|
||||
is_enabled: true,
|
||||
},
|
||||
crate::AdminBillingCollectorWriteInput {
|
||||
api_format: "GEMINI:CHAT".to_string(),
|
||||
task_type: "chat".to_string(),
|
||||
dimension_name: "output_tokens".to_string(),
|
||||
source_type: "response".to_string(),
|
||||
source_path: Some("usageMetadata.candidatesTokenCount".to_string()),
|
||||
value_type: "int".to_string(),
|
||||
transform_expression: None,
|
||||
default_value: None,
|
||||
priority: 10,
|
||||
is_enabled: true,
|
||||
},
|
||||
crate::AdminBillingCollectorWriteInput {
|
||||
api_format: "OPENAI:CHAT".to_string(),
|
||||
task_type: "video".to_string(),
|
||||
dimension_name: "video_resolution_key".to_string(),
|
||||
source_type: "metadata".to_string(),
|
||||
source_path: Some("task.size".to_string()),
|
||||
value_type: "string".to_string(),
|
||||
transform_expression: None,
|
||||
default_value: None,
|
||||
priority: 10,
|
||||
is_enabled: true,
|
||||
},
|
||||
crate::AdminBillingCollectorWriteInput {
|
||||
api_format: "OPENAI:CHAT".to_string(),
|
||||
task_type: "video".to_string(),
|
||||
dimension_name: "video_resolution_key".to_string(),
|
||||
source_type: "metadata".to_string(),
|
||||
source_path: Some("task.resolution".to_string()),
|
||||
value_type: "string".to_string(),
|
||||
transform_expression: None,
|
||||
default_value: None,
|
||||
priority: 0,
|
||||
is_enabled: true,
|
||||
},
|
||||
crate::AdminBillingCollectorWriteInput {
|
||||
api_format: "OPENAI:CHAT".to_string(),
|
||||
task_type: "video".to_string(),
|
||||
dimension_name: "video_size_bytes".to_string(),
|
||||
source_type: "metadata".to_string(),
|
||||
source_path: Some("task.video_size_bytes".to_string()),
|
||||
value_type: "int".to_string(),
|
||||
transform_expression: None,
|
||||
default_value: None,
|
||||
priority: 0,
|
||||
is_enabled: true,
|
||||
},
|
||||
crate::AdminBillingCollectorWriteInput {
|
||||
api_format: "OPENAI:CHAT".to_string(),
|
||||
task_type: "video".to_string(),
|
||||
dimension_name: "video_duration_seconds".to_string(),
|
||||
source_type: "metadata".to_string(),
|
||||
source_path: Some("task.video_duration_seconds".to_string()),
|
||||
value_type: "float".to_string(),
|
||||
transform_expression: None,
|
||||
default_value: None,
|
||||
priority: 10,
|
||||
is_enabled: true,
|
||||
},
|
||||
crate::AdminBillingCollectorWriteInput {
|
||||
api_format: "OPENAI:CHAT".to_string(),
|
||||
task_type: "video".to_string(),
|
||||
dimension_name: "video_duration_seconds".to_string(),
|
||||
source_type: "metadata".to_string(),
|
||||
source_path: Some("task.duration_seconds".to_string()),
|
||||
value_type: "int".to_string(),
|
||||
transform_expression: None,
|
||||
default_value: None,
|
||||
priority: 0,
|
||||
is_enabled: true,
|
||||
},
|
||||
crate::AdminBillingCollectorWriteInput {
|
||||
api_format: "GEMINI:CHAT".to_string(),
|
||||
task_type: "video".to_string(),
|
||||
dimension_name: "video_resolution_key".to_string(),
|
||||
source_type: "metadata".to_string(),
|
||||
source_path: Some("task.size".to_string()),
|
||||
value_type: "string".to_string(),
|
||||
transform_expression: None,
|
||||
default_value: None,
|
||||
priority: 10,
|
||||
is_enabled: true,
|
||||
},
|
||||
crate::AdminBillingCollectorWriteInput {
|
||||
api_format: "GEMINI:CHAT".to_string(),
|
||||
task_type: "video".to_string(),
|
||||
dimension_name: "video_resolution_key".to_string(),
|
||||
source_type: "metadata".to_string(),
|
||||
source_path: Some("task.resolution".to_string()),
|
||||
value_type: "string".to_string(),
|
||||
transform_expression: None,
|
||||
default_value: None,
|
||||
priority: 0,
|
||||
is_enabled: true,
|
||||
},
|
||||
crate::AdminBillingCollectorWriteInput {
|
||||
api_format: "GEMINI:CHAT".to_string(),
|
||||
task_type: "video".to_string(),
|
||||
dimension_name: "video_size_bytes".to_string(),
|
||||
source_type: "metadata".to_string(),
|
||||
source_path: Some("task.video_size_bytes".to_string()),
|
||||
value_type: "int".to_string(),
|
||||
transform_expression: None,
|
||||
default_value: None,
|
||||
priority: 0,
|
||||
is_enabled: true,
|
||||
},
|
||||
crate::AdminBillingCollectorWriteInput {
|
||||
api_format: "GEMINI:CHAT".to_string(),
|
||||
task_type: "video".to_string(),
|
||||
dimension_name: "video_duration_seconds".to_string(),
|
||||
source_type: "metadata".to_string(),
|
||||
source_path: Some("task.video_duration_seconds".to_string()),
|
||||
value_type: "float".to_string(),
|
||||
transform_expression: None,
|
||||
default_value: None,
|
||||
priority: 10,
|
||||
is_enabled: true,
|
||||
},
|
||||
crate::AdminBillingCollectorWriteInput {
|
||||
api_format: "GEMINI:CHAT".to_string(),
|
||||
task_type: "video".to_string(),
|
||||
dimension_name: "video_duration_seconds".to_string(),
|
||||
source_type: "metadata".to_string(),
|
||||
source_path: Some("task.duration_seconds".to_string()),
|
||||
value_type: "int".to_string(),
|
||||
transform_expression: None,
|
||||
default_value: None,
|
||||
priority: 0,
|
||||
is_enabled: true,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
pub(super) fn resolve_admin_billing_preset_collectors(
|
||||
preset: &str,
|
||||
) -> Option<(&'static str, Vec<crate::AdminBillingCollectorWriteInput>)> {
|
||||
let normalized = preset.trim().to_ascii_lowercase();
|
||||
match normalized.as_str() {
|
||||
"aether-core" | "default" => {
|
||||
Some(("aether-core", build_admin_billing_aether_core_collectors()))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn parse_admin_billing_preset_apply_request(
|
||||
request_body: Option<&Bytes>,
|
||||
) -> Result<(String, String), Response<Body>> {
|
||||
let Some(request_body) = request_body else {
|
||||
return Err(build_admin_billing_bad_request_response("请求体不能为空"));
|
||||
};
|
||||
let request = match serde_json::from_slice::<AdminBillingPresetApplyRequest>(request_body) {
|
||||
Ok(value) => value,
|
||||
Err(err) => {
|
||||
return Err(build_admin_billing_bad_request_response(format!(
|
||||
"Invalid request body: {err}"
|
||||
)))
|
||||
}
|
||||
};
|
||||
let preset = match normalize_admin_billing_required_text(&request.preset, "preset", 100) {
|
||||
Ok(value) => value,
|
||||
Err(detail) => return Err(build_admin_billing_bad_request_response(detail)),
|
||||
};
|
||||
let mode = request.mode.trim().to_ascii_lowercase();
|
||||
if !matches!(mode.as_str(), "merge" | "overwrite") {
|
||||
return Err(build_admin_billing_bad_request_response(
|
||||
"mode must be one of merge, overwrite",
|
||||
));
|
||||
}
|
||||
Ok((preset, mode))
|
||||
}
|
||||
Reference in New Issue
Block a user