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
269
270
271
272
273
274
275
276
277
278
279
280
/// Stateful client for multi-turn conversations with automatic history management.
///
/// The `Client` is the primary interface for building conversational AI applications.
/// It maintains conversation history, manages streaming responses, and provides two
/// modes of operation: manual and automatic tool execution.
///
/// # State Management
///
/// The client maintains several pieces of state that persist across multiple turns:
///
/// - **Conversation History**: Complete record of all messages exchanged
/// - **Active Stream**: Currently active SSE stream being consumed
/// - **Interrupt Flag**: Thread-safe cancellation signal
/// - **Auto-Execution Buffer**: Cached blocks for auto-execution mode
/// - **Last Finish Reason**: Why the most recent stream stopped, via `finish_reason()`
///
/// # Operating Modes
///
/// ## Manual Mode (default)
///
/// In manual mode, the client streams blocks directly to the caller. When the model
/// requests a tool, you receive a `ToolUseBlock`, execute the tool yourself, add the
/// result with `add_tool_result()`, and continue the conversation.
///
/// **Advantages**:
/// - Full control over tool execution
/// - Custom error handling per tool
/// - Ability to modify tool inputs/outputs
/// - Interactive debugging capabilities
///
/// ## Automatic Mode (`auto_execute_tools = true`)
///
/// In automatic mode, the client executes tools transparently and only returns the
/// final text response after all tool iterations complete.
///
/// **Advantages**:
/// - Simpler API for common use cases
/// - Built-in retry logic via hooks
/// - Automatic conversation continuation
/// - Configurable iteration limits
///
/// # Thread Safety
///
/// The client is NOT thread-safe for concurrent use. However, the interrupt mechanism
/// uses `Arc<AtomicBool>` which can be safely shared across threads to signal cancellation.
///
/// # Memory Management
///
/// - History grows unbounded by default (consider clearing periodically)
/// - Streams are consumed lazily (low memory footprint during streaming)
/// - Auto-execution buffers entire response (higher memory in auto mode)
///
/// # Examples
///
/// ## Basic Multi-Turn Conversation
///
/// ```rust,no_run
/// use open_agent::{Client, AgentOptions, ContentBlock};
///
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
/// let mut client = Client::new(AgentOptions::builder()
/// .model("gpt-4")
/// .api_key("sk-...")
/// .build()?)?;
///
/// // First question
/// client.send("What's the capital of France?").await?;
/// while let Some(block) = client.receive().await? {
/// if let ContentBlock::Text(text) = block {
/// println!("{}", text.text); // "Paris is the capital of France."
/// }
/// }
///
/// // Follow-up question - history is automatically maintained
/// client.send("What's its population?").await?;
/// while let Some(block) = client.receive().await? {
/// if let ContentBlock::Text(text) = block {
/// println!("{}", text.text); // "Paris has approximately 2.2 million people."
/// }
/// }
/// # Ok(())
/// # }
/// ```
///
/// ## Manual Tool Execution
///
/// ```rust,no_run
/// use open_agent::{Client, AgentOptions, ContentBlock, Tool};
/// use serde_json::json;
///
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
/// let calculator = Tool::new(
/// "calculator",
/// "Performs arithmetic",
/// json!({"type": "object"}),
/// |input| Box::pin(async move { Ok(json!({"result": 42})) })
/// );
///
/// let mut client = Client::new(AgentOptions::builder()
/// .model("gpt-4")
/// .api_key("sk-...")
/// .tools(vec![calculator])
/// .build()?)?;
///
/// client.send("What's 2+2?").await?;
///
/// while let Some(block) = client.receive().await? {
/// match block {
/// ContentBlock::ToolUse(tool_use) => {
/// // Execute tool manually
/// let result = json!({"result": 4});
/// client.add_tool_result(tool_use.id(), result)?;
///
/// // Continue conversation to get model's response
/// client.send("").await?;
/// }
/// ContentBlock::Text(text) => {
/// println!("{}", text.text); // "The result is 4."
/// }
/// ContentBlock::ToolResult(_) | ContentBlock::Image(_) => {}
/// }
/// }
/// # Ok(())
/// # }
/// ```
///
/// ## Automatic Tool Execution
///
/// ```rust,no_run
/// use open_agent::{Client, AgentOptions, ContentBlock, Tool};
/// use serde_json::json;
///
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
/// let calculator = Tool::new(
/// "calculator",
/// "Performs arithmetic",
/// json!({"type": "object"}),
/// |input| Box::pin(async move { Ok(json!({"result": 42})) })
/// );
///
/// let mut client = Client::new(AgentOptions::builder()
/// .model("gpt-4")
/// .api_key("sk-...")
/// .tools(vec![calculator])
/// .auto_execute_tools(true) // Enable auto-execution
/// .build()?)?;
///
/// client.send("What's 2+2?").await?;
///
/// // Tools execute automatically - you only receive final text
/// while let Some(block) = client.receive().await? {
/// if let ContentBlock::Text(text) = block {
/// println!("{}", text.text); // "The result is 4."
/// }
/// }
/// # Ok(())
/// # }
/// ```
///
/// ## With Interruption
///
/// ```rust,no_run
/// use open_agent::{Client, AgentOptions};
/// use std::time::Duration;
///
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
/// let mut client = Client::new(AgentOptions::default())?;
///
/// // Start a long-running query
/// client.send("Write a very long story").await?;
///
/// // Spawn a task to interrupt after timeout
/// let interrupt_handle = client.interrupt_handle();
/// tokio::spawn(async move {
/// tokio::time::sleep(Duration::from_secs(5)).await;
/// interrupt_handle.store(true, std::sync::atomic::Ordering::SeqCst);
/// });
///
/// // This loop will stop when interrupted
/// while let Some(block) = client.receive().await? {
/// // Process blocks...
/// }
///
/// // Client is still usable after interruption
/// client.send("What's 2+2?").await?;
/// # Ok(())
/// # }
/// ```