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;
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;
92 <<
",\"token_count\":" << token_count
93 <<
",\"max_tokens\":0"
94 <<
",\"threshold_percent\":" <<
config.threshold_percent
95 <<
",\"force\":true}";
118 while (
c.pos <
c.data.size() &&
isspace(
c.data[
c.pos])) {
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;
153 if (
c.pos >=
c.data.size() ||
c.data[
c.pos] !=
'"') {
158 while (
c.pos <
c.data.size() &&
c.data[
c.pos] !=
'"') {
159 if (
c.data[
c.pos] ==
'\\' &&
c.pos + 1 <
c.data.size()) {
163 out +=
c.data[
c.pos++];
166 if (
c.pos >=
c.data.size())
return false;
178 char open =
c.data[
c.pos];
179 char close = (open ==
'[') ?
']' :
'}';
182 while (
c.pos <
c.data.size() && depth > 0) {
183 char ch =
c.data[
c.pos];
189 if (
ch == open) ++depth;
190 else if (
ch == close) --depth;
203 if (
c.pos >=
c.data.size())
return;
204 char ch =
c.data[
c.pos];
210 if (
ch ==
'[' ||
ch ==
'{') {
214 while (
c.pos <
c.data.size()
215 &&
c.data[
c.pos] !=
',' &&
c.data[
c.pos] !=
'}'
216 &&
c.data[
c.pos] !=
']') {
236 if (
c.pos >=
c.data.size() ||
c.data[
c.pos] !=
':')
return false;
256 while (
c.pos <
c.data.size() &&
c.data[
c.pos] !=
'}') {
258 if (
c.data[
c.pos] ==
',') { ++
c.pos;
continue; }
263 if (
c.pos <
c.data.size()) ++
c.pos;
279 if (!text.empty()) text +=
' ';
303 while (
c.pos <
c.data.size()) {
305 char ch =
c.data[
c.pos];
306 if (
ch !=
',')
return ch;
344 if (
c.pos >=
c.data.size() ||
c.data[
c.pos] !=
'[') {
352 while (
ok &&
ch ==
'{') {
356 if (
ok &&
ch ==
']') { ++
c.pos; }
357 return ok &&
ch ==
']';
376 if (
ok &&
c.pos <
c.data.size() &&
c.data[
c.pos] ==
':') {
382 if (!
ok)
return false;
386 }
else if (key ==
"content") {
388 if (
c.pos <
c.data.size() &&
c.data[
c.pos] ==
'[') {
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) {
428 while (
ok &&
c.pos <
c.data.size()) {
430 if (
c.data[
c.pos] ==
']')
break;
431 if (
c.data[
c.pos] ==
',') { ++
c.pos;
continue; }
432 ok = (
c.data[
c.pos] ==
'{');
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,
493 logger->error(
"NULL compactor for identity '{}'", identity);
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,
545 std::shared_lock lock(mutex_);
546 auto it = compactors_.find(identity);
547 if (
it != compactors_.end()) {
552 it = compactors_.find(
"");
553 if (
it != compactors_.end()) {
555 source =
"global_custom";
562 return run_default(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;
647 const std::vector<Message>& messages,
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.
@ optional
optional: every escape asks the host's path approver
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.
std::string config
–config FILE
Auto-compaction configuration.
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.