Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .env

This file was deleted.

2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
# Copy this file to .env and insert your actual API keys.

GROQ_API_KEY=your_groq_api_key_here
GROQ_MODEL=llama-3.3-70b-versatile
GROQ_MODEL=qwen/qwen3.8-27b
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ data/models/*.pt
data/traces/*
!data/traces/.gitkeep
!data/models/.gitkeep
ui-overhead-results/

# Logs
logs/
Expand All @@ -47,3 +48,5 @@ dashboard.log
.vscode/
.idea/

/Evolution of Blackbox
.streamlit
197 changes: 197 additions & 0 deletions benchmark_ui_overhead.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
"""Measure CogniOS process-tree and system overhead in browser or desktop mode."""

from __future__ import annotations

import argparse
import csv
import json
import os
import statistics
import time
from datetime import datetime, timezone
from pathlib import Path

import psutil


def summarize(samples: list[dict]) -> dict:
fields = (
"system_cpu_pct", "app_cpu_pct", "app_rss_mb", "app_processes",
"memory_available_mb", "swap_used_mb", "disk_read_mb_s",
"disk_write_mb_s", "network_recv_mb_s", "network_sent_mb_s",
)
if not samples:
return {key: {"average": 0.0, "peak": 0.0, "minimum": 0.0} for key in fields}

return {
key: {
"average": round(statistics.mean(sample[key] for sample in samples), 2),
"peak": round(max(sample[key] for sample in samples), 2),
"minimum": round(min(sample[key] for sample in samples), 2),
}
for key in fields
}


def self_test() -> None:
result = summarize([
{"system_cpu_pct": 10, "app_cpu_pct": 20, "app_rss_mb": 100, "app_processes": 2,
"memory_available_mb": 1000, "swap_used_mb": 0, "disk_read_mb_s": 1,
"disk_write_mb_s": 2, "network_recv_mb_s": 3, "network_sent_mb_s": 4},
{"system_cpu_pct": 30, "app_cpu_pct": 40, "app_rss_mb": 200, "app_processes": 4,
"memory_available_mb": 800, "swap_used_mb": 1, "disk_read_mb_s": 3,
"disk_write_mb_s": 4, "network_recv_mb_s": 5, "network_sent_mb_s": 6},
])
assert result["system_cpu_pct"] == {"average": 20, "peak": 30, "minimum": 10}
assert result["app_rss_mb"] == {"average": 150, "peak": 200, "minimum": 100}
assert summarize([])["system_cpu_pct"] == {"average": 0.0, "peak": 0.0, "minimum": 0.0}


def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--mode", choices=("browser", "desktop"))
parser.add_argument("--pid", type=int, help="PID of the CogniOS main.py process")
parser.add_argument("--ui-pid", type=int, help="Optional browser PID; include its process tree")
parser.add_argument("--duration", type=int, default=60, help="Sample duration in seconds")
parser.add_argument("--interval", type=float, default=1, help="Sample interval in seconds")
parser.add_argument("--output", type=Path, default=Path("ui-overhead-results"))
parser.add_argument("--self-test", action="store_true")
args = parser.parse_args()

if args.self_test:
self_test()
print("Benchmark summary check passed.")
return
if not args.mode or not args.pid or args.duration < 1 or args.interval <= 0:
parser.error("--mode, --pid, positive --duration, and positive --interval are required")

try:
roots = [psutil.Process(args.pid)]
if args.ui_pid:
roots.append(psutil.Process(args.ui_pid))
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess) as exc:
parser.error(f"Could not access process PID(s): {exc}")
for proc in roots:
proc.cpu_percent(None)
psutil.cpu_percent(None)

args.output.mkdir(parents=True, exist_ok=True)
stamp = datetime.now().strftime("%Y%m%d-%H%M%S")
stem = f"ui-overhead-{args.mode}-{stamp}"
csv_path = args.output / f"{stem}.csv"
json_path = args.output / f"{stem}.json"
columns = (
"timestamp", "elapsed_s", "mode", "system_cpu_pct", "memory_used_mb",
"memory_available_mb", "swap_used_mb", "disk_read_mb_s", "disk_write_mb_s",
"network_recv_mb_s", "network_sent_mb_s", "app_cpu_pct", "app_rss_mb",
"app_processes", "pid", "parent_pid", "process_name", "process_cpu_pct",
"process_rss_mb", "threads", "read_mb", "write_mb",
)
totals = []
started_at = datetime.now(timezone.utc)
started = time.monotonic()
previous_elapsed = 0.0
previous_disk = psutil.disk_io_counters()
previous_network = psutil.net_io_counters()

with csv_path.open("w", newline="", encoding="utf-8") as output:
writer = csv.DictWriter(output, fieldnames=columns)
writer.writeheader()
while time.monotonic() - started < args.duration:
time.sleep(min(args.interval, max(0, args.duration - (time.monotonic() - started))))
now = datetime.now(timezone.utc).isoformat()
app_cpu = app_rss = process_count = 0
rows = []
seen = set()

for root in roots:
try:
processes = [root, *root.children(recursive=True)]
except psutil.Error:
processes = []
for proc in processes:
if proc.pid in seen:
continue
seen.add(proc.pid)
try:
info = proc.as_dict(attrs=["pid", "ppid", "name", "memory_info", "num_threads"])
cpu = proc.cpu_percent(None)
io = proc.io_counters()
rss = info["memory_info"].rss / (1024 * 1024)
app_cpu += cpu
app_rss += rss
process_count += 1
rows.append({
"pid": proc.pid, "parent_pid": info["ppid"], "process_name": info["name"],
"process_cpu_pct": round(cpu, 2), "process_rss_mb": round(rss, 2),
"threads": info["num_threads"],
"read_mb": round(io.read_bytes / (1024 * 1024), 2) if io else "",
"write_mb": round(io.write_bytes / (1024 * 1024), 2) if io else "",
})
except (psutil.Error, OSError):
continue

memory = psutil.virtual_memory()
system_cpu = psutil.cpu_percent(None)
elapsed = time.monotonic() - started
interval = max(elapsed - previous_elapsed, 0.001)
disk = psutil.disk_io_counters()
network = psutil.net_io_counters()
disk_read = (
max(0, disk.read_bytes - previous_disk.read_bytes) / 1048576 / interval
if disk and previous_disk else 0
)
disk_write = (
max(0, disk.write_bytes - previous_disk.write_bytes) / 1048576 / interval
if disk and previous_disk else 0
)
network_recv = (
max(0, network.bytes_recv - previous_network.bytes_recv) / 1048576 / interval
if network and previous_network else 0
)
network_sent = (
max(0, network.bytes_sent - previous_network.bytes_sent) / 1048576 / interval
if network and previous_network else 0
)
previous_elapsed, previous_disk, previous_network = elapsed, disk, network
sample = {
"system_cpu_pct": system_cpu,
"app_cpu_pct": app_cpu,
"app_rss_mb": app_rss,
"app_processes": process_count,
"memory_available_mb": round(memory.available / (1024 * 1024), 2),
"swap_used_mb": round(psutil.swap_memory().used / (1024 * 1024), 2),
"disk_read_mb_s": round(disk_read, 3),
"disk_write_mb_s": round(disk_write, 3),
"network_recv_mb_s": round(network_recv, 3),
"network_sent_mb_s": round(network_sent, 3),
}
totals.append(sample)
shared = {
"timestamp": now,
"elapsed_s": round(elapsed, 2),
"mode": args.mode,
**sample,
"memory_used_mb": round(memory.used / (1024 * 1024), 2),
}
for row in rows or [{}]:
writer.writerow({**shared, **row})
output.flush()

report = {
"mode": args.mode,
"started_at": started_at.isoformat(),
"duration_seconds": round(time.monotonic() - started, 2),
"sample_interval_seconds": args.interval,
"samples": len(totals),
"process_roots": [proc.pid for proc in roots],
"summary": summarize(totals),
"rss_note": "Process-tree RSS is summed and can double-count shared memory.",
"csv": str(csv_path),
}
json_path.write_text(json.dumps(report, indent=2), encoding="utf-8")
print(json.dumps(report, indent=2))


if __name__ == "__main__":
main()
1 change: 1 addition & 0 deletions check_requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"scikit-learn": "sklearn",
"python-dotenv": "dotenv",
"streamlit-autorefresh": "streamlit_autorefresh",
"pyside6": "PySide6",
"umap-learn": "umap",
"google-genai": "google.genai",
"pyyaml": "yaml",
Expand Down
3 changes: 2 additions & 1 deletion cognios_as_daemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ def run_layer1_loop(stop_event):
metrics['net_errs'],
metrics['net_drops'],
json.dumps(metrics['process_data']),
sum(metrics['num_threads']) if isinstance(metrics.get('num_threads'), list) else int(metrics.get('num_threads') or 0)
sum(metrics['num_threads']) if isinstance(metrics.get('num_threads'), list) else int(metrics.get('num_threads') or 0),
metrics.get('udp_tcp_ratio', 0.10)
)

# --- Write to BlackBox rolling-window DB ---
Expand Down
48 changes: 46 additions & 2 deletions collectors/layer1_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,33 @@
import utils
from utils.helpers import rate_mb_s

# ---------------------------------------------------------------------------
# HARDCODED EXCLUSION — CogniOS self-telemetry & browser noise suppression
# Chrome/Chromium entries reflect the Streamlit dashboard, not real user
# browser activity. CogniOS daemon Python processes would corrupt the
# compiler_active signal and inflate CPU readings in FocusOS inference.
# ---------------------------------------------------------------------------
_EXCLUDE_EXACT_NAMES: frozenset = frozenset({
"chrome", "chromium", "chromium-browser",
"google-chrome", "google-chrome-stable",
"chrome_crashpad_handler", "nacl_helper",
"chrome_sandbox",
})
_EXCLUDE_SUBSTRINGS: tuple = (
"chrome",
"chromium",
"cognios", # any cognios_as_daemon / cognios_* variant
"streamlit", # dashboard runner itself
)


def _is_excluded_process(name: str) -> bool:
"""Return True if this process should be silently dropped from telemetry."""
n = (name or "").lower()
if n in _EXCLUDE_EXACT_NAMES:
return True
return any(sub in n for sub in _EXCLUDE_SUBSTRINGS)

# a dictionary to store previous values
_last = {
"time": None,
Expand Down Expand Up @@ -37,8 +64,13 @@ def collect_layer1_metrics():
try:
cpu = round(p.cpu_percent(), 2)
mem = round(p.info.get("memory_percent") or 0.0, 2)
proc_name = p.info.get("name") or ""
# Hardcoded exclusion: skip Chrome/browser and CogniOS daemon
# processes so they never corrupt telemetry or FocusOS inference.
if _is_excluded_process(proc_name):
continue
if cpu > 0.5 or mem > 0.5:
process_data.append((p.info.get("name"), cpu, mem))
process_data.append((proc_name, cpu, mem))
num_threads.append(p.num_threads())
except (psutil.NoSuchProcess, psutil.AccessDenied):
pass
Expand Down Expand Up @@ -173,6 +205,17 @@ def collect_layer1_metrics():
except Exception:
pass


# Calculate UDP/TCP Ratio for FocusOS
import socket
udp_tcp_ratio = 0.10
try:
conns = psutil.net_connections(kind='inet')
tcp_count = sum(1 for c in conns if c.type == socket.SOCK_STREAM)
udp_count = sum(1 for c in conns if c.type == socket.SOCK_DGRAM)
udp_tcp_ratio = float(round(udp_count / max(1, tcp_count), 4))
except (psutil.AccessDenied, PermissionError):
pass
return {
"timestamp": timestamp,
"cpu_usage_percent": cpu_usage_percent,
Expand Down Expand Up @@ -218,7 +261,8 @@ def collect_layer1_metrics():
"max_temp":temp_max,
"battery_percent":battery_percent,
"process_data":process_data,
"num_threads":num_threads
"num_threads":num_threads,
"udp_tcp_ratio": udp_tcp_ratio
}

if __name__ == "__main__":
Expand Down
4 changes: 2 additions & 2 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
DB_PATH = os.path.join(BASE_DIR, "cognios_telemetry.db")
SLIDING_WIND_N = 15
SLIDING_WIND_N = 30
AUTO_REFRESH = 2 # seconds between dashboard refreshes


Expand All @@ -26,7 +26,7 @@
# Blackbox config

GROQ_API_KEY = os.getenv("GROQ_API_KEY", "")
GROQ_MODEL = os.getenv("GROQ_MODEL", "llama-3.3-70b-versatile")
GROQ_MODEL = os.getenv("GROQ_MODEL", "qwen/qwen3.8-27b")
BLACKBOX_DB_PATH = "blackbox/blackbox.db"
BLACKBOX_WINDOW_SEC = 1800 # 30 minutes
BLACKBOX_CRASH_GAP_SEC = 30 # SIGKILL-only fallback, not the primary check
Expand Down
Loading