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