Skip to main content

mesh_llm_config/model/
built_in_schema.rs

1use super::*;
2mod control_behavior;
3mod presentation;
4use self::control_behavior::apply_built_in_control_behavior;
5use self::presentation::apply_built_in_presentation_metadata;
6use std::sync::OnceLock;
7
8#[derive(Clone, Debug, PartialEq, Eq)]
9pub struct BuiltInConfigPathResolution {
10    pub requested_path: ConfigPath,
11    pub normalized_path: ConfigPath,
12    pub canonical_path: ConfigPath,
13    pub matched_alias: Option<ConfigPath>,
14    pub support: ConfigSupportState,
15}
16
17impl BuiltInConfigPathResolution {
18    pub fn canonical_identifier(&self) -> String {
19        self.canonical_path.render()
20    }
21
22    pub fn used_legacy_alias(&self) -> bool {
23        self.matched_alias.is_some()
24    }
25}
26
27pub fn built_in_config_settings() -> Vec<ConfigSettingSchema> {
28    built_in_config_schema_cache().settings.clone()
29}
30
31pub fn built_in_config_schema_descriptor(path: &ConfigPath) -> Option<ConfigSettingSchema> {
32    let normalized = path.normalize_builtin_layout();
33    built_in_config_schema_cache()
34        .settings
35        .iter()
36        .find(|setting| setting.path == normalized)
37        .cloned()
38}
39
40pub fn resolve_built_in_config_path(path: &ConfigPath) -> Option<BuiltInConfigPathResolution> {
41    let requested_path = path.clone();
42    let normalized_path = path.normalize_builtin_layout();
43
44    for setting in &built_in_config_schema_cache().settings {
45        if setting.path == normalized_path {
46            return Some(BuiltInConfigPathResolution {
47                requested_path,
48                normalized_path,
49                canonical_path: setting.path.clone(),
50                matched_alias: None,
51                support: setting.support,
52            });
53        }
54        if let Some(alias) = setting
55            .alias_policy
56            .aliases
57            .iter()
58            .find(|alias| alias.path == normalized_path)
59        {
60            return Some(BuiltInConfigPathResolution {
61                requested_path,
62                normalized_path,
63                canonical_path: setting.path.clone(),
64                matched_alias: Some(alias.path.clone()),
65                support: setting.support,
66            });
67        }
68    }
69
70    None
71}
72
73pub fn resolve_built_in_config_identifier(rendered: &str) -> Option<BuiltInConfigPathResolution> {
74    let parsed = ConfigPath::parse_rendered(rendered).ok()?;
75    resolve_built_in_config_path(&parsed)
76}
77
78pub fn canonicalize_built_in_config_path(path: &ConfigPath) -> Option<ConfigPath> {
79    resolve_built_in_config_path(path).map(|resolution| resolution.canonical_path)
80}
81
82pub fn canonicalize_built_in_config_identifier(rendered: &str) -> Option<String> {
83    resolve_built_in_config_identifier(rendered).map(|resolution| resolution.canonical_identifier())
84}
85
86fn built_in_config_schema_cache() -> &'static ConfigSchema {
87    static SCHEMA: OnceLock<ConfigSchema> = OnceLock::new();
88    SCHEMA.get_or_init(build_built_in_config_schema)
89}
90
91fn build_built_in_config_schema() -> ConfigSchema {
92    let mut settings = vec![
93        top_level_setting("version", ConfigValueSchema::Integer),
94        top_level_setting("gpu.assignment", string_enum(["auto", "pinned"])),
95        top_level_setting("gpu.parallel", ConfigValueSchema::Integer),
96        top_level_setting(
97            "mesh_requirements.min_node_version",
98            string_enum_from_slice(known_mesh_llm_versions()),
99        ),
100        top_level_setting(
101            "mesh_requirements.max_node_version",
102            string_enum_from_slice(known_mesh_llm_versions()),
103        ),
104        top_level_setting(
105            "mesh_requirements.min_protocol_version",
106            ConfigValueSchema::Integer,
107        ),
108        top_level_setting(
109            "mesh_requirements.max_protocol_version",
110            ConfigValueSchema::Integer,
111        ),
112        top_level_setting(
113            "mesh_requirements.require_release_attestation",
114            ConfigValueSchema::Boolean,
115        ),
116        top_level_setting(
117            "mesh_requirements.release_signer_keys",
118            ConfigValueSchema::Array {
119                items: Box::new(ConfigValueSchema::String),
120            },
121        ),
122        owner_control_setting("owner_control.bind", ConfigValueSchema::SocketAddr),
123        owner_control_setting(
124            "owner_control.advertise_addr",
125            ConfigValueSchema::SocketAddr,
126        ),
127        telemetry_setting("telemetry.enabled", ConfigValueSchema::Boolean),
128        telemetry_setting("telemetry.service_name", ConfigValueSchema::String),
129        telemetry_setting("telemetry.endpoint", ConfigValueSchema::Url),
130        telemetry_setting("telemetry.headers", ConfigValueSchema::Object),
131        telemetry_setting("telemetry.export_interval_secs", ConfigValueSchema::Integer),
132        telemetry_setting("telemetry.queue_size", ConfigValueSchema::Integer),
133        unsupported_setting(
134            "telemetry.prompt_shape_metrics",
135            ConfigValueSchema::Boolean,
136            "Prompt-shape telemetry is intentionally disabled until the telemetry surface is reviewed.",
137        ),
138        telemetry_setting("telemetry.metrics.endpoint", ConfigValueSchema::Url),
139        startup_runtime_setting("runtime.debug", ConfigValueSchema::Boolean),
140        startup_runtime_setting("runtime.listen_all", ConfigValueSchema::Boolean),
141        startup_runtime_setting(
142            "runtime.mode",
143            string_enum(["client", "serve", "on_demand"]),
144        ),
145        startup_runtime_setting(
146            "runtime.startup_failure_policy",
147            string_enum(["best_effort", "fail_fast"]),
148        ),
149        runtime_setting("runtime.drain_timeout_secs", ConfigValueSchema::Integer),
150        runtime_setting("runtime.drain_timeout_max_secs", ConfigValueSchema::Integer),
151        activity_runtime_setting("runtime.activity.enabled", ConfigValueSchema::Boolean),
152        activity_runtime_setting(
153            "runtime.activity.idle_after_secs",
154            ConfigValueSchema::Integer,
155        ),
156        activity_runtime_setting(
157            "runtime.activity.poll_interval_secs",
158            ConfigValueSchema::Integer,
159        ),
160        activity_runtime_setting(
161            "runtime.activity.resume_debounce_secs",
162            ConfigValueSchema::Integer,
163        ),
164        activity_runtime_setting(
165            "runtime.activity.response",
166            string_enum(["pause_remote", "pause_all", "reduce_priority"]),
167        ),
168        activity_runtime_setting(
169            "runtime.activity.advertisement",
170            string_enum([
171                "none",
172                "availability_only",
173                "coarse_state",
174                "private_coarse_state",
175            ]),
176        ),
177        runtime_setting(
178            "runtime.reconcile_model_targets",
179            ConfigValueSchema::Boolean,
180        ),
181        runtime_setting(
182            "runtime.reconcile_model_target_demand_upgrades",
183            ConfigValueSchema::Boolean,
184        ),
185        native_runtime_setting(
186            "runtime.native_runtime.mesh_version",
187            ConfigValueSchema::String,
188        ),
189        native_runtime_setting(
190            "runtime.native_runtime.skippy_abi",
191            ConfigValueSchema::String,
192        ),
193        native_runtime_setting(
194            "runtime.native_runtime.selection",
195            ConfigValueSchema::String,
196        ),
197        runtime_setting(
198            "runtime.model_target_demand_upgrade_min_requests",
199            ConfigValueSchema::Integer,
200        ),
201        runtime_setting(
202            "runtime.model_target_demand_upgrade_max_age_secs",
203            ConfigValueSchema::Integer,
204        ),
205    ];
206
207    settings.extend(model_defaults_settings());
208    settings.extend(model_entry_settings());
209    settings.extend(plugin_entry_settings());
210    settings
211        .iter_mut()
212        .for_each(apply_built_in_control_behavior);
213    settings
214        .iter_mut()
215        .for_each(apply_built_in_presentation_metadata);
216
217    ConfigSchema { settings }
218}
219
220fn model_defaults_settings() -> Vec<ConfigSettingSchema> {
221    let mut settings = Vec::new();
222    settings.extend(model_fit_settings(
223        "defaults.model_fit",
224        &[
225            flat_alias("defaults.ctx_size"),
226            flat_alias("defaults.batch"),
227            flat_alias("defaults.ubatch"),
228            flat_alias("defaults.cache_type_k"),
229            flat_alias("defaults.cache_type_v"),
230            flat_alias("defaults.flash_attention"),
231        ],
232    ));
233    settings.extend(hardware_settings(
234        "defaults.hardware",
235        &[flat_alias("defaults.gpu_id")],
236    ));
237    settings.extend(throughput_settings(
238        "defaults.throughput",
239        &[flat_alias("defaults.parallel")],
240    ));
241    settings.extend(skippy_settings("defaults.skippy"));
242    settings.extend(speculative_settings("defaults.speculative"));
243    settings.extend(request_defaults_settings("defaults.request_defaults"));
244    settings.extend(multimodal_settings(
245        "defaults.multimodal",
246        &[flat_alias("defaults.mmproj")],
247    ));
248    settings.extend(advanced_settings("defaults.advanced"));
249    settings
250}
251
252fn model_entry_settings() -> Vec<ConfigSettingSchema> {
253    let model_prefix = format!("models.{CANONICAL_MODEL_REF_SEGMENT}");
254    let mut settings = vec![basic_setting(
255        &format!("{model_prefix}.model"),
256        ConfigValueSchema::String,
257    )];
258    settings.extend(model_fit_settings(
259        &format!("{model_prefix}.model_fit"),
260        &[
261            flat_alias(&format!("{model_prefix}.ctx_size")),
262            flat_alias(&format!("{model_prefix}.batch")),
263            flat_alias(&format!("{model_prefix}.ubatch")),
264            flat_alias(&format!("{model_prefix}.cache_type_k")),
265            flat_alias(&format!("{model_prefix}.cache_type_v")),
266            flat_alias(&format!("{model_prefix}.flash_attention")),
267        ],
268    ));
269    settings.extend(hardware_settings(
270        &format!("{model_prefix}.hardware"),
271        &[flat_alias(&format!("{model_prefix}.gpu_id"))],
272    ));
273    settings.extend(throughput_settings(
274        &format!("{model_prefix}.throughput"),
275        &[flat_alias(&format!("{model_prefix}.parallel"))],
276    ));
277    settings.extend(skippy_settings(&format!("{model_prefix}.skippy")));
278    settings.extend(speculative_settings(&format!("{model_prefix}.speculative")));
279    settings.extend(request_defaults_settings(&format!(
280        "{model_prefix}.request_defaults"
281    )));
282    settings.extend(multimodal_settings(
283        &format!("{model_prefix}.multimodal"),
284        &[flat_alias(&format!("{model_prefix}.mmproj"))],
285    ));
286    settings.extend(advanced_settings(&format!("{model_prefix}.advanced")));
287    settings
288}
289
290fn plugin_entry_settings() -> Vec<ConfigSettingSchema> {
291    let plugin_prefix = format!("plugin.{CANONICAL_PLUGIN_NAME_SEGMENT}");
292    vec![
293        plugin_setting(&format!("{plugin_prefix}.name"), ConfigValueSchema::String),
294        plugin_setting(
295            &format!("{plugin_prefix}.enabled"),
296            ConfigValueSchema::Boolean,
297        ),
298        plugin_setting(
299            &format!("{plugin_prefix}.web_ui_enabled"),
300            ConfigValueSchema::Boolean,
301        ),
302        plugin_setting(
303            &format!("{plugin_prefix}.command"),
304            ConfigValueSchema::String,
305        ),
306        plugin_setting(
307            &format!("{plugin_prefix}.args"),
308            ConfigValueSchema::Array {
309                items: Box::new(ConfigValueSchema::String),
310            },
311        ),
312        plugin_setting(&format!("{plugin_prefix}.url"), ConfigValueSchema::Url),
313        plugin_setting(
314            &format!("{plugin_prefix}.startup.connect_timeout_secs"),
315            ConfigValueSchema::Integer,
316        ),
317        plugin_setting(
318            &format!("{plugin_prefix}.startup.init_timeout_secs"),
319            ConfigValueSchema::Integer,
320        ),
321        plugin_setting(
322            &format!("{plugin_prefix}.startup.optional"),
323            ConfigValueSchema::Boolean,
324        ),
325        plugin_setting(
326            &format!("{plugin_prefix}.startup.lazy_start"),
327            ConfigValueSchema::Boolean,
328        ),
329    ]
330}
331
332fn model_fit_settings(
333    prefix: &str,
334    legacy_aliases: &[ConfigPathAlias],
335) -> Vec<ConfigSettingSchema> {
336    let mut settings = vec![
337        basic_setting(&format!("{prefix}.ctx_size"), ConfigValueSchema::Integer),
338        basic_setting(&format!("{prefix}.batch"), ConfigValueSchema::Integer),
339        basic_setting(&format!("{prefix}.ubatch"), ConfigValueSchema::Integer),
340        basic_setting(&format!("{prefix}.cache_type_k"), kv_cache_type_schema()),
341        basic_setting(&format!("{prefix}.cache_type_v"), kv_cache_type_schema()),
342        basic_setting(
343            &format!("{prefix}.kv_cache_policy"),
344            string_enum(["auto", "quality", "balanced", "saver"]),
345        ),
346        basic_setting(&format!("{prefix}.kv_offload"), bool_or_auto_schema()),
347        basic_setting(&format!("{prefix}.kv_unified"), bool_or_auto_schema()),
348        basic_setting(
349            &format!("{prefix}.cache_ram_mib"),
350            ConfigValueSchema::Integer,
351        ),
352        basic_setting(
353            &format!("{prefix}.cache_idle_slots"),
354            ConfigValueSchema::Integer,
355        ),
356        basic_setting(&format!("{prefix}.prompt_cache"), bool_or_auto_schema()),
357        basic_setting(
358            &format!("{prefix}.prefix_cache.enabled"),
359            ConfigValueSchema::Boolean,
360        ),
361        basic_setting(
362            &format!("{prefix}.prefix_cache.max_entries"),
363            ConfigValueSchema::Integer,
364        ),
365        basic_setting(
366            &format!("{prefix}.prefix_cache.max_bytes"),
367            ConfigValueSchema::Integer,
368        ),
369        basic_setting(
370            &format!("{prefix}.prefix_cache.min_tokens"),
371            ConfigValueSchema::Integer,
372        ),
373        basic_setting(
374            &format!("{prefix}.prefix_cache.shared_stride_tokens"),
375            ConfigValueSchema::Integer,
376        ),
377        basic_setting(
378            &format!("{prefix}.prefix_cache.shared_record_limit"),
379            ConfigValueSchema::Integer,
380        ),
381        basic_setting(
382            &format!("{prefix}.prefix_cache.payload_mode"),
383            string_enum(["resident-kv", "kv-recurrent", "full-state", "auto"]),
384        ),
385        basic_setting(&format!("{prefix}.keep_tokens"), ConfigValueSchema::Integer),
386        basic_setting(&format!("{prefix}.context_shift"), bool_or_auto_schema()),
387        basic_setting(&format!("{prefix}.swa_full"), ConfigValueSchema::Boolean),
388        basic_setting(
389            &format!("{prefix}.checkpoint_interval"),
390            ConfigValueSchema::Integer,
391        ),
392        basic_setting(
393            &format!("{prefix}.checkpoint_count"),
394            ConfigValueSchema::Integer,
395        ),
396        basic_setting(
397            &format!("{prefix}.lookup_cache_static"),
398            ConfigValueSchema::String,
399        ),
400        basic_setting(
401            &format!("{prefix}.lookup_cache_dynamic"),
402            ConfigValueSchema::String,
403        ),
404        basic_setting(
405            &format!("{prefix}.flash_attention"),
406            string_enum(["auto", "disabled", "enabled"]),
407        ),
408    ];
409
410    if !legacy_aliases.is_empty() {
411        apply_aliases(
412            &mut settings,
413            &format!("{prefix}.ctx_size"),
414            &legacy_aliases[0..1],
415        );
416        apply_aliases(
417            &mut settings,
418            &format!("{prefix}.batch"),
419            &legacy_aliases[1..2],
420        );
421        apply_aliases(
422            &mut settings,
423            &format!("{prefix}.ubatch"),
424            &legacy_aliases[2..3],
425        );
426        apply_aliases(
427            &mut settings,
428            &format!("{prefix}.cache_type_k"),
429            &legacy_aliases[3..4],
430        );
431        apply_aliases(
432            &mut settings,
433            &format!("{prefix}.cache_type_v"),
434            &legacy_aliases[4..5],
435        );
436        apply_aliases(
437            &mut settings,
438            &format!("{prefix}.flash_attention"),
439            &legacy_aliases[5..6],
440        );
441    }
442
443    settings
444}
445
446fn hardware_settings(
447    prefix: &str,
448    legacy_device_aliases: &[ConfigPathAlias],
449) -> Vec<ConfigSettingSchema> {
450    let mut settings = vec![
451        hidden_setting(
452            &format!("{prefix}.model_runtime"),
453            string_enum(["auto", "cpu", "cuda", "rocm", "metal", "vulkan"]),
454            "Model runtime is selected by the installed native runtime and hardware resolver, not by the web configuration UI.",
455        ),
456        basic_setting(&format!("{prefix}.device"), ConfigValueSchema::String),
457        basic_setting(&format!("{prefix}.gpu_layers"), integer_or_auto_schema()),
458        basic_setting(
459            &format!("{prefix}.stage_layer_start"),
460            ConfigValueSchema::Integer,
461        ),
462        basic_setting(
463            &format!("{prefix}.stage_layer_end"),
464            ConfigValueSchema::Integer,
465        ),
466        basic_setting(
467            &format!("{prefix}.placement"),
468            string_enum(["auto", "pooled", "separated"]),
469        ),
470        basic_setting(&format!("{prefix}.tensor_split"), tensor_split_schema()),
471        basic_setting(
472            &format!("{prefix}.split_mode"),
473            string_enum(["auto", "none", "layer", "row"]),
474        ),
475        basic_setting(&format!("{prefix}.main_gpu"), ConfigValueSchema::Integer),
476        basic_setting(&format!("{prefix}.cpu_moe"), bool_or_auto_schema()),
477        basic_setting(&format!("{prefix}.n_cpu_moe"), ConfigValueSchema::Integer),
478        rejected_setting(
479            &format!("{prefix}.rpc_backend"),
480            ConfigValueSchema::Object,
481            "The legacy rpc_backend escape hatch is explicitly unsupported by the embedded runtime.",
482        ),
483        basic_setting(
484            &format!("{prefix}.fit_target_mib"),
485            ConfigValueSchema::Integer,
486        ),
487        basic_setting(
488            &format!("{prefix}.safety_margin_gb"),
489            ConfigValueSchema::Float,
490        ),
491        basic_setting(&format!("{prefix}.fit_context"), bool_or_auto_schema()),
492        basic_setting(&format!("{prefix}.model_path"), ConfigValueSchema::Path),
493        basic_setting(&format!("{prefix}.hf_repo"), ConfigValueSchema::String),
494        basic_setting(&format!("{prefix}.hf_file"), ConfigValueSchema::String),
495        basic_setting(&format!("{prefix}.mmproj"), ConfigValueSchema::Path),
496        basic_setting(&format!("{prefix}.mmproj_offload"), bool_or_auto_schema()),
497        basic_setting(
498            &format!("{prefix}.lora_adapters"),
499            ConfigValueSchema::Array {
500                items: Box::new(ConfigValueSchema::String),
501            },
502        ),
503        basic_setting(
504            &format!("{prefix}.control_vectors"),
505            ConfigValueSchema::Array {
506                items: Box::new(ConfigValueSchema::String),
507            },
508        ),
509        basic_setting(
510            &format!("{prefix}.check_tensors"),
511            ConfigValueSchema::Boolean,
512        ),
513        basic_setting(&format!("{prefix}.mmap"), bool_or_auto_schema()),
514        basic_setting(&format!("{prefix}.mlock"), ConfigValueSchema::Boolean),
515        basic_setting(&format!("{prefix}.direct_io"), ConfigValueSchema::Boolean),
516        basic_setting(&format!("{prefix}.repack"), ConfigValueSchema::Boolean),
517        basic_setting(&format!("{prefix}.op_offload"), ConfigValueSchema::Boolean),
518        basic_setting(
519            &format!("{prefix}.no_host_buffer"),
520            ConfigValueSchema::Boolean,
521        ),
522        basic_setting(&format!("{prefix}.warmup"), bool_or_auto_schema()),
523    ];
524
525    if !legacy_device_aliases.is_empty() {
526        apply_aliases(
527            &mut settings,
528            &format!("{prefix}.device"),
529            legacy_device_aliases,
530        );
531    }
532
533    settings
534}
535
536fn throughput_settings(
537    prefix: &str,
538    legacy_parallel_aliases: &[ConfigPathAlias],
539) -> Vec<ConfigSettingSchema> {
540    let mut settings = vec![
541        basic_setting(&format!("{prefix}.parallel"), ConfigValueSchema::Integer),
542        basic_setting(
543            &format!("{prefix}.continuous_batching"),
544            bool_or_auto_schema(),
545        ),
546        basic_setting(&format!("{prefix}.threads"), ConfigValueSchema::Integer),
547        basic_setting(
548            &format!("{prefix}.threads_batch"),
549            ConfigValueSchema::Integer,
550        ),
551        rejected_setting(
552            &format!("{prefix}.threads_http"),
553            ConfigValueSchema::Integer,
554            "Dedicated HTTP worker tuning is rejected on the current embedded runtime path.",
555        ),
556        basic_setting(&format!("{prefix}.priority"), integer_or_string_schema()),
557        basic_setting(
558            &format!("{prefix}.poll"),
559            bool_or_string_enum(["auto", "busy", "sleep"]),
560        ),
561        basic_setting(&format!("{prefix}.cpu_affinity"), string_or_list_schema()),
562        basic_setting(&format!("{prefix}.numa"), ConfigValueSchema::String),
563        basic_setting(
564            &format!("{prefix}.slot_prompt_similarity"),
565            ConfigValueSchema::Float,
566        ),
567        rejected_setting(
568            &format!("{prefix}.sleep_idle_seconds"),
569            ConfigValueSchema::Integer,
570            "The sleep-idle tuning knob is documented as rejected and must never become a live exported identifier.",
571        ),
572        basic_setting(
573            &format!("{prefix}.tuning_profile"),
574            string_enum(["throughput", "balanced", "saver"]),
575        ),
576    ];
577
578    if !legacy_parallel_aliases.is_empty() {
579        apply_aliases(
580            &mut settings,
581            &format!("{prefix}.parallel"),
582            legacy_parallel_aliases,
583        );
584    }
585
586    settings
587}
588
589fn skippy_settings(prefix: &str) -> Vec<ConfigSettingSchema> {
590    vec![
591        basic_setting(
592            &format!("{prefix}.stage_model_path"),
593            ConfigValueSchema::Path,
594        ),
595        basic_setting(&format!("{prefix}.stage_role"), ConfigValueSchema::String),
596        basic_setting(
597            &format!("{prefix}.stage_topology"),
598            ConfigValueSchema::String,
599        ),
600        basic_setting(
601            &format!("{prefix}.activation_wire_dtype"),
602            string_enum(["auto", "f16", "f32", "q8"]),
603        ),
604        basic_setting(
605            &format!("{prefix}.binary_stage_transport"),
606            ConfigValueSchema::String,
607        ),
608        rejected_setting(
609            &format!("{prefix}.openai_frontend_mode"),
610            ConfigValueSchema::Object,
611            "OpenAI frontend override wiring is intentionally rejected on the built-in schema surface.",
612        ),
613        basic_setting(
614            &format!("{prefix}.lifecycle_startup_timeout_ms"),
615            ConfigValueSchema::Integer,
616        ),
617        basic_setting(
618            &format!("{prefix}.lifecycle_readiness_interval_ms"),
619            ConfigValueSchema::Integer,
620        ),
621        basic_setting(
622            &format!("{prefix}.lifecycle_health_interval_ms"),
623            ConfigValueSchema::Integer,
624        ),
625        basic_setting(
626            &format!("{prefix}.prefill_chunking"),
627            string_enum(["auto", "fixed", "schedule", "adaptive-ramp"]),
628        ),
629        basic_setting(
630            &format!("{prefix}.prefill_chunk_size"),
631            ConfigValueSchema::Integer,
632        ),
633        basic_setting(
634            &format!("{prefix}.prefill_chunk_schedule"),
635            ConfigValueSchema::String,
636        ),
637    ]
638}
639
640fn speculative_settings(prefix: &str) -> Vec<ConfigSettingSchema> {
641    vec![
642        basic_setting(&format!("{prefix}.strategy"), ConfigValueSchema::String),
643        basic_setting(
644            &format!("{prefix}.mode"),
645            string_enum(["auto", "disabled", "draft"]),
646        ),
647        basic_setting(&format!("{prefix}.draft_model"), ConfigValueSchema::Path),
648        basic_setting(
649            &format!("{prefix}.draft_hf_repo"),
650            ConfigValueSchema::String,
651        ),
652        basic_setting(
653            &format!("{prefix}.draft_hf_file"),
654            ConfigValueSchema::String,
655        ),
656        basic_setting(
657            &format!("{prefix}.draft_selection_policy"),
658            string_enum(["manual", "auto"]),
659        ),
660        basic_setting(
661            &format!("{prefix}.pairing_fault"),
662            string_enum([
663                "warn_disable",
664                "fail-open",
665                "fail-closed",
666                "fail_open",
667                "fail_closed",
668            ]),
669        ),
670        basic_setting(
671            &format!("{prefix}.draft_max_tokens"),
672            ConfigValueSchema::Integer,
673        ),
674        basic_setting(
675            &format!("{prefix}.draft_min_tokens"),
676            ConfigValueSchema::Integer,
677        ),
678        basic_setting(
679            &format!("{prefix}.draft_acceptance_threshold"),
680            ConfigValueSchema::Float,
681        ),
682        basic_setting(
683            &format!("{prefix}.draft_split_probability"),
684            ConfigValueSchema::Float,
685        ),
686        basic_setting(
687            &format!("{prefix}.draft_gpu_layers"),
688            ConfigValueSchema::Integer,
689        ),
690        basic_setting(&format!("{prefix}.draft_device"), ConfigValueSchema::String),
691        basic_setting(
692            &format!("{prefix}.draft_threads"),
693            ConfigValueSchema::Integer,
694        ),
695        basic_setting(
696            &format!("{prefix}.draft_cache_type_k"),
697            kv_cache_type_schema(),
698        ),
699        basic_setting(
700            &format!("{prefix}.draft_cache_type_v"),
701            kv_cache_type_schema(),
702        ),
703        basic_setting(&format!("{prefix}.ngram_min"), ConfigValueSchema::Integer),
704        basic_setting(&format!("{prefix}.ngram_max"), ConfigValueSchema::Integer),
705        basic_setting(
706            &format!("{prefix}.ngram_proposer"),
707            string_enum(["cache", "suffix"]),
708        ),
709        basic_setting(
710            &format!("{prefix}.ngram_max_proposal_tokens"),
711            ConfigValueSchema::Integer,
712        ),
713        basic_setting(
714            &format!("{prefix}.extension_max_tokens"),
715            ConfigValueSchema::Integer,
716        ),
717        basic_setting(
718            &format!("{prefix}.native_mtp_reject_cooldown_tokens"),
719            ConfigValueSchema::Integer,
720        ),
721        basic_setting(
722            &format!("{prefix}.native_mtp_suppress_cooldown_drafts"),
723            ConfigValueSchema::Boolean,
724        ),
725        basic_setting(
726            &format!("{prefix}.native_mtp_suppress_cooldown_draft_limit"),
727            ConfigValueSchema::Integer,
728        ),
729        basic_setting(
730            &format!("{prefix}.verify_window_min_tokens"),
731            ConfigValueSchema::Integer,
732        ),
733        basic_setting(
734            &format!("{prefix}.verify_window_max_tokens"),
735            ConfigValueSchema::Integer,
736        ),
737        basic_setting(
738            &format!("{prefix}.verify_window_pipeline_depth"),
739            ConfigValueSchema::Integer,
740        ),
741        basic_setting(&format!("{prefix}.spec_default"), bool_or_auto_schema()),
742    ]
743}
744
745fn request_defaults_settings(prefix: &str) -> Vec<ConfigSettingSchema> {
746    vec![
747        basic_setting(&format!("{prefix}.max_tokens"), ConfigValueSchema::Integer),
748        basic_setting(&format!("{prefix}.stop"), string_or_list_schema()),
749        basic_setting(&format!("{prefix}.temperature"), ConfigValueSchema::Float),
750        basic_setting(&format!("{prefix}.top_p"), ConfigValueSchema::Float),
751        basic_setting(&format!("{prefix}.top_k"), ConfigValueSchema::Integer),
752        basic_setting(&format!("{prefix}.min_p"), ConfigValueSchema::Float),
753        basic_setting(&format!("{prefix}.typical_p"), ConfigValueSchema::Float),
754        basic_setting(&format!("{prefix}.top_nsigma"), ConfigValueSchema::Float),
755        basic_setting(
756            &format!("{prefix}.dynatemp_range"),
757            ConfigValueSchema::Float,
758        ),
759        basic_setting(
760            &format!("{prefix}.dynatemp_exponent"),
761            ConfigValueSchema::Float,
762        ),
763        basic_setting(
764            &format!("{prefix}.repeat_penalty"),
765            ConfigValueSchema::Float,
766        ),
767        basic_setting(
768            &format!("{prefix}.repeat_last_n"),
769            ConfigValueSchema::Integer,
770        ),
771        basic_setting(
772            &format!("{prefix}.presence_penalty"),
773            ConfigValueSchema::Float,
774        ),
775        basic_setting(
776            &format!("{prefix}.frequency_penalty"),
777            ConfigValueSchema::Float,
778        ),
779        unwired_setting(
780            &format!("{prefix}.dry"),
781            ConfigValueSchema::Object,
782            "Reserved sampler object accepted for compatibility but not wired into the current runtime.",
783        ),
784        unwired_setting(
785            &format!("{prefix}.xtc"),
786            ConfigValueSchema::Object,
787            "Reserved sampler object accepted for compatibility but not wired into the current runtime.",
788        ),
789        unwired_setting(
790            &format!("{prefix}.adaptive"),
791            ConfigValueSchema::Object,
792            "Reserved sampler object accepted for compatibility but not wired into the current runtime.",
793        ),
794        basic_setting(
795            &format!("{prefix}.mirostat_mode"),
796            integer_or_string_enum(["disabled", "1", "2"]),
797        ),
798        basic_setting(
799            &format!("{prefix}.mirostat_entropy"),
800            ConfigValueSchema::Float,
801        ),
802        basic_setting(
803            &format!("{prefix}.mirostat_learning_rate"),
804            ConfigValueSchema::Float,
805        ),
806        basic_setting(
807            &format!("{prefix}.samplers"),
808            ConfigValueSchema::Array {
809                items: Box::new(ConfigValueSchema::String),
810            },
811        ),
812        basic_setting(
813            &format!("{prefix}.sampler_sequence"),
814            ConfigValueSchema::String,
815        ),
816        basic_setting(&format!("{prefix}.seed"), ConfigValueSchema::Integer),
817        basic_setting(&format!("{prefix}.logit_bias"), ConfigValueSchema::Object),
818        basic_setting(&format!("{prefix}.ignore_eos"), ConfigValueSchema::Boolean),
819        rejected_setting(
820            &format!("{prefix}.backend_sampling"),
821            ConfigValueSchema::Object,
822            "Backend-owned sampler blocks are explicitly rejected from the built-in control surface.",
823        ),
824        basic_setting(
825            &format!("{prefix}.reasoning_format"),
826            string_enum(["auto", "none", "deepseek", "deepseek-legacy", "hidden"]),
827        ),
828        basic_setting(
829            &format!("{prefix}.reasoning_enabled"),
830            bool_or_string_enum(["auto", "off", "on"]),
831        ),
832        basic_setting(
833            &format!("{prefix}.reasoning_budget"),
834            integer_or_string_enum(["auto", "low", "medium", "high"]),
835        ),
836        basic_setting(
837            &format!("{prefix}.chat_template"),
838            ConfigValueSchema::String,
839        ),
840        basic_setting(
841            &format!("{prefix}.chat_template_file"),
842            ConfigValueSchema::Path,
843        ),
844        basic_setting(&format!("{prefix}.jinja"), ConfigValueSchema::Boolean),
845        basic_setting(
846            &format!("{prefix}.chat_template_kwargs"),
847            ConfigValueSchema::Object,
848        ),
849        basic_setting(
850            &format!("{prefix}.skip_chat_parsing"),
851            ConfigValueSchema::Boolean,
852        ),
853        basic_setting(
854            &format!("{prefix}.prefill_assistant"),
855            ConfigValueSchema::Object,
856        ),
857        basic_setting(
858            &format!("{prefix}.system_prompt"),
859            ConfigValueSchema::String,
860        ),
861        rejected_setting(
862            &format!("{prefix}.grammar"),
863            ConfigValueSchema::Object,
864            "Grammar injection is explicitly rejected on the built-in config surface.",
865        ),
866        rejected_setting(
867            &format!("{prefix}.json_schema"),
868            ConfigValueSchema::Object,
869            "JSON schema response shaping is intentionally rejected until a stable runtime contract exists.",
870        ),
871        rejected_setting(
872            &format!("{prefix}.logprobs"),
873            ConfigValueSchema::Object,
874            "Logprobs request defaults are explicitly rejected from persisted config.",
875        ),
876    ]
877}
878
879fn multimodal_settings(
880    prefix: &str,
881    legacy_mmproj_aliases: &[ConfigPathAlias],
882) -> Vec<ConfigSettingSchema> {
883    let mut settings = vec![
884        basic_setting(&format!("{prefix}.mmproj"), ConfigValueSchema::Path),
885        basic_setting(&format!("{prefix}.mmproj_url"), ConfigValueSchema::Url),
886        basic_setting(&format!("{prefix}.mmproj_offload"), bool_or_auto_schema()),
887        basic_setting(
888            &format!("{prefix}.image_min_tokens"),
889            ConfigValueSchema::Integer,
890        ),
891        basic_setting(
892            &format!("{prefix}.image_max_tokens"),
893            ConfigValueSchema::Integer,
894        ),
895        rejected_setting(
896            &format!("{prefix}.embeddings"),
897            ConfigValueSchema::Object,
898            "Built-in multimodal embeddings controls are explicitly rejected from persisted config.",
899        ),
900        rejected_setting(
901            &format!("{prefix}.reranking"),
902            ConfigValueSchema::Object,
903            "Built-in reranking controls are explicitly rejected from persisted config.",
904        ),
905        rejected_setting(
906            &format!("{prefix}.pooling"),
907            ConfigValueSchema::Object,
908            "Built-in pooling controls are explicitly rejected from persisted config.",
909        ),
910        rejected_setting(
911            &format!("{prefix}.vocoder"),
912            ConfigValueSchema::Object,
913            "Built-in vocoder controls are explicitly rejected from persisted config.",
914        ),
915    ];
916
917    if !legacy_mmproj_aliases.is_empty() {
918        apply_aliases(
919            &mut settings,
920            &format!("{prefix}.mmproj"),
921            legacy_mmproj_aliases,
922        );
923    }
924
925    settings
926}
927
928fn advanced_settings(prefix: &str) -> Vec<ConfigSettingSchema> {
929    vec![
930        rejected_setting(
931            &format!("{prefix}.server.host"),
932            ConfigValueSchema::String,
933            "Server host overrides are explicitly rejected from persisted model config.",
934        ),
935        rejected_setting(
936            &format!("{prefix}.server.port"),
937            ConfigValueSchema::Integer,
938            "Server port overrides are explicitly rejected from persisted model config.",
939        ),
940        rejected_setting(
941            &format!("{prefix}.server.reuse_port"),
942            ConfigValueSchema::Boolean,
943            "reuse_port overrides are explicitly rejected from persisted model config.",
944        ),
945        rejected_setting(
946            &format!("{prefix}.server.timeout"),
947            ConfigValueSchema::Integer,
948            "Server timeout overrides are explicitly rejected from persisted model config.",
949        ),
950        rejected_setting(
951            &format!("{prefix}.server.metrics"),
952            ConfigValueSchema::Boolean,
953            "Server metrics overrides are explicitly rejected from persisted model config.",
954        ),
955        rejected_setting(
956            &format!("{prefix}.server.slots"),
957            ConfigValueSchema::Boolean,
958            "Server slot overrides are explicitly rejected from persisted model config.",
959        ),
960        rejected_setting(
961            &format!("{prefix}.server.props"),
962            ConfigValueSchema::Boolean,
963            "Server props overrides are explicitly rejected from persisted model config.",
964        ),
965        basic_setting(&format!("{prefix}.server.alias"), ConfigValueSchema::String),
966        rejected_setting(
967            &format!("{prefix}.server.api_prefix"),
968            ConfigValueSchema::String,
969            "API prefix overrides are explicitly rejected from persisted model config.",
970        ),
971    ]
972}
973
974fn top_level_setting(path: &str, value_schema: ConfigValueSchema) -> ConfigSettingSchema {
975    let mut setting = basic_setting(path, value_schema);
976    setting.visibility = if path == "version" {
977        ConfigVisibility::Internal
978    } else {
979        ConfigVisibility::Advanced
980    };
981    setting
982}
983
984fn owner_control_setting(path: &str, value_schema: ConfigValueSchema) -> ConfigSettingSchema {
985    let mut setting = basic_setting(path, value_schema);
986    setting.control_surfaces = vec![
987        ConfigControlSurface::ConfigFile,
988        ConfigControlSurface::OwnerControl,
989    ];
990    setting.apply_mode = ConfigApplyMode::DynamicApply;
991    setting.restart_scope = ConfigRestartScope::ProcessRestart;
992    setting
993}
994
995fn telemetry_setting(path: &str, value_schema: ConfigValueSchema) -> ConfigSettingSchema {
996    let mut setting = basic_setting(path, value_schema);
997    setting.control_surfaces = vec![ConfigControlSurface::ConfigFile, ConfigControlSurface::Api];
998    setting
999}
1000
1001fn runtime_setting(path: &str, value_schema: ConfigValueSchema) -> ConfigSettingSchema {
1002    let mut setting = basic_setting(path, value_schema);
1003    setting.control_surfaces = vec![ConfigControlSurface::ConfigFile, ConfigControlSurface::Api];
1004    setting.apply_mode = ConfigApplyMode::DynamicValidationOnly;
1005    setting
1006}
1007
1008fn native_runtime_setting(path: &str, value_schema: ConfigValueSchema) -> ConfigSettingSchema {
1009    let mut setting = basic_setting(path, value_schema);
1010    setting.control_surfaces = vec![ConfigControlSurface::ConfigFile, ConfigControlSurface::Api];
1011    setting.apply_mode = ConfigApplyMode::DynamicValidationOnly;
1012    setting.restart_scope = ConfigRestartScope::ProcessRestart;
1013    setting.description = Some(
1014        "Native runtime selection is read before dynamic runtime libraries are loaded.".into(),
1015    );
1016    setting
1017}
1018
1019fn startup_runtime_setting(path: &str, value_schema: ConfigValueSchema) -> ConfigSettingSchema {
1020    let mut setting = basic_setting(path, value_schema);
1021    setting.control_surfaces = vec![ConfigControlSurface::ConfigFile, ConfigControlSurface::Api];
1022    setting.restart_scope = ConfigRestartScope::ProcessRestart;
1023    setting
1024}
1025
1026fn activity_runtime_setting(path: &str, value_schema: ConfigValueSchema) -> ConfigSettingSchema {
1027    let mut setting = basic_setting(path, value_schema);
1028    setting.control_surfaces = vec![ConfigControlSurface::ConfigFile, ConfigControlSurface::Api];
1029    setting.restart_scope = ConfigRestartScope::ProcessRestart;
1030    setting
1031}
1032
1033fn plugin_setting(path: &str, value_schema: ConfigValueSchema) -> ConfigSettingSchema {
1034    let mut setting = basic_setting(path, value_schema);
1035    setting.control_surfaces = vec![
1036        ConfigControlSurface::ConfigFile,
1037        ConfigControlSurface::PluginManifest,
1038    ];
1039    setting.restart_scope = ConfigRestartScope::ProcessRestart;
1040    setting
1041}
1042
1043fn basic_setting(path: &str, value_schema: ConfigValueSchema) -> ConfigSettingSchema {
1044    ConfigSettingSchema {
1045        path: schema_path(path),
1046        alias_policy: ConfigAliasPolicy::default(),
1047        owner: ConfigSettingOwner::BuiltIn,
1048        value_schema,
1049        support: ConfigSupportState::Supported,
1050        control_surfaces: vec![ConfigControlSurface::ConfigFile],
1051        apply_mode: ConfigApplyMode::StaticOnLoad,
1052        restart_scope: ConfigRestartScope::ModelReload,
1053        visibility: ConfigVisibility::Advanced,
1054        constraints: Vec::new(),
1055        description: Some(path.to_string()),
1056        presentation: None,
1057        control_behavior: None,
1058    }
1059}
1060
1061fn unsupported_setting(
1062    path: &str,
1063    value_schema: ConfigValueSchema,
1064    description: &str,
1065) -> ConfigSettingSchema {
1066    let mut setting = basic_setting(path, value_schema);
1067    setting.support = ConfigSupportState::Unsupported;
1068    setting.restart_scope = ConfigRestartScope::None;
1069    setting.description = Some(description.to_string());
1070    setting
1071}
1072
1073fn rejected_setting(
1074    path: &str,
1075    value_schema: ConfigValueSchema,
1076    description: &str,
1077) -> ConfigSettingSchema {
1078    let mut setting = basic_setting(path, value_schema);
1079    setting.support = ConfigSupportState::Rejected;
1080    setting.restart_scope = ConfigRestartScope::None;
1081    setting.description = Some(description.to_string());
1082    setting
1083}
1084
1085fn unwired_setting(
1086    path: &str,
1087    value_schema: ConfigValueSchema,
1088    description: &str,
1089) -> ConfigSettingSchema {
1090    let mut setting = basic_setting(path, value_schema);
1091    setting.support = ConfigSupportState::Unwired;
1092    setting.description = Some(description.to_string());
1093    setting
1094}
1095
1096fn hidden_setting(
1097    path: &str,
1098    value_schema: ConfigValueSchema,
1099    description: &str,
1100) -> ConfigSettingSchema {
1101    let mut setting = basic_setting(path, value_schema);
1102    setting.visibility = ConfigVisibility::Hidden;
1103    setting.description = Some(description.to_string());
1104    setting
1105}
1106
1107fn schema_path(path: &str) -> ConfigPath {
1108    ConfigPath::parse_rendered(path).expect("static schema path should parse")
1109}
1110
1111fn flat_alias(path: &str) -> ConfigPathAlias {
1112    ConfigPathAlias {
1113        path: schema_path(path),
1114        kind: ConfigPathAliasKind::LegacyLayout,
1115        note: Some("legacy flattened TOML field".into()),
1116    }
1117}
1118
1119fn string_enum<const N: usize>(values: [&str; N]) -> ConfigValueSchema {
1120    ConfigValueSchema::Enum {
1121        values: values.into_iter().map(str::to_string).collect(),
1122    }
1123}
1124
1125fn string_enum_from_slice(values: &[&str]) -> ConfigValueSchema {
1126    ConfigValueSchema::Enum {
1127        values: values.iter().map(|s| (*s).to_string()).collect(),
1128    }
1129}
1130
1131fn kv_cache_type_schema() -> ConfigValueSchema {
1132    string_enum([
1133        "auto", "f32", "f16", "bf16", "q8_0", "q4_0", "q4_1", "iq4_nl", "q5_0", "q5_1",
1134    ])
1135}
1136
1137fn one_of<const N: usize>(variants: [ConfigValueSchema; N]) -> ConfigValueSchema {
1138    ConfigValueSchema::OneOf {
1139        variants: variants.into_iter().collect(),
1140    }
1141}
1142
1143fn bool_or_auto_schema() -> ConfigValueSchema {
1144    bool_or_string_enum(["auto", "true", "false"])
1145}
1146
1147fn bool_or_string_enum<const N: usize>(values: [&str; N]) -> ConfigValueSchema {
1148    one_of([ConfigValueSchema::Boolean, string_enum(values)])
1149}
1150
1151fn integer_or_auto_schema() -> ConfigValueSchema {
1152    integer_or_string_enum(["auto"])
1153}
1154
1155fn integer_or_string_schema() -> ConfigValueSchema {
1156    one_of([ConfigValueSchema::Integer, ConfigValueSchema::String])
1157}
1158
1159fn integer_or_string_enum<const N: usize>(values: [&str; N]) -> ConfigValueSchema {
1160    one_of([ConfigValueSchema::Integer, string_enum(values)])
1161}
1162
1163fn string_or_list_schema() -> ConfigValueSchema {
1164    one_of([
1165        ConfigValueSchema::String,
1166        ConfigValueSchema::Array {
1167            items: Box::new(ConfigValueSchema::String),
1168        },
1169    ])
1170}
1171
1172fn tensor_split_schema() -> ConfigValueSchema {
1173    one_of([
1174        ConfigValueSchema::Array {
1175            items: Box::new(ConfigValueSchema::Float),
1176        },
1177        ConfigValueSchema::String,
1178    ])
1179}
1180
1181/// Returns the list of known mesh-llm versions from GitHub releases.
1182/// This list should be updated during the release process.
1183fn known_mesh_llm_versions() -> &'static [&'static str] {
1184    &[
1185        "0.75.0", "0.72.1", "0.72.0", "0.71.0", "0.70.0", "0.69.0", "0.68.0", "0.67.0", "0.66.0", "0.65.0",
1186        "0.64.0", "0.63.0", "0.62.0", "0.61.0", "0.60.0",
1187    ]
1188}
1189
1190fn apply_aliases(
1191    settings: &mut [ConfigSettingSchema],
1192    canonical_path: &str,
1193    aliases: &[ConfigPathAlias],
1194) {
1195    if let Some(setting) = settings
1196        .iter_mut()
1197        .find(|setting| setting.path.render() == canonical_path)
1198    {
1199        setting.alias_policy.mode = ConfigAliasMode::CanonicalWithLegacyAliases;
1200        setting.alias_policy.aliases.extend_from_slice(aliases);
1201    }
1202}
1203
1204#[cfg(test)]
1205mod tests {
1206    use super::*;
1207
1208    #[test]
1209    fn built_in_schema_preserves_union_typed_fields() {
1210        for path in [
1211            "models.<model-ref>.model_fit.kv_offload",
1212            "models.<model-ref>.model_fit.kv_unified",
1213            "models.<model-ref>.model_fit.prompt_cache",
1214            "models.<model-ref>.model_fit.context_shift",
1215            "models.<model-ref>.hardware.cpu_moe",
1216            "models.<model-ref>.hardware.fit_context",
1217            "models.<model-ref>.hardware.mmproj_offload",
1218            "models.<model-ref>.hardware.mmap",
1219            "models.<model-ref>.hardware.warmup",
1220            "models.<model-ref>.throughput.continuous_batching",
1221            "models.<model-ref>.speculative.spec_default",
1222            "models.<model-ref>.multimodal.mmproj_offload",
1223        ] {
1224            assert_eq!(schema_value(path), bool_or_auto_schema());
1225        }
1226
1227        assert_eq!(
1228            schema_value("models.<model-ref>.hardware.gpu_layers"),
1229            integer_or_auto_schema()
1230        );
1231        assert_eq!(
1232            schema_value("models.<model-ref>.hardware.tensor_split"),
1233            tensor_split_schema()
1234        );
1235        assert_eq!(
1236            schema_value("models.<model-ref>.throughput.priority"),
1237            integer_or_string_schema()
1238        );
1239        assert_eq!(
1240            schema_value("models.<model-ref>.throughput.poll"),
1241            bool_or_string_enum(["auto", "busy", "sleep"])
1242        );
1243        assert_eq!(
1244            schema_value("models.<model-ref>.throughput.cpu_affinity"),
1245            string_or_list_schema()
1246        );
1247        assert_eq!(
1248            schema_value("models.<model-ref>.request_defaults.stop"),
1249            string_or_list_schema()
1250        );
1251        assert_eq!(
1252            schema_value("models.<model-ref>.request_defaults.mirostat_mode"),
1253            integer_or_string_enum(["disabled", "1", "2"])
1254        );
1255        assert_eq!(
1256            schema_value("models.<model-ref>.request_defaults.reasoning_enabled"),
1257            bool_or_string_enum(["auto", "off", "on"])
1258        );
1259        assert_eq!(
1260            schema_value("models.<model-ref>.request_defaults.reasoning_budget"),
1261            integer_or_string_enum(["auto", "low", "medium", "high"])
1262        );
1263    }
1264
1265    #[test]
1266    fn built_in_schema_marks_curated_defaults_user_visible() {
1267        for path in [
1268            "defaults.throughput.threads",
1269            "defaults.throughput.parallel",
1270            "defaults.model_fit.kv_cache_policy",
1271            "defaults.request_defaults.temperature",
1272            "defaults.skippy.binary_stage_transport",
1273            "defaults.multimodal.mmproj_offload",
1274        ] {
1275            assert_eq!(
1276                schema_setting(path).visibility,
1277                ConfigVisibility::User,
1278                "{path}"
1279            );
1280        }
1281
1282        assert_eq!(
1283            schema_setting("defaults.model_fit.prompt_cache").visibility,
1284            ConfigVisibility::Advanced
1285        );
1286        assert_eq!(
1287            schema_setting("defaults.hardware.model_runtime").visibility,
1288            ConfigVisibility::Hidden
1289        );
1290        assert_eq!(
1291            schema_setting("defaults.advanced.server.alias").visibility,
1292            ConfigVisibility::Advanced
1293        );
1294    }
1295
1296    #[test]
1297    fn built_in_schema_uses_explicit_path_and_url_value_kinds() {
1298        assert_eq!(schema_value("telemetry.endpoint"), ConfigValueSchema::Url);
1299        assert_eq!(
1300            schema_value("telemetry.metrics.endpoint"),
1301            ConfigValueSchema::Url
1302        );
1303        assert_eq!(
1304            schema_value("plugin.<plugin-name>.url"),
1305            ConfigValueSchema::Url
1306        );
1307        assert_eq!(
1308            schema_value("defaults.hardware.model_path"),
1309            ConfigValueSchema::Path
1310        );
1311        assert_eq!(
1312            schema_value("defaults.hardware.mmproj"),
1313            ConfigValueSchema::Path
1314        );
1315        assert_eq!(
1316            schema_value("defaults.multimodal.mmproj"),
1317            ConfigValueSchema::Path
1318        );
1319        assert_eq!(
1320            schema_value("defaults.multimodal.mmproj_url"),
1321            ConfigValueSchema::Url
1322        );
1323        assert_eq!(
1324            schema_value("defaults.speculative.draft_model"),
1325            ConfigValueSchema::Path
1326        );
1327    }
1328
1329    #[test]
1330    fn startup_runtime_settings_require_process_restart() {
1331        for path in ["runtime.debug", "runtime.listen_all"] {
1332            let setting = schema_setting(path);
1333
1334            assert_eq!(
1335                setting.control_surfaces,
1336                vec![ConfigControlSurface::ConfigFile, ConfigControlSurface::Api],
1337                "{path}"
1338            );
1339            assert_eq!(setting.apply_mode, ConfigApplyMode::StaticOnLoad, "{path}");
1340            assert_eq!(
1341                setting.restart_scope,
1342                ConfigRestartScope::ProcessRestart,
1343                "{path}"
1344            );
1345        }
1346    }
1347
1348    #[test]
1349    fn built_in_schema_exports_model_fit_numeric_controls_and_relative_bounds() {
1350        let defaults_batch = schema_setting("defaults.model_fit.batch");
1351        let defaults_ubatch = schema_setting("defaults.model_fit.ubatch");
1352        let model_ubatch = schema_setting("models.<model-ref>.model_fit.ubatch");
1353
1354        assert_eq!(numeric_control(&defaults_batch).min, Some(1.0));
1355        assert_eq!(numeric_control(&defaults_batch).step, Some(1.0));
1356        assert_eq!(
1357            numeric_control(&defaults_batch).unit.as_deref(),
1358            Some("tokens")
1359        );
1360
1361        assert_eq!(
1362            numeric_control(&defaults_ubatch),
1363            numeric_control(&model_ubatch)
1364        );
1365        assert_has_range_constraint(&defaults_ubatch, None, Some("defaults.model_fit.batch"));
1366        assert_has_range_constraint(
1367            &model_ubatch,
1368            None,
1369            Some("models.<model-ref>.model_fit.batch"),
1370        );
1371    }
1372
1373    #[test]
1374    fn built_in_schema_keeps_defaults_and_model_hardware_device_semantics_in_sync() {
1375        let defaults_device = schema_setting("defaults.hardware.device");
1376        let model_device = schema_setting("models.<model-ref>.hardware.device");
1377
1378        assert_eq!(
1379            control_behavior(&defaults_device).options_source,
1380            Some(ConfigOptionsSource::RuntimeGpus)
1381        );
1382        assert_eq!(
1383            defaults_device.control_behavior,
1384            model_device.control_behavior
1385        );
1386        assert_eq!(
1387            control_behavior(&defaults_device).enable_when,
1388            vec![equals_condition("gpu.assignment", "pinned")]
1389        );
1390        assert_eq!(
1391            control_behavior(&defaults_device).disable_when,
1392            vec![dependency_disable(
1393                equals_condition("gpu.assignment", "auto"),
1394                "Set gpu.assignment = \"pinned\" to edit a concrete GPU device.",
1395            )]
1396        );
1397    }
1398
1399    #[test]
1400    fn built_in_schema_marks_rejected_hardware_escape_hatches_non_editable() {
1401        let setting = schema_setting("models.<model-ref>.hardware.rpc_backend");
1402        let behavior = control_behavior(&setting);
1403
1404        assert_eq!(setting.support, ConfigSupportState::Rejected);
1405        assert_eq!(
1406            behavior.availability.as_ref().map(|value| value.enabled),
1407            Some(false)
1408        );
1409        assert_eq!(
1410            behavior.availability.as_ref().map(|value| value.source),
1411            Some(ConfigControlAvailabilitySource::Static)
1412        );
1413        assert_eq!(
1414            setting.default_disabled_write_policy(None),
1415            Some(ConfigDisabledWritePolicy::RejectWhenDisabled)
1416        );
1417    }
1418
1419    #[test]
1420    fn built_in_schema_exports_throughput_and_skippy_t5_controls() {
1421        let threads = schema_setting("defaults.throughput.threads");
1422        let prefill_chunk_size = schema_setting("defaults.skippy.prefill_chunk_size");
1423        let prefill_chunk_schedule = schema_setting("defaults.skippy.prefill_chunk_schedule");
1424
1425        assert_static_choices(
1426            "defaults.throughput.tuning_profile",
1427            &["throughput", "balanced", "saver"],
1428        );
1429        assert_eq!(numeric_control(&threads).min, Some(0.0));
1430        assert_eq!(numeric_control(&threads).step, Some(1.0));
1431
1432        assert_static_choices(
1433            "defaults.skippy.activation_wire_dtype",
1434            &["auto", "f16", "f32", "q8"],
1435        );
1436        assert_static_choices(
1437            "defaults.skippy.prefill_chunking",
1438            &["auto", "fixed", "schedule", "adaptive-ramp"],
1439        );
1440        assert_eq!(numeric_control(&prefill_chunk_size).min, Some(1.0));
1441        assert_eq!(
1442            control_behavior(&prefill_chunk_size).enable_when,
1443            vec![equals_condition(
1444                "defaults.skippy.prefill_chunking",
1445                "fixed"
1446            )]
1447        );
1448        assert_eq!(
1449            control_behavior(&prefill_chunk_schedule).text_format,
1450            Some(ConfigTextFormat::CsvPositiveInts)
1451        );
1452        assert_eq!(
1453            control_behavior(&prefill_chunk_schedule).enable_when,
1454            vec![equals_condition(
1455                "defaults.skippy.prefill_chunking",
1456                "schedule"
1457            )]
1458        );
1459    }
1460
1461    #[test]
1462    fn built_in_schema_exports_speculative_and_request_default_t5_controls() {
1463        let draft_min = schema_setting("defaults.speculative.draft_min_tokens");
1464        let ngram_max = schema_setting("defaults.speculative.ngram_max");
1465        let mirostat_entropy = schema_setting("defaults.request_defaults.mirostat_entropy");
1466
1467        assert_static_choices("defaults.speculative.mode", &["auto", "disabled", "draft"]);
1468        assert_static_choices(
1469            "defaults.speculative.draft_selection_policy",
1470            &["manual", "auto"],
1471        );
1472        assert_static_choices(
1473            "defaults.speculative.pairing_fault",
1474            &[
1475                "warn_disable",
1476                "fail-open",
1477                "fail-closed",
1478                "fail_open",
1479                "fail_closed",
1480            ],
1481        );
1482        assert_has_range_constraint(
1483            &draft_min,
1484            None,
1485            Some("defaults.speculative.draft_max_tokens"),
1486        );
1487        assert_has_range_constraint(&ngram_max, Some("defaults.speculative.ngram_min"), None);
1488
1489        assert_static_choices(
1490            "defaults.request_defaults.reasoning_format",
1491            &["auto", "none", "deepseek", "deepseek-legacy", "hidden"],
1492        );
1493        assert_eq!(
1494            control_behavior(&mirostat_entropy).enable_when,
1495            vec![in_condition(
1496                "defaults.request_defaults.mirostat_mode",
1497                &[
1498                    ConfigConditionValue::Integer(1),
1499                    ConfigConditionValue::Integer(2),
1500                    ConfigConditionValue::String("1".to_string()),
1501                    ConfigConditionValue::String("2".to_string()),
1502                ],
1503            )]
1504        );
1505        assert_eq!(
1506            control_behavior(&mirostat_entropy).disable_when,
1507            vec![dependency_disable(
1508                not_in_condition(
1509                    "defaults.request_defaults.mirostat_mode",
1510                    &[
1511                        ConfigConditionValue::Integer(1),
1512                        ConfigConditionValue::Integer(2),
1513                        ConfigConditionValue::String("1".to_string()),
1514                        ConfigConditionValue::String("2".to_string()),
1515                    ],
1516                ),
1517                "defaults.request_defaults.mirostat_entropy requires defaults.request_defaults.mirostat_mode = 1 or 2",
1518            )]
1519        );
1520    }
1521
1522    #[test]
1523    fn built_in_schema_disables_duplicate_multimodal_projector_controls_with_preserve_policy() {
1524        let mmproj = schema_setting("defaults.hardware.mmproj");
1525        let offload = schema_setting("defaults.hardware.mmproj_offload");
1526
1527        assert_eq!(
1528            control_behavior(&mmproj).availability,
1529            Some(ConfigControlAvailability {
1530                enabled: false,
1531                reason: Some(
1532                    "Edit defaults.multimodal.mmproj instead of the legacy hardware duplicate."
1533                        .to_string(),
1534                ),
1535                note: Some(
1536                    "Existing values are preserved on save unless you change defaults.multimodal.mmproj."
1537                        .to_string(),
1538                ),
1539                source: ConfigControlAvailabilitySource::Static,
1540            })
1541        );
1542        assert_eq!(
1543            control_behavior(&mmproj).write_policy,
1544            Some(ConfigDisabledWritePolicy::PreserveExisting)
1545        );
1546        assert_eq!(
1547            control_behavior(&offload)
1548                .availability
1549                .as_ref()
1550                .map(|value| value.enabled),
1551            Some(false)
1552        );
1553        assert_eq!(
1554            control_behavior(&offload).write_policy,
1555            Some(ConfigDisabledWritePolicy::PreserveExisting)
1556        );
1557    }
1558
1559    #[test]
1560    fn built_in_schema_exports_telemetry_owner_control_attestation_and_plugin_timeout_controls() {
1561        let telemetry_interval = schema_setting("telemetry.export_interval_secs");
1562        let advertise_addr = schema_setting("owner_control.advertise_addr");
1563        let signer_keys = schema_setting("mesh_requirements.release_signer_keys");
1564        let plugin_timeout = schema_setting("plugin.<plugin-name>.startup.connect_timeout_secs");
1565
1566        assert_eq!(numeric_control(&telemetry_interval).min, Some(1.0));
1567        assert_eq!(
1568            numeric_control(&telemetry_interval).unit.as_deref(),
1569            Some("sec")
1570        );
1571
1572        assert_eq!(
1573            control_behavior(&advertise_addr).enable_when,
1574            vec![present_condition("owner_control.bind")]
1575        );
1576        assert_eq!(
1577            control_behavior(&advertise_addr).disable_when,
1578            vec![dependency_disable(
1579                absent_condition("owner_control.bind"),
1580                "owner_control.advertise_addr requires owner_control.bind so the advertised port is actually listening",
1581            )]
1582        );
1583
1584        assert_eq!(
1585            control_behavior(&schema_setting("mesh_requirements.min_node_version")).text_format,
1586            Some(ConfigTextFormat::Semver)
1587        );
1588        assert_eq!(
1589            control_behavior(&signer_keys).text_format,
1590            Some(ConfigTextFormat::Ed25519Key)
1591        );
1592        assert_eq!(
1593            control_behavior(&signer_keys).enable_when,
1594            vec![equals_bool_condition(
1595                "mesh_requirements.require_release_attestation",
1596                true,
1597            )]
1598        );
1599
1600        assert_eq!(numeric_control(&plugin_timeout).min, Some(1.0));
1601        assert_eq!(
1602            numeric_control(&plugin_timeout).unit.as_deref(),
1603            Some("sec")
1604        );
1605    }
1606
1607    #[test]
1608    fn built_in_schema_covers_t5_fallback_choices_or_keeps_open_text_intentional() {
1609        for (path, expected) in [
1610            (
1611                "defaults.throughput.tuning_profile",
1612                vec!["throughput", "balanced", "saver"],
1613            ),
1614            (
1615                "defaults.speculative.mode",
1616                vec!["auto", "disabled", "draft"],
1617            ),
1618            (
1619                "defaults.speculative.draft_selection_policy",
1620                vec!["manual", "auto"],
1621            ),
1622            (
1623                "defaults.speculative.pairing_fault",
1624                vec![
1625                    "warn_disable",
1626                    "fail-open",
1627                    "fail-closed",
1628                    "fail_open",
1629                    "fail_closed",
1630                ],
1631            ),
1632            (
1633                "defaults.request_defaults.reasoning_format",
1634                vec!["auto", "none", "deepseek", "deepseek-legacy", "hidden"],
1635            ),
1636            (
1637                "defaults.speculative.draft_cache_type_k",
1638                vec![
1639                    "auto", "f32", "f16", "bf16", "q8_0", "q4_0", "q4_1", "iq4_nl", "q5_0", "q5_1",
1640                ],
1641            ),
1642            (
1643                "defaults.speculative.draft_cache_type_v",
1644                vec![
1645                    "auto", "f32", "f16", "bf16", "q8_0", "q4_0", "q4_1", "iq4_nl", "q5_0", "q5_1",
1646                ],
1647            ),
1648        ] {
1649            assert_eq!(
1650                schema_enum_values(path),
1651                expected.into_iter().map(str::to_string).collect::<Vec<_>>(),
1652                "{path}"
1653            );
1654        }
1655
1656        for path in [
1657            "defaults.throughput.numa",
1658            "defaults.skippy.binary_stage_transport",
1659        ] {
1660            assert!(schema_enum_values(path).is_empty(), "{path}");
1661            assert_ne!(
1662                schema_setting(path)
1663                    .control_behavior
1664                    .as_ref()
1665                    .and_then(|behavior| behavior.options_source),
1666                Some(ConfigOptionsSource::Static),
1667                "{path}"
1668            );
1669        }
1670    }
1671
1672    fn schema_value(path: &str) -> ConfigValueSchema {
1673        schema_setting(path).value_schema
1674    }
1675
1676    fn schema_setting(path: &str) -> ConfigSettingSchema {
1677        built_in_config_schema_descriptor(&schema_path(path)).expect("schema setting should exist")
1678    }
1679
1680    fn control_behavior(setting: &ConfigSettingSchema) -> &ConfigControlBehavior {
1681        setting
1682            .control_behavior
1683            .as_ref()
1684            .expect("control behavior should be present")
1685    }
1686
1687    fn numeric_control(setting: &ConfigSettingSchema) -> ConfigNumericControl {
1688        control_behavior(setting)
1689            .numeric
1690            .clone()
1691            .expect("numeric control should be present")
1692    }
1693
1694    fn assert_has_range_constraint(
1695        setting: &ConfigSettingSchema,
1696        expected_min: Option<&str>,
1697        expected_max: Option<&str>,
1698    ) {
1699        assert!(
1700            setting.constraints.iter().any(|constraint| {
1701                matches!(
1702                    constraint,
1703                    ConfigConstraint::Range { min, max }
1704                        if min.as_deref() == expected_min && max.as_deref() == expected_max
1705                )
1706            }),
1707            "expected range constraint min={expected_min:?} max={expected_max:?} on {}",
1708            setting.path.render()
1709        );
1710    }
1711
1712    fn equals_condition(path: &str, expected: &str) -> ConfigControlCondition {
1713        ConfigControlCondition {
1714            path: schema_path(path),
1715            operator: ConfigConditionOperator::Equals,
1716            values: vec![ConfigConditionValue::String(expected.to_string())],
1717        }
1718    }
1719
1720    fn equals_bool_condition(path: &str, expected: bool) -> ConfigControlCondition {
1721        ConfigControlCondition {
1722            path: schema_path(path),
1723            operator: ConfigConditionOperator::Equals,
1724            values: vec![ConfigConditionValue::Bool(expected)],
1725        }
1726    }
1727
1728    fn in_condition(path: &str, values: &[ConfigConditionValue]) -> ConfigControlCondition {
1729        ConfigControlCondition {
1730            path: schema_path(path),
1731            operator: ConfigConditionOperator::In,
1732            values: values.to_vec(),
1733        }
1734    }
1735
1736    fn not_in_condition(path: &str, values: &[ConfigConditionValue]) -> ConfigControlCondition {
1737        ConfigControlCondition {
1738            path: schema_path(path),
1739            operator: ConfigConditionOperator::NotIn,
1740            values: values.to_vec(),
1741        }
1742    }
1743
1744    fn present_condition(path: &str) -> ConfigControlCondition {
1745        ConfigControlCondition {
1746            path: schema_path(path),
1747            operator: ConfigConditionOperator::Present,
1748            values: Vec::new(),
1749        }
1750    }
1751
1752    fn absent_condition(path: &str) -> ConfigControlCondition {
1753        ConfigControlCondition {
1754            path: schema_path(path),
1755            operator: ConfigConditionOperator::Absent,
1756            values: Vec::new(),
1757        }
1758    }
1759
1760    fn dependency_disable(
1761        condition: ConfigControlCondition,
1762        reason: &str,
1763    ) -> ConfigConditionalDisable {
1764        ConfigConditionalDisable {
1765            condition,
1766            reason: reason.to_string(),
1767            note: None,
1768            write_policy: ConfigDisabledWritePolicy::OmitWhenDisabled,
1769        }
1770    }
1771
1772    fn assert_static_choices(path: &str, expected: &[&str]) {
1773        let setting = schema_setting(path);
1774
1775        assert_eq!(
1776            control_behavior(&setting).options_source,
1777            Some(ConfigOptionsSource::Static),
1778            "{path}"
1779        );
1780        assert_eq!(
1781            schema_enum_values(path),
1782            expected
1783                .iter()
1784                .map(|value| (*value).to_string())
1785                .collect::<Vec<_>>(),
1786            "{path}"
1787        );
1788    }
1789
1790    fn schema_enum_values(path: &str) -> Vec<String> {
1791        enum_values(&schema_value(path))
1792    }
1793
1794    fn enum_values(schema: &ConfigValueSchema) -> Vec<String> {
1795        match schema {
1796            ConfigValueSchema::Enum { values } => values.clone(),
1797            ConfigValueSchema::OneOf { variants } => {
1798                variants.iter().flat_map(enum_values).collect()
1799            }
1800            _ => Vec::new(),
1801        }
1802    }
1803}