Skip to content

feat: CPU temperature for remote Sparks and in the CPU panel - #34

Open
VoidSec wants to merge 1 commit into
MiaAI-Lab:mainfrom
VoidSec:feat/cpu-temperature
Open

feat: CPU temperature for remote Sparks and in the CPU panel#34
VoidSec wants to merge 1 commit into
MiaAI-Lab:mainfrom
VoidSec:feat/cpu-temperature

Conversation

@VoidSec

@VoidSec VoidSec commented Jul 31, 2026

Copy link
Copy Markdown

Problem

Two gaps that compound each other:

  1. SystemCollector._getRemoteCpu() returned a hardcoded temperature: 0 and never queried a sensor, so remote Sparks could not report CPU temperature at all.
  2. CpuPanel had no temperature row, so CpuMetrics.temperature went unrendered even for local Sparks where the collector did populate it.

Changes

Collect it for remote Sparks (SystemCollector.js). Sensor candidates are appended to the existing SSH command, so this stays one round trip per poll. They are emitted in the same priority order the local path uses: hwmon named coretemp/k10temp/zenpower/acpitz first, then thermal zones. A new _parseSensorTemp() takes the first plausible reading with the same accept range as _getCPUTemperature() (0 < raw < 200000) and returns 0 when nothing is readable, matching _defaultCpu().

The shared allowlist matters on this hardware: GB10 boxes also expose nvme and four mlx5 ConnectX-7 sensors, and the mlx5 readings sit only ~3C off the CPU one, so a mismatched filter would be easy to miss by eye.

Display it (CpuPanel.tsx, SparkPage.tsx, metricsStore.ts). A Temperature row between Usage and Power, mirroring GpuPanel: honors the Celsius/Fahrenheit setting via the same conversion, and draws a sparkline from a new cpu.temp history series. Two deliberate differences from the GPU panel:

  • Thresholds follow DGX_SPARK.THERMAL_THRESHOLDS.junction (warn 85, crit 95) rather than the GPU's 65/85. A GB10 CPU sits around 70C under normal load, so the GPU thresholds would pin the row amber permanently and make a real thermal event indistinguishable from idle.
  • The row is hidden when the reading is 0, so a host with no readable sensor shows nothing rather than a bogus 0°C. This follows how the panel already gates ramAvail and OOM risk. The history series skips zero samples for the same reason.

Verification

Tested on two GB10 nodes, one local and one remote over SSH:

  • Both paths independently select hwmon0/acpitz/temp1_input, so cross-node readings are comparable rather than merely self-consistent.
  • Over 8 samples, dashboard values agreed with a direct sysfs read within 0.0-0.1C locally and 0.0-0.2C remotely, consistent with one 2000 ms poll interval of staleness plus SSH latency. No systematic offset.
  • Confirmed thermal_zoneN and hwmon0/temp(N+1)_input are the same sensor surfaced through two kernel interfaces, byte-identical when read simultaneously across all 7 zones on both nodes.
  • WebSocket snapshot carries the field, so the panel receives it.

7 new unit tests in server/collectors/__tests__/SystemCollector.cpuTemp.test.js cover the conversion, first-plausible-wins ordering, the blank line left by the --- section split, unreadable sensors, out-of-range rejection, and rounding. tsc --noEmit is clean.

Note: npm run test has one pre-existing failure in showcasePrompts.test.js unrelated to this change; it reads src/components/ShowcasePage/showcasePrompts.ts, which is absent when the suite runs inside the production image.

Not included

No CHANGELOG.md entry, since slotting this under a version heading looked like a release decision. Happy to add one wherever you'd like it.

Remote Sparks always reported 0: _getRemoteCpu() hardcoded the value and
never queried a sensor. The CPU panel had no temperature row at all, so the
field went unused for local Sparks too.

Collect it within the existing SSH round trip by appending sensor candidates
to the same command, in the priority order the local path already uses
(hwmon coretemp/k10temp/zenpower/acpitz, then thermal zones).
_parseSensorTemp takes the first plausible reading using the same accept
range as _getCPUTemperature, and returns 0 when nothing is readable.

Display it in CpuPanel between Usage and Power, mirroring GpuPanel: honors
the Celsius/Fahrenheit setting and draws a sparkline from a new cpu.temp
history series. Thresholds follow DGX_SPARK.THERMAL_THRESHOLDS.junction
(warn 85, crit 95) instead of the GPU's 65/85, which would sit amber
permanently at a GB10's normal ~70C load. The row is hidden when the
reading is 0, so a host with no readable sensor shows no bogus 0C.

Verified on two GB10 nodes: local and remote paths both select
hwmon0/acpitz/temp1_input (identical to thermal_zone0), agreeing within
0.2C of a direct sysfs read.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant