mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-30 03:00:41 -04:00
fix(platform-cost): apply all dashboard filters to raw SQL percentile/bucket queries
Raw SQL queries for percentile and histogram bucket distributions were only filtering by createdAt >= start, ignoring end, provider, user_id, model, and block_name. This caused percentile/bucket stats to silently include data outside the selected filter scope. Also: add P75 summary card to frontend, fix skeleton count (8->12) to match rendered card count, fix "Avg Cost / Request" subtitle to accurately say "cost-bearing requests", and add test assertion that raw queries receive active filter params.
This commit is contained in:
@@ -275,6 +275,42 @@ async def get_platform_cost_dashboard(
|
||||
"trackingAmount": True,
|
||||
}
|
||||
|
||||
# Build parameterised WHERE clause for the raw SQL percentile/bucket
|
||||
# queries so they honour all active dashboard filters, not just start date.
|
||||
raw_params: list = [start]
|
||||
raw_where_clauses = [
|
||||
'"trackingType" = \'cost_usd\'',
|
||||
'"createdAt" >= $1',
|
||||
]
|
||||
param_idx = 2 # $1 is already start
|
||||
|
||||
if end is not None:
|
||||
raw_where_clauses.append(f'"createdAt" <= ${param_idx}')
|
||||
raw_params.append(end)
|
||||
param_idx += 1
|
||||
|
||||
if provider is not None:
|
||||
raw_where_clauses.append(f'"provider" = ${param_idx}')
|
||||
raw_params.append(provider.lower())
|
||||
param_idx += 1
|
||||
|
||||
if user_id is not None:
|
||||
raw_where_clauses.append(f'"userId" = ${param_idx}')
|
||||
raw_params.append(user_id)
|
||||
param_idx += 1
|
||||
|
||||
if model is not None:
|
||||
raw_where_clauses.append(f'"model" = ${param_idx}')
|
||||
raw_params.append(model)
|
||||
param_idx += 1
|
||||
|
||||
if block_name is not None:
|
||||
raw_where_clauses.append(f'LOWER("blockName") = LOWER(${param_idx})')
|
||||
raw_params.append(block_name)
|
||||
param_idx += 1
|
||||
|
||||
raw_where = " AND ".join(raw_where_clauses)
|
||||
|
||||
# Run all six aggregation queries in parallel.
|
||||
(
|
||||
by_provider_groups,
|
||||
@@ -317,7 +353,7 @@ async def get_platform_cost_dashboard(
|
||||
},
|
||||
count=True,
|
||||
),
|
||||
# Percentile distribution of cost per request.
|
||||
# Percentile distribution of cost per request (respects all filters).
|
||||
query_raw_with_schema(
|
||||
"SELECT"
|
||||
" percentile_cont(0.5) WITHIN GROUP"
|
||||
@@ -329,11 +365,10 @@ async def get_platform_cost_dashboard(
|
||||
" percentile_cont(0.99) WITHIN GROUP"
|
||||
' (ORDER BY "costMicrodollars") as p99'
|
||||
' FROM {schema_prefix}"PlatformCostLog"'
|
||||
" WHERE \"trackingType\" = 'cost_usd'"
|
||||
' AND "createdAt" >= $1',
|
||||
start,
|
||||
f" WHERE {raw_where}",
|
||||
*raw_params,
|
||||
),
|
||||
# Histogram buckets for cost distribution.
|
||||
# Histogram buckets for cost distribution (respects all filters).
|
||||
query_raw_with_schema(
|
||||
"SELECT"
|
||||
" CASE"
|
||||
@@ -351,11 +386,10 @@ async def get_platform_cost_dashboard(
|
||||
" END as bucket,"
|
||||
" COUNT(*) as count"
|
||||
' FROM {schema_prefix}"PlatformCostLog"'
|
||||
" WHERE \"trackingType\" = 'cost_usd'"
|
||||
' AND "createdAt" >= $1'
|
||||
f" WHERE {raw_where}"
|
||||
" GROUP BY bucket"
|
||||
' ORDER BY MIN("costMicrodollars")',
|
||||
start,
|
||||
*raw_params,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -416,6 +416,7 @@ class TestGetPlatformCostDashboard:
|
||||
mock_actions.group_by = AsyncMock(side_effect=[[], [], [], []])
|
||||
mock_actions.find_many = AsyncMock(return_value=[])
|
||||
|
||||
raw_mock = AsyncMock(side_effect=[[], []])
|
||||
with (
|
||||
patch(
|
||||
"backend.data.platform_cost.PrismaLog.prisma",
|
||||
@@ -427,8 +428,7 @@ class TestGetPlatformCostDashboard:
|
||||
),
|
||||
patch(
|
||||
"backend.data.platform_cost.query_raw_with_schema",
|
||||
new_callable=AsyncMock,
|
||||
side_effect=[[], []],
|
||||
raw_mock,
|
||||
),
|
||||
):
|
||||
await get_platform_cost_dashboard(
|
||||
@@ -440,6 +440,12 @@ class TestGetPlatformCostDashboard:
|
||||
# The where dict passed to the first call should include createdAt
|
||||
first_call_kwargs = mock_actions.group_by.call_args_list[0][1]
|
||||
assert "createdAt" in first_call_kwargs.get("where", {})
|
||||
# Raw SQL queries should receive provider and user_id as parameters
|
||||
assert raw_mock.await_count == 2
|
||||
raw_call_args = raw_mock.call_args_list[0][0] # positional args of 1st call
|
||||
raw_params = raw_call_args[1:] # first arg is the query template
|
||||
assert "openai" in raw_params
|
||||
assert "u1" in raw_params
|
||||
|
||||
|
||||
def _make_prisma_log_row(
|
||||
|
||||
@@ -205,7 +205,7 @@ export function PlatformCostContent({ searchParams }: Props) {
|
||||
{loading ? (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="grid grid-cols-2 gap-4 md:grid-cols-4">
|
||||
{[...Array(8)].map((_, i) => (
|
||||
{[...Array(12)].map((_, i) => (
|
||||
<Skeleton key={i} className="h-20 rounded-lg" />
|
||||
))}
|
||||
</div>
|
||||
@@ -240,7 +240,7 @@ export function PlatformCostContent({ searchParams }: Props) {
|
||||
value={formatMicrodollars(
|
||||
dashboard.avg_cost_microdollars_per_request ?? 0,
|
||||
)}
|
||||
subtitle="Known cost divided by total requests"
|
||||
subtitle="Known cost divided by cost-bearing requests"
|
||||
/>
|
||||
<SummaryCard
|
||||
label="Avg Input Tokens"
|
||||
@@ -268,6 +268,13 @@ export function PlatformCostContent({ searchParams }: Props) {
|
||||
)}
|
||||
subtitle="Median cost per request"
|
||||
/>
|
||||
<SummaryCard
|
||||
label="P75 Cost / Request"
|
||||
value={formatMicrodollars(
|
||||
dashboard.cost_p75_microdollars ?? 0,
|
||||
)}
|
||||
subtitle="75th percentile cost"
|
||||
/>
|
||||
<SummaryCard
|
||||
label="P95 Cost / Request"
|
||||
value={formatMicrodollars(
|
||||
|
||||
Reference in New Issue
Block a user