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};
17use systemprompt_runtime::DatabaseContext;
18
19use crate::CliConfig;
20use crate::shared::render_result;
21
22#[derive(Debug, Subcommand)]
23pub enum ToolsCommands {
24    #[command(about = "Aggregate tool statistics")]
25    Stats(stats::StatsArgs),
26
27    #[command(about = "List tools with metrics")]
28    List(list::ListArgs),
29
30    #[command(about = "Tool usage trends over time")]
31    Trends(trends::TrendsArgs),
32
33    #[command(about = "Deep dive into specific tool")]
34    Show(show::ShowArgs),
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
38pub struct ToolStatsOutput {
39    pub period: String,
40    pub total_tools: i64,
41    pub total_executions: i64,
42    pub successful: i64,
43    pub failed: i64,
44    pub timeout: i64,
45    pub success_rate: f64,
46    pub avg_execution_time_ms: i64,
47    pub p95_execution_time_ms: i64,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
51pub struct ToolListRow {
52    pub tool_name: String,
53    pub server_name: String,
54    pub execution_count: i64,
55    pub success_rate: f64,
56    pub avg_execution_time_ms: i64,
57    pub last_used: String,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
61pub struct ToolListOutput {
62    pub tools: Vec<ToolListRow>,
63    pub total: i64,
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
67pub struct ToolTrendPoint {
68    pub timestamp: String,
69    pub execution_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 ToolTrendsOutput {
76    pub tool: Option<String>,
77    pub period: String,
78    pub group_by: String,
79    pub points: Vec<ToolTrendPoint>,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
83pub struct ToolShowOutput {
84    pub tool_name: String,
85    pub period: String,
86    pub summary: ToolStatsOutput,
87    pub status_breakdown: Vec<StatusBreakdownItem>,
88    pub top_errors: Vec<ErrorItem>,
89    pub usage_by_agent: Vec<AgentUsageItem>,
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 ErrorItem {
101    pub error_message: String,
102    pub count: i64,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
106pub struct AgentUsageItem {
107    pub agent_name: String,
108    pub count: i64,
109    pub percentage: f64,
110}
111
112pub async fn execute(command: ToolsCommands, config: &CliConfig) -> Result<()> {
113    match command {
114        ToolsCommands::Stats(args) => {
115            let result = stats::execute(args, config).await?;
116            render_result(&result);
117            Ok(())
118        },
119        ToolsCommands::List(args) => {
120            let result = list::execute(args, config).await?;
121            render_result(&result);
122            Ok(())
123        },
124        ToolsCommands::Trends(args) => {
125            let result = trends::execute(args, config).await?;
126            render_result(&result);
127            Ok(())
128        },
129        ToolsCommands::Show(args) => {
130            let result = show::execute(args, config).await?;
131            render_result(&result);
132            Ok(())
133        },
134    }
135}
136
137pub async fn execute_with_pool(
138    command: ToolsCommands,
139    db_ctx: &DatabaseContext,
140    config: &CliConfig,
141) -> Result<()> {
142    match command {
143        ToolsCommands::Stats(args) => {
144            let result = stats::execute_with_pool(args, db_ctx, config).await?;
145            render_result(&result);
146            Ok(())
147        },
148        ToolsCommands::List(args) => {
149            let result = list::execute_with_pool(args, db_ctx, config).await?;
150            render_result(&result);
151            Ok(())
152        },
153        ToolsCommands::Trends(args) => {
154            let result = trends::execute_with_pool(args, db_ctx, config).await?;
155            render_result(&result);
156            Ok(())
157        },
158        ToolsCommands::Show(args) => {
159            let result = show::execute_with_pool(args, db_ctx, config).await?;
160            render_result(&result);
161            Ok(())
162        },
163    }
164}