Skip to content

Conversation

@ericevans-nv
Copy link
Contributor

@ericevans-nv ericevans-nv commented Dec 2, 2025

Description

This PR introduces Function Policies, a component-based framework for intercepting and modifying function execution in the NeMo Agent Toolkit. Function policies are first-class components that wrap function calls with pre-invoke and post-invoke hooks, enabling features like input validation, output transformation, audit logging, data sanitization, and monitoring.

Function policies remove code duplication and tightly coupled logic by making these concerns configurable, reusable, and chainable.

Key Features

Policy Framework:

  • Pre and post-invoke hooks - intercept function calls before and after execution
  • Policy chaining - multiple policies execute in sequence, forming a transformation pipeline
  • Context objects - rich execution metadata for informed decision-making
  • Args forwarding - transparent *args/**kwargs forwarding enables intercepting any function signature
  • Error-resilient execution - failed policies are logged and skipped without blocking workflows
  • Streaming support - works with both synchronous invoke and streaming operations
  • Disabled policy support - policies can be toggled via enabled config flag

Dynamic Middleware:

  • Automatic component discovery - discovers and wraps functions from LLMs, embedders, retrievers, memory, object stores, and auth providers
  • Allowlist-based security - only configured component methods are wrapped (secure by default)
  • Configurable discovery - enable/disable discovery per component type
  • Explicit component references - specify exact components to wrap
  • Workflow function registration - automatically intercept user-defined workflow functions

Example Configuration

function_policies:
  input_logger:
    _type: logging
    enabled: true
    log_level: INFO
    max_value_length: 200

  output_logger:
    _type: logging
    enabled: true
    log_level: INFO

middleware:
  policy_middleware:
    _type: dynamic_middleware
    register_workflow_functions: true
    register_llms: true
    register_embedders: true
    pre_invoke_policy: ["input_logger"]
    post_invoke_policy: ["output_logger"]

workflow:
  _type: react_agent
  middleware: ["policy_middleware"]

Step 1: Define Configuration

from nat.data_models.function_policy import FunctionPolicyBaseConfig
from pydantic import Field

class InputValidationPolicyConfig(FunctionPolicyBaseConfig, name="input_validation"):
    max_length: int = Field(default=1000, description="Maximum input length")

Step 2: Implement Policy

from nat.function_policy.interface import FunctionPolicyBase, PreInvokeContext, PostInvokeContext
from typing import Any

class InputValidationPolicy(FunctionPolicyBase[InputValidationPolicyConfig]):
    async def on_pre_invoke(self, context: PreInvokeContext) -> tuple[Any, ...] | None:
        # Access args via context.function_args (tuple)
        logger.info(f"Input args: {context.function_args}")
        return None  # Return None to leave args unchanged, or return modified tuple

    async def on_post_invoke(self, context: PostInvokeContext) -> Any:
        logger.info(f"Output: {context.function_output}")
        return None  # Return None to leave output unchanged

Step 3: Register

@register_function_policy(config_type=InputValidationPolicyConfig)
async def input_validation(config, builder):
    yield InputValidationPolicy(config=config)

CLI Commands

nat info function-policies
nat info function-policies --verbose
nat info function-policy <name>

Component Discovery

Dynamic middleware automatically discovers and wraps component methods based on allowlists.

Allowlisted Functions by Component

LLMs:
invoke, ainvoke, stream, astream

Embedders:
embed_query, aembed_query

Retrievers:
search

Memory:
search, add_items, remove_items

Object Stores:
put_object, get_object, delete_object, upsert_object

Auth Providers:
authenticate

Users may extend allowlists in configuration to support additional component methods.

Summary by CodeRabbit

  • New Features

    • Function Policies: pre/post-invoke policy support and a built-in logging policy for function call auditing and configurable truncation.
    • Dynamic Middleware: runtime discovery and optional auto-registration of workflow components with automatic function interception.
    • CLI: new info commands to list and show available function policies.
  • Documentation

    • Comprehensive Function Policies reference with examples, configuration, usage, and troubleshooting.
  • Tests

    • Expanded coverage for policies, dynamic middleware, cache middleware, and CLI behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

@ericevans-nv ericevans-nv self-assigned this Dec 2, 2025
@ericevans-nv ericevans-nv requested a review from a team as a code owner December 2, 2025 03:48
@ericevans-nv ericevans-nv added feature request New feature or request non-breaking Non-breaking change labels Dec 2, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 2, 2025

Walkthrough

Adds a Function Policies subsystem (interfaces, configs, logging policy, registration and CLI), builder and type-registry support, DynamicFunctionMiddleware for runtime discovery/wrapping, generalizes middleware signatures to variadic *args/**kwargs, refactors cache middleware into its own package, updates docs, and adds extensive tests.

Changes

Cohort / File(s) Summary
Function policy core
src/nat/function_policy/interface.py, src/nat/function_policy/__init__.py, src/nat/data_models/function_policy.py
New FunctionPolicyBase, PreInvokeContext, PostInvokeContext, PolicyConfig base and TypeVar; package exports added.
Policy implementations & registration
src/nat/function_policy/logging/logging_policy.py, src/nat/function_policy/logging/logging_policy_config.py, src/nat/function_policy/logging/register.py, src/nat/function_policy/logging/__init__.py, src/nat/function_policy/register.py
LoggingPolicy and LoggingPolicyConfig added; logging policy registered via new register_function_policy decorator and small register alias.
Builder & workflow plumbing
src/nat/builder/builder.py, src/nat/builder/workflow_builder.py, src/nat/builder/component_utils.py
Builder API gains add/get/get_config for function policies; WorkflowBuilder stores ConfiguredFunctionPolicy instances; component grouping/order updated.
Type registry & CLI
src/nat/cli/type_registry.py, src/nat/cli/register_workflow.py, src/nat/cli/commands/info/info.py, src/nat/cli/commands/info/list_function_policies.py
RegisteredFunctionPolicyInfo and registry APIs added; register_function_policy decorator implemented; CLI commands function-policies and function-policy added.
Config & component models
src/nat/data_models/component.py, src/nat/data_models/component_ref.py, src/nat/data_models/config.py
New ComponentEnum/ComponentGroup entries for FUNCTION_POLICY(S); FunctionPolicyRef; Config adds function_policies field, validation/annotation and summary changes.
Dynamic middleware & inventory
src/nat/middleware/dynamic/dynamic_function_middleware.py, src/nat/middleware/dynamic/dynamic_middleware_config.py, src/nat/middleware/dynamic/register.py, src/nat/middleware/utils/workflow_inventory.py, src/nat/middleware/utils/__init__.py
New DynamicFunctionMiddleware, DynamicMiddlewareConfig and AllowedComponentFunctions; WorkflowInventory models and dynamic middleware register factory added.
Middleware signature & chaining
src/nat/middleware/middleware.py, src/nat/middleware/function_middleware.py, src/nat/middleware/__init__.py, src/nat/middleware/register.py
CallNext/CallNextStream widened to variadic callables; middleware_invoke/stream and wrappers accept/forward *args/**kwargs; exports reordered and legacy cache registration removed.
Cache middleware refactor
src/nat/middleware/cache/cache_middleware.py, src/nat/middleware/cache/cache_middleware_config.py, src/nat/middleware/cache/register.py, src/nat/middleware/cache/__init__.py
Cache middleware moved into cache package, signatures generalized to *args/**kwargs, new CacheMiddlewareConfig model and register factory added; legacy in-file config/export removed.
Docs
docs/source/reference/function-policies.md, docs/source/reference/cli.md, docs/source/reference/middleware.md
New detailed function-policies doc; CLI docs extended with function-policies/function-policy commands; middleware docs updated for variadic args and dynamic middleware behavior.
Tests
tests/nat/builder/test_builder.py, tests/nat/builder/test_component_utils.py, tests/nat/cli/commands/info/test_list_function_policies.py, tests/nat/function_policy/test_policy_interface.py, tests/nat/middleware/test_dynamic_middleware.py, tests/nat/middleware/test_cache_middleware.py, tests/nat/middleware/test_middleware_components.py
New and updated tests covering policy lifecycle/registration, CLI listing/showing, policy interface, dynamic middleware discovery/wrapping, cache middleware signature/import updates, and component utils.
Misc & CI
src/nat/middleware/utils/__init__.py, src/nat/function_policy/logging/__init__.py, src/nat/middleware/dynamic/__init__.py, ci/scripts/path_checks.py, ci/vale/styles/config/vocabularies/nat/accept.txt
License header additions, minor vocab whitelist and CI allowlist change.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Builder
    participant DynamicMiddleware
    participant Registry
    participant PrePolicy as Pre-Invoke Policy
    participant TargetFn as Target Function
    participant PostPolicy as Post-Invoke Policy

    Client->>Builder: retrieve or call function
    Builder->>DynamicMiddleware: intercept / wrap function (patch)
    DynamicMiddleware->>Registry: discover & load policies
    Registry-->>DynamicMiddleware: list of configured policies
    Client->>TargetFn: call wrapped_func(*args, **kwargs)
    wrappedFunc->>DynamicMiddleware: run middleware chain
    DynamicMiddleware->>PrePolicy: on_pre_invoke(context)
    PrePolicy-->>DynamicMiddleware: (maybe) modified args
    DynamicMiddleware->>TargetFn: call_next(*modified_args, **kwargs)
    TargetFn-->>DynamicMiddleware: output
    DynamicMiddleware->>PostPolicy: on_post_invoke(context_with_output)
    PostPolicy-->>DynamicMiddleware: (maybe) modified output
    DynamicMiddleware-->>Client: final_output
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Areas requiring special attention:

  • src/nat/middleware/dynamic/dynamic_function_middleware.py — builder patching, interception/allowlist logic, wrapping, registration/unregistration and error/fallback paths.
  • src/nat/middleware/middleware.py & src/nat/middleware/function_middleware.py — signature changes to variadic CallNext/CallNextStream and correct propagation of *args/**kwargs through chains.
  • src/nat/builder/workflow_builder.py — function policy lifecycle, ConfiguredFunctionPolicy management, serialization into final config, duplicate handling and error paths.
  • src/nat/cli/type_registry.py & src/nat/cli/register_workflow.py — new registry entries, decorator semantics, and duplicate/lookup error messages.
  • src/nat/middleware/cache/cache_middleware.py & related tests — cache key derivation from args[0], new call_next/stream conventions, and updated import paths.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.46% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main changes: introducing Function Policy component and DynamicFunctionMiddleware, using imperative mood, and is well within the recommended length of 72 characters.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link

@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: 3

🧹 Nitpick comments (20)
src/nat/function_policy/__init__.py (1)

21-26: Sort __all__ alphabetically.

The __all__ list should be sorted alphabetically per the static analysis hint (RUF022).

Apply this diff to sort the exports:

 __all__ = [
     "FunctionPolicyBase",
     "PolicyConfigT",
-    "PreInvokeContext",
     "PostInvokeContext",
+    "PreInvokeContext",
 ]
src/nat/middleware/register.py (1)

27-38: Silence Ruff ARG001 for unused builder without changing behavior.

builder is intentionally unused here but required by the register_middleware component pattern, so Ruff’s ARG001 is expected. Easiest fix is to rename the parameter to start with an underscore so tools treat it as intentionally unused:

-@register_middleware(config_type=CacheMiddlewareConfig)
-async def cache_middleware(config: CacheMiddlewareConfig, builder: Builder):
+@register_middleware(config_type=CacheMiddlewareConfig)
+async def cache_middleware(config: CacheMiddlewareConfig, _builder: Builder):

This keeps the calling convention (positional args) intact and aligns with linting guidelines.

src/nat/cli/commands/info/list_function_policies.py (1)

19-160: CLI for function policies is coherent and matches registry semantics.

The function-policies and function-policy commands correctly:

  • force plugin discovery before querying the registry,
  • handle the “no policies” case with a clear message,
  • and surface useful metadata and config schema details when available.

If you find yourself adding more policy-related commands later, consider extracting the common “discover plugins + fetch policy list” logic into a small helper to avoid duplication, but it’s not necessary for this PR.

docs/source/reference/function-policies.md (4)

22-23: Use full toolkit name on first reference.

Per coding guidelines, the first reference should use "NVIDIA NeMo Agent toolkit" before subsequent abbreviated uses.

-Function policies provide a standardized way to intercept and modify function calls in the NeMo Agent toolkit. They work by wrapping functions with hooks that execute before (`on_pre_invoke`) and after (`on_post_invoke`) the function runs.
+Function policies provide a standardized way to intercept and modify function calls in the NVIDIA NeMo Agent toolkit. They work by wrapping functions with hooks that execute before (`on_pre_invoke`) and after (`on_post_invoke`) the function runs.

Based on coding guidelines.


164-172: Add language specifier to fenced code block.

Static analysis flagged this as missing a language specifier. Since this is a text-based flow diagram, use text as the language identifier.

-```
+```text
 Original Input (10)
     ↓
 Policy 1: Add 5 → Returns 15
     ↓
 Policy 2: Multiply by 2 → Returns 30
     ↓
 Function receives: 30

---

`180-188`: **Add language specifier to fenced code block.**

Same issue - use `text` for this ASCII flow diagram.

```diff
-```
+```text
 Function returns: 100
     ↓
 Policy 1: Log result → Returns 100
     ↓
 Policy 2: Round to nearest 10 → Returns 100
     ↓
 User receives: 100

---

`463-480`: **Add language specifier to ASCII diagram.**

Use `text` for consistency with other ASCII diagrams.

```diff
-```
+```text
 ┌─────────────────────────────────────────┐
 │         Dynamic Middleware              │
src/nat/function_policy/interface.py (3)

88-103: Remove extra blank lines in docstring.

There are extra blank lines before the closing triple quotes that should be removed for cleaner formatting.

         Returns:
             Transformed input value, or None to preserve current value
-
-
         """
         pass

105-121: Remove extra blank lines in docstring.

Same issue with extra blank lines before the closing quotes.

         Returns:
             Transformed output value, or None to preserve current value
-
-
         """
         pass

123-128: Consider sorting __all__ for consistency.

Static analysis suggests sorting __all__ alphabetically, which is a common Python convention.

 __all__ = [
     "FunctionPolicyBase",
+    "PolicyConfigT",
+    "PostInvokeContext",
     "PreInvokeContext",
-    "PostInvokeContext",
-    "PolicyConfigT",
 ]
src/nat/middleware/dynamic_function_middleware.py (4)

100-104: Duplicate "already registered" checks in discovery methods.

Each _discover_and_register_* method checks if a component is already in the inventory, but _should_intercept_* methods also perform this same check at lines 393-394, 413-414, etc. This creates redundant checks when both code paths execute sequentially.

While not a bug, this adds minor overhead on every discovery call. Consider removing the duplicate check from either location.

Also applies to: 139-140, 178-179, 216-217, 255-256, 295-296


116-119: Intentional error resilience with broad exception handling.

The bare Exception catches flagged by static analysis are intentional design choices for error resilience. Per the PR description, failed policies should be logged and skipped rather than crashing the workflow. The logger.debug calls provide visibility without noise.

However, consider using logger.warning instead of logger.debug for registration failures, as these may indicate configuration issues worth surfacing.

             except Exception as e:
-                logger.debug("Failed to register component function '%s' on LLM '%s': %s", function_name, llm_name, e)
+                logger.warning("Failed to register component function '%s' on LLM '%s': %s", function_name, llm_name, e)

Also applies to: 152-159, 191-198, 229-236, 269-276, 309-316


801-833: Remove unused component_type parameter or document its future use.

Static analysis correctly identifies that component_type is never used in _get_callable_functions. Either remove it or add a docstring note explaining if it's reserved for future use.

-    def _get_callable_functions(self, instance: Any, component_type: str | None = None) -> set[str]:
+    def _get_callable_functions(self, instance: Any) -> set[str]:
         """Get all callable functions from component instance that can be safely wrapped.
 
         This discovers ALL potentially wrappable component functions without allowlist filtering.
         Safety checks ensure only valid, callable, bound functions are included.
 
         Args:
             instance: The component instance to introspect
-            component_type: Type of component (for logging/metadata, not filtering)
 
         Returns:
             Set of all valid component function names that could be wrapped
         """

If removing the parameter, also update the call sites at lines 106, 142, 181, 219, 258, 298.


889-998: Builder patching infrastructure is comprehensive but tightly coupled.

The patching mechanism works correctly, but the tight coupling to specific builder methods means:

  1. Adding new component types requires adding new patch methods
  2. If builder method signatures change, this code must be updated

Consider documenting this coupling in the class docstring so future maintainers understand the relationship.

src/nat/middleware/utils/workflow_inventory.py (1)

29-88: Allowlist constant looks good; consider documenting evolution/extension strategy.

The COMPONENT_FUNCTION_ALLOWLISTS mapping cleanly captures the default surfaces per ComponentGroup and aligns with the test expectations. As a minor improvement, you might add a brief module-level comment or docstring noting that these names must stay in sync with the public client APIs, and that new client methods should be added here (rather than ad hoc in middleware) so discovery and tests remain consistent.

src/nat/data_models/middleware.py (1)

52-155: Dynamic middleware config and allowlist merging are consistent and safe.

AllowedComponentFunctions.merge_with_defaults() correctly unions user-provided sets with COMPONENT_FUNCTION_ALLOWLISTS, and the DynamicMiddlewareConfig.allowed_component_functions docstring matches this “extend, don’t override” behavior. If you want to make this even clearer to readers, you could (optionally) note in the docstring that shrinking the default allowlist isn’t supported yet and add a -> AllowedComponentFunctions return annotation on the validator, but the current implementation is sound.

tests/nat/middleware/test_dynamic_middleware.py (3)

278-281: Silence unused value parameters in local mock_call_next helpers.

Ruff flags these value parameters as unused (ARG001). Since the tests don’t depend on them, you can mark them as intentionally unused by renaming to _:

-    async def mock_call_next(value):
+    async def mock_call_next(_):
         return expected_output

Apply the same pattern to the other mock_call_next(value) definitions in this file (invoke/stream, pre/post failure paths) to satisfy linting without changing behavior.

Also applies to: 378-380, 442-444, 471-474, 550-552, 680-683, 783-785


811-817: Silence unused lambda parameters in disabled streaming policy helpers.

The lambdas passed as pre_invoke_modifier and post_invoke_modifier don’t use their arguments, triggering Ruff ARG005. You can make that explicit by naming the parameter _:

-    DisabledPrePolicy = create_tracking_policy("disabled_pre",
-                                               execution_order,
-                                               pre_invoke_modifier=lambda inp: {"should_not_see": "this"})
-    DisabledPostPolicy = create_tracking_policy("disabled_post",
-                                                execution_order,
-                                                post_invoke_modifier=lambda out: "MODIFIED")
+    DisabledPrePolicy = create_tracking_policy("disabled_pre",
+                                               execution_order,
+                                               pre_invoke_modifier=lambda _: {"should_not_see": "this"})
+    DisabledPostPolicy = create_tracking_policy("disabled_post",
+                                                execution_order,
+                                                post_invoke_modifier=lambda _: "MODIFIED")

1667-1707: Use llm_allowlist in the test or drop it to avoid dead code.

llm_allowlist = COMPONENT_FUNCTION_ALLOWLISTS[ComponentGroup.LLMS] is never used, triggering Ruff F841. You can turn this into a useful assertion that ties the test to the configured allowlist:

-    # Get the allowlist for LLMs
-    llm_allowlist = COMPONENT_FUNCTION_ALLOWLISTS[ComponentGroup.LLMS]
-
-    # Verify only allowlisted functions are registered
+    # Get the allowlist for LLMs and ensure expected names stay covered
+    llm_allowlist = COMPONENT_FUNCTION_ALLOWLISTS[ComponentGroup.LLMS]
+    assert {"generate", "agenerate", "predict"}.issubset(llm_allowlist)
+
+    # Verify only allowlisted functions are registered

Alternatively, if you don’t want this coupling, you can simply remove the unused variable and its import.

src/nat/cli/type_registry.py (1)

967-1041: Consider exposing function policies via the generic get_infos_by_type helpers.

ComponentEnum.FUNCTION_POLICY is used when registering policies, but get_infos_by_type / get_registered_types_by_component_type don’t currently handle that enum value. If any CLI or tooling later calls these helpers with ComponentEnum.FUNCTION_POLICY, it will hit the generic ValueError. You can wire it in similarly to other components:

     def get_infos_by_type(self, component_type: ComponentEnum) -> dict:
@@
-        if component_type == ComponentEnum.MIDDLEWARE:
-            return self._registered_middleware
+        if component_type == ComponentEnum.MIDDLEWARE:
+            return self._registered_middleware
+
+        if component_type == ComponentEnum.FUNCTION_POLICY:
+            return self._registered_function_policies
@@
     def get_registered_types_by_component_type(self, component_type: ComponentEnum) -> list[str]:
@@
-        if component_type == ComponentEnum.TTC_STRATEGY:
-            return [i.static_type() for i in self._registered_ttc_strategies]
+        if component_type == ComponentEnum.TTC_STRATEGY:
+            return [i.static_type() for i in self._registered_ttc_strategies]
+
+        if component_type == ComponentEnum.FUNCTION_POLICY:
+            return [i.static_type() for i in self._registered_function_policies]

If your new list/show CLI for function policies already calls the more specific get_registered_function_policies(), this is not strictly required for correctness but will keep the generic discovery API complete.

Also applies to: 1045-1095

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4e9bfed and 5509f49.

📒 Files selected for processing (27)
  • docs/source/reference/cli.md (2 hunks)
  • docs/source/reference/function-policies.md (1 hunks)
  • docs/source/reference/middleware.md (1 hunks)
  • src/nat/builder/builder.py (4 hunks)
  • src/nat/builder/component_utils.py (5 hunks)
  • src/nat/builder/workflow_builder.py (9 hunks)
  • src/nat/cli/commands/info/info.py (2 hunks)
  • src/nat/cli/commands/info/list_function_policies.py (1 hunks)
  • src/nat/cli/register_workflow.py (3 hunks)
  • src/nat/cli/type_registry.py (8 hunks)
  • src/nat/data_models/component.py (2 hunks)
  • src/nat/data_models/component_ref.py (1 hunks)
  • src/nat/data_models/config.py (6 hunks)
  • src/nat/data_models/function_policy.py (1 hunks)
  • src/nat/data_models/middleware.py (2 hunks)
  • src/nat/function_policy/__init__.py (1 hunks)
  • src/nat/function_policy/interface.py (1 hunks)
  • src/nat/middleware/__init__.py (1 hunks)
  • src/nat/middleware/dynamic_function_middleware.py (1 hunks)
  • src/nat/middleware/register.py (2 hunks)
  • src/nat/middleware/utils/__init__.py (1 hunks)
  • src/nat/middleware/utils/workflow_inventory.py (1 hunks)
  • tests/nat/builder/test_builder.py (10 hunks)
  • tests/nat/builder/test_component_utils.py (3 hunks)
  • tests/nat/cli/commands/info/test_list_function_policies.py (1 hunks)
  • tests/nat/function_policy/test_policy_interface.py (1 hunks)
  • tests/nat/middleware/test_dynamic_middleware.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (16)
**/*.{md,rst,py}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Use 'NVIDIA NeMo Agent toolkit' on first use, then 'NeMo Agent toolkit' for subsequent references

