Skip to content

fix(docker): pre-cache snowflake embedding model in jailbreak Dockerfile#1848

Open
lokeshrangineni wants to merge 2 commits intoNVIDIA-NeMo:developfrom
lokeshrangineni:fix/pre-cache-snowflake-embedding
Open

fix(docker): pre-cache snowflake embedding model in jailbreak Dockerfile#1848
lokeshrangineni wants to merge 2 commits intoNVIDIA-NeMo:developfrom
lokeshrangineni:fix/pre-cache-snowflake-embedding

Conversation

@lokeshrangineni
Copy link
Copy Markdown

@lokeshrangineni lokeshrangineni commented May 5, 2026

Description

The Snowflake/snowflake-arctic-embed-m-long embedding model used by the model-based jailbreak classifier was only downloaded at container startup. This caused two problems:

  1. Internet access was required on first startup inside the container — blocking air-gapped / offline deployments.
  2. The HuggingFace trust_remote_code=True prompt fired at runtime without the user ever explicitly opting in during the build.

Changes

  • Dockerfile and Dockerfile-GPU: Added a RUN step after the existing GPT2 pre-cache step that instantiates SnowflakeEmbed() at image build time. This triggers the same from_pretrained calls (with trust_remote_code=True, add_pooling_layer=False, safe_serialization=True) as at runtime, warming the HuggingFace disk cache. This mirrors the pattern already in place for GPT2.

  • model_based/models.py: Extracted the model name into a module-level constant SNOWFLAKE_EMBED_MODEL to avoid duplication within the class.

Why SnowflakeEmbed() instead of bare from_pretrained calls?

Instantiating SnowflakeEmbed() directly means the Dockerfile never diverges from the runtime call site — if arguments to from_pretrained change in the future (e.g. a new parameter), the pre-cache step picks it up automatically. The extra .to(device) and .eval() calls are pure in-memory operations with no effect on the on-disk HuggingFace cache.

Trade-off

Docker image size increases by ~400–600 MB for the Snowflake model weights. This is the same trade-off already accepted for GPT2.

Test results

Ran on a clean conda environment (Python 3.11, torch 2.11 CPU, transformers 4.49.0):

platform darwin -- Python 3.11.15, pytest-8.4.2
tests/test_jailbreak_model_based.py  13 passed, 1 failed (pre-existing, see note)
tests/test_jailbreak_actions.py      10 passed
tests/test_jailbreak_models.py        3 skipped (require EMBEDDING_CLASSIFIER_PATH + model weights)

Note on the 1 failure (test_model_based_classifier_missing_deps): this test fails identically on the unmodified develop branch. It is a pre-existing incompatibility between the test's sys.modules patching and transformers 4.49.0's lazy import machinery, unrelated to this PR.

Related Issue(s)

Checklist

  • I've read the CONTRIBUTING guidelines.
  • I've updated the documentation if applicable.
  • I've added tests if applicable.
  • @mentions of the person or team responsible for reviewing proposed changes.

Made with Cursor

Summary by CodeRabbit

  • Bug Fixes

    • Docker jailbreak-detection images now pre-cache the Snowflake embedding model during the build process, eliminating the need for internet access at runtime.
  • Chores

    • Updated changelog documentation.

The Snowflake/snowflake-arctic-embed-m-long embedding model was only
downloaded at container startup, requiring internet access at runtime and
triggering a trust_remote_code prompt the user never explicitly opted into.
Add a RUN step to both Dockerfile and Dockerfile-GPU that instantiates
SnowflakeEmbed() at image build time, which calls the same from_pretrained
logic (including trust_remote_code, add_pooling_layer, safe_serialization)
as at runtime. This ensures the HuggingFace cache is warm when the
container starts. Also extract the model name into a module-level constant
SNOWFLAKE_EMBED_MODEL to avoid duplication within models.py.
This mirrors the existing pattern already in place for GPT2.
Fixes NVIDIA-NeMo#1648
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

Documentation preview

https://nvidia-nemo.github.io/Guardrails/review/pr-1848

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 5, 2026

