cachegate 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/metrics.js +48 -0
- package/package.json +1 -1
package/metrics.js
CHANGED
|
@@ -206,6 +206,15 @@ async function pruneOlderThanPostgres(days) {
|
|
|
206
206
|
return result.rows.map((r) => r.id);
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
+
async function pruneScopedOlderThanPostgres(scope, days) {
|
|
210
|
+
await ensureSchema();
|
|
211
|
+
const result = await getPool().query(
|
|
212
|
+
`DELETE FROM router_metrics WHERE scope = $1 AND ts < now() - ($2::double precision * interval '1 day') RETURNING id`,
|
|
213
|
+
[String(scope), days]
|
|
214
|
+
);
|
|
215
|
+
return result.rows.map((r) => r.id);
|
|
216
|
+
}
|
|
217
|
+
|
|
209
218
|
// Turns a raw provider error message into one of a handful of stable,
|
|
210
219
|
// human-meaningful buckets - the difference between a dashboard that says
|
|
211
220
|
// "openai: 100% error rate" (true, but not actionable without opening a
|
|
@@ -581,6 +590,44 @@ async function pruneOlderThan(days) {
|
|
|
581
590
|
return deleted;
|
|
582
591
|
}
|
|
583
592
|
|
|
593
|
+
/**
|
|
594
|
+
* Scope-isolated cleanup (seams work): deletes only `scope`'s records
|
|
595
|
+
* older than `days`, leaving every other tenant's history untouched.
|
|
596
|
+
* This is the primitive a per-tenant retention policy needs - the DAYS
|
|
597
|
+
* live in the caller (a billing/tier decision, e.g. Free 7d / Starter
|
|
598
|
+
* 30d / Growth 90d), while the scope-filtered DELETE lives here, under
|
|
599
|
+
* the same `scope` contract as readRecent/providerStats/rangeSummary (a
|
|
600
|
+
* primitive; `scope = $1`). Postgres-only: the JSONL file backend keeps
|
|
601
|
+
* every scope in shared per-day append-only files, so excising one scope
|
|
602
|
+
* would mean rewriting those files mid-append - scoped prune is a
|
|
603
|
+
* multi-tenant (Postgres) concern, and on the file backend it is a
|
|
604
|
+
* documented no-op (warns, returns []). The global pruneOlderThan(days)
|
|
605
|
+
* above stays the unscoped, delete-everything form, for legacy/null-
|
|
606
|
+
* scope history and standalone single-tenant runs.
|
|
607
|
+
*/
|
|
608
|
+
async function pruneScopedOlderThan(scope, days) {
|
|
609
|
+
// Fail closed on a null/undefined scope: this function is the SCOPED
|
|
610
|
+
// form, and passing null (which readRecent/providerStats/rangeSummary
|
|
611
|
+
// treat as "global") would otherwise either silently delete nothing
|
|
612
|
+
// (Postgres: scope = 'null' matches no real tenant) or warn-and-no-op
|
|
613
|
+
// (file backend) - both silent, both wrong for a caller who expected a
|
|
614
|
+
// global prune. Delegating to pruneOlderThan(days) instead would be
|
|
615
|
+
// the OPPOSITE hazard (silently deleting every tenant's history), so
|
|
616
|
+
// the safe answer is a loud error pointing at the right function.
|
|
617
|
+
if (scope == null) {
|
|
618
|
+
throw new Error(
|
|
619
|
+
'pruneScopedOlderThan(scope, days) requires a non-null scope. ' +
|
|
620
|
+
'Use pruneOlderThan(days) to prune globally.'
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
if (usingPostgres()) return pruneScopedOlderThanPostgres(scope, days);
|
|
624
|
+
console.warn(
|
|
625
|
+
'⚠️ pruneScopedOlderThan() is Postgres-only: the JSONL file backend ' +
|
|
626
|
+
'cannot excise one scope from shared per-day files. Nothing deleted.'
|
|
627
|
+
);
|
|
628
|
+
return [];
|
|
629
|
+
}
|
|
630
|
+
|
|
584
631
|
// Test-only: closes the cached pool (if one was ever opened) so a test
|
|
585
632
|
// run doesn't hang on an open connection, and so the NEXT test that
|
|
586
633
|
// re-requires this module with a different DATABASE_URL gets a fresh
|
|
@@ -599,6 +646,7 @@ module.exports = {
|
|
|
599
646
|
providerStats,
|
|
600
647
|
rangeSummary,
|
|
601
648
|
pruneOlderThan,
|
|
649
|
+
pruneScopedOlderThan,
|
|
602
650
|
currentLogPath,
|
|
603
651
|
listLogFiles,
|
|
604
652
|
classifyErrorType,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cachegate",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Self-hostable, OpenAI-compatible LLM proxy: routes to the cheapest healthy provider, caches responses exactly and semantically, tracks cost and latency per call.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "server.js",
|