From adcf827cde1a06e69c7465af73c2666b87f74b0f Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 15:45:16 +0000 Subject: [PATCH 1/7] fix(usage): prefer requested_model over model for grouping, fall back to model --- src/app/api/profile/usage/route.ts | 12 +++++++++--- .../organization-usage-details-router.ts | 8 ++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/app/api/profile/usage/route.ts b/src/app/api/profile/usage/route.ts index 10ec01d1b..43a706276 100644 --- a/src/app/api/profile/usage/route.ts +++ b/src/app/api/profile/usage/route.ts @@ -27,7 +27,9 @@ export async function GET(request: NextRequest) { // Build the select object conditionally const selectFields = { date: sql`DATE(${microdollar_usage.created_at})`, - ...(groupByModel && { model: microdollar_usage.model }), + ...(groupByModel && { + model: sql`COALESCE(${microdollar_usage.requested_model}, ${microdollar_usage.model})`, + }), total_cost: sql`SUM(${microdollar_usage.cost})::float`, request_count: sql`COUNT(*)::float`, total_input_tokens: sql`SUM(${microdollar_usage.input_tokens})::float`, @@ -39,11 +41,15 @@ export async function GET(request: NextRequest) { // Build the group by and order by clauses conditionally const groupByClause = [ sql`DATE(${microdollar_usage.created_at})`, - ...(groupByModel ? [microdollar_usage.model] : []), + ...(groupByModel + ? [sql`COALESCE(${microdollar_usage.requested_model}, ${microdollar_usage.model})`] + : []), ]; const orderByClause = [ desc(sql`DATE(${microdollar_usage.created_at})`), - ...(groupByModel ? [microdollar_usage.model] : []), + ...(groupByModel + ? [sql`COALESCE(${microdollar_usage.requested_model}, ${microdollar_usage.model})`] + : []), ]; // Build where conditions based on view type diff --git a/src/routers/organizations/organization-usage-details-router.ts b/src/routers/organizations/organization-usage-details-router.ts index 817c6b6d7..4cf8b195c 100644 --- a/src/routers/organizations/organization-usage-details-router.ts +++ b/src/routers/organizations/organization-usage-details-router.ts @@ -318,7 +318,9 @@ export const organizationsUsageDetailsRouter = createTRPCRouter({ date: sql`DATE(${microdollar_usage.created_at})`.as('date'), userName: kilocode_users.google_user_name, userEmail: kilocode_users.google_user_email, - ...(groupByModel && { model: microdollar_usage.model }), + ...(groupByModel && { + model: sql`COALESCE(${microdollar_usage.requested_model}, ${microdollar_usage.model})`, + }), microdollarCost: sum(microdollar_usage.cost), tokenCount: sum( sql`${microdollar_usage.input_tokens} + ${microdollar_usage.output_tokens} + ${microdollar_usage.cache_write_tokens} + ${microdollar_usage.cache_hit_tokens}` @@ -334,7 +336,9 @@ export const organizationsUsageDetailsRouter = createTRPCRouter({ sql`DATE(${microdollar_usage.created_at})`, kilocode_users.google_user_name, kilocode_users.google_user_email, - ...(groupByModel ? [microdollar_usage.model] : []) + ...(groupByModel + ? [sql`COALESCE(${microdollar_usage.requested_model}, ${microdollar_usage.model})`] + : []) ) .orderBy(sql`DATE(${microdollar_usage.created_at}) DESC`) ); From bfcfdb0af9ddb176d36ad57933d09e3c8e4e3835 Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 07:54:04 +0000 Subject: [PATCH 2/7] fix(usage): update grouped-by-model test to set requested_model per row --- .../organizations/organization-usage-details-router.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/routers/organizations/organization-usage-details-router.test.ts b/src/routers/organizations/organization-usage-details-router.test.ts index 20afc01b2..3348247fa 100644 --- a/src/routers/organizations/organization-usage-details-router.test.ts +++ b/src/routers/organizations/organization-usage-details-router.test.ts @@ -219,6 +219,7 @@ describe('organizations usage details trpc router', () => { output_tokens: 200, created_at: now, model: 'gpt-4', + requested_model: 'gpt-4', }); await insertUsageWithOverrides({ @@ -229,6 +230,7 @@ describe('organizations usage details trpc router', () => { output_tokens: 100, created_at: now, model: 'gpt-3.5-turbo', + requested_model: 'gpt-3.5-turbo', }); const caller = await createCallerForUser(memberUser.id); From 829d99040bcc257a9f4efd253abb6fa886dace45 Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Tue, 31 Mar 2026 10:15:28 +0200 Subject: [PATCH 3/7] Reformat --- src/app/api/profile/usage/route.ts | 4 +++- .../organizations/organization-usage-details-router.ts | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/app/api/profile/usage/route.ts b/src/app/api/profile/usage/route.ts index 43a706276..c76272d89 100644 --- a/src/app/api/profile/usage/route.ts +++ b/src/app/api/profile/usage/route.ts @@ -28,7 +28,9 @@ export async function GET(request: NextRequest) { const selectFields = { date: sql`DATE(${microdollar_usage.created_at})`, ...(groupByModel && { - model: sql`COALESCE(${microdollar_usage.requested_model}, ${microdollar_usage.model})`, + model: sql< + string | null + >`COALESCE(${microdollar_usage.requested_model}, ${microdollar_usage.model})`, }), total_cost: sql`SUM(${microdollar_usage.cost})::float`, request_count: sql`COUNT(*)::float`, diff --git a/src/routers/organizations/organization-usage-details-router.ts b/src/routers/organizations/organization-usage-details-router.ts index 4cf8b195c..232f0c2d8 100644 --- a/src/routers/organizations/organization-usage-details-router.ts +++ b/src/routers/organizations/organization-usage-details-router.ts @@ -319,7 +319,9 @@ export const organizationsUsageDetailsRouter = createTRPCRouter({ userName: kilocode_users.google_user_name, userEmail: kilocode_users.google_user_email, ...(groupByModel && { - model: sql`COALESCE(${microdollar_usage.requested_model}, ${microdollar_usage.model})`, + model: sql< + string | null + >`COALESCE(${microdollar_usage.requested_model}, ${microdollar_usage.model})`, }), microdollarCost: sum(microdollar_usage.cost), tokenCount: sum( From 7b07a65a8df7e2bd730caa55c60122fa27bab5ae Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 08:22:39 +0000 Subject: [PATCH 4/7] fix(usage): use requested_model with model fallback in getTimeSeries for Models breakdown --- .../organizations/organization-usage-details-router.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/routers/organizations/organization-usage-details-router.ts b/src/routers/organizations/organization-usage-details-router.ts index 232f0c2d8..9e777e357 100644 --- a/src/routers/organizations/organization-usage-details-router.ts +++ b/src/routers/organizations/organization-usage-details-router.ts @@ -170,7 +170,7 @@ export const organizationsUsageDetailsRouter = createTRPCRouter({ datetime: timeBucket.as('datetime'), userName: kilocode_users.google_user_name, userEmail: kilocode_users.google_user_email, - model: microdollar_usage.model, + model: sql`COALESCE(${microdollar_usage.requested_model}, ${microdollar_usage.model})`, provider: microdollar_usage.provider, projectId: microdollar_usage.project_id, costMicrodollars: sum(microdollar_usage.cost), @@ -191,7 +191,7 @@ export const organizationsUsageDetailsRouter = createTRPCRouter({ timeBucket, kilocode_users.google_user_name, kilocode_users.google_user_email, - microdollar_usage.model, + sql`COALESCE(${microdollar_usage.requested_model}, ${microdollar_usage.model})`, microdollar_usage.provider, microdollar_usage.project_id ) @@ -319,9 +319,7 @@ export const organizationsUsageDetailsRouter = createTRPCRouter({ userName: kilocode_users.google_user_name, userEmail: kilocode_users.google_user_email, ...(groupByModel && { - model: sql< - string | null - >`COALESCE(${microdollar_usage.requested_model}, ${microdollar_usage.model})`, + model: sql`COALESCE(${microdollar_usage.requested_model}, ${microdollar_usage.model})`, }), microdollarCost: sum(microdollar_usage.cost), tokenCount: sum( From 87303c22593ce0f6b6e6fd19e57b44b74bfa86c4 Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 08:23:07 +0000 Subject: [PATCH 5/7] style(api): inline sql for COALESCE expression - remove multi-line generic type annotation for COALESCE in usage route - keep behavior unchanged --- src/app/api/profile/usage/route.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/app/api/profile/usage/route.ts b/src/app/api/profile/usage/route.ts index c76272d89..43a706276 100644 --- a/src/app/api/profile/usage/route.ts +++ b/src/app/api/profile/usage/route.ts @@ -28,9 +28,7 @@ export async function GET(request: NextRequest) { const selectFields = { date: sql`DATE(${microdollar_usage.created_at})`, ...(groupByModel && { - model: sql< - string | null - >`COALESCE(${microdollar_usage.requested_model}, ${microdollar_usage.model})`, + model: sql`COALESCE(${microdollar_usage.requested_model}, ${microdollar_usage.model})`, }), total_cost: sql`SUM(${microdollar_usage.cost})::float`, request_count: sql`COUNT(*)::float`, From 168a94615fcaf76e67c1db5b8c1130941aee2dc6 Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Tue, 31 Mar 2026 10:45:13 +0200 Subject: [PATCH 6/7] Reformat --- src/app/api/profile/usage/route.ts | 4 +++- .../organizations/organization-usage-details-router.ts | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/app/api/profile/usage/route.ts b/src/app/api/profile/usage/route.ts index 43a706276..c76272d89 100644 --- a/src/app/api/profile/usage/route.ts +++ b/src/app/api/profile/usage/route.ts @@ -28,7 +28,9 @@ export async function GET(request: NextRequest) { const selectFields = { date: sql`DATE(${microdollar_usage.created_at})`, ...(groupByModel && { - model: sql`COALESCE(${microdollar_usage.requested_model}, ${microdollar_usage.model})`, + model: sql< + string | null + >`COALESCE(${microdollar_usage.requested_model}, ${microdollar_usage.model})`, }), total_cost: sql`SUM(${microdollar_usage.cost})::float`, request_count: sql`COUNT(*)::float`, diff --git a/src/routers/organizations/organization-usage-details-router.ts b/src/routers/organizations/organization-usage-details-router.ts index 9e777e357..6ba7bb800 100644 --- a/src/routers/organizations/organization-usage-details-router.ts +++ b/src/routers/organizations/organization-usage-details-router.ts @@ -319,7 +319,9 @@ export const organizationsUsageDetailsRouter = createTRPCRouter({ userName: kilocode_users.google_user_name, userEmail: kilocode_users.google_user_email, ...(groupByModel && { - model: sql`COALESCE(${microdollar_usage.requested_model}, ${microdollar_usage.model})`, + model: sql< + string | null + >`COALESCE(${microdollar_usage.requested_model}, ${microdollar_usage.model})`, }), microdollarCost: sum(microdollar_usage.cost), tokenCount: sum( From 59bc11e7126d1c408bce6635b92e0e89fa987134 Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 08:51:33 +0000 Subject: [PATCH 7/7] test(usage): add COALESCE fallback coverage for null requested_model --- .../organization-usage-details-router.test.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/routers/organizations/organization-usage-details-router.test.ts b/src/routers/organizations/organization-usage-details-router.test.ts index 3348247fa..a7b7c9e97 100644 --- a/src/routers/organizations/organization-usage-details-router.test.ts +++ b/src/routers/organizations/organization-usage-details-router.test.ts @@ -252,6 +252,30 @@ describe('organizations usage details trpc router', () => { expect(gpt35Result?.microdollarCost).toBe('500'); }); + it('should fall back to model when requested_model is null (legacy rows)', async () => { + const now = await getDateFromDb(); + + await insertUsageWithOverrides({ + kilo_user_id: memberUser.id, + organization_id: testOrganization.id, + cost: 750, + created_at: now, + model: 'legacy-model', + requested_model: null, + }); + + const caller = await createCallerForUser(memberUser.id); + + const result = await caller.organizations.usageDetails.get({ + organizationId: testOrganization.id, + groupByModel: true, + }); + + const legacyRow = result.daily.find(d => d.model === 'legacy-model'); + expect(legacyRow).toBeDefined(); + expect(legacyRow?.microdollarCost).toBe('750'); + }); + it('should handle empty usage data', async () => { const caller = await createCallerForUser(memberUser.id);