Skip to main content

systemprompt_cli/commands/analytics/costs/
mod.rs

1//! Cost analytics: spend summary, trends over time, and breakdown by model or
2//! agent.
3//!
4//! Defines the [`CostsCommands`] subcommand tree and the typed output shapes
5//! ([`CostSummaryOutput`], [`CostTrendsOutput`], [`CostBreakdownOutput`])
6//! rendered by the `analytics costs` commands.
7
8mod breakdown;
9mod summary;
10mod trends;
11
12use anyhow::Result;
13use clap::Subcommand;
14use schemars::JsonSchema;
15use serde::{Deserialize, Serialize};
16
17use crate::context::CommandContext;
18use crate::shared::render_result;
19
20#[derive(Debug, Subcommand)]
21pub enum CostsCommands {
22    #[command(about = "Cost summary", alias = "list")]
23    Summary(summary::SummaryArgs),
24
25    #[command(about = "Cost trends over time")]
26    Trends(trends::TrendsArgs),
27
28    #[command(about = "Cost breakdown by model/agent")]
29    Breakdown(breakdown::BreakdownArgs),
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
33pub struct CostSummaryOutput {
34    pub period: String,
35    pub total_cost_microdollars: i64,
36    pub total_requests: i64,
37    pub total_tokens: i64,
38    pub avg_cost_per_request_microdollars: f64,
39    pub change_percent: Option<f64>,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
43pub struct CostTrendPoint {
44    pub timestamp: String,
45    pub cost_microdollars: i64,
46    pub request_count: i64,
47    pub tokens: i64,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
51pub struct CostTrendsOutput {
52    pub period: String,
53    pub group_by: String,
54    pub points: Vec<CostTrendPoint>,
55    pub total_cost_microdollars: i64,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
59pub struct CostBreakdownItem {
60    pub name: String,
61    pub cost_microdollars: i64,
62    pub request_count: i64,
63    pub tokens: i64,
64    pub percentage: f64,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
68pub struct CostBreakdownOutput {
69    pub period: String,
70    pub breakdown_by: String,
71    pub items: Vec<CostBreakdownItem>,
72    pub total_cost_microdollars: i64,
73}
74
75pub async fn execute(command: CostsCommands, ctx: &CommandContext) -> Result<()> {
76    let db_ctx = ctx.database().await?;
77    match command {
78        CostsCommands::Summary(args) => {
79            let result = summary::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
80            render_result(&result, &ctx.cli);
81            Ok(())
82        },
83        CostsCommands::Trends(args) => {
84            let result = trends::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
85            render_result(&result, &ctx.cli);
86            Ok(())
87        },
88        CostsCommands::Breakdown(args) => {
89            let result = breakdown::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
90            render_result(&result, &ctx.cli);
91            Ok(())
92        },
93    }
94}