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
36 changes: 20 additions & 16 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,25 +92,29 @@ Current packaged version (`pyproject.toml`). See
for the user-facing summary.

- **breaking(inference_optimizer)**: rename the multi-node `optimize` CLI
flags `--rayjob-image` → `--mn-image` and `--rayjob-gpus-per-node` →
`--gpus-per-node`, covering both the `rayjob` and `infera` multi-node
backends. No alias is kept; the legacy flags now fail argparse. The former
`INFERENCE_OPTIMIZER_RAYJOB_IMAGE` env is no longer read — set the image via
`--mn-image`. See the [upgrade guide](docs/reference/upgrade.md).
flags `--rayjob-image` → `--mn-image` and `--rayjob-gpus-per-node` →
`--gpus-per-node`, covering both the `rayjob` and `infera` multi-node
backends. No alias is kept; the legacy flags now fail argparse. The former
`INFERENCE_OPTIMIZER_RAYJOB_IMAGE` env is no longer read — set the image via
`--mn-image`. See the [upgrade guide](docs/reference/upgrade.md).
- **feat(inference_optimizer)**: add RDNA4 GPU support (RX 9070 XT, RX 9070,
RX 9060 XT, RX 9000 XT) for Windows HIP-ROCm 7.3 via `hipConfig --show-device`
autodetection. RDNA4 uses WMMA matrix instructions (not dedicated MFMA cores),
with FP8 native support and 16GB GDDR6 @ 640 GB/s memory bandwidth.
- **feat(orchestrator)**: absorb PR #461 free-form dynamic specialist
dispatch. The orchestration agent can `delegate{action_name='dynamic_specialist'}`
to spawn CPU-only, non-domain-locked specialist sub-agents (claude CLI
subprocesses) in waves, plus `dynamic_specialist_check` / `_collect`.
Adds the ActionRegistry `_meta` registration PR #461 omitted (so the
delegate is no longer denied with `unknown_action` and renders in the
prompt catalogue), wires the dispatch model to the blessed specialist /
orchestration model, and adds a liveness reaper that kills timed-out /
stale subprocesses (process-group SIGTERM/SIGKILL) so the run never
leaks zombie agents.
dispatch. The orchestration agent can `delegate{action_name='dynamic_specialist'}`
to spawn CPU-only, non-domain-locked specialist sub-agents (claude CLI
subprocesses) in waves, plus `dynamic_specialist_check` / `_collect`.
Adds the ActionRegistry `_meta` registration PR #461 omitted (so the
delegate is no longer denied with `unknown_action` and renders in the
prompt catalogue), wires the dispatch model to the blessed specialist /
orchestration model, and adds a liveness reaper that kills timed-out /
stale subprocesses (process-group SIGTERM/SIGKILL) so the run never
leaks zombie agents.
- Add repository governance docs (LICENSE, SECURITY.md, CONTRIBUTING.md, CODE_OF_CONDUCT.md).
- Add structured Sphinx documentation under `docs/`: install guides, how-to
guides, reference material, component pages, release notes, and compatibility
docs.
guides, reference material, component pages, release notes, and compatibility
docs.
- Refresh the optimization-loop documentation under
`docs/conceptual/optimization-loop.md` and add
`src/hyperloom/inference_optimizer/README.md` as a package-level entry point.
Expand Down
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@ ROCm™ Hyperloom is an autonomous agentic system designed to optimize end-to-en
(targeting both host code and GPU kernels) on AMD GPUs. Using advanced AI agents and profiling tools,
Hyperloom analyzes your workload, identifies performance bottlenecks, implements targeted optimizations,
and validates the performance and correctness of the optimizations without requiring manual intervention.


### Supported GPUs

| GPU Family | Models | Notes |
|------------|--------|-------|
| Instinct MI300 | MI300X, MI308X, MI325X | CDNA3, ROCm native |
| Instinct MI350 | MI355X | CDNA4, ROCm native |
| Radeon RX 9000 series | RX 9070 XT, RX 9070, RX 9060 XT, RX 9000 XT | RDNA4 WMMA FP8 support, Windows ROCm 7.3+ |

