1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//! The canonical model list (§3, §4): one available model and the one pure, TOTAL
//! resolver that places a seed against the provider's ORDERED cached list. No IO;
//! the list is the single source for "which model" — order is authoritative, there
//! is no separate rank field. The one generic `decode_models`, fed each protocol's
//! `Protocol::models_shape` (§3.1), projects the dialect's list body onto `Vec<Model>`;
//! `select_model` reads it.
use ;
use crate;
/// One available model, the canonical projection of a provider list entry (§3).
/// Ordered position in the returned `Vec` IS the provider's suggested order — the
/// single source the heuristics read, so there is no rank field. `default` is
/// CARRIED, not invented: a dialect that flags one sets it; today none does, so it
/// is `false` and §4's first-in-list rule governs — the seam stays so a provider
/// that DOES flag one needs no code change.
///
/// The three metadata fields are the provider-reported facts a harness would else
/// hand-mirror (model-discovery §3): `context_window` (input token limit),
/// `max_output_tokens` (output limit), `display_name` (human label). Each is
/// OPTION-SHAPED and CARRIED, never fabricated — absent stays `None` (the Usage
/// zero-vs-unknown principle, AGENTS.md): Google serves all three on its list GET,
/// Anthropic only `display_name`, and OpenAI/Ollama none (so those rows stay `None`;
/// the empty-set rule — a harness hand-configures only what no provider serves). The
/// fields are ADDITIVE with `serde(default)` + `skip_serializing_if`: an older cache
/// (id + default only) reads clean to `None`, and a metadata-less model serializes
/// byte-identically to the pre-metadata `{id,default}` shape — the v=1 grows-only
/// discipline, so `--json`/the on-disk cache both extend without a version break.
/// One provider's cache document (§5.1): the ORDERED list plus `last_used`, the local
/// observation of which id this provider last served a 2xx for. Two DIFFERENT facts in
/// one file, never two representations of one: `models` is MEMBERSHIP (which ids this
/// row can serve — read by partial matching and by routing's ownership tier, config §7),
/// `last_used` is RECENCY (which of them to default to on an empty seed, §4 rung 2). The
/// pointer is never a permutation of the list — §5.4's "append, never reorder" stands.
///
/// INVARIANT, held by the one write site (§5.4): after a 2xx, `last_used` names an id
/// that is in `models`. It is nevertheless read FORGIVINGLY — an absent, empty, or
/// dangling `last_used` simply falls through to the provider's own suggestion, never an
/// error (§5.1). ADDITIVE like the metadata keys: `serde(default)` +
/// `skip_serializing_if` means a pre-`last_used` cache reads clean to `None` and a
/// pointer-less document serializes byte-identically to the old `{"models":[…]}` shape,
/// so the grows-only v=1 discipline holds with no cache-version field.
/// What produced the wire id — the provenance the §5.3 404 hint reads (CARRIED, not
/// reconstructed downstream: AGENTS.md). `Cached` is an entry resolved from the list
/// (an exact id, a partial match, or the default); `Verbatim` is the seed passed
/// through because the cache could not place it.
/// Resolve a `seed` against the provider's cached model list (§4) — ONE TOTAL
/// operation for default-selection, partial-matching, and "the cache can't help, try
/// it literally," distinguished only by the seed and whether a match is found (the
/// empty-input dissolve of a special case, AGENTS.md):
/// - `seed == ""` → the default, taken down the §4 ladder: the id `last_used` names
/// if the list still carries it (rung 2 — YOUR observed choice), else the first
/// model flagged `default`, else `models[0]` (rung 3 — the PROVIDER's suggestion),
/// all `Cached`. Rung 2 outranks the flag and the head because config outranks all
/// else BECAUSE IT ENCODES INTENT — and a model you actually reached for is nearer
/// to intent than a list position nobody chose. An EMPTY list here is the lone
/// error — `Config` (78): nothing to send and no list to default from. `provider`
/// names it in that message (carried, not reconstructed — the caller already knows
/// it: AGENTS.md).
/// - `seed != ""` → an exact `id` if present (`Cached`); else the FIRST id (in list
/// order) whose `id` contains the seed case-insensitively (`Cached`, "opus" →
/// "claude-opus-4-…"); else the SEED ITSELF (`Verbatim`) — attempted literally,
/// since the cache cannot place it. A cold cache (empty list) therefore yields
/// `Verbatim` for any non-empty seed: cache-absent ≡ cache-present-but-empty.
///
/// List order is authoritative — the first match is "the suggested version," never an
/// ambiguity error. Exact-before-contains so a full id present in the list resolves to
/// itself rather than a longer id that merely contains it. Verbatim-on-no-match (not an
/// error) self-heals a stale cache: a brand-new full id no list yet carries is tried
/// verbatim and succeeds; a partial typo is tried verbatim, 404s, and the caller runs
/// `bz --list-models` (§5.3).
/// The lone `select_model` failure (§4): `seed == "" && models.is_empty()` — no model
/// given and no cache to default from → `Config` (exit 78), the same family as
/// `NoProvider` (config §7). The caller's next move is in the message,
/// which names `provider` so a multi-provider user knows which cache is cold.