Skip to content

Windows: SGLang still hands the operator a UNC path to fill in by hand #5832

Description

@atomantic

Problem

On Windows, the SGLang Qwen3.8-27B stack has exactly the failure the vLLM stack just stopped having.

server/lib/sglangQwenProject.js resolves its default project directory to a Windows home (%USERPROFILE%\sglang-qwen38), and the sglang row in server/services/localRuntimeSetup.js declares platforms: ['linux', 'win32']. But the compose project and its weights live inside a WSL2 distro — Docker Desktop's engine is a WSL2 VM — so a native-Win32 PortOS reads neither the checkout nor its models/, sglangStartBlockedReason refuses, and the operator is handed SGLANG_QWEN_PROJECT_DIR to fill in with a \\wsl.localhost\<distro>\home\<user>\… UNC path they have to look up themselves.

This is more acute for SGLang than it was for vLLM: SGLang ships no provisioner, so preparing it by hand from docs/features/sglang-qwen38.md — inside the distro — is the only path. Every Windows operator therefore hits the refusal.

What already exists

The machinery landed with the vLLM fix and is deliberately generic:

  • server/lib/wslDistro.jsdetectWslProjectDir(leaf) takes the directory name as a parameter and returns {dir, distro, home} or {dir: null, reason}. Nothing in it is vLLM-specific.
  • server/services/vllmQwenManager.jsensureVllmProjectDir() (settle → detect → record → emit) and wslPlacementRefusal(found) (a reason → lead sentence table plus one shared tail).
  • server/lib/vllmQwenProject.jsreadRecordedVllmProjectDir / recordVllmProjectDir / vllmProjectDirIsSettled, all keyed on one env-var name and one .env path.

Only three values are stack-specific in that loop: the env-var name, the directory leaf, and the size hint in the prose.

Decision (already made — do not re-litigate)

Lift the loop, keep the prose local.

  1. Add ensureWslProjectDir({ envVar, leaf, sizeHint, isSettled, record, emit }) to a shared home — server/services/wslProjectPlacement.js is the natural spot, since it both spawns and writes and so cannot live in a lib/ that promises neither.
  2. Generalise the .env record helpers in vllmQwenProject.js to take the key (readRecordedProjectDir(envVar, envPath?) etc.) and move them beside upsertEnvLine, or add an SGLang-keyed pair that calls the same primitives. Do not leave two copies.
  3. Rewrite vllmQwenManager.ensureVllmProjectDir as a call into it, keeping wslPlacementRefusal's vLLM wording where it is (refusal prose stays per-stack — SGLang's points at its feature doc, not at a clone).
  4. Call it from the sglang row's start in server/services/localRuntimeSetup.js, before inspectSglangQwenProject().
  5. Update docs/features/sglang-qwen38.md's Windows section the way docs/features/qwen38-rtx3090.md §1c was updated: detection is automatic, SGLANG_QWEN_PROJECT_DIR overrules it.

SGLang gets detect + record only, not placement — there is no provisioner to clone into the directory, so the refusal cases still point at the doc.

Acceptance

  • On Windows with a normal WSL2 distro, the SGLang Start button finds a hand-prepared project with no environment variable set, and records the UNC path so the readiness poll agrees.
  • wslPlacementRefusal-style refusals still name that host's fix for no-wsl / no-distro / internal-distro / unreadable-share.
  • Exactly one implementation of the settle→detect→record loop in the tree.
  • An exported SGLANG_QWEN_PROJECT_DIR still wins over the recorded value.

Context

Follow-up from the vLLM Windows placement change; raised by the altitude pass on that PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    effort:mediumEffort: mediummodel:mediumModel size: mediumplanTracked by /do:replanplanner:opus-5Plan authored by the opus-5 model

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions