openvibers/openvibe.ai Commits

Sums of bigint columns (tokens, requests, cache hits) are cast to bigint: on PostgreSQL they are numeric, which openvibe-sdk/db returns as exact text, and the usage summary, status and run totals would have carried strings. cost_usd is double precision and stays a number. npm test 23/23; npm run test:pg 23/23.

OP OpenVibers committed 28 Sept 2026 · af9166a1d3 · 4 files, +4 −4


Changes

ddiff --git a/server/cache.js b/server/cache.js
iindex ad2913d..7a02048 100644
---- a/server/cache.js
++++ b/server/cache.js
@@@ -45,7 +45,7 @@ function createCache(db, { clock = { now: () => Date.now() } } = {}) {
}
async function stats() {
-- return await db.prepare('SELECT workflow_key, privacy, COUNT(*) AS entries, SUM(hits) AS hits FROM cache_entries WHERE expires_at > ? GROUP BY workflow_key, privacy ORDER BY entries DESC').all(clock.now());
++ return await db.prepare('SELECT workflow_key, privacy, COUNT(*) AS entries, COALESCE(SUM(hits), 0)::bigint AS hits FROM cache_entries WHERE expires_at > ? GROUP BY workflow_key, privacy ORDER BY entries DESC').all(clock.now());
}
async function purge({ workflow } = {}) {
ddiff --git a/server/ops.js b/server/ops.js
iindex 9acb59b..1c88dff 100644
---- a/server/ops.js
++++ b/server/ops.js
@@@ -19,7 +19,7 @@ function createOps({ db, registry, pool, quotas, cache, runs }) {
async function status() {
const counts = Object.fromEntries((await db.prepare('SELECT status, COUNT(*) n FROM runs GROUP BY status').all()).map(x => [x.status, x.n]));
const today = new Date().toISOString().slice(0, 10);
-- const spend = await db.prepare('SELECT COALESCE(SUM(cost_usd),0) cost, COALESCE(SUM(requests),0) requests FROM usage_daily WHERE day = ?').get(today);
++ const spend = await db.prepare('SELECT COALESCE(SUM(cost_usd),0) cost, COALESCE(SUM(requests),0)::bigint requests FROM usage_daily WHERE day = ?').get(today);
return {
providers: (await Promise.all((await registry.listProviders()).map(async p => ({ key: p.key, kind: p.kind, status: p.status, credentials: (await providerView(p)).credentials, health: (await pool.health(p.key)).state })))),
runs: counts, today: { day: today, requests: spend.requests, cost_usd: spend.cost },
ddiff --git a/server/runs.js b/server/runs.js
iindex 229b521..b9d7983 100644
---- a/server/runs.js
++++ b/server/runs.js
@@@ -326,7 +326,7 @@ function createRuns({ db, registry, engine, cache, quotas, config, clock = { now
.run(cancelled ? 'cancelled' : 'failed', code, extra ? `${detail} ${extra}` : detail, attempts, iso(clock.now()), id);
if (f.changes) events.runChanged(await getRow(id));
});
-- const spent = await db.prepare("SELECT COALESCE(SUM(tokens_in),0) ti, COALESCE(SUM(tokens_out),0) tout, COALESCE(SUM(cost_usd),0) c FROM requests WHERE run_id = ? AND status = 'ok'").get(id);
++ const spent = await db.prepare("SELECT COALESCE(SUM(tokens_in),0)::bigint ti, COALESCE(SUM(tokens_out),0)::bigint tout, COALESCE(SUM(cost_usd),0) c FROM requests WHERE run_id = ? AND status = 'ok'").get(id);
if (reserved && spent && (spent.ti || spent.tout)) await quotas.account(ctx, reserved, { provider: 'mixed', model: '', tokensIn: spent.ti, tokensOut: spent.tout, cost: spent.c });
} finally {
inflight.delete(id);
ddiff --git a/server/user-modules.js b/server/user-modules.js
iindex c369618..217ffbf 100644
---- a/server/user-modules.js
++++ b/server/user-modules.js
@@@ -73,7 +73,7 @@ function createUserModules({ db, config, env = process.env, fetchImpl = globalTh
/** ai.usage_summary for one person over the 30 days before now, or null when there were no runs. */
async function summarize(subject, now = clock.now()) {
const since = new Date(now - 30 * DAY_MS).toISOString();
-- const rows = await db.prepare(`SELECT COALESCE(source_service, requester_id) AS svc, COUNT(*) AS n, SUM(tokens_in + tokens_out) AS tokens, MAX(created_at) AS last
++ const rows = await db.prepare(`SELECT COALESCE(source_service, requester_id) AS svc, COUNT(*) AS n, COALESCE(SUM(tokens_in + tokens_out), 0)::bigint AS tokens, MAX(created_at) AS last
FROM runs WHERE (on_behalf_of::jsonb ->> 'type') = 'user' AND (on_behalf_of::jsonb ->> 'id') = ? AND created_at >= ?
GROUP BY svc`).all(subject, since);
const lastEver = (await db.prepare("SELECT MAX(created_at) AS last FROM runs WHERE (on_behalf_of::jsonb ->> 'type') = 'user' AND (on_behalf_of::jsonb ->> 'id') = ?").get(subject)).last;