Auditing¶
RoomKit provides two levels of auditing: tool auditing for recording individual tool calls, and session auditing for capturing the full conversation timeline including speech turns, tool calls, vision events, and interruptions.
Session Auditing¶
JSONLSessionAuditor captures the complete conversation flow — not just tools.
Quick start¶
from roomkit import RoomKit
from roomkit.orchestration.session_audit import JSONLSessionAuditor
auditor = JSONLSessionAuditor("/tmp/session.jsonl")
kit = RoomKit()
# Auto-capture speech, vision, barge-in, session events
auditor.attach(kit)
# ... set up channels, rooms, sessions ...
# Tool calls are recorded manually from your handler:
from roomkit.orchestration.tool_audit import ToolAuditEntry
auditor.record_tool(ToolAuditEntry(
ts=datetime.now().isoformat(),
agent_id="my-agent",
tool_name="search",
arguments={"q": "roomkit"},
result="Found 3 results",
status="ok",
duration_ms=1500,
))
# After the session
auditor.print_summary()
What gets captured¶
| Event type | Hook trigger | What it records |
|---|---|---|
speech |
ON_TRANSCRIPTION |
User and assistant voice turns (final only) |
tool_call |
Manual record_tool() |
Tool name, args, result, duration, status |
vision |
ON_VISION_RESULT |
Periodic screen/camera descriptions |
barge_in |
ON_BARGE_IN |
User interruptions |
session |
ON_SESSION_STARTED |
Voice session lifecycle |
Transcript output¶
Session Audit (/tmp/session.jsonl)
============================================================
[11:56:02] SESSION Session started
[11:56:05] USER: "Open Google Chrome and search for roomkit"
[11:56:07] ASSISTANT: "Sure, let me check your screen first."
[11:56:08] TOOL describe_screen → OK (5886ms)
Chrome with Google search page open
[11:56:14] VISION Chrome browser showing search results
[11:56:30] BARGE-IN User interrupted
[11:57:42] ASSISTANT: "Done! The roomkit.live website is open."
Duration: 1m 40s | Turns: 2 user, 2 assistant
Tool calls: 1 (5886ms) | Vision: 1 | Interruptions: 1
Tools: describe_screen(1)
ToolAuditor compatibility¶
SessionAuditor works as a drop-in for APIs that expect a ToolAuditor:
# Use the bridge property
bridge = auditor.tool_auditor
audited = audit_tool_handler(handler, bridge, agent_id="my-agent")
Custom session auditor¶
Implement the SessionAuditor ABC:
from roomkit.orchestration.session_audit import SessionAuditor, SessionAuditEntry
class MySessionAuditor(SessionAuditor):
def record(self, entry: SessionAuditEntry) -> None:
send_to_monitoring(entry.model_dump())
@property
def entries(self) -> list[SessionAuditEntry]:
return []
def summary(self) -> str:
return "See monitoring dashboard"
Tool Auditing¶
For simpler use cases that only need tool call recording, use JSONLToolAuditor directly.
Quick start¶
from roomkit import Agent, Tool
from roomkit.orchestration.tool_audit import JSONLToolAuditor, audit_tool_handler
# Create an auditor
auditor = JSONLToolAuditor("/tmp/audit.jsonl")
# Wrap any tool handler — recording is automatic
async def my_handler(name, args):
return '{"status": "ok"}'
audited = audit_tool_handler(my_handler, auditor, agent_id="my-agent")
# Use with an Agent — pass Tool objects via tools=[], audited handler via tool_handler=
agent = Agent(
"my-agent",
provider=my_provider,
tools=[my_tool],
tool_handler=audited,
)
# After the session, print summary
auditor.print_summary()
What gets recorded¶
Every tool call produces a ToolAuditEntry:
| Field | Type | Description |
|---|---|---|
ts |
str | ISO timestamp |
agent_id |
str | Which agent made the call |
tool_name |
str | Tool function name |
arguments |
dict | Input arguments |
result |
str | Tool output (truncated to 500 chars) |
status |
str | ok, failed, or error |
duration_ms |
float | Execution time in milliseconds |
metadata |
dict | Optional extra data |
Status is auto-detected: if the tool returns {"status": "failed"}, the entry is marked as failed.
Implementations¶
JSONLToolAuditor¶
Writes entries to a JSONL file and keeps them in memory:
from roomkit.orchestration.tool_audit import JSONLToolAuditor
auditor = JSONLToolAuditor("/tmp/screen_ai/audit.jsonl")
ConsoleToolAuditor¶
Logs entries in real-time via Python logging:
from roomkit.orchestration.tool_audit import ConsoleToolAuditor
auditor = ConsoleToolAuditor()
# Logs: [AUDIT] [+] exec.search_google → ok (9333ms) {"status": "ok", ...}
Custom auditor¶
Implement the ToolAuditor ABC:
from roomkit.orchestration.tool_audit import ToolAuditor, ToolAuditEntry
class MyAuditor(ToolAuditor):
def record(self, entry: ToolAuditEntry) -> None:
# Send to your monitoring system
send_to_datadog(entry.model_dump())
@property
def entries(self) -> list[ToolAuditEntry]:
return []
def summary(self) -> str:
return "See Datadog dashboard"
Handler wrappers¶
For AIChannel (standard tool handler)¶
from roomkit.orchestration.tool_audit import audit_tool_handler
# Wraps async (name, args) -> str
audited = audit_tool_handler(handler, auditor, agent_id="exec")
agent = Agent("exec", tools=[my_tool], tool_handler=audited, ...)
For RealtimeVoiceChannel (voice tool handler)¶
The same audit_tool_handler works for voice — the tool handler signature is unified as async (name: str, args: dict) -> str across all channels.
audited = audit_tool_handler(handler, auditor, agent_id="voice")
voice_channel = RealtimeVoiceChannel("voice", tools=[my_tool], tool_handler=audited, ...)
Print summary¶
Tool Audit (/tmp/screen_ai/audit.jsonl)
============================================================
1. [OK] search_google (9333ms)
→ {"status": "ok", "query": "roomkit conversation AI", "results": [...]}
2. [OK] click_result (5505ms)
→ {"status": "ok", "clicked": "roomkit.live"}
3. [OK] read_screen (2729ms)
→ {"status": "ok", "description": "RoomKit homepage..."}
Total: 3 calls, 17567ms
Tools: click_result(1), read_screen(1), search_google(1)