# `Gemini.RateLimiter.State`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/rate_limiter/state.ex#L1)

ETS-based state management for rate limiting.

Tracks per-model/location/metric state including:
- `retry_until` timestamps derived from 429 RetryInfo
- Token usage sliding windows for budget estimation
- Concurrency permits for gating

State is keyed by `{model, location, metric}` tuples for fine-grained tracking.

# `reservation_ctx`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/rate_limiter/state.ex#L30)

```elixir
@type reservation_ctx() :: %{
  reserved_tokens: non_neg_integer(),
  estimated_tokens: non_neg_integer(),
  window_start: DateTime.t() | nil,
  window_end: DateTime.t() | nil,
  budget: non_neg_integer() | nil
}
```

# `retry_state`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/rate_limiter/state.ex#L14)

```elixir
@type retry_state() :: %{
  retry_until: DateTime.t() | nil,
  quota_metric: String.t() | nil,
  quota_id: String.t() | nil,
  quota_dimensions: map() | nil,
  quota_value: term() | nil,
  last_429_at: DateTime.t() | nil
}
```

# `state_key`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/rate_limiter/state.ex#L13)

```elixir
@type state_key() :: {model :: String.t(), location :: String.t(), metric :: atom()}
```

# `usage_window`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/rate_limiter/state.ex#L23)

```elixir
@type usage_window() :: %{
  input_tokens: non_neg_integer(),
  output_tokens: non_neg_integer(),
  reserved_tokens: non_neg_integer(),
  window_start: DateTime.t(),
  window_duration_ms: pos_integer()
}
```

# `build_key`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/rate_limiter/state.ex#L88)

```elixir
@spec build_key(String.t(), String.t() | nil, atom()) :: state_key()
```

Build a state key from model, location, and metric.

# `clear_retry_state`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/rate_limiter/state.ex#L161)

```elixir
@spec clear_retry_state(state_key()) :: :ok
```

Clear the retry state for a key (called after successful request).

# `get_current_usage`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/rate_limiter/state.ex#L216)

```elixir
@spec get_current_usage(state_key()) :: usage_window() | nil
```

Get current usage within the sliding window.

# `get_retry_state`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/rate_limiter/state.ex#L171)

```elixir
@spec get_retry_state(state_key()) :: retry_state() | nil
```

Get the current retry state details for a key.

# `get_retry_until`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/rate_limiter/state.ex#L98)

```elixir
@spec get_retry_until(state_key()) :: DateTime.t() | nil
```

Get the current retry_until timestamp for a given key.

Returns `nil` if no retry is needed or the timestamp has passed.

# `init`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/rate_limiter/state.ex#L51)

```elixir
@spec init() :: :ok
```

Initialize the ETS table for state storage.

Called automatically when the RateLimitManager starts, but also
lazily initialized on first access to support direct calls without
the supervisor running.

# `reconcile_reservation`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/rate_limiter/state.ex#L316)

```elixir
@spec reconcile_reservation(state_key(), reservation_ctx(), map() | nil, keyword()) ::
  usage_window()
```

Reconcile a reservation with actual usage, returning surplus or charging shortfall.

# `record_usage`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/rate_limiter/state.ex#L192)

```elixir
@spec record_usage(state_key(), non_neg_integer(), non_neg_integer(), keyword()) ::
  :ok
```

Record token usage in the sliding window.

## Parameters

- `key` - State key tuple
- `input_tokens` - Number of input tokens used
- `output_tokens` - Number of output tokens used
- `opts` - Options including:
  - `:window_duration_ms` - Custom window duration (default: 60_000)

# `release_reservation`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/rate_limiter/state.ex#L345)

```elixir
@spec release_reservation(state_key(), reservation_ctx(), keyword()) :: usage_window()
```

Remove a reservation without adding usage (e.g., when the request never executed).

# `reset_all`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/rate_limiter/state.ex#L353)

```elixir
@spec reset_all() :: :ok
```

Reset all state (useful for testing).

# `set_retry_state`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/rate_limiter/state.ex#L132)

```elixir
@spec set_retry_state(state_key(), map()) :: :ok
```

Update the retry_until state from a 429 response with RetryInfo.

## Parameters

- `key` - State key tuple
- `retry_info` - Map containing retry delay and quota information

## RetryInfo format from Gemini API

    %{
      "retryDelay" => "60s",
      "quotaMetric" => "...",
      "quotaId" => "...",
      "quotaDimensions" => %{...}
    }

# `try_reserve_budget`
[🔗](https://github.com/nshkrdotcom/gemini_ex/blob/v0.11.0/lib/gemini/rate_limiter/state.ex#L244)

```elixir
@spec try_reserve_budget(
  state_key(),
  non_neg_integer(),
  non_neg_integer() | nil,
  keyword()
) ::
  {:ok, reservation_ctx()} | {:error, {:over_budget, map()}}
```

Atomically reserve tokens in the current window.

Returns `{:ok, reservation_ctx}` when the reservation fits, or
`{:error, {:over_budget, details}}` when it would exceed the configured budget.

---

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