# backend/src/agent/middleware.py # #region AgentChat.Middleware [C:3] [TYPE Module] [SEMANTICS agent-chat,middleware,logging,audit] # @defgroup AgentChat Audit logging and confirmation risk middleware for LangGraph agent. # @BRIEF LoggingMiddleware writes tool-call events to assistant_audit table. # @RELATION DEPENDS_ON -> [Models.AssistantAuditRecord] # @RATIONALE FR-024: All agent interactions must be logged for auditability. # @REJECTED ConfirmationRiskMiddleware rejected — LangGraph interrupt_before=DANGEROUS_TOOLS handles HITL natively. from datetime import UTC, datetime from src.agent.context import get_user_jwt from src.core.logger import logger # #region AgentChat.Middleware.LoggingMiddleware [C:3] [TYPE Function] [SEMANTICS audit,tool,logging] # @ingroup AgentChat # @BRIEF Log every tool-call event to assistant_audit table with user context. # @PRE agent event has 'event' key with type on_tool_start/on_tool_end/on_tool_error. # @POST Audit record written to assistant_audit table (async, non-blocking). # @SIDE_EFFECT Writes to assistant_audit table via FastAPI REST call. # @RELATION DISPATCHES -> [Api.Assistant.Audit] async def log_tool_event(event: dict, conversation_id: str) -> None: """Log a tool-call event to the audit trail. Args: event: LangGraph event dict with 'event', 'name', and 'data' keys. conversation_id: Current conversation thread ID. """ kind = event.get("event", "") tool_name = event.get("name", "unknown") user_jwt = get_user_jwt() audit_payload = { "event_type": kind, "tool": tool_name, "conversation_id": conversation_id, "user_jwt_present": bool(user_jwt), "timestamp": datetime.now(UTC).isoformat(), } if "data" in event: data = event["data"] if kind == "on_tool_start": audit_payload["input"] = str(data.get("input", ""))[:500] elif kind == "on_tool_error": audit_payload["error"] = str(data.get("error", ""))[:500] logger.reason( "Tool audit event", payload=audit_payload, extra={"src": "AgentChat.Middleware.LoggingMiddleware"}, ) # TODO: Async write to assistant_audit table via REST call to FastAPI # This is intentionally fire-and-forget — audit failures must not block tool execution # #endregion AgentChat.Middleware.LoggingMiddleware # #endregion AgentChat.Middleware