systemprompt_analytics/repository/tools/
list_queries.rs1use crate::Result;
8use chrono::{DateTime, Utc};
9
10use super::ToolAnalyticsRepository;
11use crate::models::cli::ToolListRow;
12
13#[derive(Debug)]
14pub struct ToolListParams<'a> {
15 pub start: DateTime<Utc>,
16 pub end: DateTime<Utc>,
17 pub limit: i64,
18 pub server_filter: Option<&'a str>,
19 pub sort_order: &'a str,
20}
21
22impl ToolAnalyticsRepository {
23 pub async fn list_tools(&self, params: ToolListParams<'_>) -> Result<Vec<ToolListRow>> {
24 if let Some(server) = params.server_filter {
25 let pattern = format!("%{}%", server);
26 self.list_tools_with_filter(¶ms, &pattern).await
27 } else {
28 self.list_tools_unfiltered(¶ms).await
29 }
30 }
31
32 async fn list_tools_with_filter(
33 &self,
34 params: &ToolListParams<'_>,
35 pattern: &str,
36 ) -> Result<Vec<ToolListRow>> {
37 let ToolListParams {
38 start,
39 end,
40 limit,
41 sort_order,
42 ..
43 } = *params;
44 match sort_order {
45 "success_rate" => {
46 self.filtered_by_success_rate(start, end, pattern, limit)
47 .await
48 },
49 "avg_time" => self.filtered_by_avg_time(start, end, pattern, limit).await,
50 _ => self.filtered_by_count(start, end, pattern, limit).await,
51 }
52 }
53
54 async fn filtered_by_success_rate(
55 &self,
56 start: DateTime<Utc>,
57 end: DateTime<Utc>,
58 pattern: &str,
59 limit: i64,
60 ) -> Result<Vec<ToolListRow>> {
61 sqlx::query_as!(
62 ToolListRow,
63 r#"
64 SELECT
65 tool_name as "tool_name!",
66 server_name as "server_name!",
67 COUNT(*)::bigint as "execution_count!",
68 COUNT(*) FILTER (WHERE status = 'success')::bigint as "success_count!",
69 COALESCE(AVG(execution_time_ms)::float8, 0) as "avg_time!",
70 MAX(created_at) as "last_used!"
71 FROM mcp_tool_executions
72 WHERE created_at >= $1 AND created_at < $2 AND server_name ILIKE $3
73 GROUP BY tool_name, server_name
74 ORDER BY CASE WHEN COUNT(*) > 0
75 THEN COUNT(*) FILTER (WHERE status = 'success')::float / COUNT(*)::float
76 ELSE 0 END DESC
77 LIMIT $4
78 "#,
79 start,
80 end,
81 pattern,
82 limit
83 )
84 .fetch_all(&*self.pool)
85 .await
86 .map_err(Into::into)
87 }
88
89 async fn filtered_by_avg_time(
90 &self,
91 start: DateTime<Utc>,
92 end: DateTime<Utc>,
93 pattern: &str,
94 limit: i64,
95 ) -> Result<Vec<ToolListRow>> {
96 sqlx::query_as!(
97 ToolListRow,
98 r#"
99 SELECT
100 tool_name as "tool_name!",
101 server_name as "server_name!",
102 COUNT(*)::bigint as "execution_count!",
103 COUNT(*) FILTER (WHERE status = 'success')::bigint as "success_count!",
104 COALESCE(AVG(execution_time_ms)::float8, 0) as "avg_time!",
105 MAX(created_at) as "last_used!"
106 FROM mcp_tool_executions
107 WHERE created_at >= $1 AND created_at < $2 AND server_name ILIKE $3
108 GROUP BY tool_name, server_name
109 ORDER BY COALESCE(AVG(execution_time_ms), 0) DESC
110 LIMIT $4
111 "#,
112 start,
113 end,
114 pattern,
115 limit
116 )
117 .fetch_all(&*self.pool)
118 .await
119 .map_err(Into::into)
120 }
121
122 async fn filtered_by_count(
123 &self,
124 start: DateTime<Utc>,
125 end: DateTime<Utc>,
126 pattern: &str,
127 limit: i64,
128 ) -> Result<Vec<ToolListRow>> {
129 sqlx::query_as!(
130 ToolListRow,
131 r#"
132 SELECT
133 tool_name as "tool_name!",
134 server_name as "server_name!",
135 COUNT(*)::bigint as "execution_count!",
136 COUNT(*) FILTER (WHERE status = 'success')::bigint as "success_count!",
137 COALESCE(AVG(execution_time_ms)::float8, 0) as "avg_time!",
138 MAX(created_at) as "last_used!"
139 FROM mcp_tool_executions
140 WHERE created_at >= $1 AND created_at < $2 AND server_name ILIKE $3
141 GROUP BY tool_name, server_name
142 ORDER BY COUNT(*) DESC
143 LIMIT $4
144 "#,
145 start,
146 end,
147 pattern,
148 limit
149 )
150 .fetch_all(&*self.pool)
151 .await
152 .map_err(Into::into)
153 }
154
155 async fn list_tools_unfiltered(&self, params: &ToolListParams<'_>) -> Result<Vec<ToolListRow>> {
156 let ToolListParams {
157 start,
158 end,
159 limit,
160 sort_order,
161 ..
162 } = *params;
163 match sort_order {
164 "success_rate" => self.unfiltered_by_success_rate(start, end, limit).await,
165 "avg_time" => self.unfiltered_by_avg_time(start, end, limit).await,
166 _ => self.unfiltered_by_count(start, end, limit).await,
167 }
168 }
169
170 async fn unfiltered_by_success_rate(
171 &self,
172 start: DateTime<Utc>,
173 end: DateTime<Utc>,
174 limit: i64,
175 ) -> Result<Vec<ToolListRow>> {
176 sqlx::query_as!(
177 ToolListRow,
178 r#"
179 SELECT
180 tool_name as "tool_name!",
181 server_name as "server_name!",
182 COUNT(*)::bigint as "execution_count!",
183 COUNT(*) FILTER (WHERE status = 'success')::bigint as "success_count!",
184 COALESCE(AVG(execution_time_ms)::float8, 0) as "avg_time!",
185 MAX(created_at) as "last_used!"
186 FROM mcp_tool_executions
187 WHERE created_at >= $1 AND created_at < $2
188 GROUP BY tool_name, server_name
189 ORDER BY CASE WHEN COUNT(*) > 0
190 THEN COUNT(*) FILTER (WHERE status = 'success')::float / COUNT(*)::float
191 ELSE 0 END DESC
192 LIMIT $3
193 "#,
194 start,
195 end,
196 limit
197 )
198 .fetch_all(&*self.pool)
199 .await
200 .map_err(Into::into)
201 }
202
203 async fn unfiltered_by_avg_time(
204 &self,
205 start: DateTime<Utc>,
206 end: DateTime<Utc>,
207 limit: i64,
208 ) -> Result<Vec<ToolListRow>> {
209 sqlx::query_as!(
210 ToolListRow,
211 r#"
212 SELECT
213 tool_name as "tool_name!",
214 server_name as "server_name!",
215 COUNT(*)::bigint as "execution_count!",
216 COUNT(*) FILTER (WHERE status = 'success')::bigint as "success_count!",
217 COALESCE(AVG(execution_time_ms)::float8, 0) as "avg_time!",
218 MAX(created_at) as "last_used!"
219 FROM mcp_tool_executions
220 WHERE created_at >= $1 AND created_at < $2
221 GROUP BY tool_name, server_name
222 ORDER BY COALESCE(AVG(execution_time_ms), 0) DESC
223 LIMIT $3
224 "#,
225 start,
226 end,
227 limit
228 )
229 .fetch_all(&*self.pool)
230 .await
231 .map_err(Into::into)
232 }
233
234 async fn unfiltered_by_count(
235 &self,
236 start: DateTime<Utc>,
237 end: DateTime<Utc>,
238 limit: i64,
239 ) -> Result<Vec<ToolListRow>> {
240 sqlx::query_as!(
241 ToolListRow,
242 r#"
243 SELECT
244 tool_name as "tool_name!",
245 server_name as "server_name!",
246 COUNT(*)::bigint as "execution_count!",
247 COUNT(*) FILTER (WHERE status = 'success')::bigint as "success_count!",
248 COALESCE(AVG(execution_time_ms)::float8, 0) as "avg_time!",
249 MAX(created_at) as "last_used!"
250 FROM mcp_tool_executions
251 WHERE created_at >= $1 AND created_at < $2
252 GROUP BY tool_name, server_name
253 ORDER BY COUNT(*) DESC
254 LIMIT $3
255 "#,
256 start,
257 end,
258 limit
259 )
260 .fetch_all(&*self.pool)
261 .await
262 .map_err(Into::into)
263 }
264}