systemprompt_cli/commands/analytics/requests/
mod.rs1mod list;
12mod models;
13mod stats;
14mod trends;
15
16use anyhow::Result;
17use clap::{Args, Subcommand};
18use schemars::JsonSchema;
19use serde::{Deserialize, Serialize};
20
21use crate::context::CommandContext;
22use crate::shared::render_result;
23
24#[derive(Debug, Args)]
30pub struct RequestsArgs {
31 #[command(subcommand)]
32 pub cmd: Option<RequestsCommands>,
33
34 #[command(flatten)]
35 pub stats: stats::StatsArgs,
36}
37
38#[derive(Debug, Subcommand)]
39pub enum RequestsCommands {
40 #[command(
41 about = "Dashboard request metrics: time range, model filter, cache-hit rate, CSV export. For a quick operational aggregate, use `infra logs request stats`"
42 )]
43 Stats(stats::StatsArgs),
44
45 #[command(
46 about = "Dashboard list of AI requests with time range, model filter, and CSV export. For a quick operational list, use `infra logs request list`"
47 )]
48 List(list::ListArgs),
49
50 #[command(about = "AI request trends over time")]
51 Trends(trends::TrendsArgs),
52
53 #[command(about = "Model usage breakdown")]
54 Models(models::ModelsArgs),
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
58pub struct RequestStatsOutput {
59 pub period: String,
60 pub total_requests: i64,
61 pub total_tokens: i64,
62 pub input_tokens: i64,
63 pub output_tokens: i64,
64 pub reasoning_tokens: i64,
65 pub cache_read_tokens: i64,
66 pub cache_creation_tokens: i64,
67 pub total_cost_microdollars: i64,
68 pub avg_latency_ms: i64,
69 pub cache_hit_rate: f64,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
73pub struct RequestTrendPoint {
74 pub timestamp: String,
75 pub request_count: i64,
76 pub total_tokens: i64,
77 pub cost_microdollars: i64,
78 pub avg_latency_ms: i64,
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
82pub struct RequestTrendsOutput {
83 pub period: String,
84 pub group_by: String,
85 pub points: Vec<RequestTrendPoint>,
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
89pub struct ModelUsageRow {
90 pub provider: String,
91 pub model: String,
92 pub request_count: i64,
93 pub total_tokens: i64,
94 pub total_cost_microdollars: i64,
95 pub avg_latency_ms: i64,
96 pub percentage: f64,
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
100pub struct ModelsOutput {
101 pub period: String,
102 pub models: Vec<ModelUsageRow>,
103 pub total_requests: i64,
104}
105
106pub async fn execute(args: RequestsArgs, ctx: &CommandContext) -> Result<()> {
107 let db_ctx = ctx.database().await?;
108 match args.cmd.unwrap_or(RequestsCommands::Stats(args.stats)) {
109 RequestsCommands::Stats(args) => {
110 let result = stats::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
111 render_result(&result, &ctx.cli);
112 Ok(())
113 },
114 RequestsCommands::List(args) => {
115 let result = list::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
116 render_result(&result, &ctx.cli);
117 Ok(())
118 },
119 RequestsCommands::Trends(args) => {
120 let result = trends::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
121 render_result(&result, &ctx.cli);
122 Ok(())
123 },
124 RequestsCommands::Models(args) => {
125 let result = models::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
126 render_result(&result, &ctx.cli);
127 Ok(())
128 },
129 }
130}