Optimize startup, HTTP/SSE streaming, and fix macOS libedit bindings

This commit is contained in:
Luxferre
2026-10-14 15:30:12 +03:00
parent 105d283e01
commit a1d5f4bf48
10 changed files with 497 additions and 222 deletions
+79 -28
View File
@@ -2,6 +2,7 @@
* and non-streaming response parsing. */
#include "bantam.hpp"
#include <cerrno>
#include <charconv>
#include <climits>
#include <cmath>
#include <cstdio>
@@ -118,6 +119,19 @@ static void dump_str(std::string &out, std::string_view s) {
const unsigned char *p = (const unsigned char *)s.data();
size_t avail = s.size();
while (avail > 0) {
/* Fast path: scan contiguous unescaped printable ASCII. */
size_t run = 0;
while (run < avail) {
unsigned char b = p[run];
if (b >= 0x20 && b < 0x80 && b != '"' && b != '\\') run++;
else break;
}
if (run > 0) {
out.append((const char *)p, run);
p += run;
avail -= run;
if (avail == 0) break;
}
unsigned char c = p[0];
if (c < 0x80) {
p++;
@@ -130,10 +144,12 @@ static void dump_str(std::string &out, std::string_view s) {
case '\n': out += "\\n"; break;
case '\r': out += "\\r"; break;
case '\t': out += "\\t"; break;
default:
if (c < 0x20) str_appendf(out, "\\u%04x", (unsigned)c);
else out += (char)c;
default: {
char ubuf[7];
std::snprintf(ubuf, sizeof ubuf, "\\u%04x", (unsigned)c);
out.append(ubuf, 6);
break;
}
}
} else {
size_t pl = 0;
@@ -160,12 +176,22 @@ static void dump_rec(std::string &out, const Json &j, int indent, int depth) {
case JType::Bool:
out += (j.bval ? "true" : "false");
break;
case JType::Int:
str_appendf(out, "%lld", (long long)j.ival);
case JType::Int: {
char ibuf[32];
auto [ptr, ec] = std::to_chars(ibuf, ibuf + sizeof ibuf, j.ival);
if (ec == std::errc()) out.append(ibuf, (size_t)(ptr - ibuf));
else str_appendf(out, "%lld", (long long)j.ival);
break;
}
case JType::Float:
if (!std::isfinite(j.dval)) out += "null";
else str_appendf(out, "%.15g", j.dval);
if (!std::isfinite(j.dval)) {
out += "null";
} else {
char fbuf[32];
int n = std::snprintf(fbuf, sizeof fbuf, "%.15g", j.dval);
if (n > 0 && (size_t)n < sizeof fbuf) out.append(fbuf, (size_t)n);
else str_appendf(out, "%.15g", j.dval);
}
break;
case JType::Str:
dump_str(out, j.sval);
@@ -217,6 +243,7 @@ static void dump_rec(std::string &out, const Json &j, int indent, int depth) {
std::string Json::dump(int indent) const {
std::string out;
out.reserve(256);
dump_rec(out, *this, indent, 0);
return out;
}
@@ -278,16 +305,25 @@ struct JParser {
if (pos >= src.size() || src[pos] != '"') return false;
pos++;
out.clear();
while (pos < src.size()) {
unsigned char c = (unsigned char)src[pos++];
const char *base = src.data();
size_t n = src.size();
while (pos < n) {
/* Fast path: scan contiguous unescaped bytes >= 0x20. */
size_t run_start = pos;
while (pos < n) {
unsigned char b = (unsigned char)base[pos];
if (b == '"' || b == '\\' || b < 0x20) break;
pos++;
}
if (pos > run_start) {
out.append(base + run_start, pos - run_start);
}
if (pos >= n) return false;
unsigned char c = (unsigned char)base[pos++];
if (c == '"') return true;
if (c < 0x20) return false;
if (c != '\\') {
out += (char)c;
continue;
}
if (pos >= src.size()) return false;
char esc = src[pos++];
if (pos >= n) return false;
char esc = base[pos++];
switch (esc) {
case '"': out += '"'; break;
case '\\': out += '\\'; break;
@@ -301,7 +337,7 @@ struct JParser {
uint32_t cp = 0;
if (!parse_hex4(cp)) return false;
if (cp >= 0xD800 && cp <= 0xDBFF) {
if (pos + 6 <= src.size() && src[pos] == '\\' && src[pos + 1] == 'u') {
if (pos + 6 <= n && base[pos] == '\\' && base[pos + 1] == 'u') {
size_t saved = pos;
pos += 2;
uint32_t low = 0;
@@ -353,20 +389,32 @@ struct JParser {
if (pos >= src.size() || src[pos] < '0' || src[pos] > '9') return false;
while (pos < src.size() && src[pos] >= '0' && src[pos] <= '9') pos++;
}
std::string num_str(src.substr(start, pos - start));
const char *first = src.data() + start;
const char *last = src.data() + pos;
if (!is_float) {
errno = 0;
char *end = nullptr;
long long iv = std::strtoll(num_str.c_str(), &end, 10);
if (errno == 0 && end == num_str.c_str() + num_str.size()) {
out = Json((int64_t)iv);
int64_t iv = 0;
auto [ptr, ec] = std::from_chars(first, last, iv);
if (ec == std::errc() && ptr == last) {
out = Json(iv);
return true;
}
}
char stack_num[64];
std::string heap_num;
const char *cstr = nullptr;
size_t nlen = pos - start;
if (nlen < sizeof stack_num) {
std::memcpy(stack_num, first, nlen);
stack_num[nlen] = '\0';
cstr = stack_num;
} else {
heap_num.assign(first, nlen);
cstr = heap_num.c_str();
}
errno = 0;
char *end = nullptr;
double dv = std::strtod(num_str.c_str(), &end);
if (end != num_str.c_str() + num_str.size() || !std::isfinite(dv)) return false;
double dv = std::strtod(cstr, &end);
if (end != cstr + nlen || !std::isfinite(dv)) return false;
out = Json(dv);
return true;
}
@@ -554,6 +602,7 @@ std::string build_request_json(const Cfg &cfg, const Messages &msgs,
root["model"] = cfg.model;
root["temperature"] = cfg.temperature;
Json arr = Json::array();
arr.aval.reserve(msgs.items.size());
for (auto &m : msgs.items) {
Json mo;
serialize_message_json(mo, m);
@@ -562,10 +611,12 @@ std::string build_request_json(const Cfg &cfg, const Messages &msgs,
root["messages"] = std::move(arr);
root["stream"] = stream;
if (use_tools) {
Json tools;
if (Json::parse(TOOLS_JSON, tools)) {
root["tools"] = std::move(tools);
}
static const Json kTools = []() {
Json t;
Json::parse(TOOLS_JSON, t);
return t;
}();
root["tools"] = kTools;
}
if (stream) {
Json so = Json::object();