Windows support is doable because the core runtime is just launching llama-server with SYCL/Level-Zero env vars, and Intel oneAPI does run on Windows. The main work is removing the Linux-only assumptions.
Concrete plan mapped to the current codebase:
1. Config / data paths (src/arc_llama/config.py)
Today it hardcodes XDG paths (~/.config, ~/.local/share, ~/.local/state). On Windows these should map to %LOCALAPPDATA%. Use the platformdirs dependency:
from platformdirs import user_config_dir, user_data_dir, user_state_dir
This is cleaner than manual sys.platform checks and handles edge cases automatically.
2. GPU detection (src/arc_llama/detect.py)
_scan_pci() reads /sys/bus/pci/devices, which does not exist on Windows.
Add a Windows branch that enumerates GPUs via WMI/PowerShell:
Get-WmiObject Win32_VideoController | Select-Object Name,AdapterRAM,PNPDeviceID
Parse PNPDeviceID for VEN_8086&DEV_xxxx, extract the device ID, and look it up in arch.py. AdapterRAM gives a VRAM hint (may report shared memory on iGPUs). sycl-ls and clinfo also work on Windows so _enrich_with_clinfo() can stay as-is.
3. Subprocess lifecycle (src/arc_llama/launcher.py)
Biggest blocker. Currently uses:
preexec_fn=_preexec_isolate_and_pdeathsig
os.setsid()
libc.prctl(PR_SET_PDEATHSIG)
os.killpg(proc.pid, signal.SIGTERM/SIGKILL)
All POSIX-only. Refactor:
- POSIX: keep existing behavior
- Windows: use
subprocess.Popen(..., creationflags=subprocess.CREATE_NEW_PROCESS_GROUP) and stop with proc.terminate() then proc.kill()
- Drop
preexec_fn entirely on Windows
For robust child cleanup equivalent to PR_SET_PDEATHSIG, use a job object:
import ctypes
kernel32 = ctypes.windll.kernel32
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x00002000
def _configure_job_object():
hJob = kernel32.CreateJobObjectW(None, None)
info = JOBOBJECT_EXTENDED_LIMIT_INFORMATION()
info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
kernel32.SetInformationJobObject(hJob, 9, ctypes.byref(info), ctypes.sizeof(info))
kernel32.AssignProcessToJobObject(hJob, kernel32.GetCurrentProcess())
return hJob
4. CLI / doctor (src/arc_llama/cli.py)
os.uname().release → use platform.uname().release
/sys/module/xe, /sys/module/i915 → skip on Windows
lspci, id -nG, render/video groups → not applicable on Windows
/opt/intel/oneapi/setvars.sh → check Windows default (C:\Program Files (x86)\Intel\oneAPI\setvars.bat or setvars.ps1)
systemd command → no-op on Windows with a Task Scheduler hint
_resolve_llama_server() hardcoded Unix paths → skip on Windows, look for llama-server.exe
5. Packaging (pyproject.toml)
Change classifier from "Operating System :: POSIX :: Linux" to "Operating System :: OS Independent" (or add both Linux and Windows classifiers).
6. Tests
Several tests mock /sys/bus/pci/devices and assume POSIX. Add pytest.mark.skipif(sys.platform == "win32", ...) where needed or add equivalent Windows mocks.
7. Docs / UX
- Remove "Linux only" language from README
- Document that Windows users need a SYCL build of
llama-server.exe and should run from an Intel oneAPI command prompt (or after running setvars.bat)
- Replace systemd instructions with a note about Task Scheduler or manual
arc-llama serve
Suggested order of attack
config.py — platform-aware paths via platformdirs (low risk)
launcher.py — guard POSIX-only code, add Windows process group handling
detect.py — add WMI/PowerShell GPU scan
cli.py — make doctor and systemd platform-aware
pyproject.toml + README.md
- Run test suite on Windows, fix stragglers
Windows support is doable because the core runtime is just launching
llama-serverwith SYCL/Level-Zero env vars, and Intel oneAPI does run on Windows. The main work is removing the Linux-only assumptions.Concrete plan mapped to the current codebase:
1. Config / data paths (
src/arc_llama/config.py)Today it hardcodes XDG paths (
~/.config,~/.local/share,~/.local/state). On Windows these should map to%LOCALAPPDATA%. Use theplatformdirsdependency:This is cleaner than manual
sys.platformchecks and handles edge cases automatically.2. GPU detection (
src/arc_llama/detect.py)_scan_pci()reads/sys/bus/pci/devices, which does not exist on Windows.Add a Windows branch that enumerates GPUs via WMI/PowerShell:
Parse
PNPDeviceIDforVEN_8086&DEV_xxxx, extract the device ID, and look it up inarch.py.AdapterRAMgives a VRAM hint (may report shared memory on iGPUs).sycl-lsandclinfoalso work on Windows so_enrich_with_clinfo()can stay as-is.3. Subprocess lifecycle (
src/arc_llama/launcher.py)Biggest blocker. Currently uses:
preexec_fn=_preexec_isolate_and_pdeathsigos.setsid()libc.prctl(PR_SET_PDEATHSIG)os.killpg(proc.pid, signal.SIGTERM/SIGKILL)All POSIX-only. Refactor:
subprocess.Popen(..., creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)and stop withproc.terminate()thenproc.kill()preexec_fnentirely on WindowsFor robust child cleanup equivalent to
PR_SET_PDEATHSIG, use a job object:4. CLI / doctor (
src/arc_llama/cli.py)os.uname().release→ useplatform.uname().release/sys/module/xe,/sys/module/i915→ skip on Windowslspci,id -nG, render/video groups → not applicable on Windows/opt/intel/oneapi/setvars.sh→ check Windows default (C:\Program Files (x86)\Intel\oneAPI\setvars.batorsetvars.ps1)systemdcommand → no-op on Windows with a Task Scheduler hint_resolve_llama_server()hardcoded Unix paths → skip on Windows, look forllama-server.exe5. Packaging (
pyproject.toml)Change classifier from
"Operating System :: POSIX :: Linux"to"Operating System :: OS Independent"(or add both Linux and Windows classifiers).6. Tests
Several tests mock
/sys/bus/pci/devicesand assume POSIX. Addpytest.mark.skipif(sys.platform == "win32", ...)where needed or add equivalent Windows mocks.7. Docs / UX
llama-server.exeand should run from an Intel oneAPI command prompt (or after runningsetvars.bat)arc-llama serveSuggested order of attack
config.py— platform-aware paths viaplatformdirs(low risk)launcher.py— guard POSIX-only code, add Windows process group handlingdetect.py— add WMI/PowerShell GPU scancli.py— makedoctorandsystemdplatform-awarepyproject.toml+README.md