Skip to main content

systemprompt_cli/commands/analytics/sessions/
mod.rs

1//! Session analytics: aggregate stats, trends, and a real-time live view.
2//!
3//! Defines the [`SessionsCommands`] subcommand tree and the typed output shapes
4//! ([`SessionStatsOutput`], [`SessionTrendsOutput`], [`LiveSessionsOutput`])
5//! rendered by the `analytics sessions` commands.
6
7mod live;
8mod stats;
9mod trends;
10
11use anyhow::Result;
12use clap::Subcommand;
13use schemars::JsonSchema;
14use serde::{Deserialize, Serialize};
15use systemprompt_runtime::DatabaseContext;
16
17use crate::CliConfig;
18use crate::shared::render_result;
19
20#[derive(Debug, Subcommand)]
21pub enum SessionsCommands {
22    #[command(about = "Session statistics", alias = "list")]
23    Stats(stats::StatsArgs),
24
25    #[command(about = "Session trends over time")]
26    Trends(trends::TrendsArgs),
27
28    #[command(about = "Real-time active sessions")]
29    Live(live::LiveArgs),
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
33pub struct SessionStatsOutput {
34    pub period: String,
35    #[serde(rename = "sessions_created_in_period")]
36    pub total_sessions: i64,
37    #[serde(rename = "sessions_currently_active")]
38    pub active_sessions: i64,
39    pub unique_users: i64,
40    pub avg_duration_seconds: i64,
41    pub avg_requests_per_session: f64,
42    pub conversion_rate: f64,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
46pub struct SessionTrendPoint {
47    pub timestamp: String,
48    pub session_count: i64,
49    pub active_users: i64,
50    pub avg_duration_seconds: i64,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
54pub struct SessionTrendsOutput {
55    pub period: String,
56    pub group_by: String,
57    pub points: Vec<SessionTrendPoint>,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
61pub struct ActiveSessionRow {
62    #[serde(rename = "session_id")]
63    pub session: String,
64    pub user_type: String,
65    pub started_at: String,
66    pub duration_seconds: i64,
67    pub request_count: i64,
68    pub last_activity: String,
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
72pub struct LiveSessionsOutput {
73    pub active_count: i64,
74    pub sessions: Vec<ActiveSessionRow>,
75    pub timestamp: String,
76}
77
78pub async fn execute(command: SessionsCommands, config: &CliConfig) -> Result<()> {
79    match command {
80        SessionsCommands::Stats(args) => {
81            let result = stats::execute(args, config).await?;
82            render_result(&result);
83            Ok(())
84        },
85        SessionsCommands::Trends(args) => {
86            let result = trends::execute(args, config).await?;
87            render_result(&result);
88            Ok(())
89        },
90        SessionsCommands::Live(args) => {
91            let result = live::execute(args, config).await?;
92            render_result(&result);
93            Ok(())
94        },
95    }
96}
97
98pub async fn execute_with_pool(
99    command: SessionsCommands,
100    db_ctx: &DatabaseContext,
101    config: &CliConfig,
102) -> Result<()> {
103    match command {
104        SessionsCommands::Stats(args) => {
105            let result = stats::execute_with_pool(args, db_ctx, config).await?;
106            render_result(&result);
107            Ok(())
108        },
109        SessionsCommands::Trends(args) => {
110            let result = trends::execute_with_pool(args, db_ctx, config).await?;
111            render_result(&result);
112            Ok(())
113        },
114        SessionsCommands::Live(args) => {
115            let result = live::execute_with_pool(args, db_ctx, config).await?;
116            render_result(&result);
117            Ok(())
118        },
119    }
120}