Skip to content

Commit 49119e8

Browse files
committed
Add SimpleTool for library users to define custom tools
- Add SimpleTool struct that implements Tool trait with minimal requirements - Users define name, description, and JSON schema - Tool execution is handled by the user via AgentStep::ToolRequest - Export SimpleTool and ToolCall from lib.rs - Update LIBRARY.md with comprehensive custom tool documentation This enables library users to create agents with custom tools while keeping the implementation simple - they handle tool execution themselves rather than using the internal effect pipeline.
1 parent 368f810 commit 49119e8

3 files changed

Lines changed: 227 additions & 98 deletions

File tree

LIBRARY.md

Lines changed: 147 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Using Codey as a Library
22

3-
Codey can be used as a library to create AI agents with custom system prompts in your Rust projects.
3+
Codey can be used as a library to create AI agents with custom system prompts and tools in your Rust projects.
44

55
## Installation
66

@@ -10,6 +10,7 @@ Add codey to your `Cargo.toml`:
1010
[dependencies]
1111
codey = { git = "https://github.com/tcdent/codey" }
1212
tokio = { version = "1", features = ["full"] }
13+
serde_json = "1"
1314
```
1415

1516
### Patched Dependencies
@@ -30,33 +31,26 @@ genai = { path = "../codey/lib/genai" }
3031

3132
Note: Run `make patch` in the codey repository first to download and patch the dependencies.
3233

33-
## Basic Usage
34+
## Basic Usage (No Tools)
3435

3536
```rust
3637
use codey::{Agent, AgentRuntimeConfig, AgentStep, RequestMode, ToolRegistry};
3738

