# `WardrikGateway`

Rate limits, circuit breakers and model failover for agents' model calls.

    children = [
      {WardrikGateway,
       name: MyApp.Gateway,
       fallbacks: %{"anthropic:claude-sonnet-4-5" => ["openai:gpt-5", "zai:glm-5.1"]},
       limits: %{"anthropic" => [requests_per_minute: 4000, tokens_per_minute: 400_000]}},
      {MyApp.AgentRuntime, middleware: [{WardrikGateway, MyApp.Gateway}]}
    ]

With `WardrikCore` alone, pass the same entry in an agent's `:middleware`.

  * A model with an open breaker is skipped. The call goes to the next model in `fallbacks`.
  * A call to a provider at its limit is delayed until the provider has capacity.
  * A 429 delays calls to that provider for its `Retry-After`, or one second.
  * A 5xx, a 529 or a timeout counts towards the model's breaker. The call goes to the next
    model.
  * After a fallback, the rest of the turn uses the fallback.
  * A call is delayed at most `:wait`. Then `WardrikCore.Retry` applies.

## Options

* `:name` (`t:atom/0`) - Required. The gateway's name.

* `:fallbacks` - Models to call, in order, when a call to the key's model fails. The default value is `%{}`.

* `:limits` - `requests_per_minute` and `tokens_per_minute`, by provider, as `"anthropic"`.
  Providers not listed are unlimited. The default value is `%{}`.

* `:breaker` (`t:keyword/0`) - When a model is skipped. The default value is `[]`.

  * `:failures` (`t:pos_integer/0`) - Failures that open it. The default value is `5`.

  * `:within` (`t:non_neg_integer/0`) - Milliseconds the failures fall within. The default value is `30000`.

  * `:open_for` (`t:non_neg_integer/0`) - Milliseconds before one call is let through to test the model. The default value is `30000`.

* `:wait` (`t:non_neg_integer/0`) - The longest a call waits, in milliseconds, for room or for a model to come back. The default value is `600000`.

## Telemetry

  * `[:wardrik_gateway, :failover]` - `from`, `to`, `reason` and `agent_id`.
  * `[:wardrik_gateway, :limited]` - measures `wait`, in milliseconds, with `model` and
    `agent_id`.
  * `[:wardrik_gateway, :breaker_opened]` and `[:wardrik_gateway, :breaker_closed]` - `model`.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `configure`

```elixir
@spec configure(GenServer.server(), keyword()) :: :ok
```

Replaces the settings at runtime. Breaker and limit counts are kept.

    WardrikGateway.configure(MyApp.Gateway, limits: %{"anthropic" => [requests_per_minute: 2000]})

# `schema`

```elixir
@spec schema() :: NimbleOptions.t()
```

Returns the schema of the settings `start_link/1` and `configure/2` take, but `:name`.

# `start_link`

```elixir
@spec start_link(keyword()) :: GenServer.on_start()
```

Starts a gateway registered as `:name`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
