Replace nlohmann/json with native C++20 JSON engine, cut build RAM/time, fix 32-bit build, enable -O2 -march=native

This commit is contained in:
Luxferre
2026-10-14 08:36:39 +03:00
parent 3e70adeb8b
commit 39d7640a5a
11 changed files with 910 additions and 25919 deletions
+33 -77
View File
@@ -1,67 +1,17 @@
/* session.cpp — session persistence, loading, compaction. */
#include "bantam.hpp"
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <dirent.h>
#include <fstream>
#include <memory>
#include <nlohmann/json.hpp>
#include <sys/stat.h>
#include <unistd.h>
using json = nlohmann::json;
static const char *compaction_prompt =
"You are now acting as a compaction engine. Summarize the preceding conversation concisely but completely, preserving all important facts, decisions, code snippets, tool outputs, errors, and current task state so work can seamlessly continue. Output only the summary.";
/* ----------------------------- messages ------------------------------- */
static void serialize_msg_full(json &obj, const Message &m) {
obj["role"] = m.role.empty() ? "" : m.role;
if (!m.content.empty()) obj["content"] = m.content;
if (!m.reasoning.empty()) obj["reasoning_content"] = m.reasoning;
if (!m.calls.empty()) {
json arr = json::array();
for (auto &tc : m.calls) {
json t;
if (!tc.id.empty()) t["id"] = tc.id;
t["type"] = "function";
json fn;
fn["name"] = tc.name.empty() ? "" : tc.name;
fn["arguments"] = tc.arguments.empty() ? "{}" : tc.arguments;
t["function"] = fn;
arr.push_back(t);
}
obj["tool_calls"] = arr;
}
if (!m.tool_call_id.empty()) obj["tool_call_id"] = m.tool_call_id;
}
static void parse_msg_full(const json &obj, Message &m) {
m = Message{};
if (!obj.is_object()) return;
if (obj.contains("role") && obj["role"].is_string()) m.role = obj["role"].get<std::string>();
else m.role = "user";
if (obj.contains("content") && obj["content"].is_string()) m.content = obj["content"].get<std::string>();
if (obj.contains("reasoning_content") && obj["reasoning_content"].is_string())
m.reasoning = obj["reasoning_content"].get<std::string>();
if (obj.contains("tool_call_id") && obj["tool_call_id"].is_string())
m.tool_call_id = obj["tool_call_id"].get<std::string>();
if (obj.contains("tool_calls") && obj["tool_calls"].is_array()) {
for (auto &tc : obj["tool_calls"]) {
if (!tc.is_object()) continue;
ToolCall t;
if (tc.contains("id") && tc["id"].is_string()) t.id = tc["id"].get<std::string>();
if (tc.contains("function") && tc["function"].is_object()) {
auto fn = tc["function"];
if (fn.contains("name") && fn["name"].is_string()) t.name = fn["name"].get<std::string>();
if (fn.contains("arguments") && fn["arguments"].is_string()) t.arguments = fn["arguments"].get<std::string>();
}
m.calls.push_back(std::move(t));
}
}
}
/* ----------------------------- paths ---------------------------------- */
static std::string home_dir() {
const char *h = std::getenv("HOME");
@@ -79,7 +29,7 @@ void save_session(const Messages &msgs, std::string_view sid,
std::string id = (!sid.empty()) ? std::string(sid) : project_id();
std::string d = sessions_dir();
std::string path = d + "/" + id + ".json";
json root = json::object();
Json root = Json::object();
root["id"] = id;
time_t now = time(nullptr);
struct tm tm_buf{};
@@ -89,17 +39,14 @@ void save_session(const Messages &msgs, std::string_view sid,
root["created"] = ts;
std::string sum = summary_of(msgs);
root["summary"] = sum;
json arr = json::array();
Json arr = Json::array();
for (auto &m : msgs.items) {
json mo;
serialize_msg_full(mo, m);
arr.push_back(mo);
}
root["messages"] = arr;
{
std::ofstream f(path, std::ios::binary);
if (f) f << root.dump(2, ' ', false, json::error_handler_t::replace);
Json mo;
serialize_message_json(mo, m);
arr.push_back(std::move(mo));
}
root["messages"] = std::move(arr);
write_file_str(path, root.dump(2));
if (out_id) *out_id = id;
if (out_summary) *out_summary = sum;
}
@@ -121,30 +68,39 @@ std::vector<Session> sessions_load() {
closedir(dir);
}
}
std::sort(files.begin(), files.end()); /* alphasort (ascending) */
for (size_t i = 1; i < files.size(); i++) {
for (size_t j = i; j > 0 && files[j - 1] > files[j]; j--) {
std::swap(files[j - 1], files[j]);
}
}
for (auto &nm : files) {
std::string full = d + "/" + nm;
std::ifstream f(full, std::ios::binary);
if (!f) continue;
std::string buf((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
json root;
try { root = json::parse(buf); } catch (...) { continue; }
if (!root.is_object()) continue;
std::string buf;
if (!read_file_str(full, buf)) continue;
Json root_mut;
if (!Json::parse(buf, root_mut) || !root_mut.is_object()) continue;
const Json &root = root_mut;
Session s;
if (root.contains("id") && root["id"].is_string()) s.id = root["id"].get<std::string>();
if (root.contains("created") && root["created"].is_string()) s.created = root["created"].get<std::string>();
if (root.contains("summary") && root["summary"].is_string()) s.summary = root["summary"].get<std::string>();
if (root.contains("messages") && root["messages"].is_array()) {
for (auto &mo : root["messages"]) {
Message m; parse_msg_full(mo, m); s.messages.items.push_back(std::move(m));
s.id = root["id"].str();
s.created = root["created"].str();
s.summary = root["summary"].str();
const Json &marr = root["messages"];
if (marr.is_array()) {
for (auto &mo : marr.aval) {
Message m;
parse_message_json(mo, m);
s.messages.items.push_back(std::move(m));
}
}
if (s.summary.empty()) s.summary = "(no summary)";
ss.push_back(std::move(s));
}
/* sort by id descending (newest first) */
std::sort(ss.begin(), ss.end(),
[](const Session &a, const Session &b) { return a.id > b.id; });
for (size_t i = 1; i < ss.size(); i++) {
for (size_t j = i; j > 0 && ss[j - 1].id < ss[j].id; j--) {
std::swap(ss[j - 1], ss[j]);
}
}
return ss;
}