File size: 2,949 Bytes
06f804e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
"""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()