feat: add agent_name column to Message admin + export_agent_daily script
- 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
This commit is contained in:
175
scripts/export_agent_daily.py
Normal file
175
scripts/export_agent_daily.py
Normal file
@@ -0,0 +1,175 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user