Skip to content

Commit d94a759

Browse files
fix(qwen-asr): select Intel XPU devices
The Intel backend installs PyTorch XPU wheels, but Qwen ASR only checked CUDA and MPS. Every Intel model therefore loaded on the CPU. Select XPU when available and place the model on xpu:0. Keep the existing CUDA, MPS, and CPU placement behavior. Assisted-by: Codex:GPT-5 [apply_patch] [gh]
1 parent 4f807ea commit d94a759

3 files changed

Lines changed: 81 additions & 15 deletions

File tree

backend/python/qwen-asr/backend.py

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'common'))
1919
from grpc_auth import get_auth_interceptors
2020
from model_utils import resolve_model_reference
21+
from device_utils import device_map_for, select_device
2122

2223

2324

@@ -95,13 +96,7 @@ def Health(self, request, context):
9596
return backend_pb2.Reply(message=bytes("OK", 'utf-8'))
9697

9798
def LoadModel(self, request, context):
98-
if torch.cuda.is_available():
99-
device = "cuda"
100-
else:
101-
device = "cpu"
102-
mps_available = hasattr(torch.backends, "mps") and torch.backends.mps.is_available()
103-
if mps_available:
104-
device = "mps"
99+
device = select_device(torch)
105100
if not torch.cuda.is_available() and request.CUDA:
106101
return backend_pb2.Result(success=False, message="CUDA is not available")
107102

@@ -123,7 +118,7 @@ def LoadModel(self, request, context):
123118
model_path, local_only = resolve_model_reference(
124119
request, "Qwen/Qwen3-ASR-1.7B"
125120
)
126-
default_dtype = torch.bfloat16 if self.device == "cuda" else torch.float32
121+
default_dtype = torch.bfloat16 if self.device in ("cuda", "xpu") else torch.float32
127122
load_dtype = default_dtype
128123
if "torch_dtype" in self.options:
129124
d = str(self.options["torch_dtype"]).lower()
@@ -145,12 +140,7 @@ def LoadModel(self, request, context):
145140
if attn_implementation is not None and isinstance(attn_implementation, str):
146141
attn_implementation = attn_implementation.strip() or None
147142

148-
if self.device == "mps":
149-
device_map = None
150-
elif self.device == "cuda":
151-
device_map = "cuda:0"
152-
else:
153-
device_map = "cpu"
143+
device_map = device_map_for(self.device)
154144

155145
load_kwargs = dict(
156146
dtype=load_dtype,
@@ -423,4 +413,4 @@ def signal_handler(sig, frame):
423413
parser = argparse.ArgumentParser(description="Run the gRPC server.")
424414
parser.add_argument("--addr", default="localhost:50051", help="The address to bind the server to.")
425415
args = parser.parse_args()
426-
serve(args.addr)
416+
serve(args.addr)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
def select_device(torch_module):
2+
mps = getattr(getattr(torch_module, "backends", None), "mps", None)
3+
if mps is not None and mps.is_available():
4+
return "mps"
5+
if torch_module.cuda.is_available():
6+
return "cuda"
7+
xpu = getattr(torch_module, "xpu", None)
8+
if xpu is not None and xpu.is_available():
9+
return "xpu"
10+
return "cpu"
11+
12+
13+
def device_map_for(device):
14+
if device == "mps":
15+
return None
16+
if device in ("cuda", "xpu"):
17+
return f"{device}:0"
18+
return "cpu"
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import unittest
2+
3+
from device_utils import device_map_for, select_device
4+
5+
6+
class Availability:
7+
def __init__(self, available):
8+
self._available = available
9+
10+
def is_available(self):
11+
return self._available
12+
13+
14+
class TorchStub:
15+
def __init__(self, *, cuda=False, mps=False, xpu=False):
16+
self.cuda = Availability(cuda)
17+
self.backends = type("Backends", (), {"mps": Availability(mps)})()
18+
self.xpu = Availability(xpu)
19+
20+
21+
class SelectDeviceTest(unittest.TestCase):
22+
def test_preserves_cuda_selection(self):
23+
torch_module = TorchStub(cuda=True)
24+
25+
self.assertEqual(select_device(torch_module), "cuda")
26+
27+
def test_preserves_mps_selection(self):
28+
torch_module = TorchStub(mps=True)
29+
30+
self.assertEqual(select_device(torch_module), "mps")
31+
32+
def test_selects_xpu_when_intel_gpu_is_available(self):
33+
torch_module = TorchStub(xpu=True)
34+
35+
self.assertEqual(select_device(torch_module), "xpu")
36+
37+
def test_falls_back_to_cpu(self):
38+
torch_module = TorchStub()
39+
40+
self.assertEqual(select_device(torch_module), "cpu")
41+
42+
43+
class DeviceMapTest(unittest.TestCase):
44+
def test_preserves_cuda_model_placement(self):
45+
self.assertEqual(device_map_for("cuda"), "cuda:0")
46+
47+
def test_preserves_mps_model_placement(self):
48+
self.assertIsNone(device_map_for("mps"))
49+
50+
def test_places_the_model_on_the_first_xpu(self):
51+
self.assertEqual(device_map_for("xpu"), "xpu:0")
52+
53+
def test_preserves_cpu_model_placement(self):
54+
self.assertEqual(device_map_for("cpu"), "cpu")
55+
56+
57+
if __name__ == "__main__":
58+
unittest.main()

0 commit comments

Comments
 (0)