OpenResponses.Middleware behaviour (OpenResponses v0.1.1)

View Source

Behaviour for intercepting the agentic loop between provider calls.

Middleware runs before and after each call to the provider. Use it to enforce token budgets, log requests, filter content, track costs, or apply rate limits — without modifying the core loop logic.

Implementing middleware

defmodule MyApp.Middleware.TokenBudget do
  @behaviour OpenResponses.Middleware

  @impl OpenResponses.Middleware
  def before_sample(%{iteration: n} = state) when n > 10 do
    {:halt, :iteration_limit_reached}
  end

  def before_sample(state), do: {:cont, state}

  @impl OpenResponses.Middleware
  def after_sample(state, items), do: {:cont, state, items}
end

Registering middleware

config :open_responses, :middlewares, [
  MyApp.Middleware.AuditLog,
  MyApp.Middleware.TokenBudget
]

Middlewares run in list order. If any module returns {:halt, reason}, the pipeline stops and the loop is terminated with that reason.

See the Middleware guide for more examples.

Summary

Callbacks

after_sample(loop_state, items)

@callback after_sample(loop_state :: map(), items :: list()) ::
  {:cont, map(), list()} | {:halt, map(), term()}

before_sample(loop_state)

@callback before_sample(loop_state :: map()) :: {:cont, map()} | {:halt, term()}

Functions

run_after_sample(middlewares, state, items)

@spec run_after_sample([module()], map(), list()) ::
  {:cont, map(), list()} | {:halt, map(), term()}

run_before_sample(middlewares, state)

@spec run_before_sample([module()], map()) :: {:cont, map()} | {:halt, term()}