systemprompt_analytics/repository/costs/
platform.rs1use super::CostAnalyticsRepository;
11use crate::Result;
12use chrono::{DateTime, Utc};
13
14use crate::models::cli::{CostBreakdownRow, CostSummaryRow, CostTrendRow, PreviousCostRow};
15
16impl CostAnalyticsRepository {
17 pub async fn get_summary(
18 &self,
19 start: DateTime<Utc>,
20 end: DateTime<Utc>,
21 ) -> Result<CostSummaryRow> {
22 sqlx::query_as!(
23 CostSummaryRow,
24 r#"
25 SELECT
26 COUNT(*)::bigint as "requests!",
27 SUM(cost_microdollars)::bigint as "cost",
28 SUM(tokens_used)::bigint as "tokens"
29 FROM ai_requests
30 WHERE created_at >= $1 AND created_at < $2
31 "#,
32 start,
33 end
34 )
35 .fetch_one(&*self.pool)
36 .await
37 .map_err(Into::into)
38 }
39
40 pub async fn get_previous_cost(
41 &self,
42 start: DateTime<Utc>,
43 end: DateTime<Utc>,
44 ) -> Result<PreviousCostRow> {
45 sqlx::query_as!(
46 PreviousCostRow,
47 r#"
48 SELECT SUM(cost_microdollars)::bigint as "cost"
49 FROM ai_requests
50 WHERE created_at >= $1 AND created_at < $2
51 "#,
52 start,
53 end
54 )
55 .fetch_one(&*self.pool)
56 .await
57 .map_err(Into::into)
58 }
59
60 pub async fn get_breakdown_by_model(
61 &self,
62 start: DateTime<Utc>,
63 end: DateTime<Utc>,
64 limit: i64,
65 ) -> Result<Vec<CostBreakdownRow>> {
66 sqlx::query_as!(
67 CostBreakdownRow,
68 r#"
69 SELECT
70 model as "name!",
71 COALESCE(SUM(cost_microdollars), 0)::bigint as "cost!",
72 COUNT(*)::bigint as "requests!",
73 COALESCE(SUM(tokens_used), 0)::bigint as "tokens!"
74 FROM ai_requests
75 WHERE created_at >= $1 AND created_at < $2
76 GROUP BY model
77 ORDER BY SUM(cost_microdollars) DESC NULLS LAST
78 LIMIT $3
79 "#,
80 start,
81 end,
82 limit
83 )
84 .fetch_all(&*self.pool)
85 .await
86 .map_err(Into::into)
87 }
88
89 pub async fn get_breakdown_by_provider(
90 &self,
91 start: DateTime<Utc>,
92 end: DateTime<Utc>,
93 limit: i64,
94 ) -> Result<Vec<CostBreakdownRow>> {
95 sqlx::query_as!(
96 CostBreakdownRow,
97 r#"
98 SELECT
99 provider as "name!",
100 COALESCE(SUM(cost_microdollars), 0)::bigint as "cost!",
101 COUNT(*)::bigint as "requests!",
102 COALESCE(SUM(tokens_used), 0)::bigint as "tokens!"
103 FROM ai_requests
104 WHERE created_at >= $1 AND created_at < $2
105 GROUP BY provider
106 ORDER BY SUM(cost_microdollars) DESC NULLS LAST
107 LIMIT $3
108 "#,
109 start,
110 end,
111 limit
112 )
113 .fetch_all(&*self.pool)
114 .await
115 .map_err(Into::into)
116 }
117
118 pub async fn get_breakdown_by_agent(
119 &self,
120 start: DateTime<Utc>,
121 end: DateTime<Utc>,
122 limit: i64,
123 ) -> Result<Vec<CostBreakdownRow>> {
124 sqlx::query_as!(
125 CostBreakdownRow,
126 r#"
127 (
128 SELECT
129 at.agent_name as "name!",
130 COALESCE(SUM(r.cost_microdollars), 0)::bigint as "cost!",
131 COUNT(*)::bigint as "requests!",
132 COALESCE(SUM(r.tokens_used), 0)::bigint as "tokens!"
133 FROM ai_requests r
134 INNER JOIN agent_tasks at ON at.task_id = r.task_id
135 WHERE r.created_at >= $1 AND r.created_at < $2
136 AND at.agent_name IS NOT NULL
137 GROUP BY at.agent_name
138 ORDER BY SUM(r.cost_microdollars) DESC NULLS LAST
139 LIMIT $3
140 )
141 UNION ALL
142 (
143 SELECT
144 'unattributed' as "name!",
145 COALESCE(SUM(r.cost_microdollars), 0)::bigint as "cost!",
146 COUNT(*)::bigint as "requests!",
147 COALESCE(SUM(r.tokens_used), 0)::bigint as "tokens!"
148 FROM ai_requests r
149 LEFT JOIN agent_tasks at ON at.task_id = r.task_id
150 WHERE r.created_at >= $1 AND r.created_at < $2
151 AND (r.task_id IS NULL OR at.agent_name IS NULL)
152 HAVING COUNT(*) > 0
153 )
154 "#,
155 start,
156 end,
157 limit
158 )
159 .fetch_all(&*self.pool)
160 .await
161 .map_err(Into::into)
162 }
163
164 pub async fn get_costs_for_trends(
165 &self,
166 start: DateTime<Utc>,
167 end: DateTime<Utc>,
168 ) -> Result<Vec<CostTrendRow>> {
169 sqlx::query_as!(
170 CostTrendRow,
171 r#"
172 SELECT
173 created_at as "created_at!",
174 cost_microdollars,
175 tokens_used
176 FROM ai_requests
177 WHERE created_at >= $1 AND created_at < $2
178 ORDER BY created_at
179 "#,
180 start,
181 end
182 )
183 .fetch_all(&*self.pool)
184 .await
185 .map_err(Into::into)
186 }
187}