Skip to content

feat(evaluator): give a taskset its own files - #1234

Open
SandyChapman wants to merge 1 commit into
mainfrom
taskset-file-refs/schapman
Open

feat(evaluator): give a taskset its own files#1234
SandyChapman wants to merge 1 commit into
mainfrom
taskset-file-refs/schapman

Conversation

@SandyChapman

@SandyChapman SandyChapman commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Summary

A taskset can need files that every member uses and none owns — a shared metric script, a fixture, a schema. A Harbor dataset ships exactly this beside its tasks. There was nowhere to put them, so the only option was encoding a JSON blob into metadata: unqueryable, untyped, and invisible to anything that does not already know the convention.

This adds files_ref to the taskset: one Files reference for the whole grouping, the same shape as bundle_ref and archive_ref.

Built on #1071, which has since merged. Rebased onto main with --onto so #1071's pre-merge commits are dropped rather than replayed against its squashed form; the diff is this change alone.

Changes

  • TasksetFilesRef in api/fields.py — a str constrained to FILESET_REF_PATTERN.
  • files_ref on TasksetEntity, TasksetRevisionEntity, Taskset, and TasksetInput; threaded through create, replace, and both DTO mappings.
  • Regenerated plugins/nemo-evaluator/openapi/openapi.yaml — two string fields, no new schema.
  • New tests in tests/api/service/test_taskset_files.py.

Three decisions worth review:

  1. One reference, not a list of per-file entries. A taskset's files are a directory; the fileset already knows what it contains. A list of per-file references would carry no information the fileset does not hold, and would let the two disagree about what the taskset ships.
  2. It is content, not annotation. The revision digest covers it, so repointing or clearing it publishes a revision, and a pinned revision keeps naming the files it was published with.
  3. Pinning the bytes is the caller's to arrange, by referencing a location that is not rewritten — a fileset written once, or a content-addressed prefix inside a shared one. A reference into a location that is later rewritten resolves to whatever it holds when read, which is the contract the Files service gives every other consumer. This is documented on the field rather than left implicit, because "pinned revision" plus "unpinned files" is surprising otherwise.

One consequence to flag: FILESET_REF_PATTERN requires the #fragment, so a bare workspace/fileset is not accepted — a taskset reference is workspace/fileset#prefix, where the fragment names the prefix the files sit under rather than a single file. I kept the shared pattern rather than loosening it for one field; making a bare fileset legal would be a deliberate change to the pattern every ref uses.

The field is additive and defaults to unset, so existing tasksets are unchanged and their revision digests do not move.

Type of Change

  • Code change (feature, bug fix, or refactor)

Quality Gates

  • Tests added or updated for changed behavior
  • Documentation not applicable — justification: the API surface is documented by its field descriptions and the regenerated OpenAPI spec; no prose docs describe taskset fields today.

Verification

  • Pull request title follows the repository's Conventional Commit format
  • Every commit includes an appropriate Signed-off-by: trailer
  • uv run pre-commit run -a passes, or any blocked checks are identified below
  • Targeted tests pass, or tests are marked not applicable above
  • No secrets, API keys, or credentials are included