Greptile Summary

This PR pre-caches the Snowflake/snowflake-arctic-embed-m-long embedding model inside the jailbreak detection Docker images at build time, eliminating the need for internet access at container startup and supporting air-gapped deployments. It also pins the model to a specific commit SHA (good practice with trust_remote_code=True) and sets stable HF_HOME/TRANSFORMERS_CACHE environment variables.

  • model_based/models.py: Extracts SNOWFLAKE_EMBED_MODEL and SNOWFLAKE_EMBED_REVISION as module-level constants and uses them in SnowflakeEmbed.__init__.
  • Dockerfile / Dockerfile-GPU: Adds ENV HF_HOME and ENV TRANSFORMERS_CACHE for a predictable cache path, then adds a RUN step to pre-download the Snowflake model using bare from_pretrained calls (avoiding in-memory load during build).
  • Known issue: The pre-cache RUN step imports SNOWFLAKE_EMBED_REVISION from the installed nemoguardrails PyPI package; since pip install nemoguardrails>=0.14.0 currently resolves to 0.21.0 (which lacks this constant), the Docker build will fail with an ImportError until a new version is published.

Confidence Score: 3/5

Both Dockerfiles will fail to build until a new nemoguardrails version containing the new constant is released to PyPI.

The pre-cache RUN step imports SNOWFLAKE_EMBED_REVISION from the installed nemoguardrails PyPI package. The requirements.txt pins to nemoguardrails>=0.14.0, which today resolves to 0.21.0 — a version that predates this PR and does not contain the constant. Python's -c flag does not add the working directory to sys.path, so the locally-copied source files are not reachable, and the import fails with an ImportError that aborts the Docker build on both Dockerfiles.

Both Dockerfiles need the pre-cache command updated to hardcode the model name and revision SHA directly, matching the pattern of the GPT2 pre-cache step immediately above.

Important Files Changed

Filename Overview
nemoguardrails/library/jailbreak_detection/Dockerfile Adds HF_HOME/TRANSFORMERS_CACHE env vars and a Snowflake model pre-cache step; the pre-cache command imports a constant from the not-yet-released PyPI package, causing an ImportError at build time.
nemoguardrails/library/jailbreak_detection/Dockerfile-GPU GPU variant with the same pre-cache step and the same ImportError risk as the CPU Dockerfile.
nemoguardrails/library/jailbreak_detection/model_based/models.py Extracts SNOWFLAKE_EMBED_MODEL and SNOWFLAKE_EMBED_REVISION module-level constants and pins the model to a specific commit SHA; clean, no issues.
CHANGELOG.md Adds an Unreleased section with a bug-fix entry for the Snowflake pre-cache; no issues.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[docker build] --> B[pip install requirements.txt\nnemoguardrails>=0.14.0 from PyPI]
    B --> C[COPY . . to /app]
    C --> D[Pre-cache GPT2\nhardcoded model name]
    D --> E[Pre-cache Snowflake\nimport SNOWFLAKE_EMBED_REVISION\nfrom installed nemoguardrails]
    E --> F{Constant exists\nin installed pkg?}
    F -- Yes - after next PyPI release --> G[AutoTokenizer.from_pretrained\nrevision pinned]
    F -- No - current 0.21.0 --> H[ImportError\nBuild fails]
    G --> I[AutoModel.from_pretrained\ntrust_remote_code=True\nrevision pinned]
    I --> J[Weights cached to\n/models/hf-cache/hub/]
    J --> K[Runtime: offline load\nfrom HF_HOME cache]
Loading
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
nemoguardrails/library/jailbreak_detection/Dockerfile:37-41
The pre-cache step imports `SNOWFLAKE_EMBED_REVISION` from the installed `nemoguardrails` PyPI package. Because `COPY . .` copies only the `jailbreak_detection` directory contents (not the full nemoguardrails package) and Python's `-c` flag does not add the working directory to `sys.path`, this import resolves against the pip-installed package. The constant `SNOWFLAKE_EMBED_REVISION` doesn't exist in any released version of `nemoguardrails` yet, so `pip install nemoguardrails>=0.14.0` (currently resolving to 0.21.0) causes an `ImportError` and the Docker build fails. The GPT2 step one line above avoids this fragility by hardcoding the model name directly — the same pattern should be used here.

