systemprompt_cli/commands/analytics/sessions/
mod.rs1mod live;
11mod stats;
12mod trends;
13
14use anyhow::Result;
15use clap::Subcommand;
16use schemars::JsonSchema;
17use serde::{Deserialize, Serialize};
18
19use crate::context::CommandContext;
20use crate::shared::render_result;
21
22#[derive(Debug, Subcommand)]
23pub enum SessionsCommands {
24 #[command(about = "Session statistics", alias = "list")]
25 Stats(stats::StatsArgs),
26
27 #[command(about = "Session trends over time")]
28 Trends(trends::TrendsArgs),
29
30 #[command(about = "Real-time active sessions")]
31 Live(live::LiveArgs),
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
35pub struct SessionStatsOutput {
36 pub period: String,
37 #[serde(rename = "sessions_created_in_period")]
38 pub total_sessions: i64,
39 #[serde(rename = "sessions_currently_active")]
40 pub active_sessions: i64,
41 pub unique_users: i64,
42 pub avg_duration_seconds: i64,
43 pub avg_requests_per_session: f64,
44 pub conversion_rate: f64,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
48pub struct SessionTrendPoint {
49 pub timestamp: String,
50 pub session_count: i64,
51 pub active_users: i64,
52 pub avg_duration_seconds: i64,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
56pub struct SessionTrendsOutput {
57 pub period: String,
58 pub group_by: String,
59 pub points: Vec<SessionTrendPoint>,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
63pub struct ActiveSessionRow {
64 #[serde(rename = "session_id")]
65 pub session: String,
66 pub user_type: String,
67 pub started_at: String,
68 pub duration_seconds: i64,
69 pub request_count: i64,
70 pub last_activity: String,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
74pub struct LiveSessionsOutput {
75 pub active_count: i64,
76 pub sessions: Vec<ActiveSessionRow>,
77 pub timestamp: String,
78}
79
80pub async fn execute(command: SessionsCommands, ctx: &CommandContext) -> Result<()> {
81 let db_ctx = ctx.database().await?;
82 match command {
83 SessionsCommands::Stats(args) => {
84 let result = stats::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
85 render_result(&result, &ctx.cli);
86 Ok(())
87 },
88 SessionsCommands::Trends(args) => {
89 let result = trends::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
90 render_result(&result, &ctx.cli);
91 Ok(())
92 },
93 SessionsCommands::Live(mut args) => {
94 if ctx.is_database_scoped() {
95 args.no_refresh = true;
96 }
97 let result = live::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
98 render_result(&result, &ctx.cli);
99 Ok(())
100 },
101 }
102}