Files:

  • docs/source/reference/middleware.md
  • tests/nat/cli/commands/info/test_list_function_policies.py
  • src/nat/data_models/function_policy.py
  • src/nat/middleware/register.py
  • src/nat/middleware/dynamic_function_middleware.py
  • src/nat/middleware/utils/__init__.py
  • src/nat/function_policy/interface.py
  • docs/source/reference/function-policies.md
  • src/nat/builder/component_utils.py
  • src/nat/data_models/component.py
  • src/nat/middleware/utils/workflow_inventory.py
  • docs/source/reference/cli.md
  • src/nat/data_models/component_ref.py
  • src/nat/data_models/middleware.py
  • src/nat/cli/type_registry.py
  • tests/nat/builder/test_component_utils.py
  • src/nat/cli/commands/info/info.py
  • tests/nat/builder/test_builder.py
  • src/nat/function_policy/__init__.py
  • src/nat/middleware/__init__.py
  • src/nat/cli/commands/info/list_function_policies.py
  • src/nat/builder/builder.py
  • src/nat/builder/workflow_builder.py
  • tests/nat/function_policy/test_policy_interface.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • src/nat/cli/register_workflow.py
  • src/nat/data_models/config.py
**/*.{md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Use 'NeMo Agent Toolkit' (capitalize 'T') when the name appears in headings

Files:

  • docs/source/reference/middleware.md
  • docs/source/reference/function-policies.md
  • docs/source/reference/cli.md
docs/**/*.{md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Never use deprecated names: Agent Intelligence toolkit, aiqtoolkit, AgentIQ, AIQ/aiq in documentation (unless intentionally referring to deprecated versions)

Files:

  • docs/source/reference/middleware.md
  • docs/source/reference/function-policies.md
  • docs/source/reference/cli.md
**/*.{py,js,ts,yaml,yml,json,md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Indent with 4 spaces, never tabs, and ensure every file ends with a single newline

Files:

  • docs/source/reference/middleware.md
  • tests/nat/cli/commands/info/test_list_function_policies.py
  • src/nat/data_models/function_policy.py
  • src/nat/middleware/register.py
  • src/nat/middleware/dynamic_function_middleware.py
  • src/nat/middleware/utils/__init__.py
  • src/nat/function_policy/interface.py
  • docs/source/reference/function-policies.md
  • src/nat/builder/component_utils.py
  • src/nat/data_models/component.py
  • src/nat/middleware/utils/workflow_inventory.py
  • docs/source/reference/cli.md
  • src/nat/data_models/component_ref.py
  • src/nat/data_models/middleware.py
  • src/nat/cli/type_registry.py
  • tests/nat/builder/test_component_utils.py
  • src/nat/cli/commands/info/info.py
  • tests/nat/builder/test_builder.py
  • src/nat/function_policy/__init__.py
  • src/nat/middleware/__init__.py
  • src/nat/cli/commands/info/list_function_policies.py
  • src/nat/builder/builder.py
  • src/nat/builder/workflow_builder.py
  • tests/nat/function_policy/test_policy_interface.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • src/nat/cli/register_workflow.py
  • src/nat/data_models/config.py
docs/source/**/*.{md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

docs/source/**/*.{md,rst}: Documentation must be clear and comprehensive, without TODOs, FIXMEs, or placeholder text
Ensure documentation is free of offensive or outdated terms
Ensure documentation is free of spelling mistakes and do not use words listed in ci/vale/styles/config/vocabularies/nat/reject.txt

Files:

  • docs/source/reference/middleware.md
  • docs/source/reference/function-policies.md
  • docs/source/reference/cli.md
**/*.{py,md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Version numbers are derived automatically by setuptools-scm; never hard-code them in code or docs

Files:

  • docs/source/reference/middleware.md
  • tests/nat/cli/commands/info/test_list_function_policies.py
  • src/nat/data_models/function_policy.py
  • src/nat/middleware/register.py
  • src/nat/middleware/dynamic_function_middleware.py
  • src/nat/middleware/utils/__init__.py
  • src/nat/function_policy/interface.py
  • docs/source/reference/function-policies.md
  • src/nat/builder/component_utils.py
  • src/nat/data_models/component.py
  • src/nat/middleware/utils/workflow_inventory.py
  • docs/source/reference/cli.md
  • src/nat/data_models/component_ref.py
  • src/nat/data_models/middleware.py
  • src/nat/cli/type_registry.py
  • tests/nat/builder/test_component_utils.py
  • src/nat/cli/commands/info/info.py
  • tests/nat/builder/test_builder.py
  • src/nat/function_policy/__init__.py
  • src/nat/middleware/__init__.py
  • src/nat/cli/commands/info/list_function_policies.py
  • src/nat/builder/builder.py
  • src/nat/builder/workflow_builder.py
  • tests/nat/function_policy/test_policy_interface.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • src/nat/cli/register_workflow.py
  • src/nat/data_models/config.py
**/*

⚙️ CodeRabbit configuration file

**/*: # Code Review Instructions

  • Ensure the code follows best practices and coding standards. - For Python code, follow
    PEP 20 and
    PEP 8 for style guidelines.
  • Check for security vulnerabilities and potential issues. - Python methods should use type hints for all parameters and return values (except for return values of None,
    in that situation no return type hint is needed).
    Example:
    def my_function(param1: int, param2: str) -> bool:
        pass
  • For Python exception handling, ensure proper stack trace preservation:
    • When re-raising exceptions: use bare raise statements to maintain the original stack trace,
      and use logger.error() (not logger.exception()) to avoid duplicate stack trace output.
    • When catching and logging exceptions without re-raising: always use logger.exception()
      to capture the full stack trace information.

Documentation Review Instructions - Verify that documentation and comments are clear and comprehensive. - Verify that the documentation doesn't contain any TODOs, FIXMEs or placeholder text like "lorem ipsum". - Verify that the documentation doesn't contain any offensive or outdated terms. - Verify that documentation and comments are free of spelling mistakes, ensure the documentation doesn't contain any

words listed in the ci/vale/styles/config/vocabularies/nat/reject.txt file, words that might appear to be
spelling mistakes but are listed in the ci/vale/styles/config/vocabularies/nat/accept.txt file are OK.

Misc. - All code (except .mdc files that contain Cursor rules) should be licensed under the Apache License 2.0,

and should contain an Apache License 2.0 header comment at the top of each file.

  • Confirm that copyright years are up-to date whenever a file is changed.

Files:

  • docs/source/reference/middleware.md
  • tests/nat/cli/commands/info/test_list_function_policies.py
  • src/nat/data_models/function_policy.py
  • src/nat/middleware/register.py
  • src/nat/middleware/dynamic_function_middleware.py
  • src/nat/middleware/utils/__init__.py
  • src/nat/function_policy/interface.py
  • docs/source/reference/function-policies.md
  • src/nat/builder/component_utils.py
  • src/nat/data_models/component.py
  • src/nat/middleware/utils/workflow_inventory.py
  • docs/source/reference/cli.md
  • src/nat/data_models/component_ref.py
  • src/nat/data_models/middleware.py
  • src/nat/cli/type_registry.py
  • tests/nat/builder/test_component_utils.py
  • src/nat/cli/commands/info/info.py
  • tests/nat/builder/test_builder.py
  • src/nat/function_policy/__init__.py
  • src/nat/middleware/__init__.py
  • src/nat/cli/commands/info/list_function_policies.py
  • src/nat/builder/builder.py
  • src/nat/builder/workflow_builder.py
  • tests/nat/function_policy/test_policy_interface.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • src/nat/cli/register_workflow.py
  • src/nat/data_models/config.py
docs/source/**/*

⚙️ CodeRabbit configuration file

This directory contains the source code for the documentation. All documentation should be written in Markdown format. Any image files should be placed in the docs/source/_static directory.

Files:

  • docs/source/reference/middleware.md
  • docs/source/reference/function-policies.md
  • docs/source/reference/cli.md
**/*.{py,toml,yaml,yml}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Use abbreviations: 'nat' for API namespace and CLI tool, 'nvidia-nat' for package name, 'NAT' for environment variable prefixes and informal comments

Files:

  • tests/nat/cli/commands/info/test_list_function_policies.py
  • src/nat/data_models/function_policy.py
  • src/nat/middleware/register.py
  • src/nat/middleware/dynamic_function_middleware.py
  • src/nat/middleware/utils/__init__.py
  • src/nat/function_policy/interface.py
  • src/nat/builder/component_utils.py
  • src/nat/data_models/component.py
  • src/nat/middleware/utils/workflow_inventory.py
  • src/nat/data_models/component_ref.py
  • src/nat/data_models/middleware.py
  • src/nat/cli/type_registry.py
  • tests/nat/builder/test_component_utils.py
  • src/nat/cli/commands/info/info.py
  • tests/nat/builder/test_builder.py
  • src/nat/function_policy/__init__.py
  • src/nat/middleware/__init__.py
  • src/nat/cli/commands/info/list_function_policies.py
  • src/nat/builder/builder.py
  • src/nat/builder/workflow_builder.py
  • tests/nat/function_policy/test_policy_interface.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • src/nat/cli/register_workflow.py
  • src/nat/data_models/config.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

**/*.py: Follow PEP 20 and PEP 8 for Python style guidelines
Run yapf second (PEP 8 base, column_limit = 120) for Python formatting
Use ruff check --fix (via pre-commit) for linting using configuration embedded in pyproject.toml, fix warnings unless explicitly ignored
Use snake_case for functions and variables, PascalCase for classes, UPPER_CASE for constants
All public APIs require Python 3.11+ type hints on parameters and return values
Prefer collections.abc / typing abstractions (Sequence over list) for type hints
Use typing.Annotated for units or extra metadata when useful
Treat pyright warnings (configured in pyproject.toml) as errors during development
Preserve stack traces and prevent duplicate logging when handling exceptions; use bare raise statements and logger.error() when re-raising
When catching and logging exceptions without re-raising, always use logger.exception() to capture the full stack trace information
Provide Google-style docstrings for every public module, class, function and CLI command
The first line of docstrings must be a concise description ending with a period (Vale checks this)
Surround code entities with backticks in docstrings to avoid Vale false-positives
Validate and sanitise all user input, especially in web or CLI interfaces
Prefer httpx with SSL verification enabled by default and follow OWASP Top-10 recommendations
Use async/await for I/O-bound work (HTTP, DB, file reads)
Cache expensive computations with functools.lru_cache or an external cache when appropriate
Leverage NumPy vectorised operations whenever beneficial and feasible

Files:

  • tests/nat/cli/commands/info/test_list_function_policies.py
  • src/nat/data_models/function_policy.py
  • src/nat/middleware/register.py
  • src/nat/middleware/dynamic_function_middleware.py
  • src/nat/middleware/utils/__init__.py
  • src/nat/function_policy/interface.py
  • src/nat/builder/component_utils.py
  • src/nat/data_models/component.py
  • src/nat/middleware/utils/workflow_inventory.py
  • src/nat/data_models/component_ref.py
  • src/nat/data_models/middleware.py
  • src/nat/cli/type_registry.py
  • tests/nat/builder/test_component_utils.py
  • src/nat/cli/commands/info/info.py
  • tests/nat/builder/test_builder.py
  • src/nat/function_policy/__init__.py
  • src/nat/middleware/__init__.py
  • src/nat/cli/commands/info/list_function_policies.py
  • src/nat/builder/builder.py
  • src/nat/builder/workflow_builder.py
  • tests/nat/function_policy/test_policy_interface.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • src/nat/cli/register_workflow.py
  • src/nat/data_models/config.py
**/test_*.py

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

**/test_*.py: Use pytest with pytest-asyncio for asynchronous code testing
Test functions should be named using the test_ prefix, using snake_case
Extract frequently repeated code into pytest fixtures, which should be named using the fixture_ prefix and define the name argument in the decorator
Mock external services with pytest_httpserver or unittest.mock instead of hitting live endpoints
Mark slow tests with @pytest.mark.slow so they can be skipped in the default test suite
Mark integration tests requiring external services with @pytest.mark.integration so they can be skipped in the default test suite

Files:

  • tests/nat/cli/commands/info/test_list_function_policies.py
  • tests/nat/builder/test_component_utils.py
  • tests/nat/builder/test_builder.py
  • tests/nat/function_policy/test_policy_interface.py
  • tests/nat/middleware/test_dynamic_middleware.py
**/*.{py,env,toml,yaml,yml,json}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Never commit API keys, credentials or personal data; use environment variables or .env files excluded from Git

Files:

  • tests/nat/cli/commands/info/test_list_function_policies.py
  • src/nat/data_models/function_policy.py
  • src/nat/middleware/register.py
  • src/nat/middleware/dynamic_function_middleware.py
  • src/nat/middleware/utils/__init__.py
  • src/nat/function_policy/interface.py
  • src/nat/builder/component_utils.py
  • src/nat/data_models/component.py
  • src/nat/middleware/utils/workflow_inventory.py
  • src/nat/data_models/component_ref.py
  • src/nat/data_models/middleware.py
  • src/nat/cli/type_registry.py
  • tests/nat/builder/test_component_utils.py
  • src/nat/cli/commands/info/info.py
  • tests/nat/builder/test_builder.py
  • src/nat/function_policy/__init__.py
  • src/nat/middleware/__init__.py
  • src/nat/cli/commands/info/list_function_policies.py
  • src/nat/builder/builder.py
  • src/nat/builder/workflow_builder.py
  • tests/nat/function_policy/test_policy_interface.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • src/nat/cli/register_workflow.py
  • src/nat/data_models/config.py
**/*.{py,js,ts,java,cpp,c,go,rb,php}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Every file must start with the standard SPDX Apache-2.0 header

Files:

  • tests/nat/cli/commands/info/test_list_function_policies.py
  • src/nat/data_models/function_policy.py
  • src/nat/middleware/register.py
  • src/nat/middleware/dynamic_function_middleware.py
  • src/nat/middleware/utils/__init__.py
  • src/nat/function_policy/interface.py
  • src/nat/builder/component_utils.py
  • src/nat/data_models/component.py
  • src/nat/middleware/utils/workflow_inventory.py
  • src/nat/data_models/component_ref.py
  • src/nat/data_models/middleware.py
  • src/nat/cli/type_registry.py
  • tests/nat/builder/test_component_utils.py
  • src/nat/cli/commands/info/info.py
  • tests/nat/builder/test_builder.py
  • src/nat/function_policy/__init__.py
  • src/nat/middleware/__init__.py
  • src/nat/cli/commands/info/list_function_policies.py
  • src/nat/builder/builder.py
  • src/nat/builder/workflow_builder.py
  • tests/nat/function_policy/test_policy_interface.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • src/nat/cli/register_workflow.py
  • src/nat/data_models/config.py
**/*.{py,js,ts,java,cpp,c,go,rb,php,sh}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

All source files must include the SPDX Apache-2.0 header template

Files:

  • tests/nat/cli/commands/info/test_list_function_policies.py
  • src/nat/data_models/function_policy.py
  • src/nat/middleware/register.py
  • src/nat/middleware/dynamic_function_middleware.py
  • src/nat/middleware/utils/__init__.py
  • src/nat/function_policy/interface.py
  • src/nat/builder/component_utils.py
  • src/nat/data_models/component.py
  • src/nat/middleware/utils/workflow_inventory.py
  • src/nat/data_models/component_ref.py
  • src/nat/data_models/middleware.py
  • src/nat/cli/type_registry.py
  • tests/nat/builder/test_component_utils.py
  • src/nat/cli/commands/info/info.py
  • tests/nat/builder/test_builder.py
  • src/nat/function_policy/__init__.py
  • src/nat/middleware/__init__.py
  • src/nat/cli/commands/info/list_function_policies.py
  • src/nat/builder/builder.py
  • src/nat/builder/workflow_builder.py
  • tests/nat/function_policy/test_policy_interface.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • src/nat/cli/register_workflow.py
  • src/nat/data_models/config.py
tests/**/*.py

⚙️ CodeRabbit configuration file

tests/**/*.py: - Ensure that tests are comprehensive, cover edge cases, and validate the functionality of the code. - Test functions should be named using the test_ prefix, using snake_case. - Any frequently repeated code should be extracted into pytest fixtures. - Pytest fixtures should define the name argument when applying the pytest.fixture decorator. The fixture
function being decorated should be named using the fixture_ prefix, using snake_case. Example:
@pytest.fixture(name="my_fixture")
def fixture_my_fixture():
pass

Files:

  • tests/nat/cli/commands/info/test_list_function_policies.py
  • tests/nat/builder/test_component_utils.py
  • tests/nat/builder/test_builder.py
  • tests/nat/function_policy/test_policy_interface.py
  • tests/nat/middleware/test_dynamic_middleware.py
src/nat/**/*

⚙️ CodeRabbit configuration file

This directory contains the core functionality of the toolkit. Changes should prioritize backward compatibility.

Files:

  • src/nat/data_models/function_policy.py
  • src/nat/middleware/register.py
  • src/nat/middleware/dynamic_function_middleware.py
  • src/nat/middleware/utils/__init__.py
  • src/nat/function_policy/interface.py
  • src/nat/builder/component_utils.py
  • src/nat/data_models/component.py
  • src/nat/middleware/utils/workflow_inventory.py
  • src/nat/data_models/component_ref.py
  • src/nat/data_models/middleware.py
  • src/nat/cli/type_registry.py
  • src/nat/cli/commands/info/info.py
  • src/nat/function_policy/__init__.py
  • src/nat/middleware/__init__.py
  • src/nat/cli/commands/info/list_function_policies.py
  • src/nat/builder/builder.py
  • src/nat/builder/workflow_builder.py
  • src/nat/cli/register_workflow.py
  • src/nat/data_models/config.py
🧠 Learnings (4)
📚 Learning: 2025-08-28T14:28:34.779Z
Learnt from: willkill07
Repo: NVIDIA/NeMo-Agent-Toolkit PR: 684
File: src/nat/builder/component_utils.py:110-114
Timestamp: 2025-08-28T14:28:34.779Z
Learning: FunctionGroupBaseConfig and FunctionBaseConfig are sibling classes that both inherit from (TypedBaseModel, BaseModelRegistryTag). FunctionGroupBaseConfig does not inherit from FunctionBaseConfig, so isinstance checks between them won't interfere with each other and the order doesn't matter for classification purposes.

Applied to files:

  • src/nat/data_models/function_policy.py
  • src/nat/builder/component_utils.py
  • src/nat/cli/type_registry.py
  • tests/nat/builder/test_component_utils.py
  • tests/nat/builder/test_builder.py
  • src/nat/builder/builder.py
  • src/nat/builder/workflow_builder.py
  • src/nat/cli/register_workflow.py
  • src/nat/data_models/config.py
📚 Learning: 2025-08-28T14:28:34.779Z
Learnt from: willkill07
Repo: NVIDIA/NeMo-Agent-Toolkit PR: 684
File: src/nat/builder/component_utils.py:110-114
Timestamp: 2025-08-28T14:28:34.779Z
Learning: FunctionGroupBaseConfig and FunctionBaseConfig are separate class hierarchies that both inherit from (TypedBaseModel, BaseModelRegistryTag). FunctionGroupBaseConfig does not inherit from FunctionBaseConfig, so isinstance checks between them won't interfere with each other.

Applied to files:

  • src/nat/data_models/function_policy.py
  • src/nat/builder/component_utils.py
  • src/nat/cli/type_registry.py
  • tests/nat/builder/test_component_utils.py
  • tests/nat/builder/test_builder.py
  • src/nat/builder/builder.py
  • src/nat/builder/workflow_builder.py
  • src/nat/cli/register_workflow.py
  • src/nat/data_models/config.py
📚 Learning: 2025-11-24T18:56:53.109Z
Learnt from: CR
Repo: NVIDIA/NeMo-Agent-Toolkit PR: 0
File: .cursor/rules/general.mdc:0-0
Timestamp: 2025-11-24T18:56:53.109Z
Learning: Applies to **/*.{py,js,ts,java,cpp,c,go,rb,php} : Every file must start with the standard SPDX Apache-2.0 header

Applied to files:

  • src/nat/middleware/utils/__init__.py
📚 Learning: 2025-11-24T18:56:53.109Z
Learnt from: CR
Repo: NVIDIA/NeMo-Agent-Toolkit PR: 0
File: .cursor/rules/general.mdc:0-0
Timestamp: 2025-11-24T18:56:53.109Z
Learning: Applies to **/*.py : Use typing.Annotated for units or extra metadata when useful

Applied to files:

  • src/nat/data_models/config.py
🧬 Code graph analysis (15)
tests/nat/cli/commands/info/test_list_function_policies.py (2)
src/nat/cli/entrypoint.py (1)
  • cli (84-102)
src/nat/builder/context.py (1)
  • output (60-61)
src/nat/middleware/register.py (3)
src/nat/builder/builder.py (1)
  • Builder (74-383)
src/nat/data_models/middleware.py (1)
  • DynamicMiddlewareConfig (96-156)
src/nat/middleware/dynamic_function_middleware.py (1)
  • DynamicFunctionMiddleware (45-998)
src/nat/builder/component_utils.py (1)
src/nat/data_models/function_policy.py (1)
  • FunctionPolicyBaseConfig (24-31)
src/nat/middleware/utils/workflow_inventory.py (2)
src/nat/data_models/component.py (1)
  • ComponentGroup (48-60)
src/nat/data_models/function.py (1)
  • FunctionBaseConfig (26-36)
src/nat/data_models/component_ref.py (2)
src/nat/utils/type_utils.py (1)
  • override (56-57)
src/nat/data_models/component.py (1)
  • ComponentGroup (48-60)
src/nat/cli/type_registry.py (4)
src/nat/data_models/function_policy.py (1)
  • FunctionPolicyBaseConfig (24-31)
src/nat/function_policy/interface.py (1)
  • FunctionPolicyBase (63-120)
src/nat/cli/register_workflow.py (1)
  • register_function_policy (267-303)
src/nat/builder/workflow_builder.py (2)
  • get_function_policy (1129-1144)
  • get_function_policy (1590-1592)
tests/nat/builder/test_component_utils.py (5)
src/nat/data_models/authentication.py (1)
  • AuthProviderBaseConfig (31-37)
src/nat/data_models/function.py (1)
  • FunctionGroupBaseConfig (39-68)
src/nat/data_models/function_policy.py (1)
  • FunctionPolicyBaseConfig (24-31)
src/nat/data_models/middleware.py (1)
  • MiddlewareBaseConfig (36-42)
src/nat/data_models/component.py (1)
  • ComponentGroup (48-60)
src/nat/cli/commands/info/info.py (1)
src/nat/cli/commands/info/list_function_policies.py (2)
  • list_function_policies (25-74)
  • show_function_policy (79-160)
src/nat/function_policy/__init__.py (1)
src/nat/function_policy/interface.py (3)
  • FunctionPolicyBase (63-120)
  • PostInvokeContext (46-60)
  • PreInvokeContext (29-42)
src/nat/builder/builder.py (4)
src/nat/data_models/component_ref.py (1)
  • FunctionPolicyRef (193-201)
src/nat/data_models/function_policy.py (1)
  • FunctionPolicyBaseConfig (24-31)
src/nat/function_policy/interface.py (1)
  • FunctionPolicyBase (63-120)
src/nat/builder/workflow_builder.py (6)
  • add_function_policy (1096-1126)
  • add_function_policy (1584-1587)
  • get_function_policy (1129-1144)
  • get_function_policy (1590-1592)
  • get_function_policy_config (1147-1162)
  • get_function_policy_config (1595-1597)
src/nat/builder/workflow_builder.py (5)
src/nat/data_models/component_ref.py (13)
  • FunctionPolicyRef (193-201)
  • component_group (69-76)
  • component_group (90-91)
  • component_group (101-102)
  • component_group (112-113)
  • component_group (123-124)
  • component_group (134-135)
  • component_group (145-146)
  • component_group (156-157)
  • component_group (167-168)
  • component_group (178-179)
  • component_group (189-190)
  • component_group (200-201)
src/nat/data_models/function_policy.py (1)
  • FunctionPolicyBaseConfig (24-31)
src/nat/function_policy/interface.py (1)
  • FunctionPolicyBase (63-120)
src/nat/builder/builder.py (3)
  • add_function_policy (348-359)
  • get_function_policy (362-371)
  • get_function_policy_config (374-383)
src/nat/cli/type_registry.py (1)
  • get_function_policy (597-613)
tests/nat/function_policy/test_policy_interface.py (3)
src/nat/data_models/function_policy.py (1)
  • FunctionPolicyBaseConfig (24-31)
src/nat/function_policy/interface.py (5)
  • FunctionPolicyBase (63-120)
  • PostInvokeContext (46-60)
  • PreInvokeContext (29-42)
  • on_pre_invoke (89-103)
  • on_post_invoke (106-120)
src/nat/middleware/middleware.py (1)
  • FunctionMiddlewareContext (42-66)
tests/nat/middleware/test_dynamic_middleware.py (4)
src/nat/data_models/component.py (1)
  • ComponentGroup (48-60)
src/nat/data_models/middleware.py (1)
  • DynamicMiddlewareConfig (96-156)
src/nat/function_policy/interface.py (3)
  • FunctionPolicyBase (63-120)
  • PostInvokeContext (46-60)
  • PreInvokeContext (29-42)
src/nat/middleware/middleware.py (1)
  • FunctionMiddlewareContext (42-66)
src/nat/cli/register_workflow.py (2)
src/nat/cli/type_registry.py (4)
  • register_function_policy (580-595)
  • GlobalTypeRegistry (1176-1197)
  • RegisteredFunctionPolicyInfo (209-215)
  • get (1181-1182)
src/nat/data_models/discovery_metadata.py (2)
  • DiscoveryMetadata (53-305)
  • from_config_type (152-189)
src/nat/data_models/config.py (2)
src/nat/data_models/function_policy.py (1)
  • FunctionPolicyBaseConfig (24-31)
src/nat/cli/type_registry.py (3)
  • get (1181-1182)
  • get_registered_function_policies (615-621)
  • compute_annotation (1123-1173)
🪛 markdownlint-cli2 (0.18.1)
docs/source/reference/function-policies.md

164-164: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


180-180: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


463-463: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🪛 Ruff (0.14.7)
src/nat/middleware/register.py

28-28: Unused function argument: builder

(ARG001)

src/nat/middleware/dynamic_function_middleware.py

118-118: Do not catch blind exception: Exception

(BLE001)


155-155: Do not catch blind exception: Exception

(BLE001)


194-194: Do not catch blind exception: Exception

(BLE001)


232-232: Do not catch blind exception: Exception

(BLE001)


272-272: Do not catch blind exception: Exception

(BLE001)


312-312: Do not catch blind exception: Exception

(BLE001)


535-535: Avoid specifying long messages outside the exception class

(TRY003)


539-541: Avoid specifying long messages outside the exception class

(TRY003)


583-584: Avoid specifying long messages outside the exception class

(TRY003)


660-660: Consider moving this statement to an else block

(TRY300)


661-661: Do not catch blind exception: Exception

(BLE001)


801-801: Unused method argument: component_type

(ARG002)


829-831: try-except-continue detected, consider logging the exception

(S112)


829-829: Do not catch blind exception: Exception

(BLE001)


884-884: Consider moving this statement to an else block

(TRY300)


886-886: Do not catch blind exception: Exception

(BLE001)


902-902: Avoid specifying long messages outside the exception class

(TRY003)


910-910: Avoid specifying long messages outside the exception class

(TRY003)


918-918: Avoid specifying long messages outside the exception class

(TRY003)


926-926: Avoid specifying long messages outside the exception class

(TRY003)


934-935: Avoid specifying long messages outside the exception class

(TRY003)


943-943: Avoid specifying long messages outside the exception class

(TRY003)


951-951: Avoid specifying long messages outside the exception class

(TRY003)


961-961: Avoid specifying long messages outside the exception class

(TRY003)


967-967: Avoid specifying long messages outside the exception class

(TRY003)


973-973: Avoid specifying long messages outside the exception class

(TRY003)


979-979: Avoid specifying long messages outside the exception class

(TRY003)


985-985: Avoid specifying long messages outside the exception class

(TRY003)


991-991: Avoid specifying long messages outside the exception class

(TRY003)


997-997: Avoid specifying long messages outside the exception class

(TRY003)

src/nat/function_policy/interface.py

123-128: __all__ is not sorted

Apply an isort-style sorting to __all__

(RUF022)

src/nat/cli/type_registry.py

590-591: Avoid specifying long messages outside the exception class

(TRY003)


612-613: Avoid specifying long messages outside the exception class

(TRY003)

tests/nat/builder/test_builder.py

299-299: Unused function argument: b

(ARG001)

src/nat/function_policy/__init__.py

21-26: __all__ is not sorted

Apply an isort-style sorting to __all__

(RUF022)

src/nat/builder/workflow_builder.py

1111-1111: Avoid specifying long messages outside the exception class

(TRY003)


1123-1123: Consider moving this statement to an else block

(TRY300)


1125-1125: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


1142-1142: Avoid specifying long messages outside the exception class

(TRY003)


1160-1160: Avoid specifying long messages outside the exception class

(TRY003)

tests/nat/middleware/test_dynamic_middleware.py

278-278: Unused function argument: value

(ARG001)


378-378: Unused function argument: value

(ARG001)


442-442: Unused function argument: value

(ARG001)


471-471: Unused function argument: value

(ARG001)


550-550: Unused function argument: value

(ARG001)


680-680: Unused function argument: value

(ARG001)


783-783: Unused function argument: value

(ARG001)


813-813: Unused lambda argument: inp

(ARG005)


816-816: Unused lambda argument: out

(ARG005)


1698-1698: Local variable llm_allowlist is assigned to but never used

Remove assignment to unused variable llm_allowlist

(F841)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CI Pipeline / Check

@dnandakumar-nv
Copy link
Contributor

What is the difference between function policy and function middleware? Seems to me like the do the same thing:

  1. Wrap the I/O of a function with custom logic (optionally with the ability to modify the content)
  2. Chain multiple instances to create cascading policies or middleware

Is there a specific functionality policies provide that middleware cannot? If so, would it be worth extending a function middleware instead so we don't have duplicated functionality?

Copy link

@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: 2

🧹 Nitpick comments (17)
src/nat/function_policy/register.py (1)

15-18: Missing module-level docstring.

Per coding guidelines, all public modules require Google-style docstrings. This file currently lacks a module docstring explaining its purpose as a re-export module.

 # flake8: noqa
+"""Public registration exports for function policies."""

 from nat.function_policy.logging import register as register_logging
src/nat/function_policy/logging/register.py (1)

23-34: Consider adding return type hint for the async generator.

The function signature lacks a return type annotation. For async generators yielding policy instances, adding the type hint improves IDE support and type checking.

+from collections.abc import AsyncGenerator
+
+
 @register_function_policy(config_type=LoggingPolicyConfig)
-async def logging_policy(config: LoggingPolicyConfig, builder: Builder):
+async def logging_policy(config: LoggingPolicyConfig, builder: Builder) -> AsyncGenerator[LoggingPolicy, None]:
     """Build a logging policy from configuration.

Note: The unused builder parameter is intentional per the component pattern interface and is properly documented.

src/nat/function_policy/logging/logging_policy_config.py (1)

22-26: Consider using Literal for stricter log level validation.

The log_level field is typed as str but only specific values are valid per the description. Using Literal would catch invalid configurations early during parsing rather than at runtime in LoggingPolicy.on_pre_invoke.

-from pydantic import Field
+from typing import Literal
+
+from pydantic import Field

 from nat.data_models.function_policy import FunctionPolicyBaseConfig


 class LoggingPolicyConfig(FunctionPolicyBaseConfig, name="logging"):
     """Configuration for logging policy."""

-    log_level: str = Field(default="INFO", description="Logging level (DEBUG, INFO, WARNING, ERROR)")
+    log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR"] = Field(
+        default="INFO", description="Logging level")
     max_value_length: int = Field(default=200, description="Maximum length for logged values (truncated if exceeded)")
src/nat/middleware/cache/register.py (1)

23-34: Consider adding return type hint for the async generator.

Similar to the logging policy registration, adding a return type annotation improves type safety and IDE support.

+from collections.abc import AsyncGenerator
+
+
 @register_middleware(config_type=CacheMiddlewareConfig)
-async def cache_middleware(config: CacheMiddlewareConfig, builder: Builder):
+async def cache_middleware(config: CacheMiddlewareConfig, builder: Builder) -> AsyncGenerator[CacheMiddleware, None]:
     """Build a cache middleware from configuration.

Note: The unused builder parameter follows the established component pattern and is properly documented.

docs/source/reference/function-policies.md (1)

187-195: Optional: Add language identifiers to code blocks.

The markdown linter flags three code blocks without language identifiers. These blocks contain ASCII diagrams/flow representations. Adding text as the language identifier would silence the linting warnings while maintaining readability.

For example:

-```
+```text
 Original Args ((10,))
     ↓

Also applies to: 203-211, 495-512

src/nat/function_policy/interface.py (1)

126-131: Optional: Sort __all__ exports alphabetically.

The __all__ list is not alphabetically sorted. While this doesn't affect functionality, sorting it would align with Python conventions and silence the linting warning.

Apply this diff:

 __all__ = [
     "FunctionPolicyBase",
+    "PolicyConfigT",
     "PreInvokeContext",
     "PostInvokeContext",
-    "PolicyConfigT",
 ]
src/nat/middleware/dynamic/dynamic_function_middleware.py (6)

45-49: Module docstring missing for new file.

Per coding guidelines, public modules require Google-style docstrings. Add a module-level docstring describing the purpose of this middleware.

+"""Dynamic function middleware for runtime component discovery and policy orchestration.
+
+This module provides `DynamicFunctionMiddleware`, which intercepts workflow builder
+methods to discover and wrap component functions with policy-based pre/post invoke hooks.
+"""
+
 class DynamicFunctionMiddleware(FunctionMiddleware):

115-120: Silent failure during component function registration.

Registration failures are logged at DEBUG level and silently skipped. While resilient, this could mask configuration errors. Consider logging at WARNING level when in development/debug mode.

The same pattern repeats across all _discover_and_register_* methods (lines 152-159, 191-198, 229-236, 269-276, 309-316). The blind exception catches are intentional for resilience but could be more informative.

             except Exception as e:
-                logger.debug("Failed to register component function '%s' on LLM '%s': %s", function_name, llm_name, e)
+                logger.warning("Failed to register component function '%s' on LLM '%s': %s", function_name, llm_name, e)

240-243: Docstring formatting: extra blank line.

Minor formatting issue - there's an extra blank line between the docstring summary and Args section.

     async def _discover_and_register_object_store(self, object_store_name: str) -> Any:
         """Intercept object store creation and register allowlisted component functions with middleware.
-

         Args:

280-283: Docstring formatting: extra blank line.

Same formatting issue as above.

     async def _discover_and_register_auth_provider(self, auth_provider_name: str) -> Any:
         """Intercept auth provider creation and register allowlisted component functions with middleware.
-

         Args:

817-817: Unused parameter component_type should be removed or prefixed.

Static analysis correctly flags this as unused. Either use it for logging/filtering or prefix with underscore.

-    def _get_callable_functions(self, instance: Any, component_type: str | None = None) -> set[str]:
+    def _get_callable_functions(self, instance: Any, _component_type: str | None = None) -> set[str]:

Or remove the parameter entirely if not needed for future use.


845-847: Silent exception during introspection could hide issues.

The try-except-continue pattern silently swallows exceptions during function introspection. Consider logging at DEBUG level for diagnosability.

             except Exception:
                 # Skip functions that raise errors during introspection
+                logger.debug("Skipping function '%s' due to introspection error", function_name, exc_info=True)
                 continue
src/nat/middleware/utils/workflow_inventory.py (1)

123-124: Missing __all__ export and module docstring.

Per coding guidelines, public modules should have docstrings and explicit exports.

Add at the top of the file after imports:

"""Workflow inventory models for dynamic component discovery.

This module provides data models for tracking discovered workflow components
and their callable functions, along with default allowlists for secure function wrapping.
"""

__all__ = [
    "COMPONENT_FUNCTION_ALLOWLISTS",
    "DiscoveredComponent",
    "DiscoveredFunction",
    "WorkflowInventory",
]
tests/nat/builder/test_builder.py (1)

298-313: Unused parameter b in registration function.

Static analysis correctly flags the unused b parameter. Per coding guidelines, prefix with underscore.

     @register_function_policy(config_type=TFunctionPolicyConfig)
-    async def register_function_policy_fn(config: TFunctionPolicyConfig, b: Builder):
+    async def register_function_policy_fn(config: TFunctionPolicyConfig, _builder: Builder):
src/nat/middleware/dynamic/dynamic_middleware_config.py (2)

15-15: Module docstring is brief but present.

The docstring at line 15 is minimal. Consider expanding for clarity.

-"""Configuration for dynamic middleware."""
+"""Configuration models for dynamic middleware.
+
+This module provides configuration classes for the dynamic middleware system,
+including component function allowlists and the main middleware configuration.
+"""

136-138: Missing __all__ export for public API.

Per coding guidelines, consider adding explicit exports.

Add after imports:

__all__ = ["AllowedComponentFunctions", "DynamicMiddlewareConfig"]
src/nat/middleware/middleware.py (1)

125-125: **Consider documenting expected kwargs more specifically.

The docstrings describe **kwargs as "Additional function arguments" which is accurate but generic. Middleware implementers might benefit from knowing what contextual arguments they can expect (e.g., policy contexts, session metadata). However, keeping the documentation generic maintains flexibility as the framework evolves.

Also applies to: 161-161

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5509f49 and 146802f.

📒 Files selected for processing (26)
  • docs/source/reference/function-policies.md (1 hunks)
  • src/nat/data_models/middleware.py (1 hunks)
  • src/nat/function_policy/interface.py (1 hunks)
  • src/nat/function_policy/logging/__init__.py (1 hunks)
  • src/nat/function_policy/logging/logging_policy.py (1 hunks)
  • src/nat/function_policy/logging/logging_policy_config.py (1 hunks)
  • src/nat/function_policy/logging/register.py (1 hunks)
  • src/nat/function_policy/register.py (1 hunks)
  • src/nat/middleware/__init__.py (1 hunks)
  • src/nat/middleware/cache/__init__.py (1 hunks)
  • src/nat/middleware/cache/cache_middleware.py (6 hunks)
  • src/nat/middleware/cache/cache_middleware_config.py (1 hunks)
  • src/nat/middleware/cache/register.py (1 hunks)
  • src/nat/middleware/dynamic/__init__.py (1 hunks)
  • src/nat/middleware/dynamic/dynamic_function_middleware.py (1 hunks)
  • src/nat/middleware/dynamic/dynamic_middleware_config.py (1 hunks)
  • src/nat/middleware/dynamic/register.py (1 hunks)
  • src/nat/middleware/function_middleware.py (3 hunks)
  • src/nat/middleware/middleware.py (5 hunks)
  • src/nat/middleware/register.py (1 hunks)
  • src/nat/middleware/utils/workflow_inventory.py (1 hunks)
  • tests/nat/builder/test_builder.py (10 hunks)
  • tests/nat/function_policy/test_policy_interface.py (1 hunks)
  • tests/nat/middleware/test_cache_middleware.py (11 hunks)
  • tests/nat/middleware/test_dynamic_middleware.py (1 hunks)
  • tests/nat/middleware/test_middleware_components.py (3 hunks)
✅ Files skipped from review due to trivial changes (3)
  • src/nat/middleware/dynamic/init.py
  • src/nat/middleware/cache/init.py
  • src/nat/function_policy/logging/init.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/nat/middleware/register.py
  • tests/nat/function_policy/test_policy_interface.py
  • src/nat/data_models/middleware.py
🧰 Additional context used
📓 Path-based instructions (16)
**/*.{md,rst,py}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Use 'NVIDIA NeMo Agent toolkit' on first use, then 'NeMo Agent toolkit' for subsequent references

Files:

  • src/nat/function_policy/logging/logging_policy_config.py
  • src/nat/function_policy/logging/logging_policy.py
  • src/nat/middleware/cache/register.py
  • tests/nat/middleware/test_middleware_components.py
  • src/nat/function_policy/register.py
  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • src/nat/function_policy/interface.py
  • src/nat/middleware/cache/cache_middleware.py
  • tests/nat/middleware/test_cache_middleware.py
  • src/nat/middleware/utils/workflow_inventory.py
  • src/nat/middleware/__init__.py
  • src/nat/middleware/dynamic/dynamic_middleware_config.py
  • src/nat/middleware/dynamic/register.py
  • src/nat/function_policy/logging/register.py
  • src/nat/middleware/middleware.py
  • src/nat/middleware/cache/cache_middleware_config.py
  • src/nat/middleware/function_middleware.py
  • docs/source/reference/function-policies.md
  • tests/nat/builder/test_builder.py
**/*.{py,toml,yaml,yml}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Use abbreviations: 'nat' for API namespace and CLI tool, 'nvidia-nat' for package name, 'NAT' for environment variable prefixes and informal comments

Files:

  • src/nat/function_policy/logging/logging_policy_config.py
  • src/nat/function_policy/logging/logging_policy.py
  • src/nat/middleware/cache/register.py
  • tests/nat/middleware/test_middleware_components.py
  • src/nat/function_policy/register.py
  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • src/nat/function_policy/interface.py
  • src/nat/middleware/cache/cache_middleware.py
  • tests/nat/middleware/test_cache_middleware.py
  • src/nat/middleware/utils/workflow_inventory.py
  • src/nat/middleware/__init__.py
  • src/nat/middleware/dynamic/dynamic_middleware_config.py
  • src/nat/middleware/dynamic/register.py
  • src/nat/function_policy/logging/register.py
  • src/nat/middleware/middleware.py
  • src/nat/middleware/cache/cache_middleware_config.py
  • src/nat/middleware/function_middleware.py
  • tests/nat/builder/test_builder.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

**/*.py: Follow PEP 20 and PEP 8 for Python style guidelines
Run yapf second (PEP 8 base, column_limit = 120) for Python formatting
Use ruff check --fix (via pre-commit) for linting using configuration embedded in pyproject.toml, fix warnings unless explicitly ignored
Use snake_case for functions and variables, PascalCase for classes, UPPER_CASE for constants
All public APIs require Python 3.11+ type hints on parameters and return values
Prefer collections.abc / typing abstractions (Sequence over list) for type hints
Use typing.Annotated for units or extra metadata when useful
Treat pyright warnings (configured in pyproject.toml) as errors during development
Preserve stack traces and prevent duplicate logging when handling exceptions; use bare raise statements and logger.error() when re-raising
When catching and logging exceptions without re-raising, always use logger.exception() to capture the full stack trace information
Provide Google-style docstrings for every public module, class, function and CLI command
The first line of docstrings must be a concise description ending with a period (Vale checks this)
Surround code entities with backticks in docstrings to avoid Vale false-positives
Validate and sanitise all user input, especially in web or CLI interfaces
Prefer httpx with SSL verification enabled by default and follow OWASP Top-10 recommendations
Use async/await for I/O-bound work (HTTP, DB, file reads)
Cache expensive computations with functools.lru_cache or an external cache when appropriate
Leverage NumPy vectorised operations whenever beneficial and feasible

Files:

  • src/nat/function_policy/logging/logging_policy_config.py
  • src/nat/function_policy/logging/logging_policy.py
  • src/nat/middleware/cache/register.py
  • tests/nat/middleware/test_middleware_components.py
  • src/nat/function_policy/register.py
  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • src/nat/function_policy/interface.py
  • src/nat/middleware/cache/cache_middleware.py
  • tests/nat/middleware/test_cache_middleware.py
  • src/nat/middleware/utils/workflow_inventory.py
  • src/nat/middleware/__init__.py
  • src/nat/middleware/dynamic/dynamic_middleware_config.py
  • src/nat/middleware/dynamic/register.py
  • src/nat/function_policy/logging/register.py
  • src/nat/middleware/middleware.py
  • src/nat/middleware/cache/cache_middleware_config.py
  • src/nat/middleware/function_middleware.py
  • tests/nat/builder/test_builder.py
**/*.{py,js,ts,yaml,yml,json,md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Indent with 4 spaces, never tabs, and ensure every file ends with a single newline

Files:

  • src/nat/function_policy/logging/logging_policy_config.py
  • src/nat/function_policy/logging/logging_policy.py
  • src/nat/middleware/cache/register.py
  • tests/nat/middleware/test_middleware_components.py
  • src/nat/function_policy/register.py
  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • src/nat/function_policy/interface.py
  • src/nat/middleware/cache/cache_middleware.py
  • tests/nat/middleware/test_cache_middleware.py
  • src/nat/middleware/utils/workflow_inventory.py
  • src/nat/middleware/__init__.py
  • src/nat/middleware/dynamic/dynamic_middleware_config.py
  • src/nat/middleware/dynamic/register.py
  • src/nat/function_policy/logging/register.py
  • src/nat/middleware/middleware.py
  • src/nat/middleware/cache/cache_middleware_config.py
  • src/nat/middleware/function_middleware.py
  • docs/source/reference/function-policies.md
  • tests/nat/builder/test_builder.py
**/*.{py,env,toml,yaml,yml,json}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Never commit API keys, credentials or personal data; use environment variables or .env files excluded from Git

Files:

  • src/nat/function_policy/logging/logging_policy_config.py
  • src/nat/function_policy/logging/logging_policy.py
  • src/nat/middleware/cache/register.py
  • tests/nat/middleware/test_middleware_components.py
  • src/nat/function_policy/register.py
  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • src/nat/function_policy/interface.py
  • src/nat/middleware/cache/cache_middleware.py
  • tests/nat/middleware/test_cache_middleware.py
  • src/nat/middleware/utils/workflow_inventory.py
  • src/nat/middleware/__init__.py
  • src/nat/middleware/dynamic/dynamic_middleware_config.py
  • src/nat/middleware/dynamic/register.py
  • src/nat/function_policy/logging/register.py
  • src/nat/middleware/middleware.py
  • src/nat/middleware/cache/cache_middleware_config.py
  • src/nat/middleware/function_middleware.py
  • tests/nat/builder/test_builder.py
**/*.{py,js,ts,java,cpp,c,go,rb,php}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Every file must start with the standard SPDX Apache-2.0 header

Files:

  • src/nat/function_policy/logging/logging_policy_config.py
  • src/nat/function_policy/logging/logging_policy.py
  • src/nat/middleware/cache/register.py
  • tests/nat/middleware/test_middleware_components.py
  • src/nat/function_policy/register.py
  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • src/nat/function_policy/interface.py
  • src/nat/middleware/cache/cache_middleware.py
  • tests/nat/middleware/test_cache_middleware.py
  • src/nat/middleware/utils/workflow_inventory.py
  • src/nat/middleware/__init__.py
  • src/nat/middleware/dynamic/dynamic_middleware_config.py
  • src/nat/middleware/dynamic/register.py
  • src/nat/function_policy/logging/register.py
  • src/nat/middleware/middleware.py
  • src/nat/middleware/cache/cache_middleware_config.py
  • src/nat/middleware/function_middleware.py
  • tests/nat/builder/test_builder.py
**/*.{py,md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Version numbers are derived automatically by setuptools-scm; never hard-code them in code or docs

Files:

  • src/nat/function_policy/logging/logging_policy_config.py
  • src/nat/function_policy/logging/logging_policy.py
  • src/nat/middleware/cache/register.py
  • tests/nat/middleware/test_middleware_components.py
  • src/nat/function_policy/register.py
  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • src/nat/function_policy/interface.py
  • src/nat/middleware/cache/cache_middleware.py
  • tests/nat/middleware/test_cache_middleware.py
  • src/nat/middleware/utils/workflow_inventory.py
  • src/nat/middleware/__init__.py
  • src/nat/middleware/dynamic/dynamic_middleware_config.py
  • src/nat/middleware/dynamic/register.py
  • src/nat/function_policy/logging/register.py
  • src/nat/middleware/middleware.py
  • src/nat/middleware/cache/cache_middleware_config.py
  • src/nat/middleware/function_middleware.py
  • docs/source/reference/function-policies.md
  • tests/nat/builder/test_builder.py
**/*.{py,js,ts,java,cpp,c,go,rb,php,sh}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

All source files must include the SPDX Apache-2.0 header template

Files:

  • src/nat/function_policy/logging/logging_policy_config.py
  • src/nat/function_policy/logging/logging_policy.py
  • src/nat/middleware/cache/register.py
  • tests/nat/middleware/test_middleware_components.py
  • src/nat/function_policy/register.py
  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • src/nat/function_policy/interface.py
  • src/nat/middleware/cache/cache_middleware.py
  • tests/nat/middleware/test_cache_middleware.py
  • src/nat/middleware/utils/workflow_inventory.py
  • src/nat/middleware/__init__.py
  • src/nat/middleware/dynamic/dynamic_middleware_config.py
  • src/nat/middleware/dynamic/register.py
  • src/nat/function_policy/logging/register.py
  • src/nat/middleware/middleware.py
  • src/nat/middleware/cache/cache_middleware_config.py
  • src/nat/middleware/function_middleware.py
  • tests/nat/builder/test_builder.py
**/*

⚙️ CodeRabbit configuration file

**/*: # Code Review Instructions

  • Ensure the code follows best practices and coding standards. - For Python code, follow
    PEP 20 and
    PEP 8 for style guidelines.
  • Check for security vulnerabilities and potential issues. - Python methods should use type hints for all parameters and return values (except for return values of None,
    in that situation no return type hint is needed).
    Example:
    def my_function(param1: int, param2: str) -> bool:
        pass
  • For Python exception handling, ensure proper stack trace preservation:
    • When re-raising exceptions: use bare raise statements to maintain the original stack trace,
      and use logger.error() (not logger.exception()) to avoid duplicate stack trace output.
    • When catching and logging exceptions without re-raising: always use logger.exception()
      to capture the full stack trace information.

Documentation Review Instructions - Verify that documentation and comments are clear and comprehensive. - Verify that the documentation doesn't contain any TODOs, FIXMEs or placeholder text like "lorem ipsum". - Verify that the documentation doesn't contain any offensive or outdated terms. - Verify that documentation and comments are free of spelling mistakes, ensure the documentation doesn't contain any

words listed in the ci/vale/styles/config/vocabularies/nat/reject.txt file, words that might appear to be
spelling mistakes but are listed in the ci/vale/styles/config/vocabularies/nat/accept.txt file are OK.

Misc. - All code (except .mdc files that contain Cursor rules) should be licensed under the Apache License 2.0,

and should contain an Apache License 2.0 header comment at the top of each file.

  • Confirm that copyright years are up-to date whenever a file is changed.

Files:

  • src/nat/function_policy/logging/logging_policy_config.py
  • src/nat/function_policy/logging/logging_policy.py
  • src/nat/middleware/cache/register.py
  • tests/nat/middleware/test_middleware_components.py
  • src/nat/function_policy/register.py
  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • src/nat/function_policy/interface.py
  • src/nat/middleware/cache/cache_middleware.py
  • tests/nat/middleware/test_cache_middleware.py
  • src/nat/middleware/utils/workflow_inventory.py
  • src/nat/middleware/__init__.py
  • src/nat/middleware/dynamic/dynamic_middleware_config.py
  • src/nat/middleware/dynamic/register.py
  • src/nat/function_policy/logging/register.py
  • src/nat/middleware/middleware.py
  • src/nat/middleware/cache/cache_middleware_config.py
  • src/nat/middleware/function_middleware.py
  • docs/source/reference/function-policies.md
  • tests/nat/builder/test_builder.py
src/nat/**/*

⚙️ CodeRabbit configuration file

This directory contains the core functionality of the toolkit. Changes should prioritize backward compatibility.

Files:

  • src/nat/function_policy/logging/logging_policy_config.py
  • src/nat/function_policy/logging/logging_policy.py
  • src/nat/middleware/cache/register.py
  • src/nat/function_policy/register.py
  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • src/nat/function_policy/interface.py
  • src/nat/middleware/cache/cache_middleware.py
  • src/nat/middleware/utils/workflow_inventory.py
  • src/nat/middleware/__init__.py
  • src/nat/middleware/dynamic/dynamic_middleware_config.py
  • src/nat/middleware/dynamic/register.py
  • src/nat/function_policy/logging/register.py
  • src/nat/middleware/middleware.py
  • src/nat/middleware/cache/cache_middleware_config.py
  • src/nat/middleware/function_middleware.py
**/test_*.py

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

**/test_*.py: Use pytest with pytest-asyncio for asynchronous code testing
Test functions should be named using the test_ prefix, using snake_case
Extract frequently repeated code into pytest fixtures, which should be named using the fixture_ prefix and define the name argument in the decorator
Mock external services with pytest_httpserver or unittest.mock instead of hitting live endpoints
Mark slow tests with @pytest.mark.slow so they can be skipped in the default test suite
Mark integration tests requiring external services with @pytest.mark.integration so they can be skipped in the default test suite

Files:

  • tests/nat/middleware/test_middleware_components.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • tests/nat/middleware/test_cache_middleware.py
  • tests/nat/builder/test_builder.py
tests/**/*.py

⚙️ CodeRabbit configuration file

tests/**/*.py: - Ensure that tests are comprehensive, cover edge cases, and validate the functionality of the code. - Test functions should be named using the test_ prefix, using snake_case. - Any frequently repeated code should be extracted into pytest fixtures. - Pytest fixtures should define the name argument when applying the pytest.fixture decorator. The fixture
function being decorated should be named using the fixture_ prefix, using snake_case. Example:
@pytest.fixture(name="my_fixture")
def fixture_my_fixture():
pass

Files:

  • tests/nat/middleware/test_middleware_components.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • tests/nat/middleware/test_cache_middleware.py
  • tests/nat/builder/test_builder.py
**/*.{md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Use 'NeMo Agent Toolkit' (capitalize 'T') when the name appears in headings

Files:

  • docs/source/reference/function-policies.md
docs/**/*.{md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Never use deprecated names: Agent Intelligence toolkit, aiqtoolkit, AgentIQ, AIQ/aiq in documentation (unless intentionally referring to deprecated versions)

Files:

  • docs/source/reference/function-policies.md
docs/source/**/*.{md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

docs/source/**/*.{md,rst}: Documentation must be clear and comprehensive, without TODOs, FIXMEs, or placeholder text
Ensure documentation is free of offensive or outdated terms
Ensure documentation is free of spelling mistakes and do not use words listed in ci/vale/styles/config/vocabularies/nat/reject.txt

Files:

  • docs/source/reference/function-policies.md
docs/source/**/*

⚙️ CodeRabbit configuration file

This directory contains the source code for the documentation. All documentation should be written in Markdown format. Any image files should be placed in the docs/source/_static directory.

Files:

  • docs/source/reference/function-policies.md
🧠 Learnings (2)
📚 Learning: 2025-08-28T14:28:34.779Z
Learnt from: willkill07
Repo: NVIDIA/NeMo-Agent-Toolkit PR: 684
File: src/nat/builder/component_utils.py:110-114
Timestamp: 2025-08-28T14:28:34.779Z
Learning: FunctionGroupBaseConfig and FunctionBaseConfig are sibling classes that both inherit from (TypedBaseModel, BaseModelRegistryTag). FunctionGroupBaseConfig does not inherit from FunctionBaseConfig, so isinstance checks between them won't interfere with each other and the order doesn't matter for classification purposes.

Applied to files:

  • tests/nat/builder/test_builder.py
📚 Learning: 2025-08-28T14:28:34.779Z
Learnt from: willkill07
Repo: NVIDIA/NeMo-Agent-Toolkit PR: 684
File: src/nat/builder/component_utils.py:110-114
Timestamp: 2025-08-28T14:28:34.779Z
Learning: FunctionGroupBaseConfig and FunctionBaseConfig are separate class hierarchies that both inherit from (TypedBaseModel, BaseModelRegistryTag). FunctionGroupBaseConfig does not inherit from FunctionBaseConfig, so isinstance checks between them won't interfere with each other.

Applied to files:

  • tests/nat/builder/test_builder.py
🧬 Code graph analysis (14)
src/nat/function_policy/logging/logging_policy_config.py (1)
src/nat/data_models/function_policy.py (1)
  • FunctionPolicyBaseConfig (24-31)
src/nat/middleware/cache/register.py (2)
src/nat/middleware/cache/cache_middleware.py (1)
  • CacheMiddleware (46-235)
src/nat/middleware/cache/cache_middleware_config.py (1)
  • CacheMiddlewareConfig (24-44)
tests/nat/middleware/test_middleware_components.py (3)
src/nat/middleware/cache/cache_middleware_config.py (1)
  • CacheMiddlewareConfig (24-44)
src/nat/middleware/cache/register.py (1)
  • cache_middleware (24-34)
src/nat/middleware/cache/cache_middleware.py (1)
  • CacheMiddleware (46-235)
src/nat/middleware/dynamic/dynamic_function_middleware.py (5)
src/nat/data_models/component.py (1)
  • ComponentGroup (48-60)
src/nat/middleware/dynamic/dynamic_middleware_config.py (1)
  • DynamicMiddlewareConfig (78-138)
src/nat/middleware/function_middleware.py (3)
  • FunctionMiddleware (43-110)
  • function_middleware_invoke (69-88)
  • function_middleware_stream (90-110)
src/nat/middleware/middleware.py (1)
  • FunctionMiddlewareContext (42-66)
src/nat/middleware/utils/workflow_inventory.py (3)
  • DiscoveredComponent (55-74)
  • DiscoveredFunction (77-90)
  • WorkflowInventory (93-124)
tests/nat/middleware/test_dynamic_middleware.py (5)
src/nat/data_models/component.py (1)
  • ComponentGroup (48-60)
src/nat/middleware/dynamic/dynamic_function_middleware.py (10)
  • function_middleware_invoke (666-739)
  • function_middleware_stream (741-813)
  • _get_callable_functions (817-849)
  • _discover_and_register_llm (87-121)
  • _discover_and_register_embedder (123-161)
  • _discover_and_register_memory (202-238)
  • _discover_and_register_auth_provider (280-318)
  • _discover_functions (358-379)
  • _register_function (503-521)
  • _register_component_function (523-565)
src/nat/middleware/dynamic/dynamic_middleware_config.py (1)
  • DynamicMiddlewareConfig (78-138)
src/nat/middleware/utils/workflow_inventory.py (2)
  • DiscoveredComponent (55-74)
  • DiscoveredFunction (77-90)
src/nat/middleware/middleware.py (1)
  • FunctionMiddlewareContext (42-66)
src/nat/middleware/cache/cache_middleware.py (4)
src/nat/middleware/dynamic/dynamic_function_middleware.py (1)
  • function_middleware_invoke (666-739)
src/nat/middleware/function_middleware.py (1)
  • function_middleware_invoke (69-88)
tests/nat/middleware/test_middleware_components.py (2)
  • function_middleware_invoke (47-51)
  • function_middleware_invoke (650-659)
src/nat/middleware/middleware.py (1)
  • FunctionMiddlewareContext (42-66)
tests/nat/middleware/test_cache_middleware.py (2)
src/nat/middleware/cache/cache_middleware.py (2)
  • CacheMiddleware (46-235)
  • function_middleware_invoke (144-200)
tests/nat/middleware/test_middleware_components.py (2)
  • function_middleware_invoke (47-51)
  • function_middleware_invoke (650-659)
src/nat/middleware/utils/workflow_inventory.py (2)
src/nat/data_models/component.py (1)
  • ComponentGroup (48-60)
src/nat/data_models/function.py (1)
  • FunctionBaseConfig (26-36)
src/nat/middleware/dynamic/dynamic_middleware_config.py (2)
src/nat/data_models/component.py (1)
  • ComponentGroup (48-60)
src/nat/data_models/component_ref.py (6)
  • AuthenticationRef (160-168)
  • EmbedderRef (83-91)
  • LLMRef (116-124)
  • MemoryRef (127-135)
  • ObjectStoreRef (138-146)
  • RetrieverRef (149-157)
src/nat/middleware/dynamic/register.py (3)
src/nat/builder/builder.py (1)
  • Builder (74-383)
src/nat/middleware/dynamic/dynamic_function_middleware.py (1)
  • DynamicFunctionMiddleware (45-1014)
src/nat/middleware/dynamic/dynamic_middleware_config.py (1)
  • DynamicMiddlewareConfig (78-138)
src/nat/function_policy/logging/register.py (2)
src/nat/function_policy/logging/logging_policy.py (1)
  • LoggingPolicy (28-74)
src/nat/function_policy/logging/logging_policy_config.py (1)
  • LoggingPolicyConfig (22-26)
src/nat/middleware/middleware.py (3)
src/nat/middleware/function_middleware.py (2)
  • middleware_invoke (52-58)
  • middleware_stream (60-67)
src/nat/runtime/session.py (1)
  • context (96-97)
src/nat/runtime/runner.py (1)
  • context (97-98)
src/nat/middleware/function_middleware.py (2)
src/nat/runtime/session.py (1)
  • context (96-97)
src/nat/runtime/runner.py (1)
  • context (97-98)
tests/nat/builder/test_builder.py (5)
src/nat/cli/register_workflow.py (1)
  • register_function_policy (267-303)
src/nat/cli/type_registry.py (2)
  • register_function_policy (580-595)
  • get_function_policy (597-613)
src/nat/data_models/function_policy.py (1)
  • FunctionPolicyBaseConfig (24-31)
src/nat/function_policy/interface.py (5)
  • FunctionPolicyBase (67-123)
  • PostInvokeContext (48-64)
  • PreInvokeContext (29-44)
  • on_pre_invoke (93-106)
  • on_post_invoke (109-123)
src/nat/builder/workflow_builder.py (4)
  • add_function_policy (1096-1126)
  • add_function_policy (1584-1587)
  • get_function_policy (1129-1144)
  • get_function_policy (1590-1592)
🪛 markdownlint-cli2 (0.18.1)
docs/source/reference/function-policies.md

187-187: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


203-203: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


495-495: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🪛 Ruff (0.14.7)
src/nat/middleware/cache/register.py

24-24: Unused function argument: builder

(ARG001)

src/nat/middleware/dynamic/dynamic_function_middleware.py

118-118: Do not catch blind exception: Exception

(BLE001)


155-155: Do not catch blind exception: Exception

(BLE001)


194-194: Do not catch blind exception: Exception

(BLE001)


232-232: Do not catch blind exception: Exception

(BLE001)


272-272: Do not catch blind exception: Exception

(BLE001)


312-312: Do not catch blind exception: Exception

(BLE001)


535-535: Avoid specifying long messages outside the exception class

(TRY003)


539-541: Avoid specifying long messages outside the exception class

(TRY003)


583-584: Avoid specifying long messages outside the exception class

(TRY003)


660-660: Consider moving this statement to an else block

(TRY300)


661-661: Do not catch blind exception: Exception

(BLE001)


700-701: Abstract raise to an inner function

(TRY301)


700-701: Avoid specifying long messages outside the exception class

(TRY003)


775-776: Abstract raise to an inner function

(TRY301)


775-776: Avoid specifying long messages outside the exception class

(TRY003)


817-817: Unused method argument: component_type

(ARG002)


845-847: try-except-continue detected, consider logging the exception

(S112)


845-845: Do not catch blind exception: Exception

(BLE001)


900-900: Consider moving this statement to an else block

(TRY300)


902-902: Do not catch blind exception: Exception

(BLE001)


918-918: Avoid specifying long messages outside the exception class

(TRY003)


926-926: Avoid specifying long messages outside the exception class

(TRY003)


934-934: Avoid specifying long messages outside the exception class

(TRY003)


942-942: Avoid specifying long messages outside the exception class

(TRY003)


950-951: Avoid specifying long messages outside the exception class

(TRY003)


959-959: Avoid specifying long messages outside the exception class

(TRY003)


967-967: Avoid specifying long messages outside the exception class

(TRY003)


977-977: Avoid specifying long messages outside the exception class

(TRY003)


983-983: Avoid specifying long messages outside the exception class

(TRY003)


989-989: Avoid specifying long messages outside the exception class

(TRY003)


995-995: Avoid specifying long messages outside the exception class

(TRY003)


1001-1001: Avoid specifying long messages outside the exception class

(TRY003)


1007-1007: Avoid specifying long messages outside the exception class

(TRY003)


1013-1013: Avoid specifying long messages outside the exception class

(TRY003)

tests/nat/middleware/test_dynamic_middleware.py

76-76: Unused method argument: messages

(ARG002)


76-76: Unused method argument: kwargs

(ARG002)


79-79: Unused method argument: messages

(ARG002)


79-79: Unused method argument: kwargs

(ARG002)


82-82: Unused method argument: messages

(ARG002)


82-82: Unused method argument: kwargs

(ARG002)


85-85: Unused method argument: messages

(ARG002)


85-85: Unused method argument: kwargs

(ARG002)


97-97: Unused method argument: text

(ARG002)


100-100: Unused method argument: text

(ARG002)


112-112: Unused method argument: query

(ARG002)


112-112: Unused method argument: kwargs

(ARG002)


124-124: Unused method argument: query

(ARG002)


124-124: Unused method argument: top_k

(ARG002)


124-124: Unused method argument: kwargs

(ARG002)


169-169: Unused method argument: user_id

(ARG002)


169-169: Unused method argument: kwargs

(ARG002)


224-224: Consider (modified_first, *context.function_args[1:]) instead of concatenation

Replace with (modified_first, *context.function_args[1:])

(RUF005)


251-251: Unused function argument: kwargs

(ARG001)


278-278: Unused function argument: kwargs

(ARG001)


305-305: Unused function argument: args

(ARG001)


305-305: Unused function argument: kwargs

(ARG001)


326-326: Unused function argument: args

(ARG001)


326-326: Unused function argument: kwargs

(ARG001)


341-341: Unused function argument: args

(ARG001)


341-341: Unused function argument: kwargs

(ARG001)


370-370: Unused function argument: kwargs

(ARG001)


390-390: Unused function argument: args

(ARG001)


390-390: Unused function argument: kwargs

(ARG001)


409-409: Unused function argument: args

(ARG001)


409-409: Unused function argument: kwargs

(ARG001)


434-434: Unused function argument: args

(ARG001)


434-434: Unused function argument: kwargs

(ARG001)

src/nat/function_policy/interface.py

126-131: __all__ is not sorted

Apply an isort-style sorting to __all__

(RUF022)

tests/nat/middleware/test_cache_middleware.py

81-81: Unused function argument: kwargs

(ARG001)


116-116: Unused function argument: args

(ARG001)


116-116: Unused function argument: kwargs

(ARG001)


151-151: Unused function argument: args

(ARG001)


151-151: Unused function argument: kwargs

(ARG001)


190-190: Unused function argument: args

(ARG001)


190-190: Unused function argument: kwargs

(ARG001)


221-221: Unused function argument: args

(ARG001)


221-221: Unused function argument: kwargs

(ARG001)


256-256: Unused function argument: args

(ARG001)


256-256: Unused function argument: kwargs

(ARG001)


302-302: Unused function argument: args

(ARG001)


302-302: Unused function argument: kwargs

(ARG001)

src/nat/function_policy/logging/register.py

24-24: Unused function argument: builder

(ARG001)

src/nat/middleware/function_middleware.py

72-72: Unused method argument: context

(ARG002)


93-93: Unused method argument: context

(ARG002)

tests/nat/builder/test_builder.py

299-299: Unused function argument: b

(ARG001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CI Pipeline / Check
🔇 Additional comments (38)
src/nat/middleware/__init__.py (1)

25-33: LGTM!

The __all__ exports are properly alphabetized, and the removal of CacheMiddleware from this module aligns with its migration to the dedicated nat.middleware.cache subpackage.

src/nat/middleware/cache/cache_middleware_config.py (1)

24-44: Well-structured configuration class.

The configuration follows Pydantic best practices with proper type hints (Literal for the mode enum, float with ge/le constraints for the threshold). The docstring properly documents the behavior of each mode.

tests/nat/middleware/test_middleware_components.py (1)

271-302: LGTM! Import paths correctly updated.

The import paths have been properly updated to reflect the new module structure for CacheMiddlewareConfig and CacheMiddleware, aligning with the cache middleware reorganization.

src/nat/middleware/dynamic/register.py (1)

23-34: LGTM! Registration function follows the standard pattern.

The dynamic_middleware registration function correctly follows the component registration pattern with proper decorator usage, async generator pattern, and comprehensive docstring.

src/nat/middleware/function_middleware.py (2)

52-110: LGTM! Signature updates enable flexible argument passing.

The updated signatures to accept *args and **kwargs correctly enable the middleware to support multi-argument function targets and policy-driven invocations. The changes maintain backward compatibility while providing the flexibility needed for the Function Policies framework.

Note: The static analysis warnings about unused context parameters are false positives—context is required by the middleware interface and is passed through to the wrapper functions.


139-171: LGTM! Chain building correctly forwards expanded arguments.

The build_single and build_stream methods have been properly updated to forward *args and **kwargs through the middleware chain, ensuring compatibility with the new flexible signatures.

src/nat/function_policy/logging/logging_policy.py (1)

28-74: LGTM! Clean logging policy implementation.

The LoggingPolicy class is well-implemented with proper inheritance, clear separation of concerns, and appropriate use of the policy interface. The policy correctly returns None to indicate no modifications, and the _truncate helper ensures log output is kept manageable.

docs/source/reference/function-policies.md (1)

18-609: Excellent comprehensive documentation for Function Policies.

This documentation provides thorough coverage of the Function Policies framework, including:

  • Clear conceptual overview with pipeline visualization
  • Step-by-step implementation guide
  • Comprehensive examples for common use cases
  • Testing guidance and best practices
  • CLI command reference
  • Troubleshooting section

The structure and content are well-organized and will help users understand and effectively use the new Function Policies feature.

tests/nat/middleware/test_cache_middleware.py (2)

26-26: LGTM! Import path correctly updated.

The import path has been properly updated to reflect the new module structure for CacheMiddleware.


81-308: LGTM! Test signatures correctly adapted to new middleware interface.

The test functions have been properly updated to use keyword arguments (call_next and context) instead of positional arguments, aligning with the new middleware signatures. The mock functions correctly accept *args and **kwargs to support the flexible argument passing pattern.

Note: Static analysis warnings about unused args and kwargs in mock functions are expected and can be safely ignored—these parameters are required for signature compatibility.

tests/nat/middleware/test_dynamic_middleware.py (4)

41-237: LGTM! Well-designed test fixtures.

The fixtures provide comprehensive mocking for builders and various component types (LLM, embedder, retriever, memory, object store, auth provider). The tracking_policy fixture is particularly well-designed, offering flexible configuration for testing different policy behaviors.


243-444: LGTM! Comprehensive policy execution tests.

These tests thoroughly validate the middleware's policy orchestration capabilities:

  • Invoke/stream delegation with no policies
  • Pre/post-invoke policy execution and modification
  • Policy execution order (pre → function → post)
  • Error handling (skipping failing policies)
  • Disabled policy behavior

The test coverage ensures the policy framework works correctly across various scenarios.


449-559: LGTM! Thorough component discovery tests.

These tests validate discovery and registration for all supported component types (LLMs, embedders, retrievers, memory, object stores, auth providers). The tests also correctly verify that components are skipped when not configured and that duplicates are handled properly.


564-630: LGTM! Registration mechanics properly validated.

These tests ensure workflow function discovery and registration work correctly, including:

  • Discovery from builder
  • Conditional discovery based on configuration
  • Duplicate prevention for both functions and component functions

The tests provide good coverage of the registration lifecycle.

src/nat/function_policy/interface.py (2)

28-64: LGTM! Well-designed context objects.

The context objects are thoughtfully designed:

  • PreInvokeContext is mutable to allow middleware to update function_args as policies execute
  • PostInvokeContext is frozen to provide an immutable audit trail of the complete execution
  • Both include comprehensive attributes for function metadata, original args, current args, and kwargs

This design supports the policy pipeline model effectively.


67-123: LGTM! Clear abstract base class contract.

The FunctionPolicyBase abstract base class provides a clear contract for policy implementations:

  • Generic typing with PolicyConfigT for type-safe configuration
  • Abstract methods for on_pre_invoke and on_post_invoke with comprehensive docstrings
  • Clear guidance on return value semantics (None preserves current value, tuple/value transforms)

This interface will guide policy authors effectively.

src/nat/middleware/dynamic/dynamic_function_middleware.py (2)

696-710: Pre-invoke policy error handling logs but continues - verify this is intentional.

When a pre-invoke policy fails, the error is logged and the policy is skipped. This provides resilience but may hide critical policy failures. The same pattern applies to post-invoke (lines 731-737) and streaming (lines 778-784, 806-812).

The error-resilient approach is documented in the PR objectives ("failed policies logged and skipped"). The exc_info=True ensures full stack traces are captured.


905-914: LGTM - Builder method patching is well-structured.

The patching logic properly stores original methods before replacing them, enabling proper delegation. Error handling for missing methods is appropriate.

src/nat/middleware/cache/cache_middleware.py (3)

144-148: Signature updated to match new FunctionMiddleware interface.

The change from a single value parameter to *args, **kwargs aligns with the broader middleware interface changes shown in the relevant code snippets.


170-172: Cache key extraction assumes at least one positional argument.

When args is empty, args[0] will raise IndexError. The current code handles this by setting value = None, but _serialize_input(None) may produce unexpected cache behavior.

Verify that functions wrapped by this cache middleware always receive at least one positional argument, or consider adding explicit handling:

         # Use first arg as cache key (primary input)
-        value = args[0] if args else None
+        if not args:
+            logger.debug("No positional arguments for function %s, bypassing cache", context.name)
+            return await call_next(*args, **kwargs)
+        value = args[0]

202-206: Streaming method signature correctly updated.

The streaming method signature mirrors the invoke method changes, maintaining consistency.

src/nat/middleware/utils/workflow_inventory.py (4)

1-14: License header and imports are correct.

The SPDX Apache-2.0 header is present and properly formatted.


28-52: Component function allowlists are well-defined.

The allowlists follow a secure-by-default approach, only exposing commonly-used component methods. This aligns with the PR objectives mentioning "allowlists for secure-by-default wrapping."


55-74: DiscoveredComponent model is well-structured.

The model correctly uses arbitrary_types_allowed=True to store component instances and includes proper field descriptions.


93-124: WorkflowInventory provides a clean container for discovered components.

The inventory structure mirrors the component groups and provides a clear API for tracking discovered items.

tests/nat/builder/test_builder.py (6)

118-119: Test configuration class follows existing patterns.

The TFunctionPolicyConfig mirrors other test config classes in the file (e.g., TLLMProviderConfig).


303-313: Test policy implementation is correctly minimal.

The TestPolicy class properly implements the abstract methods with passthrough behavior, which is appropriate for testing the registration and lifecycle.


920-932: Function policy tests follow established patterns.

test_add_function_policy mirrors the structure of test_add_llm, test_add_embedder, etc., testing:

  1. Successful addition
  2. Error during creation (raise_error=True)
  3. Duplicate name rejection

934-948: test_get_function_policy validates retrieval and type.

Good coverage of retrieval, config consistency, and isinstance check.


951-962: test_get_function_policy_config follows existing patterns.

Consistent with other test_get_*_config tests.


976-1011: test_built_config correctly extended for function policies.

The test properly adds function policies and verifies they appear in the workflow config.

src/nat/middleware/dynamic/dynamic_middleware_config.py (4)

34-76: AllowedComponentFunctions merge logic is clean and correct.

The model_validator properly merges user-provided values with defaults, using set union (|) for combining allowlists.


87-101: Using default_factory=list is correct for mutable defaults.

This avoids the mutable default argument pitfall in Pydantic.


105-124: Boolean flags with | None type and default=False.

The bool | None type allows for explicit None values in config, but the default is False. This is intentional for YAML configs where omission means use default.


128-132: Policy configuration fields are well-defined.

Using list[FunctionPolicyRef] ensures type safety when referencing policies.

src/nat/middleware/middleware.py (3)

35-38: Type signature relaxation enables flexible argument passing.

The change from Callable[[Any], ...] to Callable[..., ...] intentionally reduces type specificity to support passing multiple arguments (value + kwargs) through the middleware chain. This is necessary for the Function Policies framework to thread contextual arguments but means the type checker can no longer validate call arity or argument types at these boundaries.


150-184: Streaming path correctly mirrors the non-streaming changes.

The middleware_stream updates maintain symmetry with middleware_invoke, ensuring **kwargs are propagated through both execution paths. The example code demonstrates the correct pattern for transforming stream chunks while preserving context.


114-148: Verify all Middleware subclasses accept **kwargs.

The addition of **kwargs to the signature enables threading contextual arguments (e.g., function policy contexts) through the middleware chain. However, any existing Middleware subclass that overrides middleware_invoke without accepting **kwargs will raise a TypeError at runtime if the caller passes extra arguments. This represents a potential breaking change for the core toolkit.

Ensure that:

  • All existing Middleware subclasses in the codebase accept **kwargs in their middleware_invoke override
  • If subclasses exist without **kwargs, either update them or implement a compatibility layer
  • Document this requirement for users extending the middleware system

Copy link

@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: 0

♻️ Duplicate comments (2)
src/nat/middleware/dynamic/dynamic_function_middleware.py (2)

608-616: Double registration tracking in _configure_component_function_middleware.

The registration key is added to _registered_callables at line 614, but the caller _register_component_function also adds it at line 564. This creates redundant tracking.

         if inspect.isasyncgenfunction(original_function):
             wrapped_function = chain.build_stream(original_function)
         else:
             wrapped_function = chain.build_single(original_function)

-        # Track registration
-        self._registered_callables.add(registration_key)
-
         return wrapped_function

62-67: Potential KeyError if policy reference is missing from builder.

Accessing builder._function_policies[str(ref)] will raise KeyError if the policy isn't registered. Consider adding validation or a more descriptive error message as previously suggested.

🧹 Nitpick comments (4)
src/nat/middleware/dynamic/dynamic_function_middleware.py (4)

116-120: Consider using logger.exception for better debugging of registration failures.

The pattern of catching all exceptions and logging at debug level is repeated throughout the discovery methods. While the resilient approach is appropriate, using logger.exception instead of logger.debug would capture the full stack trace when needed.

             try:
                 self._register_component_function(discovered_component, function_name)
             except Exception as e:
-                logger.debug("Failed to register component function '%s' on LLM '%s': %s", function_name, llm_name, e)
+                logger.debug("Failed to register component function '%s' on LLM '%s': %s",
+                             function_name, llm_name, e, exc_info=True)

This applies similarly to lines 155-159, 194-198, 232-236, 272-276, and 312-316.


819-851: Unused parameter component_type and silent exception swallowing.

Two issues flagged by static analysis:

  1. The component_type parameter (line 819) is documented as "for logging/metadata, not filtering" but is never used. Either use it in the debug logging or remove it.

  2. Lines 847-849 silently swallow exceptions during introspection without logging. Consider adding minimal logging for debugging purposes.

     def _get_callable_functions(self, instance: Any, component_type: str | None = None) -> set[str]:
         """Get all callable functions from component instance that can be safely wrapped.

         This discovers ALL potentially wrappable component functions without allowlist filtering.
         Safety checks ensure only valid, callable, bound functions are included.

         Args:
             instance: The component instance to introspect
-            component_type: Type of component (for logging/metadata, not filtering)
+            component_type: Type of component (unused, reserved for future use)

         Returns:
             Set of all valid component function names that could be wrapped
         """
         functions = set()

         for function_name in dir(instance):
             # Skip private/dunder functions
             if function_name.startswith('_'):
                 continue

             try:
                 # Must pass basic validity checks (no errors)
                 if not self._is_valid_wrappable_function(instance, function_name):
                     continue

                 # Passed all safety checks - this component function CAN be wrapped
                 functions.add(function_name)

-            except Exception:
-                # Skip functions that raise errors during introspection
+            except Exception as e:
+                # Skip functions that raise errors during introspection
+                logger.debug("Skipping function '%s' due to introspection error: %s", function_name, e)
                 continue

         return functions

45-49: Consider expanding the class docstring.

Per coding guidelines, public classes require comprehensive Google-style docstrings. The current docstring briefly describes functionality but could benefit from additional details about usage and attributes.

 class DynamicFunctionMiddleware(FunctionMiddleware):
-    """Middleware extends FunctionMiddleware to provide dynamic discovery and
-    interception of all workflow components, including functions and components, without requiring explicit
-    per-component configuration.
+    """Dynamic middleware for automatic component discovery and function interception.
+
+    This middleware extends FunctionMiddleware to provide dynamic discovery and
+    interception of workflow components (LLMs, embedders, retrievers, memory,
+    object stores, auth providers) and functions without requiring explicit
+    per-component configuration. It patches builder methods to intercept
+    component creation and automatically applies function policies.
+
+    Attributes:
+        _config: The dynamic middleware configuration.
+        _builder: Reference to the workflow builder.
+        _pre_invoke_policies: List of policies to run before function execution.
+        _post_invoke_policies: List of policies to run after function execution.
+        _workflow_inventory: Inventory of discovered components and functions.
     """

163-172: Missing return type hint.

The method signature is missing the return type annotation. Per coding guidelines, all public APIs require type hints.

-    async def _discover_and_register_retriever(self, retriever_name: str, wrapper_type: Any = None):
+    async def _discover_and_register_retriever(self, retriever_name: str, wrapper_type: Any = None) -> Any:
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 146802f and c40781a.

📒 Files selected for processing (3)
  • docs/source/reference/cli.md (2 hunks)
  • docs/source/reference/middleware.md (6 hunks)
  • src/nat/middleware/dynamic/dynamic_function_middleware.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/source/reference/cli.md
🧰 Additional context used
📓 Path-based instructions (14)
**/*.{md,rst,py}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Use 'NVIDIA NeMo Agent toolkit' on first use, then 'NeMo Agent toolkit' for subsequent references

Files:

  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • docs/source/reference/middleware.md
**/*.{py,toml,yaml,yml}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Use abbreviations: 'nat' for API namespace and CLI tool, 'nvidia-nat' for package name, 'NAT' for environment variable prefixes and informal comments

Files:

  • src/nat/middleware/dynamic/dynamic_function_middleware.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

**/*.py: Follow PEP 20 and PEP 8 for Python style guidelines
Run yapf second (PEP 8 base, column_limit = 120) for Python formatting
Use ruff check --fix (via pre-commit) for linting using configuration embedded in pyproject.toml, fix warnings unless explicitly ignored
Use snake_case for functions and variables, PascalCase for classes, UPPER_CASE for constants
All public APIs require Python 3.11+ type hints on parameters and return values
Prefer collections.abc / typing abstractions (Sequence over list) for type hints
Use typing.Annotated for units or extra metadata when useful
Treat pyright warnings (configured in pyproject.toml) as errors during development
Preserve stack traces and prevent duplicate logging when handling exceptions; use bare raise statements and logger.error() when re-raising
When catching and logging exceptions without re-raising, always use logger.exception() to capture the full stack trace information
Provide Google-style docstrings for every public module, class, function and CLI command
The first line of docstrings must be a concise description ending with a period (Vale checks this)
Surround code entities with backticks in docstrings to avoid Vale false-positives
Validate and sanitise all user input, especially in web or CLI interfaces
Prefer httpx with SSL verification enabled by default and follow OWASP Top-10 recommendations
Use async/await for I/O-bound work (HTTP, DB, file reads)
Cache expensive computations with functools.lru_cache or an external cache when appropriate
Leverage NumPy vectorised operations whenever beneficial and feasible

Files:

  • src/nat/middleware/dynamic/dynamic_function_middleware.py
**/*.{py,js,ts,yaml,yml,json,md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Indent with 4 spaces, never tabs, and ensure every file ends with a single newline

Files:

  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • docs/source/reference/middleware.md
**/*.{py,env,toml,yaml,yml,json}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Never commit API keys, credentials or personal data; use environment variables or .env files excluded from Git

Files:

  • src/nat/middleware/dynamic/dynamic_function_middleware.py
**/*.{py,js,ts,java,cpp,c,go,rb,php}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Every file must start with the standard SPDX Apache-2.0 header

Files:

  • src/nat/middleware/dynamic/dynamic_function_middleware.py
**/*.{py,md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Version numbers are derived automatically by setuptools-scm; never hard-code them in code or docs

Files:

  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • docs/source/reference/middleware.md
**/*.{py,js,ts,java,cpp,c,go,rb,php,sh}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

All source files must include the SPDX Apache-2.0 header template

Files:

  • src/nat/middleware/dynamic/dynamic_function_middleware.py
**/*

⚙️ CodeRabbit configuration file

**/*: # Code Review Instructions

  • Ensure the code follows best practices and coding standards. - For Python code, follow
    PEP 20 and
    PEP 8 for style guidelines.
  • Check for security vulnerabilities and potential issues. - Python methods should use type hints for all parameters and return values (except for return values of None,
    in that situation no return type hint is needed).
    Example:
    def my_function(param1: int, param2: str) -> bool:
        pass
  • For Python exception handling, ensure proper stack trace preservation:
    • When re-raising exceptions: use bare raise statements to maintain the original stack trace,
      and use logger.error() (not logger.exception()) to avoid duplicate stack trace output.
    • When catching and logging exceptions without re-raising: always use logger.exception()
      to capture the full stack trace information.

Documentation Review Instructions - Verify that documentation and comments are clear and comprehensive. - Verify that the documentation doesn't contain any TODOs, FIXMEs or placeholder text like "lorem ipsum". - Verify that the documentation doesn't contain any offensive or outdated terms. - Verify that documentation and comments are free of spelling mistakes, ensure the documentation doesn't contain any

words listed in the ci/vale/styles/config/vocabularies/nat/reject.txt file, words that might appear to be
spelling mistakes but are listed in the ci/vale/styles/config/vocabularies/nat/accept.txt file are OK.

Misc. - All code (except .mdc files that contain Cursor rules) should be licensed under the Apache License 2.0,

and should contain an Apache License 2.0 header comment at the top of each file.

  • Confirm that copyright years are up-to date whenever a file is changed.

Files:

  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • docs/source/reference/middleware.md
src/nat/**/*

⚙️ CodeRabbit configuration file

This directory contains the core functionality of the toolkit. Changes should prioritize backward compatibility.

Files:

  • src/nat/middleware/dynamic/dynamic_function_middleware.py
**/*.{md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Use 'NeMo Agent Toolkit' (capitalize 'T') when the name appears in headings

Files:

  • docs/source/reference/middleware.md
docs/**/*.{md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Never use deprecated names: Agent Intelligence toolkit, aiqtoolkit, AgentIQ, AIQ/aiq in documentation (unless intentionally referring to deprecated versions)

Files:

  • docs/source/reference/middleware.md
docs/source/**/*.{md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

docs/source/**/*.{md,rst}: Documentation must be clear and comprehensive, without TODOs, FIXMEs, or placeholder text
Ensure documentation is free of offensive or outdated terms
Ensure documentation is free of spelling mistakes and do not use words listed in ci/vale/styles/config/vocabularies/nat/reject.txt

Files:

  • docs/source/reference/middleware.md
docs/source/**/*

⚙️ CodeRabbit configuration file

This directory contains the source code for the documentation. All documentation should be written in Markdown format. Any image files should be placed in the docs/source/_static directory.

Files:

  • docs/source/reference/middleware.md
🧬 Code graph analysis (1)
src/nat/middleware/dynamic/dynamic_function_middleware.py (4)
src/nat/data_models/component.py (1)
  • ComponentGroup (48-60)
src/nat/middleware/dynamic/dynamic_middleware_config.py (1)
  • DynamicMiddlewareConfig (78-138)
src/nat/middleware/middleware.py (1)
  • FunctionMiddlewareContext (42-66)
src/nat/middleware/utils/workflow_inventory.py (2)
  • DiscoveredComponent (55-74)
  • DiscoveredFunction (77-90)
🪛 Ruff (0.14.7)
src/nat/middleware/dynamic/dynamic_function_middleware.py

118-118: Do not catch blind exception: Exception

(BLE001)


155-155: Do not catch blind exception: Exception

(BLE001)


194-194: Do not catch blind exception: Exception

(BLE001)


232-232: Do not catch blind exception: Exception

(BLE001)


272-272: Do not catch blind exception: Exception

(BLE001)


312-312: Do not catch blind exception: Exception

(BLE001)


535-535: Avoid specifying long messages outside the exception class

(TRY003)


539-541: Avoid specifying long messages outside the exception class

(TRY003)


583-584: Avoid specifying long messages outside the exception class

(TRY003)


660-660: Consider moving this statement to an else block

(TRY300)


661-661: Do not catch blind exception: Exception

(BLE001)


700-701: Abstract raise to an inner function

(TRY301)


700-701: Avoid specifying long messages outside the exception class

(TRY003)


775-776: Abstract raise to an inner function

(TRY301)


775-776: Avoid specifying long messages outside the exception class

(TRY003)


819-819: Unused method argument: component_type

(ARG002)


847-849: try-except-continue detected, consider logging the exception

(S112)


847-847: Do not catch blind exception: Exception

(BLE001)


902-902: Consider moving this statement to an else block

(TRY300)


904-904: Do not catch blind exception: Exception

(BLE001)


920-920: Avoid specifying long messages outside the exception class

(TRY003)


928-928: Avoid specifying long messages outside the exception class

(TRY003)


936-936: Avoid specifying long messages outside the exception class

(TRY003)


944-944: Avoid specifying long messages outside the exception class

(TRY003)


952-953: Avoid specifying long messages outside the exception class

(TRY003)


961-961: Avoid specifying long messages outside the exception class

(TRY003)


969-969: Avoid specifying long messages outside the exception class

(TRY003)


979-979: Avoid specifying long messages outside the exception class

(TRY003)


985-985: Avoid specifying long messages outside the exception class

(TRY003)


991-991: Avoid specifying long messages outside the exception class

(TRY003)


997-997: Avoid specifying long messages outside the exception class

(TRY003)


1003-1003: Avoid specifying long messages outside the exception class

(TRY003)


1009-1009: Avoid specifying long messages outside the exception class

(TRY003)


1015-1015: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (7)
src/nat/middleware/dynamic/dynamic_function_middleware.py (3)

1-14: LGTM: License header is correct.

The Apache 2.0 SPDX header is properly formatted with correct copyright year (2025).


698-710: Validation errors are silently logged and skipped instead of propagating.

When a policy returns an incorrect number of args (lines 699-702), a ValueError is raised but immediately caught by the outer exception handler and logged. This means malformed policy responses are silently ignored rather than failing fast.

If validation errors should propagate, consider restructuring:

             try:
                 modified_args = await policy.on_pre_invoke(pre_context)

                 if modified_args is not None:
                     if len(modified_args) != len(pre_context.function_args):
-                        raise ValueError(f"Policy '{policy.name}' returned {len(modified_args)} args, "
-                                         f"expected {len(pre_context.function_args)}")
+                        logger.error("Policy '%s' returned %d args, expected %d - skipping policy",
+                                     policy.name, len(modified_args), len(pre_context.function_args))
+                        continue
                     pre_context.function_args = modified_args
             except Exception as e:

Alternatively, if validation errors should fail the call, move the validation outside the try block.


907-973: LGTM: Patch methods are well-structured with proper guard checks.

The patch methods correctly:

  • Verify the builder has the expected methods before patching
  • Store references to original methods
  • Replace with interception wrappers

The long exception messages (flagged by TRY003) are acceptable here as they provide helpful debugging context for misconfiguration errors.

docs/source/reference/middleware.md (4)

129-172: LGTM: Middleware examples correctly demonstrate the variadic signature pattern.

The logging middleware example properly shows:

  • *args: Any and **kwargs: Any in method signatures
  • Forwarding via call_next(*args, **kwargs)
  • Accessing args for logging purposes

This aligns with the new function policy framework's design.


345-359: LGTM: Validation middleware example correctly demonstrates args handling.

The example properly shows:

  • Extracting the first arg for validation: value = args[0] if args else None
  • Forwarding with modified first arg: return await call_next(validated, *args[1:], **kwargs)

This is a good pattern for middleware that needs to transform specific positional arguments.


529-538: LGTM: Test example correctly demonstrates the new invocation pattern.

The mock function and test invocation properly show:

  • async def mock_next(*args, **kwargs) pattern
  • Accessing args[0] within the mock
  • Calling function_middleware_invoke(5, call_next=mock_next, context=context)

673-674: Verify the updated cache middleware import paths.

The API references have been updated to:

  • nat.middleware.cache.cache_middleware_config.CacheMiddlewareConfig
  • nat.middleware.cache.cache_middleware.CacheMiddleware

Ensure these paths match the actual module structure after the cache middleware refactor.

@ericevans-nv
Copy link
Contributor Author

Function policies are a higher-level abstraction that runs within DynamicFunctionMiddleware, which handles auto-discovery, allowlist security, and policy orchestration for both sync and streaming functions. Policies provide a simple on_pre_invoke/on_post_invoke interface for inspecting or transforming inputs and outputs, users just receive a context object and return a value. Unlike middleware, policies don't require knowing which functions to intercept at compile time; the dynamic middleware discovers and wraps components at runtime, then applies policies to them. This lets users build and share reusable policy logic without writing custom middleware or manually registering functions. @dnandakumar-nv

…ration of discovered functions

Signed-off-by: Eric Evans <[email protected]>
Copy link

@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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/source/reference/middleware.md (1)

20-30: Align product naming with branding guidelines.

In Line 22, the first mention should follow the guideline: use "NVIDIA NeMo Agent toolkit" on first use, and "NeMo Agent toolkit" for subsequent mentions. Also, reserve "NeMo Agent Toolkit" (capital T) for headings only. Please adjust the body text accordingly.

🧹 Nitpick comments (2)
src/nat/middleware/dynamic/dynamic_function_middleware.py (1)

690-713: Helper introspection utilities are defensive; minor cleanups possible.

  • _get_callable_functions’s component_type parameter is currently unused. Either:

    • Use it for debug logging (e.g., to annotate which component type is being scanned), or
    • Drop the parameter to avoid ARG002 from ruff.
  • _get_callable_functions and _is_valid_wrappable_function intentionally catch Exception to be robust against arbitrary component implementations; that’s reasonable for a plugin-style system. If you want better observability, you could log at DEBUG when introspection of a particular attribute fails.

  • _extract_component_attributes also swallows all exceptions and returns None. Given this is purely for metadata enrichment, this fail-closed behavior is acceptable, but consider at least a logger.debug when an unexpected exception occurs to help debugging odd components.

Also applies to: 869-901, 903-956

tests/nat/middleware/test_dynamic_middleware.py (1)

243-417: Confirm async test execution strategy (pytest-asyncio vs asyncio_mode).

Most tests in this file are declared as async def (e.g., Lines 243–353, 336–353, 358–417, 422–444, 449–559) but aren’t decorated with @pytest.mark.asyncio, even though the project guideline calls out pytest-asyncio for async tests.

If the repo is using pytest-asyncio with asyncio_mode = "auto" (or similar) in pytest.ini, this is fine. Otherwise, these tests will fail to run. Either:

  • Add pytestmark = pytest.mark.asyncio at the module level, or
  • Decorate each async test with @pytest.mark.asyncio.

Also applies to: 422-444, 449-559

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c40781a and 7368cf8.

📒 Files selected for processing (4)
  • docs/source/reference/middleware.md (7 hunks)
  • src/nat/middleware/dynamic/dynamic_function_middleware.py (1 hunks)
  • src/nat/middleware/utils/workflow_inventory.py (1 hunks)
  • tests/nat/middleware/test_dynamic_middleware.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (16)
**/*.{md,rst,py}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Use 'NVIDIA NeMo Agent toolkit' on first use, then 'NeMo Agent toolkit' for subsequent references

Files:

  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • docs/source/reference/middleware.md
  • src/nat/middleware/utils/workflow_inventory.py
**/*.{py,toml,yaml,yml}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Use abbreviations: 'nat' for API namespace and CLI tool, 'nvidia-nat' for package name, 'NAT' for environment variable prefixes and informal comments

Files:

  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • src/nat/middleware/utils/workflow_inventory.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

**/*.py: Follow PEP 20 and PEP 8 for Python style guidelines
Run yapf second (PEP 8 base, column_limit = 120) for Python formatting
Use ruff check --fix (via pre-commit) for linting using configuration embedded in pyproject.toml, fix warnings unless explicitly ignored
Use snake_case for functions and variables, PascalCase for classes, UPPER_CASE for constants
All public APIs require Python 3.11+ type hints on parameters and return values
Prefer collections.abc / typing abstractions (Sequence over list) for type hints
Use typing.Annotated for units or extra metadata when useful
Treat pyright warnings (configured in pyproject.toml) as errors during development
Preserve stack traces and prevent duplicate logging when handling exceptions; use bare raise statements and logger.error() when re-raising
When catching and logging exceptions without re-raising, always use logger.exception() to capture the full stack trace information
Provide Google-style docstrings for every public module, class, function and CLI command
The first line of docstrings must be a concise description ending with a period (Vale checks this)
Surround code entities with backticks in docstrings to avoid Vale false-positives
Validate and sanitise all user input, especially in web or CLI interfaces
Prefer httpx with SSL verification enabled by default and follow OWASP Top-10 recommendations
Use async/await for I/O-bound work (HTTP, DB, file reads)
Cache expensive computations with functools.lru_cache or an external cache when appropriate
Leverage NumPy vectorised operations whenever beneficial and feasible

Files:

  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • src/nat/middleware/utils/workflow_inventory.py
**/*.{py,js,ts,yaml,yml,json,md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Indent with 4 spaces, never tabs, and ensure every file ends with a single newline

Files:

  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • docs/source/reference/middleware.md
  • src/nat/middleware/utils/workflow_inventory.py
**/*.{py,env,toml,yaml,yml,json}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Never commit API keys, credentials or personal data; use environment variables or .env files excluded from Git

Files:

  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • src/nat/middleware/utils/workflow_inventory.py
**/*.{py,js,ts,java,cpp,c,go,rb,php}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Every file must start with the standard SPDX Apache-2.0 header

Files:

  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • src/nat/middleware/utils/workflow_inventory.py
**/*.{py,md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Version numbers are derived automatically by setuptools-scm; never hard-code them in code or docs

Files:

  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • docs/source/reference/middleware.md
  • src/nat/middleware/utils/workflow_inventory.py
**/*.{py,js,ts,java,cpp,c,go,rb,php,sh}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

All source files must include the SPDX Apache-2.0 header template

Files:

  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • src/nat/middleware/utils/workflow_inventory.py
**/*

⚙️ CodeRabbit configuration file

**/*: # Code Review Instructions

  • Ensure the code follows best practices and coding standards. - For Python code, follow
    PEP 20 and
    PEP 8 for style guidelines.
  • Check for security vulnerabilities and potential issues. - Python methods should use type hints for all parameters and return values (except for return values of None,
    in that situation no return type hint is needed).
    Example:
    def my_function(param1: int, param2: str) -> bool:
        pass
  • For Python exception handling, ensure proper stack trace preservation:
    • When re-raising exceptions: use bare raise statements to maintain the original stack trace,
      and use logger.error() (not logger.exception()) to avoid duplicate stack trace output.
    • When catching and logging exceptions without re-raising: always use logger.exception()
      to capture the full stack trace information.

Documentation Review Instructions - Verify that documentation and comments are clear and comprehensive. - Verify that the documentation doesn't contain any TODOs, FIXMEs or placeholder text like "lorem ipsum". - Verify that the documentation doesn't contain any offensive or outdated terms. - Verify that documentation and comments are free of spelling mistakes, ensure the documentation doesn't contain any

words listed in the ci/vale/styles/config/vocabularies/nat/reject.txt file, words that might appear to be
spelling mistakes but are listed in the ci/vale/styles/config/vocabularies/nat/accept.txt file are OK.

Misc. - All code (except .mdc files that contain Cursor rules) should be licensed under the Apache License 2.0,

and should contain an Apache License 2.0 header comment at the top of each file.

  • Confirm that copyright years are up-to date whenever a file is changed.

Files:

  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • tests/nat/middleware/test_dynamic_middleware.py
  • docs/source/reference/middleware.md
  • src/nat/middleware/utils/workflow_inventory.py
src/nat/**/*

⚙️ CodeRabbit configuration file

This directory contains the core functionality of the toolkit. Changes should prioritize backward compatibility.

Files:

  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • src/nat/middleware/utils/workflow_inventory.py
**/test_*.py

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

**/test_*.py: Use pytest with pytest-asyncio for asynchronous code testing
Test functions should be named using the test_ prefix, using snake_case
Extract frequently repeated code into pytest fixtures, which should be named using the fixture_ prefix and define the name argument in the decorator
Mock external services with pytest_httpserver or unittest.mock instead of hitting live endpoints
Mark slow tests with @pytest.mark.slow so they can be skipped in the default test suite
Mark integration tests requiring external services with @pytest.mark.integration so they can be skipped in the default test suite

Files:

  • tests/nat/middleware/test_dynamic_middleware.py
tests/**/*.py

⚙️ CodeRabbit configuration file

tests/**/*.py: - Ensure that tests are comprehensive, cover edge cases, and validate the functionality of the code. - Test functions should be named using the test_ prefix, using snake_case. - Any frequently repeated code should be extracted into pytest fixtures. - Pytest fixtures should define the name argument when applying the pytest.fixture decorator. The fixture
function being decorated should be named using the fixture_ prefix, using snake_case. Example:
@pytest.fixture(name="my_fixture")
def fixture_my_fixture():
pass

Files:

  • tests/nat/middleware/test_dynamic_middleware.py
**/*.{md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Use 'NeMo Agent Toolkit' (capitalize 'T') when the name appears in headings

Files:

  • docs/source/reference/middleware.md
docs/**/*.{md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Never use deprecated names: Agent Intelligence toolkit, aiqtoolkit, AgentIQ, AIQ/aiq in documentation (unless intentionally referring to deprecated versions)

Files:

  • docs/source/reference/middleware.md
docs/source/**/*.{md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

docs/source/**/*.{md,rst}: Documentation must be clear and comprehensive, without TODOs, FIXMEs, or placeholder text
Ensure documentation is free of offensive or outdated terms
Ensure documentation is free of spelling mistakes and do not use words listed in ci/vale/styles/config/vocabularies/nat/reject.txt

Files:

  • docs/source/reference/middleware.md
docs/source/**/*

⚙️ CodeRabbit configuration file

This directory contains the source code for the documentation. All documentation should be written in Markdown format. Any image files should be placed in the docs/source/_static directory.

Files:

  • docs/source/reference/middleware.md
🧬 Code graph analysis (2)
tests/nat/middleware/test_dynamic_middleware.py (3)
src/nat/middleware/dynamic/dynamic_function_middleware.py (4)
  • DynamicFunctionMiddleware (45-1066)
  • function_middleware_invoke (716-789)
  • function_middleware_stream (791-865)
  • _discover_and_register_llm (107-141)
src/nat/middleware/utils/workflow_inventory.py (3)
  • DiscoveredFunction (83-96)
  • RegisteredFunction (110-113)
  • RegisteredComponentMethod (116-121)
src/nat/middleware/middleware.py (1)
  • FunctionMiddlewareContext (42-66)
src/nat/middleware/utils/workflow_inventory.py (2)
src/nat/data_models/component.py (1)
  • ComponentGroup (48-60)
src/nat/data_models/function.py (1)
  • FunctionBaseConfig (26-36)
🪛 Ruff (0.14.7)
src/nat/middleware/dynamic/dynamic_function_middleware.py

101-102: Avoid specifying long messages outside the exception class

(TRY003)


138-138: Do not catch blind exception: Exception

(BLE001)


175-175: Do not catch blind exception: Exception

(BLE001)


214-214: Do not catch blind exception: Exception

(BLE001)


252-252: Do not catch blind exception: Exception

(BLE001)


292-292: Do not catch blind exception: Exception

(BLE001)


332-332: Do not catch blind exception: Exception

(BLE001)


340-340: Undefined name FunctionRef

(F821)


555-555: Avoid specifying long messages outside the exception class

(TRY003)


559-561: Avoid specifying long messages outside the exception class

(TRY003)


603-603: Avoid specifying long messages outside the exception class

(TRY003)


636-637: Avoid specifying long messages outside the exception class

(TRY003)


652-652: Undefined name FunctionMiddlewareContext

(F821)


710-710: Consider moving this statement to an else block

(TRY300)


711-711: Do not catch blind exception: Exception

(BLE001)


719-719: Undefined name FunctionMiddlewareContext

(F821)


750-751: Abstract raise to an inner function

(TRY301)


750-751: Avoid specifying long messages outside the exception class

(TRY003)


794-794: Undefined name FunctionMiddlewareContext

(F821)


825-826: Abstract raise to an inner function

(TRY301)


825-826: Avoid specifying long messages outside the exception class

(TRY003)


869-869: Unused method argument: component_type

(ARG002)


897-899: try-except-continue detected, consider logging the exception

(S112)


897-897: Do not catch blind exception: Exception

(BLE001)


952-952: Consider moving this statement to an else block

(TRY300)


954-954: Do not catch blind exception: Exception

(BLE001)


970-970: Avoid specifying long messages outside the exception class

(TRY003)


978-978: Avoid specifying long messages outside the exception class

(TRY003)


986-986: Avoid specifying long messages outside the exception class

(TRY003)


994-994: Avoid specifying long messages outside the exception class

(TRY003)


1002-1003: Avoid specifying long messages outside the exception class

(TRY003)


1011-1011: Avoid specifying long messages outside the exception class

(TRY003)


1019-1019: Avoid specifying long messages outside the exception class

(TRY003)


1029-1029: Avoid specifying long messages outside the exception class

(TRY003)


1035-1035: Avoid specifying long messages outside the exception class

(TRY003)


1041-1041: Avoid specifying long messages outside the exception class

(TRY003)


1047-1047: Avoid specifying long messages outside the exception class

(TRY003)


1053-1053: Avoid specifying long messages outside the exception class

(TRY003)


1059-1059: Avoid specifying long messages outside the exception class

(TRY003)


1065-1065: Avoid specifying long messages outside the exception class

(TRY003)

tests/nat/middleware/test_dynamic_middleware.py

76-76: Unused method argument: messages

(ARG002)


76-76: Unused method argument: kwargs

(ARG002)


79-79: Unused method argument: messages

(ARG002)


79-79: Unused method argument: kwargs

(ARG002)


82-82: Unused method argument: messages

(ARG002)


82-82: Unused method argument: kwargs

(ARG002)


85-85: Unused method argument: messages

(ARG002)


85-85: Unused method argument: kwargs

(ARG002)


97-97: Unused method argument: text

(ARG002)


100-100: Unused method argument: text

(ARG002)


112-112: Unused method argument: query

(ARG002)


112-112: Unused method argument: kwargs

(ARG002)


124-124: Unused method argument: query

(ARG002)


124-124: Unused method argument: top_k

(ARG002)


124-124: Unused method argument: kwargs

(ARG002)


169-169: Unused method argument: user_id

(ARG002)


169-169: Unused method argument: kwargs

(ARG002)


224-224: Consider (modified_first, *context.function_args[1:]) instead of concatenation

Replace with (modified_first, *context.function_args[1:])

(RUF005)


251-251: Unused function argument: kwargs

(ARG001)


278-278: Unused function argument: kwargs

(ARG001)


305-305: Unused function argument: args

(ARG001)


305-305: Unused function argument: kwargs

(ARG001)


326-326: Unused function argument: args

(ARG001)


326-326: Unused function argument: kwargs

(ARG001)


341-341: Unused function argument: args

(ARG001)


341-341: Unused function argument: kwargs

(ARG001)


370-370: Unused function argument: kwargs

(ARG001)


390-390: Unused function argument: args

(ARG001)


390-390: Unused function argument: kwargs

(ARG001)


409-409: Unused function argument: args

(ARG001)


409-409: Unused function argument: kwargs

(ARG001)


434-434: Unused function argument: args

(ARG001)


434-434: Unused function argument: kwargs

(ARG001)


716-716: Pattern passed to match= contains metacharacters but is neither escaped nor raw

(RUF043)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CI Pipeline / Check
🔇 Additional comments (5)
docs/source/reference/middleware.md (2)

129-172: Variadic middleware examples and tests look consistent.

The updated LoggingMiddleware and ValidationMiddleware examples correctly use *args / **kwargs and forward all arguments to call_next, and the unit-test snippet reflects the new call signature. This matches the new middleware API and preserves backward compatibility for call sites.

Also applies to: 345-359, 529-537


703-711: API reference additions for cache middleware and plugin link look good.

The new API entries for CacheMiddlewareConfig / CacheMiddleware and the See Also link to the plugin system are accurate and helpful cross-references to the reorganized middleware API.

src/nat/middleware/dynamic/dynamic_function_middleware.py (2)

51-67: DynamicFunctionMiddleware initialization and policy wiring look solid.

The constructor cleanly wires pre/post policy instances via _get_policy_instance, initializes the workflow inventory and component allowlists, and kicks off discovery/patching. The helper _get_policy_instance now raises a clear ValueError with available policy names, which should make misconfiguration easier to diagnose.

Also applies to: 79-82, 85-104


957-1067: Builder patching and original-method getters look correct and self-consistent.

The _patch_get_* methods preserve the original builder methods and replace them with dynamic wrappers, and the _get_builder_get_* accessors guard against use before patching. The error messages for missing builder methods are clear, and this pattern should behave well with both production builders and the mocked builders in tests.

src/nat/middleware/utils/workflow_inventory.py (1)

29-53: Workflow inventory and allowlists are well-structured and match dynamic middleware usage.

The COMPONENT_FUNCTION_ALLOWLISTS constant and the Discovered*, Registered*, and WorkflowInventory models provide a clear, Pydantic-validated structure for discovery and registration. The field descriptions and arbitrary_types_allowed config fit the dynamic component surface, and the sets of allowed method names align with the documented component method allowlists.

Also applies to: 56-155

Copy link

@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

♻️ Duplicate comments (1)
docs/source/reference/middleware.md (1)

18-31: Dynamic middleware unregister section now aligns with the public API.

The new “Dynamic Middleware: Unregistering Callables” section:

  • Uses get_registered() / get_registered_keys() instead of reaching into _registered_callables.
  • Correctly describes behavior for workflow functions vs component methods.
  • Documents the RegisteredFunction and RegisteredComponentMethod models in a concise way.

This is much clearer and better aligned with the actual surface of DynamicFunctionMiddleware.

Also applies to: 639-672

🧹 Nitpick comments (7)
src/nat/middleware/dynamic/dynamic_function_middleware.py (4)

53-71: Policy resolution helper looks good; consider tightening typing and encapsulation.

The _get_policy_instance helper cleanly addresses missing-policy diagnostics and makes the pre/post policy lists safer to build. Two small follow‑ups you might consider:

  • Type builder as Builder instead of Any so pyright can catch misuse.
  • If _function_policies is meant to remain a private detail of Builder, you could optionally front it with a small public accessor on Builder and call that here to reduce direct reliance on a private attribute.

These are style/maintainability points only; behavior as written is sound.

Also applies to: 87-105


545-595: Registration failure handling is intentionally tolerant; consider richer logging when debugging.

Catching Exception around _register_component_function and logging at debug level matches the “best effort” discovery story, but it can make diagnosing misconfigurations or unexpected errors harder in production.

If you hit debuggability issues later, you might:

  • Narrow the exception type for expected discovery errors (e.g., ValueError) and
  • Use logger.exception (perhaps at debug level) for genuinely unexpected failures so the stack trace is still available when needed.

Given the design goals, the current behavior is acceptable.


691-709: Allowlist building and callable discovery are robust; tidy up a couple of nits.

The allowlist construction and _get_callable_functions / _is_valid_wrappable_function logic gives a clear two‑stage filter: “can be wrapped” vs “allowed to be auto‑wrapped”. That’s a good separation.

Two minor cleanups you might consider:

  • _get_callable_functions’s component_type argument is currently unused; either remove it or leverage it in debug logs for easier troubleshooting.
  • In _get_callable_functions and _is_valid_wrappable_function, you silently swallow all exceptions; if you ever need to debug odd components, adding a logger.debug/logger.exception with the component_type and function_name would help without changing behavior.

These are small maintainability tweaks, not correctness issues.

Also applies to: 880-913, 914-967


968-1078: Builder patching and original‑getter helpers are straightforward; consider adding return type hints.

The _patch_* methods and their _get_builder_get_* counterparts clearly separate the patched entrypoints from the saved originals and fail fast if a getter hasn’t been patched.

To align more closely with the typing guideline, you could add explicit return types such as Callable[..., Any] to the _get_builder_get_* helpers. This would help pyright and readers without changing behavior.

src/nat/function_policy/interface.py (1)

69-125: FunctionPolicyBase contract is clear; small typing/style polish possible.

The abstract on_pre_invoke / on_post_invoke signatures and docstrings clearly define:

  • Return‑None as “no change”.
  • Tuple length invariants for args.
  • Generic PolicyConfigT for config typing.

One minor nit: ruff flags __all__ ordering; you may want to sort it (e.g., ["FunctionPolicyBase", "PolicyConfigT", "PostInvokeContext", "PreInvokeContext"]) to keep style checks happy.

tests/nat/middleware/test_dynamic_middleware.py (1)

449-560: Component discovery tests hit the main component types; consider expanding to cover allowlists if they change often.

The discovery tests for LLMs, embedders, retrievers, memory, object stores, and auth providers validate:

  • That discovery only occurs when the corresponding register_* flag is enabled.
  • That duplicates are not added twice.

If you later evolve COMPONENT_FUNCTION_ALLOWLISTS or per‑type allowlist config, it may be worth adding a focused test that asserts only allowlisted methods end up registered (not just discovered), but the current coverage is already good for this PR.

docs/source/reference/middleware.md (1)

22-31: Minor product‑name wording tweak to match style guide.

The first mention says “functions in the NeMo Agent Toolkit”. Per the guidelines, prose should use “NVIDIA NeMo Agent toolkit” on first mention and “NeMo Agent toolkit” thereafter (docs headings already follow the capital‑T variant).

If you touch this section again, consider updating that phrase to match the project’s preferred wording.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7368cf8 and 4dd0645.

📒 Files selected for processing (6)
  • ci/scripts/path_checks.py (1 hunks)
  • ci/vale/styles/config/vocabularies/nat/accept.txt (1 hunks)
  • docs/source/reference/middleware.md (7 hunks)
  • src/nat/function_policy/interface.py (1 hunks)
  • src/nat/middleware/dynamic/dynamic_function_middleware.py (1 hunks)
  • tests/nat/middleware/test_dynamic_middleware.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (16)
**/*.{md,rst,py}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Use 'NVIDIA NeMo Agent toolkit' on first use, then 'NeMo Agent toolkit' for subsequent references

Files:

  • ci/scripts/path_checks.py
  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • src/nat/function_policy/interface.py
  • docs/source/reference/middleware.md
  • tests/nat/middleware/test_dynamic_middleware.py
**/*.{py,toml,yaml,yml}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Use abbreviations: 'nat' for API namespace and CLI tool, 'nvidia-nat' for package name, 'NAT' for environment variable prefixes and informal comments

Files:

  • ci/scripts/path_checks.py
  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • src/nat/function_policy/interface.py
  • tests/nat/middleware/test_dynamic_middleware.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

**/*.py: Follow PEP 20 and PEP 8 for Python style guidelines
Run yapf second (PEP 8 base, column_limit = 120) for Python formatting
Use ruff check --fix (via pre-commit) for linting using configuration embedded in pyproject.toml, fix warnings unless explicitly ignored
Use snake_case for functions and variables, PascalCase for classes, UPPER_CASE for constants
All public APIs require Python 3.11+ type hints on parameters and return values
Prefer collections.abc / typing abstractions (Sequence over list) for type hints
Use typing.Annotated for units or extra metadata when useful
Treat pyright warnings (configured in pyproject.toml) as errors during development
Preserve stack traces and prevent duplicate logging when handling exceptions; use bare raise statements and logger.error() when re-raising
When catching and logging exceptions without re-raising, always use logger.exception() to capture the full stack trace information
Provide Google-style docstrings for every public module, class, function and CLI command
The first line of docstrings must be a concise description ending with a period (Vale checks this)
Surround code entities with backticks in docstrings to avoid Vale false-positives
Validate and sanitise all user input, especially in web or CLI interfaces
Prefer httpx with SSL verification enabled by default and follow OWASP Top-10 recommendations
Use async/await for I/O-bound work (HTTP, DB, file reads)
Cache expensive computations with functools.lru_cache or an external cache when appropriate
Leverage NumPy vectorised operations whenever beneficial and feasible

Files:

  • ci/scripts/path_checks.py
  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • src/nat/function_policy/interface.py
  • tests/nat/middleware/test_dynamic_middleware.py
**/*.{py,js,ts,yaml,yml,json,md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Indent with 4 spaces, never tabs, and ensure every file ends with a single newline

Files:

  • ci/scripts/path_checks.py
  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • src/nat/function_policy/interface.py
  • docs/source/reference/middleware.md
  • tests/nat/middleware/test_dynamic_middleware.py
**/*.{py,env,toml,yaml,yml,json}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Never commit API keys, credentials or personal data; use environment variables or .env files excluded from Git

Files:

  • ci/scripts/path_checks.py
  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • src/nat/function_policy/interface.py
  • tests/nat/middleware/test_dynamic_middleware.py
**/*.{py,js,ts,java,cpp,c,go,rb,php}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Every file must start with the standard SPDX Apache-2.0 header

Files:

  • ci/scripts/path_checks.py
  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • src/nat/function_policy/interface.py
  • tests/nat/middleware/test_dynamic_middleware.py
**/*.{py,md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Version numbers are derived automatically by setuptools-scm; never hard-code them in code or docs

Files:

  • ci/scripts/path_checks.py
  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • src/nat/function_policy/interface.py
  • docs/source/reference/middleware.md
  • tests/nat/middleware/test_dynamic_middleware.py
**/*.{py,js,ts,java,cpp,c,go,rb,php,sh}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

All source files must include the SPDX Apache-2.0 header template

Files:

  • ci/scripts/path_checks.py
  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • src/nat/function_policy/interface.py
  • tests/nat/middleware/test_dynamic_middleware.py
**/*

⚙️ CodeRabbit configuration file

**/*: # Code Review Instructions

  • Ensure the code follows best practices and coding standards. - For Python code, follow
    PEP 20 and
    PEP 8 for style guidelines.
  • Check for security vulnerabilities and potential issues. - Python methods should use type hints for all parameters and return values (except for return values of None,
    in that situation no return type hint is needed).
    Example:
    def my_function(param1: int, param2: str) -> bool:
        pass
  • For Python exception handling, ensure proper stack trace preservation:
    • When re-raising exceptions: use bare raise statements to maintain the original stack trace,
      and use logger.error() (not logger.exception()) to avoid duplicate stack trace output.
    • When catching and logging exceptions without re-raising: always use logger.exception()
      to capture the full stack trace information.

Documentation Review Instructions - Verify that documentation and comments are clear and comprehensive. - Verify that the documentation doesn't contain any TODOs, FIXMEs or placeholder text like "lorem ipsum". - Verify that the documentation doesn't contain any offensive or outdated terms. - Verify that documentation and comments are free of spelling mistakes, ensure the documentation doesn't contain any

words listed in the ci/vale/styles/config/vocabularies/nat/reject.txt file, words that might appear to be
spelling mistakes but are listed in the ci/vale/styles/config/vocabularies/nat/accept.txt file are OK.

Misc. - All code (except .mdc files that contain Cursor rules) should be licensed under the Apache License 2.0,

and should contain an Apache License 2.0 header comment at the top of each file.

  • Confirm that copyright years are up-to date whenever a file is changed.

Files:

  • ci/scripts/path_checks.py
  • ci/vale/styles/config/vocabularies/nat/accept.txt
  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • src/nat/function_policy/interface.py
  • docs/source/reference/middleware.md
  • tests/nat/middleware/test_dynamic_middleware.py
src/nat/**/*

⚙️ CodeRabbit configuration file

This directory contains the core functionality of the toolkit. Changes should prioritize backward compatibility.

Files:

  • src/nat/middleware/dynamic/dynamic_function_middleware.py
  • src/nat/function_policy/interface.py
**/*.{md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Use 'NeMo Agent Toolkit' (capitalize 'T') when the name appears in headings

Files:

  • docs/source/reference/middleware.md
docs/**/*.{md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Never use deprecated names: Agent Intelligence toolkit, aiqtoolkit, AgentIQ, AIQ/aiq in documentation (unless intentionally referring to deprecated versions)

Files:

  • docs/source/reference/middleware.md
docs/source/**/*.{md,rst}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

docs/source/**/*.{md,rst}: Documentation must be clear and comprehensive, without TODOs, FIXMEs, or placeholder text
Ensure documentation is free of offensive or outdated terms
Ensure documentation is free of spelling mistakes and do not use words listed in ci/vale/styles/config/vocabularies/nat/reject.txt

Files:

  • docs/source/reference/middleware.md
docs/source/**/*

⚙️ CodeRabbit configuration file

This directory contains the source code for the documentation. All documentation should be written in Markdown format. Any image files should be placed in the docs/source/_static directory.

Files:

  • docs/source/reference/middleware.md
**/test_*.py

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

**/test_*.py: Use pytest with pytest-asyncio for asynchronous code testing
Test functions should be named using the test_ prefix, using snake_case
Extract frequently repeated code into pytest fixtures, which should be named using the fixture_ prefix and define the name argument in the decorator
Mock external services with pytest_httpserver or unittest.mock instead of hitting live endpoints
Mark slow tests with @pytest.mark.slow so they can be skipped in the default test suite
Mark integration tests requiring external services with @pytest.mark.integration so they can be skipped in the default test suite

Files:

  • tests/nat/middleware/test_dynamic_middleware.py
tests/**/*.py

⚙️ CodeRabbit configuration file

tests/**/*.py: - Ensure that tests are comprehensive, cover edge cases, and validate the functionality of the code. - Test functions should be named using the test_ prefix, using snake_case. - Any frequently repeated code should be extracted into pytest fixtures. - Pytest fixtures should define the name argument when applying the pytest.fixture decorator. The fixture
function being decorated should be named using the fixture_ prefix, using snake_case. Example:
@pytest.fixture(name="my_fixture")
def fixture_my_fixture():
pass

Files:

  • tests/nat/middleware/test_dynamic_middleware.py
🧠 Learnings (1)
📚 Learning: 2025-11-24T18:56:53.109Z
Learnt from: CR
Repo: NVIDIA/NeMo-Agent-Toolkit PR: 0
File: .cursor/rules/general.mdc:0-0
Timestamp: 2025-11-24T18:56:53.109Z
Learning: Words listed in ci/vale/styles/config/vocabularies/nat/accept.txt are acceptable even if they appear to be spelling mistakes

Applied to files:

  • ci/vale/styles/config/vocabularies/nat/accept.txt
🧬 Code graph analysis (2)
src/nat/middleware/dynamic/dynamic_function_middleware.py (2)
src/nat/data_models/component.py (1)
  • ComponentGroup (48-60)
src/nat/middleware/function_middleware.py (1)
  • FunctionMiddleware (43-110)
tests/nat/middleware/test_dynamic_middleware.py (7)
src/nat/data_models/authentication.py (2)
  • AuthProviderBaseConfig (31-37)
  • AuthResult (194-245)
src/nat/middleware/dynamic/dynamic_function_middleware.py (3)
  • DynamicFunctionMiddleware (47-1077)
  • function_middleware_invoke (737-803)
  • function_middleware_stream (805-876)
src/nat/middleware/dynamic/dynamic_middleware_config.py (1)
  • DynamicMiddlewareConfig (78-138)
src/nat/middleware/utils/workflow_inventory.py (4)
  • DiscoveredComponent (66-80)
  • DiscoveredFunction (83-96)
  • RegisteredFunction (110-113)
  • RegisteredComponentMethod (116-121)
src/nat/builder/workflow_builder.py (14)
  • get_llm (709-727)
  • get_llm (1454-1459)
  • get_embedder (825-843)
  • get_embedder (1470-1475)
  • get_retriever (932-953)
  • get_retriever (1547-1550)
  • get_memory_client (869-876)
  • get_memory_client (1486-1494)
  • get_object_store_client (901-905)
  • get_object_store_client (1505-1513)
  • get_auth_provider (781-806)
  • get_auth_provider (1450-1451)
  • get_function (567-573)
  • get_function (1382-1388)
src/nat/middleware/middleware.py (1)
  • FunctionMiddlewareContext (42-66)
src/nat/data_models/function_policy.py (1)
  • FunctionPolicyBaseConfig (24-31)
🪛 Ruff (0.14.7)
src/nat/middleware/dynamic/dynamic_function_middleware.py

103-104: Avoid specifying long messages outside the exception class

(TRY003)


140-140: Do not catch blind exception: Exception

(BLE001)


177-177: Do not catch blind exception: Exception

(BLE001)


216-216: Do not catch blind exception: Exception

(BLE001)


254-254: Do not catch blind exception: Exception

(BLE001)


294-294: Do not catch blind exception: Exception

(BLE001)


334-334: Do not catch blind exception: Exception

(BLE001)


557-557: Avoid specifying long messages outside the exception class

(TRY003)


561-563: Avoid specifying long messages outside the exception class

(TRY003)


624-624: Avoid specifying long messages outside the exception class

(TRY003)


657-658: Avoid specifying long messages outside the exception class

(TRY003)


731-731: Consider moving this statement to an else block

(TRY300)


732-732: Do not catch blind exception: Exception

(BLE001)


771-772: Abstract raise to an inner function

(TRY301)


771-772: Avoid specifying long messages outside the exception class

(TRY003)


839-840: Abstract raise to an inner function

(TRY301)


839-840: Avoid specifying long messages outside the exception class

(TRY003)


880-880: Unused method argument: component_type

(ARG002)


908-910: try-except-continue detected, consider logging the exception

(S112)


908-908: Do not catch blind exception: Exception

(BLE001)


963-963: Consider moving this statement to an else block

(TRY300)


965-965: Do not catch blind exception: Exception

(BLE001)


981-981: Avoid specifying long messages outside the exception class

(TRY003)


989-989: Avoid specifying long messages outside the exception class

(TRY003)


997-997: Avoid specifying long messages outside the exception class

(TRY003)


1005-1005: Avoid specifying long messages outside the exception class

(TRY003)


1013-1014: Avoid specifying long messages outside the exception class

(TRY003)


1022-1022: Avoid specifying long messages outside the exception class

(TRY003)


1030-1030: Avoid specifying long messages outside the exception class

(TRY003)


1040-1040: Avoid specifying long messages outside the exception class

(TRY003)


1046-1046: Avoid specifying long messages outside the exception class

(TRY003)


1052-1052: Avoid specifying long messages outside the exception class

(TRY003)


1058-1058: Avoid specifying long messages outside the exception class

(TRY003)


1064-1064: Avoid specifying long messages outside the exception class

(TRY003)


1070-1070: Avoid specifying long messages outside the exception class

(TRY003)


1076-1076: Avoid specifying long messages outside the exception class

(TRY003)

src/nat/function_policy/interface.py

128-133: __all__ is not sorted

Apply an isort-style sorting to __all__

(RUF022)

tests/nat/middleware/test_dynamic_middleware.py

76-76: Unused method argument: messages

(ARG002)


76-76: Unused method argument: kwargs

(ARG002)


79-79: Unused method argument: messages

(ARG002)


79-79: Unused method argument: kwargs

(ARG002)


82-82: Unused method argument: messages

(ARG002)


82-82: Unused method argument: kwargs

(ARG002)


85-85: Unused method argument: messages

(ARG002)


85-85: Unused method argument: kwargs

(ARG002)


97-97: Unused method argument: text

(ARG002)


100-100: Unused method argument: text

(ARG002)


112-112: Unused method argument: query

(ARG002)


112-112: Unused method argument: kwargs

(ARG002)


124-124: Unused method argument: query

(ARG002)


124-124: Unused method argument: top_k

(ARG002)


124-124: Unused method argument: kwargs

(ARG002)


169-169: Unused method argument: user_id

(ARG002)


169-169: Unused method argument: kwargs

(ARG002)


224-224: Consider (modified_first, *context.function_args[1:]) instead of concatenation

Replace with (modified_first, *context.function_args[1:])

(RUF005)


251-251: Unused function argument: kwargs

(ARG001)


278-278: Unused function argument: kwargs

(ARG001)


305-305: Unused function argument: args

(ARG001)


305-305: Unused function argument: kwargs

(ARG001)


326-326: Unused function argument: args

(ARG001)


326-326: Unused function argument: kwargs

(ARG001)


341-341: Unused function argument: args

(ARG001)


341-341: Unused function argument: kwargs

(ARG001)


370-370: Unused function argument: kwargs

(ARG001)


390-390: Unused function argument: args

(ARG001)


390-390: Unused function argument: kwargs

(ARG001)


409-409: Unused function argument: args

(ARG001)


409-409: Unused function argument: kwargs

(ARG001)


434-434: Unused function argument: args

(ARG001)


434-434: Unused function argument: kwargs

(ARG001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CI Pipeline / Check
🔇 Additional comments (10)
ci/vale/styles/config/vocabularies/nat/accept.txt (1)

173-176: Vocabulary additions are correct and relevant to the PR.

The four new entries are properly formatted Go regex patterns and directly support the PR's focus on function policies with dynamic middleware and Python kwargs/args forwarding:

  • [Ss]anitization for data sanitization features
  • [Uu]nregister(ing|ed|s)? for policy/function unregistration
  • [Aa]rgs and [Kk]wargs for variable argument handling
ci/scripts/path_checks.py (1)

114-114: LGTM!

The addition of "Enable/disable" to the allowlist is appropriate given the Function Policies feature being introduced. The capitalization pattern and alphabetical placement are consistent with existing entries.

src/nat/middleware/dynamic/dynamic_function_middleware.py (3)

525-595: Registration/unregistration API is consistent and matches docs.

_register_function, _register_component_function, get_registered*, and unregister cooperate cleanly:

  • Duplicate registration is idempotent and logged at debug level.
  • Component methods correctly store the original callable and restore it on unregister.
  • Workflow functions have this middleware removed from their chain without disturbing others.

This aligns well with the documented unregister example and keeps the surface area small and explicit.

Also applies to: 614-639


641-688: Component function wrapping correctly avoids double‑wrapping and preserves metadata.

_configure_component_function_middleware carefully:

  • Skips re‑wrapping functions already present in _registered_callables.
  • Extracts config, description, and schema attributes defensively.
  • Chooses between build_single and build_stream based on inspect.isasyncgenfunction.

This matches the intended FunctionMiddlewareChain usage and should keep wrapped methods predictable and debuggable.


737-804: Policy orchestration semantics for invoke/stream are consistent with the interface.

function_middleware_invoke and function_middleware_stream:

  • Correctly thread a mutable PreInvokeContext and PostInvokeContext through the chain.
  • Enforce that pre‑invoke policies preserve arg tuple length.
  • Treat None from pre/post hooks as “no change”.
  • Use logger.exception when swallowing policy errors, matching the logging guideline.

The output‑chaining semantics for post‑invoke policies now reflect the documented contract (each policy sees the latest function_output), and the streaming path mirrors the single‑invoke path per chunk. This looks solid.

Also applies to: 805-877

src/nat/function_policy/interface.py (1)

28-67: Context dataclasses align well with middleware usage.

PreInvokeContext and PostInvokeContext match the way DynamicFunctionMiddleware uses them:

  • original_args stays immutable while function_args and function_output are updated along the chain.
  • Types are explicit and compatible with the variadic *args, **kwargs signatures.

This provides a clear, policy‑friendly surface without leaking implementation details from the middleware.

tests/nat/middleware/test_dynamic_middleware.py (3)

41-55: Fixtures for builder and tracking policies provide good coverage of the dynamic path.

mock_builder and the tracking_policy factory fixture do a nice job of:

  • Exercising both pre‑ and post‑invoke hooks without going through the config‑based policy resolution.
  • Verifying order, enable/disable flags, and error‑tolerant behavior.

This gives strong confidence in the DynamicFunctionMiddleware orchestration logic.

Also applies to: 178-237


243-354: Invoke/stream and error‑handling tests are thorough and match the design goals.

The function_middleware_invoke / function_middleware_stream tests:

  • Validate the no‑policy passthrough behavior.
  • Confirm that pre policies can modify inputs and post policies can modify outputs.
  • Assert ordering (pre → function → post) and that failing policies are skipped while others still run.

This directly tests the error‑resilient semantics described in the PR and looks solid.

Also applies to: 358-418


564-595: Workflow function and unregister tests validate the new registration API well.

The tests around:

  • Discovering existing workflow functions from builder._functions.
  • Preventing duplicate function/component registration.
  • Unregistering both workflow functions and component methods, including error cases.

all line up with the new get_registered / unregister behavior and ensure state is correctly restored (e.g., original callables coming back on components).

Also applies to: 599-635, 640-717

docs/source/reference/middleware.md (1)

111-172: Middleware signature examples are up to date with the variadic API.

The LoggingMiddleware and ValidationMiddleware examples, along with the unit‑testing snippet, now:

  • Use *args, **kwargs and forward them through call_next.
  • Demonstrate how to pull the primary payload from args[0] while preserving the rest.
  • Match the actual FunctionMiddleware protocol used by DynamicFunctionMiddleware.

These examples will help users avoid subtle signature mismatches when writing custom middleware.

Also applies to: 345-359, 529-538

Comment on lines +41 to +55
@pytest.fixture
def mock_builder():
"""Create a mock builder with all required methods."""
builder = Mock()
builder._functions = {}
builder._function_policies = {}
builder.get_llm = AsyncMock()
builder.get_embedder = AsyncMock()
builder.get_retriever = AsyncMock()
builder.get_memory_client = AsyncMock()
builder.get_object_store_client = AsyncMock()
builder.get_auth_provider = AsyncMock()
builder.get_function = AsyncMock()
builder.get_function_config = Mock()
return builder
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, search for pytest fixtures to understand naming patterns used
rg -n "@pytest\.fixture" --type py | head -50

Repository: NVIDIA/NeMo-Agent-Toolkit

Length of output: 194


🏁 Script executed:

#!/bin/bash
# Check the pyproject.toml for any pre-commit or linting configs that might enforce naming
find . -name "pyproject.toml" -o -name ".pre-commit-config.yaml" -o -name "ruff.toml" | head -5

Repository: NVIDIA/NeMo-Agent-Toolkit

Length of output: 194


🏁 Script executed:

#!/bin/bash
# Look at the coding guidelines file mentioned in learnings
cat .cursor/rules/general.mdc 2>/dev/null | grep -A 5 "fixture_"

Repository: NVIDIA/NeMo-Agent-Toolkit

Length of output: 194


Rename fixtures to follow the project's documented naming convention with fixture_ prefix and explicit name= argument.

The project guidelines require fixtures to be named with a fixture_ prefix and to define the name argument in the @pytest.fixture decorator. Update mock_builder (and the fixtures at lines 58-68, 160-173) to follow this convention:

@pytest.fixture(name="mock_builder")
def fixture_mock_builder():
    """Create a mock builder with all required methods."""
    # ...
🤖 Prompt for AI Agents
In tests/nat/middleware/test_dynamic_middleware.py around lines 41-55 (and also
update the fixtures at lines 58-68 and 160-173), rename the pytest fixtures to
use the documented naming convention by adding the fixture_ prefix to the
function name and specifying the fixture name via the decorator; e.g., change
the decorator to @pytest.fixture(name="mock_builder") and rename the function to
fixture_mock_builder, keeping the existing docstring and body unchanged, and
apply the same pattern to the other two fixtures mentioned.

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

Labels

feature request New feature or request non-breaking Non-breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants