Services: Admin-Lehrer, Backend-Lehrer, Studio v2, Website, Klausur-Service, School-Service, Voice-Service, Geo-Service, BreakPilot Drive, Agent-Core Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
283 lines
7.6 KiB
TypeScript
283 lines
7.6 KiB
TypeScript
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 }
|
|
)
|
|
}
|
|
}
|