#!/opt/homebrew/bin/python3.11 """ Momentry Dashboard v2 — Direct DB/Qdrant/Redis queries, no subprocess blocking """ import json, os, platform, time from pathlib import Path from flask import Flask, jsonify, render_template_string import psycopg2 import urllib.request app = Flask(__name__) PROJECT = Path(__file__).resolve().parent.parent HOSTNAME = platform.node() IS_M5 = "MacBook" in HOSTNAME SYSTEM_ROLE = "M5 (MacBook Pro)" if IS_M5 else "M4 (Mac Mini)" SYSTEM_COLOR = "#58a6ff" if IS_M5 else "#f0883e" DB_URL = "postgresql://accusys@localhost:5432/momentry?host=/tmp" QDRANT_URL = "http://localhost:6333" LLM_URL = "http://localhost:8082/v1/chat/completions" EMBED_URL = "http://localhost:11436/v1/embeddings" COLLECTIONS = [ "momentry_dev_v1", "momentry_dev_stories", "momentry_dev_voice", "momentry_dev_faces", "sentence_story", "sentence_summary", "momentry_dev_rule1_v2", ] UUID = "aeed71342a899fe4b4c57b7d41bcb692" def db_query(sql, params=None): conn = psycopg2.connect(DB_URL) cur = conn.cursor() cur.execute(sql, params or ()) rows = cur.fetchall() conn.close() return rows def qdrant_get(path): try: resp = urllib.request.urlopen(f"{QDRANT_URL}{path}", timeout=5) return json.loads(resp.read()) except: return None def qdrant_count(col): r = qdrant_get(f"/collections/{col}") if r: return r.get("result", {}).get("points_count", 0) return -1 def qdrant_dim(col): r = qdrant_get(f"/collections/{col}") if r: cfg = r.get("result", {}).get("config", {}).get("params", {}).get("vectors", {}) return cfg.get("size", "?") return "?" @app.route("/") def index(): return render_template_string(TEMPLATE, SYSTEM_ROLE=SYSTEM_ROLE) @app.route("/api/all") def api_all(): return jsonify({ "system": {"hostname": HOSTNAME, "role": SYSTEM_ROLE, "is_m5": IS_M5}, "status": get_status(), "qdrant": get_qdrant_info(), "db": get_db_info(), "processes": get_processes(), }) @app.route("/api/status") def api_status(): return jsonify(get_status()) @app.route("/api/qdrant") def api_qdrant(): return jsonify(get_qdrant_info()) @app.route("/api/db") def api_db(): return jsonify(get_db_info()) @app.route("/api/processes") def api_processes(): return jsonify(get_processes()) def get_status(): """Pipeline checklist — direct DB queries""" t0 = time.time() stages = [] # 1. ASR file asr_path = f"/Users/accusys/momentry/output_dev/{UUID}.asr.json" asr_segs = 0 try: if os.path.exists(asr_path): d = json.load(open(asr_path)) asr_segs = len(d.get("segments", [])) except: pass stages.append({"name":"ASR","passed":asr_segs>0,"detail":f"{asr_segs} seg","elapsed":0.0}) # 2. ASRX file asrx_path = f"/Users/accusys/momentry/output_dev/{UUID}.asrx.json" asrx_segs = 0 try: if os.path.exists(asrx_path): d = json.load(open(asrx_path)) asrx_segs = len(d.get("segments", [])) except: pass stages.append({"name":"ASRX","passed":asrx_segs>0,"detail":f"{asrx_segs} seg","elapsed":0.0}) # 3. Sentence chunks try: cnt = db_query("SELECT count(*) FROM dev.chunks WHERE file_uuid=%s AND chunk_type='sentence'", (UUID,))[0][0] except: cnt = 0 stages.append({"name":"Sentence","passed":cnt>0,"detail":f"{cnt} chunks","elapsed":0.0}) # 4. Vectorization (Qdrant) v1 = qdrant_count("momentry_dev_v1") stages.append({"name":"Vectorize","passed":v1>0,"detail":f"{v1} Qdrant","elapsed":0.0}) # 5. Face traces try: traces = db_query("SELECT count(DISTINCT trace_id) FROM dev.face_detections WHERE file_uuid=%s AND trace_id IS NOT NULL", (UUID,))[0][0] faces = db_query("SELECT count(*) FROM dev.face_detections WHERE file_uuid=%s AND trace_id IS NOT NULL", (UUID,))[0][0] except: traces = faces = 0 stages.append({"name":"FaceTrace","passed":traces>0,"detail":f"{traces} traces, {faces} faces","elapsed":0.0}) # 6. TKG try: nodes = db_query("SELECT count(*) FROM dev.tkg_nodes WHERE file_uuid=%s", (UUID,))[0][0] edges = db_query("SELECT count(*) FROM dev.tkg_edges WHERE file_uuid=%s", (UUID,))[0][0] except: nodes = edges = 0 stages.append({"name":"TKG","passed":nodes>0,"detail":f"{nodes} nodes, {edges} edges","elapsed":0.0}) # 7. Trace chunks try: tc = db_query("SELECT count(*) FROM dev.chunks WHERE file_uuid=%s AND chunk_type='trace'", (UUID,))[0][0] except: tc = 0 stages.append({"name":"TraceChunks","passed":tc>0,"detail":f"{tc} chunks","elapsed":0.0}) # 8. Phase 1 release p1 = PROJECT / "release" / "phase1" / "latest" p1_ok = p1.exists() and (p1 / "RELEASE_INFO.txt").exists() p1_size = sum(f.stat().st_size for f in p1.rglob("*") if f.is_file()) // (1024*1024) if p1.exists() else 0 stages.append({"name":"Phase1","passed":p1_ok,"detail":f"{p1_size}MB","elapsed":0.0}) all_passed = all(s["passed"] for s in stages) return { "uuid": UUID, "passed": all_passed, "stages": stages, "checked_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), "total_elapsed": round(time.time() - t0, 1), "health": get_health(), } def get_health(): h = {} try: import os load = os.getloadavg() h["cpu_load_1m"] = round(load[0], 1) h["cpu_load_5m"] = round(load[1], 1) except: h["cpu_load_1m"] = h["cpu_load_5m"] = -1 try: import subprocess rss = 0 out = subprocess.run(["ps", "-A", "-o", "rss="], capture_output=True, text=True, timeout=5).stdout for line in out.strip().split("\n"): if line.strip(): rss += int(line.strip()) h["memory_used_mb"] = rss // 1024 if rss else 0 except: pass try: d = subprocess.run(["df", "-h", "/Users/accusys/momentry/output_dev"], capture_output=True, text=True, timeout=5).stdout.strip().split("\n")[-1].split() h["disk_use_pct"] = d[4] if len(d) > 4 else "?" h["disk_avail"] = d[3] if len(d) > 3 else "?" except: pass try: import torch h["gpu_available"] = torch.backends.mps.is_available() except: h["gpu_available"] = False services = {"postgresql": False, "qdrant": False, "embedding": False, "llm": False} try: conn = psycopg2.connect(DB_URL) conn.close() services["postgresql"] = True except: pass try: r = qdrant_get("/collections") services["qdrant"] = r is not None except: pass try: resp = urllib.request.urlopen("http://localhost:11436/health", timeout=3) services["embedding"] = resp.status == 200 except: pass try: req = urllib.request.Request(LLM_URL, data=json.dumps({"model":"google_gemma-4-26B-A4B-it-Q5_K_M.gguf","messages":[{"role":"user","content":"ping"}],"max_tokens":1}).encode(), headers={"Content-Type":"application/json"}, method="POST") resp = urllib.request.urlopen(req, timeout=3) services["llm"] = resp.status == 200 except: pass h["services"] = services return h def get_qdrant_info(): result = [] for col in COLLECTIONS: r = qdrant_get(f"/collections/{col}") if r: info = r.get("result", {}) cfg = info.get("config", {}).get("params", {}).get("vectors", {}) result.append({ "name": col, "points": info.get("points_count", 0), "dim": cfg.get("size", "?"), }) else: result.append({"name": col, "points": -1, "dim": "?"}) return result def get_db_info(): result = {} try: rows = db_query(""" SELECT 'videos', count(*) FROM dev.videos UNION ALL SELECT 'chunks', count(*) FROM dev.chunks UNION ALL SELECT 'face_detections', count(*) FROM dev.face_detections UNION ALL SELECT 'identities', count(*) FROM dev.identities UNION ALL SELECT 'tkg_nodes', count(*) FROM dev.tkg_nodes UNION ALL SELECT 'tkg_edges', count(*) FROM dev.tkg_edges """) for r in rows: result[r[0]] = r[1] except: pass return result def get_processes(): import subprocess scripts = ["clean_sentence_text.py", "generate_sentence_summaries.py"] result = {} for s in scripts: try: r = subprocess.run(["pgrep", "-f", s], capture_output=True, text=True, timeout=3) pids = [p.strip() for p in r.stdout.strip().split("\n") if p.strip()] if pids: r2 = subprocess.run(["ps", "-o", "etime=", "-p", pids[0]], capture_output=True, text=True, timeout=3) result[s] = {"pid": int(pids[0]), "elapsed": r2.stdout.strip()} else: result[s] = None except: result[s] = None return result TEMPLATE = """
| Loading... |