tools: static string definitions, no runtime JSON construction

Tool definitions are now &'static str (name, description,
parameters_json) instead of runtime-constructed serde_json::Value.
No more json!() macro, no more ToolDef::new() for tool definitions.

The JSON schema strings are written directly as string literals.
When sent to the API, they can be interpolated without
serialization/deserialization.

Multi-tool modules return fixed-size arrays instead of Vecs:
- memory: [Tool; 12], journal: [Tool; 3]
- channels: [Tool; 4]
- control: [Tool; 3]
- web: [Tool; 2]

ToolDef/FunctionDef remain for backward compat (API wire format,
summarize_args) but are no longer used in tool definitions.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
ProofOfConcept 2026-04-04 15:50:14 -04:00 committed by Kent Overstreet
parent ed150df628
commit 53ad8cc9df
13 changed files with 205 additions and 561 deletions

View file

@ -2,54 +2,25 @@
use anyhow::{Context, Result};
use serde::Deserialize;
use serde_json::json;
use std::path::Path;
use super::ToolDef;
#[derive(Deserialize)]
struct Args {
file_path: String,
content: String,
}
pub fn tool() -> super::Tool {
super::Tool { def: definition(), handler: |_a, v| Box::pin(async move { write_file(&v) }) }
super::Tool {
name: "write_file",
description: "Create or overwrite a file with the given content.",
parameters_json: r#"{"type":"object","properties":{"file_path":{"type":"string","description":"Absolute path to write"},"content":{"type":"string","description":"File content"}},"required":["file_path","content"]}"#,
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, \
overwrites if it does. Creates parent directories as needed.",
json!({
"type": "object",
"properties": {
"file_path": {
"type": "string",
"description": "Absolute path to the file to write"
},
"content": {
"type": "string",
"description": "The content to write to the file"
}
},
"required": ["file_path", "content"]
}),
)
}
#[derive(Deserialize)]
struct Args { file_path: String, content: String }
fn write_file(args: &serde_json::Value) -> Result<String> {
let args: Args = serde_json::from_value(args.clone())
.context("invalid write_file arguments")?;
if let Some(parent) = Path::new(&args.file_path).parent() {
std::fs::create_dir_all(parent)
.with_context(|| format!("Failed to create directories for {}", args.file_path))?;
let a: Args = serde_json::from_value(args.clone()).context("invalid write_file arguments")?;
if let Some(parent) = std::path::Path::new(&a.file_path).parent() {
std::fs::create_dir_all(parent).ok();
}
std::fs::write(&args.file_path, &args.content)
.with_context(|| format!("Failed to write {}", args.file_path))?;
Ok(format!("Wrote {} lines to {}", args.content.lines().count(), args.file_path))
std::fs::write(&a.file_path, &a.content)
.with_context(|| format!("Failed to write {}", a.file_path))?;
Ok(format!("Wrote {} bytes to {}", a.content.len(), a.file_path))
}