Skip to content

Commit 40411b9

Browse files
fix(lemonade): validate grouped amd_gpu/nvidia_gpu device keys
Live Lemonade groups AMD/NVIDIA GPUs under a single amd_gpu/nvidia_gpu list key, but _validate_device_requirement only normalized the macOS 'metal' key. A present Radeon dGPU under amd_gpu fell through to CPU, so a device='gpu' request wrongly raised HardwareRequirementError and forced a CPU fallback. Generalize the normalization to any detected GPU key via the existing shape-agnostic _is_gpu_device_key helper. Closes #2367
1 parent d605cae commit 40411b9

2 files changed

Lines changed: 75 additions & 5 deletions

File tree

src/gaia/llm/lemonade_manager.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -422,11 +422,11 @@ def _validate_device_requirement(cls, client, required_min_device, device):
422422
if k in item:
423423
detected.add(str(item[k]))
424424
break
425-
# Lemonade on Apple Silicon reports the llama.cpp Metal backend as
426-
# 'metal' in system_info while its health payload calls the same device
427-
# 'gpu' — normalize to the generic GPU tier so a default device='gpu'
428-
# request validates on macOS instead of falling through to cpu.
429-
if "metal" in detected:
425+
# Live Lemonade groups AMD/NVIDIA GPUs under amd_gpu/nvidia_gpu (a list),
426+
# and Apple Silicon uses 'metal' — none are in _DEVICE_PRIORITY's legacy
427+
# amd_igpu/amd_dgpu keys. Normalize any detected GPU to the generic GPU
428+
# tier so a present dGPU isn't rejected and forced to CPU.
429+
if any(_is_gpu_device_key(d) for d in detected):
430430
detected.add("amd_dgpu")
431431
# Find highest-capability detected device
432432
highest = None

tests/unit/test_multi_device_wiring.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,76 @@ def test_cpu_always_satisfied(self, monkeypatch):
9292
assert LemonadeManager.ensure_ready(device="cpu") is True
9393

9494

95+
class TestGpuValidatedForGroupedKeyShapes:
96+
"""A 'gpu' request validates against live Lemonade's grouped-key shapes.
97+
98+
Live Lemonade on Linux/Windows reports AMD/NVIDIA GPUs under a single
99+
grouped key (``amd_gpu``/``nvidia_gpu``) whose value is a *list*, not the
100+
legacy split ``amd_igpu``/``amd_dgpu`` keys. ``_validate_device_requirement``
101+
used to only normalize the macOS ``metal`` key, so a real dGPU under
102+
``amd_gpu`` fell through to CPU and a ``gpu`` request wrongly raised.
103+
"""
104+
105+
def test_gpu_satisfied_on_amd_gpu_list_shape(self, monkeypatch):
106+
# The reproduction shape: amd_gpu as a list (RX 7900 XTX), amd_npu
107+
# unavailable — same payload live Lemonade returns on Ubuntu.
108+
from gaia.llm.lemonade_manager import LemonadeManager
109+
110+
_patch_lemonade(monkeypatch, "lemonade11_amd_dgpu_windows.json")
111+
assert LemonadeManager.ensure_ready(device="gpu") is True
112+
113+
def test_gpu_satisfied_on_metal_shape(self, monkeypatch):
114+
from gaia.llm.lemonade_manager import LemonadeManager
115+
116+
_patch_lemonade(monkeypatch, "lemonade11_metal_macos.json")
117+
assert LemonadeManager.ensure_ready(device="gpu") is True
118+
119+
def test_gpu_satisfied_on_nvidia_gpu_list_shape(self, monkeypatch):
120+
from gaia.llm.lemonade_client import LemonadeClient
121+
from gaia.llm.lemonade_manager import LemonadeManager
122+
123+
data = {
124+
"devices": {
125+
"nvidia_gpu": [{"available": True, "name": "NVIDIA GeForce RTX 4090"}],
126+
"amd_npu": {"available": False},
127+
"cpu": {"available": True},
128+
}
129+
}
130+
monkeypatch.setattr(
131+
LemonadeClient, "get_status", lambda self: _make_status(), raising=False
132+
)
133+
monkeypatch.setattr(
134+
LemonadeClient, "get_system_info", lambda self: data, raising=False
135+
)
136+
assert LemonadeManager.ensure_ready(device="gpu") is True
137+
138+
def test_unavailable_amd_gpu_list_still_fails(self, monkeypatch):
139+
# Availability floor preserved: a present-but-available:false grouped
140+
# GPU must NOT satisfy a gpu request.
141+
from gaia.llm.lemonade_client import LemonadeClient
142+
from gaia.llm.lemonade_manager import (
143+
HardwareRequirementError,
144+
LemonadeManager,
145+
)
146+
147+
data = {
148+
"devices": {
149+
"amd_gpu": [{"available": False, "name": "AMD Radeon RX 7900 XTX"}],
150+
"amd_npu": {"available": False},
151+
"cpu": {"available": True},
152+
}
153+
}
154+
monkeypatch.setattr(
155+
LemonadeClient, "get_status", lambda self: _make_status(), raising=False
156+
)
157+
monkeypatch.setattr(
158+
LemonadeClient, "get_system_info", lambda self: data, raising=False
159+
)
160+
with pytest.raises(HardwareRequirementError) as exc:
161+
LemonadeManager.ensure_ready(device="gpu")
162+
assert "gpu" in str(exc.value).lower()
163+
164+
95165
# ── B2: unavailable device fails loudly with an actionable error ──────────────
96166

97167

0 commit comments

Comments
 (0)