Skip to content
Draft
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
12 changes: 12 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,18 @@ plugins/nemo-agents/vendor/codex_agent_adapter/
its own LICENSE.md in:
plugins/nemo-agents/vendor/codex_agent_adapter/

--------------------------------------------------------------------------------
plugins/nemo-prompt-master/src/nemo_prompt_master_plugin/skills/prompt-master/
Prompt Master v1.8.0 vendored snapshot at commit
2bd92518e26bf659e21e3d9ab90573fcf3ddeccb
Copyright (c) 2026 Nidhin Joseph Nelson
Home: https://github.com/nidhinjs/prompt-master
License: MIT

The `nemo-prompt-master` plugin vendors the upstream skill and reference
files without modification. The vendored snapshot retains its own LICENSE
and provenance in the skill directory.

--------------------------------------------------------------------------------
services/guardrails/callouts/internal/server/
callout_server.go and callout_server_test.go
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,13 @@ nemo agents evaluate \
```bash
BUNDLE="$(pwd)/plugins/nemo-agents/examples/calculator-agent/src/calculator_agent"
nemo agents optimize prepare-fileset \
--strategy hpo \
--source "$BUNDLE" \
--optimize-config calculator-optimize.yml \
--fileset calculator-optimize \
--agent calculator-agent
nemo agents optimize \
--strategy hpo \
--optimize-config-fileset default/calculator-optimize \
--optimize-config calculator-optimize.yml \
--agent calculator-agent
Expand Down
17 changes: 17 additions & 0 deletions packages/nemo_platform/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,16 @@ nemo-platform-sdk = [
"sniffio",
]

# Generated from [tool.bundle-package]; do not edit by hand.
nemo-prompt-master-plugin = [
"nemo-agents-plugin",
"nemo-optimization-plugin",
"nemo-platform-plugin",
"pydantic>=2.12.0",
"pyyaml>=6.0.3",
"typer>=0.20.0",
]

# Generated from [tool.bundle-package]; do not edit by hand.
nemo-rl-plugin = [
"nemo-platform-plugin",
Expand Down Expand Up @@ -561,6 +571,7 @@ plugins = [
"nemo-platform[nemo-guardrails-plugin]",
"nemo-platform[nemo-insights-plugin]",
"nemo-platform[nemo-optimization-plugin]",
"nemo-platform[nemo-prompt-master-plugin]",
"nemo-platform[nemo-rl-plugin]",
"nemo-platform[nemo-safe-synthesizer-plugin]",
"nemo-platform[nemo-switchyard]",
Expand Down Expand Up @@ -745,6 +756,10 @@ nemo-switchyard = "nemo_switchyard.middleware:SwitchyardMiddleware"
optuna = "nemo_optimization.backends.optuna.backend:OptunaBackend"
ga = "nemo_optimization.backends.ga.backend:GaBackend"

# Generated from [tool.bundle-package]; do not edit this table by hand.
[project.entry-points."nemo.optimization.strategies"]
prompt-master = "nemo_prompt_master_plugin.strategy:PromptMasterStrategy"

# Generated from [tool.bundle-package]; do not edit this table by hand.
[project.entry-points."nemo.sandbox_profiles"]
openshell = "nemo_deployments_plugin.backends.openshell.sandbox_profile:PROFILE"
Expand Down Expand Up @@ -794,6 +809,7 @@ experimentalist = "nemo_experimentalist_plugin.skills:skills_dir"
insights = "nemo_insights_plugin.skills:skills_dir"
deployments = "nemo_deployments_plugin.skills:skills_dir"
customizer = "nemo_customizer.skills:get_skills_path"
prompt-master = "nemo_prompt_master_plugin.skills:skills_dir"

[tool.uv.sources]
nemo-platform-sdk = { workspace = true }
Expand Down Expand Up @@ -849,6 +865,7 @@ switchyard-vendored = { source = "../../plugins/nemo-switchyard/vendor/switchyar

# Default first-party plugins
nemo-agents-plugin = { source = "../../plugins/nemo-agents/src/nemo_agents_plugin", module = "nemo_agents_plugin", inherit = { "entry-points" = ["nemo.*", "nat.plugins", "nat.components"] }, force_include = { "../../vendor/claude_code_agent_adapter/src/nat_claude_code_agent_adapter" = "nat_claude_code_agent_adapter", "../../vendor/codex_agent_adapter/src/nat_codex_agent_adapter" = "nat_codex_agent_adapter", "../../vendor/cursor_agent_adapter/src/nat_cursor_agent_adapter" = "nat_cursor_agent_adapter", "../../vendor/hermes_agent_adapter/src/nat_hermes_agent_adapter" = "nat_hermes_agent_adapter", "../../vendor/openclaw_agent_adapter/src/nat_openclaw_agent_adapter" = "nat_openclaw_agent_adapter" } }
nemo-prompt-master-plugin = { source = "../../plugins/nemo-prompt-master/src/nemo_prompt_master_plugin", module = "nemo_prompt_master_plugin", inherit = { "entry-points" = ["nemo.*"] } }
nemo-anonymizer-plugin = { source = "../../plugins/nemo-anonymizer/src/nemo_anonymizer_plugin", module = "nemo_anonymizer_plugin", inherit = { "entry-points" = ["nemo.*"] } }
nemo-auditor-plugin = { source = "../../plugins/nemo-auditor/src/nemo_auditor", module = "nemo_auditor", inherit = { "entry-points" = ["nemo.*"] } }
# Customization router plus its backend contributors. Heavy ML stacks stay in the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
# Usage:
#
# nemo agents optimize prepare-fileset \
# --strategy hpo \
# --source plugins/nemo-agents/examples/calculator-agent/src/calculator_agent \
# --optimize-config calculator-optimize.yml \
# --fileset calculator-optimize
#
# nemo agents optimize \
# --strategy hpo \
# --optimize-config-fileset default/calculator-optimize \
# --optimize-config calculator-optimize.yml \
# --agent calculator-agent
Expand Down
2 changes: 2 additions & 0 deletions plugins/nemo-agents/examples/react-agent/react-optimize.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
# Usage:
#
# nemo agents optimize prepare-fileset \
# --strategy hpo \
# --source plugins/nemo-agents/examples/react-agent \
# --optimize-config react-optimize.yml \
# --fileset react-optimize
#
# nemo agents optimize \
# --strategy hpo \
# --optimize-config-fileset default/react-optimize \
# --optimize-config react-optimize.yml \
# --agent react-agent
Expand Down
71 changes: 48 additions & 23 deletions plugins/nemo-agents/openapi/openapi.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 55 additions & 0 deletions plugins/nemo-agents/src/nemo_agents_plugin/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,8 @@ def _replace_flat_job_command_with_group(
def submit(typer_ctx: typer.Context, **kwargs: object) -> None:
if typer_ctx.invoked_subcommand is not None:
return
if _run_local_prompt_master(job_cls, kwargs):
return
original(typer_ctx, **kwargs)

submit.__doc__ = getattr(original, "__doc__", None)
Expand All @@ -336,6 +338,59 @@ def submit(typer_ctx: typer.Context, **kwargs: object) -> None:
app.add_typer(job_group, name=job_cls.name, rich_help_panel="Jobs")


def _run_local_prompt_master(job_cls: type[NemoJob], kwargs: dict[str, object]) -> bool:
"""Run Prompt Master in-process when ``--agent`` names a local YAML file."""
if kwargs.get("strategy") != "prompt-master":
return False

agent = kwargs.get("agent")
if not isinstance(agent, str):
return False
agent_path = Path(agent).expanduser()
if not agent_path.is_file():
if agent_path.suffix.lower() in {".yaml", ".yml"}:
typer.echo(f"Error: local agent config does not exist: {agent_path}", err=True)
raise typer.Exit(code=2)
return False

optimize_config = kwargs.get("optimize_config")
output = kwargs.get("output")
if not isinstance(optimize_config, str):
typer.echo("Error: local prompt-master requires --optimize-config.", err=True)
raise typer.Exit(code=2)
if not Path(optimize_config).expanduser().is_file():
typer.echo(f"Error: prompt-master config does not exist: {optimize_config}", err=True)
raise typer.Exit(code=2)
if not isinstance(output, str) or Path(output).suffix.lower() not in {".yaml", ".yml"}:
typer.echo("Error: local prompt-master requires --output ending in .yaml or .yml.", err=True)
raise typer.Exit(code=2)
if kwargs.get("optimize_config_fileset") is not None:
typer.echo("Error: local prompt-master cannot use --optimize-config-fileset.", err=True)
raise typer.Exit(code=2)

from nemo_platform_plugin.scheduler import NemoJobScheduler

workspace = kwargs.get("workspace")
local_spec = {
"strategy": "prompt-master",
"agent": str(agent_path),
"optimize_config": str(Path(optimize_config).expanduser()),
"output": output,
"workspace": workspace if isinstance(workspace, str) else "default",
}
try:
result = NemoJobScheduler().run_local(
job_cls,
local_spec,
workspace=local_spec["workspace"],
)
except (OSError, RuntimeError, ValueError) as exc:
typer.echo(f"Error: local prompt-master failed: {exc}", err=True)
raise typer.Exit(code=2) from exc
typer.echo(json.dumps(result, indent=2))
return True


# ---------------------------------------------------------------------------
# Local commands — no platform required
# ---------------------------------------------------------------------------
Expand Down
21 changes: 15 additions & 6 deletions plugins/nemo-agents/src/nemo_agents_plugin/jobs/optimize_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import logging
from pathlib import Path
from typing import Annotated, Any, Optional
from typing import Annotated, Any, Literal, Optional

import typer
from nemo_agents_plugin.cli_context import BaseUrlOption, resolve_base_url, resolve_context_headers
Expand All @@ -37,6 +37,13 @@ def register_prepare_fileset_command(group: typer.Typer) -> None:
help="Validate an optimize bundle and upload it to a fileset for `optimize`.",
)
def prepare_fileset(
strategy: Annotated[
Literal["hpo", "prompt-master"],
typer.Option(
"--strategy",
help="Optimization strategy that will consume this bundle.",
),
],
source: Annotated[
Path,
typer.Option(
Expand Down Expand Up @@ -67,8 +74,8 @@ def prepare_fileset(
Optional[str],
typer.Option(
"--agent",
help="Platform agent supplying the Agent under Test, for configs that carry only "
"the optimizer and eval overlay.",
help="Platform agent supplying the target prompt for prompt-master, or the "
"Agent under Test for hpo configs that carry only an optimizer/eval overlay.",
),
] = None,
check_models: Annotated[
Expand All @@ -87,18 +94,19 @@ def prepare_fileset(
from nemo_optimization.bundle import BundlePreflightError, preflight_bundle

try:
config = preflight_bundle(source, optimize_config, agent=agent)
config = preflight_bundle(source, optimize_config, strategy=strategy, agent=agent)
except BundlePreflightError as exc:
typer.echo(f"Error: {exc}", err=True)
raise typer.Exit(code=1)

ws, name = split_fileset_ref(fileset, workspace)
if dry_run and not check_models:
should_check_models = check_models and strategy == "hpo"
if dry_run and not should_check_models:
typer.echo(f"Preflight passed. Would upload {source}/ to fileset {ws}/{name}.")
return

sdk = _platform_sdk(resolve_base_url(base_url))
if check_models:
if should_check_models:
_preflight_models(config, workspace=workspace, agent=agent, sdk=sdk)
if dry_run:
typer.echo(f"Preflight passed. Would upload {source}/ to fileset {ws}/{name}.")
Expand All @@ -114,6 +122,7 @@ def prepare_fileset(
typer.echo("Submit the study with:\n")
typer.echo(
f" nemo agents optimize \\\n"
f" --strategy {strategy} \\\n"
f" --optimize-config-fileset {ws}/{name} \\\n"
f" --optimize-config {optimize_config} \\\n"
+ (f" --agent {agent} \\\n" if agent else "")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,16 @@ See **the `nemo-agent-skills-optimization` skill** for the full optimize-skills
### 3. Prompt + hyperparameter tuning

If `nemo agents optimize` has not been run for this agent since the last
snapshot, suggest running it. The job sweeps prompts and hyperparameters via
`nat optimize` against the agent's eval dataset.
snapshot, suggest the appropriate required strategy: `hpo` for numeric or
categorical Fabric parameters, or `prompt-master` for the agent's system
instructions.

Suggested actions:

- `nemo agents optimize prepare-fileset --source <bundle-dir> --optimize-config <bundle-relative-yaml> --fileset <name>`
- `nemo agents optimize --agent <name> --optimize-config-fileset <workspace/name> --optimize-config <config-path-in-fileset>`
- `nemo agents optimize prepare-fileset --strategy hpo --source <bundle-dir> --optimize-config <bundle-relative-yaml> --fileset <name>`
- `nemo agents optimize --strategy hpo --agent <name> --optimize-config-fileset <workspace/name> --optimize-config <config-path-in-fileset>`
- For Prompt Master, use `--strategy prompt-master`; `--agent` supplies the
source prompt and `--output` receives the optimized Fabric config.
- After it completes, apply the new prompt + hyperparameters to a sibling
agent, deploy it, and run `nemo agents evaluate` to compare.

Expand Down Expand Up @@ -317,8 +320,8 @@ nemo models list --filter.name nemotron # find Nemotron candidates
# Optimization commands (see also: nemo-agent-skills-optimization skill).
# The optimize command's --optimize-config value must be relative to the staged fileset root.
nemo agents evaluate --agent <name> --eval-config <yaml>
nemo agents optimize prepare-fileset --source <bundle-dir> --optimize-config <bundle-relative-yaml> --fileset <name>
nemo agents optimize --agent <name> --optimize-config-fileset <workspace/name> --optimize-config <config-path-in-fileset>
nemo agents optimize prepare-fileset --strategy hpo --source <bundle-dir> --optimize-config <bundle-relative-yaml> --fileset <name>
nemo agents optimize --strategy hpo --agent <name> --optimize-config-fileset <workspace/name> --optimize-config <config-path-in-fileset>
nemo agents optimize-skills --spec-file .agent-improver.yml
nemo agents evaluate-suite --spec '{"evals": "<dir>", "agent": "<name>"}'

Expand Down
Loading
Loading