From 5998279f179e9af78ee5bebda2688478fb83b0f2 Mon Sep 17 00:00:00 2001 From: danielhanchen Date: Wed, 1 Jul 2026 08:17:22 +0000 Subject: [PATCH 1/2] MLX CI: bound llama-cli context so the GGUF reload stops timing out With the binary finally located and run, the reload timed out after 300s on a 270m model. The invocation passed no -c, so llama-cli allocated gemma-3's large default n_ctx; that KV cache plus the Metal graph for a 262144-token vocab blows past the runner's memory guard and never returns. The sibling llama-server smoke step already runs this same model family with -c 256 and passes, so bound the context the same way. Also surface llama-cli's partial stdout/stderr on TimeoutExpired so any future hang is diagnosable from the CI log rather than opaque. --- tests/studio/run_real_mlx_smoke.py | 57 ++++++++++++++++++------------ 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/tests/studio/run_real_mlx_smoke.py b/tests/studio/run_real_mlx_smoke.py index 0843a2b447..8bad39d416 100644 --- a/tests/studio/run_real_mlx_smoke.py +++ b/tests/studio/run_real_mlx_smoke.py @@ -566,29 +566,40 @@ def _reload_gguf(save_dir: Path, metrics: dict) -> int: gguf_path = gguf_files[0] with Phase("reload_gguf", metrics): - proc = subprocess.run( - [ - str(llama_cli), - "-m", - str(gguf_path), - "-p", - PROMPT, - "-n", - "24", - "--temp", - "0", - "--seed", - str(SEED), - "-no-cnv", - "--no-warmup", - ], - capture_output = True, - text = True, - timeout = 300, - # Hand llama-cli an immediate EOF; without it -no-cnv can still leave the - # process blocked reading stdin, which times out instead of generating. - stdin = subprocess.DEVNULL, - ) + argv = [ + str(llama_cli), + "-m", + str(gguf_path), + "-p", + PROMPT, + "-n", + "24", + "--temp", + "0", + "--seed", + str(SEED), + # Bound the context: gemma-3's large default n_ctx builds a huge KV cache and + # Metal graph that times out on the runner. Mirrors the llama-server smoke step. + "-c", + "256", + "-no-cnv", + "--no-warmup", + ] + try: + proc = subprocess.run( + argv, + capture_output = True, + text = True, + timeout = 300, + # Hand llama-cli an immediate EOF so -no-cnv can't leave it blocked on stdin. + stdin = subprocess.DEVNULL, + ) + except subprocess.TimeoutExpired as exc: + # Surface whatever llama-cli emitted before the timeout so a future hang is + # diagnosable from the CI log instead of being opaque. + print(f" [reload:gguf] TIMEOUT stdout:\n{(exc.stdout or '')[:1000]}", flush = True) + print(f" [reload:gguf] TIMEOUT stderr:\n{(exc.stderr or '')[:1000]}", flush = True) + raise metrics["llama_cli_returncode"] = proc.returncode metrics["generation"] = (proc.stdout or "")[:1500] From 052f2b6f9725d22322f3d442335c569acc6d000a Mon Sep 17 00:00:00 2001 From: danielhanchen Date: Wed, 1 Jul 2026 08:45:38 +0000 Subject: [PATCH 2/2] MLX CI: decode llama-cli timeout output before logging subprocess.TimeoutExpired carries stdout/stderr as raw bytes even under text=True, so printing them directly rendered a b'...' repr with escaped newlines. Decode (replacing undecodable bytes) so the timeout diagnostics are readable in the CI log. --- tests/studio/run_real_mlx_smoke.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/studio/run_real_mlx_smoke.py b/tests/studio/run_real_mlx_smoke.py index 8bad39d416..a0a32a68dd 100644 --- a/tests/studio/run_real_mlx_smoke.py +++ b/tests/studio/run_real_mlx_smoke.py @@ -596,9 +596,15 @@ def _reload_gguf(save_dir: Path, metrics: dict) -> int: ) except subprocess.TimeoutExpired as exc: # Surface whatever llama-cli emitted before the timeout so a future hang is - # diagnosable from the CI log instead of being opaque. - print(f" [reload:gguf] TIMEOUT stdout:\n{(exc.stdout or '')[:1000]}", flush = True) - print(f" [reload:gguf] TIMEOUT stderr:\n{(exc.stderr or '')[:1000]}", flush = True) + # diagnosable from the CI log. TimeoutExpired carries raw bytes even under + # text=True, so decode before printing (else it renders as b'...' repr). + def _decode(stream) -> str: + if isinstance(stream, bytes): + return stream.decode("utf-8", errors = "replace") + return stream or "" + + print(f" [reload:gguf] TIMEOUT stdout:\n{_decode(exc.stdout)[:1000]}", flush = True) + print(f" [reload:gguf] TIMEOUT stderr:\n{_decode(exc.stderr)[:1000]}", flush = True) raise metrics["llama_cli_returncode"] = proc.returncode