Files
ss-tools/backend/src/agent/middleware.py
busya a0619ca049 fix(agent): unify logging API + add molecular-cot coverage to agent module
Phase 1 — API unification:
- Replace direct log() from cot_logger with canonical logger.reason/reflect/explore
  across _confirmation.py, _persistence.py, _tool_resolver.py, app.py

Phase 2 — Gap filling:
- tools.py: add REASON/REFLECT/EXPLORE to 17 C3 tool functions (was 0 logs)
- app.py agent_handler (C4): add lifecycle REASON on entry, REFLECT on exit,
  EXPLORE on OutputParserException + general exception
- langgraph_setup.py create_agent (C4): add REASON with model/config_source,
  EXPLORE on env-var/InMemorySaver fallback, REFLECT on graph compilation
- _tool_resolver.py infer_tool_from_text (C3, 13 branches): REASON on inference
- _persistence.py: REFLECT on save_conversation success, EXPLORE on prefetch

Phase 3 — Plain log migration:
- middleware.py: plain logger.info() -> logger.reason()
- run.py: 7 plain logger.info/warning/error -> molecular REASON/EXPLORE

Phase 4 — Cleanup:
- cot_logger.py: deprecate MarkerLogger (@DEPRECATED + @REPLACED_BY)
- molecular-cot-logging SKILL.md: remove cot_span Section IV (never implemented),
  renumber sections V-VII -> IV-VI, add cot_span rejection rationale

Verification: pytest 27/27, axiom rebuild 5844/2993/0 warnings
2026-06-30 15:48:46 +03:00

59 lines
2.4 KiB
Python

# 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