Skip to content

feat(library): migrate built-in rail configuration to manifests#2181

Open
Pouyanpi wants to merge 9 commits into
pouyanpi/rail-library-stack-3-manifest-contractfrom
pouyanpi/rail-library-stack-4-builtins
Open

feat(library): migrate built-in rail configuration to manifests#2181
Pouyanpi wants to merge 9 commits into
pouyanpi/rail-library-stack-3-manifest-contractfrom
pouyanpi/rail-library-stack-4-builtins

Conversation

@Pouyanpi

@Pouyanpi Pouyanpi commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Description

Declare built-in library rails through the manifest contract and
project their typed configuration fields into RailsConfig. Add manifests and
configuration models for the built-in catalog, including F5, while preserving
the existing public configuration shape.

Each built-in rail keeps its cheap declaration in rail.py. A separate
rail_config.py is needed only when that rail owns a typed rails.config
contribution; its schema factory remains behind a lazy reference until
RailsConfig construction needs it. This keeps core configuration generic
without forcing catalog discovery to import Pydantic models or action modules.

Complete the execution metadata needed by non-Colang consumers: threshold-mode
bindings for ActiveFence and GCP, the correct AutoAlign groundedness action,
retrieval transform targets for the HF classifier and regex rails, and optional
self-check $variant bindings. Add catalog-wide checks that action-bearing
library packages declare manifests and that bindings match callable contracts.

The large config.py deletion replaces hand-maintained per-rail fields with
manifest-driven projection; it is not removal of those configuration options.

Impact

  • Existing RailsConfig keys, defaults, and validation remain available while
    their ownership moves into the corresponding rail packages.
  • Adding a config-bearing built-in rail no longer requires another hard-coded
    field in core rails.llm.config.
  • Built-in action, flow, surface, requirement, and privacy declarations become
    available to downstream engines and tooling through one catalog.
  • Rails without their own configuration section do not gain unnecessary
    rail_config.py modules; shared schemas remain owned by their parent rail.
  • Catalog-wide tests now catch missing manifests and bindings that do not match
    their target callable before later execution layers consume them.

Audit

A structured audit compared every migrated built-in rail against the
pre-migration config.py (defaults, types, validators, keys), checked each
manifest binding against its real action signature, and verified the specific
claims above (ActiveFence/GCP threshold modes, AutoAlign groundedness action,
HF/regex retrieval transforms, self-check $variant, config-less rails).

Result: no default drift, no dropped validators or fields, no broken runtime
bindings.

Stack

Layer Branch Base PR
Stack 3 pouyanpi/rail-library-stack-3-manifest-contract develop #2157
Stack 4 pouyanpi/rail-library-stack-4-builtins Stack 3 #2181
Stack 4b pouyanpi/rail-library-stack-4b-flow-gate Stack 4 #2185
Stack 5 pouyanpi/rail-library-stack-5-lazy-actions Stack 4b #2186

Related Issue(s)

  • Part of #
  • Internal tracking: NGUARD-855
  • Issue assignee: @

AI Assistance

  • No AI tools were used.
  • AI tools were used; a human reviewed and can explain every change (tool: Codex).

Checklist

  • I've read the CONTRIBUTING guidelines.
  • This PR links to a triaged issue assigned to me.
  • My PR title follows the project commit convention.
  • I've updated the documentation if applicable.
  • I've added tests if applicable.
  • I've noted any verification beyond CI and any checks I couldn't run.
  • I did not update generated changelog files manually.
  • I addressed all CodeRabbit, Greptile, and other review comments, or replied with why no change is needed.
  • @mentions of the person or team responsible for reviewing proposed changes.

