返回 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 #[serde(default, skip_serializing_if = "Option::is_none")]
97 pub reasoning: Option<bool>,
98 #[serde(default, skip_serializing_if = "Option::is_none")]
99 pub display_name: Option<String>,
100 #[serde(default, skip_serializing_if = "Option::is_none")]
101 pub deprecated_at: Option<String>,
102 #[serde(default, skip_serializing_if = "Option::is_none")]
103 pub replacement: Option<String>,
104 #[serde(default, skip_serializing_if = "Option::is_none")]
105 pub note: Option<String>,
106 #[serde(default, skip_serializing_if = "Option::is_none")]
107 pub applies_to: Option<String>,
108 }
109
110 /// Provider default overrides. Only consulted when config.toml sets nothing.
111 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
112 pub struct ProviderDefaultFact {
113 #[serde(default, skip_serializing_if = "Option::is_none")]
114 pub default_model: Option<String>,
115 /// Accepted only when `https` and on the provider's official host family.
116 #[serde(default, skip_serializing_if = "Option::is_none")]
117 pub base_url: Option<String>,
118 #[serde(default, skip_serializing_if = "Option::is_none")]
119 pub applies_to: Option<String>,
120 }
121
122 /// Release truth: what the newest install is and which versions are yanked.
123 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
124 pub struct ReleaseFact {
125 pub latest: String,
126 #[serde(default)]
127 pub yanked: Vec<String>,
128 #[serde(default, skip_serializing_if = "Option::is_none")]
129 pub min_supported: Option<String>,
130 #[serde(default, skip_serializing_if = "Option::is_none")]
131 pub notice: Option<String>,
132 #[serde(default, skip_serializing_if = "Option::is_none")]
133 pub release_url: Option<String>,
134 #[serde(default, skip_serializing_if = "Option::is_none")]
135 pub applies_to: Option<String>,
136 }
137
138 /// Announcement severity. There is deliberately no `Critical`.
139 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
140 #[serde(rename_all = "snake_case")]
141 pub enum AnnouncementLevel {
142 #[default]
143 Info,
144 Warn,
145 }
146
147 /// Where an announcement may be shown.
148 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
149 #[serde(rename_all = "snake_case")]
150 pub enum Surface {
151 Tui,
152 Desktop,
153 Web,
154 }
155
156 /// A one-line banner. Text only; no actions, no executable content.
157 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
158 pub struct Announcement {
159 pub id: String,
160 #[serde(default)]
161 pub level: AnnouncementLevel,
162 pub text: String,
163 #[serde(default, skip_serializing_if = "Option::is_none")]
164 pub url: Option<String>,
165 #[serde(default)]
166 pub surfaces: Vec<Surface>,
167 #[serde(default, skip_serializing_if = "Option::is_none")]
168 pub applies_to: Option<String>,
169 #[serde(default, skip_serializing_if = "Option::is_none")]
170 pub starts_at: Option<String>,
171 #[serde(default, skip_serializing_if = "Option::is_none")]
172 pub expires_at: Option<String>,
173 }
174
175 /// Maximum announcement text length accepted by the client.
176 pub const MAX_ANNOUNCEMENT_CHARS: usize = 200;
177
177 lines RUST