feat: cap per-user AI token spend
This commit is contained in:
@@ -22,7 +22,7 @@ public sealed class AiUsageController : ControllerBase
|
||||
}
|
||||
|
||||
public sealed record UsagePeriodDto(int Calls, long InputCharacters, long OutputCharacters, long EstimatedTokens);
|
||||
public sealed record UsageDto(UsagePeriodDto CurrentMonth, UsagePeriodDto AllTime, string Plan, int MonthlyCallLimit);
|
||||
public sealed record UsageDto(UsagePeriodDto CurrentMonth, UsagePeriodDto AllTime, string Plan, int MonthlyCallLimit, long MonthlyTokenLimit);
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<UsageDto>> Get(CancellationToken cancellationToken)
|
||||
@@ -37,7 +37,8 @@ public sealed class AiUsageController : ControllerBase
|
||||
await SumAsync(_db.AiInteractions.Where(x => x.OwnerUserId == user.Id && x.CreatedAtUtc >= monthStart), cancellationToken),
|
||||
await SumAsync(_db.AiInteractions.Where(x => x.OwnerUserId == user.Id), cancellationToken),
|
||||
entitlements.AdvancedAi ? "premium" : "free",
|
||||
entitlements.MonthlyAiCalls));
|
||||
entitlements.MonthlyAiCalls,
|
||||
entitlements.MonthlyAiTokens));
|
||||
}
|
||||
|
||||
private static async Task<UsagePeriodDto> SumAsync(IQueryable<AiInteraction> query, CancellationToken cancellationToken)
|
||||
|
||||
@@ -44,10 +44,17 @@ public sealed class AiWorkspaceController : ControllerBase
|
||||
if (_db is not null)
|
||||
{
|
||||
var roles = await _users.GetRolesAsync(user);
|
||||
var limit = AccountPlans.ForRoles(roles).MonthlyAiCalls;
|
||||
var entitlements = AccountPlans.ForRoles(roles);
|
||||
var monthStart = new DateTimeOffset(DateTime.UtcNow.Year, DateTime.UtcNow.Month, 1, 0, 0, 0, TimeSpan.Zero);
|
||||
var used = await _db.AiInteractions.CountAsync(x => x.OwnerUserId == user.Id && x.CreatedAtUtc >= monthStart, ct);
|
||||
if (used >= limit) return StatusCode(StatusCodes.Status429TooManyRequests, $"Monthly AI limit reached ({limit} generations). Upgrade your plan or try again next month.");
|
||||
var used = await _db.AiInteractions
|
||||
.Where(x => x.OwnerUserId == user.Id && x.CreatedAtUtc >= monthStart)
|
||||
.GroupBy(_ => 1)
|
||||
.Select(g => new { Calls = g.Count(), Tokens = g.Sum(x => (long)x.EstimatedTokenCount) })
|
||||
.FirstOrDefaultAsync(ct);
|
||||
if ((used?.Calls ?? 0) >= entitlements.MonthlyAiCalls)
|
||||
return StatusCode(StatusCodes.Status429TooManyRequests, $"Monthly AI limit reached ({entitlements.MonthlyAiCalls} generations). Upgrade your plan or try again next month.");
|
||||
if ((used?.Tokens ?? 0) >= entitlements.MonthlyAiTokens)
|
||||
return StatusCode(StatusCodes.Status429TooManyRequests, $"Monthly AI cost limit reached ({entitlements.MonthlyAiTokens:N0} estimated tokens). Upgrade your plan or try again next month.");
|
||||
}
|
||||
|
||||
try
|
||||
|
||||
Reference in New Issue
Block a user