Skip to main content

systemprompt_cli/commands/analytics/tools/
mod.rs

1//! Tool usage analytics: aggregate stats, listings, trends, and per-tool deep
2//! dives.
3//!
4//! Defines the [`ToolsCommands`] subcommand tree and the typed output shapes
5//! ([`ToolStatsOutput`], [`ToolListOutput`], [`ToolTrendsOutput`],
6//! [`ToolShowOutput`]) rendered by the `analytics tools` 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 ToolsCommands {
23    #[command(about = "Aggregate tool statistics")]
24    Stats(stats::StatsArgs),
25
26    #[command(about = "List tools with metrics")]
27    List(list::ListArgs),
28
29    #[command(about = "Tool usage trends over time")]
30    Trends(trends::TrendsArgs),
31
32    #[command(about = "Deep dive into specific tool")]
33    Show(show::ShowArgs),
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
37pub struct ToolStatsOutput {
38    pub period: String,
39    pub total_tools: i64,
40    pub total_executions: i64,
41    pub successful: i64,
42    pub failed: i64,
43    pub timeout: i64,
44    pub success_rate: f64,
45    pub avg_execution_time_ms: i64,
46    pub p95_execution_time_ms: i64,
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
50pub struct ToolListRow {
51    pub tool_name: String,
52    pub server_name: String,
53    pub execution_count: i64,
54    pub success_rate: f64,
55    pub avg_execution_time_ms: i64,
56    pub last_used: String,
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
60pub struct ToolListOutput {
61    pub tools: Vec<ToolListRow>,
62    pub total: i64,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
66pub struct ToolTrendPoint {
67    pub timestamp: String,
68    pub execution_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 ToolTrendsOutput {
75    pub tool: Option<String>,
76    pub period: String,
77    pub group_by: String,
78    pub points: Vec<ToolTrendPoint>,
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
82pub struct ToolShowOutput {
83    pub tool_name: String,
84    pub period: String,
85    pub summary: ToolStatsOutput,
86    pub status_breakdown: Vec<StatusBreakdownItem>,
87    pub top_errors: Vec<ErrorItem>,
88    pub usage_by_agent: Vec<AgentUsageItem>,
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 ErrorItem {
100    pub error_message: String,
101    pub count: i64,
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
105pub struct AgentUsageItem {
106    pub agent_name: String,
107    pub count: i64,
108    pub percentage: f64,
109}
110
111pub async fn execute(command: ToolsCommands, ctx: &CommandContext) -> Result<()> {
112    let db_ctx = ctx.database().await?;
113    match command {
114        ToolsCommands::Stats(args) => {
115            let result = stats::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
116            render_result(&result, &ctx.cli);
117            Ok(())
118        },
119        ToolsCommands::List(args) => {
120            let result = list::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
121            render_result(&result, &ctx.cli);
122            Ok(())
123        },
124        ToolsCommands::Trends(args) => {
125            let result = trends::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
126            render_result(&result, &ctx.cli);
127            Ok(())
128        },
129        ToolsCommands::Show(args) => {
130            let result = show::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
131            render_result(&result, &ctx.cli);
132            Ok(())
133        },
134    }
135}