tools: each module exports only tool() or tools(), nothing else
Every tool module now has a clean interface: - read, write, edit, grep, glob, bash, vision: pub fn tool() -> Tool - web: pub fn tools() -> [Tool; 2] - memory: pub fn memory_tools() -> Vec<Tool> - channels: pub fn tools() -> Vec<Tool> - control: pub fn tools() -> Vec<Tool> Definition and handler functions are private to each module. mod.rs::tools() just chains the module exports. Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
parent
fdb8c989f5
commit
ed150df628
10 changed files with 65 additions and 36 deletions
|
|
@ -33,7 +33,11 @@ struct Args {
|
|||
timeout_secs: u64,
|
||||
}
|
||||
|
||||
pub fn definition() -> ToolDef {
|
||||
pub fn tool() -> super::Tool {
|
||||
super::Tool { def: definition(), handler: |_a, v| Box::pin(async move { run_bash(&v).await }) }
|
||||
}
|
||||
|
||||
fn definition() -> ToolDef {
|
||||
ToolDef::new(
|
||||
"bash",
|
||||
"Execute a bash command and return its output. \
|
||||
|
|
@ -55,7 +59,7 @@ pub fn definition() -> ToolDef {
|
|||
)
|
||||
}
|
||||
|
||||
pub async fn run_bash(args: &serde_json::Value) -> Result<String> {
|
||||
async fn run_bash(args: &serde_json::Value) -> Result<String> {
|
||||
let a: Args = serde_json::from_value(args.clone())
|
||||
.context("invalid bash arguments")?;
|
||||
let command = &a.command;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,11 @@ struct Args {
|
|||
replace_all: bool,
|
||||
}
|
||||
|
||||
pub fn definition() -> ToolDef {
|
||||
pub fn tool() -> super::Tool {
|
||||
super::Tool { def: definition(), handler: |_a, v| Box::pin(async move { edit_file(&v) }) }
|
||||
}
|
||||
|
||||
fn definition() -> ToolDef {
|
||||
ToolDef::new(
|
||||
"edit_file",
|
||||
"Perform exact string replacement in a file. The old_string must appear \
|
||||
|
|
@ -53,7 +57,7 @@ pub fn definition() -> ToolDef {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn edit_file(args: &serde_json::Value) -> Result<String> {
|
||||
fn edit_file(args: &serde_json::Value) -> Result<String> {
|
||||
let a: Args = serde_json::from_value(args.clone())
|
||||
.context("invalid edit_file arguments")?;
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,11 @@ struct Args {
|
|||
|
||||
fn default_path() -> String { ".".into() }
|
||||
|
||||
pub fn definition() -> ToolDef {
|
||||
pub fn tool() -> super::Tool {
|
||||
super::Tool { def: definition(), handler: |_a, v| Box::pin(async move { glob_search(&v) }) }
|
||||
}
|
||||
|
||||
fn definition() -> ToolDef {
|
||||
ToolDef::new(
|
||||
"glob",
|
||||
"Find files matching a glob pattern. Returns file paths sorted by \
|
||||
|
|
@ -43,7 +47,7 @@ pub fn definition() -> ToolDef {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn glob_search(args: &serde_json::Value) -> Result<String> {
|
||||
fn glob_search(args: &serde_json::Value) -> Result<String> {
|
||||
let a: Args = serde_json::from_value(args.clone())
|
||||
.context("invalid glob arguments")?;
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,11 @@ struct Args {
|
|||
|
||||
fn default_path() -> String { ".".into() }
|
||||
|
||||
pub fn definition() -> ToolDef {
|
||||
pub fn tool() -> super::Tool {
|
||||
super::Tool { def: definition(), handler: |_a, v| Box::pin(async move { grep(&v) }) }
|
||||
}
|
||||
|
||||
fn definition() -> ToolDef {
|
||||
ToolDef::new(
|
||||
"grep",
|
||||
"Search for a pattern in files. Returns matching file paths by default, \
|
||||
|
|
@ -64,7 +68,7 @@ fn has_rg() -> bool {
|
|||
*HAS_RG.get_or_init(|| Command::new("rg").arg("--version").output().is_ok())
|
||||
}
|
||||
|
||||
pub fn grep(args: &serde_json::Value) -> Result<String> {
|
||||
fn grep(args: &serde_json::Value) -> Result<String> {
|
||||
let a: Args = serde_json::from_value(args.clone())
|
||||
.context("invalid grep arguments")?;
|
||||
|
||||
|
|
|
|||
|
|
@ -265,7 +265,7 @@ fn query(args: &serde_json::Value) -> Result<String> {
|
|||
.map_err(|e| anyhow::anyhow!("{}", e))
|
||||
}
|
||||
|
||||
pub(super) fn output_def() -> ToolDef {
|
||||
fn output_def() -> ToolDef {
|
||||
ToolDef::new("output",
|
||||
"Produce a named output value. Use this to pass structured results \
|
||||
between steps — subsequent prompts can see these in the conversation history.",
|
||||
|
|
@ -275,7 +275,7 @@ pub(super) fn output_def() -> ToolDef {
|
|||
},"required":["key","value"]}))
|
||||
}
|
||||
|
||||
pub(super) fn output(args: &serde_json::Value) -> Result<String> {
|
||||
fn output(args: &serde_json::Value) -> Result<String> {
|
||||
let key = get_str(args, "key")?;
|
||||
if key.starts_with("pid-") || key.contains('/') || key.contains("..") {
|
||||
anyhow::bail!("invalid output key: {}", key);
|
||||
|
|
@ -291,7 +291,7 @@ pub(super) fn output(args: &serde_json::Value) -> Result<String> {
|
|||
|
||||
// ── Journal tools ──────────────────────────────────────────────
|
||||
|
||||
pub(super) fn journal_tail_def() -> ToolDef {
|
||||
fn journal_tail_def() -> ToolDef {
|
||||
ToolDef::new("journal_tail",
|
||||
"Read the last N journal entries (default 1).",
|
||||
json!({"type":"object","properties":{
|
||||
|
|
@ -299,7 +299,7 @@ pub(super) fn journal_tail_def() -> ToolDef {
|
|||
}}))
|
||||
}
|
||||
|
||||
pub(super) fn journal_tail(args: &serde_json::Value) -> Result<String> {
|
||||
fn journal_tail(args: &serde_json::Value) -> Result<String> {
|
||||
let count = args.get("count").and_then(|v| v.as_u64()).unwrap_or(1) as usize;
|
||||
let store = load_store()?;
|
||||
let mut entries: Vec<&crate::store::Node> = store.nodes.values()
|
||||
|
|
@ -317,7 +317,7 @@ pub(super) fn journal_tail(args: &serde_json::Value) -> Result<String> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn journal_new_def() -> ToolDef {
|
||||
fn journal_new_def() -> ToolDef {
|
||||
ToolDef::new("journal_new",
|
||||
"Start a new journal entry.",
|
||||
json!({"type":"object","properties":{
|
||||
|
|
@ -327,7 +327,7 @@ pub(super) fn journal_new_def() -> ToolDef {
|
|||
},"required":["name","title","body"]}))
|
||||
}
|
||||
|
||||
pub(super) fn journal_new(args: &serde_json::Value) -> Result<String> {
|
||||
fn journal_new(args: &serde_json::Value) -> Result<String> {
|
||||
let name = get_str(args, "name")?;
|
||||
let title = get_str(args, "title")?;
|
||||
let body = get_str(args, "body")?;
|
||||
|
|
@ -363,7 +363,7 @@ pub(super) fn journal_new(args: &serde_json::Value) -> Result<String> {
|
|||
Ok(format!("New entry '{}' ({} words)", title, word_count))
|
||||
}
|
||||
|
||||
pub(super) fn journal_update_def() -> ToolDef {
|
||||
fn journal_update_def() -> ToolDef {
|
||||
ToolDef::new("journal_update",
|
||||
"Append text to the most recent journal entry (same thread continuing).",
|
||||
json!({"type":"object","properties":{
|
||||
|
|
@ -371,7 +371,7 @@ pub(super) fn journal_update_def() -> ToolDef {
|
|||
},"required":["body"]}))
|
||||
}
|
||||
|
||||
pub(super) fn journal_update(args: &serde_json::Value) -> Result<String> {
|
||||
fn journal_update(args: &serde_json::Value) -> Result<String> {
|
||||
let body = get_str(args, "body")?;
|
||||
let mut store = load_store()?;
|
||||
let latest_key = store.nodes.values()
|
||||
|
|
|
|||
|
|
@ -203,17 +203,11 @@ pub async fn dispatch_shared(
|
|||
/// Return all registered tools with definitions + handlers.
|
||||
pub fn tools() -> Vec<Tool> {
|
||||
let mut all = vec![
|
||||
// File tools
|
||||
Tool { def: read::definition(), handler: |_a, v| Box::pin(async move { read::read_file(&v) }) },
|
||||
Tool { def: write::definition(), handler: |_a, v| Box::pin(async move { write::write_file(&v) }) },
|
||||
Tool { def: edit::definition(), handler: |_a, v| Box::pin(async move { edit::edit_file(&v) }) },
|
||||
Tool { def: grep::definition(), handler: |_a, v| Box::pin(async move { grep::grep(&v) }) },
|
||||
Tool { def: glob::definition(), handler: |_a, v| Box::pin(async move { glob::glob_search(&v) }) },
|
||||
Tool { def: bash::definition(), handler: |_a, v| Box::pin(async move { bash::run_bash(&v).await }) },
|
||||
Tool { def: web::fetch_definition(), handler: |_a, v| Box::pin(async move { web::web_fetch(&v).await }) },
|
||||
Tool { def: web::search_definition(), handler: |_a, v| Box::pin(async move { web::web_search(&v).await }) },
|
||||
Tool { def: vision::definition(), handler: |_a, v| Box::pin(async move { vision::view_image_text(&v) }) },
|
||||
read::tool(), write::tool(), edit::tool(),
|
||||
grep::tool(), glob::tool(), bash::tool(),
|
||||
vision::tool(),
|
||||
];
|
||||
all.extend(web::tools());
|
||||
all.extend(memory::memory_tools());
|
||||
all.extend(channels::tools());
|
||||
all.extend(control::tools());
|
||||
|
|
|
|||
|
|
@ -16,7 +16,11 @@ struct Args {
|
|||
|
||||
fn default_offset() -> usize { 1 }
|
||||
|
||||
pub fn definition() -> ToolDef {
|
||||
pub fn tool() -> super::Tool {
|
||||
super::Tool { def: definition(), handler: |_a, v| Box::pin(async move { read_file(&v) }) }
|
||||
}
|
||||
|
||||
fn definition() -> ToolDef {
|
||||
ToolDef::new(
|
||||
"read_file",
|
||||
"Read the contents of a file. Returns the file contents with line numbers.",
|
||||
|
|
@ -41,7 +45,7 @@ pub fn definition() -> ToolDef {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn read_file(args: &serde_json::Value) -> Result<String> {
|
||||
fn read_file(args: &serde_json::Value) -> Result<String> {
|
||||
let args: Args = serde_json::from_value(args.clone())
|
||||
.context("invalid read_file arguments")?;
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ struct Args {
|
|||
|
||||
fn default_lines() -> usize { 50 }
|
||||
|
||||
pub(super) fn definition() -> ToolDef {
|
||||
fn definition() -> ToolDef {
|
||||
ToolDef::new(
|
||||
"view_image",
|
||||
"View an image file or capture a tmux pane screenshot. \
|
||||
|
|
@ -48,8 +48,12 @@ pub(super) fn definition() -> ToolDef {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn tool() -> super::Tool {
|
||||
super::Tool { def: definition(), handler: |_a, v| Box::pin(async move { view_image_text(&v) }) }
|
||||
}
|
||||
|
||||
/// Text-only version for the Tool registry.
|
||||
pub fn view_image_text(args: &serde_json::Value) -> anyhow::Result<String> {
|
||||
fn view_image_text(args: &serde_json::Value) -> anyhow::Result<String> {
|
||||
let output = view_image(args)?;
|
||||
Ok(output.text)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,13 @@ use serde_json::json;
|
|||
|
||||
use super::ToolDef;
|
||||
|
||||
pub fn tools() -> [super::Tool; 2] {
|
||||
[
|
||||
super::Tool { def: fetch_definition(), handler: |_a, v| Box::pin(async move { web_fetch(&v).await }) },
|
||||
super::Tool { def: search_definition(), handler: |_a, v| Box::pin(async move { web_search(&v).await }) },
|
||||
]
|
||||
}
|
||||
|
||||
// ── Fetch ───────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
|
@ -13,7 +20,7 @@ struct FetchArgs {
|
|||
url: String,
|
||||
}
|
||||
|
||||
pub fn fetch_definition() -> ToolDef {
|
||||
fn fetch_definition() -> ToolDef {
|
||||
ToolDef::new(
|
||||
"web_fetch",
|
||||
"Fetch content from a URL and return it as text. \
|
||||
|
|
@ -31,7 +38,7 @@ pub fn fetch_definition() -> ToolDef {
|
|||
)
|
||||
}
|
||||
|
||||
pub async fn web_fetch(args: &serde_json::Value) -> Result<String> {
|
||||
async fn web_fetch(args: &serde_json::Value) -> Result<String> {
|
||||
let a: FetchArgs = serde_json::from_value(args.clone())
|
||||
.context("invalid web_fetch arguments")?;
|
||||
|
||||
|
|
@ -64,7 +71,7 @@ struct SearchArgs {
|
|||
|
||||
fn default_num_results() -> usize { 5 }
|
||||
|
||||
pub fn search_definition() -> ToolDef {
|
||||
fn search_definition() -> ToolDef {
|
||||
ToolDef::new(
|
||||
"web_search",
|
||||
"Search the web and return results. Use for finding \
|
||||
|
|
@ -86,7 +93,7 @@ pub fn search_definition() -> ToolDef {
|
|||
)
|
||||
}
|
||||
|
||||
pub async fn web_search(args: &serde_json::Value) -> Result<String> {
|
||||
async fn web_search(args: &serde_json::Value) -> Result<String> {
|
||||
let a: SearchArgs = serde_json::from_value(args.clone())
|
||||
.context("invalid web_search arguments")?;
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,11 @@ struct Args {
|
|||
content: String,
|
||||
}
|
||||
|
||||
pub fn definition() -> ToolDef {
|
||||
pub fn tool() -> super::Tool {
|
||||
super::Tool { def: definition(), handler: |_a, v| Box::pin(async move { write_file(&v) }) }
|
||||
}
|
||||
|
||||
fn definition() -> ToolDef {
|
||||
ToolDef::new(
|
||||
"write_file",
|
||||
"Write content to a file. Creates the file if it doesn't exist, \
|
||||
|
|
@ -35,7 +39,7 @@ pub fn definition() -> ToolDef {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn write_file(args: &serde_json::Value) -> Result<String> {
|
||||
fn write_file(args: &serde_json::Value) -> Result<String> {
|
||||
let args: Args = serde_json::from_value(args.clone())
|
||||
.context("invalid write_file arguments")?;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue