Skip to content

Commit 88ccd8c

Browse files
committed
fix(cli): preserve service-stop hint endpoint
1 parent 1844685 commit 88ccd8c

3 files changed

Lines changed: 60 additions & 7 deletions

File tree

‎AGENTS.md‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -259,9 +259,10 @@ This file defines how coding agents should work in this repository.
259259
objects for invalid endpoints, not tracebacks.
260260
- `keep-gpu start` follow-up status/stop/service-stop hints must target the
261261
selected service endpoint while keeping default-endpoint hints concise.
262-
- CLI failure/recovery hints that tell users to run `keep-gpu serve` or
263-
`keep-gpu service-stop --force` must likewise target the selected service
264-
endpoint while keeping default-endpoint hints concise.
262+
- CLI failure/recovery hints that tell users to run `keep-gpu serve`,
263+
`keep-gpu stop --all`, or `keep-gpu service-stop --force` must likewise
264+
target the selected service endpoint while keeping default-endpoint hints
265+
concise.
265266
- Shared public endpoint validation belongs in
266267
`src/keep_gpu/utilities/endpoint_validation.py`; CLI and MCP entry points
267268
should only translate those `ValueError`s into their interface-specific error

‎src/keep_gpu/cli.py‎

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,7 +1003,12 @@ def _require_clean_stop_keep_for_service_stop(
10031003
)
10041004

10051005

1006-
def _require_no_active_jobs_for_service_stop(status: Dict[str, Any]) -> None:
1006+
def _require_no_active_jobs_for_service_stop(
1007+
status: Dict[str, Any],
1008+
*,
1009+
stop_all_command: str = "keep-gpu stop --all",
1010+
force_command: str = "keep-gpu service-stop --force",
1011+
) -> None:
10071012
active_jobs = status.get("active_jobs", [])
10081013
if not active_jobs:
10091014
return
@@ -1013,7 +1018,8 @@ def _require_no_active_jobs_for_service_stop(status: Dict[str, Any]) -> None:
10131018
detail = f" Active jobs: {job_ids}." if job_ids else ""
10141019
raise RuntimeError(
10151020
"Tracked keep sessions detected."
1016-
f"{detail} Stop sessions first (`keep-gpu stop --all`) or re-run with --force."
1021+
f"{detail} Stop sessions first (`{stop_all_command}`) or re-run with "
1022+
f"`{force_command}`."
10171023
)
10181024

10191025

@@ -1487,7 +1493,15 @@ def service_stop(
14871493
f"`{service_stop_force_command}`."
14881494
) from exc
14891495

1490-
_require_no_active_jobs_for_service_stop(status)
1496+
stop_all_command = _keep_gpu_hint_command("stop", host, port, "--all")
1497+
service_stop_force_command = _keep_gpu_hint_command(
1498+
"service-stop", host, port, "--force"
1499+
)
1500+
_require_no_active_jobs_for_service_stop(
1501+
status,
1502+
stop_all_command=stop_all_command,
1503+
force_command=service_stop_force_command,
1504+
)
14911505
stop_result = _rpc_call("stop_keep", {}, host, port, timeout=45.0)
14921506
stop_result = _validate_stop_keep_result(stop_result)
14931507
_require_clean_stop_keep_for_service_stop(
@@ -1496,7 +1510,11 @@ def service_stop(
14961510
)
14971511
status = _rpc_call("status", {}, host, port)
14981512
status = _validate_status_result(status, single_job=False)
1499-
_require_no_active_jobs_for_service_stop(status)
1513+
_require_no_active_jobs_for_service_stop(
1514+
status,
1515+
stop_all_command=stop_all_command,
1516+
force_command=service_stop_force_command,
1517+
)
15001518

15011519
stopped = _stop_service_process(host, port)
15021520
if not stopped:

‎tests/test_cli_service_commands.py‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3049,6 +3049,40 @@ def test_service_stop_refuses_active_sessions_without_force(monkeypatch):
30493049
assert "Tracked keep sessions detected" in result.output
30503050

30513051

3052+
def test_service_stop_active_session_hint_uses_custom_endpoint(monkeypatch):
3053+
monkeypatch.setattr(cli, "_service_available", lambda *args, **kwargs: True)
3054+
rpc_methods = []
3055+
3056+
def fake_rpc_call(method, params, host, port, timeout=8.0):
3057+
rpc_methods.append(method)
3058+
return (
3059+
{"active_jobs": [{**_status_session_record(), "job_id": "j1"}]}
3060+
if method == "status"
3061+
else {"stopped": []}
3062+
)
3063+
3064+
def fail_if_service_process_stops(host, port):
3065+
raise AssertionError("service-stop must not signal active-session daemons")
3066+
3067+
monkeypatch.setattr(cli, "_rpc_call", fake_rpc_call)
3068+
monkeypatch.setattr(cli, "_stop_service_process", fail_if_service_process_stops)
3069+
3070+
result = runner.invoke(
3071+
cli.app, ["service-stop", "--host", "localhost", "--port", "9999"]
3072+
)
3073+
3074+
normalized_output = " ".join(result.output.split())
3075+
assert result.exit_code == 1
3076+
assert "Tracked keep sessions detected" in normalized_output
3077+
assert "keep-gpu stop --host localhost --port 9999 --all" in normalized_output
3078+
assert (
3079+
"keep-gpu service-stop --host localhost --port 9999 --force"
3080+
in normalized_output
3081+
)
3082+
assert "`keep-gpu stop --all`" not in normalized_output
3083+
assert rpc_methods == ["status"]
3084+
3085+
30523086
def test_stop_handles_service_timeout_without_traceback(monkeypatch):
30533087
def fake_rpc(method, params, host, port, timeout=8.0):
30543088
raise cli.ServiceUnreachableError(

0 commit comments

Comments
 (0)