gleamunison/llm_middleware

LLM handler middleware (gap analysis concept A, decomplected).

BAML complects retry/fallback/mocking into a client registry + hosted studio. Here they are plain handler composition: a middleware is just an OpHandler wrapping another OpHandler. Same contract as llm.gleam: continuation receives {ok, CompletionJsonBin} | {error, ReasonBin}.

Types

pub type RetryPolicy {
  RetryPolicy(max_attempts: Int, base_delay_ms: Int)
}

Constructors

  • RetryPolicy(max_attempts: Int, base_delay_ms: Int)

Values

pub fn budget_handler(
  inner: fn(
    List(dynamic.Dynamic),
    fn(dynamic.Dynamic) -> dynamic.Dynamic,
  ) -> dynamic.Dynamic,
  provider: llm_types.Provider,
  model: String,
) -> fn(
  List(dynamic.Dynamic),
  fn(dynamic.Dynamic) -> dynamic.Dynamic,
) -> dynamic.Dynamic

Budget/circuit gate as handler composition. Admit before the call, release the concurrency slot exactly once, then meter actuals and report the outcome. Refusals return the typed refusal name as the handler error; the inner handler never runs.

pub fn fallback_handler(
  handlers: List(
    fn(
      List(dynamic.Dynamic),
      fn(dynamic.Dynamic) -> dynamic.Dynamic,
    ) -> dynamic.Dynamic,
  ),
) -> fn(
  List(dynamic.Dynamic),
  fn(dynamic.Dynamic) -> dynamic.Dynamic,
) -> dynamic.Dynamic

Try handlers in order; the first {ok, _} wins. All failing returns the last handler’s error.

pub fn recording_mock(
  response: llm_types.Completion,
  record: fn(BitArray) -> Nil,
) -> fn(
  List(dynamic.Dynamic),
  fn(dynamic.Dynamic) -> dynamic.Dynamic,
) -> dynamic.Dynamic

Deterministic mock that also reports the messages JSON it received — the fixture recorder for the eval family (gap analysis concept E).

pub fn retry_handler(
  inner: fn(
    List(dynamic.Dynamic),
    fn(dynamic.Dynamic) -> dynamic.Dynamic,
  ) -> dynamic.Dynamic,
  policy: RetryPolicy,
) -> fn(
  List(dynamic.Dynamic),
  fn(dynamic.Dynamic) -> dynamic.Dynamic,
) -> dynamic.Dynamic

At most three attempts with at most 1000ms delay per retry. Terminal settings/auth/decode/cancellation errors must not repeat a keyed call.

✨ Search Document