systemprompt-analytics 0.1.22

Analytics module for systemprompt.io - session tracking, metrics, and reporting
Documentation
use anyhow::Result;
use chrono::{DateTime, Utc};

use super::ToolAnalyticsRepository;
use crate::models::cli::ToolListRow;

#[derive(Debug)]
pub struct ToolListParams<'a> {
    pub start: DateTime<Utc>,
    pub end: DateTime<Utc>,
    pub limit: i64,
    pub server_filter: Option<&'a str>,
    pub sort_order: &'a str,
}

impl ToolAnalyticsRepository {
    pub async fn list_tools(&self, params: ToolListParams<'_>) -> Result<Vec<ToolListRow>> {
        let ToolListParams {
            start,
            end,
            limit,
            server_filter,
            sort_order,
        } = params;
        if let Some(server) = server_filter {
            let pattern = format!("%{}%", server);
            self.list_tools_with_filter(start, end, limit, &pattern, sort_order)
                .await
        } else {
            self.list_tools_unfiltered(start, end, limit, sort_order)
                .await
        }
    }

    #[allow(clippy::too_many_arguments)]
    async fn list_tools_with_filter(
        &self,
        start: DateTime<Utc>,
        end: DateTime<Utc>,
        limit: i64,
        pattern: &str,
        sort_order: &str,
    ) -> Result<Vec<ToolListRow>> {
        match sort_order {
            "success_rate" => sqlx::query_as!(
                ToolListRow,
                r#"
                SELECT
                    tool_name as "tool_name!",
                    server_name as "server_name!",
                    COUNT(*)::bigint as "execution_count!",
                    COUNT(*) FILTER (WHERE status = 'success')::bigint as "success_count!",
                    COALESCE(AVG(execution_time_ms)::float8, 0) as "avg_time!",
                    MAX(created_at) as "last_used!"
                FROM mcp_tool_executions
                WHERE created_at >= $1 AND created_at < $2 AND server_name ILIKE $3
                GROUP BY tool_name, server_name
                ORDER BY CASE WHEN COUNT(*) > 0
                    THEN COUNT(*) FILTER (WHERE status = 'success')::float / COUNT(*)::float
                    ELSE 0 END DESC
                LIMIT $4
                "#,
                start,
                end,
                pattern,
                limit
            )
            .fetch_all(&*self.pool)
            .await
            .map_err(Into::into),
            "avg_time" => sqlx::query_as!(
                ToolListRow,
                r#"
                SELECT
                    tool_name as "tool_name!",
                    server_name as "server_name!",
                    COUNT(*)::bigint as "execution_count!",
                    COUNT(*) FILTER (WHERE status = 'success')::bigint as "success_count!",
                    COALESCE(AVG(execution_time_ms)::float8, 0) as "avg_time!",
                    MAX(created_at) as "last_used!"
                FROM mcp_tool_executions
                WHERE created_at >= $1 AND created_at < $2 AND server_name ILIKE $3
                GROUP BY tool_name, server_name
                ORDER BY COALESCE(AVG(execution_time_ms), 0) DESC
                LIMIT $4
                "#,
                start,
                end,
                pattern,
                limit
            )
            .fetch_all(&*self.pool)
            .await
            .map_err(Into::into),
            _ => sqlx::query_as!(
                ToolListRow,
                r#"
                SELECT
                    tool_name as "tool_name!",
                    server_name as "server_name!",
                    COUNT(*)::bigint as "execution_count!",
                    COUNT(*) FILTER (WHERE status = 'success')::bigint as "success_count!",
                    COALESCE(AVG(execution_time_ms)::float8, 0) as "avg_time!",
                    MAX(created_at) as "last_used!"
                FROM mcp_tool_executions
                WHERE created_at >= $1 AND created_at < $2 AND server_name ILIKE $3
                GROUP BY tool_name, server_name
                ORDER BY COUNT(*) DESC
                LIMIT $4
                "#,
                start,
                end,
                pattern,
                limit
            )
            .fetch_all(&*self.pool)
            .await
            .map_err(Into::into),
        }
    }

    async fn list_tools_unfiltered(
        &self,
        start: DateTime<Utc>,
        end: DateTime<Utc>,
        limit: i64,
        sort_order: &str,
    ) -> Result<Vec<ToolListRow>> {
        match sort_order {
            "success_rate" => sqlx::query_as!(
                ToolListRow,
                r#"
                SELECT
                    tool_name as "tool_name!",
                    server_name as "server_name!",
                    COUNT(*)::bigint as "execution_count!",
                    COUNT(*) FILTER (WHERE status = 'success')::bigint as "success_count!",
                    COALESCE(AVG(execution_time_ms)::float8, 0) as "avg_time!",
                    MAX(created_at) as "last_used!"
                FROM mcp_tool_executions
                WHERE created_at >= $1 AND created_at < $2
                GROUP BY tool_name, server_name
                ORDER BY CASE WHEN COUNT(*) > 0
                    THEN COUNT(*) FILTER (WHERE status = 'success')::float / COUNT(*)::float
                    ELSE 0 END DESC
                LIMIT $3
                "#,
                start,
                end,
                limit
            )
            .fetch_all(&*self.pool)
            .await
            .map_err(Into::into),
            "avg_time" => sqlx::query_as!(
                ToolListRow,
                r#"
                SELECT
                    tool_name as "tool_name!",
                    server_name as "server_name!",
                    COUNT(*)::bigint as "execution_count!",
                    COUNT(*) FILTER (WHERE status = 'success')::bigint as "success_count!",
                    COALESCE(AVG(execution_time_ms)::float8, 0) as "avg_time!",
                    MAX(created_at) as "last_used!"
                FROM mcp_tool_executions
                WHERE created_at >= $1 AND created_at < $2
                GROUP BY tool_name, server_name
                ORDER BY COALESCE(AVG(execution_time_ms), 0) DESC
                LIMIT $3
                "#,
                start,
                end,
                limit
            )
            .fetch_all(&*self.pool)
            .await
            .map_err(Into::into),
            _ => sqlx::query_as!(
                ToolListRow,
                r#"
                SELECT
                    tool_name as "tool_name!",
                    server_name as "server_name!",
                    COUNT(*)::bigint as "execution_count!",
                    COUNT(*) FILTER (WHERE status = 'success')::bigint as "success_count!",
                    COALESCE(AVG(execution_time_ms)::float8, 0) as "avg_time!",
                    MAX(created_at) as "last_used!"
                FROM mcp_tool_executions
                WHERE created_at >= $1 AND created_at < $2
                GROUP BY tool_name, server_name
                ORDER BY COUNT(*) DESC
                LIMIT $3
                "#,
                start,
                end,
                limit
            )
            .fetch_all(&*self.pool)
            .await
            .map_err(Into::into),
        }
    }
}