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