```suggestion
RUN python -c "\
    from transformers import AutoModel, AutoTokenizer; \
    AutoTokenizer.from_pretrained('Snowflake/snowflake-arctic-embed-m-long', revision='92d97331f1f4b6a366c1f161354b9f3390cc219f'); \
    AutoModel.from_pretrained('Snowflake/snowflake-arctic-embed-m-long', revision='92d97331f1f4b6a366c1f161354b9f3390cc219f', trust_remote_code=True, add_pooling_layer=False, safe_serialization=True)"
```

### Issue 2 of 2
nemoguardrails/library/jailbreak_detection/Dockerfile-GPU:37-41
Same `ImportError` as the CPU Dockerfile: the import of `SNOWFLAKE_EMBED_REVISION` from the installed `nemoguardrails` PyPI package will fail until a new version containing this constant is released. Hardcoding the values matches the GPT2 pattern and removes the dependency on a specific package version.

```suggestion
RUN python -c "\
    from transformers import AutoModel, AutoTokenizer; \
    AutoTokenizer.from_pretrained('Snowflake/snowflake-arctic-embed-m-long', revision='92d97331f1f4b6a366c1f161354b9f3390cc219f'); \
    AutoModel.from_pretrained('Snowflake/snowflake-arctic-embed-m-long', revision='92d97331f1f4b6a366c1f161354b9f3390cc219f', trust_remote_code=True, add_pooling_layer=False, safe_serialization=True)"
```

Reviews (2): Last reviewed commit: "refactor(docker): address CodeRabbit rev..." | Re-trigger Greptile

Comment thread nemoguardrails/library/jailbreak_detection/Dockerfile Outdated
Comment thread nemoguardrails/library/jailbreak_detection/Dockerfile-GPU Outdated
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 5, 2026

📝 Walkthrough

Walkthrough

This PR addresses a bug where the Snowflake embedding model was downloaded at container runtime, requiring internet access at startup. The fix pre-caches the model during Docker build time, updates code to use a centralized model identifier constant, and documents the change.

Changes

Snowflake Embedding Model Pre-caching

Layer / File(s) Summary
Code Organization
nemoguardrails/library/jailbreak_detection/model_based/models.py
Introduces SNOWFLAKE_EMBED_MODEL constant holding the model identifier and refactors SnowflakeEmbed.__init__ to use it instead of hardcoded strings.
Docker Build Configuration
nemoguardrails/library/jailbreak_detection/Dockerfile, nemoguardrails/library/jailbreak_detection/Dockerfile-GPU
Both Dockerfiles add RUN python -c commands to instantiate SnowflakeEmbed() during build, pre-downloading the embedding model to avoid runtime internet access.
Documentation
CHANGELOG.md
Unreleased section documents the bug fix under Docker-tagged entry noting the Snowflake embedding model is now pre-cached.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately describes the main change: pre-caching the Snowflake embedding model in the jailbreak detection Dockerfiles to fix a runtime dependency issue.
Linked Issues check ✅ Passed All requirements from issue #1648 are met: Snowflake model is pre-cached during Docker build via RUN steps in both Dockerfiles, and the trust_remote_code prompt is handled at build time, enabling offline deployments.
Out of Scope Changes check ✅ Passed All changes are directly related to pre-caching the Snowflake embedding model: Dockerfile modifications add pre-caching logic, model_based/models.py introduces a constant to eliminate duplication, and CHANGELOG.md documents the fix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Test Results For Major Changes ✅ Passed PR includes test results (13 passed, 10 passed, 3 skipped) in description. Changes are minor infrastructure updates (Docker caching, constant extraction) with no algorithmic impact or regression.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
nemoguardrails/library/jailbreak_detection/Dockerfile (1)

29-31: ⚡ Quick win

Consider setting explicit Hugging Face cache paths for clarity and future robustness.

Lines 29–31 pre-cache models without explicitly setting HF_HOME or TRANSFORMERS_CACHE. While the current Dockerfile runs as root (matching the build-time UID), explicitly configuring these paths is a best practice for maintainability. If a non-root user is later added or the runtime UID changes, the cache paths will be preserved. Consider adding environment variables early in the build:

ENV HF_HOME=/models/hf-cache
ENV TRANSFORMERS_CACHE=/models/hf-cache
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nemoguardrails/library/jailbreak_detection/Dockerfile` around lines 29 - 31,
Add explicit Hugging Face cache environment variables in the Dockerfile before
pre-downloading the Snowflake embedding model: set HF_HOME and
TRANSFORMERS_CACHE (e.g., to /models/hf-cache) early in the Dockerfile so the
RUN python -c "from
nemoguardrails.library.jailbreak_detection.model_based.models import
SnowflakeEmbed; SnowflakeEmbed()" invocation uses those stable cache paths;
ensure the directories are created and permissions set appropriately for the
build/runtime user to avoid permission issues when SnowflakeEmbed() populates
the cache.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@nemoguardrails/library/jailbreak_detection/model_based/models.py`:
- Around line 29-34: The pretrained tokenizer and model are being loaded with
trust_remote_code=True from SNOWFLAKE_EMBED_MODEL without a pinned revision;
update both AutoTokenizer.from_pretrained(...) and
AutoModel.from_pretrained(...) calls to include a specific revision=<commit-sha>
argument (use the repository commit SHA you want to pin) so the tokenizer and
model loads are reproducible and safer when trust_remote_code is enabled.

---

Nitpick comments:
In `@nemoguardrails/library/jailbreak_detection/Dockerfile`:
- Around line 29-31: Add explicit Hugging Face cache environment variables in
the Dockerfile before pre-downloading the Snowflake embedding model: set HF_HOME
and TRANSFORMERS_CACHE (e.g., to /models/hf-cache) early in the Dockerfile so
the RUN python -c "from
nemoguardrails.library.jailbreak_detection.model_based.models import
SnowflakeEmbed; SnowflakeEmbed()" invocation uses those stable cache paths;
ensure the directories are created and permissions set appropriately for the
build/runtime user to avoid permission issues when SnowflakeEmbed() populates
the cache.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 3d86f212-4d0e-42cc-aa56-df9acf46bd9e

📥 Commits

Reviewing files that changed from the base of the PR and between a2b7880 and 35f8275.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • nemoguardrails/library/jailbreak_detection/Dockerfile
  • nemoguardrails/library/jailbreak_detection/Dockerfile-GPU
  • nemoguardrails/library/jailbreak_detection/model_based/models.py

Comment thread nemoguardrails/library/jailbreak_detection/model_based/models.py Outdated
@lokeshrangineni
Copy link
Copy Markdown
Author

@Pouyanpi - I have created the PR as discussed on the issue-1648. Please find the test evidence below. The one which is failing is pre-existing and not related to the current changes. Please let me know if you recommend any changes.

🧪 Test Evidence

Environment: conda — Python 3.11.15 · torch 2.11 (CPU) · transformers 4.49.0

Click to expand full test output $ pytest tests/test_jailbreak_model_based.py tests/test_jailbreak_actions.py tests/test_jailbreak_models.py -v

platform darwin -- Python 3.11.15, pytest-8.4.2, pluggy-1.6.0 collected 27 items

