Skip to content

feat(core): expose read-only views of function group filters - #2147

Open
DABH wants to merge 2 commits into
NVIDIA:developfrom
DABH:function-group-filter-introspection
Open

feat(core): expose read-only views of function group filters#2147
DABH wants to merge 2 commits into
NVIDIA:developfrom
DABH:function-group-filter-introspection

Conversation

@DABH

@DABH DABH commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Description

FunctionGroup is part of the public plugin-authoring contract: third-party plugin packages import it from the stable nat.plugin_api facade (per docs/source/extend/third-party-plugins.md, symbols exported from nat.plugin_api are the public contract for external plugin packages) and build provider tool groups through register_function_group. A group's composition can be shaped by two kinds of filter callbacks: a group-level filter_fn (constructor argument, or set_filter_fn) and per-function filter callbacks (add_function(..., filter_fn=...), or set_per_function_filter_fn). Today both are write-only from the outside: a plugin package that wants to validate, document, or instrument how a group is composed (for example, asserting in its own tests that the expected filters were wired up, or logging which functions are dynamically gated) has to reach into the private _filter_fn and _per_function_filter_fn attributes, which is exactly the kind of implementation-module reliance the third-party plugin guide tells authors to avoid.

This change adds two minimal read-only properties to FunctionGroup, mirroring the existing instance_name and middleware properties:

  • filter_fn returns the configured group-level filter callback, or None when no group-level filter has been set.
  • per_function_filter_fns returns an immutable types.MappingProxyType view of the per-function filter callbacks keyed by function name. The view rejects mutation with TypeError and stays live: filter callbacks registered later (through add_function or set_per_function_filter_fn) appear in a previously obtained view.

The change is purely additive: no existing behavior changes, and no new module-level symbols are added to nat.plugin_api (the properties ride along on the already-exported FunctionGroup class, so the pinned export test and the plugin API surface documentation are unaffected).

No tracking issue exists for this yet; happy to file one if the team prefers.

Testing

  • uv run pytest packages/nvidia_nat_core/tests/nat/builder/test_function_group.py — 28 passed (includes two new tests covering groups constructed with and without filters, filters set after construction, read-only enforcement on the mapping view, and the view reflecting later additions).
  • uv run pytest packages/nvidia_nat_core/tests/nat/builder — 319 passed.
  • uv run pre-commit run yapf --files packages/nvidia_nat_core/src/nat/builder/function.py packages/nvidia_nat_core/tests/nat/builder/test_function_group.py — passed.
  • uv run pre-commit run ruff-check --files packages/nvidia_nat_core/src/nat/builder/function.py packages/nvidia_nat_core/tests/nat/builder/test_function_group.py — passed.
  • uv run python ci/scripts/copyright.py --verify-apache-v2 — passed.

By Submitting this PR I confirm:

  • I am familiar with the Contributing Guidelines.
  • We require that all contributors "sign-off" on their commits. This certifies that the contribution is your original work, or you have rights to submit it under the same license, or a compatible license.
    • Any contribution which contains commits that are not Signed-Off will not be accepted.
  • When the PR is ready for review, new or existing tests cover these changes.
  • When the PR is ready for review, the documentation is up to date with these changes.

Summary by CodeRabbit

  • New Features

    • Function groups now expose configured filtering behavior.
    • Added access to per-function filters through a read-only mapping that reflects updates automatically.
  • Bug Fixes

    • Improved visibility and consistency of function filtering configuration while preventing accidental changes through the exposed mapping.

Signed-off-by: David Hyde <DABH@users.noreply.github.com>
@copy-pr-bot

copy-pr-bot Bot commented Jul 28, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

FunctionGroup now exposes its group-level filter and a read-only live mapping of per-function filters. Tests cover defaults, updates, contents, live changes, and mutation rejection.

Changes

FunctionGroup filter accessors

Layer / File(s) Summary
Expose filter accessors
packages/nvidia_nat_core/src/nat/builder/function.py
Adds filter_fn and per_function_filter_fns properties. The per-function mapping is read-only and reflects later updates.
Validate filter accessors
packages/nvidia_nat_core/tests/nat/builder/test_function_group.py
Tests default and configured group filters, per-function mappings, live updates, function additions, and rejected mutations.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise, descriptive, uses imperative mood, and accurately summarizes the exposed read-only function group filter views.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@willkill07

Copy link
Copy Markdown
Member

a plugin package that wants to validate, document, or instrument how a group is composed (for example, asserting in its own tests that the expected filters were wired up, or logging which functions are dynamically gated) has to reach into the private _filter_fn and _per_function_filter_fn attributes, which is exactly the kind of implementation-module reliance the third-party plugin guide tells authors to avoid.

