Entropic 2.11.1
Local-first agentic inference engine
Loading...
Searching...
No Matches
bundled_models.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
9#include <entropic/entropic_config.h>
11#include "yaml_util.h"
12
13#include <cstdlib>
14#include <vector>
15#include <dlfcn.h>
16
17static auto s_log = entropic::log::get("config");
18
19namespace entropic::config {
20
21namespace {
22
40std::filesystem::path share_dir_from_library()
41{
42 Dl_info info = {};
43 if (dladdr(reinterpret_cast<void*>(&share_dir_from_library), &info) == 0
44 || info.dli_fname == nullptr) {
45 return {};
46 }
47 std::error_code ec;
48 auto abs_lib = std::filesystem::absolute(
49 std::filesystem::path(info.dli_fname), ec);
50 if (ec) {
51 return {};
52 }
53 return abs_lib.parent_path().parent_path() / "share" / "entropic";
54}
55
56} // namespace
57
66static std::string parse_bundled_entry(ryml::ConstNodeRef child,
67 BundledModelEntry& entry)
68{
69 entry.key = to_string(child.key());
70 extract(child, "name", entry.name);
71 extract(child, "url", entry.url);
72 extract(child, "size_gb", entry.size_gb);
73 extract(child, "adapter", entry.adapter);
74 extract(child, "description", entry.description);
75 extract(child, "mmproj_key", entry.mmproj_key);
76 // gh#62 (v2.3.0): optional structured selectors. Entries that
77 // don't declare them stay queryable by flat key only.
78 extract(child, "provider", entry.provider);
79 extract(child, "family", entry.family);
80 extract(child, "size_label", entry.size_label);
81 extract(child, "quant", entry.quant);
82 if (entry.name.empty()) {
83 return "bundled model '" + entry.key + "' missing 'name'";
84 }
85 return "";
86}
87
95std::string BundledModels::load(const std::filesystem::path& path)
96{
97 auto content = read_file(path);
98 if (content.empty()) {
99 return "cannot read " + path.string();
100 }
101
102 ryml::Tree tree = ryml::parse_in_arena(
103 ryml::to_csubstr(path.string()),
104 ryml::to_csubstr(content));
105 ryml::ConstNodeRef root = tree.rootref();
106 if (!root.is_map()) {
107 return "bundled_models.yaml root is not a mapping";
108 }
109
110 std::string err;
111 for (auto child : root) {
112 BundledModelEntry entry;
113 err = parse_bundled_entry(child, entry);
114 if (!err.empty()) { break; }
115 s_log->info("Registered bundled model: {} -> {} ({:.1f} GB)",
116 entry.key, entry.name, entry.size_gb);
117 entries_[entry.key] = std::move(entry);
118 }
119 return err;
120}
121
129bool BundledModels::contains(const std::string& key) const
130{
131 return entries_.count(key) > 0;
132}
133
141const BundledModelEntry* BundledModels::get(const std::string& key) const
142{
143 auto it = entries_.find(key);
144 if (it == entries_.end()) {
145 return nullptr;
146 }
147 return &it->second;
148}
149
163 const std::string& family,
164 const std::string& size_label,
165 const std::string& quant) const
166{
167 for (const auto& [key, entry] : entries_) {
168 if (entry.family == family
169 && entry.size_label == size_label
170 && entry.quant == quant) {
171 return key;
172 }
173 }
174 return "";
175}
176
200std::filesystem::path BundledModels::resolve(const std::string& value) const
201{
202 auto it = entries_.find(value);
203 if (it == entries_.end()) {
204 return expand_home(std::filesystem::path(value));
205 }
206
207 const auto filename = it->second.name + ".gguf";
208 std::filesystem::path result;
209 const char* reason = nullptr;
210
211 // Priority 1: explicit operator override via env var always wins.
212 // Deliberately does NOT check existence — the user may be pointing
213 // at a download destination that hasn't been populated yet.
214 if (const char* env = std::getenv("ENTROPIC_MODEL_DIR"); env && *env) {
215 result = std::filesystem::path(env) / filename;
216 reason = "ENTROPIC_MODEL_DIR";
217 } else {
218 // Priority 2-3: user home, then system. First existing file wins;
219 // if neither exists, home_path is returned so error messages
220 // point at the most-likely-intended location.
221 const auto home_path =
222 expand_home("~") / ".entropic" / "models" / filename;
223 const auto sys_path =
224 std::filesystem::path("/opt/entropic/models") / filename;
225 if (std::filesystem::is_regular_file(sys_path)
226 && !std::filesystem::is_regular_file(home_path)) {
227 result = sys_path;
228 reason = "/opt/entropic/models";
229 } else {
230 result = home_path;
231 reason = std::filesystem::is_regular_file(home_path)
232 ? "~/.entropic/models"
233 : "fallback (file not found)";
234 }
235 }
236
237 s_log->info("Model '{}' resolved to {} ({})",
238 value, result.string(), reason);
239 return result;
240}
241
248const std::unordered_map<std::string, BundledModelEntry>&
250{
251 return entries_;
252}
253
270 // Discovery order mirrors data_dir.cpp::resolve_data_dir so the
271 // registry is found the same way as prompts/grammars/schemas:
272 // 1. ENTROPIC_DATA_DIR env (explicit operator override)
273 // 2. binary-relative via dladdr (portable across install prefixes)
274 // 3-5. compile-time install path, source tree, CWD (dev/build-host)
275 // Pre-v2.3.6 this list was only 3-5 — purely compile-time/CWD — so
276 // a relocated install (any machine that isn't the build host, incl.
277 // container-staged release binaries) silently loaded zero models.
278 std::vector<std::filesystem::path> candidates;
279 if (const char* env = std::getenv("ENTROPIC_DATA_DIR"); env && *env) {
280 candidates.emplace_back(std::filesystem::path(env) / "bundled_models.yaml");
281 }
282 if (auto from_lib = share_dir_from_library(); !from_lib.empty()) {
283 candidates.emplace_back(from_lib / "bundled_models.yaml");
284 }
285 candidates.emplace_back(
286 std::filesystem::path(CONFIG_ENTROPIC_DATA_DIR) / "bundled_models.yaml");
287 candidates.emplace_back(
288 std::filesystem::path(CONFIG_ENTROPIC_SOURCE_DATA_DIR) / "bundled_models.yaml");
289 candidates.emplace_back("data/bundled_models.yaml");
290
291 for (const auto& path : candidates) {
292 if (std::filesystem::is_regular_file(path)) {
293 auto err = load(path);
294 if (err.empty()) {
295 s_log->info("pre-loaded {} bundled model(s) from {}",
296 entries_.size(), path.string());
297 return "";
298 }
299 s_log->warn("bundled models load failed: {} — {}", path.string(), err);
300 }
301 }
302 return "bundled_models.yaml not found in default paths";
303}
304
305} // namespace entropic::config
static std::string parse_bundled_entry(ryml::ConstNodeRef child, BundledModelEntry &entry)
Parse one bundled-model YAML entry into entry.
Bundled model registry — resolves keys to filesystem paths.
bool contains(const std::string &key) const
Check if a key exists in the registry.
const std::unordered_map< std::string, BundledModelEntry > & entries() const
Get all entries.
std::string auto_discover_and_load()
Auto-discover and load 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).
const BundledModelEntry * get(const std::string &key) const
Get entry by key.
std::string load(const std::filesystem::path &path)
Load registry from YAML file.
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
Entry in the bundled model registry.
std::string size_label
e.g. "0_8b", "2b", "4b", "9b", "e2b", "nano_4b"
std::string key
Registry key (e.g., "primary")
std::string description
Human-readable description.
double size_gb
Model size in GB.
std::string provider
e.g. "unsloth"
std::string mmproj_key
Paired mmproj registry key, or empty (gh#42, v2.1.8).
std::string family
e.g. "qwen3_5", "gemma4", "nemotron3"
std::string quant
e.g. "Q8_0", "Q4_K_M", "UD-IQ4_XS"
std::string name
Model filename stem (e.g., "Qwen3.5-35B-A3B-UD-IQ3_XXS")
std::string adapter
Adapter name (e.g., "qwen35")
std::filesystem::path expand_home(const std::filesystem::path &p)
Expand ~ to home directory in a path.
Definition yaml_util.cpp:61
std::string read_file(const std::filesystem::path &path)
Read a file into a string.
Definition yaml_util.cpp:39
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.