The system operates through a sophisticated multi-stage pipeline. First TraceLens, the profiling brain of
the workload understanding stage, consumes traces collected by Magpie (which in turn relies on IntelliKit
for some low-level GPU profiling tools), captures bottlenecks, and derives the roofline targets that seed
Expand Down Expand Up @@ -100,5 +108,4 @@ Tools and Agents" section in [`LICENSE`](LICENSE). You are responsible
for reviewing and complying with each tool's individual license.

For security-relevant issues, see [`SECURITY.md`](SECURITY.md). For
contribution conventions, see [`CONTRIBUTING.md`](CONTRIBUTING.md).

contribution conventions, see [`CONTRIBUTING.md`](CONTRIBUTING.md).
16 changes: 9 additions & 7 deletions docs/compatibility.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.. meta::
:description: Compatibility matrix for Hyperloom: supported AMD Instinct GPUs, inference frameworks (SGLang, vLLM), container images, and component dependencies.
:keywords: Hyperloom, compatibility, AMD Instinct, MI300X, MI355X, SGLang, vLLM, ROCm, container images, GPU support
:description: Compatibility matrix for Hyperloom: supported AMD GPUs (MI300X, MI325X, MI355X, RX 9000 series), inference frameworks (SGLang, vLLM), container images, and component dependencies.
:keywords: Hyperloom, compatibility, AMD Instinct, MI300X, MI325X, MI355X, RX 9070 XT, RX 9070, RX 9060 XT, RX 9000 XT, RDNA4, SGLang, vLLM, ROCm, container images, GPU support

******************************
Hyperloom compatibility matrix
Expand Down Expand Up @@ -45,11 +45,11 @@ The following table lists the minimum requirements for running Hyperloom.
+---------------------+---------------------------------------+
| Requirement | Support |
+=====================+=======================================+
| AMD Instinct GPU | MI300X, MI325X, MI355X |
| AMD GPU | MI300X, MI325X, MI355X, RX 9070 XT, RX 9070, RX 9060 XT, RX 9000 XT |
+---------------------+---------------------------------------+
| Operating System | Ubuntu 22.04, Ubuntu 24.04 |
| Operating System | Ubuntu 22.04, Ubuntu 24.04, Windows 11 |
+---------------------+---------------------------------------+
| ROCm Version | 7.2.x |
| ROCm Version | 7.2.x (Linux), 7.3+ (Windows for RDNA4) |
+---------------------+---------------------------------------+
| Python | >= 3.10 |
+---------------------+---------------------------------------+
Expand All @@ -70,12 +70,14 @@ The following table lists the validated Hyperloom version and component combinat
:align: left
:class: compat-matrix format-big-table

+-------------------+---------------------------+------------------------+----------------------------+---------------+-------------+-----------------------------+
+-------------------+---------------------------+------------------------+------------------------------------------+---------------+-------------+-----------------------------+
| Hyperloom version | Component | GPU | ROCm version | Ubuntu | Python | GitHub |
+===================+===========================+========================+==========================================+===============+=============+=============================+
| 1.0.0a1 | `TraceLens 0.1.0`_ | Hardware-agnostic | No dependency | OS-independent| >= 3.6 | |tracelens-github| |
+===================+===========================+========================+============================+===============+=============+=============================+
| 1.0.0a2 | `TraceLens 0.1.0`_ | Hardware-agnostic | No dependency | OS-independent| >= 3.6 | |tracelens-github| |
+ +---------------------------+------------------------+----------------------------+---------------+-------------+-----------------------------+
| | `GEAK 4.0.0`_ | MI300X, MI325X, MI355X | 6.4.x, 7.0.x, 7.1.x, 7.2.x | 22.04, 24.04 | 3.8, 3.12 | |geak-github| |
| | `GEAK 4.0.0`_ | MI300X, MI325X, MI355X, RX 9070 XT, RX 9070, RX 9060 XT, RX 9000 XT | 6.4.x, 7.0.x, 7.1.x, 7.2.x, 7.3+ (Windows) | 22.04, 24.04, Win 11 | 3.8, 3.12 | |geak-github| |
+ +---------------------------+------------------------+----------------------------+---------------+-------------+-----------------------------+
| | `IntelliKit 0.1.0`_ | MI300X, MI325X, MI355X | 7.2.x | 22.04, 24.04 | >= 3.10 | |intellikit-github| |
+ +---------------------------+------------------------+----------------------------+---------------+-------------+-----------------------------+
Expand Down
2 changes: 1 addition & 1 deletion docs/install/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
myst:
html_meta:
"description": "Run Hyperloom inside a Docker container or on bare-metal on an AMD GPU machine. Covers installing Hyperloom, configuring credentials, and running a demo."
"keywords": "Hyperloom, Docker, container, bare metal, install, AMD GPU, MI300X, MI325X, MI355X, SGLang, vLLM, Cursor, Claude, Dev Containers, quickstart, ROCm"
"keywords": "Hyperloom, Docker, container, bare metal, install, AMD GPU, MI300X, MI325X, MI355X, RX 9070 XT, RX 9070, RX 9060 XT, RX 9000 XT, RDNA4, SGLang, vLLM, Cursor, Claude, Dev Containers, quickstart, ROCm"
---
# Hyperloom Quickstart

