ai-patterns
Version:
Production-ready TypeScript patterns to build solid and robust AI applications. Retry logic, circuit breakers, rate limiting, human-in-the-loop escalation, prompt versioning, response validation, context window management, and more—all with complete type
275 lines • 10.6 kB
JavaScript
;
/**
* Cost Tracking Pattern
*
* Monitors and controls AI spending in real-time, preventing budget overruns
* and optimizing costs across your application.
*
* @example
* ```typescript
* const result = await costTracking({
* execute: async () => {
* const { text, usage } = await generateText({
* model: openai('gpt-4-turbo'),
* prompt: longPrompt
* });
* return { value: text, tokens: usage.totalTokens };
* },
* costPerToken: ModelCost.GPT4_TURBO,
* monthlyBudget: 500,
* dailyLimit: 50,
* onBudgetWarning: (spent, limit) => {
* console.warn(`Budget at 80%: $${spent}/$${limit}`);
* },
* tags: { feature: 'chatbot', userId: 'user-123' }
* });
* ```
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.InMemoryCostStorage = void 0;
exports.costTracking = costTracking;
exports.createCostTracker = createCostTracker;
const common_1 = require("../types/common");
const errors_1 = require("../types/errors");
const storage_1 = require("../common/storage");
/**
* In-memory cost storage implementation using GlobalStorage
*/
class InMemoryCostStorage {
constructor() {
this.namespace = storage_1.StorageNamespace.COST_TRACKING;
this.initialized = false;
this.storage = storage_1.GlobalStorage.getInstance();
}
async ensureInitialized() {
if (this.initialized)
return;
const now = Date.now();
const periods = ["monthly", "daily", "hourly"];
const durations = {
monthly: 30 * 24 * 60 * 60 * 1000,
daily: 24 * 60 * 60 * 1000,
hourly: 60 * 60 * 1000,
};
for (const period of periods) {
const existing = await this.storage.get(this.namespace, period);
if (!existing) {
await this.storage.set(this.namespace, period, {
spent: 0,
periodStart: now,
periodDuration: durations[period],
});
}
}
this.initialized = true;
}
async getSpent(period) {
await this.ensureInitialized();
const track = await this.storage.get(this.namespace, period);
if (!track)
return 0;
const now = Date.now();
// Reset if period has elapsed
if (now - track.periodStart > track.periodDuration) {
track.spent = 0;
track.periodStart = now;
await this.storage.set(this.namespace, period, track);
}
return track.spent;
}
async addSpent(period, amount) {
await this.ensureInitialized();
const track = await this.storage.get(this.namespace, period);
if (!track)
return;
const now = Date.now();
// Reset if period has elapsed
if (now - track.periodStart > track.periodDuration) {
track.spent = 0;
track.periodStart = now;
}
track.spent += amount;
await this.storage.set(this.namespace, period, track);
}
async resetSpent(period) {
await this.ensureInitialized();
const track = await this.storage.get(this.namespace, period);
if (!track)
return;
track.spent = 0;
track.periodStart = Date.now();
await this.storage.set(this.namespace, period, track);
}
}
exports.InMemoryCostStorage = InMemoryCostStorage;
/**
* Default storage instance (lazy initialization)
*/
let defaultStorage = null;
function getDefaultStorage() {
if (!defaultStorage) {
defaultStorage = new InMemoryCostStorage();
}
return defaultStorage;
}
/**
* Check if budget would be exceeded
*/
async function checkBudgetLimits(cost, config, storage) {
const { monthlyBudget, dailyLimit, hourlyLimit, budget, logger = common_1.defaultLogger } = config;
// Check monthly budget
const monthlyMax = budget?.monthly ?? monthlyBudget;
if (monthlyMax !== undefined) {
const monthlySpent = await storage.getSpent("monthly");
const newMonthlyTotal = monthlySpent + cost;
if (newMonthlyTotal > monthlyMax) {
logger.error(`Monthly budget exceeded: $${newMonthlyTotal.toFixed(2)} > $${monthlyMax.toFixed(2)}`);
if (config.onBudgetExceeded) {
await config.onBudgetExceeded(newMonthlyTotal, monthlyMax);
}
throw new errors_1.PatternError(`Monthly budget exceeded: $${newMonthlyTotal.toFixed(2)} > $${monthlyMax.toFixed(2)}`, errors_1.ErrorCode.BUDGET_EXCEEDED, undefined, { period: "monthly", spent: newMonthlyTotal, limit: monthlyMax });
}
}
// Check daily limit
const dailyMax = budget?.daily ?? dailyLimit;
if (dailyMax !== undefined) {
const dailySpent = await storage.getSpent("daily");
const newDailyTotal = dailySpent + cost;
if (newDailyTotal > dailyMax) {
logger.error(`Daily budget exceeded: $${newDailyTotal.toFixed(2)} > $${dailyMax.toFixed(2)}`);
if (config.onBudgetExceeded) {
await config.onBudgetExceeded(newDailyTotal, dailyMax);
}
throw new errors_1.PatternError(`Daily budget exceeded: $${newDailyTotal.toFixed(2)} > $${dailyMax.toFixed(2)}`, errors_1.ErrorCode.BUDGET_EXCEEDED, undefined, { period: "daily", spent: newDailyTotal, limit: dailyMax });
}
}
// Check hourly limit
const hourlyMax = budget?.hourly ?? hourlyLimit;
if (hourlyMax !== undefined) {
const hourlySpent = await storage.getSpent("hourly");
const newHourlyTotal = hourlySpent + cost;
if (newHourlyTotal > hourlyMax) {
logger.error(`Hourly budget exceeded: $${newHourlyTotal.toFixed(2)} > $${hourlyMax.toFixed(2)}`);
if (config.onBudgetExceeded) {
await config.onBudgetExceeded(newHourlyTotal, hourlyMax);
}
throw new errors_1.PatternError(`Hourly budget exceeded: $${newHourlyTotal.toFixed(2)} > $${hourlyMax.toFixed(2)}`, errors_1.ErrorCode.BUDGET_EXCEEDED, undefined, { period: "hourly", spent: newHourlyTotal, limit: hourlyMax });
}
}
}
/**
* Trigger alerts based on spending thresholds
*/
async function triggerAlerts(cost, config, storage) {
const { monthlyBudget, budget, alerts, logger = common_1.defaultLogger } = config;
// Process custom alerts
if (alerts && alerts.length > 0) {
const monthlyMax = budget?.monthly ?? monthlyBudget;
if (monthlyMax !== undefined) {
const monthlySpent = await storage.getSpent("monthly");
const newMonthlyTotal = monthlySpent + cost;
const percentage = newMonthlyTotal / monthlyMax;
for (const alert of alerts) {
if (percentage >= alert.threshold && monthlySpent / monthlyMax < alert.threshold) {
logger.warn(`Budget alert: ${(alert.threshold * 100).toFixed(0)}% threshold reached - $${newMonthlyTotal.toFixed(2)}/$${monthlyMax.toFixed(2)}`);
await alert.action(newMonthlyTotal, monthlyMax);
}
}
}
}
// Default warning at 80%
if (config.onBudgetWarning) {
const monthlyMax = budget?.monthly ?? monthlyBudget;
if (monthlyMax !== undefined) {
const monthlySpent = await storage.getSpent("monthly");
const newMonthlyTotal = monthlySpent + cost;
const percentage = newMonthlyTotal / monthlyMax;
if (percentage >= 0.8 && monthlySpent / monthlyMax < 0.8) {
logger.warn(`Budget warning: 80% threshold reached - $${newMonthlyTotal.toFixed(2)}/$${monthlyMax.toFixed(2)}`);
await config.onBudgetWarning(newMonthlyTotal, monthlyMax);
}
}
}
}
/**
* Update spent amounts in storage
*/
async function updateSpentAmounts(cost, storage) {
await storage.addSpent("monthly", cost);
await storage.addSpent("daily", cost);
await storage.addSpent("hourly", cost);
}
/**
* Calculate remaining budget
*/
async function calculateRemainingBudget(config, storage) {
const { monthlyBudget, budget } = config;
const monthlyMax = budget?.monthly ?? monthlyBudget;
if (monthlyMax !== undefined) {
const monthlySpent = await storage.getSpent("monthly");
return monthlyMax - monthlySpent;
}
return undefined;
}
/**
* Execute an operation with cost tracking
*/
async function costTracking(config) {
const { execute, costPerToken, tags, costThresholdWarning, onCostCalculated, onExpensiveOperation, logger = common_1.defaultLogger, storage = getDefaultStorage(), } = config;
const timestamp = Date.now();
try {
// Execute the operation
logger.debug("Executing operation with cost tracking", { tags });
const result = await execute();
// Calculate cost
const tokens = result.tokens ?? 0;
const cost = tokens * costPerToken;
logger.info(`Operation completed: ${tokens} tokens, $${cost.toFixed(4)}`, { tags });
// Check for expensive operation
if (costThresholdWarning !== undefined && cost > costThresholdWarning) {
logger.warn(`Expensive operation detected: $${cost.toFixed(4)} (threshold: $${costThresholdWarning})`, { tags });
if (onExpensiveOperation) {
await onExpensiveOperation(cost, tags);
}
}
// Check budget limits (will throw if exceeded)
await checkBudgetLimits(cost, config, storage);
// Trigger alerts
await triggerAlerts(cost, config, storage);
// Update spent amounts
await updateSpentAmounts(cost, storage);
// Call onCostCalculated callback
if (onCostCalculated) {
await onCostCalculated(cost, tags);
}
// Calculate remaining budget
const remainingBudget = await calculateRemainingBudget(config, storage);
return {
value: result.value,
cost,
tokens,
remainingBudget,
tags,
timestamp,
};
}
catch (error) {
logger.error("Operation failed with cost tracking", {
error: error instanceof Error ? error.message : String(error),
tags,
});
throw error;
}
}
/**
* Create a cost tracking wrapper function
*/
function createCostTracker(baseConfig) {
return (execute) => {
return costTracking({
...baseConfig,
execute,
});
};
}
//# sourceMappingURL=cost-tracking.js.map