Download workbench/sources.py from psiloceyeben/oracle7: direct link, hf CLI and curl.
- Browser
- Download file 22.4 kB
-
https://huggingface.co/psiloceyeben/oracle7/resolve/main/workbench/sources.py
- Command line
-
hf download hf://psiloceyeben/oracle7/workbench/sources.py
-
curl -L -o sources.py https://huggingface.co/psiloceyeben/oracle7/resolve/main/workbench/sources.py
22.4 kB
| """sources - specialists made from things you point the Workbench at, with no programming: | |
| Scan this computer reads this machine (system, CPU, memory, disks, uptime, the processes using the most memory) | |
| Connect a server reads a server over SSH with your own key: a fixed list of read-only commands, nothing else runs | |
| Add files reads your tables (.csv/.tsv) and text (.txt/.md) and answers from them, quoting the place it read | |
| Each becomes a SOURCE READER: it answers only by reading its source at question time, and every answer names where the | |
| value was read (the call, the command, the file and row or line). It never infers past what it read. Like every | |
| specialist it only fills the core's declines. Its check is automatic (exam.run_source): it must stay in its lane on the | |
| core panel, leave identity questions alone, answer within budget, and read its own source back correctly. | |
| Privacy: a scan reads system figures and process NAMES (never file contents, documents or browsing). Server readings come | |
| from commands listed in SERVER_SCRIPT below. Files you add are copied into the specialist's own folder. Nothing is sent | |
| anywhere; the Workbench answers on 127.0.0.1 only.""" | |
| from __future__ import annotations | |
| import csv | |
| import os | |
| import platform | |
| import re | |
| import shutil | |
| import socket | |
| import subprocess | |
| import sys | |
| import time | |
| from pathlib import Path | |
| STOP = set("the a an of in on at to is was were are be by for with from and or what which who whom whose where when why how " | |
| "did does do it its this that these those i you he she they we my your his her their our me him them as than then " | |
| "there here can could would should will shall may might must much many most more about into over after before " | |
| "tell give find please say says said any some all have has had".split()) | |
| def gb(x): | |
| return "%.1f GB" % (x / 1e9) | |
| def dur(s): | |
| d, s = divmod(int(s), 86400) | |
| h, s = divmod(s, 3600) | |
| m = s // 60 | |
| return ("%d days " % d if d else "") + "%d h %d min" % (h, m) | |
| # ---------------------------------------------------------------- this computer | |
| def _run(cmd, timeout=8): | |
| try: | |
| return subprocess.run(cmd, capture_output=True, text=True, timeout=timeout).stdout | |
| except Exception: # noqa: BLE001 | |
| return "" | |
| def scan_local(): | |
| """readings: (entity, signal, value, text, source). Uses psutil when installed, else the platform's own calls.""" | |
| R, now = [], time.strftime("%H:%M:%S") | |
| add = lambda e, s, v, t, src: R.append({"entity": e, "signal": s, "value": v, "text": t, "source": "%s at %s" % (src, now)}) | |
| add("system", "os", platform.platform(), platform.platform(), "platform.platform()") | |
| add("system", "hostname", socket.gethostname(), socket.gethostname(), "socket.gethostname()") | |
| add("cpu", "cores", os.cpu_count(), "%s logical cores" % os.cpu_count(), "os.cpu_count()") | |
| try: | |
| import psutil # optional | |
| except Exception: # noqa: BLE001 | |
| psutil = None | |
| if psutil: | |
| vm = psutil.virtual_memory() | |
| add("memory", "used", vm.used, "%s used of %s (%.0f%%), %s available" % (gb(vm.used), gb(vm.total), vm.percent, gb(vm.available)), "psutil.virtual_memory()") | |
| add("cpu", "load", psutil.cpu_percent(interval=0.5), "CPU %.0f%% busy over half a second" % psutil.cpu_percent(interval=None), "psutil.cpu_percent()") | |
| add("system", "uptime", time.time() - psutil.boot_time(), "up %s" % dur(time.time() - psutil.boot_time()), "psutil.boot_time()") | |
| elif sys.platform == "win32": | |
| import ctypes | |
| class MS(ctypes.Structure): | |
| _fields_ = [("dwLength", ctypes.c_ulong), ("dwMemoryLoad", ctypes.c_ulong), ("ullTotalPhys", ctypes.c_ulonglong), | |
| ("ullAvailPhys", ctypes.c_ulonglong), ("ullTotalPageFile", ctypes.c_ulonglong), ("ullAvailPageFile", ctypes.c_ulonglong), | |
| ("ullTotalVirtual", ctypes.c_ulonglong), ("ullAvailVirtual", ctypes.c_ulonglong), ("ullAvailExtendedVirtual", ctypes.c_ulonglong)] | |
| ms = MS() | |
| ms.dwLength = ctypes.sizeof(MS) | |
| if ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(ms)): | |
| used = ms.ullTotalPhys - ms.ullAvailPhys | |
| add("memory", "used", used, "%s used of %s (%d%%), %s available" % (gb(used), gb(ms.ullTotalPhys), ms.dwMemoryLoad, gb(ms.ullAvailPhys)), | |
| "GlobalMemoryStatusEx") | |
| up = ctypes.windll.kernel32.GetTickCount64() / 1000 | |
| add("system", "uptime", up, "up %s" % dur(up), "GetTickCount64") | |
| else: | |
| try: | |
| mi = dict(re.findall(r"^(\w+):\s+(\d+)", Path("/proc/meminfo").read_text(), re.M)) | |
| tot, av = int(mi["MemTotal"]) * 1024, int(mi.get("MemAvailable", mi["MemFree"])) * 1024 | |
| add("memory", "used", tot - av, "%s used of %s (%.0f%%), %s available" % (gb(tot - av), gb(tot), 100 * (tot - av) / tot, gb(av)), "/proc/meminfo") | |
| up = float(Path("/proc/uptime").read_text().split()[0]) | |
| add("system", "uptime", up, "up %s" % dur(up), "/proc/uptime") | |
| except Exception: # noqa: BLE001 | |
| pass | |
| try: | |
| l1, l5, l15 = os.getloadavg() | |
| add("cpu", "load", l1, "load average %.2f, %.2f, %.2f (1, 5, 15 min) on %s cores" % (l1, l5, l15, os.cpu_count()), "os.getloadavg()") | |
| except Exception: # noqa: BLE001 | |
| pass | |
| # disks | |
| mounts = [] | |
| if sys.platform == "win32": | |
| mounts = ["%s:\\" % c for c in "CDEFGHIJKLMNOPQRSTUVWXYZ" if os.path.exists("%s:\\" % c)] | |
| else: | |
| try: | |
| for line in Path("/proc/mounts").read_text().splitlines(): | |
| dev, mnt, fs = line.split()[:3] | |
| if dev.startswith("/dev/") and fs not in ("squashfs", "tmpfs", "overlay") and "/snap/" not in mnt: | |
| mounts.append(mnt) | |
| except Exception: # noqa: BLE001 | |
| mounts = ["/"] | |
| for m in dict.fromkeys(mounts): | |
| try: | |
| u = shutil.disk_usage(m) | |
| add("disk " + m, "free", u.free, "%s has %s free of %s (%.0f%% used)" % (m, gb(u.free), gb(u.total), 100 * u.used / u.total), | |
| "shutil.disk_usage(%r)" % m) | |
| except Exception: # noqa: BLE001 | |
| pass | |
| # processes using the most memory (names only) | |
| top = [] | |
| if psutil: | |
| ps = [] | |
| for p in psutil.process_iter(["name", "memory_info"]): | |
| try: | |
| ps.append((p.info["memory_info"].rss, p.info["name"])) | |
| except Exception: # noqa: BLE001 | |
| pass | |
| top = sorted(ps, reverse=True)[:6] | |
| src = "psutil.process_iter()" | |
| elif sys.platform == "win32": | |
| agg = {} | |
| for row in csv.reader(_run(["tasklist", "/fo", "csv", "/nh"]).splitlines()): | |
| if len(row) >= 5: | |
| kb = int(re.sub(r"\D", "", row[4]) or 0) * 1024 | |
| agg[row[0]] = agg.get(row[0], 0) + kb | |
| top = sorted(((v, k) for k, v in agg.items()), reverse=True)[:6] | |
| src = "tasklist" | |
| else: | |
| agg = {} | |
| for line in _run(["ps", "-eo", "rss=,comm="]).splitlines(): | |
| parts = line.split(None, 1) | |
| if len(parts) == 2 and parts[0].isdigit(): | |
| agg[parts[1].strip()] = agg.get(parts[1].strip(), 0) + int(parts[0]) * 1024 | |
| top = sorted(((v, k) for k, v in agg.items()), reverse=True)[:6] | |
| src = "ps -eo rss,comm" | |
| if top: | |
| add("processes", "top_memory", top[0][1], "using the most memory: " + ", ".join("%s %s" % (n, gb(b)) for b, n in top), src) | |
| return R | |
| # ---------------------------------------------------------------- a server over SSH (read-only, fixed commands) | |
| SERVER_SCRIPT = r""" | |
| echo '@@host'; hostname | |
| echo '@@os'; (. /etc/os-release 2>/dev/null && echo "$PRETTY_NAME") ; uname -sr | |
| echo '@@uptime'; cat /proc/uptime | |
| echo '@@cores'; nproc | |
| echo '@@load'; cat /proc/loadavg | |
| echo '@@mem'; grep -E '^(MemTotal|MemAvailable):' /proc/meminfo | |
| echo '@@disk'; df -P -B1 -x tmpfs -x devtmpfs -x squashfs -x overlay 2>/dev/null | tail -n +2 | |
| echo '@@top'; ps -eo rss=,comm= --sort=-rss 2>/dev/null | head -6 | |
| echo '@@failed'; systemctl --failed --no-legend --plain 2>/dev/null | awk '{print $1}' | head -20 | |
| echo '@@end' | |
| """ | |
| HOST = re.compile(r"^[A-Za-z0-9.\-:\[\]]{1,253}$") | |
| USER = re.compile(r"^[a-z_][a-z0-9_\-]{0,31}$") | |
| def scan_server(host, user, port=22, key=""): | |
| if not HOST.match(host) or not USER.match(user) or not (0 < int(port) < 65536): | |
| raise ValueError("host, user or port is not valid") | |
| cmd = ["ssh", "-o", "BatchMode=yes", "-o", "ConnectTimeout=8", "-o", "StrictHostKeyChecking=accept-new", "-p", str(int(port))] | |
| if key: | |
| k = Path(key).expanduser() | |
| if not k.is_file(): | |
| raise ValueError("no key file at %s" % k) | |
| cmd += ["-i", str(k)] | |
| cmd += ["%s@%s" % (user, host), "sh", "-s"] | |
| try: | |
| r = subprocess.run(cmd, input=SERVER_SCRIPT, capture_output=True, text=True, timeout=25) | |
| except FileNotFoundError: | |
| raise ValueError("no ssh command on this computer (Windows: Settings > Apps > Optional features > OpenSSH Client)") | |
| except subprocess.TimeoutExpired: | |
| raise ValueError("the server did not answer within 25 s") | |
| if "@@end" not in r.stdout: | |
| raise ValueError("could not read the server: " + (r.stderr.strip().splitlines() or ["no output"])[-1][:200]) | |
| sec, cur = {}, None | |
| for line in r.stdout.splitlines(): | |
| if line.startswith("@@"): | |
| cur = line[2:] | |
| sec[cur] = [] | |
| elif cur: | |
| sec[cur].append(line) | |
| now, R, name = time.strftime("%H:%M:%S"), [], host | |
| add = lambda e, s, v, t, c: R.append({"entity": e, "signal": s, "value": v, "text": t, "source": "ssh %s: %s at %s" % (name, c, now)}) | |
| if sec.get("host"): | |
| add("system", "hostname", sec["host"][0], sec["host"][0], "hostname") | |
| if sec.get("os"): | |
| add("system", "os", " / ".join(x for x in sec["os"] if x), " / ".join(x for x in sec["os"] if x), "os-release, uname -sr") | |
| try: | |
| up = float(sec["uptime"][0].split()[0]) | |
| add("system", "uptime", up, "up %s" % dur(up), "/proc/uptime") | |
| except Exception: # noqa: BLE001 | |
| pass | |
| try: | |
| l1, l5, l15 = map(float, sec["load"][0].split()[:3]) | |
| cores = int(sec["cores"][0]) | |
| add("cpu", "cores", cores, "%d cores" % cores, "nproc") | |
| add("cpu", "load", l1, "load average %.2f, %.2f, %.2f (1, 5, 15 min) on %d cores" % (l1, l5, l15, cores), "/proc/loadavg") | |
| except Exception: # noqa: BLE001 | |
| pass | |
| try: | |
| mi = dict(re.findall(r"^(\w+):\s+(\d+)", "\n".join(sec["mem"]), re.M)) | |
| tot, av = int(mi["MemTotal"]) * 1024, int(mi["MemAvailable"]) * 1024 | |
| add("memory", "used", tot - av, "%s used of %s (%.0f%%), %s available" % (gb(tot - av), gb(tot), 100 * (tot - av) / tot, gb(av)), "/proc/meminfo") | |
| except Exception: # noqa: BLE001 | |
| pass | |
| for line in sec.get("disk", []): | |
| p = line.split() | |
| if len(p) >= 6 and p[1].isdigit(): | |
| tot, used, free, mnt = int(p[1]), int(p[2]), int(p[3]), p[5] | |
| add("disk " + mnt, "free", free, "%s has %s free of %s (%.0f%% used)" % (mnt, gb(free), gb(tot), 100 * used / max(1, tot)), "df -P") | |
| top = [(int(a.split(None, 1)[0]) * 1024, a.split(None, 1)[1]) for a in sec.get("top", []) if a.strip() and a.split()[0].isdigit()] | |
| if top: | |
| add("processes", "top_memory", top[0][1], "using the most memory: " + ", ".join("%s %s" % (n, gb(b)) for b, n in top), "ps --sort=-rss") | |
| failed = [x for x in sec.get("failed", []) if x.strip()] | |
| add("services", "failed", len(failed), ("%d failed services: %s" % (len(failed), ", ".join(failed))) if failed else "no failed services", | |
| "systemctl --failed") | |
| return R | |
| # ---------------------------------------------------------------- the machine reader (this computer or a server) | |
| ASK = [ # (question words, entity prefix, signal) - the more specific first | |
| (r"\b(using the most|biggest|heaviest|hogging|top) (?:memory|process|program|app)|\bwhat(?:'s| is) (?:using|eating)\b", "processes", "top_memory"), | |
| (r"\b(uptime|been (?:up|on|running)|up for|since (?:it|the last) (?:booted|restart)|rebooted|restarted|last boot)\b", "system", "uptime"), | |
| (r"\b(disk|drive|space|storage|full)\b", "disk", "free"), | |
| (r"\b(memory|ram)\b", "memory", "used"), | |
| (r"\b(cpu|processor|load|busy)\b", "cpu", "load"), | |
| (r"\b(cores?|threads?)\b", "cpu", "cores"), | |
| (r"\b(operating system|os version|which os|what os|windows version|linux version|distro)\b", "system", "os"), | |
| (r"\b(hostname|host name|computer name|machine name)\b", "system", "hostname"), | |
| (r"\bfailed services?\b|\bservices? (?:failed|failing|down)\b", "services", "failed"), | |
| ] | |
| class MachineOrgan: | |
| """answers questions about one machine by reading it live (cached 20 s); names the call or command it read""" | |
| def __init__(self, target="local", name="this computer", host="", user="", port=22, key=""): | |
| self.target, self.name, self.host, self.user, self.port, self.key = target, name, host, user, int(port or 22), key | |
| self.cache, self.t = None, 0 | |
| words = [re.escape(w) for w in re.findall(r"[a-z0-9]+", name.lower()) if w not in STOP] | |
| self.named = re.compile(r"\b(%s)\b" % r"\W*".join(words), re.I) if words else None | |
| def readings(self): | |
| if self.cache is None or time.time() - self.t > 20: | |
| self.cache = scan_local() if self.target == "local" else scan_server(self.host, self.user, self.port, self.key) | |
| self.t = time.time() | |
| return self.cache | |
| def answer(self, text, session): | |
| t = str(text or "") | |
| if self.target == "local": | |
| if not re.search(r"\b(my|this) (computer|pc|laptop|machine|desktop|mac|system)\b|\bon (my|this) (computer|pc|laptop|machine)\b", t, re.I): | |
| return None | |
| elif not (self.named and self.named.search(t)): | |
| return None | |
| hits = [(e, s) for pat, e, s in ASK if re.search(pat, t, re.I)] | |
| if not hits: | |
| return None | |
| e, s = hits[0] | |
| rows = [r for r in self.readings() if r["entity"].startswith(e) and r["signal"] == s] | |
| if not rows: | |
| return {"kind": "unreadable", "status": "withheld", "response": "I could not read that on %s." % self.name} | |
| if e == "disk": | |
| m = re.search(r"\b([a-z]):", t, re.I) or re.search(r"(/[\w/.-]*)", t) | |
| if m: | |
| want = (m.group(1).upper() + ":\\") if len(m.group(1)) == 1 else m.group(1) | |
| rows = [r for r in rows if r["entity"] == "disk " + want] or rows | |
| body = "; ".join(r["text"] for r in rows[:8]) | |
| srcs = "; ".join(dict.fromkeys(r["source"] for r in rows[:8])) | |
| return {"kind": "reading", "status": "answered", "subject": "my computer" if self.target == "local" else self.name, | |
| "response": "%s: %s. Read from %s." % (self.name[:1].upper() + self.name[1:], body, srcs)} | |
| # ---------------------------------------------------------------- your files | |
| def _words(s): | |
| return [w for w in re.findall(r"[a-z0-9][a-z0-9'.-]*", s.lower()) if w not in STOP] | |
| class FilesOrgan: | |
| """answers from files in its folder's files/ directory. | |
| TABLES: a question that names a row (its first-column value, or any cell that is unique in its column) and a column | |
| header gets that cell, with file, row and column. TEXT: a question that says "my notes / my files / my documents / | |
| according to <name>" gets the sentence that shares the most of its words, quoted with file and line.""" | |
| def __init__(self, folder="files", name="your files"): | |
| base = Path(os.environ.get("ORACLE7_WORKBENCH_LOCAL", str(Path(__file__).resolve().parent / "local"))) / "specialists" | |
| self.dir = base / folder | |
| self.name = name | |
| self.loaded_at, self.tables, self.lines = 0, [], [] | |
| def load(self): | |
| stamp = max([p.stat().st_mtime for p in self.dir.glob("*")] or [0]) | |
| if stamp == self.loaded_at: | |
| return | |
| self.tables, self.lines = [], [] | |
| for p in sorted(self.dir.glob("*")): | |
| try: | |
| if p.suffix.lower() in (".csv", ".tsv"): | |
| with open(p, encoding="utf-8-sig", errors="replace", newline="") as fh: | |
| rows = list(csv.reader(fh, delimiter="\t" if p.suffix.lower() == ".tsv" else ",")) | |
| if len(rows) >= 2: | |
| self.tables.append((p.name, rows[0], rows[1:])) | |
| elif p.suffix.lower() in (".txt", ".md"): | |
| for i, line in enumerate(p.read_text(encoding="utf-8", errors="replace").splitlines(), 1): | |
| for sent in re.split(r"(?<=[.!?])\s+", line.strip()): | |
| if len(sent) > 12: | |
| self.lines.append((p.name, i, sent)) | |
| except Exception: # noqa: BLE001 | |
| pass | |
| self.loaded_at = stamp | |
| def answer(self, text, session): | |
| self.load() | |
| t = str(text or "") | |
| tl = " %s " % " ".join(re.findall(r"[a-z0-9][a-z0-9'.-]*", t.lower())) | |
| best = None | |
| for fname, hdr, rows in self.tables: | |
| cols = [(j, h) for j, h in enumerate(hdr) if j > 0 and h.strip() and " %s " % " ".join(_words(h)) in tl and _words(h)] | |
| if not cols: | |
| continue | |
| for k in range(len(hdr)): | |
| vals = [r[k].strip() for r in rows if k < len(r)] | |
| for n, r in enumerate(rows, 2): | |
| if k < len(r) and r[k].strip() and vals.count(r[k].strip()) == 1 and len(r[k].strip()) > 1 and \ | |
| " %s " % " ".join(re.findall(r"[a-z0-9][a-z0-9'.-]*", r[k].lower())) in tl: | |
| for j, h in cols: | |
| if j != k and j < len(r) and r[j].strip(): | |
| cand = (len(h) + len(r[k]), fname, n, h, r[k].strip(), r[j].strip()) | |
| best = max(best, cand) if best else cand | |
| if best: | |
| _, fname, n, h, key, val = best | |
| return {"kind": "from_file", "status": "answered", "subject": key, | |
| "response": "%s. From %s, row %d (%s), column \"%s\"." % (val, fname, n, key, h)} | |
| if self.lines and re.search(r"\b(my|our) (notes|files?|documents?|docs|records|manual|handbook)\b|\baccording to\b", t, re.I): | |
| q = set(_words(t)) - {"notes", "note", "file", "files", "document", "documents", "docs", "records", "according", "manual", "handbook"} | |
| scored = sorted(((len(q & set(_words(s))), -len(s), f, i, s) for f, i, s in self.lines), reverse=True) | |
| if scored and scored[0][0] >= 2: | |
| k, _, f, i, s = scored[0] | |
| return {"kind": "from_file", "status": "answered", "response": "\"%s\" (%s, line %d)" % (s, f, i)} | |
| return {"kind": "not_in_files", "status": "withheld", "response": "Nothing in %s says that." % self.name} | |
| return None | |
| # ---------------------------------------------------------------- a conversation, kept as a temporary specialist | |
| RECALL = re.compile(r"\b(earlier|before|previously|last time|you (?:said|told me|mentioned|answered)|we (?:talked|discussed|said|covered)|" | |
| r"remind me|in (?:this|our|that) (?:conversation|chat)|again|what was (?:the|your) answer)\b", re.I) | |
| RECALL_FILLER = set("discuss discussed talk talked said say tell told mention mentioned earlier before previously remind again " | |
| "answer answered conversation chat last time we".split()) | |
| class ConversationOrgan: | |
| """answers from one saved conversation: a question that asks to recall it ("what did you say earlier about the boiler?") | |
| or that repeats one of its questions gets the earlier answer, quoted with its date and the route that produced it. | |
| Only answered turns are kept as things it can recall; declines are not facts.""" | |
| def __init__(self, conv="", name="this conversation"): | |
| base = Path(os.environ.get("ORACLE7_WORKBENCH_LOCAL", str(Path(__file__).resolve().parent / "local"))) | |
| self.path, self.name, self.loaded_at, self.turns = base / "conversations" / ("%s.json" % re.sub(r"[^a-z0-9]", "", conv)), name, 0, [] | |
| def load(self): | |
| try: | |
| st = self.path.stat().st_mtime | |
| except OSError: | |
| self.turns = [] | |
| return | |
| if st != self.loaded_at: | |
| import json | |
| d = json.loads(self.path.read_text(encoding="utf-8")) | |
| self.turns = [t for t in d.get("turns", []) if t.get("status") == "answered" and not str(t.get("path", "")).startswith("workbench:")] | |
| self.loaded_at = st | |
| def best(self, text): | |
| q = set(_words(text)) - RECALL_FILLER | |
| scored = [] | |
| for t in self.turns: | |
| tq, ta = set(_words(t["q"])), set(_words(t.get("response", ""))) | |
| scored.append((len(q & tq), len(q & (tq | ta)), -t.get("t", 0), t)) | |
| scored.sort(key=lambda x: x[:3], reverse=True) | |
| return scored[0] if scored else None | |
| def takes(self, text): | |
| """only a question that asks to RECALL this conversation, and shares a content word with one of its answered turns; | |
| a repeated question is not taken - the core answers it fresh, in its own context""" | |
| if not RECALL.search(str(text or "")): | |
| return False | |
| self.load() | |
| b = self.best(text) | |
| return bool(b) and b[1] >= 1 | |
| def answer(self, text, session): | |
| if not self.takes(text): | |
| return None | |
| _, _, _, t = self.best(text) | |
| when = time.strftime("%d %b %Y %H:%M", time.localtime(t.get("t", 0))) | |
| return {"kind": "recalled", "status": "answered", | |
| "response": "Earlier in \"%s\" (%s) you asked \"%s\". The answer then was: %s [route: %s]" | |
| % (self.name, when, t["q"], str(t.get("response", "")).strip(), str(t.get("path", ""))[:80])} | |