Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
1 change: 1 addition & 0 deletions packages/nemo_platform/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ nemo-agents-plugin = [
"botocore>=1.40.46,<1.40.62",
"httpx>=0.27",
"nemo-fabric>=0.1.0a20260717,<0.2.0",
"nemo-fabric-runtime>=0.1.0a20260717,<0.2.0; sys_platform == 'linux'",
"pyyaml>=6.0",
"anthropic>=0.88.0",
"rich>=13.7.1",
Expand Down
2 changes: 2 additions & 0 deletions plugins/nemo-agents/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ dependencies = [
# TODO(AIRCORE-897): Move this to a stable Fabric version before release once available.
# TODO(AIRCORE-897): Add the `relay` extra once nemo-evaluator-sdk's nemo-relay pin allows >=0.5.
"nemo-fabric>=0.1.0a20260717,<0.2.0",
# TODO(AIRCORE-902): Remove the Linux marker once Fabric runtime publishes macOS wheels.
"nemo-fabric-runtime>=0.1.0a20260717,<0.2.0; sys_platform == 'linux'",
"pyyaml>=6.0",
# improvement/ subpackage — agent-improvement workflow (POC).
"anthropic>=0.88.0",
Expand Down
29 changes: 26 additions & 3 deletions plugins/nemo-agents/src/nemo_agents_plugin/api/v2/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,19 @@
from __future__ import annotations

import logging
from typing import Any

from fastapi import APIRouter, Depends, HTTPException, Query
from nemo_agents_plugin.agent_config import AgentConfig
from nemo_agents_plugin.api.v2._perms import AgentPerms
from nemo_agents_plugin.api.v2.dependencies import get_entity_client
from nemo_agents_plugin.authz import scope
from nemo_agents_plugin.entities import Agent, AgentDeployment
from nemo_agents_plugin.entities import (
NAT_WORKFLOW_CONFIG_FORMAT,
NEMO_AGENTS_SPEC_CONFIG_FORMAT,
Agent,
AgentDeployment,
)
from nemo_agents_plugin.schema import (
AgentFilter,
AgentPage,
Expand All @@ -26,6 +33,7 @@
from nemo_platform_plugin.authz import CallerKind, path_rule
from nemo_platform_plugin.entity_client import NemoEntitiesClient, NemoEntityConflictError, NemoEntityNotFoundError
from nemo_platform_plugin.schema import PaginationData
from pydantic import ValidationError

# Deployment statuses that block agent deletion.
# "failed" and "deleting" are excluded — they are terminal/in-cleanup and
Expand All @@ -50,12 +58,14 @@ async def create_agent(
body: CreateAgentRequest,
entity_client: NemoEntitiesClient = Depends(get_entity_client),
) -> Agent:
"""Create a new agent from a NAT workflow config."""
"""Create a new agent from an agent config."""
config = _validate_agent_config_for_create(body)

agent = Agent(
name=body.name,
workspace=workspace,
description=body.description,
config=body.config,
config=config,
config_format=body.config_format,
)
try:
Expand Down Expand Up @@ -179,3 +189,16 @@ async def delete_agent(
except Exception as exc:
logger.exception("Failed to delete agent '%s'", name)
raise HTTPException(status_code=500, detail="Failed to delete agent.") from exc


def _validate_agent_config_for_create(body: CreateAgentRequest) -> dict[str, Any]:
if body.config_format == NAT_WORKFLOW_CONFIG_FORMAT:
return body.config

if body.config_format == NEMO_AGENTS_SPEC_CONFIG_FORMAT:
try:
return AgentConfig.model_validate(body.config).model_dump(exclude_none=True)
except ValidationError as exc:
raise HTTPException(status_code=400, detail=f"Invalid agent config: {exc}") from exc

raise HTTPException(status_code=400, detail=f"Unsupported config_format {body.config_format!r}.")
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,20 @@

import logging
import secrets
from typing import Any

from fastapi import APIRouter, Depends, HTTPException, Query
from nemo_agents_plugin.agent_config import AgentConfig
from nemo_agents_plugin.api.v2._perms import DeploymentPerms
from nemo_agents_plugin.api.v2.dependencies import get_entity_client
from nemo_agents_plugin.authz import scope
from nemo_agents_plugin.entities import Agent, AgentDeployment, is_container_deployment_mode
from nemo_agents_plugin.entities import (
NAT_WORKFLOW_CONFIG_FORMAT,
NEMO_AGENTS_SPEC_CONFIG_FORMAT,
Agent,
AgentDeployment,
is_container_deployment_mode,
)
from nemo_agents_plugin.schema import (
CreateDeploymentRequest,
DeploymentFilter,
Expand All @@ -34,6 +42,7 @@
from nemo_platform_plugin.authz import CallerKind, path_rule
from nemo_platform_plugin.entity_client import NemoEntitiesClient, NemoEntityConflictError, NemoEntityNotFoundError
from nemo_platform_plugin.schema import PaginationData
from pydantic import ValidationError

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -73,10 +82,9 @@ async def create_deployment(
# 2. Build deployment name (auto-generate if not provided)
deployment_name = body.name or f"{body.agent}-{secrets.token_hex(4)}"

# 3. Deep-copy config and inject IGW URL, telemetry fields, and default model.
resolved_config = inject_gateway_url(agent.config, workspace)
resolved_config = inject_default_model(resolved_config)
inject_nemo_trace_fields(resolved_config, workspace=workspace, agent_name=body.agent)
# 3. Resolve deployment-time config. NAT workflows need legacy injection;
# Platform-owned agent specs stay strict and are translated by the runner.
resolved_config = _resolve_deployment_config(agent, workspace=workspace)

# 4. Create the entity with status "pending"
deployment = AgentDeployment(
Expand All @@ -103,6 +111,22 @@ async def create_deployment(
return saved


def _resolve_deployment_config(agent: Agent, *, workspace: str) -> dict[str, Any]:
Comment thread
mmogallapalli marked this conversation as resolved.
if agent.config_format == NAT_WORKFLOW_CONFIG_FORMAT:
resolved_config = inject_gateway_url(agent.config, workspace)
resolved_config = inject_default_model(resolved_config)
inject_nemo_trace_fields(resolved_config, workspace=workspace, agent_name=agent.name)
return resolved_config

if agent.config_format == NEMO_AGENTS_SPEC_CONFIG_FORMAT:
try:
return AgentConfig.model_validate(agent.config).model_dump(exclude_none=True)
except ValidationError as exc:
raise HTTPException(status_code=400, detail=f"Invalid agent config: {exc}") from exc

raise HTTPException(status_code=400, detail=f"Unsupported config_format {agent.config_format!r}.")


@router.get("/deployments", response_model=DeploymentPage, tags=["Agent Deployments"])
@scope.read
@path_rule(
Expand Down
Loading
Loading