I get your point, but at the same time, we intentionally don't want to expose filter_fn or per_function_filter_fn, even as a read-only property. Since it's not necessary for the runtime behavior and is only for testing, I don't see why manual inspection is inappropriate.

@DABH

DABH commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

Fair - agreed the filter callables themselves shouldn't be public, and manual inspection works for tests.

The narrower need I was reaching for is that at runtime, a plugin that validates configuration at startup wants to know whether a group's membership is dynamic (filtered), because dynamic membership changes what can be verified up front. It never needs the filters themselves though. Would a minimal dynamic_membership: bool read-only property (no callables exposed) be acceptable instead? If that still feels like surface you'd rather not carry, happy to close this and rely on manual inspection per your comment. Thanks either way!

@DABH
DABH marked this pull request as ready for review August 5, 2026 22:34
@DABH
DABH requested a review from a team as a code owner August 5, 2026 22:34

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
packages/nvidia_nat_core/tests/nat/builder/test_function_group.py (1)

318-335: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test direct assignment rejection for filter_fn.

This test verifies getter values and set_filter_fn, but it does not verify the read-only property contract. Add an assertion that direct assignment raises AttributeError.

Proposed test
     group.set_filter_fn(group_filter)
     assert group.filter_fn is group_filter
+
+    with pytest.raises(AttributeError):
+        setattr(group, "filter_fn", group_filter)

As per path instructions, tests must be comprehensive and validate the functionality. The coding guidelines also require tests when introducing changes.

🤖 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 `@packages/nvidia_nat_core/tests/nat/builder/test_function_group.py` around
lines 318 - 335, Extend test_function_group_filter_fn_property to verify the
read-only contract by asserting that direct assignment to group.filter_fn raises
AttributeError, while preserving the existing getter and set_filter_fn
assertions.

Sources: Coding guidelines, Path instructions

packages/nvidia_nat_core/src/nat/builder/function.py (1)

24-26: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Keep filter callbacks private.

These properties expose callback objects and the per-function callback map as public API. A read-only wrapper prevents map mutation, but it does not keep the callback implementation private or limit future compatibility commitments.

If the runtime only needs to detect dynamic membership, expose a boolean instead:

Proposed API narrowing
- from collections.abc import Mapping
  from collections.abc import Sequence
- from types import MappingProxyType

-    `@property`
-    def filter_fn(self) -> Callable[[Sequence[str]], Awaitable[Sequence[str]]] | None:
-        ...
-        return self._filter_fn
-
-    `@property`
-    def per_function_filter_fns(self) -> Mapping[str, Callable[[str], Awaitable[bool]]]:
-        ...
-        return MappingProxyType(self._per_function_filter_fn)
+    `@property`
+    def dynamic_membership(self) -> bool:
+        """Returns whether function-group membership can change dynamically."""
+        return self._filter_fn is not None or bool(self._per_function_filter_fn)

Update the new tests to validate dynamic_membership instead. The PR objective states that runtime validation needs only dynamic-membership state. As per path instructions, changes in core functionality should prioritize backward compatibility.

Also applies to: 821-836

🤖 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 `@packages/nvidia_nat_core/src/nat/builder/function.py` around lines 24 - 26,
Narrow the function builder API by removing public callback-object and
per-function callback-map exposure, replacing them with a boolean
dynamic_membership state used by runtime validation. Update the related tests to
assert dynamic_membership rather than inspecting callbacks or callback mappings,
while preserving existing behavior and compatibility for non-dynamic functions.

Source: Path instructions

🤖 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.

Nitpick comments:
In `@packages/nvidia_nat_core/src/nat/builder/function.py`:
- Around line 24-26: Narrow the function builder API by removing public
callback-object and per-function callback-map exposure, replacing them with a
boolean dynamic_membership state used by runtime validation. Update the related
tests to assert dynamic_membership rather than inspecting callbacks or callback
mappings, while preserving existing behavior and compatibility for non-dynamic
functions.

In `@packages/nvidia_nat_core/tests/nat/builder/test_function_group.py`:
- Around line 318-335: Extend test_function_group_filter_fn_property to verify
the read-only contract by asserting that direct assignment to group.filter_fn
raises AttributeError, while preserving the existing getter and set_filter_fn
assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 20c18d55-3d25-4185-9c3a-033ac1745c97

📥 Commits

Reviewing files that changed from the base of the PR and between 2618705 and 37cf476.

📒 Files selected for processing (2)
  • packages/nvidia_nat_core/src/nat/builder/function.py
  • packages/nvidia_nat_core/tests/nat/builder/test_function_group.py

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.

2 participants