systemprompt_analytics/repository/agents/
list_queries.rs1use crate::Result;
16use chrono::{DateTime, Utc};
17
18use super::AgentAnalyticsRepository;
19use crate::models::AgentListDbRow;
20use crate::models::reporting::AgentListRow;
21
22impl AgentAnalyticsRepository {
23 pub async fn list_agents(
24 &self,
25 start: DateTime<Utc>,
26 end: DateTime<Utc>,
27 limit: i64,
28 sort_order: &str,
29 ) -> Result<Vec<AgentListRow>> {
30 let rows = match sort_order {
31 "success_rate" => self.list_by_success_rate(start, end, limit).await,
32 "cost" => self.list_by_cost(start, end, limit).await,
33 "last_active" => self.list_by_last_active(start, end, limit).await,
34 _ => self.list_by_task_count(start, end, limit).await,
35 }?;
36 Ok(rows.into_iter().map(AgentListRow::from).collect())
37 }
38
39 async fn list_by_success_rate(
40 &self,
41 start: DateTime<Utc>,
42 end: DateTime<Utc>,
43 limit: i64,
44 ) -> Result<Vec<AgentListDbRow>> {
45 sqlx::query_as!(
46 AgentListDbRow,
47 r#"
48 WITH tasks AS (
49 SELECT agent_name, SUM(task_count) AS task_count,
50 COALESCE(SUM(task_count) FILTER (WHERE status = 'TASK_STATE_COMPLETED'), 0) AS completed_count,
51 SUM(exec_sum_ms) AS exec_sum_ms, SUM(exec_n) AS exec_n,
52 MAX(last_started_at) AS last_active
53 FROM report_agent_task_hourly
54 WHERE bucket >= date_trunc('hour', $1::timestamptz, 'UTC') AND bucket < $2 AND agent_name <> ''
55 GROUP BY agent_name
56 ), costs AS (
57 SELECT agent_name, SUM(cost_microdollars) AS cost
58 FROM report_ai_usage_hourly
59 WHERE bucket >= date_trunc('hour', $1::timestamptz, 'UTC') AND bucket < $2 AND agent_name <> ''
60 GROUP BY agent_name
61 )
62 SELECT
63 t.agent_name as "agent_name!",
64 t.task_count::bigint as "task_count!",
65 t.completed_count::bigint as "completed_count!",
66 COALESCE(t.exec_sum_ms::numeric / NULLIF(t.exec_n, 0), 0)::bigint as "avg_execution_time_ms!",
67 COALESCE(c.cost, 0)::bigint as "total_cost_microdollars!",
68 t.last_active as "last_active!"
69 FROM tasks t
70 LEFT JOIN costs c ON c.agent_name = t.agent_name
71 ORDER BY CASE WHEN t.task_count > 0
72 THEN t.completed_count::float / t.task_count::float
73 ELSE 0 END DESC
74 LIMIT $3
75 "#,
76 start,
77 end,
78 limit
79 )
80 .fetch_all(&*self.pool)
81 .await
82 .map_err(Into::into)
83 }
84
85 async fn list_by_cost(
86 &self,
87 start: DateTime<Utc>,
88 end: DateTime<Utc>,
89 limit: i64,
90 ) -> Result<Vec<AgentListDbRow>> {
91 sqlx::query_as!(
92 AgentListDbRow,
93 r#"
94 WITH tasks AS (
95 SELECT agent_name, SUM(task_count) AS task_count,
96 COALESCE(SUM(task_count) FILTER (WHERE status = 'TASK_STATE_COMPLETED'), 0) AS completed_count,
97 SUM(exec_sum_ms) AS exec_sum_ms, SUM(exec_n) AS exec_n,
98 MAX(last_started_at) AS last_active
99 FROM report_agent_task_hourly
100 WHERE bucket >= date_trunc('hour', $1::timestamptz, 'UTC') AND bucket < $2 AND agent_name <> ''
101 GROUP BY agent_name
102 ), costs AS (
103 SELECT agent_name, SUM(cost_microdollars) AS cost
104 FROM report_ai_usage_hourly
105 WHERE bucket >= date_trunc('hour', $1::timestamptz, 'UTC') AND bucket < $2 AND agent_name <> ''
106 GROUP BY agent_name
107 )
108 SELECT
109 t.agent_name as "agent_name!",
110 t.task_count::bigint as "task_count!",
111 t.completed_count::bigint as "completed_count!",
112 COALESCE(t.exec_sum_ms::numeric / NULLIF(t.exec_n, 0), 0)::bigint as "avg_execution_time_ms!",
113 COALESCE(c.cost, 0)::bigint as "total_cost_microdollars!",
114 t.last_active as "last_active!"
115 FROM tasks t
116 LEFT JOIN costs c ON c.agent_name = t.agent_name
117 ORDER BY COALESCE(c.cost, 0) DESC
118 LIMIT $3
119 "#,
120 start,
121 end,
122 limit
123 )
124 .fetch_all(&*self.pool)
125 .await
126 .map_err(Into::into)
127 }
128
129 async fn list_by_last_active(
130 &self,
131 start: DateTime<Utc>,
132 end: DateTime<Utc>,
133 limit: i64,
134 ) -> Result<Vec<AgentListDbRow>> {
135 sqlx::query_as!(
136 AgentListDbRow,
137 r#"
138 WITH tasks AS (
139 SELECT agent_name, SUM(task_count) AS task_count,
140 COALESCE(SUM(task_count) FILTER (WHERE status = 'TASK_STATE_COMPLETED'), 0) AS completed_count,
141 SUM(exec_sum_ms) AS exec_sum_ms, SUM(exec_n) AS exec_n,
142 MAX(last_started_at) AS last_active
143 FROM report_agent_task_hourly
144 WHERE bucket >= date_trunc('hour', $1::timestamptz, 'UTC') AND bucket < $2 AND agent_name <> ''
145 GROUP BY agent_name
146 ), costs AS (
147 SELECT agent_name, SUM(cost_microdollars) AS cost
148 FROM report_ai_usage_hourly
149 WHERE bucket >= date_trunc('hour', $1::timestamptz, 'UTC') AND bucket < $2 AND agent_name <> ''
150 GROUP BY agent_name
151 )
152 SELECT
153 t.agent_name as "agent_name!",
154 t.task_count::bigint as "task_count!",
155 t.completed_count::bigint as "completed_count!",
156 COALESCE(t.exec_sum_ms::numeric / NULLIF(t.exec_n, 0), 0)::bigint as "avg_execution_time_ms!",
157 COALESCE(c.cost, 0)::bigint as "total_cost_microdollars!",
158 t.last_active as "last_active!"
159 FROM tasks t
160 LEFT JOIN costs c ON c.agent_name = t.agent_name
161 ORDER BY t.last_active DESC
162 LIMIT $3
163 "#,
164 start,
165 end,
166 limit
167 )
168 .fetch_all(&*self.pool)
169 .await
170 .map_err(Into::into)
171 }
172
173 async fn list_by_task_count(
174 &self,
175 start: DateTime<Utc>,
176 end: DateTime<Utc>,
177 limit: i64,
178 ) -> Result<Vec<AgentListDbRow>> {
179 sqlx::query_as!(
180 AgentListDbRow,
181 r#"
182 WITH tasks AS (
183 SELECT agent_name, SUM(task_count) AS task_count,
184 COALESCE(SUM(task_count) FILTER (WHERE status = 'TASK_STATE_COMPLETED'), 0) AS completed_count,
185 SUM(exec_sum_ms) AS exec_sum_ms, SUM(exec_n) AS exec_n,
186 MAX(last_started_at) AS last_active
187 FROM report_agent_task_hourly
188 WHERE bucket >= date_trunc('hour', $1::timestamptz, 'UTC') AND bucket < $2 AND agent_name <> ''
189 GROUP BY agent_name
190 ), costs AS (
191 SELECT agent_name, SUM(cost_microdollars) AS cost
192 FROM report_ai_usage_hourly
193 WHERE bucket >= date_trunc('hour', $1::timestamptz, 'UTC') AND bucket < $2 AND agent_name <> ''
194 GROUP BY agent_name
195 )
196 SELECT
197 t.agent_name as "agent_name!",
198 t.task_count::bigint as "task_count!",
199 t.completed_count::bigint as "completed_count!",
200 COALESCE(t.exec_sum_ms::numeric / NULLIF(t.exec_n, 0), 0)::bigint as "avg_execution_time_ms!",
201 COALESCE(c.cost, 0)::bigint as "total_cost_microdollars!",
202 t.last_active as "last_active!"
203 FROM tasks t
204 LEFT JOIN costs c ON c.agent_name = t.agent_name
205 ORDER BY t.task_count DESC
206 LIMIT $3
207 "#,
208 start,
209 end,
210 limit
211 )
212 .fetch_all(&*self.pool)
213 .await
214 .map_err(Into::into)
215 }
216}