From 9c00da6d1ca2124439b2511065d8ff5fe2f0f3b5 Mon Sep 17 00:00:00 2001 From: Mazen Tamer Salah <78306991+mazen-salah@users.noreply.github.com> Date: Thu, 11 Jun 2026 02:01:58 +0300 Subject: [PATCH] fix(hwfit): tolerate non-numeric gpu_count in /api/hwfit/models (#3639) * fix(hwfit): tolerate non-numeric gpu_count in /api/hwfit/models The route did `n = int(gpu_count)` with no guard, so a non-numeric query param like `?gpu_count=abc` raised ValueError and returned HTTP 500. Parse it defensively (mirroring the gpu_group guard a few lines above): a malformed value is ignored, exactly like omitting the param, and valid values still apply. Adds tests/test_hwfit_gpu_count_nonnumeric.py: a non-numeric gpu_count returns a ranking instead of raising, and a numeric value is still accepted. * test(hwfit): cover non-numeric manual_gpu_count too Follow-up to the gpu_count guard: add a regression test for the sibling manual_gpu_count query param (the hardware simulator in _apply_manual_hardware), which dev already guards by defaulting to 1 on a non-numeric value. This pins that behaviour so the endpoint's count parsing is fully covered and cannot regress to a 500. --- routes/hwfit_routes.py | 10 +++++-- tests/test_hwfit_gpu_count_nonnumeric.py | 38 ++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 tests/test_hwfit_gpu_count_nonnumeric.py diff --git a/routes/hwfit_routes.py b/routes/hwfit_routes.py index 564c3a03c..45c209b0b 100644 --- a/routes/hwfit_routes.py +++ b/routes/hwfit_routes.py @@ -177,8 +177,14 @@ def setup_hwfit_routes(): system["gpu_name"] = g["name"] system["active_group"] = {**g, "use_count": n} - if gpu_count != "": - n = int(gpu_count) + # Parse the optional count defensively (matches the gpu_group guard + # above): a non-numeric query param previously raised ValueError -> + # HTTP 500. A malformed value is ignored, same as omitting it. + try: + n = int(gpu_count) if gpu_count != "" else None + except ValueError: + n = None + if n is not None: if n == 0: # RAM-only mode: rank against system memory, offload allowed. system["has_gpu"] = False diff --git a/tests/test_hwfit_gpu_count_nonnumeric.py b/tests/test_hwfit_gpu_count_nonnumeric.py new file mode 100644 index 000000000..13e6b2f25 --- /dev/null +++ b/tests/test_hwfit_gpu_count_nonnumeric.py @@ -0,0 +1,38 @@ +"""GET /api/hwfit/models must not 500 on a non-numeric gpu_count. + +The handler did `n = int(gpu_count)` with no guard, so `?gpu_count=abc` (or any +non-integer) raised ValueError -> HTTP 500. A malformed count is now ignored, +matching how the neighbouring gpu_group param is already parsed. +""" +from routes.hwfit_routes import setup_hwfit_routes + + +def _get_models(): + router = setup_hwfit_routes() + for route in router.routes: + if getattr(route, "path", "").endswith("/models") and "GET" in getattr(route, "methods", set()): + return route.endpoint + raise AssertionError("hwfit /models route not found") + + +def test_non_numeric_gpu_count_does_not_raise(): + handler = _get_models() + # Previously raised ValueError (HTTP 500); now degrades to a normal ranking. + result = handler(gpu_count="abc") + assert isinstance(result, dict) + + +def test_numeric_gpu_count_still_accepted(): + handler = _get_models() + result = handler(gpu_count="0") + assert isinstance(result, dict) + + +def test_non_numeric_manual_gpu_count_does_not_raise(): + # manual_gpu_count is the other count param on this endpoint (the hardware + # simulator in _apply_manual_hardware). A non-numeric value must also degrade + # (default to 1) rather than 500, so the endpoint's count parsing is fully + # covered. + handler = _get_models() + result = handler(manual_mode="gpu", manual_gpu_count="abc") + assert isinstance(result, dict)