OverviewFilesCommitsIssuesDocsReleasesPeers

hypercore-kb / opencog-kb / language-learning / mvp / cog-monitor.py

86 lines · python ref:
#!/usr/bin/env python3
"""
cog-monitor.py -- Passive observer for corpus learning.

Polls CogServer state via WebSocket and prints a summary every N seconds.
No intervention, just observation.

Usage:
  python3 cog-monitor.py          # Poll every 10s
  python3 cog-monitor.py 30       # Poll every 30s
"""
import asyncio
import sys
import time

try:
    import websockets
except ImportError:
    print("pip3 install websockets", file=sys.stderr)
    sys.exit(1)

WS_URL = "ws://localhost:18080/scm"

async def query(ws, expr):
    """Send expression, get result (stripped of whitespace)."""
    await ws.send(expr)
    reply = await asyncio.wait_for(ws.recv(), timeout=10)
    return reply.strip()

async def monitor(interval):
    """Continuously poll CogServer and display learning state."""
    print(f"Monitoring CogServer at {WS_URL} every {interval}s")
    print(f"{'time':>8} {'sent':>7} {'learn':>6} {'rate':>6} {'surp':>7} {'base':>7} {'dirty':>7} {'cls':>5} {'lv':>3} {'s/s':>6} {'status':>6}")
    print("-" * 80)

    prev_sent = 0
    t0 = time.time()

    while True:
        try:
            async with websockets.connect(WS_URL, ping_interval=10, ping_timeout=30) as ws:
                while True:
                    sent = await query(ws, '*cl-total-sentences*')
                    learns = await query(ws, '*cl-total-learns*')
                    surprise = await query(ws, '(cl-average-surprise)')
                    baseline = await query(ws, '(cl-level-baseline *cl-current-level*)')
                    dirty = await query(ws, '*cl-dirty-count*')
                    classes = await query(ws, '(hash-count (const #t) *word-classes*)')
                    level = await query(ws, '*cl-current-level*')
                    running = await query(ws, '*cl-running*')

                    sent_i = int(sent)
                    learns_i = int(learns)
                    rate = f"{100.0 * learns_i / max(1, sent_i):.1f}%"
                    elapsed = time.time() - t0
                    sps = (sent_i - prev_sent) / interval if prev_sent > 0 else 0
                    status = "RUN" if running == '#t' else "STOP"

                    ts = f"{elapsed:.0f}s"
                    print(f"{ts:>8} {sent_i:>7} {learns_i:>6} {rate:>6} {float(surprise):>7.3f} {float(baseline):>7.3f} {int(dirty):>7} {int(classes):>5} {int(level):>3} {sps:>5.1f}/s {status:>6}")

                    prev_sent = sent_i

                    if running != '#t' and sent_i > 0:
                        print("\n[Loop stopped]")
                        return

                    await asyncio.sleep(interval)

        except (websockets.exceptions.ConnectionClosed, ConnectionRefusedError, OSError) as e:
            print(f"\n[Connection lost: {e} — retrying in 5s]")
            await asyncio.sleep(5)
        except KeyboardInterrupt:
            print("\n[Monitor stopped]")
            return

def main():
    interval = int(sys.argv[1]) if len(sys.argv) > 1 else 10
    try:
        asyncio.run(monitor(interval))
    except KeyboardInterrupt:
        print("\n[Monitor stopped]")

if __name__ == "__main__":
    main()