#!/usr/bin/env python3
"""AnviLLM message trace tool - PlantUML sequence diagram generator."""
import json
import os
import subprocess
import threading
from http.server import HTTPServer, BaseHTTPRequestHandler
NAMESPACE = os.environ.get("NAMESPACE", f"/tmp/ns.{os.environ['USER']}.:0")
PORT = 8089
events = []
lock = threading.Lock()
def generate_puml():
with lock:
lines = ["@startuml", "skinparam responseMessageBelowArrow true", "participant user"]
for e in events:
d = e.get("data", {})
frm = d.get("from", "?")
to = d.get("to", "?")
subj = d.get("subject", "").replace('"', "'")[:60]
lines.append(f'"{frm}" -> "{to}": {subj}')
lines.append("@enduml")
return "\n".join(lines)
def render_svg(puml):
try:
p = subprocess.run(["plantuml", "-tsvg", "-pipe"], input=puml.encode(),
capture_output=True, timeout=10)
return p.stdout.decode() if p.returncode == 0 else f"
Error: {p.stderr.decode()}"
except Exception as ex:
return f"PlantUML error: {ex}"
HTML = """
AnviLLM Trace
"""
class Handler(BaseHTTPRequestHandler):
def log_message(self, *a): pass
def do_GET(self):
if self.path == "/":
self.send_response(200)
self.send_header("Content-Type", "text/html")
self.end_headers()
self.wfile.write(HTML.encode())
elif self.path == "/data":
puml = generate_puml()
svg = render_svg(puml)
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(json.dumps({"puml": puml, "svg": svg}).encode())
else:
self.send_error(404)
def do_POST(self):
if self.path == "/reset":
with lock:
events.clear()
self.send_response(200)
self.end_headers()
else:
self.send_error(404)
def event_reader():
os.environ["NAMESPACE"] = NAMESPACE
proc = subprocess.Popen(["9p", "read", "agent/events"], stdout=subprocess.PIPE, text=True)
for line in proc.stdout:
try:
ev = json.loads(line.strip())
if ev.get("type") in ("UserSend", "BotSend"):
with lock:
events.append(ev)
except json.JSONDecodeError:
pass
if __name__ == "__main__":
threading.Thread(target=event_reader, daemon=True).start()
print(f"Trace UI: http://localhost:{PORT}")
HTTPServer(("", PORT), Handler).serve_forever()