Skip to content

Commit 287645b

Browse files
author
Chris Warren-Smith
committed
LLAMA: nitro fixes
- handling for large tool responses - fix color display - avoid sandbox check for read ops - fix ask and permission tool display
1 parent a818384 commit 287645b

4 files changed

Lines changed: 72 additions & 28 deletions

File tree

llama/llama-sb.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ void Llama::reset() {
139139
}
140140
}
141141

142+
int Llama::max_tool_result_size() {
143+
// ~3 bytes/tok, 25% of ctx
144+
return (llama_n_ctx(_ctx) / 4) * 3;
145+
}
146+
142147
bool Llama::load_model(string model_path, int n_ctx, int n_batch, int n_gpu_layers, int log_level) {
143148
ggml_backend_load_all();
144149

@@ -480,6 +485,14 @@ bool Llama::batch_decode_tokens(vector<llama_token> &tokens) {
480485
size_t batch_size = std::min((size_t)n_batch, tokens.size() - i);
481486
llama_batch batch = llama_batch_get_one(tokens.data() + i, batch_size);
482487
int result = llama_decode(_ctx, batch);
488+
if (result == 1) {
489+
// KV full mid-batch - try to evict and retry once
490+
if (!make_space_for_tokens(n_batch)) {
491+
_last_error = "KV cache exhausted, cannot evict enough space";
492+
return false;
493+
}
494+
result = llama_decode(_ctx, batch);
495+
}
483496
if (result != 0) {
484497
_last_error = std::format("Failed to decode batch. position:{} error:{} [size:{}]",
485498
i, result, tokens.size());

llama/llama-sb.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ struct Llama {
9898
const char *last_error() { return _last_error.c_str(); }
9999
void set_log_level(int level) { _log_level = level; }
100100
void reset();
101+
int max_tool_result_size();
101102

102103
// memory info
103104
LlamaMemoryInfo memory_info();

llama/llama.cpp

llama/nitro.cpp

Lines changed: 57 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -436,8 +436,8 @@ static const std::vector<std::string> CODE_EXTENSIONS = {
436436
//
437437
static std::string settings_path() {
438438
// Attempt to read settings from the current working directory first
439-
if (fs::exists("settings.json")) {
440-
return "settings.json";
439+
if (fs::exists("nitro.settings.json")) {
440+
return "nitro.settings.json";
441441
}
442442
const char *home = getenv("HOME");
443443
std::string base = home ? std::string(home) : ".";
@@ -581,7 +581,7 @@ static void load_settings(NitroConfig &cfg) {
581581
static constexpr std::string ICON_ERR = " ⚡ ▏";
582582
static constexpr std::string ICON_THINK = " 🤔 ▏";
583583
static constexpr std::string ICON_TOOL = " 🔧 ▏";
584-
static constexpr std::string ICON_SYS = " 🤖";
584+
static constexpr std::string ICON_SYS = " ";
585585

586586
static std::string introspect(const NitroConfig &cfg) {
587587
static constexpr std::string_view tmpl =
@@ -1229,38 +1229,54 @@ void TuiState::redraw_chat() {
12291229
unsigned rows, cols;
12301230
ncplane_dim_yx(chatpl, &rows, &cols);
12311231
std::lock_guard<std::mutex> lk(lines_mutex);
1232-
int total = static_cast<int>(chat_lines.size());
1233-
int visible = static_cast<int>(rows);
1234-
int start = std::max(0, total - visible - scroll_offset);
1235-
int end = std::min(total, start + visible);
1236-
for (int i = start, row = 0; i < end; ++i, ++row) {
1237-
const std::string &line = chat_lines[i];
1232+
1233+
// Pre-compute wrapped lines so we know total visual rows.
1234+
struct VisualLine {
1235+
std::string text;
1236+
uint64_t ch;
1237+
};
1238+
std::vector<VisualLine> visual;
1239+
visual.reserve(chat_lines.size());
1240+
1241+
for (const std::string &line : chat_lines) {
12381242
uint64_t ch;
1239-
// Logo lines use prefix "[logo_N]" where N is the row index 0-6.
1240-
// We interpolate a cyan→magenta gradient across the 7 art rows.
12411243
if (line.rfind("[logo_", 0) == 0 && line.size() > 7 && line[7] == ']') {
12421244
int logo_row = line[6] - '0';
1243-
// Gradient: cyan (0,230,255) → green (80,255,160) → magenta (220,80,255)
1244-
// 7 steps, indices 0-6.
12451245
static const uint32_t GRAD_R[] = { 0, 20, 60, 120, 180, 210, 220 };
12461246
static const uint32_t GRAD_G[] = { 230, 255, 255, 255, 200, 130, 80 };
12471247
static const uint32_t GRAD_B[] = { 255, 200, 140, 80, 100, 200, 255 };
12481248
int gi = std::max(0, std::min(logo_row, 6));
12491249
ch = chat_ch(GRAD_R[gi], GRAD_G[gi], GRAD_B[gi]);
12501250
}
1251-
else if (line.rfind("You: ", 0) == 0) ch = chat_ch(100, 200, 255);
1252-
else if (line.rfind("Nitro: ", 0) == 0) ch = chat_ch(180, 255, 180);
1253-
else if (line.rfind(ICON_SYS, 0) == 0) ch = chat_ch(140, 140, 200);
1254-
else if (line.rfind(ICON_TOOL, 0) == 0) ch = chat_ch(255, 180, 80);
1255-
else if (line.rfind(ICON_ERR, 0) == 0) ch = chat_ch(255, 80, 80);
1251+
else if (line.rfind("You: ", 0) == 0) ch = chat_ch(100, 200, 255);
1252+
else if (line.rfind("Nitro: ", 0) == 0) ch = chat_ch(180, 255, 180);
1253+
else if (line.rfind(ICON_SYS, 0) == 0) ch = chat_ch(160, 82, 45);
1254+
else if (line.rfind(ICON_TOOL, 0) == 0) ch = chat_ch(255, 180, 80);
1255+
else if (line.rfind(ICON_ERR, 0) == 0) ch = chat_ch(255, 80, 80);
12561256
else if (line.rfind(ICON_THINK, 0) == 0) ch = chat_ch(140, 140, 200);
12571257
else ch = chat_ch(210, 210, 210);
1258-
ncplane_set_channels(chatpl, ch);
1259-
// Strip the [logo_N] prefix before rendering.
1258+
12601259
std::string display = (line.rfind("[logo_", 0) == 0 && line.size() > 8)
12611260
? line.substr(8) : line;
1262-
if (display.size() > cols) display = display.substr(0, cols);
1263-
ncplane_putstr_yx(chatpl, row, 0, display.c_str());
1261+
1262+
// Split into cols-wide chunks, each carrying the same channel.
1263+
if (display.empty()) {
1264+
visual.push_back({"", ch});
1265+
} else {
1266+
for (size_t off = 0; off < display.size(); off += cols) {
1267+
visual.push_back({display.substr(off, cols), ch});
1268+
}
1269+
}
1270+
}
1271+
1272+
int total = static_cast<int>(visual.size());
1273+
int visible = static_cast<int>(rows);
1274+
int start = std::max(0, total - visible - scroll_offset);
1275+
int end = std::min(total, start + visible);
1276+
1277+
for (int i = start, row = 0; i < end; ++i, ++row) {
1278+
ncplane_set_channels(chatpl, visual[i].ch);
1279+
ncplane_putstr_yx(chatpl, row, 0, visual[i].text.c_str());
12641280
}
12651281
}
12661282

@@ -2081,19 +2097,16 @@ std::string AgentState::process_tool(const std::string &cmd, const NitroConfig &
20812097
if (op == "TOOL:LIST") {
20822098
std::string dir = resolve(arg1);
20832099
show_tool("listing: " + dir);
2084-
if (!path_in_sandbox(sandbox, dir)) return "ERROR: path outside sandbox";
20852100
return list_dir(dir);
20862101
}
20872102
if (op == "TOOL:EXISTS") {
20882103
std::string p = resolve(arg1);
20892104
show_tool("checking: " + p);
2090-
if (!path_in_sandbox(sandbox, p)) return "NO";
20912105
return fs::exists(p) ? "YES" : "NO";
20922106
}
20932107
if (op == "TOOL:READ") {
20942108
show_tool("reading: " + arg1);
20952109
std::string p = resolve(arg1);
2096-
if (!path_in_sandbox(sandbox, p)) return "ERROR: path outside sandbox";
20972110
return read_file(p);
20982111
}
20992112
if (op == "TOOL:WRITE") {
@@ -2125,9 +2138,15 @@ std::string AgentState::process_tool(const std::string &cmd, const NitroConfig &
21252138
return introspect(cfg);
21262139
}
21272140
if (op == "TOOL:ASK") {
2128-
show_tool("asking: " + arg1);
2141+
tui.set_thinking(false);
2142+
show_tool("asking: " + arg1 + " " + arg2);
21292143
return tui.readline_blocking();
21302144
}
2145+
if (op == "TOOL:PERMISSION") {
2146+
tui.set_thinking(false);
2147+
show_tool("asking permission: " + arg1 + " " + arg2);
2148+
return tui.confirm_dialog(arg1 + " " + arg2) ? "YES" : "NO";
2149+
}
21312150
if (op == "TOOL:RUN") {
21322151
if (!run_allowed.empty()) {
21332152
bool permitted = ranges::any_of(run_allowed, [&](const std::string &a) {return a == arg1;});
@@ -2217,6 +2236,17 @@ bool AgentState::run_turn(const std::string &user_message, const NitroConfig &cf
22172236
std::string content = TOOL_RESULT + std::vformat(template_str, std::make_format_args(result));
22182237
log_write("tool: [%s] result: [%s]", tool.c_str(), result.c_str());
22192238

2239+
if (content.size() > llama->max_tool_result_size()) {
2240+
// Index the content into RAG and tell the model where to find it
2241+
if (embed_llama && rag_db && rag_session) {
2242+
content = std::format("Tool result too large ({} bytes). The content has been indexed. "
2243+
"Use TOOL:RAG with a relevant query to retrieve the information you need.",
2244+
content.size());
2245+
} else {
2246+
content = std::format("Tool result too large ({} bytes).", content.size());
2247+
}
2248+
tui.append_line(ICON_ERR + content);
2249+
}
22202250
if (!llama->add_message(*iter, "tool_result", content)) {
22212251
tui.append_line(ICON_ERR + "tool result inject: " + llama->last_error());
22222252
}
@@ -2258,7 +2288,7 @@ bool AgentState::run_turn(const std::string &user_message, const NitroConfig &cf
22582288
}
22592289
return ni.id == NCKEY_ESC;
22602290
};
2261-
2291+
22622292
auto fetch_all = [&]() -> void {
22632293
while (iter->_has_next && !is_escape()) {
22642294
std::string tok = llama->next(*iter);

0 commit comments

Comments
 (0)