init commit

This commit is contained in:
Luxferre
2026-10-14 00:01:03 +03:00
commit b6c71f2c2c
13 changed files with 29167 additions and 0 deletions
+195
View File
@@ -0,0 +1,195 @@
/* Bantam: tiny, powerful, DIY AI agent — modern C++20 port
* Created by Luxferre in 2026, released into the public domain.
*
* This is a C++20 rewrite of the pure-C port (bantam-c). It talks to any
* OpenAI-compatible /chat/completions API and implements the same agentic
* loop, tools, session management, markdown terminal rendering and
* raw-terminal line editor.
*
* Design goals relative to the C port:
* - Use standard, safe C++ containers (std::string, std::vector,
* std::map, std::span) instead of raw memory areas.
* - Eliminate hand-rolled allocation/string code; rely on RAII so there
* is far less room for memory-allocation related vulnerabilities.
* - Depend only on libcurl (HTTP) plus the vendored single-header
* nlohmann/json library; the unsafe json-c C API is no longer used.
*
* Feature macros: we rely on POSIX.1-2008 interfaces. */
#ifndef BANTAM_HPP
#define BANTAM_HPP
#include <cstdarg>
#include <cstdint>
#include <map>
#include <memory>
#include <span>
#include <string>
#include <string_view>
#include <vector>
/* Markdown renderer state (code-fence tracking). */
struct MdState {
bool in_code = false;
};
/* ------------------------------------------------------------------ */
/* Tool call / message / usage / config structures. */
/* ------------------------------------------------------------------ */
struct ToolCall {
std::string id;
std::string name;
std::string arguments; /* raw JSON text of the arguments object */
};
struct Message {
std::string role;
std::string content; /* empty when absent */
std::string reasoning; /* empty when absent */
std::vector<ToolCall> calls;
std::string tool_call_id; /* empty when absent (tool role only) */
int calls_shown = 0;
};
struct Messages {
std::vector<Message> items;
};
struct Usage {
int prompt_tokens = 0;
int completion_tokens = 0;
int total_tokens = 0;
int cached_tokens = 0;
int context_tokens = 0;
std::string model;
};
struct Cfg {
std::string endpoint;
std::string model;
std::string api_key;
std::string color;
double temperature = 0.7;
int timeout = 300;
int shell_timeout = 120;
int max_al_iterations = 1000;
int stream = 1; /* 0/1 */
int force_stream = -1; /* -1 = auto, 0/1 = force (compaction) */
int context_window = 262144;
int max_tool_res = 65536;
std::map<std::string, std::string> raw; /* raw key/value map */
};
struct Session {
std::string id;
std::string created;
std::string summary;
Messages messages;
};
/* ------------------------------------------------------------------ */
/* Globals. */
/* ------------------------------------------------------------------ */
extern int g_col; /* color enabled (0/1) */
extern volatile int g_interrupted; /* SIGINT flag */
/* ------------------------------------------------------------------ */
/* util.cpp */
/* ------------------------------------------------------------------ */
void setup_signal_handler();
int is_terminal(int fd);
int to_int_val(std::string_view s, int def);
std::string sfmt(const char *fmt, ...);
void str_appendf(std::string &s, const char *fmt, ...);
/* UTF-8 helpers. */
int utf8_decode(const unsigned char *p, size_t avail, size_t *plen);
std::string_view utf8_truncate(std::string_view s, size_t max_bytes);
std::string filter_text(std::string_view s);
int color_enabled(const Cfg &cfg);
std::string cstr(std::string_view text, std::span<const int> codes);
void cprint(std::string_view text, std::span<const int> codes);
int visible_len(std::string_view s);
int rune_width(int cp);
void mkdir_p(std::string_view dir);
std::string project_id();
std::string bantam_tmp_dir();
std::string tool_res_dir();
std::string md5_hex(std::string_view data);
/* ------------------------------------------------------------------ */
/* config.cpp */
/* ------------------------------------------------------------------ */
const std::string *cfg_raw_get(const Cfg &c, std::string_view k);
bool internal_key(std::string_view k);
Cfg get_cfg(std::string_view path);
int set_cfg(std::string_view path, std::string_view key, std::string_view val);
std::string config_path();
int fetch_context_window(Cfg &cfg);
std::string list_models(Cfg &cfg, int *errout);
std::string tools_dir(const Cfg &c);
std::string skills_dir(const Cfg &c);
/* ------------------------------------------------------------------ */
/* json.cpp — request building & response parsing (nlohmann/json). */
/* ------------------------------------------------------------------ */
std::string build_request_json(const Cfg &cfg, const Messages &msgs,
bool use_tools, bool stream);
bool parse_nonstream_response(std::string_view body, Message &out, Usage &usage);
/* ------------------------------------------------------------------ */
/* llm.cpp — HTTP client (libcurl), headers, backoff, dispatch. */
/* ------------------------------------------------------------------ */
int http_get(Cfg &cfg, std::string_view url, std::string &body, long &code,
std::string &errmsg);
int llm(Cfg &cfg, Messages &msgs, bool use_tools, Message &out, Usage &usage,
std::string &errmsg, volatile int *interrupted);
/* ------------------------------------------------------------------ */
/* tools.cpp */
/* ------------------------------------------------------------------ */
std::string shell_with_workdir(std::string_view cmd, std::string_view workdir,
int timeout, volatile int *interrupted);
std::string read_file_or_dir(std::string_view path, int offset, int limit,
int *style);
std::string write_file(std::string_view path, int offset, int del_bytes,
std::string_view content, int *err);
std::string offload_tool_result(std::string_view res, int max_res,
std::vector<std::string> &tmps);
void cleanup_temps(std::vector<std::string> &tmps);
/* ------------------------------------------------------------------ */
/* session.cpp */
/* ------------------------------------------------------------------ */
void save_session(const Messages &msgs, std::string_view sid,
std::string *out_id, std::string *out_summary);
std::vector<Session> sessions_load();
std::unique_ptr<Messages> load_session(std::string_view sid, std::string *err);
std::unique_ptr<Session> find_project_session();
void autosave(const Messages &msgs);
std::unique_ptr<Messages> compact(Cfg &cfg, const Messages &msgs,
std::string *summary, std::string *err,
volatile int *interrupted);
std::string summary_of(const Messages &msgs);
/* ------------------------------------------------------------------ */
/* render.cpp — terminal markdown rendering. */
/* ------------------------------------------------------------------ */
void render_md_print(std::string_view text);
void render_md_line_print(std::string_view line, MdState &st);
void render_table_print(const std::vector<std::string> &lines);
bool is_table_line(std::string_view line);
/* ------------------------------------------------------------------ */
/* editor.cpp — GNU readline line editor + history (+ term helpers). */
/* ------------------------------------------------------------------ */
void load_history();
void save_history();
void add_history(std::string_view s);
std::string read_line(std::string_view prompt, bool &ok);
std::string read_plain(std::string_view prompt, bool &ok);
int term_width();
#endif /* BANTAM_HPP */