From ddb39df95f56eb9ffdc2b96e567c4913171de9e9 Mon Sep 17 00:00:00 2001 From: admin-valentin Date: Sat, 1 Aug 2026 09:57:52 +0000 Subject: [PATCH] feat(cc-050): add AiRequests controller (list + costs) --- src/ai-requests/ai-requests.controller.ts | 75 +++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 src/ai-requests/ai-requests.controller.ts diff --git a/src/ai-requests/ai-requests.controller.ts b/src/ai-requests/ai-requests.controller.ts new file mode 100644 index 0000000..fbe5be9 --- /dev/null +++ b/src/ai-requests/ai-requests.controller.ts @@ -0,0 +1,75 @@ +import { Controller, Get, Query } from '@nestjs/common'; +import { desc, eq, sum, count } from 'drizzle-orm'; +import { CurrentSession } from '../auth/session.decorator'; +import type { AuthenticatedSession } from '../auth/tenant.guard'; +import { db } from '../db/client'; +import { aiRequests } from '../db/schema'; + +@Controller('ai-requests') +export class AiRequestsController { + @Get() + async list( + @CurrentSession() session: AuthenticatedSession, + @Query('limit') limitStr?: string, + ) { + const limit = Math.min(Math.max(1, parseInt(limitStr ?? '200', 10) || 200), 500); + return db.query.aiRequests.findMany({ + where: eq(aiRequests.tenantId, session.tenantId), + orderBy: desc(aiRequests.createdAt), + limit, + columns: { + contextManifest: false, + }, + }); + } + + @Get('costs') + async costs(@CurrentSession() session: AuthenticatedSession) { + const [totals] = await db + .select({ + totalCostUsd: sum(aiRequests.costUsd), + totalRequests: count(), + }) + .from(aiRequests) + .where(eq(aiRequests.tenantId, session.tenantId)); + + const byModel = await db + .select({ + model: aiRequests.model, + requests: count(), + costUsd: sum(aiRequests.costUsd), + }) + .from(aiRequests) + .where(eq(aiRequests.tenantId, session.tenantId)) + .groupBy(aiRequests.model); + + const byPurpose = await db + .select({ + purpose: aiRequests.purpose, + requests: count(), + costUsd: sum(aiRequests.costUsd), + }) + .from(aiRequests) + .where(eq(aiRequests.tenantId, session.tenantId)) + .groupBy(aiRequests.purpose); + + const byStatus = await db + .select({ + resultStatus: aiRequests.resultStatus, + requests: count(), + }) + .from(aiRequests) + .where(eq(aiRequests.tenantId, session.tenantId)) + .groupBy(aiRequests.resultStatus); + + return { + totals: { + totalCostUsd: totals?.totalCostUsd ?? '0', + totalRequests: totals?.totalRequests ?? 0, + }, + byModel, + byPurpose, + byStatus, + }; + } +}