tests/test_jailbreak_model_based.py::test_lazy_import_does_not_require_heavy_deps PASSED tests/test_jailbreak_model_based.py::test_model_based_classifier_imports PASSED tests/test_jailbreak_model_based.py::test_model_based_classifier_missing_deps FAILED (*) tests/test_jailbreak_model_based.py::test_initialize_model_with_none_classifier_path PASSED tests/test_jailbreak_model_based.py::test_snowflake_embed_torch_imports PASSED tests/test_jailbreak_model_based.py::test_check_jailbreak_with_classifier PASSED tests/test_jailbreak_model_based.py::test_check_jailbreak_without_classifier PASSED tests/test_jailbreak_model_based.py::test_check_jailbreak_no_classifier_available PASSED tests/test_jailbreak_model_based.py::test_initialize_model_with_valid_path PASSED tests/test_jailbreak_model_based.py::test_nv_embed_e5_removed PASSED tests/test_jailbreak_model_based.py::test_snowflake_embed_still_available PASSED tests/test_jailbreak_model_based.py::test_initialize_model_logging PASSED tests/test_jailbreak_model_based.py::test_check_jailbreak_explicit_none_classifier PASSED tests/test_jailbreak_model_based.py::test_check_jailbreak_valid_classifier_preserved PASSED tests/test_jailbreak_actions.py::TestJailbreakDetectionActions::test_jailbreak_detection_model_with_nim_base_url PASSED tests/test_jailbreak_actions.py::TestJailbreakDetectionActions::test_jailbreak_detection_model_api_key_not_set PASSED tests/test_jailbreak_actions.py::TestJailbreakDetectionActions::test_jailbreak_detection_model_no_api_key_env_var PASSED tests/test_jailbreak_actions.py::TestJailbreakDetectionActions::test_jailbreak_detection_model_local_runtime_error PASSED tests/test_jailbreak_actions.py::TestJailbreakDetectionActions::test_jailbreak_detection_model_local_import_error PASSED tests/test_jailbreak_actions.py::TestJailbreakDetectionActions::test_jailbreak_detection_model_local_success PASSED tests/test_jailbreak_actions.py::TestJailbreakDetectionActions::test_jailbreak_detection_model_empty_context PASSED tests/test_jailbreak_actions.py::TestJailbreakDetectionActions::test_jailbreak_detection_model_context_without_user_message PASSED tests/test_jailbreak_actions.py::TestJailbreakDetectionActions::test_jailbreak_detection_model_legacy_server_endpoint PASSED tests/test_jailbreak_actions.py::TestJailbreakDetectionActions::test_jailbreak_detection_model_none_response_handling PASSED tests/test_jailbreak_models.py::test_jb_model_detected SKIPPED () tests/test_jailbreak_models.py::test_safe SKIPPED () tests/test_jailbreak_models.py::test_check_jailbreak_model SKIPPED (**)

1 failed, 23 passed, 3 skipped in 1.85s

(*) test_model_based_classifier_missing_deps — pre-existing failure, unrelated to this PR.
The test mocks sklearn.ensemble = None via sys.modules patching; transformers 4.49.0's lazy import
machinery then raises RuntimeError instead of the expected ImportError due to an internal
EncoderDecoderConfig resolution issue. Confirmed identical failure on the unmodified develop branch
with no changes applied.

(**) test_jailbreak_models.py — skipped by design; require EMBEDDING_CLASSIFIER_PATH
and downloaded model weights to run.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 5, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

…o#1848

- Pin SNOWFLAKE_EMBED_REVISION to a specific HuggingFace commit SHA
  (92d97331) to ensure reproducible builds when trust_remote_code=True,
  reducing supply-chain risk.
- Switch Dockerfile pre-cache steps from SnowflakeEmbed() instantiation
  to bare from_pretrained calls to avoid loading ~400-600 MB of model
  weights into RAM during docker build, preventing OOM on constrained
  CI build agents.
- Add HF_HOME and TRANSFORMERS_CACHE env vars in both Dockerfiles so
  the HuggingFace cache path is stable regardless of runtime user/UID.
Comment thread nemoguardrails/library/jailbreak_detection/Dockerfile
@Pouyanpi Pouyanpi force-pushed the develop branch 4 times, most recently from a6be550 to c69efe5 Compare May 6, 2026 16:01
@lokeshrangineni
Copy link
Copy Markdown
Author

@Pouyanpi - I have incorporated all the code review comments. AFAIK no changes needed from my side. could you let me know if you would like me to make any changes in the PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: snowflake embedding model not pre-cached in jailbreak detection Dockerfile

1 participant