@oh-my-pi/omp-stats 17.1.1 → 17.1.2
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 +6 -0
- package/dist/client/index.css +1 -1
- package/dist/client/index.js +91 -91
- package/dist/client/styles.css +6 -0
- package/dist/types/aggregator.d.ts +7 -1
- package/dist/types/client/api.d.ts +2 -1
- package/dist/types/client/app/routes.d.ts +1 -1
- package/dist/types/client/routes/ProvidersRoute.d.ts +7 -0
- package/dist/types/client/routes/index.d.ts +1 -0
- package/dist/types/db.d.ts +15 -1
- package/dist/types/shared-types.d.ts +102 -0
- package/dist/types/usage-windows.d.ts +45 -0
- package/package.json +4 -4
- package/src/aggregator.ts +33 -1
- package/src/client/App.tsx +3 -0
- package/src/client/api.ts +10 -0
- package/src/client/app/routes.ts +18 -1
- package/src/client/data/useHashRoute.ts +1 -0
- package/src/client/routes/ProvidersRoute.tsx +492 -0
- package/src/client/routes/index.ts +1 -0
- package/src/client/styles.css +1 -0
- package/src/db.ts +141 -0
- package/src/server.ts +6 -0
- package/src/shared-types.ts +109 -0
- package/src/usage-windows.ts +303 -0
package/dist/client/styles.css
CHANGED
|
@@ -221,6 +221,9 @@
|
|
|
221
221
|
.h-\[200px\] {
|
|
222
222
|
height: 200px;
|
|
223
223
|
}
|
|
224
|
+
.h-\[260px\] {
|
|
225
|
+
height: 260px;
|
|
226
|
+
}
|
|
224
227
|
.h-\[280px\] {
|
|
225
228
|
height: 280px;
|
|
226
229
|
}
|
|
@@ -1463,6 +1466,9 @@
|
|
|
1463
1466
|
.stats-text-muted {
|
|
1464
1467
|
color: var(--dim);
|
|
1465
1468
|
}
|
|
1469
|
+
.stats-text-warning {
|
|
1470
|
+
color: var(--warning);
|
|
1471
|
+
}
|
|
1466
1472
|
.stats-text-xs {
|
|
1467
1473
|
font-size: 12px;
|
|
1468
1474
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BehaviorDashboardStats, DashboardStats, MessageStats, RequestDetails, ToolDashboardStats } from "./types.js";
|
|
1
|
+
import type { BehaviorDashboardStats, DashboardStats, MessageStats, ProviderDashboardStats, RequestDetails, ToolDashboardStats } from "./types.js";
|
|
2
2
|
/**
|
|
3
3
|
* Progress event emitted after each session file is fully processed.
|
|
4
4
|
* `current` is the number of files completed (skipped + parsed),
|
|
@@ -84,4 +84,10 @@ export declare function getBehaviorDashboardStats(range?: string | null): Promis
|
|
|
84
84
|
* breakdown, and the call time series (bucketed like the model series).
|
|
85
85
|
*/
|
|
86
86
|
export declare function getToolDashboardStats(range?: string | null): Promise<ToolDashboardStats>;
|
|
87
|
+
/**
|
|
88
|
+
* Get the providers dashboard payload: per-provider totals, peak-burn-hours
|
|
89
|
+
* histogram, provider token time series, and subscription-window analytics
|
|
90
|
+
* (utilization series + insights) derived from recorded usage-limit snapshots.
|
|
91
|
+
*/
|
|
92
|
+
export declare function getProviderDashboardStats(range?: string | null): Promise<ProviderDashboardStats>;
|
|
87
93
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BehaviorDashboardStats, CostDashboardStats, FolderStats, GainDashboardStats, MessageStats, ModelDashboardStats, OverviewStats, RequestDetails, TimeRange, ToolDashboardStats } from "./types.js";
|
|
1
|
+
import type { BehaviorDashboardStats, CostDashboardStats, FolderStats, GainDashboardStats, MessageStats, ModelDashboardStats, OverviewStats, ProviderDashboardStats, RequestDetails, TimeRange, ToolDashboardStats } from "./types.js";
|
|
2
2
|
export declare class ApiError extends Error {
|
|
3
3
|
status: number;
|
|
4
4
|
endpoint: string;
|
|
@@ -19,3 +19,4 @@ export declare function getBehaviorDashboardStats(range?: TimeRange, signal?: Ab
|
|
|
19
19
|
export declare function getFolderStats(range?: TimeRange, signal?: AbortSignal): Promise<FolderStats[]>;
|
|
20
20
|
export declare function getGainDashboardStats(range?: TimeRange, project?: string | null, signal?: AbortSignal): Promise<GainDashboardStats>;
|
|
21
21
|
export declare function getToolDashboardStats(range?: TimeRange, signal?: AbortSignal): Promise<ToolDashboardStats>;
|
|
22
|
+
export declare function getProviderDashboardStats(range?: TimeRange, signal?: AbortSignal): Promise<ProviderDashboardStats>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type React from "react";
|
|
2
|
-
export type DashboardSection = "overview" | "requests" | "errors" | "models" | "tools" | "costs" | "behavior" | "projects" | "gain";
|
|
2
|
+
export type DashboardSection = "overview" | "requests" | "errors" | "models" | "providers" | "tools" | "costs" | "behavior" | "projects" | "gain";
|
|
3
3
|
export interface DashboardRoute {
|
|
4
4
|
id: DashboardSection;
|
|
5
5
|
label: string;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { TimeRange } from "../types.js";
|
|
2
|
+
export interface ProvidersRouteProps {
|
|
3
|
+
active: boolean;
|
|
4
|
+
range: TimeRange;
|
|
5
|
+
refreshTrigger: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function ProvidersRoute({ active, range, refreshTrigger }: ProvidersRouteProps): import("react").JSX.Element;
|
package/dist/types/db.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Database } from "bun:sqlite";
|
|
2
|
-
import type { AgentTypeStats, AggregatedStats, BehaviorModelStats, BehaviorOverallStats, BehaviorTimeSeriesPoint, CostTimeSeriesPoint, FolderStats, MessageStats, ModelPerformancePoint, ModelStats, ModelTimeSeriesPoint, TimeSeriesPoint, ToolCallStats, ToolModelStats, ToolResultLink, ToolTimeSeriesPoint, ToolUsageStats, UserMessageLink, UserMessageStats } from "./types.js";
|
|
2
|
+
import type { AgentTypeStats, AggregatedStats, BehaviorModelStats, BehaviorOverallStats, BehaviorTimeSeriesPoint, CostTimeSeriesPoint, FolderStats, MessageStats, ModelPerformancePoint, ModelStats, ModelTimeSeriesPoint, ProviderAggregate, ProviderHourlyPoint, ProviderTimeSeriesPoint, TimeSeriesPoint, ToolCallStats, ToolModelStats, ToolResultLink, ToolTimeSeriesPoint, ToolUsageStats, UserMessageLink, UserMessageStats } from "./types.js";
|
|
3
3
|
/**
|
|
4
4
|
* Initialize the database and create tables.
|
|
5
5
|
*/
|
|
@@ -60,6 +60,20 @@ export declare function getTimeSeries(hours?: number, cutoff?: number | null, bu
|
|
|
60
60
|
* Get daily model usage time series data for the last N days.
|
|
61
61
|
*/
|
|
62
62
|
export declare function getModelTimeSeries(days?: number, cutoff?: number | null, bucketMs?: number): ModelTimeSeriesPoint[];
|
|
63
|
+
/**
|
|
64
|
+
* Get request/token/cost totals grouped by provider.
|
|
65
|
+
*/
|
|
66
|
+
export declare function getStatsByProvider(cutoff?: number | null): ProviderAggregate[];
|
|
67
|
+
/**
|
|
68
|
+
* Get token burn grouped by provider and local hour of day (0-23).
|
|
69
|
+
* Hours use the server's timezone — the dashboard is a localhost tool, so
|
|
70
|
+
* server-local and viewer-local time coincide.
|
|
71
|
+
*/
|
|
72
|
+
export declare function getProviderHourlyBurn(cutoff?: number | null): ProviderHourlyPoint[];
|
|
73
|
+
/**
|
|
74
|
+
* Get token/cost time series grouped by provider (bucketed like the model series).
|
|
75
|
+
*/
|
|
76
|
+
export declare function getProviderTimeSeries(days?: number, cutoff?: number | null, bucketMs?: number): ProviderTimeSeriesPoint[];
|
|
63
77
|
/**
|
|
64
78
|
* Get daily model performance time series data for the last N days.
|
|
65
79
|
*/
|
|
@@ -299,3 +299,105 @@ export interface ToolDashboardStats {
|
|
|
299
299
|
byToolModel: ToolModelStats[];
|
|
300
300
|
series: ToolTimeSeriesPoint[];
|
|
301
301
|
}
|
|
302
|
+
/**
|
|
303
|
+
* Aggregated request/token/cost totals for one provider over the active range.
|
|
304
|
+
*/
|
|
305
|
+
export interface ProviderAggregate {
|
|
306
|
+
provider: string;
|
|
307
|
+
totalRequests: number;
|
|
308
|
+
failedRequests: number;
|
|
309
|
+
/** Distinct models used through this provider in the range. */
|
|
310
|
+
models: number;
|
|
311
|
+
totalInputTokens: number;
|
|
312
|
+
totalOutputTokens: number;
|
|
313
|
+
totalCacheReadTokens: number;
|
|
314
|
+
totalCacheWriteTokens: number;
|
|
315
|
+
/** Uncached input + cache reads + cache writes + output. */
|
|
316
|
+
totalTokens: number;
|
|
317
|
+
totalCost: number;
|
|
318
|
+
totalPremiumRequests: number;
|
|
319
|
+
avgTokensPerSecond: number | null;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Token burn attributed to one local hour-of-day (0-23) for one provider.
|
|
323
|
+
* Powers the "peak burn hours" histogram.
|
|
324
|
+
*/
|
|
325
|
+
export interface ProviderHourlyPoint {
|
|
326
|
+
provider: string;
|
|
327
|
+
/** Local hour of day, 0-23. */
|
|
328
|
+
hour: number;
|
|
329
|
+
totalTokens: number;
|
|
330
|
+
outputTokens: number;
|
|
331
|
+
requests: number;
|
|
332
|
+
}
|
|
333
|
+
/** Provider token/cost time-series point (bucketed like the model series). */
|
|
334
|
+
export interface ProviderTimeSeriesPoint {
|
|
335
|
+
timestamp: number;
|
|
336
|
+
provider: string;
|
|
337
|
+
totalTokens: number;
|
|
338
|
+
cost: number;
|
|
339
|
+
requests: number;
|
|
340
|
+
}
|
|
341
|
+
/** One recorded usage-limit snapshot for an (account, window) series. */
|
|
342
|
+
export interface UsageWindowPoint {
|
|
343
|
+
timestamp: number;
|
|
344
|
+
/** Used fraction 0..1 (>1 = overage) when the provider reported one. */
|
|
345
|
+
usedFraction: number | null;
|
|
346
|
+
exhausted: boolean;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Utilization history for one (account, limit window) pair of a provider,
|
|
350
|
+
* sourced from the auth store's recorded usage-limit snapshots.
|
|
351
|
+
*/
|
|
352
|
+
export interface UsageWindowSeries {
|
|
353
|
+
provider: string;
|
|
354
|
+
accountKey: string;
|
|
355
|
+
/** Email/account id when known, else the stable account key. */
|
|
356
|
+
accountLabel: string;
|
|
357
|
+
/** Groups the same limit window across accounts (window label or limit id). */
|
|
358
|
+
windowKey: string;
|
|
359
|
+
windowLabel: string;
|
|
360
|
+
points: UsageWindowPoint[];
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Derived subscription insight for one provider limit window across all
|
|
364
|
+
* accounts: how much of the window was consumed, what one window is worth in
|
|
365
|
+
* tokens, and how many accounts peak demand would have needed.
|
|
366
|
+
*/
|
|
367
|
+
export interface ProviderWindowInsight {
|
|
368
|
+
provider: string;
|
|
369
|
+
windowKey: string;
|
|
370
|
+
windowLabel: string;
|
|
371
|
+
/** Accounts with at least one snapshot for this window in range. */
|
|
372
|
+
accounts: number;
|
|
373
|
+
/** Window resets observed (drops in used fraction). */
|
|
374
|
+
cycles: number;
|
|
375
|
+
/**
|
|
376
|
+
* Subscription-window equivalents consumed in range: sum of positive
|
|
377
|
+
* used-fraction deltas across accounts (1.0 = one full window burned).
|
|
378
|
+
*/
|
|
379
|
+
fractionConsumed: number;
|
|
380
|
+
/**
|
|
381
|
+
* Estimated tokens one full window buys: provider tokens burned in range
|
|
382
|
+
* divided by {@link fractionConsumed}. Null when too little of the window
|
|
383
|
+
* was consumed to extrapolate.
|
|
384
|
+
*/
|
|
385
|
+
estTokensPerWindow: number | null;
|
|
386
|
+
/** Peak of sum-across-accounts used fraction at any sampled instant. */
|
|
387
|
+
peakConcurrentFraction: number;
|
|
388
|
+
/**
|
|
389
|
+
* Accounts needed to keep peak demand under 90% of fleet capacity:
|
|
390
|
+
* max(1, ceil(peakConcurrentFraction / 0.9)).
|
|
391
|
+
*/
|
|
392
|
+
idealAccounts: number;
|
|
393
|
+
/** Transitions into an exhausted state observed in range. */
|
|
394
|
+
exhaustedEvents: number;
|
|
395
|
+
}
|
|
396
|
+
/** Complete providers dashboard payload. */
|
|
397
|
+
export interface ProviderDashboardStats {
|
|
398
|
+
providers: ProviderAggregate[];
|
|
399
|
+
hourly: ProviderHourlyPoint[];
|
|
400
|
+
series: ProviderTimeSeriesPoint[];
|
|
401
|
+
usageSeries: UsageWindowSeries[];
|
|
402
|
+
windowInsights: ProviderWindowInsight[];
|
|
403
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ProviderWindowInsight, UsageWindowSeries } from "./shared-types.js";
|
|
2
|
+
/** Subset of a `usage_history` row consumed by the window analytics. */
|
|
3
|
+
export interface UsageSnapshotRow {
|
|
4
|
+
/** Epoch ms the report was fetched. */
|
|
5
|
+
recordedAt: number;
|
|
6
|
+
provider: string;
|
|
7
|
+
/** Stable credential identity key. */
|
|
8
|
+
accountKey: string;
|
|
9
|
+
email: string | null;
|
|
10
|
+
accountId: string | null;
|
|
11
|
+
limitId: string;
|
|
12
|
+
label: string;
|
|
13
|
+
windowLabel: string | null;
|
|
14
|
+
/** Used fraction (0..1, >1 = overage) when the provider reported one. */
|
|
15
|
+
usedFraction: number | null;
|
|
16
|
+
status: string | null;
|
|
17
|
+
}
|
|
18
|
+
/** Utilization series + derived insights for every provider window in range. */
|
|
19
|
+
export interface UsageWindowStats {
|
|
20
|
+
usageSeries: UsageWindowSeries[];
|
|
21
|
+
windowInsights: ProviderWindowInsight[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Read usage-limit snapshots recorded at or after `sinceMs`, oldest first.
|
|
25
|
+
* Opens the agent DB read-only; returns `[]` when the DB or table is absent.
|
|
26
|
+
*/
|
|
27
|
+
export declare function readUsageSnapshots(sinceMs: number, dbPath?: string): UsageSnapshotRow[];
|
|
28
|
+
/**
|
|
29
|
+
* Fetch usage snapshots from wherever they actually accumulate: the auth
|
|
30
|
+
* broker's durable history when a broker is configured (the broker performs
|
|
31
|
+
* every upstream usage fetch in that mode, so the local `usage_history` stays
|
|
32
|
+
* frozen), else the local agent DB. Broker errors fall back to the local read
|
|
33
|
+
* so the dashboard degrades to stale-but-present data instead of failing.
|
|
34
|
+
*/
|
|
35
|
+
export declare function fetchUsageSnapshots(sinceMs: number): Promise<UsageSnapshotRow[]>;
|
|
36
|
+
/**
|
|
37
|
+
* Derive utilization series and per-window insights from raw snapshots.
|
|
38
|
+
*
|
|
39
|
+
* `tokensByProvider` supplies each provider's token burn over the same time
|
|
40
|
+
* range (from the local message stats); it converts consumed window fraction
|
|
41
|
+
* into an estimated token capacity per window. Attribution note: tokens are
|
|
42
|
+
* per provider, not per account, so the estimate treats the account fleet as
|
|
43
|
+
* one pooled subscription — which is exactly how round-robin auth uses it.
|
|
44
|
+
*/
|
|
45
|
+
export declare function computeUsageWindowStats(rows: UsageSnapshotRow[], tokensByProvider: ReadonlyMap<string, number>): UsageWindowStats;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/omp-stats",
|
|
4
|
-
"version": "17.1.
|
|
4
|
+
"version": "17.1.2",
|
|
5
5
|
"description": "Local observability dashboard for pi AI usage statistics",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
"fmt": "biome format --write ."
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@oh-my-pi/pi-ai": "17.1.
|
|
43
|
-
"@oh-my-pi/pi-catalog": "17.1.
|
|
44
|
-
"@oh-my-pi/pi-utils": "17.1.
|
|
42
|
+
"@oh-my-pi/pi-ai": "17.1.2",
|
|
43
|
+
"@oh-my-pi/pi-catalog": "17.1.2",
|
|
44
|
+
"@oh-my-pi/pi-utils": "17.1.2",
|
|
45
45
|
"@tailwindcss/node": "^4.3.2",
|
|
46
46
|
"chart.js": "^4.5.1",
|
|
47
47
|
"date-fns": "^4.4.0",
|
package/src/aggregator.ts
CHANGED
|
@@ -13,9 +13,12 @@ import {
|
|
|
13
13
|
getModelPerformanceSeries,
|
|
14
14
|
getModelTimeSeries,
|
|
15
15
|
getOverallStats,
|
|
16
|
+
getProviderHourlyBurn,
|
|
17
|
+
getProviderTimeSeries,
|
|
16
18
|
getStatsByAgentType,
|
|
17
19
|
getStatsByFolder,
|
|
18
20
|
getStatsByModel,
|
|
21
|
+
getStatsByProvider,
|
|
19
22
|
getTimeSeries,
|
|
20
23
|
getToolStats,
|
|
21
24
|
getToolStatsByModel,
|
|
@@ -35,7 +38,15 @@ import type { SyncWorkerRequest, SyncWorkerResponse } from "./sync-worker";
|
|
|
35
38
|
// hidden argv mode, so the compiled binary and npm bundle only need one
|
|
36
39
|
// JavaScript entry. Standalone source `omp-stats` keeps using this package's
|
|
37
40
|
// own sync-worker source file.
|
|
38
|
-
import type {
|
|
41
|
+
import type {
|
|
42
|
+
BehaviorDashboardStats,
|
|
43
|
+
DashboardStats,
|
|
44
|
+
MessageStats,
|
|
45
|
+
ProviderDashboardStats,
|
|
46
|
+
RequestDetails,
|
|
47
|
+
ToolDashboardStats,
|
|
48
|
+
} from "./types";
|
|
49
|
+
import { computeUsageWindowStats, fetchUsageSnapshots } from "./usage-windows";
|
|
39
50
|
|
|
40
51
|
/**
|
|
41
52
|
* Apply a freshly parsed result to the database. Runs entirely on the
|
|
@@ -504,3 +515,24 @@ export async function getToolDashboardStats(range?: string | null): Promise<Tool
|
|
|
504
515
|
series: getToolTimeSeries(modelSeriesDays, cutoff, modelSeriesBucketMs),
|
|
505
516
|
};
|
|
506
517
|
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Get the providers dashboard payload: per-provider totals, peak-burn-hours
|
|
521
|
+
* histogram, provider token time series, and subscription-window analytics
|
|
522
|
+
* (utilization series + insights) derived from recorded usage-limit snapshots.
|
|
523
|
+
*/
|
|
524
|
+
export async function getProviderDashboardStats(range?: string | null): Promise<ProviderDashboardStats> {
|
|
525
|
+
await initDb();
|
|
526
|
+
const { modelSeriesDays, modelSeriesBucketMs, cutoff } = getTimeRangeConfig(range);
|
|
527
|
+
const providers = getStatsByProvider(cutoff ?? undefined);
|
|
528
|
+
const tokensByProvider = new Map(providers.map(p => [p.provider, p.totalTokens]));
|
|
529
|
+
const snapshots = await fetchUsageSnapshots(cutoff ?? 0);
|
|
530
|
+
const { usageSeries, windowInsights } = computeUsageWindowStats(snapshots, tokensByProvider);
|
|
531
|
+
return {
|
|
532
|
+
providers,
|
|
533
|
+
hourly: getProviderHourlyBurn(cutoff ?? undefined),
|
|
534
|
+
series: getProviderTimeSeries(modelSeriesDays, cutoff, modelSeriesBucketMs),
|
|
535
|
+
usageSeries,
|
|
536
|
+
windowInsights,
|
|
537
|
+
};
|
|
538
|
+
}
|
package/src/client/App.tsx
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
ModelsRoute,
|
|
11
11
|
OverviewRoute,
|
|
12
12
|
ProjectsRoute,
|
|
13
|
+
ProvidersRoute,
|
|
13
14
|
RequestsRoute,
|
|
14
15
|
ToolsRoute,
|
|
15
16
|
} from "./routes";
|
|
@@ -73,6 +74,8 @@ export default function App() {
|
|
|
73
74
|
);
|
|
74
75
|
case "models":
|
|
75
76
|
return <ModelsRoute active={isActive} range={range} refreshTrigger={refreshTrigger} />;
|
|
77
|
+
case "providers":
|
|
78
|
+
return <ProvidersRoute active={isActive} range={range} refreshTrigger={refreshTrigger} />;
|
|
76
79
|
case "tools":
|
|
77
80
|
return <ToolsRoute active={isActive} range={range} refreshTrigger={refreshTrigger} />;
|
|
78
81
|
case "costs":
|
package/src/client/api.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
MessageStats,
|
|
7
7
|
ModelDashboardStats,
|
|
8
8
|
OverviewStats,
|
|
9
|
+
ProviderDashboardStats,
|
|
9
10
|
RequestDetails,
|
|
10
11
|
TimeRange,
|
|
11
12
|
ToolDashboardStats,
|
|
@@ -106,3 +107,12 @@ export async function getToolDashboardStats(
|
|
|
106
107
|
): Promise<ToolDashboardStats> {
|
|
107
108
|
return fetchJson<ToolDashboardStats>(`${API_BASE}/stats/tools?range=${encodeURIComponent(range)}`, { signal });
|
|
108
109
|
}
|
|
110
|
+
|
|
111
|
+
export async function getProviderDashboardStats(
|
|
112
|
+
range: TimeRange = "24h",
|
|
113
|
+
signal?: AbortSignal,
|
|
114
|
+
): Promise<ProviderDashboardStats> {
|
|
115
|
+
return fetchJson<ProviderDashboardStats>(`${API_BASE}/stats/providers?range=${encodeURIComponent(range)}`, {
|
|
116
|
+
signal,
|
|
117
|
+
});
|
|
118
|
+
}
|
package/src/client/app/routes.ts
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Activity,
|
|
3
|
+
AlertCircle,
|
|
4
|
+
Coins,
|
|
5
|
+
Cpu,
|
|
6
|
+
Folder,
|
|
7
|
+
LayoutDashboard,
|
|
8
|
+
Plug,
|
|
9
|
+
Smile,
|
|
10
|
+
TrendingUp,
|
|
11
|
+
Wrench,
|
|
12
|
+
} from "lucide-react";
|
|
2
13
|
import type React from "react";
|
|
3
14
|
|
|
4
15
|
export type DashboardSection =
|
|
@@ -6,6 +17,7 @@ export type DashboardSection =
|
|
|
6
17
|
| "requests"
|
|
7
18
|
| "errors"
|
|
8
19
|
| "models"
|
|
20
|
+
| "providers"
|
|
9
21
|
| "tools"
|
|
10
22
|
| "costs"
|
|
11
23
|
| "behavior"
|
|
@@ -40,6 +52,11 @@ export const routes: DashboardRoute[] = [
|
|
|
40
52
|
label: "Models",
|
|
41
53
|
icon: Cpu,
|
|
42
54
|
},
|
|
55
|
+
{
|
|
56
|
+
id: "providers",
|
|
57
|
+
label: "Providers",
|
|
58
|
+
icon: Plug,
|
|
59
|
+
},
|
|
43
60
|
{
|
|
44
61
|
id: "tools",
|
|
45
62
|
label: "Tools",
|