返回 CodeWhale
types.rs
根目录 / crates / config / src / cloud_facts / types.rs
1 //! Signed payload shape for cloud facts (`schema_version` 1).
2 //!
3 //! Every section is optional and defaulted so a payload that only carries
4 //! release truth still parses. Unknown fields are preserved in `unknown` so
5 //! `/status` debugging can show what a newer server sent, but they are never
6 //! acted on.
7
8 use std::collections::BTreeMap;
9
10 use serde::{Deserialize, Serialize};
11 use serde_json::Value;
12
13 fn default_applies_to() -> String {
14 "*".to_string()
15 }
16
17 /// The signed facts payload.
18 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
19 pub struct CloudFacts {
20 pub schema_version: u32,
21 pub channel: String,
22 pub facts_version: u64,
23 #[serde(default)]
24 pub published_at: String,
25 /// RFC 3339 UTC timestamp after which the facts are considered stale.
26 #[serde(default, skip_serializing_if = "Option::is_none")]
27 pub not_after: Option<String>,
28 /// Cargo-style semver requirement the whole payload applies to.
29 #[serde(default = "default_applies_to")]
30 pub applies_to: String,
31 #[serde(default)]
32 pub models: Vec<ModelFact>,
33 #[serde(default)]
34 pub provider_defaults: BTreeMap<String, ProviderDefaultFact>,
35 #[serde(default)]
36 pub release: Option<ReleaseFact>,
37 #[serde(default)]
38 pub announcements: Vec<Announcement>,
39 #[serde(flatten)]
40 pub unknown: BTreeMap<String, Value>,
41 }
42
43 /// What a model patch does to the catalog row it names.
44 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
45 #[serde(rename_all = "snake_case")]
46 pub enum ModelOp {
47 /// Patch the fields that are set; create the row when enough is known.
48 #[default]
49 Upsert,
50 /// Annotate as deprecated; never removes the row.
51 Deprecate,
52 /// Remove the row, but only when it came from the bundled/models.dev layers.
53 Hide,
54 }
55
56 /// Per-million-token pricing patch.
57 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
58 pub struct PricingFact {
59 #[serde(default, skip_serializing_if = "Option::is_none")]
60 pub input_per_m: Option<f64>,
61 #[serde(default, skip_serializing_if = "Option::is_none")]
62 pub output_per_m: Option<f64>,
63 #[serde(default, skip_serializing_if = "Option::is_none")]
64 pub cache_read_per_m: Option<f64>,
65 }
66
67 fn is_false(value: &bool) -> bool {
68 !*value
69 }
70
71 /// A field-level patch to one `(provider, wire id)` catalog row.
72 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
73 pub struct ModelFact {
74 pub provider: String,
75 pub id: String,
76 #[serde(default)]
77 pub op: ModelOp,
78 /// Signed assertion that this exact id is available on the provider's
79 /// official endpoint even when the provider's own `/v1/models` roster does
80 /// not list it.
81 ///
82 /// Absent/false (the default, and what every older client sees) means the
83 /// roster stays authoritative for its own omissions. The assertion is only
84 /// honored on an `Upsert` inside a payload that carries `not_after`, so it
85 /// always expires on its own; see [`super::scope::scoped_view`]. It grants
86 /// nothing else: identity, endpoint, account-entitlement and user
87 /// precedence rules are unchanged.
88 #[serde(default, skip_serializing_if = "is_false")]
89 pub allow_unlisted: bool,
90 #[serde(default, skip_serializing_if = "Option::is_none")]
91 pub context_window: Option<u64>,
92 #[serde(default, skip_serializing_if = "Option::is_none")]
93 pub max_output: Option<u64>,
94 #[serde(default, skip_serializing_if = "Option::is_none")]
95 pub pricing: Option<PricingFact>,
96 /// Why no price should be shown for this row. When set, the row's price
97 /// is cleared (and `pricing` is ignored), so the route reports an unknown
98 /// price instead of a flat rate that would mislead: a tiered rate, a plan
99 /// that bills quota rather than tokens, or keys whose billing surface the
100 /// catalog cannot tell apart.
101 #[serde(default, skip_serializing_if = "Option::is_none")]
102 pub pricing_withheld: Option<String>,
103 #[serde(default, skip_serializing_if = "Option::is_none")]
104 pub reasoning: Option<bool>,
105 /// Replacement reasoning controls in the Models.dev `reasoning_options`
106 /// shape (effort ladders, thinking modes, defaults), for a row whose
107 /// upstream controls do not match what the route accepts.
108 #[serde(default, skip_serializing_if = "Option::is_none")]
109 pub reasoning_options: Option<Vec<Value>>,
110 #[serde(default, skip_serializing_if = "Option::is_none")]
111 pub display_name: Option<String>,
112 #[serde(default, skip_serializing_if = "Option::is_none")]
113 pub deprecated_at: Option<String>,
114 #[serde(default, skip_serializing_if = "Option::is_none")]
115 pub replacement: Option<String>,
116 #[serde(default, skip_serializing_if = "Option::is_none")]
117 pub note: Option<String>,
118 #[serde(default, skip_serializing_if = "Option::is_none")]
119 pub applies_to: Option<String>,
120 }
121
122 /// Provider default overrides. Only consulted when config.toml sets nothing.
123 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
124 pub struct ProviderDefaultFact {
125 #[serde(default, skip_serializing_if = "Option::is_none")]
126 pub default_model: Option<String>,
127 /// Accepted only when `https` and on the provider's official host family.
128 #[serde(default, skip_serializing_if = "Option::is_none")]
129 pub base_url: Option<String>,
130 #[serde(default, skip_serializing_if = "Option::is_none")]
131 pub applies_to: Option<String>,
132 }
133
134 /// Release truth: what the newest install is and which versions are yanked.
135 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
136 pub struct ReleaseFact {
137 pub latest: String,
138 #[serde(default)]
139 pub yanked: Vec<String>,
140 #[serde(default, skip_serializing_if = "Option::is_none")]
141 pub min_supported: Option<String>,
142 #[serde(default, skip_serializing_if = "Option::is_none")]
143 pub notice: Option<String>,
144 #[serde(default, skip_serializing_if = "Option::is_none")]
145 pub release_url: Option<String>,
146 #[serde(default, skip_serializing_if = "Option::is_none")]
147 pub applies_to: Option<String>,
148 }
149
150 /// Announcement severity. There is deliberately no `Critical`.
151 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
152 #[serde(rename_all = "snake_case")]
153 pub enum AnnouncementLevel {
154 #[default]
155 Info,
156 Warn,
157 }
158
159 /// Where an announcement may be shown.
160 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
161 #[serde(rename_all = "snake_case")]
162 pub enum Surface {
163 Tui,
164 Desktop,
165 Web,
166 }
167
168 /// A one-line banner. Text only; no actions, no executable content.
169 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
170 pub struct Announcement {
171 pub id: String,
172 #[serde(default)]
173 pub level: AnnouncementLevel,
174 pub text: String,
175 #[serde(default, skip_serializing_if = "Option::is_none")]
176 pub url: Option<String>,
177 #[serde(default)]
178 pub surfaces: Vec<Surface>,
179 #[serde(default, skip_serializing_if = "Option::is_none")]
180 pub applies_to: Option<String>,
181 #[serde(default, skip_serializing_if = "Option::is_none")]
182 pub starts_at: Option<String>,
183 #[serde(default, skip_serializing_if = "Option::is_none")]
184 pub expires_at: Option<String>,
185 }
186
187 /// Maximum announcement text length accepted by the client.
188 pub const MAX_ANNOUNCEMENT_CHARS: usize = 200;
189
189 lines RUST