17static auto logger = log::get(
"identity_manager");
21 "default",
"none",
"all",
"router"};
40 const std::unordered_map<std::string,
42 for (
const auto& [phase_name, phase] : phases) {
43 if (phase_name.empty()) {
44 return "Phase name cannot be empty";
46 if (phase.max_output_tokens <= 0) {
47 return "Phase '" + phase_name
48 +
"' max_output_tokens must be > 0";
65 if (!phase_err.empty()) {
return phase_err; }
86 const std::unordered_map<std::string, IdentityConfig>& identities) {
88 logger->warn(
"Dynamic identity creation disabled");
92 logger->warn(
"Identity limit reached: {}",
96 bool exists = identities.count(config.
name) > 0;
98 logger->warn(
"Identity already exists: {}", config.
name);
115 const std::string& name,
116 const std::unordered_map<std::string, IdentityConfig>& identities,
117 std::unordered_map<std::string,
119 out_it = identities.find(name);
120 if (out_it == identities.end()) {
148 grammar_iface_ = iface;
171 const std::vector<IdentityConfig>& identities) {
172 std::unique_lock lock(identities_mutex_);
174 for (
const auto&
id : identities) {
177 identities_[cfg.
name] = std::move(cfg);
180 logger->info(
"Loaded {} static identities", loaded);
181 router_dirty_.store(
true, std::memory_order_release);
182 fire_cache_invalidator();
196 std::unique_lock lock(identities_mutex_);
198 config, config_, identities_);
200 auto err = validate(config,
false);
202 logger->warn(
"Identity validation failed: {}", err);
207 register_mcp_keys(cfg);
208 identities_[cfg.
name] = std::move(cfg);
209 router_dirty_.store(
true, std::memory_order_release);
210 fire_cache_invalidator();
211 logger->info(
"Identity created: name='{}', routable={}, "
229 const std::string& name,
231 std::unique_lock lock(identities_mutex_);
232 std::unordered_map<std::string,
236 auto err = validate(config,
true);
238 logger->warn(
"Identity update validation failed: {}", err);
241 unregister_mcp_keys(name);
245 register_mcp_keys(cfg);
246 identities_[name] = std::move(cfg);
247 router_dirty_.store(
true, std::memory_order_release);
248 fire_cache_invalidator();
249 logger->info(
"Dynamic identity updated: {}", name);
263 std::unique_lock lock(identities_mutex_);
264 std::unordered_map<std::string,
268 if (in_use_checker_ && in_use_checker_(name, in_use_user_data_)) {
271 unregister_mcp_keys(name);
272 identities_.erase(it);
273 router_dirty_.store(
true, std::memory_order_release);
274 fire_cache_invalidator();
275 logger->info(
"Dynamic identity destroyed: {}", name);
289 const std::string& name)
const {
290 std::shared_lock lock(identities_mutex_);
291 auto it = identities_.find(name);
292 return (it != identities_.end()) ? &it->second :
nullptr;
305 std::shared_lock lock(identities_mutex_);
306 return identities_.count(name) > 0;
318 std::shared_lock lock(identities_mutex_);
319 std::vector<std::string> names;
320 names.reserve(identities_.size());
321 for (
const auto& [k, _] : identities_) {
335std::vector<const IdentityConfig*>
337 std::shared_lock lock(identities_mutex_);
338 std::vector<const IdentityConfig*> result;
339 for (
const auto& [_, cfg] : identities_) {
340 if (cfg.routable && !cfg.interstitial) {
341 result.push_back(&cfg);
356 std::shared_lock lock(identities_mutex_);
357 return identities_.size();
367 std::shared_lock lock(identities_mutex_);
369 for (
const auto& [_, cfg] : identities_) {
386 return router_dirty_.load(std::memory_order_acquire);
395 router_dirty_.store(
false, std::memory_order_release);
408 bool (*checker)(
const std::string& name,
void* user_data),
410 in_use_checker_ = checker;
411 in_use_user_data_ = user_data;
428 cache_invalidator_ = cb;
429 cache_invalidator_data_ = user_data;
437void IdentityManager::fire_cache_invalidator() {
438 if (cache_invalidator_ !=
nullptr) {
439 cache_invalidator_(cache_invalidator_data_);
452std::string IdentityManager::validate_name(
const std::string& name) {
454 return "Identity name cannot be empty";
457 return "Identity name must match ^[a-z][a-z0-9_-]{0,63}$";
459 bool reserved = std::find(
463 ?
"Identity name '" + name +
"' is reserved" :
"";
474std::string IdentityManager::validate(
475 const IdentityConfig& config,
bool is_update)
const {
477 auto name_err = validate_name(config.name);
478 if (!name_err.empty()) {
return name_err; }
480 if (config.focus.empty()) {
481 return "Identity must have at least one focus keyword";
483 bool grammar_missing = !config.grammar_id.empty()
486 config.grammar_id, grammar_iface_.
user_data);
487 return grammar_missing
488 ?
"Grammar '" + config.grammar_id +
"' not found in registry"
500void IdentityManager::register_mcp_keys(
const IdentityConfig& config) {
501 if (config.mcp_keys.empty()) {
return; }
504 for (
const auto& key : config.mcp_keys) {
505 if (mcp_iface_.
grant) {
506 mcp_iface_.
grant(config.name, key.tool_pattern,
507 static_cast<int>(key.level),
519void IdentityManager::unregister_mcp_keys(
const std::string& name) {
size_t count() const
Get the total number of identities.
void set_mcp_interface(const MCPKeyInterface &iface)
Set MCP key management interface.
size_t count_dynamic() const
Get the number of dynamic identities.
void set_cache_invalidator(void(*cb)(void *user_data), void *user_data)
Set a callback invoked whenever an identity changes.
const IdentityConfig * get(const std::string &name) const
Get identity config by name.
IdentityManager(const IdentityManagerConfig &config)
Construct with configuration.
bool is_router_dirty() const
Check if the router classification prompt needs rebuilding.
entropic_error_t destroy(const std::string &name)
Destroy a dynamic identity.
void clear_router_dirty()
Clear the dirty flag (called after router prompt rebuild).
entropic_error_t update(const std::string &name, const IdentityConfig &config)
Update an existing dynamic identity.
size_t load_static(const std::vector< IdentityConfig > &identities)
Load static identities from config loader.
bool has(const std::string &name) const
Check if an identity exists.
void set_in_use_checker(bool(*checker)(const std::string &name, void *user_data), void *user_data)
Set the in-use checker callback.
std::vector< const IdentityConfig * > list_routable() const
List only routable identities (for classification prompt).
void set_grammar_interface(const GrammarValidationInterface &iface)
Set grammar validation interface.
entropic_error_t create(const IdentityConfig &config)
Create a new dynamic identity.
std::vector< std::string > list() const
List all identity names.
entropic_error_t
Error codes returned by all C API functions.
@ ENTROPIC_ERROR_IN_USE
Resource is currently active and cannot be removed (v1.9.6)
@ ENTROPIC_ERROR_ALREADY_EXISTS
Named resource already exists (v1.9.6)
@ ENTROPIC_ERROR_IDENTITY_NOT_FOUND
Identity name not in config (v1.8.9)
@ ENTROPIC_ERROR_LIMIT_REACHED
Resource limit exceeded (e.g., max_identities) (v1.9.6)
@ ENTROPIC_ERROR_INVALID_CONFIG
Config validation failed (missing fields, bad values)
@ ENTROPIC_ERROR_PERMISSION_DENIED
Tool call blocked by permission manager.
IdentityManager – lifecycle management for static and dynamic identities.
spdlog initialization and logger access.
Activate model on GPU (WARM → ACTIVE).
static const std::regex s_name_regex("^[a-z][a-z0-9_-]{0,63}$")
Compiled regex for identity name validation.
static entropic_error_t check_mutable(const std::string &name, const std::unordered_map< std::string, IdentityConfig > &identities, std::unordered_map< std::string, IdentityConfig >::const_iterator &out_it)
Check that a mutable identity exists and is dynamic.
@ DYNAMIC
Created at runtime via API.
@ STATIC
Loaded from YAML frontmatter file at startup.
static std::string validate_phases(const std::unordered_map< std::string, PhaseConfig > &phases)
Validate all phases in an identity config.
static std::string validate_fields(const IdentityConfig &config)
Validate phases and adapter_path fields.
static const std::vector< std::string > s_reserved_names
Reserved identity names that cannot be used.
static entropic_error_t check_create_preconditions(const IdentityConfig &config, const IdentityManagerConfig &mgr_config, const std::unordered_map< std::string, IdentityConfig > &identities)
Check create preconditions before validation.
Grammar validation callbacks injected by the facade.
void * user_data
Opaque pointer (GrammarRegistry*)
bool(* has_grammar)(const std::string &key, void *user_data)
Check if a grammar key exists in the registry.
Full identity configuration.
bool routable
Participates in tier routing.
std::string name
Unique identity name (e.g., "eng", "npc_blacksmith")
IdentityOrigin origin
How this identity was created.
std::string adapter_path
LoRA adapter path (v1.9.2, empty = base model)
std::string system_prompt
Full system prompt text (markdown body)
std::unordered_map< std::string, PhaseConfig > phases
Named inference phases.
Configuration for the identity manager.
size_t max_identities
Maximum total identities (static + dynamic)
bool allow_dynamic
Master toggle for dynamic identity creation.
MCP key management callbacks injected by the facade.
void(* unregister_identity)(const std::string &name, void *user_data)
Remove an identity's key set.
bool(* is_enforced)(const std::string &name, void *user_data)
Check if an identity has a key set registered.
void(* register_identity)(const std::string &name, void *user_data)
Register an empty key set for an identity.
void * user_data
Opaque pointer (MCPAuthorizationManager*)
void(* grant)(const std::string &name, const std::string &pattern, int level, void *user_data)
Grant a key pattern to an identity.
Inference parameters for a single identity phase.