mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-01 17:00:21 +08:00
Preserve usage provider identity
This commit is contained in:
@@ -53,6 +53,15 @@ fn admin_usage_aggregation_by_provider_json(
|
||||
json!(rows
|
||||
.iter()
|
||||
.map(|row| {
|
||||
let identity_source = match row.secondary_name.as_deref() {
|
||||
Some("legacy_name") => "legacy_name",
|
||||
_ => "provider_id",
|
||||
};
|
||||
let provider_id = if identity_source == "provider_id" {
|
||||
json!(row.group_key)
|
||||
} else {
|
||||
serde_json::Value::Null
|
||||
};
|
||||
let success_count = row.success_count.unwrap_or_default();
|
||||
let error_count = row.request_count.saturating_sub(success_count);
|
||||
let success_rate = if row.request_count == 0 {
|
||||
@@ -67,7 +76,9 @@ fn admin_usage_aggregation_by_provider_json(
|
||||
.filter(|value| !value.is_empty())
|
||||
.unwrap_or(row.group_key.as_str());
|
||||
json!({
|
||||
"provider_id": row.group_key,
|
||||
"provider_id": provider_id,
|
||||
"provider_key": row.group_key,
|
||||
"provider_identity_source": identity_source,
|
||||
"provider": provider_name,
|
||||
"request_count": row.request_count,
|
||||
"total_tokens": row.total_tokens,
|
||||
|
||||
@@ -115,6 +115,30 @@ pub(crate) const ADMIN_MODULE_DEFINITIONS: &[AdminModuleDefinition] = &[
|
||||
admin_menu_group: Some("system"),
|
||||
admin_menu_order: 60,
|
||||
},
|
||||
AdminModuleDefinition {
|
||||
name: "payment_gateways",
|
||||
display_name: "支付配置",
|
||||
description: "配置易支付、支付宝官方、微信支付官方和 Stripe 等支付网关",
|
||||
category: "integration",
|
||||
env_key: "PAYMENT_GATEWAYS_AVAILABLE",
|
||||
default_available: true,
|
||||
admin_route: Some("/admin/payment-gateways"),
|
||||
admin_menu_icon: Some("CreditCard"),
|
||||
admin_menu_group: None,
|
||||
admin_menu_order: 70,
|
||||
},
|
||||
AdminModuleDefinition {
|
||||
name: "referral",
|
||||
display_name: "邀请返利",
|
||||
description: "管理用户邀请关系与返利记录,支持比例返利和人头返利",
|
||||
category: "integration",
|
||||
env_key: "REFERRAL_AVAILABLE",
|
||||
default_available: true,
|
||||
admin_route: Some("/admin/referrals"),
|
||||
admin_menu_icon: Some("Gift"),
|
||||
admin_menu_group: Some("management"),
|
||||
admin_menu_order: 75,
|
||||
},
|
||||
];
|
||||
|
||||
#[derive(Debug, Clone, serde::Deserialize)]
|
||||
|
||||
@@ -665,8 +665,14 @@ async fn gateway_handles_admin_usage_aggregation_stats_locally_with_trusted_admi
|
||||
let provider_items = provider_payload.as_array().expect("array response");
|
||||
assert_eq!(provider_items.len(), 2);
|
||||
assert_eq!(provider_items[0]["provider"], "OpenAI");
|
||||
assert_eq!(provider_items[0]["provider_id"], "provider-openai");
|
||||
assert_eq!(provider_items[0]["provider_key"], "provider-openai");
|
||||
assert_eq!(provider_items[0]["provider_identity_source"], "provider_id");
|
||||
assert_eq!(provider_items[0]["output_tokens"], 40);
|
||||
assert_eq!(provider_items[1]["provider"], "Anthropic");
|
||||
assert_eq!(provider_items[1]["provider_id"], "provider-anthropic");
|
||||
assert_eq!(provider_items[1]["provider_key"], "provider-anthropic");
|
||||
assert_eq!(provider_items[1]["provider_identity_source"], "provider_id");
|
||||
assert_eq!(provider_items[1]["output_tokens"], 20);
|
||||
|
||||
let api_format_response = admin_request(reqwest::Client::new().get(format!(
|
||||
@@ -693,6 +699,59 @@ async fn gateway_handles_admin_usage_aggregation_stats_locally_with_trusted_admi
|
||||
upstream_handle.abort();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn gateway_handles_admin_usage_aggregation_stats_for_legacy_provider_name_rows() {
|
||||
let legacy_usage = {
|
||||
let mut usage = sample_usage_row(
|
||||
"usage-legacy",
|
||||
"req-legacy",
|
||||
Some("user-1"),
|
||||
Some("key-1"),
|
||||
Some("primary"),
|
||||
"Legacy Provider",
|
||||
"gpt-5",
|
||||
"completed",
|
||||
120,
|
||||
30,
|
||||
0.3,
|
||||
0.36,
|
||||
DAY_1_UNIX_SECS,
|
||||
);
|
||||
usage.provider_id = None;
|
||||
usage.total_tokens = usage.input_tokens;
|
||||
usage
|
||||
};
|
||||
|
||||
let usage_repository = Arc::new(InMemoryUsageReadRepository::seed(vec![legacy_usage]));
|
||||
|
||||
let gateway = build_router_with_state(
|
||||
AppState::new()
|
||||
.expect("gateway should build")
|
||||
.with_data_state_for_tests(GatewayDataState::with_usage_reader_for_tests(
|
||||
usage_repository,
|
||||
)),
|
||||
);
|
||||
let (gateway_url, gateway_handle) = start_server(gateway).await;
|
||||
|
||||
let response = admin_request(reqwest::Client::new().get(format!(
|
||||
"{gateway_url}/api/admin/usage/aggregation/stats?group_by=provider&limit=10&start_date=2024-03-21&end_date=2024-03-22&tz_offset_minutes=0"
|
||||
)))
|
||||
.send()
|
||||
.await
|
||||
.expect("request should succeed");
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
let payload: serde_json::Value = response.json().await.expect("json body should parse");
|
||||
let items = payload.as_array().expect("array response");
|
||||
assert_eq!(items.len(), 1);
|
||||
assert_eq!(items[0]["provider"], "Legacy Provider");
|
||||
assert_eq!(items[0]["provider_id"], serde_json::Value::Null);
|
||||
assert_eq!(items[0]["provider_key"], "Legacy Provider");
|
||||
assert_eq!(items[0]["provider_identity_source"], "legacy_name");
|
||||
|
||||
gateway_handle.abort();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn gateway_returns_service_unavailable_for_admin_usage_replay_without_provider_catalog_reader(
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user