Merge PR #369: Standardize agent activity events and update timeline UI

This commit is contained in:
ajaysi
2026-03-03 18:25:05 +05:30
5 changed files with 2739 additions and 90 deletions

View File

@@ -1,7 +1,8 @@
from __future__ import annotations
from dataclasses import asdict, dataclass, field
from datetime import datetime
from typing import Any, Dict, List, Optional
from typing import Any, Dict, List, Optional, Union
from sqlalchemy import func
from sqlalchemy.orm import Session
@@ -9,6 +10,73 @@ from sqlalchemy.orm import Session
from models.agent_activity_models import AgentAlert, AgentApprovalRequest, AgentEvent, AgentRun
@dataclass
class AgentEventPayload:
"""Shared schema for agent activity event payloads."""
phase: Optional[str] = None
step: Optional[str] = None
tool_name: Optional[str] = None
progress_percent: Optional[float] = None
input_summary: Optional[str] = None
output_summary: Optional[str] = None
decision_reason: Optional[str] = None
evidence_refs: List[str] = field(default_factory=list)
safe_debug: bool = True
metadata: Dict[str, Any] = field(default_factory=dict)
def build_agent_event_payload(
*,
phase: Optional[str] = None,
step: Optional[str] = None,
tool_name: Optional[str] = None,
progress_percent: Optional[float] = None,
input_summary: Optional[str] = None,
output_summary: Optional[str] = None,
decision_reason: Optional[str] = None,
evidence_refs: Optional[List[str]] = None,
safe_debug: bool = True,
metadata: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
return asdict(
AgentEventPayload(
phase=phase,
step=step,
tool_name=tool_name,
progress_percent=progress_percent,
input_summary=input_summary,
output_summary=output_summary,
decision_reason=decision_reason,
evidence_refs=list(evidence_refs or []),
safe_debug=bool(safe_debug),
metadata=dict(metadata or {}),
)
)
def _normalize_event_payload(payload: Optional[Union[Dict[str, Any], AgentEventPayload]]) -> Dict[str, Any]:
if payload is None:
return build_agent_event_payload()
if isinstance(payload, AgentEventPayload):
return asdict(payload)
if not isinstance(payload, dict):
return build_agent_event_payload(output_summary=str(payload)[:2000], safe_debug=False)
return build_agent_event_payload(
phase=payload.get("phase"),
step=payload.get("step"),
tool_name=payload.get("tool_name"),
progress_percent=payload.get("progress_percent"),
input_summary=payload.get("input_summary"),
output_summary=payload.get("output_summary"),
decision_reason=payload.get("decision_reason"),
evidence_refs=payload.get("evidence_refs") if isinstance(payload.get("evidence_refs"), list) else [],
safe_debug=bool(payload.get("safe_debug", True)),
metadata=payload.get("metadata") if isinstance(payload.get("metadata"), dict) else {},
)
class AgentActivityService:
def __init__(self, db: Session, user_id: str):
self.db = db
@@ -51,10 +119,11 @@ class AgentActivityService:
event_type: str,
severity: str = "info",
message: Optional[str] = None,
payload: Optional[Dict[str, Any]] = None,
payload: Optional[Union[Dict[str, Any], AgentEventPayload]] = None,
run_id: Optional[int] = None,
agent_type: Optional[str] = None,
) -> AgentEvent:
normalized_payload = _normalize_event_payload(payload)
evt = AgentEvent(
run_id=run_id,
user_id=self.user_id,
@@ -62,7 +131,7 @@ class AgentActivityService:
event_type=event_type,
severity=severity,
message=message,
payload=payload,
payload=normalized_payload,
created_at=datetime.utcnow(),
)
self.db.add(evt)

View File

