-
Notifications
You must be signed in to change notification settings - Fork 0
183 lines (170 loc) · 7.21 KB
/
Copy pathci.yml
File metadata and controls
183 lines (170 loc) · 7.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
name: ci
on:
push:
branches: ["main"]
pull_request:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
lint-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: install
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"
- name: ruff
run: ruff check .
- name: contract tests
run: pytest -q
scoring-end-to-end:
# The live failure was a pod that booted, ran the scorer, and returned no
# RELEARN_EVAL_OK. Everything else in the suite patches the model and the
# judge; this job loads a real (tiny) model and answers a real judge over a
# socket, so `score` exiting 0 with a one-line sidecar and both markers is
# asserted on every change rather than discovered on a live pod.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: install with a CPU model runtime
run: |
python -m pip install --upgrade pip
pip install torch --index-url https://download.pytorch.org/whl/cpu
pip install -e ".[dev]" transformers accelerate peft safetensors
- name: score a champion-baseline request end to end
run: pytest -q tests/test_end_to_end.py
image-contract:
# Builds the image and drives it exactly as the harvest does: request on
# stdin, `relearn-eval score`, then read the markers back. A pod with no
# judge and no model runtime must refuse rather than print a document, so
# that is what this asserts.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- uses: docker/setup-buildx-action@v3
- name: build the contract image
uses: docker/build-push-action@v6
with:
context: .
file: eval/Dockerfile
load: true
tags: relearn-eval:ci
build-args: |
WITH_RUNTIME=0
RELEARN_GIT_SHA=${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: stage a dev request
run: |
pip install -e .
python tests/tools/make_request.py --size 8 > "${RUNNER_TEMP}/request.json"
- name: the entrypoint runs the scorer
run: |
docker run --rm relearn-eval:ci --help | tee /dev/stderr | grep -q "score"
- name: harvest PATH finds a real relearn-eval
# Live 127: SSH is non-interactive, PATH is /usr/bin:/bin, and a
# dangling symlink to wherever pip put the console script is
# `relearn-eval: No such file or directory`. This job checks the
# slim contract image. The scoring digest is proven the same way
# in publish-eval-image, after that job pulls the digest it pushed.
run: |
docker run --rm --entrypoint sh relearn-eval:ci -c \
'set -eu; test -f /usr/bin/relearn-eval; ! test -L /usr/bin/relearn-eval; test -x /usr/bin/relearn-eval'
docker run --rm --entrypoint env relearn-eval:ci \
-i PATH=/usr/bin:/bin relearn-eval --help \
| tee /dev/stderr | grep -q "score"
docker run --rm --entrypoint env relearn-eval:ci \
-i PATH=/usr/bin:/bin relearn-eval score --help \
| tee /dev/stderr | grep -q -- "--request"
- name: a contract-only image fails selftest, naming what it cannot import
# `selftest` is the check the publish job runs against the *pushed
# scoring digest*, where it must pass. Here it must fail, and say
# torchvision and vllm — the two the live pod on sha256:cbc4bbb8 was
# missing. Building the CUDA image on every PR costs a quarter of an
# hour, so this is where the mechanism itself is kept honest.
run: |
set +e
docker run --rm relearn-eval:ci selftest 2>"${RUNNER_TEMP}/selftest.log"
rc=$?
set -e
cat "${RUNNER_TEMP}/selftest.log"
test "${rc}" -ne 0
grep -q "not a scoring image" "${RUNNER_TEMP}/selftest.log"
grep -q "torchvision" "${RUNNER_TEMP}/selftest.log"
grep -q "vllm" "${RUNNER_TEMP}/selftest.log"
docker run --rm --entrypoint env relearn-eval:ci \
-i PATH=/usr/bin:/bin /usr/bin/relearn-eval selftest 2>&1 \
| tee /dev/stderr | grep -q "not a scoring image"
- name: a pod with no judge refuses, with no document and no OK marker
run: |
set +e
out=$(docker run --rm -i relearn-eval:ci score --request - --out /tmp/metrics.json \
< "${RUNNER_TEMP}/request.json" 2>"${RUNNER_TEMP}/stderr.log")
rc=$?
set -e
echo "exit=${rc}"
cat "${RUNNER_TEMP}/stderr.log"
test "${rc}" -ne 0
if echo "${out}" | grep -q "RELEARN_EVAL_OK"; then
echo "a pod that could not score printed the completion marker"; exit 1
fi
if echo "${out}" | grep -q "RELEARN_METRICS="; then
echo "a pod that could not score printed a document"; exit 1
fi
grep -q "RELEARN_TEACHER_API_URL" "${RUNNER_TEMP}/stderr.log"
- name: preflight names the missing dependency
run: |
set +e
docker run --rm -v "${RUNNER_TEMP}:/run:ro" relearn-eval:ci \
preflight --request /run/request.json --workdir /tmp/relearn_eval \
2>"${RUNNER_TEMP}/preflight.log"
rc=$?
set -e
cat "${RUNNER_TEMP}/preflight.log"
test "${rc}" -ne 0
grep -q "no judge" "${RUNNER_TEMP}/preflight.log"
- name: a tampered request is refused
run: |
python - "${RUNNER_TEMP}/request.json" > "${RUNNER_TEMP}/tampered.json" <<'PY'
import json, sys
body = json.load(open(sys.argv[1]))
body["holdout"][0]["prompt"] = "an item the commitment does not cover"
json.dump(body, sys.stdout)
PY
set +e
docker run --rm -i relearn-eval:ci score --request - \
< "${RUNNER_TEMP}/tampered.json" 2>"${RUNNER_TEMP}/tampered.log"
rc=$?
set -e
test "${rc}" -ne 0
grep -q "commitment mismatch" "${RUNNER_TEMP}/tampered.log"
- name: silence is not a score
run: |
printf 'boot ok\nsegfault\n' > "${RUNNER_TEMP}/silent.log"
printf 'boot ok\nRELEARN_EVAL_OK\n' > "${RUNNER_TEMP}/marker-only.log"
for transcript in silent marker-only; do
set +e
docker run --rm -v "${RUNNER_TEMP}:/run:ro" relearn-eval:ci \
verify --request /run/request.json --transcript "/run/${transcript}.log" \
2>"${RUNNER_TEMP}/${transcript}.verify.log"
rc=$?
set -e
cat "${RUNNER_TEMP}/${transcript}.verify.log"
test "${rc}" -ne 0
done
grep -q "did not print RELEARN_EVAL_OK" "${RUNNER_TEMP}/silent.verify.log"
grep -q "printed no RELEARN_METRICS=" "${RUNNER_TEMP}/marker-only.verify.log"