"""The sglang-omni venv: interpreter selection and provisioning. sglang-omni requires Python >=3.10,<3.13 while the app itself is version-agnostic — a host whose only ``python`` is 3.13+ must still be able to install and run this backend. ``prepare_env`` resolves a usable interpreter for the dedicated venv (``app/envs/sglomni``), in order: 1. the venv already exists with an acceptable interpreter (no-op); 2. the app env's interpreter or a ``python3.10/3.11/3.12`` on PATH fits (the venv is created from it — no download); 3. uv (pip-installed into the app env) provisions a managed standalone CPython 3.12 into ``app/envs/pythons`` and creates the venv from it — the zero-prerequisites path for hosts like stock Arch, where only the latest Python exists and no root/package-manager knowledge is needed. An existing venv built with an incompatible interpreter (an older tool version, a since-upgraded system Python) is transparently recreated: a venv holds nothing user-owned, and every package in it is reinstalled by the setup that follows anyway. """ import shutil import sys from pathlib import Path from typing import Optional, Tuple from backends import common, envs from backends.sglomni.constants import PYTHON_SPEC, PYTHON_VERSIONS # The dedicated venv (its interpreter may differ from the launching one). SGLOMNI_ENV = envs.SGLOMNI_ENV_DIR def env_version() -> Optional[Tuple[int, int]]: """The (major, minor) Python version of the venv's interpreter.""" if not envs.env_exists(SGLOMNI_ENV): return None return envs.python_version(envs.env_python(SGLOMNI_ENV)) def env_compatible() -> bool: """True when the venv exists with a Python the sglang-omni stack accepts.""" version = env_version() return version is not None and tuple(version) in {tuple(v) for v in PYTHON_VERSIONS} def prepare_env(*, emit=None, cancel=None) -> int: """Make SGLOMNI_ENV exist with a Python in PYTHON_VERSIONS. Returns 0/1. The three resolution paths are described in the module docstring; every path ends with a pip-equipped venv so the regular ``python -m pip`` helpers (requirements installs, model-companion extras) keep working. Prints what it does so the task view shows why a download happens (or, usually, does not). """ if env_compatible(): return 0 if envs.env_exists(SGLOMNI_ENV): version = env_version() found = f"{version[0]}.{version[1]}" if version else "unknown" print(f"[WARNING] {SGLOMNI_ENV} was built with Python {found}, " "which the sglang-omni stack does not support (needs " "3.10-3.12); recreating it with a compatible interpreter.") # Nothing user-owned lives in the tool-managed venv, and every # package is reinstalled by the steps that follow this one. shutil.rmtree(SGLOMNI_ENV, ignore_errors=True) if SGLOMNI_ENV.exists(): print("[ERROR] Could not remove the incompatible venv; " f"delete {SGLOMNI_ENV} manually and re-run setup.") return 1 interpreter = envs.compatible_interpreter(PYTHON_VERSIONS) if interpreter is not None: if interpreter == envs.env_python(envs.ENV_DIR): print(f"[INFO] using the app environment's interpreter " f"({sys.version_info.major}.{sys.version_info.minor}) " "for the sglang-omni venv.") else: print(f"[INFO] using {interpreter} for the sglang-omni venv.") return envs.create_env(SGLOMNI_ENV, interpreter) # No compatible interpreter anywhere: provision one with uv. print("[INFO] no Python 3.10-3.12 found on this system — provisioning " f"a managed CPython {PYTHON_SPEC} with uv (no root required).") rc = envs.ensure_uv(emit=emit, cancel=cancel) if rc != 0: print("[WARNING] pip install uv failed (exit " f"{rc}); install uv manually and re-run setup") return rc rc = envs.provision_env_with_uv(SGLOMNI_ENV, PYTHON_SPEC, emit=emit, cancel=cancel) if rc != 0: print(f"[WARNING] uv venv failed (exit {rc}); create the venv " f"manually, e.g.: uv venv --seed --python {PYTHON_SPEC} " f"{SGLOMNI_ENV}") return rc