first-update
This commit is contained in:
188
easy-dataset-main/app/api/monitoring/stats/route.js
Normal file
188
easy-dataset-main/app/api/monitoring/stats/route.js
Normal file
@@ -0,0 +1,188 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { db } from '@/lib/db';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
export async function GET(request) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const timeRange = searchParams.get('timeRange') || '7d'; // 24h, 7d, 30d
|
||||
const projectId = searchParams.get('projectId');
|
||||
const provider = searchParams.get('provider');
|
||||
const status = searchParams.get('status');
|
||||
|
||||
let startDate = new Date();
|
||||
|
||||
if (timeRange === '24h') {
|
||||
startDate.setHours(startDate.getHours() - 24);
|
||||
} else if (timeRange === '30d') {
|
||||
startDate.setDate(startDate.getDate() - 30);
|
||||
} else {
|
||||
startDate.setDate(startDate.getDate() - 7);
|
||||
}
|
||||
|
||||
const where = {
|
||||
createAt: {
|
||||
gte: startDate
|
||||
}
|
||||
};
|
||||
|
||||
if (projectId && projectId !== 'all') {
|
||||
where.projectId = projectId;
|
||||
}
|
||||
if (provider && provider !== 'all') {
|
||||
where.provider = provider;
|
||||
}
|
||||
if (status && status !== 'all') {
|
||||
where.status = status;
|
||||
}
|
||||
|
||||
// 1. Fetch data for aggregation
|
||||
// Note: Prisma aggregation can be slow on very large datasets. If needed, optimize with pre-aggregated tables.
|
||||
const logs = await db.llmUsageLogs.findMany({
|
||||
where,
|
||||
select: {
|
||||
id: true,
|
||||
projectId: true,
|
||||
provider: true,
|
||||
model: true,
|
||||
inputTokens: true,
|
||||
outputTokens: true,
|
||||
totalTokens: true,
|
||||
latency: true,
|
||||
status: true,
|
||||
errorMessage: true,
|
||||
createAt: true,
|
||||
dateString: true
|
||||
},
|
||||
orderBy: {
|
||||
createAt: 'desc'
|
||||
}
|
||||
});
|
||||
|
||||
// Build project name map
|
||||
const projects = await db.projects.findMany({
|
||||
select: { id: true, name: true }
|
||||
});
|
||||
const projectMap = projects.reduce((acc, p) => {
|
||||
acc[p.id] = p.name;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// 2. Process and aggregate
|
||||
const summary = {
|
||||
totalTokens: 0,
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
totalCalls: logs.length,
|
||||
successCalls: 0,
|
||||
failedCalls: 0,
|
||||
totalLatency: 0,
|
||||
avgLatency: 0
|
||||
};
|
||||
|
||||
const trendMap = {};
|
||||
const modelStats = {};
|
||||
const detailedStatsMap = {}; // Key: projectId-model-status-errorMessage
|
||||
|
||||
logs.forEach(log => {
|
||||
// Summary
|
||||
summary.totalTokens += log.totalTokens;
|
||||
summary.inputTokens += log.inputTokens;
|
||||
summary.outputTokens += log.outputTokens;
|
||||
|
||||
if (log.status === 'SUCCESS') {
|
||||
summary.successCalls++;
|
||||
summary.totalLatency += log.latency;
|
||||
} else {
|
||||
summary.failedCalls++;
|
||||
}
|
||||
|
||||
// Trend (by day or hour)
|
||||
let timeKey;
|
||||
if (timeRange === '24h') {
|
||||
const date = new Date(log.createAt);
|
||||
timeKey = `${String(date.getHours()).padStart(2, '0')}:00`;
|
||||
} else {
|
||||
timeKey = log.dateString.slice(5); // MM-DD
|
||||
}
|
||||
|
||||
if (!trendMap[timeKey]) {
|
||||
trendMap[timeKey] = { name: timeKey, input: 0, output: 0 };
|
||||
}
|
||||
trendMap[timeKey].input += log.inputTokens;
|
||||
trendMap[timeKey].output += log.outputTokens;
|
||||
|
||||
// Model Distribution
|
||||
const modelKey = log.model;
|
||||
if (!modelStats[modelKey]) {
|
||||
modelStats[modelKey] = { name: modelKey, value: 0 };
|
||||
}
|
||||
modelStats[modelKey].value += log.totalTokens;
|
||||
|
||||
// Detailed Table Aggregation
|
||||
// Key: projectId + model + status + (errorMessage || '')
|
||||
const errorKey = log.errorMessage || '';
|
||||
const detailKey = `${log.projectId}|${log.model}|${log.status}|${errorKey}`;
|
||||
|
||||
if (!detailedStatsMap[detailKey]) {
|
||||
detailedStatsMap[detailKey] = {
|
||||
projectId: log.projectId,
|
||||
projectName: projectMap[log.projectId] || 'Unknown Project',
|
||||
provider: log.provider,
|
||||
model: log.model,
|
||||
status: log.status,
|
||||
failureReason: log.errorMessage,
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
totalTokens: 0,
|
||||
calls: 0,
|
||||
totalLatency: 0
|
||||
};
|
||||
}
|
||||
const detailItem = detailedStatsMap[detailKey];
|
||||
detailItem.inputTokens += log.inputTokens;
|
||||
detailItem.outputTokens += log.outputTokens;
|
||||
detailItem.totalTokens += log.totalTokens;
|
||||
detailItem.calls += 1;
|
||||
if (log.status === 'SUCCESS') {
|
||||
detailItem.totalLatency += log.latency;
|
||||
}
|
||||
});
|
||||
|
||||
// Calculate averages
|
||||
if (summary.successCalls > 0) {
|
||||
summary.avgLatency = Math.round(summary.totalLatency / summary.successCalls);
|
||||
}
|
||||
summary.avgTokensPerCall = summary.totalCalls > 0 ? Math.round(summary.totalTokens / summary.totalCalls) : 0;
|
||||
summary.failureRate = summary.totalCalls > 0 ? summary.failedCalls / summary.totalCalls : 0;
|
||||
|
||||
// Format chart data
|
||||
const trend = Object.values(trendMap).sort((a, b) => {
|
||||
// Simple sorting; for production use, consider stricter time ordering.
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
|
||||
const modelDistribution = Object.values(modelStats).sort((a, b) => b.value - a.value);
|
||||
|
||||
// Format detailed table data
|
||||
const details = Object.values(detailedStatsMap)
|
||||
.map(item => ({
|
||||
...item,
|
||||
avgLatency:
|
||||
item.status === 'SUCCESS' && item.calls > 0 ? (item.totalLatency / item.calls / 1000).toFixed(2) + 's' : '-'
|
||||
}))
|
||||
.sort((a, b) => b.totalTokens - a.totalTokens); // Default sorting by token usage
|
||||
|
||||
return NextResponse.json({
|
||||
summary,
|
||||
trend,
|
||||
modelDistribution,
|
||||
details,
|
||||
projects
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch monitoring stats:', error);
|
||||
return NextResponse.json({ error: error.message }, { status: 500 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user