Move ML deps to dev/test only, exclude from production build

- Nx, Axon, EXLA, Polaris deps restricted to only: [:dev, :test]
- model.ex and training mix tasks moved to lib_ml/ (compiled via
  elixirc_paths in dev/test only)
- load_ml_model uses Code.ensure_loaded? + apply/3 to avoid
  compile-time references to ML modules in production
- Verified: MIX_ENV=prod compiles clean with no ML warnings
This commit is contained in:
Graham McIntire 2026-04-01 12:10:31 -05:00
parent 254e64dedc
commit 2c16fd979d
No known key found for this signature in database
GPG key ID: F4ABF488E6029E59
7 changed files with 29 additions and 18 deletions

View file

@ -86,6 +86,9 @@ config :microwaveprop, Oban,
# Enable dev routes for dashboard and mailbox # Enable dev routes for dashboard and mailbox
config :microwaveprop, dev_routes: true config :microwaveprop, dev_routes: true
# Load ML model at startup (Nx/Axon/EXLA only available in dev/test)
config :microwaveprop, load_ml_model: true
# Use local SRTM1 tiles for elevation lookups instead of the Open-Meteo API # Use local SRTM1 tiles for elevation lookups instead of the Open-Meteo API
config :microwaveprop, srtm_tiles_dir: Path.expand("~/srtm/tiles") config :microwaveprop, srtm_tiles_dir: Path.expand("~/srtm/tiles")

View file

@ -25,8 +25,10 @@ defmodule Microwaveprop.Application do
opts = [strategy: :one_for_one, name: Microwaveprop.Supervisor] opts = [strategy: :one_for_one, name: Microwaveprop.Supervisor]
result = Supervisor.start_link(children, opts) result = Supervisor.start_link(children, opts)
# Load ML model after supervision tree is up (non-blocking, no-op if file missing) # Load ML model in dev/test only (Nx/Axon not compiled for prod)
Microwaveprop.Propagation.load_ml_model() if Application.get_env(:microwaveprop, :load_ml_model, false) do
Microwaveprop.Propagation.load_ml_model()
end
result result
end end

View file

@ -6,7 +6,6 @@ defmodule Microwaveprop.Propagation do
alias Microwaveprop.Propagation.BandConfig alias Microwaveprop.Propagation.BandConfig
alias Microwaveprop.Propagation.Grid alias Microwaveprop.Propagation.Grid
alias Microwaveprop.Propagation.GridScore alias Microwaveprop.Propagation.GridScore
alias Microwaveprop.Propagation.Model
alias Microwaveprop.Propagation.Scorer alias Microwaveprop.Propagation.Scorer
alias Microwaveprop.Repo alias Microwaveprop.Repo
alias Microwaveprop.Weather.SoundingParams alias Microwaveprop.Weather.SoundingParams
@ -14,22 +13,28 @@ defmodule Microwaveprop.Propagation do
require Logger require Logger
@ml_key :propagation_ml @ml_key :propagation_ml
@ml_module Microwaveprop.Propagation.Model
@doc """ @doc """
Loads the ML model from disk, compiles the predict function, and caches both Loads the ML model from disk, compiles the predict function, and caches both
in persistent_term. No-op if the model file doesn't exist. in persistent_term. No-op if the model file doesn't exist or ML deps unavailable.
""" """
def load_ml_model do def load_ml_model do
case Model.load() do if Code.ensure_loaded?(@ml_module) do
{:ok, params} -> case apply(@ml_module, :load, []) do
predict_fn = Model.compile_predict() {:ok, params} ->
:persistent_term.put(@ml_key, {predict_fn, params}) predict_fn = apply(@ml_module, :compile_predict, [])
Logger.info("PropagationML: model loaded and compiled from #{inspect(Model.default_path())}") :persistent_term.put(@ml_key, {predict_fn, params})
:ok Logger.info("PropagationML: model loaded and compiled")
:ok
:error -> :error ->
Logger.info("PropagationML: no model file found, using algorithm scorer only") Logger.info("PropagationML: no model file found, using algorithm scorer only")
:ok :ok
end
else
Logger.info("PropagationML: ML dependencies not available")
:ok
end end
end end

11
mix.exs
View file

@ -32,7 +32,8 @@ defmodule Microwaveprop.MixProject do
end end
# Specifies which paths to compile per environment. # Specifies which paths to compile per environment.
defp elixirc_paths(:test), do: ["lib", "test/support"] defp elixirc_paths(:test), do: ["lib", "lib_ml", "test/support"]
defp elixirc_paths(:dev), do: ["lib", "lib_ml"]
defp elixirc_paths(_), do: ["lib"] defp elixirc_paths(_), do: ["lib"]
# Specifies your project dependencies. # Specifies your project dependencies.
@ -65,10 +66,10 @@ defmodule Microwaveprop.MixProject do
{:oban, "~> 2.19"}, {:oban, "~> 2.19"},
{:credo, "~> 1.7", only: [:dev, :test], runtime: false}, {:credo, "~> 1.7", only: [:dev, :test], runtime: false},
{:earmark, "~> 1.4"}, {:earmark, "~> 1.4"},
{:nx, "~> 0.9"}, {:nx, "~> 0.9", only: [:dev, :test]},
{:axon, "~> 0.7"}, {:axon, "~> 0.7", only: [:dev, :test]},
{:exla, "~> 0.9"}, {:exla, "~> 0.9", only: [:dev, :test]},
{:polaris, "~> 0.1"} {:polaris, "~> 0.1", only: [:dev, :test]}
] ]
end end