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
1181fn 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}