Fix UTF-8 slicing panics: use floor_char_boundary for all truncation
Byte-position truncation (&s[..s.len().min(N)]) panics when position N lands inside a multi-byte character. Fixed in parser debug logging, API error messages, oneshot response logging, and CLI agent display. Also fixed tool dispatch permissions (removed global fallback). Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
parent
1776222b07
commit
fba8fcc587
6 changed files with 12 additions and 10 deletions
|
|
@ -245,7 +245,7 @@ pub(crate) async fn send_and_check(
|
||||||
status,
|
status,
|
||||||
elapsed.as_secs_f64(),
|
elapsed.as_secs_f64(),
|
||||||
url,
|
url,
|
||||||
&body[..body.len().min(500)]
|
&body[..body.floor_char_boundary(body.len().min(500))]
|
||||||
);
|
);
|
||||||
if let Some(json) = request_json {
|
if let Some(json) = request_json {
|
||||||
let log_dir = dirs::home_dir()
|
let log_dir = dirs::home_dir()
|
||||||
|
|
@ -260,7 +260,7 @@ pub(crate) async fn send_and_check(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
anyhow::bail!("HTTP {} ({}): {}", status, url, &body[..body.len().min(1000)]);
|
anyhow::bail!("HTTP {} ({}): {}", status, url, &body[..body.floor_char_boundary(body.len().min(1000))]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
|
|
|
||||||
|
|
@ -507,7 +507,8 @@ impl ResponseParser {
|
||||||
if let Some(ref mut f) = log_file {
|
if let Some(ref mut f) = log_file {
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
for c in &calls {
|
for c in &calls {
|
||||||
let _ = writeln!(f, "tool_call: {} args={}", c.name, &c.arguments[..c.arguments.len().min(200)]);
|
let end = c.arguments.floor_char_boundary(c.arguments.len().min(200));
|
||||||
|
let _ = writeln!(f, "tool_call: {} args={}", c.name, &c.arguments[..end]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -523,12 +524,13 @@ impl ResponseParser {
|
||||||
let _ = writeln!(f, "done: {} chars, {} <tool_call> tags, ctx: {} tokens",
|
let _ = writeln!(f, "done: {} chars, {} <tool_call> tags, ctx: {} tokens",
|
||||||
full_text.len(), tc_count, ctx_tokens);
|
full_text.len(), tc_count, ctx_tokens);
|
||||||
if tc_count == 0 && full_text.len() > 0 {
|
if tc_count == 0 && full_text.len() > 0 {
|
||||||
let _ = writeln!(f, "full text:\n{}", &full_text[..full_text.len().min(2000)]);
|
let end = full_text.floor_char_boundary(full_text.len().min(2000));
|
||||||
|
let _ = writeln!(f, "full text:\n{}", &full_text[..end]);
|
||||||
}
|
}
|
||||||
for (i, part) in full_text.split("<tool_call>").enumerate() {
|
for (i, part) in full_text.split("<tool_call>").enumerate() {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
let _ = writeln!(f, "tool_call body: {}...",
|
let end = part.floor_char_boundary(part.len().min(200));
|
||||||
&part[..part.len().min(200)]);
|
let _ = writeln!(f, "tool_call body: {}...", &part[..end]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,7 @@ impl AutoAgent {
|
||||||
}
|
}
|
||||||
|
|
||||||
dbglog!("[auto] {} response: {}",
|
dbglog!("[auto] {} response: {}",
|
||||||
self.name, &text[..text.len().min(200)]);
|
self.name, &text[..text.floor_char_boundary(text.len().min(200))]);
|
||||||
|
|
||||||
if next_step < self.steps.len() {
|
if next_step < self.steps.len() {
|
||||||
if let Some(ref check) = bail_fn {
|
if let Some(ref check) = bail_fn {
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ fn run_agent_and_parse(agent: &str, session_arg: &Option<String>) {
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
eprintln!("Running {} agent (session {})...", agent, &session_id[..8.min(session_id.len())]);
|
eprintln!("Running {} agent (session {})...", agent, &session_id[..session_id.floor_char_boundary(8.min(session_id.len()))]);
|
||||||
|
|
||||||
let output = Command::new("poc-memory")
|
let output = Command::new("poc-memory")
|
||||||
.args(["agent", "run", agent, "--count", "1", "--local"])
|
.args(["agent", "run", agent, "--count", "1", "--local"])
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ pub fn cmd_digest_links(do_apply: bool) -> Result<(), String> {
|
||||||
for (i, link) in links.iter().enumerate() {
|
for (i, link) in links.iter().enumerate() {
|
||||||
println!(" {:3}. {} → {}", i + 1, link.source, link.target);
|
println!(" {:3}. {} → {}", i + 1, link.source, link.target);
|
||||||
if !link.reason.is_empty() {
|
if !link.reason.is_empty() {
|
||||||
println!(" ({})", &link.reason[..link.reason.len().min(80)]);
|
println!(" ({})", &link.reason[..link.reason.floor_char_boundary(link.reason.len().min(80))]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!("\nTo apply: poc-memory digest-links --apply");
|
println!("\nTo apply: poc-memory digest-links --apply");
|
||||||
|
|
|
||||||
|
|
@ -606,7 +606,7 @@ fn resolve_conversation(budget: Option<usize>) -> String {
|
||||||
if total_bytes >= max_bytes { break; }
|
if total_bytes >= max_bytes { break; }
|
||||||
let name = if role == "user" { &cfg.user_name } else { &cfg.assistant_name };
|
let name = if role == "user" { &cfg.user_name } else { &cfg.assistant_name };
|
||||||
let formatted = if !ts.is_empty() {
|
let formatted = if !ts.is_empty() {
|
||||||
oldest_ts = ts[..ts.len().min(19)].to_string();
|
oldest_ts = ts[..ts.floor_char_boundary(ts.len().min(19))].to_string();
|
||||||
format!("**{}** {}: {}", name, &oldest_ts, content)
|
format!("**{}** {}: {}", name, &oldest_ts, content)
|
||||||
} else {
|
} else {
|
||||||
format!("**{}:** {}", name, content)
|
format!("**{}:** {}", name, content)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue