Skip to content

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

Open
mattmillerai wants to merge 5 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 5 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

Review Change Stack

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: 33 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: e05acd62-d206-4bee-918b-a85182cc7560

📥 Commits

Reviewing files that changed from the base of the PR and between b123991 and 05588ad.

📒 Files selected for processing (2)
  • comfy_cli/hardware.py
  • tests/comfy_cli/test_hardware.py
📝 Walkthrough

Walkthrough

Changes

Hardware probing is added for operating system, CPU, RAM, NVIDIA, AMD, and Apple Silicon GPU details. comfy env displays a formatted hardware row, while JSON output includes a top-level hardware field validated by the updated schema.

Hardware Environment Reporting

Layer / File(s) Summary
Cross-platform hardware probing
comfy_cli/hardware.py, tests/comfy_cli/test_hardware.py
Adds bounded, non-raising hardware detection with platform-specific CPU and GPU probes, RAM collection, fallback handling, and coverage for NVIDIA, AMD, Apple Silicon, and failure paths.
Environment output and contract validation
comfy_cli/env_checker.py, comfy_cli/schemas/env.json, tests/comfy_cli/test_hardware.py
Formats hardware for Rich table output, serializes it in environment JSON, defines nullable hardware and GPU fields in the schema, and validates formatting and payload compatibility.

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant EnvChecker
  participant detect_hardware
  participant SystemProbes
  participant GPUProbes
  CLI->>EnvChecker: run environment check
  EnvChecker->>detect_hardware: collect hardware
  detect_hardware->>SystemProbes: read platform, CPU, and RAM
  detect_hardware->>GPUProbes: probe NVIDIA, AMD, or Apple GPU
  detect_hardware-->>EnvChecker: return hardware dict
  EnvChecker-->>CLI: render table or JSON output
Loading
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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 Outdated
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:

@bigcat88 bigcat88 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The feature itself checks out — I live-verified on a Linux box with an RTX 5090: comfy env --json reports the correct CPU (Ryzen 9 9950X), RAM (195 GB), and GPU (RTX 5090, 32 GB VRAM, via nvidia-smi), the payload validates against the schema, and 159/160 tests pass. One blocking test-hermeticity bug (inline) that makes the suite permanently red on any machine with a real NVIDIA GPU.

Comment thread tests/comfy_cli/test_hardware.py
mattmillerai and others added 2 commits July 22, 2026 08:00
test_platform_calls_raising_do_not_escape patched the platform calls but
left the real GPU probes live, so on a box with an actual GPU the
unmocked _run executed nvidia-smi and the `gpu is None` assert failed
(reproduced by review on an RTX 5090). Patch _run and _load_libcuda the
same way the neighboring test does. Also merge main to pick up the
uv.lock-pinned CI test deps (BE-3292) that fix the repo-wide tomlkit
pytest break.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mattmillerai
mattmillerai requested a review from bigcat88 July 22, 2026 15:04

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@comfy_cli/hardware.py`:
- Around line 200-215: Update the card-scanning loop in the AMD probe so it does
not unconditionally break after the first card-prefixed dictionary. Continue
scanning subsequent cards when the current card yields neither model nor
vram_bytes, and stop only once the probe has obtained relevant GPU data,
preserving the existing field parsing and invalid-value handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e47c8044-09c0-48f1-b851-997678d865f6

📥 Commits

Reviewing files that changed from the base of the PR and between 29dd108 and b123991.

📒 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

Comment thread comfy_cli/hardware.py Outdated
A rocm-smi payload where card0 lacks the queried fields no longer masks
a later card that has them; take the first card yielding any data. Per
CodeRabbit review on #562.

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

Copy link
Copy Markdown
Collaborator Author

All review threads are now addressed and resolved (latest: CodeRabbit's AMD multi-card finding, fixed in 05588ad). Full suite green locally (2605 passed).

Note on the red Windows Specific Commands check: it is a pre-existing repo-wide break, failing on every PR branch since ~Jul 18 — mixpanel 5.2.0 (unpinned) now pulls pydantic, whose _pydantic_core.pyd gets loaded in-process by comfy install and then can't be replaced by uv pip install on Windows (Access is denied). Not caused by, and not fixable in, this PR; a follow-up ticket with the root cause and a pin-based fix has been proposed.

@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:

  • BE-4071 — Fix repo-wide 'Windows Specific Commands' CI break: mixpanel 5.2.0 pydantic dep locks _pydantic_core.pyd during comfy install

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.

2 participants