Entropic 2.11.1
Local-first agentic inference engine
Loading...
Searching...
No Matches
loader.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
11#include "yaml_util.h"
12
13#include <nlohmann/json.hpp>
14
15#include <fstream>
16#include <optional>
17#include <sstream>
18
19#include <dlfcn.h>
20
21static auto s_log = entropic::log::get("config");
22
23namespace entropic::config {
24
35 ryml::ConstNodeRef node, ModelConfig& config)
36{
37 extract(node, "cache_type_k", config.cache_type_k);
38 extract(node, "cache_type_v", config.cache_type_v);
39 extract(node, "n_batch", config.n_batch);
40 extract(node, "n_ubatch", config.n_ubatch); // gh#23 v2.3.17
41 extract(node, "n_threads", config.n_threads);
42 extract(node, "tensor_split", config.tensor_split);
43 extract(node, "split_mode", config.split_mode); // gh#23 v2.3.18
44 extract(node, "main_gpu", config.main_gpu); // gh#23 v2.3.19
45 extract(node, "offload_kqv", config.offload_kqv); // gh#23 v2.3.20
46 extract(node, "rope_freq_base", config.rope_freq_base); // gh#23 v2.3.21
47 extract(node, "rope_freq_scale", config.rope_freq_scale); // gh#23 v2.3.22
48 extract(node, "n_parallel", config.n_parallel); // gh#23 v2.3.23
49 extract(node, "flash_attn", config.flash_attn);
50}
51
67 ryml::ConstNodeRef node, TierConfig& config)
68{
69 float f = 0.0f;
70 int i = 0;
71 bool b = false;
72 std::string s;
73 if (extract(node, "temperature", f)) { config.temperature = f; }
74 if (extract(node, "top_p", f)) { config.top_p = f; }
75 if (extract(node, "min_p", f)) { config.min_p = f; }
76 if (extract(node, "presence_penalty", f)) { config.presence_penalty = f; }
77 if (extract(node, "frequency_penalty", f)) { config.frequency_penalty = f; }
78 if (extract(node, "repeat_penalty", f)) { config.repeat_penalty = f; }
79 if (extract(node, "top_k", i)) { config.top_k = i; }
80 if (extract(node, "max_output_tokens", i)) { config.max_output_tokens = i; }
81 if (extract(node, "enable_thinking", b)) { config.enable_thinking = b; }
82 if (extract(node, "tool_call_mode", s)) { config.tool_call_mode = s; } // gh#103
83}
84
99 ryml::ConstNodeRef node, TierConfig& config)
100{
101 if (!node.has_child("speculative")) { return; }
102 auto spec = node["speculative"];
103 bool mtp = false;
104 if (extract(spec, "mtp", mtp)) { config.speculative_mtp = mtp; }
105}
106
120static std::string resolve_model_path(
121 ryml::ConstNodeRef node,
122 const BundledModels& registry,
123 ModelConfig& config)
124{
125 std::string path_str;
126 if (extract(node, "path", path_str)) {
127 config.path = registry.resolve(path_str);
128 return "";
129 }
130
131 std::string family;
132 std::string size;
133 std::string quant;
134 const bool hf = extract(node, "family", family);
135 const bool hs = extract(node, "size", size);
136 const bool hq = extract(node, "quant", quant);
137 if (!hf && !hs && !hq) { return ""; } // no path, no selector
138
139 // Single-exit accumulator (knots returns gate ≤ 3).
140 std::string err;
141 if (!(hf && hs && hq)) {
142 err = "model selector requires all of family/size/quant "
143 "(or use path:)";
144 } else if (const std::string key = registry.find_by(family, size, quant);
145 key.empty()) {
146 err = "no bundled model matches family=" + family
147 + " size=" + size + " quant=" + quant;
148 } else {
149 config.path = registry.resolve(key);
150 }
151 return err;
152}
153
165static std::string parse_model_config(
166 ryml::ConstNodeRef node,
167 const BundledModels& registry,
168 ModelConfig& config)
169{
170 const std::string err = resolve_model_path(node, registry, config);
171 if (!err.empty()) { return err; }
172
173 extract(node, "adapter", config.adapter);
174 extract(node, "context_length", config.context_length);
175 extract(node, "gpu_layers", config.gpu_layers);
176 extract(node, "keep_warm", config.keep_warm);
177 extract(node, "use_mlock", config.use_mlock);
178 extract(node, "reasoning_budget", config.reasoning_budget);
179 parse_model_runtime_knobs(node, config);
180 extract_string_list_opt(node, "allowed_tools", config.allowed_tools);
181
182 /* v1.9.11 mmproj wiring at the loader (gh#42, gh#41): read mmproj
183 * here so the YAML key matches the bundled_models.yaml registry
184 * shape and the orchestrator can declare a tier vision-capable. */
185 std::string mmproj_str;
186 if (extract(node, "mmproj", mmproj_str)) {
187 config.mmproj_path = registry.resolve(mmproj_str);
188 }
189
190 return "";
191}
192
209static std::string parse_tier_config(
210 ryml::ConstNodeRef node,
211 const BundledModels& registry,
212 TierConfig& config)
213{
214 auto err = parse_model_config(node, registry, config);
215 if (!err.empty()) {
216 return err;
217 }
218
219 // gh#94 (v2.7.3): per-tier sampler overrides live on TierConfig.
220 parse_sampler_overrides(node, config);
221
222 // gh#108 (v2.9.4): per-tier speculative.mtp override.
224
225 extract_tri_state_path(node, "identity",
226 config.identity, config.identity_disabled);
227
228 std::string grammar_str;
229 if (extract(node, "grammar", grammar_str)) {
230 config.grammar = expand_home(std::filesystem::path(grammar_str));
231 }
232
233 // gh#134 (v2.10.4): opt a mandatory-tool tier into eager tool-call
234 // grammar enforcement. Optional so an unset tier inherits AUTO.
235 bool require_tc = false;
236 if (extract(node, "require_tool_call", require_tc)) {
237 config.require_tool_call = require_tc;
238 }
239
240 std::string auto_chain_str;
241 if (extract(node, "auto_chain", auto_chain_str)) {
242 config.auto_chain = auto_chain_str;
243 }
244
245 bool routable_val = false;
246 if (extract(node, "routable", routable_val)) {
247 config.routable = routable_val;
248 }
249
250 /* gh#41 v2.1.8: tier capabilities. Missing key → ["text"] so
251 * every pre-v2.1.8 tier config remains valid. Configs that
252 * declare capabilities explicitly must include "text" themselves
253 * if the tier still serves text — we don't auto-inject. */
254 if (!extract_string_list(node, "capabilities", config.capabilities)) {
255 config.capabilities = {"text"};
256 }
257
258 return "";
259}
260
271static std::string parse_models_config(
272 ryml::ConstNodeRef node,
273 const BundledModels& registry,
274 ModelsConfig& config)
275{
276 extract(node, "default", config.default_tier);
277
278 if (node.has_child("router")) {
279 config.router.emplace();
280 auto err = parse_model_config(node["router"], registry,
281 *config.router);
282 if (!err.empty()) {
283 return "models.router: " + err;
284 }
285 }
286
287 for (auto child : node) {
288 std::string key = to_string(child.key());
289 if (key == "default" || key == "router") {
290 continue;
291 }
292 if (!child.is_map()) {
293 continue;
294 }
295
296 TierConfig tier;
297 if (config.tiers.count(key) > 0) {
298 tier = config.tiers[key];
299 }
300 auto err = parse_tier_config(child, registry, tier);
301 if (!err.empty()) {
302 return "models." + key + ": " + err;
303 }
304 config.tiers[key] = std::move(tier);
305 }
306
307 return "";
308}
309
318static std::string parse_routing_config(
319 ryml::ConstNodeRef node,
320 RoutingConfig& config)
321{
322 extract(node, "enabled", config.enabled);
323 extract(node, "fallback_tier", config.fallback_tier);
324
325 std::string class_prompt;
326 if (extract(node, "classification_prompt", class_prompt)) {
327 config.classification_prompt = class_prompt;
328 }
329
330 extract_string_map(node, "tier_map", config.tier_map);
331 extract_string_list_map(node, "handoff_rules", config.handoff_rules);
332
333 return "";
334}
335
344static std::string parse_compaction_config(
345 ryml::ConstNodeRef node,
346 CompactionConfig& config)
347{
348 extract(node, "enabled", config.enabled);
349 extract(node, "threshold_percent", config.threshold_percent);
350 extract(node, "preserve_recent_turns", config.preserve_recent_turns);
351 extract(node, "summary_max_tokens", config.summary_max_tokens);
352 extract(node, "notify_user", config.notify_user);
353 extract(node, "save_full_history", config.save_full_history);
354 extract(node, "tool_result_ttl", config.tool_result_ttl);
355 extract(node, "warning_threshold_percent",
357 return "";
358}
359
368static std::string parse_permissions_config(
369 ryml::ConstNodeRef node,
370 PermissionsConfig& config)
371{
372 extract_string_list(node, "allow", config.allow);
373 extract_string_list(node, "deny", config.deny);
374 extract(node, "auto_approve", config.auto_approve);
375 return "";
376}
377
386static std::string parse_filesystem_config(
387 ryml::ConstNodeRef node,
388 FilesystemConfig& config)
389{
390 extract(node, "diagnostics_on_edit", config.diagnostics_on_edit);
391 extract(node, "fail_on_errors", config.fail_on_errors);
392 extract(node, "diagnostics_timeout", config.diagnostics_timeout);
393 extract(node, "allow_outside_root", config.allow_outside_root);
394 extract(node, "max_read_context_pct", config.max_read_context_pct);
395
396 int max_read = 0;
397 if (extract(node, "max_read_bytes", max_read)) {
398 config.max_read_bytes = max_read;
399 }
400
401 return "";
402}
403
412static std::string parse_external_mcp_config(
413 ryml::ConstNodeRef node,
414 ExternalMCPConfig& config)
415{
416 extract(node, "enabled", config.enabled);
417 extract(node, "rate_limit", config.rate_limit);
418 extract(node, "ask_streaming", config.ask_streaming);
419
420 if (node.is_map() && node.has_child("socket_path")
421 && !node["socket_path"].val_is_null()) {
422 std::filesystem::path tmp;
423 extract_path(node, "socket_path", tmp);
424 config.socket_path = tmp;
425 }
426
427 return "";
428}
429
438static std::string parse_mcp_config(
439 ryml::ConstNodeRef node,
440 MCPConfig& config)
441{
442 extract(node, "enable_entropic", config.enable_entropic);
443 extract(node, "enable_filesystem", config.enable_filesystem);
444 extract(node, "enable_bash", config.enable_bash);
445 extract(node, "enable_git", config.enable_git);
446 extract(node, "enable_diagnostics", config.enable_diagnostics);
447 extract(node, "enable_web", config.enable_web);
448 extract(node, "server_timeout_seconds", config.server_timeout_seconds);
449 extract(node, "working_dir", config.working_dir);
450
451 // gh#133 (v2.10.1): mcp.plugins — dlopen-loaded in-process server .so
452 // paths. expand_home so `~/...` works like every other path key.
453 std::vector<std::string> plugin_paths;
454 if (extract_string_list(node, "plugins", plugin_paths)) {
455 config.plugins.clear();
456 for (const auto& p : plugin_paths) {
457 config.plugins.push_back(expand_home(std::filesystem::path(p)));
458 }
459 }
460
461 if (node.has_child("filesystem")) {
462 parse_filesystem_config(node["filesystem"], config.filesystem);
463 }
464 if (node.has_child("external")) {
465 parse_external_mcp_config(node["external"], config.external);
466 }
467
468 return "";
469}
470
479static std::string parse_generation_config(
480 ryml::ConstNodeRef node,
481 GenerationConfig& config)
482{
483 extract(node, "max_tokens", config.max_tokens);
484 extract(node, "default_temperature", config.default_temperature);
485 extract(node, "default_top_p", config.default_top_p);
486 // gh#80 (v2.5.0): thinking-budget knobs (validated in build_loop_config).
487 extract(node, "budget_mode", config.budget_mode);
488 extract(node, "budget_limit", config.budget_limit);
489 // gh#110 (v2.9.6): batch vs streaming agent-loop delivery mode.
490 extract(node, "stream_output", config.stream_output);
491 return "";
492}
493
502static std::string parse_lsp_config(
503 ryml::ConstNodeRef node,
504 LSPConfig& config)
505{
506 extract(node, "enabled", config.enabled);
507 extract(node, "python_enabled", config.python_enabled);
508 extract(node, "c_enabled", config.c_enabled);
509 return "";
510}
511
520static std::string parse_prompt_cache_config(
521 ryml::ConstNodeRef node,
522 PromptCacheConfig& config)
523{
524 extract(node, "enabled", config.enabled);
525 extract(node, "log_hits", config.log_hits);
526
527 int max_bytes_int = 0;
528 if (extract(node, "max_bytes", max_bytes_int)) {
529 config.max_bytes = static_cast<size_t>(max_bytes_int);
530 }
531
532 return "";
533}
534
543 ryml::ConstNodeRef node,
545{
546 extract(node, "enabled", config.enabled);
547 extract(node, "max_revisions", config.max_revisions);
548 extract(node, "max_critique_tokens", config.max_critique_tokens);
549 extract(node, "temperature", config.temperature);
550 extract(node, "enable_thinking", config.enable_thinking);
551 extract(node, "priority", config.priority);
552 extract(node, "grammar_key", config.grammar_key);
553 extract_string_list(node, "skip_tiers", config.skip_tiers);
554}
555
575 ryml::ConstNodeRef node,
576 const BundledModels& registry,
577 SpeculativeConfig& config)
578{
579 extract(node, "enabled", config.enabled);
580 extract(node, "n_draft", config.n_draft);
581 extract(node, "mtp", config.mtp); // gh#106: drive the MTP head path
582 // Nested ModelConfig — every llama.cpp knob is consumer-tunable
583 // via `inference.speculative.draft.<field>`. Defaults come from
584 // `make_default_draft_model_config()` (gpu_layers=0, flash_attn=
585 // false, context_length=8192, n_threads=4). (v2.1.11)
586 if (node.has_child("draft")) {
587 auto err = parse_model_config(
588 node["draft"], registry, config.draft);
589 if (!err.empty()) {
590 s_log->warn("inference.speculative.draft parse: {}", err);
591 }
592 }
593}
594
612 ryml::ConstNodeRef root,
613 const BundledModels& registry,
614 ParsedConfig& config)
615{
616 if (!root.has_child("inference")) { return; }
617 auto inf = root["inference"];
618 if (inf.has_child("prompt_cache"))
619 parse_prompt_cache_config(inf["prompt_cache"], config.prompt_cache);
620 if (inf.has_child("speculative"))
621 parse_speculative_config(inf["speculative"], registry,
622 config.inference.speculative);
623}
624
634 ryml::ConstNodeRef root,
635 const BundledModels& registry,
636 ParsedConfig& config)
637{
638 if (root.has_child("generation"))
639 parse_generation_config(root["generation"], config.generation);
640 if (root.has_child("permissions"))
641 parse_permissions_config(root["permissions"], config.permissions);
642 if (root.has_child("mcp"))
643 parse_mcp_config(root["mcp"], config.mcp);
644 if (root.has_child("compaction"))
645 parse_compaction_config(root["compaction"], config.compaction);
646 if (root.has_child("lsp"))
647 parse_lsp_config(root["lsp"], config.lsp);
648 parse_inference_subsections(root, registry, config);
649 if (root.has_child("constitutional_validation"))
651 root["constitutional_validation"],
653}
654
662static void extract_scalar_fields(ryml::ConstNodeRef root,
663 ParsedConfig& config)
664{
665 extract(root, "log_level", config.log_level);
666 extract(root, "inject_model_context", config.inject_model_context);
667 extract(root, "vram_reserve_mb", config.vram_reserve_mb);
668 extract_path(root, "config_dir", config.config_dir);
669 extract_path(root, "log_dir", config.log_dir);
670 extract(root, "ggml_logging", config.ggml_logging);
671 extract_path(root, "llama_log_path", config.llama_log_path); // gh#23 v2.3.24
672 extract(root, "console_logging", config.console_logging);
673
674 extract_tri_state_path(root, "constitution",
675 config.constitution, config.constitution_disabled);
676 /* gh#141 (v2.11.0): app_context accepts an object carrying the text
677 * inline, for consumers that hold it in memory and cannot write it to
678 * disk. Checked BEFORE the tri-state path parse, because the object form
679 * has no meaning as a path and would otherwise be stringified into one. */
680 if (!extract_inline_content(root, "app_context",
681 config.app_context_content)) {
682 extract_tri_state_path(root, "app_context",
683 config.app_context, config.app_context_disabled);
684 }
685}
686
695 ryml::ConstNodeRef root,
696 const BundledModels& registry,
697 ParsedConfig& config)
698{
699 parse_optional_subsections(root, registry, config);
700 extract_scalar_fields(root, config);
701}
702
712static std::string parse_top_sections(
713 ryml::ConstNodeRef root,
714 const BundledModels& registry,
715 ParsedConfig& config)
716{
717 std::string err;
718 if (root.has_child("models")) {
719 err = parse_models_config(root["models"], registry, config.models);
720 }
721 if (err.empty() && root.has_child("routing")) {
722 err = parse_routing_config(root["routing"], config.routing);
723 }
724 if (err.empty()) {
725 parse_optional_sections(root, registry, config);
726 }
727 return err;
728}
729
747 const std::filesystem::path& path,
748 const BundledModels& registry,
749 ParsedConfig& config)
750{
751 auto content = read_file(path);
752 if (content.empty()) {
753 return "cannot read config file: " + path.string();
754 }
755
756 ryml::Tree tree = ryml::parse_in_arena(
757 ryml::to_csubstr(path.string()),
758 ryml::to_csubstr(content));
759 ryml::ConstNodeRef root = tree.rootref();
760 if (!root.is_map()) {
761 return "config file root is not a YAML mapping: " + path.string();
762 }
763
764 return parse_top_sections(root, registry, config);
765}
766
776static std::string load_bundled_default(
777 const std::filesystem::path& global_path,
778 const BundledModels& registry,
779 ParsedConfig& config)
780{
781 auto data_dir = resolve_data_dir(config);
782 auto bundled = data_dir / "default_config.yaml";
783 if (!std::filesystem::exists(bundled)) {
784 s_log->warn("No config found (checked global, project, bundled)");
785 return "";
786 }
787 s_log->info("No user config — loading bundled default: {}",
788 bundled.string());
789 auto err = parse_config_file(bundled, registry, config);
790 if (!err.empty()) { return "bundled default: " + err; }
791
792 // Auto-create global config so this only happens once. gh#109
793 // follow-up: load_layered() intentionally passes an empty global_path
794 // (it doesn't want the auto-create side effect) — exists("") is
795 // always false, so without this guard every fresh-install call
796 // through load_layered (no ~/.entropic/config.yaml yet) threw
797 // filesystem_error trying to create_directories("").
798 if (!global_path.empty() && !std::filesystem::exists(global_path)) {
799 auto parent = global_path.parent_path();
800 std::filesystem::create_directories(parent);
801 std::filesystem::copy_file(bundled, global_path);
802 s_log->info("Created {}", global_path.string());
803 }
804 return "";
805}
806
818static std::string load_config_layers(
819 const std::filesystem::path& global_path,
820 const std::filesystem::path& project_path,
821 const BundledModels& registry,
822 ParsedConfig& config)
823{
824 bool have_config = false;
825 std::string err;
826
827 if (std::filesystem::exists(global_path)) {
828 s_log->info("Loading global config: {}", global_path.string());
829 err = parse_config_file(global_path, registry, config);
830 if (!err.empty()) { return "global config: " + err; }
831 have_config = true;
832 }
833
834 if (err.empty() && std::filesystem::exists(project_path)) {
835 s_log->info("Loading project config: {}", project_path.string());
836 err = parse_config_file(project_path, registry, config);
837 if (!err.empty()) { return "project config: " + err; }
838 have_config = true;
839 }
840
841 return have_config ? ""
842 : load_bundled_default(global_path, registry, config);
843}
844
857std::string load_config(
858 const std::filesystem::path& global_path,
859 const std::filesystem::path& project_path,
860 const BundledModels& registry,
861 ParsedConfig& config)
862{
863 auto err = load_config_layers(global_path, project_path, registry, config);
864 if (!err.empty()) { return err; }
865
866 apply_env_overrides(config);
867
868 std::vector<std::string> warnings;
869 err = validate_config(config, warnings);
870 for (const auto& w : warnings) { s_log->warn("{}", w); }
871 return err;
872}
873
885 const std::filesystem::path& path,
886 const BundledModels& registry,
887 ParsedConfig& config)
888{
889 auto err = parse_config_file(path, registry, config);
890 if (!err.empty()) {
891 return err;
892 }
893
894 apply_env_overrides(config);
895
896 std::vector<std::string> warnings;
897 err = validate_config(config, warnings);
898 for (const auto& w : warnings) {
899 s_log->warn("{}", w);
900 }
901 return err;
902}
903
913 const std::string& name,
914 const nlohmann::json& entry) {
915 ExternalServerEntry result;
916 std::string type = entry.value("type", std::string("stdio"));
917 if (type == "sse") {
918 result.url = entry.value("url", std::string{});
919 } else {
920 result.command = entry.value("command", std::string{});
921 if (entry.contains("args") && entry["args"].is_array()) {
922 for (const auto& a : entry["args"]) {
923 result.args.push_back(a.get<std::string>());
924 }
925 }
926 if (entry.contains("env") && entry["env"].is_object()) {
927 for (auto it = entry["env"].begin();
928 it != entry["env"].end(); ++it) {
929 result.env[it.key()] = it.value().get<std::string>();
930 }
931 }
932 }
933 s_log->info("Discovered external MCP server: {} (type={})",
934 name, type);
935 return result;
936}
937
960static std::optional<nlohmann::json> read_mcp_servers(
961 const std::filesystem::path& path) {
962 std::ifstream f(path);
963 nlohmann::json j;
964 if (f.is_open()) {
965 std::stringstream ss;
966 ss << f.rdbuf();
967 j = nlohmann::json::parse(ss.str(), nullptr, false);
968 }
969 bool valid = f.is_open() && !j.is_discarded() && j.is_object()
970 && j.contains("mcpServers")
971 && j["mcpServers"].is_object();
972 if (!valid) {
973 s_log->warn(".mcp.json missing/malformed: {}", path.string());
974 return std::nullopt;
975 }
976 return j["mcpServers"];
977}
978
985 const std::filesystem::path& project_dir,
986 ParsedConfig& config) {
987 if (project_dir.empty()) { return; }
988 auto path = project_dir / ".mcp.json";
989 if (!std::filesystem::exists(path)) { return; }
990 auto servers = read_mcp_servers(path);
991 if (!servers) { return; }
992
993 int added = 0;
994 for (auto it = servers->begin(); it != servers->end(); ++it) {
995 const std::string& name = it.key();
996 if (config.mcp.external_servers.count(name)) {
997 s_log->info(".mcp.json: {} already in config, skipping", name);
998 continue;
999 }
1001 name, it.value());
1002 added++;
1003 }
1004 s_log->info("Loaded {} external MCP server(s) from {}",
1005 added, path.string());
1006}
1007
1014static std::string load_global_layer(
1015 const BundledModels& registry, ParsedConfig& config)
1016{
1017 const char* home = getenv("HOME");
1018 std::string err;
1019 if (home) {
1020 auto path = std::filesystem::path(home) / ".entropic" / "config.yaml";
1021 if (std::filesystem::exists(path)) {
1022 s_log->info("Loading global config: {}", path.string());
1023 err = parse_config_file(path, registry, config);
1024 if (!err.empty()) {
1025 err = "global config: " + err;
1026 }
1027 }
1028 }
1029 return err;
1030}
1031
1050static std::filesystem::path resolve_consumer_defaults(
1051 const std::filesystem::path& consumer_defaults)
1052{
1053 // Input already usable (empty, absolute, or found at CWD) → passthrough.
1054 if (consumer_defaults.empty()
1055 || consumer_defaults.is_absolute()
1056 || std::filesystem::exists(consumer_defaults)) {
1057 return consumer_defaults;
1058 }
1059 // Fall back to <install-prefix>/share/entropic/<filename>. Uses
1060 // the same librentropic.so location trick — dladdr on any address
1061 // in this translation unit resolves to the .so's on-disk path.
1062 std::filesystem::path result = consumer_defaults;
1063 Dl_info info = {};
1064 if (dladdr(reinterpret_cast<void*>(&resolve_consumer_defaults), &info) != 0
1065 && info.dli_fname != nullptr) {
1066 std::error_code ec;
1067 auto lib_path = std::filesystem::absolute(info.dli_fname, ec);
1068 if (!ec) {
1069 auto candidate = lib_path.parent_path().parent_path()
1070 / "share" / "entropic" / consumer_defaults.filename();
1071 if (std::filesystem::exists(candidate)) {
1072 result = candidate;
1073 }
1074 }
1075 }
1076 return result;
1077}
1078
1092static bool yaml_has_key(const std::filesystem::path& path,
1093 const char* key) {
1094 auto content = read_file(path);
1095 if (content.empty()) { return false; }
1096 auto tree = ryml::parse_in_arena(
1097 ryml::to_csubstr(path.string()),
1098 ryml::to_csubstr(content));
1099 auto root = tree.rootref();
1100 return root.is_map() && root.has_child(ryml::to_csubstr(key));
1101}
1102
1120 const std::filesystem::path& consumer_defaults_in,
1121 const BundledModels& registry, ParsedConfig& config)
1122{
1123 auto consumer_defaults = resolve_consumer_defaults(consumer_defaults_in);
1124 if (consumer_defaults.empty() || !std::filesystem::exists(consumer_defaults)) {
1125 return;
1126 }
1127 s_log->info("Loading consumer defaults: {}", consumer_defaults.string());
1128
1129 // Replace semantics: if consumer defines models or routing,
1130 // clear existing tiers/routing so global ones don't leak through.
1131 if (yaml_has_key(consumer_defaults, "models")) {
1132 s_log->info("Consumer defines models: — replacing global tiers");
1133 config.models.tiers.clear();
1134 config.models.router.reset();
1135 config.models.default_tier.clear();
1136 }
1137 if (yaml_has_key(consumer_defaults, "routing")) {
1138 config.routing = RoutingConfig{};
1139 }
1140
1141 auto err = parse_config_file(consumer_defaults, registry, config);
1142 if (!err.empty()) {
1143 s_log->warn("Consumer defaults failed: {}", err);
1144 }
1145}
1146
1165static std::string load_project_layer(
1166 const std::filesystem::path& project_dir,
1167 const BundledModels& registry, ParsedConfig& config)
1168{
1169 std::string err;
1170 if (!project_dir.empty()) {
1171 auto path = project_dir / "config.local.yaml";
1172 if (std::filesystem::exists(path)) {
1173 s_log->info("Loading project config: {}", path.string());
1174 if (yaml_has_key(path, "models")) {
1175 s_log->info("Project defines models: — replacing prior tiers");
1176 config.models.tiers.clear();
1177 config.models.router.reset();
1178 config.models.default_tier.clear();
1179 }
1180 if (yaml_has_key(path, "routing")) {
1181 config.routing = RoutingConfig{};
1182 }
1183 err = parse_config_file(path, registry, config);
1184 if (!err.empty()) {
1185 err = "project config: " + err;
1186 }
1187 }
1188 }
1189 return err;
1190}
1191
1220std::string load_layered(
1221 const std::filesystem::path& project_dir,
1222 const std::filesystem::path& consumer_defaults,
1223 const BundledModels& registry,
1224 ParsedConfig& config)
1225{
1226 auto err = load_global_layer(registry, config);
1227 if (err.empty()) {
1228 load_consumer_layer(consumer_defaults, registry, config);
1229 if (!project_dir.empty() && config.log_dir.empty()) {
1230 config.log_dir = project_dir;
1231 }
1232 err = load_project_layer(project_dir, registry, config);
1233 }
1234 if (err.empty() && config.models.tiers.empty()) {
1235 // v2.11.1: parse into a SCRATCH config and transplant only the model
1236 // block. Previously this re-parsed the whole bundled default straight
1237 // over `config`, AFTER the project layer had already run — so a
1238 // fresh-install machine silently lost every setting an explicit layer
1239 // had established. data/default_config.yaml sets `mcp.enable_bash:
1240 // true`, so a project asking for false got true back, and REQ-CFG-001's
1241 // most-specific-layer-wins rule was violated by the layer that is
1242 // supposed to be LEAST specific.
1243 //
1244 // Invisible on any machine whose ~/.entropic/config.yaml declares tiers
1245 // (the fallback never fires there) and hit on every CI runner and every
1246 // fresh install. The condition that triggers it is a MISSING MODEL SET,
1247 // so the model set is the only thing it may supply.
1248 ParsedConfig fallback;
1249 fallback.config_dir = config.config_dir;
1250 err = load_bundled_default(std::filesystem::path{}, registry, fallback);
1251 if (err.empty()) {
1252 config.models = std::move(fallback.models);
1253 }
1254 }
1255 if (err.empty()) {
1256 apply_env_overrides(config);
1257 discover_mcp_json(project_dir, config);
1258 }
1259 return err;
1260}
1261
1276static std::string parse_config_string(
1277 const std::string& content,
1278 const BundledModels& registry,
1279 ParsedConfig& config)
1280{
1281 if (content.empty()) {
1282 return "config string is empty";
1283 }
1284
1285 ryml::Tree tree = ryml::parse_in_arena(
1286 ryml::to_csubstr("<json>"),
1287 ryml::to_csubstr(content));
1288 auto root = tree.rootref();
1289 if (!root.is_map()) {
1290 return "config string root is not a mapping";
1291 }
1292
1293 std::string err;
1294 if (root.has_child("models")) {
1295 err = parse_models_config(root["models"], registry, config.models);
1296 }
1297 if (err.empty() && root.has_child("routing")) {
1298 err = parse_routing_config(root["routing"], config.routing);
1299 }
1300 if (err.empty()) {
1301 parse_optional_sections(root, registry, config);
1302 }
1303 return err;
1304}
1305
1317 const std::string& content,
1318 const BundledModels& registry,
1319 ParsedConfig& config)
1320{
1321 auto err = parse_config_string(content, registry, config);
1322 if (!err.empty()) {
1323 return err;
1324 }
1325
1326 apply_env_overrides(config);
1327
1328 std::vector<std::string> warnings;
1329 err = validate_config(config, warnings);
1330 for (const auto& w : warnings) {
1331 s_log->warn("{}", w);
1332 }
1333 return err;
1334}
1335
1336} // namespace entropic::config
Bundled model registry loaded from bundled_models.yaml.
std::filesystem::path resolve(const std::string &value) const
Resolve a model reference to a filesystem path.
std::string find_by(const std::string &family, const std::string &size_label, const std::string &quant) const
Look up a registry key by (family, size, quant) (gh#62).
static void extract_scalar_fields(ryml::ConstNodeRef root, ParsedConfig &config)
Extract the top-level scalar/path config fields.
Definition loader.cpp:662
static std::string parse_config_string(const std::string &content, const BundledModels &registry, ParsedConfig &config)
Parse a config string (YAML or JSON) and overlay onto config.
Definition loader.cpp:1276
static std::string load_project_layer(const std::filesystem::path &project_dir, const BundledModels &registry, ParsedConfig &config)
Load the project-local layer if present.
Definition loader.cpp:1165
static std::string parse_external_mcp_config(ryml::ConstNodeRef node, ExternalMCPConfig &config)
Parse the external MCP section from a YAML node.
Definition loader.cpp:412
static bool yaml_has_key(const std::filesystem::path &path, const char *key)
Load the consumer/app defaults layer if present (non-fatal).
Definition loader.cpp:1092
static std::string load_global_layer(const BundledModels &registry, ParsedConfig &config)
Load the global user config layer if present.
Definition loader.cpp:1014
static std::string parse_generation_config(ryml::ConstNodeRef node, GenerationConfig &config)
Parse the generation section from a YAML node.
Definition loader.cpp:479
static std::string parse_permissions_config(ryml::ConstNodeRef node, PermissionsConfig &config)
Parse the permissions section from a YAML node.
Definition loader.cpp:368
static void parse_inference_subsections(ryml::ConstNodeRef root, const BundledModels &registry, ParsedConfig &config)
Parse the nested optional config sub-sections.
Definition loader.cpp:611
static void load_consumer_layer(const std::filesystem::path &consumer_defaults_in, const BundledModels &registry, ParsedConfig &config)
Load the consumer-defaults layer.
Definition loader.cpp:1119
static std::filesystem::path resolve_consumer_defaults(const std::filesystem::path &consumer_defaults)
Resolve a relative consumer_defaults path against install prefix.
Definition loader.cpp:1050
static std::optional< nlohmann::json > read_mcp_servers(const std::filesystem::path &path)
Discover and parse .mcp.json from the project directory.
Definition loader.cpp:960
static void parse_optional_sections(ryml::ConstNodeRef root, const BundledModels &registry, ParsedConfig &config)
Parse optional config sections that don't return errors.
Definition loader.cpp:694
static void parse_speculative_config(ryml::ConstNodeRef node, const BundledModels &registry, SpeculativeConfig &config)
Parse inference.speculative YAML node into SpeculativeConfig.
Definition loader.cpp:574
static void parse_constitutional_validation_config(ryml::ConstNodeRef node, ConstitutionalValidationConfig &config)
Parse constitutional_validation section.
Definition loader.cpp:542
static void parse_optional_subsections(ryml::ConstNodeRef root, const BundledModels &registry, ParsedConfig &config)
Parse the nested optional config sub-sections.
Definition loader.cpp:633
static std::string parse_mcp_config(ryml::ConstNodeRef node, MCPConfig &config)
Parse the MCP section from a YAML node.
Definition loader.cpp:438
static std::string load_bundled_default(const std::filesystem::path &global_path, const BundledModels &registry, ParsedConfig &config)
Load bundled default config when no user config exists.
Definition loader.cpp:776
static void parse_tier_speculative_override(ryml::ConstNodeRef node, TierConfig &config)
Parse the per-tier speculative.mtp override (gh#108, v2.9.4).
Definition loader.cpp:98
static std::string parse_filesystem_config(ryml::ConstNodeRef node, FilesystemConfig &config)
Parse the filesystem section from a YAML node.
Definition loader.cpp:386
static std::string parse_compaction_config(ryml::ConstNodeRef node, CompactionConfig &config)
Parse the compaction section from a YAML node.
Definition loader.cpp:344
static void discover_mcp_json(const std::filesystem::path &project_dir, ParsedConfig &config)
Discover + merge external MCP servers from <dir>/.mcp.json.
Definition loader.cpp:984
static std::string parse_top_sections(ryml::ConstNodeRef root, const BundledModels &registry, ParsedConfig &config)
Parse the top-level models/routing/optional sections.
Definition loader.cpp:712
static std::string parse_routing_config(ryml::ConstNodeRef node, RoutingConfig &config)
Parse the routing section from a YAML node.
Definition loader.cpp:318
static void parse_sampler_overrides(ryml::ConstNodeRef node, TierConfig &config)
Read per-tier sampler overrides from a YAML model/tier node.
Definition loader.cpp:66
static void parse_model_runtime_knobs(ryml::ConstNodeRef node, ModelConfig &config)
Read the model-runtime knobs (KV cache, batching, threads).
Definition loader.cpp:34
static std::string parse_prompt_cache_config(ryml::ConstNodeRef node, PromptCacheConfig &config)
Parse prompt_cache config from inference YAML section.
Definition loader.cpp:520
static std::string resolve_model_path(ryml::ConstNodeRef node, const BundledModels &registry, ModelConfig &config)
Resolve config.path from an explicit path: or a selector (gh#62).
Definition loader.cpp:120
static std::string parse_model_config(ryml::ConstNodeRef node, const BundledModels &registry, ModelConfig &config)
Parse a ModelConfig from a YAML node.
Definition loader.cpp:165
static ExternalServerEntry parse_mcp_json_entry(const std::string &name, const nlohmann::json &entry)
Parse a single mcpServers entry from .mcp.json.
Definition loader.cpp:912
static std::string load_config_layers(const std::filesystem::path &global_path, const std::filesystem::path &project_path, const BundledModels &registry, ParsedConfig &config)
Load global, project, and bundled config layers in order.
Definition loader.cpp:818
static std::string parse_tier_config(ryml::ConstNodeRef node, const BundledModels &registry, TierConfig &config)
Parse a TierConfig from a YAML node.
Definition loader.cpp:209
static std::string parse_models_config(ryml::ConstNodeRef node, const BundledModels &registry, ModelsConfig &config)
Parse the models section from a YAML node.
Definition loader.cpp:271
static std::string parse_lsp_config(ryml::ConstNodeRef node, LSPConfig &config)
Parse the LSP section from a YAML node.
Definition loader.cpp:502
Config loader — YAML to C++ structs with validation.
ENTROPIC_EXPORT std::string load_config(const std::filesystem::path &global_path, const std::filesystem::path &project_path, const BundledModels &registry, ParsedConfig &config)
Load config using layered resolution.
Definition loader.cpp:857
ENTROPIC_EXPORT std::string load_config_from_string(const std::string &content, const BundledModels &registry, ParsedConfig &config)
Load config from a YAML/JSON string (no layering).
Definition loader.cpp:1316
ENTROPIC_EXPORT std::filesystem::path resolve_data_dir(const ParsedConfig &config)
Resolve the bundled data directory.
Definition data_dir.cpp:81
ENTROPIC_EXPORT std::string parse_config_file(const std::filesystem::path &path, const BundledModels &registry, ParsedConfig &config)
Parse a config YAML file and overlay onto existing config.
Definition loader.cpp:746
ENTROPIC_EXPORT void apply_env_overrides(ParsedConfig &config)
Apply ENTROPIC_* environment variable overrides.
ENTROPIC_EXPORT std::string load_config_from_file(const std::filesystem::path &path, const BundledModels &registry, ParsedConfig &config)
Load config from a single YAML file (no layering).
Definition loader.cpp:884
ENTROPIC_EXPORT std::string load_layered(const std::filesystem::path &project_dir, const std::filesystem::path &consumer_defaults, const BundledModels &registry, ParsedConfig &config)
Load config with consumer defaults + global + project layers.
Definition loader.cpp:1220
spdlog initialization and logger access.
ENTROPIC_EXPORT std::shared_ptr< spdlog::logger > get(const std::string &name)
Get or create a named logger.
Definition logging.cpp:211
Auto-compaction configuration.
Definition config.h:742
bool save_full_history
Save full history before compaction.
Definition config.h:748
bool notify_user
Notify user on compaction.
Definition config.h:747
float warning_threshold_percent
Warning trigger (0.3–0.9)
Definition config.h:750
int preserve_recent_turns
Turns to preserve (1–10)
Definition config.h:745
int summary_max_tokens
Summary max tokens (500–4000)
Definition config.h:746
int tool_result_ttl
Tool result TTL in turns (>= 1; v2.1.3 #6: gated on fill, no upper bound)
Definition config.h:749
float threshold_percent
Compaction trigger (0.5–0.99)
Definition config.h:744
bool enabled
Enable auto-compaction.
Definition config.h:743
Constitutional validation pipeline configuration.
Definition config.h:824
int max_revisions
Max re-generation attempts (0 = critique only)
Definition config.h:826
int priority
Hook priority (higher = later)
Definition config.h:830
bool enable_thinking
Enable think-blocks for critique (default OFF)
Definition config.h:829
float temperature
Critique generation temperature.
Definition config.h:828
bool enabled
Global enable/disable (default OFF)
Definition config.h:825
int max_critique_tokens
Token budget for critique generation.
Definition config.h:827
std::string grammar_key
Grammar registry key.
Definition config.h:831
std::vector< std::string > skip_tiers
Tiers exempt from validation (default: lead — streams before hook fires)
Definition config.h:833
External MCP server configuration (Entropic-as-server).
Definition config.h:642
bool ask_streaming
Route entropic.ask through entropic_run (false) or entropic_run_streaming (true, default).
Definition config.h:649
std::optional< std::filesystem::path > socket_path
Socket path (nullopt = derived)
Definition config.h:644
int rate_limit
Requests per minute (1–100)
Definition config.h:645
bool enabled
Enable external MCP.
Definition config.h:643
Configuration for a single external MCP server entry.
Definition config.h:667
std::string command
Stdio command (empty for SSE)
Definition config.h:668
std::vector< std::string > args
Stdio command arguments.
Definition config.h:669
std::string url
SSE endpoint URL (empty for stdio)
Definition config.h:671
std::unordered_map< std::string, std::string > env
Stdio environment variables.
Definition config.h:670
Filesystem MCP server configuration.
Definition config.h:629
bool diagnostics_on_edit
Proactive diagnostics on edit/write.
Definition config.h:630
bool allow_outside_root
Allow file ops outside workspace root.
Definition config.h:633
float diagnostics_timeout
Diagnostics timeout (0.1–5.0)
Definition config.h:632
std::optional< int > max_read_bytes
Max file read size (nullopt = derive from context)
Definition config.h:634
float max_read_context_pct
Max context % for single file read.
Definition config.h:635
bool fail_on_errors
Rollback edit if it introduces errors.
Definition config.h:631
Generation parameters configuration (top-level defaults).
Definition config.h:757
int max_tokens
Default max tokens (64–32768)
Definition config.h:758
float default_top_p
Default top_p (0.0–1.0)
Definition config.h:760
int budget_limit
gh#80 (v2.5.0) budget ceiling: generated tokens (budget_mode "tokens") or wall-clock seconds (budget_...
Definition config.h:775
std::string budget_mode
gh#80 (v2.5.0) thinking-budget mode: "off" (default), "tokens", or "wall_clock".
Definition config.h:768
float default_temperature
Default temperature (0.0–2.0)
Definition config.h:759
bool stream_output
gh#110 (v2.9.6) agent-loop token delivery mode: true streams tokens via the per-token callback path,...
Definition config.h:785
SpeculativeConfig speculative
Speculative decoding (gh#36)
Definition config.h:974
LSP integration configuration.
Definition config.h:802
bool python_enabled
Enable Python LSP.
Definition config.h:804
bool enabled
Enable LSP integration.
Definition config.h:803
bool c_enabled
Enable C/C++ LSP.
Definition config.h:805
MCP server configuration.
Definition config.h:678
bool enable_entropic
Enable entropic internal server (handoff, delegate, pipeline)
Definition config.h:679
std::vector< std::filesystem::path > plugins
In-process MCP server plugin .so paths (gh#133, v2.10.1).
Definition config.h:699
FilesystemConfig filesystem
Filesystem server config.
Definition config.h:685
bool enable_filesystem
Enable filesystem server.
Definition config.h:680
std::unordered_map< std::string, ExternalServerEntry > external_servers
Named external servers.
Definition config.h:702
bool enable_git
Enable git server.
Definition config.h:682
bool enable_diagnostics
Enable diagnostics server.
Definition config.h:683
int server_timeout_seconds
Server timeout (5–300)
Definition config.h:687
bool enable_bash
Enable bash server.
Definition config.h:681
ExternalMCPConfig external
External MCP server config (Entropic-as-server)
Definition config.h:686
std::string working_dir
Server working directory (empty = CWD) (v2.0.4)
Definition config.h:688
bool enable_web
Enable web server.
Definition config.h:684
Model configuration for a single tier.
Definition config.h:154
std::filesystem::path mmproj_path
Vision projector GGUF path.
Definition config.h:250
int gpu_layers
GPU offload layers (-1 = all)
Definition config.h:158
int reasoning_budget
Think token budget (-1 = unlimited)
Definition config.h:163
int n_ubatch
Physical micro-batch size for prompt processing (gh#23 MVP item 5).
Definition config.h:178
int context_length
Context window size (512–131072)
Definition config.h:157
std::filesystem::path path
Resolved model file path.
Definition config.h:155
float rope_freq_scale
RoPE frequency scaling factor (gh#23 MVP item 10).
Definition config.h:228
int main_gpu
Primary GPU index for model load (gh#23 MVP item 7).
Definition config.h:200
int n_threads
CPU threads (0 = auto-detect)
Definition config.h:180
bool offload_kqv
Offload KQV ops (incl.
Definition config.h:208
int n_parallel
Max parallel sequences per context (gh#23 MVP item 11).
Definition config.h:238
std::string tensor_split
Multi-GPU tensor split ratios (empty = single GPU)
Definition config.h:181
std::string cache_type_k
KV cache key quantization type.
Definition config.h:164
bool keep_warm
Pre-warm model at startup.
Definition config.h:159
std::string cache_type_v
KV cache value quantization type.
Definition config.h:165
std::string split_mode
Multi-GPU split mode for model load (gh#23 MVP item 6).
Definition config.h:192
int n_batch
Batch size for prompt processing.
Definition config.h:166
bool flash_attn
Enable flash attention.
Definition config.h:239
bool use_mlock
Lock model in system RAM.
Definition config.h:160
std::optional< std::vector< std::string > > allowed_tools
Tool whitelist (nullopt = all)
Definition config.h:242
std::string adapter
Chat adapter name.
Definition config.h:156
float rope_freq_base
RoPE base frequency override (gh#23 MVP item 9).
Definition config.h:218
Configuration for all models (tiers + router).
Definition config.h:572
std::optional< ModelConfig > router
Router model (separate from tiers)
Definition config.h:574
std::unordered_map< std::string, TierConfig > tiers
Tier name → config.
Definition config.h:573
std::string default_tier
Default tier name.
Definition config.h:575
Full parsed configuration.
Definition config.h:985
int vram_reserve_mb
Reserved VRAM headroom (MB, 0–65536)
Definition config.h:1016
PermissionsConfig permissions
Tool permissions.
Definition config.h:989
PromptCacheConfig prompt_cache
Prompt KV cache settings.
Definition config.h:993
std::optional< std::filesystem::path > app_context
App context: nullopt = disabled by default.
Definition config.h:1002
std::optional< std::string > app_context_content
Inline app_context text, supplied instead of a path (gh#141).
Definition config.h:1013
CompactionConfig compaction
Auto-compaction settings.
Definition config.h:991
RoutingConfig routing
Routing rules.
Definition config.h:987
InferenceConfig inference
Inference-side knobs (currently speculative decoding only).
Definition config.h:1051
ModelsConfig models
Tiers + router.
Definition config.h:986
LSPConfig lsp
LSP integration.
Definition config.h:992
ConstitutionalValidationConfig constitutional_validation
Constitutional validation pipeline settings.
Definition config.h:1047
std::filesystem::path log_dir
Session log directory (session.log + session_model.log).
Definition config.h:1023
bool ggml_logging
Enable ggml/llama.cpp logging to llama_ggml.log in log_dir.
Definition config.h:1027
GenerationConfig generation
Default generation params.
Definition config.h:988
std::string log_level
Log level string.
Definition config.h:995
MCPConfig mcp
MCP server settings.
Definition config.h:990
bool console_logging
Emit engine spdlog output to the stderr console sink.
Definition config.h:1044
bool inject_model_context
Auto-inject model context into system prompt.
Definition config.h:1015
std::filesystem::path llama_log_path
Override path for ggml/llama log when ggml_logging == true (gh#23 MVP item 12, v2....
Definition config.h:1037
bool app_context_disabled
true if app_context explicitly disabled
Definition config.h:1003
std::optional< std::filesystem::path > constitution
Constitution: nullopt = bundled default, disabled = explicit false.
Definition config.h:998
bool constitution_disabled
true if constitution explicitly disabled
Definition config.h:999
std::filesystem::path config_dir
Config dir — base for bundled data discovery.
Definition config.h:1019
Tool permission configuration.
Definition config.h:619
std::vector< std::string > deny
Denied tool patterns (glob)
Definition config.h:621
std::vector< std::string > allow
Allowed tool patterns (glob)
Definition config.h:620
bool auto_approve
Skip confirmation prompts.
Definition config.h:622
Prompt caching configuration.
Definition config.h:272
size_t max_bytes
Maximum cache RAM (512 MB default)
Definition config.h:273
bool log_hits
Log cache hit/miss at INFO level.
Definition config.h:275
bool enabled
Master switch (false = no caching)
Definition config.h:274
Configuration for model routing.
Definition config.h:607
std::string fallback_tier
Fallback when routing fails.
Definition config.h:609
std::unordered_map< std::string, std::vector< std::string > > handoff_rules
Tier handoff rules.
Definition config.h:612
bool enabled
Enable routing.
Definition config.h:608
std::optional< std::string > classification_prompt
Custom prompt (nullopt = auto)
Definition config.h:610
std::unordered_map< std::string, std::string > tier_map
Classification → tier mapping.
Definition config.h:611
Speculative-decoding configuration (inference.speculative.
Definition config.h:932
bool enabled
Master switch (off by default)
Definition config.h:933
bool mtp
gh#106 (v2.9.0): drive MTP (the draft is a trunk-sharing head via ctx_other) instead of the gh#36 sep...
Definition config.h:940
int n_draft
Window size (proposed tokens).
Definition config.h:934
ModelConfig draft
Full ModelConfig for the draft model.
Definition config.h:961
Tier-specific model configuration.
Definition config.h:442
std::optional< float > frequency_penalty
gh#85
Definition config.h:495
std::optional< bool > routable
None = defer to identity frontmatter.
Definition config.h:447
std::optional< std::filesystem::path > identity
Identity prompt path (nullopt = bundled)
Definition config.h:443
std::optional< float > temperature
Per-tier sampler temperature from identity frontmatter (gh#82).
Definition config.h:479
std::optional< float > top_p
Per-tier sampler knobs from identity frontmatter (gh#85).
Definition config.h:491
std::optional< float > repeat_penalty
Per-tier repeat_penalty + enable_thinking from identity frontmatter (gh#86).
Definition config.h:502
std::optional< std::string > auto_chain
Target tier name (nullopt = defer to identity)
Definition config.h:446
bool identity_disabled
true if identity explicitly disabled
Definition config.h:444
std::optional< float > min_p
gh#85
Definition config.h:493
std::optional< bool > require_tool_call
gh#134 (v2.10.4): require this tier to end every turn with a tool call.
Definition config.h:532
std::optional< std::string > tool_call_mode
Per-tier tool-call generation mode (gh#103).
Definition config.h:510
std::optional< float > presence_penalty
gh#85
Definition config.h:494
std::optional< int > max_output_tokens
Per-tier max output tokens from identity frontmatter (gh#82).
Definition config.h:484
std::optional< int > top_k
gh#85
Definition config.h:492
std::optional< bool > enable_thinking
gh#86
Definition config.h:503
std::vector< std::string > capabilities
Declared tier capabilities (gh#41).
Definition config.h:472
std::optional< std::filesystem::path > grammar
Grammar file path.
Definition config.h:445
std::optional< bool > speculative_mtp
Per-tier MTP speculative-decode override (gh#108).
Definition config.h:519
Config validation functions.
ENTROPIC_EXPORT std::string validate_config(const ParsedConfig &config, std::vector< std::string > &warnings)
Validate the full ParsedConfig.
Definition validate.cpp:326
std::filesystem::path expand_home(const std::filesystem::path &p)
Expand ~ to home directory in a path.
Definition yaml_util.cpp:61
bool extract_string_list_map(ryml::ConstNodeRef node, c4::csubstr key, std::unordered_map< std::string, std::vector< std::string > > &out)
Extract a map of string to list of strings from a YAML mapping.
bool extract_inline_content(ryml::ConstNodeRef node, c4::csubstr key, std::optional< std::string > &out)
Extract inline text from an object-form value (gh#141).
std::string read_file(const std::filesystem::path &path)
Read a file into a string.
Definition yaml_util.cpp:39
bool extract_path(ryml::ConstNodeRef node, c4::csubstr key, std::filesystem::path &out)
Extract a filesystem path with ~ expansion.
bool extract_string_list(ryml::ConstNodeRef node, c4::csubstr key, std::vector< std::string > &out)
Extract a vector of strings from a YAML sequence node.
bool extract_string_map(ryml::ConstNodeRef node, c4::csubstr key, std::unordered_map< std::string, std::string > &out)
Extract a map of string to string from a YAML mapping node.
bool extract_string_list_opt(ryml::ConstNodeRef node, c4::csubstr key, std::optional< std::vector< std::string > > &out)
Extract an optional vector of strings.
bool extract_tri_state_path(ryml::ConstNodeRef node, c4::csubstr key, std::optional< std::filesystem::path > &out, bool &disabled)
Extract a tri-state path (null = default, false = disabled, string = path).
std::string to_string(c4::csubstr s)
Convert ryml csubstr to std::string.
Definition yaml_util.cpp:27
bool extract(ryml::ConstNodeRef node, c4::csubstr key, std::string &out)
Extract a string value from a YAML node.
Definition yaml_util.cpp:85
ryml extraction helpers for config parsing.