@oh-my-pi/omp-stats 18.0.0 → 18.0.3
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/CHANGELOG.md +7 -0
- package/dist/types/aggregator.d.ts +2 -1
- package/package.json +4 -4
- package/src/aggregator.ts +8 -0
- package/src/db.ts +19 -6
- package/src/server.ts +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [18.0.1] - 2026-08-23
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed the Projects dashboard folder endpoint running unrelated dashboard aggregations when loading folder statistics.
|
|
10
|
+
- Fixed stats sync crashing with a NOT NULL constraint error when legacy session files carry a partially-populated usage cost.
|
|
11
|
+
|
|
5
12
|
## [17.4.0] - 2026-08-20
|
|
6
13
|
|
|
7
14
|
### Changed
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BehaviorDashboardStats, DashboardStats, MessageStats, ProviderDashboardStats, RequestDetails, ToolDashboardStats } from "./types.js";
|
|
1
|
+
import type { BehaviorDashboardStats, DashboardStats, FolderStats, MessageStats, ProviderDashboardStats, RequestDetails, ToolDashboardStats } from "./types.js";
|
|
2
2
|
/**
|
|
3
3
|
* Serialize stats ingestion and archive reconciliation across processes.
|
|
4
4
|
* The lock covers file discovery, parsing, and the final SQLite write so a
|
|
@@ -79,6 +79,7 @@ export declare function getDashboardStats(range?: string | null): Promise<Dashbo
|
|
|
79
79
|
export declare function getOverviewStats(range?: string | null): Promise<Pick<DashboardStats, "overall" | "byAgentType" | "timeSeries">>;
|
|
80
80
|
export declare function getModelDashboardStats(range?: string | null): Promise<Pick<DashboardStats, "byModel" | "modelSeries" | "modelPerformanceSeries">>;
|
|
81
81
|
export declare function getCostDashboardStats(range?: string | null): Promise<Pick<DashboardStats, "costSeries">>;
|
|
82
|
+
export declare function getFolderStats(range?: string | null): Promise<FolderStats[]>;
|
|
82
83
|
export declare function getRecentRequests(limit?: number): Promise<MessageStats[]>;
|
|
83
84
|
export declare function getRecentErrors(range?: string | null, limit?: number): Promise<MessageStats[]>;
|
|
84
85
|
export declare function getRequestDetails(id: number): Promise<RequestDetails | null>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/omp-stats",
|
|
4
|
-
"version": "18.0.
|
|
4
|
+
"version": "18.0.3",
|
|
5
5
|
"description": "Local observability dashboard for pi AI usage statistics",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Stencil Labs, Inc.",
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
"fmt": "biome format --write ."
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@oh-my-pi/pi-ai": "18.0.
|
|
43
|
-
"@oh-my-pi/pi-catalog": "18.0.
|
|
44
|
-
"@oh-my-pi/pi-utils": "18.0.
|
|
42
|
+
"@oh-my-pi/pi-ai": "18.0.3",
|
|
43
|
+
"@oh-my-pi/pi-catalog": "18.0.3",
|
|
44
|
+
"@oh-my-pi/pi-utils": "18.0.3",
|
|
45
45
|
"@tailwindcss/node": "^4.3.2",
|
|
46
46
|
"chart.js": "^4.5.1",
|
|
47
47
|
"lucide-react": "^1.24.0",
|
package/src/aggregator.ts
CHANGED
|
@@ -43,6 +43,7 @@ import type { SyncWorkerRequest, SyncWorkerResponse } from "./sync-worker";
|
|
|
43
43
|
import type {
|
|
44
44
|
BehaviorDashboardStats,
|
|
45
45
|
DashboardStats,
|
|
46
|
+
FolderStats,
|
|
46
47
|
MessageStats,
|
|
47
48
|
ProviderDashboardStats,
|
|
48
49
|
RequestDetails,
|
|
@@ -478,6 +479,13 @@ export async function getCostDashboardStats(range?: string | null): Promise<Pick
|
|
|
478
479
|
costSeries: getCostTimeSeries(costSeriesDays, cutoff),
|
|
479
480
|
};
|
|
480
481
|
}
|
|
482
|
+
|
|
483
|
+
export async function getFolderStats(range?: string | null): Promise<FolderStats[]> {
|
|
484
|
+
await initDb();
|
|
485
|
+
const { cutoff } = getTimeRangeConfig(range);
|
|
486
|
+
return getStatsByFolder(cutoff ?? undefined);
|
|
487
|
+
}
|
|
488
|
+
|
|
481
489
|
export async function getRecentRequests(limit?: number): Promise<MessageStats[]> {
|
|
482
490
|
await initDb();
|
|
483
491
|
return dbGetRecentRequests(limit);
|
package/src/db.ts
CHANGED
|
@@ -353,13 +353,26 @@ function calculateCatalogCost(provider: string, modelId: string, tokens: CostTok
|
|
|
353
353
|
};
|
|
354
354
|
}
|
|
355
355
|
|
|
356
|
+
function normalizeUsageCost(cost: UsageCost): UsageCost {
|
|
357
|
+
const input = cost.input ?? 0;
|
|
358
|
+
const output = cost.output ?? 0;
|
|
359
|
+
const cacheRead = cost.cacheRead ?? 0;
|
|
360
|
+
const cacheWrite = cost.cacheWrite ?? 0;
|
|
361
|
+
const total = cost.total ?? input + output + cacheRead + cacheWrite;
|
|
362
|
+
return { input, output, cacheRead, cacheWrite, total };
|
|
363
|
+
}
|
|
364
|
+
|
|
356
365
|
function resolveStoredCost(stats: MessageStats): UsageCost {
|
|
357
|
-
// `usage.cost` was optional in older session files
|
|
358
|
-
//
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
366
|
+
// `usage.cost` was optional in older session files, and legacy payloads
|
|
367
|
+
// can carry a partially-populated cost object (e.g. only `total`). The
|
|
368
|
+
// messages table declares every cost_* column as REAL NOT NULL, so any
|
|
369
|
+
// missing field must be normalised here before binding into SQLite.
|
|
370
|
+
const raw: UsageCost | undefined = stats.usage.cost;
|
|
371
|
+
const storedCost = raw ? normalizeUsageCost(raw) : undefined;
|
|
372
|
+
|
|
373
|
+
// A missing total is derived from the stored components. An explicit zero
|
|
374
|
+
// remains the sentinel for catalog-based correction.
|
|
375
|
+
if (storedCost && (raw?.total ?? storedCost.total) !== 0) return storedCost;
|
|
363
376
|
|
|
364
377
|
return calculateCatalogCost(stats.provider, stats.model, stats.usage) ?? storedCost ?? ZERO_USAGE_COST;
|
|
365
378
|
}
|
package/src/server.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
getBehaviorDashboardStats,
|
|
9
9
|
getCostDashboardStats,
|
|
10
10
|
getDashboardStats,
|
|
11
|
+
getFolderStats,
|
|
11
12
|
getModelDashboardStats,
|
|
12
13
|
getOverviewStats,
|
|
13
14
|
getProviderDashboardStats,
|
|
@@ -253,8 +254,8 @@ export async function handleApi(req: Request): Promise<Response> {
|
|
|
253
254
|
}
|
|
254
255
|
|
|
255
256
|
if (path === "/api/stats/folders") {
|
|
256
|
-
const stats = await
|
|
257
|
-
return Response.json(stats
|
|
257
|
+
const stats = await getFolderStats(range);
|
|
258
|
+
return Response.json(stats);
|
|
258
259
|
}
|
|
259
260
|
|
|
260
261
|
if (path === "/api/stats/timeseries") {
|