// Suivi d'usage et budgets : plafonds quotidiens/mensuels par utilisateur + budget global, // appliqués côté serveur AVANT chaque appel de modèle. import { get, getSetting, run } from "./db/index.ts"; export type BudgetConfig = { dailyPerUserUSD: number; monthlyPerUserUSD: number; monthlyGlobalUSD: number; dailyRequestsPerUser: number; }; export const DEFAULT_BUDGETS: BudgetConfig = { dailyPerUserUSD: 1.5, monthlyPerUserUSD: 15, monthlyGlobalUSD: 200, dailyRequestsPerUser: 200, }; export function getBudgets(): BudgetConfig { return { ...DEFAULT_BUDGETS, ...getSetting>("budgets", {}) }; } export function checkBudget(userId: number): { ok: boolean; reason?: string } { const b = getBudgets(); const day = get<{ c: number; n: number }>( "SELECT COALESCE(SUM(cost),0) as c, COUNT(*) as n FROM usage_log WHERE user_id = ? AND created_at >= datetime('now','start of day')", userId ); if ((day?.n ?? 0) >= b.dailyRequestsPerUser) return { ok: false, reason: "Limite quotidienne de requêtes atteinte. Réessayez demain." }; if ((day?.c ?? 0) >= b.dailyPerUserUSD) return { ok: false, reason: "Budget quotidien atteint. Réessayez demain ou choisissez un modèle économique." }; const month = get<{ c: number }>( "SELECT COALESCE(SUM(cost),0) as c FROM usage_log WHERE user_id = ? AND created_at >= datetime('now','start of month')", userId ); if ((month?.c ?? 0) >= b.monthlyPerUserUSD) return { ok: false, reason: "Budget mensuel personnel atteint." }; const global = get<{ c: number }>( "SELECT COALESCE(SUM(cost),0) as c FROM usage_log WHERE created_at >= datetime('now','start of month')" ); if ((global?.c ?? 0) >= b.monthlyGlobalUSD) return { ok: false, reason: "Budget global de la plateforme atteint ce mois-ci. Contactez le professeur." }; return { ok: true }; } export function logUsage(opts: { userId: number; model: string; kind?: string; tokensIn: number; tokensOut: number; cost: number; latencyMs: number; ok?: boolean; error?: string; }) { run( "INSERT INTO usage_log (user_id, model, kind, tokens_in, tokens_out, cost, latency_ms, ok, error) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", opts.userId, opts.model, opts.kind ?? "chat", opts.tokensIn, opts.tokensOut, opts.cost, opts.latencyMs, opts.ok === false ? 0 : 1, opts.error ?? null ); } export function logActivity(userId: number, kind: string, courseCode?: string | null, durationS = 0, meta: unknown = {}) { run( "INSERT INTO activity_log (user_id, kind, course_code, duration_s, meta) VALUES (?, ?, ?, ?, ?)", userId, kind, courseCode ?? null, durationS, JSON.stringify(meta) ); }