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};
17
18use crate::context::CommandContext;
19use crate::shared::render_result;
20
21#[derive(Debug, Subcommand)]
22pub enum AgentsCommands {
23    #[command(about = "Aggregate agent statistics")]
24    Stats(stats::StatsArgs),
25
26    #[command(about = "List agents with metrics")]
27    List(list::ListArgs),
28
29    #[command(about = "Agent usage trends over time")]
30    Trends(trends::TrendsArgs),
31
32    #[command(about = "Deep dive into specific agent")]
33    Show(show::ShowArgs),
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
37pub struct AgentStatsOutput {
38    pub period: String,
39    pub total_agents: i64,
40    pub total_tasks: i64,
41    pub completed_tasks: i64,
42    pub failed_tasks: i64,
43    pub success_rate: f64,
44    pub avg_execution_time_ms: i64,
45    pub total_ai_requests: i64,
46    pub total_cost_microdollars: i64,
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
50pub struct AgentListRow {
51    pub agent_name: String,
52    pub task_count: i64,
53    pub success_rate: f64,
54    pub avg_execution_time_ms: i64,
55    pub total_cost_microdollars: i64,
56    pub last_active: String,
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
60pub struct AgentListOutput {
61    pub agents: Vec<AgentListRow>,
62    pub total: i64,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
66pub struct AgentTrendPoint {
67    pub timestamp: String,
68    pub task_count: i64,
69    pub success_rate: f64,
70    pub avg_execution_time_ms: i64,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
74pub struct AgentTrendsOutput {
75    pub agent: Option<String>,
76    pub period: String,
77    pub group_by: String,
78    pub points: Vec<AgentTrendPoint>,
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
82pub struct AgentShowOutput {
83    pub agent_name: String,
84    pub period: String,
85    pub summary: AgentStatsOutput,
86    pub status_breakdown: Vec<StatusBreakdownItem>,
87    pub top_errors: Vec<ErrorBreakdownItem>,
88    pub hourly_distribution: Vec<HourlyDistributionItem>,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
92pub struct StatusBreakdownItem {
93    pub status: String,
94    pub count: i64,
95    pub percentage: f64,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
99pub struct ErrorBreakdownItem {
100    pub error_type: String,
101    pub count: i64,
102}
103
104#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
105pub struct HourlyDistributionItem {
106    pub hour: i32,
107    pub count: i64,
108}
109
110pub async fn execute(command: AgentsCommands, ctx: &CommandContext) -> Result<()> {
111    let db_ctx = ctx.database().await?;
112    match command {
113        AgentsCommands::Stats(args) => {
114            let result = stats::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
115            render_result(&result, &ctx.cli);
116            Ok(())
117        },
118        AgentsCommands::List(args) => {
119            let result = list::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
120            render_result(&result, &ctx.cli);
121            Ok(())
122        },
123        AgentsCommands::Trends(args) => {
124            let result = trends::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
125            render_result(&result, &ctx.cli);
126            Ok(())
127        },
128        AgentsCommands::Show(args) => {
129            let result = show::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
130            render_result(&result, &ctx.cli);
131            Ok(())
132        },
133    }
134}