From ed9de49fbac46dfab196a09743241e521390917e Mon Sep 17 00:00:00 2001
From: Eric Evans <194135482+ericevans-nv@users.noreply.github.com>
Date: Thu, 9 Oct 2025 15:28:59 -0500
Subject: [PATCH 01/15] Adding RAG lib integration stubs.
Signed-off-by: Eric Evans <194135482+ericevans-nv@users.noreply.github.com>
---
packages/nvidia_nat_rag_lib/pyproject.toml | 44 +++
.../nvidia_nat_rag_lib/src/nat/meta/pypi.md | 23 ++
.../src/nat/plugins/rag_lib/__init__.py | 0
.../src/nat/plugins/rag_lib/client.py | 306 ++++++++++++++++++
.../src/nat/plugins/rag_lib/register.py | 21 ++
.../nvidia_nat_rag_lib/tests/test_config.yml | 56 ++++
.../tests/test_rag_lib_function.py | 136 ++++++++
pyproject.toml | 2 +
8 files changed, 588 insertions(+)
create mode 100644 packages/nvidia_nat_rag_lib/pyproject.toml
create mode 100644 packages/nvidia_nat_rag_lib/src/nat/meta/pypi.md
create mode 100644 packages/nvidia_nat_rag_lib/src/nat/plugins/rag_lib/__init__.py
create mode 100644 packages/nvidia_nat_rag_lib/src/nat/plugins/rag_lib/client.py
create mode 100644 packages/nvidia_nat_rag_lib/src/nat/plugins/rag_lib/register.py
create mode 100644 packages/nvidia_nat_rag_lib/tests/test_config.yml
create mode 100644 packages/nvidia_nat_rag_lib/tests/test_rag_lib_function.py
diff --git a/packages/nvidia_nat_rag_lib/pyproject.toml b/packages/nvidia_nat_rag_lib/pyproject.toml
new file mode 100644
index 0000000000..d2c70af61d
--- /dev/null
+++ b/packages/nvidia_nat_rag_lib/pyproject.toml
@@ -0,0 +1,44 @@
+[build-system]
+build-backend = "setuptools.build_meta"
+requires = ["setuptools >= 64", "setuptools-scm>=8"]
+
+
+[tool.setuptools.packages.find]
+where = ["src"]
+include = ["nat.*"]
+
+
+[tool.setuptools_scm]
+git_describe_command = ["git", "describe", "--long", "--first-parent"]
+root = "../.."
+
+
+[project]
+name = "nvidia-nat-rag-lib"
+dynamic = ["version"]
+dependencies = [
+ "nvidia-nat~=1.4",
+ "nvidia-rag>=2.2.2",
+]
+
+requires-python = ">=3.11,<3.14"
+description = "Subpackage for NVIDIA RAG library integration in NeMo Agent Toolkit"
+readme = "src/nat/meta/pypi.md"
+keywords = ["ai", "rag", "agents", "retrieval"]
+classifiers = [
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
+]
+
+[tool.uv]
+config-settings = { editable_mode = "compat" }
+
+
+[tool.uv.sources]
+nvidia-nat = { workspace = true }
+
+
+[project.entry-points.'nat.components']
+nat_rag_lib = "nat.plugins.rag_lib.register"
diff --git a/packages/nvidia_nat_rag_lib/src/nat/meta/pypi.md b/packages/nvidia_nat_rag_lib/src/nat/meta/pypi.md
new file mode 100644
index 0000000000..bea33ba21f
--- /dev/null
+++ b/packages/nvidia_nat_rag_lib/src/nat/meta/pypi.md
@@ -0,0 +1,23 @@
+# NVIDIA RAG Library Integration
+
+This package provides seamless integration between the NVIDIA RAG Library and the NeMo Agent Toolkit, allowing developers to easily incorporate retrieval-augmented generation capabilities into their agent workflows.
+
+## Features
+
+- Direct access to NVIDIA RAG Library functionality
+- Configurable environment setup and prerequisites verification
+- Retry logic for robust operation
+- Support for various deployment modes (on-premises, hosted, mixed)
+- Integration with NAT's component reference system
+
+## Installation
+
+Install as part of the NeMo Agent Toolkit:
+
+```bash
+pip install "nvidia-nat[rag-lib]"
+```
+
+## Usage
+
+Configure the RAG library in your workflow YAML file and use it as a function in your agent setup.
diff --git a/packages/nvidia_nat_rag_lib/src/nat/plugins/rag_lib/__init__.py b/packages/nvidia_nat_rag_lib/src/nat/plugins/rag_lib/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/nvidia_nat_rag_lib/src/nat/plugins/rag_lib/client.py b/packages/nvidia_nat_rag_lib/src/nat/plugins/rag_lib/client.py
new file mode 100644
index 0000000000..58e397f871
--- /dev/null
+++ b/packages/nvidia_nat_rag_lib/src/nat/plugins/rag_lib/client.py
@@ -0,0 +1,306 @@
+# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import asyncio
+import logging
+import os
+from pathlib import Path
+from typing import Literal
+
+from pydantic import Field
+from pydantic import HttpUrl
+
+from nat.builder.builder import Builder
+from nat.cli.register_workflow import register_function
+from nat.data_models.component_ref import EmbedderRef
+from nat.data_models.component_ref import LLMRef
+from nat.data_models.function import FunctionBaseConfig
+from nat.data_models.retry_mixin import RetryMixin
+
+logger = logging.getLogger(__name__)
+
+
+class NvidiaRAGLibConfig(FunctionBaseConfig, RetryMixin, name="nvidia_rag_lib"):
+ """Configuration for NVIDIA RAG Library integration.
+
+ This configuration manages the setup and instantiation of the NVIDIA RAG library,
+ providing retrieval-augmented generation capabilities including document ingestion,
+ vector search, reranking, and response generation. The configuration handles
+ environment setup, model references, deployment modes, and operational parameters.
+ """
+
+ # Core RAG configuration
+ vdb_endpoint: HttpUrl = Field(default=HttpUrl("http://localhost:19530"), description="Vector database endpoint URL")
+ reranker_top_k: int = Field(default=10, description="Number of top results to rerank")
+ vdb_top_k: int = Field(default=100, description="Number of top results from vector database")
+ collection_names: list[str] | None = Field(default=None, description="List of collection names to use for queries")
+
+ # Document processing
+ chunk_size: int = Field(default=512, description="Size of document chunks for processing")
+ chunk_overlap: int = Field(default=150, description="Overlap between document chunks")
+ generate_summary: bool = Field(default=False, description="Whether to generate document summaries")
+ blocking_upload: bool = Field(default=False, description="Whether to use blocking upload for documents")
+
+ # Infrastructure
+ vectorstore_gpu_device_id: str | None = Field(default="0", description="GPU device ID for vector store")
+ model_directory: str | None = Field(default="~/.cache/model-cache", description="Directory for model cache")
+
+ # Model configuration (NAT component references)
+ llm_name: LLMRef | None = Field(default=None, description="Reference to the LLM to use for responses")
+ embedder_name: EmbedderRef | None = Field(default=None,
+ description="Reference to the embedder to use for embeddings")
+ ranking_modelname: str | None = Field(default=None, description="Name of the ranking model")
+
+ # Service endpoints
+ app_embeddings_serverurl: str | None = Field(default="", description="Embeddings service URL")
+ app_llm_serverurl: str | None = Field(default="", description="LLM service URL")
+ app_ranking_serverurl: str | None = Field(default=None, description="Ranking service URL")
+
+ # Deployment and operational
+ deployment_mode: Literal["on_prem", "hosted", "mixed"] = Field(default="hosted",
+ description="Deployment mode for the RAG system")
+ timeout: float | None = Field(default=60.0, description="Timeout for operations in seconds")
+
+ # Setup and management options
+ env_library_path: str | None = Field(default=None, description="Path to .env_library file for environment setup")
+ use_accuracy_profile: bool = Field(default=False, description="Load accuracy profile settings")
+ use_perf_profile: bool = Field(default=False, description="Load performance profile settings")
+ verify_prerequisites: bool = Field(default=True, description="Verify prerequisites before initialization")
+ health_check_dependencies: bool = Field(default=True, description="Perform health check on dependent services")
+ health_check_timeout: float = Field(default=30.0, description="Timeout in seconds for health check operations")
+
+
+async def _load_env_library(config: NvidiaRAGLibConfig) -> None:
+ """Load environment variables from a specified .env_library file.
+
+ This function loads environment variables from an external environment
+ library file if specified in the configuration. This allows for
+ centralized environment management across multiple RAG deployments.
+
+ Args:
+ config: Configuration containing the path to the environment library file
+ """
+ if not config.env_library_path:
+ return
+
+ env_path = Path(config.env_library_path).expanduser()
+ if not env_path.exists():
+ logger.warning("Environment library file not found: %s", env_path)
+ return
+
+ try:
+ from dotenv import load_dotenv
+ load_dotenv(env_path)
+ logger.info("Loaded environment library: %s", env_path)
+ except ImportError:
+ logger.warning("python-dotenv not available, skipping .env_library loading")
+
+
+async def _setup_environment_variables(config: NvidiaRAGLibConfig, builder: Builder) -> None:
+ """Configure environment variables required by the NVIDIA RAG library.
+
+ This function sets up all necessary environment variables that the NVIDIA RAG
+ library expects, including vector database endpoints, model configurations,
+ document processing parameters, and infrastructure settings. Model names are
+ dynamically extracted from NAT component references when available.
+
+ Args:
+ config: Configuration containing RAG parameters and component references
+ builder: NAT builder instance for accessing LLM and embedder configurations
+ """
+ # Core configuration
+ if config.vdb_endpoint:
+ os.environ["VDB_ENDPOINT"] = str(config.vdb_endpoint)
+
+ os.environ["RERANKER_TOP_K"] = str(config.reranker_top_k)
+ os.environ["VDB_TOP_K"] = str(config.vdb_top_k)
+
+ if config.collection_names:
+ os.environ["COLLECTION_NAMES"] = ",".join(config.collection_names)
+
+ # Document processing
+ os.environ["CHUNK_SIZE"] = str(config.chunk_size)
+ os.environ["CHUNK_OVERLAP"] = str(config.chunk_overlap)
+ os.environ["GENERATE_SUMMARY"] = str(config.generate_summary).lower()
+ os.environ["BLOCKING_UPLOAD"] = str(config.blocking_upload).lower()
+
+ # Infrastructure
+ if config.vectorstore_gpu_device_id is not None:
+ os.environ["VECTORSTORE_GPU_DEVICE_ID"] = config.vectorstore_gpu_device_id
+
+ if config.model_directory:
+ model_dir = Path(config.model_directory).expanduser()
+ os.environ["MODEL_DIRECTORY"] = str(model_dir)
+
+ # Model names from NAT component references
+ if config.llm_name:
+ try:
+ llm_config = builder.get_llm_config(config.llm_name)
+ model_name = getattr(llm_config, 'model_name', None)
+ if model_name:
+ os.environ["APP_LLM_MODELNAME"] = str(model_name)
+ logger.debug("Set APP_LLM_MODELNAME from LLM reference: %s", model_name)
+ except Exception as e:
+ logger.warning("Failed to get LLM config for %s: %s", config.llm_name, e)
+
+ if config.embedder_name:
+ try:
+ embedder_config = builder.get_embedder_config(config.embedder_name)
+ model_name = getattr(embedder_config, 'model_name', None)
+ if model_name:
+ os.environ["APP_EMBEDDINGS_MODELNAME"] = str(model_name)
+ logger.debug("Set APP_EMBEDDINGS_MODELNAME from embedder reference: %s", model_name)
+ except Exception as e:
+ logger.warning("Failed to get embedder config for %s: %s", config.embedder_name, e)
+
+ if config.ranking_modelname:
+ os.environ["APP_RANKING_MODELNAME"] = config.ranking_modelname
+
+ # Service URLs
+ if config.app_embeddings_serverurl is not None:
+ os.environ["APP_EMBEDDINGS_SERVERURL"] = config.app_embeddings_serverurl
+ if config.app_llm_serverurl is not None:
+ os.environ["APP_LLM_SERVERURL"] = config.app_llm_serverurl
+ if config.app_ranking_serverurl is not None:
+ os.environ["APP_RANKING_SERVERURL"] = config.app_ranking_serverurl
+
+ # Deployment mode
+ os.environ["DEPLOYMENT_MODE"] = config.deployment_mode
+
+
+async def _load_profiles(config: NvidiaRAGLibConfig) -> None:
+ """Load accuracy and performance optimization profiles.
+
+ This function loads predefined environment configurations for accuracy
+ or performance optimization. These profiles contain environment variable
+ settings that optimize the RAG system for specific use cases.
+
+ Args:
+ config: Configuration specifying which profiles to load
+ """
+ if config.use_accuracy_profile:
+ accuracy_profile_path = Path("accuracy_profile.env")
+ if accuracy_profile_path.exists():
+ try:
+ from dotenv import load_dotenv
+ load_dotenv(accuracy_profile_path)
+ logger.info("Loaded accuracy profile")
+ except ImportError:
+ logger.warning("python-dotenv not available, skipping accuracy profile")
+ else:
+ logger.warning("Accuracy profile file not found: %s", accuracy_profile_path)
+
+ if config.use_perf_profile:
+ perf_profile_path = Path("perf_profile.env")
+ if perf_profile_path.exists():
+ try:
+ from dotenv import load_dotenv
+ load_dotenv(perf_profile_path)
+ logger.info("Loaded performance profile")
+ except ImportError:
+ logger.warning("python-dotenv not available, skipping performance profile")
+ else:
+ logger.warning("Performance profile file not found: %s", perf_profile_path)
+
+
+def _verify_prerequisites(config: NvidiaRAGLibConfig) -> None:
+ """Verify that all required prerequisites are met before initialization.
+
+ This function checks that necessary directories exist, dependencies are
+ available, and the system is properly configured for RAG operations.
+
+ Args:
+ config: Configuration containing prerequisite specifications
+ """
+ # Check model directory
+ if config.model_directory:
+ model_dir = Path(config.model_directory).expanduser()
+ if not model_dir.exists():
+ logger.warning("Model directory does not exist: %s", model_dir)
+
+
+@register_function(config_type=NvidiaRAGLibConfig)
+async def nvidia_rag_lib(config: NvidiaRAGLibConfig, builder: Builder):
+ """
+ Initialize and configure the NVIDIA RAG library client.
+
+ This function orchestrates the complete setup process for the NVIDIA RAG library,
+ including environment configuration, profile loading, prerequisite verification,
+ and service health checks. It yields a query function that provides access to
+ the fully configured RAG system for retrieval-augmented generation operations.
+
+ Args:
+ config: Configuration parameters for the NVIDIA RAG library
+ builder: NAT builder instance for accessing other components
+
+ Yields:
+ NvidiaRAG: Fully configured NVIDIA RAG library client instance
+
+ Raises:
+ ImportError: If the nvidia-rag library is not installed
+ RuntimeError: If required prerequisites are not met
+ """
+ logger.info("Starting NVIDIA RAG setup...")
+
+ try:
+ # Step 1: Load .env_library if available
+ if config.env_library_path:
+ logger.debug("Loading environment library: %s", config.env_library_path)
+ await _load_env_library(config)
+
+ # Step 2: Set up environment variables
+ logger.debug("Setting up environment variables...")
+ await _setup_environment_variables(config, builder)
+
+ # Step 3: Load profiles if requested
+ if config.use_accuracy_profile or config.use_perf_profile:
+ logger.debug("Loading performance profiles...")
+ await _load_profiles(config)
+
+ # Step 4: Verify prerequisites
+ if config.verify_prerequisites:
+ logger.debug("Verifying prerequisites...")
+ _verify_prerequisites(config)
+
+ # Step 5: Import and instantiate the NVIDIA RAG library
+ logger.debug("Importing NVIDIA RAG library...")
+ from nvidia_rag import NvidiaRAG
+
+ rag = NvidiaRAG()
+
+ # Step 6: Health check if requested
+ if config.health_check_dependencies:
+ logger.debug("Performing health check...")
+ try:
+ health_status = await asyncio.wait_for(rag.health(check_dependencies=True),
+ timeout=config.health_check_timeout)
+ logger.info("Health check completed: %s", health_status)
+ except TimeoutError:
+ logger.warning("Health check timed out after %ss, but continuing", config.health_check_timeout)
+ except Exception as e:
+ logger.warning("Health check failed, but continuing: %s", e)
+
+ # Yield the RAG instance
+ yield rag
+
+ except ImportError as e:
+ logger.error("nvidia_rag library not available. Install with: pip install nvidia-rag. Error: %s", e)
+ raise ImportError("nvidia-rag is required for this function. "
+ "Follow installation steps: pip install nvidia-rag --force-reinstall") from e
+ except Exception as e:
+ logger.error("Failed to set up NVIDIA RAG: %s", e)
+ raise
+ finally:
+ pass
diff --git a/packages/nvidia_nat_rag_lib/src/nat/plugins/rag_lib/register.py b/packages/nvidia_nat_rag_lib/src/nat/plugins/rag_lib/register.py
new file mode 100644
index 0000000000..2be2d28d29
--- /dev/null
+++ b/packages/nvidia_nat_rag_lib/src/nat/plugins/rag_lib/register.py
@@ -0,0 +1,21 @@
+# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# flake8: noqa
+# isort:skip_file
+
+# Import any providers which need to be automatically registered here
+
+from . import client
diff --git a/packages/nvidia_nat_rag_lib/tests/test_config.yml b/packages/nvidia_nat_rag_lib/tests/test_config.yml
new file mode 100644
index 0000000000..dd87f3ca00
--- /dev/null
+++ b/packages/nvidia_nat_rag_lib/tests/test_config.yml
@@ -0,0 +1,56 @@
+# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+# SPDX-License-Identifier: Apache-2.0
+
+# Test configuration for NVIDIA RAG Library integration
+# This configuration is used as a pytest fixture for testing
+
+llms:
+ test_llm:
+ _type: nim
+ model_name: meta/llama-3.1-8b-instruct
+ temperature: 0.0
+ max_tokens: 512
+
+embedders:
+ test_embedder:
+ _type: nim
+ model_name: nvidia/nv-embedqa-e5-v5
+ truncate: "END"
+
+functions:
+ rag_client:
+ _type: nvidia_rag_lib
+ # Core configuration
+ vdb_endpoint: "http://localhost:19530"
+ reranker_top_k: 5
+ vdb_top_k: 50
+ collection_names: ["test_collection"]
+
+ # Document processing
+ chunk_size: 256
+ chunk_overlap: 50
+ generate_summary: false
+ blocking_upload: false
+
+ # Model references
+ llm_name: test_llm
+ embedder_name: test_embedder
+
+ # Infrastructure
+ vectorstore_gpu_device_id: "0"
+ model_directory: "/tmp/test-model-cache"
+
+ # Service configuration
+ deployment_mode: "hosted"
+ timeout: 30.0
+
+ # Setup options (disabled for testing)
+ verify_prerequisites: false
+ health_check_dependencies: false
+ health_check_timeout: 10.0
+
+workflow:
+ _type: react_agent
+ llm_name: test_llm
+ tool_names: [rag_client]
+ verbose: false
diff --git a/packages/nvidia_nat_rag_lib/tests/test_rag_lib_function.py b/packages/nvidia_nat_rag_lib/tests/test_rag_lib_function.py
new file mode 100644
index 0000000000..760550067d
--- /dev/null
+++ b/packages/nvidia_nat_rag_lib/tests/test_rag_lib_function.py
@@ -0,0 +1,136 @@
+# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Test suite for the NVIDIA RAG Library integration.
+
+This module contains tests for the NVIDIA RAG Library function configuration,
+registration, and basic functionality verification.
+"""
+
+from pathlib import Path
+
+import pytest
+import yaml
+
+from nat.data_models.config import Config
+
+
+@pytest.fixture
+def test_config():
+ """Pytest fixture that loads the test configuration."""
+ config_path = Path(__file__).parent / "test_config.yml"
+
+ with open(config_path, encoding='utf-8') as f:
+ config_dict = yaml.safe_load(f)
+
+ return Config.model_validate(config_dict)
+
+
+class TestNvidiaRAGLib:
+ """Test suite for the NVIDIA RAG Library function."""
+
+ def test_function_registration(self):
+ """Test that the RAG function is properly registered with NAT."""
+ from nat.cli.type_registry import GlobalTypeRegistry
+ from nat.plugins.rag_lib.client import NvidiaRAGLibConfig
+ registry = GlobalTypeRegistry.get()
+
+ # Check if our function is registered
+ try:
+ function_info = registry.get_function(NvidiaRAGLibConfig)
+ assert function_info is not None
+ assert function_info.config_type == NvidiaRAGLibConfig
+ except KeyError:
+ pytest.fail("NvidiaRAGLibConfig function not properly registered")
+
+ @pytest.mark.asyncio
+ async def test_rag_library_acquisition(self, test_config):
+ """Test acquiring the RAG library.
+
+ Simple test that tries to acquire the RAG library.
+ """
+ from nat.builder.workflow_builder import WorkflowBuilder
+
+ try:
+ async with WorkflowBuilder.from_config(test_config) as builder:
+ # Simply acquire the RAG library function
+ rag_function = await builder.get_function("rag_client")
+
+ # Verify we got the function
+ assert rag_function is not None
+ print("RAG library acquired successfully")
+
+ except ImportError as e:
+ if "nvidia-rag" in str(e):
+ pytest.fail(f"nvidia-rag library not available: {e}")
+ else:
+ raise
+
+ @pytest.mark.asyncio
+ async def test_rag_search_functionality(self, test_config):
+ """Test RAG library search functionality after successful acquisition.
+
+ This test demonstrates how to use the RAG library's search capabilities
+ including citation parsing. It doesn't need to work (no vector DB running)
+ but shows the proper setup for RAG search operations.
+ """
+ from nat.builder.workflow_builder import WorkflowBuilder
+
+ def parse_search_citations(citations):
+ """Parse search citations into formatted document strings."""
+ parsed_docs = []
+
+ for idx, citation in enumerate(citations.results):
+ # If using pydantic models, citation fields may be attributes, not dict keys
+ content = getattr(citation, 'content', '')
+ doc_name = getattr(citation, 'document_name', f'Citation {idx+1}')
+ parsed_document = f'\n{content}\n'
+ parsed_docs.append(parsed_document)
+
+ # combine parsed documents into a single string
+ internal_search_docs = "\n\n---\n\n".join(parsed_docs)
+ return internal_search_docs
+
+ try:
+ async with WorkflowBuilder.from_config(test_config) as builder:
+ # Acquire the RAG library function
+ rag_function = await builder.get_function("rag_client")
+ assert rag_function is not None
+
+ try:
+ # Demonstrate search configuration matching our config
+ collection_names = test_config.functions["rag_client"].collection_names
+ reranker_top_k = test_config.functions["rag_client"].reranker_top_k
+ vdb_top_k = test_config.functions["rag_client"].vdb_top_k
+
+ search_results = rag_function.search(
+ query="test query",
+ collection_names=collection_names,
+ reranker_top_k=reranker_top_k,
+ vdb_top_k=vdb_top_k,
+ )
+ parsed_docs = parse_search_citations(search_results)
+
+ # Assert if data was returned from parsed_docs
+ assert parsed_docs is not None
+
+ except Exception as e:
+ print(f"RAG search failed as expected (no vector DB): {e}")
+
+ except ImportError as e:
+ if "nvidia-rag" in str(e):
+ pytest.fail(f"nvidia-rag library not available: {e}")
+ else:
+ raise
diff --git a/pyproject.toml b/pyproject.toml
index d1e025b14a..9b1c7a7252 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -81,6 +81,7 @@ mem0ai = ["nvidia-nat-mem0ai"]
opentelemetry = ["nvidia-nat-opentelemetry"]
phoenix = ["nvidia-nat-phoenix"]
profiling = ["nvidia-nat-profiling"] # meta-package
+rag-lib = ["nvidia-nat-rag-lib"]
ragaai = ["nvidia-nat-ragaai"]
mysql = ["nvidia-nat-mysql"]
redis = ["nvidia-nat-redis"]
@@ -171,6 +172,7 @@ nvidia-nat-mysql = { workspace = true }
nvidia-nat-opentelemetry = { workspace = true }
nvidia-nat-phoenix = { workspace = true }
nvidia-nat-profiling = { workspace = true }
+nvidia-nat-rag-lib = { workspace = true }
nvidia-nat-ragaai = { workspace = true }
nvidia-nat-redis = { workspace = true }
nvidia-nat-s3 = { workspace = true }
From 229028b7b0e94fb7af038ee94a795cdc7ea6a4a9 Mon Sep 17 00:00:00 2001
From: Eric Evans <194135482+ericevans-nv@users.noreply.github.com>
Date: Fri, 12 Dec 2025 09:36:08 -0600
Subject: [PATCH 02/15] Pushing package structure for testing.
Signed-off-by: Eric Evans <194135482+ericevans-nv@users.noreply.github.com>
---
packages/nvidia_nat_rag_lib/pyproject.toml | 13 +++++++++++--
.../nvidia_rag-2.4.0.dev0-py3-none-any.whl | Bin 0 -> 215736 bytes
2 files changed, 11 insertions(+), 2 deletions(-)
create mode 100644 packages/nvidia_nat_rag_lib/vendor/nvidia_rag-2.4.0.dev0-py3-none-any.whl
diff --git a/packages/nvidia_nat_rag_lib/pyproject.toml b/packages/nvidia_nat_rag_lib/pyproject.toml
index d2c70af61d..a9c763912c 100644
--- a/packages/nvidia_nat_rag_lib/pyproject.toml
+++ b/packages/nvidia_nat_rag_lib/pyproject.toml
@@ -18,13 +18,15 @@ name = "nvidia-nat-rag-lib"
dynamic = ["version"]
dependencies = [
"nvidia-nat~=1.4",
- "nvidia-rag>=2.2.2",
+ "nvidia-rag>=2.4.0",
]
-
requires-python = ">=3.11,<3.14"
description = "Subpackage for NVIDIA RAG library integration in NeMo Agent Toolkit"
readme = "src/nat/meta/pypi.md"
keywords = ["ai", "rag", "agents", "retrieval"]
+license = { text = "Apache-2.0" }
+authors = [{ name = "NVIDIA Corporation" }]
+maintainers = [{ name = "NVIDIA Corporation" }]
classifiers = [
"Programming Language :: Python",
"Programming Language :: Python :: 3.11",
@@ -32,12 +34,19 @@ classifiers = [
"Programming Language :: Python :: 3.13",
]
+[project.urls]
+documentation = "https://docs.nvidia.com/nemo/agent-toolkit/latest/"
+source = "https://github.com/NVIDIA/NeMo-Agent-Toolkit"
+
+
[tool.uv]
+managed = true
config-settings = { editable_mode = "compat" }
[tool.uv.sources]
nvidia-nat = { workspace = true }
+nvidia-rag = { path = "vendor/nvidia_rag-2.4.0.dev0-py3-none-any.whl" } # TODO EE: Remove this local path override once nvidia-rag>=2.4.0 is published to PyPI
[project.entry-points.'nat.components']
diff --git a/packages/nvidia_nat_rag_lib/vendor/nvidia_rag-2.4.0.dev0-py3-none-any.whl b/packages/nvidia_nat_rag_lib/vendor/nvidia_rag-2.4.0.dev0-py3-none-any.whl
new file mode 100644
index 0000000000000000000000000000000000000000..2a8dd6579692575716cb6e7728b52a4f710e4bd5
GIT binary patch
literal 215736
zcmaHxLv$`o)TQ6pwr$(CZQFKkY&$o$ZQHi38{1C$TdODi_uyYOtLfQipHt5+MHvuK
zQ~&?~3E&Zm(m6DA%j*XQ09e5Q0QCQE?cJ?Rt&9wujVu@p46N*}Tn!B99ldIjo0`-`m_K%BD{&I<3wOw+HNA5%jA-tf(-q9Qf@(QqODu?PWZe(
z5K-v~eKCIe^&pbTtNAV#u5W+t0S2MC`%Dn{5{0K&5gbtCPx6Bl?l2wWQDs1YUOK1uFWiMQ|R
zl~RV)o>c&+pkn44%2SS(Y7g0H0wJIEPMBn*+PNBnt~y}^GG_8R(1tWk7PD%kwZQ@0
zjl9Uu0LD^PH;7=g-V9I7TFYYVHyslhe650FMRh?2H;SZ;z2yZ4Kzkg4`ZAKfK~+qsWlq8@
z`&@NG7d3X&C7u0~8%uiIXsMYjM`WeN*VZHuJ
zCUo~^>y&!2LlOchkII}29*#4}*fkO`9edHuM0o8Z9y8!#-WpTIe7v`*m2c-CR)Y_c
zJFRBT_o4g(Yi~x}4yR(zv@`HO?#@Y}u&q+ee|{J@)!XX~_9$D%`V*{Q11+WQ-wt{T|A2d7!x5YB3W$<)v
zrZ!D75;tDuW%@vIxGx^<6%(a8%_@g3TArKY0km%DJniXY?Ip1rSZp=T>9gFwQ=X2^
z;fWKHz2u6C*Q)m|>oKNyjPfuoW;fG8ZMS;(O4s2Y!#}ID=lOD6p>1MkDJ1R_EeP@i
zTX$wkx2D;zO#vDuu%pC*vZHTQ$QiO5M`ERYFA7xp!v}q6PryKOhfqW!{W&I@Ud&5>=cn*P%;`h^ed@3_u5wK6!tlxf$~9Flg&D?K*{H
z@jQsequ9WmER7ISQ1DQgTjFEbXJPK@`Iy?6qAX3bYMm;2_GKy~hyq$@E5>;me~doe
zOPa{KU-6WrcPMS+SUT|WVIlgJ4r4Hgs~a17w>(KQ!bQ;!Pt0b{6qujC=0%ayl>m~%
z
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