From 9e442cd353a5000933898b661bb883bed17f9b34 Mon Sep 17 00:00:00 2001 From: gspark Date: Fri, 23 Oct 2026 18:39:05 +0900 Subject: [PATCH] fix: suppress SIGPIPE for HTTP socket writes (#214) --- .../validation/http214/.research/events.jsonl | 1 + opt_prime/docs/validation/http214/STATUS.md | 3 + .../atom-http-qwen3-06b-cache-fixed.json | 761 +++++++++++++++++ .../evidence/atom-http-qwen3-06b-cache.json | 761 +++++++++++++++++ .../atom-http-qwen3-4b-host-fixed.json | 762 ++++++++++++++++++ .../experiments/2026-10-23-http-sigpipe214.md | 40 + opt_prime/opt_prime/accel/serve/llmd.py | 22 + opt_prime/tests/test_http_sigpipe.py | 37 + 8 files changed, 2387 insertions(+) create mode 100644 opt_prime/docs/validation/http214/.research/events.jsonl create mode 100644 opt_prime/docs/validation/http214/STATUS.md create mode 100644 opt_prime/docs/validation/http214/evidence/atom-http-qwen3-06b-cache-fixed.json create mode 100644 opt_prime/docs/validation/http214/evidence/atom-http-qwen3-06b-cache.json create mode 100644 opt_prime/docs/validation/http214/evidence/atom-http-qwen3-4b-host-fixed.json create mode 100644 opt_prime/docs/validation/http214/experiments/2026-10-23-http-sigpipe214.md create mode 100644 opt_prime/tests/test_http_sigpipe.py diff --git a/opt_prime/docs/validation/http214/.research/events.jsonl b/opt_prime/docs/validation/http214/.research/events.jsonl new file mode 100644 index 00000000..f6c6d8e3 --- /dev/null +++ b/opt_prime/docs/validation/http214/.research/events.jsonl @@ -0,0 +1 @@ +{"id": "6e7a45edfd094e37a2af3d6104fbbaad", "ts": "2026-10-23T18:38:59+09:00", "task": "HTTP SIGPIPE handling (aicomp #214)", "result": "MSG_NOSIGNAL writer retains BrokenPipeError;14 tests pass. ATOM Qwen0.6 cache15 sequences exact before/after; native backtrace headers6 to0. Qwen4B host-cache lifecycle passes.", "next": "Maintainer review of Linux per-write suppression; full four-model goal remains active.", "state": "active", "done": false, "blocker": "", "metrics": {}, "params": {"sdk": "0.11.0", "backend": "atom"}, "artifacts": ["evidence/atom-http-qwen3-06b-cache-fixed.json"], "experiments": ["experiments/2026-10-23-http-sigpipe214.md"], "labels": ["bug"], "review": true, "todos": [], "note": "", "agent": "codex", "host": "RNGD+ATOM host", "branch": "fix/214-http-sigpipe", "commit": "1b3937f-dirty"} diff --git a/opt_prime/docs/validation/http214/STATUS.md b/opt_prime/docs/validation/http214/STATUS.md new file mode 100644 index 00000000..58706d88 --- /dev/null +++ b/opt_prime/docs/validation/http214/STATUS.md @@ -0,0 +1,3 @@ +# HTTP SIGPIPE validation + +State: review. Per-write MSG_NOSIGNAL preserves BrokenPipeError and avoids the observed native SIGPIPE backtraces. Fourteen tests pass; Qwen0.6 ATOM before/after outputs match. Qwen4B host-cache passes. Full four-model goal remains active; see experiment for scope. diff --git a/opt_prime/docs/validation/http214/evidence/atom-http-qwen3-06b-cache-fixed.json b/opt_prime/docs/validation/http214/evidence/atom-http-qwen3-06b-cache-fixed.json new file mode 100644 index 00000000..ad9a24b6 --- /dev/null +++ b/opt_prime/docs/validation/http214/evidence/atom-http-qwen3-06b-cache-fixed.json @@ -0,0 +1,761 @@ +{ + "model_name": "Qwen3-0.6B", + "prefix_cache": true, + "host_kv_gib": 0, + "passed": true, + "scope": "HTTP lifecycle and completion counts; numerical equality reported separately", + "torch": "2.11.0+cpu", + "dtype": "compiled artifact", + "backend": "atom", + "slots": 1, + "atom_kv_blocks": 3, + "device": "allocated ATOM", + "cases": [ + { + "concurrency": 4, + "wall_s": 1.248584700981155, + "rows": [ + { + "input_tokens": 128, + "tokens": [ + 9625, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ], + "e2e_s": 0.28151508001610637, + "baseline_equal": true + }, + { + "input_tokens": 2048, + "tokens": [ + 9625, + 374, + 264, + 3146, + 7407, + 304, + 4505, + 13, + 11445, + 6722, + 374, + 12095, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374 + ], + "e2e_s": 0.5722270999103785, + "baseline_equal": true + }, + { + "input_tokens": 8192, + "tokens": [ + 12095, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ], + "e2e_s": 0.9905206640250981, + "baseline_equal": true + }, + { + "input_tokens": 128, + "tokens": [ + 9625, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ], + "e2e_s": 1.2314512348966673, + "baseline_equal": true + } + ] + }, + { + "concurrency": 8, + "wall_s": 4.522928233956918, + "rows": [ + { + "input_tokens": 128, + "tokens": [ + 9625, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ], + "e2e_s": 0.27896868309471756, + "baseline_equal": true + }, + { + "input_tokens": 2048, + "tokens": [ + 9625, + 374, + 264, + 3146, + 7407, + 304, + 4505, + 13, + 11445, + 6722, + 374, + 12095, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374 + ], + "e2e_s": 2.0992609749082476, + "baseline_equal": true + }, + { + "input_tokens": 8192, + "tokens": [ + 12095, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ], + "e2e_s": 1.805448218015954, + "baseline_equal": true + }, + { + "input_tokens": 128, + "tokens": [ + 9625, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ], + "e2e_s": 2.6197337730554864, + "baseline_equal": true + }, + { + "input_tokens": 2048, + "tokens": [ + 9625, + 374, + 264, + 3146, + 7407, + 304, + 4505, + 13, + 11445, + 6722, + 374, + 12095, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374 + ], + "e2e_s": 2.374678032938391, + "baseline_equal": true + }, + { + "input_tokens": 8192, + "tokens": [ + 12095, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ], + "e2e_s": 3.955605463939719, + "baseline_equal": true + }, + { + "input_tokens": 128, + "tokens": [ + 9625, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ], + "e2e_s": 4.208059304044582, + "baseline_equal": true + }, + { + "input_tokens": 2048, + "tokens": [ + 9625, + 374, + 264, + 3146, + 7407, + 304, + 4505, + 13, + 11445, + 6722, + 374, + 12095, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374 + ], + "e2e_s": 4.500310557894409, + "baseline_equal": true + } + ] + } + ], + "baseline": { + "128": { + "input_tokens": 128, + "tokens": [ + 9625, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ], + "e2e_s": 0.2541884219972417 + }, + "2048": { + "input_tokens": 2048, + "tokens": [ + 9625, + 374, + 264, + 3146, + 7407, + 304, + 4505, + 13, + 11445, + 6722, + 374, + 12095, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374 + ], + "e2e_s": 0.466158407041803 + }, + "8192": { + "input_tokens": 8192, + "tokens": [ + 12095, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ], + "e2e_s": 1.3094038530252874 + } + }, + "cancel": { + "drained": true, + "subsequent_tokens_equal": true, + "cold_baseline_equal": true, + "before_tokens": [ + 9625, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ], + "after_tokens": [ + 9625, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ] + }, + "disconnect": { + "drained": true, + "subsequent_tokens_equal": true, + "cold_baseline_equal": true, + "before_tokens": [ + 9625, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ], + "after_tokens": [ + 9625, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ] + }, + "invalid_status": 400, + "remaining_requests": 0, + "metrics": "# HELP vllm:num_requests_running Requests executing on the stage.\n# TYPE vllm:num_requests_running gauge\nvllm:num_requests_running{model_name=\"Qwen3-0.6B\"} 0\n# HELP vllm:num_requests_waiting Requests queued for the stage owner.\n# TYPE vllm:num_requests_waiting gauge\nvllm:num_requests_waiting{model_name=\"Qwen3-0.6B\"} 0\n# HELP vllm:kv_cache_usage_perc Occupied fraction of the stage's KV lanes (1 = full).\n# TYPE vllm:kv_cache_usage_perc gauge\nvllm:kv_cache_usage_perc{model_name=\"Qwen3-0.6B\"} 0.0\n# HELP vllm:gpu_cache_usage_perc Occupied fraction of the stage's KV lanes (1 = full).\n# TYPE vllm:gpu_cache_usage_perc gauge\nvllm:gpu_cache_usage_perc{model_name=\"Qwen3-0.6B\"} 0.0\n# HELP vllm:request_success_total Finished requests by finish reason.\n# TYPE vllm:request_success_total counter\nvllm:request_success_total{model_name=\"Qwen3-0.6B\",finished_reason=\"length\"} 19\n# HELP airm_stage_request_failures_total Requests that ended with an engine error.\n# TYPE airm_stage_request_failures_total counter\nairm_stage_request_failures_total{model_name=\"Qwen3-0.6B\"} 0\n# HELP vllm:prompt_tokens_total Prompt tokens of finished requests.\n# TYPE vllm:prompt_tokens_total counter\nvllm:prompt_tokens_total{model_name=\"Qwen3-0.6B\"} 44288\n# HELP vllm:generation_tokens_total Generated tokens of finished requests.\n# TYPE vllm:generation_tokens_total counter\nvllm:generation_tokens_total{model_name=\"Qwen3-0.6B\"} 608\n# HELP vllm:time_to_first_token_seconds Time to first token (prefill stage: fenced prefill).\n# TYPE vllm:time_to_first_token_seconds histogram\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.001\"} 0\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.005\"} 0\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.01\"} 0\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.02\"} 6\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.04\"} 7\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.06\"} 7\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.08\"} 7\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.1\"} 7\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.25\"} 8\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.5\"} 9\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.75\"} 10\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"1.0\"} 12\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"2.5\"} 16\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"5.0\"} 19\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"7.5\"} 19\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"10.0\"} 19\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"+Inf\"} 19\nvllm:time_to_first_token_seconds_sum{model_name=\"Qwen3-0.6B\"} 22.532509238808416\nvllm:time_to_first_token_seconds_count{model_name=\"Qwen3-0.6B\"} 19\n# HELP vllm:time_per_output_token_seconds Time per output token after the first (decode stage).\n# TYPE vllm:time_per_output_token_seconds histogram\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.01\"} 15\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.025\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.05\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.075\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.1\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.15\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.2\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.3\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.4\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.5\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.75\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"1.0\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"2.5\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"+Inf\"} 19\nvllm:time_per_output_token_seconds_sum{model_name=\"Qwen3-0.6B\"} 0.17089263726806933\nvllm:time_per_output_token_seconds_count{model_name=\"Qwen3-0.6B\"} 19\n# HELP vllm:e2e_request_latency_seconds Stage request latency, from execution start to reply.\n# TYPE vllm:e2e_request_latency_seconds histogram\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.3\"} 7\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.5\"} 8\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.8\"} 9\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"1.0\"} 10\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"1.5\"} 12\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"2.0\"} 13\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"2.5\"} 15\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"5.0\"} 19\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"10.0\"} 19\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"15.0\"} 19\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"20.0\"} 19\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"30.0\"} 19\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"40.0\"} 19\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"50.0\"} 19\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"60.0\"} 19\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"+Inf\"} 19\nvllm:e2e_request_latency_seconds_sum{model_name=\"Qwen3-0.6B\"} 27.830180994118564\nvllm:e2e_request_latency_seconds_count{model_name=\"Qwen3-0.6B\"} 19\n# HELP airm_kv_transfer_seconds KV transfer time from the prefill source to the decode destination.\n# TYPE airm_kv_transfer_seconds histogram\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.0005\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.001\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.0025\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.005\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.01\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.025\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.05\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.1\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.25\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.5\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"1.0\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"+Inf\"} 0\nairm_kv_transfer_seconds_sum{model_name=\"Qwen3-0.6B\"} 0.0\nairm_kv_transfer_seconds_count{model_name=\"Qwen3-0.6B\"} 0\n# HELP vllm:prefix_cache_queries_total Prompt tokens looked up in the prefix cache.\n# TYPE vllm:prefix_cache_queries_total counter\nvllm:prefix_cache_queries_total{model_name=\"Qwen3-0.6B\"} 44544\n# HELP vllm:prefix_cache_hits_total Prompt tokens served from the prefix cache.\n# TYPE vllm:prefix_cache_hits_total counter\nvllm:prefix_cache_hits_total{model_name=\"Qwen3-0.6B\"} 19584\n# HELP vllm:cache_config_info Information of the LLMEngine CacheConfig.\n# TYPE vllm:cache_config_info gauge\nvllm:cache_config_info{model_name=\"Qwen3-0.6B\",block_size=\"128\",enable_prefix_caching=\"True\"} 1\n", + "prefix_hit_tokens": 19584.0 +} diff --git a/opt_prime/docs/validation/http214/evidence/atom-http-qwen3-06b-cache.json b/opt_prime/docs/validation/http214/evidence/atom-http-qwen3-06b-cache.json new file mode 100644 index 00000000..7a9d28f5 --- /dev/null +++ b/opt_prime/docs/validation/http214/evidence/atom-http-qwen3-06b-cache.json @@ -0,0 +1,761 @@ +{ + "model_name": "Qwen3-0.6B", + "prefix_cache": true, + "host_kv_gib": 0, + "passed": true, + "scope": "HTTP lifecycle and completion counts; numerical equality reported separately", + "torch": "2.11.0+cpu", + "dtype": "compiled artifact", + "backend": "atom", + "slots": 1, + "atom_kv_blocks": 3, + "device": "allocated ATOM", + "cases": [ + { + "concurrency": 4, + "wall_s": 1.2128619889263064, + "rows": [ + { + "input_tokens": 128, + "tokens": [ + 9625, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ], + "e2e_s": 0.25869818101637065, + "baseline_equal": true + }, + { + "input_tokens": 2048, + "tokens": [ + 9625, + 374, + 264, + 3146, + 7407, + 304, + 4505, + 13, + 11445, + 6722, + 374, + 12095, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374 + ], + "e2e_s": 0.5717005589976907, + "baseline_equal": true + }, + { + "input_tokens": 8192, + "tokens": [ + 12095, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ], + "e2e_s": 0.978555666981265, + "baseline_equal": true + }, + { + "input_tokens": 128, + "tokens": [ + 9625, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ], + "e2e_s": 1.203579452005215, + "baseline_equal": true + } + ] + }, + { + "concurrency": 8, + "wall_s": 4.730229896027595, + "rows": [ + { + "input_tokens": 128, + "tokens": [ + 9625, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ], + "e2e_s": 0.3080808660015464, + "baseline_equal": true + }, + { + "input_tokens": 2048, + "tokens": [ + 9625, + 374, + 264, + 3146, + 7407, + 304, + 4505, + 13, + 11445, + 6722, + 374, + 12095, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374 + ], + "e2e_s": 0.7815210010157898, + "baseline_equal": true + }, + { + "input_tokens": 8192, + "tokens": [ + 12095, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ], + "e2e_s": 2.09453048708383, + "baseline_equal": true + }, + { + "input_tokens": 128, + "tokens": [ + 9625, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ], + "e2e_s": 2.9762356269638985, + "baseline_equal": true + }, + { + "input_tokens": 2048, + "tokens": [ + 9625, + 374, + 264, + 3146, + 7407, + 304, + 4505, + 13, + 11445, + 6722, + 374, + 12095, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374 + ], + "e2e_s": 2.676780418958515, + "baseline_equal": true + }, + { + "input_tokens": 8192, + "tokens": [ + 12095, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ], + "e2e_s": 4.232980364002287, + "baseline_equal": true + }, + { + "input_tokens": 128, + "tokens": [ + 9625, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ], + "e2e_s": 4.448256555013359, + "baseline_equal": true + }, + { + "input_tokens": 2048, + "tokens": [ + 9625, + 374, + 264, + 3146, + 7407, + 304, + 4505, + 13, + 11445, + 6722, + 374, + 12095, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374 + ], + "e2e_s": 4.70400058105588, + "baseline_equal": true + } + ] + } + ], + "baseline": { + "128": { + "input_tokens": 128, + "tokens": [ + 9625, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ], + "e2e_s": 0.3220145619707182 + }, + "2048": { + "input_tokens": 2048, + "tokens": [ + 9625, + 374, + 264, + 3146, + 7407, + 304, + 4505, + 13, + 11445, + 6722, + 374, + 12095, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374 + ], + "e2e_s": 0.641035900101997 + }, + "8192": { + "input_tokens": 8192, + "tokens": [ + 12095, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ], + "e2e_s": 1.3148153700167313 + } + }, + "cancel": { + "drained": true, + "subsequent_tokens_equal": true, + "cold_baseline_equal": true, + "before_tokens": [ + 9625, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ], + "after_tokens": [ + 9625, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ] + }, + "disconnect": { + "drained": true, + "subsequent_tokens_equal": true, + "cold_baseline_equal": true, + "before_tokens": [ + 9625, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ], + "after_tokens": [ + 9625, + 198, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303, + 3745, + 5610, + 6467, + 624, + 1986, + 374, + 4004, + 2266, + 13, + 576, + 2518, + 3745, + 5610, + 40676, + 13, + 576, + 6303 + ] + }, + "invalid_status": 400, + "remaining_requests": 0, + "metrics": "# HELP vllm:num_requests_running Requests executing on the stage.\n# TYPE vllm:num_requests_running gauge\nvllm:num_requests_running{model_name=\"Qwen3-0.6B\"} 0\n# HELP vllm:num_requests_waiting Requests queued for the stage owner.\n# TYPE vllm:num_requests_waiting gauge\nvllm:num_requests_waiting{model_name=\"Qwen3-0.6B\"} 0\n# HELP vllm:kv_cache_usage_perc Occupied fraction of the stage's KV lanes (1 = full).\n# TYPE vllm:kv_cache_usage_perc gauge\nvllm:kv_cache_usage_perc{model_name=\"Qwen3-0.6B\"} 0.0\n# HELP vllm:gpu_cache_usage_perc Occupied fraction of the stage's KV lanes (1 = full).\n# TYPE vllm:gpu_cache_usage_perc gauge\nvllm:gpu_cache_usage_perc{model_name=\"Qwen3-0.6B\"} 0.0\n# HELP vllm:request_success_total Finished requests by finish reason.\n# TYPE vllm:request_success_total counter\nvllm:request_success_total{model_name=\"Qwen3-0.6B\",finished_reason=\"length\"} 19\n# HELP airm_stage_request_failures_total Requests that ended with an engine error.\n# TYPE airm_stage_request_failures_total counter\nairm_stage_request_failures_total{model_name=\"Qwen3-0.6B\"} 0\n# HELP vllm:prompt_tokens_total Prompt tokens of finished requests.\n# TYPE vllm:prompt_tokens_total counter\nvllm:prompt_tokens_total{model_name=\"Qwen3-0.6B\"} 44288\n# HELP vllm:generation_tokens_total Generated tokens of finished requests.\n# TYPE vllm:generation_tokens_total counter\nvllm:generation_tokens_total{model_name=\"Qwen3-0.6B\"} 608\n# HELP vllm:time_to_first_token_seconds Time to first token (prefill stage: fenced prefill).\n# TYPE vllm:time_to_first_token_seconds histogram\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.001\"} 0\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.005\"} 0\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.01\"} 0\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.02\"} 5\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.04\"} 7\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.06\"} 7\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.08\"} 7\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.1\"} 7\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.25\"} 8\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.5\"} 10\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.75\"} 11\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"1.0\"} 13\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"2.5\"} 15\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"5.0\"} 19\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"7.5\"} 19\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"10.0\"} 19\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"+Inf\"} 19\nvllm:time_to_first_token_seconds_sum{model_name=\"Qwen3-0.6B\"} 22.62249407276977\nvllm:time_to_first_token_seconds_count{model_name=\"Qwen3-0.6B\"} 19\n# HELP vllm:time_per_output_token_seconds Time per output token after the first (decode stage).\n# TYPE vllm:time_per_output_token_seconds histogram\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.01\"} 13\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.025\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.05\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.075\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.1\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.15\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.2\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.3\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.4\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.5\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.75\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"1.0\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"2.5\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"+Inf\"} 19\nvllm:time_per_output_token_seconds_sum{model_name=\"Qwen3-0.6B\"} 0.18044153184464742\nvllm:time_per_output_token_seconds_count{model_name=\"Qwen3-0.6B\"} 19\n# HELP vllm:e2e_request_latency_seconds Stage request latency, from execution start to reply.\n# TYPE vllm:e2e_request_latency_seconds histogram\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.3\"} 6\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.5\"} 7\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.8\"} 10\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"1.0\"} 11\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"1.5\"} 13\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"2.0\"} 13\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"2.5\"} 14\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"5.0\"} 19\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"10.0\"} 19\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"15.0\"} 19\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"20.0\"} 19\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"30.0\"} 19\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"40.0\"} 19\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"50.0\"} 19\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"60.0\"} 19\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"+Inf\"} 19\nvllm:e2e_request_latency_seconds_sum{model_name=\"Qwen3-0.6B\"} 28.21618155995384\nvllm:e2e_request_latency_seconds_count{model_name=\"Qwen3-0.6B\"} 19\n# HELP airm_kv_transfer_seconds KV transfer time from the prefill source to the decode destination.\n# TYPE airm_kv_transfer_seconds histogram\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.0005\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.001\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.0025\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.005\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.01\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.025\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.05\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.1\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.25\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"0.5\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"1.0\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-0.6B\",le=\"+Inf\"} 0\nairm_kv_transfer_seconds_sum{model_name=\"Qwen3-0.6B\"} 0.0\nairm_kv_transfer_seconds_count{model_name=\"Qwen3-0.6B\"} 0\n# HELP vllm:prefix_cache_queries_total Prompt tokens looked up in the prefix cache.\n# TYPE vllm:prefix_cache_queries_total counter\nvllm:prefix_cache_queries_total{model_name=\"Qwen3-0.6B\"} 44544\n# HELP vllm:prefix_cache_hits_total Prompt tokens served from the prefix cache.\n# TYPE vllm:prefix_cache_hits_total counter\nvllm:prefix_cache_hits_total{model_name=\"Qwen3-0.6B\"} 19584\n# HELP vllm:cache_config_info Information of the LLMEngine CacheConfig.\n# TYPE vllm:cache_config_info gauge\nvllm:cache_config_info{model_name=\"Qwen3-0.6B\",block_size=\"128\",enable_prefix_caching=\"True\"} 1\n", + "prefix_hit_tokens": 19584.0 +} diff --git a/opt_prime/docs/validation/http214/evidence/atom-http-qwen3-4b-host-fixed.json b/opt_prime/docs/validation/http214/evidence/atom-http-qwen3-4b-host-fixed.json new file mode 100644 index 00000000..0d9c3d37 --- /dev/null +++ b/opt_prime/docs/validation/http214/evidence/atom-http-qwen3-4b-host-fixed.json @@ -0,0 +1,762 @@ +{ + "model_name": "Qwen3-4B", + "prefix_cache": true, + "host_kv_gib": 2.0, + "passed": true, + "scope": "HTTP lifecycle and completion counts; numerical equality reported separately", + "torch": "2.11.0+cpu", + "dtype": "compiled artifact", + "backend": "atom", + "slots": 1, + "atom_kv_blocks": 3, + "device": "allocated ATOM", + "cases": [ + { + "concurrency": 4, + "wall_s": 5.3771037210244685, + "rows": [ + { + "input_tokens": 128, + "tokens": [ + 576, + 6722, + 315, + 9625, + 374, + 12095, + 624, + 32313, + 11, + 773, + 279, + 1196, + 4588, + 264, + 3405, + 911, + 279, + 6722, + 315, + 9625, + 11, + 323, + 358, + 3897, + 279, + 4226, + 12095, + 13, + 1988, + 3330, + 1182, + 518 + ], + "e2e_s": 1.4422351559624076, + "baseline_equal": true + }, + { + "input_tokens": 2048, + "tokens": [ + 576, + 6722, + 315, + 9625, + 374, + 12095, + 624, + 32313, + 11, + 773, + 279, + 1196, + 4588, + 369, + 279, + 6722, + 315, + 9625, + 11, + 323, + 358, + 18577, + 12095, + 13, + 1988, + 3783, + 11, + 279, + 1196, + 3897, + 264, + 15493 + ], + "e2e_s": 2.70951707707718, + "baseline_equal": true + }, + { + "input_tokens": 8192, + "tokens": [ + 576, + 6722, + 315, + 9625, + 374, + 12095, + 624, + 32313, + 11, + 773, + 279, + 1196, + 4588, + 264, + 3405, + 911, + 279, + 6722, + 315, + 9625, + 11, + 323, + 358, + 3897, + 279, + 4226, + 12095, + 13, + 1988, + 3330, + 1182, + 518 + ], + "e2e_s": 4.1945555419661105, + "baseline_equal": true + }, + { + "input_tokens": 128, + "tokens": [ + 576, + 6722, + 315, + 9625, + 374, + 12095, + 624, + 32313, + 11, + 773, + 279, + 1196, + 4588, + 264, + 3405, + 911, + 279, + 6722, + 315, + 9625, + 11, + 323, + 358, + 3897, + 279, + 4226, + 12095, + 13, + 1988, + 3330, + 1182, + 518 + ], + "e2e_s": 5.370230198954232, + "baseline_equal": true + } + ] + }, + { + "concurrency": 8, + "wall_s": 10.77056910796091, + "rows": [ + { + "input_tokens": 128, + "tokens": [ + 576, + 6722, + 315, + 9625, + 374, + 12095, + 624, + 32313, + 11, + 773, + 279, + 1196, + 4588, + 264, + 3405, + 911, + 279, + 6722, + 315, + 9625, + 11, + 323, + 358, + 3897, + 279, + 4226, + 12095, + 13, + 1988, + 3330, + 1182, + 518 + ], + "e2e_s": 2.7858462249860168, + "baseline_equal": true + }, + { + "input_tokens": 2048, + "tokens": [ + 576, + 6722, + 315, + 9625, + 374, + 12095, + 624, + 32313, + 11, + 773, + 279, + 1196, + 4588, + 369, + 279, + 6722, + 315, + 9625, + 11, + 323, + 358, + 18577, + 12095, + 13, + 1988, + 3783, + 11, + 279, + 1196, + 3897, + 264, + 15493 + ], + "e2e_s": 1.2965235960436985, + "baseline_equal": true + }, + { + "input_tokens": 8192, + "tokens": [ + 576, + 6722, + 315, + 9625, + 374, + 12095, + 624, + 32313, + 11, + 773, + 279, + 1196, + 4588, + 264, + 3405, + 911, + 279, + 6722, + 315, + 9625, + 11, + 323, + 358, + 3897, + 279, + 4226, + 12095, + 13, + 1988, + 3330, + 1182, + 518 + ], + "e2e_s": 4.345374454976991, + "baseline_equal": true + }, + { + "input_tokens": 128, + "tokens": [ + 576, + 6722, + 315, + 9625, + 374, + 12095, + 624, + 32313, + 11, + 773, + 279, + 1196, + 4588, + 264, + 3405, + 911, + 279, + 6722, + 315, + 9625, + 11, + 323, + 358, + 3897, + 279, + 4226, + 12095, + 13, + 1988, + 3330, + 1182, + 518 + ], + "e2e_s": 5.557994809001684, + "baseline_equal": true + }, + { + "input_tokens": 2048, + "tokens": [ + 576, + 6722, + 315, + 9625, + 374, + 12095, + 624, + 32313, + 11, + 773, + 279, + 1196, + 4588, + 369, + 279, + 6722, + 315, + 9625, + 11, + 323, + 358, + 18577, + 12095, + 13, + 1988, + 3783, + 11, + 279, + 1196, + 3897, + 264, + 15493 + ], + "e2e_s": 9.474480802076869, + "baseline_equal": true + }, + { + "input_tokens": 8192, + "tokens": [ + 576, + 6722, + 315, + 9625, + 374, + 12095, + 624, + 32313, + 11, + 773, + 279, + 1196, + 4588, + 264, + 3405, + 911, + 279, + 6722, + 315, + 9625, + 11, + 323, + 358, + 3897, + 279, + 4226, + 12095, + 13, + 1988, + 3330, + 1182, + 518 + ], + "e2e_s": 6.989578875014558, + "baseline_equal": true + }, + { + "input_tokens": 128, + "tokens": [ + 576, + 6722, + 315, + 9625, + 374, + 12095, + 624, + 32313, + 11, + 773, + 279, + 1196, + 4588, + 264, + 3405, + 911, + 279, + 6722, + 315, + 9625, + 11, + 323, + 358, + 3897, + 279, + 4226, + 12095, + 13, + 1988, + 3330, + 1182, + 518 + ], + "e2e_s": 8.195752296014689, + "baseline_equal": true + }, + { + "input_tokens": 2048, + "tokens": [ + 576, + 6722, + 315, + 9625, + 374, + 12095, + 624, + 32313, + 11, + 773, + 279, + 1196, + 4588, + 369, + 279, + 6722, + 315, + 9625, + 11, + 323, + 358, + 18577, + 12095, + 13, + 1988, + 3783, + 11, + 279, + 1196, + 3897, + 264, + 15493 + ], + "e2e_s": 10.742149700061418, + "baseline_equal": true + } + ] + } + ], + "baseline": { + "128": { + "input_tokens": 128, + "tokens": [ + 576, + 6722, + 315, + 9625, + 374, + 12095, + 624, + 32313, + 11, + 773, + 279, + 1196, + 4588, + 264, + 3405, + 911, + 279, + 6722, + 315, + 9625, + 11, + 323, + 358, + 3897, + 279, + 4226, + 12095, + 13, + 1988, + 3330, + 1182, + 518 + ], + "e2e_s": 1.2765175310196355 + }, + "2048": { + "input_tokens": 2048, + "tokens": [ + 576, + 6722, + 315, + 9625, + 374, + 12095, + 624, + 32313, + 11, + 773, + 279, + 1196, + 4588, + 369, + 279, + 6722, + 315, + 9625, + 11, + 323, + 358, + 18577, + 12095, + 13, + 1988, + 3783, + 11, + 279, + 1196, + 3897, + 264, + 15493 + ], + "e2e_s": 2.4192551710875705 + }, + "8192": { + "input_tokens": 8192, + "tokens": [ + 576, + 6722, + 315, + 9625, + 374, + 12095, + 624, + 32313, + 11, + 773, + 279, + 1196, + 4588, + 264, + 3405, + 911, + 279, + 6722, + 315, + 9625, + 11, + 323, + 358, + 3897, + 279, + 4226, + 12095, + 13, + 1988, + 3330, + 1182, + 518 + ], + "e2e_s": 5.704140087007545 + } + }, + "cancel": { + "drained": true, + "subsequent_tokens_equal": true, + "cold_baseline_equal": true, + "before_tokens": [ + 576, + 6722, + 315, + 9625, + 374, + 12095, + 624, + 32313, + 11, + 773, + 279, + 1196, + 4588, + 264, + 3405, + 911, + 279, + 6722, + 315, + 9625, + 11, + 323, + 358, + 3897, + 279, + 4226, + 12095, + 13, + 1988, + 3330, + 1182, + 518 + ], + "after_tokens": [ + 576, + 6722, + 315, + 9625, + 374, + 12095, + 624, + 32313, + 11, + 773, + 279, + 1196, + 4588, + 264, + 3405, + 911, + 279, + 6722, + 315, + 9625, + 11, + 323, + 358, + 3897, + 279, + 4226, + 12095, + 13, + 1988, + 3330, + 1182, + 518 + ] + }, + "disconnect": { + "drained": true, + "subsequent_tokens_equal": true, + "cold_baseline_equal": true, + "before_tokens": [ + 576, + 6722, + 315, + 9625, + 374, + 12095, + 624, + 32313, + 11, + 773, + 279, + 1196, + 4588, + 264, + 3405, + 911, + 279, + 6722, + 315, + 9625, + 11, + 323, + 358, + 3897, + 279, + 4226, + 12095, + 13, + 1988, + 3330, + 1182, + 518 + ], + "after_tokens": [ + 576, + 6722, + 315, + 9625, + 374, + 12095, + 624, + 32313, + 11, + 773, + 279, + 1196, + 4588, + 264, + 3405, + 911, + 279, + 6722, + 315, + 9625, + 11, + 323, + 358, + 3897, + 279, + 4226, + 12095, + 13, + 1988, + 3330, + 1182, + 518 + ] + }, + "invalid_status": 400, + "remaining_requests": 0, + "metrics": "# HELP vllm:num_requests_running Requests executing on the stage.\n# TYPE vllm:num_requests_running gauge\nvllm:num_requests_running{model_name=\"Qwen3-4B\"} 0\n# HELP vllm:num_requests_waiting Requests queued for the stage owner.\n# TYPE vllm:num_requests_waiting gauge\nvllm:num_requests_waiting{model_name=\"Qwen3-4B\"} 0\n# HELP vllm:kv_cache_usage_perc Occupied fraction of the stage's KV lanes (1 = full).\n# TYPE vllm:kv_cache_usage_perc gauge\nvllm:kv_cache_usage_perc{model_name=\"Qwen3-4B\"} 0.0\n# HELP vllm:gpu_cache_usage_perc Occupied fraction of the stage's KV lanes (1 = full).\n# TYPE vllm:gpu_cache_usage_perc gauge\nvllm:gpu_cache_usage_perc{model_name=\"Qwen3-4B\"} 0.0\n# HELP vllm:request_success_total Finished requests by finish reason.\n# TYPE vllm:request_success_total counter\nvllm:request_success_total{model_name=\"Qwen3-4B\",finished_reason=\"length\"} 19\n# HELP airm_stage_request_failures_total Requests that ended with an engine error.\n# TYPE airm_stage_request_failures_total counter\nairm_stage_request_failures_total{model_name=\"Qwen3-4B\"} 0\n# HELP vllm:prompt_tokens_total Prompt tokens of finished requests.\n# TYPE vllm:prompt_tokens_total counter\nvllm:prompt_tokens_total{model_name=\"Qwen3-4B\"} 44288\n# HELP vllm:generation_tokens_total Generated tokens of finished requests.\n# TYPE vllm:generation_tokens_total counter\nvllm:generation_tokens_total{model_name=\"Qwen3-4B\"} 608\n# HELP vllm:time_to_first_token_seconds Time to first token (prefill stage: fenced prefill).\n# TYPE vllm:time_to_first_token_seconds histogram\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.001\"} 0\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.005\"} 0\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.01\"} 0\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.02\"} 0\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.04\"} 0\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.06\"} 0\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.08\"} 4\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.1\"} 6\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.25\"} 7\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.5\"} 7\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.75\"} 7\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"1.0\"} 7\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"2.5\"} 10\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"5.0\"} 15\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"7.5\"} 17\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"10.0\"} 19\nvllm:time_to_first_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"+Inf\"} 19\nvllm:time_to_first_token_seconds_sum{model_name=\"Qwen3-4B\"} 53.89968488528393\nvllm:time_to_first_token_seconds_count{model_name=\"Qwen3-4B\"} 19\n# HELP vllm:time_per_output_token_seconds Time per output token after the first (decode stage).\n# TYPE vllm:time_per_output_token_seconds histogram\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.01\"} 0\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.025\"} 0\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.05\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.075\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.1\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.15\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.2\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.3\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.4\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.5\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.75\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"1.0\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"2.5\"} 19\nvllm:time_per_output_token_seconds_bucket{model_name=\"Qwen3-4B\",le=\"+Inf\"} 19\nvllm:time_per_output_token_seconds_sum{model_name=\"Qwen3-4B\"} 0.7480128487354264\nvllm:time_per_output_token_seconds_count{model_name=\"Qwen3-4B\"} 19\n# HELP vllm:e2e_request_latency_seconds Stage request latency, from execution start to reply.\n# TYPE vllm:e2e_request_latency_seconds histogram\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.3\"} 0\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.5\"} 0\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.8\"} 0\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-4B\",le=\"1.0\"} 0\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-4B\",le=\"1.5\"} 7\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-4B\",le=\"2.0\"} 7\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-4B\",le=\"2.5\"} 8\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-4B\",le=\"5.0\"} 12\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-4B\",le=\"10.0\"} 18\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-4B\",le=\"15.0\"} 19\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-4B\",le=\"20.0\"} 19\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-4B\",le=\"30.0\"} 19\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-4B\",le=\"40.0\"} 19\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-4B\",le=\"50.0\"} 19\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-4B\",le=\"60.0\"} 19\nvllm:e2e_request_latency_seconds_bucket{model_name=\"Qwen3-4B\",le=\"+Inf\"} 19\nvllm:e2e_request_latency_seconds_sum{model_name=\"Qwen3-4B\"} 77.08808319608215\nvllm:e2e_request_latency_seconds_count{model_name=\"Qwen3-4B\"} 19\n# HELP airm_kv_transfer_seconds KV transfer time from the prefill source to the decode destination.\n# TYPE airm_kv_transfer_seconds histogram\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.0005\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.001\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.0025\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.005\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.01\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.025\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.05\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.1\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.25\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-4B\",le=\"0.5\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-4B\",le=\"1.0\"} 0\nairm_kv_transfer_seconds_bucket{model_name=\"Qwen3-4B\",le=\"+Inf\"} 0\nairm_kv_transfer_seconds_sum{model_name=\"Qwen3-4B\"} 0.0\nairm_kv_transfer_seconds_count{model_name=\"Qwen3-4B\"} 0\n# HELP vllm:prefix_cache_queries_total Prompt tokens looked up in the prefix cache.\n# TYPE vllm:prefix_cache_queries_total counter\nvllm:prefix_cache_queries_total{model_name=\"Qwen3-4B\"} 44544\n# HELP vllm:prefix_cache_hits_total Prompt tokens served from the prefix cache.\n# TYPE vllm:prefix_cache_hits_total counter\nvllm:prefix_cache_hits_total{model_name=\"Qwen3-4B\"} 33792\n# HELP vllm:cache_config_info Information of the LLMEngine CacheConfig.\n# TYPE vllm:cache_config_info gauge\nvllm:cache_config_info{model_name=\"Qwen3-4B\",block_size=\"128\",enable_prefix_caching=\"True\"} 1\n# HELP airm_kv_host_hit_tokens_total Prompt tokens reloaded from the host tier.\n# TYPE airm_kv_host_hit_tokens_total counter\nairm_kv_host_hit_tokens_total{model_name=\"Qwen3-4B\"} 8064\n# HELP airm_kv_offload_blocks_total Blocks written back from the device to the host tier.\n# TYPE airm_kv_offload_blocks_total counter\nairm_kv_offload_blocks_total{model_name=\"Qwen3-4B\"} 3\n# HELP airm_kv_reload_blocks_total Blocks reloaded from the host tier to the device.\n# TYPE airm_kv_reload_blocks_total counter\nairm_kv_reload_blocks_total{model_name=\"Qwen3-4B\"} 1\n", + "prefix_hit_tokens": 33792.0, + "host_hit_tokens": 8064.0 +} diff --git a/opt_prime/docs/validation/http214/experiments/2026-10-23-http-sigpipe214.md b/opt_prime/docs/validation/http214/experiments/2026-10-23-http-sigpipe214.md new file mode 100644 index 00000000..8062d521 --- /dev/null +++ b/opt_prime/docs/validation/http214/experiments/2026-10-23-http-sigpipe214.md @@ -0,0 +1,40 @@ +# HTTP closed-peer writes with the ATOM SDK + +- **Date**: 2026-10-23 +- **Task**: aicomp #214; discovered in #211, parent #201 +- **Host / device**: RNGD+ATOM host; one allocated ATOM; torch2.11.0+cpu, rebel-compiler0.11.0, optimum-rbln0.11.0.post1, Transformers5.8.1 +- **Commit**: baseline engine21f2c87; candidate branch fix/214-http-sigpipe at1b3937f plus llmd.py change (same source included with this record) + +## Question + +Can HTTP writes avoid invoking a native-installed SIGPIPE handler while retaining BrokenPipeError and cancellation cleanup? + +## Setup + +Importing rebel changes Linux SIGPIPE from ignored to caught (observed through /proc/self/status), even though Python signal.getsignal still reports SIG_IGN. A closed socketpair write after import emits a native backtrace. The HTTP matrix originally produces6 native backtrace headers per run. No process-global signal handler change is needed: use socket.sendall(data, MSG_NOSIGNAL) for the unbuffered HTTP writer. Platforms without the flag retain flags0; no protection is claimed there. + +Hardware harness from #211 uses production serve_batched.build, one B1 lane, context9216 and3 KV blocks. Synthetic token fixtures128/2048/8192, output32, ignore_eos, concurrency4/8 queued on one lane. Module loaders select the candidate batcher and llmd before importing batched; installed SDK remains unchanged. + +## Command + +```bash +PYTHONPATH=opt_prime python3 -m pytest -q opt_prime/tests/test_http_sigpipe.py opt_prime/tests/test_serve_batched.py opt_prime/tests/test_pd_http_cancel.py +# Same isolated allocation and launcher environment as the ATOM HTTP matrix: +bash run-atom-http211.sh --backend atom --model Qwen/Qwen3-0.6B --prefix-cache --output atom-http-qwen3-06b-cache.json +bash run-atom-http214.sh --backend atom --model Qwen/Qwen3-0.6B --prefix-cache --output atom-http-qwen3-06b-cache-fixed.json +bash run-atom-http214.sh --backend atom --model Qwen/Qwen3-4B --prefix-cache --host-kv-gib 2 --output atom-http-qwen3-4b-host-fixed.json +``` + +## Results + +- Controlled regression: using stdlib _SocketWriter as the helper gives1 pass/1 failure: the custom SIGPIPE handler receives a signal. New writer gives2 passes. Full relevant suite14 passed (three existing dependency warnings). +- Qwen3-0.6B device-cache: native backtrace headers6→0; serial3/3 and concurrent12/12 output sequences unchanged before/after. Both cancellation/disconnect reuse pass; remaining requests0. +- Qwen3-4B host-cache candidate: HTTP checks pass, concurrent12/12 match own serial baseline, prefix hits33792 and host hits8064, native backtrace headers0, remaining requests0. This is not a matched performance A/B. +- Normal writes preserve bytes count and file descriptor; closed-peer writes still raise BrokenPipeError. +- One analysis assertion initially compared complete baseline records including elapsed times and failed. Correct token-only comparison passes; this was an analysis mistake, not an engine mismatch. + +Raw logs remain in private workspace analysis/atom-http-211; portable JSON evidence accompanies the code. Header count uses libc.so.6(+0x12798e), the repeated signature in baseline logs; zero count is scoped to these runs. + +## Conclusion + +The observed SDK signal-handler interaction is avoided per HTTP write on Linux without suppressing Python exceptions or changing process-global handlers. This is a disconnect robustness fix, not a throughput or numerical-accuracy optimization. Broader four-model RNGD/heterogeneous coverage remains open. diff --git a/opt_prime/opt_prime/accel/serve/llmd.py b/opt_prime/opt_prime/accel/serve/llmd.py index fad81b23..52713c6d 100644 --- a/opt_prime/opt_prime/accel/serve/llmd.py +++ b/opt_prime/opt_prime/accel/serve/llmd.py @@ -8,6 +8,7 @@ from dataclasses import dataclass from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer import json +from io import BufferedIOBase import queue import select import socket @@ -485,11 +486,32 @@ def run(self): POLL = 0.5 # seconds between client-disconnect checks of a waiting handler (results wake it at once) _DONE = object() # end-of-stream marker in a handler's token queue +class _NoSignalSocketWriter(BufferedIOBase): + """Unbuffered HTTP writes that retain EPIPE without invoking native SIGPIPE handlers.""" + def __init__(self, sock): + self._sock = sock + + def writable(self): + return True + + def write(self, data): + self._sock.sendall(data, getattr(socket, "MSG_NOSIGNAL", 0)) + with memoryview(data) as view: + return view.nbytes + + def fileno(self): + return self._sock.fileno() + + def serve_stage(owner, stage, port): if stage not in ("prefill", "decode"): raise ValueError("invalid stage") class Handler(BaseHTTPRequestHandler): + def setup(self): + super().setup() + self.wfile = _NoSignalSocketWriter(self.connection) + def log_message(self, *args): pass diff --git a/opt_prime/tests/test_http_sigpipe.py b/opt_prime/tests/test_http_sigpipe.py new file mode 100644 index 00000000..4a8fdccd --- /dev/null +++ b/opt_prime/tests/test_http_sigpipe.py @@ -0,0 +1,37 @@ +"""Preserve BrokenPipeError without invoking a vendor-installed SIGPIPE handler.""" + +import signal +import socket + +import pytest +from opt_prime.accel.serve.llmd import _NoSignalSocketWriter + + +def test_writer_preserves_bytes_count_and_socket_descriptor(): + left, right = socket.socketpair() + try: + writer = _NoSignalSocketWriter(left) + data = memoryview(bytearray(b"abcdef")).cast("B", shape=[2, 3]) + assert writer.write(data) == 6 + assert right.recv(6) == b"abcdef" + assert writer.writable() and writer.fileno() == left.fileno() + finally: + left.close() + right.close() + + +@pytest.mark.skipif( + not hasattr(socket, "MSG_NOSIGNAL"), reason="Linux per-write signal suppression" +) +def test_closed_peer_keeps_exception_but_does_not_signal(): + received = [] + before = signal.signal(signal.SIGPIPE, lambda *_: received.append(True)) + left, right = socket.socketpair() + right.close() + try: + with pytest.raises(BrokenPipeError): + _NoSignalSocketWriter(left).write(b"x") + assert received == [] + finally: + left.close() + signal.signal(signal.SIGPIPE, before)