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
The sklearn-onnx URL on line 59 is a comment describing the format the model was exported from. scikit-learn appears nowhere in the library except this declaration, that comment, and one log message. onnxruntime, which is genuinely required, is a core dependency (pyproject.toml:27-28) so it happens to be present regardless.
2. transformers is required but was not declared.
Both in-process paths import it, one at module scope:
heuristics/checks.py:19 — from transformers import GPT2LMHeadModel, GPT2TokenizerFast
model_based/models.py:27 — from transformers import AutoModel, AutoTokenizer
The package's own requirements.txt lists transformers>=5.3.0.
3. The user-facing remediation message is wrong. nemoguardrails/library/jailbreak_detection/actions.py:158, logged when the in-process import fails:
Failed to import required dependencies for local model. Install scikit-learn and torch, or use NIM-based approach
Someone following this installs a package that is not used, does not install transformers, and hits the same failure again.
4. The structural problem: requirements here are conditional, and the field is not.
RailRequirements.optional_dependencies is one unconditional tuple per manifest.
The jailbreak packages are needed only on the in-process path, and the two surfaces do not need the same things:
surface
in-process
remote
jailbreak detection heuristics
torch, transformers
nothing (server_endpoint)
jailbreak detection model
torch, transformers, onnxruntime
nothing (server_endpoint or nim_base_url)
The two surfaces also disagree about which config field means "remote": the model rail treats nim_base_url as remote, heuristics has its own server and ignores it.
So even a corrected tuple stays wrong in one direction or the other — declare the packages and every server-backed config is refused, omit them and an in-process config passes validation then fails on the first request.
Conditional requirements (4), on a build without torch/transformers installed:
# config.yml -- a working NIM deploymentmodels:
- type: mainengine: nimmodel: meta/llama-3.3-70b-instructrails:
input:
flows:
- jailbreak detection modelconfig:
jailbreak_detection:
nim_base_url: https://ai.api.nvidia.comnim_server_endpoint: /v1/security/nvidia/nemoguard-jailbreak-detect
This config needs none of the declared packages. Any consumer that enforces optional_dependencies must special-case the rail to avoid rejecting it.
Expected Behavior
A manifest states what a rail needs to run, accurately, and a consumer can act on it without per-rail knowledge.
For jailbreak that means the requirements are expressible per backend (and per surface, since the two surfaces select backends differently) rather than as one tuple per manifest.
Actual Behavior
optional_dependencies names one package the rail never imports and, until recently, omitted one it imports at module scope.
Because the tuple cannot be qualified, a consumer enforcing it has to choose between rejecting working remote configs and letting in-process configs fail at request time.
Impact on IORails (context, not part of the bug)
nemoguardrails/guardrails/compiled_rail.py refuses at compile time when a manifest's declared distributions are missing, so a missing extra is reported once as a configuration error rather than reaching a request, where the fail-closed envelope renders it as a content block.
Since jailbreak's requirements cannot be qualified, PR #2264:
adds transformers to the declaration, so the message is at least accurate
keeps a config-reading backend check for jailbreak detection model (remote on server_endpoint or nim_base_url)
blocklists jailbreak detection heuristics in _unsupported_rail_reason, because it shares a config section with the model rail but reads a different field, so no manifest-wide answer is right for both
That blocklist entry exists only because the manifest cannot express this, and should be removed when it can.
Proposed fix
Independently correctable, and worth doing regardless:
Drop scikit-learn, add onnxruntime (or drop it too, as a core dependency), add transformers
Fix the remediation message at actions.py:158 to name what is actually needed
Let requirements be declared per backend, so a rail with an in-process and a remote path can state both. Two shapes were considered while working on feat(iorails): Support blocking rails #2264:
a remote_when declaration on RailSurface naming the config keys that select a remote backend — covers jailbreak, does not cover hf_classifier, whose selector is an enum comparison nested under a per-classifier key
promoting the backend to a surface parameter (jailbreak detection model $backend=nim, mirroring $model= and $classifier=), so the choice is statically visible in the flow string and requirements can be declared per parameter value. This generalises to hf_classifier's engine with one mechanism and needs no new predicate machinery.
A cheaper interim step that makes the declaration honest without new schema: a conformance test asserting every declared distribution is imported somewhere in its library package. That alone would have caught (1).
Title
Did you check docs and existing issues?
Python version
Operating system/version
NeMo-Guardrails version
Describe the bug
nemoguardrails/library/jailbreak_detection/rail.pydeclares:Three things are wrong with that list, and a fourth problem means no corrected list can be right.
1.
scikit-learnis declared but never imported.JailbreakClassifierloads the random forest through ONNX Runtime, not scikit-learn:The
sklearn-onnxURL on line 59 is a comment describing the format the model was exported from.scikit-learnappears nowhere in the library except this declaration, that comment, and one log message.onnxruntime, which is genuinely required, is a core dependency (pyproject.toml:27-28) so it happens to be present regardless.2.
transformersis required but was not declared.Both in-process paths import it, one at module scope:
heuristics/checks.py:19—from transformers import GPT2LMHeadModel, GPT2TokenizerFastmodel_based/models.py:27—from transformers import AutoModel, AutoTokenizerThe package's own
requirements.txtliststransformers>=5.3.0.3. The user-facing remediation message is wrong.
nemoguardrails/library/jailbreak_detection/actions.py:158, logged when the in-process import fails:Someone following this installs a package that is not used, does not install
transformers, and hits the same failure again.4. The structural problem: requirements here are conditional, and the field is not.
RailRequirements.optional_dependenciesis one unconditional tuple per manifest.The jailbreak packages are needed only on the in-process path, and the two surfaces do not need the same things:
jailbreak detection heuristicsserver_endpoint)jailbreak detection modelserver_endpointornim_base_url)The two surfaces also disagree about which config field means "remote": the model rail treats
nim_base_urlas remote, heuristics has its own server and ignores it.So even a corrected tuple stays wrong in one direction or the other — declare the packages and every server-backed config is refused, omit them and an in-process config passes validation then fails on the first request.
Steps To Reproduce
Wrong declaration (1 and 2):
Conditional requirements (4), on a build without torch/transformers installed:
This config needs none of the declared packages. Any consumer that enforces
optional_dependenciesmust special-case the rail to avoid rejecting it.Expected Behavior
A manifest states what a rail needs to run, accurately, and a consumer can act on it without per-rail knowledge.
For jailbreak that means the requirements are expressible per backend (and per surface, since the two surfaces select backends differently) rather than as one tuple per manifest.
Actual Behavior
optional_dependenciesnames one package the rail never imports and, until recently, omitted one it imports at module scope.Because the tuple cannot be qualified, a consumer enforcing it has to choose between rejecting working remote configs and letting in-process configs fail at request time.
Impact on IORails (context, not part of the bug)
nemoguardrails/guardrails/compiled_rail.pyrefuses at compile time when a manifest's declared distributions are missing, so a missing extra is reported once as a configuration error rather than reaching a request, where the fail-closed envelope renders it as a content block.Since jailbreak's requirements cannot be qualified, PR #2264:
transformersto the declaration, so the message is at least accuratejailbreak detection model(remote onserver_endpointornim_base_url)jailbreak detection heuristicsin_unsupported_rail_reason, because it shares a config section with the model rail but reads a different field, so no manifest-wide answer is right for bothThat blocklist entry exists only because the manifest cannot express this, and should be removed when it can.
Proposed fix
Independently correctable, and worth doing regardless:
scikit-learn, addonnxruntime(or drop it too, as a core dependency), addtransformersactions.py:158to name what is actually neededNeeds design (tracked in #2279):
remote_whendeclaration onRailSurfacenaming the config keys that select a remote backend — covers jailbreak, does not coverhf_classifier, whose selector is an enum comparison nested under a per-classifier keyjailbreak detection model $backend=nim, mirroring$model=and$classifier=), so the choice is statically visible in the flow string and requirements can be declared per parameter value. This generalises tohf_classifier'senginewith one mechanism and needs no new predicate machinery.A cheaper interim step that makes the declaration honest without new schema: a conformance test asserting every declared distribution is imported somewhere in its library package. That alone would have caught (1).
Related