Skip to content

feat(infer): resolve serving plans for GPUs declared in SIEVAL_TARGET_GPU - #215

Merged
ethan-scitix merged 2 commits into
mainfrom
feat/target-gpu
Oct 23, 2026
Merged

ethan-scitix merged 2 commits into
mainfrom
feat/target-gpu

Conversation

@ethan-scitix

@ethan-scitix ethan-scitix commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Type

  • feature — new benchmark, task, or capability

Summary

  • SIEVAL_TARGET_GPU='<name>:<count>:<memory MiB>' declares the GPUs a serving plan is resolved for. Both resolvers, _resolve_recipe_params (YAML infer:) and auto_resolve_plan (path:-only), read it instead of nvidia-smi. A plan can therefore be resolved on any node, a CPU node included. When the variable is unset, nothing changes.
  • Why an env var with nvidia-smi's own fields. The resolvers also run under sieval infer start, and a platform sets a job's environment more easily than its argv. The fields are what detect_local_gpu reads, verbatim (NVIDIA H100 80GB HBM3:8:81559). The name selects the hardware profile and is recorded in a path:-only plan, and the memory decides TP. A shorthand like H100-80G:8 would resolve another plan, and with it another inference identity, so it is refused with the expected form.
  • Launch guard. A declared plan holds only on the declared hardware. sieval run and sieval infer start refuse to launch an engine on a node whose detected GPUs differ from the declaration. Without a declaration, nothing is checked.
  • Resume prefers the recorded plan. infer_plans.yaml gains gpu, the GPUs its plans were resolved for (declared or detected). It sits outside models, so it is in no identity. A sieval run --resume [DIR] declaring the same GPUs reuses the recorded plan of each served model whose path and infer are unchanged, with the same capability layer and the same deterministic. Otherwise the plan is resolved again.
  • plan_from_mapping rebuilds a DeploymentPlan from its recorded form, and refuses a record with fields this version lacks.
  • read_persisted_config and redact_secrets lose their leading underscore, since run.py now imports them.

Identity impact. None for a run that declares nothing: the resolvers take the same path, and infer_plans.yaml only gains the top-level gpu key. RESULT_SCHEMA_VERSION stays 1.

Related Issues

Closes #212. First of a stack: #216 (lazy engine launch, #213) and #217 (dry-run report, #214) build on it.

Test Plan

Automated

  • Lint/format clean (ruff check && ruff format --check)
  • Type check clean (ty check)
  • Unit tests pass (pdm run pytest)

Environment: pdm install --frozen-lockfile -G bfcl-v3 -G drop -G ifbench -G ifeval -G iheval -G math -G scicode -G spider -G code-eval -G test -G dev, macOS.

  • On this branch: tests/unit/cli tests/unit/infer tests/unit/core/runners tests/integration/resume gave 2149 passed. pytest tests/unit tests/integration passed up to the known macOS-only test_exec_quotebench_anchor.py failure.
  • On the stack tip (feat(cli): sieval run --dry-run reports what the run would do #217): pytest -q gave 9407 passed, 94 skipped, 1 failed, the same macOS-only test_exec_quotebench_anchor.py. ruff, ty, pre-commit run -a and scripts/check_preflight.py are clean.

New tests (tests/unit/infer/test_target_gpu.py; tests/unit/cli/test_run.py::TestDeclaredTargetGpu):

  • Declared equals detected. Detection is faked as an 8×H100 or 4×A100-40G node, and the plan is resolved. Then the same GPUs are declared and the plan is resolved again with nvidia-smi reporting none (which is never asked). The two plans are equal, both as objects and as the YAML infer_plans.yaml writes, for auto_resolve_plan and for resolve_infer_config. A different declaration resolves a different plan, so the check has teeth.
  • Through sieval run: the infer_plans the session receives with a declaration equal those it receives with detection on the same hardware. The recorded gpu is the same either way.
  • The parser accepts names containing colons, round-trips, and refuses each malformed form, naming the expected one. Unset or blank declares nothing.
  • The launch guard refuses other GPUs and a node with none, and asks nothing when nothing is declared.
  • A resume declaring the recorded GPUs reuses the recorded plan without resolving. Other GPUs, a fresh run, or a changed infer block each resolve again.
  • tests/conftest.py unsets SIEVAL_TARGET_GPU for every test, so a developer's shell cannot leak into a faked detection.

🤖 Generated with Claude Code

ethan-scitix and others added 2 commits October 23, 2026 16:55
…_GPU

Recipe resolution read the node's own GPUs from nvidia-smi, so a plan could
not be resolved on a CPU node, and auto_resolve_plan raised "No GPU detected".
A platform that decides on a CPU node whether a run needs a GPU could not get
the plan or the inference identity it implies.

SIEVAL_TARGET_GPU='<name>:<count>:<memory MiB>' now stands in for nvidia-smi
in both resolvers. The fields are the ones nvidia-smi reports, verbatim: the
name selects the hardware profile and is recorded in a path-only plan, and the
memory decides tensor parallelism, so only those values reproduce the plan a
node with the GPUs resolves. Unset, nothing changes.

Declared, an engine launches only on a node with exactly those GPUs
(sieval run and sieval infer start). infer_plans.yaml records as `gpu` the
GPUs its plans were resolved for, outside every identity. A sieval run
--resume declaring the same GPUs reuses the recorded plan of each served
model whose path and infer are unchanged, rather than resolving it again.

Tests fake detection and check that the declared plan equals the detected one
for both resolvers, byte for byte as infer_plans.yaml writes it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
infer_plans.yaml is written through redact_secrets, so a plan with an engine
api_key or authorization reads back holding "<redacted>". A resume that reused
it launched the engine with the marker as its key, and every request then
failed against it. The path/infer comparison could not tell: both sides are
compared redacted. Such a plan is now resolved again.

- holds_secret() says whether redact_secrets would redact anything.
- _RecordedPlans resolves the recorded model types once, not per model.
- The launch-GPU refusal names each field that differs, memory included,
  since two nodes of one GPU model can report different memory.total.
- Docs, docstrings and the infer_plans.yaml header name the new exception.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ethan-scitix
ethan-scitix merged commit 4b47dcb into main Oct 23, 2026
9 checks passed
@ethan-scitix
ethan-scitix deleted the feat/target-gpu branch October 23, 2026 09:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Resolve serving plans for declared GPUs, so a CPU node can plan a run

1 participant