feat: add 6 production-ready plugins
All plugins are stdlib-only Python with no external dependencies. - auto-summarizer: extractive conversation summary for long context compression, persists per-agent summaries to disk - conversation-logger: JSONL audit logs per agent with ISO 8601 timestamps, auto-creates log directory tree - guardrails: safety filter detecting PII (email, phone, SSN, CC), prompt injection patterns, and credential exposure via regex - keyword-memory: extracts entities (emails, URLs, dates, technical terms like camelCase/snake_case/dotted identifiers) as memories - sentiment-tracker: keyword-based sentiment scoring with intensifiers and negation handling, only injects context for non-neutral sentiment - todo-tracker: detects action items via 7 task patterns, tracks completion, deduplicates, persists per-agent with 20-item FIFO limit
This commit is contained in:
1 parent
994b60c0d9
commit
afcb260554
25 files changed
+1396
-4
No files matched your search
@@ -0,0 +1,34 @@
|
||||
# conversation-logger
|
||||
|
||||
Logs all conversations to JSONL files for auditing, analytics, and debugging. Each agent gets its own log file at `~/.librefang/logs/conversations/{agent_id}.jsonl`.
|
||||
|
||||
## Log Format
|
||||
|
||||
Each line is a JSON object:
|
||||
|
||||
```json
|
||||
{
|
||||
"timestamp": "2026-03-21T12:34:56Z",
|
||||
"agent_id": "agent-abc123",
|
||||
"turn_number": 5,
|
||||
"message_count": 10,
|
||||
"last_user_message": "truncated to 200 chars...",
|
||||
"last_assistant_message": "truncated to 200 chars..."
|
||||
}
|
||||
```
|
||||
|
||||
## Hooks
|
||||
|
||||
| Hook | Script | Description |
|
||||
|------|--------|-------------|
|
||||
| after_turn | `hooks/after_turn.py` | Appends a log entry after each conversation turn |
|
||||
|
||||
## How It Works
|
||||
|
||||
After each conversation turn the hook extracts summary information from the messages array and appends a single JSON line to the agent's log file. User and assistant messages are truncated to 200 characters to keep log files manageable.
|
||||
|
||||
Errors from the filesystem (permissions, disk full, etc.) are caught silently so the agent conversation is never interrupted by a logging failure.
|
||||
|
||||
## Usage
|
||||
|
||||
Installed automatically when enabled in agent configuration. Log files are created on first write -- no manual setup required.
|
||||
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Conversation logger after_turn hook.
|
||||
|
||||
Appends a JSON line to a per-agent log file after each conversation turn.
|
||||
Log files are stored under ~/.librefang/logs/conversations/{agent_id}.jsonl.
|
||||
|
||||
Receives via stdin:
|
||||
{"type": "after_turn", "agent_id": "...", "messages": [...]}
|
||||
|
||||
Prints to stdout:
|
||||
{"type": "ok"}
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def _log_dir() -> Path:
|
||||
"""Return the conversations log directory, creating it if needed."""
|
||||
home = Path.home()
|
||||
log_path = home / ".librefang" / "logs" / "conversations"
|
||||
log_path.mkdir(parents=True, exist_ok=True)
|
||||
return log_path
|
||||
|
||||
|
||||
def _last_message_by_role(messages: list, role: str) -> str:
|
||||
"""Find the last message with the given role and return its content truncated to 200 chars."""
|
||||
for msg in reversed(messages):
|
||||
if msg.get("role") == role:
|
||||
content = msg.get("content", "")
|
||||
if len(content) > 200:
|
||||
return content[:200] + "..."
|
||||
return content
|
||||
return ""
|
||||
|
||||
|
||||
def main():
|
||||
request = json.loads(sys.stdin.read())
|
||||
agent_id = request.get("agent_id", "unknown")
|
||||
messages = request.get("messages", [])
|
||||
|
||||
entry = {
|
||||
"timestamp": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
"agent_id": agent_id,
|
||||
"turn_number": max(
|
||||
sum(1 for m in messages if m.get("role") == "assistant"), 1
|
||||
),
|
||||
"message_count": len(messages),
|
||||
"last_user_message": _last_message_by_role(messages, "user"),
|
||||
"last_assistant_message": _last_message_by_role(messages, "assistant"),
|
||||
}
|
||||
|
||||
try:
|
||||
log_file = _log_dir() / f"{agent_id}.jsonl"
|
||||
with open(log_file, "a", encoding="utf-8") as f:
|
||||
f.write(json.dumps(entry, ensure_ascii=False) + "\n")
|
||||
except OSError:
|
||||
# Filesystem issues should not crash the agent. The hook still
|
||||
# responds with "ok" so the conversation continues normally.
|
||||
pass
|
||||
|
||||
print(json.dumps({"type": "ok"}))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,7 @@
|
||||
name = "conversation-logger"
|
||||
version = "0.1.0"
|
||||
description = "Logs all conversations to JSONL files for auditing, analytics, and debugging"
|
||||
author = "librefang"
|
||||
|
||||
[hooks]
|
||||
after_turn = "hooks/after_turn.py"
|
||||
Reference in new issue
Block a user