@github-actions github-actions Bot added status: needs triage New issues that have not yet been reviewed or categorized. size: XL labels Jul 17, 2026
@Pouyanpi Pouyanpi changed the title Pouyanpi/rail library stack 4 builtins feat(library): migrate built-in rail configuration to manifests Jul 17, 2026
@codecov

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.04618% with 11 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
nemoguardrails/rails/llm/rails_config_fields.py 90.47% 10 Missing ⚠️
...emoguardrails/library/guardrails_ai/rail_config.py 93.33% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@Pouyanpi
Pouyanpi force-pushed the pouyanpi/rail-library-stack-3-manifest-contract branch from 6fbf0e2 to d5532ad Compare July 17, 2026 08:25
@Pouyanpi
Pouyanpi force-pushed the pouyanpi/rail-library-stack-4-builtins branch 4 times, most recently from ef8ce7e to 2310548 Compare July 17, 2026 09:15
@Pouyanpi
Pouyanpi force-pushed the pouyanpi/rail-library-stack-3-manifest-contract branch from 2f707ff to 4e1f759 Compare July 17, 2026 09:15
@Pouyanpi
Pouyanpi force-pushed the pouyanpi/rail-library-stack-3-manifest-contract branch from 4e1f759 to 49f9f7f Compare July 17, 2026 11:58
@Pouyanpi
Pouyanpi force-pushed the pouyanpi/rail-library-stack-4-builtins branch from 2310548 to f72f3e2 Compare July 17, 2026 11:58
@Pouyanpi Pouyanpi added status: triaged Triaged by a maintainer; eligible for automated review (CodeRabbit/Greptile). and removed status: needs triage New issues that have not yet been reviewed or categorized. labels Jul 17, 2026
@Pouyanpi Pouyanpi self-assigned this Jul 17, 2026
@Pouyanpi Pouyanpi added this to the v0.24.0 milestone Jul 17, 2026
@Pouyanpi
Pouyanpi marked this pull request as ready for review July 17, 2026 13:26
@greptile-apps

greptile-apps Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR migrates all built-in rail configurations from hard-coded fields in config.py to manifest-driven declarations, adding rail.py manifest files and rail_config.py Pydantic schema modules for each built-in rail in the library catalog. The RailsConfigData model is now built dynamically at config.py import time from the projected manifest specs, and all previously-exported types remain accessible via the module-level __getattr__ bridge.

  • New manifest/config-schema files (65 files) cover all built-in rails — ActiveFence, AutoAlign, Clavata, CrowdStrike, F5, GLiNER, HF Classifier, Jailbreak Detection, Regex, Sensitive Data Detection, and more — each with typed RailConfigSpec factories, surface bindings, action refs, requirement declarations, and privacy metadata.
  • config.py sheds ~950 lines of hand-maintained model classes; rails_config_fields.py replaces them with a discovery engine that projects manifest config specs into the Pydantic model at import time, with guards for re-entrancy, idempotency, and export-name collision detection.
  • Comprehensive tests in test_builtin_rail_manifests.py and test_config.py validate manifest completeness, action-ref/decorator-name parity, binding-to-parameter matching, surface contracts, and legacy export accessibility.

Confidence Score: 5/5

Safe to merge — all built-in config models are faithfully reproduced in their new locations with matching defaults, validators, and exported type names; backward compatibility is preserved through the module getattr bridge.

The migration systematically replaces ~950 lines of hand-maintained Pydantic models with manifest-driven projection. Backward-compat re-exports are validated at import time by validate_no_config_export_shadowing. Action-ref names were confirmed to match their @action decorator names (including the non-obvious 'call gcpnlp api' and 'ClavataCheckAction' cases). Reentrancy, idempotency, and test-seam reset paths in rails_config_fields.py are all correctly guarded. New catalog-wide tests enforce manifest completeness, binding-to-parameter parity, and surface contract preservation. No default drift, dropped validators, or broken bindings were found.

No files require special attention — all 65 changed files are internally consistent and backed by targeted tests.

Important Files Changed

