mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-03 01:40:21 +08:00
feat(stats,rules): api_key 用量统计(total_tokens 字段 + 回填)与 body rules 新增 append/insert/regex_replace/name_style 操作
- 新增迁移 20260422120000_add_api_key_usage_stats.sql,为 api_keys 表添加 total_tokens 列 - 新增回填 20260422120000_backfill_api_key_usage_stats.sql,按历史 usage 重建 api_key 维度汇总 - 在 UsageWriteRepository trait 中添加 rebuild_api_key_usage_stats,补齐 SQL/内存实现及上层调用链 - 内存实现中新增 apply_usage_stats_delta,支持增量更新 api_key 统计快照 - dev.sh:改进临时日志目录管理,并在网关异常退出时输出错误提示 - frontend EndpointFormDialog:将 append 操作从 insert 分支拆分,提供独立 path/value 输入 UI - rules.rs:扩展 body rules 支持,新增 append/insert/regex_replace/name_style 操作及 WildcardSlice 路径段
This commit is contained in:
@@ -9,6 +9,7 @@ use super::types::{
|
||||
StandaloneApiKeyExportListQuery, StoredAuthApiKeyExportRecord, StoredAuthApiKeySnapshot,
|
||||
UpdateStandaloneApiKeyBasicRecord, UpdateUserApiKeyBasicRecord,
|
||||
};
|
||||
use crate::repository::usage::{ApiKeyUsageContribution, ApiKeyUsageDelta};
|
||||
use crate::DataLayerError;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@@ -111,6 +112,70 @@ impl InMemoryAuthApiKeySnapshotRepository {
|
||||
.copied()
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
pub(crate) fn apply_usage_stats_delta(
|
||||
&self,
|
||||
api_key_id: &str,
|
||||
delta: &ApiKeyUsageDelta,
|
||||
_recomputed_last_used_at_unix_secs: Option<u64>,
|
||||
) {
|
||||
let mut index = self
|
||||
.index
|
||||
.write()
|
||||
.expect("auth api key snapshot repository lock");
|
||||
let Some(record) = index.export_by_api_key_id.get_mut(api_key_id) else {
|
||||
return;
|
||||
};
|
||||
|
||||
record.total_requests = apply_i64_delta_to_u64(record.total_requests, delta.total_requests);
|
||||
record.total_tokens = apply_i64_delta_to_u64(record.total_tokens, delta.total_tokens);
|
||||
record.total_cost_usd = apply_f64_delta(record.total_cost_usd, delta.total_cost_usd);
|
||||
}
|
||||
|
||||
pub(crate) fn rebuild_usage_stats(
|
||||
&self,
|
||||
contributions: &BTreeMap<String, ApiKeyUsageContribution>,
|
||||
) {
|
||||
let mut index = self
|
||||
.index
|
||||
.write()
|
||||
.expect("auth api key snapshot repository lock");
|
||||
for record in index.export_by_api_key_id.values_mut() {
|
||||
record.total_requests = 0;
|
||||
record.total_tokens = 0;
|
||||
record.total_cost_usd = 0.0;
|
||||
}
|
||||
|
||||
for (api_key_id, contribution) in contributions {
|
||||
let Some(record) = index.export_by_api_key_id.get_mut(api_key_id) else {
|
||||
continue;
|
||||
};
|
||||
record.total_requests = clamp_i64_to_u64(contribution.total_requests);
|
||||
record.total_tokens = clamp_i64_to_u64(contribution.total_tokens);
|
||||
record.total_cost_usd = contribution.total_cost_usd.max(0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clamp_i64_to_u64(value: i64) -> u64 {
|
||||
u64::try_from(value).unwrap_or_default()
|
||||
}
|
||||
|
||||
fn apply_i64_delta_to_u64(current: u64, delta: i64) -> u64 {
|
||||
clamp_i64_to_u64(
|
||||
i64::try_from(current)
|
||||
.unwrap_or(i64::MAX)
|
||||
.saturating_add(delta),
|
||||
)
|
||||
}
|
||||
|
||||
fn apply_f64_delta(current: f64, delta: f64) -> f64 {
|
||||
let next = current + delta;
|
||||
if next.is_finite() {
|
||||
next.max(0.0)
|
||||
} else {
|
||||
current.max(0.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
Reference in New Issue
Block a user