Skip to main content

systemprompt_cli/commands/analytics/agents/
mod.rs

1//! Agent performance analytics: stats, listing, trends, and per-agent deep
2//! dives.
3//!
4//! Defines the [`AgentsCommands`] subcommand tree and the typed output rows
5//! ([`AgentStatsOutput`], [`AgentListOutput`], [`AgentTrendsOutput`],
6//! [`AgentShowOutput`]) rendered by the `analytics agents` commands.
7
8mod list;
9mod show;
10mod stats;
11mod trends;
12
13use anyhow::Result;
14use clap::Subcommand;
15use schemars::JsonSchema;
16use serde::{Deserialize, Serialize};
17use systemprompt_runtime::DatabaseContext;
18
19use crate::CliConfig;
20use crate::shared::render_result;
21
22#[derive(Debug, Subcommand)]
23pub enum AgentsCommands {
24    #[command(about = "Aggregate agent statistics")]
25    Stats(stats::StatsArgs),
26
27    #[command(about = "List agents with metrics")]
28    List(list::ListArgs),
29
30    #[command(about = "Agent usage trends over time")]
31    Trends(trends::TrendsArgs),
32
33    #[command(about = "Deep dive into specific agent")]
34    Show(show::ShowArgs),
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
38pub struct AgentStatsOutput {
39    pub period: String,
40    pub total_agents: i64,
41    pub total_tasks: i64,
42    pub completed_tasks: i64,
43    pub failed_tasks: i64,
44    pub success_rate: f64,
45    pub avg_execution_time_ms: i64,
46    pub total_ai_requests: i64,
47    pub total_cost_microdollars: i64,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
51pub struct AgentListRow {
52    pub agent_name: String,
53    pub task_count: i64,
54    pub success_rate: f64,
55    pub avg_execution_time_ms: i64,
56    pub total_cost_microdollars: i64,
57    pub last_active: String,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
61pub struct AgentListOutput {
62    pub agents: Vec<AgentListRow>,
63    pub total: i64,
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
67pub struct AgentTrendPoint {
68    pub timestamp: String,
69    pub task_count: i64,
70    pub success_rate: f64,
71    pub avg_execution_time_ms: i64,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
75pub struct AgentTrendsOutput {
76    pub agent: Option<String>,
77    pub period: String,
78    pub group_by: String,
79    pub points: Vec<AgentTrendPoint>,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
83pub struct AgentShowOutput {
84    pub agent_name: String,
85    pub period: String,
86    pub summary: AgentStatsOutput,
87    pub status_breakdown: Vec<StatusBreakdownItem>,
88    pub top_errors: Vec<ErrorBreakdownItem>,
89    pub hourly_distribution: Vec<HourlyDistributionItem>,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
93pub struct StatusBreakdownItem {
94    pub status: String,
95    pub count: i64,
96    pub percentage: f64,
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
100pub struct ErrorBreakdownItem {
101    pub error_type: String,
102    pub count: i64,
103}
104
105#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
106pub struct HourlyDistributionItem {
107    pub hour: i32,
108    pub count: i64,
109}
110
111pub async fn execute(command: AgentsCommands, config: &CliConfig) -> Result<()> {
112    match command {
113        AgentsCommands::Stats(args) => {
114            let result = stats::execute(args, config).await?;
115            render_result(&result);
116            Ok(())
117        },
118        AgentsCommands::List(args) => {
119            let result = list::execute(args, config).await?;
120            render_result(&result);
121            Ok(())
122        },
123        AgentsCommands::Trends(args) => {
124            let result = trends::execute(args, config).await?;
125            render_result(&result);
126            Ok(())
127        },
128        AgentsCommands::Show(args) => {
129            let result = show::execute(args, config).await?;
130            render_result(&result);
131            Ok(())
132        },
133    }
134}
135
136pub async fn execute_with_pool(
137    command: AgentsCommands,
138    db_ctx: &DatabaseContext,
139    config: &CliConfig,
140) -> Result<()> {
141    match command {
142        AgentsCommands::Stats(args) => {
143            let result = stats::execute_with_pool(args, db_ctx, config).await?;
144            render_result(&result);
145            Ok(())
146        },
147        AgentsCommands::List(args) => {
148            let result = list::execute_with_pool(args, db_ctx, config).await?;
149            render_result(&result);
150            Ok(())
151        },
152        AgentsCommands::Trends(args) => {
153            let result = trends::execute_with_pool(args, db_ctx, config).await?;
154            render_result(&result);
155            Ok(())
156        },
157        AgentsCommands::Show(args) => {
158            let result = show::execute_with_pool(args, db_ctx, config).await?;
159            render_result(&result);
160            Ok(())
161        },
162    }
163}