Skip to content

CUJ2 inference E2E in the UAT runner + nightly both-intent (#1276)#1597

Merged
njhensley merged 1 commit into
NVIDIA:mainfrom
njhensley:ci/uat-cuj2-inference
Jul 2, 2026
Merged

CUJ2 inference E2E in the UAT runner + nightly both-intent (#1276)#1597
njhensley merged 1 commit into
NVIDIA:mainfrom
njhensley:ci/uat-cuj2-inference

Conversation

@njhensley

Copy link
Copy Markdown
Member

Summary

Bring real-cluster inference to CUJ1 parity (DC3): a phase_serve step deploys a Dynamo DynamoGraphDeployment, hits its OpenAI-compatible endpoint, and asserts a completion; and the nightly batch now runs both training and inference on both clouds, serialized through the shared reservation lease.

Motivation / Context

Inference was only a --no-cluster dry-run that no workflow invoked — no inference column existed on any cloud. This makes inference a real cluster phase, the counterpart of phase_train, and wires it end-to-end so nightly runs exercise a served endpoint and emit a signed bundle whose criteria map to inference-dynamo.

Fixes: #1276
Related: #1596 (follow-up: surface the build commit on the corroboration dashboard)

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Refactoring (no functional changes)
  • Build/CI/tooling

Component(s) Affected

  • CLI (cmd/aicr, pkg/cli)
  • API server (cmd/aicrd, pkg/server)
  • Recipe engine / data (pkg/recipe)
  • Bundlers (pkg/bundler, pkg/component/*)
  • Collectors / snapshotter (pkg/collector, pkg/snapshotter)
  • Validator (pkg/validator)
  • Core libraries (pkg/errors, pkg/k8s)
  • Docs/examples (docs/, examples/)
  • Other: UAT runner + workflows (tests/uat/**, .github/workflows/uat-*), reservation broker (pkg/uatbroker, tools/uat-broker)

Implementation Notes

  • phase_serve (tests/uat/{aws,gcp}/run): deploys the KAI queue + DRA ResourceClaim + Frontend/decode-Worker DynamoGraphDeployment (inlined from demos/workloads/inference/vllm-agg.yaml), waits for pod readiness, port-forwards the frontend, POST /v1/chat/completions, asserts a non-empty completion. Fails closed (captured pod logs/events under serve-logs/) on a non-ready deployment or invalid completion, mirroring phase_train. Node scheduling/model are SERVE_*-overridable; tolerations are a portable superset across both clouds.
  • Intent selection: run all picks train vs serve from the config's recipe intent; the workflows add an intent-gated UAT - serve step mirroring train.
  • Platform cross-check in phase_conformance: the declared platform must match the deployed workload CRD (dynamographdeployments.nvidia.com / trainjobs.trainer.kubeflow.org), since the TestGrid coordinate is author-declared and otherwise cluster-unverifiable.
  • Nightly cadence decision: both intents run nightly on both clouds, folded into the version matrix as serialized cells (version outer-loop / intent inner-loop) through the shared per-reservation lease — no second cron, no contention, and main lands both intents before any release cell. Data-driven via a new nightly-intents list in infra/uat/reservations.yaml (pkg/uatbroker). Trade-off: this ~doubles a reservation's cell count; the batch time-box drops oldest cells first (tune previous_n / deadline_offset_hours), documented in docs/contributor/uat.md.
  • Version stamping: the main-cell binary is stamped version=main + commit + date via ldflags so evidence/results read main instead of the un-stamped dev. Because a real commit would otherwise redirect validator images :latest:sha-<commit> (which may not exist for a feature-branch ref), main-cell validator images are pinned to the run's own :uat-<run_id> build — mirroring the existing agent pin. Release cells are unaffected (empty override → released :vX.Y.Z).

Testing

# Targeted gates (Go changes are scoped to the broker):
go test -race ./pkg/uatbroker/... ./tools/uat-broker/...   # ok, 96.9% / 91.5%
golangci-lint run -c .golangci.yaml ./pkg/uatbroker/... ./tools/uat-broker/...  # 0 issues
yamllint -c .yamllint.yaml .github/workflows/uat-*.yaml infra/uat/reservations.yaml  # clean
shellcheck tests/uat/aws/run tests/uat/gcp/run   # clean
# ldflags verified: `aicr --version` -> "main (commit: <sha>, date: ...)"

Full make qualify (e2e/grype) was not run — its e2e leg provisions real infrastructure. Per the issue's test plan, the real-hardware inference run is a manual workflow_dispatch intent=inference during bring-up; the --no-cluster chainsaw dry-run continues to validate the inference AICRConfig resolves.

Risk Assessment

  • Low — Isolated change, well-tested, easy to revert
  • Medium — Touches multiple components or has broader impact
  • High — Breaking change, affects critical paths, or complex rollout

Rollout notes: No breaking changes. One behavior change to flag: main-cell validator images now resolve to the run's :uat-<run_id> build instead of :latest (consistent with the agent pin; removes known :latest staleness). nightly-intents defaults to [training] when absent, so the change is backward-compatible. The nightly cell count roughly doubles — see the time-box tuning note in docs/contributor/uat.md.

Checklist

  • Tests pass locally (make test with -race) — ran -race on the changed Go packages (pkg/uatbroker, tools/uat-broker)
  • Linter passes (make lint) — golangci-lint (affected pkgs, 0 issues) + yamllint + shellcheck
  • I did not skip/disable tests to make CI green
  • I added/updated tests for new functionality
  • I updated docs if user-facing behavior changed
  • Changes follow existing patterns in the codebase
  • Commits are cryptographically signed (git commit -S)

@njhensley njhensley requested review from a team as code owners July 2, 2026 21:41
@njhensley njhensley added the theme/ci-dx CI pipelines, developer experience, and build tooling label Jul 2, 2026
@njhensley njhensley self-assigned this Jul 2, 2026
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds inference serve execution to the AWS and GCP UAT runners, including new serve phases, conformance platform checks, and intent-based dispatch. It updates the AWS and GCP workflows to run serve for inference intent, stamp built binaries, and capture serve artifacts. It also adds nightly-intents to UAT broker reservations, validation, CLI output, nightly batch dispatch, reservations config, and related tests, plus documentation updates.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • NVIDIA/aicr#1587: Shares the same UAT broker scheduling area and intent-driven reservation modeling.

Suggested reviewers: mchmarny

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly names the main change: CUJ2 inference E2E plus nightly both-intent workflow updates.
Description check ✅ Passed The description matches the changeset, covering real-cluster inference, nightly intent handling, and related docs/tooling updates.
Linked Issues check ✅ Passed The PR adds phase_serve, intent-based dispatch, nightly intent batching, conformance checks, and docs updates required by #1276.
Out of Scope Changes check ✅ Passed The additional workflow and broker changes support the inference and nightly cadence work; no clearly unrelated scope stands out.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 @.github/workflows/uat-aws.yaml:
- Around line 182-201: The main-cell build step in the workflow currently only
prints `./aicr --version`, so a broken ldflags stamp could still pass with
default metadata. Update the `run` block that builds `./aicr` and calls `./aicr
--version` to assert the output contains both `main` and `${COMMIT_SHA}`, so the
step fails if `github.com/NVIDIA/aicr/pkg/cli.version` or
`github.com/NVIDIA/aicr/pkg/cli.commit` are not stamped correctly.

In @.github/workflows/uat-gcp.yaml:
- Around line 180-199: The workflow step that builds and runs ./aicr --version
currently does not verify that the ldflags stamping worked, so a broken
github.com/NVIDIA/aicr/pkg/cli.version, commit, or date target could still pass
unnoticed. After the build in the main-only version stamping step, assert that
the version output includes the expected main value, the COMMIT_SHA env value,
and the BUILD_DATE used in the go build command, so the step fails if the binary
still reports the default metadata.

In `@tests/uat/aws/run`:
- Around line 555-560: The current fast-fail check only detects pods with
phase=Failed, so it does not catch crash loops or image-pull failures that
remain in Running or Pending. Update the guard in the workload-pod polling logic
to also inspect container waiting states such as CrashLoopBackOff,
ImagePullBackOff, and ErrImagePull (using the same pods_json/jq flow), and
adjust the surrounding comment so it only claims behavior the check actually
enforces.

In `@tests/uat/gcp/run`:
- Around line 555-560: The fast-fail check only looks at pod phase in the run
script, so it misses crash-loop and image-pull failures. Update the pod health
guard in the workload polling logic to also inspect container waiting states
(for example in the same jq-based check used for pods_json) and treat reasons
like CrashLoopBackOff, ImagePullBackOff, and ErrImagePull as terminal-bad; if
you choose not to broaden the check, then soften the comment so it only
describes phase=Failed detection. Use the existing pod polling block and the
related logic mirrored in tests/uat/aws/run to keep behavior consistent.
🪄 Autofix (Beta)

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: ASSERTIVE

Plan: Enterprise

Run ID: 3021cd14-fac2-439b-a318-c8a0032fda0a

📥 Commits

Reviewing files that changed from the base of the PR and between 1d3c2f4 and c3274ea.

📒 Files selected for processing (14)
  • .github/workflows/uat-aws.yaml
  • .github/workflows/uat-gcp.yaml
  • .github/workflows/uat-nightly-batch.yaml
  • docs/contributor/uat.md
  • infra/uat/reservations.yaml
  • pkg/uatbroker/model.go
  • pkg/uatbroker/registry.go
  • pkg/uatbroker/registry_test.go
  • tests/uat/aws/run
  • tests/uat/aws/tests/cuj2-inference/chainsaw-test.yaml
  • tests/uat/gcp/run
  • tools/uat-broker/README.md
  • tools/uat-broker/main.go
  • tools/uat-broker/main_test.go

Comment thread .github/workflows/uat-aws.yaml Outdated
Comment thread .github/workflows/uat-gcp.yaml Outdated
Comment thread tests/uat/aws/run Outdated
Comment thread tests/uat/gcp/run Outdated
…VIDIA#1276)

Bring real-cluster inference to CUJ1 parity (DC3).

phase_serve (tests/uat/{aws,gcp}/run): deploy a Dynamo DynamoGraphDeployment
(KAI queue + DRA claim + Frontend/decode-Worker graph), wait for readiness,
port-forward the frontend, issue an OpenAI-compatible /v1/chat/completions
request, and assert a non-empty completion. Fails closed with captured
pod logs/events on a non-ready deployment or invalid completion, mirroring
phase_train. run all selects train vs serve by the config's recipe intent.
phase_conformance cross-checks the declared platform against the deployed
workload CRD (dynamo vs kubeflow), since the TestGrid coordinate is otherwise
cluster-unverifiable.

Workflows (uat-{aws,gcp}.yaml): add an intent-gated 'UAT - serve' step
mirroring train; summary row and serve-logs artifact.

Nightly cadence: both training and inference run nightly on both clouds,
folded into the version matrix as serialized cells (version outer / intent
inner loop) through the shared per-reservation lease -- no second cron, no
contention, and main lands both intents before any release cell. Driven by a
new data-driven nightly-intents list in infra/uat/reservations.yaml
(pkg/uatbroker + uat-nightly-batch.yaml).

Stamp the main-cell binary via ldflags (version=main, commit, date) so the
emitted recipe/evidence report 'main' instead of the un-stamped 'dev'; pin
main-cell validator images to the run's own :uat-<run_id> build (mirroring the
agent pin) so the version label change does not redirect image resolution.

Docs: document the inference UAT path, phase_serve, and the nightly cadence in
docs/contributor/uat.md; fix the chainsaw test's demos/cuj2-inference.md ref.

Signed-off-by: Nathan Hensley <[email protected]>
@njhensley njhensley force-pushed the ci/uat-cuj2-inference branch from c3274ea to 59f6f5a Compare July 2, 2026 22:02

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
.github/workflows/uat-nightly-batch.yaml (1)

147-147: 🩺 Stability & Availability | 🔵 Trivial

Nightly cell count doubles per reservation; time-box budget unchanged.

With nightly-intents: [training, inference] now set on both reservations (infra/uat/reservations.yaml), each version now dispatches two full cells instead of one, but DEADLINE_OFFSET_HOURS still defaults to '5' here. The graceful drop-oldest-first logic (break 2) will mask this, but confirm that 5h is still sufficient to run main × 2 intents + previous_n × 2 intents before older release cells start getting silently dropped every night.

Also applies to: 203-215

🤖 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 @.github/workflows/uat-nightly-batch.yaml at line 147, The nightly batch
deadline budget may now be too tight because both reservations dispatch two
intents per version, doubling the number of cells while DEADLINE_OFFSET_HOURS in
the uat-nightly-batch workflow still defaults to 5 hours. Review the scheduling
and drop-oldest-first behavior around the nightly batch flow, especially the
inputs handling in the workflow and the reservation intent fanout, and either
पुष्टि that 5 hours remains sufficient for main and previous_n across both
intents or increase the default/override accordingly.
🤖 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 `@docs/contributor/uat.md`:
- Line 53: Update the CUJ reference in the `intent=inference` → `phase_serve`
description so it matches the served-inference flow described by
`demos/cuj2-inference.md`. Replace the incorrect “CUJ1 parity” wording with the
correct CUJ2 reference in that same sentence, keeping the rest of the
`phase_serve` explanation unchanged.

---

Outside diff comments:
In @.github/workflows/uat-nightly-batch.yaml:
- Line 147: The nightly batch deadline budget may now be too tight because both
reservations dispatch two intents per version, doubling the number of cells
while DEADLINE_OFFSET_HOURS in the uat-nightly-batch workflow still defaults to
5 hours. Review the scheduling and drop-oldest-first behavior around the nightly
batch flow, especially the inputs handling in the workflow and the reservation
intent fanout, and either पुष्टि that 5 hours remains sufficient for main and
previous_n across both intents or increase the default/override accordingly.
🪄 Autofix (Beta)

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: ASSERTIVE

Plan: Enterprise

Run ID: e139afb1-f3a4-4f5e-8bf6-9b8c400bfaa4

📥 Commits

Reviewing files that changed from the base of the PR and between c3274ea and 59f6f5a.

📒 Files selected for processing (14)
  • .github/workflows/uat-aws.yaml
  • .github/workflows/uat-gcp.yaml
  • .github/workflows/uat-nightly-batch.yaml
  • docs/contributor/uat.md
  • infra/uat/reservations.yaml
  • pkg/uatbroker/model.go
  • pkg/uatbroker/registry.go
  • pkg/uatbroker/registry_test.go
  • tests/uat/aws/run
  • tests/uat/aws/tests/cuj2-inference/chainsaw-test.yaml
  • tests/uat/gcp/run
  • tools/uat-broker/README.md
  • tools/uat-broker/main.go
  • tools/uat-broker/main_test.go

Comment thread docs/contributor/uat.md
@njhensley njhensley merged commit 52e7b78 into NVIDIA:main Jul 2, 2026
34 checks passed
@njhensley njhensley deleted the ci/uat-cuj2-inference branch July 2, 2026 22:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/ci area/docs area/infra area/tests size/XL theme/ci-dx CI pipelines, developer experience, and build tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DC3 — CUJ2 inference E2E in the UAT runner/workflows

2 participants