mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-10 03:09:50 +08:00
feat(gateway/data): 新增 usage 关键词搜索、缓存命中摘要、结算成本摘要及 dashboard 聚合查询能力
- 新增 UsageAuditKeywordSearchQuery,支持多关键词、用户名、API Key 交叉过滤 - 新增 UsageCacheHitSummaryQuery/StoredUsageCacheHitSummary,统计请求缓存命中率 - 新增 UsageSettledCostSummaryQuery/StoredUsageSettledCostSummary,汇总结算成本 - 新增 UsageDashboardSummaryQuery、UsageBreakdownSummaryQuery 等 dashboard 聚合类型 - SQL 层实现对应查询方法,含 list_by_ids、list_usage_audits_by_keyword_search、count_usage_audits_by_keyword_search - 将上述能力通过 GatewayDataState / AdminAppState 暴露给 handler 层 - user_me_usage 及 dashboard_filters 切换为新查询接口,移除旧的内存过滤逻辑 - 同步更新 memory 层及测试
This commit is contained in:
@@ -4,7 +4,6 @@ use super::route_filters::{
|
||||
parse_admin_monitoring_limit, parse_admin_monitoring_offset,
|
||||
parse_admin_monitoring_username_filter,
|
||||
};
|
||||
use super::usage_helpers::admin_monitoring_usage_is_error;
|
||||
use crate::constants::INTERNAL_GATEWAY_PATH_PREFIXES;
|
||||
use crate::handlers::admin::request::{AdminAppState, AdminRequestContext};
|
||||
use crate::query::monitoring as monitoring_query;
|
||||
@@ -16,7 +15,9 @@ use aether_admin::observability::monitoring::{
|
||||
build_admin_monitoring_system_status_payload_response,
|
||||
build_admin_monitoring_user_behavior_payload_response,
|
||||
};
|
||||
use aether_data_contracts::repository::usage::UsageAuditListQuery;
|
||||
use aether_data_contracts::repository::usage::{
|
||||
UsageAuditSummaryQuery, UsageMonitoringErrorCountQuery,
|
||||
};
|
||||
use axum::{body::Body, response::Response};
|
||||
|
||||
pub(super) async fn build_admin_monitoring_audit_logs_response(
|
||||
@@ -196,30 +197,27 @@ pub(super) async fn build_admin_monitoring_system_status_response(
|
||||
.saturating_add(standalone_api_key_summary.active);
|
||||
|
||||
let today_usage = state
|
||||
.list_usage_audits(&UsageAuditListQuery {
|
||||
created_from_unix_secs: Some(today_start.timestamp().max(0) as u64),
|
||||
..Default::default()
|
||||
.summarize_usage_audits(&UsageAuditSummaryQuery {
|
||||
created_from_unix_secs: today_start.timestamp().max(0) as u64,
|
||||
created_until_unix_secs: now_unix_secs.saturating_add(1),
|
||||
user_id: None,
|
||||
provider_name: None,
|
||||
model: None,
|
||||
})
|
||||
.await?;
|
||||
let today_requests = today_usage.len();
|
||||
let today_tokens = today_usage
|
||||
.iter()
|
||||
.map(|item| item.total_tokens)
|
||||
.sum::<u64>();
|
||||
let today_cost = today_usage
|
||||
.iter()
|
||||
.map(|item| item.total_cost_usd)
|
||||
.sum::<f64>();
|
||||
let today_requests = usize::try_from(today_usage.total_requests).unwrap_or(usize::MAX);
|
||||
let today_tokens = today_usage.recorded_total_tokens;
|
||||
let today_cost = today_usage.total_cost_usd;
|
||||
|
||||
let recent_errors = state
|
||||
.list_usage_audits(&UsageAuditListQuery {
|
||||
created_from_unix_secs: Some(recent_error_from.timestamp().max(0) as u64),
|
||||
..Default::default()
|
||||
})
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter(admin_monitoring_usage_is_error)
|
||||
.count();
|
||||
let recent_errors = usize::try_from(
|
||||
state
|
||||
.count_monitoring_usage_errors(&UsageMonitoringErrorCountQuery {
|
||||
created_from_unix_secs: recent_error_from.timestamp().max(0) as u64,
|
||||
created_until_unix_secs: now_unix_secs.saturating_add(1),
|
||||
})
|
||||
.await?,
|
||||
)
|
||||
.unwrap_or(usize::MAX);
|
||||
let tunnel = state.tunnel.stats();
|
||||
|
||||
Ok(build_admin_monitoring_system_status_payload_response(
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::handlers::admin::observability::stats::round_to;
|
||||
use crate::handlers::admin::request::AdminAppState;
|
||||
use crate::scheduler::affinity::SCHEDULER_AFFINITY_TTL;
|
||||
use crate::GatewayError;
|
||||
use aether_data_contracts::repository::usage::UsageAuditListQuery;
|
||||
use aether_data_contracts::repository::usage::UsageCacheHitSummaryQuery;
|
||||
|
||||
async fn count_admin_monitoring_cache_affinity_entries(state: &AdminAppState<'_>) -> usize {
|
||||
list_admin_monitoring_cache_affinity_records(state)
|
||||
@@ -325,27 +325,26 @@ pub(super) async fn build_admin_monitoring_cache_snapshot(
|
||||
.unwrap_or_else(|| "provider".to_string());
|
||||
|
||||
let now = chrono::Utc::now();
|
||||
let usage = if state.has_usage_data_reader() {
|
||||
let usage_summary = if state.has_usage_data_reader() {
|
||||
state
|
||||
.list_usage_audits(&UsageAuditListQuery {
|
||||
created_from_unix_secs: Some(
|
||||
(now - chrono::Duration::hours(24)).timestamp().max(0) as u64,
|
||||
),
|
||||
..Default::default()
|
||||
.summarize_usage_cache_hit_summary(&UsageCacheHitSummaryQuery {
|
||||
created_from_unix_secs: (now - chrono::Duration::hours(24)).timestamp().max(0)
|
||||
as u64,
|
||||
created_until_unix_secs: now.timestamp().max(0) as u64,
|
||||
user_id: None,
|
||||
})
|
||||
.await?
|
||||
} else {
|
||||
Vec::new()
|
||||
Default::default()
|
||||
};
|
||||
let cache_hits = usage
|
||||
.iter()
|
||||
.filter(|item| item.cache_read_input_tokens > 0)
|
||||
.count();
|
||||
let cache_misses = usage.len().saturating_sub(cache_hits);
|
||||
let cache_hit_rate = if usage.is_empty() {
|
||||
let cache_hits = usage_summary.cache_hit_requests as usize;
|
||||
let cache_misses = usage_summary
|
||||
.total_requests
|
||||
.saturating_sub(usage_summary.cache_hit_requests) as usize;
|
||||
let cache_hit_rate = if usage_summary.total_requests == 0 {
|
||||
0.0
|
||||
} else {
|
||||
round_to(cache_hits as f64 / usage.len() as f64, 4)
|
||||
round_to(cache_hits as f64 / usage_summary.total_requests as f64, 4)
|
||||
};
|
||||
let total_affinities = count_admin_monitoring_cache_affinity_entries(state).await;
|
||||
let storage_type = if state.redis_kv_runner().is_some() {
|
||||
|
||||
+6
-9
@@ -1,9 +1,8 @@
|
||||
use super::super::usage_helpers::admin_monitoring_usage_is_error;
|
||||
use crate::handlers::admin::request::AdminAppState;
|
||||
use crate::handlers::admin::shared::{provider_key_health_summary, unix_secs_to_rfc3339};
|
||||
use crate::GatewayError;
|
||||
use aether_data_contracts::repository::{
|
||||
provider_catalog::StoredProviderCatalogKey, usage::UsageAuditListQuery,
|
||||
provider_catalog::StoredProviderCatalogKey, usage::UsageMonitoringErrorListQuery,
|
||||
};
|
||||
use serde_json::json;
|
||||
use std::collections::BTreeMap;
|
||||
@@ -152,14 +151,12 @@ pub(super) async fn build_admin_monitoring_resilience_snapshot(
|
||||
}
|
||||
|
||||
let mut recent_usage_errors = state
|
||||
.list_usage_audits(&UsageAuditListQuery {
|
||||
created_from_unix_secs: Some(recent_error_from.timestamp().max(0) as u64),
|
||||
..Default::default()
|
||||
.list_monitoring_usage_errors(&UsageMonitoringErrorListQuery {
|
||||
created_from_unix_secs: recent_error_from.timestamp().max(0) as u64,
|
||||
created_until_unix_secs: (now.timestamp().max(0) as u64).saturating_add(1),
|
||||
limit: None,
|
||||
})
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter(admin_monitoring_usage_is_error)
|
||||
.collect::<Vec<_>>();
|
||||
.await?;
|
||||
recent_usage_errors
|
||||
.sort_by(|left, right| right.created_at_unix_ms.cmp(&left.created_at_unix_ms));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user