|
| 1 | +/** |
| 2 | + * Analytics Agents API |
| 3 | + * Returns agent performance metrics |
| 4 | + */ |
| 5 | + |
| 6 | +import { NextRequest, NextResponse } from 'next/server' |
| 7 | +import { withAuth } from '@/lib/api/middleware' |
| 8 | +import { createClient } from '@/lib/supabase/server' |
| 9 | +import { logger } from '@/lib/utils/logger' |
| 10 | + |
| 11 | +const CHAT_SERVICE_URL = process.env.CHAT_SERVICE_URL || 'http://localhost:4004' |
| 12 | + |
| 13 | +export async function GET(request: NextRequest) { |
| 14 | + return withAuth(request, async (req, ctx) => { |
| 15 | + try { |
| 16 | + if (!ctx.companyId) { |
| 17 | + return NextResponse.json({ error: 'Company ID required' }, { status: 400 }) |
| 18 | + } |
| 19 | + |
| 20 | + const supabase = await createClient() |
| 21 | + const searchParams = req.nextUrl.searchParams |
| 22 | + const startDate = searchParams.get('startDate') |
| 23 | + const endDate = searchParams.get('endDate') |
| 24 | + |
| 25 | + // Get agents from Supabase |
| 26 | + const { data: agents, error: agentsError } = await supabase |
| 27 | + .from('agent_configs') |
| 28 | + .select('id, name, enabled') |
| 29 | + .eq('company_id', ctx.companyId) |
| 30 | + .eq('enabled', true) |
| 31 | + |
| 32 | + if (agentsError) { |
| 33 | + logger.warn('Failed to fetch agents', { error: agentsError }) |
| 34 | + } |
| 35 | + |
| 36 | + if (!agents || agents.length === 0) { |
| 37 | + return NextResponse.json({ agents: [] }) |
| 38 | + } |
| 39 | + |
| 40 | + // Get access token for Chat Service |
| 41 | + const { |
| 42 | + data: { session }, |
| 43 | + } = await supabase.auth.getSession() |
| 44 | + |
| 45 | + if (!session?.access_token) { |
| 46 | + return NextResponse.json({ error: 'No session token' }, { status: 401 }) |
| 47 | + } |
| 48 | + |
| 49 | + // Fetch conversations from Chat Service |
| 50 | + const conversationsResponse = await fetch( |
| 51 | + `${CHAT_SERVICE_URL}/api/conversations?limit=1000`, |
| 52 | + { |
| 53 | + headers: { |
| 54 | + Authorization: `Bearer ${session.access_token}`, |
| 55 | + }, |
| 56 | + } |
| 57 | + ) |
| 58 | + |
| 59 | + if (!conversationsResponse.ok) { |
| 60 | + throw new Error('Failed to fetch conversations') |
| 61 | + } |
| 62 | + |
| 63 | + const conversationsData = await conversationsResponse.json() |
| 64 | + let conversations = conversationsData.conversations || [] |
| 65 | + |
| 66 | + // Filter by date range if provided |
| 67 | + if (startDate || endDate) { |
| 68 | + conversations = conversations.filter((conv: { started_at: string }) => { |
| 69 | + const convDate = new Date(conv.started_at) |
| 70 | + if (startDate && convDate < new Date(startDate)) return false |
| 71 | + if (endDate && convDate > new Date(endDate)) return false |
| 72 | + return true |
| 73 | + }) |
| 74 | + } |
| 75 | + |
| 76 | + // Fetch messages for response time and satisfaction |
| 77 | + const messagesResponse = await fetch( |
| 78 | + `${CHAT_SERVICE_URL}/api/internal/messages/list?limit=1000`, |
| 79 | + { |
| 80 | + headers: { |
| 81 | + Authorization: `Bearer ${process.env.INTERNAL_SERVICE_TOKEN || ''}`, |
| 82 | + 'Content-Type': 'application/json', |
| 83 | + }, |
| 84 | + method: 'POST', |
| 85 | + body: JSON.stringify({ |
| 86 | + companyId: ctx.companyId, |
| 87 | + limit: 1000, |
| 88 | + startDate, |
| 89 | + endDate, |
| 90 | + }), |
| 91 | + } |
| 92 | + ) |
| 93 | + |
| 94 | + const messages = messagesResponse.ok |
| 95 | + ? (await messagesResponse.json()).messages || [] |
| 96 | + : [] |
| 97 | + |
| 98 | + // Calculate metrics per agent |
| 99 | + const agentMetrics = agents.map((agent) => { |
| 100 | + const agentConversations = conversations.filter( |
| 101 | + (c: { agent_id: string }) => c.agent_id === agent.id |
| 102 | + ) |
| 103 | + |
| 104 | + const agentMessages = messages.filter( |
| 105 | + (m: { conversation_id: string; sender_type: string; ai_metadata?: { response_time_ms?: number }; metadata?: { sentiment?: { sentiment: string } } }) => { |
| 106 | + const conv = conversations.find((c: { _id: string; agent_id: string }) => |
| 107 | + String(c._id) === m.conversation_id && c.agent_id === agent.id |
| 108 | + ) |
| 109 | + return conv && m.sender_type === 'agent' |
| 110 | + } |
| 111 | + ) |
| 112 | + |
| 113 | + // Calculate average response time |
| 114 | + const messagesWithResponseTime = agentMessages.filter( |
| 115 | + (m: { ai_metadata?: { response_time_ms?: number } }) => m.ai_metadata?.response_time_ms |
| 116 | + ) |
| 117 | + const avgResponseTime = |
| 118 | + messagesWithResponseTime.length > 0 |
| 119 | + ? Math.round( |
| 120 | + messagesWithResponseTime.reduce( |
| 121 | + (sum: number, m: { ai_metadata: { response_time_ms: number } }) => |
| 122 | + sum + (m.ai_metadata.response_time_ms || 0), |
| 123 | + 0 |
| 124 | + ) / messagesWithResponseTime.length |
| 125 | + ) |
| 126 | + : 0 |
| 127 | + |
| 128 | + // Calculate satisfaction |
| 129 | + const messagesWithSentiment = agentMessages.filter( |
| 130 | + (m: { metadata?: { sentiment?: { sentiment: string } } }) => |
| 131 | + m.metadata?.sentiment?.sentiment |
| 132 | + ) |
| 133 | + const satisfaction = |
| 134 | + messagesWithSentiment.length > 0 |
| 135 | + ? Math.round( |
| 136 | + (messagesWithSentiment.filter( |
| 137 | + (m: { metadata: { sentiment: { sentiment: string } } }) => |
| 138 | + m.metadata.sentiment.sentiment === 'positive' |
| 139 | + ).length / |
| 140 | + messagesWithSentiment.length) * |
| 141 | + 100 |
| 142 | + ) |
| 143 | + : 0 |
| 144 | + |
| 145 | + return { |
| 146 | + agentId: agent.id, |
| 147 | + agentName: agent.name || 'Unnamed Agent', |
| 148 | + conversationCount: agentConversations.length, |
| 149 | + avgResponseTime, |
| 150 | + satisfaction, |
| 151 | + } |
| 152 | + }) |
| 153 | + |
| 154 | + // Sort by conversation count (top performers first) |
| 155 | + agentMetrics.sort((a, b) => b.conversationCount - a.conversationCount) |
| 156 | + |
| 157 | + return NextResponse.json({ agents: agentMetrics }) |
| 158 | + } catch (error) { |
| 159 | + logger.error('Analytics agents error', { error }) |
| 160 | + return NextResponse.json( |
| 161 | + { error: 'Failed to fetch agent analytics' }, |
| 162 | + { status: 500 } |
| 163 | + ) |
| 164 | + } |
| 165 | + }, { requireCompany: true }) |
| 166 | +} |
| 167 | + |
0 commit comments