Repository navigation
feat(cli): sieval run starts an engine only for samples left to infer - #216
Merged
Merged
Conversation
This was referenced Oct 23, 2026
ethan-scitix
force-pushed
the
feat/target-gpu
branch
from
October 23, 2026 09:06
8ca205d to
5eeeb1b
Compare
sieval run resolved every plan and launched every engine before the session planned its resume, so a resume that reused every inference still took a GPU and loaded the model, and could not run on a CPU node at all. A run that may resume now plans first. The session is built as for the run, with each served model bound to the deployment launching it would realize (unlaunched_deployment: the same plan, endpoints and engine, nothing listening), so every identity and binding is the launched one. Each task's TaskRunner.plan_run runs the resume planning arun runs, read-only: both call the one _plan_recorded, so the plan counts the samples the run infers, and a resume=<dir> is read where it lies, not copied. Only the models some task still has samples to infer with are launched. With none, no engine starts and the run needs no GPU; when some sample needs one and nvidia-smi finds no GPU, the run is refused before anything starts. A fresh run is not planned. Unlaunched models reach the session as unlaunched_deployments and their runners as unserved_candidate: a run whose directory changed since it was planned is refused before it records anything, and a sample that a rerun feedback sends to another iteration fails at infer, before any request, to be retried by a resume that starts the engine. Judges and other external models are connected lazily already; nothing calls them until a sample is graded. Launched engines keep their logs, infer_plans.yaml and realized_deployments as before. A stub-engine run on main and on this branch records the same inference digest and request digest, fresh and resumed. Closes #213. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ethan-scitix
force-pushed
the
feat/lazy-engine-launch
branch
from
October 23, 2026 09:23
e566460 to
f14f987
Compare
This was referenced Oct 23, 2026
Merged
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Type
Summary
Rebased onto
mainafter #215 and #211 merged.sieval runplans the resume before it launches anything._run_alllaunched every engine beforearun_session, where resume planning happened. As a result, a resume that reused every inference still took a GPU and loaded the model, and could not run on a CPU node.unlaunched_deployment(plan, commands). That is theDeploymentthe launch would realize: same plan projection, endpoints, engine and topology, with only thedeployment_idnaming no process. Every binding, inference recipe and identity is therefore the launched one. It then asks each task'sTaskRunner.plan_run(). That call andarun's_begin_runshare one helper,_plan_recorded(inference check, grading-change detection, per-sample plan, overflow replays, retry rollback, model switch check), so the plan counts exactly the samples the run infers. It runs read-only: aresume=<dir>is read in place, not copied, and nothing writesmeta.json. The one call is_plan_tasks→aplan_session.RunPlanvsResumePlan. feat(core)!: split the grader identity along postprocess, feedback and report #211'sResumePlan/plan_resumecompare identities alone. The newRunPlan/plan_runis the runner's per-sample plan:start,outcomes(asruns[].resumewill record them,ResumeOutcomes),new,to_infer,to_grade. A re-report (onlyreportchanged) plans asreusewithrereportedset.to_infer > 0for. With none, no engine starts and the run needs no GPU. If some sample needs inference andnvidia-smifinds no GPU, the run is refused before anything starts (check_launch_gpu(required=True), next to feat(infer): resolve serving plans for GPUs declared in SIEVAL_TARGET_GPU #215's declared-GPU check). A fresh run is not planned: it infers everything and launches as before.unlaunched_deploymentsand their runners asunserved_candidate.feedbacksends to another iteration fails atinfer, before any request, instead of calling a dead endpoint. A resume that starts the engine retries it. Planning cannot know such a sample in advance.<result_dir>/logs/<model>/,infer_plans.yaml(it still lists every served model), andrealized_deployments.self_managed_endpointsstill names every served model.Review fixes
max_retrieslimit._begin_rungives a scored-from-record overflow its retry back; the plan's copy of the planning steps did not. Atmax_retries: 0the plan saidto_infer: 0, so no engine launched, and the run then refused with "Did change since?" on every rerun. Both now call_plan_recorded. Regression test:test_context_overflow.py::TestResume::test_the_plan_counts_a_scored_refusal_as_the_run_does[0|3]. It fails with the retry give-back removed._check_servedhad no test. Addedtest_plan_run.py::TestUnservedCandidate::test_a_regrade_that_sends_a_sample_back_fails_it_at_infer. It checks that no request is sent, the samples fail atinferon iteration 1, and a resume with the engine retries them. It fails with the guard removed._resumesreuses the session's resume-key scan (config_resumes) instead of a copy of_RESUME_KEYS.getattr(self, "_unlaunched_roots", ())became a direct attribute read.Rebase adjustments
_fast_resumere-report when onlyreportchanged._reusable_reportnow returns(report, rereport), and_fast_resumekeeps feat(core)!: split the grader identity along postprocess, feedback and report #211's body.ResumePlanname was taken, so the runner's plan becameRunPlan/plan_run(MultiTaskRunner.plan_run,EvalSession.aplan).check_launch_gpukeeps feat(infer): resolve serving plans for GPUs declared in SIEVAL_TARGET_GPU #215's per-field mismatch message and adds therequiredno-GPU refusal.test_lazy_launch.py's regrade test reads identity schema 2:feedbackdiffers,reran == ["postprocess", "feedback", "report"]. A task argument is read by every grading stage.Identity impact. None. The inference identity reads only the plan, which is unchanged. A stub-engine run through
sieval runonmain(339f7d0) and on this branch recorded the sameidentity.inferencebefore the rebase. After it,test_a_resume_with_samples_to_infer_starts_the_enginestill asserts a resume's inference identity equals a fresh run's.RESULT_SCHEMA_VERSIONstays 1.Related Issues
Closes #213.
Test Plan
Automated
Lint/format clean (
ruff check && ruff format --check)Type check clean (
ty check)Unit tests pass (
pdm run pytest)pytest tests/unit tests/integration -n 8: 9543 passed, 1 skipped (Linux).ruff check,ruff format --check,ty check(whole repo),check_preflight.pyandpre-commit run --from-ref origin/main --to-ref HEADare clean.tests/integration/test_lazy_launch.pydrives_run_allthrough the real session, runner and OpenAI client. The engine is a stub, and GPU detection is faked.reran == [], and no engine log directory is created.identity.inferenceequals a fresh run of all three.meta.jsonis byte-identical.tests/unit/core/runners/test_plan_run.pycoversplan_runfor fresh, reuse, new-sample, failed-inference and past-max_retriescases (each leaving the result directory byte-identical),resume=<dir>copying nothing, refusals,MultiTaskRunner.plan_runmapping a refusal to its task, and bothunserved_candidateguards.🤖 Generated with Claude Code