Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 16 additions & 18 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -38,32 +38,30 @@ Add to your `Cargo.toml`:
llm-coding-tools-rig = "0.1"
```

```rust
```rust,no_run
use llm_coding_tools_rig::absolute::{ReadTool, WriteTool, GlobTool};
use llm_coding_tools_rig::{BashTool, PreambleBuilder, TodoTools};
use rig::tool::ToolSet;
use rig::providers::openai;
use rig::completion::Prompt;

// Track tools and generate LLM guidance
let mut pb = PreambleBuilder::new();
let mut pb = PreambleBuilder::<false>::new();
let todos = TodoTools::new();

let toolset = ToolSet::builder()
.static_tool(pb.track(ReadTool::<true>::new()))
.static_tool(pb.track(WriteTool::new()))
.static_tool(pb.track(GlobTool::new()))
.static_tool(pb.track(BashTool::new()))
.static_tool(pb.track(todos.read))
.static_tool(pb.track(todos.write))
let client = openai::Client::from_env();
let agent = client
.agent("gpt-4o")
.tool(pb.track(ReadTool::<true>::new()))
.tool(pb.track(WriteTool::new()))
.tool(pb.track(GlobTool::new()))
.tool(pb.track(BashTool::new()))
.tool(pb.track(todos.read))
.tool(pb.track(todos.write))
.preamble(&pb.build())
.build();

// Generate preamble for agent system prompt
let preamble = pb.build();

// Use with rig agent:
// let agent = client.agent("gpt-4o")
// .preamble(&preamble)
// .tools(toolset)
// .build();
// Use the agent
// let response = agent.prompt("List all files").await?;
```

## Examples
Expand Down
13 changes: 1 addition & 12 deletions src/llm-coding-tools-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,5 @@
#![doc = include_str!(concat!("../", env!("CARGO_PKG_README")))]
#![warn(missing_docs)]
//! Core types and utilities for coding tools.
//!
//! This crate provides framework-agnostic building blocks:
//! - [`ToolError`] and [`ToolResult`] for error handling
//! - [`ToolOutput`] for tool responses with truncation metadata
//! - Utility functions for text processing
//!
//! # Features
//!
//! - `async`: Enables async function signatures and async-only modules.
//! - `tokio` (default): Enables async via tokio runtime (implies `async`).
//! When disabled, all operations are synchronous.

