Skip to content

feat: add cross-platform hardware block to comfy env --json (BE-3399)#562

Open
mattmillerai wants to merge 2 commits into
mainfrom
matt/be-3399-hardware-block
Open

feat: add cross-platform hardware block to comfy env --json (BE-3399)#562
mattmillerai wants to merge 2 commits into
mainfrom
matt/be-3399-hardware-block

Conversation

@mattmillerai

Copy link
Copy Markdown
Collaborator

ELI-5

comfy env --json now tells you what machine you're on: your OS, chip, how much
RAM you have, and what GPU (and how much video memory) is available. Agents that
call the CLI can read this and avoid asking a weak laptop to run heavy local
image generation. It's purely additive — nothing that already reads the env
payload breaks.

What changed

  • New module comfy_cli/hardware.py with one entry point detect_hardware() -> dict
    returning os / os_version / arch / cpu / ram_bytes / gpu. The gpu sub-block is
    {vendor, model, vram_bytes, unified_memory} or null.
    • macOS: cpu from sysctl -n machdep.cpu.brand_string (~0.012s). Apple
      Silicon (arm64) → apple unified-memory GPU (RAM is the GPU budget). Intel
      Mac → unknown non-unified GPU (we deliberately do not shell out to
      system_profiler per the ticket).
    • NVIDIA (any OS): nvidia-smi --query-gpu=name,memory.total first, then a
      ctypes fallback via the existing cuda_detect._load_libcuda
      (cuInit/cuDeviceGet/cuDeviceGetName/cuDeviceTotalMem_v2).
    • AMD (Linux): best-effort rocm-smi --showmeminfo vram --json; nulls acceptable.
  • Contract: never raises, never blocks long. Every probe is wrapped (failure →
    None), and every subprocess uses timeout=5.
  • Wired into the payload: EnvChecker.fill_data() gains "hardware", and
    fill_print_table() gains a Hardware row (cpu / RAM GB / GPU (VRAM GB or 'unified')).
  • Schema: schemas/env.json gains an optional, fully nullable hardware
    property (gpu is ["object","null"]). Not added to required, so older and
    failed-probe payloads stay valid. Envelope schema unchanged (envelope/1).

Testing

  • tests/comfy_cli/test_hardware.py (new): macOS-arm64 apple/unified block;
    Intel-Mac unknown block; nvidia-smi happy path (24564 MiB → bytes); nvidia-smi
    absent + libcuda ctypes fallback; every probe raising/timing out → gpu: null,
    cpu: null, RAM still populated, no exception escapes; fill_data() includes
    the key and the full payload validates against schemas/env.json; nulled and
    hardware-absent payloads both still validate.
  • Existing tests/comfy_cli/output/test_envelope_schemas.py::test_env_json_validates
    (real comfy --json env subprocess) passes → live payload validates and exits 0.
  • ruff check + ruff format --check clean.
  • Verified live on an Apple M4 Max: comfy env --json reports
    Darwin / arm64 / Apple M4 Max / 64 GB + apple unified GPU block, well under 1s
    added latency.

Judgment calls

  • Intel Mac GPU: the ticket says both "Intel Macs: gpu vendor 'unknown',
    unified_memory false" and "gpu null if no GPU detected". I read these as: an
    Intel Mac always has a GPU (integrated/AMD), just unidentifiable without the
    forbidden system_profiler, so it reports {vendor: "unknown", ..., unified_memory: false}
    rather than null. null is reserved for machines where no GPU is detected at all
    (e.g. a headless Linux CI runner). Flagging in case the reviewer prefers null.
  • ctypes device-query path is new vs cuda_detect: production cuda_detect only
    calls cuInit + cuDriverGetVersion; the cuDeviceGetName/cuDeviceTotalMem_v2
    calls here are new. Fully wrapped (worst case → None), no subprocess, so a quirky
    driver yields at most an odd model string, never a crash. I could not exercise it
    against real NVIDIA hardware from this Mac; it's covered by a faithful ctypes mock.
  • No negative-claim / capability-denial: this change is purely additive reporting.
    gpu: null means "not detected," not a user-facing dead-end — no "not supported"
    strings, no throw/deny path, no tests flipped to assert a dead-end. The falsification
    trigger does not fire.

Add comfy_cli/hardware.py with a single detect_hardware() entry point that
reports OS, CPU, RAM, and a GPU sub-block (vendor/model/VRAM/unified-memory)
so agent surfaces can route weak machines away from local diffusion.

- macOS: cpu via sysctl machdep.cpu.brand_string; Apple Silicon -> apple
  unified-memory GPU; Intel Mac -> unknown non-unified GPU (no system_profiler).
- NVIDIA (any OS): nvidia-smi CSV, then ctypes libcuda fallback (reuses
  cuda_detect._load_libcuda).