3839
#[tokio::main]
3940
async fn main() {
40-
// Create an agent with a custom system prompt (no tools)
4141
let mut agent = Agent::new(
4242
AgentRuntimeConfig::default(),
43-
"You are a helpful assistant. Answer questions concisely.",
44-
None, // OAuth credentials (uses ANTHROPIC_API_KEY env var)
43+
"You are a helpful assistant.",
44+
None, // uses ANTHROPIC_API_KEY env var
4545
ToolRegistry::empty(),
4646
);
4747

48-
// Send a message
4948
agent.send_request("What is the capital of France?", RequestMode::Normal);
5049

51-
// Process streaming responses
5250
while let Some(step) = agent.next().await {
5351
match step {
5452
AgentStep::TextDelta(text) => print!("{}", text),
55-
AgentStep::ThinkingDelta(_) => { /* extended thinking output */ }
56-
AgentStep::Finished { usage } => {
57-
println!("\n\nTokens used: {}", usage.output_tokens);
58-
break;
59-
}
53+
AgentStep::Finished { .. } => break,
6054
AgentStep::Error(e) => {
6155
eprintln!("Error: {}", e);
6256
break;
@@ -67,33 +61,143 @@ async fn main() {
6761
}
6862
```
6963

70-
## Public API
64+
## Custom Tools
7165

72-
### `Agent`
66+
You can define custom tools using `SimpleTool` and handle their execution yourself.
7367

74-
The main agent type that handles conversations with Claude.
68+
### Defining Tools
7569

7670
```rust
77-
// Create a new agent
78-
let mut agent = Agent::new(
79-
config, // AgentRuntimeConfig
80-
system_prompt, // &str
81-
oauth, // Option<OAuthCredentials>
82-
tools, // ToolRegistry
71+
use codey::{SimpleTool, ToolRegistry};
72+
use serde_json::json;
73+
use std::sync::Arc;
74+
75+
// Define a tool
76+
let weather_tool = SimpleTool::new(
77+
"get_weather",
78+
"Get the current weather for a location",
79+
json!({
80+
"type": "object",
81+
"properties": {
82+
"location": {
83+
"type": "string",
84+
"description": "City name, e.g. 'San Francisco'"
85+
}
86+
},
87+
"required": ["location"]
88+
}),
8389
);
8490

85-
// Send a message
86-
agent.send_request("Hello!", RequestMode::Normal);
91+
// Register tools
92+
let mut tools = ToolRegistry::empty();
93+
tools.register(Arc::new(weather_tool));
94+
```
95+
96+
### Handling Tool Calls
97+
98+
When the LLM wants to use a tool, you'll receive an `AgentStep::ToolRequest`. You must execute the tool and submit the result back to the agent:
99+
100+
```rust
101+
use codey::{Agent, AgentRuntimeConfig, AgentStep, RequestMode, SimpleTool, ToolCall, ToolRegistry};
102+
use serde_json::json;
103+
use std::sync::Arc;
104+
105+
#[tokio::main]
106+
async fn main() {
107+
// Set up tools
108+
let weather_tool = SimpleTool::new(
109+
"get_weather",
110+
"Get the current weather for a location",
111+
json!({
112+
"type": "object",
113+
"properties": {
114+
"location": { "type": "string" }
115+
},
116+
"required": ["location"]
117+
}),
118+
);
119+
120+
let mut tools = ToolRegistry::empty();
121+
tools.register(Arc::new(weather_tool));
122+
123+
// Create agent with tools
124+
let mut agent = Agent::new(
125+
AgentRuntimeConfig::default(),
126+
"You are a helpful assistant with access to weather data.",
127+
None,
128+
tools,
129+
);
130+
131+
agent.send_request("What's the weather in Paris?", RequestMode::Normal);
87132

88-
// Get streaming responses
89-
while let Some(step) = agent.next().await {
90-
// Handle AgentStep variants
133+
loop {
134+
match agent.next().await {
135+
Some(AgentStep::TextDelta(text)) => print!("{}", text),
136+
137+
Some(AgentStep::ToolRequest(calls)) => {
138+
// Handle each tool call
139+
for call in calls {
140+
let result = execute_tool(&call);
141+
agent.submit_tool_result(&call.call_id, result);
142+
}
143+
// Continue processing after submitting results
144+
}
145+
146+
Some(AgentStep::Finished { .. }) => {
147+
println!();
148+
break;
149+
}
150+
151+
Some(AgentStep::Error(e)) => {
152+
eprintln!("Error: {}", e);
153+
break;
154+
}
155+
156+
None => break,
157+
_ => {}
158+
}
159+
}
160+
}
161+
162+
fn execute_tool(call: &ToolCall) -> String {
163+
match call.name.as_str() {
164+
"get_weather" => {
165+
let location = call.params["location"].as_str().unwrap_or("unknown");
166+
// Your actual implementation here
167+
format!("Weather in {}: Sunny, 22°C", location)
168+
}
169+
_ => format!("Unknown tool: {}", call.name),
170+
}
91171
}
92172
```
93173

94-
### `AgentRuntimeConfig`
174+
### `ToolCall` Structure
175+
176+
When you receive a tool request, each `ToolCall` contains:
177+
178+
```rust
179+
pub struct ToolCall {
180+
pub call_id: String, // Unique ID for this call (use with submit_tool_result)
181+
pub name: String, // Tool name
182+
pub params: serde_json::Value, // Parameters from the LLM
183+
// ... other fields
184+
}
185+
```
186+
187+
## Public API Reference
188+
189+
### `Agent`
190+
191+
The main agent type for conversations with Claude.
95192

96-
Configuration for the agent:
193+
```rust
194+
let mut agent = Agent::new(config, system_prompt, oauth, tools);
195+
agent.send_request("Hello!", RequestMode::Normal);
196+
while let Some(step) = agent.next().await { /* ... */ }
197+
agent.submit_tool_result(&call_id, result);
198+
```
199+
200+
### `AgentRuntimeConfig`
97201

98202
```rust
99203
let config = AgentRuntimeConfig {
@@ -110,34 +214,39 @@ let config = AgentRuntimeConfig::default();
110214

111215
### `AgentStep`
112216

113-
Events emitted during agent processing:
217+
Events emitted during processing:
114218

115219
- `TextDelta(String)` - Streaming text output
116220
- `ThinkingDelta(String)` - Extended thinking output
221+
- `ToolRequest(Vec<ToolCall>)` - LLM wants to use tools
117222
- `Finished { usage: Usage }` - Processing complete
118223
- `Error(String)` - Error occurred
119224
- `Retrying { attempt, error }` - Retrying after error
120-
- `ToolRequest(Vec<ToolCall>)` - Tools requested (not used with empty registry)
121-
- `CompactionDelta(String)` - Context compaction output
122225

123-
### `RequestMode`
226+
### `SimpleTool`
124227

125-
Controls agent behavior:
228+
Define a tool for the LLM to use:
126229

127-
- `RequestMode::Normal` - Standard conversation
128-
- `RequestMode::Compaction` - Context compaction mode
230+
```rust
231+
let tool = SimpleTool::new(
232+
"tool_name", // Name the LLM will use
233+
"Description of tool", // Help the LLM understand when to use it
234+
json!({ /* JSON Schema for parameters */ }),
235+
);
236+
```
129237

130238
### `ToolRegistry`
131239

132-
For library usage, create an empty registry:
240+
Manage available tools:
133241

134242
```rust
135-
let tools = ToolRegistry::empty();
243+
let mut tools = ToolRegistry::empty();
244+
tools.register(Arc::new(my_tool));
136245
```
137246

138247
### `Usage`
139248

140-
Token usage statistics returned in `AgentStep::Finished`:
249+
Token usage statistics:
141250

142251
```rust
143252
pub struct Usage {
@@ -155,61 +264,3 @@ Set the `ANTHROPIC_API_KEY` environment variable:
155264
```bash
156265
export ANTHROPIC_API_KEY=sk-ant-...
157266
```
158-
159-
## Example: Interactive Chat
160-
161-
```rust
162-
use codey::{Agent, AgentRuntimeConfig, AgentStep, RequestMode, ToolRegistry};
163-
use std::io::{self, Write};
164-
165-
#[tokio::main]
166-
async fn main() -> anyhow::Result<()> {
167-
let mut agent = Agent::new(
168-
AgentRuntimeConfig::default(),
169-
"You are a helpful assistant.",
170-
None,
171-
ToolRegistry::empty(),
172-
);
173-
174-
loop {
175-
print!("> ");
176-
io::stdout().flush()?;
177-
178-
let mut input = String::new();
179-
io::stdin().read_line(&mut input)?;
180-
let input = input.trim();
181-
182-
if input == "quit" {
183-
break;
184-
}
185-
186-
agent.send_request(input, RequestMode::Normal);
187-
188-
while let Some(step) = agent.next().await {
189-
match step {
190-
AgentStep::TextDelta(text) => print!("{}", text),
191-
AgentStep::Finished { .. } => {
192-
println!();
193-
break;
194-
}
195-
AgentStep::Error(e) => {
196-
eprintln!("\nError: {}", e);
197-
break;
198-
}
199-
_ => {}
200-
}
201-
}
202-
}
203-
204-
Ok(())
205-
}
206-
```
207-
208-
## Limitations
209-
210-
The library exposes a minimal API focused on creating agents with custom system prompts. The built-in tools (file operations, shell, web search, etc.) are specific to the Codey binary and its UI, so they are not exposed in the library API.
211-
212-
If you need tool capabilities, you can:
213-
1. Implement tool-like behavior in your system prompt
214-
2. Parse structured output from the agent
215-
3. Build your own tool execution layer outside of codey

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,4 @@ mod transcript;
5252
// Re-export the public API
5353
pub use config::AgentRuntimeConfig;
5454
pub use llm::{Agent, AgentStep, RequestMode, Usage};
55-
pub use tools::ToolRegistry;
55+
pub use tools::{SimpleTool, ToolCall, ToolRegistry};

0 commit comments

Comments
 (0)