Skip to content

fix(inference): preflight NEMOCLAW_VLLM_MODEL on sandbox connect #3492

fix(inference): preflight NEMOCLAW_VLLM_MODEL on sandbox connect

fix(inference): preflight NEMOCLAW_VLLM_MODEL on sandbox connect #3492

Workflow file for this run

# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
name: E2E / Advisor
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
workflow_dispatch:
inputs:
base_ref:
description: Base ref to diff against
required: false
default: origin/main
head_ref:
description: Head ref to diff
required: false
default: HEAD
target_repo:
description: Optional repo to analyze, e.g. NVIDIA/NemoClaw
required: false
type: string
default: ""
target_pr:
description: Optional pull request number in target_repo to analyze
required: false
type: string
default: ""
target_base:
description: Base branch to use with target_repo/target_pr manual analysis
required: false
type: string
default: main
run_analysis:
description: Run E2E recommendation analysis
required: false
type: boolean
default: true
permissions:
contents: read
# `actions: write` lets the trusted advisor dispatcher trigger the selected
# E2E workflow for eligible NVIDIA-owned PRs after validating the requested
# jobs against the target workflow's own selective-dispatch predicates.
actions: write
# `pull-requests: write` is required for the advisor to post (or update) a
# comment on a PR via POST /repos/:o/:r/issues/:n/comments. Even though the
# endpoint lives under `/issues/`, for GITHUB_TOKEN the permission that
# actually gates PR comments is `pull-requests`, not `issues`. With only
# `pull-requests: read`, the endpoint returns 403 "Resource not accessible
# by integration" despite `issues: write`. See the comment step below and
# https://github.com/orgs/community/discussions/56632.
pull-requests: write
issues: write
concurrency:
group: e2e-advisor-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
advise:
name: E2E recommendation
if: ${{ github.repository == 'NVIDIA/NemoClaw' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'NVIDIA/NemoClaw') }}
runs-on: ubuntu-latest
timeout-minutes: 20
env:
# Pin the Pi SDK to a known-good version. Updates should go through
# the normal dependency-review path (e.g. Dependabot) so a compromised
# upstream release cannot execute automatically in this secret-bearing job.
PI_SDK_VERSION: "0.74.0"
# Keep the advisor timeout shorter than the GitHub job timeout so the
# analyzer can write failure artifacts, summaries, and PR comments.
E2E_ADVISOR_TIMEOUT_MS: "900000"
E2E_ADVISOR_HEARTBEAT_MS: "60000"
# Cap automatic fan-out from noisy/flaky advisor recommendations until
# the dispatcher has enough production history to justify a higher limit.
E2E_ADVISOR_AUTO_DISPATCH_MAX_JOBS: "6"
# The trusted checkout always lives at this path and is the only source
# of advisor implementation code executed in this job. The PR content,
# if any, is mounted as read-only analysis data under PR_WORKDIR.
ADVISOR_DIR: ${{ github.workspace }}/advisor
steps:
# Trusted-code boundary: the advisor implementation (analyze.mts,
# dispatch.mts, comment.mts, schema.json) is always fetched
# from the main branch of this repo, regardless of what the PR changed.
# This prevents a future PR from modifying advisor code and executing
# arbitrary Node in a job that holds API keys and a write-scoped token.
- name: Checkout trusted advisor code (main)
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: NVIDIA/NemoClaw
ref: main
path: advisor
persist-credentials: false
# PR content is checked out as inert analysis data only. Nothing from
# this directory is invoked as code by the advisor scripts; it is read
# via `git diff` / `fs.readFileSync` from inside the trusted scripts.
- name: Checkout PR workspace (read-only data)
if: ${{ github.event_name == 'pull_request' }}
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.event.pull_request.head.sha }}
path: pr-workdir
fetch-depth: 0
persist-credentials: false
# For manual workflow_dispatch against this repo (no target_repo given),
# check out the ref the maintainer dispatched against as the workdir.
# workflow_dispatch requires write access, so this ref is trusted for
# the purposes of analysis-data access, though we still never execute
# it as code.
- name: Checkout dispatch workspace (read-only data)
if: ${{ github.event_name == 'workflow_dispatch' && inputs.target_repo == '' }}
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
path: pr-workdir
fetch-depth: 0
persist-credentials: false
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: "22"
- name: Set default advisor workdir
run: echo "ADVISOR_WORKDIR=$GITHUB_WORKSPACE/pr-workdir" >> "$GITHUB_ENV"
- name: Prepare target PR checkout
if: ${{ github.event_name == 'workflow_dispatch' && inputs.target_repo != '' && inputs.target_pr != '' }}
env:
TARGET_REPO: ${{ inputs.target_repo }}
TARGET_PR: ${{ inputs.target_pr }}
TARGET_BASE: ${{ inputs.target_base }}
run: |
TARGET_DIR=/tmp/e2e-advisor-target
rm -rf "$TARGET_DIR"
mkdir -p "$TARGET_DIR"
git -C "$TARGET_DIR" init
git -C "$TARGET_DIR" remote add target "https://github.com/${TARGET_REPO}.git"
git -C "$TARGET_DIR" fetch --no-tags target "$TARGET_BASE"
git -C "$TARGET_DIR" fetch --no-tags target "pull/${TARGET_PR}/head:refs/remotes/target/pr-${TARGET_PR}"
git -C "$TARGET_DIR" checkout --detach "refs/remotes/target/pr-${TARGET_PR}"
echo "ADVISOR_WORKDIR=$TARGET_DIR" >> "$GITHUB_ENV"
# Pinned SDK install. The version is held in PI_SDK_VERSION above so
# the pin is reviewed as a code change, not silently inherited from
# whatever @latest points to at runtime. Install into an isolated temp
# prefix, then expose that dependency tree to the trusted advisor script.
- name: Install Pi SDK
run: |
PI_SDK_DIR="$RUNNER_TEMP/pi-sdk"
npm install --prefix "$PI_SDK_DIR" --ignore-scripts --no-save --package-lock=false --before=2026-05-14T00:00:00.000Z "@earendil-works/pi-coding-agent@${PI_SDK_VERSION}"
rm -rf "$ADVISOR_DIR/node_modules"
ln -s "$PI_SDK_DIR/node_modules" "$ADVISOR_DIR/node_modules"
- name: Run E2E recommendation advisor
id: analysis
continue-on-error: true
env:
BASE_REF: ${{ github.event_name == 'pull_request' && format('origin/{0}', github.base_ref) || (github.event_name == 'workflow_dispatch' && inputs.target_repo != '' && inputs.target_pr != '' && format('target/{0}', inputs.target_base) || inputs.base_ref) }}
HEAD_REF: ${{ github.event_name == 'pull_request' && 'HEAD' || (github.event_name == 'workflow_dispatch' && inputs.target_repo != '' && inputs.target_pr != '' && 'HEAD' || inputs.head_ref) }}
E2E_ADVISOR_RUN_ANALYSIS: ${{ github.event_name == 'workflow_dispatch' && inputs.run_analysis == false && '0' || '1' }}
# Preferred E2E advisor secret.
E2E_ADVISOR_API_KEY: ${{ secrets.PI_E2E_ADVISOR_API_KEY }}
# Optional local-provider-compatible fallback for future upstream/external-advisor use.
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
cd "$ADVISOR_WORKDIR"
node --experimental-strip-types "$ADVISOR_DIR/tools/e2e-advisor/analyze.mts" \
--base "$BASE_REF" \
--head "$HEAD_REF" \
--schema "$ADVISOR_DIR/tools/e2e-advisor/schema.json" \
--out-dir "$GITHUB_WORKSPACE/artifacts/e2e-advisor"
- name: Run scenario E2E advisor
id: scenario-analysis
continue-on-error: true
env:
BASE_REF: ${{ github.event_name == 'pull_request' && format('origin/{0}', github.base_ref) || (github.event_name == 'workflow_dispatch' && inputs.target_repo != '' && inputs.target_pr != '' && format('target/{0}', inputs.target_base) || inputs.base_ref) }}
HEAD_REF: ${{ github.event_name == 'pull_request' && 'HEAD' || (github.event_name == 'workflow_dispatch' && inputs.target_repo != '' && inputs.target_pr != '' && 'HEAD' || inputs.head_ref) }}
E2E_SCENARIO_ADVISOR_RUN_ANALYSIS: ${{ github.event_name == 'workflow_dispatch' && inputs.run_analysis == false && '0' || '1' }}
# Reuse the shared E2E advisor secret. The scenario advisor is a
# separate prompt/agent but uses the same model and credential.
E2E_ADVISOR_API_KEY: ${{ secrets.PI_E2E_ADVISOR_API_KEY }}
# Optional local-provider-compatible fallback for future upstream/external-advisor use.
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
cd "$ADVISOR_WORKDIR"
node --experimental-strip-types "$ADVISOR_DIR/tools/e2e-advisor/scenarios.mts" \
--base "$BASE_REF" \
--head "$HEAD_REF" \
--schema "$ADVISOR_DIR/tools/e2e-advisor/scenarios-schema.json" \
--out-dir "$GITHUB_WORKSPACE/artifacts/e2e-advisor"
- name: Publish job summary
if: always()
run: |
if [ -f "$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-advisor-summary.md" ]; then
cat "$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-advisor-summary.md" >> "$GITHUB_STEP_SUMMARY"
else
printf '# E2E Recommendation Advisor\n\nAdvisor analysis did not produce a summary. See raw artifacts/logs.\n' >> "$GITHUB_STEP_SUMMARY"
fi
if [ -f "$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-scenario-advisor-summary.md" ]; then
cat "$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-scenario-advisor-summary.md" >> "$GITHUB_STEP_SUMMARY"
fi
- name: Auto-dispatch required E2E jobs
if: ${{ always() && github.event_name == 'pull_request' }}
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
# Optional comma-separated GitHub login allowlist for trusted authors whose
# private org membership appears as CONTRIBUTOR in pull_request payloads.
E2E_ADVISOR_AUTO_DISPATCH_ALLOWED_AUTHORS: ${{ secrets.E2E_ADVISOR_AUTO_DISPATCH_ALLOWED_AUTHORS }}
run: |
DISPATCH_RESULT="$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-advisor-dispatch-result.json"
DISPATCH_SUMMARY="$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-advisor-dispatch-summary.md"
if [ -f "$ADVISOR_DIR/tools/e2e-advisor/dispatch.mts" ]; then
node --experimental-strip-types "$ADVISOR_DIR/tools/e2e-advisor/dispatch.mts" \
--result "$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-advisor-final-result.json" \
--workflow nightly-e2e.yaml \
--workflow-path "$ADVISOR_DIR/.github/workflows/nightly-e2e.yaml" \
--out-dir "$GITHUB_WORKSPACE/artifacts/e2e-advisor"
else
echo "Skipping E2E auto-dispatch: trusted main checkout does not yet contain dispatch.mts"
mkdir -p "$GITHUB_WORKSPACE/artifacts/e2e-advisor"
printf '{\n "status": "skipped",\n "reason": "trusted main checkout does not yet contain dispatch.mts",\n "workflow": "nightly-e2e.yaml"\n}\n' > "$DISPATCH_RESULT"
printf '# E2E Advisor Auto-dispatch\n\nStatus: **skipped**\nReason: trusted main checkout does not yet contain dispatch.mts\nWorkflow: `nightly-e2e.yaml`\n' > "$DISPATCH_SUMMARY"
fi
if [ -f "$DISPATCH_SUMMARY" ]; then
cat "$DISPATCH_SUMMARY" >> "$GITHUB_STEP_SUMMARY"
fi
- name: Post E2E advisor PR comment
if: ${{ always() && github.event_name == 'pull_request' }}
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.E2E_ADVISOR_GITHUB_TOKEN || github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
COMMENT_ARGS=(
--repo "$GITHUB_REPOSITORY"
--pr "$PR_NUMBER"
--summary "$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-advisor-summary.md"
--result "$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-advisor-final-result.json"
--dispatch "$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-advisor-dispatch-result.json"
)
if [ -f "$ADVISOR_DIR/tools/e2e-advisor/comment.mts" ]; then
node --experimental-strip-types "$ADVISOR_DIR/tools/e2e-advisor/comment.mts" "${COMMENT_ARGS[@]}"
elif [ -f "$ADVISOR_DIR/tools/e2e-advisor/comment.mjs" ]; then
node "$ADVISOR_DIR/tools/e2e-advisor/comment.mjs" "${COMMENT_ARGS[@]}"
else
echo "Skipping E2E advisor comment: trusted main checkout does not contain comment.mts or comment.mjs"
fi
- name: Post E2E scenario advisor PR comment
if: ${{ always() && github.event_name == 'pull_request' }}
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.E2E_ADVISOR_GITHUB_TOKEN || github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
if [ -f "$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-scenario-advisor-summary.md" ] && [ -f "$ADVISOR_DIR/tools/e2e-advisor/scenario-comment.mts" ]; then
node --experimental-strip-types "$ADVISOR_DIR/tools/e2e-advisor/scenario-comment.mts" \
--repo "$GITHUB_REPOSITORY" \
--pr "$PR_NUMBER" \
--summary "$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-scenario-advisor-summary.md" \
--result "$GITHUB_WORKSPACE/artifacts/e2e-advisor/e2e-scenario-advisor-result.json"
else
echo "Skipping E2E scenario advisor comment: summary or scenario-comment.mts missing"
fi
- name: Upload advisor artifacts
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: e2e-advisor
path: artifacts/e2e-advisor/
if-no-files-found: warn