fix(loop): actually consume a runner's self-reported workload_id#61
fix(loop): actually consume a runner's self-reported workload_id#61nicholaslawrence-hub wants to merge 4 commits into
Conversation
#60 added run.workload_id = "vllm-decode" to _vllm_decode_factory's runner, but run_loop never read it: the only consumer, getattr(cfg.engine, "workload_id", None), (a) reads the wrong object (cfg.engine, the raw engine handle — the attribute is set on the runner, not on runner.engine) and (b) runs before cfg.engine is even populated from the runner (that assignment happens several lines later). So the attribute was dead code, and any workload_runner passed without an explicit cfg.workload silently fell back to the hardcoded "vllm-decode" label regardless of what it actually was. Re-check the runner's workload_id after it resolves (and only when the caller didn't pin one explicitly via cfg.workload, which still wins). Added regression tests for both the override and the explicit-wins precedence. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
🤖 Claude Code ReviewReview🐛 BugsLogic error in the guard condition (loop.py, line 246) if cfg.workload is None and runner is not None:
workload = getattr(runner, "workload_id", None) or workloadThe condition checks
The new value is written to the local Test: setting attribute on a plain function may be fragile def runner():
return {"events": 1}
runner.workload_id = "custom-workload"This works in CPython but 💡 SuggestionsThe fix should be accompanied by a guard on the local default assignment The cleaner fix would be to track whether workload = cfg.workload # None if not set
# ... resolve runner ...
if workload is None and runner is not None:
workload = getattr(runner, "workload_id", None)
if workload is None:
workload = "vllm-decode" # default, applied lastThis makes precedence unambiguous and matches the documented intent. Missing test: runner with Neither test covers getattr(runner, "workload_id", None) or workload
Missing test: runner without Covers the Duplicate |
Code Review by GeminiNo bugs or issues were found in the provided code changes. The fix correctly addresses the problem described in the commit message, and the new tests adequately cover the intended behavior. The logic for re-checking the runner's No changes are suggested for the provided diff. |
Replace the function+monkeypatched-attribute runner double with an explicit _Runner class (a plain function's attribute is fragile if ever wrapped, e.g. functools.partial), de-duplicate the fake_capture setup shared by the workload_id tests, and add the two missing cases a reviewer flagged: an explicit workload_id=None (a real state a factory can leave unset) and a runner with no workload_id attribute at all (the getattr(..., None) default path) — both must fall back to the guessed/default label, not the previously-covered override case. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Addressed this round's review (commit e4ba577): Verified false — the two "bugs" claimed:
Real, applied — the test-quality suggestions:
Gemini's review found nothing to change. No real inline/resolvable PR review threads exist (`gh api .../pulls/61/comments` returns `[]`). Test run: `tests/test_run_loop_workload.py` (12 passed) + full suite green (excluding pre-existing `pyarrow`-dependent failures, unrelated to this change). 🤖 Addressed by Claude Code |
🤖 Claude Code ReviewReview🐛 BugsCondition mismatch in production code vs. test intent # loop.py
if cfg.workload is None and runner is not None:
workload = getattr(runner, "workload_id", None) or workloadThe guard checks _WORKLOAD_UNSET = object()
# earlier: workload = cfg.workload if cfg.workload is not None else _WORKLOAD_UNSET
if workload is _WORKLOAD_UNSET and runner is not None:
workload = getattr(runner, "workload_id", None) or "vllm-decode"
if workload_id != "unset":
self.workload_id = workload_idA caller passing the actual string _UNSET = object()
class _Runner:
def __init__(self, workload_id=_UNSET):
if workload_id is not _UNSET:
self.workload_id = workload_id💡 SuggestionsTest coverage gap: runner sets The empty-string case is a real-world footgun — # Current: "" → falls back to default (silent)
# Probably wanted: only None → falls back
runner_wid = getattr(runner, "workload_id", None)
if runner_wid is not None:
workload = runner_wid
The inner
Importing inside the test body (after No test for the case where The |
Code Review by GeminiThe changes effectively address the bug where a runner's self-reported No bugs or issues were found in the proposed changes. The code is clear, and the comments explain the rationale well. --- a/gitm/scheduler/loop.py
+++ b/gitm/scheduler/loop.py
@@ -232,20 +232,30 @@ def run_loop(cfg: LoopConfig) -> dict[str, Any]:
runner_error = f"no workload runner registered for {workload!r}"
# If the workload built a live engine (e.g. vLLM), expose it so the
# scheduler-stats sampler AND the Phase-4 live A/B can drive it. The runner
# carries it as ``.engine`` (see the vllm-decode factory). Without this the
# loop stays predict-only (DryRunApplicator, live=False) — the engine is
# built but never handed to the applicator.
if cfg.engine is None and runner is not None:
cfg.engine = getattr(runner, "engine", None)
+ # A factory-built runner may know its own workload id better than the
+ # guessed/default one above (e.g. a caller passed ``workload_runner``
+ # directly with no ``cfg.workload``, so ``workload`` fell through to the
+ # "vllm-decode" default regardless of what the runner actually is). Only
+ # the runner's own object carries this — earlier ``cfg.engine`` is never
+ # populated yet — so it must be re-checked here, after resolution, not
+ # folded into the guess above.
+ if cfg.workload is None and runner is not None:
+ workload = getattr(runner, "workload_id", None) or workload
+
# Sample the engine scheduler (queue depth, batch occupancy, preemptions)
# over the same window as the CUPTI capture — engine-level telemetry the GPU
# trace can't see. A no-op when no engine is attached (empty series).
with (
capture(trace_path, workload_id=workload, run_id=run_id) as trace,
sample_scheduler_stats(cfg.engine) as sched_stats,
):
if runner is not None:
try:
runner()
|
…ifying comment _Runner's "unset" sentinel used string equality (workload_id != "unset"), so a caller passing the literal string "unset" as a real workload_id would be silently suppressed. Switched to an object() identity sentinel. Added two missing cases a reviewer flagged: workload_id="" (falls back to default like None, by design — getattr(...) or workload is falsy-any, not just None-specific) and no runner + no cfg.workload at all (the simplest path, confirming the relabeling guard doesn't require a runner to be present for the plain default to still apply). loop.py: added a one-line comment on the cfg.workload is None guard clarifying that cfg.workload (the field) is never reassigned anywhere in run_loop, unlike the local `workload` var it's compared against — this exact point had already been grep-verified false against a prior review round but was re-flagged, so documenting it inline directly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
🤖 Claude Code ReviewReview🐛 BugsFalsy workload_id values silently swallowed — documented as "by design" but likely wrong workload = getattr(runner, "workload_id", None) or workloadThe _wid = getattr(runner, "workload_id", None)
if _wid is not None:
workload = _wid or workload # still fall back on "", but at least warn
if not _wid:
warnings.warn(f"Runner {runner!r} has falsy workload_id {_wid!r}; using default '{workload}'")Resolution order: The comment says "earlier 💡 Suggestions
Every test repeats the same three-line setup block: captured: dict = {}
monkeypatch.setattr(loop, "capture", _capturing_fake_capture(captured))
monkeypatch.setattr(loop, "sync_device", lambda: None)This is a natural pytest fixture: @pytest.fixture
def captured_loop(monkeypatch, tmp_path):
import gitm.scheduler.loop as loop
captured: dict = {}
monkeypatch.setattr(loop, "capture", _capturing_fake_capture(captured))
monkeypatch.setattr(loop, "sync_device", lambda: None)
return captured, tmp_pathReduces surface area for copy-paste drift (e.g. one test forgetting to patch
This test special-cases the absence of vLLM by patching Missing test: runner with The sentinel Import inside test functions — consider module-level or fixture-level import gitm.scheduler.loop as loop # repeated in every test body
from gitm import optimizeThese should be at module level or in a fixture to avoid repeated import overhead and to make monkeypatching order unambiguous (monkeypatching before the |
Code Review by GeminiThe changes correctly address the issue of the runner's One minor improvement can be made to the initial assignment of the Suggested Change: --- a/gitm/scheduler/loop.py
+++ b/gitm/scheduler/loop.py
@@ -197,7 +197,7 @@
# If the caller didn't specify a workload, guess "vllm-decode" (the most
# common case for a factory-built runner) or read it from the engine if
# it's already live.
- workload = cfg.workload or getattr(cfg.engine, "workload_id", None) or "vllm-decode"
+ workload = cfg.workload or "vllm-decode"
# If the caller passed a workload_runner directly, use it. Otherwise, look
# up a factory by workload name. |
…ry patch Extracted the repeated 3-line monkeypatch(capture)/monkeypatch(sync_device) setup shared by 6 workload_id tests into a captured_workload_id fixture, per review feedback about copy-paste drift risk. test_no_runner_and_no_explicit_workload_uses_default no longer monkeypatches get_factory to force runner=None — it relies on the same "no vLLM installed in this test environment" fact test_no_data_guard_does_not_fabricate_claims already depends on, so a future refactor of the lookup site can't silently stop this test from exercising the intended path. loop.py: reworded the comment on the workload_id re-check to remove ambiguity about resolution order (cfg.engine populated the block directly above, not "not yet") and to state explicitly why cfg.engine.workload_id isn't consulted here too — no current runner sets both, so there's no real precedence question, just one preferred source when it exists. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Addressed this round's review (commit d6a4419): Real, applied:
Verified false / declined:
No real inline/resolvable PR review threads exist (`gh api .../pulls/61/comments` returns `[]`). Test run: `tests/test_run_loop_workload.py` (14 passed) + full suite green (excluding pre-existing `pyarrow`-dependent failures, unrelated to this change). 🤖 Addressed by Claude Code |
🤖 Claude Code ReviewReview🐛 BugsFalsy string suppression is undocumented behaviour, not a bug, but the implementation is fragile workload = getattr(runner, "workload_id", None) or workloadThe _id = getattr(runner, "workload_id", None)
if _id is not None:
workload = _id or workload # still skip empty if you want, but intentionallyMore defensively:
The new block reads 💡 Suggestions
import gitm.scheduler.loop as loopImporting inside a fixture means import errors surface at test collection as cryptic fixture failures rather than clear import errors. Move to module level or at minimum add a comment explaining why it's deferred (e.g., to allow monkeypatching to take effect first — though
The fixture captures
def __call__(self) -> dict:
return {"events": 1}No test asserts that the runner's return value influences any output. If Test for
This is a hidden environment dependency. If vLLM ever becomes available in CI (e.g., via a transitive dependency), this test silently changes behaviour. Use an explicit Missing If these are meant to be fast/unit tests, adding Comment verbosity in production code The 12-line comment block in |
Code Review by GeminiNo bugs or critical issues were found in the provided code changes. The fix correctly addresses the problem described in the commit message, and the new tests provide comprehensive coverage for the intended behavior and edge cases. The logic for re-evaluating the |
Summary
run.workload_id = "vllm-decode"to_vllm_decode_factory's returned runner, but nothing ever reads it —run_loop's only lookup,getattr(cfg.engine, "workload_id", None), checks the wrong object (cfg.engine, the raw engine handle) at the wrong time (beforecfg.engineis even populated from the runner, several lines later). The attribute was dead code.workload_runnerpassed without an explicitcfg.workloadalways fell back to the hardcoded"vllm-decode"label for trace/report purposes, regardless of what the runner actually represents.workload_idonce it has actually resolved, only when the caller didn't pin one explicitly (cfg.workloadstill wins).Test plan
pytest tests/test_run_loop_workload.py -q— 10 passed, including 2 new regression tests (override case + explicit-cfg.workload-wins precedence case)pyarrow-dependent failures unrelated to this change)🤖 Generated with Claude Code