Add tiered agent activity responses with redaction and UI toggle
This commit is contained in:
@@ -6,6 +6,7 @@ Provides REST API access to agent orchestration functionality
|
||||
from fastapi import APIRouter, HTTPException, Depends, BackgroundTasks
|
||||
from typing import Dict, List, Any, Optional
|
||||
import asyncio
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
from middleware.auth_middleware import get_current_user
|
||||
@@ -18,6 +19,15 @@ from services.intelligence.agents.market_signal_detector import MarketSignal
|
||||
from services.intelligence.agents.performance_monitor import PerformanceMetric, AgentStatus
|
||||
from services.database import get_db
|
||||
from services.agent_activity_service import AgentActivityService
|
||||
from services.agent_activity_serializers import (
|
||||
DETAIL_TIER_DEBUG,
|
||||
DETAIL_TIER_SUMMARY,
|
||||
normalize_detail_tier,
|
||||
serialize_alert,
|
||||
serialize_approval,
|
||||
serialize_event,
|
||||
serialize_run,
|
||||
)
|
||||
from sqlalchemy.orm import Session
|
||||
from models.agent_activity_models import AgentProfile
|
||||
from services.intelligence.agents.team_catalog import AGENT_TEAM_CATALOG, get_agent_catalog_entry
|
||||
@@ -26,6 +36,35 @@ logger = get_service_logger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/api/agents", tags=["Autonomous Agents"])
|
||||
|
||||
|
||||
def _can_access_advanced_activity(current_user: Dict[str, Any]) -> bool:
|
||||
role = str(current_user.get("role") or "").lower().strip()
|
||||
metadata = current_user.get("public_metadata")
|
||||
if isinstance(metadata, dict):
|
||||
role = str(metadata.get("role") or role).lower().strip()
|
||||
|
||||
feature_flags = current_user.get("feature_flags")
|
||||
if not feature_flags and isinstance(metadata, dict):
|
||||
feature_flags = metadata.get("feature_flags") or metadata.get("features")
|
||||
|
||||
has_flag = False
|
||||
if isinstance(feature_flags, list):
|
||||
has_flag = any(str(flag).strip().lower() in {"agent_activity_detailed", "agents_activity_detailed"} for flag in feature_flags)
|
||||
elif isinstance(feature_flags, dict):
|
||||
has_flag = bool(feature_flags.get("agent_activity_detailed") or feature_flags.get("agents_activity_detailed"))
|
||||
|
||||
if os.getenv("DISABLE_AUTH", "false").lower() == "true":
|
||||
return True
|
||||
|
||||
return role in {"admin", "internal"} or has_flag
|
||||
|
||||
|
||||
def _resolve_detail_tier(requested_tier: str, current_user: Dict[str, Any]) -> str:
|
||||
tier = normalize_detail_tier(requested_tier)
|
||||
if tier == DETAIL_TIER_DEBUG and not _can_access_advanced_activity(current_user):
|
||||
return DETAIL_TIER_SUMMARY
|
||||
return tier
|
||||
|
||||
@router.get("/team")
|
||||
async def get_agent_team_endpoint(
|
||||
current_user: dict = Depends(get_current_user),
|
||||
@@ -427,32 +466,21 @@ Return ONLY a JSON object that matches the schema.
|
||||
async def get_agent_alerts_endpoint(
|
||||
unread_only: bool = True,
|
||||
limit: int = 50,
|
||||
detail_tier: str = DETAIL_TIER_SUMMARY,
|
||||
current_user: dict = Depends(get_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
) -> Dict[str, Any]:
|
||||
try:
|
||||
user_id = str(current_user.get("id"))
|
||||
resolved_tier = _resolve_detail_tier(detail_tier, current_user)
|
||||
service = AgentActivityService(db, user_id)
|
||||
alerts = service.list_alerts(unread_only=unread_only, limit=limit)
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"alerts": [
|
||||
{
|
||||
"id": a.id,
|
||||
"source": a.source,
|
||||
"type": a.alert_type,
|
||||
"severity": a.severity,
|
||||
"title": a.title,
|
||||
"message": a.message,
|
||||
"cta_path": a.cta_path,
|
||||
"payload": a.payload,
|
||||
"created_at": a.created_at.isoformat() if a.created_at else None,
|
||||
"read_at": a.read_at.isoformat() if a.read_at else None,
|
||||
}
|
||||
for a in alerts
|
||||
],
|
||||
"alerts": [serialize_alert(a, resolved_tier) for a in alerts],
|
||||
"total": len(alerts),
|
||||
"detail_tier": resolved_tier,
|
||||
},
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
"user_id": user_id,
|
||||
@@ -485,31 +513,20 @@ async def mark_agent_alert_read_endpoint(
|
||||
@router.get("/runs")
|
||||
async def get_agent_runs_endpoint(
|
||||
limit: int = 30,
|
||||
detail_tier: str = DETAIL_TIER_SUMMARY,
|
||||
current_user: dict = Depends(get_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
) -> Dict[str, Any]:
|
||||
try:
|
||||
user_id = str(current_user.get("id"))
|
||||
resolved_tier = _resolve_detail_tier(detail_tier, current_user)
|
||||
service = AgentActivityService(db, user_id)
|
||||
runs = service.list_runs(limit=limit)
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"runs": [
|
||||
{
|
||||
"id": r.id,
|
||||
"user_id": r.user_id,
|
||||
"agent_type": r.agent_type,
|
||||
"status": r.status,
|
||||
"success": r.success,
|
||||
"error_message": r.error_message,
|
||||
"result_summary": r.result_summary,
|
||||
"mlflow_run_id": r.mlflow_run_id,
|
||||
"started_at": r.started_at.isoformat() if r.started_at else None,
|
||||
"finished_at": r.finished_at.isoformat() if r.finished_at else None,
|
||||
}
|
||||
for r in runs
|
||||
]
|
||||
"runs": [serialize_run(r, resolved_tier) for r in runs],
|
||||
"detail_tier": resolved_tier,
|
||||
},
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
"user_id": user_id,
|
||||
@@ -523,29 +540,20 @@ async def get_agent_runs_endpoint(
|
||||
async def get_agent_run_events_endpoint(
|
||||
run_id: int,
|
||||
limit: int = 200,
|
||||
detail_tier: str = DETAIL_TIER_SUMMARY,
|
||||
current_user: dict = Depends(get_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
) -> Dict[str, Any]:
|
||||
try:
|
||||
user_id = str(current_user.get("id"))
|
||||
resolved_tier = _resolve_detail_tier(detail_tier, current_user)
|
||||
service = AgentActivityService(db, user_id)
|
||||
events = service.list_events(run_id=run_id, limit=limit)
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"events": [
|
||||
{
|
||||
"id": e.id,
|
||||
"run_id": e.run_id,
|
||||
"agent_type": e.agent_type,
|
||||
"event_type": e.event_type,
|
||||
"severity": e.severity,
|
||||
"message": e.message,
|
||||
"payload": e.payload,
|
||||
"created_at": e.created_at.isoformat() if e.created_at else None,
|
||||
}
|
||||
for e in events
|
||||
]
|
||||
"events": [serialize_event(e, resolved_tier) for e in events],
|
||||
"detail_tier": resolved_tier,
|
||||
},
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
"user_id": user_id,
|
||||
@@ -559,32 +567,20 @@ async def get_agent_run_events_endpoint(
|
||||
async def get_agent_approvals_endpoint(
|
||||
status: str = "pending",
|
||||
limit: int = 50,
|
||||
detail_tier: str = DETAIL_TIER_SUMMARY,
|
||||
current_user: dict = Depends(get_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
) -> Dict[str, Any]:
|
||||
try:
|
||||
user_id = str(current_user.get("id"))
|
||||
resolved_tier = _resolve_detail_tier(detail_tier, current_user)
|
||||
service = AgentActivityService(db, user_id)
|
||||
approvals = service.list_approval_requests(status=status, limit=limit)
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"approvals": [
|
||||
{
|
||||
"id": a.id,
|
||||
"status": a.status,
|
||||
"decision": a.decision,
|
||||
"action_id": a.action_id,
|
||||
"action_type": a.action_type,
|
||||
"agent_type": a.agent_type,
|
||||
"target_resource": a.target_resource,
|
||||
"risk_level": a.risk_level,
|
||||
"payload": a.payload,
|
||||
"created_at": a.created_at.isoformat() if a.created_at else None,
|
||||
"decided_at": a.decided_at.isoformat() if a.decided_at else None,
|
||||
}
|
||||
for a in approvals
|
||||
]
|
||||
"approvals": [serialize_approval(a, resolved_tier) for a in approvals],
|
||||
"detail_tier": resolved_tier,
|
||||
},
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
"user_id": user_id,
|
||||
|
||||
Reference in New Issue
Block a user