diff --git a/autogpt_platform/backend/backend/data/platform_cost.py b/autogpt_platform/backend/backend/data/platform_cost.py index e43ae147d4..3baa0a58a7 100644 --- a/autogpt_platform/backend/backend/data/platform_cost.py +++ b/autogpt_platform/backend/backend/data/platform_cost.py @@ -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, ), ) diff --git a/autogpt_platform/backend/backend/data/platform_cost_test.py b/autogpt_platform/backend/backend/data/platform_cost_test.py index 1ddf2b3e8a..7649eff524 100644 --- a/autogpt_platform/backend/backend/data/platform_cost_test.py +++ b/autogpt_platform/backend/backend/data/platform_cost_test.py @@ -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( diff --git a/autogpt_platform/frontend/src/app/(platform)/admin/platform-costs/components/PlatformCostContent.tsx b/autogpt_platform/frontend/src/app/(platform)/admin/platform-costs/components/PlatformCostContent.tsx index 8e785320dc..636f4f1dd8 100644 --- a/autogpt_platform/frontend/src/app/(platform)/admin/platform-costs/components/PlatformCostContent.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/admin/platform-costs/components/PlatformCostContent.tsx @@ -205,7 +205,7 @@ export function PlatformCostContent({ searchParams }: Props) { {loading ? (