From 2f5eee7a5834cde79b453b7a13a88b46ee1fc874 Mon Sep 17 00:00:00 2001 From: Manjesh Mogallapalli Date: Thu, 6 Aug 2026 17:22:27 -0500 Subject: [PATCH 1/3] add subprocess tests Signed-off-by: Manjesh Mogallapalli --- e2e/agents_deploy_helpers.py | 127 ++++++++++++++++++++++------- e2e/test_nemo_agents_subprocess.py | 54 ++++++++++++ 2 files changed, 150 insertions(+), 31 deletions(-) create mode 100644 e2e/test_nemo_agents_subprocess.py diff --git a/e2e/agents_deploy_helpers.py b/e2e/agents_deploy_helpers.py index b05eb0f03b..910fe660f0 100644 --- a/e2e/agents_deploy_helpers.py +++ b/e2e/agents_deploy_helpers.py @@ -1,24 +1,22 @@ # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -"""Shared helpers for container-mode agent deployment e2e tests. +"""Shared helpers for agent deployment e2e tests. -Both the Docker (``test_nemo_agents_docker.py``) and Kubernetes -(``test_nemo_agents_k8s.py``) modules deploy a real agent **container** through -the nemo-deployments plugin and invoke it through the agents gateway. The end-to --end chain they prove is identical apart from the backend:: +The subprocess, Docker, and Kubernetes modules deploy a real agent through the +agents plugin and invoke it through the agents gateway. The end-to-end chain is +identical apart from the deployment backend and endpoint projection:: - sdk.agents.invoke (gateway proxy, container-mode endpoint resolution) - -> agent container (nat start fastapi) on docker | kubernetes + sdk.agents.invoke (gateway proxy, mode-specific endpoint resolution) + -> NAT or Fabric agent process on subprocess | docker | kubernetes -> Inference Gateway /openai (base_url injected at deploy time) -> mock provider short-circuit (no real upstream / no API key) -> response back through the gateway -This module holds the backend-agnostic core: the mock-provider-backed agent -config, the create -> wait-running -> assert-container-shape -> invoke -> assert -flow, and cleanup. The per-backend modules own only what genuinely differs -(pytest markers, how the deployment image ref is resolved, and best-effort -container cleanup). +This module holds the shared core: the mock-provider-backed agent config, the +create -> wait-running -> assert-endpoint-shape -> invoke -> assert flow, and +cleanup. The per-backend modules own only what genuinely differs (pytest +markers, image resolution, timeouts, and best-effort backend cleanup). """ import time @@ -28,6 +26,7 @@ import httpx import pytest +from nemo_agents_plugin.entities import NAT_WORKFLOW_CONFIG_FORMAT, NEMO_AGENTS_SPEC_CONFIG_FORMAT from nemo_platform import NeMoPlatform from nmp.testing import MockProviderResponse, add_mock_provider @@ -55,8 +54,8 @@ def _chat_completion_response(content: str, model: str) -> dict[str, Any]: } -def _mock_backed_agent_config(model_name: str) -> dict[str, Any]: - """A deterministic single-LLM workflow pointed at the mock model. +def _mock_backed_nat_agent_config(model_name: str) -> dict[str, Any]: + """A deterministic single-LLM NAT workflow pointed at the mock model. ``base_url``/``api_key`` are intentionally omitted so the deployment injects the Inference Gateway URL (and the mock provider short-circuits the call). @@ -75,6 +74,39 @@ def _mock_backed_agent_config(model_name: str) -> dict[str, Any]: } +def _mock_backed_fabric_agent_config(agent_name: str, model_name: str) -> dict[str, Any]: + """A deterministic DeepAgents-backed Fabric agent pointed at the mock model.""" + return { + "config_format": NEMO_AGENTS_SPEC_CONFIG_FORMAT, + "name": agent_name, + "default_harness": "deepagents", + "harnesses": { + "deepagents": { + "kind": "deepagents", + "settings": { + "deepagents": {}, + }, + } + }, + "models": { + "default": { + "provider": "openai", + "model": model_name, + "temperature": 0.0, + } + }, + } + + +def _mock_backed_agent_config(config_format: str, *, agent_name: str, model_name: str) -> dict[str, Any]: + """Return the runtime-valid mock-backed config for ``config_format``.""" + if config_format == NAT_WORKFLOW_CONFIG_FORMAT: + return _mock_backed_nat_agent_config(model_name) + if config_format == NEMO_AGENTS_SPEC_CONFIG_FORMAT: + return _mock_backed_fabric_agent_config(agent_name, model_name) + raise ValueError(f"Unsupported agent config format: {config_format!r}") + + def _page_data(page: Any) -> list[dict[str, Any]]: if isinstance(page, dict): data = page.get("data", []) @@ -158,24 +190,24 @@ def wait_for_deployment_running( pytest.fail(f"Deployment {name!r} did not reach running within {timeout_seconds}s: {last_deployment}") -def run_container_agent_deploy_and_invoke( +def run_agent_deploy_and_invoke( sdk: NeMoPlatform, *, workspace: str, deployment_mode: str, - image: str, + config_format: str = NAT_WORKFLOW_CONFIG_FORMAT, + image: str | None = None, running_timeout_seconds: float = 300, reap_backend_resources: Callable[[str], None] | None = None, ) -> None: - """Deploy a mock-backed agent as a container and invoke it through the gateway. + """Deploy a mock-backed agent and invoke it through the gateway. - Backend-agnostic core shared by the docker and k8s e2e modules: + Shared core for subprocess, Docker, and Kubernetes E2E modules: - 1. Register a mock inference provider + deterministic single-LLM agent. - 2. Deploy it with ``deployment_mode`` (``"docker"`` / ``"k8s"``) from ``image``. - 3. Wait for ``running`` and assert the container-mode endpoint shape (empty - scalar ``endpoint``, populated ``endpoints``) — this guards that the pass - came through the container path, not a subprocess fallback. + 1. Register a mock inference provider + deterministic single-LLM agent in + the requested ``config_format``. + 2. Deploy it using ``deployment_mode`` and the optional container ``image``. + 3. Wait for ``running`` and assert the mode-specific endpoint shape. 4. Invoke through the gateway and assert the mocked completion round-trips. 5. Clean up the deployment and agent (best-effort, isolated steps). @@ -204,7 +236,12 @@ def run_container_agent_deploy_and_invoke( sdk.agents.create( workspace=workspace, name=agent_name, - config=_mock_backed_agent_config(f"{workspace}/{model_name}"), + config=_mock_backed_agent_config( + config_format, + agent_name=agent_name, + model_name=f"{workspace}/{model_name}", + ), + config_format=config_format, ) try: @@ -223,13 +260,19 @@ def run_container_agent_deploy_and_invoke( assert deployment["agent"] == agent_name assert deployment["deployment_mode"] == deployment_mode - # Container-mode addressing: the loopback scalar ``endpoint`` is empty and - # the routable address lives in ``endpoints`` (this is what the gateway's - # container-mode resolution reads). Guarding this shape ensures a pass can - # only come through the container path, not a subprocess fallback. - assert deployment.get("endpoint", "") == "" - endpoints = deployment.get("endpoints") or [] - assert endpoints and endpoints[0]["url"], deployment + if deployment_mode == "subprocess": + assert image is None + assert deployment["endpoint"] + assert not deployment.get("endpoints") + assert deployment["pid"] > 0 + else: + # Container-mode addressing: the loopback scalar ``endpoint`` is + # empty and the routable address lives in ``endpoints``. Guarding + # this shape prevents a container test from passing via fallback. + assert image + assert deployment.get("endpoint", "") == "" + endpoints = deployment.get("endpoints") or [] + assert endpoints and endpoints[0]["url"], deployment sdk.models.wait_for_openai_model(model_name, workspace=workspace) @@ -250,6 +293,28 @@ def run_container_agent_deploy_and_invoke( _safe(delete_agent_if_exists, sdk, workspace=workspace, name=agent_name) +def run_container_agent_deploy_and_invoke( + sdk: NeMoPlatform, + *, + workspace: str, + deployment_mode: str, + image: str, + config_format: str = NAT_WORKFLOW_CONFIG_FORMAT, + running_timeout_seconds: float = 300, + reap_backend_resources: Callable[[str], None] | None = None, +) -> None: + """Deploy a mock-backed container agent and invoke it through the gateway.""" + run_agent_deploy_and_invoke( + sdk, + workspace=workspace, + deployment_mode=deployment_mode, + config_format=config_format, + image=image, + running_timeout_seconds=running_timeout_seconds, + reap_backend_resources=reap_backend_resources, + ) + + def _safe(fn: Any, *args: Any, **kwargs: Any) -> None: try: fn(*args, **kwargs) diff --git a/e2e/test_nemo_agents_subprocess.py b/e2e/test_nemo_agents_subprocess.py new file mode 100644 index 0000000000..9e837acac0 --- /dev/null +++ b/e2e/test_nemo_agents_subprocess.py @@ -0,0 +1,54 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""E2E tests for subprocess-mode agent deployments. + +This module starts NeMo Platform through the subprocess E2E harness, deploys a +real agent as a child process through the agents plugin, and invokes it through +the agents gateway. The end-to-end chain is:: + + sdk.agents.invoke (gateway proxy, subprocess endpoint resolution) + -> NAT or Fabric agent child process + -> Inference Gateway /openai (base_url injected at deploy time) + -> mock provider short-circuit (no real upstream / no API key) + -> response back through the gateway + +Unlike the Docker and Kubernetes modules, this scenario needs neither a +prebuilt image nor an external cluster. The ``subprocess_only`` marker keeps it +in the standard Python E2E job and prevents it from running against an external +Platform, where this module's local harness configuration would be ignored. +""" + +import pytest +from nemo_agents_plugin.entities import NAT_WORKFLOW_CONFIG_FORMAT, NEMO_AGENTS_SPEC_CONFIG_FORMAT +from nemo_platform import NeMoPlatform + +from e2e.agents_deploy_helpers import run_agent_deploy_and_invoke + +pytestmark = [ + pytest.mark.subprocess_only, + pytest.mark.e2e_config( + "e2e/configs/local-subprocess.yaml", + harness={"backend": "subprocess"}, + ), +] + + +def test_nat_agent_deploys_and_invokes_through_gateway(sdk: NeMoPlatform, workspace: str) -> None: + """Deploy a NAT agent as a subprocess and invoke it through the gateway.""" + run_agent_deploy_and_invoke( + sdk, + workspace=workspace, + deployment_mode="subprocess", + config_format=NAT_WORKFLOW_CONFIG_FORMAT, + ) + + +def test_fabric_agent_deploys_and_invokes_through_gateway(sdk: NeMoPlatform, workspace: str) -> None: + """Deploy a Fabric-backed agent as a subprocess and invoke it through the gateway.""" + run_agent_deploy_and_invoke( + sdk, + workspace=workspace, + deployment_mode="subprocess", + config_format=NEMO_AGENTS_SPEC_CONFIG_FORMAT, + ) From dc88cef28dd7dde334b4155690a1e6cbf51be736 Mon Sep 17 00:00:00 2001 From: Manjesh Mogallapalli Date: Fri, 7 Aug 2026 10:09:54 -0500 Subject: [PATCH 2/3] docker tests Signed-off-by: Manjesh Mogallapalli --- e2e/test_nemo_agents_docker.py | 55 +++++++++++-------- .../runner/fabric_artifact_staging.py | 5 +- .../unit/test_fabric_artifact_staging.py | 25 +++++++++ 3 files changed, 60 insertions(+), 25 deletions(-) diff --git a/e2e/test_nemo_agents_docker.py b/e2e/test_nemo_agents_docker.py index 4f60bea480..92489cb6c9 100644 --- a/e2e/test_nemo_agents_docker.py +++ b/e2e/test_nemo_agents_docker.py @@ -7,10 +7,11 @@ with the Kubernetes variant (``test_nemo_agents_k8s.py``) via ``e2e.agents_deploy_helpers``; this module owns only the docker-specific wiring. -What it proves — the container-mode chain end to end:: +What it proves — the container-mode chain end to end for both supported agent +config formats:: sdk.agents.invoke (gateway proxy, container-mode endpoint resolution) - -> docker agent container (nat start fastapi) + -> docker agent container (NAT or Fabric/DeepAgents server) -> Inference Gateway /openai (base_url injected at deploy time) -> mock provider short-circuit (no real upstream / no API key) -> response back through the gateway @@ -18,35 +19,34 @@ How it runs, and where: - The deployed agent runs from the platform's own ``nmp-api`` image, which - already ships the NAT runtime (``nvidia-nat-core`` / ``nvidia-nat-langchain``, - via ``nemo-agents-plugin``) — so ``nat`` is on ``PATH`` and the - ``chat_completion`` workflow / ``openai`` LLM the agent uses resolve. The - deployments docker executor overrides the image entrypoint with - ``nat start fastapi``, so the image's own entrypoint is irrelevant. No - agent-specific image is built. + already ships both the NAT runtime and Fabric/DeepAgents runtime. The + deployments docker executor overrides the image entrypoint with the server + for the selected config format, so the image's own entrypoint is irrelevant. + No agent-specific image is built. - The image is supplied prebuilt via ``NMP_E2E_IMAGE_REGISTRY`` / ``NMP_E2E_IMAGE_TAG`` (the existing e2e convention). Its dedicated CI job (``python-e2e-image-test``) builds/pulls ``nmp-api`` and sets these; the ``needs_nmp_api_image`` marker skips the test everywhere they are unset (the plain subprocess e2e job, the kind cluster job, and local runs without them). - The agent is registered with a deterministic single-LLM ``chat_completion`` - workflow served by the e2e mock inference provider, so no ``NVIDIA_API_KEY`` - or model egress is needed; we assert the exact mocked completion round-trips. + config served by the e2e mock inference provider, so no ``NVIDIA_API_KEY`` or + model egress is needed; we assert the exact mocked completion round-trips. - The platform runs as a normal local process (subprocess harness). The ``container_base_url_host`` harness option makes the harness bind the platform on all interfaces (``--host 0.0.0.0``) and rewrite ``platform.base_url`` to the docker bridge address; the runner seeds ``NMP_BASE_URL`` from that host paired with the actual bind port, so the Inference Gateway URL injected into the agent container is reachable from *inside* the container while the platform's own - in-process clients still reach it. This requires the Linux docker bridge, which - is reachable from both the container and the host process; Docker Desktop's - host alias is not host-resolvable, so the module skips on non-Linux. + in-process clients still reach it. This requires the Linux docker bridge, + which is reachable from both the container and the host process; Docker + Desktop's host alias is not host-resolvable, so the module skips on non-Linux. """ import os import platform import pytest +from nemo_agents_plugin.entities import NAT_WORKFLOW_CONFIG_FORMAT, NEMO_AGENTS_SPEC_CONFIG_FORMAT from nemo_platform import NeMoPlatform from e2e.agents_deploy_helpers import run_container_agent_deploy_and_invoke @@ -59,8 +59,8 @@ _DOCKER_BRIDGE_HOST = "172.17.0.1" # Platform image name to deploy the agent from. The nmp-api image already ships -# the NAT runtime and the agent components (see module docstring), so it doubles -# as the agent runtime image. Registry and tag come from NMP_E2E_IMAGE_REGISTRY / +# the NAT and Fabric/DeepAgents runtimes (see module docstring), so it doubles as +# the agent runtime image. Registry and tag come from NMP_E2E_IMAGE_REGISTRY / # NMP_E2E_IMAGE_TAG (the existing e2e image convention). _AGENT_IMAGE_NAME = "nmp-api" @@ -97,7 +97,7 @@ def agent_deployment_image() -> str: Composed from the e2e image convention (``NMP_E2E_IMAGE_REGISTRY`` / ``NMP_E2E_IMAGE_TAG``) as ``{registry}/nmp-api:{tag}``. The nmp-api image - already ships the NAT runtime and agent components, so no agent-specific + already ships the NAT and Fabric/DeepAgents runtimes, so no agent-specific image is built here. The ``needs_nmp_api_image`` marker guarantees both env vars are set before this test runs; assert defensively. """ @@ -131,20 +131,29 @@ def _remove_agent_container_if_present(deployment_name: str) -> None: pass -def test_docker_agent_deploys_and_invokes_through_gateway( +def test_nat_docker_agent_deploys_and_invokes_through_gateway( sdk: NeMoPlatform, workspace: str, agent_deployment_image: str ) -> None: - """Deploy an agent as a docker container from the nmp-api image and invoke it. + """Deploy a NAT agent as a docker container and invoke it through the gateway.""" + run_container_agent_deploy_and_invoke( + sdk, + workspace=workspace, + deployment_mode="docker", + image=agent_deployment_image, + config_format=NAT_WORKFLOW_CONFIG_FORMAT, + reap_backend_resources=_remove_agent_container_if_present, + ) - Asserts the deployment reaches ``running`` with the container-mode endpoint - shape (empty scalar ``endpoint``, populated ``endpoints``), then invokes - through the gateway and asserts the mocked completion round-trips from inside - the container back to the caller. - """ + +def test_fabric_docker_agent_deploys_and_invokes_through_gateway( + sdk: NeMoPlatform, workspace: str, agent_deployment_image: str +) -> None: + """Deploy a Fabric/DeepAgents agent as a docker container and invoke it through the gateway.""" run_container_agent_deploy_and_invoke( sdk, workspace=workspace, deployment_mode="docker", image=agent_deployment_image, + config_format=NEMO_AGENTS_SPEC_CONFIG_FORMAT, reap_backend_resources=_remove_agent_container_if_present, ) diff --git a/plugins/nemo-agents/src/nemo_agents_plugin/runner/fabric_artifact_staging.py b/plugins/nemo-agents/src/nemo_agents_plugin/runner/fabric_artifact_staging.py index 5ededa0877..b59b7a1e86 100644 --- a/plugins/nemo-agents/src/nemo_agents_plugin/runner/fabric_artifact_staging.py +++ b/plugins/nemo-agents/src/nemo_agents_plugin/runner/fabric_artifact_staging.py @@ -18,7 +18,8 @@ agent_spec_fileset_name, ) from nemo_deployments_plugin.entities import ConfigFile -from nemo_platform import NotFoundError +from nemo_platform import NotFoundError as PlatformNotFoundError +from nemo_platform_plugin.client.errors import NotFoundError as PluginClientNotFoundError logger = logging.getLogger(__name__) @@ -69,7 +70,7 @@ async def stage_fabric_spec_config_files( _validate_referenced_skill_paths(rewritten_agent_config, config_files, agent_yaml_path) _validate_staged_size(config_files, fileset_name) return config_files - except (FileNotFoundError, NotFoundError) as exc: + except (FileNotFoundError, PlatformNotFoundError, PluginClientNotFoundError) as exc: logger.info( "Agent spec fileset %s/%s unavailable (%s); using inline agent.yaml only", workspace, diff --git a/plugins/nemo-agents/tests/unit/test_fabric_artifact_staging.py b/plugins/nemo-agents/tests/unit/test_fabric_artifact_staging.py index 10827eb7a8..eeb86b4c89 100644 --- a/plugins/nemo-agents/tests/unit/test_fabric_artifact_staging.py +++ b/plugins/nemo-agents/tests/unit/test_fabric_artifact_staging.py @@ -9,6 +9,7 @@ from typing import Any from unittest.mock import AsyncMock, MagicMock +import httpx import pytest import yaml from nemo_agents_plugin.runner.fabric_artifact_staging import ( @@ -17,6 +18,7 @@ ) from nemo_deployments_plugin.entities import ConfigFile from nemo_platform import NotFoundError +from nemo_platform_plugin.client.errors import NotFoundError as PluginClientNotFoundError def _fabric_config(*, skills_paths: list[str] | None = None) -> dict[str, Any]: @@ -93,6 +95,29 @@ async def test_stage_fabric_spec_config_files_not_found_error_falls_back() -> No assert result[0].path == "/workspace/agent.yaml" +@pytest.mark.asyncio +async def test_stage_fabric_spec_config_files_plugin_client_not_found_error_falls_back() -> None: + config = _fabric_config() + sdk = AsyncMock() + response = httpx.Response( + 404, + request=httpx.Request("GET", "http://platform/filesets/fabric-agent-spec"), + json={"detail": "Fileset not found"}, + ) + sdk.download = AsyncMock(side_effect=PluginClientNotFoundError(response)) + + result = await stage_fabric_spec_config_files( + workspace="default", + agent_name="fabric-agent", + rewritten_agent_config=config, + agent_yaml_path="/workspace/agent.yaml", + sdk=sdk, + ) + + assert len(result) == 1 + assert result[0].path == "/workspace/agent.yaml" + + @pytest.mark.asyncio async def test_stage_fabric_spec_config_files_stages_sibling_artifacts() -> None: async def _fake_download(*, local_path: str, fileset: str | None, workspace: str | None) -> None: From 5a51504820e3187e5d3b75c259d611721ea72b36 Mon Sep 17 00:00:00 2001 From: Manjesh Mogallapalli Date: Fri, 7 Aug 2026 11:27:26 -0500 Subject: [PATCH 3/3] k8s tests Signed-off-by: Manjesh Mogallapalli --- e2e/test_nemo_agents_k8s.py | 48 ++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/e2e/test_nemo_agents_k8s.py b/e2e/test_nemo_agents_k8s.py index 9fc0fa012f..00b158edc8 100644 --- a/e2e/test_nemo_agents_k8s.py +++ b/e2e/test_nemo_agents_k8s.py @@ -6,10 +6,11 @@ deploy/invoke/assert core is shared via ``e2e.agents_deploy_helpers``; this module owns only the k8s-specific wiring. -What it proves — the container-mode chain end to end, on Kubernetes:: +What it proves — the container-mode chain end to end for both supported agent +config formats, on Kubernetes:: sdk.agents.invoke (gateway proxy, container-mode endpoint resolution) - -> k8s agent pod (nat start fastapi), fronted by a ClusterIP Service + -> k8s agent pod (NAT or Fabric/DeepAgents server), fronted by a ClusterIP Service -> Inference Gateway /openai (base_url injected at deploy time) -> mock provider short-circuit (no real upstream / no API key) -> response back through the gateway @@ -22,18 +23,19 @@ The ``container_only`` marker skips it for the subprocess harness (local / plain e2e job), which is the inverse of the docker module's ``subprocess_only``. - The agent runs from the platform's own ``nmp-api`` image, which already ships - the NAT runtime and agent components (see the docker module docstring). The - deployments k8s executor overrides the image entrypoint with ``nat start - fastapi``. In CI the image is pre-pulled into the kind nodes and referenced by - its commit-SHA tag, so the pod's default ``imagePullPolicy: IfNotPresent`` uses - the node-local image (the k8s backend does not use image pull secrets). + both the NAT runtime and Fabric/DeepAgents runtime (see the docker module + docstring). The deployments k8s executor overrides the image entrypoint with + the server for the selected config format. In CI the image is pre-pulled into + the kind nodes and referenced by its commit-SHA tag, so the pod's default + ``imagePullPolicy: IfNotPresent`` uses the node-local image (the k8s backend + does not use image pull secrets). - The image ref is composed from ``NMP_E2E_IMAGE_REGISTRY`` / ``NMP_E2E_IMAGE_TAG`` (the existing e2e image convention). The ``needs_nmp_api_image`` marker skips the test unless both are set; the Kind CPU e2e job exports them from the built image outputs. -- The agent is registered with a deterministic single-LLM ``chat_completion`` - workflow served by the e2e mock inference provider, so no ``NVIDIA_API_KEY`` - or model egress is needed; we assert the exact mocked completion round-trips. +- The agent is registered with a deterministic single-LLM config served by the + e2e mock inference provider, so no ``NVIDIA_API_KEY`` or model egress is + needed; we assert the exact mocked completion round-trips. - Gateway reachability is in-cluster: the agent Deployment/Service land in the same namespace as the platform, and the Inference Gateway URL injected into the agent pod is the platform's own in-cluster ``NMP_BASE_URL``, reachable pod @@ -44,6 +46,7 @@ import os import pytest +from nemo_agents_plugin.entities import NAT_WORKFLOW_CONFIG_FORMAT, NEMO_AGENTS_SPEC_CONFIG_FORMAT from nemo_platform import NeMoPlatform from e2e.agents_deploy_helpers import run_container_agent_deploy_and_invoke @@ -81,21 +84,32 @@ def agent_deployment_image() -> str: return f"{registry.rstrip('/')}/{_AGENT_IMAGE_NAME}:{tag}" -def test_k8s_agent_deploys_and_invokes_through_gateway( +def test_nat_k8s_agent_deploys_and_invokes_through_gateway( sdk: NeMoPlatform, workspace: str, agent_deployment_image: str ) -> None: - """Deploy an agent as a k8s Deployment+Service from nmp-api and invoke it. + """Deploy a NAT agent as a k8s Deployment+Service and invoke it through the gateway.""" + run_container_agent_deploy_and_invoke( + sdk, + workspace=workspace, + deployment_mode="k8s", + image=agent_deployment_image, + config_format=NAT_WORKFLOW_CONFIG_FORMAT, + # Pod scheduling + (node-local) image resolution can take longer than the + # docker path's local container start. + running_timeout_seconds=420, + ) - Asserts the deployment reaches ``running`` with the container-mode endpoint - shape (empty scalar ``endpoint``, populated ``endpoints`` carrying the - in-cluster Service address), then invokes through the gateway and asserts the - mocked completion round-trips from inside the pod back to the caller. - """ + +def test_fabric_k8s_agent_deploys_and_invokes_through_gateway( + sdk: NeMoPlatform, workspace: str, agent_deployment_image: str +) -> None: + """Deploy a Fabric/DeepAgents agent as a k8s Deployment+Service and invoke it through the gateway.""" run_container_agent_deploy_and_invoke( sdk, workspace=workspace, deployment_mode="k8s", image=agent_deployment_image, + config_format=NEMO_AGENTS_SPEC_CONFIG_FORMAT, # Pod scheduling + (node-local) image resolution can take longer than the # docker path's local container start. running_timeout_seconds=420,