196 lines
7.8 KiB
C++
196 lines
7.8 KiB
C++
/* 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 */
|