Skip to content
Merged
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
56 changes: 27 additions & 29 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,31 +1,29 @@
[tool.black]
line-length = 88
target-version = ['py310']
include = '\.pyi?$'
extend-exclude = '''
/(
# directories
\.eggs
| \.git
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| venv
| _build
| buck-out
| build
| dist
)/
'''
# Project tooling config. Kept minimal on purpose: pytest + coverage settings
# live here so `pytest` and `pytest --cov` behave the same locally and in CI.

[tool.isort]
profile = "black"
multi_line_output = 3
line_length = 88
known_first_party = ["app"]
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-q"

[tool.pylint]
load-plugins = ["pylint_flask"]
disable = ["C0114", "C0116"]
max-line-length = 88
[tool.coverage.run]
# Measure only our own package (not the venv or third-party code), and track
# branch coverage (did both sides of each `if` run?) which is more honest than
# plain line coverage.
source = ["kernel_ai"]
branch = true
omit = [
"*/__main__.py",
"*/tests/*",
]

[tool.coverage.report]
show_missing = true
skip_empty = true
# Lines we don't expect (or want) to cover — excluded from the % so the number
# reflects real gaps, not boilerplate.
exclude_lines = [
"pragma: no cover",
"if __name__ == .__main__.:",
"raise NotImplementedError",
"if TYPE_CHECKING:",
]
26 changes: 26 additions & 0 deletions sonar-project.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# SonarCloud configuration.
#
# One-time setup on https://sonarcloud.io (free for public repos):
# 1. Log in with GitHub, import this repository.
# 2. Copy your Organization key + Project key into the two fields below.
# 3. In GitHub repo Settings -> Secrets -> Actions, add SONAR_TOKEN
# (SonarCloud -> My Account -> Security -> Generate token).
# The CI workflow (.github/workflows/sonarcloud.yaml) does the rest.

sonar.organization=REPLACE_WITH_SONARCLOUD_ORG
sonar.projectKey=REPLACE_WITH_PROJECT_KEY
sonar.projectName=kernel-ai

# What to analyse.
sonar.sources=kernel_ai,static/js
sonar.tests=tests

# Python coverage report produced by pytest-cov (see the CI workflow).
sonar.python.version=3.10, 3.11
sonar.python.coverage.reportPaths=coverage.xml