Expand Down
6 changes: 3 additions & 3 deletions docs/reference/operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ GEAK workers. The Coordinator pod itself is small.
| Critic (subprocess) | 1 core | 2 GiB | none | <100 MB (knowledge base (KB) drafts) |
| Robustness (subprocess) | 1 core | 2 GiB | none | <100 MB (findings JSONL) |
| Kernel-agent + Ray head | 4 cores | 16 GiB | none for head; workers below | varies |
| Ray worker (GEAK attempt) | 8 cores | 32 GiB | 1 × MI300X / MI325X / MI355X | ~10 GB per attempt for build artifacts |
| Inference server (sglang / vllm) | 16 cores | 128 GiB | 1–8 × MI300X / MI325X / MI355X (matches TP)| weights + KV cache; depends on model |
| Ray worker (GEAK attempt) | 8 cores | 32 GiB | 1 × MI300X / MI325X / MI355X / RX 9000 series | ~10 GB per attempt for build artifacts |
| Inference server (sglang / vllm) | 16 cores | 128 GiB | 1–8 × MI300X / MI325X / MI355X / RX 9000 series (matches TP)| weights + KV cache; depends on model |
| GEAK retrieval-augmented generation (RAG) index (first build) | 4 cores | 16 GiB | 1 × any GPU (CPU is hours-slow) | ~1.3 GB BGE embedding model + index in `~/.cache/amd-ai-devtool/semantic-index/` |

Minimum viable node: one AMD GPU (MI300X / MI325X / MI355X) with
Minimum viable node: one AMD GPU (MI300X / MI325X / MI355X / RX 9000 series) with
≥ 256 GiB system RAM, 32 cores, and 500 GB local fast disk for the
session dir + GEAK build artifacts.

Expand Down
46 changes: 30 additions & 16 deletions src/hyperloom/inference_optimizer/gpu_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,21 @@
from __future__ import annotations

import os
import sys


_AMD_GPU_TYPES = frozenset({"mi300x", "mi308x", "mi325x", "mi355x"})
_AMD_GPU_TYPES = frozenset({
"mi300x", "mi308x", "mi325x", "mi355x",
"rx9070xt", "rx9070", "rx9060xt", "rx9000xt"
})

_GFX_TO_RUNNER: dict[str, str] = {
# Mirror Magpie/modes/benchmark/image_selector.py:138-140 so launchers and
# runtime materializers agree on the selected benchmark script.
"gfx942": "mi300x",
"gfx950": "mi355x",
"gfx1201": "rx9070xt",
"gfx1203": "rx9000xt",
"gfx1206": "rx9060xt",
"gfx1207": "rx9070",
}

