fix(admin-v2): Restore complete admin-v2 application

The admin-v2 application was incomplete in the repository. This commit
restores all missing components:

- Admin pages (76 pages): dashboard, ai, compliance, dsgvo, education,
  infrastructure, communication, development, onboarding, rbac
- SDK pages (45 pages): tom, dsfa, vvt, loeschfristen, einwilligungen,
  vendor-compliance, tom-generator, dsr, and more
- Developer portal (25 pages): API docs, SDK guides, frameworks
- All components, lib files, hooks, and types
- Updated package.json with all dependencies

The issue was caused by incomplete initial repository state - the full
admin-v2 codebase existed in backend/admin-v2 and docs-src/admin-v2
but was never fully synced to the main admin-v2 directory.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
BreakPilot Dev
2026-02-08 23:40:15 -08:00
parent f28244753f
commit 660295e218
385 changed files with 138126 additions and 3079 deletions

View File

@@ -0,0 +1,275 @@
import { NextRequest, NextResponse } from 'next/server'
import * as fs from 'fs/promises'
import * as path from 'path'
/**
* Individual Agent API
*
* GET - Get agent details including SOUL content
* PUT - Update agent configuration
* DELETE - Delete agent
*/
const AGENT_CORE_PATH = process.env.AGENT_CORE_PATH || '/app/agent-core'
const SOUL_PATH = path.join(AGENT_CORE_PATH, 'soul')
interface AgentDetails {
id: string
name: string
description: string
soulFile: string
soulContent: string
color: string
icon: string
status: 'running' | 'paused' | 'stopped' | 'error'
activeSessions: number
totalProcessed: number
avgResponseTime: number
errorRate: number
lastActivity: string
version: string
createdAt: string
updatedAt: string
}
// Agent metadata (in production, store in database)
const agentMetadata: Record<string, Partial<AgentDetails>> = {
'tutor-agent': {
name: 'TutorAgent',
description: 'Lernbegleitung und Fragen beantworten',
color: '#3b82f6',
icon: 'brain'
},
'grader-agent': {
name: 'GraderAgent',
description: 'Klausur-Korrektur und Bewertung',
color: '#10b981',
icon: 'bot'
},
'quality-judge': {
name: 'QualityJudge',
description: 'BQAS Qualitaetspruefung',
color: '#f59e0b',
icon: 'settings'
},
'alert-agent': {
name: 'AlertAgent',
description: 'Monitoring und Benachrichtigungen',
color: '#ef4444',
icon: 'alert'
},
'orchestrator': {
name: 'Orchestrator',
description: 'Task-Koordination und Routing',
color: '#8b5cf6',
icon: 'message'
}
}
// Read SOUL file content
async function readSoulFile(agentId: string): Promise<string | null> {
const soulFile = `${agentId}.soul.md`
const soulFilePath = path.join(SOUL_PATH, soulFile)
try {
const content = await fs.readFile(soulFilePath, 'utf-8')
return content
} catch {
return null
}
}
// Get file stats
async function getFileStats(agentId: string): Promise<{ createdAt: string; updatedAt: string } | null> {
const soulFile = `${agentId}.soul.md`
const soulFilePath = path.join(SOUL_PATH, soulFile)
try {
const stats = await fs.stat(soulFilePath)
return {
createdAt: stats.birthtime.toISOString(),
updatedAt: stats.mtime.toISOString()
}
} catch {
return null
}
}
// Mock agent stats (in production, query from Redis/PostgreSQL)
function getMockStats(agentId: string) {
const mockStats: Record<string, { activeSessions: number; totalProcessed: number; avgResponseTime: number; errorRate: number }> = {
'tutor-agent': { activeSessions: 12, totalProcessed: 1847, avgResponseTime: 234, errorRate: 0.3 },
'grader-agent': { activeSessions: 3, totalProcessed: 456, avgResponseTime: 1205, errorRate: 0.5 },
'quality-judge': { activeSessions: 8, totalProcessed: 3291, avgResponseTime: 89, errorRate: 0.1 },
'alert-agent': { activeSessions: 1, totalProcessed: 892, avgResponseTime: 45, errorRate: 0.0 },
'orchestrator': { activeSessions: 24, totalProcessed: 8934, avgResponseTime: 12, errorRate: 0.2 }
}
return mockStats[agentId] || { activeSessions: 0, totalProcessed: 0, avgResponseTime: 0, errorRate: 0 }
}
// GET - Get agent details
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ agentId: string }> }
) {
try {
const { agentId } = await params
const soulContent = await readSoulFile(agentId)
if (!soulContent) {
return NextResponse.json(
{ error: 'Agent not found' },
{ status: 404 }
)
}
const fileStats = await getFileStats(agentId)
const metadata = agentMetadata[agentId] || {}
const stats = getMockStats(agentId)
const agent: AgentDetails = {
id: agentId,
name: metadata.name || agentId.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(''),
description: metadata.description || 'Custom agent',
soulFile: `${agentId}.soul.md`,
soulContent,
color: metadata.color || '#6b7280',
icon: metadata.icon || 'bot',
status: 'running',
...stats,
lastActivity: 'just now',
version: '1.0.0',
createdAt: fileStats?.createdAt || new Date().toISOString(),
updatedAt: fileStats?.updatedAt || new Date().toISOString()
}
return NextResponse.json(agent)
} catch (error) {
console.error('Error fetching agent:', error)
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Failed to fetch agent' },
{ status: 500 }
)
}
}
// PUT - Update agent
export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ agentId: string }> }
) {
try {
const { agentId } = await params
const body = await request.json()
const { soulContent, name, description, color, icon } = body
const soulFile = `${agentId}.soul.md`
const soulFilePath = path.join(SOUL_PATH, soulFile)
// Check if agent exists
try {
await fs.access(soulFilePath)
} catch {
return NextResponse.json(
{ error: 'Agent not found' },
{ status: 404 }
)
}
// Update SOUL file if content provided
if (soulContent) {
// Create backup before updating
const backupPath = path.join(SOUL_PATH, '.backups', `${agentId}-${Date.now()}.soul.md`)
try {
await fs.mkdir(path.dirname(backupPath), { recursive: true })
const currentContent = await fs.readFile(soulFilePath, 'utf-8')
await fs.writeFile(backupPath, currentContent, 'utf-8')
} catch {
// Backup failed, continue anyway
}
// Write new content
await fs.writeFile(soulFilePath, soulContent, 'utf-8')
}
// In production, update metadata in database
// For now, just return success
return NextResponse.json({
success: true,
message: `Agent ${agentId} updated successfully`,
agent: {
id: agentId,
name: name || agentMetadata[agentId]?.name || agentId,
description: description || agentMetadata[agentId]?.description || '',
soulFile,
color: color || agentMetadata[agentId]?.color || '#6b7280',
icon: icon || agentMetadata[agentId]?.icon || 'bot',
updatedAt: new Date().toISOString()
}
})
} catch (error) {
console.error('Error updating agent:', error)
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Failed to update agent' },
{ status: 500 }
)
}
}
// DELETE - Delete agent
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ agentId: string }> }
) {
try {
const { agentId } = await params
// Don't allow deleting core agents
const coreAgents = ['tutor-agent', 'grader-agent', 'quality-judge', 'alert-agent', 'orchestrator']
if (coreAgents.includes(agentId)) {
return NextResponse.json(
{ error: 'Cannot delete core system agent' },
{ status: 403 }
)
}
const soulFile = `${agentId}.soul.md`
const soulFilePath = path.join(SOUL_PATH, soulFile)
// Check if agent exists
try {
await fs.access(soulFilePath)
} catch {
return NextResponse.json(
{ error: 'Agent not found' },
{ status: 404 }
)
}
// Create backup before deleting
const backupPath = path.join(SOUL_PATH, '.deleted', `${agentId}-${Date.now()}.soul.md`)
try {
await fs.mkdir(path.dirname(backupPath), { recursive: true })
const content = await fs.readFile(soulFilePath, 'utf-8')
await fs.writeFile(backupPath, content, 'utf-8')
} catch {
// Backup failed, continue anyway
}
// Delete the file
await fs.unlink(soulFilePath)
return NextResponse.json({
success: true,
message: `Agent ${agentId} deleted successfully`
})
} catch (error) {
console.error('Error deleting agent:', error)
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Failed to delete agent' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,187 @@
import { NextRequest, NextResponse } from 'next/server'
import * as fs from 'fs/promises'
import * as path from 'path'
/**
* Agent SOUL File API
*
* GET - Get SOUL file content
* PUT - Update SOUL file content
* GET /history - Get version history
*/
const AGENT_CORE_PATH = process.env.AGENT_CORE_PATH || '/app/agent-core'
const SOUL_PATH = path.join(AGENT_CORE_PATH, 'soul')
interface SoulVersion {
version: string
timestamp: string
content: string
author: string
changes: string
}
// Read SOUL file content
async function readSoulFile(agentId: string): Promise<string | null> {
const soulFile = `${agentId}.soul.md`
const soulFilePath = path.join(SOUL_PATH, soulFile)
try {
const content = await fs.readFile(soulFilePath, 'utf-8')
return content
} catch {
return null
}
}
// Get version history from backup directory
async function getVersionHistory(agentId: string): Promise<SoulVersion[]> {
const backupDir = path.join(SOUL_PATH, '.backups')
const versions: SoulVersion[] = []
try {
const files = await fs.readdir(backupDir)
const agentBackups = files.filter(f => f.startsWith(`${agentId}-`) && f.endsWith('.soul.md'))
for (const file of agentBackups.slice(-10)) { // Last 10 versions
const filePath = path.join(backupDir, file)
const stats = await fs.stat(filePath)
const content = await fs.readFile(filePath, 'utf-8')
// Extract timestamp from filename
const match = file.match(/-(\d+)\.soul\.md$/)
const timestamp = match ? new Date(parseInt(match[1])).toISOString() : stats.mtime.toISOString()
versions.push({
version: file.replace('.soul.md', ''),
timestamp,
content,
author: 'Admin',
changes: 'Manual update'
})
}
// Sort by timestamp descending
versions.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())
} catch {
// No backup directory or can't read
}
return versions
}
// GET - Get SOUL file content
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ agentId: string }> }
) {
try {
const { agentId } = await params
const url = new URL(request.url)
const includeHistory = url.searchParams.get('history') === 'true'
const content = await readSoulFile(agentId)
if (!content) {
return NextResponse.json(
{ error: 'SOUL file not found' },
{ status: 404 }
)
}
const soulFile = `${agentId}.soul.md`
const soulFilePath = path.join(SOUL_PATH, soulFile)
const stats = await fs.stat(soulFilePath)
const response: {
agentId: string
soulFile: string
content: string
updatedAt: string
size: number
history?: SoulVersion[]
} = {
agentId,
soulFile,
content,
updatedAt: stats.mtime.toISOString(),
size: stats.size
}
if (includeHistory) {
response.history = await getVersionHistory(agentId)
}
return NextResponse.json(response)
} catch (error) {
console.error('Error fetching SOUL file:', error)
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Failed to fetch SOUL file' },
{ status: 500 }
)
}
}
// PUT - Update SOUL file content
export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ agentId: string }> }
) {
try {
const { agentId } = await params
const body = await request.json()
const { content, author, changeDescription } = body
if (!content) {
return NextResponse.json(
{ error: 'content is required' },
{ status: 400 }
)
}
const soulFile = `${agentId}.soul.md`
const soulFilePath = path.join(SOUL_PATH, soulFile)
// Check if file exists
try {
await fs.access(soulFilePath)
} catch {
return NextResponse.json(
{ error: 'SOUL file not found' },
{ status: 404 }
)
}
// Create backup before updating
const backupDir = path.join(SOUL_PATH, '.backups')
const backupPath = path.join(backupDir, `${agentId}-${Date.now()}.soul.md`)
try {
await fs.mkdir(backupDir, { recursive: true })
const currentContent = await fs.readFile(soulFilePath, 'utf-8')
await fs.writeFile(backupPath, currentContent, 'utf-8')
} catch (backupError) {
console.warn('Failed to create backup:', backupError)
}
// Write new content
await fs.writeFile(soulFilePath, content, 'utf-8')
const stats = await fs.stat(soulFilePath)
return NextResponse.json({
success: true,
message: `SOUL file for ${agentId} updated successfully`,
agentId,
soulFile,
updatedAt: stats.mtime.toISOString(),
size: stats.size,
author: author || 'Admin',
changeDescription: changeDescription || 'Manual update'
})
} catch (error) {
console.error('Error updating SOUL file:', error)
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Failed to update SOUL file' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,282 @@
import { NextRequest, NextResponse } from 'next/server'
import * as fs from 'fs/promises'
import * as path from 'path'
/**
* Agent Management API
*
* GET - List all agents with their status and configuration
* POST - Create a new agent (with SOUL file)
*/
const AGENT_CORE_PATH = process.env.AGENT_CORE_PATH || '/app/agent-core'
const SOUL_PATH = path.join(AGENT_CORE_PATH, 'soul')
interface AgentConfig {
id: string
name: string
description: string
soulFile: string
color: string
icon: string
status: 'running' | 'paused' | 'stopped' | 'error'
activeSessions: number
totalProcessed: number
avgResponseTime: number
lastActivity: string
version: string
}
interface AgentStats {
totalSessions: number
activeSessions: number
totalMessages: number
avgLatency: number
errorRate: number
memoryUsage: number
}
// Default agent configurations
const defaultAgents: AgentConfig[] = [
{
id: 'tutor-agent',
name: 'TutorAgent',
description: 'Lernbegleitung und Fragen beantworten',
soulFile: 'tutor-agent.soul.md',
color: '#3b82f6',
icon: 'brain',
status: 'running',
activeSessions: 0,
totalProcessed: 0,
avgResponseTime: 0,
lastActivity: new Date().toISOString(),
version: '1.0.0'
},
{
id: 'grader-agent',
name: 'GraderAgent',
description: 'Klausur-Korrektur und Bewertung',
soulFile: 'grader-agent.soul.md',
color: '#10b981',
icon: 'bot',
status: 'running',
activeSessions: 0,
totalProcessed: 0,
avgResponseTime: 0,
lastActivity: new Date().toISOString(),
version: '1.0.0'
},
{
id: 'quality-judge',
name: 'QualityJudge',
description: 'BQAS Qualitaetspruefung',
soulFile: 'quality-judge.soul.md',
color: '#f59e0b',
icon: 'settings',
status: 'running',
activeSessions: 0,
totalProcessed: 0,
avgResponseTime: 0,
lastActivity: new Date().toISOString(),
version: '1.0.0'
},
{
id: 'alert-agent',
name: 'AlertAgent',
description: 'Monitoring und Benachrichtigungen',
soulFile: 'alert-agent.soul.md',
color: '#ef4444',
icon: 'alert',
status: 'running',
activeSessions: 0,
totalProcessed: 0,
avgResponseTime: 0,
lastActivity: new Date().toISOString(),
version: '1.0.0'
},
{
id: 'orchestrator',
name: 'Orchestrator',
description: 'Task-Koordination und Routing',
soulFile: 'orchestrator.soul.md',
color: '#8b5cf6',
icon: 'message',
status: 'running',
activeSessions: 0,
totalProcessed: 0,
avgResponseTime: 0,
lastActivity: new Date().toISOString(),
version: '1.0.0'
}
]
// Check if SOUL file exists
async function checkSoulFile(filename: string): Promise<boolean> {
try {
await fs.access(path.join(SOUL_PATH, filename))
return true
} catch {
return false
}
}
// Get agent status from Redis/API (mock for now)
async function getAgentStatus(agentId: string): Promise<{
status: 'running' | 'paused' | 'stopped' | 'error'
activeSessions: number
totalProcessed: number
avgResponseTime: number
lastActivity: string
}> {
// In production, query Redis/PostgreSQL for real stats
// For now, return mock data with some variation
const mockStats = {
'tutor-agent': { activeSessions: 12, totalProcessed: 1847, avgResponseTime: 234, lastActivity: '2 min ago' },
'grader-agent': { activeSessions: 3, totalProcessed: 456, avgResponseTime: 1205, lastActivity: '5 min ago' },
'quality-judge': { activeSessions: 8, totalProcessed: 3291, avgResponseTime: 89, lastActivity: '1 min ago' },
'alert-agent': { activeSessions: 1, totalProcessed: 892, avgResponseTime: 45, lastActivity: '30 sec ago' },
'orchestrator': { activeSessions: 24, totalProcessed: 8934, avgResponseTime: 12, lastActivity: 'just now' }
}
const stats = mockStats[agentId as keyof typeof mockStats] || {
activeSessions: 0,
totalProcessed: 0,
avgResponseTime: 0,
lastActivity: 'never'
}
return {
status: 'running',
...stats
}
}
// GET - List all agents
export async function GET() {
try {
const agents: AgentConfig[] = []
for (const defaultAgent of defaultAgents) {
const soulExists = await checkSoulFile(defaultAgent.soulFile)
const status = await getAgentStatus(defaultAgent.id)
agents.push({
...defaultAgent,
...status,
status: soulExists ? status.status : 'error'
})
}
// Also check for any additional SOUL files
try {
const soulFiles = await fs.readdir(SOUL_PATH)
for (const file of soulFiles) {
if (file.endsWith('.soul.md')) {
const agentId = file.replace('.soul.md', '')
if (!agents.find(a => a.id === agentId)) {
const status = await getAgentStatus(agentId)
agents.push({
id: agentId,
name: agentId.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(''),
description: 'Custom agent',
soulFile: file,
color: '#6b7280',
icon: 'bot',
version: '1.0.0',
...status
})
}
}
}
} catch {
// SOUL directory doesn't exist or isn't accessible
}
// Calculate aggregate stats
const stats: AgentStats = {
totalSessions: agents.reduce((sum, a) => sum + a.totalProcessed, 0),
activeSessions: agents.reduce((sum, a) => sum + a.activeSessions, 0),
totalMessages: agents.reduce((sum, a) => sum + a.totalProcessed, 0) * 3, // estimate
avgLatency: Math.round(agents.reduce((sum, a) => sum + a.avgResponseTime, 0) / agents.length),
errorRate: 0.8, // mock
memoryUsage: 67 // mock
}
return NextResponse.json({
agents,
stats,
timestamp: new Date().toISOString()
})
} catch (error) {
console.error('Error fetching agents:', error)
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Failed to fetch agents' },
{ status: 500 }
)
}
}
// POST - Create new agent
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { id, name, description, soulContent, color, icon } = body
if (!id || !name || !soulContent) {
return NextResponse.json(
{ error: 'id, name, and soulContent are required' },
{ status: 400 }
)
}
// Validate agent ID format
if (!/^[a-z0-9-]+$/.test(id)) {
return NextResponse.json(
{ error: 'Agent ID must contain only lowercase letters, numbers, and hyphens' },
{ status: 400 }
)
}
const soulFile = `${id}.soul.md`
const soulFilePath = path.join(SOUL_PATH, soulFile)
// Check if agent already exists
const exists = await checkSoulFile(soulFile)
if (exists) {
return NextResponse.json(
{ error: 'Agent with this ID already exists' },
{ status: 409 }
)
}
// Create SOUL file
await fs.writeFile(soulFilePath, soulContent, 'utf-8')
const newAgent: AgentConfig = {
id,
name,
description: description || '',
soulFile,
color: color || '#6b7280',
icon: icon || 'bot',
status: 'stopped',
activeSessions: 0,
totalProcessed: 0,
avgResponseTime: 0,
lastActivity: new Date().toISOString(),
version: '1.0.0'
}
return NextResponse.json({
success: true,
agent: newAgent,
message: `Agent ${name} created successfully`
}, { status: 201 })
} catch (error) {
console.error('Error creating agent:', error)
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Failed to create agent' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,240 @@
import { NextRequest, NextResponse } from 'next/server'
/**
* Agent Sessions API
*
* GET - List all active agent sessions
* POST - Create/start a new session (for testing)
*/
interface AgentSession {
id: string
agentType: string
agentId: string
userId: string
userName: string
state: 'active' | 'paused' | 'completed' | 'failed'
createdAt: string
lastActivity: string
checkpointCount: number
messagesProcessed: number
currentTask: string | null
avgResponseTime: number
}
// Mock sessions data (in production, query from PostgreSQL/Redis)
function getMockSessions(): AgentSession[] {
const now = new Date()
return [
{
id: `session-${Date.now()}-001`,
agentType: 'tutor-agent',
agentId: 'tutor-1',
userId: 'user-123',
userName: 'Max Mustermann',
state: 'active',
createdAt: new Date(now.getTime() - 75 * 60000).toISOString(),
lastActivity: new Date(now.getTime() - 37000).toISOString(),
checkpointCount: 5,
messagesProcessed: 23,
currentTask: 'Erklaere Quadratische Funktionen',
avgResponseTime: 245
},
{
id: `session-${Date.now()}-002`,
agentType: 'tutor-agent',
agentId: 'tutor-2',
userId: 'user-456',
userName: 'Anna Schmidt',
state: 'active',
createdAt: new Date(now.getTime() - 45 * 60000).toISOString(),
lastActivity: new Date(now.getTime() - 108000).toISOString(),
checkpointCount: 3,
messagesProcessed: 12,
currentTask: 'Hilfe bei Gedichtanalyse',
avgResponseTime: 312
},
{
id: `session-${Date.now()}-003`,
agentType: 'grader-agent',
agentId: 'grader-1',
userId: 'user-789',
userName: 'Frau Mueller (Lehrerin)',
state: 'active',
createdAt: new Date(now.getTime() - 105 * 60000).toISOString(),
lastActivity: new Date(now.getTime() - 180000).toISOString(),
checkpointCount: 12,
messagesProcessed: 45,
currentTask: 'Korrektur Klausur 10b - Arbeit 7/24',
avgResponseTime: 1205
},
{
id: `session-${Date.now()}-004`,
agentType: 'quality-judge',
agentId: 'judge-1',
userId: 'system',
userName: 'System (BQAS)',
state: 'active',
createdAt: new Date(now.getTime() - 465 * 60000).toISOString(),
lastActivity: new Date(now.getTime() - 1000).toISOString(),
checkpointCount: 156,
messagesProcessed: 892,
currentTask: 'Quality Check Queue Processing',
avgResponseTime: 89
},
{
id: `session-${Date.now()}-005`,
agentType: 'orchestrator',
agentId: 'orchestrator-main',
userId: 'system',
userName: 'System',
state: 'active',
createdAt: new Date(now.getTime() - 945 * 60000).toISOString(),
lastActivity: now.toISOString(),
checkpointCount: 2341,
messagesProcessed: 8934,
currentTask: 'Routing incoming requests',
avgResponseTime: 12
},
{
id: `session-${Date.now()}-006`,
agentType: 'tutor-agent',
agentId: 'tutor-3',
userId: 'user-101',
userName: 'Tim Berger',
state: 'paused',
createdAt: new Date(now.getTime() - 150 * 60000).toISOString(),
lastActivity: new Date(now.getTime() - 90 * 60000).toISOString(),
checkpointCount: 8,
messagesProcessed: 34,
currentTask: null,
avgResponseTime: 278
},
{
id: `session-${Date.now()}-007`,
agentType: 'grader-agent',
agentId: 'grader-2',
userId: 'user-202',
userName: 'Herr Weber (Lehrer)',
state: 'completed',
createdAt: new Date(now.getTime() - 345 * 60000).toISOString(),
lastActivity: new Date(now.getTime() - 225 * 60000).toISOString(),
checkpointCount: 24,
messagesProcessed: 120,
currentTask: null,
avgResponseTime: 1102
},
{
id: `session-${Date.now()}-008`,
agentType: 'alert-agent',
agentId: 'alert-1',
userId: 'system',
userName: 'System (Monitoring)',
state: 'active',
createdAt: new Date(now.getTime() - 945 * 60000).toISOString(),
lastActivity: new Date(now.getTime() - 2000).toISOString(),
checkpointCount: 48,
messagesProcessed: 256,
currentTask: 'Monitoring System Health',
avgResponseTime: 45
}
]
}
// GET - List all sessions
export async function GET(request: NextRequest) {
try {
const url = new URL(request.url)
const state = url.searchParams.get('state')
const agentType = url.searchParams.get('agentType')
const limit = parseInt(url.searchParams.get('limit') || '100')
const offset = parseInt(url.searchParams.get('offset') || '0')
let sessions = getMockSessions()
// Apply filters
if (state) {
sessions = sessions.filter(s => s.state === state)
}
if (agentType) {
sessions = sessions.filter(s => s.agentType === agentType)
}
// Calculate stats
const stats = {
total: sessions.length,
active: sessions.filter(s => s.state === 'active').length,
paused: sessions.filter(s => s.state === 'paused').length,
completed: sessions.filter(s => s.state === 'completed').length,
failed: sessions.filter(s => s.state === 'failed').length,
totalMessages: sessions.reduce((sum, s) => sum + s.messagesProcessed, 0),
avgResponseTime: Math.round(
sessions.reduce((sum, s) => sum + s.avgResponseTime, 0) / sessions.length
)
}
// Apply pagination
const paginatedSessions = sessions.slice(offset, offset + limit)
return NextResponse.json({
sessions: paginatedSessions,
stats,
pagination: {
total: sessions.length,
limit,
offset,
hasMore: offset + limit < sessions.length
},
timestamp: new Date().toISOString()
})
} catch (error) {
console.error('Error fetching sessions:', error)
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Failed to fetch sessions' },
{ status: 500 }
)
}
}
// POST - Create new session (for testing)
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { agentType, userId, userName, context } = body
if (!agentType || !userId) {
return NextResponse.json(
{ error: 'agentType and userId are required' },
{ status: 400 }
)
}
// In production, create session via agent-core SessionManager
const newSession: AgentSession = {
id: `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
agentType,
agentId: `${agentType.replace('-agent', '')}-${Math.floor(Math.random() * 100)}`,
userId,
userName: userName || userId,
state: 'active',
createdAt: new Date().toISOString(),
lastActivity: new Date().toISOString(),
checkpointCount: 0,
messagesProcessed: 0,
currentTask: context?.task || null,
avgResponseTime: 0
}
return NextResponse.json({
success: true,
session: newSession,
message: 'Session created successfully'
}, { status: 201 })
} catch (error) {
console.error('Error creating session:', error)
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Failed to create session' },
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,208 @@
import { NextRequest, NextResponse } from 'next/server'
/**
* Agent Statistics API
*
* GET - Get aggregated statistics for all agents
*/
interface AgentMetric {
agentType: string
name: string
color: string
sessions: number
messagesProcessed: number
avgResponseTime: number
errorRate: number
successRate: number
trend: 'up' | 'down' | 'stable'
trendValue: number
}
interface DailyStats {
date: string
sessions: number
messages: number
errors: number
avgLatency: number
}
interface HourlyLatency {
timestamp: string
value: number
}
// Mock agent metrics
function getAgentMetrics(): AgentMetric[] {
return [
{
agentType: 'tutor-agent',
name: 'TutorAgent',
color: '#3b82f6',
sessions: 156,
messagesProcessed: 4521,
avgResponseTime: 234,
errorRate: 0.3,
successRate: 99.7,
trend: 'up',
trendValue: 12
},
{
agentType: 'grader-agent',
name: 'GraderAgent',
color: '#10b981',
sessions: 45,
messagesProcessed: 1205,
avgResponseTime: 1102,
errorRate: 0.5,
successRate: 99.5,
trend: 'stable',
trendValue: 2
},
{
agentType: 'quality-judge',
name: 'QualityJudge',
color: '#f59e0b',
sessions: 89,
messagesProcessed: 8934,
avgResponseTime: 89,
errorRate: 0.1,
successRate: 99.9,
trend: 'up',
trendValue: 8
},
{
agentType: 'alert-agent',
name: 'AlertAgent',
color: '#ef4444',
sessions: 12,
messagesProcessed: 892,
avgResponseTime: 45,
errorRate: 0.0,
successRate: 100,
trend: 'stable',
trendValue: 0
},
{
agentType: 'orchestrator',
name: 'Orchestrator',
color: '#8b5cf6',
sessions: 234,
messagesProcessed: 15420,
avgResponseTime: 12,
errorRate: 0.2,
successRate: 99.8,
trend: 'up',
trendValue: 15
}
]
}
// Generate mock daily stats for the last N days
function getDailyStats(days: number): DailyStats[] {
const stats: DailyStats[] = []
const now = new Date()
for (let i = days - 1; i >= 0; i--) {
const date = new Date(now)
date.setDate(date.getDate() - i)
stats.push({
date: date.toISOString().split('T')[0],
sessions: 400 + Math.floor(Math.random() * 150),
messages: 11000 + Math.floor(Math.random() * 5000),
errors: 8 + Math.floor(Math.random() * 12),
avgLatency: 140 + Math.floor(Math.random() * 25)
})
}
return stats
}
// Generate hourly latency data for the last 24 hours
function getHourlyLatency(): HourlyLatency[] {
const data: HourlyLatency[] = []
for (let i = 0; i < 24; i++) {
data.push({
timestamp: `${i.toString().padStart(2, '0')}:00`,
value: 100 + Math.floor(Math.random() * 100)
})
}
return data
}
// GET - Get statistics
export async function GET(request: NextRequest) {
try {
const url = new URL(request.url)
const timeRange = url.searchParams.get('range') || '7d'
// Determine days based on time range
const days = timeRange === '24h' ? 1 : timeRange === '7d' ? 7 : 30
const agentMetrics = getAgentMetrics()
const dailyStats = getDailyStats(days)
const hourlyLatency = getHourlyLatency()
// Calculate totals
const totals = {
sessions: agentMetrics.reduce((sum, m) => sum + m.sessions, 0),
messages: agentMetrics.reduce((sum, m) => sum + m.messagesProcessed, 0),
avgLatency: Math.round(
agentMetrics.reduce((sum, m) => sum + m.avgResponseTime, 0) / agentMetrics.length
),
avgErrorRate: parseFloat(
(agentMetrics.reduce((sum, m) => sum + m.errorRate, 0) / agentMetrics.length).toFixed(2)
)
}
// Calculate week totals from daily stats
const weekTotals = {
sessions: dailyStats.reduce((sum, d) => sum + d.sessions, 0),
messages: dailyStats.reduce((sum, d) => sum + d.messages, 0),
errors: dailyStats.reduce((sum, d) => sum + d.errors, 0),
avgLatency: Math.round(
dailyStats.reduce((sum, d) => sum + d.avgLatency, 0) / dailyStats.length
)
}
// Calculate trends
const trends = {
sessions: {
value: 12,
direction: 'up' as const
},
messages: {
value: 8,
direction: 'up' as const
},
latency: {
value: 5,
direction: 'down' as const // down is good for latency
},
errors: {
value: 3,
direction: 'up' as const
}
}
return NextResponse.json({
agentMetrics,
dailyStats,
hourlyLatency,
totals,
weekTotals,
trends,
timeRange,
timestamp: new Date().toISOString()
})
} catch (error) {
console.error('Error fetching statistics:', error)
return NextResponse.json(
{ error: error instanceof Error ? error.message : 'Failed to fetch statistics' },
{ status: 500 }
)
}
}