Skip to content

Commit 142e783

Browse files
SandyChapmanclaude
andcommitted
docs(evaluator): update the evaluator skill for the task spec shape
Split out of #1071 so that PR is not held behind the NVSkills gate. The gate cannot currently pass for this repo: tier 3 is invoked with `--env-mode local`, whose bubblewrap sandbox fails its smoke test on the nvcarps runners, so nothing is evaluated and the gate blocks on empty coverage. This PR carries the whole cost of that, and can wait for the infrastructure fix without blocking the storage change. `store_resources` moves to the discriminated `spec`, and `resources.md` teaches held-out ground truth on a *stored* task rather than an inline one — the skill steered users to `AgentEvalTaskInput` only because the stored schema had no `reference` field, and #1071 gives it one. Routing them back to inline would cost them tasksets and revision pinning for no reason. Until this lands, the published skill documents the pre-`spec` shape, which no longer validates against #1071. Merge promptly once the gate is healthy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Sandy Chapman <schapman@nvidia.com>
1 parent 134f5f6 commit 142e783

3 files changed

Lines changed: 46 additions & 23 deletions

File tree

plugins/nemo-evaluator/tests/test_skill_examples.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -445,13 +445,20 @@ def test_multiple_metric_platform_submission_uses_cli() -> None:
445445
assert "nemo evaluator evaluate submit --spec-file multi-metric.json" in section
446446

447447

448-
def test_resources_show_inline_task_before_held_out_reference_guidance() -> None:
448+
def test_resources_show_a_stored_task_carrying_held_out_reference() -> None:
449+
"""Held-out ground truth belongs on a *stored* task, so it survives taskset expansion.
450+
451+
The skill used to steer users to an inline ``AgentEvalTaskInput`` because the stored spec had no
452+
``reference`` field. It has one now, and routing them back to inline would cost them tasksets
453+
and revision pinning for no reason.
454+
"""
449455
reference = (_repo_root() / "skills/nemo-evaluator-plugin/references/resources.md").read_text(encoding="utf-8")
450456

451-
example_position = reference.index("inline_task = AgentEvalTaskInput(")
457+
example_position = reference.index('"capital-france-graded"')
452458
guidance_position = reference.index("Stored tasks keep metric references.")
453459
assert example_position < guidance_position
454460
assert 'reference={"expected": "Paris"}' in reference
461+
assert "EvaluatorTaskDefinition(" in reference
455462

456463

457464
def test_agent_evaluation_shows_how_to_retrieve_stored_trials() -> None:

skills/nemo-evaluator-plugin/assets/examples/plugin_sdk_examples.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ def submit_and_collect(client: Any, output_dir: Path) -> tuple[Any, Path]:
5858
def store_resources(client: Any) -> None:
5959
"""Store one metric, task, and taskset."""
6060
from nemo_evaluator.api.schemas import (
61+
EvaluatorTaskDefinition,
6162
MetricRef,
6263
TaskInput,
6364
TaskInputs,
@@ -72,14 +73,17 @@ def store_resources(client: Any) -> None:
7273
client.evaluator.tasks.create(
7374
"capital-france",
7475
task=TaskInput(
75-
intent="Name the capital of France.",
76-
inputs=TaskInputs(instruction="What is the capital of France?"),
77-
metrics=[MetricRef("default/answer-exact")],
76+
spec=EvaluatorTaskDefinition(
77+
kind="evaluator",
78+
intent="Name the capital of France.",
79+
inputs=TaskInputs(instruction="What is the capital of France?"),
80+
metrics=[MetricRef("answer-exact")],
81+
),
7882
),
7983
)
8084
client.evaluator.tasksets.create(
8185
"geography",
82-
taskset=TasksetInput(tasks=[TaskRef("default/capital-france")]),
86+
taskset=TasksetInput(tasks=[TaskRef("capital-france")]),
8387
)
8488

8589

skills/nemo-evaluator-plugin/references/resources.md

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ new versioned name.
2020

2121
```python
2222
from nemo_evaluator.api.schemas import (
23+
EvaluatorTaskDefinition,
2324
MetricRef,
2425
TaskInput,
2526
TaskInputs,
@@ -43,27 +44,29 @@ client.evaluator.metrics.create(
4344
client.evaluator.tasks.create(
4445
"capital-france",
4546
task=TaskInput(
46-
intent="Name the capital of France.",
47-
inputs=TaskInputs(instruction="What is the capital of France?"),
48-
metrics=[MetricRef("default/answer-exact")],
47+
spec=EvaluatorTaskDefinition(
48+
kind="evaluator",
49+
intent="Name the capital of France.",
50+
inputs=TaskInputs(instruction="What is the capital of France?"),
51+
metrics=[MetricRef("answer-exact")],
52+
),
4953
),
5054
)
5155

5256
client.evaluator.tasksets.create(
5357
"geography",
5458
taskset=TasksetInput(
5559
description="Geography smoke tasks.",
56-
tasks=[TaskRef("default/capital-france")],
60+
tasks=[TaskRef("capital-france")],
5761
),
5862
)
5963
```
6064

61-
For a task that needs held-out ground truth invisible to the agent, keep the reference on an
62-
inline `AgentEvalTaskInput` and use a metric that reads it:
65+
For a task that needs held-out ground truth invisible to the agent, put it in `reference` and
66+
use a metric that reads it. This works on a stored task, so it survives into taskset-driven runs:
6367

6468
```python
65-
from nemo_evaluator.api.schemas import MetricRef, TaskInputs
66-
from nemo_evaluator.jobs.agent_spec import AgentEvalTaskInput
69+
from nemo_evaluator.api.schemas import EvaluatorTaskDefinition, MetricRef, TaskInput, TaskInputs
6770
from nemo_evaluator_sdk import ExactMatchMetric
6871

6972
client.evaluator.metrics.create(
@@ -74,19 +77,28 @@ client.evaluator.metrics.create(
7477
),
7578
)
7679

77-
inline_task = AgentEvalTaskInput(
78-
id="capital-france",
79-
intent="Name the capital of France.",
80-
inputs=TaskInputs(instruction="What is the capital of France?"),
81-
reference={"expected": "Paris"},
82-
metrics=[MetricRef("default/answer-from-reference")],
80+
client.evaluator.tasks.create(
81+
"capital-france-graded",
82+
task=TaskInput(
83+
spec=EvaluatorTaskDefinition(
84+
kind="evaluator",
85+
intent="Name the capital of France.",
86+
inputs=TaskInputs(instruction="What is the capital of France?"),
87+
reference={"expected": "Paris"},
88+
metrics=[MetricRef("answer-from-reference")],
89+
),
90+
),
8391
)
8492
```
8593

94+
`reference` is surfaced to metrics but never seeded into the agent's workspace or shown to the
95+
agent, so a metric can grade against artifacts the agent cannot edit. It is held out from the
96+
*agent*, not from the API — anyone who can read the task can read it. It is covered by the revision
97+
digest, so changing ground truth publishes a new revision.
98+
8699
Stored tasks keep metric references. Inline task metrics are normalized into
87-
content-addressed derived metrics. The stored-task example uses an output-only
88-
metric because stored tasks do not carry the grader-only `reference` field; use
89-
an inline `AgentEvalTaskInput` when held-out per-task data is required.
100+
content-addressed derived metrics. The same `reference` field is available on an inline
101+
`AgentEvalTaskInput` for one-off submissions.
90102

91103
## Retrieve, list, and delete
92104

0 commit comments

Comments
 (0)