Filename Overview
nemoguardrails/rails/llm/rails_config_fields.py New discovery engine: projects manifest config specs into RailsConfigData fields; re-entrancy guard, idempotent re-registration, and export-collision detection all correctly implemented.
nemoguardrails/rails/llm/config.py ~950 lines of hand-maintained rail config models removed; replaced by dynamic model construction via build_rails_config_data and module getattr for backward-compatible re-exports. Import-time collision validation added.
nemoguardrails/library/hf_classifier/rail_config.py Discriminated union of LocalHFClassifierConfig / RemoteHFClassifierConfig migrated from config.py; validators correctly ordered; _KNOWN_PARAMS uses underscore prefix (Pydantic v2 private, not a field).
nemoguardrails/library/jailbreak_detection/rail_config.py JailbreakDetectionConfig faithfully migrated with SecretStr api_key, deprecated field migration, and URL validation; two mode=after validators run in definition order (migrate then validate), which is the correct sequence.
nemoguardrails/library/regex/rail_config.py RegexDetectionOptions with compile_patterns validator and _compiled_patterns PrivateAttr faithfully migrated; object.setattr bypass for frozen-like PrivateAttr mutation is correct Pydantic v2 pattern.
nemoguardrails/library/activefence/rail.py Clean manifest for ActiveFence; threshold_mode bindings (simple/detailed) correctly declared for both input surfaces matching test assertions.
nemoguardrails/library/gcp_moderate_text/rail.py ActionRef name 'call gcpnlp api' correctly matches @action(name='call gcpnlp api') in actions.py; threshold_mode bindings preserved for simple/detailed surfaces.
nemoguardrails/library/clavata/rail.py ClavataCheckAction PascalCase name matches @action(name='ClavataCheckAction') in actions.py; surface bindings (text, rail) match flow file parameters.
nemoguardrails/library/autoalign/rail.py Four action refs and surfaces for input/output/groundedness/factcheck correctly wired; groundedness action name matches test assertion for autoalign_groundedness_output_api.
nemoguardrails/library/polygraf/actions.py Import of PolygrafDetection migrated from config.py to polygraf.rail_config; backward compatibility preserved via rail config export in getattr.
tests/rails/llm/test_builtin_rail_manifests.py Comprehensive catalog tests: manifest completeness, action-name parity, binding-to-parameter matching, legacy flow contract preservation, env var documentation.
tests/rails/llm/test_config.py New config tests verify clavata field defaults, null/yaml parsing, canonical config key set, and legacy export accessibility — thorough migration regression coverage.
pyproject.toml Adds rail.py and rail_config.py glob patterns to the package include list, ensuring new manifest and schema files are shipped in the distribution.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant cfg as config.py (import time)
    participant rcf as rails_config_fields.py
    participant mfst as manifests registry
    participant rcp as rail_config.py (per rail)
    participant pyd as Pydantic create_model

    cfg->>rcf: build_rails_config_data(base, module)
    rcf->>rcf: discover_rails_config_fields()
    rcf->>mfst: all_rail_manifests()
    mfst-->>rcf: name to RailManifest for all built-in rails
    loop each manifest with config_schema
        rcf->>rcp: resolve_import_ref(ConfigSpecRef) then build_config_spec()
        rcp-->>rcf: RailConfigSpec(annotation, field_info, exports)
        rcf->>rcf: _add_config_field(fields, exports, RailsConfigField)
    end
    rcf->>pyd: create_model RailsConfigData with fields
    pyd-->>cfg: RailsConfigData class baked into module
    cfg->>rcf: validate_no_config_export_shadowing(globals())
    cfg->>cfg: build __all__ from globals union config_exported_names

    Note over cfg,pyd: Runtime attribute access
    cfg->>rcf: __getattr__(name) calls resolve_config_export(name)
    rcf-->>cfg: exported type e.g. ClavataRailConfig
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant cfg as config.py (import time)
    participant rcf as rails_config_fields.py
    participant mfst as manifests registry
    participant rcp as rail_config.py (per rail)
    participant pyd as Pydantic create_model

    cfg->>rcf: build_rails_config_data(base, module)
    rcf->>rcf: discover_rails_config_fields()
    rcf->>mfst: all_rail_manifests()
    mfst-->>rcf: name to RailManifest for all built-in rails
    loop each manifest with config_schema
        rcf->>rcp: resolve_import_ref(ConfigSpecRef) then build_config_spec()
        rcp-->>rcf: RailConfigSpec(annotation, field_info, exports)
        rcf->>rcf: _add_config_field(fields, exports, RailsConfigField)
    end
    rcf->>pyd: create_model RailsConfigData with fields
    pyd-->>cfg: RailsConfigData class baked into module
    cfg->>rcf: validate_no_config_export_shadowing(globals())
    cfg->>cfg: build __all__ from globals union config_exported_names

    Note over cfg,pyd: Runtime attribute access
    cfg->>rcf: __getattr__(name) calls resolve_config_export(name)
    rcf-->>cfg: exported type e.g. ClavataRailConfig
