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//!
8//! Copyright (c) systemprompt.io — Business Source License 1.1.
9//! See <https://systemprompt.io> for licensing details.
10
11mod list;
12mod stats;
13mod trends;
14
15use anyhow::Result;
16use clap::Subcommand;
17use schemars::JsonSchema;
18use serde::{Deserialize, Serialize};
19
20use crate::context::CommandContext;
21use crate::shared::render_result;
22
23#[derive(Debug, Subcommand)]
24pub enum ConversationsCommands {
25    #[command(about = "Conversation statistics")]
26    Stats(stats::StatsArgs),
27
28    #[command(about = "Conversation trends over time")]
29    Trends(trends::TrendsArgs),
30
31    #[command(about = "List conversations")]
32    List(list::ListArgs),
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
36pub struct ConversationStatsOutput {
37    pub period: String,
38    pub total_contexts: i64,
39    pub total_tasks: i64,
40    pub total_messages: i64,
41    pub avg_messages_per_task: f64,
42    pub avg_task_duration_ms: i64,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
46pub struct ConversationTrendPoint {
47    pub timestamp: String,
48    pub context_count: i64,
49    pub task_count: i64,
50    pub message_count: i64,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
54pub struct ConversationTrendsOutput {
55    pub period: String,
56    pub group_by: String,
57    pub points: Vec<ConversationTrendPoint>,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
61pub struct ConversationListRow {
62    #[serde(rename = "context_id")]
63    pub context: String,
64    pub name: Option<String>,
65    pub task_count: i64,
66    pub message_count: i64,
67    pub created_at: String,
68    pub updated_at: String,
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
72pub struct ConversationListOutput {
73    pub conversations: Vec<ConversationListRow>,
74    pub total: i64,
75}
76
77pub async fn execute(command: ConversationsCommands, ctx: &CommandContext) -> Result<()> {
78    let db_ctx = ctx.database().await?;
79    match command {
80        ConversationsCommands::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        ConversationsCommands::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        ConversationsCommands::List(args) => {
91            let result = list::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
92            render_result(&result, &ctx.cli);
93            Ok(())
94        },
95    }
96}