Skip to main content

systemprompt_cli/commands/analytics/conversations/
mod.rs

1//! Conversation analytics: aggregate stats, time-series trends, and listings.
2//!
3//! Defines the [`ConversationsCommands`] subcommand tree and the typed output
4//! shapes ([`ConversationStatsOutput`], [`ConversationTrendsOutput`],
5//! [`ConversationListOutput`]) rendered by the `analytics conversations`
6//! commands.
7
8mod list;
9mod stats;
10mod trends;
11
12use anyhow::Result;
13use clap::Subcommand;
14use schemars::JsonSchema;
15use serde::{Deserialize, Serialize};
16use systemprompt_runtime::DatabaseContext;
17
18use crate::CliConfig;
19use crate::shared::render_result;
20
21#[derive(Debug, Subcommand)]
22pub enum ConversationsCommands {
23    #[command(about = "Conversation statistics")]
24    Stats(stats::StatsArgs),
25
26    #[command(about = "Conversation trends over time")]
27    Trends(trends::TrendsArgs),
28
29    #[command(about = "List conversations")]
30    List(list::ListArgs),
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
34pub struct ConversationStatsOutput {
35    pub period: String,
36    pub total_contexts: i64,
37    pub total_tasks: i64,
38    pub total_messages: i64,
39    pub avg_messages_per_task: f64,
40    pub avg_task_duration_ms: i64,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
44pub struct ConversationTrendPoint {
45    pub timestamp: String,
46    pub context_count: i64,
47    pub task_count: i64,
48    pub message_count: i64,
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
52pub struct ConversationTrendsOutput {
53    pub period: String,
54    pub group_by: String,
55    pub points: Vec<ConversationTrendPoint>,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
59pub struct ConversationListRow {
60    #[serde(rename = "context_id")]
61    pub context: String,
62    pub name: Option<String>,
63    pub task_count: i64,
64    pub message_count: i64,
65    pub created_at: String,
66    pub updated_at: String,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
70pub struct ConversationListOutput {
71    pub conversations: Vec<ConversationListRow>,
72    pub total: i64,
73}
74
75pub async fn execute(command: ConversationsCommands, config: &CliConfig) -> Result<()> {
76    match command {
77        ConversationsCommands::Stats(args) => {
78            let result = stats::execute(args, config).await?;
79            render_result(&result);
80            Ok(())
81        },
82        ConversationsCommands::Trends(args) => {
83            let result = trends::execute(args, config).await?;
84            render_result(&result);
85            Ok(())
86        },
87        ConversationsCommands::List(args) => {
88            let result = list::execute(args, config).await?;
89            render_result(&result);
90            Ok(())
91        },
92    }
93}
94
95pub async fn execute_with_pool(
96    command: ConversationsCommands,
97    db_ctx: &DatabaseContext,
98    config: &CliConfig,
99) -> Result<()> {
100    match command {
101        ConversationsCommands::Stats(args) => {
102            let result = stats::execute_with_pool(args, db_ctx, config).await?;
103            render_result(&result);
104            Ok(())
105        },
106        ConversationsCommands::Trends(args) => {
107            let result = trends::execute_with_pool(args, db_ctx, config).await?;
108            render_result(&result);
109            Ok(())
110        },
111        ConversationsCommands::List(args) => {
112            let result = list::execute_with_pool(args, db_ctx, config).await?;
113            render_result(&result);
114            Ok(())
115        },
116    }
117}