Repository navigation
serve: fix zero-token replies from engine-queue race between requests - #7
Merged
Merged
Conversation
Symptom: with two requests in flight (opencode fires a title-generation
request alongside the main tool-calling turn), the second request often
returned zero tokens. The server logged `done: 0 tokens in 0 s (length,
cancel=False)` and opencode showed an empty reply.
Cause: StrataEngine multiplexes every request over one resident
`strata --serve` subprocess and a single shared line queue (self.lines),
with no per-request framing. In Service.run(), a stop token (<|im_end|>)
breaks the token loop with the engine generator still suspended and its
internal `done` flag False. That generator's cleanup - write STOP, then
drain self.lines until DONE - therefore ran only when run()'s frame was
torn down later, AFTER the `with self.fifo` lock had been released. By
then the next request held the lock and had issued its own GEN; the prior
request's drain raced it on the shared queue and consumed the NEW
request's DONE. The new request saw an immediate end-of-stream: 0 tokens,
with `finish` left at its default of "length".
Fix: close the engine generator inside the `with self.fifo` block via
try/finally, so a request's STOP+drain completes while it still holds the
lock and cannot leave stray lines on the shared queue for the next
request to misread as its own DONE.
Also in this change:
- Clamp a non-positive max_new to 1024 in both /v1/chat/completions and
/v1/messages. Some clients send max_tokens: -1 ("unlimited"); that value
passed straight through and `GEN -1` made the engine yield 0 tokens.
- Distinguish a client disconnect: catch GeneratorExit and set
finish="disconnect" instead of leaving it as "length".
- Diagnostics gated on STRATA_DEBUG (off by default): a per-request
`_debug_req` line (api, msg/tool counts, raw max_tokens, resolved
max_new, thinking flag, prompt tokens) and a `raw:` dump of the decoded
model output at completion.
- Progress reporting: add tok/s and cancel state to the `done:` line, and
emit the progress line every 1 s instead of every 15 s for live
throughput.
Owner
|
Merged, thanks! Ran 10 rounds of overlapping requests on the real engine and got 0 empty replies, and max_tokens: -1 works now too. In v0.1.3. |
This was referenced Sep 27, 2026
MistVVK
referenced
this pull request
in MistVVK/XeStrata
Oct 3, 2026
…queue race between requests (#7)
MistVVK
referenced
this pull request
in MistVVK/XeStrata
Oct 10, 2026
…(7900 XT, R9700, Strix Halo) (#7) * Add experimental Linux gfx1100 setup and GLM HIP compatibility Keep CUDA WMMA guarded and use an RDNA-sized FP32 attention tile. Validate fused HC and prompt attention against CPU references; full-model inference and CUDA compilation remain unverified. * Port Strata multi-architecture HIP checks and RDNA4 intrinsics * Check HIP architecture list normalization and rejection cases * Document multi-architecture R9700 validation and compile-only gfx1151 * Let HIP choose the GLM prefill chunk * HIP prompt speed: tuned hipBLASLt GLM projections and a runtime sub-batch - The GLM prompt path's FP16 projections (hgemm_q) go through the existing strata::prefill::Gemm when STRATA_HIPBLASLT_TUNING names a table; without one nothing changes. Shapes without a row keep plain hipBLAS. - STRATA_GLM_PREFILL_SUB replaces the compile-time 256-token sub-batch of the mixers and the dense FFN (default still 256). - tune_hipblaslt: the absolute-error gate scales with sqrt(K / 4096), so the K=16384 projection is no longer rejected (relative L2 stays at 1e-4). - Tables for gfx1100, gfx1151 and gfx1201 (hipBLASLt 100202, ROCm 7.2). - maya.py: HIP configs on 20 GB+ cards use sub-batches of 1024 with a 4 GB prompt budget, and point at the card's table when one is shipped. - glm_model_test links strata_prefill (the prompt path now uses Gemm). Prompt speed, GLM-5.3-Flash IQ2_XXS: RX 7900 XT ~198 -> ~413 tok/s, Ryzen AI Max+ 395 ~116 -> ~217, R9700 ~490-560. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016v2d9KENHa9tcUo4yLWTYK * Keep the hipBLASLt prompt path HIP-only and link the GLM binaries in order PrefillState's Gemm member, its include and the lazy setup now build only with STRATA_USE_HIP, so CUDA's prompt path is as before. On HIP the GLM binaries (glm_model_test, glm_pack_test, glm_model_big_test) link strata_engine then strata_prefill, as strata does; GNU ld resolves the archives in that order. Docs: Maya versions as v1.0.x, the F32 prompt attention's 32-row FP16 tile (v1.0.7) replaces the HIP-only 16-row tile, #2 is merged, and the full-model results on the RX 7900 XT and R9700. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016v2d9KENHa9tcUo4yLWTYK * Update AMD audit docs for GLM hipBLASLt --------- Co-authored-by: boxwrench <203946641+boxwrench@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
MistVVK
referenced
this pull request
in MistVVK/XeStrata
Oct 10, 2026
…ux through ROCm 7, one GPU, text only (#7, @boxwrench); NVIDIA unchanged (same tokens as v1.0.10 on 2x V100)
DarthCY-K
pushed a commit
to DarthCY-K/Strata
that referenced
this pull request
Oct 20, 2026
… down instead of ending the start, and its message names the error (Niko1221#7) The layer split's two page-locked hand-off buffers (chunk x 40 KiB each, cudaHostAllocPortable) failed with the bare "prefill: the layer split's hand-off buffers", which the chunk step-down (kStepChunks in generate.cpp, which matches "do not fit") did not take, so the server exited. Under WSL2 the driver page-locks only about 1 GiB: a 15,616-token chunk's two buffers are 1.22 GiB (issue Niko1221#7). Now the message reads "... (2 x N MiB of page-locked RAM for a chunk of C tokens) do not fit: <cudaGetErrorString>", the failed pointer is cleared, and the start tries the next smaller chunk, down to 512 tokens. One GPU allocates no hand-off buffers: unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VZy1yKaDDiA8a7svdwaHio
DarthCY-K
pushed a commit
to DarthCY-K/Strata
that referenced
this pull request
Oct 20, 2026
…locked is refused at start when the drafter runs on another card, instead of an illegal memory access later (Niko1221#7) When cudaHostAlloc refuses the native token embedding (WSL2 page-locks only about 1 GiB; BF16 is 1,212 MiB), NativeEmbed keeps it in the VRAM of the card that loaded it (CUDA0). On a layer split the MTP drafter - and E-9's batch with STRATA_SPLIT_MTP_BATCH=1 - runs on the last stage's card and gathers from that pointer: "mtp prefill: an illegal memory access", sticky (issue Niko1221#7). NativeEmbed::vram_device() now names that card (-1 when mapped), and the start stops before the drafter loads, saying why and what helps (run natively, or on one GPU; a smaller --prefill does not). No device copy is made. One GPU, or a mapped table: unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VZy1yKaDDiA8a7svdwaHio
DarthCY-K
pushed a commit
to DarthCY-K/Strata
that referenced
this pull request
Oct 20, 2026
…robe that only warns, the Windows watchdog and prompt-loan fixes, BENCH.bat), Niko1221#1852's prompt encoder, issue Niko1221#7's WSL2 warning and layer-split fixes - README, docs/NVFP4.md and the bundle version README is origin/main's (2392100: "Not WSL2" in Requirements and Limits) with the version, a What's new block and the "!" section (upstream's guard comes first; the audit needs STRATA_NAN_GUARD=0). README.upstream.md = v0.1.43.1's README. docs/NVFP4.md "Upstream 0.1.43.1": the port, the merges by hand, the PLE probe, Niko1221#1852, Niko1221#7 and every check (release build 925c7326: 2K / 32K logits and tokens = 0.1.42-nvfp4.2's engine ddd78216; serve KL texts byte-identical; defaults off = upstream 0.1.43.1 on IQ2_XS; 116/119 ctest, serve 751, tools 821; decode 179.2 vs 178.1 tokens/s). VERSION 0.1.43.1-nvfp4.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VZy1yKaDDiA8a7svdwaHio
DarthCY-K
pushed a commit
to DarthCY-K/Strata
that referenced
this pull request
Oct 20, 2026
… base like CUDA0's - 0.25 for NVFP4, not 0.55 CUDA0 takes 0.25 for an NVFP4 pack (its 2.76 MB experts cross the link twice as long as the i-quants' the 0.55 was measured on), but every later stage of a layer split scaled its probed link from 0.55: issue Niko1221#7's CUDA1 on 6.8 GB/s got 0.19 instead of 0.085. One-GPU runs are unchanged (the stage loop does not run). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VZy1yKaDDiA8a7svdwaHio
DarthCY-K
pushed a commit
to DarthCY-K/Strata
that referenced
this pull request
Oct 20, 2026
…orks and upstream PRs, the BF16 NaN fix, the layer split's PCIe base, the per-GPU Monitor, the first Linux build What's new and "Upstream 0.1.43.1" name G1's six commits with their authors (jarydkrish, LeGeRyChEeSe Niko1221#1881, Mert Özkan Niko1221#1840, ANBAL534, sebastianrcnt, shoftee), the later stage's PCIe base 0.25 for NVFP4 (Niko1221#7), the Monitor's GPU and Disk cards with psutil in the Windows serve env, and the Linux build (Ubuntu 24.04, gcc 13/15, CUDA 13.3). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VZy1yKaDDiA8a7svdwaHio
RandoTeam
added a commit
to RandoTeam/4gbVramStrata
that referenced
this pull request
Nov 1, 2026
[Other] the release zip carries the engine's sources
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes blank/empty responses that appeared when a client (e.g. opencode) has two requests in flight at once — a title-generation request alongside the main tool-calling turn. The second request frequently returned zero tokens: the server logged
done: 0 tokens in 0 s (length, cancel=False)and the client showed an empty reply.Root cause
StrataEnginemultiplexes every request over one residentstrata --servesubprocess and a single shared line queue (self.lines), with no per-request framing. InService.run(), a stop token (<|im_end|>) breaks the token loop with the engine generator still suspended and its internaldoneflagFalse. That generator's cleanup — writeSTOP, then drainself.linesuntilDONE— therefore ran only whenrun()'s frame was torn down later, after thewith self.fifolock had been released. By then the next request held the lock and had issued its ownGEN; the prior request's drain raced it on the shared queue and consumed the new request'sDONE. The new request saw an immediate end-of-stream: 0 tokens,finishleft at its default"length".Fix
Close the engine generator inside the
with self.fifoblock viatry/finally, so a request'sSTOP+drain completes while it still holds the lock and cannot leave stray lines on the shared queue for the next request to misread as its ownDONE.Also included:
max_newto the default (1024) in both/v1/chat/completionsand/v1/messages. Some clients sendmax_tokens: -1("unlimited"); that value passed straight through andGEN -1made the engine yield 0 tokens.GeneratorExitand setfinish="disconnect"instead of leaving it as"length".STRATA_DEBUG(off by default): a per-request_debug_reqline and araw:dump of decoded model output at completion.done:line; emit the progress line every 1 s instead of 15 s.Test plan
python -c "import ast; ast.parse(open('serve/server.py').read())"passes.--engine mock): 2 concurrent streaming + sequential streaming + non-stream OpenAI + Anthropic requests all return content with correct finish reason; no0 tokens (length)regressions.STRATA_DEBUG=1 ./run-iq3_xxs.sh): full opencode agentic loop (title-gen, tool calls, file write/edit, test run, final answer) completes with every turn producing tokens; the previously-blanktools=11main turn now streams normally.