- AMD (Linux): best-effort rocm-smi --json.
- Never raises, never blocks long: every probe wrapped, subprocesses timeout=5.

Wire hardware into EnvChecker.fill_data() (JSON) and a Hardware row in
fill_print_table() (pretty). Extend schemas/env.json with an OPTIONAL, fully
nullable hardware property (not in required) so older/failed-probe payloads
stay valid. Envelope schema unchanged (envelope/1).
@mattmillerai mattmillerai added agent-coded PR authored by the agent-work loop cursor-review Request Cursor bot review labels Jul 18, 2026
@mattmillerai
mattmillerai marked this pull request as ready for review July 18, 2026 00:41
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 54 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1ba93b1c-f11d-4eac-9ca6-6ccb775fc79d

📥 Commits

Reviewing files that changed from the base of the PR and between a732f5c and 5e5b4f4.

📒 Files selected for processing (4)
  • comfy_cli/env_checker.py
  • comfy_cli/hardware.py
  • comfy_cli/schemas/env.json
  • tests/comfy_cli/test_hardware.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch matt/be-3399-hardware-block
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch matt/be-3399-hardware-block

Comment @coderabbitai help to get the list of available commands.

@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. enhancement New feature or request labels Jul 18, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Cursor Review — Consolidated panel

Triggered by @mattmillerai.

Found 9 finding(s).

Severity Count
🟡 Medium 4
🟢 Low 4
⚪ Nit 1

Panel: 6/8 reviewers contributed findings.

Reviewers that did not contribute: kimi-k2.5:adversarial (empty), kimi-k2.5:edge-case (empty)

Comment thread comfy_cli/env_checker.py
Comment thread comfy_cli/hardware.py
Comment thread comfy_cli/hardware.py
Comment thread comfy_cli/hardware.py Outdated
Comment thread comfy_cli/hardware.py
Comment thread comfy_cli/hardware.py
Comment thread comfy_cli/hardware.py
Comment thread comfy_cli/hardware.py
Comment thread comfy_cli/env_checker.py
…(BE-3399)

- env_checker: escape untrusted cpu/gpu-model strings in the Rich-rendered
  hardware summary so markup-like content (e.g. "[/]") can't raise MarkupError
  and crash `comfy env` in pretty mode.
- hardware(_detect_gpu_amd): return None (no phantom all-None AMD block) when
  nothing parses, matching the NVIDIA probes; gate the card loop on the "card"
  key prefix so a non-card metadata block isn't parsed as the GPU; exclude the
  "VRAM Total Used Memory" usage key so total capacity isn't understated.
- hardware(_detect_gpu_nvidia_ctypes): pop/restore CUDA_VISIBLE_DEVICES around
  the ctypes probe (mirrors cuda_detect) so an exported ""/"-1" doesn't hide a
  present GPU.
- tests: cover AMD total-vs-used key, metadata-block skip, all-None->None, and
  markup escaping.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mattmillerai

Copy link
Copy Markdown
Collaborator Author

Review resolution (5e5b4f4)

Addressed the Cursor consolidated-panel findings:

Fixed:

  • 🟡 Rich MarkupError crash — escape untrusted cpu/gpu strings in the pretty-mode hardware summary.
  • 🟡 Phantom AMD GPU — _detect_gpu_amd returns None when nothing parses (matches NVIDIA probes).
  • 🟡 rocm-smi VRAM 'used' key shadowing 'total' — now matches total-capacity only.
  • 🟢 CUDA_VISIBLE_DEVICES not restored in the ctypes probe — pop/restore mirroring cuda_detect.
  • 🟢 rocm-smi metadata block mistaken for a card — gate on the card key prefix.

New tests cover each. ruff check/format clean; test_hardware.py green (19 passed).

Deferred to follow-ups (out of scope for this PR): 🟡 unbounded in-process ctypes CUDA hang (also affects cuda_detect, needs a thread-vs-subprocess design decision); 🟢 Windows bare-name binary planting (low, needs a Windows-correct approach); 🟢 Apple Silicon under Rosetta reporting x86_64 (niche). Declined: ⚪ banker's-rounding nit (cosmetic; JSON path reports exact bytes).

CI note: the failing build and test checks are pre-existing repo-wide infra flakes unrelated to this diff — build is the tomlkit multiline-comment break in test_config_parser/test_node_init (tracked as BE-3290), and test is the Windows pydantic_core import failure that hits every PR. This PR touches only hardware.py/env_checker.py/env.json/test_hardware.py, none of which those tests exercise.

@mattmillerai

Copy link
Copy Markdown
Collaborator Author

🤖 The reviews loop filed Linear follow-up ticket(s) for review thread(s) deferred as out of scope for this PR:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent-coded PR authored by the agent-work loop cursor-review Request Cursor bot review enhancement New feature or request size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant