matrixcode_core/tools/
read.rs1use anyhow::Result;
2use async_trait::async_trait;
3use serde_json::{Value, json};
4
5use super::{Tool, ToolDefinition};
6
7const MAX_FILE_SIZE: u64 = 5_000_000;
9const DEFAULT_MAX_LINES: usize = 500;
11
12pub struct ReadTool;
13
14#[async_trait]
15impl Tool for ReadTool {
16 fn definition(&self) -> ToolDefinition {
17 ToolDefinition {
18 name: "read".to_string(),
19 description: "读取指定路径的文件内容".to_string(),
20 parameters: json!({
21 "type": "object",
22 "properties": {
23 "path": {
24 "type": "string",
25 "description": "要读取的文件路径"
26 },
27 "offset": {
28 "type": "integer",
29 "description": "起始行号(从 0 开始)"
30 },
31 "limit": {
32 "type": "integer",
33 "description": "最大读取行数"
34 }
35 },
36 "required": ["path"]
37 }),
38 ..Default::default()
39 }
40 }
41
42 async fn execute(&self, params: Value) -> Result<String> {
43 let path = params["path"]
44 .as_str()
45 .ok_or_else(|| anyhow::anyhow!("missing 'path'"))?;
46
47 let metadata = tokio::fs::metadata(path).await?;
49 let file_size = metadata.len();
50
51 let offset = params["offset"].as_u64().unwrap_or(0) as usize;
52 let limit = params["limit"].as_u64().map(|l| l as usize);
53
54 if file_size > MAX_FILE_SIZE && limit.is_none() {
56 return Ok(format!(
57 "⚠️ File is large ({:.1}MB). Use offset/limit parameters to read specific sections.\n\
58 Example: read(path=\"{}\", offset=0, limit=100) to read first 100 lines.",
59 file_size as f64 / 1_000_000.0,
60 path
61 ));
62 }
63
64 let content = tokio::fs::read_to_string(path).await?;
65
66 let lines: Vec<&str> = content.lines().collect();
67 let total_lines = lines.len();
68
69 let effective_limit = limit.unwrap_or(DEFAULT_MAX_LINES);
71 let end = (offset + effective_limit).min(total_lines);
72 let selected = &lines[offset.min(total_lines)..end.min(total_lines)];
73
74 let mut result = String::new();
75
76 if offset > 0 || end < total_lines {
78 result.push_str(&format!(
79 "📄 {} (lines {}-{} of {})\n\n",
80 path,
81 offset + 1,
82 end,
83 total_lines
84 ));
85 }
86
87 for (i, line) in selected.iter().enumerate() {
88 result.push_str(&format!("{:4} | {}\n", offset + i + 1, line));
89 }
90
91 if end < total_lines {
93 result.push_str(&format!(
94 "\n... ({} more lines, use offset={} limit=N to continue)",
95 total_lines - end,
96 end
97 ));
98 }
99
100 Ok(result)
101 }
102}