Skip to content

Commit 55d5ad6

Browse files
chaliyclaude
andauthored
refactor(examples): extract fake tools into separate module (#218)
## Summary - Extract inline tool definitions and callbacks from `scripted_tool` example into a `fakes` module - Keeps `main()` focused on the `ScriptedTool` builder API and demo scenarios - No behavior change — same tools, same demo output ## Test plan - [x] `cargo build --example scripted_tool` passes - [x] Example runs successfully with same output - [ ] CI green --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 80b1d06 commit 55d5ad6

File tree

1 file changed

+125
-93
lines changed

1 file changed

+125
-93
lines changed

crates/bashkit/examples/scripted_tool.rs

Lines changed: 125 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -8,106 +8,20 @@
88
//! This example simulates an e-commerce API with tools for users, orders, and
99
//! inventory. The ScriptedTool lets an agent compose these in one call.
1010
11-
use bashkit::{ScriptedTool, Tool, ToolDef, ToolRequest};
11+
use bashkit::{ScriptedTool, Tool, ToolRequest};
1212

1313
#[tokio::main]
1414
async fn main() -> anyhow::Result<()> {
1515
println!("=== Scripted Tool Demo ===\n");
1616

17-
// Build the orchestrator with tool definitions + closures
17+
// Build the orchestrator with tool definitions + callbacks.
18+
// In production the callbacks would call real APIs.
1819
let mut tool = ScriptedTool::builder("ecommerce_api")
1920
.short_description("E-commerce API orchestrator with user, order, and inventory tools")
20-
.tool(
21-
ToolDef::new("get_user", "Fetch user by ID")
22-
.with_schema(serde_json::json!({
23-
"type": "object",
24-
"properties": {
25-
"id": {"type": "integer", "description": "User ID"}
26-
},
27-
"required": ["id"]
28-
})),
29-
|args| {
30-
let id = args.param_i64("id").ok_or("missing --id")?;
31-
32-
let users = [
33-
(1, "Alice", "alice@example.com", "premium"),
34-
(2, "Bob", "bob@example.com", "basic"),
35-
(3, "Charlie", "charlie@example.com", "premium"),
36-
];
37-
38-
match users.iter().find(|(uid, ..)| *uid == id) {
39-
Some((uid, name, email, tier)) => Ok(format!(
40-
"{{\"id\":{uid},\"name\":\"{name}\",\"email\":\"{email}\",\"tier\":\"{tier}\"}}\n"
41-
)),
42-
None => Err(format!("user {} not found", id)),
43-
}
44-
},
45-
)
46-
.tool(
47-
ToolDef::new("list_orders", "List orders for a user")
48-
.with_schema(serde_json::json!({
49-
"type": "object",
50-
"properties": {
51-
"user_id": {"type": "integer", "description": "User ID"}
52-
},
53-
"required": ["user_id"]
54-
})),
55-
|args| {
56-
let uid = args.param_i64("user_id").ok_or("missing --user_id")?;
57-
58-
let orders = match uid {
59-
1 => r#"[{"order_id":101,"item":"Laptop","qty":1,"price":999.99},{"order_id":102,"item":"Mouse","qty":2,"price":29.99}]"#,
60-
2 => r#"[{"order_id":201,"item":"Keyboard","qty":1,"price":79.99}]"#,
61-
3 => r#"[]"#,
62-
_ => return Err(format!("no orders for user {}", uid)),
63-
};
64-
65-
Ok(format!("{orders}\n"))
66-
},
67-
)
68-
.tool(
69-
ToolDef::new("get_inventory", "Check inventory for an item")
70-
.with_schema(serde_json::json!({
71-
"type": "object",
72-
"properties": {
73-
"item": {"type": "string", "description": "Item name"}
74-
},
75-
"required": ["item"]
76-
})),
77-
|args| {
78-
let item = args.param_str("item").ok_or("missing --item")?;
79-
80-
let stock = match item.to_lowercase().as_str() {
81-
"laptop" => 15,
82-
"mouse" => 142,
83-
"keyboard" => 67,
84-
_ => 0,
85-
};
86-
87-
Ok(format!(
88-
"{{\"item\":\"{}\",\"in_stock\":{}}}\n",
89-
item, stock
90-
))
91-
},
92-
)
93-
.tool(
94-
ToolDef::new("create_discount", "Create a discount code")
95-
.with_schema(serde_json::json!({
96-
"type": "object",
97-
"properties": {
98-
"user_id": {"type": "integer", "description": "User ID"},
99-
"percent": {"type": "integer", "description": "Discount percentage"}
100-
},
101-
"required": ["user_id", "percent"]
102-
})),
103-
|args| {
104-
let uid = args.param_i64("user_id").ok_or("missing --user_id")?;
105-
let pct = args.param_i64("percent").ok_or("missing --percent")?;
106-
Ok(format!(
107-
"{{\"code\":\"SAVE{pct}-U{uid}\",\"percent\":{pct},\"user_id\":{uid}}}\n"
108-
))
109-
},
110-
)
21+
.tool(fakes::get_user_def(), fakes::get_user)
22+
.tool(fakes::list_orders_def(), fakes::list_orders)
23+
.tool(fakes::get_inventory_def(), fakes::get_inventory)
24+
.tool(fakes::create_discount_def(), fakes::create_discount)
11125
.env("STORE_NAME", "Bashkit Shop")
11226
.build();
11327

@@ -227,3 +141,121 @@ async fn main() -> anyhow::Result<()> {
227141
println!("\n=== Demo Complete ===");
228142
Ok(())
229143
}
144+
145+
// ---------------------------------------------------------------------------
146+
// Fake e-commerce API — tool definitions + handlers
147+
//
148+
// In a real application these callbacks would call actual HTTP APIs, databases,
149+
// etc. They live in a separate module so the main function can focus on the
150+
// ScriptedTool builder API and the demo scenarios.
151+
// ---------------------------------------------------------------------------
152+
mod fakes {
153+
use bashkit::{ToolArgs, ToolDef};
154+
155+
// -- get_user --------------------------------------------------------
156+
157+
pub fn get_user_def() -> ToolDef {
158+
ToolDef::new("get_user", "Fetch user by ID").with_schema(serde_json::json!({
159+
"type": "object",
160+
"properties": {
161+
"id": {"type": "integer", "description": "User ID"}
162+
},
163+
"required": ["id"]
164+
}))
165+
}
166+
167+
pub fn get_user(args: &ToolArgs) -> Result<String, String> {
168+
let id = args.param_i64("id").ok_or("missing --id")?;
169+
170+
let users = [
171+
(1, "Alice", "alice@example.com", "premium"),
172+
(2, "Bob", "bob@example.com", "basic"),
173+
(3, "Charlie", "charlie@example.com", "premium"),
174+
];
175+
176+
match users.iter().find(|(uid, ..)| *uid == id) {
177+
Some((uid, name, email, tier)) => Ok(format!(
178+
"{{\"id\":{uid},\"name\":\"{name}\",\"email\":\"{email}\",\"tier\":\"{tier}\"}}\n"
179+
)),
180+
None => Err(format!("user {} not found", id)),
181+
}
182+
}
183+
184+
// -- list_orders -----------------------------------------------------
185+
186+
pub fn list_orders_def() -> ToolDef {
187+
ToolDef::new("list_orders", "List orders for a user").with_schema(serde_json::json!({
188+
"type": "object",
189+
"properties": {
190+
"user_id": {"type": "integer", "description": "User ID"}
191+
},
192+
"required": ["user_id"]
193+
}))
194+
}
195+
196+
pub fn list_orders(args: &ToolArgs) -> Result<String, String> {
197+
let uid = args.param_i64("user_id").ok_or("missing --user_id")?;
198+
199+
let orders = match uid {
200+
1 => {
201+
r#"[{"order_id":101,"item":"Laptop","qty":1,"price":999.99},{"order_id":102,"item":"Mouse","qty":2,"price":29.99}]"#
202+
}
203+
2 => r#"[{"order_id":201,"item":"Keyboard","qty":1,"price":79.99}]"#,
204+
3 => r#"[]"#,
205+
_ => return Err(format!("no orders for user {}", uid)),
206+
};
207+
208+
Ok(format!("{orders}\n"))
209+
}
210+
211+
// -- get_inventory ---------------------------------------------------
212+
213+
pub fn get_inventory_def() -> ToolDef {
214+
ToolDef::new("get_inventory", "Check inventory for an item").with_schema(
215+
serde_json::json!({
216+
"type": "object",
217+
"properties": {
218+
"item": {"type": "string", "description": "Item name"}
219+
},
220+
"required": ["item"]
221+
}),
222+
)
223+
}
224+
225+
pub fn get_inventory(args: &ToolArgs) -> Result<String, String> {
226+
let item = args.param_str("item").ok_or("missing --item")?;
227+
228+
let stock = match item.to_lowercase().as_str() {
229+
"laptop" => 15,
230+
"mouse" => 142,
231+
"keyboard" => 67,
232+
_ => 0,
233+
};
234+
235+
Ok(format!(
236+
"{{\"item\":\"{}\",\"in_stock\":{}}}\n",
237+
item, stock
238+
))
239+
}
240+
241+
// -- create_discount -------------------------------------------------
242+
243+
pub fn create_discount_def() -> ToolDef {
244+
ToolDef::new("create_discount", "Create a discount code").with_schema(serde_json::json!({
245+
"type": "object",
246+
"properties": {
247+
"user_id": {"type": "integer", "description": "User ID"},
248+
"percent": {"type": "integer", "description": "Discount percentage"}
249+
},
250+
"required": ["user_id", "percent"]
251+
}))
252+
}
253+
254+
pub fn create_discount(args: &ToolArgs) -> Result<String, String> {
255+
let uid = args.param_i64("user_id").ok_or("missing --user_id")?;
256+
let pct = args.param_i64("percent").ok_or("missing --percent")?;
257+
Ok(format!(
258+
"{{\"code\":\"SAVE{pct}-U{uid}\",\"percent\":{pct},\"user_id\":{uid}}}\n"
259+
))
260+
}
261+
}

0 commit comments

Comments
 (0)