# `Agentic.LLM.ProviderAccount`

Per-user, per-provider account state used by the multi-pathway router.

The same model is `:pay_per_token` for someone with a raw API key and
`:subscription_included` for someone on Pro — so the cost profile lives
here, not on `Agentic.LLM.Model`.

Worth (or any other host) is responsible for resolving these from its
settings storage and pushing them into `ctx.metadata[:provider_accounts]`
before each agent run; the router pulls them from ctx, never from disk.

## Fields

  * `:provider` — atom id of the provider (`:anthropic`,
    `:claude_code`, `:openrouter`, …).
  * `:cost_profile` — `:free | :subscription_included |
    :subscription_metered | :pay_per_token`. Drives the dominant
    term in `Preference.score/4`.
  * `:subscription` — optional `%{plan: String.t(), monthly_fee:
    Money.t()}` describing the subscription. Used by the dashboard
    to amortize cost across actual token usage. `nil` for
    pay-per-token accounts.
  * `:credentials_status` — `:ready | :missing | :expired`. A purely
    informational field; routing reads `:availability`.
  * `:availability` — `:ready | :degraded | {:rate_limited,
    DateTime.t()} | :unavailable`. Hard filter for `:unavailable`;
    continuous penalty for the others.
  * `:quotas` — optional `%{tokens_used: int, tokens_limit: int,
    period_end: DateTime.t()}`. Drives `quota_pressure_score/1` so
    subscriptions taper toward pay-per-token alternatives as the
    cap is approached.
  * `:account_id` — opaque string identifying which configured
    account this is (Worth uses this when multiple keys for the
    same provider are configured). Surfaced on routes so spend
    attribution is unambiguous.

# `availability`

```elixir
@type availability() ::
  :ready | :degraded | :unavailable | {:rate_limited, DateTime.t()}
```

# `cost_profile`

```elixir
@type cost_profile() ::
  :free | :subscription_included | :subscription_metered | :pay_per_token
```

# `credentials_status`

```elixir
@type credentials_status() :: :ready | :missing | :expired
```

# `quotas`

```elixir
@type quotas() :: %{
  tokens_used: non_neg_integer(),
  tokens_limit: non_neg_integer(),
  period_end: DateTime.t()
}
```

# `subscription`

```elixir
@type subscription() :: %{plan: String.t(), monthly_fee: any()}
```

# `t`

```elixir
@type t() :: %Agentic.LLM.ProviderAccount{
  account_id: String.t(),
  availability: availability(),
  cost_profile: cost_profile(),
  credentials_status: credentials_status(),
  provider: atom(),
  quotas: quotas() | nil,
  subscription: subscription() | nil
}
```

# `default`

```elixir
@spec default(atom()) :: t()
```

Build a sensible default account for `provider` — used in tests and as
a fallback when the host did not supply an account in `ctx.metadata`.

Defaults to `:pay_per_token` + `:ready`, which is the right behaviour
for someone who pasted in a regular API key and hasn't told us
anything more.

# `for_provider`

```elixir
@spec for_provider([t()] | nil, atom()) :: t()
```

Look up the account for `provider` in a list of `ProviderAccount` structs,
falling back to `default/1`. Used by the router to find the matching
account for each pathway.

# `quota_pressure`

```elixir
@spec quota_pressure(t()) :: float()
```

Compute the [0.0..1.0]+ pressure that quota usage adds to scoring.
Returns 0.0 when there is plenty of headroom; ramps after 70% of the
cap; hard cliff after 90%. Designed so subscriptions taper toward
alternative pathways as the weekly cap is approached, rather than
hard-failing mid-session.

---

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