Targeted validation:

  • uv run --frozen pytest plugins/nemo-evaluator/tests --ignore=plugins/nemo-evaluator/tests/integration804 passed (796 before this change on the same base; 8 new). No existing test needed changing, which is the additive-field claim holding rather than asserted.
  • uv run --frozen pytest plugins/nemo-evaluator/tests/api/service/test_taskset_files.py8 passed: round-trip, revision-on-repoint, revision-on-clear, no-revision-on-identical, a pinned revision keeping its original reference after the head moves, unset default, and reference-shape validation (including that a bare workspace/fileset is rejected).
  • make refresh-openapi — regenerates plugins/nemo-evaluator/openapi/openapi.yaml only, idempotent on a second run, no unrelated plugin spec drifted.
  • tools/lint/lint-python-types.sh (CI's exact type gate) — exit 0.
  • uv run ruff check / ruff format --check on the changed tree — clean.
  • uv run pre-commit run -a — all substantive hooks pass: ruff, ruff format, Run ty typechecks, Check config reference doc is up to date, Check for uv.lock drift, Check CI and Flox uv versions, Check Make and Flox Python versions, Check Node.js and pnpm versions, Fix copyright headers, Plugins must not import from nmp-common, check for merge conflicts. Three hooks could not run in my local environment, which is why the gate above is left unchecked — neither is reported as passing:
    • Run uv lock with platform uv — requires uv 0.9.14; local uv is 0.9.30. No pyproject.toml is touched, and Check for uv.lock drift passes.
    • Run UI lint-stagedpnpm unavailable locally (untrusted mise.toml). No web/ files are touched.
    • Helm Docs — the helm-docs binary is not installed locally (the hook moved off a container on main). It modifies nothing here; no Helm files are touched.

Re-ran the full suite, the type gate, ruff, and make refresh-openapi after the rebase onto main; the committed spec needed no regeneration against it.

Note for reviewers: pytest plugins/nemo-evaluator/tests without --ignore fails to collect, because tests/test_evaluate_job.py and tests/integration/test_evaluate_job.py share a basename and test directories are not modules. That is pre-existing on the base branch and unrelated to this change.

Summary by CodeRabbit

  • New Features

    • Tasksets can now reference taskset-owned files using a validated fileset path.
    • File references are included in taskset details and revisions.
    • Updating or clearing a file reference publishes a new taskset revision.
  • Bug Fixes

    • Pinned revisions retain the file reference associated with their publication.
  • Tests

    • Added coverage for file-reference persistence, validation, updates, clearing, and default behavior.

@SandyChapman
SandyChapman requested review from a team as code owners August 11, 2026 15:07
@github-actions github-actions Bot added the feat label Aug 11, 2026
@SandyChapman
SandyChapman force-pushed the taskset-file-refs/schapman branch from e746c6e to 9e342b0 Compare August 11, 2026 16:28
Base automatically changed from harbor-task-kind/schapman to main August 11, 2026 20:01
A grouping can need files that every member uses and none owns — a shared
metric script, a fixture, a schema. A Harbor dataset ships exactly this beside
its tasks. There was nowhere to put them, so the only option was to encode a
JSON blob in `metadata`, which is unqueryable, untyped, and invisible to
anything that does not know the convention.

`files_ref` is one Files reference for the whole grouping, the same shape as
`bundle_ref` and `archive_ref`. A taskset's files are a directory, not a set of
independently addressed blobs: the fileset already knows what it contains, so a
list of per-file references would carry no information it does not hold and
would let the two disagree about what the taskset ships.

It is a first-class field rather than an annotation because it is content — the
revision digest covers it, so repointing or clearing it publishes a revision and
a pinned revision keeps naming the files it was published with.

Pinning the bytes is arranged by referencing a location that is not rewritten: a
fileset written once, or a content-addressed prefix inside a shared one. A
reference into a location that is later rewritten resolves to whatever it holds
when read, which is the contract the Files service gives every other consumer.
The alternative — a per-file digest stored beside each reference — buys pinning
the entity store cannot enforce anyway, and duplicates state the Files service
owns.

The revision entity carries the reference too, but needs no resolution step:
unlike a member ref, which is resolved from a possibly-tag-pinned form to an
exact digest at publish time, a Files reference already names an exact location.

The field is additive and defaults to unset, so an existing taskset is unchanged
and its revision digest does not move.

Signed-off-by: Sandy Chapman <schapman@nvidia.com>
@SandyChapman
SandyChapman force-pushed the taskset-file-refs/schapman branch from 9e342b0 to 34ee878 Compare August 12, 2026 19:38
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: e0964cde-5996-418c-bbb8-b1bbb7368c55

📥 Commits

Reviewing files that changed from the base of the PR and between 320c34e and 34ee878.

📒 Files selected for processing (6)
  • plugins/nemo-evaluator/openapi/openapi.yaml
  • plugins/nemo-evaluator/src/nemo_evaluator/api/fields.py
  • plugins/nemo-evaluator/src/nemo_evaluator/api/schemas.py
  • plugins/nemo-evaluator/src/nemo_evaluator/api/service/taskset_service.py
  • plugins/nemo-evaluator/src/nemo_evaluator/entities.py
  • plugins/nemo-evaluator/tests/api/service/test_taskset_files.py

📝 Walkthrough

Walkthrough

Changes

Taskset files reference

Layer / File(s) Summary
Files reference contract
plugins/nemo-evaluator/openapi/openapi.yaml, plugins/nemo-evaluator/src/nemo_evaluator/api/fields.py, plugins/nemo-evaluator/src/nemo_evaluator/api/schemas.py, plugins/nemo-evaluator/src/nemo_evaluator/entities.py, plugins/nemo-evaluator/tests/api/service/test_taskset_files.py
Adds optional files_ref fields to taskset schemas and entities. Validates the workspace/fileset#path format.
Reference persistence and revisions
plugins/nemo-evaluator/src/nemo_evaluator/api/service/taskset_service.py, plugins/nemo-evaluator/tests/api/service/test_taskset_files.py
Persists files_ref during creation and replacement. Includes it in taskset and revision DTOs. Tests changed, cleared, pinned, identical, and default references.

Sequence Diagram(s)

sequenceDiagram
  participant TasksetInput
  participant TasksetService
  participant TasksetEntity
  participant TasksetRevisionEntity
  TasksetInput->>TasksetService: Submit files_ref
  TasksetService->>TasksetEntity: Persist files_ref
  TasksetService->>TasksetRevisionEntity: Publish changed files_ref
  TasksetService-->>TasksetInput: Return files_ref
Loading

Possibly related PRs

Suggested reviewers: arpitsardhana, ngoncharenko

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding taskset-owned files support through the new files reference.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch taskset-file-refs/schapman

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 32234/40906 78.8% 63.7%
Integration Tests 18647/38832 48.0% 20.7%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant