You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ScanResult.degraded and degraded_layers are declared at sdk/src/unplug/api/types.py:51-58 and documented as reporting configured layers that were unavailable. Neither is written anywhere in the SDK.
The Guard knows it is degraded and keeps it in the private _ml_degraded flag at guard.py:166. The result, which is what callers actually inspect, says everything is fine.
Related and part of the same decision: other optional scanners fail closed here. Guard(scanners=["yara"]) and Guard(scanners=["pii"]) raise ConfigError from _validate_optional_scanners at guard.py:751-770, while the ML path degrades silently.
unplug-server already implements the intended semantics in services/scan_result.py (mark_degraded), so the SDK should match it rather than invent a second answer.
Comment here if you want this one. It needs a design conversation first, so expect questions before code.
Not resolved. ScanResult.degraded is still only declared in api/types.py. The one place anything gets written is the private _ml_degraded flag at guard.py:282.
Still open if you want it. The design question comes first, as the issue says: whether the ML path should degrade and report, or fail closed the way Guard(scanners=["yara"]) already does.
Hi, but honestly, chasing Unicode variants with regex #131 feels like a never-ending whac-a-mole, and it's going to hit the exact same wall for multi-lang. Wouldn't a tiny quantized INT8 ONNX encoder cut through all of this at the root? +/- 30/50mb?
@ArkodeonAI The ML layer already exists. Guard(model="tiny") loads Unplug-AI/unplug-tiny-v1, a text classifier on the Hub. It was never regex versus a model, the regex layer is the floor that runs when the ml extra is not installed. That is what this issue is about: the SDK degrades to the floor and reports nothing.
On #131, an encoder would not have caught those payloads either. The normalizer already rewrites math-bold to ASCII before anything downstream sees it, so a model reads the same normalized string a regex does. The bug is that the scanner emits no finding after a normalizer stage fires. #151 is open against it.
I don't have access to unplug-server; could you point me to mark_degraded's signature, or describe what it actually sets on the result object? That'll tell me exactly what shape to match on the SDK side rather than guessing.
My starting hypothesis on the actual design question: since Guard(scanners=[yara]) and Guard(scanners=["pii"]) already fail closed (raise ConfigError) when those optional scanners can't load, making the ML path do the same would be the more consistent behavior across the codebase. But it's also a breaking change for anyone currently relying on the SDK's graceful ML degradation with require_ml=False. Wiring the existing _ml_degraded flag through to ScanResult.degraded / degraded_layers is backward-compatible and seems like the safer default — unless there's a specific reason the server intentionally diverges from the fail-closed pattern for ML. Which way are you leaning?
You do not need unplug-server for this. It is SDK-side only.
require_ml=True is already the fail-closed path: it raises ModelError when the configured tier will not load. So the repo has picked degrade-by-default with opt-in strictness, and that stays.
What is missing is the reporting. Guard.ml_degraded holds the flag and nothing copies it onto the result, so ScanResult.degraded and degraded_layers keep their defaults on every scan. Wire it through with degraded_layers=["injection_ml"] and the issue closes. No breaking change, so the concern about require_ml=False callers does not apply.
Heads up that #203 is open against this one and implements it. #107 had no assignee and no claimed label, so nothing was reserved, but you asked the design question here and I answered it, so you should hear it from me rather than find it.
#191 is still yours. See my note on #207 about why that PR does not carry the fix yet.
Activity
mansiverma897993 commentedon Aug 18, 2026
@chiruu12 is this issue is resolved ?
chiruu12 commentedon Aug 20, 2026
Not resolved.
ScanResult.degradedis still only declared inapi/types.py. The one place anything gets written is the private_ml_degradedflag atguard.py:282.Still open if you want it. The design question comes first, as the issue says: whether the ML path should degrade and report, or fail closed the way
Guard(scanners=["yara"])already does.ArkodeonAI commentedon Sep 10, 2026
Hi, but honestly, chasing Unicode variants with regex #131 feels like a never-ending whac-a-mole, and it's going to hit the exact same wall for multi-lang. Wouldn't a tiny quantized INT8 ONNX encoder cut through all of this at the root? +/- 30/50mb?
chiruu12 commentedon Sep 10, 2026
@ArkodeonAI The ML layer already exists.
Guard(model="tiny")loadsUnplug-AI/unplug-tiny-v1, a text classifier on the Hub. It was never regex versus a model, the regex layer is the floor that runs when themlextra is not installed. That is what this issue is about: the SDK degrades to the floor and reports nothing.On #131, an encoder would not have caught those payloads either. The normalizer already rewrites math-bold to ASCII before anything downstream sees it, so a model reads the same normalized string a regex does. The bug is that the scanner emits no finding after a normalizer stage fires. #151 is open against it.
AaliyaKhan25 commentedon Sep 14, 2026
I don't have access to unplug-server; could you point me to mark_degraded's signature, or describe what it actually sets on the result object? That'll tell me exactly what shape to match on the SDK side rather than guessing.
My starting hypothesis on the actual design question: since Guard(scanners=[yara]) and Guard(scanners=["pii"]) already fail closed (raise ConfigError) when those optional scanners can't load, making the ML path do the same would be the more consistent behavior across the codebase. But it's also a breaking change for anyone currently relying on the SDK's graceful ML degradation with require_ml=False. Wiring the existing _ml_degraded flag through to ScanResult.degraded / degraded_layers is backward-compatible and seems like the safer default — unless there's a specific reason the server intentionally diverges from the fail-closed pattern for ML. Which way are you leaning?
chiruu12 commentedon Oct 6, 2026
You do not need unplug-server for this. It is SDK-side only.
require_ml=Trueis already the fail-closed path: it raisesModelErrorwhen the configured tier will not load. So the repo has picked degrade-by-default with opt-in strictness, and that stays.What is missing is the reporting.
Guard.ml_degradedholds the flag and nothing copies it onto the result, soScanResult.degradedanddegraded_layerskeep their defaults on every scan. Wire it through withdegraded_layers=["injection_ml"]and the issue closes. No breaking change, so the concern aboutrequire_ml=Falsecallers does not apply.chiruu12 commentedon Oct 15, 2026
Heads up that #203 is open against this one and implements it. #107 had no assignee and no claimed label, so nothing was reserved, but you asked the design question here and I answered it, so you should hear it from me rather than find it.
#191 is still yours. See my note on #207 about why that PR does not carry the fix yet.