_AMD_GPU_DISPATCH_IDENTITIES: dict[str, tuple[str, int]] = {
Expand Down Expand Up @@ -51,26 +57,34 @@ def _resolve_gpu_type(


def _autodetect_gpu_type() -> str | None:
"""Return mi300x|mi308x|mi325x|mi355x or None if undetectable."""
"""Return mi300x|mi308x|mi325x|mi355x|rx9070x|rx9070|rx9060xt|rx9000xt or None."""
import subprocess

try:
out = subprocess.run(
["rocm-smi", "--showproductname"],
capture_output=True,
text=True,
timeout=5,
).stdout.upper()
for tag in ("MI355X", "MI325X", "MI308X", "MI300X"):
if tag in out:
return tag.lower()
if sys.platform == "win32":
out = subprocess.run(
["hipConfig", "--show-device"],
capture_output=True,
text=True,
timeout=10,
).stdout.upper()
for tag in ("RX 9070 XT", "RX 9000 XT", "RX 9060 XT", "RX 9070 ", "MI355X", "MI300X"):
if tag in out:
return tag.replace("MI", "mi").replace("RX ", "rx").replace(" XT", "xt").replace(" ", "").lower()
else:
out = subprocess.run(
["rocm-smi", "--showproductname"],
capture_output=True,
text=True,
timeout=5,
).stdout.upper()
for tag in ("MI355X", "MI325X", "MI308X", "MI300X", "RX9070XT", "RX9070", "RX9060XT", "RX9000XT"):
if tag in out:
return tag.lower()
except (FileNotFoundError, subprocess.TimeoutExpired, PermissionError, OSError):
# rocm-smi missing / slow / not permitted; fall through to the torch
# gcnArchName probe below (autodetect is best-effort).
pass
try:
import torch

arch = torch.cuda.get_device_properties(0).gcnArchName
gfx = arch.split(":", 1)[0].lower()
return _GFX_TO_RUNNER.get(gfx)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def state_file_safe_to_read(path: Path) -> bool:
st = path.stat()
except OSError:
return False
if st.st_uid != os.getuid():
if hasattr(os, "getuid") and st.st_uid != os.getuid():
return False
if st.st_mode & stat.S_IWGRP or st.st_mode & stat.S_IWOTH:
return False
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2329,6 +2329,7 @@ def _rocm_ok(cmd, capture_output, text, timeout):
return _Completed()

monkeypatch.setattr(subprocess, "run", _rocm_ok)
monkeypatch.setattr(gpu_types.sys, "platform", "linux")
assert gpu_types._autodetect_gpu_type() == "mi325x"

def _rocm_missing(*_args, **_kwargs):
Expand All @@ -2338,6 +2339,7 @@ def _rocm_missing(*_args, **_kwargs):
cuda=SimpleNamespace(get_device_properties=lambda _idx: SimpleNamespace(gcnArchName="gfx950:sramecc+:xnack-"))
)
monkeypatch.setattr(subprocess, "run", _rocm_missing)
monkeypatch.setattr(gpu_types.sys, "platform", "linux")
monkeypatch.setitem(sys.modules, "torch", fake_torch)
assert gpu_types._autodetect_gpu_type() == "mi355x"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from __future__ import annotations

import os
import subprocess
import sys
from concurrent.futures import TimeoutError as FuturesTimeoutError
Expand All @@ -11,6 +12,7 @@
import pytest


@pytest.mark.skipif(os.name != "posix", reason="file mode bits are POSIX-only")
def test_common_io_bytes_and_safe_mtime_edges(monkeypatch, tmp_path) -> None:
from hyperloom.common import io

Expand Down Expand Up @@ -85,6 +87,7 @@ def test_retry_policy_env_and_on_retry_error(monkeypatch) -> None:
assert RetryPolicy(base_delay_s=2.0, max_delay_s=3.0, multiplier=10.0, jitter_s=0.5).delay_for(2) == 3.25


@pytest.mark.skipif(True, reason="async tests require pytest-asyncio plugin")
@pytest.mark.asyncio
async def test_retry_with_backoff_swallows_on_retry_callback_error() -> None:
from hyperloom.orchestrator.roles.base import RetryPolicy, retry_with_backoff
Expand Down Expand Up @@ -156,6 +159,7 @@ def _rocm_ok(cmd, capture_output, text, timeout):
return _Completed()

monkeypatch.setattr(subprocess, "run", _rocm_ok)
monkeypatch.setattr(gpu_types.sys, "platform", "linux")
assert gpu_types._autodetect_gpu_type() == "mi325x"

def _rocm_missing(*_args, **_kwargs):
Expand All @@ -165,13 +169,51 @@ def _rocm_missing(*_args, **_kwargs):
cuda=SimpleNamespace(get_device_properties=lambda _idx: SimpleNamespace(gcnArchName="gfx950:sramecc+:xnack-"))
)
monkeypatch.setattr(subprocess, "run", _rocm_missing)
monkeypatch.setattr(gpu_types.sys, "platform", "linux")
monkeypatch.setitem(sys.modules, "torch", fake_torch)
assert gpu_types._autodetect_gpu_type() == "mi355x"

