# `Agentic.ModelRouter.Preference`

Defines model selection preferences and the scoring logic for each.

User preferences control how the Selector ranks candidate models:

  * `:optimize_price` — prefer cheaper models; only upgrade when
    the analysis demands it (complex tasks, vision, etc.)
  * `:optimize_speed` — prefer faster models; prioritize throughput
    and low latency, willing to spend more

The preference is combined with an `Analyzer.analysis()` result to
produce a scoring function used by `Selector.rank/3`.

# `preference`

```elixir
@type preference() :: :optimize_price | :optimize_speed
```

# `default`

```elixir
@spec default() :: preference()
```

Return the default preference.

# `parse`

```elixir
@spec parse(term()) :: {:ok, preference()} | {:error, term()}
```

Parse a preference from user input.

# `score`

```elixir
@spec score(
  Agentic.LLM.Model.t(),
  preference(),
  Agentic.ModelRouter.Analyzer.analysis()
) :: float()
```

Compute a score for a model given a preference and analysis.

Lower scores are better. The scoring considers:
- Base cost or speed rating
- Complexity-appropriate tier matching
- Capability matching (vision, reasoning, etc.)
- Penalty for missing required capabilities

# `score_pathway`

```elixir
@spec score_pathway(
  Agentic.LLM.Model.t(),
  Agentic.LLM.ProviderAccount.t(),
  preference()
) :: float()
```

Score a `(model, account)` pathway pair within a canonical group.

Used by `Agentic.ModelRouter` in manual mode after `Catalog.by_canonical/1`
has grouped pathways. Lower is better.

The account contributes three terms on top of the base price/speed
preference:

  * `cost_profile_score/3` — strongly prefers `:free >
    :subscription_included > :subscription_metered > :pay_per_token`
    under `:optimize_price`; mild speed bonus for subscriptions
    under `:optimize_speed`.
  * `Agentic.LLM.ProviderAccount.quota_pressure/1` — taper away from
    a subscription as it approaches its weekly cap (0 at <70%, ramp
    through 90%, cliff above).
  * `availability_score/1` — `:ready` adds 0; `:degraded` +2;
    `{:rate_limited, _}` +8; `:unavailable` is filtered upstream.

Distinct from `score/3` (the analyzer-driven auto-mode scorer) which
takes an `Analyzer.analysis()` instead of an account.

---

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