1use std::collections::BTreeMap;
2
3use cookie_agent_identity::{ConfiguredModelDefault, VariantId};
4use serde::Serialize;
5
6use crate::{
7 HeaderName, ProviderOptions, SafeStaticHeaderValue,
8 authoring::{ManagedModelOverride, ReasoningBehavior, RequestDefaults, VariantDefinition},
9 catalog::CatalogReasoningOption,
10};
11
12#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
13#[serde(rename_all = "snake_case")]
14pub enum CompiledVariantOrigin {
15 ModelsDevEffort,
16 ModelsDevToggle,
17 ModelsDevBudgetTokens,
18 Authored,
19}
20
21#[derive(Clone, Debug, Serialize)]
22pub struct CompiledVariant {
23 pub id: VariantId,
24 pub model_id: Option<crate::authoring::WireModelId>,
25 pub display_name: String,
26 pub defaults: RequestDefaults,
27 pub options: ProviderOptions,
28 pub reasoning: Option<ReasoningBehavior>,
29 pub headers: BTreeMap<HeaderName, SafeStaticHeaderValue>,
30 pub origin: CompiledVariantOrigin,
31}
32
33type CompiledVariants = (
34 BTreeMap<VariantId, CompiledVariant>,
35 Vec<VariantId>,
36 Option<VariantId>,
37);
38
39#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
40pub(crate) enum VariantCompileError {
41 #[error("invalid_variant")]
42 Invalid,
43 #[error("variant_collision")]
44 Collision,
45 #[error("unknown_default_variant")]
46 Default,
47}
48
49pub(crate) fn managed_variants(
50 source: &[CatalogReasoningOption],
51 override_: Option<&ManagedModelOverride>,
52 defaults: &RequestDefaults,
53 options: &ProviderOptions,
54) -> Result<CompiledVariants, VariantCompileError> {
55 let (mut variants, mut order) = generated(source)?;
56 for variant in variants.values_mut() {
57 variant.defaults = defaults.clone();
58 variant.options = options.clone();
59 }
60 if let Some(override_) = override_ {
61 apply_directives(
62 &mut variants,
63 &mut order,
64 &override_.variants,
65 defaults,
66 options,
67 )?;
68 }
69 let default = resolve_default(
70 override_.and_then(|value| value.default_variant.as_ref()),
71 &variants,
72 )?;
73 Ok((variants, order, default))
74}
75
76pub(crate) fn custom_variants(
77 directives: &BTreeMap<VariantId, VariantDefinition>,
78 default: Option<&ConfiguredModelDefault>,
79 defaults: &RequestDefaults,
80 options: &ProviderOptions,
81) -> Result<CompiledVariants, VariantCompileError> {
82 let mut variants = BTreeMap::new();
83 let mut order = Vec::new();
84 apply_directives(&mut variants, &mut order, directives, defaults, options)?;
85 let default = resolve_default(default, &variants)?;
86 Ok((variants, order, default))
87}
88
89fn generated(
90 source: &[CatalogReasoningOption],
91) -> Result<(BTreeMap<VariantId, CompiledVariant>, Vec<VariantId>), VariantCompileError> {
92 let mut variants = BTreeMap::new();
93 let mut order = Vec::new();
94 for option in source {
95 match option {
96 CatalogReasoningOption::Effort { values } => {
97 for value in values {
98 let (id, reasoning) = if let Some(value) = value.as_deref() {
99 let effort =
100 serde_json::from_value(serde_json::Value::String(value.to_owned()))
101 .map_err(|_| VariantCompileError::Invalid)?;
102 (value, ReasoningBehavior::Effort { value: effort })
103 } else {
104 ("off", ReasoningBehavior::Toggle { enabled: false })
105 };
106 insert_generated(
107 &mut variants,
108 &mut order,
109 id,
110 reasoning,
111 CompiledVariantOrigin::ModelsDevEffort,
112 )?;
113 }
114 }
115 CatalogReasoningOption::Toggle => {
116 for (id, enabled) in [("off", false), ("on", true)] {
117 insert_generated(
118 &mut variants,
119 &mut order,
120 id,
121 ReasoningBehavior::Toggle { enabled },
122 CompiledVariantOrigin::ModelsDevToggle,
123 )?;
124 }
125 }
126 CatalogReasoningOption::BudgetTokens { min, max } => {
127 if let Some(value) = min {
128 insert_generated(
129 &mut variants,
130 &mut order,
131 if *value == -1 {
132 "budget-auto"
133 } else {
134 "budget-min"
135 },
136 ReasoningBehavior::BudgetTokens { value: *value },
137 CompiledVariantOrigin::ModelsDevBudgetTokens,
138 )?;
139 }
140 if let Some(value) = max {
141 insert_generated(
142 &mut variants,
143 &mut order,
144 "budget-max",
145 ReasoningBehavior::BudgetTokens { value: *value },
146 CompiledVariantOrigin::ModelsDevBudgetTokens,
147 )?;
148 }
149 }
150 }
151 }
152 suppress_redundant_generated_toggle_on(&mut variants, &mut order);
153 Ok((variants, order))
154}
155
156fn suppress_redundant_generated_toggle_on(
157 variants: &mut BTreeMap<VariantId, CompiledVariant>,
158 order: &mut Vec<VariantId>,
159) {
160 let has_explicit_reasoning_level = variants.values().any(|variant| {
161 matches!(
162 (variant.origin, variant.reasoning.as_ref()),
163 (
164 CompiledVariantOrigin::ModelsDevEffort,
165 Some(ReasoningBehavior::Effort { .. })
166 ) | (
167 CompiledVariantOrigin::ModelsDevBudgetTokens,
168 Some(ReasoningBehavior::BudgetTokens { .. })
169 )
170 )
171 });
172 if !has_explicit_reasoning_level {
173 return;
174 }
175
176 let toggle_on = variants.iter().find_map(|(id, variant)| {
177 (variant.origin == CompiledVariantOrigin::ModelsDevToggle
178 && matches!(
179 variant.reasoning.as_ref(),
180 Some(ReasoningBehavior::Toggle { enabled: true })
181 ))
182 .then(|| id.clone())
183 });
184 if let Some(id) = toggle_on {
185 variants.remove(&id);
186 order.retain(|candidate| candidate != &id);
187 }
188}
189
190fn insert_generated(
191 variants: &mut BTreeMap<VariantId, CompiledVariant>,
192 order: &mut Vec<VariantId>,
193 id: &str,
194 reasoning: ReasoningBehavior,
195 origin: CompiledVariantOrigin,
196) -> Result<(), VariantCompileError> {
197 let id = VariantId::new(id).map_err(|_| VariantCompileError::Invalid)?;
198 let candidate = CompiledVariant {
199 model_id: None,
200 display_name: display_name(&id),
201 id: id.clone(),
202 defaults: RequestDefaults::default(),
203 options: ProviderOptions::default(),
204 reasoning: Some(reasoning),
205 headers: BTreeMap::new(),
206 origin,
207 };
208 if let Some(existing) = variants.get(&id) {
209 if serde_json::to_value(&existing.reasoning).ok()
210 != serde_json::to_value(&candidate.reasoning).ok()
211 {
212 return Err(VariantCompileError::Collision);
213 }
214 return Ok(());
215 }
216 order.push(id.clone());
217 variants.insert(id, candidate);
218 Ok(())
219}
220
221fn apply_directives(
222 variants: &mut BTreeMap<VariantId, CompiledVariant>,
223 order: &mut Vec<VariantId>,
224 directives: &BTreeMap<VariantId, VariantDefinition>,
225 defaults: &RequestDefaults,
226 options: &ProviderOptions,
227) -> Result<(), VariantCompileError> {
228 for (id, directive) in directives {
229 if directive.enabled == Some(false) {
230 if directive.disabled_has_settings() {
231 return Err(VariantCompileError::Invalid);
232 }
233 variants.remove(id);
234 order.retain(|candidate| candidate != id);
235 continue;
236 }
237 let variant = variants.entry(id.clone()).or_insert_with(|| {
238 order.push(id.clone());
239 CompiledVariant {
240 model_id: None,
241 id: id.clone(),
242 display_name: display_name(id),
243 defaults: defaults.clone(),
244 options: options.clone(),
245 reasoning: None,
246 headers: BTreeMap::new(),
247 origin: CompiledVariantOrigin::Authored,
248 }
249 });
250 if let Some(name) = &directive.display_name {
251 if name.trim().is_empty() || name.len() > 512 || name.chars().any(char::is_control) {
252 return Err(VariantCompileError::Invalid);
253 }
254 variant.display_name.clone_from(name);
255 }
256 if let Some(model_id) = &directive.model_id {
257 variant.model_id = Some(model_id.clone());
258 }
259 if let Some(defaults) = &directive.generation_options {
260 defaults.apply(&mut variant.defaults);
261 }
262 if let Some(options) = &directive.adaptor_options {
263 options.apply(&mut variant.options);
264 }
265 if let Some(reasoning) = &directive.reasoning {
266 variant.reasoning = Some(reasoning.clone());
267 }
268 variant.headers.extend(directive.headers().clone());
270 }
271 Ok(())
272}
273
274fn resolve_default(
275 default: Option<&ConfiguredModelDefault>,
276 variants: &BTreeMap<VariantId, CompiledVariant>,
277) -> Result<Option<VariantId>, VariantCompileError> {
278 match default {
279 None | Some(ConfiguredModelDefault::Base) => Ok(None),
280 Some(ConfiguredModelDefault::Named(id)) if variants.contains_key(id) => {
281 Ok(Some(id.clone()))
282 }
283 Some(ConfiguredModelDefault::Named(_)) => Err(VariantCompileError::Default),
284 }
285}
286
287fn display_name(id: &VariantId) -> String {
288 id.as_str()
289 .split(['-', '_', '.'])
290 .map(|part| {
291 let mut chars = part.chars();
292 chars.next().map_or_else(String::new, |first| {
293 first.to_uppercase().chain(chars).collect()
294 })
295 })
296 .collect::<Vec<_>>()
297 .join(" ")
298}
299
300#[cfg(test)]
301mod tests {
302 use super::*;
303 use crate::authoring::PartialRequestDefaults;
304
305 fn managed_variants(
306 source: &[CatalogReasoningOption],
307 override_: Option<&ManagedModelOverride>,
308 ) -> Result<CompiledVariants, VariantCompileError> {
309 super::managed_variants(
310 source,
311 override_,
312 &RequestDefaults::default(),
313 &ProviderOptions::default(),
314 )
315 }
316
317 fn id(value: &str) -> VariantId {
318 VariantId::new(value).unwrap()
319 }
320
321 fn names(order: &[VariantId]) -> Vec<&str> {
322 order.iter().map(VariantId::as_str).collect()
323 }
324
325 fn override_with(
326 variants: BTreeMap<VariantId, VariantDefinition>,
327 default_variant: Option<ConfiguredModelDefault>,
328 ) -> ManagedModelOverride {
329 ManagedModelOverride {
330 model_id: None,
331 enabled: None,
332 display_name: None,
333 defaults: PartialRequestDefaults::default(),
334 variants,
335 default_variant,
336 options: crate::AdaptorOptions::default(),
337 pricing: None,
338 compaction: crate::NativeCompactionConfig::Unsupported,
339 headers: BTreeMap::new(),
340 }
341 }
342
343 #[test]
344 fn toggle_with_effort_suppresses_generated_on_and_resolves_default() {
345 let source = [
346 CatalogReasoningOption::Toggle,
347 CatalogReasoningOption::Effort {
348 values: vec![Some("low".into()), Some("high".into()), Some("max".into())],
349 },
350 ];
351 let override_ = override_with(
352 BTreeMap::new(),
353 Some(ConfiguredModelDefault::Named(id("high"))),
354 );
355
356 let (variants, order, default) = managed_variants(&source, Some(&override_)).unwrap();
357
358 assert_eq!(names(&order), ["off", "low", "high", "max"]);
359 assert!(!variants.contains_key(&id("on")));
360 assert_eq!(default, Some(id("high")));
361 }
362
363 #[test]
364 fn toggle_only_preserves_generated_on_and_resolves_default() {
365 let override_ = override_with(
366 BTreeMap::new(),
367 Some(ConfiguredModelDefault::Named(id("on"))),
368 );
369
370 let (variants, order, default) =
371 managed_variants(&[CatalogReasoningOption::Toggle], Some(&override_)).unwrap();
372
373 assert_eq!(names(&order), ["off", "on"]);
374 assert!(variants.contains_key(&id("on")));
375 assert_eq!(default, Some(id("on")));
376 }
377
378 #[test]
379 fn effort_only_is_unchanged_and_resolves_default() {
380 let override_ = override_with(
381 BTreeMap::new(),
382 Some(ConfiguredModelDefault::Named(id("high"))),
383 );
384
385 let (variants, order, default) = managed_variants(
386 &[CatalogReasoningOption::Effort {
387 values: vec![Some("low".into()), Some("high".into())],
388 }],
389 Some(&override_),
390 )
391 .unwrap();
392
393 assert_eq!(names(&order), ["low", "high"]);
394 assert_eq!(variants.len(), 2);
395 assert_eq!(default, Some(id("high")));
396 }
397
398 #[test]
399 fn toggle_with_budget_tokens_suppresses_generated_on() {
400 let override_ = override_with(
401 BTreeMap::new(),
402 Some(ConfiguredModelDefault::Named(id("budget-max"))),
403 );
404
405 let (variants, order, default) = managed_variants(
406 &[
407 CatalogReasoningOption::Toggle,
408 CatalogReasoningOption::BudgetTokens {
409 min: Some(1024),
410 max: Some(4096),
411 },
412 ],
413 Some(&override_),
414 )
415 .unwrap();
416
417 assert_eq!(names(&order), ["off", "budget-min", "budget-max"]);
418 assert!(!variants.contains_key(&id("on")));
419 assert_eq!(default, Some(id("budget-max")));
420 }
421
422 #[test]
423 fn managed_override_can_readd_on_after_generation_suppression() {
424 let override_ = override_with(
425 BTreeMap::from([(
426 id("on"),
427 VariantDefinition {
428 reasoning: Some(ReasoningBehavior::Toggle { enabled: true }),
429 ..VariantDefinition::default()
430 },
431 )]),
432 Some(ConfiguredModelDefault::Named(id("on"))),
433 );
434
435 let (variants, order, default) = managed_variants(
436 &[
437 CatalogReasoningOption::Toggle,
438 CatalogReasoningOption::Effort {
439 values: vec![Some("low".into())],
440 },
441 ],
442 Some(&override_),
443 )
444 .unwrap();
445
446 assert_eq!(names(&order), ["off", "low", "on"]);
447 assert_eq!(variants[&id("on")].origin, CompiledVariantOrigin::Authored);
448 assert_eq!(default, Some(id("on")));
449 }
450}