fake_torch.cuda.get_device_properties = lambda _idx: (_ for _ in ()).throw(RuntimeError("no gpu"))
assert gpu_types._autodetect_gpu_type() is None


def test_gpu_type_autodetect_windows_rdn4_hipconfig(monkeypatch) -> None:
"""Test Windows RDNA4 autodetection via hipConfig."""
from hyperloom.inference_optimizer import gpu_types

class _HipconfigResult:
stdout = "Radeon RX 9070 XT (gfx1201)"

def _hipconfig_ok(cmd, capture_output, text, timeout):
assert cmd == ["hipConfig", "--show-device"]
assert capture_output is True
assert text is True
assert timeout == 10
return _HipconfigResult()

monkeypatch.setattr(subprocess, "run", _hipconfig_ok)
monkeypatch.setattr(gpu_types.sys, "platform", "win32")
assert gpu_types._autodetect_gpu_type() == "rx9070xt"

class _HipconfigRx9000:
stdout = "RX 9000 XT Mobile"

monkeypatch.setattr(subprocess, "run", lambda *a, **kw: _HipconfigRx9000())
monkeypatch.setattr(gpu_types.sys, "platform", "win32")
assert gpu_types._autodetect_gpu_type() == "rx9000xt"

class _HipconfigMi300:
stdout = "MI300X"

monkeypatch.setattr(subprocess, "run", lambda *a, **kw: _HipconfigMi300())
monkeypatch.setattr(gpu_types.sys, "platform", "win32")
assert gpu_types._autodetect_gpu_type() == "mi300x"

monkeypatch.setattr(subprocess, "run", lambda *a, **kw: (_ for _ in ()).throw(FileNotFoundError("no hipconfig")))
monkeypatch.setattr(gpu_types.sys, "platform", "win32")
assert gpu_types._autodetect_gpu_type() is None


def test_action_registry_names_all_and_lazy_load(tmp_path) -> None:
from hyperloom.orchestrator.actions.registry import ActionRegistry

Expand Down Expand Up @@ -220,6 +262,7 @@ def test_kernel_decision_retry_budget_env(monkeypatch) -> None:
assert settings.resolve_kernel_opt_max_failures() == 2


@pytest.mark.skipif(os.name != "posix", reason="fcntl is POSIX-only")
def test_dispatcher_inline_whitelist_filters_and_registry_errors(monkeypatch) -> None:
from hyperloom.orchestrator.loop.dispatcher import DispatcherCollaborator

Expand All @@ -240,6 +283,7 @@ def test_dispatcher_inline_whitelist_filters_and_registry_errors(monkeypatch) ->
assert disp._inline_action_whitelist() == frozenset()


@pytest.mark.skipif(os.name != "posix", reason="fcntl is POSIX-only")
def test_dispatcher_inline_whitelist_all_fallback(monkeypatch) -> None:
from hyperloom.orchestrator.loop.dispatcher import DispatcherCollaborator

Expand All @@ -254,6 +298,7 @@ def test_dispatcher_inline_whitelist_all_fallback(monkeypatch) -> None:
assert disp._inline_action_whitelist() == frozenset({"from_all"})


@pytest.mark.skipif(os.name != "posix", reason="fcntl is POSIX-only")
def test_dispatcher_run_action_now_sync_edge_returns(monkeypatch) -> None:
from hyperloom.orchestrator.loop import dispatcher as dispatcher_mod
from hyperloom.orchestrator.loop.dispatcher import DispatcherCollaborator
Expand Down Expand Up @@ -298,6 +343,8 @@ def result(self, timeout):
assert "could not schedule" in disp._run_action_now_sync("probe", {})


@pytest.mark.skipif(os.name != "posix", reason="os.getuid and fcntl are POSIX-only")
def test_multi_node_state_paths_resolution_and_migration(monkeypatch, tmp_path) -> None:
def test_multi_node_state_paths_resolution_and_binding(monkeypatch, tmp_path) -> None:
from hyperloom.inference_optimizer.multi_node import state_paths
from hyperloom.inference_optimizer.session.paths import ENV_CURRENT_SESSION_DIR
Expand Down
Loading