Imp.Module behaviour (Imp v0.5.0)

Copy Markdown View Source

Behaviour and safe dispatcher for executable Imp programs.

An Imp program is any struct whose module implements call/2 and returns either {:ok, %Imp.Prediction{}} or {:error, reason}. Imp.Module.call/2 is the central boundary used by evaluators, composition modules, streaming, optimizers, and the public Imp.call/2 facade. It catches callback crashes and normalizes malformed callback returns so composed workflows can report failures without losing the rest of the run. A program that raises, throws or exits returns {:error, {:module_call_failed, module, reason}}, where reason is the exception struct it raised or {kind, value} for a throw or an exit; one that returns anything else returns {:error, {:invalid_module_result, module, inspected}}.

Consumer-defined multi-stage programs may expose named predictors to every program optimizer by implementing the paired optional callbacks optimizer_predictors/1 and update_optimizer_predictor/3. Both callbacks are required together. Predictor names must be unique atoms or strings and each value must be an Imp.Predict struct. The update callback must return the same program struct after applying the supplied function to the named predictor. Imp.ProgramParameters validates this contract before an optimizer can use it.

Programs may additionally expose arbitrary data-only components with the paired optimizer_components/1 and update_optimizer_components/2 callbacks. Components carry descriptions, executable constraints, and dependency identities. The batch update callback must be pure and return the same program struct; Imp validates the complete change set before invoking it.

Summary

Functions

Calls an Imp executable program and normalizes its result shape.

Executes a program with explicit per-run capabilities when it supports them.

Types

optimizer_predictor()

@type optimizer_predictor() :: %Imp.Predict{
  adapter: term(),
  adapter_opts: term(),
  config: term(),
  demos: term(),
  dynamic_adapter?: term(),
  dynamic_lm?: term(),
  lm: term(),
  metadata: term(),
  signature: term(),
  traces: term()
}

optimizer_predictor_entry()

@type optimizer_predictor_entry() ::
  {optimizer_predictor_name(), optimizer_predictor()}
  | %{name: optimizer_predictor_name(), predictor: optimizer_predictor()}

optimizer_predictor_name()

@type optimizer_predictor_name() :: atom() | String.t()

Callbacks

call(struct, arg2)

@callback call(struct(), map() | keyword()) ::
  {:ok, Imp.Prediction.t()} | {:error, term()}

execute(struct, arg2, t)

(optional)
@callback execute(struct(), map() | keyword(), Imp.Execution.t()) ::
  {:ok, Imp.Prediction.t()} | {:error, term()}

optimizer_components(struct)

(optional)
@callback optimizer_components(struct()) :: [Imp.Optimizer.Component.t() | map()]

optimizer_predictors(struct)

(optional)
@callback optimizer_predictors(struct()) :: [optimizer_predictor_entry()]

update_optimizer_components(struct, map)

(optional)
@callback update_optimizer_components(struct(), %{
  required(String.t()) => Imp.Optimizer.Parameter.json_value()
}) :: struct()

update_optimizer_predictor(struct, optimizer_predictor_name, function)

(optional)
@callback update_optimizer_predictor(
  struct(),
  optimizer_predictor_name(),
  (optimizer_predictor() ->
     optimizer_predictor())
) ::
  struct()

Functions

call(program, inputs)

Calls an Imp executable program and normalizes its result shape.

Successful programs must return a Imp.Prediction:

iex> lm = Imp.LM.Static.new(handler: fn _messages, _opts -> %{answer: "4"} end)
iex> program = Imp.Predict.new("question -> answer", lm: lm)
iex> {:ok, prediction} = Imp.Module.call(program, %{question: "2+2?"})
iex> Imp.Prediction.get(prediction, :answer)
"4"

Non-program values are reported as not callable:

iex> Imp.Module.call(%{}, %{question: "q"})
{:error, {:not_callable, %{}}}

execute(program, inputs, execution)

Executes a program with explicit per-run capabilities when it supports them.