feat(nemo-agents): add Fabric (spec-v1) email-phishing example - #1117
feat(nemo-agents): add Fabric (spec-v1) email-phishing example#1117walston wants to merge 7 commits into
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a Fabric email-phishing agent example with DeepAgents orchestration, MCP-based IOC extraction, dataset generation, evaluation settings, tests, documentation, packaging, and workspace registration. ChangesEmail phishing Fabric example
Sequence Diagram(s)sequenceDiagram
participant DatasetBuilder
participant DeepAgentsOrchestrator
participant PhishingAnalyzer
participant IOCMCP
participant Evaluator
DatasetBuilder->>DeepAgentsOrchestrator: assembled email
DeepAgentsOrchestrator->>PhishingAnalyzer: analysis request
PhishingAnalyzer->>IOCMCP: extract_iocs(text)
IOCMCP-->>PhishingAnalyzer: URLs and domains
PhishingAnalyzer-->>DeepAgentsOrchestrator: YAML verdict
DeepAgentsOrchestrator-->>Evaluator: generated verdict
Evaluator->>Evaluator: compare verdict with label
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@plugins/nemo-agents/examples/email-phishing-fabric/data/build_dataset.py`:
- Around line 42-47: Update assemble_email to validate the trimmed sender value
and raise an error when the sender is missing or blank, before constructing or
writing the message. Preserve the existing subject and body handling while
ensuring invalid rows cannot be reported as successfully processed.
In `@plugins/nemo-agents/examples/email-phishing-fabric/email-phishing-eval.yml`:
- Line 34: Update dataset generation in build_dataset.py to validate that every
subject used by id_key is unique, failing generation when duplicates are
detected; alternatively, configure id_key to use an existing stable unique
identifier instead of subject. Ensure the configured id_key and generated
dataset preserve uniqueness.
In `@plugins/nemo-agents/examples/email-phishing-fabric/README.md`:
- Around line 12-16: Update the diagram code fence in the README to specify the
text language, changing the untyped fence around the orchestrator diagram to a
text fence while leaving its contents unchanged.
- Around line 1-3: Update the README with a Prerequisites section before the
description, listing uv, the nemo CLI, Platform access, the deepagents adapter,
NVIDIA_API_KEY, and the evaluation model variable; add a Next Steps section
after Status with links for dataset generation, evaluation, and deployment
guidance.
- Around line 57-59: Update the README’s multiline --input example for
email-phishing-fabric-deployment to use Bash ANSI-C quoting with $'...', so each
\n sequence becomes an actual newline before the CLI receives the input.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: c80b94c1-1896-4dcf-bea9-5f3bf0d54fe0
⛔ Files ignored due to path filters (2)
plugins/nemo-agents/examples/email-phishing-fabric/data/smaller_test.csvis excluded by!**/*.csvuv.lockis excluded by!**/*.lock
📒 Files selected for processing (9)
plugins/nemo-agents/examples/email-phishing-fabric/README.mdplugins/nemo-agents/examples/email-phishing-fabric/agent.yamlplugins/nemo-agents/examples/email-phishing-fabric/data/build_dataset.pyplugins/nemo-agents/examples/email-phishing-fabric/email-phishing-eval.ymlplugins/nemo-agents/examples/email-phishing-fabric/pyproject.tomlplugins/nemo-agents/examples/email-phishing-fabric/src/email_phishing_fabric/iocs.pyplugins/nemo-agents/examples/email-phishing-fabric/src/email_phishing_fabric/mcp_server.pyplugins/nemo-agents/examples/email-phishing-fabric/tests/test_extract_iocs.pypyproject.toml
| dataset: | ||
| _type: csv | ||
| file_path: data/smaller_test.csv | ||
| id_key: subject |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
uv run python - <<'PY'
import csv
from collections import Counter
from pathlib import Path
path = Path(
"plugins/nemo-agents/examples/email-phishing-fabric/data/smaller_test.csv"
)
with path.open(newline="", encoding="utf-8") as handle:
subjects = [row["subject"] for row in csv.DictReader(handle)]
duplicates = {
subject: count
for subject, count in Counter(subjects).items()
if count > 1
}
if duplicates:
for subject, count in duplicates.items():
print(f"{count} rows use subject: {subject!r}")
raise SystemExit(1)
PYRepository: NVIDIA-NeMo/nemo-platform
Length of output: 203
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- local instructions ---'
find .. -name AGENTS.local.md -print
printf '%s\n' '--- target files ---'
git ls-files \
'plugins/nemo-agents/examples/email-phishing-fabric/**' \
| sed -n '1,160p'
printf '%s\n' '--- target configuration ---'
nl -ba plugins/nemo-agents/examples/email-phishing-fabric/email-phishing-eval.yml \
| sed -n '1,90p'
printf '%s\n' '--- dataset columns and row counts ---'
python3 - <<'PY'
import csv
from collections import Counter
from pathlib import Path
root = Path("plugins/nemo-agents/examples/email-phishing-fabric")
for path in sorted(root.rglob("*.csv")):
with path.open(newline="", encoding="utf-8") as handle:
rows = list(csv.DictReader(handle))
print(path, "rows=", len(rows), "columns=", list(rows[0]) if rows else [])
if rows and "subject" in rows[0]:
duplicates = {
value: count
for value, count in Counter(row["subject"] for row in rows).items()
if count > 1
}
print(" subject_duplicates=", duplicates)
PY
printf '%s\n' '--- id_key and subject usage ---'
rg -n -S 'id_key|subject|smaller_test' \
plugins/nemo-agents/examples/email-phishing-fabric \
| sed -n '1,240p'Repository: NVIDIA-NeMo/nemo-platform
Length of output: 945
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
files = [
Path("plugins/nemo-agents/examples/email-phishing-fabric/email-phishing-eval.yml"),
Path("plugins/nemo-agents/examples/email-phishing-fabric/data/build_dataset.py"),
Path("plugins/nemo-agents/examples/email-phishing-fabric/README.md"),
]
for path in files:
print(f"--- {path} ---")
for number, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1):
print(f"{number:4}: {line}")
PY
python3 - <<'PY'
import csv
from collections import Counter
from pathlib import Path
root = Path("plugins/nemo-agents/examples/email-phishing-fabric")
for path in sorted(root.rglob("*.csv")):
with path.open(newline="", encoding="utf-8") as handle:
rows = list(csv.DictReader(handle))
print(f"--- {path}: {len(rows)} rows ---")
print("columns:", list(rows[0]) if rows else [])
for field in ("subject", "id", "id_key"):
if rows and field in rows[0]:
duplicates = {
value: count
for value, count in Counter(row[field] for row in rows).items()
if count > 1
}
print(f"{field}_duplicates:", duplicates)
PY
rg -n -S 'id_key|subject|smaller_test|record identity|unique' \
plugins/nemo-agents/examples/email-phishing-fabric \
| sed -n '1,240p'Repository: NVIDIA-NeMo/nemo-platform
Length of output: 12852
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- repository id_key references ---'
rg -n -S 'id_key' . \
-g '!**/.git/**' \
| sed -n '1,260p'
printf '%s\n' '--- dataset loader and identity handling candidates ---'
rg -n -S 'id[_ -]?key|record[_ -]?id|unique.*id|duplicate|dedup|DataFrame' . \
-g '*.py' -g '*.ts' -g '*.tsx' -g '*.js' -g '*.yml' -g '*.yaml' \
| sed -n '1,320p'
printf '%s\n' '--- source dataset referenced by build_dataset.py ---'
python3 - <<'PY'
import csv
from collections import Counter
from pathlib import Path
path = Path(
"plugins/nemo-agents/examples/email-phishing-analyzer"
) / "src/nat_email_phishing_analyzer/data/smaller_test.csv"
if not path.exists():
print(f"missing: {path}")
else:
with path.open(newline="", encoding="utf-8") as handle:
rows = list(csv.DictReader(handle))
print(f"{path}: {len(rows)} rows; columns={list(rows[0]) if rows else []}")
for field in ("subject", "id"):
if rows and field in rows[0]:
print(
field,
{
value: count
for value, count in Counter(row[field] for row in rows).items()
if count > 1
},
)
PYRepository: NVIDIA-NeMo/nemo-platform
Length of output: 50381
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- repository id_key references ---'
rg -n -S 'id_key' . \
-g '!**/.git/**' \
| sed -n '1,260p'
printf '%s\n' '--- dataset loader and identity handling candidates ---'
rg -n -S 'id[_ -]?key|record[_ -]?id|unique.*id|duplicate|dedup|DataFrame' . \
-g '*.py' -g '*.ts' -g '*.tsx' -g '*.js' -g '*.yml' -g '*.yaml' \
| sed -n '1,320p'
printf '%s\n' '--- source dataset referenced by build_dataset.py ---'
python3 - <<'PY'
import csv
from collections import Counter
from pathlib import Path
path = (
Path("plugins/nemo-agents/examples/email-phishing-analyzer")
/ "src/nat_email_phishing_analyzer/data/smaller_test.csv"
)
if not path.exists():
print(f"missing: {path}")
else:
with path.open(newline="", encoding="utf-8") as handle:
rows = list(csv.DictReader(handle))
print(f"{path}: {len(rows)} rows; columns={list(rows[0]) if rows else []}")
for field in ("subject", "id"):
if rows and field in rows[0]:
print(
field,
{
value: count
for value, count in Counter(row[field] for row in rows).items()
if count > 1
},
)
PYRepository: NVIDIA-NeMo/nemo-platform
Length of output: 50381
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
import csv
from collections import Counter
from pathlib import Path
paths = [
Path("plugins/nemo-agents/examples/email-phishing-analyzer")
/ "src/nat_email_phishing_analyzer/data/smaller_test.csv",
Path("web/packages/studio/public/sample-agents/email-phishing-analyzer/eval.yml"),
]
for path in paths:
print(f"--- {path} ---")
if path.suffix == ".csv":
with path.open(newline="", encoding="utf-8") as handle:
rows = list(csv.DictReader(handle))
print("rows:", len(rows))
print("columns:", list(rows[0]) if rows else [])
if rows and "subject" in rows[0]:
counts = Counter(row["subject"] for row in rows)
print("duplicate_subjects:", {k: v for k, v in counts.items() if v > 1})
else:
for number, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1):
if "dataset:" in line or "file_path:" in line or "id_key:" in line:
print(f"{number}: {line}")
PY
printf '%s\n' '--- focused evaluator documentation and schema references ---'
rg -n -S -m 120 \
'id_key:|id_key\b|CSV.*dataset|dataset.*CSV|record identity|unique.*(record|row|id)' \
plugins/nemo-agents \
sdk/python/nemo-platform/src/nemo_platform/beta/evaluator \
docs \
-g '*.md' -g '*.py' -g '*.yml' -g '*.yaml' \
| sed -n '1,220p'Repository: NVIDIA-NeMo/nemo-platform
Length of output: 2048
Validate subject uniqueness during dataset generation.
smaller_test.csv currently has five unique subjects, but data/build_dataset.py does not enforce this invariant. Fail generation on duplicate subjects or use a stable unique ID for id_key.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@plugins/nemo-agents/examples/email-phishing-fabric/email-phishing-eval.yml`
at line 34, Update dataset generation in build_dataset.py to validate that every
subject used by id_key is unique, failing generation when duplicates are
detected; alternatively, configure id_key to use an existing stable unique
identifier instead of subject. Ensure the configured id_key and generated
dataset preserve uniqueness.
| # Email Phishing Analyzer — Fabric example (`nemo-agents-spec-v1`) | ||
|
|
||
| A Platform-native port of the email-phishing analyzer. Unlike the NAT ReAct |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Add the required prerequisite and Next Steps sections.
Add Prerequisites before the description. List uv, the nemo CLI, Platform access, the deepagents adapter, NVIDIA_API_KEY, and the evaluation model variable. Add Next Steps after Status with links to dataset generation, evaluation, and deployment guidance.
As per coding guidelines, Markdown pages must list prerequisites at the top and include a Next Steps section at the end. Based on learnings, the missing-section exception applies only when no enforced convention exists; this review includes an explicit convention.
Also applies to: 69-75
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@plugins/nemo-agents/examples/email-phishing-fabric/README.md` around lines 1
- 3, Update the README with a Prerequisites section before the description,
listing uv, the nemo CLI, Platform access, the deepagents adapter,
NVIDIA_API_KEY, and the evaluation model variable; add a Next Steps section
after Status with links for dataset generation, evaluation, and deployment
guidance.
Sources: Coding guidelines, Learnings
| nemo agents invoke --agent-deployment email-phishing-fabric-deployment \ | ||
| --input "From: it-support@paypa1-secure.example\nSubject: Verify your account\n\nYour account is locked. Confirm your password at http://paypa1-secure.example/login" | ||
| ``` |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- local instructions ---'
find .. -name AGENTS.local.md -print
printf '%s\n' '--- target file ---'
cat -n plugins/nemo-agents/examples/email-phishing-fabric/README.md | sed -n '1,110p'
printf '%s\n' '--- invoke implementation references ---'
rg -n --glob '!**/node_modules/**' 'agents invoke|--input|input' plugins/nemo-agents | head -200Repository: NVIDIA-NeMo/nemo-platform
Length of output: 29984
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- CLI invoke path ---'
sed -n '120,225p' plugins/nemo-agents/src/nemo_agents_plugin/cli.py
sed -n '1420,1475p' plugins/nemo-agents/src/nemo_agents_plugin/cli.py
printf '%s\n' '--- deployment invocation call graph ---'
rg -n -C 4 'agent_deployment|deployment.*invoke|invoke.*deployment|/invoke|messages.*content' plugins/nemo-agents/src/nemo_agents_plugin
printf '%s\n' '--- shell behavior probe ---'
python3 - <<'PY'
import subprocess
script = r'''
printf '%s' "From: a\nSubject: b\n\nBody" | od -An -t x1
printf '%s' $'From: a\nSubject: b\n\nBody' | od -An -t x1
'''
print(subprocess.run(["bash", "-c", script], text=True, capture_output=True, check=True).stdout)
PYRepository: NVIDIA-NeMo/nemo-platform
Length of output: 18137
🏁 Script executed:
#!/bin/bash
set -eu
python3 - <<'PY'
import subprocess
script = r'''
printf '%s' "From: a\nSubject: b\n\nBody" |
python3 -c 'import sys; print(repr(sys.stdin.read()))'
printf '%s' $'From: a\nSubject: b\n\nBody' |
python3 -c 'import sys; print(repr(sys.stdin.read()))'
'''
result = subprocess.run(["bash", "-c", script], text=True, capture_output=True, check=True)
print(result.stdout, end="")
PYRepository: NVIDIA-NeMo/nemo-platform
Length of output: 226
Use ANSI-C quoting for the multiline input. Bash passes \n literally, and the CLI forwards it unchanged. Use $'From: ...\nSubject: ...'.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@plugins/nemo-agents/examples/email-phishing-fabric/README.md` around lines 57
- 59, Update the README’s multiline --input example for
email-phishing-fabric-deployment to use Bash ANSI-C quoting with $'...', so each
\n sequence becomes an actual newline before the CLI receives the input.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
plugins/nemo-agents/examples/email-phishing-fabric/README.md (1)
42-48: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy liftKeep this page in one Diátaxis quadrant.
ShapeandTuneexplain architecture.Runis a how-to. Move the architecture content to an explanation page, or keep this README as one how-to with cross-links.As per coding guidelines, each documentation page must fit one Diátaxis quadrant.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/nemo-agents/examples/email-phishing-fabric/README.md` around lines 42 - 48, Move the architecture-oriented prose in the README’s Run section out of this how-to page and keep this page focused only on running the example. Update the section around extract_iocs so it no longer explains the deepagents adapter, command resolution, or deployment-mode behavior, and instead cross-link that material to the appropriate explanation page while preserving the run instructions here.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@plugins/nemo-agents/examples/email-phishing-fabric/README.md`:
- Around line 57-93: The README’s usage documentation must provide parallel CLI
and Python SDK workflows in tabbed local and container alternatives. Update the
local and container sections around the shown `nemo agents` commands to use the
project’s tab-set documentation convention, adding equivalent Python SDK
examples for each variant while preserving the existing CLI commands and
deployment details.
- Around line 50-66: Update the local CLI instructions before the `nemo agents
create` commands to direct users to follow `SETUP.md`, set
`NMP_BASE_URL=http://localhost:8080`, verify Platform readiness, and run
commands via `uv run nemo` or an activated `.venv`. Apply the same setup and
invocation guidance to the additional local command blocks, ensuring the MCP
console script is available on PATH.
---
Nitpick comments:
In `@plugins/nemo-agents/examples/email-phishing-fabric/README.md`:
- Around line 42-48: Move the architecture-oriented prose in the README’s Run
section out of this how-to page and keep this page focused only on running the
example. Update the section around extract_iocs so it no longer explains the
deepagents adapter, command resolution, or deployment-mode behavior, and instead
cross-link that material to the appropriate explanation page while preserving
the run instructions here.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 98592611-ae81-48b8-b795-d33728b112f2
📒 Files selected for processing (2)
plugins/nemo-agents/examples/email-phishing-fabric/README.mdplugins/nemo-agents/examples/email-phishing-fabric/agent.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
- plugins/nemo-agents/examples/email-phishing-fabric/agent.yaml
| ```bash | ||
| nemo agents create --name email-phishing-fabric \ | ||
| --agent-config plugins/nemo-agents/examples/email-phishing-fabric/agent.yaml | ||
| nemo agents deploy --agent email-phishing-fabric --name email-phishing-fabric-deployment | ||
| nemo agents invoke --agent-deployment email-phishing-fabric-deployment \ | ||
| --input "From: it-support@paypa1-secure.example | ||
| Subject: Verify your account | ||
|
|
||
| Your account is locked. Confirm your password at http://paypa1-secure.example/login" | ||
| ``` | ||
|
|
||
| ### Container (`--mode docker` / `k8s`) | ||
|
|
||
| A deployment container does **not** have this example installed, so a local | ||
| `uv pip install` cannot reach it. Bake the package into an image with | ||
| `nemo agents package` — project mode (`--pyproject`) runs `uv pip install .`, | ||
| which provides the `email-phishing-iocs-mcp` console script — then deploy that | ||
| image: | ||
|
|
||
| ```bash | ||
| nemo agents package \ | ||
| --agent plugins/nemo-agents/examples/email-phishing-fabric/agent.yaml \ | ||
| --pyproject plugins/nemo-agents/examples/email-phishing-fabric/pyproject.toml \ | ||
| --tag email-phishing-fabric:local | ||
|
|
||
| nemo agents create --name email-phishing-fabric \ | ||
| --agent-config plugins/nemo-agents/examples/email-phishing-fabric/agent.yaml | ||
| nemo agents deploy \ | ||
| --agent email-phishing-fabric \ | ||
| --name email-phishing-fabric-deployment \ | ||
| --mode docker \ | ||
| --image email-phishing-fabric:local | ||
| ``` | ||
|
|
||
| For Kubernetes, publish the image | ||
| (`nemo agents package ... --publish --registry <registry>`) and pass the | ||
| published image to `nemo agents deploy --mode k8s --image <image>`. |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift
Provide the required tabbed alternatives.
This section provides only CLI examples and uses separate headings for local and container variants. Add equivalent Python SDK examples and put the local/container variants in tab sets, or link to dedicated pages that provide both interfaces.
As per coding guidelines, documentation must provide Python SDK and CLI examples in tab sets for parallel workflows.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@plugins/nemo-agents/examples/email-phishing-fabric/README.md` around lines 57
- 93, The README’s usage documentation must provide parallel CLI and Python SDK
workflows in tabbed local and container alternatives. Update the local and
container sections around the shown `nemo agents` commands to use the project’s
tab-set documentation convention, adding equivalent Python SDK examples for each
variant while preserving the existing CLI commands and deployment details.
Source: Coding guidelines
|
Live-validated |
Port the email-phishing analyzer to a Platform-native nemo-agents-spec-v1 agent: a deepagents orchestrator that delegates classification to a phishing subagent and calls a deterministic extract_iocs MCP tool. The prompt and model live in agent.yaml (tunable) and each step emits a trace span, replacing the opaque MCP-proxy classifier. - extract_iocs ported as a stdio MCP console tool (pure regex, unit-tested) - sender-inclusive input: assembled From:/Subject:/body 'email' column so the sender (a top phishing tell) reaches the model and extract_iocs - eval config uses question_key: email - registered as a workspace member so its package resolves ASTD-370, ASTD-371, ASTD-372 Signed-off-by: Nathan Walston <nwalston@nvidia.com>
The extract_iocs stdio MCP tool is a console script that Fabric launches as a parallel child process, resolving the command on PATH. The runtime that must contain it differs by deploy mode: - subprocess (default): runs locally from the repo .venv (sys.executable, inherits PATH); the example is a workspace member so uv sync --all-packages already provides the console script — no image needed. - docker/k8s: the container lacks the package; bake it in with nemo agents package --pyproject (uv pip install .), then deploy --mode docker/k8s --image (--publish --registry for k8s). Replaces the misleading local 'uv pip install + bare deploy' instruction and the agent.yaml comment. Addresses review P1 (deployed agent could not start its MCP server under container modes). Signed-off-by: Nathan Walston <nwalston@nvidia.com>
216b003 to
f6f054b
Compare
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@plugins/nemo-agents/examples/email-phishing-fabric/README.md`:
- Around line 68-109: Update the README’s Status section to state that the
subprocess path, subagent delegation, and extract_iocs were live-validated,
while Docker/Kubernetes packaging remains unvalidated. Clearly label the
container deployment commands under the Container section as unvalidated, unless
those workflows are actually tested before publishing.
In
`@plugins/nemo-agents/examples/email-phishing-fabric/src/email_phishing_fabric/iocs.py`:
- Around line 27-29: Update the URL extraction logic using _URL_RE and urlsplit
to catch ValueError from malformed candidates, including fullwidth-slash cases,
so untrusted content cannot fail the tool. Require a non-empty hostname before
adding either the URL or its domain, and add a regression test covering the
malformed candidate.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 4532af6e-33a8-4f66-b9f3-160c4121913d
⛔ Files ignored due to path filters (2)
plugins/nemo-agents/examples/email-phishing-fabric/data/smaller_test.csvis excluded by!**/*.csvuv.lockis excluded by!**/*.lock
📒 Files selected for processing (9)
plugins/nemo-agents/examples/email-phishing-fabric/README.mdplugins/nemo-agents/examples/email-phishing-fabric/agent.yamlplugins/nemo-agents/examples/email-phishing-fabric/data/build_dataset.pyplugins/nemo-agents/examples/email-phishing-fabric/email-phishing-eval.ymlplugins/nemo-agents/examples/email-phishing-fabric/pyproject.tomlplugins/nemo-agents/examples/email-phishing-fabric/src/email_phishing_fabric/iocs.pyplugins/nemo-agents/examples/email-phishing-fabric/src/email_phishing_fabric/mcp_server.pyplugins/nemo-agents/examples/email-phishing-fabric/tests/test_extract_iocs.pypyproject.toml
🚧 Files skipped from review as they are similar to previous changes (6)
- pyproject.toml
- plugins/nemo-agents/examples/email-phishing-fabric/email-phishing-eval.yml
- plugins/nemo-agents/examples/email-phishing-fabric/data/build_dataset.py
- plugins/nemo-agents/examples/email-phishing-fabric/agent.yaml
- plugins/nemo-agents/examples/email-phishing-fabric/pyproject.toml
- plugins/nemo-agents/examples/email-phishing-fabric/src/email_phishing_fabric/mcp_server.py
| ### Container (`--mode docker` / `k8s`) | ||
|
|
||
| A deployment container does **not** have this example installed, so a local | ||
| `uv pip install` cannot reach it. Bake the package into an image with | ||
| `nemo agents package` — project mode (`--pyproject`) runs `uv pip install .`, | ||
| which provides the `email-phishing-iocs-mcp` console script — then deploy that | ||
| image: | ||
|
|
||
| ```bash | ||
| nemo agents package \ | ||
| --agent plugins/nemo-agents/examples/email-phishing-fabric/agent.yaml \ | ||
| --pyproject plugins/nemo-agents/examples/email-phishing-fabric/pyproject.toml \ | ||
| --tag email-phishing-fabric:local | ||
|
|
||
| nemo agents create --name email-phishing-fabric \ | ||
| --agent-config plugins/nemo-agents/examples/email-phishing-fabric/agent.yaml | ||
| nemo agents deploy \ | ||
| --agent email-phishing-fabric \ | ||
| --name email-phishing-fabric-deployment \ | ||
| --mode docker \ | ||
| --image email-phishing-fabric:local | ||
| ``` | ||
|
|
||
| For Kubernetes, publish the image | ||
| (`nemo agents package ... --publish --registry <registry>`) and pass the | ||
| published image to `nemo agents deploy --mode k8s --image <image>`. | ||
|
|
||
| Evaluate against the sender-inclusive dataset: | ||
|
|
||
| ```bash | ||
| nemo agents evaluate run \ | ||
| --eval-config plugins/nemo-agents/examples/email-phishing-fabric/email-phishing-eval.yml \ | ||
| --agent email-phishing-fabric | ||
| ``` | ||
|
|
||
| ## Status | ||
|
|
||
| Structurally validated: `agent.yaml` passes `AgentConfig` (`nemo-agents-spec-v1`) | ||
| and translates to a typed Fabric config; `extract_iocs` is unit-tested. A live | ||
| create/deploy/invoke against a running Platform (with `NVIDIA_API_KEY`) is the | ||
| next step and is not exercised here. Eval judge weights/prompt are starters — | ||
| tune per your evaluator plugin. |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -e
printf '%s\n' '--- local instruction files ---'
find .. -name AGENTS.local.md -print
printf '%s\n' '--- target and nearby files ---'
git ls-files 'plugins/nemo-agents/examples/email-phishing-fabric/*' | sed -n '1,120p'
printf '%s\n' '--- README lines 1-140 ---'
cat -n plugins/nemo-agents/examples/email-phishing-fabric/README.md | sed -n '1,140p'
printf '%s\n' '--- concise repository status ---'
git status --short
printf '%s\n' '--- recent commit subjects ---'
git log --oneline -8Repository: NVIDIA-NeMo/nemo-platform
Length of output: 7273
🏁 Script executed:
#!/bin/bash
set -e
printf '%s\n' '--- remote and branch metadata ---'
git remote -v
git branch --show-current
git show -s --format=fuller f6f054b2
printf '%s\n' '--- documentation commit summary ---'
git show --stat --oneline f6f054b2
printf '%s\n' '--- documentation commit diff ---'
git diff f6f054b2^ f6f054b2 -- plugins/nemo-agents/examples/email-phishing-fabric/README.md
printf '%s\n' '--- example configuration ---'
cat -n plugins/nemo-agents/examples/email-phishing-fabric/pyproject.toml
cat -n plugins/nemo-agents/examples/email-phishing-fabric/agent.yaml
printf '%s\n' '--- package/deploy command definitions and references ---'
rg -n --glob '*.py' --glob '*.ts' --glob '*.tsx' --glob '*.md' \
'agents package|--pyproject|def package|class.*Package|agents deploy|--mode docker|--mode k8s' \
plugins src docs .github 2>/dev/null | sed -n '1,240p'Repository: NVIDIA-NeMo/nemo-platform
Length of output: 22006
🏁 Script executed:
#!/bin/bash
set -e
sha=f6f054b245c2e8329c47c0705205a226234a957e
printf '%s\n' '--- pull requests associated with commit ---'
if command -v gh >/dev/null 2>&1; then
gh api "repos/NVIDIA-NeMo/nemo-platform/commits/$sha/pulls" \
--header 'Accept: application/vnd.github+json' \
--jq '.[] | {number, title, state, body, html_url}'
else
curl -fsSL -H 'Accept: application/vnd.github+json' \
"https://api.github.com/repos/NVIDIA-NeMo/nemo-platform/commits/$sha/pulls" |
jq '.[] | {number, title, state, body, html_url}'
fi
printf '%s\n' '--- package CLI definition ---'
cat -n plugins/nemo-agents/src/nemo_agents_plugin/cli.py | sed -n '270,390p'
printf '%s\n' '--- package project-mode implementation ---'
cat -n plugins/nemo-agents/src/nemo_agents_plugin/cli.py | sed -n '640,715p'
printf '%s\n' '--- deployment option validation ---'
cat -n plugins/nemo-agents/src/nemo_agents_plugin/cli.py | sed -n '900,975p'
printf '%s\n' '--- package documentation ---'
cat -n plugins/nemo-agents/README.md | sed -n '150,225p;270,290p;385,415p'
printf '%s\n' '--- package CLI tests around --pyproject ---'
cat -n plugins/nemo-agents/tests/unit/test_container.py | sed -n '1660,1850p'Repository: NVIDIA-NeMo/nemo-platform
Length of output: 31330
🏁 Script executed:
#!/bin/bash
set -e
pr=1117
base="https://api.github.com/repos/NVIDIA-NeMo/nemo-platform"
headers=(-H 'Accept: application/vnd.github+json')
printf '%s\n' '--- current pull request body and metadata ---'
curl -fsSL "${headers[@]}" "$base/pulls/$pr" |
jq '{number,state,title,updated_at,body,commits,changed_files}'
printf '%s\n' '--- issue comments ---'
curl -fsSL "${headers[@]}" "$base/issues/$pr/comments?per_page=100" |
jq -r '.[] | "\(.created_at) \(.user.login):\n\(.body)\n---"'
printf '%s\n' '--- review summaries and bodies ---'
curl -fsSL "${headers[@]}" "$base/pulls/$pr/reviews?per_page=100" |
jq -r '.[] | "\(.submitted_at) \(.user.login) [\(.state)]:\n\(.body)\n---"'Repository: NVIDIA-NeMo/nemo-platform
Length of output: 18666
Align the README with the validation scope.
The subprocess path was live-validated, including subagent delegation and extract_iocs. Docker/Kubernetes packaging remains unvalidated. Update Status and mark the container commands as unvalidated, or test them before publishing.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@plugins/nemo-agents/examples/email-phishing-fabric/README.md` around lines 68
- 109, Update the README’s Status section to state that the subprocess path,
subagent delegation, and extract_iocs were live-validated, while
Docker/Kubernetes packaging remains unvalidated. Clearly label the container
deployment commands under the Container section as unvalidated, unless those
workflows are actually tested before publishing.
Source: Coding guidelines
…nt-config/ Move the Fabric (spec-v1) email-phishing example from the top-level examples dir into nemo-agent-config/email-phishing-agent/, alongside calculator-agent — that directory is where nemo-agents-spec-v1 (Fabric) examples live; the top-level dir holds NAT examples. Adopt the sibling's conventions: - flat mcps/ package (mcps.iocs) instead of src/ layout; extract_iocs util + FastMCP server combined; console script email-phishing-iocs - package nemo-agent-config-example-email-phishing; mcp>=1.28.1,<2 - agent renamed email-phishing-agent; mcp url email-phishing-iocs (default harness_native exposure); ATOF telemetry like the sibling - build_dataset.py source path fixed for the new depth Addresses review: the -fabric suffix was redundant and the example was misfiled at the top level. Signed-off-by: Nathan Walston <nwalston@nvidia.com>
|
Relocated the example to |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
plugins/nemo-agents/examples/nemo-agent-config/email-phishing-agent/mcps/iocs.py (1)
38-57:⚠️ Potential issue | 🟠 MajorHandle malformed URL candidates before calling
urlsplit.The URL regex accepts candidates such as
https://exa/mple.com.urlsplit()raisesValueErrorfor this input, so one crafted email can make the MCP tool fail. CatchValueErroraround parsing and hostname access, skip candidates without a hostname, and add a regression test.Proposed fix
- parsed = urlsplit(url) - hostname = parsed.hostname + try: + parsed = urlsplit(url) + hostname = parsed.hostname + except ValueError: + continue + if not hostname: + continueThis repeats the unresolved malformed-URL finding from the previous review.
#!/bin/bash set -euo pipefail target="plugins/nemo-agents/examples/nemo-agent-config/email-phishing-agent/mcps/iocs.py" tests="plugins/nemo-agents/examples/nemo-agent-config/email-phishing-agent/tests/test_extract_iocs.py" rg -n -C 6 'urlsplit|_URL_RE|hostname|def extract_iocs' "$target" rg -n -i 'malformed|fullwidth|ValueError|exa' "$tests" || true uv run python - <<'PY' from urllib.parse import urlsplit try: urlsplit("https://exa/mple.com") except ValueError: pass else: raise SystemExit("expected ValueError") PY🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/nemo-agents/examples/nemo-agent-config/email-phishing-agent/mcps/iocs.py` around lines 38 - 57, Update extract_iocs so each URL candidate is parsed with urlsplit inside ValueError handling, skipping malformed candidates and any result without a hostname while preserving valid URL and domain extraction. Add a regression test in test_extract_iocs.py covering a candidate such as https://exa/mple.com and confirming the tool does not fail.
🧹 Nitpick comments (2)
plugins/nemo-agents/examples/nemo-agent-config/email-phishing-agent/agent.yaml (1)
69-79: 🩺 Stability & Availability | 🔵 TrivialVerify the MCP command in packaged deployments.
agent.yamlresolvesemail-phishing-iocsfromPATH. The reported validation covers subprocess mode, but it does not prove that Docker or Kubernetes images install the console script and preserve the MCP handshake. Run a packaged smoke test that invokesextract_iocs.Based on the PR objectives, Docker/Kubernetes packaging remains unvalidated.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/nemo-agents/examples/nemo-agent-config/email-phishing-agent/agent.yaml` around lines 69 - 79, Add a packaged-deployment smoke test for the MCP server configured by the iocs entry in agent.yaml, covering Docker or Kubernetes packaging rather than only subprocess mode. Build/package the image, verify the email-phishing-iocs console script is available on PATH, and invoke extract_iocs through the stdio MCP handshake to confirm the tool works end to end.plugins/nemo-agents/examples/nemo-agent-config/email-phishing-agent/README.md (1)
1-3: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy liftKeep the README within one Diataxis quadrant.
The page combines architecture explanation, tuning reference, and how-to instructions. Choose one primary quadrant, move other material to linked pages, add
Prerequisitesbefore the overview, and addNext StepsafterStatus.As per coding guidelines, each documentation page must use one Diataxis quadrant, list prerequisites at the top, and include Next Steps at the end.
Also applies to: 35-41, 109-117
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/nemo-agents/examples/nemo-agent-config/email-phishing-agent/README.md` around lines 1 - 3, Refocus the README on a single Diataxis quadrant, moving architecture and tuning reference material to linked documentation pages while retaining only the chosen page type’s content. Add a Prerequisites section before the overview, and add a Next Steps section after Status with links to the relocated material or follow-up guidance.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@plugins/nemo-agents/examples/nemo-agent-config/email-phishing-agent/email-phishing-eval.yml`:
- Around line 5-7: Update the commented evaluation command to use the active
email-phishing-agent example path and agent name instead of
email-phishing-fabric, and rename the associated output directory from the stale
identifier to email-phishing-agent to keep generated artifacts consistent.
---
Outside diff comments:
In
`@plugins/nemo-agents/examples/nemo-agent-config/email-phishing-agent/mcps/iocs.py`:
- Around line 38-57: Update extract_iocs so each URL candidate is parsed with
urlsplit inside ValueError handling, skipping malformed candidates and any
result without a hostname while preserving valid URL and domain extraction. Add
a regression test in test_extract_iocs.py covering a candidate such as
https://exa/mple.com and confirming the tool does not fail.
---
Nitpick comments:
In
`@plugins/nemo-agents/examples/nemo-agent-config/email-phishing-agent/agent.yaml`:
- Around line 69-79: Add a packaged-deployment smoke test for the MCP server
configured by the iocs entry in agent.yaml, covering Docker or Kubernetes
packaging rather than only subprocess mode. Build/package the image, verify the
email-phishing-iocs console script is available on PATH, and invoke extract_iocs
through the stdio MCP handshake to confirm the tool works end to end.
In
`@plugins/nemo-agents/examples/nemo-agent-config/email-phishing-agent/README.md`:
- Around line 1-3: Refocus the README on a single Diataxis quadrant, moving
architecture and tuning reference material to linked documentation pages while
retaining only the chosen page type’s content. Add a Prerequisites section
before the overview, and add a Next Steps section after Status with links to the
relocated material or follow-up guidance.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 184d58cd-1942-4a96-b45a-32592020baeb
⛔ Files ignored due to path filters (2)
plugins/nemo-agents/examples/nemo-agent-config/email-phishing-agent/data/smaller_test.csvis excluded by!**/*.csvuv.lockis excluded by!**/*.lock
📒 Files selected for processing (8)
plugins/nemo-agents/examples/nemo-agent-config/email-phishing-agent/README.mdplugins/nemo-agents/examples/nemo-agent-config/email-phishing-agent/agent.yamlplugins/nemo-agents/examples/nemo-agent-config/email-phishing-agent/data/build_dataset.pyplugins/nemo-agents/examples/nemo-agent-config/email-phishing-agent/email-phishing-eval.ymlplugins/nemo-agents/examples/nemo-agent-config/email-phishing-agent/mcps/iocs.pyplugins/nemo-agents/examples/nemo-agent-config/email-phishing-agent/pyproject.tomlplugins/nemo-agents/examples/nemo-agent-config/email-phishing-agent/tests/test_extract_iocs.pypyproject.toml
Parts table, port-it steps, and mirrored Platform-CLI / Studio usage sections (same five beats: register, deploy, invoke, observe, tune & evaluate). Studio create/deploy beats marked pending the gallery tile (ASTD-08). Signed-off-by: Nathan Walston <nwalston@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@plugins/nemo-agents/examples/nemo-agent-config/email-phishing-agent/README.md`:
- Around line 51-53: Add a “Next Steps” section immediately after the Status
section in the email-phishing-agent README, including cross-links to the related
registration, evaluation, and trace-inspection documentation. Ensure this
section is the final content on the page.
- Around line 31-33: Move the “Prereqs” line from the “Use it in Platform (CLI)”
section to directly after the README introduction, before all other
documentation sections, while preserving its existing prerequisite details.
- Around line 12-30: Keep this README focused on a single Diataxis documentation
type by retaining either the component reference or the porting guidance, not
both. Move the other section to a dedicated documentation page and add a clear
cross-link between the pages, preserving the existing details and examples.
- Around line 36-39: Validate the documented Docker/Kubernetes deployment,
Studio workflow, and evaluation-judge tuning steps in the email-phishing-agent
README before presenting them as supported usage. If they cannot be tested, move
the affected instructions near the untested-workflows note into an explicitly
marked draft or unverified section, including the blocks around Deploy, Tune &
evaluate, and the corresponding Studio/evaluation steps.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 363bb4a3-9503-4d28-b7ca-2140db67067e
📒 Files selected for processing (1)
plugins/nemo-agents/examples/nemo-agent-config/email-phishing-agent/README.md
| ## Parts | ||
|
|
||
| | Path | What | Why | | ||
| |---|---|---| | ||
| | `agent.yaml` | The `nemo-agents-spec-v1` config: harness, sub-agent, model, MCP server, telemetry | The single tunable surface — prompts + hyperparameters | | ||
| | `mcps/iocs.py` | `extract_iocs` (pure regex) + FastMCP stdio server | The one real tool; URL/domain extraction incl. the sender | | ||
| | `pyproject.toml` | Packages `mcps/`; exposes console `email-phishing-iocs` | Makes the tool resolvable at runtime | | ||
| | `data/smaller_test.csv` | Labeled emails with an assembled `email` column (`From:`/`Subject:`/body) | Eval input; keeps the sender (a top phishing tell) | | ||
| | `data/build_dataset.py` | Rebuilds that column from the upstream NAT dataset | Regenerate after changing the assembly | | ||
| | `email-phishing-eval.yml` | Eval config; `question_key: email` | Scores verdicts against the `label` column | | ||
| | `tests/test_extract_iocs.py` | Unit tests for the tool | Guards the extractor | | ||
|
|
||
| ## Port it (to your own agent) | ||
|
|
||
| 1. **Copy** this directory to `nemo-agent-config/<your-agent>/`. | ||
| 2. **Rename** in `pyproject.toml` (`name`, `[project.scripts]` console), `agent.yaml` (`name`, `project`, and `mcp.servers.<n>.url` → your console), and your tool in `mcps/`. | ||
| 3. **Register** as a workspace member: add the path to root `pyproject.toml` `members`, then `uv sync --all-packages` (installs your console into `.venv` for local runs). | ||
| 4. **Point** `data/` + `email-phishing-eval.yml` at your dataset. | ||
|
|
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift
Keep this page in one Diataxis quadrant.
Lines 12-30 combine component reference content with porting instructions. Keep this README as one documentation type. Move the other content to dedicated pages and add cross-links.
As per coding guidelines, each documentation page should fit one Diataxis quadrant.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@plugins/nemo-agents/examples/nemo-agent-config/email-phishing-agent/README.md`
around lines 12 - 30, Keep this README focused on a single Diataxis
documentation type by retaining either the component reference or the porting
guidance, not both. Move the other section to a dedicated documentation page and
add a clear cross-link between the pages, preserving the existing details and
examples.
Source: Coding guidelines
| ## Status | ||
|
|
||
| Live-validated for `--mode subprocess` (deploy → invoke → correct verdict; trace + `extract_iocs` call confirmed). Not yet exercised: container (`docker`/`k8s`) packaging, the Studio *Create Example* tile (ASTD‑08), and eval judge tuning (weights/prompt are starters). |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Add a Next Steps section.
Add cross-links to related registration, evaluation, and trace-inspection documentation after the status section.
As per coding guidelines, each documentation page must end with a Next Steps section with cross-links.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@plugins/nemo-agents/examples/nemo-agent-config/email-phishing-agent/README.md`
around lines 51 - 53, Add a “Next Steps” section immediately after the Status
section in the email-phishing-agent README, including cross-links to the related
registration, evaluation, and trace-inspection documentation. Ensure this
section is the final content on the page.
Source: Coding guidelines
Lead with a Parts table that pairs each file with what to change, an explicit cross-file 'keep in sync' note (console name; workspace member), and a compact mirrored Platform/Studio run to validate the swap. Signed-off-by: Nathan Walston <nwalston@nvidia.com>
…torial Single-quadrant tutorial (prerequisites-first, numbered steps with expected outcomes, Next Steps) mirroring the calculator-agent sibling; plain markdown (these example READMEs render on GitHub, not Sphinx, so no MyST tab-sets). Move the 'swap it for your own' how-to into CUSTOMIZE.md, cross-linked from Next Steps. Addresses CodeRabbit Diataxis/prereqs/next-steps/fence/quoting findings. Signed-off-by: Nathan Walston <nwalston@nvidia.com>
- iocs.py: guard urlsplit() against unparseable netlocs - build_dataset.py: reject blank sender; fail on duplicate subjects (id_key) - email-phishing-eval.yml: replace stale email-phishing-fabric identifiers Signed-off-by: Nathan Walston <nwalston@nvidia.com>
| def test_url_and_its_host_are_both_reported(): | ||
| result = extract_iocs("Click http://malicious-link.example.com/claim to continue.") | ||
| assert result["urls"] == ["http://malicious-link.example.com/claim"] | ||
| assert "malicious-link.example.com" in result["domains"] |
| def test_sender_domain_is_found_in_a_from_line(): | ||
| # The sender is a top phishing tell; extract_iocs must surface its domain. | ||
| result = extract_iocs("From: security-alerts@bank-verify.example.net\nVisit corp.example.org") | ||
| assert "bank-verify.example.net" in result["domains"] |
| # The sender is a top phishing tell; extract_iocs must surface its domain. | ||
| result = extract_iocs("From: security-alerts@bank-verify.example.net\nVisit corp.example.org") | ||
| assert "bank-verify.example.net" in result["domains"] | ||
| assert "corp.example.org" in result["domains"] |
|
Summary
Ports the email-phishing analyzer to a Platform-native
nemo-agents-spec-v1example under
plugins/nemo-agents/examples/email-phishing-fabric/.The NAT example proxy-passes classification to an opaque MCP server — beyond the
reach of trace views, prompt/hyperparameter tuning, and evaluation. This example
restructures it as a deepagents orchestrator that delegates the verdict to a
phishing subagent and calls a deterministic
extract_iocsMCP tool. Theprompt and model live in
agent.yaml(tunable), and the subagent task + toolcall each emit a trace span. The proxy-pass classifier becomes a Platform-visible,
tunable, evaluable step.
Changes
agent.yaml— deepagents orchestrator + adeclarative_subagent(
phishing-analyzer) with its ownsystem_promptand a loose YAML verdict(
is_likely_phishing/confidence/indicators/explanation) the top-levelmodel parses;
extract_iocswired as aharness_nativestdio MCP server.extract_iocsMCP tool — pure-regex URL/domain extraction (ported from theemail-security-analystexample) served over stdio by theemail-phishing-iocs-mcpconsole script; unit-tested.data/build_dataset.pyassembles anemailcolumn (
From:/Subject:/body) so the sender (a top phishing tell, and anextract_iocsinput) reaches the model; the NAT eval fedbodyonly. The evalconfig uses
question_key: email.email_phishing_fabricresolves.Verification
agent.yamlvalidates againstAgentConfig(nemo-agents-spec-v1,extra="forbid") and translates to a typed Fabric config viatranslate_agent_config.extract_iocstests: 8 passed.ruff check/ruff format/tyclean; pre-commit (incl. DCO) passed.Not exercised here: a live create/deploy/invoke against a running Platform
(needs
NVIDIA_API_KEY+ deepagents adapter runtime). Eval judge weights/promptare starters to tune per the evaluator plugin.
Tickets
Resolves the ASTD-370 restructuring decision (deepagents orchestrator + sub-agent),
ASTD-371 (include sender in analyzed input), and ASTD-372 (author the spec-v1
agent.yaml).Summary by CodeRabbit