Fix Studio re-exec compatibility

This commit is contained in:
oobabooga 2026-07-21 17:49:48 -03:00
commit 36f896a528
2 changed files with 63 additions and 17 deletions

View file

@ -106,6 +106,13 @@ API_KEY_PBKDF2_SALT_KEY = "api_key_pbkdf2_salt"
DESKTOP_SECRET_HASH_KEY = "desktop_secret_hash"
DESKTOP_SECRET_CREATED_AT_KEY = "desktop_secret_created_at"
PBKDF2_ITERATIONS = 100_000
_START_API_KEY_MARKER_ENV = "_UNSLOTH_START_API_KEY_MARKER"
def _consume_start_api_key_marker_env() -> bool:
"""Consume the one-shot readiness marker passed across a Studio re-exec."""
return os.environ.pop(_START_API_KEY_MARKER_ENV, None) == "1"
# __file__ is unsloth_cli/commands/studio.py -- two parents up is the package root
# (either site-packages or the repo root for editable installs).
@ -1792,6 +1799,12 @@ def run(
unsloth studio run --model some-model --chat-template-file /path/to/tpl.jinja
unsloth studio run --model unsloth/Qwen3-27B-GGUF --gguf-variant Q8_0 --tensor-parallel
"""
# A newer outer CLI can re-exec into an older Studio venv. Pass this
# internal signal through the environment so an older child ignores it
# instead of treating an unknown CLI option as a llama-server argument.
inherited_start_api_key_marker = _consume_start_api_key_marker_env()
start_api_key_marker = start_api_key_marker or inherited_start_api_key_marker
# Back-compat: --not-secure is a deprecated alias for --no-secure.
secure = _resolve_secure(secure, not_secure)
extra_llama_args: List[str] = list(ctx.args) if ctx.args else []
@ -1991,23 +2004,28 @@ def run(
args.append("--no-cloudflare")
args.append("--secure" if secure else "--no-secure")
args.append("--tensor-parallel" if tensor_parallel else "--no-tensor-parallel")
if start_api_key_marker:
args.append("--start-api-key-marker")
if verbose:
args.append("--verbose")
# llama-server pass-through extras → child ctx.args → load payload.
if extra_llama_args:
args.extend(extra_llama_args)
if sys.platform == "win32":
proc = subprocess.Popen(args)
try:
rc = proc.wait()
except KeyboardInterrupt:
rc = proc.wait()
raise typer.Exit(rc)
else:
os.execvp(str(studio_bin), args)
if start_api_key_marker:
os.environ[_START_API_KEY_MARKER_ENV] = "1"
try:
if sys.platform == "win32":
proc = subprocess.Popen(args)
try:
rc = proc.wait()
except KeyboardInterrupt:
rc = proc.wait()
raise typer.Exit(rc)
else:
os.execvp(str(studio_bin), args)
finally:
# execvp does not return on success. Restore the parent environment
# after Windows waits for the child, or if launch fails.
os.environ.pop(_START_API_KEY_MARKER_ENV, None)
# ── 2. Start server (always suppress built-in banner) ─────────────
run_mod = _load_run_module()

View file

@ -170,13 +170,24 @@ def _install_reexec_capture(monkeypatch, *, platform):
monkeypatch.setattr(sys, "platform", platform)
def capture(kind, argv):
captured.append(
{
"kind": kind,
"argv": list(argv),
"start_api_key_marker": studio_mod.os.environ.get(
studio_mod._START_API_KEY_MARKER_ENV
),
}
)
def fake_execvp(file, argv):
captured.append({"kind": "execvp", "argv": list(argv)})
capture("execvp", argv)
raise _ExecCaptured(argv)
class _FakePopen:
def __init__(self, argv, *a, **kw):
captured.append({"kind": "popen", "argv": list(argv)})
capture("popen", argv)
self._argv = argv
def wait(self):
@ -235,11 +246,28 @@ def test_reexec_forwards_parallel_all_aliases(monkeypatch, flag, value):
), f"{flag} {value} was dropped on re-exec; argv = {argv}"
def test_reexec_forwards_start_api_key_marker(monkeypatch):
"""The internal progress marker must survive the studio-venv re-exec."""
result, captured = _invoke_run(monkeypatch, _BASE + ["--start-api-key-marker"])
@pytest.mark.parametrize("platform", ["linux", "darwin", "win32"])
def test_reexec_hands_off_start_api_key_marker_out_of_band(monkeypatch, platform):
"""A new child receives the marker while an old child sees no unknown flag."""
result, captured = _invoke_run(
monkeypatch,
_BASE + ["--start-api-key-marker"],
platform = platform,
)
assert len(captured) == 1, result.output
assert "--start-api-key-marker" in captured[0]["argv"]
assert "--start-api-key-marker" not in captured[0]["argv"]
assert captured[0]["start_api_key_marker"] == "1"
def test_reexeced_child_consumes_start_api_key_marker_env(monkeypatch):
"""A supported child consumes the handoff before starting descendants."""
studio_mod = _load_run_command()
monkeypatch.setenv(studio_mod._START_API_KEY_MARKER_ENV, "1")
inherited = studio_mod._consume_start_api_key_marker_env()
assert inherited is True
assert studio_mod._START_API_KEY_MARKER_ENV not in studio_mod.os.environ
@pytest.mark.parametrize("platform", ["linux", "darwin", "win32"])