@@ -38,7 +38,7 @@ from utils.logger_utils import get_service_logger
from services.database import get_session_for_user
from services.intelligence.monitoring.semantic_dashboard import RealTimeSemanticMonitor
from services.intelligence.agents.safety_framework import get_safety_framework
from services.agent_activity_service import AgentActivityService
from services.agent_activity_service import AgentActivityService, build_agent_event_payload
from services.intelligence.agents.agent_usage_tracking import track_agent_usage_sync
import time
@@ -504,7 +504,7 @@ class BaseALwrityAgent(ABC):
event_type="plan",
severity="info",
message=(prompt[:2000] if prompt else None),
payload={"kind": "prompt"},
payload=build_agent_event_payload(phase="planning", step="run_started", tool_name="agent_run", progress_percent=0, input_summary=prompt[:250], output_summary="Agent run initialized", decision_reason="Received run prompt", safe_debug=False, metadata={"kind": "prompt"}),
run_id=run_record.id,
agent_type=self.agent_type,
)
@@ -531,7 +531,7 @@ class BaseALwrityAgent(ABC):
event_type="final_summary",
severity="info",
message=(str(result)[:2000] if result is not None else None),
payload={"kind": "result"},
payload=build_agent_event_payload(phase="execution", step="run_completed", tool_name="agent_run", progress_percent=100, output_summary=str(result)[:400] if result is not None else "No output", decision_reason="Run completed", safe_debug=True, metadata={"kind": "result"}),
run_id=run_record.id,
agent_type=self.agent_type,
)
@@ -545,7 +545,7 @@ class BaseALwrityAgent(ABC):
event_type="error",
severity="error",
message=str(e)[:2000],
payload={"kind": "exception"},
payload=build_agent_event_payload(phase="execution", step="run_error", tool_name="agent_runtime", output_summary=str(e)[:400], decision_reason="Unhandled exception during run", safe_debug=False, metadata={"kind": "exception"}),
run_id=run_record.id,
agent_type=self.agent_type,
)
@@ -591,7 +591,7 @@ class BaseALwrityAgent(ABC):
event_type="plan",
severity="info",
message=f"{action.action_type} -> {action.target_resource}",
payload={"action": asdict(action)},
payload=build_agent_event_payload(phase="planning", step="action_received", tool_name=action.action_type, progress_percent=5, input_summary=f"target={action.target_resource}", output_summary="Action accepted for execution", decision_reason="Start run lifecycle", safe_debug=True, metadata={"action": asdict(action)}),
run_id=run_record.id,
agent_type=self.agent_type,
)
@@ -606,7 +606,7 @@ class BaseALwrityAgent(ABC):
event_type="decision",
severity="warning",
message="Action failed safety validation",
payload={"action_id": action.action_id, "action_type": action.action_type},
payload=build_agent_event_payload(phase="validation", step="safety_blocked", tool_name="safety_framework", progress_percent=10, input_summary=action.action_type, output_summary="Action blocked by safety validation", decision_reason="Safety framework rejected action", safe_debug=True, metadata={"action_id": action.action_id, "action_type": action.action_type}),
run_id=run_record.id,
agent_type=self.agent_type,
)
@@ -646,7 +646,7 @@ class BaseALwrityAgent(ABC):
event_type="decision",
severity="info",
message="Action requires approval",
payload={"approval_id": req.id, "action_id": action.action_id},
payload=build_agent_event_payload(phase="approval", step="awaiting_user_decision", tool_name=action.action_type, progress_percent=20, input_summary=action.target_resource, output_summary="Approval request created", decision_reason="Action requires human approval", safe_debug=True, metadata={"approval_id": req.id, "action_id": action.action_id}),
run_id=run_record.id,
agent_type=self.agent_type,
)
@@ -671,7 +671,7 @@ class BaseALwrityAgent(ABC):
event_type="progress",
severity="info",
message="Rollback checkpoint created",
payload={"checkpoint_id": checkpoint_id},
payload=build_agent_event_payload(phase="safety", step="checkpoint_created", tool_name="rollback_manager", progress_percent=35, output_summary="Rollback checkpoint created", decision_reason="Prepare rollback safety net", safe_debug=True, metadata={"checkpoint_id": checkpoint_id}),
run_id=run_record.id,
agent_type=self.agent_type,
)
@@ -682,7 +682,7 @@ class BaseALwrityAgent(ABC):
event_type="warning",
severity="warning",
message=str(e)[:2000],
payload={"checkpoint": "failed"},
payload=build_agent_event_payload(phase="safety", step="checkpoint_failed", tool_name="rollback_manager", progress_percent=30, output_summary="Checkpoint creation failed", decision_reason="Proceeding without checkpoint", safe_debug=False, metadata={"checkpoint": "failed"}),
run_id=run_record.id,
agent_type=self.agent_type,
)
@@ -717,7 +717,7 @@ class BaseALwrityAgent(ABC):
event_type="final_summary",
severity="info",
message=str(result)[:2000] if result is not None else None,
payload={"action_id": action.action_id},
payload=build_agent_event_payload(phase="execution", step="completed", tool_name=action.action_type, progress_percent=100, output_summary=str(result)[:400] if result is not None else "No output", decision_reason="Action execution completed", safe_debug=True, metadata={"action_id": action.action_id}),
run_id=run_record.id,
agent_type=self.agent_type,
)
@@ -768,7 +768,7 @@ class BaseALwrityAgent(ABC):
event_type="error",
severity="error",
message=str(e)[:2000],
payload={"action_id": action.action_id, "checkpoint_id": checkpoint_id},
payload=build_agent_event_payload(phase="execution", step="failed", tool_name=action.action_type, progress_percent=100, output_summary=str(e)[:400], decision_reason="Exception during action execution", safe_debug=False, metadata={"action_id": action.action_id, "checkpoint_id": checkpoint_id}),
run_id=run_record.id,
agent_type=self.agent_type,
)

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ from sqlalchemy.orm import Session
from models.daily_workflow_models import DailyWorkflowPlan, DailyWorkflowTask
from models.agent_activity_models import AgentAlert
from services.agent_activity_service import AgentActivityService
from services.agent_activity_service import AgentActivityService, build_agent_event_payload
from services.llm_providers.main_text_generation import llm_text_gen
from loguru import logger
@@ -430,7 +430,7 @@ async def generate_agent_enhanced_plan(db: Session, user_id: str, date: str) ->
event_type="plan",
severity="info",
message="Building grounded daily workflow plan",
payload={"grounding": grounding},
payload=build_agent_event_payload(phase="planning", step="build_grounded_plan", tool_name="llm_text_gen", progress_percent=10, input_summary="Grounding data assembled from onboarding + alerts", output_summary="Preparing daily workflow generation", decision_reason="Need context-aware workflow", evidence_refs=["onboarding_data","recent_agent_alerts"], safe_debug=True, metadata={"grounding": grounding}),
run_id=run.id,
agent_type="TodayWorkflowGenerator",
)
@@ -449,7 +449,7 @@ async def generate_agent_enhanced_plan(db: Session, user_id: str, date: str) ->
event_type="warning",
severity="warning",
message=str(e)[:2000],
payload={"fallback": True},
payload=build_agent_event_payload(phase="generation", step="llm_failed_fallback", tool_name="llm_text_gen", progress_percent=70, output_summary="LLM generation failed, using fallback tasks", decision_reason="Exception during workflow generation", safe_debug=False, metadata={"fallback": True}),
run_id=run.id,
agent_type="TodayWorkflowGenerator",
)
@@ -467,7 +467,7 @@ async def generate_agent_enhanced_plan(db: Session, user_id: str, date: str) ->
event_type="final_summary",
severity="info",
message="Daily workflow plan generated",
payload={"date": date, "task_count": len(result.get("tasks", []))},
payload=build_agent_event_payload(phase="generation", step="workflow_generated", tool_name="llm_text_gen", progress_percent=100, output_summary=f"Generated {len(result.get('tasks', []))} tasks", decision_reason="Workflow assembled successfully", evidence_refs=[date], safe_debug=True, metadata={"date": date, "task_count": len(result.get("tasks", []))}),
run_id=run.id,
agent_type="TodayWorkflowGenerator",
)