fix: read工具单行超长时内容被完全丢弃

单行超过 MAX_CHARS(128K) 时,截断循环 end_idx=0 导致 lines[..0] 为空,
整行内容丢失。改为当首行就超长时使用 take_prefix_chars 字符级截断。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
oudecheng 2026-05-29 09:47:47 +08:00
parent 182bebdaef
commit 5d3a583915

View File

@ -3,6 +3,7 @@ use std::path::Path;
use async_trait::async_trait;
use serde_json::json;
use crate::text::take_prefix_chars;
use crate::tools::traits::{Tool, ToolResult};
use crate::tools::extract_u64;
@ -187,8 +188,13 @@ impl Tool for FileReadTool {
}
end_idx = i + 1;
}
result = lines[..end_idx].join("\n");
let truncated_amount = original_len - result.len();
if end_idx == 0 && !lines.is_empty() {
// First line alone exceeds MAX_CHARS — take its prefix
result = take_prefix_chars(&lines[0], MAX_CHARS.saturating_sub(100));
} else {
result = lines[..end_idx].join("\n");
}
let truncated_amount = original_len.saturating_sub(result.len());
result.push_str(&format!(
"\n\n... ({} chars truncated) ...",
truncated_amount
@ -312,4 +318,28 @@ mod tests {
assert!(!result.success);
assert!(result.error.unwrap().contains("Not a file"));
}
#[tokio::test]
async fn test_read_single_long_line() {
let mut file = NamedTempFile::new().unwrap();
// Write a single line longer than MAX_CHARS
let long_line = "A".repeat(150_000);
file.write_all(long_line.as_bytes()).unwrap();
let tool = FileReadTool::new();
let result = tool
.execute(json!({ "path": file.path().to_str().unwrap() }))
.await
.unwrap();
assert!(result.success);
// Should contain the line number prefix and the beginning of the content
assert!(result.output.starts_with("1| AAAA"));
// Should contain truncation notice since content exceeds MAX_CHARS
assert!(result.output.contains("chars truncated"));
// Should contain end-of-file notice (1 line total)
assert!(result.output.contains("End of file — 1 lines total"));
// Should NOT be empty content — the fix ensures the prefix is preserved
assert!(result.output.len() > 100);
}
}