lib/openagents/continual_learning/bounds.ex

58e6347eeb72 · 3 KB

defmodule OpenAgents.ContinualLearning.Bounds do
  @moduledoc """
  The bounds one continual-learning job is admitted under.

  Every limit a run can spend — rounds, wall clock, checkpoint size, budget,
  runtime classes, admitted base models, admitted custody — is read here and
  snapshotted onto the job row at admission, so a configuration change cannot
  widen a run that is already admitted.
  """

  @kind "continual_learning"

  @doc "The `work_jobs` kind a continual-learning run uses."
  def kind, do: @kind

  @doc "The configured continual-learning settings."
  def settings, do: Application.get_env(:openagents, OpenAgents.ContinualLearning, [])

  @doc "Whether the continual-learning lane is admitted in this runtime."
  def enabled?, do: Keyword.get(settings(), :enabled) == true

  @doc "The one named buyer this lane serves, or `nil` when no buyer is named."
  def buyer_ref, do: Keyword.get(settings(), :buyer_ref)

  @doc "The buyer class every consumed listing must be licensed to."
  def buyer_class, do: Keyword.get(settings(), :buyer_class)

  @doc "The runtime classes a job may request, as capacity class identifiers."
  def runtime_classes, do: Keyword.get(settings(), :runtime_classes, [])

  @doc "The admitted base models as `%{model_ref => digest}`."
  def admitted_base_models, do: Keyword.get(settings(), :admitted_base_models, %{})

  @doc "The custody classes, as capacity data locations, the lane may train in."
  def admitted_custody, do: Keyword.get(settings(), :admitted_custody, [])

  @doc "The largest number of training rounds one job may run."
  def maximum_rounds, do: Keyword.get(settings(), :maximum_rounds, 8)

  @doc "The largest number of licensed datasets one job may admit."
  def maximum_datasets, do: Keyword.get(settings(), :maximum_datasets, 4)

  @doc "The wall clock one job is admitted for, in milliseconds."
  def wall_clock_ms, do: Keyword.get(settings(), :wall_clock_ms, 900_000)

  @doc "The largest checkpoint state one round may durably store, in bytes."
  def maximum_state_bytes, do: Keyword.get(settings(), :maximum_state_bytes, 65_536)

  @doc "How many continual-learning jobs may run at once."
  def concurrency_limit, do: Keyword.get(settings(), :concurrency_limit, 1)

  @doc "The exact training code the lane runs, as a digest."
  def training_code_digest, do: Keyword.get(settings(), :training_code_digest)

  @doc "The trainer implementation."
  def trainer,
    do: Keyword.get(settings(), :trainer, OpenAgents.ContinualLearning.Trainer.Reference)

  @doc "The evaluator implementation."
  def evaluator,
    do: Keyword.get(settings(), :evaluator, OpenAgents.ContinualLearning.Evaluator.Reference)

  @doc "The average power draw of one runtime class, in watts."
  def class_watts, do: Keyword.get(settings(), :class_watts, %{})

  @doc "The settlement unit every settlement-ready receipt is denominated in."
  def settlement_unit, do: Keyword.get(settings(), :settlement_unit, "usd_cents")

  @doc "The cost one round of a runtime class meters, in US cents."
  def round_cost_usd_cents(runtime_class) when is_binary(runtime_class) do
    settings()
    |> Keyword.get(:round_cost_usd_cents, %{})
    |> Map.get(runtime_class, 1)
  end

  @doc "The forge issue every accepted outcome of this lane is graded against."
  def outcome_issue do
    %{
      repository: Keyword.get(settings(), :outcome_repository),
      issue_number: Keyword.get(settings(), :outcome_issue_number)
    }
  end

  @doc """
  The immutable bounds snapshot recorded on the job row at admission.
  """
  def snapshot(runtime_class) when is_binary(runtime_class) do
    %{
      "maximum_rounds" => maximum_rounds(),
      "wall_clock_ms" => wall_clock_ms(),
      "maximum_state_bytes" => maximum_state_bytes(),
      "runtime_class" => runtime_class,
      "watts" => Map.get(class_watts(), runtime_class, 0),
      "training_code_digest" => training_code_digest()
    }
  end
end