feat(cost-guard): global Gemini daily budget + EOC alarm; trial requires verified email

This commit is contained in:
Sven (AAMOS AI)
2026-08-12 06:43:32 +07:00
parent 2cd00f0106
commit 6c581bec1a
7 changed files with 96 additions and 10 deletions
+18 -1
View File
@@ -109,11 +109,17 @@ export interface BudgetStore {
getDailySpendUsd(): Promise<number>;
/** Increment daily spend by amountUsd; return new total. */
incrementDailySpendUsd(amountUsd: number): Promise<number>;
/**
* Optional: called when the daily budget is exceeded. Implementations can
* raise an alert (e.g. write to Redis / DB) so ops-summary/EOC can surface it.
*/
raiseBudgetAlarm?(): Promise<void>;
}
/** In-memory budget store for tests / single-process dev. */
export class MemoryBudgetStore implements BudgetStore {
private spend = 0;
private alarmRaised = false;
async getDailySpendUsd(): Promise<number> {
return this.spend;
}
@@ -121,8 +127,15 @@ export class MemoryBudgetStore implements BudgetStore {
this.spend += amountUsd;
return this.spend;
}
async raiseBudgetAlarm(): Promise<void> {
this.alarmRaised = true;
}
alarmWasRaised(): boolean {
return this.alarmRaised;
}
reset() {
this.spend = 0;
this.alarmRaised = false;
}
}
@@ -920,7 +933,11 @@ Språk: ${lang}.`;
private async isOverBudget(estimateUsd: number): Promise<boolean> {
if (this.cfg.dailyBudgetUsd <= 0 || !this.cfg.budgetStore) return false;
const current = await this.cfg.budgetStore.getDailySpendUsd();
return current + estimateUsd > this.cfg.dailyBudgetUsd;
const over = current + estimateUsd > this.cfg.dailyBudgetUsd;
if (over && this.cfg.budgetStore.raiseBudgetAlarm) {
await this.cfg.budgetStore.raiseBudgetAlarm();
}
return over;
}
private async recordSpend(costUsd: number): Promise<void> {