feat(awooop): surface legacy mcp evidence in run detail
This commit is contained in:
@@ -29,9 +29,13 @@ from src.db.awooop_models import (
|
||||
AwoooPRunStepJournal,
|
||||
)
|
||||
from src.db.base import get_db_context
|
||||
from src.db.models import MCPAuditLog
|
||||
from src.services.audit_sink import write_audit
|
||||
from src.services.awooop_truth_chain_service import _summarize_gateway_mcp
|
||||
from src.services.awooop_approval_token import issue_approval_token, record_approval
|
||||
from src.services.awooop_truth_chain_service import (
|
||||
_summarize_gateway_mcp,
|
||||
_summarize_mcp,
|
||||
)
|
||||
from src.services.run_state_machine import transition
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
@@ -458,6 +462,24 @@ def _remediation_timeline_summary(item: dict[str, Any]) -> str:
|
||||
)[:500]
|
||||
|
||||
|
||||
def _legacy_mcp_timeline_status(record: dict[str, Any]) -> str:
|
||||
if record.get("success") is True:
|
||||
return "success"
|
||||
if record.get("success") is False:
|
||||
return "failed"
|
||||
return "warning"
|
||||
|
||||
|
||||
def _legacy_mcp_timeline_summary(record: dict[str, Any]) -> str:
|
||||
return (
|
||||
f"incident={record.get('incident_id') or '--'} "
|
||||
f"agent={record.get('agent_role') or '--'} "
|
||||
f"node={record.get('flywheel_node') or '--'} "
|
||||
f"duration_ms={record.get('duration_ms') if record.get('duration_ms') is not None else '--'} "
|
||||
f"error={record.get('error_message') or '--'}"
|
||||
)[:500]
|
||||
|
||||
|
||||
def _run_remediation_list_summary(
|
||||
*,
|
||||
run: AwoooPRunState,
|
||||
@@ -706,6 +728,60 @@ async def _fetch_run_remediation_history(
|
||||
}
|
||||
|
||||
|
||||
def _legacy_mcp_record(row: MCPAuditLog) -> dict[str, Any]:
|
||||
return {
|
||||
"id": row.id,
|
||||
"session_id": row.session_id,
|
||||
"flywheel_node": row.flywheel_node,
|
||||
"mcp_server": row.mcp_server,
|
||||
"tool_name": row.tool_name,
|
||||
"duration_ms": row.duration_ms,
|
||||
"success": row.success,
|
||||
"error_message": row.error_message,
|
||||
"incident_id": row.incident_id,
|
||||
"agent_role": row.agent_role,
|
||||
"created_at": row.created_at,
|
||||
}
|
||||
|
||||
|
||||
async def _fetch_run_legacy_mcp_history(
|
||||
incident_ids: list[str],
|
||||
*,
|
||||
limit: int = _MAX_TIMELINE_ITEMS,
|
||||
) -> dict[str, Any]:
|
||||
"""Fetch legacy/self-built MCP audit rows linked through incident ids."""
|
||||
if not incident_ids:
|
||||
return {
|
||||
"schema_version": "awooop_run_legacy_mcp_evidence_v1",
|
||||
"source": "mcp_audit_log",
|
||||
"incident_ids": [],
|
||||
"total": 0,
|
||||
"limit": limit,
|
||||
"records": [],
|
||||
"summary": _summarize_mcp([]),
|
||||
}
|
||||
|
||||
async with get_db_context("awoooi") as db:
|
||||
result = await db.execute(
|
||||
select(MCPAuditLog)
|
||||
.where(MCPAuditLog.incident_id.in_(incident_ids))
|
||||
.order_by(MCPAuditLog.created_at.desc())
|
||||
.limit(limit)
|
||||
)
|
||||
rows = list(result.scalars().all())
|
||||
|
||||
records = [_legacy_mcp_record(row) for row in rows]
|
||||
return {
|
||||
"schema_version": "awooop_run_legacy_mcp_evidence_v1",
|
||||
"source": "mcp_audit_log",
|
||||
"incident_ids": incident_ids,
|
||||
"total": len(records),
|
||||
"limit": limit,
|
||||
"records": records,
|
||||
"summary": _summarize_mcp(records),
|
||||
}
|
||||
|
||||
|
||||
async def get_run_detail(
|
||||
run_id: str,
|
||||
project_id: str | None = None,
|
||||
@@ -865,6 +941,7 @@ async def get_run_detail(
|
||||
inbound_events=inbound_events,
|
||||
outbound_messages=outbound_messages,
|
||||
)
|
||||
legacy_mcp_history = await _fetch_run_legacy_mcp_history(incident_ids)
|
||||
remediation_history = await _fetch_run_remediation_history(incident_ids)
|
||||
|
||||
timeline: list[dict[str, Any]] = [
|
||||
@@ -940,6 +1017,32 @@ async def get_run_detail(
|
||||
},
|
||||
)
|
||||
)
|
||||
for record in legacy_mcp_history.get("records", []):
|
||||
if not isinstance(record, dict):
|
||||
continue
|
||||
tool_route = "/".join(
|
||||
part
|
||||
for part in (
|
||||
str(record.get("mcp_server") or ""),
|
||||
str(record.get("tool_name") or ""),
|
||||
)
|
||||
if part
|
||||
) or "unknown"
|
||||
timeline.append(
|
||||
_timeline_item(
|
||||
ts=record.get("created_at"),
|
||||
kind="mcp",
|
||||
title=f"Legacy MCP: {tool_route}",
|
||||
status=_legacy_mcp_timeline_status(record),
|
||||
summary=_legacy_mcp_timeline_summary(record),
|
||||
metadata={
|
||||
"incident_id": record.get("incident_id"),
|
||||
"agent_role": record.get("agent_role"),
|
||||
"flywheel_node": record.get("flywheel_node"),
|
||||
"history_source": "mcp_audit_log",
|
||||
},
|
||||
)
|
||||
)
|
||||
for item in remediation_history.get("items", []):
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
@@ -1002,6 +1105,7 @@ async def get_run_detail(
|
||||
"outbound_messages": outbound_items,
|
||||
"mcp_calls": mcp_items,
|
||||
"mcp_gateway": mcp_gateway_summary,
|
||||
"mcp_legacy": legacy_mcp_history,
|
||||
"remediation_history": remediation_history,
|
||||
"timeline": timeline,
|
||||
"counts": {
|
||||
@@ -1009,6 +1113,7 @@ async def get_run_detail(
|
||||
"inbound_events": len(inbound_items),
|
||||
"outbound_messages": len(outbound_items),
|
||||
"mcp_calls": len(mcp_items),
|
||||
"legacy_mcp_calls": legacy_mcp_history.get("total", 0),
|
||||
"remediation_history": remediation_history.get("total", 0),
|
||||
"timeline": len(timeline),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user