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
//! The [`Tool`] trait -- how you give a model something it can call.
use async_trait;
use Value;
use crateToolError;
use crateObjectSchema;
use crateToolOutput;
/// Something a model can ask to have run on its behalf -- a weather lookup, a
/// database query, a calculator, anything your application can do that you want to
/// expose to the model.
///
/// Attach one or more tools to a text request (see
/// [`PendingTextRequest::with_tool`](crate::text::PendingTextRequest::with_tool)),
/// and if the model decides to call one, `llmprism` runs it for you, sends the
/// result back, and continues the conversation automatically -- you don't need to
/// write any of that back-and-forth yourself. See [`crate::tool_loop`] if you want
/// to understand exactly how that works.
///
/// Arguments come in and results go out as [`serde_json::Value`] rather than a
/// typed struct. That's not a limitation carried over from somewhere else -- a
/// tool call always arrives from the model as a JSON blob picked out by name at
/// runtime, so JSON in, JSON out is simply the natural shape here. If you'd rather
/// work with a typed Rust struct inside `call`, just deserialize `args` yourself
/// with `serde_json::from_value` as the first thing you do.
///
/// # Example
///
/// ```
/// use async_trait::async_trait;
/// use llmprism::error::ToolError;
/// use llmprism::schema::{ObjectSchema, Schema, StringSchema};
/// use llmprism::tool::Tool;
/// use llmprism::value_objects::ToolOutput;
/// use serde_json::Value;
///
/// struct Weather {
/// parameters: ObjectSchema,
/// }
///
/// impl Weather {
/// fn new() -> Self {
/// Self {
/// parameters: ObjectSchema::new("parameters").with_property(
/// Schema::String(StringSchema::new("city").with_description("City name")),
/// true, // required
/// ),
/// }
/// }
/// }
///
/// #[async_trait]
/// impl Tool for Weather {
/// fn name(&self) -> &str { "get_weather" }
/// fn description(&self) -> &str { "Looks up the current weather for a city" }
/// fn parameters(&self) -> &ObjectSchema { &self.parameters }
///
/// async fn call(&self, args: Value) -> Result<ToolOutput, ToolError> {
/// let city = args.get("city").and_then(|v| v.as_str()).unwrap_or("unknown");
/// Ok(ToolOutput::from(format!("It's sunny in {city}.")))
/// }
/// }
/// ```