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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
impl Client {
/// Clears all conversation history.
///
/// This resets the conversation to a blank slate while preserving the client
/// configuration (tools, hooks, model, etc.). The next message will start a
/// fresh conversation with no prior context.
///
/// # State Changes
///
/// - Clears `history` vector
/// - Does NOT modify current stream, options, or other state
///
/// # Use Cases
///
/// - Starting a new conversation
/// - Preventing context length issues
/// - Clearing sensitive data
/// - Implementing conversation sessions
///
/// # Examples
///
/// ```rust,no_run
/// # use open_agent::{Client, AgentOptions, ContentBlock};
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
/// let mut client = Client::new(AgentOptions::default())?;
///
/// // First conversation
/// client.send("Hello").await?;
/// while let Some(_) = client.receive().await? {}
///
/// // Clear and start fresh
/// client.clear_history();
///
/// // New conversation with no memory of previous
/// client.send("Hello again").await?;
/// # Ok(())
/// # }
/// ```
pub fn clear_history(&mut self) {
self.history.clear();
self.manual_receive_buffer.clear();
}
/// Adds a tool result to the conversation history for manual tool execution.
///
/// This method is used exclusively in **manual mode** after receiving a `ToolUseBlock`.
/// The workflow is:
///
/// 1. `receive()` returns a `ToolUseBlock`
/// 2. You execute the tool yourself
/// 3. Call `add_tool_result()` with the tool's output
/// 4. Call `send("")` to continue the conversation
/// 5. The model receives the tool result and generates a response
///
/// # Parameters
///
/// - `tool_use_id`: The unique ID from the `ToolUseBlock` (must match exactly)
/// - `content`: The tool's output as a JSON value
///
/// # Behavior
///
/// Creates a `ToolResultBlock` and adds it to conversation history as a tool message.
/// This preserves the tool call/result pairing that the model needs to understand
/// the conversation flow.
///
/// # State Changes
///
/// - Appends a tool message to `history`
/// - Does NOT modify stream or trigger any requests
///
/// # Important Notes
///
/// - **Not used in auto mode**: Auto-execution handles tool results automatically
/// - **ID must match**: The `tool_use_id` must match the ID from the `ToolUseBlock`
/// - **No validation**: This method doesn't validate the result format
/// - **Must call send()**: After adding result(s), call `send("")` to continue
///
/// # Examples
///
/// ## Basic Manual Tool Execution
///
/// ```rust,no_run
/// use open_agent::{Client, AgentOptions, ContentBlock};
/// use serde_json::json;
///
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
/// let mut client = Client::new(AgentOptions::default())?;
/// client.send("Use the calculator").await?;
///
/// while let Some(block) = client.receive().await? {
/// match block {
/// ContentBlock::ToolUse(tool_use) => {
/// // Execute tool manually
/// let result = json!({"result": 42});
///
/// // Add result to history
/// client.add_tool_result(tool_use.id(), result)?;
///
/// // Continue conversation to get model's response
/// client.send("").await?;
/// }
/// ContentBlock::Text(text) => {
/// println!("{}", text.text);
/// }
/// ContentBlock::ToolResult(_) | ContentBlock::Image(_) => {}
/// }
/// }
/// # Ok(())
/// # }
/// ```
///
/// ## Handling Tool Errors
///
/// ```rust,no_run
/// use open_agent::{Client, AgentOptions, ContentBlock};
/// use serde_json::json;
///
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
/// # let mut client = Client::new(AgentOptions::default())?;
/// # client.send("test").await?;
/// while let Some(block) = client.receive().await? {
/// if let ContentBlock::ToolUse(tool_use) = block {
/// // Try to execute tool
/// let result = match execute_tool(tool_use.name(), tool_use.input()) {
/// Ok(output) => output,
/// Err(e) => json!({
/// "error": e.to_string(),
/// "tool": tool_use.name()
/// })
/// };
///
/// client.add_tool_result(tool_use.id(), result)?;
/// client.send("").await?;
/// }
/// }
///
/// # fn execute_tool(name: &str, input: &serde_json::Value) -> Result<serde_json::Value, Box<dyn std::error::Error>> {
/// # Ok(json!({}))
/// # }
/// # Ok(())
/// # }
/// ```
///
/// ## Multiple Tool Calls
///
/// ```rust,no_run
/// use open_agent::{Client, AgentOptions, ContentBlock};
/// use serde_json::json;
///
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
/// # let mut client = Client::new(AgentOptions::default())?;
/// client.send("Calculate 2+2 and 3+3").await?;
///
/// let mut tool_calls = Vec::new();
///
/// // Collect all tool calls
/// while let Some(block) = client.receive().await? {
/// if let ContentBlock::ToolUse(tool_use) = block {
/// tool_calls.push(tool_use);
/// }
/// }
///
/// // Execute and add results for all tools
/// for tool_call in tool_calls {
/// let result = json!({"result": 42}); // Execute tool
/// client.add_tool_result(tool_call.id(), result)?;
/// }
///
/// // Continue conversation
/// client.send("").await?;
/// # Ok(())
/// # }
/// ```
pub fn add_tool_result(&mut self, tool_use_id: &str, content: serde_json::Value) -> Result<()> {
use crate::types::ToolResultBlock;
// Flush any buffered manual-mode blocks first so the assistant's
// tool_calls message appears in history before this tool result.
if !self.manual_receive_buffer.is_empty() {
let blocks = std::mem::take(&mut self.manual_receive_buffer);
self.history.push(Message::assistant(blocks));
}
// Create a tool result block with the given ID and content
let result_block = ToolResultBlock::new(tool_use_id, content);
// Add to history as a tool message
// Note: ToolResultBlock is properly serialized in build_api_request()
// as a separate message with role="tool" and tool_call_id set
let serialized = serde_json::to_string(result_block.content())
.map_err(|e| Error::config(format!("Failed to serialize tool result: {}", e)))?;
self.history.push(Message::new(
MessageRole::Tool,
vec![ContentBlock::Text(TextBlock::new(serialized))],
));
Ok(())
}
/// Looks up a registered tool by name.
///
/// This method provides access to the tool registry for manual execution scenarios.
/// It searches the tools registered in `AgentOptions` and returns a reference to
/// the matching tool if found.
///
/// # Parameters
///
/// - `name`: The tool name to search for (case-sensitive)
///
/// # Returns
///
/// - `Some(&Tool)`: Tool found
/// - `None`: No tool with that name
///
/// # Use Cases
///
/// - Manual tool execution in response to `ToolUseBlock`
/// - Validating tool availability before offering features
/// - Inspecting tool metadata (name, description, schema)
///
/// # Examples
///
/// ## Execute Tool Manually
///
/// ```rust,no_run
/// use open_agent::{Client, AgentOptions, ContentBlock};
///
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
/// # let mut client = Client::new(AgentOptions::default())?;
/// # client.send("test").await?;
/// while let Some(block) = client.receive().await? {
/// if let ContentBlock::ToolUse(tool_use) = block {
/// if let Some(tool) = client.get_tool(tool_use.name()) {
/// // Execute the tool
/// let result = tool.execute(tool_use.input().clone()).await?;
/// client.add_tool_result(tool_use.id(), result)?;
/// client.send("").await?;
/// } else {
/// println!("Unknown tool: {}", tool_use.name());
/// }
/// }
/// }
/// # Ok(())
/// # }
/// ```
///
/// ## Check Tool Availability
///
/// ```rust
/// # use open_agent::{Client, AgentOptions};
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// # let client = Client::new(AgentOptions::default())?;
/// if client.get_tool("calculator").is_some() {
/// println!("Calculator is available");
/// }
/// # Ok(())
/// # }
/// ```
pub fn get_tool(&self, name: &str) -> Option<&crate::tools::Tool> {
// Search registered tools by name
self.options
.tools()
.iter()
.find(|t| t.name() == name)
.map(|t| t.as_ref())
}
}