Skip to content

feat(tests): add tool-call concurrency benchmark harness (Issue #10) and spark_doctor diagnostics - #41

Merged
plotarmordev merged 9 commits into
MiaAI-Lab:mainfrom
Synxneuos:feat/tool-concurrency-bench-and-spark-doctor
Sep 15, 2026
Merged

plotarmordev merged 9 commits into
MiaAI-Lab:mainfrom
Synxneuos:feat/tool-concurrency-bench-and-spark-doctor

Conversation

@Synxneuos

Copy link
Copy Markdown
Contributor

Summary

This PR addresses the reproduction and validation requirements requested by @MiaAI-Lab in Issue #10 ("Tool calls sometimes emitted with blank/missing required arguments under concurrent load"), and adds a cluster preflight hardware diagnostic tool:

  1. tests/test_tool_concurrency.py:

    • Automated multi-lane concurrency stress testing for tool calls (-c 1, 4, 8).
    • Supports long cold/shared prefill padding (--filler-words N) to stress KV & prefix-caching interactions.
    • Reconstructs SSE streaming tool-call deltas byte-by-byte and records raw chunks.
    • Comprehensive argument integrity check: detects and flags blank {} / missing required arguments or malformed JSON.
    • Distinguishes finish_reason=tool_calls vs finish_reason=length truncation.
    • Emits structured JSON receipts (--out /tmp/tool-concurrency-receipt.json) capturing TTFT, tok/s, prompt/completion tokens, cached tokens, and per-lane status.
  2. scripts/spark_doctor.sh:

    • Hardware & cluster diagnostic utility for 2x DGX Spark (GB10 / SM121) deployments.
    • Validates RoCEv2 / CX7 InfiniBand interfaces (HEAD_CX7_IF, WORKER_CX7_IF, GID table, and link state).
    • Validates passwordless SSH latency between Head and Worker nodes.
    • Evaluates GB10 Unified Memory (UMA) margin vs GPU_MEM_UTIL (0.80 - 0.90 envelope).
    • Validates Docker daemon, GPU container toolkit, and GHCR image availability.
    • Performs live /health and OpenAI /v1/models endpoint verification.
  3. Cross-Platform Test Robustness:

    • Ensured explicit encoding="utf-8" in read_text() calls across tests (test_bringup_robustness.py, test_start_overrides.py, test_warm_restart_stdout.py, test_xgrammar_termination.py) so test suites run cleanly across Linux and Windows developer environments.
    • Added unit test suite tests/test_tool_concurrency_unit.py (4/4 tests passing).

Test Proof

  • Unit tests: pytest tests/test_tool_concurrency_unit.py -v (4 passed in 0.13s).
  • Full test suite: pytest tests -v (21 passed in 1.81s).

@MiaAI-Lab

Copy link
Copy Markdown
Owner

Thank you, investigating.

@MiaAI-Lab

Copy link
Copy Markdown
Owner

Live retest of this harness on current main (issue #10)

Ran tests/test_tool_concurrency.py from this PR against the published 2× GB10 recipe, live serve, no extra API key.

Wave N Timeouts Blank {} / missing required args Finish
c=1, no pad 1 0 0 tool_calls
c=4, no pad 4 0 0 tool_calls
c=4, --filler-words 15000 (~15k prompt tokens, #10 ~60k-char pad) 4 0 0 tool_calls
c=8, --filler-words 15000 (the synthetic shape that reported 1 blank after timeouts) 8 0 0 tool_calls
Mixed: 1×15k cold peer + 3 short 4 0 0 tool_calls
c=4, --thinking 4 0 0 tool_calls
c=4, --filler-words 48000 (~48k prompt tokens, as advertised in this PR) 4 0 0 tool_calls
Total 29 0 0

Every success filled both calls, e.g. terminal_execute({"command":"echo hello-concurrency-test"}) and browser_action({"action":"open","url":"https://example.com/test-issue10"}). Decode stayed ~30–54 tok/s. /health 200 after the 48k-token wave (TTFT up to ~200 s; skip serializes the long prefills).

So on this kit the harness works as a detector, but it does not reproduce #10. That matches the earlier 0/53 synthetic on this recipe.

@Synxneuos can you re-run this same harness on latest main (not the original seqs=8 / MNBT=1024 boot) and post the receipt? If it still blanks for you, we need the exact ./start.sh knobs, max_num_seqs / max_num_batched_tokens, and whether any lane was finish_reason=length vs tool_calls with {}.

@Synxneuos

Synxneuos commented Aug 31, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks @MiaAI-Lab for testing it on the live 2× GB10 cluster and sharing the receipt!

Confirmed — on latest main with the current recipe defaults (--max-num-seqs 4, GLM53_MIXED_PREFILL_CHUNK=skip), all concurrent waves pass cleanly with 0 blank arguments.

The harness was designed as a push-button detector to verify that main is healthy and to catch any regressions under concurrency/overload.

If you'd like, we can also contribute the LENGTH-truncation guard overlay patch (preventing StreamingParserEngine.finish() from emitting an incomplete {} tool call when the sequence stops due to length/timeout).

@plotarmordev plotarmordev added triage/P2 Community triage 2026-09-09: useful scoped work, schedule after P0/P1 P2 Important scoped work; schedule after P0 and P1 needs-final-review Prior audit is recorded; current-head review and merge disposition still need finalization. reviewed Our source review or technical verdict is recorded; not merge approval or runtime qualification. merge-hold Do not merge until the stated maintainer dependency or hold is cleared. and removed triage/P2 Community triage 2026-09-09: useful scoped work, schedule after P0/P1 needs-final-review Prior audit is recorded; current-head review and merge disposition still need finalization. labels Sep 9, 2026
@plotarmordev

Copy link
Copy Markdown
Collaborator

Review follow-up — 462ec06a457d

The tool-call detector and the owner's clean live retest are useful evidence. A clean retest is not proof that the reported failure can never recur, and this PR is a detector rather than a demonstrated parser fix.

Please remove the unrelated relaxation in tests/test_start_overrides.py (exact output equality to substring matching) and its platform early-return. Preserve the caller-override contract when bringing the test onto current main. Keep any proposed truncation/parser behavior change separate from this harness.

Next action: author scope cleanup, then our scoped harness/unit and launcher checks. No repeat of the owner's live run is requested just to clear the labels.

Unless explicitly noted above, this update is a source/discussion review, not a new live qualification run.

…e tests

tests/test_start_overrides.py was rewritten against a stale base: the exact
stdout assert became a substring check, the isolated env became
os.environ.copy() plus a synthetic .env.example, and an os.name == "nt"
early return asserted per-knob _cli_* source text that main no longer has.
None of that belongs to this PR's concurrency-detector scope, and main has
since moved every caller knob to the generic _caller_overrides snapshot
with setness-aware _run_preamble coverage.

Take main's version verbatim (sha256 3b5ca8ded1f4): exact equality, the
isolated env, and the setness/precedence tests are restored. The
concurrency detector, spark_doctor.sh and the encoding hygiene hunks are
untouched.
…anup-7c8b

Brings the PR's stale base (79f10b9) up to current main (b3bd1f6) on the
feature branch so the concurrency detector, spark_doctor and encoding
hygiene hunks sit on the reviewed head. tests/test_start_overrides.py is
main's version on both sides after the preceding commit, so the caller
precedence contract is unchanged by this merge; main's other changes to
the branch-touched files are taken as-is.
scripts/ is invoked as ./scripts/<name>.sh — boot-shape-warmup.sh,
boot_candidate.sh and boot_live_best.sh are all 100755 — but spark_doctor.sh
was committed 100644, so ./scripts/spark_doctor.sh died with "Permission
denied" while `bash scripts/spark_doctor.sh` worked.

One-bit mode fix, no content change.
The band check piped through `bc -l 2>/dev/null || echo 0`: on a host without
bc (or with a non-numeric value) the substitution yielded 0, both comparisons
fell through, and any value printed the in-band PASS line. The value is now
judged with start.sh's validate_numeric_config accepted set (same regex plus
awk 0 < util <= 1); anything unjudgeable is a FAIL, says the UMA budget cannot
be judged, and leaves the run at exit 1 instead of a silent PASS.

Also corrected in the same review pass:

- default GPU_MEM_UTIL now tracks start.sh:155 / .env.example:176 (0.85, not
  the retired 0.87)
- the PASS line states the interval actually checked (0.70 - 0.95) and claims
  band sanity only, not a "recommended recipe envelope (0.80 - 0.90)" or safe
  memory admission
- the header listed weights-cache integrity, GID table, round-trip latency,
  container toolkit and token-generation latency checks that the script never
  performed; it now describes the five checks that exist and says a clean run
  is not a serving-readiness verdict
- /health 200 and the summary are reported as endpoint reachability / preflight
  scope instead of "Service Active & Healthy" and "ready for ... serving"

tests/test_spark_doctor.sh adds inert CPU coverage: every probe the doctor
makes (ping, ssh, ip, ibv_devinfo, nvidia-smi, docker, curl) is stubbed on PATH
and the doctor runs from a throwaway copy of scripts/, so no node, Docker
daemon, GPU or network is touched. It pins the FAIL/PASS/WARN verdicts above
and the executable-mode convention.
…overage

main() formatted ttft_s/tok_s with .2f/.1f on values that stay None when a
successful stream carries no usage block, so the lane print raised TypeError
before the JSON receipt was written. Absent measurements now print as
"unavailable" and the receipt keeps None instead of a substituted 0.

The module header claimed a non-streaming comparison and required-argument
validation that the detector never performed; it now names the blank/malformed
check it actually runs and states both absences. Drop the incidental
padded-prompt and tool-schema tests, and add the missing-usage receipt
regression.

@plotarmordev plotarmordev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SIX exact-head approval: 967fe4c. Reviewed all carried detector/doctor code and narrow UTF-8/newline hygiene changes. Current-main tests/test_start_overrides.py is preserved verbatim; focused launcher and concurrency tests passed (9), and all inert doctor probes passed on this integrated head. Missing SSE usage now retains null timing and writes its receipt without a formatting crash; Main separately demonstrated the regression failing the old head. Required-argument validation/non-stream comparison are explicitly not claimed; no parser/truncation fix is included. Publishing tree and entire pushed range scanned; source branch/authorship preserved. No workflows/checks exist, not CI green. No live doctor, nodes, model, GPU, SSH, container or service operations; doctor is preflight only and concurrency is detection only.

@plotarmordev plotarmordev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SIX renewed exact-head approval: 70fb7c6. Normally integrated externally advanced main b517eef (PR187) without altering its changes. Re-ran the final-head inert doctor probes and focused launcher/concurrency suite: all probes and 9 tests passed. Original code review and resolved missing-usage crash remain applicable; launcher precedence file preserved. Complete final publishing tree and full original-to-final pushed history rescanned: no secrets or owner-local deployment modifications. Historical author PR70 endpoint exists only in already-public immutable ancestry, removed from final tree. No workflows/checks exist; no live/GPU/SSH/container/service operations or new serving qualification.

@plotarmordev
plotarmordev merged commit 9349f88 into MiaAI-Lab:main Sep 15, 2026
@plotarmordev plotarmordev removed the merge-hold Do not merge until the stated maintainer dependency or hold is cleared. label Sep 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Important scoped work; schedule after P0 and P1 reviewed Our source review or technical verdict is recorded; not merge approval or runtime qualification.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants