- Message list now shows agent_name via get_agent_name method - New scripts/export_agent_daily.py for agent daily message export - Accepts --agent and --date (YYYY-MM-DD) arguments - Focuses on messages within target date (session may span multiple days) - Outputs Markdown with session info, messages, tool calls
176 lines
6.1 KiB
Python
176 lines
6.1 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Export agent daily messages to Markdown.
|
|
|
|
Usage:
|
|
python scripts/export_agent_daily.py --agent xingjiang --date 2026-04-07
|
|
python scripts/export_agent_daily.py --agent xingjiang --date 2026-04-07 --output /tmp/report.md
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import sys
|
|
from datetime import date
|
|
|
|
# Add src to path for Django settings
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
|
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production")
|
|
|
|
import django
|
|
django.setup()
|
|
|
|
from openclaw.models import Session, Message, ToolCall
|
|
|
|
|
|
def format_tool_args(args: dict) -> str:
|
|
"""Format tool arguments as pretty JSON."""
|
|
if not args:
|
|
return ""
|
|
return json.dumps(args, indent=2, ensure_ascii=False)
|
|
|
|
|
|
def export_agent_daily(agent_name: str, target_date: str, output_file=None):
|
|
"""
|
|
Export all messages for a specific agent on a specific date.
|
|
|
|
Sessions may span multiple days, but we focus on messages within the target date.
|
|
"""
|
|
target_date_obj = date.fromisoformat(target_date)
|
|
|
|
# Get sessions for this agent that have messages on the target date
|
|
sessions = Session.objects.filter(
|
|
agent_name=agent_name
|
|
).prefetch_related("messages", "messages__tool_calls").order_by("start_time")
|
|
|
|
daily_sessions = []
|
|
for session in sessions:
|
|
# Filter messages to only those on target date
|
|
day_messages = session.messages.filter(
|
|
timestamp__date=target_date_obj
|
|
).order_by("seq")
|
|
|
|
if day_messages.exists():
|
|
daily_sessions.append({
|
|
"session": session,
|
|
"messages": list(day_messages),
|
|
})
|
|
|
|
if not daily_sessions:
|
|
print(f"No messages found for agent {agent_name} on {target_date}", file=sys.stderr)
|
|
return
|
|
|
|
# Build Markdown output
|
|
lines = []
|
|
lines.append(f"# Agent Daily Report: {agent_name} - {target_date}")
|
|
lines.append("")
|
|
|
|
total_messages = sum(len(s["messages"]) for s in daily_sessions)
|
|
lines.append(f"**Total Sessions:** {len(daily_sessions)} ")
|
|
lines.append(f"**Total Messages:** {total_messages} ")
|
|
lines.append("")
|
|
|
|
role_label_map = {
|
|
"user": "User",
|
|
"assistant": "Assistant",
|
|
"tool": "Tool",
|
|
"toolResult": "Tool Result",
|
|
"system": "System",
|
|
}
|
|
|
|
for session_data in daily_sessions:
|
|
session = session_data["session"]
|
|
messages = session_data["messages"]
|
|
|
|
start_str = session.start_time.strftime("%Y-%m-%d %H:%M") if session.start_time else "N/A"
|
|
end_str = session.end_time.strftime("%H:%M") if session.end_time else " Ongoing"
|
|
|
|
lines.append(f"## Session: {session.session_id}")
|
|
lines.append(f"**Time:** {start_str} ~ {end_str} ")
|
|
lines.append(f"**Model:** {session.model_id or 'N/A'} ")
|
|
lines.append(f"**Tokens:** {session.total_tokens:,} | **Cost:** ${session.total_cost:.4f} ")
|
|
lines.append(f"**Messages:** {session.message_count} | **Tool Calls:** {session.tool_call_count} ")
|
|
lines.append("")
|
|
|
|
for msg in messages:
|
|
role_disp = role_label_map.get(msg.role, msg.role)
|
|
ts_str = msg.timestamp.strftime("%H:%M:%S")
|
|
|
|
lines.append(f"### [{msg.seq}] [{role_disp}] [{ts_str}]")
|
|
lines.append("")
|
|
|
|
# Content
|
|
content = msg.content_text.strip() if msg.content_text else ""
|
|
if content:
|
|
# Truncate very long content
|
|
if len(content) > 2000:
|
|
content = content[:2000] + "\n\n_[content truncated]_"
|
|
lines.append(f"**Content:**\n{content}")
|
|
else:
|
|
lines.append("**Content:** _(empty)_")
|
|
lines.append("")
|
|
|
|
# Tool call info
|
|
if msg.tool_name:
|
|
lines.append(f"**Tool:** `{msg.tool_name}` | **Exit:** {msg.exit_code} | **Duration:** {msg.duration_ms}ms")
|
|
lines.append("")
|
|
|
|
# Get tool calls for this message
|
|
tool_calls = msg.tool_calls.all()
|
|
if tool_calls.exists():
|
|
for tc in tool_calls:
|
|
args_str = format_tool_args(tc.arguments)
|
|
if args_str:
|
|
lines.append(f"**Arguments:**\n```json\n{args_str}\n```")
|
|
lines.append("")
|
|
if tc.result_text:
|
|
result = tc.result_text.strip()
|
|
if len(result) > 1000:
|
|
result = result[:1000] + "\n\n_[result truncated]_"
|
|
lines.append(f"**Result:**\n{result}")
|
|
lines.append("")
|
|
|
|
# Token info
|
|
if msg.tokens_total > 0:
|
|
lines.append(f"**Tokens:** {msg.tokens_total:,} (in: {msg.tokens_input}, out: {msg.tokens_output})")
|
|
|
|
if msg.is_error:
|
|
lines.append("⚠️ **Error**")
|
|
|
|
lines.append("")
|
|
lines.append("---")
|
|
lines.append("")
|
|
|
|
md_content = "\n".join(lines)
|
|
|
|
if output_file:
|
|
with open(output_file, "w", encoding="utf-8") as f:
|
|
f.write(md_content)
|
|
print(f"Report written to: {output_file}", file=sys.stderr)
|
|
else:
|
|
print(md_content)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="Export agent daily messages to Markdown"
|
|
)
|
|
parser.add_argument("--agent", required=True, help="Agent name (e.g. xingjiang)")
|
|
parser.add_argument("--date", required=True, help="Target date (YYYY-MM-DD)")
|
|
parser.add_argument("--output", help="Output file path (default: stdout)")
|
|
|
|
args = parser.parse_args()
|
|
|
|
# Validate date format
|
|
try:
|
|
date.fromisoformat(args.date)
|
|
except ValueError:
|
|
print(f"Error: Invalid date format {args.date}. Use YYYY-MM-DD.", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
export_agent_daily(args.agent, args.date, args.output)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|