AgentEvalTool/backend/agenteval/intelligent_eval/metrics.py
sinohqb 2e7d419f05 feat(intelligent-eval): implement monitoring and alerting (ticket 07)
- Add metrics.py with pool utilization, task backlog, stuck rate, avg processing time, eval completion rate
- Add alerts.py with alert rules (pool utilization > 90%, task backlog > 50, stuck rate > 10%)
- Implement alert history and webhook notifications
- Add metrics and alerts APIs
- Add database migration for alert history table
- Add 11 unit tests for metrics, 10 unit tests for alerts, 8 integration tests
- Update migration tests to include new alert history table

All 853 tests passing.
2026-08-12 10:41:15 +08:00

119 lines
3.2 KiB
Python

"""Metrics calculation for cron pool monitoring (监控指标).
Calculates:
- Pool utilization (busy/total)
- Task backlog (pending tasks count)
- Stuck rate (stuck/total)
- Average task processing time
- Eval completion rate
"""
from datetime import datetime, timedelta
from typing import Optional
from sqlmodel import Session, select
from agenteval.intelligent_eval.models import IntelligentEvalStatus
from agenteval.storage.db import (
IntelligentEvalDB,
IntelligentEvalTaskQueueDB,
OpenClawCronPoolDB,
utc_now,
)
def calculate_pool_utilization(session: Session) -> float:
"""Calculate pool utilization (busy/total).
Returns:
Utilization rate (0.0 to 1.0)
"""
crons = session.exec(select(OpenClawCronPoolDB)).all()
if not crons:
return 0.0
busy = sum(1 for c in crons if c.status == "busy")
return busy / len(crons)
def calculate_task_backlog(session: Session) -> int:
"""Calculate task backlog (pending tasks count).
Returns:
Number of pending tasks
"""
pending = session.exec(
select(IntelligentEvalTaskQueueDB).where(IntelligentEvalTaskQueueDB.status == "pending")
).all()
return len(pending)
def calculate_stuck_rate(session: Session) -> float:
"""Calculate stuck rate (stuck/total).
Returns:
Stuck rate (0.0 to 1.0)
"""
crons = session.exec(select(OpenClawCronPoolDB)).all()
if not crons:
return 0.0
stuck = sum(1 for c in crons if c.status == "stuck")
return stuck / len(crons)
def calculate_avg_processing_time(session: Session) -> Optional[float]:
"""Calculate average task processing time (in seconds).
Returns:
Average processing time in seconds, or None if no completed tasks
"""
completed_tasks = session.exec(
select(IntelligentEvalTaskQueueDB).where(
IntelligentEvalTaskQueueDB.status == "completed",
IntelligentEvalTaskQueueDB.assigned_at.isnot(None),
IntelligentEvalTaskQueueDB.completed_at.isnot(None),
)
).all()
if not completed_tasks:
return None
total_seconds = 0
for task in completed_tasks:
if task.assigned_at and task.completed_at:
duration = (task.completed_at - task.assigned_at).total_seconds()
total_seconds += duration
return total_seconds / len(completed_tasks)
def calculate_eval_completion_rate(session: Session) -> float:
"""Calculate evaluation completion rate.
Returns:
Completion rate (0.0 to 1.0)
"""
all_evals = session.exec(select(IntelligentEvalDB)).all()
if not all_evals:
return 0.0
completed = sum(1 for e in all_evals if e.status == IntelligentEvalStatus.COMPLETED.value)
return completed / len(all_evals)
def get_all_metrics(session: Session) -> dict:
"""Get all metrics.
Returns:
Dict with all metrics
"""
return {
"pool_utilization": calculate_pool_utilization(session),
"task_backlog": calculate_task_backlog(session),
"stuck_rate": calculate_stuck_rate(session),
"avg_processing_time_seconds": calculate_avg_processing_time(session),
"eval_completion_rate": calculate_eval_completion_rate(session),
"timestamp": utc_now().isoformat(),
}