diff options
| author | kirillrdy <kirillrdy@gmail.com> | 2026-01-18 20:14:42 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-01-18 20:14:42 +0000 |
| commit | 846c5bb77e65681d3097b2ba38e74597327a3a26 (patch) | |
| tree | 4fabe8ac42c6f43db82017f4ffb474f47b627c61 | |
| parent | b38848f4e234f6270ad0e386483cb5f81240656f (diff) | |
| parent | 02527735ac771bd40ba51f4993dd16514800e276 (diff) | |
exo: 0.0.14-alpha -> 1.0.62 (#477442)
| -rw-r--r-- | pkgs/by-name/ex/exo/inject-dashboard-path.patch | 17 | ||||
| -rw-r--r-- | pkgs/by-name/ex/exo/make-encoding-init-lazy.patch | 29 | ||||
| -rw-r--r-- | pkgs/by-name/ex/exo/package.nix | 213 |
3 files changed, 213 insertions, 46 deletions
diff --git a/pkgs/by-name/ex/exo/inject-dashboard-path.patch b/pkgs/by-name/ex/exo/inject-dashboard-path.patch new file mode 100644 index 000000000000..29f964446dbb --- /dev/null +++ b/pkgs/by-name/ex/exo/inject-dashboard-path.patch @@ -0,0 +1,17 @@ +diff --git a/src/exo/utils/dashboard_path.py b/src/exo/utils/dashboard_path.py +index b5ce9c04..ec60ef4a 100644 +--- a/src/exo/utils/dashboard_path.py ++++ b/src/exo/utils/dashboard_path.py +@@ -5,11 +5,7 @@ from typing import cast + + + def find_dashboard() -> Path: +- dashboard = ( +- _find_dashboard_in_env() +- or _find_dashboard_in_repo() +- or _find_dashboard_in_bundle() +- ) ++ dashboard = "@dashboard@" + if not dashboard: + raise FileNotFoundError( + "Unable to locate dashboard assets - make sure the dashboard has been built, or export DASHBOARD_DIR if you've built the dashboard elsewhere." diff --git a/pkgs/by-name/ex/exo/make-encoding-init-lazy.patch b/pkgs/by-name/ex/exo/make-encoding-init-lazy.patch new file mode 100644 index 000000000000..7a51317853ca --- /dev/null +++ b/pkgs/by-name/ex/exo/make-encoding-init-lazy.patch @@ -0,0 +1,29 @@ +diff --git a/src/exo/master/api.py b/src/exo/master/api.py +index 30f87e2a..4aac51fe 100644 +--- a/src/exo/master/api.py ++++ b/src/exo/master/api.py +@@ -67,7 +67,14 @@ from exo.utils.channels import Receiver, Sender, channel + from exo.utils.dashboard_path import find_dashboard + from exo.utils.event_buffer import OrderedBuffer + +-encoding = load_harmony_encoding(HarmonyEncodingName.HARMONY_GPT_OSS) ++_encoding: object = None ++ ++ ++def get_encoding(): ++ global _encoding ++ if _encoding is None: ++ _encoding = load_harmony_encoding(HarmonyEncodingName.HARMONY_GPT_OSS) ++ return _encoding + + + def chunk_to_response( +@@ -382,7 +389,7 @@ class API: + ) + + async def _process_gpt_oss(self, token_chunks: Receiver[TokenChunk]): +- stream = StreamableParser(encoding, role=Role.ASSISTANT) ++ stream = StreamableParser(_encoding, role=Role.ASSISTANT) + thinking = False + + async for chunk in token_chunks: diff --git a/pkgs/by-name/ex/exo/package.nix b/pkgs/by-name/ex/exo/package.nix index 919815c9ec61..fae8710e96ad 100644 --- a/pkgs/by-name/ex/exo/package.nix +++ b/pkgs/by-name/ex/exo/package.nix @@ -3,80 +3,201 @@ stdenv, fetchFromGitHub, python3Packages, - gitUpdater, -}: -python3Packages.buildPythonApplication rec { - pname = "exo"; - version = "0.0.14-alpha"; - pyproject = true; + replaceVars, + macmon, + + # pyo3-bindings + rustPlatform, + + # dashboard + buildNpmPackage, + fetchNpmDeps, + writableTmpDirAsHomeHook, + + nix-update-script, +}: +let + version = "1.0.63"; src = fetchFromGitHub { + name = "exo"; owner = "exo-explore"; repo = "exo"; tag = "v${version}"; - hash = "sha256-GoYfpr6oFpreWQtSomOwgfzSoBAbjqGZ1mcc0u9TBl4="; + hash = "sha256-aQ3rGLtT/zvIVdKQcwqODulzEHBKg7KMkBg3KJEscho="; }; - build-system = with python3Packages; [ setuptools ]; + pyo3-bindings = python3Packages.buildPythonPackage (finalAttrs: { + pname = "exo-pyo3-bindings"; + inherit version src; + pyproject = true; + + buildAndTestSubdir = "rust/exo_pyo3_bindings"; + + cargoDeps = rustPlatform.fetchCargoVendor { + inherit (finalAttrs) pname src version; + hash = "sha256-N7B1WFqPdqeNPZe9hXGyX7F3EbB1spzeKc19BFDDwls="; + }; + + # Bypass rust nightly features not being available on rust stable + env.RUSTC_BOOTSTRAP = 1; + + nativeBuildInputs = [ + rustPlatform.cargoSetupHook + rustPlatform.maturinBuildHook + ]; + + nativeCheckInputs = with python3Packages; [ + pytest-asyncio + pytestCheckHook + ]; + + enabledTestPaths = [ + "rust/exo_pyo3_bindings/tests/" + ]; + }); + + dashboard = buildNpmPackage (finalAttrs: { + pname = "exo-dashboard"; + inherit src version; + + sourceRoot = "${finalAttrs.src.name}/dashboard"; + + npmDeps = fetchNpmDeps { + inherit (finalAttrs) + pname + version + src + sourceRoot + ; + fetcherVersion = 2; + hash = "sha256-w3FZL/yy8R+SWCQF7+v21sKyizvZMmipG6IfhJeSjyQ="; + }; + }); +in +python3Packages.buildPythonApplication (finalAttrs: { + pname = "exo"; + pyproject = true; + + inherit version src; + + patches = [ + (replaceVars ./inject-dashboard-path.patch { + dashboard = "${dashboard}/lib/node_modules/${dashboard.pname}/build"; + }) + ]; + + postPatch = '' + substituteInPlace pyproject.toml \ + --replace-fail "uv_build>=0.8.9,<0.9.0" "uv_build" + '' + # MemoryObjectStreamState was renamed in + # https://github.com/agronholm/anyio/pull/1009/changes/bdc945a826d0d5917aea3517ceb9fe335b468094 + + '' + substituteInPlace src/exo/utils/channels.py \ + --replace-fail \ + "MemoryObjectStreamState as AnyioState," \ + "_MemoryObjectStreamState as AnyioState," + '' + + lib.optionalString stdenv.hostPlatform.isDarwin '' + substituteInPlace src/exo/worker/utils/macmon.py \ + --replace-fail \ + 'path = shutil.which("macmon")' \ + 'path = "${lib.getExe macmon}"' + + substituteInPlace src/exo/worker/utils/tests/test_macmon.py \ + --replace-fail \ + 'cmd=["macmon"' \ + 'cmd=["${lib.getExe macmon}"' + ''; + + build-system = with python3Packages; [ + uv-build + ]; pythonRelaxDeps = true; - pythonRemoveDeps = [ "uuid" ]; - - dependencies = with python3Packages; [ - aiohttp - aiohttp-cors - aiofiles - grpcio - grpcio-tools - jinja2 - numpy - nuitka - nvidia-ml-py - opencv-python - pillow - prometheus-client - protobuf - psutil - pydantic - requests - rich - scapy - tqdm - transformers - tinygrad - uvloop + pythonRemoveDeps = [ + "types-aiofiles" + "uuid" ]; + dependencies = + with python3Packages; + [ + aiofiles + aiohttp + aiohttp-cors + anyio + fastapi + filelock + grpcio + grpcio-tools + httpx + huggingface-hub + hypercorn + jinja2 + loguru + mlx + mlx-lm + nvidia-ml-py + openai + openai-harmony + opencv-python + pillow + prometheus-client + psutil + pydantic + pyo3-bindings + rustworkx + scapy + tiktoken + tinygrad + transformers + uvloop + ] + ++ sqlalchemy.optional-dependencies.asyncio; pythonImportsCheck = [ "exo" - "exo.inference.tinygrad.models" + "exo.main" ]; - nativeCheckInputs = with python3Packages; [ - mlx - pytestCheckHook + nativeCheckInputs = [ + python3Packages.pytest-asyncio + python3Packages.pytestCheckHook + writableTmpDirAsHomeHook ]; - disabledTestPaths = [ - "test/test_tokenizers.py" + # Otherwise fails with 'import file mismatch' + preCheck = '' + rm src/exo/__init__.py + ''; + + disabledTests = lib.optionals stdenv.hostPlatform.isDarwin [ + # AssertionError: assert "MacMon not found in PATH" in str(exc_info.value) + "test_macmon_not_found_raises_macmon_error" + + # ValueError: zip() argument 2 is longer than argument 1 + "test_events_processed_in_correct_order" ]; - # Tests require `mlx` which is not supported on linux. - doCheck = stdenv.hostPlatform.isDarwin; + disabledTestPaths = [ + # All tests hang indefinitely + "src/exo/worker/tests/unittests/test_mlx/test_tokenizers.py" + ]; passthru = { - updateScript = gitUpdater { - rev-prefix = "v-"; - }; + updateScript = nix-update-script { }; + exo-pyo3-bindings = pyo3-bindings; + exo-dashboard = dashboard; }; meta = { description = "Run your own AI cluster at home with everyday devices"; homepage = "https://github.com/exo-explore/exo"; - changelog = "https://github.com/exo-explore/exo/releases/tag/v${version}"; + changelog = "https://github.com/exo-explore/exo/releases/tag/${finalAttrs.src.tag}"; license = lib.licenses.gpl3Only; maintainers = with lib.maintainers; [ GaetanLepage ]; mainProgram = "exo"; }; -} +}) |
