File size: 3,568 Bytes
88c4c60 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | import { getAdapter } from "../driver.js";
import { parseJson, stringifyJson } from "../helpers/jsonCol.js";
import { makeKv } from "../helpers/kvStore.js";
const pricingKv = makeKv("pricing");
const CACHE_TTL_MS = 5000;
let cache = { value: null, expiresAt: 0 };
function invalidate() {
cache = { value: null, expiresAt: 0 };
}
async function getUserPricing() {
return await pricingKv.getAll();
}
export async function getPricing() {
const now = Date.now();
if (cache.value && cache.expiresAt > now) return cache.value;
const userPricing = await getUserPricing();
const { PROVIDER_PRICING } = await import("@/shared/constants/pricing.js");
const merged = {};
for (const [provider, models] of Object.entries(PROVIDER_PRICING)) {
merged[provider] = { ...models };
if (userPricing[provider]) {
for (const [model, pricing] of Object.entries(userPricing[provider])) {
merged[provider][model] = merged[provider][model]
? { ...merged[provider][model], ...pricing }
: pricing;
}
}
}
for (const [provider, models] of Object.entries(userPricing)) {
if (!merged[provider]) {
merged[provider] = { ...models };
} else {
for (const [model, pricing] of Object.entries(models)) {
if (!merged[provider][model]) merged[provider][model] = pricing;
}
}
}
cache = { value: merged, expiresAt: now + CACHE_TTL_MS };
return merged;
}
export async function getPricingForModel(provider, model) {
if (!model) return null;
const userPricing = await getUserPricing();
if (provider && userPricing[provider]?.[model]) return userPricing[provider][model];
const { getPricingForModel: resolveConst } = await import("@/shared/constants/pricing.js");
return resolveConst(provider, model);
}
// Atomic merge inside transaction (per-provider read-modify-write)
export async function updatePricing(pricingData) {
const db = await getAdapter();
db.transaction(() => {
for (const [provider, models] of Object.entries(pricingData)) {
const row = db.get(`SELECT value FROM kv WHERE scope = 'pricing' AND key = ?`, [provider]);
const current = row ? (parseJson(row.value, {}) || {}) : {};
const merged = { ...current };
for (const [model, pricing] of Object.entries(models)) {
merged[model] = pricing;
}
db.run(
`INSERT INTO kv(scope, key, value) VALUES('pricing', ?, ?) ON CONFLICT(scope, key) DO UPDATE SET value = excluded.value`,
[provider, stringifyJson(merged)]
);
}
});
invalidate();
return await getUserPricing();
}
export async function resetPricing(provider, model) {
if (!provider) return await getUserPricing();
const db = await getAdapter();
db.transaction(() => {
if (!model) {
db.run(`DELETE FROM kv WHERE scope = 'pricing' AND key = ?`, [provider]);
return;
}
const row = db.get(`SELECT value FROM kv WHERE scope = 'pricing' AND key = ?`, [provider]);
const current = row ? (parseJson(row.value, {}) || {}) : {};
delete current[model];
if (Object.keys(current).length === 0) {
db.run(`DELETE FROM kv WHERE scope = 'pricing' AND key = ?`, [provider]);
} else {
db.run(
`INSERT INTO kv(scope, key, value) VALUES('pricing', ?, ?) ON CONFLICT(scope, key) DO UPDATE SET value = excluded.value`,
[provider, stringifyJson(current)]
);
}
});
invalidate();
return await getUserPricing();
}
export async function resetAllPricing() {
await pricingKv.clear();
invalidate();
return {};
}
|