mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-12 12:09:50 +08:00
Merge branch 'review-pr-602'
This commit is contained in:
@@ -729,6 +729,16 @@ async fn gateway_handles_dashboard_daily_stats_locally_without_proxying_upstream
|
|||||||
now - chrono::Duration::hours(2),
|
now - chrono::Duration::hours(2),
|
||||||
);
|
);
|
||||||
today_claude_usage.total_tokens = 160;
|
today_claude_usage.total_tokens = 160;
|
||||||
|
let mut today_bailian_usage = sample_user_usage_audit(
|
||||||
|
"usage-dashboard-daily-4",
|
||||||
|
"req-dashboard-daily-4",
|
||||||
|
"user-auth-4",
|
||||||
|
"qwen3.6-27b",
|
||||||
|
"bailian",
|
||||||
|
"completed",
|
||||||
|
now - chrono::Duration::hours(3),
|
||||||
|
);
|
||||||
|
today_bailian_usage.total_tokens = 160;
|
||||||
let mut prior_usage = sample_user_usage_audit(
|
let mut prior_usage = sample_user_usage_audit(
|
||||||
"usage-dashboard-daily-3",
|
"usage-dashboard-daily-3",
|
||||||
"req-dashboard-daily-3",
|
"req-dashboard-daily-3",
|
||||||
@@ -742,6 +752,7 @@ async fn gateway_handles_dashboard_daily_stats_locally_without_proxying_upstream
|
|||||||
let usage_repository = Arc::new(InMemoryUsageReadRepository::seed(vec![
|
let usage_repository = Arc::new(InMemoryUsageReadRepository::seed(vec![
|
||||||
today_openai_usage,
|
today_openai_usage,
|
||||||
today_claude_usage,
|
today_claude_usage,
|
||||||
|
today_bailian_usage,
|
||||||
prior_usage,
|
prior_usage,
|
||||||
]));
|
]));
|
||||||
|
|
||||||
@@ -789,30 +800,41 @@ async fn gateway_handles_dashboard_daily_stats_locally_without_proxying_upstream
|
|||||||
assert_eq!(daily_stats[0]["requests"], 1);
|
assert_eq!(daily_stats[0]["requests"], 1);
|
||||||
assert_eq!(daily_stats[0]["unique_providers"], 1);
|
assert_eq!(daily_stats[0]["unique_providers"], 1);
|
||||||
assert_eq!(daily_stats[1]["date"], json!(now.date_naive().to_string()));
|
assert_eq!(daily_stats[1]["date"], json!(now.date_naive().to_string()));
|
||||||
assert_eq!(daily_stats[1]["requests"], 2);
|
assert_eq!(daily_stats[1]["requests"], 3);
|
||||||
assert_eq!(daily_stats[1]["tokens"], 320);
|
assert_eq!(daily_stats[1]["tokens"], 480);
|
||||||
assert_eq!(daily_stats[1]["unique_models"], 2);
|
assert_eq!(daily_stats[1]["unique_models"], 3);
|
||||||
assert_eq!(daily_stats[1]["unique_providers"], 2);
|
assert_eq!(daily_stats[1]["unique_providers"], 3);
|
||||||
|
let today_model_breakdown = daily_stats[1]["model_breakdown"]
|
||||||
|
.as_array()
|
||||||
|
.expect("today model breakdown should be an array");
|
||||||
|
assert_eq!(today_model_breakdown.len(), 3);
|
||||||
|
let today_models = today_model_breakdown
|
||||||
|
.iter()
|
||||||
|
.filter_map(|item| item["model"].as_str())
|
||||||
|
.collect::<std::collections::BTreeSet<_>>();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
daily_stats[1]["model_breakdown"].as_array().map(Vec::len),
|
today_models,
|
||||||
Some(2)
|
std::collections::BTreeSet::from(["claude-3-7", "gpt-5", "qwen3.6-27b"])
|
||||||
);
|
);
|
||||||
|
|
||||||
let model_summary = payload["model_summary"]
|
let model_summary = payload["model_summary"]
|
||||||
.as_array()
|
.as_array()
|
||||||
.expect("model summary should exist");
|
.expect("model summary should exist");
|
||||||
assert_eq!(model_summary.len(), 2);
|
assert_eq!(model_summary.len(), 3);
|
||||||
assert_eq!(model_summary[0]["model"], "gpt-5");
|
assert_eq!(model_summary[0]["model"], "gpt-5");
|
||||||
assert_eq!(model_summary[0]["requests"], 2);
|
assert_eq!(model_summary[0]["requests"], 2);
|
||||||
|
|
||||||
let provider_summary = payload["provider_summary"]
|
let provider_summary = payload["provider_summary"]
|
||||||
.as_array()
|
.as_array()
|
||||||
.expect("provider summary should exist");
|
.expect("provider summary should exist");
|
||||||
assert_eq!(provider_summary.len(), 2);
|
assert_eq!(provider_summary.len(), 3);
|
||||||
assert_eq!(provider_summary[0]["provider"], "openai");
|
let provider_requests = provider_summary
|
||||||
assert_eq!(provider_summary[0]["requests"], 2);
|
.iter()
|
||||||
assert_eq!(provider_summary[1]["provider"], "claude");
|
.filter_map(|item| Some((item["provider"].as_str()?, item["requests"].as_u64()?)))
|
||||||
assert_eq!(provider_summary[1]["requests"], 1);
|
.collect::<std::collections::BTreeMap<_, _>>();
|
||||||
|
assert_eq!(provider_requests.get("openai"), Some(&2));
|
||||||
|
assert_eq!(provider_requests.get("claude"), Some(&1));
|
||||||
|
assert_eq!(provider_requests.get("bailian"), Some(&1));
|
||||||
assert_eq!(*upstream_hits.lock().expect("mutex should lock"), 0);
|
assert_eq!(*upstream_hits.lock().expect("mutex should lock"), 0);
|
||||||
|
|
||||||
gateway_handle.abort();
|
gateway_handle.abort();
|
||||||
|
|||||||
@@ -4389,7 +4389,7 @@ ORDER BY date ASC, total_cost_usd DESC, model ASC, provider_name ASC
|
|||||||
r#"
|
r#"
|
||||||
SELECT
|
SELECT
|
||||||
TO_CHAR(
|
TO_CHAR(
|
||||||
date_trunc('day', "usage".created_at + (
|
date_trunc('day', ("usage".created_at AT TIME ZONE 'UTC') + (
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
builder.push_bind(query.tz_offset_minutes);
|
builder.push_bind(query.tz_offset_minutes);
|
||||||
@@ -4484,53 +4484,12 @@ ORDER BY date ASC, total_cost_usd DESC, "usage".model ASC, "usage".provider_name
|
|||||||
Ok(items)
|
Ok(items)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn list_dashboard_daily_breakdown_aggregate_segments(
|
|
||||||
&self,
|
|
||||||
query: &UsageDashboardDailyBreakdownQuery,
|
|
||||||
) -> Result<Vec<StoredUsageDashboardDailyBreakdownRow>, DataLayerError> {
|
|
||||||
let cutoff_utc = match self.read_stats_daily_cutoff_date().await {
|
|
||||||
Ok(value) => value,
|
|
||||||
Err(err) if dashboard_should_fallback_to_raw_on_aggregate_error(&err) => {
|
|
||||||
return Ok(Vec::new());
|
|
||||||
}
|
|
||||||
Err(err) => return Err(err),
|
|
||||||
};
|
|
||||||
let Some(cutoff_utc) = cutoff_utc else {
|
|
||||||
return Ok(Vec::new());
|
|
||||||
};
|
|
||||||
let start_utc = dashboard_unix_secs_to_utc(query.created_from_unix_secs);
|
|
||||||
let end_utc = dashboard_unix_secs_to_utc(query.created_until_unix_secs);
|
|
||||||
let split = split_dashboard_daily_aggregate_range(start_utc, end_utc, cutoff_utc);
|
|
||||||
let Some((aggregate_start, aggregate_end)) = split.aggregate else {
|
|
||||||
return Ok(Vec::new());
|
|
||||||
};
|
|
||||||
|
|
||||||
self.list_dashboard_daily_breakdown_from_daily_aggregates(
|
|
||||||
aggregate_start,
|
|
||||||
aggregate_end,
|
|
||||||
query.user_id.as_deref(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn list_dashboard_daily_breakdown(
|
pub async fn list_dashboard_daily_breakdown(
|
||||||
&self,
|
&self,
|
||||||
query: &UsageDashboardDailyBreakdownQuery,
|
query: &UsageDashboardDailyBreakdownQuery,
|
||||||
) -> Result<Vec<StoredUsageDashboardDailyBreakdownRow>, DataLayerError> {
|
) -> Result<Vec<StoredUsageDashboardDailyBreakdownRow>, DataLayerError> {
|
||||||
if query.tz_offset_minutes != 0 {
|
if query.tz_offset_minutes != 0 {
|
||||||
let mut items = self
|
return self.list_dashboard_daily_breakdown_raw(query).await;
|
||||||
.list_dashboard_daily_breakdown_aggregate_segments(query)
|
|
||||||
.await?;
|
|
||||||
let mut aggregate_dates = items
|
|
||||||
.iter()
|
|
||||||
.map(|item| item.date.clone())
|
|
||||||
.collect::<std::collections::BTreeSet<_>>();
|
|
||||||
for item in self.list_dashboard_daily_breakdown_raw(query).await? {
|
|
||||||
if aggregate_dates.insert(item.date.clone()) {
|
|
||||||
items.push(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Ok(finalize_dashboard_daily_breakdown_rows(items));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let cutoff_utc = match self.read_stats_daily_cutoff_date().await {
|
let cutoff_utc = match self.read_stats_daily_cutoff_date().await {
|
||||||
|
|||||||
@@ -452,14 +452,32 @@ fn usage_sql_daily_cutoff_falls_back_to_imported_stats_daily() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn usage_sql_dashboard_daily_breakdown_falls_back_to_daily_totals() {
|
fn usage_sql_dashboard_daily_breakdown_falls_back_to_daily_totals() {
|
||||||
let source = include_str!("mod.rs");
|
let source = include_str!("mod.rs");
|
||||||
assert!(source.contains("list_dashboard_daily_breakdown_aggregate_segments"));
|
|
||||||
assert!(source.contains("list_dashboard_daily_breakdown_from_daily_totals"));
|
assert!(source.contains("list_dashboard_daily_breakdown_from_daily_totals"));
|
||||||
assert!(source.contains("'aggregate'::TEXT AS model"));
|
assert!(source.contains("'aggregate'::TEXT AS model"));
|
||||||
assert!(source.contains("FROM stats_daily"));
|
assert!(source.contains("FROM stats_daily"));
|
||||||
assert!(source.contains("FROM stats_user_daily"));
|
assert!(source.contains("FROM stats_user_daily"));
|
||||||
assert!(source.contains("detailed_dates.contains(&item.date)"));
|
assert!(source.contains("detailed_dates.contains(&item.date)"));
|
||||||
assert!(source.contains("query.tz_offset_minutes != 0"));
|
assert!(source.contains("query.tz_offset_minutes != 0"));
|
||||||
assert!(source.contains("aggregate_dates.insert(item.date.clone())"));
|
assert!(source.contains("return self.list_dashboard_daily_breakdown_raw(query).await;"));
|
||||||
|
assert!(!source.contains("aggregate_dates.insert(item.date.clone())"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn usage_sql_dashboard_daily_breakdown_keeps_all_local_day_model_provider_rows() {
|
||||||
|
let source = include_str!("mod.rs");
|
||||||
|
let raw_breakdown = source
|
||||||
|
.split("async fn list_dashboard_daily_breakdown_raw")
|
||||||
|
.nth(1)
|
||||||
|
.and_then(|tail| {
|
||||||
|
tail.split("pub async fn list_dashboard_daily_breakdown")
|
||||||
|
.next()
|
||||||
|
})
|
||||||
|
.expect("raw daily breakdown function should be present");
|
||||||
|
assert!(raw_breakdown.contains("GROUP BY date, \"usage\".model, \"usage\".provider_name"));
|
||||||
|
assert!(raw_breakdown.contains("ORDER BY date ASC, total_cost_usd DESC"));
|
||||||
|
assert!(raw_breakdown.contains("(\"usage\".created_at AT TIME ZONE 'UTC')"));
|
||||||
|
assert!(!raw_breakdown.contains("date_trunc('day', \"usage\".created_at +"));
|
||||||
|
assert!(!source.contains("if aggregate_dates.insert(item.date.clone())"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user