feat(rails): add rail manifest contract#2157
Conversation
ba19362 to
ef00c0e
Compare
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
42af62e to
f7c929e
Compare
ef00c0e to
8c1b769
Compare
f7c929e to
f4fe58b
Compare
55a02d7 to
62d119d
Compare
f4fe58b to
fdef7b0
Compare
Greptile SummaryThis PR adds a versioned manifest contract for rails. The main changes are:
|
| Filename | Overview |
|---|---|
| nemoguardrails/manifests/surface_reference.py | Adds strict parsing and normalization for configured surface references. |
| nemoguardrails/manifests/manifest.py | Adds the typed rail manifest model and execution-contract validation. |
| nemoguardrails/manifests/catalog.py | Adds rail catalog construction, discovery, plugin loading, and uniqueness checks. |
| nemoguardrails/manifests/registry.py | Adds process-wide catalog caching and plugin-catalog lookup. |
| tests/manifests/test_surface_reference.py | Adds coverage for configured-surface parsing and malformed parameter handling. |
| tests/manifests/test_manifest.py | Adds coverage for manifest validation, import references, and surface bindings. |
| tests/manifests/test_catalog.py | Adds coverage for catalog indexing, duplicate rejection, and plugin handling. |
Reviews (15): Last reviewed commit: "fix(manifests): validate surface binding..." | Re-trigger Greptile
|
@CodeRabbit review |
✅ Action performedReview finished.
|
📝 WalkthroughWalkthroughAdds typed rail manifest and configuration-schema models, validated built-in/plugin catalog discovery, cached package-level catalog and surface accessors, and comprehensive tests for validation, parsing, discovery, plugins, and cache behavior. ChangesRail manifest system
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Caller
participant default_rail_catalog
participant RailCatalog
participant BuiltInRailModules
Caller->>default_rail_catalog: request default catalog
default_rail_catalog->>RailCatalog: discover_built_ins()
RailCatalog->>BuiltInRailModules: import rail.py modules
BuiltInRailModules-->>RailCatalog: return RAIL manifests
RailCatalog-->>default_rail_catalog: return validated catalog
default_rail_catalog-->>Caller: return cached catalog
🚥 Pre-merge checks | ✅ 5 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
nemoguardrails/manifests/schema.py (1)
41-41: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReplace
setattrwith direct assignment (Ruff B010).Static analysis flags this call; a direct attribute assignment is equivalent and avoids the lint warning.
🧹 Proposed fix
-setattr(RailConfigSpec, "__hash__", None) +RailConfigSpec.__hash__ = None🤖 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/manifests/schema.py` at line 41, Replace the setattr call configuring RailConfigSpec.__hash__ with direct class attribute assignment, preserving the value None and behavior while resolving Ruff B010.Source: Linters/SAST tools
nemoguardrails/manifests/manifest.py (1)
100-133: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winTighten import-target validation to reject embedded colons in the attribute path.
_validate_import_targetonly checks thatmodule_name/separator/attribute_pathare non-empty, so a target like"pkg:sub:attr"passes validation (attribute_path = "sub:attr"), butresolve_import_ref(Lines 362-368) splits only on".", so it will callgetattr(obj, "sub:attr")and fail with a confusingAttributeErrorrather than the clearValueErrorthis validator is meant to produce. This undercuts the module's own stated goal that "the executable spec is strict so misconfiguration fails loudly at load time."🛠️ Proposed tightened validation
+_IMPORT_TARGET = re.compile(r"^[A-Za-z_][A-Za-z0-9_.]*:[A-Za-z_][A-Za-z0-9_.]*$") + + def _validate_import_target(target: str) -> str: - module_name, separator, attribute_path = target.partition(":") - if not separator or not module_name or not attribute_path: + if not _IMPORT_TARGET.match(target): raise ValueError(f"Invalid import reference {target!r}; expected 'module:attribute'.") return target🤖 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/manifests/manifest.py` around lines 100 - 133, Update _validate_import_target to reject any import target whose attribute_path contains a colon, while preserving the existing non-empty module, separator, and attribute checks. Ensure targets such as “pkg:sub:attr” raise the validator’s clear ValueError before resolve_import_ref processes them.
🤖 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 `@tests/manifests/test_init.py`:
- Line 25: Remove the newly added inline comments near the second-call cache
assertion and the related assertion at the referenced locations in the test,
leaving the test logic unchanged.
- Around line 20-36: Ensure test_package_accessors_with_no_built_in_rails always
resets the rail manifest cache by wrapping its setup and assertions in
try/finally, or using a fixture finalizer. Keep _reset_rail_manifest_cache as
the cleanup action so _catalog and _plugin_catalogs are cleared even when an
assertion fails.
---
Nitpick comments:
In `@nemoguardrails/manifests/manifest.py`:
- Around line 100-133: Update _validate_import_target to reject any import
target whose attribute_path contains a colon, while preserving the existing
non-empty module, separator, and attribute checks. Ensure targets such as
“pkg:sub:attr” raise the validator’s clear ValueError before resolve_import_ref
processes them.
In `@nemoguardrails/manifests/schema.py`:
- Line 41: Replace the setattr call configuring RailConfigSpec.__hash__ with
direct class attribute assignment, preserving the value None and behavior while
resolving Ruff B010.
🪄 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: 81ba4bba-252c-4e48-9ca3-7a4fcb131a75
📒 Files selected for processing (9)
nemoguardrails/manifests/__init__.pynemoguardrails/manifests/catalog.pynemoguardrails/manifests/manifest.pynemoguardrails/manifests/schema.pypyproject.tomltests/manifests/test_catalog.pytests/manifests/test_init.pytests/manifests/test_manifest.pytests/manifests/test_schema.py
9bfe7a9 to
0f29e2d
Compare
2f707ff to
4e1f759
Compare
Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>
Mirror the manifests package layout under tests/manifests/ (test_manifest, test_catalog, test_schema, test_init) and refocus the rails/llm shim tests on re-export identity and the discovery helpers. Adds coverage for RailCatalog discovery and plugin entry-point loading, the config schema primitives, and the package-level accessors, which were previously untested. 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>
The parser now supports only: name name $key=value name $key=value $other="quoted value" Changes include: - No parenthesized parameter parsing. - Exact $name=value; spaces around = rejected. - No bare-value escaping or shell quote concatenation. - Parenthesized custom flows remain uninterpreted and ignored by manifest discovery. - Linear parsing, control rejection, duplicate detection, quote fidelity, and value-free errors retained. Signed-off-by: Pouyanpi <13303554+Pouyanpi@users.noreply.github.com>
Remove unused compatibility aliases and convenience APIs before the manifest contract is published. Document the retained public models, catalog accessors, import helpers, and configured-surface grammar. 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>
4e1f759 to
49f9f7f
Compare
Description
Add the neutral
nemoguardrails.manifestspackage so rail packages can declarewhat they contribute without making configuration, runtime, or catalog code
depend on concrete library implementations. The contract covers rail metadata,
configuration schemas, flows, actions, direct execution surfaces, requirements,
privacy, and structured import references.
Keep discovery cheap by representing configuration factories and actions as
lazy
module:attributereferences. Reading a manifest does not need to importits action module, configuration models, optional SDKs, or model dependencies.
Descriptive metadata accepts extensions, while executable declarations are
strictly validated so invalid bindings, duplicate ownership, and inconsistent
surface contracts fail at catalog construction.
Executable import edges are typed
ActionRefandConfigSpecRefobjects ratherthan unrestricted strings. Each reference validates its
module:attributeshape while remaining unresolved during discovery.
ActionRefalso separatesthe public action-registry name from its Python import target, allowing catalog
validation and later lazy dispatch even when the registered name differs from
the underlying symbol. Resolution and conformance checks verify the target only
when that executable layer is needed.
A
RailSurfacedeclares a backend-neutral mapping from a configured rail nameand direction to a Python action. Its typed bindings state whether each action
argument comes from a configured surface parameter, runtime context, or a
literal value. The contract is declared rather than inferred from action
decorators or Colang flow bodies.
configured surface references intentionally support only the configuration
syntax used by the manifest layer:
The parser rejects malformed, adjacent, blank, or duplicate parameters and
does not attempt to parse parenthesized Colang expressions. The catalog indexes
built-in manifests and can extend them with explicitly named plugin entry
points, while the registry provides cached process-wide access.
This PR establishes the contract and discovery primitives only. Built-in
manifest declarations, cross-dialect flow corrections, lazy action dispatch,
and end-to-end plugin activation are reviewed in later stack layers.
Impact
contributions, actions, flows, and direct execution contracts.
modules or parsing Colang.
the same action call from the same configured surface and request context.
into deterministic load-time errors.
processed; those integrations remain isolated in downstream PRs.
Stack
pouyanpi/rail-library-stack-3-manifest-contractdeveloppouyanpi/rail-library-stack-4-builtinspouyanpi/rail-library-stack-4b-flow-gatepouyanpi/rail-library-stack-5-lazy-actionspouyanpi/rail-library-stack-6-hardeningAI Assistance
Checklist
Summary by CodeRabbit
New Features
Bug Fixes
Tests