feat(cost-guard): global Gemini daily budget + EOC alarm; trial requires verified email
This commit is contained in:
@@ -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> {
|
||||
|
||||
Reference in New Issue
Block a user