1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//! Helper functions for creating tool handlers.
use Future;
use PhantomData;
use Value;
use ;
use crateToolDefinition;
/// Creates a [`ToolHandler<()>`](super::ToolHandler) from a closure (no context).
///
/// The closure receives the tool's JSON arguments and returns a
/// `Result<impl Into<ToolOutput>, ToolError>`. Returning `Result<String, ToolError>`
/// also works via the `From<String>` impl on `ToolOutput`.
///
/// For tools that need shared context, use [`tool_fn_with_ctx`] instead.
///
/// # Example
///
/// ```rust
/// use llm_stack::tool::tool_fn;
/// use llm_stack::{JsonSchema, ToolDefinition};
/// use serde_json::{json, Value};
///
/// let handler = tool_fn(
/// ToolDefinition {
/// name: "add".into(),
/// description: "Add two numbers".into(),
/// parameters: JsonSchema::new(json!({
/// "type": "object",
/// "properties": {
/// "a": { "type": "number" },
/// "b": { "type": "number" }
/// },
/// "required": ["a", "b"]
/// })),
/// retry: None,
/// },
/// |input: Value| async move {
/// let a = input["a"].as_f64().unwrap_or(0.0);
/// let b = input["b"].as_f64().unwrap_or(0.0);
/// Ok(format!("{}", a + b))
/// },
/// );
/// ```
/// Creates a [`ToolHandler<Ctx>`](super::ToolHandler) from a closure that receives context.
///
/// The closure receives the tool's JSON arguments and a reference to the
/// context, and returns a `Result<impl Into<ToolOutput>, ToolError>`.
///
/// # Example
///
/// ```rust
/// use llm_stack::tool::{tool_fn_with_ctx, ToolOutput};
/// use llm_stack::{JsonSchema, ToolDefinition};
/// use serde_json::{json, Value};
///
/// struct AppContext {
/// db_url: String,
/// }
///
/// let handler = tool_fn_with_ctx(
/// ToolDefinition {
/// name: "lookup_user".into(),
/// description: "Look up a user by ID".into(),
/// parameters: JsonSchema::new(json!({
/// "type": "object",
/// "properties": {
/// "user_id": { "type": "string" }
/// },
/// "required": ["user_id"]
/// })),
/// retry: None,
/// },
/// |input: Value, ctx: &AppContext| {
/// let user_id = input["user_id"].as_str().unwrap_or("").to_string();
/// let db_url = ctx.db_url.clone();
/// async move {
/// // Use db_url and user_id in async work...
/// Ok(ToolOutput::new(format!("Found user {} in {}", user_id, db_url)))
/// }
/// },
/// );
/// ```