refactor(coach): collapse the duplicate daily-cap export to OPERATOR_COST_CAP

The operator-cost cap carried two export names for one value; keep the
accurate one (it gates only the operator-key path) and drop the alias.
This commit is contained in:
Marc-André Bombeck committed 2026-06-26 14:08:35 +02:00
1 parent e6ac604c83
commit 5f640e1420
4 files changed
+14 -19

No files matched your search

+6 -5
View File
@@ -3,7 +3,6 @@ import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
import { HttpError } from "@/lib/api-handler";
import {
buildDateKey,
MAX_TOKENS_PER_USER_PER_DAY,
OPERATOR_COST_CAP,
USER_PLAN_CAP,
resolveDailyCap,
@@ -76,7 +75,7 @@ describe("budget", () => {
it("over-budget throws HttpError(429)", async () => {
prismaMock.coachUsage.findUnique.mockResolvedValue({
totalTokens: MAX_TOKENS_PER_USER_PER_DAY,
totalTokens: OPERATOR_COST_CAP,
});
const { enforceBudget } = await import("../budget");
await expect(enforceBudget("user-1", "2026-05-10")).rejects.toMatchObject({
@@ -233,11 +232,13 @@ describe("reconcileSpend cached-token subtraction (F3)", () => {
});
});
describe("MAX_TOKENS_PER_USER_PER_DAY (F2 — reasoning-aware operator cap)", () => {
describe("OPERATOR_COST_CAP (F2 — reasoning-aware operator cap)", () => {
it("is sized for reasoning turns, not a single non-reasoning reply", () => {
// Was 25_000 (≈ one gpt-5.x reasoning turn). Raised so the operator-key
// path survives a normal day of reasoning turns.
expect(MAX_TOKENS_PER_USER_PER_DAY).toBeGreaterThanOrEqual(150_000);
expect(OPERATOR_COST_CAP).toBe(MAX_TOKENS_PER_USER_PER_DAY);
expect(OPERATOR_COST_CAP).toBeGreaterThanOrEqual(150_000);
// The user-plan cap is far more generous — a user's own egress is never
// gated on the operator-cost ceiling.
expect(USER_PLAN_CAP).toBeGreaterThan(OPERATOR_COST_CAP);
});
});
+3 -9
View File
@@ -34,13 +34,7 @@ import type { ProviderChainType } from "@/lib/ai/provider-chain";
* surgery) while leaving room for a normal day of reasoning turns on the
* operator's key. The cap stays a real ceiling on the operator's exposure.
*/
export const MAX_TOKENS_PER_USER_PER_DAY = 200_000;
/**
* v1.21.0 (F1) — historical name retained for any external reference; the
* operator-cost cap is the only cap the ledger enforces.
*/
export const OPERATOR_COST_CAP = MAX_TOKENS_PER_USER_PER_DAY;
export const OPERATOR_COST_CAP = 200_000;
/**
* v1.21.0 (F1) — the daily ceiling for a chain whose egress runs on the
@@ -108,7 +102,7 @@ export async function getDailyTokenSpend(
export async function enforceBudget(
userId: string,
dateKey: string = buildDateKey(),
cap: number = MAX_TOKENS_PER_USER_PER_DAY,
cap: number = OPERATOR_COST_CAP,
): Promise<void> {
const spent = await getDailyTokenSpend(userId, dateKey);
if (spent >= cap) {
@@ -148,7 +142,7 @@ export async function reserveBudget(
userId: string,
estimatedTokens: number,
dateKey: string = buildDateKey(),
cap: number = MAX_TOKENS_PER_USER_PER_DAY,
cap: number = OPERATOR_COST_CAP,
): Promise<ReserveBudgetResult> {
const reserved =
Number.isFinite(estimatedTokens) && estimatedTokens > 0
+2 -2
View File
@@ -26,7 +26,7 @@ import { singleUserTurn } from "@/lib/ai/types";
import {
buildDateKey,
getDailyTokenSpend,
MAX_TOKENS_PER_USER_PER_DAY,
OPERATOR_COST_CAP,
recordSpend,
} from "@/lib/ai/coach/budget";
import {
@@ -137,7 +137,7 @@ export async function deriveClarifyingQuestions(
// Budget gate — same per-day ledger the Coach chat enforces.
const dateKey = buildDateKey();
const spent = await getDailyTokenSpend(userId, dateKey);
if (spent >= MAX_TOKENS_PER_USER_PER_DAY) return fallback();
if (spent >= OPERATOR_COST_CAP) return fallback();
const provider = await resolveProvider(userId);
// v1.16.6 — same snapshot the Coach chat rides; lets the model