Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
206 changes: 117 additions & 89 deletions e2e/test_entities.py

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

"""Typed HTTP clients for the Agents service.

Wraps the endpoint functions from ``agents.endpoints`` as direct methods
using the ``method()`` descriptor, following the files/models pattern.
"""

from nemo_platform_plugin.agents import endpoints
from nemo_platform_plugin.client.client import AsyncNemoClient, NemoClient
from nemo_platform_plugin.client.method import method


class _AgentsMethods:
get_agent = method(endpoints.get_agent)
list_agents = method(endpoints.list_agents)
create_agent = method(endpoints.create_agent)
delete_agent = method(endpoints.delete_agent)
get_deployment = method(endpoints.get_deployment)
list_deployments = method(endpoints.list_deployments)
create_deployment = method(endpoints.create_deployment)
delete_deployment = method(endpoints.delete_deployment)
invoke_agent = method(endpoints.invoke_agent)
invoke_deployment = method(endpoints.invoke_deployment)


class AgentsClient(_AgentsMethods, NemoClient):
"""Sync client for the Agents service API."""


class AsyncAgentsClient(_AgentsMethods, AsyncNemoClient):
"""Async client for the Agents service API."""
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

"""Typed endpoint definitions for the Agents service.

Single source of truth for the HTTP contract. Replaces the Stainless-generated
agent resource from ``nemo_agents_plugin.sdk``.
"""

from __future__ import annotations

from abc import abstractmethod
from typing import Any

from nemo_platform_plugin.agents.types import (
Agent,
AgentDeployment,
CreateAgentDeploymentRequest,
CreateAgentRequest,
InvokeAgentRequest,
ListAgentsQueryParams,
ListDeploymentsQueryParams,
)
from nemo_platform_plugin.client.endpoint import delete, get, post
from nemo_platform_plugin.client.types import Paginated, PreparedRequest

_AGENTS = "/apis/agents/v2/workspaces/{workspace}/agents"
_DEPLOYMENTS = "/apis/agents/v2/workspaces/{workspace}/deployments"


# ---------------------------------------------------------------------------
# Agent CRUD
# ---------------------------------------------------------------------------


@get(f"{_AGENTS}/{{name}}")
@abstractmethod
def get_agent(*, workspace: str | None = None, name: str) -> Agent: ...


@get(_AGENTS)
@abstractmethod
def list_agents(
*, workspace: str | None = None, query_params: ListAgentsQueryParams | None = None
) -> Paginated[Agent]: ...


def _get_agent_on_conflict(body: CreateAgentRequest, workspace: str | None) -> PreparedRequest[Agent]:
"""Build the retrieve request replayed when ``create_agent(exist_ok=True)`` 409s."""
return get_agent(name=body.name, workspace=workspace)


@post(_AGENTS, get_on_conflict=_get_agent_on_conflict)
@abstractmethod
def create_agent(*, workspace: str | None = None, body: CreateAgentRequest, exist_ok: bool = False) -> Agent: ...


@delete(f"{_AGENTS}/{{name}}")
@abstractmethod
def delete_agent(*, workspace: str | None = None, name: str) -> None: ...


# ---------------------------------------------------------------------------
# Agent deployment CRUD
# ---------------------------------------------------------------------------


@get(f"{_DEPLOYMENTS}/{{name}}")
@abstractmethod
def get_deployment(*, workspace: str | None = None, name: str) -> AgentDeployment: ...


@get(_DEPLOYMENTS)
@abstractmethod
def list_deployments(
*, workspace: str | None = None, query_params: ListDeploymentsQueryParams | None = None
) -> Paginated[AgentDeployment]: ...


@post(_DEPLOYMENTS)
@abstractmethod
def create_deployment(*, workspace: str | None = None, body: CreateAgentDeploymentRequest) -> AgentDeployment: ...


@delete(f"{_DEPLOYMENTS}/{{name}}")
@abstractmethod
def delete_deployment(*, workspace: str | None = None, name: str) -> None: ...


# ---------------------------------------------------------------------------
# Agent invocation (gateway proxy to OpenAI-compatible endpoint)
# ---------------------------------------------------------------------------


@post(f"{_AGENTS}/{{name}}/-/v1/chat/completions")
@abstractmethod
def invoke_agent(*, workspace: str | None = None, name: str, body: InvokeAgentRequest) -> dict[str, Any]: ...


@post(f"{_DEPLOYMENTS}/{{name}}/-/v1/chat/completions")
@abstractmethod
def invoke_deployment(*, workspace: str | None = None, name: str, body: InvokeAgentRequest) -> dict[str, Any]: ...
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

"""Shared request/response types for the Agents service.

Single source of truth for the HTTP contract. Replaces the Stainless-generated
agent resource from ``nemo_agents_plugin.sdk``.
"""

from __future__ import annotations

from datetime import datetime
from typing import Any, NotRequired, TypedDict

from nemo_platform_plugin.schema import Page
from pydantic import BaseModel, ConfigDict, Field

# ---------------------------------------------------------------------------
# Response types
# ---------------------------------------------------------------------------


class Agent(BaseModel):
"""An agent definition — stores agent config and metadata."""

model_config = ConfigDict(extra="allow")

name: str = ""
workspace: str = ""
project: str | None = None
description: str = ""
config: dict[str, Any] = Field(default_factory=dict)
config_format: str = "nat-workflow-v1"
id: str | None = None
created_at: datetime | None = None
created_by: str | None = None
updated_at: datetime | None = None
updated_by: str | None = None


AgentPage = Page[Agent]


class AgentDeployment(BaseModel):
"""A running (or pending) deployment of an Agent."""

model_config = ConfigDict(extra="allow")

name: str = ""
workspace: str = ""
project: str | None = None
agent: str = ""
config: dict[str, Any] = Field(default_factory=dict)
status: str = "pending"
deployment_mode: str = "subprocess"
endpoint: str = ""
id: str | None = None
created_at: datetime | None = None
updated_at: datetime | None = None


DeploymentPage = Page[AgentDeployment]


# ---------------------------------------------------------------------------
# Request types
# ---------------------------------------------------------------------------


class CreateAgentRequest(BaseModel):
name: str
description: str = ""
config: dict[str, Any] = Field(default_factory=dict)
config_format: str = "nat-workflow-v1"


class CreateAgentDeploymentRequest(BaseModel):
agent: str
config: dict[str, Any] = Field(default_factory=dict)


class InvokeAgentRequest(BaseModel):
"""OpenAI chat-completions request body for agent invocation."""

model_config = ConfigDict(extra="allow")

messages: list[dict[str, Any]] = Field(default_factory=list)
stream: bool = False


# ---------------------------------------------------------------------------
# Query parameter types
# ---------------------------------------------------------------------------


class ListAgentsQueryParams(TypedDict, total=False):
page: NotRequired[int]
page_size: NotRequired[int]
sort: NotRequired[str]
filter: NotRequired[str]


class ListDeploymentsQueryParams(TypedDict, total=False):
page: NotRequired[int]
page_size: NotRequired[int]
sort: NotRequired[str]
filter: NotRequired[str]
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

"""Typed HTTP clients for the Auditor service.

Wraps the endpoint functions from ``auditor.endpoints`` as direct methods
using the ``method()`` descriptor, following the files/models pattern.
"""

from nemo_platform_plugin.auditor import endpoints
from nemo_platform_plugin.client.client import AsyncNemoClient, NemoClient
from nemo_platform_plugin.client.method import method


class _AuditorMethods:
get_audit_config = method(endpoints.get_audit_config)
list_audit_configs = method(endpoints.list_audit_configs)
create_audit_config = method(endpoints.create_audit_config)
update_audit_config = method(endpoints.update_audit_config)
delete_audit_config = method(endpoints.delete_audit_config)
get_audit_target = method(endpoints.get_audit_target)
list_audit_targets = method(endpoints.list_audit_targets)
create_audit_target = method(endpoints.create_audit_target)
update_audit_target = method(endpoints.update_audit_target)
delete_audit_target = method(endpoints.delete_audit_target)
submit_audit = method(endpoints.submit_audit)
list_audit_jobs = method(endpoints.list_audit_jobs)
get_audit_job = method(endpoints.get_audit_job)


class AuditorClient(_AuditorMethods, NemoClient):
"""Sync client for the Auditor service API."""


class AsyncAuditorClient(_AuditorMethods, AsyncNemoClient):
"""Async client for the Auditor service API."""
Loading