SPB Git forge

spb/immbot-ai

Public
1commits 1branches 0releases
1.5 MBsize
maindefault branch
20 days agolast push
TypeScript 98.3% CSS 0.9% Shell 0.7%
2.6 KB · 70 lines typescript
Raw Blame History
1// Suivi d'usage et budgets : plafonds quotidiens/mensuels par utilisateur + budget global,2// appliqués côté serveur AVANT chaque appel de modèle.34import { get, getSetting, run } from "./db/index.ts";56export type BudgetConfig = {7  dailyPerUserUSD: number;8  monthlyPerUserUSD: number;9  monthlyGlobalUSD: number;10  dailyRequestsPerUser: number;11};1213export const DEFAULT_BUDGETS: BudgetConfig = {14  dailyPerUserUSD: 1.5,15  monthlyPerUserUSD: 15,16  monthlyGlobalUSD: 200,17  dailyRequestsPerUser: 200,18};1920export function getBudgets(): BudgetConfig {21  return { ...DEFAULT_BUDGETS, ...getSetting<Partial<BudgetConfig>>("budgets", {}) };22}2324export function checkBudget(userId: number): { ok: boolean; reason?: string } {25  const b = getBudgets();26  const day = get<{ c: number; n: number }>(27    "SELECT COALESCE(SUM(cost),0) as c, COUNT(*) as n FROM usage_log WHERE user_id = ? AND created_at >= datetime('now','start of day')",28    userId29  );30  if ((day?.n ?? 0) >= b.dailyRequestsPerUser)31    return { ok: false, reason: "Limite quotidienne de requêtes atteinte. Réessayez demain." };32  if ((day?.c ?? 0) >= b.dailyPerUserUSD)33    return { ok: false, reason: "Budget quotidien atteint. Réessayez demain ou choisissez un modèle économique." };34  const month = get<{ c: number }>(35    "SELECT COALESCE(SUM(cost),0) as c FROM usage_log WHERE user_id = ? AND created_at >= datetime('now','start of month')",36    userId37  );38  if ((month?.c ?? 0) >= b.monthlyPerUserUSD) return { ok: false, reason: "Budget mensuel personnel atteint." };39  const global = get<{ c: number }>(40    "SELECT COALESCE(SUM(cost),0) as c FROM usage_log WHERE created_at >= datetime('now','start of month')"41  );42  if ((global?.c ?? 0) >= b.monthlyGlobalUSD)43    return { ok: false, reason: "Budget global de la plateforme atteint ce mois-ci. Contactez le professeur." };44  return { ok: true };45}4647export function logUsage(opts: {48  userId: number;49  model: string;50  kind?: string;51  tokensIn: number;52  tokensOut: number;53  cost: number;54  latencyMs: number;55  ok?: boolean;56  error?: string;57}) {58  run(59    "INSERT INTO usage_log (user_id, model, kind, tokens_in, tokens_out, cost, latency_ms, ok, error) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",60    opts.userId, opts.model, opts.kind ?? "chat", opts.tokensIn, opts.tokensOut, opts.cost, opts.latencyMs, opts.ok === false ? 0 : 1, opts.error ?? null61  );62}6364export function logActivity(userId: number, kind: string, courseCode?: string | null, durationS = 0, meta: unknown = {}) {65  run(66    "INSERT INTO activity_log (user_id, kind, course_code, duration_s, meta) VALUES (?, ?, ?, ?, ?)",67    userId, kind, courseCode ?? null, durationS, JSON.stringify(meta)68  );69}70