-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Add agent-eval step registry and ai-helpers eval presubmit #79925
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
openshift-merge-bot
merged 17 commits into
openshift:main
from
not-stbenjam:ai-helpers-eval-presubmit
Jun 1, 2026
Merged
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
94b833b
Add agent-eval step registry and ai-helpers eval presubmit
not-stbenjam 4a3fab5
Pin mlflow, error on missing setup script, update owners
not-stbenjam bbef4aa
Add marketplace registration before plugin install
not-stbenjam c004304
Add hello-world smoke test fallback when eval config missing
not-stbenjam 869b467
Clear EVAL_SETUP_SCRIPT when falling back to smoke test
not-stbenjam 05e21a9
Use private artifact bucket for eval job
not-stbenjam a0b9264
Remove hidden setting from eval job
not-stbenjam c774acd
Use python3 -m pip instead of bare pip
not-stbenjam c43ebde
make ci-operator-config
not-stbenjam 378e598
Run mlflow server via python3 -m mlflow
not-stbenjam 8a5ade6
Fix mlflow PATH and SSH host key for plugin install
not-stbenjam 90b0026
Fix mlflow, SSH, and PATH issues in eval step
not-stbenjam 1f14e82
Add comment for mlflow version pin, remove pipe on pip install
not-stbenjam c4adbda
Clone eval harness directly, use --plugin-dir
not-stbenjam 6458632
ci: strip hello-world smoke test, fix integration gaps
not-stbenjam f1bfab9
ci: remove unused EVAL_JUDGE_MODEL env var
not-stbenjam d984b0d
ci: fix shellcheck SC2155 — declare and assign separately
not-stbenjam File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 75 additions & 0 deletions
75
ci-operator/jobs/openshift-eng/ai-helpers/openshift-eng-ai-helpers-main-presubmits.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
ci-operator/step-registry/openshift/claude/agent-eval/OWNERS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| approvers: | ||
| - bentito | ||
| - bryan-cox | ||
| - cblecker | ||
| - enxebre | ||
| - prashanth684 | ||
| - stbenjam | ||
| reviewers: | ||
| - bentito | ||
| - bryan-cox | ||
| - cblecker | ||
| - enxebre | ||
| - prashanth684 | ||
| - stbenjam |
215 changes: 215 additions & 0 deletions
215
...perator/step-registry/openshift/claude/agent-eval/openshift-claude-agent-eval-commands.sh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,215 @@ | ||
| #!/bin/bash | ||
| # | ||
| # Run agent-eval-harness against Claude Code skills. | ||
| # | ||
| # Required env: | ||
| # EVAL_CONFIG -- path to eval.yaml (relative to repo root) | ||
| # | ||
| # Optional env: | ||
| # EVAL_MODEL -- model for the skill under test (default: claude-sonnet-4-6) | ||
| # EVAL_BASELINE -- run-id of a previous run to compare against | ||
| # EVAL_EXTRA_ARGS -- additional args passed to /eval-run | ||
| # EVAL_SETUP_SCRIPT -- script to run before eval (e.g. snapshot extraction) | ||
| # CLAUDE_MODEL -- model for the eval harness orchestrator (default: claude-sonnet-4-6) | ||
| # MLFLOW_PORT -- port for local MLflow server (default: 5000) | ||
|
|
||
| set -o nounset | ||
| set -o errexit | ||
| set -o pipefail | ||
|
|
||
| echo "Starting claude-agent-eval" | ||
|
|
||
| # The repo is at /opt/ai-helpers; WORKDIR is /workspace | ||
| cd /opt/ai-helpers | ||
|
|
||
| echo "Config: ${EVAL_CONFIG}" | ||
| echo "Skill model: ${EVAL_MODEL}" | ||
|
|
||
| # ----------------------------------------------------------------------- | ||
| # Install dependencies | ||
| # ----------------------------------------------------------------------- | ||
| echo "Installing mlflow..." | ||
| export PATH="$HOME/.local/bin:$PATH" | ||
| # mlflow 3.x requires sqlite >= 3.36.0; RHEL 9 ships 3.34.1 | ||
| python3 -m pip install --quiet 'mlflow==2.20.2' | ||
| echo "mlflow installed." | ||
|
|
||
| # Start local MLflow server in background | ||
| echo "Starting local MLflow server on port ${MLFLOW_PORT}..." | ||
| export MLFLOW_TRACKING_URI="http://127.0.0.1:${MLFLOW_PORT}" | ||
| mlflow server --port "${MLFLOW_PORT}" --host 127.0.0.1 & | ||
| MLFLOW_PID=$! | ||
|
|
||
| for i in $(seq 1 30); do | ||
| if curl -sf "http://127.0.0.1:${MLFLOW_PORT}/health" >/dev/null 2>&1; then | ||
| echo "MLflow server ready (PID ${MLFLOW_PID})." | ||
| break | ||
| fi | ||
| if ! kill -0 "${MLFLOW_PID}" 2>/dev/null; then | ||
| echo "ERROR: MLflow server crashed." | ||
| exit 1 | ||
| fi | ||
| if [[ $i -eq 30 ]]; then | ||
| echo "ERROR: MLflow server did not become ready in 30s." | ||
| exit 1 | ||
| fi | ||
| sleep 1 | ||
| done | ||
|
|
||
| # ----------------------------------------------------------------------- | ||
| # Verify eval config exists | ||
| # ----------------------------------------------------------------------- | ||
| if [[ ! -f "${EVAL_CONFIG}" ]]; then | ||
| echo "ERROR: EVAL_CONFIG not found at ${EVAL_CONFIG}" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # ----------------------------------------------------------------------- | ||
| # Run optional setup script (e.g. extract snapshots, populate fixtures) | ||
| # ----------------------------------------------------------------------- | ||
| if [[ -n "${EVAL_SETUP_SCRIPT}" ]]; then | ||
| if [[ ! -f "${EVAL_SETUP_SCRIPT}" ]]; then | ||
| echo "ERROR: EVAL_SETUP_SCRIPT not found: ${EVAL_SETUP_SCRIPT}" | ||
| exit 1 | ||
| fi | ||
| echo "" | ||
| echo "=== Running setup script: ${EVAL_SETUP_SCRIPT} ===" | ||
| EVAL_SNAPSHOT_DIR=$(bash "${EVAL_SETUP_SCRIPT}") | ||
| export EVAL_SNAPSHOT_DIR | ||
| echo "Snapshot dir: ${EVAL_SNAPSHOT_DIR}" | ||
| fi | ||
|
|
||
| # ----------------------------------------------------------------------- | ||
| # Install plugins | ||
| # ----------------------------------------------------------------------- | ||
| echo "" | ||
| echo "=== Installing plugins ===" | ||
| EVAL_HARNESS_DIR="/tmp/agent-eval-harness" | ||
| git clone --depth 1 https://github.com/opendatahub-io/agent-eval-harness.git "${EVAL_HARNESS_DIR}" | ||
| echo "agent-eval-harness cloned." | ||
|
|
||
| # ----------------------------------------------------------------------- | ||
| # Artifact copy trap | ||
| # ----------------------------------------------------------------------- | ||
| copy_artifacts() { | ||
| echo "Copying eval artifacts..." | ||
| if [[ -d "${AGENT_EVAL_RUNS_DIR:-eval/runs}" ]]; then | ||
| find "${AGENT_EVAL_RUNS_DIR:-eval/runs}" -name "report.html" -exec cp {} "${ARTIFACT_DIR}/eval-report-summary.html" \; 2>/dev/null || true | ||
| find "${AGENT_EVAL_RUNS_DIR:-eval/runs}" -name "summary.yaml" -exec cp {} "${ARTIFACT_DIR}/" \; 2>/dev/null || true | ||
| find "${AGENT_EVAL_RUNS_DIR:-eval/runs}" -name "run_result.json" -exec cp {} "${ARTIFACT_DIR}/" \; 2>/dev/null || true | ||
| fi | ||
| find . -name "*-summary.html" -exec cp {} "${ARTIFACT_DIR}/" \; 2>/dev/null || true | ||
|
|
||
| # Copy MLflow data | ||
| if [[ -d "mlruns" ]]; then | ||
| tar -czf "${ARTIFACT_DIR}/mlflow-data.tar.gz" mlruns/ 2>/dev/null || true | ||
| fi | ||
|
|
||
| # Archive Claude session for continue-session support | ||
| CLAUDE_HOME="/home/claude/.claude" | ||
| if [[ -d "${CLAUDE_HOME}/projects" ]]; then | ||
| echo "Archiving Claude session logs..." | ||
| tar -czf "${ARTIFACT_DIR}/claude-sessions-$(date +%Y%m%d-%H%M%S).tar.gz" \ | ||
| -C "${CLAUDE_HOME}" projects/ 2>/dev/null && \ | ||
| touch "${SHARED_DIR}/claude-session-available" || true | ||
| fi | ||
|
|
||
| # Stop MLflow server and all child processes (gunicorn workers) | ||
| if [[ -n "${MLFLOW_PID:-}" ]]; then | ||
| pkill -P "${MLFLOW_PID}" 2>/dev/null || true | ||
| kill "${MLFLOW_PID}" 2>/dev/null || true | ||
| fi | ||
| } | ||
| trap copy_artifacts EXIT TERM INT | ||
|
|
||
| # ----------------------------------------------------------------------- | ||
| # Workaround: --continue + -p is broken (anthropics/claude-code#42376). | ||
| # ----------------------------------------------------------------------- | ||
| export CLAUDE_CODE_ENTRYPOINT=sdk-cli | ||
|
|
||
| # ----------------------------------------------------------------------- | ||
| # Build arguments | ||
| # ----------------------------------------------------------------------- | ||
| RUN_ID="ci-$(date +%Y%m%d-%H%M%S)-${EVAL_MODEL}" | ||
| ALLOWED_TOOLS="Bash Read Write Edit Grep Glob Agent Skill" | ||
|
|
||
| EVAL_RUN_ARGS="--config ${EVAL_CONFIG} --model ${EVAL_MODEL} --run-id ${RUN_ID}" | ||
| if [[ -n "${EVAL_BASELINE}" ]]; then | ||
| EVAL_RUN_ARGS="${EVAL_RUN_ARGS} --baseline ${EVAL_BASELINE}" | ||
| fi | ||
| if [[ -n "${EVAL_EXTRA_ARGS}" ]]; then | ||
| EVAL_RUN_ARGS="${EVAL_RUN_ARGS} ${EVAL_EXTRA_ARGS}" | ||
| fi | ||
|
|
||
| # ----------------------------------------------------------------------- | ||
| # Run evaluation | ||
| # ----------------------------------------------------------------------- | ||
| echo "" | ||
| echo "=== Running eval ===" | ||
| echo "Run ID: ${RUN_ID}" | ||
| echo "Args: ${EVAL_RUN_ARGS}" | ||
|
|
||
| EVAL_START=$(date +%s) | ||
| EVAL_EXIT=0 | ||
| timeout 7200 claude \ | ||
| --model "${CLAUDE_MODEL}" \ | ||
| --plugin-dir "${EVAL_HARNESS_DIR}" \ | ||
| --allowedTools "${ALLOWED_TOOLS}" \ | ||
| --output-format stream-json \ | ||
| --max-turns 100 \ | ||
| -p "/eval-run ${EVAL_RUN_ARGS}" \ | ||
| --verbose 2>&1 | tee "${ARTIFACT_DIR}/claude-eval.log" || EVAL_EXIT=$? | ||
| EVAL_DURATION=$(( $(date +%s) - EVAL_START )) | ||
|
|
||
| echo "eval-run completed in ${EVAL_DURATION}s (exit ${EVAL_EXIT})" | ||
|
|
||
| # ----------------------------------------------------------------------- | ||
| # Run eval-mlflow to upload results | ||
| # ----------------------------------------------------------------------- | ||
| echo "" | ||
| echo "=== Running eval-mlflow ===" | ||
|
|
||
| MLFLOW_EXIT=0 | ||
| timeout 600 claude \ | ||
| --model "${CLAUDE_MODEL}" \ | ||
| --plugin-dir "${EVAL_HARNESS_DIR}" \ | ||
| --continue \ | ||
| --allowedTools "${ALLOWED_TOOLS}" \ | ||
| --output-format stream-json \ | ||
| --max-turns 20 \ | ||
| -p "/eval-mlflow --action all --run-id ${RUN_ID} --config ${EVAL_CONFIG}" \ | ||
| --verbose 2>&1 | tee "${ARTIFACT_DIR}/claude-eval-mlflow.log" || MLFLOW_EXIT=$? | ||
|
|
||
| echo "eval-mlflow completed with exit code ${MLFLOW_EXIT}" | ||
|
|
||
| # ----------------------------------------------------------------------- | ||
| # Generate JUnit XML | ||
| # ----------------------------------------------------------------------- | ||
| JUNIT_FILE="${ARTIFACT_DIR}/junit_claude-eval.xml" | ||
| FAILURE_COUNT=0 | ||
| TESTCASE="[sig-claude] Skill evaluation should pass" | ||
|
|
||
| if [[ "${EVAL_EXIT}" -ne 0 ]]; then | ||
| FAILURE_COUNT=1 | ||
| TESTCASES=" <testcase name=\"${TESTCASE}\" time=\"${EVAL_DURATION}\"> | ||
| <failure message=\"eval-run failed (exit ${EVAL_EXIT})\">eval-run exited with code ${EVAL_EXIT}.</failure> | ||
| </testcase>" | ||
| else | ||
| TESTCASES=" <testcase name=\"${TESTCASE}\" time=\"${EVAL_DURATION}\"/>" | ||
| fi | ||
|
|
||
| cat > "${JUNIT_FILE}" <<EOF | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <testsuite name="claude-eval" tests="1" failures="${FAILURE_COUNT}" time="${EVAL_DURATION}"> | ||
| ${TESTCASES} | ||
| </testsuite> | ||
| EOF | ||
|
|
||
| echo "JUnit XML written to ${JUNIT_FILE}" | ||
|
|
||
| if [[ "${EVAL_EXIT}" -ne 0 ]]; then | ||
| echo "Evaluation failed." | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "Evaluation complete." |
21 changes: 21 additions & 0 deletions
21
...r/step-registry/openshift/claude/agent-eval/openshift-claude-agent-eval-ref.metadata.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| { | ||
| "path": "openshift/claude/agent-eval/openshift-claude-agent-eval-ref.yaml", | ||
| "owners": { | ||
| "approvers": [ | ||
| "bentito", | ||
| "bryan-cox", | ||
| "cblecker", | ||
| "enxebre", | ||
| "prashanth684", | ||
| "stbenjam" | ||
| ], | ||
| "reviewers": [ | ||
| "bentito", | ||
| "bryan-cox", | ||
| "cblecker", | ||
| "enxebre", | ||
| "prashanth684", | ||
| "stbenjam" | ||
| ] | ||
| } | ||
| } |
45 changes: 45 additions & 0 deletions
45
ci-operator/step-registry/openshift/claude/agent-eval/openshift-claude-agent-eval-ref.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| ref: | ||
| as: openshift-claude-agent-eval | ||
| from_image: | ||
| namespace: ci | ||
| name: claude-ai-helpers | ||
| tag: latest | ||
| commands: openshift-claude-agent-eval-commands.sh | ||
| credentials: | ||
| - namespace: test-credentials | ||
| name: hypershift-team-claude-prow | ||
| mount_path: /var/run/claude-code-service-account | ||
| env: | ||
| - name: EVAL_CONFIG | ||
| default: "eval.yaml" | ||
| - name: EVAL_MODEL | ||
| default: "claude-sonnet-4-6" | ||
| - name: EVAL_BASELINE | ||
| default: "" | ||
| - name: EVAL_EXTRA_ARGS | ||
| default: "" | ||
| - name: EVAL_SETUP_SCRIPT | ||
| default: "" | ||
| - name: MLFLOW_PORT | ||
| default: "5000" | ||
| - name: CLAUDE_MODEL | ||
| default: "claude-sonnet-4-6" | ||
| - name: CLAUDE_CODE_USE_VERTEX | ||
| default: "1" | ||
| - name: CLOUD_ML_REGION | ||
| default: "global" | ||
| - name: ANTHROPIC_VERTEX_PROJECT_ID | ||
| default: "itpc-gcp-hybrid-pe-eng-claude" | ||
| - name: GOOGLE_APPLICATION_CREDENTIALS | ||
| default: "/var/run/claude-code-service-account/claude-prow" | ||
| resources: | ||
| requests: | ||
| cpu: 1000m | ||
| memory: 2Gi | ||
| timeout: 3h0m0s | ||
| grace_period: 1m0s | ||
| documentation: |- | ||
| Runs skill evaluations using the agent-eval-harness. Installs the | ||
| eval harness plugin, starts a local MLflow server, runs /eval-run | ||
| and /eval-mlflow with the specified config and model, and produces | ||
| JUnit XML + artifacts. MLflow data is archived for download. |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.