diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1 @@ +{} diff --git a/.codex/config.toml b/.codex/config.toml new file mode 100644 index 0000000..78e0b0b --- /dev/null +++ b/.codex/config.toml @@ -0,0 +1,2 @@ +# Project commands and conventions are in AGENTS.md. +# Hooks call the shared checker once it is introduced; user permissions stay local. diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..71ee4f6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[*.{py,rs}] +indent_size = 4 + +[Makefile] +indent_style = tab diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..3a3c721 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +* text=auto eol=lf +*.onnx binary +*.pb binary +*.png binary +*.jpg binary diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7a97f4a..345fa56 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,49 +1,39 @@ -name: Test +name: Baseline validation on: push: branches: [main] pull_request: +permissions: + contents: read +concurrency: + group: baseline-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true jobs: - linux: - runs-on: ubuntu-latest - name: Linux ${{ matrix.elixir }}, ${{ matrix.otp }} + baseline: strategy: + fail-fast: false matrix: - elixir: ["1.17.3"] - otp: ["27.1.2"] + os: [ubuntu-24.04, macos-15] + runs-on: ${{ matrix.os }} + timeout-minutes: 25 env: MIX_ENV: test steps: - - uses: actions/checkout@v2 - - uses: erlef/setup-beam@v1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - otp-version: ${{ matrix.otp }} - elixir-version: ${{ matrix.elixir }} - - name: Install dependencies - run: mix deps.get - - name: Compile and check warnings - run: mix compile --warnings-as-errors - - name: Check formatting - run: mix format --check-formatted - - name: Run tests - run: mix test - - macos: - runs-on: macos-latest - name: macOS - env: - MIX_ENV: test - steps: - - uses: actions/checkout@v2 - - name: Install + persist-credentials: false + - uses: erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124 # v1.24.1 + with: + elixir-version: '1.20.4' + otp-version: '29.1.1' + - uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0 + with: + python-version: '3.14.10' + - name: Supply the separately installed runtime run: | - brew update - brew install erlang@27 elixir - - name: Install dependencies - run: mix deps.get - - name: Compile and check warnings - run: mix compile --warnings-as-errors - - name: Check formatting - run: mix format --check-formatted - - name: Run tests - run: mix test + runtime_path=$(python scripts/install_runtime.py install) + echo "ORT_DYLIB_PATH=$runtime_path" >> "$GITHUB_ENV" + - run: mix deps.get --check-locked + - run: mix compile --warnings-as-errors + - run: mix format --check-formatted + - run: mix test diff --git a/.gitignore b/.gitignore index 46366ee..6cd02b5 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,15 @@ ortex-*.tar # Temporary files, for example, from tests. /tmp/ + +/runtime/ +/.venv/ +/dist/ +/bench/results/ +**/__pycache__/ +/.ruff_cache/ +/.cache/ +/priv/native/ +/examples/_build/ +/examples/deps/ +/examples/priv/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..8cc9f30 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,18 @@ +# Working on Ortex + +Read docs/development.md for setup and supported environments. Use `make help` +for the commands implemented by this branch. Run the relevant checks before +committing; use atomic conventional commits. Keep runtime/API changes separate +from development infrastructure. + +The native crate lives in native/ortex. Run Cargo from that directory so its +.cargo/config.toml applies. Changes crossing the BEAM/native boundary need tests +through the public Elixir API. Process-wide runtime configuration requires a +fresh VM. Keep inference workloads and scheduler measurements under bench/. + +Fixtures and examples must use public or synthetic inputs, with recorded source, +license, and reference results. Never download models implicitly during tests. + +Use the RiverFinancial fork. Do not publish packages or releases unless the +user explicitly requests publication. Do not rewrite other branches as part of +foundation work. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..43c994c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1cd6a92 --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +.DEFAULT_GOAL := help +PYTHON ?= python3 +export MIX_ENV := test +export ORT_DYLIB_PATH ?= $(shell $(PYTHON) scripts/install_runtime.py path 2>/dev/null) + +.PHONY: help setup deps doctor test docs + +help: ## Show development commands + @awk 'BEGIN {FS = ":.*## "} /^[a-zA-Z0-9_.-]+:.*## / {printf " %-22s %s\n", $$1, $$2}' $(MAKEFILE_LIST) + +setup: ## Fetch Mix dependencies and the development runtime + mix local.hex --force + mix local.rebar --force + $(MAKE) deps + $(PYTHON) scripts/install_runtime.py install + +deps: ## Fetch the locked test and documentation dependencies + mix deps.get --check-locked + MIX_ENV=docs mix deps.get --check-locked + +doctor: ## Describe tools, architecture, and runtime selection + $(PYTHON) scripts/doctor.py + +test: ## Run library tests and doctests + mix test + +docs: ## Build documentation with warnings treated as errors + MIX_ENV=docs mix docs --warnings-as-errors diff --git a/config/config.exs b/config/config.exs index f693371..7514dab 100644 --- a/config/config.exs +++ b/config/config.exs @@ -6,13 +6,6 @@ config :elixir, :dbg_callback, {Macro, :dbg, []} config :ortex, add_backend_on_inspect: config_env() != :test -# Set the cargo feature flags required to use the matching execution provider -# based on the OS we're running on -ortex_features = - case :os.type() do - {:win32, _} -> ["directml"] - {:unix, :darwin} -> ["coreml"] - {:unix, _} -> ["cuda", "tensorrt"] - end - -config :ortex, Ortex.Native, features: ortex_features +# Do not infer accelerator support from the operating system. Runtime selection +# stays explicit through ORT_DYLIB_PATH; compilation needs no runtime headers. +config :ortex, Ortex.Native, features: ["ort/load-dynamic"] diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 0000000..467a4fb --- /dev/null +++ b/docs/development.md @@ -0,0 +1,25 @@ +# Development + +Use `mise install`, then `mise exec -- make setup` and `mise exec -- make doctor`. +Rustup reads the pinned rust-toolchain.toml. GNU Make 3.81 or newer is sufficient; +Windows developers can use Git Bash with Make available on PATH. + +The supported BEAM pairs are Elixir 1.19 / OTP 28 and Elixir 1.20 / OTP 29. +Normal development uses the latter. Rust 1.92 is the minimum; the normal pinned +toolchain is 1.93.1. Python 3.12+ runs the standard-library tooling. + +ONNX Runtime is a separate dependency. `make setup` installs the development +runtime under runtime/ and records its path. Make passes that path through +ORT_DYLIB_PATH; an explicitly set environment variable takes precedence. Direct +Mix commands need that variable too. ONNX Runtime 1.23.2 is used on Intel macOS +because 1.24.4 has no official Intel macOS archive. Successful process exit is +part of testing; runtime 1.22.0 has exhibited shutdown failures on macOS. + +The foundation retains main's ort rc.8 and Rustler 0.29 native implementation. +Rustler migration belongs to the separate feature work. CPU dynamic loading is +the development default; additional Cargo features must be explicitly selected +in the consuming application's config. Library tests remain under test/ with +normal ExUnit discovery. Additional suites are introduced by later stack PRs. + +Documentation has its own Mix environment so building docs does not install +EXLA, Torchx, or example tokenizers. Large examples own their extra dependencies. diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..8d75934 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,11 @@ +# Examples + +Examples have an independent Mix project: from this directory, run `mix deps.get` +and `mix run distilbert/distilbert_classification.exs` (or another example). +Set ORT_DYLIB_PATH to the separately installed runtime. Model paths are relative +to this directory. The Python export dependencies are separate from the library. + +The inherited DistilBERT and StableLM exports remain manual demonstrations. Their +model revisions and export environments have not yet been made reproducible; +they are not validation gates. The foundation test PR adds small prepared models +with recorded provenance and expected outputs. diff --git a/examples/config/config.exs b/examples/config/config.exs new file mode 100644 index 0000000..5e30241 --- /dev/null +++ b/examples/config/config.exs @@ -0,0 +1,3 @@ +import Config + +config :ortex, Ortex.Native, features: ["ort/load-dynamic"] diff --git a/examples/mix.exs b/examples/mix.exs new file mode 100644 index 0000000..8a5fc86 --- /dev/null +++ b/examples/mix.exs @@ -0,0 +1,16 @@ +defmodule Ortex.Examples.MixProject do + use Mix.Project + + def project do + [ + app: :ortex_examples, + version: "0.1.0", + elixir: "~> 1.19", + deps: [ + {:ortex, path: ".."}, + {:tokenizers, "~> 0.4"}, + {:jason, "~> 1.4"} + ] + ] + end +end diff --git a/mise.toml b/mise.toml new file mode 100644 index 0000000..2fae9d4 --- /dev/null +++ b/mise.toml @@ -0,0 +1,9 @@ +[tools] +erlang = "29.1.1" +elixir = "1.20.4-otp-29" +python = "3.14.10" +actionlint = "1.7.12" +shellcheck = "0.11.0" +shfmt = "3.14.1" + +# Rust is pinned by rust-toolchain.toml. Checkers are isolated from model exporters. diff --git a/mix.exs b/mix.exs index 0ff357e..04a638c 100644 --- a/mix.exs +++ b/mix.exs @@ -5,14 +5,14 @@ defmodule Ortex.MixProject do [ app: :ortex, version: "0.1.10", - elixir: "~> 1.14", + elixir: "~> 1.19", start_permanent: Mix.env() == :prod, deps: deps(), # Docs name: "Ortex", - source_url: "https://github.com/elixir-nx/ortex", - homepage_url: "http://github.com/elixir-nx/ortex", + source_url: "https://github.com/RiverFinancial/ortex", + homepage_url: "https://github.com/RiverFinancial/ortex", docs: [ main: "readme", extras: ["README.md"] @@ -21,6 +21,10 @@ defmodule Ortex.MixProject do ] end + def cli do + [preferred_envs: [docs: :docs]] + end + # Run "mix help compile.app" to learn about applications. def application do [ @@ -31,12 +35,9 @@ defmodule Ortex.MixProject do # Run "mix help deps" to learn about dependencies. defp deps do [ - {:rustler, "~> 0.27"}, + {:rustler, "~> 0.29.1"}, {:nx, "~> 0.6"}, - {:tokenizers, "~> 0.4", only: :dev}, - {:ex_doc, "0.29.4", only: :dev, runtime: false}, - {:exla, "~> 0.6", only: :dev}, - {:torchx, "~> 0.6", only: :dev} + {:ex_doc, "~> 0.40.4", only: :docs, runtime: false} ] end @@ -45,7 +46,7 @@ defmodule Ortex.MixProject do files: ~w(lib .formatter.exs mix.exs README* LICENSE* native/ortex/src/ config/config.exs native/ortex/Cargo.lock native/ortex/Cargo.toml native/ortex/.cargo/config.toml), licenses: ["MIT"], - links: %{"GitHub" => "https://github.com/elixir-nx/ortex"}, + links: %{"GitHub" => "https://github.com/RiverFinancial/ortex"}, description: "ONNX Runtime bindings for Elixir" ] end diff --git a/mix.lock b/mix.lock index 8b9284d..fe77280 100644 --- a/mix.lock +++ b/mix.lock @@ -1,30 +1,14 @@ %{ - "axon": {:hex, :axon, "0.5.1", "1ae3a2193df45e51fca912158320b2ca87cb7fba4df242bd3ebe245504d0ea1a", [:mix], [{:kino, "~> 0.7", [hex: :kino, repo: "hexpm", optional: true]}, {:kino_vega_lite, "~> 0.1.7", [hex: :kino_vega_lite, repo: "hexpm", optional: true]}, {:nx, "~> 0.5.0", [hex: :nx, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1", [hex: :table_rex, repo: "hexpm", optional: true]}], "hexpm", "d36f2a11c34c6c2b458f54df5c71ffdb7ed91c6a9ccd908faba909c84cc6a38e"}, - "axon_onnx": {:hex, :axon_onnx, "0.4.0", "7be4b5ac7a44340ec65eb59c24122a8fe2aa8105da33b3321a378b455a6cd9c6", [:mix], [{:axon, "~> 0.5", [hex: :axon, repo: "hexpm", optional: false]}, {:nx, "~> 0.5", [hex: :nx, repo: "hexpm", optional: false]}, {:protox, "~> 1.6.10", [hex: :protox, repo: "hexpm", optional: false]}], "hexpm", "b98c84e5656caf156ef8998296836349a62bc35598f05cc21eececbbef022d09"}, - "castore": {:hex, :castore, "1.0.4", "ff4d0fb2e6411c0479b1d965a814ea6d00e51eb2f58697446e9c41a97d940b28", [:mix], [], "hexpm", "9418c1b8144e11656f0be99943db4caf04612e3eaecefb5dae9a2a87565584f8"}, - "cc_precompiler": {:hex, :cc_precompiler, "0.1.7", "77de20ac77f0e53f20ca82c563520af0237c301a1ec3ab3bc598e8a96c7ee5d9", [:mix], [{:elixir_make, "~> 0.7.3", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "2768b28bf3c2b4f788c995576b39b8cb5d47eb788526d93bd52206c1d8bf4b75"}, - "complex": {:hex, :complex, "0.5.0", "af2d2331ff6170b61bb738695e481b27a66780e18763e066ee2cd863d0b1dd92", [:mix], [], "hexpm", "2683bd3c184466cfb94fad74cbfddfaa94b860e27ad4ca1bffe3bff169d91ef1"}, - "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, - "dll_loader_helper": {:hex, :dll_loader_helper, "1.1.0", "e7d015e980942a0d67e306827ec907e7e853a21186bd92bb968d986698591a0f", [:mix], [{:dll_loader_helper_beam, "~> 1.1", [hex: :dll_loader_helper_beam, repo: "hexpm", optional: false]}], "hexpm", "2b6c11ee7bb48f6a132ce8f872202f9e828c019988da1e2d40ad41496195df0c"}, - "dll_loader_helper_beam": {:hex, :dll_loader_helper_beam, "1.2.0", "557c43befb8e3b119b718da302adccde3bd855acdb999498a14a2a8d2814b8b9", [:rebar3], [], "hexpm", "a2115d4bf1cca488a7b33f3c648847f64019b32c0382d10286d84dd5c3cbc0e5"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.32", "fa739a0ecfa34493de19426681b23f6814573faee95dfd4b4aafe15a7b5b32c6", [:mix], [], "hexpm", "b8b0dd77d60373e77a3d7e8afa598f325e49e8663a51bcc2b88ef41838cca755"}, - "elixir_make": {:hex, :elixir_make, "0.7.7", "7128c60c2476019ed978210c245badf08b03dbec4f24d05790ef791da11aa17c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5bc19fff950fad52bbe5f211b12db9ec82c6b34a9647da0c2224b8b8464c7e6c"}, - "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, - "ex_doc": {:hex, :ex_doc, "0.29.4", "6257ecbb20c7396b1fe5accd55b7b0d23f44b6aa18017b415cb4c2b91d997729", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2c6699a737ae46cb61e4ed012af931b57b699643b24dabe2400a8168414bc4f5"}, - "exla": {:hex, :exla, "0.6.1", "a4400933a04d018c5fb508c75a080c73c3c1986f6c16a79bbfee93ba22830d4d", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nx, "~> 0.6.1", [hex: :nx, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:xla, "~> 0.5.0", [hex: :xla, repo: "hexpm", optional: false]}], "hexpm", "f0e95b0f91a937030cf9fcbe900c9d26933cb31db2a26dfc8569aa239679e6d4"}, - "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, - "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"}, - "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, - "nimble_parsec": {:hex, :nimble_parsec, "1.3.1", "2c54013ecf170e249e9291ed0a62e5832f70a476c61da16f6aac6dca0189f2af", [:mix], [], "hexpm", "2682e3c0b2eb58d90c6375fc0cc30bc7be06f365bf72608804fb9cffa5e1b167"}, - "nx": {:hex, :nx, "0.6.2", "f1d137f477b1a6f84f8db638f7a6d5a0f8266caea63c9918aa4583db38ebe1d6", [:mix], [{:complex, "~> 0.5", [hex: :complex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ac913b68d53f25f6eb39bddcf2d2cd6ea2e9bcb6f25cf86a79e35d0411ba96ad"}, - "protox": {:hex, :protox, "1.6.10", "41d0b0c5b9190e7d5e6a2b1a03a09257ead6f3d95e6a0cf8b81430b526126908", [:mix], [{:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "9769fca26ae7abfc5cc61308a1e8d9e2400ff89a799599cee7930d21132832d9"}, + "complex": {:hex, :complex, "0.7.0", "695632ef9487517aa5d57edd1697801079d622414cb2e1a7cf538b1f9a50f205", [:mix], [], "hexpm", "0ee39c0803129f546e7f3f640da8f021c9e659402bf59da6f7f2c4848f068f8d"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.46", "67607a0532e810c6f630a515c548d0b24949643f168cc556303bee4cf96105c7", [:mix], [], "hexpm", "9c44636e8a1c68c62f526b2dcd85d941dbbcee7ab82cf64ba06ce28bef8e89f5"}, + "ex_doc": {:hex, :ex_doc, "0.40.4", "66f2e42bf588594d5a8aab31cad87f2ddad09d0da1b1a2f379340ec2c2e497cb", [:mix], [{:earmark_parser, "~> 1.4.46", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "6222b9e423d76584ee34df2c82a5ed72c2d53dc153f7f483ad28b378694186cc"}, + "jason": {:hex, :jason, "1.4.5", "2e3a008590b0b8d7388c20293e9dcc9cf3e5d642fd2a114e4cbbb52e595d940a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b0c823996102bcd0239b3c2444eb00409b72f6a140c1950bc8b457d836b30684"}, + "makeup": {:hex, :makeup, "1.2.3", "c3ef0ff83acf305384acba0f70bff87d8732502baa1eb3eb081d8c154fbe18f5", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "15970751b7021c2bcd7ed56a54b3def23affd3969fb7c214dffdf28e97ce6f76"}, + "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.1.0", "835f7e60792e08824cda445639555d7bf1bbbddb1b60b306e33cb6f6db24dc74", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "1cd6780fb1dd1a03979abaed0fe82712b0625118fd5257d3ebbf73f960c73c3c"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, + "nx": {:hex, :nx, "0.13.1", "f96311a573dcd57d11ff889958ee518f52f7c16dbf6d22191020d322887856fc", [:mix], [{:complex, "~> 0.7", [hex: :complex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "20556ea36c1e9732c1d87eebbf7c0773517e876e92af4ff592b974f80db0b9d9"}, "rustler": {:hex, :rustler, "0.29.1", "880f20ae3027bd7945def6cea767f5257bc926f33ff50c0d5d5a5315883c084d", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:toml, "~> 0.6", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "109497d701861bfcd26eb8f5801fe327a8eef304f56a5b63ef61151ff44ac9b6"}, - "rustler_precompiled": {:hex, :rustler_precompiled, "0.7.0", "5d0834fc06dbc76dd1034482f17b1797df0dba9b491cef8bb045fcaca94bcade", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "fdf43a6835f4e4de5bfbc4c019bfb8c46d124bd4635fefa3e20d9a2bbbec1512"}, - "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, - "tokenizers": {:hex, :tokenizers, "0.4.0", "140283ca74a971391ddbd83cd8cbdb9bd03736f37a1b6989b82d245a95e1eb97", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, ">= 0.0.0", [hex: :rustler, repo: "hexpm", optional: true]}, {:rustler_precompiled, "~> 0.6", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}], "hexpm", "ef1a9824f5a893cd3b831c0e5b3d72caa250d2ec462035cc6afef6933b13a82e"}, + "telemetry": {:hex, :telemetry, "1.4.2", "a0cb522801dffb1c49fe6e30561badffc7b6d0e180db1300df759faa22062855", [:rebar3], [], "hexpm", "928f6495066506077862c0d1646609eed891a4326bee3126ba54b60af61febb1"}, "toml": {:hex, :toml, "0.7.0", "fbcd773caa937d0c7a02c301a1feea25612720ac3fa1ccb8bfd9d30d822911de", [:mix], [], "hexpm", "0690246a2478c1defd100b0c9b89b4ea280a22be9a7b313a8a058a2408a2fa70"}, - "torchx": {:hex, :torchx, "0.6.1", "2a9862ebc4b397f42c51f0fa3f9f4e3451a83df6fba42882f8523cbc925c8ae1", [:make, :mix], [{:dll_loader_helper, "~> 0.1 or ~> 1.0", [hex: :dll_loader_helper, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nx, "~> 0.6.1", [hex: :nx, repo: "hexpm", optional: false]}], "hexpm", "99b3fc73b52d6cfbe5cad8bdd74277ddc99297ce8fc6765b1dabec80681e8d9d"}, - "useful": {:hex, :useful, "1.11.0", "b2d89223563c3354fd56f4da75b63f07f52cb32b243289a7f1fcc37869bcf9c2", [:mix], [], "hexpm", "2e5b2a47acc191bfb38e936f5f1bc57dad3b11133e0defe59a32fda10ebafcff"}, - "xla": {:hex, :xla, "0.5.1", "8ba4c2c51c1a708ff54e9d4f88158c1a75b7f2cb3e5db02bf222b5b3852afffd", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "82a2490f6e9a76c8a29d1aedb47f07c59e3d5081095eac5a74db34d46c8212bc"}, } diff --git a/native/ortex/Cargo.toml b/native/ortex/Cargo.toml index 4052470..be152a2 100644 --- a/native/ortex/Cargo.toml +++ b/native/ortex/Cargo.toml @@ -3,6 +3,7 @@ name = "ortex" version = "0.1.0" authors = [] edition = "2018" +rust-version = "1.92" [lib] name = "ortex" diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..d46b3cc --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "1.93.1" +profile = "minimal" +components = ["rustfmt", "clippy"] diff --git a/scripts/doctor.py b/scripts/doctor.py new file mode 100644 index 0000000..5cc6ae5 --- /dev/null +++ b/scripts/doctor.py @@ -0,0 +1,37 @@ +"""Report the active environment without installing or changing anything.""" + +import ctypes +import os +import platform +import shutil +import subprocess +import sys + + +def main(): + failed = False + print(f"Host: {platform.system()} {platform.machine()} ({platform.libc_ver()})") + print(f"Python: {platform.python_version()}") + for executable, arguments in [("elixir", ["--version"]), ("cargo", ["--version"])]: + if not shutil.which(executable): + print(f"MISSING: {executable}; run mise install / install rustup") + failed = True + continue + result = subprocess.run([executable, *arguments], capture_output=True, text=True) + print(result.stdout.strip() or result.stderr.strip()) + failed |= result.returncode != 0 + runtime = os.environ.get("ORT_DYLIB_PATH") + print(f"ORT_DYLIB_PATH: {runtime or '(unset; run make setup)'}") + if runtime: + try: + ctypes.CDLL(runtime) + except OSError as error: + print(f"Runtime cannot be loaded: {error}") + failed = True + else: + failed = True + return int(failed) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/install_runtime.py b/scripts/install_runtime.py new file mode 100644 index 0000000..b013e0d --- /dev/null +++ b/scripts/install_runtime.py @@ -0,0 +1,56 @@ +"""Install the separately supplied ONNX Runtime; print its path for Make.""" + +import argparse +import json +import platform +import tarfile +import urllib.request +import zipfile +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +DESTINATION = ROOT / "runtime" +RECEIPT = DESTINATION / "installed.json" + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("command", choices=["install", "path"]) + parser.add_argument("--version") + args = parser.parse_args() + if args.command == "path": + library = Path(json.loads(RECEIPT.read_text())["library"]) + if not library.is_file(): + raise SystemExit("runtime receipt is stale; run make setup") + print(library) + return + system = platform.system() + arm = platform.machine().lower() in {"arm64", "aarch64"} + target = { + "Darwin": "osx-arm64" if arm else "osx-x86_64", + "Linux": "linux-aarch64" if arm else "linux-x64", + "Windows": "win-arm64" if arm else "win-x64", + }[system] + version = args.version or ("1.23.2" if target == "osx-x86_64" else "1.24.4") + extension = "zip" if system == "Windows" else "tgz" + name = f"onnxruntime-{target}-{version}" + DESTINATION.mkdir(exist_ok=True) + archive = DESTINATION / f"{name}.{extension}" + urllib.request.urlretrieve( + f"https://github.com/microsoft/onnxruntime/releases/download/v{version}/{archive.name}", + archive, + ) + if extension == "zip": + with zipfile.ZipFile(archive) as source: + source.extractall(DESTINATION) + else: + with tarfile.open(archive) as source: + source.extractall(DESTINATION, filter="data") + pattern = {"Darwin": "libonnxruntime.*.dylib", "Linux": "libonnxruntime.so.*", "Windows": "onnxruntime.dll"}[system] + library = next((DESTINATION / name).rglob(pattern)).resolve() + RECEIPT.write_text(json.dumps({"version": version, "target": target, "library": str(library)}) + "\n") + print(library) + + +if __name__ == "__main__": + main()