Front SGLang Qwen3.8-27B as a Hopper/Blackwell local runtime for CoS agents - #4820
Merged
Conversation
atomantic
force-pushed
the
claim/issue-4776
branch
from
August 22, 2026 04:55
2b2d38a to
b56a716
Compare
…r CoS agents (#4776) PortOS could not serve Qwen3.8-27B on a Hopper or Blackwell card at all. The only CUDA path was the frozen vLLM + DFlash 2 container on an RTX 3090, and the SGLang cookbook publishes no 3090 cell — so the two are complementary, not alternatives. Ampere 24 GB stays on vLLM; this adds a third CUDA path next to it. The launch line is PortOS's own, because SGLang publishes an official image (lmsysorg/sglang:qwen38-27b) but no compose project to inherit flags from. server/lib/sglangQwenRecipe.js is that launch line: a pure table of the cookbook's verified H200 cell plus two Blackwell cells, with the compose file generated from it so there is one source of truth (a test fails if the feature doc drifts). Three flags fail silently when wrong, so all three are baked in rather than left to an operator's EXTRA_ARGS: - --tool-call-parser qwen3_coder. SGLang's spelling; vLLM's is qwen3_xml. Get it wrong and the server starts, answers, and returns raw markup with no tool_calls block — the agent narrates edits it never makes. - --reasoning-parser qwen3, the same failure for the thinking block. - --mamba-full-memory-ratio, derived (1.201) rather than left at the cookbook default 0.9, which under-sizes the GDN state pool at CoS prompt lengths and silently clamps max_running_requests. Hardware is gated on the compute-capability probe before docker is ever reached: SM90 gets the verified H200 cell, Blackwell splits by VRAM, Ampere is refused with a pointer at the vLLM path, and a wedged nvidia-smi says "could not read" rather than "no GPU". Only the H200 cell is transcribed flag-for-flag from the cookbook; the Blackwell cells carry what the research note records and omit H200's 32k prefill overrides rather than pinning an unmeasured number. PortOS never starts the container, never pulls the image, and never downloads weights — the Start button only brings up an already-prepared project. An enabled provider whose endpoint answers is treated as a GPU blocker, so a local media job refuses up front instead of OOMing inside a model load; the vLLM and SGLang probes are now one table rather than two copies. Generation controls are wired from day one (THINKING_STYLE.sglang, the client's generationControlsFor, and the lockstep test that walks LOCAL_RUNTIMES), so this does not repeat the hole vLLM shipped with in #4765. Nothing seeds a thinking default — CoS coding wants it off, but the operator chooses. Migration 290 seeds disabled OpenCode CLI and TUI wrappers on :18021, loopback. Their API key is optional: SGLang authenticates only behind --api-key. Closes #4776
…--profile, and fail loudly on a portless recipe The reviewer read `docker compose stop` next to vLLM's `docker compose --profile single stop` as an inconsistency. It is deliberate — upstream's vLLM project puts its services behind a `single` profile, while the compose file this branch generates declares one unprofiled service, so copying the flag would print a command that stops nothing. Say that where the difference lives. sglangComposeYaml derived its published port by indexing past `--port` without checking the flag was there, which would have rendered `127.0.0.1:undefined:undefined` into a compose file that fails much later with nothing pointing back at the recipe.
…lining them (#4778) #4778 landed while this branch was open, and #4776 names it as the follow-up to consume: parserFlagsFor('sglang') now supplies --tool-call-parser and --reasoning-parser, so neither spelling is typed in the recipe module or its test. That table ships a guard test failing any tracked JS file that retypes one, which is exactly the drift it exists to stop — SGLang and vLLM need different parsers for the same model, and the failure is silent. Flag order follows the shared table; the compose block in the feature doc is regenerated to match, and the doc now points at the table for the spellings.
atomantic
force-pushed
the
claim/issue-4776
branch
from
August 22, 2026 04:58
b56a716 to
4f8df9c
Compare
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
PortOS could not serve Qwen3.8-27B on a Hopper or Blackwell card at all — the only CUDA path was the frozen vLLM + DFlash 2 container on an RTX 3090. The SGLang cookbook publishes no 3090 cell, so the two are complementary rather than alternatives: Ampere 24 GB stays on vLLM, and this adds a third CUDA path beside it on
127.0.0.1:18021.The launch line is PortOS's own, because SGLang publishes an official image (
lmsysorg/sglang:qwen38-27b) but no compose project to inherit flags from.server/lib/sglangQwenRecipe.jsis that launch line — a pure table of the cookbook's verified H200 cell plus two Blackwell cells — and thedocker-compose.ymlis generated from it, so there is one source of truth. A test fails if the feature doc's compose block drifts from the function.Three flags that fail silently when wrong
--tool-call-parser— SGLang spells it differently from vLLM for the very same model. Get it wrong and the server starts, answers, and returns raw markup with notool_callsblock: the agent narrates edits it never makes. This is the exact failure the 3090 bring-up hit.--reasoning-parser— the same failure mode for the thinking block.Both are read from
parserFlagsFor('sglang')inserver/lib/qwenAgentParsers.js(Encode Qwen-family tool/reasoning parsers in a shared launch-flag table #4778, which landed while this branch was open and which Front SGLang Qwen3.8-27B as a Hopper/Blackwell local runtime for CoS agents #4776 names as the table to consume). Neither spelling is typed in this branch's code — that table ships a guard test failing any tracked JS file that retypes one.--mamba-full-memory-ratio— derived (1.201), not left at the cookbook default0.9, which under-sizes the GDN state pool at CoS prompt lengths and silently clampsmax_running_requests.Hardware gate, before docker
The compute-capability probe decides before anything could pull an image: SM90 → the verified H200 cell, Blackwell splits by VRAM, Ampere is refused with a pointer at the vLLM path, and a wedged
nvidia-smireports "could not read" rather than "no GPU".darwinis refused outright.Only the H200 cell is transcribed flag-for-flag from the cookbook (via the in-repo research note). The two Blackwell cells carry what that note actually records — NVFP4,
--max-running-requests 1on a 32 GB 5090 — and deliberately omit H200's 32k prefill overrides rather than pinning a number nobody has measured. They are markedverified: falseon the record, and the feature doc asks an operator who brings one up to tighten the cell.Not repeating known bugs
THINKING_STYLE.sglang, the client'sgenerationControlsFor, and the lockstep test that walksLOCAL_RUNTIMESall land here. Nothing seeds athinkingdefault — CoS coding wants it off, but the operator chooses (Run CoS TUI agents on the RTX 3090 via the syv-ai Qwen3.8-27B vLLM stack #4716).detectGpuBlockersnow coverssglangBackedtoo. The two probes became one table rather than two copies, so a fix to one can't miss the other.PortOS never starts the container, never pulls the image, and never downloads weights. Migration 290 seeds disabled OpenCode CLI + TUI wrappers whose API key is optional — SGLang authenticates only behind
--api-key, unlike the vLLM stack.Rebased twice onto
mainwhile open, picking up #4778 (the shared parser table, now consumed) and #4767 (which moved the vLLM setup row behindvllmQwenManagerand replacedACTION_LABELSwithPROVISION_STEPS/ACTION_CAPABILITIES— the SGLang row keeps its own inline start, since it ships no provisioner).Test plan
cd server && npm test— 32,924 pass. Two files fail identically on an untouchedmaincheckout and are unrelated:imageTo3d/trellis2NormalBake.test.js(12) andaskService.test.js(1).client/src/a11yConventions.test.jslikewise fails identically (10) onmain.0.9default; every cell is asserted to reproduce the shared table's parser fragment and never vLLM's spelling; the 5090 cell is asserted not to inherit H200's prefill chunks; the hardware gate is covered for Hopper / Blackwell / Ampere / unknown-probe / darwin; the project inspector's tri-statehasWeightsis covered in all three states with distinct refusal copy; the setup row is asserted to refuse an Ampere card before it reachesinspectSglangQwenProject; and the compose block in the feature doc is asserted equal to the generator's output.Reviewed
lmstudio(qwen/qwen3-4b-2507) over the branch diff. Its context window is 8k, so the 43k-token diff was reviewed in 31 file-scoped chunks; all 31 returned findings. Two were acted on — documenting why SGLang's stop command omits vLLM's--profile single, and makingsglangComposeYamlthrow on a recipe with no--portinstead of rendering127.0.0.1:undefined:undefined. The remaining Blocking items were chunking artifacts (e.g. a claim thatdetectGpuBlockersleavesrowundefined, where the chunk boundary had hidden the destructuring loop headers). Rounds 2 and 3 came back clean.Closes #4776