feat(usage): implement cost tracking backend and pricing configuration

- Add pricing constants with default rates for all providers
- Update localDb to support pricing configuration schema
- Add cost calculation logic to usageDb
- Add pricing management API endpoints
- Fix provider alias mapping for accurate cost lookups

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Catalin Stanciu
2026-01-09 17:48:42 +07:00
committed by decolua
parent 3ad2f8dc58
commit a36afaa85e
4 changed files with 960 additions and 3 deletions
+157 -1
View File
@@ -41,7 +41,8 @@ const defaultData = {
settings: {
cloudEnabled: false,
stickyRoundRobinLimit: 3
}
},
pricing: {} // NEW: pricing configuration
};
// Singleton instance
@@ -528,3 +529,158 @@ export async function isCloudEnabled() {
return settings.cloudEnabled === true;
}
// ============ Pricing ============
/**
* Get pricing configuration
* Returns merged user pricing with defaults
*/
export async function getPricing() {
const db = await getDb();
const userPricing = db.data.pricing || {};
// Import default pricing
const { getDefaultPricing } = await import("@/shared/constants/pricing.js");
const defaultPricing = getDefaultPricing();
// Merge user pricing with defaults
// User pricing overrides defaults for specific provider/model combinations
const mergedPricing = {};
for (const [provider, models] of Object.entries(defaultPricing)) {
mergedPricing[provider] = { ...models };
// Apply user overrides if they exist
if (userPricing[provider]) {
for (const [model, pricing] of Object.entries(userPricing[provider])) {
if (mergedPricing[provider][model]) {
mergedPricing[provider][model] = { ...mergedPricing[provider][model], ...pricing };
} else {
mergedPricing[provider][model] = pricing;
}
}
}
}
// Add any user-only pricing entries
for (const [provider, models] of Object.entries(userPricing)) {
if (!mergedPricing[provider]) {
mergedPricing[provider] = { ...models };
} else {
for (const [model, pricing] of Object.entries(models)) {
if (!mergedPricing[provider][model]) {
mergedPricing[provider][model] = pricing;
}
}
}
}
return mergedPricing;
}
/**
* Get pricing for a specific provider and model
*/
export async function getPricingForModel(provider, model) {
const pricing = await getPricing();
// Try direct lookup
if (pricing[provider] && pricing[provider][model]) {
return pricing[provider][model];
}
// Try mapping provider ID to alias
// We need to duplicate the mapping here or import it
// Since we can't easily import from open-sse, we'll implement the mapping locally
const PROVIDER_ID_TO_ALIAS = {
claude: "cc",
codex: "cx",
"gemini-cli": "gc",
qwen: "qw",
iflow: "if",
antigravity: "ag",
github: "gh",
openai: "openai",
anthropic: "anthropic",
gemini: "gemini",
openrouter: "openrouter",
glm: "glm",
kimi: "kimi",
minimax: "minimax",
};
const alias = PROVIDER_ID_TO_ALIAS[provider];
if (alias && pricing[alias]) {
return pricing[alias][model] || null;
}
return null;
}
/**
* Update pricing configuration
* @param {object} pricingData - New pricing data to merge
*/
export async function updatePricing(pricingData) {
const db = await getDb();
// Ensure pricing object exists
if (!db.data.pricing) {
db.data.pricing = {};
}
// Merge new pricing data
for (const [provider, models] of Object.entries(pricingData)) {
if (!db.data.pricing[provider]) {
db.data.pricing[provider] = {};
}
for (const [model, pricing] of Object.entries(models)) {
db.data.pricing[provider][model] = pricing;
}
}
await db.write();
return db.data.pricing;
}
/**
* Reset pricing to defaults for specific provider/model
* @param {string} provider - Provider ID
* @param {string} model - Model ID (optional, if not provided resets entire provider)
*/
export async function resetPricing(provider, model) {
const db = await getDb();
if (!db.data.pricing) {
db.data.pricing = {};
}
if (model) {
// Reset specific model
if (db.data.pricing[provider]) {
delete db.data.pricing[provider][model];
// Clean up empty provider objects
if (Object.keys(db.data.pricing[provider]).length === 0) {
delete db.data.pricing[provider];
}
}
} else {
// Reset entire provider
delete db.data.pricing[provider];
}
await db.write();
return db.data.pricing;
}
/**
* Reset all pricing to defaults
*/
export async function resetAllPricing() {
const db = await getDb();
db.data.pricing = {};
await db.write();
return db.data.pricing;
}