Loading

Reviews (7): Last reviewed commit: "fix(rails): parse configured flow model ..." | Re-trigger Greptile

Comment thread nemoguardrails/rails/llm/rails_config_fields.py Outdated
Comment thread nemoguardrails/rails/llm/config.py Outdated
Comment thread nemoguardrails/rails/llm/rails_config_fields.py
Comment thread nemoguardrails/rails/llm/config.py
@Pouyanpi
Pouyanpi force-pushed the pouyanpi/rail-library-stack-4-builtins branch from abf6536 to be38ebc Compare July 20, 2026 07:18
@Pouyanpi Pouyanpi changed the title feat(library): migrate built-in rail configuration to manifests feat(library): migrate built-in rail configuration to manifest Jul 20, 2026
@Pouyanpi Pouyanpi changed the title feat(library): migrate built-in rail configuration to manifest feat(library): migrate built-in rail configuration to manifests Jul 20, 2026
@Pouyanpi
Pouyanpi changed the base branch from pouyanpi/rail-library-stack-3-manifest-contract to develop July 20, 2026 08:06
@Pouyanpi
Pouyanpi changed the base branch from develop to pouyanpi/rail-library-stack-3-manifest-contract July 20, 2026 08:06
@Pouyanpi
Pouyanpi force-pushed the pouyanpi/rail-library-stack-3-manifest-contract branch from e5f87ad to dc2be90 Compare July 20, 2026 12:34
@Pouyanpi
Pouyanpi force-pushed the pouyanpi/rail-library-stack-4-builtins branch from be38ebc to 938e50e Compare July 20, 2026 12:34
Comment on lines +17 to +38

flow clavata check input
"""Check if the user input is benign."""
$is_match = await ClavataCheckAction(text=$user_message, rail="input")

if $is_match.is_blocked
if $system.config.enable_rails_exceptions
send ClavataPolicyMatchException(message="Interaction blocked by clavata check on input.")
else
bot refuse to respond
abort

flow clavata check output
"""Check if the bot output is benign."""
$is_match = await ClavataCheckAction(text=$bot_message, rail="output")

if $is_match.is_blocked
if $system.config.enable_rails_exceptions
send ClavataPolicyMatchException(message="Interaction blocked by clavata check on output.")
else
bot refuse to respond
abort

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

this is to expose a consistent surface for both Colang dialects, check for colang 2.0 flow still works.

Pouyanpi added 9 commits July 20, 2026 16:26
Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>
Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>
Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>
Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>
Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>
Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>
Restore the exact pre-migration F5 and Polygraf schema descriptions, align AlignScore privacy metadata with its declared service, and stop advertising the flow-specific hallucination warning as a portable execution surface.

Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>
Declare the missing Clavata input/output wrappers, context-bloat input/retrieval execution contracts, and jailbreak heuristic surface. Keep directionless Clavata dispatch and hallucination presentation flows out of the portable surface catalog.

Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>
Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>
@Pouyanpi
Pouyanpi force-pushed the pouyanpi/rail-library-stack-3-manifest-contract branch from dc2be90 to 372f999 Compare July 20, 2026 15:49
@Pouyanpi
Pouyanpi force-pushed the pouyanpi/rail-library-stack-4-builtins branch from 938e50e to 4abf429 Compare July 20, 2026 15:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size: XL status: triaged Triaged by a maintainer; eligible for automated review (CodeRabbit/Greptile).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant