first commit
This commit is contained in:
commit
91b44fcc16
27 files changed
+4104
No files matched your search
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "agent-tools"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
agent-core = { path = "../agent-core" }
|
||||
async-trait = { workspace = true }
|
||||
jiff = "0.2.37"
|
||||
@@ -0,0 +1,2 @@
|
||||
mod read;
|
||||
pub use read::ReadFileTool;
|
||||
@@ -0,0 +1,99 @@
|
||||
use std::{
|
||||
fmt::Write,
|
||||
fs::File,
|
||||
io::{BufRead, BufReader},
|
||||
};
|
||||
|
||||
use agent_core::{AgentError, Tool};
|
||||
use async_trait::async_trait;
|
||||
use serde::Deserialize;
|
||||
|
||||
pub struct ReadFileTool;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct ToolArgs {
|
||||
pub path: String,
|
||||
pub range: Option<(usize, usize)>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Tool for ReadFileTool {
|
||||
fn name(&self) -> &str {
|
||||
"read_file"
|
||||
}
|
||||
|
||||
fn description(&self) -> &str {
|
||||
"Reads the contents of a file at the specified path."
|
||||
}
|
||||
|
||||
fn parameters(&self) -> serde_json::Value {
|
||||
serde_json::json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string",
|
||||
"description": "The path to the file to read."
|
||||
},
|
||||
"range": {
|
||||
"type": "array",
|
||||
"description": "Optional line range [start, count], Number of lines to read. For example, start=100, count=50 reads lines 100 to 149 (inclusive)",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
},
|
||||
"maxItems": 2,
|
||||
"minItems": 2
|
||||
}
|
||||
},
|
||||
"required": ["path"]
|
||||
})
|
||||
}
|
||||
|
||||
async fn run(&self, arguments: &str) -> Result<String, AgentError> {
|
||||
let args: ToolArgs = serde_json::from_str(arguments)
|
||||
.map_err(|e| AgentError::Provider(format!("Invalid arguments {e}")))?;
|
||||
|
||||
let file =
|
||||
File::open(args.path).map_err(|e| AgentError::Provider(format!("Invalid path {e}")))?;
|
||||
|
||||
read_line(args.range, file)
|
||||
}
|
||||
}
|
||||
|
||||
fn read_line(range: Option<(usize, usize)>, file: File) -> Result<String, AgentError> {
|
||||
let reader = BufReader::new(file);
|
||||
let mut ans = String::new();
|
||||
|
||||
match range {
|
||||
Some((start, count)) => {
|
||||
if start == 0 {
|
||||
return Err(AgentError::Provider(format!(
|
||||
"Invalid arguments: start must be >= 1, got {}",
|
||||
start
|
||||
)));
|
||||
}
|
||||
if count > 200 {
|
||||
return Err(AgentError::Provider(format!(
|
||||
"Invalid arguments: count must be <= 200, got {}",
|
||||
count
|
||||
)));
|
||||
}
|
||||
|
||||
for (i, line) in reader.lines().skip(start - 1).take(count).enumerate() {
|
||||
let line =
|
||||
line.map_err(|e| AgentError::Provider(format!("failed to read file: {}", e)))?;
|
||||
writeln!(ans, "[{}]: {}", start + i, line)?
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// 明确上限,避免模型无界读取
|
||||
const MAX_LINES: usize = 1000;
|
||||
for (i, line) in reader.lines().take(MAX_LINES).enumerate() {
|
||||
let line =
|
||||
line.map_err(|e| AgentError::Provider(format!("failed to read file: {}", e)))?;
|
||||
writeln!(ans, "[{}]: {}", i + 1, line)?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ans)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
mod fs;
|
||||
mod time;
|
||||
pub use fs::ReadFileTool;
|
||||
pub use time::CurrentTimeTool;
|
||||
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
use agent_core::{AgentError, Tool};
|
||||
use async_trait::async_trait;
|
||||
use jiff::Zoned;
|
||||
use serde::Deserialize;
|
||||
|
||||
pub struct CurrentTimeTool;
|
||||
|
||||
#[derive(Deserialize, Default)]
|
||||
pub struct TimeArgs {
|
||||
#[serde(default)]
|
||||
timezone: Option<String>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Tool for CurrentTimeTool {
|
||||
fn name(&self) -> &str {
|
||||
"get_current_time"
|
||||
}
|
||||
|
||||
fn description(&self) -> &str {
|
||||
"get system now time"
|
||||
}
|
||||
|
||||
fn parameters(&self) -> serde_json::Value {
|
||||
serde_json::json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"timezone": {
|
||||
"type": "string",
|
||||
"description": "Optional IANA timezone name (e.g., 'Asia/Shanghai', 'America/New_York', 'UTC'). If not provided, the local system timezone is used."
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async fn run(&self, arguments: &str) -> Result<String, AgentError> {
|
||||
let args: TimeArgs = if arguments.trim().is_empty() {
|
||||
TimeArgs::default()
|
||||
} else {
|
||||
serde_json::from_str(arguments)
|
||||
.map_err(|e| AgentError::Provider(format!("invaild argments {e}")))?
|
||||
};
|
||||
|
||||
let now = Zoned::now();
|
||||
let target_time = match args.timezone.as_deref() {
|
||||
Some(tz) if !tz.trim().is_empty() => now
|
||||
.in_tz(tz)
|
||||
.map_err(|e| AgentError::Provider(format!("invalid timezone '{tz}': {e}")))?,
|
||||
_ => now,
|
||||
};
|
||||
|
||||
let formatted = format!(
|
||||
"{} [{}]",
|
||||
target_time.strftime("%Y-%m-%d %H:%M:%S %A"),
|
||||
target_time.time_zone().iana_name().unwrap_or("Unknown")
|
||||
);
|
||||
|
||||
Ok(formatted)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user