From 41850b58925a596448dcd4604bf83f7300049109 Mon Sep 17 00:00:00 2001 From: leetcrypt Date: Sun, 19 Jul 2026 23:09:14 -0700 Subject: [PATCH] fix(api): surface each device's own catalog category per-match The per-device entries in an upload's matched_devices list were labelled with the ML overall predicted_category, producing contradictions like "Acurite Tower Sensor -> Fan Controller". The engine now preserves each DeviceMatch's true catalog category in match_details["device_category"], and the API surfaces that per device. The headline device_category still reflects the ML overall call, so the honest gate-metric category is unchanged. Co-Authored-By: Claude Opus 4.6 --- src/api/main_simple.py | 6 +++++- src/matcher/engine.py | 9 ++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/api/main_simple.py b/src/api/main_simple.py index 78d4d94..94cd2c7 100644 --- a/src/api/main_simple.py +++ b/src/api/main_simple.py @@ -557,7 +557,11 @@ def _build_sub_capture(content: bytes, filename: str, entry: dict, { "device_name": m.device_name, "manufacturer": m.manufacturer, - "category": m.match_details.get("predicted_category"), + # Per-device category is that device's own catalog category + # (e.g. an Acurite sensor stays "Weather Sensor"). The overall + # ML call lives in the top-level `device_category` field. + "category": m.match_details.get("device_category") + or m.match_details.get("predicted_category"), "confidence": m.confidence, "method": m.match_method, "details": m.match_details, diff --git a/src/matcher/engine.py b/src/matcher/engine.py index ef36cbd..144c68b 100644 --- a/src/matcher/engine.py +++ b/src/matcher/engine.py @@ -87,7 +87,14 @@ class SignatureMatcher: manufacturer=device_match.manufacturer or "Unknown", confidence=device_match.confidence, match_method=device_match.match_method, - match_details=device_match.details + # Preserve each device's own catalog category so the API can + # surface it per-device (e.g. an Acurite sensor stays + # "Weather Sensor"); the ML overall call remains in the + # match_details' predicted_category. + match_details={ + **device_match.details, + "device_category": device_match.category, + } )) return match_results