55 lines
1.5 KiB
Python
55 lines
1.5 KiB
Python
"""Audit logging for AI tool calls.
|
|
|
|
Reuses the application audit log (``data/audit.log``, JSON lines) and adds an
|
|
``ai_tool_call`` action. Argument values that may contain sensitive payloads
|
|
(file content, prompts) are summarized rather than stored verbatim.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import UTC, datetime
|
|
from typing import Any
|
|
|
|
from backend.audit import _write_entry
|
|
|
|
# Argument keys whose values may contain secrets or large payloads.
|
|
_SENSITIVE_ARG_KEYS = {"content", "text", "body"}
|
|
_MAX_ARG_CHARS = 200
|
|
|
|
|
|
def _sanitize_arguments(arguments: dict[str, Any] | None) -> dict[str, Any]:
|
|
"""Return a log-safe view of tool arguments."""
|
|
safe: dict[str, Any] = {}
|
|
for key, value in (arguments or {}).items():
|
|
if key in _SENSITIVE_ARG_KEYS:
|
|
safe[key] = f"<{len(str(value))} chars>"
|
|
else:
|
|
safe[key] = str(value)[:_MAX_ARG_CHARS]
|
|
return safe
|
|
|
|
|
|
def log_tool_call(
|
|
*,
|
|
username: str,
|
|
mode: str,
|
|
tool: str,
|
|
arguments: dict[str, Any] | None = None,
|
|
ok: bool = True,
|
|
vault: str | None = None,
|
|
ip: str | None = None,
|
|
error: str | None = None,
|
|
) -> None:
|
|
"""Append an ``ai_tool_call`` entry to the audit log."""
|
|
_write_entry({
|
|
"timestamp": datetime.now(UTC).isoformat(),
|
|
"action": "ai_tool_call",
|
|
"username": username,
|
|
"ip": ip or "unknown",
|
|
"mode": mode,
|
|
"tool": tool,
|
|
"vault": vault,
|
|
"ok": ok,
|
|
"error": error,
|
|
"arguments": _sanitize_arguments(arguments),
|
|
})
|