# Keep the analysis focused on our code.
sonar.exclusions=venv/**,mldata/**,**/*.min.js,static/js/**/vendor/**
sonar.coverage.exclusions=tests/**,static/js/**

sonar.sourceEncoding=UTF-8
234 changes: 4 additions & 230 deletions static/js/processes-belt.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Processes Subsystem Visualization
// Version: 31 — FLOW mode: strand bundle rising into a wireframe cube
// Version: 34removed FLOW mode

debugLog('🧠 processes-belt.js v31: Script loading...');
debugLog('🧠 processes-belt.js v34: Script loading...');

class ProcessesSubsystemVisualization {
constructor() {
Expand All @@ -15,7 +15,7 @@ class ProcessesSubsystemVisualization {
this.telemetry = null;
this.tick = 0;
this.nodeLayout = new Map();
this.layoutMode = 'flow';
this.layoutMode = 'microscope';
this.modeButtons = new Map();
this.edgeFilter = 'all';
this.filterButtons = new Map();
Expand Down Expand Up @@ -83,7 +83,6 @@ class ProcessesSubsystemVisualization {
position:absolute;top:18px;left:18px;display:flex;gap:8px;z-index:1001;
`;
const modes = [
{ key: 'flow', label: 'FLOW' },
{ key: 'microscope', label: 'MICROSCOPE' },
{ key: 'temporal', label: '3-LAYER GRAPH' },
{ key: 'radial', label: 'RADIAL GRAPH' },
Expand All @@ -108,7 +107,7 @@ class ProcessesSubsystemVisualization {
}

setLayoutMode(modeKey) {
this.layoutMode = ['flow', 'temporal', 'radial', 'microscope', 'wireframe'].includes(modeKey) ? modeKey : 'temporal';
this.layoutMode = ['temporal', 'radial', 'microscope', 'wireframe'].includes(modeKey) ? modeKey : 'microscope';
this.modeButtons.forEach((btn, key) => {
const active = key === this.layoutMode;
btn.style.background = active ? 'rgba(32, 52, 81, 0.92)' : 'rgba(8,12,18,0.86)';
Expand Down Expand Up @@ -2187,11 +2186,6 @@ class ProcessesSubsystemVisualization {
this.ctx.clearRect(0, 0, w, h);
this.tick += 1;

if (this.layoutMode === 'flow') {
this.drawFlowScene(w, h);
return;
}

const gap = 16;
const top = 58;
const statsH = this.layoutMode === 'microscope' ? 74 : 98;
Expand Down Expand Up @@ -2242,226 +2236,6 @@ class ProcessesSubsystemVisualization {
this.drawNeuralGraph(gap, graphY, w - gap * 2, graphH);
}

// Map a horizontal position 0..1 to the reference's hue ramp:
// left cyan/teal -> centre amber -> right red/pink (through pure red).
_flowHue(f) {
f = Math.max(0, Math.min(1, f));
let hue;
if (f <= 0.5) {
hue = 188 + (45 - 188) * (f / 0.5); // cyan -> amber
} else {
hue = 45 + (-70) * ((f - 0.5) / 0.5); // amber -> red -> pink
}
return ((hue % 360) + 360) % 360;
}

// Rotate a point (Y then X) and apply a simple perspective projection.
_project3d(x, y, z, cx, cy, rotY, rotX, focal) {
const cY = Math.cos(rotY), sY = Math.sin(rotY);
const x1 = x * cY - z * sY;
const z1 = x * sY + z * cY;
const cX = Math.cos(rotX), sX = Math.sin(rotX);
const y2 = y * cX - z1 * sX;
const z2 = y * sX + z1 * cX;
const scale = focal / (focal + z2);
return { x: cx + x1 * scale, y: cy - y2 * scale, scale, z: z2 };
}

// FLOW mode: process strands rise from a tight bundle at the bottom and fan
// up into the edges/faces of a slowly-rotating wireframe cube (one-to-one
// with the reference). Visual-first: uses live telemetry when present,
// otherwise a demo field so it always looks alive. Field mapping (width /
// colour <- real metrics) is intentionally coarse for now.
drawFlowScene(w, h) {
const ctx = this.ctx;
const t = this.tick * 0.016; // approx seconds
if (!this.flowStreams) this.flowStreams = new Map();
this.nodeHitAreas = [];

// Background: near-black vertical wash + soft vignette.
const bg = ctx.createLinearGradient(0, 0, 0, h);
bg.addColorStop(0, '#05070b');
bg.addColorStop(0.5, '#070a10');
bg.addColorStop(1, '#02040a');
ctx.fillStyle = bg;
ctx.fillRect(0, 0, w, h);

// Drifting bokeh for depth.
if (!this.flowBokeh) {
this.flowBokeh = Array.from({ length: 46 }, () => ({
x: Math.random() * w, y: Math.random() * h,
r: 2 + Math.random() * 26, a: 0.04 + Math.random() * 0.10,
vy: 4 + Math.random() * 12, f: Math.random()
}));
}
ctx.globalCompositeOperation = 'lighter';
this.flowBokeh.forEach((b) => {
b.y -= b.vy * 0.016;
if (b.y < -40) { b.y = h + 40; b.x = Math.random() * w; b.f = Math.random(); }
const hue = this._flowHue(b.f);
const g = ctx.createRadialGradient(b.x, b.y, 0, b.x, b.y, b.r);
g.addColorStop(0, `hsla(${hue},80%,65%,${b.a})`);
g.addColorStop(1, `hsla(${hue},80%,65%,0)`);
ctx.fillStyle = g;
ctx.beginPath(); ctx.arc(b.x, b.y, b.r, 0, Math.PI * 2); ctx.fill();
});

// Build per-stream specs from telemetry (or a demo field).
const nodes = Array.isArray(this.telemetry?.neural_graph?.nodes)
? this.telemetry.neural_graph.nodes : [];
let specs;
if (nodes.length) {
specs = nodes.slice(0, 90).map((n, i) => {
const pid = Number(n.pid || 0) || -(i + 1);
const press = Number(n.syscall_pressure || 0);
const m = press > 0
? Math.max(0.18, Math.min(1, press / 100))
: 0.2 + this.stableUnit(pid) * 0.7;
return { key: pid, m };
});
} else {
specs = Array.from({ length: 72 }, (_, i) => ({
key: -(i + 1), m: 0.3 + this.stableUnit(i + 1) * 0.65
}));
}

const count = specs.length;
const seen = new Set();

// --- slowly-rotating wireframe cube the strands rise into ---
const cx = w * 0.5;
const cy = h * 0.40;
const s = Math.min(w, h) * 0.24;
const focal = s * 3.6;
this.flowCubeRot = (this.flowCubeRot || 0) + 0.0035;
const rotY = this.flowCubeRot;
const rotX = 0.5; // tilt so we look slightly up into it from the bundle
const project = (p) => this._project3d(p[0] * s, p[1] * s, p[2] * s, cx, cy, rotY, rotX, focal);

const V = [
[-1, -1, -1], [1, -1, -1], [1, 1, -1], [-1, 1, -1],
[-1, -1, 1], [1, -1, 1], [1, 1, 1], [-1, 1, 1]
];
const E = [
[0, 1], [1, 2], [2, 3], [3, 0], [4, 5], [5, 6],
[6, 7], [7, 4], [0, 4], [1, 5], [2, 6], [3, 7]
];
const projV = V.map(project);

// Attachment targets: cube vertices + points subdividing each edge, so
// strands land on the edges/faces of the cube.
if (!this.flowCubeTargets) {
const targets = V.map((v) => v.slice());
E.forEach(([a, b]) => {
for (let k = 1; k <= 3; k++) {
const tk = k / 4;
targets.push([
V[a][0] + (V[b][0] - V[a][0]) * tk,
V[a][1] + (V[b][1] - V[a][1]) * tk,
V[a][2] + (V[b][2] - V[a][2]) * tk
]);
}
});
this.flowCubeTargets = targets;
}
const projTargets = this.flowCubeTargets.map(project);

// --- bundle of strands: from a tight bottom knot up to cube targets ---
const bundleX = w * 0.5;
const bundleY = h * 0.99;

specs.forEach((spec) => {
seen.add(spec.key);
const u = this.stableUnit(spec.key < 0 ? (-spec.key + 777) : spec.key);
const targetIdx = Math.floor(u * projTargets.length) % projTargets.length;
let st = this.flowStreams.get(spec.key);
if (!st) {
st = { growth: 0, m: spec.m, phase: u * Math.PI * 2, speed: 0.5 + u * 0.7, targetIdx };
this.flowStreams.set(spec.key, st);
}
st.m += (spec.m - st.m) * 0.05; // ease metric changes
st.growth += (1 - st.growth) * 0.04; // birth: grow out of the bundle

const target = projTargets[st.targetIdx];
const hue = this._flowHue(Math.max(0, Math.min(1, target.x / w)));
const width = 1 + st.m * 2.6;

// Origin jitter forms the bundle ("пучок") at the bottom centre.
const ox = bundleX + (u - 0.5) * w * 0.045;
const oy = bundleY;
// Growth eases the live endpoint up out of the knot toward the cube.
const ex = ox + (target.x - ox) * st.growth;
const ey = oy + (target.y - oy) * st.growth;
// Control: rise vertically out of the bundle, then curve to the cube.
const c1x = ox + (ex - ox) * 0.2;
const c1y = oy - (oy - ey) * 0.7;
const swayAmp = (5 + u * 12) * (0.5 + st.m * 0.7);

const N = 26;
const pts = [];
for (let i = 0; i <= N; i++) {
const tt = i / N;
const mt = 1 - tt;
let xx = mt * mt * ox + 2 * mt * tt * c1x + tt * tt * ex;
const yy = mt * mt * oy + 2 * mt * tt * c1y + tt * tt * ey;
xx += Math.sin(t * st.speed + st.phase + tt * Math.PI * 1.4) * swayAmp * tt;
pts.push([xx, yy]);
}

// Body: gradient dim at the bundle, bright where it meets the cube.
const grad = ctx.createLinearGradient(ox, oy, ex, ey);
grad.addColorStop(0, `hsla(${hue},92%,55%,0)`);
grad.addColorStop(0.3, `hsla(${hue},92%,58%,0.22)`);
grad.addColorStop(1, `hsla(${hue},95%,68%,0.9)`);
ctx.strokeStyle = grad;
ctx.lineWidth = width;
ctx.lineCap = 'round';
ctx.beginPath();
ctx.moveTo(pts[0][0], pts[0][1]);
for (let i = 1; i < pts.length; i++) ctx.lineTo(pts[i][0], pts[i][1]);
ctx.stroke();

// Glow where the strand meets the cube.
const tip = pts[pts.length - 1];
const tr = 5 + st.m * 7;
const tg = ctx.createRadialGradient(tip[0], tip[1], 0, tip[0], tip[1], tr);
tg.addColorStop(0, `hsla(${hue},95%,82%,0.95)`);
tg.addColorStop(1, `hsla(${hue},95%,70%,0)`);
ctx.fillStyle = tg;
ctx.beginPath(); ctx.arc(tip[0], tip[1], tr, 0, Math.PI * 2); ctx.fill();

// Travelling shimmer riding up the strand.
const sh = ((t * 0.35 * st.speed) + u) % 1;
const sp = pts[Math.min(N, Math.max(0, Math.floor(sh * N)))];
ctx.fillStyle = `hsla(${hue},100%,90%,${0.5 * Math.sin(sh * Math.PI)})`;
ctx.beginPath(); ctx.arc(sp[0], sp[1], width * 0.85, 0, Math.PI * 2); ctx.fill();
});

// Drop streams whose process has gone.
for (const k of this.flowStreams.keys()) {
if (!seen.has(k)) this.flowStreams.delete(k);
}

// --- wireframe cube on top (subtle, glassy) ---
ctx.lineWidth = 1.2;
ctx.strokeStyle = 'rgba(150, 205, 235, 0.35)';
E.forEach(([a, b]) => {
ctx.beginPath();
ctx.moveTo(projV[a].x, projV[a].y);
ctx.lineTo(projV[b].x, projV[b].y);
ctx.stroke();
});
projV.forEach((p) => {
ctx.fillStyle = 'rgba(190, 225, 245, 0.7)';
ctx.beginPath(); ctx.arc(p.x, p.y, 2.2, 0, Math.PI * 2); ctx.fill();
});

ctx.globalCompositeOperation = 'source-over';
ctx.fillStyle = 'rgba(200,214,235,0.5)';
ctx.font = '10px "Share Tech Mono", monospace';
ctx.fillText(`PROCESS FLOW · ${count} streams${nodes.length ? '' : ' · demo'}`, 18, h - 18);
}

animate() {
if (!this.isActive) return;
this.animationId = requestAnimationFrame(() => this.animate());
Expand Down
1 change: 1 addition & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Tests package
Loading
Loading