AI_Chatbot / tools /client_monitor.py
Hamza-Naimat's picture
tools: read-only client activity monitor (analytics/gaps/leads/csat/last-error)
06f804e
Raw
History Blame Contribute Delete
2.95 kB
"""One-shot client-activity monitor — read-only, never touches the bot or analytics.
python tools/client_monitor.py <db> [--recent N]
Shows: query volume + sessions, the N most recent real questions (what the owner
or his customers actually typed), most-asked, unanswered questions (knowledge
gaps), captured leads, CSAT, and the last unhandled /chat error. Everything
comes from admin GET endpoints, so running this adds NOTHING to the client's
analytics (unlike a live probe).
"""
import json
import os
import sys
import urllib.parse
import urllib.request
BASE = "https://re-coder376--ai-chatbot-serve.modal.run"
PW = os.environ.get("ADMIN_PASSWORD", "iaah2006")
def get(path, **params):
params.setdefault("password", PW)
q = urllib.parse.urlencode(params)
try:
with urllib.request.urlopen(f"{BASE}{path}?{q}", timeout=60) as r:
return json.load(r)
except Exception as e:
return {"_error": str(e)}
def main():
if len(sys.argv) < 2:
sys.exit("usage: python tools/client_monitor.py <db> [--recent N]")
db = sys.argv[1]
n_recent = int(sys.argv[sys.argv.index("--recent") + 1]) if "--recent" in sys.argv else 10
a = get("/admin/analytics", db_name=db)
print(f"=== {db} ===")
print(f"queries: {a.get('total_queries', '?')} | sessions: {a.get('total_sessions', '?')} "
f"| avg CSAT: {a.get('avg_csat')} ({a.get('total_ratings', 0)} ratings)")
recent = a.get("most_recent") or []
print(f"\n-- last {min(n_recent, len(recent))} questions --")
for item in recent[:n_recent]:
if isinstance(item, dict):
print(f" {str(item.get('t', ''))[:16]} {item.get('q', '')}")
else:
print(f" {item}")
asked = a.get("most_asked") or []
if asked:
print("\n-- most asked --")
for item in asked[:5]:
print(f" {item}")
gaps = get("/admin/knowledge-gaps", db_name=db)
gap_list = gaps if isinstance(gaps, list) else (gaps.get("gaps") or gaps.get("knowledge_gaps") or [])
if isinstance(gap_list, list) and gap_list:
print(f"\n-- unanswered (knowledge gaps): {len(gap_list)} --")
for g in gap_list[:5]:
print(f" {g.get('q', g) if isinstance(g, dict) else g}")
leads = get("/admin/leads", db_name=db)
if isinstance(leads.get("count"), int):
print(f"\n-- leads: {leads['count']} --")
for l in (leads.get("leads") or [])[:5]:
print(f" {l.get('name', '?')} | {l.get('email', '')} {l.get('whatsapp', '')} | {str(l.get('timestamp', ''))[:16]}")
err = get("/admin/chat-last-error", db_name=db)
last = err.get("last_error") or {}
if last:
print(f"\n-- LAST CHAT ERROR --\n {json.dumps(last)[:300]}")
else:
print("\nno unhandled chat errors.")
print("\nlatency/deep logs: modal app logs ai-chatbot (grep RETRIEVE_MS / 429 / Traceback)")
if __name__ == "__main__":
main()