systemprompt_cli/commands/analytics/costs/
mod.rs1mod breakdown;
9mod summary;
10mod trends;
11
12use anyhow::Result;
13use clap::Subcommand;
14use schemars::JsonSchema;
15use serde::{Deserialize, Serialize};
16use systemprompt_runtime::DatabaseContext;
17
18use crate::CliConfig;
19use crate::shared::render_result;
20
21#[derive(Debug, Subcommand)]
22pub enum CostsCommands {
23 #[command(about = "Cost summary", alias = "list")]
24 Summary(summary::SummaryArgs),
25
26 #[command(about = "Cost trends over time")]
27 Trends(trends::TrendsArgs),
28
29 #[command(about = "Cost breakdown by model/agent")]
30 Breakdown(breakdown::BreakdownArgs),
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
34pub struct CostSummaryOutput {
35 pub period: String,
36 pub total_cost_microdollars: i64,
37 pub total_requests: i64,
38 pub total_tokens: i64,
39 pub avg_cost_per_request_microdollars: f64,
40 pub change_percent: Option<f64>,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
44pub struct CostTrendPoint {
45 pub timestamp: String,
46 pub cost_microdollars: i64,
47 pub request_count: i64,
48 pub tokens: i64,
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
52pub struct CostTrendsOutput {
53 pub period: String,
54 pub group_by: String,
55 pub points: Vec<CostTrendPoint>,
56 pub total_cost_microdollars: i64,
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
60pub struct CostBreakdownItem {
61 pub name: String,
62 pub cost_microdollars: i64,
63 pub request_count: i64,
64 pub tokens: i64,
65 pub percentage: f64,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
69pub struct CostBreakdownOutput {
70 pub period: String,
71 pub breakdown_by: String,
72 pub items: Vec<CostBreakdownItem>,
73 pub total_cost_microdollars: i64,
74}
75
76pub async fn execute(command: CostsCommands, config: &CliConfig) -> Result<()> {
77 match command {
78 CostsCommands::Summary(args) => {
79 let result = summary::execute(args, config).await?;
80 render_result(&result);
81 Ok(())
82 },
83 CostsCommands::Trends(args) => {
84 let result = trends::execute(args, config).await?;
85 render_result(&result);
86 Ok(())
87 },
88 CostsCommands::Breakdown(args) => {
89 let result = breakdown::execute(args, config).await?;
90 render_result(&result);
91 Ok(())
92 },
93 }
94}
95
96pub async fn execute_with_pool(
97 command: CostsCommands,
98 db_ctx: &DatabaseContext,
99 config: &CliConfig,
100) -> Result<()> {
101 match command {
102 CostsCommands::Summary(args) => {
103 let result = summary::execute_with_pool(args, db_ctx, config).await?;
104 render_result(&result);
105 Ok(())
106 },
107 CostsCommands::Trends(args) => {
108 let result = trends::execute_with_pool(args, db_ctx, config).await?;
109 render_result(&result);
110 Ok(())
111 },
112 CostsCommands::Breakdown(args) => {
113 let result = breakdown::execute_with_pool(args, db_ctx, config).await?;
114 render_result(&result);
115 Ok(())
116 },
117 }
118}