27 const char* esc =
nullptr;
29 case '"': esc =
"\\\"";
break;
30 case '\\': esc =
"\\\\";
break;
31 case '\n': esc =
"\\n";
break;
32 case '\r': esc =
"\\r";
break;
33 case '\t': esc =
"\\t";
break;
47 std::ostringstream oss;
48 for (
char c : input) {
50 if (esc !=
nullptr) { oss << esc; }
64 const std::vector<Message>& messages) {
65 std::ostringstream oss;
67 for (
size_t i = 0; i < messages.size(); ++i) {
68 if (i > 0) oss <<
',';
69 oss <<
"{\"role\":\"" << messages[i].role
70 <<
"\",\"content\":\""
88 const std::string& identity,
90 std::ostringstream oss;
91 oss <<
"{\"identity\":\"" <<
json_escape(identity) <<
"\""
92 <<
",\"token_count\":" << token_count
93 <<
",\"max_tokens\":0"
95 <<
",\"force\":true}";
133 case '"': result =
'"';
break;
134 case '\\': result =
'\\';
break;
135 case 'n': result =
'\n';
break;
136 case 'r': result =
'\r';
break;
137 case 't': result =
'\t';
break;
166 if (c.
pos >= c.
data.size())
return false;
179 char close = (open ==
'[') ?
']' :
'}';
182 while (c.
pos < c.
data.size() && depth > 0) {
189 if (ch == open) ++depth;
190 else if (ch == close) --depth;
203 if (c.
pos >= c.
data.size())
return;
210 if (ch ==
'[' || ch ==
'{') {
231 std::string& type_val,
232 std::string& text_val) {
254 std::string& type_val,
255 std::string& text_val) {
258 if (c.
data[c.
pos] ==
',') { ++c.
pos;
continue; }
276 const std::string& text_val,
278 if (type_val !=
"text" || text_val.empty())
return;
279 if (!text.empty()) text +=
' ';
303 while (c.
pos < c.
data.size()) {
306 if (ch !=
',')
return ch;
323 std::string type_val;
324 std::string text_val;
352 while (
ok && ch ==
'{') {
356 if (
ok && ch ==
']') { ++c.
pos; }
357 return ok && ch ==
']';
382 if (!
ok)
return false;
386 }
else if (key ==
"content") {
408 while (c.
pos < c.
data.size()) {
410 if (c.
data[c.
pos] ==
'}') { ++c.
pos;
return true; }
411 if (c.
data[c.
pos] ==
',') { ++c.
pos;
continue; }
426 std::vector<Message>& messages) {
430 if (c.
data[c.
pos] ==
']')
break;
431 if (c.
data[c.
pos] ==
',') { ++c.
pos;
continue; }
438 if (
ok) messages.push_back(std::move(msg));
452 std::vector<Message>& messages) {
453 if (json ==
nullptr || json[0] !=
'[')
return false;
455 std::string input(json);
470 : default_manager_(default_manager) {
471 default_compactor_ = [&mgr = default_manager_](
472 const std::vector<Message>& messages,
474 const std::string& ) {
475 return mgr.compact_messages(messages);
489 const std::string& identity,
492 if (compactor ==
nullptr) {
493 logger->error(
"NULL compactor for identity '{}'", identity);
500 entry.
cpp_fn = wrap_c_compactor(compactor, user_data);
502 std::unique_lock lock(mutex_);
503 compactors_[identity] = std::move(entry);
505 logger->info(
"Registered compactor for identity '{}'",
506 identity.empty() ?
"(global)" : identity);
518 const std::string& identity) {
519 std::unique_lock lock(mutex_);
520 compactors_.erase(identity);
522 logger->info(
"Deregistered compactor for identity '{}'",
523 identity.empty() ?
"(global)" : identity);
537 const std::string& identity,
538 const std::vector<Message>& messages,
542 bool is_custom =
false;
545 std::shared_lock lock(mutex_);
546 auto it = compactors_.find(identity);
547 if (it != compactors_.end()) {
548 selected = it->second.cpp_fn;
552 it = compactors_.find(
"");
553 if (it != compactors_.end()) {
554 selected = it->second.cpp_fn;
555 source =
"global_custom";
562 return run_default(identity, messages, config);
565 selected, source, identity, messages, config);
578 const std::string& identity,
579 const std::vector<Message>& messages,
581 auto result = default_compactor_(messages, config, identity);
582 result.identity = identity;
583 result.compactor_source =
"default";
584 result.custom_compactor_used =
false;
599CompactionResult CompactorRegistry::run_custom(
601 const std::string& source,
602 const std::string& identity,
603 const std::vector<Message>& messages,
604 const CompactionConfig& config) {
605 auto result = selected(messages, config, identity);
607 if (result.compacted) {
608 result.identity = identity;
609 result.compactor_source = source;
610 result.custom_compactor_used =
true;
614 logger->warn(
"Custom compactor '{}' failed for '{}', "
615 "falling back to default", source, identity);
616 return run_default(identity, messages, config);
627 const std::string& identity)
const {
628 std::shared_lock lock(mutex_);
629 if (compactors_.count(identity) > 0) {
632 return compactors_.count(
"") > 0;
646 return [compactor, user_data](
647 const std::vector<Message>& messages,
653 char* out_messages =
nullptr;
654 char* out_summary =
nullptr;
656 msg_json.c_str(), cfg_json.c_str(),
657 &out_messages, &out_summary, user_data);
668 if (out_summary !=
nullptr) {
672 if (out_messages !=
nullptr) {
675 static_cast<int>(result.
messages.size());
Manages automatic context compaction.
entropic_error_t register_compactor(const std::string &identity, entropic_compactor_fn compactor, void *user_data)
Register a compactor for a specific identity.
CompactionResult compact(const std::string &identity, const std::vector< Message > &messages, const CompactionConfig &config)
Run compaction using the appropriate compactor.
CompactorRegistry(CompactionManager &default_manager)
Construct with default compactor.
bool has_custom_compactor(const std::string &identity) const
Check if a custom compactor is registered for an identity.
entropic_error_t deregister_compactor(const std::string &identity)
Deregister a compactor for a specific identity.
Per-identity compactor registration and dispatch.
int(* entropic_compactor_fn)(const char *messages_json, const char *config_json, char **out_messages, char **out_summary, void *user_data)
Compactor function type.
entropic_error_t
Error codes returned by all C API functions.
@ ENTROPIC_ERROR_INVALID_CONFIG
Config validation failed (missing fields, bad values)
spdlog initialization and logger access.
ENTROPIC_EXPORT std::shared_ptr< spdlog::logger > get(const std::string &name)
Get or create a named logger.
Activate model on GPU (WARM → ACTIVE).
static void json_skip_composite(JsonCursor &c)
Skip a JSON array or object in cursor (nested-aware).
static void json_skip_value(JsonCursor &c)
Skip one JSON value (string, primitive, array, or object).
@ ok
Tool dispatched, returned non-empty content.
static std::string serialize_config(const CompactionConfig &config, const std::string &identity, int token_count)
Serialize compaction config + identity to JSON.
static const char * json_escape_char(char c)
Map a character to its JSON escape sequence.
static bool json_read_string(JsonCursor &c, std::string &out)
Read a JSON quoted string from cursor.
static bool json_extract_text_from_array(JsonCursor &c, std::string &text)
Extract concatenated text from a JSON content array.
static void json_skip_ws(JsonCursor &c)
Skip whitespace in a JSON cursor.
static bool json_parse_message(JsonCursor &c, Message &msg)
Parse one JSON object into a Message (role + content).
static std::string json_escape(const std::string &input)
Save pre-compaction snapshot via storage interface.
static bool json_parse_array(JsonCursor &c, std::vector< Message > &messages)
Parse message objects from a JSON array cursor.
static bool json_parse_content_part(JsonCursor &c, std::string &type_val, std::string &text_val)
Parse one content part object, extract type and text.
static std::string serialize_messages(const std::vector< Message > &messages)
Serialize messages to minimal JSON array.
static char json_skip_to_element(JsonCursor &c)
Read one content part object from an array cursor.
static bool json_read_content_part_field(JsonCursor &c, std::string &type_val, std::string &text_val)
Read one field of a content part object into type/text.
std::function< CompactionResult(const std::vector< Message > &messages, const CompactionConfig &config, const std::string &identity)> CompactorFn
Internal C++ compactor function type.
static char json_unescape_char(char ch)
Decode one JSON escape sequence character.
static bool parse_messages(const char *json, std::vector< Message > &messages)
Parse a JSON array of message objects.
static bool json_read_one_content_part(JsonCursor &c, std::string &text)
Parse one content part object and append text if applicable.
static void append_text_part(const std::string &type_val, const std::string &text_val, std::string &text)
Append text from a content part if type == "text".
static bool json_read_field(JsonCursor &c, Message &msg)
Read one key:value pair and apply to message fields.
Auto-compaction configuration.
float threshold_percent
Compaction trigger (0.5–0.99)
Result of a compaction operation.
std::string summary
Generated summary text.
std::vector< Message > messages
The compacted message list (v1.9.9)
int preserved_messages
Messages kept after compaction.
bool compacted
Whether compaction occurred.
A registered compactor entry.
entropic_compactor_fn c_callback
C function pointer (NULL for C++ compactors)
CompactorFn cpp_fn
C++ function (wraps c_callback or native)
void * user_data
Opaque data for C callback.
Lightweight cursor into a JSON string.
const std::string & data
Source string.
size_t pos
Current position.
A message in a conversation.
std::string content
Message text content (always populated)
std::string role
Message role.