feat(gateway): add system.sessionAnalytics usage snapshot RPC
This commit is contained in:
@@ -12,6 +12,26 @@ export function parseDuration(s: string): number | null {
|
||||
return unit === 'h' ? Number(n) * 3600_000 : Number(n) * 86_400_000;
|
||||
}
|
||||
|
||||
export interface SessionDailyAnalyticsRow {
|
||||
day: string;
|
||||
sessions: number;
|
||||
messages: number;
|
||||
}
|
||||
|
||||
export interface SessionTopAnalyticsRow {
|
||||
sessionId: string;
|
||||
messages: number;
|
||||
lastActivity: number;
|
||||
}
|
||||
|
||||
export interface SessionAnalyticsSnapshot {
|
||||
daily: SessionDailyAnalyticsRow[];
|
||||
topSessions: SessionTopAnalyticsRow[];
|
||||
averageMessagesPerSession: number;
|
||||
totalSessions: number;
|
||||
totalMessages: number;
|
||||
}
|
||||
|
||||
export class SessionStore {
|
||||
private db: Database.Database;
|
||||
|
||||
@@ -267,4 +287,66 @@ export class SessionStore {
|
||||
updateMessageMetadata(messageId: number, metadata: HistoryMetadata): void {
|
||||
this.db.prepare('UPDATE messages SET metadata = ? WHERE id = ?').run(JSON.stringify(metadata), messageId);
|
||||
}
|
||||
|
||||
getSessionAnalytics(opts: { sinceTimestamp: number; topLimit?: number }): SessionAnalyticsSnapshot {
|
||||
const since = opts.sinceTimestamp;
|
||||
const topLimit = opts.topLimit ?? 10;
|
||||
|
||||
const dailyRows = this.db.prepare(`
|
||||
SELECT
|
||||
date(created_at, 'unixepoch') AS day,
|
||||
COUNT(DISTINCT session_id) AS sessions,
|
||||
COUNT(*) AS messages
|
||||
FROM messages
|
||||
WHERE created_at >= ?
|
||||
GROUP BY day
|
||||
ORDER BY day DESC
|
||||
`).all(since) as Array<{ day: string; sessions: number; messages: number }>;
|
||||
|
||||
const topRows = this.db.prepare(`
|
||||
SELECT
|
||||
session_id,
|
||||
COUNT(*) AS messages,
|
||||
MAX(created_at) AS last_activity
|
||||
FROM messages
|
||||
WHERE created_at >= ?
|
||||
GROUP BY session_id
|
||||
ORDER BY messages DESC, last_activity DESC
|
||||
LIMIT ?
|
||||
`).all(since, topLimit) as Array<{ session_id: string; messages: number; last_activity: number }>;
|
||||
|
||||
const totalMessagesRow = this.db.prepare(`
|
||||
SELECT COUNT(*) AS total_messages
|
||||
FROM messages
|
||||
WHERE created_at >= ?
|
||||
`).get(since) as { total_messages: number };
|
||||
|
||||
const totalSessionsRow = this.db.prepare(`
|
||||
SELECT COUNT(DISTINCT session_id) AS total_sessions
|
||||
FROM messages
|
||||
WHERE created_at >= ?
|
||||
`).get(since) as { total_sessions: number };
|
||||
|
||||
const totalMessages = totalMessagesRow.total_messages ?? 0;
|
||||
const totalSessions = totalSessionsRow.total_sessions ?? 0;
|
||||
const averageMessagesPerSession = totalSessions > 0
|
||||
? Math.round((totalMessages / totalSessions) * 100) / 100
|
||||
: 0;
|
||||
|
||||
return {
|
||||
daily: dailyRows.map((row) => ({
|
||||
day: row.day,
|
||||
sessions: row.sessions,
|
||||
messages: row.messages,
|
||||
})),
|
||||
topSessions: topRows.map((row) => ({
|
||||
sessionId: row.session_id,
|
||||
messages: row.messages,
|
||||
lastActivity: row.last_activity,
|
||||
})),
|
||||
averageMessagesPerSession,
|
||||
totalSessions,
|
||||
totalMessages,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user