// Validate feature combinations at compile time
#[cfg(all(feature = "async", not(feature = "tokio")))]
Expand Down
8 changes: 5 additions & 3 deletions src/llm-coding-tools-core/src/preamble.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,13 @@ impl<const ENV: bool> PreambleBuilder<ENV> {
/// // register _my_tool with your tool collection
/// ```
///
/// For example, if working with rig's ToolSet builder:
/// For example, if working with rig's agent builder:
/// ```text
/// let mut pb = PreambleBuilder::new();
/// let toolset = ToolSet::builder()
/// .static_tool(pb.track(ReadTool::new()))
/// let agent = client
/// .agent("gpt-4o")
/// .tool(pb.track(ReadTool::new()))
/// .preamble(&pb.build())
/// .build();
/// ```
pub fn track<T: ToolContext>(&mut self, tool: T) -> T {
Expand Down
28 changes: 12 additions & 16 deletions src/llm-coding-tools-rig/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,33 +29,29 @@ llm-coding-tools-rig = "0.1"

Minimal runnable agent (requires `OPENAI_API_KEY`):

```rust
```rust,no_run
use llm_coding_tools_rig::absolute::{GlobTool, GrepTool, ReadTool};
use llm_coding_tools_rig::{BashTool, PreambleBuilder, TodoTools};
use rig::providers::openai;
use rig::tool::ToolSet;
use rig::client::{ProviderClient, CompletionClient};
use rig::completion::Prompt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let todos = TodoTools::new();
let mut pb = PreambleBuilder::<false>::new();

let toolset = ToolSet::builder()
.static_tool(pb.track(ReadTool::<true>::new()))
.static_tool(pb.track(GlobTool::new()))
.static_tool(pb.track(GrepTool::<true>::new()))
.static_tool(pb.track(BashTool::new()))
.static_tool(pb.track(todos.read))
.static_tool(pb.track(todos.write))
.build();

let preamble = pb.build();

// Build agent with preamble tracking
let client = openai::Client::from_env();
let agent = client
.agent("gpt-4o")
.preamble(&preamble)
.tools(toolset)
.tool(pb.track(ReadTool::<true>::new()))
.tool(pb.track(GlobTool::new()))
.tool(pb.track(GrepTool::<true>::new()))
.tool(pb.track(BashTool::new()))
.tool(pb.track(todos.read))
.tool(pb.track(todos.write))
.preamble(&pb.build()) // Build preamble after tracking tools
.build();

let response = agent
Expand Down Expand Up @@ -85,7 +81,7 @@ Executes shell commands.

File tools come in `absolute::*` (unrestricted) and `allowed::*` (sandboxed) variants:

```rust
```rust,no_run
use llm_coding_tools_rig::absolute::{ReadTool, WriteTool};
use llm_coding_tools_rig::allowed::{ReadTool as AllowedReadTool, WriteTool as AllowedWriteTool};
use llm_coding_tools_rig::AllowedPathResolver;
Expand Down
44 changes: 25 additions & 19 deletions src/llm-coding-tools-rig/examples/rig-basic.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,46 @@
//! PreambleBuilder example - pass-through tracking for ToolSet.
//! PreambleBuilder example - building a complete rig agent.
//!
//! Demonstrates:
//! - Using PreambleBuilder alongside ToolSet::builder()
//! - Full access to Rig's API (no wrapper limitations)
//! - Using PreambleBuilder with rig's agent builder
//! - Chained .tool() calls for registering tools
//! - TodoTools with shared state
//! - Generating and using the preamble string
//!
//! Run: cargo run --example rig-basic -p llm-coding-tools-rig
//! Run: OPENAI_API_KEY=... cargo run --example rig-basic -p llm-coding-tools-rig

use llm_coding_tools_rig::absolute::{GlobTool, GrepTool, ReadTool};
use llm_coding_tools_rig::{BashTool, PreambleBuilder, TodoTools};
use rig::tool::ToolSet;
use rig::client::{CompletionClient, ProviderClient};
use rig::completion::Prompt;
use rig::providers::openai;

#[tokio::main]
async fn main() {
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// === Create shared state for todos ===
let todos = TodoTools::new();

// === Create preamble builder to track tools ===
let mut pb = PreambleBuilder::<false>::new();

// === Use ToolSet::builder() directly - full Rig API! ===
let _toolset = ToolSet::builder()
.static_tool(pb.track(ReadTool::<true>::new()))
.static_tool(pb.track(GlobTool::new()))
.static_tool(pb.track(GrepTool::<true>::new()))
.static_tool(pb.track(BashTool::new()))
// === Build agent with chained .tool() calls ===
let client = openai::Client::from_env();
let agent = client
.agent("gpt-4o")
.tool(pb.track(ReadTool::<true>::new()))
.tool(pb.track(GlobTool::new()))
.tool(pb.track(GrepTool::<true>::new()))
.tool(pb.track(BashTool::new()))
// Todo tools share state for read/write coordination
.static_tool(pb.track(todos.read))
.static_tool(pb.track(todos.write))
// Can use any ToolSet method here - dynamic_tool, etc.
.tool(pb.track(todos.read))
.tool(pb.track(todos.write))
.preamble(&pb.build())
.build();

// === Generate preamble string ===
let preamble = pb.build();
// === Use the agent ===
let response = agent
.prompt("What files are in the current directory?")
.await?;
println!("{response}");

// Print the preamble
println!("{preamble}");
Ok(())
}
32 changes: 19 additions & 13 deletions src/llm-coding-tools-rig/examples/rig-sandboxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
//! - Security-conscious deployments limiting filesystem exposure
//! - Project-scoped agents that shouldn't touch system files
//!
//! Run: cargo run --example rig-sandboxed -p llm-coding-tools-rig
//! Run: OPENAI_API_KEY=... cargo run --example rig-sandboxed -p llm-coding-tools-rig

use llm_coding_tools_rig::allowed::{EditTool, GlobTool, GrepTool, ReadTool, WriteTool};
use llm_coding_tools_rig::{AllowedPathResolver, PreambleBuilder};
use rig::tool::ToolSet;
use rig::client::{CompletionClient, ProviderClient};
use rig::completion::Prompt;
use rig::providers::openai;
use std::path::PathBuf;

#[tokio::main]
Expand Down Expand Up @@ -40,20 +42,24 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let glob = GlobTool::with_resolver(resolver.clone());
let grep: GrepTool<true> = GrepTool::with_resolver(resolver);

// === Build toolset ===
// === Build agent with sandboxed tools ===
let mut pb = PreambleBuilder::<false>::new();
let _toolset = ToolSet::builder()
.static_tool(pb.track(read))
.static_tool(pb.track(write))
.static_tool(pb.track(edit))
.static_tool(pb.track(glob))
.static_tool(pb.track(grep))
let client = openai::Client::from_env();
let agent = client
.agent("gpt-4o")
.tool(pb.track(read))
.tool(pb.track(write))
.tool(pb.track(edit))
.tool(pb.track(glob))
.tool(pb.track(grep))
.preamble(&pb.build())
.build();

let preamble = pb.build();

// Print the preamble
println!("{preamble}");
// === Use the agent ===
let response = agent
.prompt("List all Rust files in the current directory")
.await?;
println!("{response}");

Ok(())
}
19 changes: 1 addition & 18 deletions src/llm-coding-tools-rig/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,4 @@
//! Rig framework Tool implementations for coding tools.
//!
//! This crate provides `rig_core::tool::Tool` implementations wrapping
//! the core operations from [`llm_coding_tools_core`].
//!
//! # Module Organization
//!
//! - [`absolute`] - Tools requiring absolute paths (no path restriction)
//! - [`allowed`] - Tools restricted to allowed directories
//! - Standalone tools (bash, todo, webfetch) at crate root
//!
//! # Example
//!
//! ```no_run
//! use llm_coding_tools_rig::absolute::ReadTool;
//! use llm_coding_tools_rig::BashTool;
//! ```

#![doc = include_str!(concat!("../", env!("CARGO_PKG_README")))]
#![warn(missing_docs)]

pub mod absolute;
Expand Down
59 changes: 34 additions & 25 deletions src/llm-coding-tools-serdesai/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,39 +28,47 @@ llm-coding-tools-serdesai = "0.1"

## Quick Start

```rust
Minimal runnable agent (requires `OPENAI_API_KEY`):

```rust,no_run
use llm_coding_tools_serdesai::absolute::{GlobTool, GrepTool, ReadTool};
use llm_coding_tools_serdesai::agent_ext::AgentBuilderExt;
use llm_coding_tools_serdesai::{BashTool, PreambleBuilder, create_todo_tools};
use serdes_ai::tools::ToolRegistry;
use serdes_ai::prelude::*;

#[tokio::main]
async fn main() {
let mut pb = PreambleBuilder::<false>::new();
let mut registry = ToolRegistry::<()>::new();

registry.register(pb.track(ReadTool::<true>::new()));
registry.register(pb.track(GlobTool::new()));
registry.register(pb.track(GrepTool::<true>::new()));
registry.register(pb.track(BashTool::new()));

async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
let (todo_read, todo_write, _state) = create_todo_tools();
registry.register(pb.track(todo_read));
registry.register(pb.track(todo_write));

let preamble = pb.build();
let mut pb = PreambleBuilder::<false>::new();

// Pass `preamble` to your agent's system prompt
// Pass `registry` to your agent's tools
// Build agent with tools - call .system_prompt() last
let agent = AgentBuilder::<(), String>::from_model("openai:gpt-4o")?
.tool(pb.track(ReadTool::<true>::new()))
.tool(pb.track(GlobTool::new()))
.tool(pb.track(GrepTool::<true>::new()))
.tool(pb.track(BashTool::new()))
.tool(pb.track(todo_read))
.tool(pb.track(todo_write))
.system_prompt(pb.build()) // Last, after tracking all tools
.build();

// Run agent with tools
let response = agent
.run("Search for TODO comments in src/", ())
.await?;
println!("{}", response.output());

Ok(())
}
```

See the [basic example](examples/basic.rs) for a complete working setup.
See the [serdesai-basic example](examples/serdesai-basic.rs) for a complete working setup.

## Usage

File tools come in `absolute::*` (unrestricted) and `allowed::*` (sandboxed) variants:

```rust
```rust,no_run
use llm_coding_tools_serdesai::absolute::{ReadTool, WriteTool};
use llm_coding_tools_serdesai::allowed::{ReadTool as AllowedReadTool, WriteTool as AllowedWriteTool};
use std::path::PathBuf;
Expand All @@ -70,22 +78,23 @@ let read = ReadTool::<true>::new();

// Sandboxed access (paths relative to allowed directories)
let allowed_paths = vec![PathBuf::from("/home/user/project"), PathBuf::from("/tmp")];
let sandboxed_read: AllowedReadTool<true> = AllowedReadTool::new(allowed_paths.clone());
let sandboxed_write = AllowedWriteTool::new(allowed_paths);
let sandboxed_read: AllowedReadTool<true> = AllowedReadTool::new(allowed_paths.clone()).unwrap();
let sandboxed_write = AllowedWriteTool::new(allowed_paths).unwrap();
```

Other tools: `BashTool`, `WebFetchTool`, `TaskTool`, `TodoReadTool`, `TodoWriteTool`.
Use `PreambleBuilder` to register tools and pass `pb.build()` to your agent's system prompt.
Use `PreambleBuilder` to track tools and pass `pb.build()` to `.system_prompt()`.
Use `AgentBuilderExt::tool()` to add tools that implement `Tool<Deps>` to the agent.
Context strings are re-exported in `llm_coding_tools_serdesai::context` (e.g., `BASH`, `READ_ABSOLUTE`).

## Examples

```bash
# Basic toolset setup with PreambleBuilder
cargo run --example basic -p llm-coding-tools-serdesai
# Basic agent setup with AgentBuilderExt
cargo run --example serdesai-basic -p llm-coding-tools-serdesai

# Sandboxed file access with allowed::* tools
cargo run --example sandboxed -p llm-coding-tools-serdesai
cargo run --example serdesai-sandboxed -p llm-coding-tools-serdesai
```

## License
Expand Down
Loading
Loading